File indexing completed on 2024-05-12 04:51:32

0001 /*
0002     SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bmad.h"
0007 
0008 #include <QDebug>
0009 #include <QFile>
0010 
0011 
0012 static const int INPUT_BUFFER_SIZE = 5*8192;
0013 
0014 
0015 K3bMad::K3bMad()
0016   : madStream(NULL),
0017     madFrame(NULL),
0018     madSynth(NULL),
0019     madTimer(NULL),
0020     m_madStructuresInitialized(false),
0021     m_bInputError(false)
0022 {
0023   madStream = new mad_stream;
0024   madFrame  = new mad_frame;
0025   madSynth  = new mad_synth;
0026   madTimer  = new mad_timer_t;
0027 
0028   //
0029   // we allocate additional MAD_BUFFER_GUARD bytes to always be able to append the
0030   // zero bytes needed for decoding the last frame.
0031   //
0032   m_inputBuffer = new unsigned char[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD];
0033 }
0034 
0035 
0036 K3bMad::~K3bMad()
0037 {
0038   cleanup();
0039 
0040   delete madStream; madStream = NULL;
0041   delete madFrame; madFrame = NULL;
0042   delete madSynth; madSynth = NULL;
0043   delete madTimer; madTimer = NULL;
0044 
0045   delete [] m_inputBuffer;
0046 }
0047 
0048 
0049 bool K3bMad::open( const QString& filename )
0050 {
0051   cleanup();
0052 
0053   m_bInputError = false;
0054   m_channels = m_sampleRate = 0;
0055 
0056   m_inputFile.setFileName( filename );
0057 
0058   if( !m_inputFile.open( QIODevice::ReadOnly ) ) {
0059     qCritical() << "(K3bMad) could not open file " << m_inputFile.fileName() << Qt::endl;
0060     return false;
0061   }
0062 
0063   initMad();
0064 
0065   memset( m_inputBuffer, 0, INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD );
0066 
0067   return true;
0068 }
0069 
0070 
0071 bool K3bMad::inputError() const
0072 {
0073   return m_bInputError;
0074 }
0075 
0076 
0077 bool K3bMad::fillStreamBuffer()
0078 {
0079   /* The input bucket must be filled if it becomes empty or if
0080    * it's the first execution of the loop.
0081    */
0082   if( madStream->buffer == 0 || madStream->error == MAD_ERROR_BUFLEN ) {
0083     if( eof() )
0084       return false;
0085 
0086     long readSize, remaining;
0087     unsigned char* readStart;
0088 
0089     if( madStream->next_frame != 0 ) {
0090       remaining = madStream->bufend - madStream->next_frame;
0091       memmove( m_inputBuffer, madStream->next_frame, remaining );
0092       readStart = m_inputBuffer + remaining;
0093       readSize = INPUT_BUFFER_SIZE - remaining;
0094     }
0095     else {
0096       readSize  = INPUT_BUFFER_SIZE;
0097       readStart = m_inputBuffer;
0098       remaining = 0;
0099     }
0100 
0101     // Fill-in the buffer.
0102     qint64 result = m_inputFile.read( (char*)readStart, readSize );
0103     if( result < 0 ) {
0104       qDebug() << "(K3bMad) read error on bitstream)";
0105       m_bInputError = true;
0106       return false;
0107     }
0108     else if( result == 0 ) {
0109       qDebug() << "(K3bMad) end of input stream";
0110       return false;
0111     }
0112     else {
0113       readStart += result;
0114 
0115       if( eof() ) {
0116     qDebug() << "(K3bMad::fillStreamBuffer) MAD_BUFFER_GUARD";
0117     memset( readStart, 0, MAD_BUFFER_GUARD );
0118     result += MAD_BUFFER_GUARD;
0119       }
0120 
0121       // Pipe the new buffer content to libmad's stream decoder facility.
0122       mad_stream_buffer( madStream, m_inputBuffer, result + remaining );
0123       madStream->error = MAD_ERROR_NONE;
0124     }
0125   }
0126 
0127   return true;
0128 }
0129 
0130 
0131 bool K3bMad::skipTag()
0132 {
0133   // skip the tag at the beginning of the file
0134   m_inputFile.seek( 0 );
0135 
0136   //
0137   // now check if the file starts with an id3 tag and skip it if so
0138   //
0139   char buf[4096];
0140   int bufLen = 4096;
0141   if( m_inputFile.read( buf, bufLen ) < bufLen ) {
0142     qDebug() << "(K3bMad) unable to read " << bufLen << " bytes from "
0143           << m_inputFile.fileName() << Qt::endl;
0144     return false;
0145   }
0146 
0147   if( ( buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3' ) &&
0148       ( (unsigned short)buf[3] < 0xff && (unsigned short)buf[4] < 0xff ) ) {
0149     // do we have a footer?
0150     bool footer = (buf[5] & 0x10);
0151 
0152     // the size is saved as a synched int meaning bit 7 is always cleared to 0
0153     unsigned int size =
0154       ( (buf[6] & 0x7f) << 21 ) |
0155       ( (buf[7] & 0x7f) << 14 ) |
0156       ( (buf[8] & 0x7f) << 7) |
0157       (buf[9] & 0x7f);
0158     unsigned int offset = size + 10;
0159     if( footer )
0160       offset += 10;
0161 
0162     qDebug() << "(K3bMad) skipping past ID3 tag to " << offset;
0163 
0164     // skip the id3 tag
0165     if( !m_inputFile.seek(offset) ) {
0166       qDebug() << "(K3bMad) " << m_inputFile.fileName()
0167         << ": couldn't seek to " << offset << Qt::endl;
0168       return false;
0169     }
0170   }
0171   else {
0172     // reset file
0173     return m_inputFile.seek( 0 );
0174   }
0175 
0176   return true;
0177 }
0178 
0179 
0180 bool K3bMad::seekFirstHeader()
0181 {
0182   //
0183   // A lot of mp3 files start with a lot of junk which confuses mad.
0184   // We "allow" an mp3 file to start with at most 1 KB of junk. This is just
0185   // some random value since we do not want to search the hole file. That would
0186   // take way to long for non-mp3 files.
0187   //
0188   bool headerFound = findNextHeader();
0189   qint64 inputPos = streamPos();
0190   while( !headerFound &&
0191      !m_inputFile.atEnd() &&
0192      streamPos() <= inputPos+1024 ) {
0193     headerFound = findNextHeader();
0194   }
0195 
0196   // seek back to the begin of the frame
0197   if( headerFound ) {
0198     int streamSize = madStream->bufend - madStream->buffer;
0199     int bytesToFrame = madStream->this_frame - madStream->buffer;
0200     m_inputFile.seek( m_inputFile.pos() - streamSize + bytesToFrame );
0201 
0202     qDebug() << "(K3bMad) found first header at " << m_inputFile.pos();
0203   }
0204 
0205   // reset the stream to make sure mad really starts decoding at out seek position
0206   mad_stream_finish( madStream );
0207   mad_stream_init( madStream );
0208 
0209   return headerFound;
0210 }
0211 
0212 
0213 bool K3bMad::eof() const
0214 {
0215   return m_inputFile.atEnd();
0216 }
0217 
0218 
0219 qint64 K3bMad::inputPos() const
0220 {
0221   return m_inputFile.pos();
0222 }
0223 
0224 
0225 qint64 K3bMad::streamPos() const
0226 {
0227   return inputPos() - (madStream->bufend - madStream->this_frame + 1);
0228 }
0229 
0230 
0231 bool K3bMad::inputSeek( qint64 pos )
0232 {
0233   return m_inputFile.seek( pos );
0234 }
0235 
0236 
0237 void K3bMad::initMad()
0238 {
0239   if( !m_madStructuresInitialized ) {
0240     mad_stream_init( madStream );
0241     mad_timer_reset( madTimer );
0242     mad_frame_init( madFrame );
0243     mad_synth_init( madSynth );
0244 
0245     m_madStructuresInitialized = true;
0246   }
0247 }
0248 
0249 
0250 void K3bMad::cleanup()
0251 {
0252   if (m_inputFile.isOpen()) {
0253 #ifdef K3B_DEBUG
0254     if (madStream) {
0255       qDebug() << "(K3bMad) cleanup at offset: "
0256             << "Input file at: " << m_inputFile.pos() << " "
0257             << "Input file size: " << m_inputFile.size() << " "
0258             << "stream pos: "
0259             << (m_inputFile.pos() - (madStream->bufend - madStream->this_frame + 1));
0260     }
0261 #endif
0262     m_inputFile.close();
0263   }
0264 
0265   if (m_madStructuresInitialized && madFrame && madSynth && madStream) {
0266     mad_frame_finish(madFrame);
0267     mad_synth_finish(madSynth);
0268     mad_stream_finish(madStream);
0269   }
0270 
0271   m_madStructuresInitialized = false;
0272 }
0273 
0274 
0275 //
0276 // LOSTSYNC could happen when mad encounters the id3 tag...
0277 //
0278 bool K3bMad::findNextHeader()
0279 {
0280   if( !fillStreamBuffer() )
0281     return false;
0282 
0283   //
0284   // MAD_RECOVERABLE == true:  frame was read, decoding failed (about to skip frame)
0285   // MAD_RECOVERABLE == false: frame was not read, need data
0286   //
0287 
0288   if( mad_header_decode( &madFrame->header, madStream ) < 0 ) {
0289     if( MAD_RECOVERABLE( madStream->error ) ||
0290     madStream->error == MAD_ERROR_BUFLEN ) {
0291       return findNextHeader();
0292     }
0293     else
0294       qDebug() << "(K3bMad::findNextHeader) error: " << mad_stream_errorstr( madStream );
0295 
0296     // FIXME probably we should not do this here since we don't do it
0297     // in the frame decoding
0298 //     if( !checkFrameHeader( &madFrame->header ) )
0299 //       return findNextHeader();
0300 
0301     return false;
0302   }
0303 
0304   if( !m_channels ) {
0305     m_channels = MAD_NCHANNELS(&madFrame->header);
0306     m_sampleRate = madFrame->header.samplerate;
0307   }
0308 
0309   mad_timer_add( madTimer, madFrame->header.duration );
0310 
0311   return true;
0312 }
0313 
0314 
0315 bool K3bMad::decodeNextFrame()
0316 {
0317   if( !fillStreamBuffer() )
0318     return false;
0319 
0320   if( mad_frame_decode( madFrame, madStream ) < 0 ) {
0321     if( MAD_RECOVERABLE( madStream->error ) ||
0322     madStream->error == MAD_ERROR_BUFLEN ) {
0323       return decodeNextFrame();
0324     }
0325 
0326     return false;
0327   }
0328 
0329   if( !m_channels ) {
0330     m_channels = MAD_NCHANNELS(&madFrame->header);
0331     m_sampleRate = madFrame->header.samplerate;
0332   }
0333 
0334   mad_timer_add( madTimer, madFrame->header.duration );
0335 
0336   return true;
0337 }
0338 
0339 
0340 //
0341 // This is from the arts mad decoder
0342 //
0343 bool K3bMad::checkFrameHeader( mad_header* header ) const
0344 {
0345   int frameSize = MAD_NSBSAMPLES( header ) * 32;
0346 
0347   if( frameSize <= 0 )
0348     return false;
0349 
0350   if( m_channels && m_channels != MAD_NCHANNELS(header) )
0351     return false;
0352 
0353   return true;
0354 }
0355 
0356