CLAM-Development  1.4.0
MpegAudioStream.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "MpegAudioStream.hxx"
23 #include "AudioFile.hxx"
24 #include "Assert.hxx"
25 #include <iostream>
26 
27 namespace CLAM
28 {
29 
30 namespace AudioCodecs
31 {
32  // A reasonable multiple of four
33  const TSize MpegAudioStream::mMaxDecodedBlockSize = 8192;
34 
36  : mpHandle( NULL )
37  {
38  mName = file.GetLocation();
39  mEncodedSampleRate = (int)file.GetHeader().GetSampleRate();
40  mChannels = (int)file.GetHeader().GetChannels();
41  mDecodeBuffer.resize( mChannels );
42  }
43 
45  {
46  if ( not mpHandle ) return;
47  fclose(mpHandle);
48  }
49 
51  {
52  mpHandle = fopen( mName.c_str(), "rb");
53 
54  if ( !mpHandle )
55  {
56  std::string msgString = "Could not open ";
57  msgString += mName;
58  msgString += " for reading!";
59 
60  CLAM_ASSERT( false, msgString.c_str() );
61  }
62 
63  mBitstream.Init( mpHandle );
64 
65  mSamplesDecoded = 0;
66  mFramePosition = 0;
67  _mp3Frame = 0;
68  }
69 
71  {
72  CLAM_ASSERT( false, "CLAM does not encode Mpeg Audio!!!");
73  }
74 
76  {
77  mBitstream.Finish();
78  }
79 
81  {
82  unsigned nFrames = mInterleavedData.size()/mChannels;
83  while( mDecodeBuffer[0].size() < nFrames
84  && mBitstream.NextFrame() )
85  {
86  unsigned long filePos = mBitstream.CurrentFrameFileOffset();
87  if (_mp3Frame>=_seekCache.size())
88  _seekCache.push_back(filePos);
89  else
90  {
91 /*
92  std::cout
93  << "Mp3 frame "
94  << mFramePosition << " "
95  << _mp3Frame << " R:"
96  << filePos << " E:"
97  << _seekCache[_mp3Frame] << " R-E:"
98  << int(filePos)-int(_seekCache[_mp3Frame])
99  << std::endl;
100 */
101  CLAM_WARNING(filePos==_seekCache[_mp3Frame],
102  "MP3 indexing not matching");
103  }
104  mBitstream.SynthesizeCurrent();
105 
106  CLAM_ASSERT( mChannels == MAD_NCHANNELS( &mBitstream.CurrentFrame().header ),
107  "MpegAudioStream: A frame had not the expected channels." );
108  CLAM_ASSERT( mChannels == mBitstream.CurrentSynthesis().pcm.channels,
109  "MpegAudioStream: Synthesis result had not the expected number of channels" );
110 
111  TSize samplesDecodedThisTime = mBitstream.CurrentSynthesis().pcm.length;
112  for(unsigned i = 0; i < mChannels; i++ )
113  {
114  mad_fixed_t* channelData = mBitstream.CurrentSynthesis().pcm.samples[i];
115  mDecodeBuffer[i].insert( mDecodeBuffer[i].end(),
116  channelData,
117  channelData + samplesDecodedThisTime );
118  }
119  mSamplesDecoded += samplesDecodedThisTime;
120  _mp3Frame++;
121  }
122 
123  mFramesLastRead = mDecodeBuffer[0].size();
124  mEOFReached = mBitstream.EOS() && mDecodeBuffer[0].empty();
125 
126  if (mDecodeBuffer[0].empty()) return;
127 
128  for (unsigned i = 0; i < mChannels; i++ )
129  {
130  if ( mDecodeBuffer[i].size() < nFrames )
131  {
132  mDecodeBuffer[i].insert(
133  mDecodeBuffer[i].end(),
134  nFrames - mDecodeBuffer[i].size(),
135  mad_fixed_t(0) );
136  }
137  }
138  ConsumeDecodedSamples();
139  }
140 
141  void MpegAudioStream::ConsumeDecodedSamples()
142  {
143  unsigned nFrames = mInterleavedData.size()/mChannels;
144  for (unsigned i = 0; i < mChannels; i++ )
145  {
146  unsigned currOffset = 0;
147  for ( std::deque<mad_fixed_t>::iterator j = mDecodeBuffer[i].begin();
148  currOffset < mInterleavedData.size();
149  j++, currOffset+=mChannels )
150  {
151  double sampleValue = mad_f_todouble(*j);
152 
153  // :TODO: Finding a nicer way to clamp things
154  // to the -1,1 could be necessary
155  // clipping
156  if ( sampleValue > 1.0 )
157  sampleValue = 1.0;
158  else if ( sampleValue < -1.0 )
159  sampleValue = -1.0;
160 
161  mInterleavedData[ currOffset + i ] = TData(sampleValue);
162  }
163  mDecodeBuffer[i].erase( mDecodeBuffer[i].begin(),
164  mDecodeBuffer[i].begin() + nFrames );
165  }
166  mFramePosition += nFrames;
167  }
168 
170  {
171  CLAM_ASSERT( false, "CLAM does not encode Mpeg Audio!!!");
172  }
173 
174  void MpegAudioStream::SeekTo(unsigned long framePosition)
175  {
176  // Warning: two 'frames' here, mp3's and AudioStream's (items*channels)
177  if (framePosition==mFramePosition) return;
178  unsigned mp3FrameSize = 32*MAD_NSBSAMPLES(&mBitstream.CurrentFrame().header);
179  unsigned targetMp3Frame = framePosition/mp3FrameSize;
180  std::cout << "targetMp3Frame: " << targetMp3Frame << std::endl;
181  // TODO: Jumps beyond the last played frame are too expensive to be frame accurate
182  unsigned maxForwarJump = _seekCache.size()+100;
183  if (targetMp3Frame>maxForwarJump) targetMp3Frame=maxForwarJump;
184  if (targetMp3Frame>=_seekCache.size())
185  {
186  // Construct the index beyond current limits, til the target frame
187  fseek(mpHandle, _seekCache[_seekCache.size()-2], SEEK_SET);
188  mBitstream.Init();
189  for (unsigned mp3Frame=_seekCache.size();
190  mp3Frame<=targetMp3Frame; mp3Frame++)
191  {
192  if (not mBitstream.NextFrame()) return;
193  unsigned long filePos = mBitstream.CurrentFrameFileOffset();
194 /*
195  std::cout << "fwd "
196  << _seekCache.back() << " "
197  << filePos << " "
198  << int(filePos)-_seekCache.back() << std::endl;
199 */
200  if (filePos<=_seekCache.back()) continue; // filtering already done
201  _seekCache.push_back(filePos);
202  }
203  }
204  unsigned skip = targetMp3Frame>4?3:0;
205  unsigned jumpingFrame = targetMp3Frame-skip;
206  std::cout << "jumpingFrame:" << jumpingFrame << std::endl;
207  fseek(mpHandle, _seekCache[jumpingFrame], SEEK_SET);
208  mBitstream.Init();
209  _mp3Frame = targetMp3Frame;
210  // TODO: Because the already buffered bins this is not true
211  mFramePosition = mp3FrameSize * _mp3Frame;
212  if (targetMp3Frame == 0) return;
213  unsigned long previousFrameFilePos = _seekCache[targetMp3Frame-1];
214  while (true)
215  {
216  mBitstream.NextFrame();
217  unsigned long filePos = mBitstream.CurrentFrameFileOffset();
218 /*
219  std::cout << "seek "
220  << filePos << " "
221  << previousFrameFilePos << std::endl;
222 */
223  if (filePos >= previousFrameFilePos) break;
224  }
225  mBitstream.SynthesizeCurrent();
226 
227  unsigned long filePos = mBitstream.CurrentFrameFileOffset();
228 /*
229  std::cout << "Jump n target: "
230  << _mp3Frame << " "
231  << targetMp3Frame << " "
232  << filePos << " "
233  << _seekCache[targetMp3Frame-1]
234  << std::endl;
235 */
236  // TODO: Sample accurate seek (now it is frame accurate)
237 /*
238  CLAM_ASSERT(_seekCache[targetMp3Frame-1]==filePos,
239  "File postions don't match");
240  CLAM_ASSERT(_mp3Frame==targetMp3Frame, "Seek mp3 frames don't match");
241  // We should synthesize the previous frame to the one we want.
242 */ }
243 }
244 
245 }
246