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