Main Page | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

mp3frame.cpp

Go to the documentation of this file.
00001 
00006 //#define DEBUG
00007 
00008 #include <cassert>
00009 #include "mp3frame.h"
00010 #include "mp3stream.h"
00011 #include "except.h"
00012 
00013 #ifdef DEBUG
00014 #include <iostream>
00015 #endif
00016 
00017 MP3Frame::MP3Frame(MP3Stream *stream, MP3Frame *prev)
00018 {
00019   m_stream = stream;
00020   m_corruption = false;
00021   m_firstframe = (prev == 0); //First frame has no previous
00022   SetZeroes(); //Initialize everything else to zero
00023   
00024   while(!Init(prev)) //Until we don't find valid frame
00025   {                  // (or EOF via exception)
00026     Cleanup();
00027     SetZeroes();
00028   }
00029     
00030   #ifdef DEBUG
00031   std::cout << "newframe @" << m_totalbytes << ", size " << m_spacesize << ", data " << m_datasize << " begin -" << m_databegin << std::endl;
00032   #endif
00033 }  
00034 
00035 MP3Frame::~MP3Frame()
00036 {
00037   Cleanup();
00038 }  
00039 
00040 void MP3Frame::Cleanup()
00041 {
00042   delete m_fh;
00043   delete m_dh;
00044   delete m_ch[0][0];
00045   delete m_ch[0][1];
00046   delete m_ch[1][0];
00047   delete m_ch[1][1];
00048   delete m_data;
00049 }  
00050 
00051 void MP3Frame::SetZeroes()
00052 {
00053   //Zeroes everywhere
00054   m_fh = 0;
00055   m_dh = 0;
00056   m_ch[0][0] = m_ch[0][1] = m_ch[1][0] = m_ch[1][1] = 0;
00057   m_data = 0;
00058   m_next = 0;
00059   m_spacesize = m_datasize = m_databegin = 0;
00060   m_framesize = m_newdatabegin = m_stuffing = 0;
00061   m_sizechange = 0;
00062   m_locked = false;
00063 }
00064 
00065 bool MP3Frame::Init(MP3Frame *prev)
00066 {
00067   int headersize = mp3::syncbits; //Synchronization bits are not in data structure
00068   Bitstream &bs = m_stream->GetBitstream(); //Read operations work with bitstream
00069   
00070   try {
00071     int garbage = bs.Find(mp3::sync, mp3::syncbits); //Synchronize
00072     
00073     if(garbage) //If synchronization was needed
00074     {  
00075       #ifdef DEBUG
00076       std::cout << "corruption: garbage (" << garbage << ')' << std::endl;
00077       #endif
00078       
00079       m_corruption = !m_firstframe; //HACK: to skip ID3 tag at the beginning
00080     }
00081     
00082     m_fh = new FrameHeader(bs);        
00083     
00084     #ifdef DEBUG
00085     std::cout << "frameheader: V" << m_fh->Version() << " L" << m_fh->Layer() << ' ' << m_fh->Bitrate() << "kbps" << std::endl;
00086     #endif
00087 
00088     if(!m_fh->isValid()) //Check validity (format, layer, etc.)
00089     {  
00090       #ifdef DEBUG
00091       std::cout << "corruption: invalid frame header" << std::endl;
00092       #endif
00093       
00094       m_corruption = true;
00095       return false;
00096     }
00097     
00098     //TODO: Really check CRC (now i just store it)
00099     if(m_fh->isCRC()) 
00100     {  
00101       #ifdef DEBUG
00102       std::cout << "CRC code found" << std::endl;
00103       #endif
00104       
00105       bs.Read(&m_CRC, mp3::CRCbits);
00106       headersize += mp3::CRCbits; //Don't forget to count bits!
00107     }
00108     
00109     //Data headers differ by field sizes and positions
00110     if(m_fh->Version() == 1)
00111       if(m_fh->NumChannels() == 1)
00112         m_dh = new DataHeaderMPEG1Mono(bs);
00113       else
00114         m_dh = new DataHeaderMPEG1Stereo(bs);
00115     else
00116       if(m_fh->NumChannels() == 1)
00117         m_dh = new DataHeaderMPEG2Mono(bs);
00118       else
00119         m_dh = new DataHeaderMPEG2Stereo(bs);
00120     
00121     headersize += m_fh->Size() + m_dh->Size();
00122     m_framesize = m_fh->FrameSize();
00123     m_databegin = m_dh->DataBegin();
00124          
00125     //Read Channel Headers, count data
00126     for(int i = 0; i < m_fh->NumGranules(); i++)
00127       for(int j = 0; j < m_fh->NumChannels(); j++)
00128       {  
00129         if(m_fh->Version() == 1)
00130           m_ch[i][j] = new ChannelHeaderMPEG1(bs);
00131         else
00132           m_ch[i][j] = new ChannelHeaderMPEG2(bs);
00133         
00134         headersize += m_ch[i][j]->Size();
00135         m_datasize += m_ch[i][j]->DataSize();
00136       }
00137     
00138     m_datasize = (m_datasize + 7) >> 3; //The sizes are in bits
00139     m_spacesize = m_fh->FrameSize() - (headersize >> 3); //Header size too
00140     //Calculate the position in stream (after previous frame, if there's one)
00141     m_totalbytes = prev ? (prev->m_totalbytes + prev->m_spacesize) : 0;
00142     
00143     //Check if the frames don't overlap and if the frame fits in its space
00144     if( (prev ? (prev->m_totalbytes - prev->m_databegin + prev->m_datasize) : 0) > m_totalbytes - m_databegin ||
00145         m_datasize - m_databegin > m_spacesize )
00146     {
00147       #ifdef DEBUG
00148       std::cout << "corruption: invalid databegin -" << m_databegin << ", data " << m_datasize << std::endl;
00149       #endif
00150       
00151       m_datasize = 0; //Discard frame's data
00152       m_corruption = (m_datasize != 0); //If no data were discarded, everything is fine
00153     }
00154     
00155     //Read data, if we'll need them
00156     if( m_stream->WriteMode() == nowrite )
00157       bs.Seek(8 * m_fh->FrameSize() - headersize);
00158     else
00159       m_data = new AudioData(m_spacesize, m_datasize - m_databegin, bs);
00160     
00161     return true;
00162   }    
00163   catch(eEOF e) //End of file
00164   {
00165     if(e.type() == eEOF::final) //End of last file: stop processing
00166     {  
00167       Cleanup();
00168       throw e;
00169     }
00170     
00171     #ifdef DEBUG
00172     std::cout << "##########################################\n";
00173     #endif
00174     
00175     m_corruption = false;
00176     m_firstframe = true;
00177     return false;
00178   }
00179 }
00180     
00181 MP3Frame *MP3Frame::Destroy()
00182 {
00183   #ifdef DEBUG
00184   std::cout << "destroy @" << m_totalbytes << std::endl;
00185   #endif
00186   
00187   MP3Frame *frame = m_next; //### There must be a local variable!
00188   delete this;
00189   return frame;
00190 }    
00191 
00192 MP3Frame *MP3Frame::Next()
00193 {
00194   if(!m_next) //Read a new frame if there isn't one
00195     m_next = new MP3Frame(m_stream, this);
00196   
00197   return m_next;
00198 }
00199 
00200 void MP3Frame::WriteHeaders(Bitstream &bs)
00201 {
00202   //Bitstream::Write() writs high-order bits first
00203   unsigned int sync = mp3::sync << (32 - mp3::syncbits);
00204   bs.Write(&sync, mp3::syncbits); //Write synchronization bits
00205   
00206   if(m_fh) m_fh->Write(bs); //Frame Header
00207   if(m_fh->isCRC()) bs.Write(&m_CRC, mp3::CRCbits); //CRC
00208   if(m_dh) m_dh->Write(bs); //Data Header
00209   
00210   //Channel Headers
00211   for(int i = 0; i < 2; i++)
00212     for(int j = 0; j < m_fh->NumChannels(); j++)
00213       if(m_ch[i][j]) m_ch[i][j]->Write(bs);
00214 }
00215 
00216 void MP3Frame::Write(Bitstream &bs)
00217 {
00218   #ifdef DEBUG
00219   std::cout << "-----------------" << std::endl;
00220   std::cout << "writeframe @" << m_totalbytes << ", size " << m_spacesize << ", data " << m_datasize << " begin -" << m_databegin << std::endl;
00221   #endif
00222   
00223   assert(m_stream->WriteMode() != nowrite);
00224 
00225   //Fastwrite: simply write headers and data
00226   if(m_stream->WriteMode() == fastwrite)
00227   {
00228     WriteHeaders(bs);
00229     if(m_data) m_data->Write(bs);
00230   }
00231   else
00232   {  
00233     //If the data won't fit in frame
00234     if(m_datasize - m_newdatabegin > m_spacesize)
00235       Enlarge(m_datasize - m_newdatabegin - m_spacesize);
00236       
00237     m_dh->SetDataBegin(m_newdatabegin); //Save newdatabegin in Data Header
00238     WriteHeaders(bs); //Must be after SetDataBegin!
00239       
00240     m_locked = true; //Lock the frame so it won't be deleted
00241     //Write data from stream, use new (possibly enlarged) size of frame,
00242     // also position in frame must be corrected (so the end would match next frame)
00243     m_stream->WriteData(bs, m_spacesize + m_sizechange, m_totalbytes - m_sizechange);
00244     m_locked = false;
00245   }
00246 }
00247     
00248 void MP3Frame::SetDataBegin(int startpos)
00249 {
00250   int maxdatabegin = m_dh->MaxDataBegin();
00251   
00252   //Offset between position of frame space and its data
00253   m_newdatabegin = m_totalbytes - startpos; 
00254   
00255   //If there's too little data, add padding bytes
00256   if(m_newdatabegin > maxdatabegin)
00257   {  
00258     m_stuffing = m_newdatabegin - maxdatabegin;
00259     m_newdatabegin = maxdatabegin;
00260   }
00261 }
00262 
00263 void MP3Frame::Enlarge(int num)  
00264 {
00265   //Try to resize at least by num bytes, -1 is invalid bitrate
00266   while(m_fh->Bitrate() != -1 && m_sizechange < num)
00267   {  
00268     m_fh->Enlarge();
00269     m_sizechange = m_fh->FrameSize() - m_framesize;
00270   }
00271   
00272   if(m_fh->Bitrate() == -1) //This probably would never happen
00273     throw eCannotProceed("Cannot resize frame, it already has maximum bitrate");
00274   
00275   #ifdef DEBUG
00276   std::cout << "resized @" << m_totalbytes << " to " << m_sizechange + m_spacesize << std::endl;
00277   #endif
00278 }
00279 
00280 void MP3Frame::WriteData(Bitstream &bs, MP3Frame *ff, int cutstart, int cutend)
00281 {
00282   //First byte of frame data in stream
00283   int start = m_totalbytes - m_databegin - m_stuffing + cutstart; 
00284   //End of frame data in stream
00285   int end = m_totalbytes - m_databegin + m_datasize - cutend; 
00286   int stuffing = 0; //How many padding bytes should be written
00287   
00288   #ifdef DEBUG
00289   std::cout << "write @" << m_totalbytes << " of size " << m_datasize << '(' << DataSize() << ") cut " << cutstart << ' ' << cutend << std::endl;
00290   #endif
00291   
00292   assert(cutstart >= 0);
00293   assert(cutend >= 0);
00294   assert(start < end);
00295   
00296   if(cutstart < m_stuffing) //Some padding bytes need to be written
00297   {
00298     if(end - start < m_stuffing - cutstart) 
00299       stuffing = end - start; //All bytes are padding
00300     else
00301       stuffing = m_stuffing - cutstart; //Some of bytes are padding
00302     
00303     bs.WriteNullBytes(stuffing); //Write pading (zero bytes)
00304     start += m_stuffing - cutstart; //Continue with real data
00305     
00306     #ifdef DEBUG
00307     std::cout << "written stuffing " << stuffing << std::endl;
00308     #endif
00309   }
00310   
00311   assert(start >= ff->m_totalbytes);
00312   
00313   while(true) //Welcome to the hell. This is the ugliest part of this program.
00314   {
00315     //If there is something to write and the data are in ff
00316     if(start < end && ff->m_totalbytes + ff->m_spacesize > start)
00317     {
00318       //Position of first byte to write in frame's data (not stream)
00319       int startbyte = (start > ff->m_totalbytes) ? (start - ff->m_totalbytes) : 0;
00320       //End of block to write from frame's data
00321       int endbyte = (end < ff->m_totalbytes + ff->m_spacesize) ? (end - ff->m_totalbytes) : ff->m_spacesize;
00322       
00323       if(ff->m_data) ff->m_data->Write(bs, startbyte, endbyte); //Finally write the real data
00324       
00325       #ifdef DEBUG
00326       std::cout << "written bytes " << startbyte << '-' << endbyte << " from frame @" << ff->m_totalbytes << std::endl;
00327       #endif
00328     }
00329     
00330     //There MUST be '<' (otherwise it can destroy the frame being written)
00331     if(ff->m_next && ff->m_next->m_totalbytes < end)  
00332       ff = ff->m_next; //If some data are in next frame, continue. 
00333     else
00334     {  
00335       m_stream->SeekTo(ff); //Delete "emptied" frames
00336       return;
00337     }
00338   }
00339 }
00340 
00341 void MP3Frame::SetFirst()
00342 {
00343   m_stream->SetFirst(this);
00344 }
00345 
00346 bool MP3Frame::isLocked() const
00347 {
00348   return m_locked;
00349 }
00350 
00351 bool MP3Frame::CorruptionFound() const
00352 {
00353   return m_corruption;
00354 }
00355 
00356 int MP3Frame::Bitrate() const
00357 {
00358   return m_fh->Bitrate();
00359 }
00360 
00361 int MP3Frame::DataSize() const
00362 {  
00363   return m_datasize + m_stuffing; //Return size with padding data 
00364 }                                 // (ie. size in output stream)
00365 
00366 void MP3Frame::ChangeGain(int n)
00367 {
00368   //Change volume in every granule and channel
00369   for(int i = 0; i < 2; i++)
00370     for(int j = 0; j < m_fh->NumChannels(); j++)
00371       if(m_ch[i][j]) m_ch[i][j]->ChangeGain(n);
00372 }
00373 
00374 bool MP3Frame::isSilent() const
00375 {
00376   int size = 0;
00377   
00378   //HACK: Just check if both channels of second granule
00379   //      have no data, works better than it seems
00380   for(int j = 0; j < m_fh->NumChannels(); j++)
00381     size += m_ch[1][j]->DataSize();
00382   
00383   return size == 0;
00384 }
00385     
00386 void MP3Frame::ShowInfo(std::ostream &str) const
00387 {
00388   static const char *versiontext[] = { 0, "MPEG1", "MPEG2", "MPEG2.5" };
00389   static const char *modetext[] = { "Stereo", "Joint Stereo", "Dual Channel", "Single Channel" };
00390     
00391   if( !m_fh->isValid() )
00392     str << "Invalid file!" << std::endl;
00393   else
00394   {  
00395     str << "Format: " << versiontext[m_fh->Version()] << " layer " << m_fh->Layer() << std::endl;
00396     str << "Bitrate: " << m_fh->Bitrate() << " kbps" << std::endl;
00397     str << "Frequency: " << m_fh->Frequency() << " kHz" << std::endl;
00398     str << "Mode: " << modetext[m_fh->Mode()];
00399     if(m_fh->isMSstereo()) str << " MS";
00400     if(m_fh->isIntensStereo()) str << " Intensity";
00401     str << std::endl;
00402   }
00403 }

Generated on Wed Sep 6 00:17:57 2006 for Kraken by  doxygen 1.4.4