input_mp3.cpp

Go to the documentation of this file.
00001 /*
00002   MP3 input for Audiere by Matt Campbell <mattcampbell@pobox.com>, based on
00003   libavcodec from ffmpeg (http://ffmpeg.sourceforge.net/).  I hope this will
00004   turn out better than mpegsound did.
00005 */
00006 
00007 #include <string.h>
00008 #include "input_mp3.h"
00009 #include "utility.h"
00010 #include "debug.h"
00011 
00012 
00013 namespace audiere {
00014 
00015 
00016   MP3InputStream::MP3InputStream() {
00017     m_eof = false;
00018 
00019     m_channel_count = 2;
00020     m_sample_rate = 44100;
00021     m_sample_format = SF_S16;
00022 
00023     m_context = 0;
00024 
00025     m_input_position = 0;
00026     m_input_length = 0;
00027     m_decode_buffer = 0;
00028     m_first_frame = true;
00029 
00030     m_seekable = false;
00031     m_length = 0;
00032     m_position = 0;
00033   }
00034 
00035   
00036   MP3InputStream::~MP3InputStream() {
00037     delete[] m_decode_buffer;
00038     if (m_context) {
00039       mpaudec_clear(m_context);
00040       delete m_context;
00041     }
00042   }
00043 
00044 
00045   bool
00046   MP3InputStream::initialize(FilePtr file) {
00047     m_file = file;
00048     m_seekable = m_file->seek(0, File::END);
00049     readID3v1Tags();
00050     readID3v2Tags();
00051     m_file->seek(0, File::BEGIN);
00052     m_eof = false;
00053 
00054     m_context = new MPAuDecContext;
00055     if (!m_context)
00056       return false;
00057     if (mpaudec_init(m_context) < 0) {
00058       delete m_context;
00059       m_context = 0;
00060       return false;
00061     }
00062 
00063     m_input_position = 0;
00064     m_input_length = 0;
00065     m_decode_buffer = new u8[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
00066     if (!m_decode_buffer)
00067         return false;
00068     m_first_frame = true;
00069 
00070     if (m_seekable) {
00071       // Scan the file to determine the length.
00072       m_context->parse_only = 1;
00073       while (!m_eof) {
00074         if (!decodeFrame())
00075           return false;
00076         if (!m_eof)
00077           m_frame_sizes.push_back(m_context->frame_size);
00078           int frame_offset = m_file->tell() -
00079                              (m_input_length - m_input_position) -
00080                              m_context->coded_frame_size;
00081           m_frame_offsets.push_back(frame_offset);
00082           m_length += m_context->frame_size;
00083       }
00084       reset();
00085     }
00086 
00087     // this should fill in the audio format if it isn't set already
00088     return decodeFrame();
00089   }
00090 
00091   bool
00092   MP3InputStream::isSeekable() {
00093     return m_seekable;
00094   }
00095 
00096   int
00097   MP3InputStream::getPosition() {
00098      return m_position;
00099   }
00100 
00101   void
00102   MP3InputStream::setPosition(int position) {
00103     if (!m_seekable || position > m_length)
00104       return;
00105     int scan_position = 0;
00106     int target_frame = 0;
00107     int frame_count = m_frame_sizes.size();
00108     while (target_frame < frame_count) {
00109       int frame_size = m_frame_sizes[target_frame];
00110       if (position <= scan_position + frame_size)
00111         break;
00112       else {
00113         scan_position += frame_size;
00114         target_frame++;
00115       }
00116     }
00117     // foobar2000's MP3 input plugin decodes and throws away the 10 frames
00118     // before the target frame whenever possible, presumably to ensure correct
00119     // output when jumping into the middle of a stream.  So we'll do that here.
00120     const int MAX_FRAME_DEPENDENCY = 10;
00121     target_frame = std::max(0, target_frame - MAX_FRAME_DEPENDENCY);
00122     reset();
00123     m_file->seek(m_frame_offsets[target_frame], File::BEGIN);
00124     int i;
00125     for (i = 0; i < target_frame; i++) {
00126       m_position += m_frame_sizes[i];
00127     }
00128     if (!decodeFrame() || m_eof) {
00129       reset();
00130       return;
00131     }
00132     int frames_to_consume = position - m_position; // PCM frames now
00133     if (frames_to_consume > 0) {
00134       u8 *buf = new u8[frames_to_consume * GetFrameSize(this)];
00135       doRead(frames_to_consume, buf);
00136       delete[] buf;
00137     }
00138   }
00139 
00140   int
00141   MP3InputStream::getLength() {
00142     return m_length;
00143   }
00144 
00145   void
00146   MP3InputStream::getFormat(
00147     int& channel_count,
00148     int& sample_rate,
00149     SampleFormat& sample_format)
00150   {
00151     channel_count = m_channel_count;
00152     sample_rate = m_sample_rate;
00153     sample_format = m_sample_format;
00154   }
00155 
00156   
00157   int
00158   MP3InputStream::doRead(int frame_count, void* samples) {
00159     ADR_GUARD("MP3InputStream::doRead");
00160 
00161     const int frame_size = GetFrameSize(this);
00162 
00163     int frames_read = 0;
00164     u8* out = (u8*)samples;
00165 
00166     while (frames_read < frame_count) {
00167 
00168       // no more samples?  ask the MP3 for more
00169       if (m_buffer.getSize() < frame_size) {
00170         if (!decodeFrame() || m_eof) {
00171           // done decoding?
00172           return frames_read;
00173         }
00174 
00175         // if the buffer is still empty, we are done
00176         if (m_buffer.getSize() < frame_size) {
00177           return frames_read;
00178         }
00179       }
00180 
00181       const int frames_left = frame_count - frames_read;
00182       const int frames_to_read = std::min(
00183         frames_left,
00184         m_buffer.getSize() / frame_size);
00185 
00186       m_buffer.read(out, frames_to_read * frame_size);
00187       out += frames_to_read * frame_size;
00188       frames_read += frames_to_read;
00189       m_position += frames_to_read;
00190     }
00191 
00192     return frames_read;
00193   }
00194 
00195 
00196   void
00197   MP3InputStream::reset() {
00198     ADR_GUARD("MP3InputStream::reset");
00199 
00200     m_file->seek(0, File::BEGIN);
00201     m_eof = false;
00202 
00203     m_buffer.clear();
00204 
00205     mpaudec_clear(m_context);
00206     mpaudec_init(m_context);
00207 
00208     m_input_position = 0;
00209     m_input_length = 0;
00210     m_position = 0;
00211   }
00212 
00213 
00214   bool
00215   MP3InputStream::decodeFrame() {
00216     int output_size = 0;
00217     while (output_size == 0) {
00218       if (m_input_position == m_input_length) {
00219         m_input_position = 0;
00220         m_input_length = m_file->read(m_input_buffer, INPUT_BUFFER_SIZE);
00221         if (m_input_length == 0) {
00222           m_eof = true;
00223           return true;
00224         }
00225       }
00226       int rv = mpaudec_decode_frame(
00227           m_context, (s16*)m_decode_buffer,
00228           &output_size,
00229           (unsigned char*)m_input_buffer + m_input_position,
00230           m_input_length - m_input_position);
00231       if (rv < 0)
00232         return false;
00233       m_input_position += rv;
00234     }
00235     if (m_first_frame) {
00236       m_channel_count = m_context->channels;
00237       m_sample_rate = m_context->sample_rate;
00238       m_sample_format = SF_S16;
00239       m_first_frame = false;
00240     } else if (m_context->channels != m_channel_count ||
00241                m_context->sample_rate != m_sample_rate) {
00242       // Can't handle format changes mid-stream.
00243       return false;
00244     }
00245     if (!m_context->parse_only) {
00246       if (output_size < 0) {
00247         // Couldn't decode this frame.  Too bad, already lost it.
00248         // This should only happen when seeking.
00249         output_size = m_context->frame_size;
00250         memset(m_decode_buffer, 0, output_size * GetFrameSize(this));
00251       }
00252       m_buffer.write(m_decode_buffer, output_size);
00253     }
00254     return true;
00255   }
00256 
00257 
00258   const char* getGenre(u8 code) {
00259     const char* genres[] = {
00260       // From Appendix A.3 at http://www.id3.org/id3v2-00.txt and
00261 
00262       "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
00263       "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other",
00264       "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
00265       "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack",
00266       "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
00267       "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
00268       "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
00269       "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
00270       "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
00271       "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance",
00272       "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
00273       "Christian Rap", "Pop/Funk", "Jungle", "Native American",
00274       "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
00275       "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka",
00276       "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock",
00277       "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
00278       "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
00279       "Progressive Rock", "Psychedelic Rock", "Symphonic Rock",
00280       "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
00281       "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata",
00282       "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire",
00283       "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad",
00284       "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock",
00285       "Drum Solo", "Acapella", "Euro-House", "Dance Hall",
00286       
00287       // http://lame.sourceforge.net/doc/html/id3.html
00288 
00289       "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
00290       "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta",
00291       "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
00292       "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime",
00293       "JPop", "SynthPop",
00294     };
00295     const int genre_count = sizeof(genres) / sizeof(*genres);
00296 
00297     return (code < genre_count ? genres[code] : "");
00298   }
00299 
00300 
00301   // Return a null-terminated std::string from the beginning of 'buffer'
00302   // up to 'maxlen' chars in length.
00303   std::string getString(u8* buffer, int maxlen) {
00304     char* begin = reinterpret_cast<char*>(buffer);
00305     int end = 0;
00306     for (; end < maxlen && begin[end]; ++end) {
00307     }
00308     return std::string(begin, begin + end);
00309   }
00310 
00311 
00312   void
00313   MP3InputStream::readID3v1Tags() {
00314     // Actually, this function reads both ID3v1 and ID3v1.1.
00315 
00316     if (!m_file->seek(-128, File::END)) {
00317       return;
00318     }
00319 
00320     u8 buffer[128];
00321     if (m_file->read(buffer, 128) != 128) {
00322       return;
00323     }
00324 
00325     // Verify that it's really an ID3 tag.
00326     if (memcmp(buffer + 0, "TAG", 3) != 0) {
00327       return;
00328     }
00329 
00330     std::string title   = getString(buffer + 3,  30);
00331     std::string artist  = getString(buffer + 33, 30);
00332     std::string album   = getString(buffer + 63, 30);
00333     std::string year    = getString(buffer + 93, 4);
00334     std::string comment = getString(buffer + 97, 30);
00335     std::string genre   = getGenre(buffer[127]);
00336 
00337     addTag("title",   title,   "ID3v1");
00338     addTag("artist",  artist,  "ID3v1");
00339     addTag("album",   album,   "ID3v1");
00340     addTag("year",    year,    "ID3v1");
00341     addTag("comment", comment, "ID3v1");
00342     addTag("genre",   genre,   "ID3v1");
00343 
00344     // This is the ID3v1.1 part.
00345     if (buffer[97 + 28] == 0 && buffer[97 + 29] != 0) {
00346       char track[20];
00347       sprintf(track, "%d", int(buffer[97 + 29]));
00348       addTag("track", track, "ID3v1.1");
00349     }
00350   }
00351 
00352   
00353   void
00354   MP3InputStream::readID3v2Tags() {
00355     // ID3v2 is super complicated.
00356   }
00357 
00358 }

Generated on Mon Feb 13 23:07:46 2006 for audiere by  doxygen 1.4.6