MpegAudioStream.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include "MpegAudioStream.hxx"
00023 #include "AudioFile.hxx"
00024 #include "Assert.hxx"
00025 #include <iostream>
00026 
00027 namespace CLAM
00028 {
00029 
00030 namespace AudioCodecs
00031 {
00032         // A reasonable multiple of four
00033         const TSize MpegAudioStream::mMaxDecodedBlockSize = 8192;
00034 
00035         MpegAudioStream::MpegAudioStream( const AudioFile& file )
00036                 : mpHandle( NULL )
00037         {
00038                 mName = file.GetLocation();
00039                 mEncodedSampleRate = (int)file.GetHeader().GetSampleRate();
00040                 mChannels = (int)file.GetHeader().GetChannels();
00041                 mDecodeBuffer.resize( mChannels );
00042         }
00043 
00044         MpegAudioStream::~MpegAudioStream()
00045         {
00046                 if ( not mpHandle ) return;
00047                 fclose(mpHandle);
00048         }
00049         
00050         void MpegAudioStream::PrepareReading()
00051         {
00052                 mpHandle = fopen( mName.c_str(), "rb");
00053 
00054                 if ( !mpHandle )
00055                 {
00056                         std::string msgString = "Could not open ";
00057                         msgString += mName;
00058                         msgString += " for reading!";
00059 
00060                         CLAM_ASSERT( false, msgString.c_str() );
00061                 }
00062 
00063                 mBitstream.Init( mpHandle );
00064 
00065                 mSamplesDecoded = 0;
00066                 mFramePosition = 0;
00067                 _mp3Frame = 0;
00068         }
00069 
00070         void MpegAudioStream::PrepareWriting()
00071         {
00072                 CLAM_ASSERT( false, "CLAM does not encode Mpeg Audio!!!");
00073         }
00074 
00075         void MpegAudioStream::Dispose()
00076         {
00077                 mBitstream.Finish();
00078         }
00079 
00080         void MpegAudioStream::DiskToMemoryTransfer()
00081         {
00082                 unsigned nFrames = mInterleavedData.size()/mChannels;
00083                 while( mDecodeBuffer[0].size() < nFrames
00084                        && mBitstream.NextFrame() )
00085                 {
00086                         unsigned long filePos = mBitstream.CurrentFrameFileOffset();
00087                         if (_mp3Frame>=_seekCache.size())
00088                                 _seekCache.push_back(filePos);
00089                         else
00090                         {
00091 /*
00092                                 std::cout
00093                                         << "Mp3 frame "
00094                                         << mFramePosition << " "
00095                                         << _mp3Frame << " R:"
00096                                         << filePos << " E:"
00097                                         << _seekCache[_mp3Frame] << " R-E:"
00098                                         << int(filePos)-int(_seekCache[_mp3Frame])
00099                                         << std::endl;
00100 */
00101                                 CLAM_WARNING(filePos==_seekCache[_mp3Frame],
00102                                         "MP3 indexing not matching");
00103                         }
00104                         mBitstream.SynthesizeCurrent();
00105                         
00106                         CLAM_ASSERT( mChannels == MAD_NCHANNELS( &mBitstream.CurrentFrame().header ),
00107                                      "MpegAudioStream: A frame had not the expected channels." );
00108                         CLAM_ASSERT( mChannels == mBitstream.CurrentSynthesis().pcm.channels,
00109                                      "MpegAudioStream: Synthesis result had not the expected number of channels" );
00110                         
00111                         TSize samplesDecodedThisTime = mBitstream.CurrentSynthesis().pcm.length;
00112                         for(unsigned i = 0; i < mChannels; i++ )
00113                         {
00114                                 mad_fixed_t* channelData = mBitstream.CurrentSynthesis().pcm.samples[i];
00115                                 mDecodeBuffer[i].insert( mDecodeBuffer[i].end(),
00116                                                          channelData,
00117                                                          channelData + samplesDecodedThisTime );
00118                         }
00119                         mSamplesDecoded += samplesDecodedThisTime;
00120                         _mp3Frame++;
00121                 }
00122                 
00123                 mFramesLastRead = mDecodeBuffer[0].size();
00124                 mEOFReached = mBitstream.EOS() && mDecodeBuffer[0].empty();
00125 
00126                 if (mDecodeBuffer[0].empty()) return;
00127 
00128                 for (unsigned i = 0; i < mChannels; i++ )
00129                 {
00130                         if ( mDecodeBuffer[i].size() < nFrames )
00131                         {
00132                                 mDecodeBuffer[i].insert(
00133                                         mDecodeBuffer[i].end(),
00134                                         nFrames - mDecodeBuffer[i].size(),
00135                                         mad_fixed_t(0) );
00136                         }
00137                 }
00138                 ConsumeDecodedSamples();
00139         }
00140 
00141         void MpegAudioStream::ConsumeDecodedSamples()
00142         {
00143                 unsigned nFrames = mInterleavedData.size()/mChannels;
00144                 for (unsigned i = 0; i < mChannels; i++ )
00145                 {
00146                         unsigned currOffset = 0;
00147                         for ( std::deque<mad_fixed_t>::iterator j = mDecodeBuffer[i].begin();
00148                               currOffset < mInterleavedData.size(); 
00149                               j++, currOffset+=mChannels )
00150                         {
00151                                 double sampleValue = mad_f_todouble(*j);
00152 
00153                                 // :TODO: Finding a nicer way to clamp things
00154                                 // to the -1,1 could be necessary
00155                                 // clipping
00156                                 if ( sampleValue > 1.0 )
00157                                         sampleValue = 1.0;
00158                                 else if ( sampleValue < -1.0 )
00159                                         sampleValue = -1.0;
00160 
00161                                 mInterleavedData[ currOffset + i ] = TData(sampleValue);
00162                         }
00163                         mDecodeBuffer[i].erase( mDecodeBuffer[i].begin(),
00164                                 mDecodeBuffer[i].begin() + nFrames );
00165                 }
00166                 mFramePosition += nFrames;
00167         }
00168 
00169         void MpegAudioStream::MemoryToDiskTransfer()
00170         {
00171                 CLAM_ASSERT( false, "CLAM does not encode Mpeg Audio!!!");
00172         }
00173 
00174         void MpegAudioStream::SeekTo(unsigned long framePosition)
00175         {
00176                 // Warning: two 'frames' here, mp3's and AudioStream's (items*channels)
00177                 if (framePosition==mFramePosition) return;
00178                 unsigned mp3FrameSize = 32*MAD_NSBSAMPLES(&mBitstream.CurrentFrame().header);
00179                 unsigned targetMp3Frame = framePosition/mp3FrameSize;
00180                 std::cout << "targetMp3Frame: " << targetMp3Frame << std::endl;
00181                 // TODO: Jumps beyond the last played frame are too expensive to be frame accurate
00182                 unsigned maxForwarJump = _seekCache.size()+100;
00183                 if (targetMp3Frame>maxForwarJump) targetMp3Frame=maxForwarJump;
00184                 if (targetMp3Frame>=_seekCache.size())
00185                 {
00186                         // Construct the index beyond current limits, til the target frame
00187                         fseek(mpHandle, _seekCache[_seekCache.size()-2], SEEK_SET);
00188                         mBitstream.Init();
00189                         for (unsigned mp3Frame=_seekCache.size();
00190                                 mp3Frame<=targetMp3Frame; mp3Frame++)
00191                         {
00192                                 if (not mBitstream.NextFrame()) return;
00193                                 unsigned long filePos = mBitstream.CurrentFrameFileOffset();
00194 /*
00195                                 std::cout << "fwd "
00196                                         << _seekCache.back() << " "
00197                                         << filePos << " " 
00198                                         << int(filePos)-_seekCache.back() << std::endl;
00199 */
00200                                 if (filePos<=_seekCache.back()) continue; // filtering already done
00201                                 _seekCache.push_back(filePos);
00202                         }
00203                 }
00204                 unsigned skip = targetMp3Frame>4?3:0;
00205                 unsigned jumpingFrame = targetMp3Frame-skip;
00206                 std::cout << "jumpingFrame:" << jumpingFrame << std::endl;
00207                 fseek(mpHandle, _seekCache[jumpingFrame], SEEK_SET);
00208                 mBitstream.Init();
00209                 _mp3Frame = targetMp3Frame;
00210                 // TODO: Because the already buffered bins this is not true
00211                 mFramePosition = mp3FrameSize * _mp3Frame;
00212                 if (targetMp3Frame == 0) return;
00213                 unsigned long previousFrameFilePos = _seekCache[targetMp3Frame-1];
00214                 while (true)
00215                 {
00216                         mBitstream.NextFrame();
00217                         unsigned long filePos = mBitstream.CurrentFrameFileOffset();
00218 /*
00219                         std::cout << "seek "
00220                                 << filePos << " "
00221                                 << previousFrameFilePos << std::endl;
00222 */
00223                         if (filePos >= previousFrameFilePos) break;
00224                 }
00225                 mBitstream.SynthesizeCurrent();
00226 
00227                 unsigned long filePos = mBitstream.CurrentFrameFileOffset();
00228 /*
00229                 std::cout << "Jump n target: " 
00230                         << _mp3Frame << " "
00231                         << targetMp3Frame << " "
00232                         << filePos << " "
00233                         << _seekCache[targetMp3Frame-1]
00234                         << std::endl;
00235 */
00236                 // TODO: Sample accurate seek (now it is frame accurate)
00237 /*
00238                 CLAM_ASSERT(_seekCache[targetMp3Frame-1]==filePos,
00239                         "File postions don't match");
00240                 CLAM_ASSERT(_mp3Frame==targetMp3Frame, "Seek mp3 frames don't match");
00241                 // We should synthesize the previous frame to the one we want.
00242 */      }
00243 }
00244         
00245 }
00246 
Generated by  doxygen 1.6.3