MpegCodec.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 #if USE_MAD != 1
00023 #error with_mad was not set to 1 with scons, but you are including files that require this. Please fix your settings.cfg
00024 #endif
00025 
00026 #if USE_ID3 != 1
00027 #error with_id3 was not set to 1 with scons, but you are including files that require this. Please fix your settings.cfg
00028 #endif
00029 
00030 
00031 #include "MpegCodec.hxx"
00032 #include "AudioFileFormats.hxx"
00033 #include "AudioFile.hxx"
00034 #include "AudioFileHeader.hxx"
00035 #include "MpegBitstream.hxx"
00036 #include "MpegAudioStream.hxx"
00037 #include <mad.h>
00038 #include <id3/tag.h>
00039 #include <cstdio>
00040 #include <iostream>
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 
00044 namespace CLAM
00045 {
00046 
00047 namespace AudioCodecs
00048 {
00049 
00050 
00051 /* XING parsing is from the MAD winamp input plugin */
00052 /* Ripped mercilessly from mpg321 */
00053 
00054         struct xing {
00055                 int flags;
00056                 unsigned long frames;
00057                 unsigned long bytes;
00058                 unsigned char toc[100];
00059                 long scale;
00060         };
00061         
00062         enum {
00063                 XING_FRAMES = 0x0001,
00064                 XING_BYTES  = 0x0002,
00065                 XING_TOC    = 0x0004,
00066                 XING_SCALE  = 0x0008
00067         };
00068         
00069 # define XING_MAGIC     (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
00070         
00071         static
00072         int parse_xing(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
00073         {
00074                 if (bitlen < 64) goto fail;
00075                 if (mad_bit_read(&ptr, 32) != XING_MAGIC) goto fail;
00076 
00077                 xing->flags = mad_bit_read(&ptr, 32);
00078                 bitlen -= 64;
00079                 
00080                 if (xing->flags & XING_FRAMES) {
00081                         if (bitlen < 32) goto fail;
00082                         
00083                         xing->frames = mad_bit_read(&ptr, 32);
00084                         bitlen -= 32;
00085                 }
00086                 
00087                 if (xing->flags & XING_BYTES) {
00088                         if (bitlen < 32) goto fail;
00089                         
00090                         xing->bytes = mad_bit_read(&ptr, 32);
00091                         bitlen -= 32;
00092                 }
00093                 
00094                 if (xing->flags & XING_TOC) {
00095                         if (bitlen < 800) goto fail;
00096                         
00097                         // MRJ: We just need the 8 least significant bits
00098                         for (unsigned i = 0; i < 100; ++i)
00099                                 xing->toc[i] = (unsigned char)mad_bit_read(&ptr, 8);
00100                         bitlen -= 800;
00101                 }
00102                 
00103                 if (xing->flags & XING_SCALE) {
00104                         if (bitlen < 32) goto fail;
00105                         xing->scale = mad_bit_read(&ptr, 32);
00106                         bitlen -= 32;
00107                 }
00108                 return 1;
00109                 
00110         fail:
00111                 xing->flags = 0;
00112                 return 0;
00113         }
00114 
00115 
00116 
00117         MpegCodec::MpegCodec()
00118         {
00119 
00120         }
00121 
00122         MpegCodec::~MpegCodec()
00123         {
00124         }
00125 
00126         MpegCodec& MpegCodec::Instantiate()
00127         {
00128                 static MpegCodec theInstance;
00129                 return theInstance;
00130         }
00131 
00132         bool MpegCodec::IsReadable( std::string uri ) const
00133         {
00134                 // If has extension and it is not mp3 reject it
00135                 std::string::size_type startExt = uri.rfind( '.' );
00136                 if ( startExt != std::string::npos )
00137                 {
00138                         std::string ext = uri.substr(startExt+1);
00139                         if ( ext!="mp3" && ext!="mpg" && ext!="mpeg")
00140                                 return false;
00141                 }
00142 
00143                 // If it is not readable reject it
00144                 FILE* handle = fopen( uri.c_str(), "rb" );
00145                 if ( !handle ) // File doesn't exists / not readable
00146                         return false;
00147 
00148                 // Look for an Mpeg frame
00149                 MpegBitstream bitstream;
00150                 bitstream.Init(handle);
00151                 bool   foundSomeMpegFrame = false;
00152                 while( !foundSomeMpegFrame 
00153                        && !bitstream.EOS() && !bitstream.FatalError() )
00154                         foundSomeMpegFrame = bitstream.NextFrame();
00155                 bitstream.Finish();
00156                 fclose( handle );
00157                 return foundSomeMpegFrame;
00158         }
00159 
00160         bool MpegCodec::IsWritable( std::string uri, const AudioFileHeader& header ) const
00161         {
00162                 // CLAM does not encode Mpeg
00163                 return false;
00164         }
00165 
00166         Stream* MpegCodec::GetStreamFor( const AudioFile& file )
00167         {
00168                 return new MpegAudioStream(file);
00169         }
00170 
00171         void MpegCodec::RetrieveHeaderData( std::string uri, AudioFileHeader& hdr )
00172         {
00173 
00174                 FILE* handle = fopen( uri.c_str(), "rb" );
00175 
00176                 if ( !handle ) // File doesn't exists / not readable
00177                         return;
00178 
00179                 struct stat fileStats;
00180 
00181                 if ( stat( uri.c_str(), &fileStats ) != 0 )
00182                 {
00183                         // Error reading stats from file
00184                         fclose(handle);
00185                         return;
00186                 }
00187 
00188                 unsigned long fileLength = fileStats.st_size;
00189                 
00190                 if ( fseek( handle, -128, SEEK_END ) < 0 )
00191                 {
00192                         /* File empty */
00193                         fclose(handle);
00194                         return;
00195                 }
00196 
00197                 char buffer[3];
00198 
00199                 if ( fread( buffer, 1, 3, handle ) != 3 )
00200                 {
00201                         fclose(handle);
00202                         return;
00203                 }
00204 
00205                 if ( !strncmp( buffer, "TAG", 3 ) )
00206                 {
00207                         fileLength -=128;
00208                 }
00209 
00210                 fclose( handle );
00211                 handle = fopen( uri.c_str(), "rb" );
00212 
00213                 hdr.AddSampleRate();
00214                 hdr.AddChannels();
00215                 hdr.AddSamples();
00216                 hdr.AddFormat();
00217                 hdr.AddEncoding();
00218                 hdr.AddEndianess();
00219                 hdr.AddLength();
00220                 hdr.UpdateData();
00221                 
00222                 MpegBitstream bitstream;
00223 
00224                 bitstream.Init(handle);
00225 
00226                 int frameCount = 0;
00227                 struct xing xingHeader;
00228                 xingHeader.frames=0;
00229                 bool   hasXingHeader = false;
00230                 bool   isVBR = false;
00231                 unsigned int    bitrate = 0;
00232                 
00233                 /* There are three ways of calculating the length of an mp3:
00234                    1) Constant bitrate: One frame can provide the information
00235                    needed: # of frames and duration. Just see how long it
00236                    is and do the division.
00237                    2) Variable bitrate: Xing tag. It provides the number of 
00238                    frames. Each frame has the same number of samples, so
00239                    just use that.
00240                    3) All: Count up the frames and duration of each frames
00241                    by decoding each one. We do this if we've no other
00242                    choice, i.e. if it's a VBR file with no Xing tag.
00243                 */
00244                 
00245                 long numFrames = 0;
00246 
00247                 while ( !bitstream.FatalError() && bitstream.NextFrame() )
00248                 {
00249                         if ( frameCount == 0 ) // first frame, for retrieving info about encoding
00250                         {
00251                                 RetrieveMPEGFrameInfo( bitstream.CurrentFrame(),
00252                                                        hdr );
00253                                 if ( parse_xing( &xingHeader, 
00254                                                  bitstream.StreamState().anc_ptr, 
00255                                                  bitstream.StreamState().anc_bitlen ) )
00256                                 {
00257                                         isVBR = true;
00258 
00259                                         if ( xingHeader.flags & XING_FRAMES )
00260                                         {
00261                                                 /* We use the Xing tag only for frames. If it doesn't have that
00262                                                    information, it's useless to us and we have to treat it as a
00263                                                    normal VBR file */
00264                                                 hasXingHeader = true;
00265                         numFrames = xingHeader.frames;
00266                                                 break;                                  
00267                                         }
00268                                 }
00269                                 bitrate = bitstream.CurrentFrame().header.bitrate;
00270                         }
00271                         
00272                         if ( frameCount <= 20 )
00273                         {
00274                                 if ( bitstream.CurrentFrame().header.bitrate != bitrate )
00275                                         isVBR = true;
00276                         }                      
00277                         
00278                         if ( !isVBR && frameCount > 20 )
00279                                 break;
00280                         
00281                         frameCount++;
00282                 }
00283 
00284                 if ( !isVBR )
00285                 {
00286                         double time = ( fileLength * 8.0 ) / bitstream.CurrentFrame().header.bitrate;
00287                         double timeFrac = (double)time - ((long)(time));
00288                         long   nsamples = 32 * MAD_NSBSAMPLES(&bitstream.CurrentFrame().header); // samples per frame
00289                         numFrames = ( long) ( time * bitstream.CurrentFrame().header.samplerate / nsamples );
00290                         mad_timer_t   madFmtTime;
00291                         mad_timer_set( &madFmtTime, (long)time, (long)(timeFrac*100), 100 );
00292                         bitstream.Finish();
00293 
00294             //std::cerr << "Not VBR: " << (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS )/1000.  << std::endl;
00295                         hdr.SetLength( (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS ) );
00296                 }
00297                 else if ( hasXingHeader )
00298                 {
00299                         mad_timer_multiply( &bitstream.CurrentFrame().header.duration, numFrames );
00300                         mad_timer_t madFmtTime = bitstream.CurrentFrame().header.duration;
00301                         bitstream.Finish();
00302 
00303             //std::cerr << "Has XING Header: "<< (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS )/1000.  << std::endl;
00304                         hdr.SetLength( (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS ) );
00305                 }
00306                 else
00307                 { 
00308 
00309                         TTime decodedFramesLength = bitstream.Finish();
00310             //std::cerr << "Brute force time guessing: " <<  decodedFramesLength/1000. << " s" << std::endl;
00311                         hdr.SetLength( decodedFramesLength );
00312                 }
00313                 
00314                 // @TODO@: Find a way to estimate reasonably well the actual
00315                 // number of samples.
00316                 hdr.SetSamples(  TSize((hdr.GetLength()/1000.)*hdr.GetSampleRate()) );
00317                 hdr.SetEndianess( EAudioFileEndianess::eDefault );
00318 
00319                 fclose( handle );
00320         }
00321 
00322         void MpegCodec::RetrieveMPEGFrameInfo( const struct mad_frame& MPEGFrame,
00323                                                AudioFileHeader& header )
00324         {
00325                 switch( MPEGFrame.header.layer )
00326                 {
00327                 case MAD_LAYER_I:
00328                         header.SetFormat( EAudioFileFormat::eMpegLayer1 );
00329                         break;
00330                 case MAD_LAYER_II:
00331                         header.SetFormat( EAudioFileFormat::eMpegLayer2 );
00332                         break;
00333                 case MAD_LAYER_III:
00334                         header.SetFormat( EAudioFileFormat::eMpegLayer3 );
00335                         break;
00336                 default:
00337                         break;         
00338                 }
00339 
00340                 switch( MPEGFrame.header.emphasis )
00341                 {
00342                 case MAD_EMPHASIS_NONE:
00343                         header.SetEncoding( EAudioFileEncoding::eDefault );
00344                         break;
00345                 case MAD_EMPHASIS_50_15_US:
00346                         header.SetEncoding( EAudioFileEncoding::e5015US );
00347                         break;
00348                 case MAD_EMPHASIS_CCITT_J_17:
00349                         header.SetEncoding( EAudioFileEncoding::eCCITTJ17 );
00350                         break;
00351                 default:
00352                         break;
00353                 }
00354                 
00355                 header.SetSampleRate( TData(MPEGFrame.header.samplerate) );
00356                 header.SetChannels( MAD_NCHANNELS(&MPEGFrame.header) );
00357         }
00358 
00359 
00360         static const char * getField(ID3_Tag & tag, const ID3_FrameID & field)
00361         {
00362                 ID3_Frame* fieldFrame = tag.Find( field );
00363                 if (not fieldFrame) return 0;
00364                 ID3_Field* fieldString = fieldFrame->GetField( ID3FN_TEXT );
00365                 if (not fieldString) return 0;
00366                 return fieldString->GetRawText();
00367         }
00368 
00369         void   MpegCodec::RetrieveTextDescriptors( std::string uri, AudioTextDescriptors& txt )
00370         {
00371                 ID3_Tag fileTag;
00372                 fileTag.Link( uri.c_str() );
00373 
00374                 const char * artist = getField(fileTag, ID3FID_LEADARTIST);
00375                 const char * title = getField(fileTag, ID3FID_TITLE);
00376                 const char * album = getField(fileTag, ID3FID_ALBUM);
00377                 const char * tracknum = getField(fileTag, ID3FID_TRACKNUM);
00378                 const char * composer = getField(fileTag, ID3FID_COMPOSER);
00379                 const char * performer = getField(fileTag, ID3FID_CONDUCTOR);
00380 
00381                 if (artist) txt.AddArtist();
00382                 if (title) txt.AddTitle();
00383                 if (album) txt.AddAlbum();
00384                 if (tracknum) txt.AddTrackNumber();
00385                 if (composer) txt.AddComposer();
00386                 if (performer) txt.AddPerformer();
00387 
00388                 txt.UpdateData();
00389 
00390                 if (artist) txt.SetArtist(artist);
00391                 if (title) txt.SetTitle(title);
00392                 if (album) txt.SetAlbum(album);
00393                 if (tracknum) txt.SetTrackNumber(tracknum);
00394                 if (composer) txt.SetComposer(composer);
00395                 if (performer) txt.SetPerformer(performer);
00396         }
00397 }
00398 
00399 }
00400 
Generated by  doxygen 1.6.3