/* * * $Id: k3baudiodecoder.cpp 619556 2007-01-03 17:38:12Z trueg $ * Copyright (C) 2003 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * See the file "COPYING" for the exact licensing terms. */ #include #include #include "k3baudiodecoder.h" #include "k3bpluginmanager.h" #include #include #include #include #ifdef HAVE_LIBSAMPLERATE #include #else #include "libsamplerate/samplerate.h" #endif #if !(HAVE_LRINT && HAVE_LRINTF) #define lrint(dbl) ((int) (dbl)) #define lrintf(flt) ((int) (flt)) #endif // use a one second buffer static const int DECODING_BUFFER_SIZE = 75*2352; class K3bAudioDecoder::Private { public: Private() : metaInfo(0), resampleState(0), resampleData(0), inBuffer(0), inBufferPos(0), inBufferFill(0), outBuffer(0), monoBuffer(0), decodingBufferPos(0), decodingBufferFill(0), valid(true) { } // the current position of the decoder // This does NOT include the decodingBuffer K3b::Msf currentPos; // since the current position above is measured in frames // there might be a little offset since the decoded data is not // always a multiple of 2353 bytes int currentPosOffset; // already decoded bytes from last init or last seek // TODO: replace alreadyDecoded with currentPos unsigned long alreadyDecoded; K3b::Msf decodingStartPos; KFileMetaInfo* metaInfo; // set to true once decodeInternal() returned 0 bool decoderFinished; // resampling SRC_STATE* resampleState; SRC_DATA* resampleData; float* inBuffer; float* inBufferPos; int inBufferFill; float* outBuffer; int samplerate; int channels; // mono -> stereo conversion char* monoBuffer; char decodingBuffer[DECODING_BUFFER_SIZE]; char* decodingBufferPos; int decodingBufferFill; TQMap technicalInfoMap; TQMap metaInfoMap; bool valid; }; K3bAudioDecoder::K3bAudioDecoder( TQObject* tqparent, const char* name ) : TQObject( tqparent, name ) { d = new Private(); } K3bAudioDecoder::~K3bAudioDecoder() { cleanup(); if( d->inBuffer ) delete [] d->inBuffer; if( d->outBuffer ) delete [] d->outBuffer; if( d->monoBuffer ) delete [] d->monoBuffer; delete d->metaInfo; delete d->resampleData; if( d->resampleState ) src_delete( d->resampleState ); delete d; } void K3bAudioDecoder::setFilename( const TQString& filename ) { m_fileName = filename; delete d->metaInfo; d->metaInfo = 0; } bool K3bAudioDecoder::isValid() const { return d->valid; } bool K3bAudioDecoder::analyseFile() { d->technicalInfoMap.clear(); d->metaInfoMap.clear(); delete d->metaInfo; d->metaInfo = 0; cleanup(); bool ret = analyseFileInternal( m_length, d->samplerate, d->channels ); if( ret && ( d->channels == 1 || d->channels == 2 ) && m_length > 0 ) { d->valid = initDecoder(); return d->valid; } else { d->valid = false; return false; } } bool K3bAudioDecoder::initDecoder( const K3b::Msf& startOffset ) { if( initDecoder() ) { if( startOffset > 0 ) return seek( startOffset ); else return true; } else return false; } bool K3bAudioDecoder::initDecoder() { cleanup(); if( d->resampleState ) src_reset( d->resampleState ); d->alreadyDecoded = 0; d->currentPos = 0; d->currentPosOffset = 0; d->decodingBufferFill = 0; d->decodingBufferPos = 0; d->decodingStartPos = 0; d->inBufferFill = 0; d->decoderFinished = false; return initDecoderInternal(); } int K3bAudioDecoder::decode( char* _data, int maxLen ) { unsigned long lengthToDecode = (m_length - d->decodingStartPos).audioBytes(); if( d->alreadyDecoded >= lengthToDecode ) return 0; if( maxLen <= 0 ) return 0; int read = 0; if( d->decodingBufferFill == 0 ) { // // now we decode into the decoding buffer // to ensure a minimum buffer size // d->decodingBufferFill = 0; d->decodingBufferPos = d->decodingBuffer; if( !d->decoderFinished ) { if( d->samplerate != 44100 ) { // check if we have data left from some previous conversion if( d->inBufferFill > 0 ) { read = resample( d->decodingBuffer, DECODING_BUFFER_SIZE ); } else { if( !d->inBuffer ) { d->inBuffer = new float[DECODING_BUFFER_SIZE/2]; } if( (read = decodeInternal( d->decodingBuffer, DECODING_BUFFER_SIZE )) == 0 ) d->decoderFinished = true; d->inBufferFill = read/2; d->inBufferPos = d->inBuffer; from16bitBeSignedToFloat( d->decodingBuffer, d->inBuffer, d->inBufferFill ); read = resample( d->decodingBuffer, DECODING_BUFFER_SIZE ); } } else if( d->channels == 1 ) { if( !d->monoBuffer ) { d->monoBuffer = new char[DECODING_BUFFER_SIZE/2]; } // we simply duplicate every frame if( (read = decodeInternal( d->monoBuffer, DECODING_BUFFER_SIZE/2 )) == 0 ) d->decoderFinished = true; for( int i = 0; i < read; i+=2 ) { d->decodingBuffer[2*i] = d->decodingBuffer[2*i+2] = d->monoBuffer[i]; d->decodingBuffer[2*i+1] = d->decodingBuffer[2*i+3] = d->monoBuffer[i+1]; } read *= 2; } else { if( (read = decodeInternal( d->decodingBuffer, DECODING_BUFFER_SIZE )) == 0 ) d->decoderFinished = true; } } if( read < 0 ) { return -1; } else if( read == 0 ) { // check if we need to pad int bytesToPad = lengthToDecode - d->alreadyDecoded; if( bytesToPad > 0 ) { kdDebug() << "(K3bAudioDecoder) track length: " << lengthToDecode << "; decoded module data: " << d->alreadyDecoded << "; we need to pad " << bytesToPad << " bytes." << endl; if( DECODING_BUFFER_SIZE < bytesToPad ) bytesToPad = DECODING_BUFFER_SIZE; ::memset( d->decodingBuffer, 0, bytesToPad ); kdDebug() << "(K3bAudioDecoder) padded " << bytesToPad << " bytes." << endl; read = bytesToPad; } else { kdDebug() << "(K3bAudioDecoder) decoded " << d->alreadyDecoded << " bytes." << endl; return 0; } } else { // check if we decoded too much if( d->alreadyDecoded + read > lengthToDecode ) { kdDebug() << "(K3bAudioDecoder) we decoded too much. Cutting output by " << (read + d->alreadyDecoded - lengthToDecode) << endl; read = lengthToDecode - d->alreadyDecoded; } } d->decodingBufferFill = read; } // clear out the decoding buffer read = TQMIN( maxLen, d->decodingBufferFill ); ::memcpy( _data, d->decodingBufferPos, read ); d->decodingBufferPos += read; d->decodingBufferFill -= read; d->alreadyDecoded += read; d->currentPos += (read+d->currentPosOffset)/2352; d->currentPosOffset = (read+d->currentPosOffset)%2352; return read; } // resample data in d->inBufferPos and save the result to data // // int K3bAudioDecoder::resample( char* data, int maxLen ) { if( !d->resampleState ) { d->resampleState = src_new( SRC_SINC_MEDIUM_TQUALITY, d->channels, 0 ); if( !d->resampleState ) { kdDebug() << "(K3bAudioDecoder) unable to initialize resampler." << endl; return -1; } d->resampleData = new SRC_DATA; } if( !d->outBuffer ) { d->outBuffer = new float[DECODING_BUFFER_SIZE/2]; } d->resampleData->data_in = d->inBufferPos; d->resampleData->data_out = d->outBuffer; d->resampleData->input_frames = d->inBufferFill/d->channels; d->resampleData->output_frames = maxLen/2/2; // in case of mono files we need the space anyway d->resampleData->src_ratio = 44100.0/(double)d->samplerate; if( d->inBufferFill == 0 ) d->resampleData->end_of_input = 1; // this should force libsamplerate to output the last frames else d->resampleData->end_of_input = 0; int len = 0; if( (len = src_process( d->resampleState, d->resampleData ) ) ) { kdDebug() << "(K3bAudioDecoder) error while resampling: " << src_strerror(len) << endl; return -1; } if( d->channels == 2 ) fromFloatTo16BitBeSigned( d->outBuffer, data, d->resampleData->output_frames_gen*d->channels ); else { for( int i = 0; i < d->resampleData->output_frames_gen; ++i ) { fromFloatTo16BitBeSigned( &d->outBuffer[i], &data[4*i], 1 ); fromFloatTo16BitBeSigned( &d->outBuffer[i], &data[4*i+2], 1 ); } } d->inBufferPos += d->resampleData->input_frames_used*d->channels; d->inBufferFill -= d->resampleData->input_frames_used*d->channels; if( d->inBufferFill <= 0 ) { d->inBufferPos = d->inBuffer; d->inBufferFill = 0; } // 16 bit frames, so we need to multiply by 2 // and we always have two channels return d->resampleData->output_frames_gen*2*2; } void K3bAudioDecoder::from16bitBeSignedToFloat( char* src, float* dest, int samples ) { while( samples ) { samples--; dest[samples] = static_cast( TQ_INT16(((src[2*samples]<<8)&0xff00)|(src[2*samples+1]&0x00ff)) / 32768.0 ); } } void K3bAudioDecoder::fromFloatTo16BitBeSigned( float* src, char* dest, int samples ) { while( samples ) { samples--; float scaled = src[samples] * 32768.0; TQ_INT16 val = 0; // clipping if( scaled >= ( 1.0 * 0x7FFF ) ) val = 32767; else if( scaled <= ( -8.0 * 0x1000 ) ) val = -32768; else val = lrintf(scaled); dest[2*samples] = val>>8; dest[2*samples+1] = val; } } void K3bAudioDecoder::from8BitTo16BitBeSigned( char* src, char* dest, int samples ) { while( samples ) { samples--; float scaled = static_cast(TQ_UINT8(src[samples])-128) / 128.0 * 32768.0; TQ_INT16 val = 0; // clipping if( scaled >= ( 1.0 * 0x7FFF ) ) val = 32767; else if( scaled <= ( -8.0 * 0x1000 ) ) val = -32768; else val = lrintf(scaled); dest[2*samples] = val>>8; dest[2*samples+1] = val; } } bool K3bAudioDecoder::seek( const K3b::Msf& pos ) { kdDebug() << "(K3bAudioDecoder) seek from " << d->currentPos.toString() << " (+" << d->currentPosOffset << ") to " << pos.toString() << endl; if( pos > length() ) return false; d->decoderFinished = false; if( pos == d->currentPos && d->currentPosOffset == 0 ) return true; if( pos == 0 ) return initDecoder(); bool success = false; // // First check if we may do a "perfect seek". // We cannot rely on the decoding plugins to seek perfectly. Especially // the mp3 decoder does not. But in case we want to split a live recording // it is absolutely nesseccary to perform a perfect seek. // So if we did not already decode past the seek position and the difference // between the current position and the seek position is less than some fixed // value we simply decode up to the seek position. // if( ( pos > d->currentPos || ( pos == d->currentPos && d->currentPosOffset == 0 ) ) && ( pos - d->currentPos < K3b::Msf(0,10,0) ) ) { // < 10 seconds is ok kdDebug() << "(K3bAudioDecoder) performing perfect seek from " << d->currentPos.toString() << " to " << pos.toString() << ". :)" << endl; unsigned long bytesToDecode = pos.audioBytes() - d->currentPos.audioBytes() - d->currentPosOffset; kdDebug() << "(K3bAudioDecoder) seeking " << bytesToDecode << " bytes." << endl; char buffi[10*2352]; while( bytesToDecode > 0 ) { int read = decode( buffi, TQMIN(10*2352, bytesToDecode) ); if( read <= 0 ) return false; bytesToDecode -= read; } kdDebug() << "(K3bAudioDecoder) perfect seek done." << endl; success = true; } else { // // Here we have to reset the resampling stuff since we restart decoding at another position. // if( d->resampleState ) src_reset( d->resampleState ); d->inBufferFill = 0; // // And also reset the decoding buffer to not return any garbage from previous decoding. // d->decodingBufferFill = 0; success = seekInternal( pos ); } d->alreadyDecoded = 0; d->currentPos = d->decodingStartPos = pos; d->currentPosOffset = 0; return success; } void K3bAudioDecoder::cleanup() { } TQString K3bAudioDecoder::metaInfo( MetaDataField f ) { if( d->metaInfoMap.tqcontains( f ) ) return d->metaInfoMap[f]; // fall back to KFileMetaInfo if( !d->metaInfo ) d->metaInfo = new KFileMetaInfo( filename() ); if( d->metaInfo->isValid() ) { TQString tag; switch( f ) { case META_TITLE: tag = "Title"; break; case META_ARTIST: tag = "Artist"; break; case META_SONGWRITER: tag = "Songwriter"; break; case META_COMPOSER: tag = "Composer"; break; case META_COMMENT: tag = "Comment"; break; } KFileMetaInfoItem item = d->metaInfo->item( tag ); if( item.isValid() ) return item.string(); } return TQString(); } void K3bAudioDecoder::addMetaInfo( MetaDataField f, const TQString& value ) { if( !value.isEmpty() ) d->metaInfoMap[f] = value; else kdDebug() << "(K3bAudioDecoder) empty meta data field." << endl; } TQStringList K3bAudioDecoder::supportedTechnicalInfos() const { TQStringList l; for( TQMap::const_iterator it = d->technicalInfoMap.begin(); it != d->technicalInfoMap.end(); ++it ) l.append( it.key() ); return l; } TQString K3bAudioDecoder::technicalInfo( const TQString& key ) const { return d->technicalInfoMap[key]; } void K3bAudioDecoder::addTechnicalInfo( const TQString& key, const TQString& value ) { d->technicalInfoMap[key] = value; } K3bAudioDecoder* K3bAudioDecoderFactory::createDecoder( const KURL& url ) { kdDebug() << "(K3bAudioDecoderFactory::createDecoder( " << url.path() << " )" << endl; TQPtrList fl = k3bcore->pluginManager()->plugins( "AudioDecoder" ); // first search for a single format decoder for( TQPtrListIterator it( fl ); it.current(); ++it ) { K3bAudioDecoderFactory* f = dynamic_cast( it.current() ); if( f && !f->multiFormatDecoder() && f->canDecode( url ) ) { kdDebug() << "1" << endl; return f->createDecoder();} } // no single format decoder. Search for a multi format decoder for( TQPtrListIterator it( fl ); it.current(); ++it ) { K3bAudioDecoderFactory* f = dynamic_cast( it.current() ); if( f && f->multiFormatDecoder() && f->canDecode( url ) ) { kdDebug() << "2" << endl; return f->createDecoder();} } kdDebug() << "(K3bAudioDecoderFactory::createDecoder( " << url.path() << " ) no success" << endl; // nothing found return 0; } #include "k3baudiodecoder.moc"