/* MAD is available from: http://www.mars.org/home/rob/proj/mpeg/ */ #include "global.h" #include "RageSoundReader_MP3.h" #include "RageLog.h" #include "RageUtil.h" #include #include #if defined(_WINDOWS) || defined(_XBOX) #include "mad-0.15.1b/mad.h" #ifdef _MSC_VER #ifdef _XBOX #ifdef _DEBUG #pragma comment(lib, "mad-0.15.1b/xboxmad/debug/xboxmad.lib") #else #pragma comment(lib, "mad-0.15.1b/xboxmad/Release/xboxmad.lib") #endif // _DEBUG #else #pragma comment(lib, "mad-0.15.1b/msvc++/Release/libmad.lib") #endif // _XBOX #endif //_MSC_VER #else #include #endif // _WINDOWS /* ID3 code from libid3: */ enum tagtype { TAGTYPE_NONE = 0, TAGTYPE_ID3V1, TAGTYPE_ID3V2, TAGTYPE_ID3V2_FOOTER }; typedef unsigned long id3_length_t; static const int ID3_TAG_FLAG_FOOTERPRESENT = 0x10; static tagtype tagtype( const unsigned char *data, id3_length_t length ) { if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G') return TAGTYPE_ID3V1; if (length >= 10 && ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') || (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) && data[3] < 0xff && data[4] < 0xff && data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80) return data[0] == 'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER; return TAGTYPE_NONE; } static unsigned long id3_parse_uint( const unsigned char **ptr, unsigned int bytes ) { unsigned long value = 0; ASSERT(bytes >= 1 && bytes <= 4); switch (bytes) { case 4: value = (value << 8) | *(*ptr)++; case 3: value = (value << 8) | *(*ptr)++; case 2: value = (value << 8) | *(*ptr)++; case 1: value = (value << 8) | *(*ptr)++; } return value; } static unsigned long id3_parse_syncsafe( const unsigned char **ptr, unsigned int bytes ) { unsigned long value = 0; ASSERT(bytes == 4 || bytes == 5); switch (bytes) { case 5: value = (value << 4) | (*(*ptr)++ & 0x0f); case 4: value = (value << 7) | (*(*ptr)++ & 0x7f); value = (value << 7) | (*(*ptr)++ & 0x7f); value = (value << 7) | (*(*ptr)++ & 0x7f); value = (value << 7) | (*(*ptr)++ & 0x7f); } return value; } static void parse_header(const unsigned char **ptr, unsigned int *version, int *flags, id3_length_t *size) { *ptr += 3; *version = id3_parse_uint(ptr, 2); *flags = id3_parse_uint(ptr, 1); *size = id3_parse_syncsafe(ptr, 4); } /* * NAME: tag->query() * DESCRIPTION: if a tag begins at the given location, return its size */ signed long id3_tag_query( const unsigned char *data, id3_length_t length ) { unsigned int version; int flags; id3_length_t size; switch (tagtype(data, length)) { case TAGTYPE_ID3V1: return 128; case TAGTYPE_ID3V2: parse_header(&data, &version, &flags, &size); if (flags & ID3_TAG_FLAG_FOOTERPRESENT) size += 10; return 10 + size; case TAGTYPE_ID3V2_FOOTER: parse_header(&data, &version, &flags, &size); return -(int)size - 10; case TAGTYPE_NONE: break; } return 0; } /* XING code ripped out of madplay (header) * * mad - MPEG audio decoder * Copyright (C) 2000-2001 Robert Leslie */ struct xing { long flags; /* valid fields (see below) */ unsigned long frames; /* total number of frames */ unsigned long bytes; /* total number of bytes */ unsigned char toc[100]; /* 100-point seek table */ long scale; /* ?? */ enum { XING, INFO } type; }; enum { XING_FRAMES = 0x00000001L, XING_BYTES = 0x00000002L, XING_TOC = 0x00000004L, XING_SCALE = 0x00000008L }; void xing_init( struct xing *xing ); int xing_parse( struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen ); /* end XING header */ /* a -= b */ static void mad_timer_sub(mad_timer_t *a, mad_timer_t b) { /* a = a - b -> a = a + -b */ mad_timer_negate(&b); mad_timer_add(a, b); } /* internal->decoder_private field */ struct madlib_t { uint8_t inbuf[16384], outbuf[8192]; int outpos; unsigned outleft; struct mad_stream Stream; struct mad_frame Frame; struct mad_synth Synth; /* Timestamp of the next frame. */ mad_timer_t Timer; /* Whether Timer is trusted; this is false after doing a quick seek. */ int timer_accurate; /* Frame index of each percentage of the file. This is like the * Xing TOC, except it's actual byte indices, not percentages, so * it's accurate enough for "precise" seeking. -1 indicates we don't * yet know; we always fill this in from beginning to end, so if n * is not -1, 0..n-1 are also not -1. * * If we're VBR and have no Xing tag, we don't know the exact length * ahead of time, so we might end up going past the length we thought * the file was. We don't want to change length after we've set it * (that would change all of the toc entries), so allow the TOC to * go up to 200%. (Beyond that, we'll always hard seek; perhaps this * should be larger?) */ int toc[200]; /* Position in the file of inbuf: */ int inbuf_filepos; /* File size. */ int filesize; /* Number of bytes of header data at the beginning; used for seeking. */ int header_bytes; /* Whether we've decoded the first frame of real audio yet. If this * is false, we're somewhere before the first frame; we might be in * the middle of headers. */ int first_frame; /* This data is filled in when the first frame is decoded. */ int has_xing; /* whether xingtag is valid */ struct xing xingtag; /* If has_xing is true, this is filled in based on the xing header. * If it's false, this is filled in based on the size of the file * and the first frame. */ int length; mad_timer_t framelength; /* If we have a Xing tag, this is the average bitrate; otherwise it's * the bitrate of the first frame. */ int bitrate; }; static signed int scale(mad_fixed_t sample) { /* round */ sample += (1L << (MAD_F_FRACBITS - 16)); /* clip */ if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE; /* quantize */ return sample >> (MAD_F_FRACBITS + 1 - 16); } static int get_this_frame_byte( const madlib_t *mad ) { int ret = mad->inbuf_filepos; /* If we have a frame, adjust. */ if( mad->Stream.this_frame != NULL ) ret += mad->Stream.this_frame-mad->inbuf; return ret; } /* Called on the first frame decoded. Returns true if this frame * should be ignored. */ bool RageSoundReader_MP3::handle_first_frame() { bool ret = false; /* Check for a XING tag. */ xing_init( &mad->xingtag ); if( xing_parse(&mad->xingtag, mad->Stream.anc_ptr, mad->Stream.anc_bitlen) == 0 ) { /* * "Info" tags are written by some tools. They're just Xing tags, but for * CBR files. * * However, DWI's decoder, BASS, doesn't understand this, and treats it as a * corrupt frame, outputting a frame of silence. Let's ignore the tag, so * it'll be treated as an invalid frame, so we match DWI sync. * * The information in it isn't very useful to us. The TOC is less accurate * then computing it ourself (low resolution). The file length computation * might be wrong, if the tag is incorrect, and we can compute that accurately * ourself for CBR files. */ if( mad->xingtag.type == xing::INFO ) return false; mad->header_bytes = max( mad->header_bytes, get_this_frame_byte(mad) ); mad->has_xing = true; mad_timer_t tm = mad->Frame.header.duration; /* XXX: does this include the Xing header itself? */ mad_timer_multiply( &tm, mad->xingtag.frames ); mad->length = mad_timer_count( tm, MAD_UNITS_MILLISECONDS ); /* XXX: an id3v2 footer tag would throw this off a little. This also assumes * the Xing tag is the last header; it always is, I think. */ int bytes = mad->filesize - mad->header_bytes; mad->bitrate = (int)(bytes * 8 / (mad->length/1000.f)); if( mad->xingtag.type == xing::XING ) ret = 1; } /* If there's no Xing tag, mad->length will be filled in by _open. */ return ret; } int RageSoundReader_MP3::fill_buffer() { /* Need more data. */ int inbytes = 0; if( mad->Stream.next_frame != NULL ) { /* Pull out remaining data from the last buffer. */ inbytes = mad->Stream.bufend-mad->Stream.next_frame; memmove( mad->inbuf, mad->Stream.next_frame, inbytes ); mad->inbuf_filepos += mad->Stream.next_frame - mad->inbuf; } const bool bWasAtEOF = file.AtEOF(); int rc = file.Read( mad->inbuf + inbytes, sizeof(mad->inbuf)-inbytes-MAD_BUFFER_GUARD ); if( rc < 0 ) { SetError( file.GetError() ); return -1; } if ( file.AtEOF() && !bWasAtEOF ) { /* We just reached EOF. Append MAD_BUFFER_GUARD bytes of NULs to the * buffer, to ensure that the last frame is flushed. */ memset( mad->inbuf + inbytes + rc, 0, MAD_BUFFER_GUARD ); rc += MAD_BUFFER_GUARD; } if ( rc == 0 ) return 0; inbytes += rc; mad_stream_buffer( &mad->Stream, mad->inbuf, inbytes ); return rc; } void fill_frame_index_cache( madlib_t *mad ) { int ms, percent, pos; /* Only update the frame cache if our timer is consistent. */ if(!mad->timer_accurate) return; /* ms of the frame we just decoded: */ ms = mad_timer_count(mad->Timer, MAD_UNITS_MILLISECONDS); percent = ms * 100 / mad->length; pos = get_this_frame_byte(mad); /* Fill in the TOC percent. Also, fill in all values before it * that are unknown; for example, if toc[4] is "50 -1 -1 -1", and filling * in toc[3] value with "100", toc[1] and toc[2] are also 100. This * can probably only happen with a file with less than 100 frames, so * this isn't too important. */ while(percent >= 0 && percent < 200 && mad->toc[percent] == -1) mad->toc[percent--] = pos; } /* Handle first-stage decoding: extracting the MP3 frame data. */ int RageSoundReader_MP3::do_mad_frame_decode( bool headers_only ) { int bytes_read = 0; while(1) { int ret; /* Always actually decode the first packet, so we cleanly parse Xing tags. */ if( headers_only && !mad->first_frame ) ret=mad_header_decode( &mad->Frame.header,&mad->Stream ); else ret=mad_frame_decode( &mad->Frame,&mad->Stream ); if( ret == -1 && (mad->Stream.error == MAD_ERROR_BUFLEN || mad->Stream.error == MAD_ERROR_BUFPTR) ) { if( bytes_read > 25000 ) { /* We've read this much without actually getting a frame; error. */ SetError( "Can't find data" ); return -1; } int ret = fill_buffer(); if( ret <= 0 ) return ret; bytes_read += ret; continue; } if( ret == -1 && mad->Stream.error == MAD_ERROR_LOSTSYNC ) { /* This might be an ID3V2 tag. */ const int tagsize = id3_tag_query(mad->Stream.this_frame, mad->Stream.bufend - mad->Stream.this_frame); if( tagsize ) { mad_stream_skip(&mad->Stream, tagsize); /* Don't count the tagsize against the max-read-per-call figure. */ bytes_read -= tagsize; continue; } } if( ret == -1 && mad->Stream.error == MAD_ERROR_BADDATAPTR ) { /* * Something's corrupt. One cause of this is cutting an MP3 in the middle * without reencoding; the first two frames will reference data from previous * frames that have been removed. The frame is valid--we can get a header from * it, we just can't synth useful data. * * BASS pretends the bad frames are silent. Emulate that, for compatibility. */ ret = 0; /* pretend success */ } if( !ret ) { /* OK. */ if( mad->first_frame ) { /* We're at the beginning. Is this a Xing tag? */ if(handle_first_frame()) { /* The first frame contained a header. Continue searching. */ continue; } /* We've decoded the first frame of data. * * We want mad->Timer to represent the timestamp of the first sample of the * currently decoded frame. Don't increment mad->Timer on the first frame, * or it'll be the time of the *next* frame. (All frames have the same * duration.) */ mad->first_frame = false; mad->Timer = mad_timer_zero; mad->header_bytes = get_this_frame_byte(mad); } else { mad_timer_add( &mad->Timer,mad->Frame.header.duration ); } fill_frame_index_cache( mad ); return 1; } if( mad->Stream.error == MAD_ERROR_BADCRC ) { /* XXX untested */ mad_frame_mute(&mad->Frame); mad_synth_mute(&mad->Synth); continue; } if( !MAD_RECOVERABLE(mad->Stream.error) ) { /* We've received an unrecoverable error. */ SetError( mad_stream_errorstr(&mad->Stream) ); return -1; } } } void RageSoundReader_MP3::synth_output() { mad->outleft = 0; if( MAD_NCHANNELS(&mad->Frame.header) != this->Channels ) { /* This frame contains a different number of channels than the first. * I've never actually seen this--it's just to prevent exploding if * it happens--and we could continue on easily enough, so if it happens, * just continue. */ return; } mad_synth_frame(&mad->Synth, &mad->Frame); for(int i=0; i < mad->Synth.pcm.length; i++) { for(int chan = 0; chan < this->Channels; ++chan) { short Sample = (short) scale(mad->Synth.pcm.samples[chan][i]); *((short *) (mad->outbuf + mad->outleft)) = Sample; mad->outleft += 2; } } } /* Seek to a byte in the file. If you're going to put the file position * back when you're done, and not going to read any data, you don't have * to use this. */ int RageSoundReader_MP3::seek_stream_to_byte( int byte ) { if( file.Seek(byte) == -1 ) { SetError( strerror(errno) ); return 0; } mad_frame_mute(&mad->Frame); mad_synth_mute(&mad->Synth); mad_stream_finish(&mad->Stream); mad_stream_init(&mad->Stream); mad->outleft = mad->outpos = 0; mad->inbuf_filepos = byte; /* If the position is <= the position of the first audio sample, then * we're at the beginning. */ if( mad->toc[0] != -1 ) mad->first_frame = ( byte <= mad->toc[0] ); return 1; } #if 0 /* Call this after seeking the stream. We'll back up a bit and reread * frames until we're back where we started, so the next read is aligned * to a frame and synced. This must never leave the position ahead of where * it way, since that can confuse the seek optimizations. */ int RageSoundReader_MP3::resync() { /* Save the timer; decoding will change it, and we need to put it back. */ mad_timer_t orig = mad->Timer; /* Seek backwards up to 4k. */ const int origpos = mad->inbuf_filepos; const int seekpos = max( 0, origpos - 1024*4 ); seek_stream_to_byte( seekpos ); /* Agh. This is annoying. We want to decode enough so that the next frame * read will be the first frame after the current file pointer. If we just * read until the file pointer is >= what it was, we've passed it already. * So, read until it's >= what it was, counting the number of times we had * to read; then back up again and read n-1 times. Gross. */ int reads = 0; do { if( do_mad_frame_decode() <= 0 ) /* XXX eof */ return -1; /* it set the error */ reads++; } while( get_this_frame_byte(mad) < origpos ); seek_stream_to_byte( seekpos ); reads--; while( reads-- > 0 ) { if( do_mad_frame_decode() <= 0 ) /* XXX eof */ return -1; /* it set the error */ } /* Restore the timer. */ mad->Timer = orig; mad->outpos = mad->outleft = 0; return 1; } #endif RageSoundReader_MP3::RageSoundReader_MP3() { mad = new madlib_t; memset(mad, '\0', sizeof (madlib_t)); mad_stream_init( &mad->Stream ); mad_frame_init( &mad->Frame ); mad_synth_init( &mad->Synth ); mad_frame_mute( &mad->Frame ); mad_timer_reset( &mad->Timer ); mad->length = -1; mad->inbuf_filepos = 0; mad->header_bytes = 0; mad->has_xing = false; mad->timer_accurate = 1; mad->bitrate = -1; mad->first_frame = true; for(int i = 0; i < 200; ++i) mad->toc[i] = -1; } RageSoundReader_MP3::~RageSoundReader_MP3() { mad_synth_finish( &mad->Synth ); mad_frame_finish( &mad->Frame ); mad_stream_finish( &mad->Stream ); delete mad; } SoundReader_FileReader::OpenResult RageSoundReader_MP3::Open( CString filename_ ) { filename = filename_; if( !file.Open( filename ) ) { SetError( ssprintf("Couldn't open file: %s", file.GetError().c_str()) ); return OPEN_FATAL_ERROR; } mad->filesize = file.GetFileSize(); ASSERT( mad->filesize != -1 ); /* Make sure we can decode at least one frame. This will also fill in header info. */ mad->outpos = 0; int ret = do_mad_frame_decode(); switch(ret) { case 0: SetError( "Failed to read any data at all" ); return OPEN_UNKNOWN_FILE_FORMAT; case -1: SetError( GetError() + " (not an MP3 stream?)" ); return OPEN_UNKNOWN_FILE_FORMAT; } /* Store the bitrate of the frame we just got. */ if( mad->bitrate == -1 ) /* might have been set by a Xing tag */ mad->bitrate = mad->Frame.header.bitrate; SampleRate = mad->Frame.header.samplerate; mad->framelength = mad->Frame.header.duration; this->Channels = MAD_NCHANNELS( &mad->Frame.header ); /* Since we've already decoded a frame, just synth it instead of rewinding * the stream. */ synth_output(); if(mad->length == -1) { /* If vbr and !xing, this is just an estimate. */ int bps = mad->bitrate / 8; float secs = (float)(mad->filesize - mad->header_bytes) / bps; mad->length = (int)(secs * 1000.f); } return OPEN_OK; } SoundReader *RageSoundReader_MP3::Copy() const { RageSoundReader_MP3 *ret = new RageSoundReader_MP3; ret->filename = filename; bool b = ret->file.Open( filename ); ASSERT( b ); ret->mad->filesize = mad->filesize; ret->mad->bitrate = mad->bitrate; ret->SampleRate = SampleRate; ret->mad->framelength = mad->framelength; ret->Channels = Channels; ret->mad->length = mad->length; // int n = ret->do_mad_frame_decode(); // ASSERT( n > 0 ); // ret->synth_output(); return ret; } /* dst and src are buffers of 16-bit samples. len is the number of bytes * to copy from src. len must be even. */ static void mono_to_stereo( char *dst, const char *src, unsigned len ) { ASSERT( (len % 2) == 0 ); const char *src_end = src + len; while( src != src_end ) { dst[0] = dst[2] = src[0]; dst[1] = dst[3] = src[1]; dst += 4; src += 2; } } int RageSoundReader_MP3::Read( char *buf, unsigned len ) { uint32_t bw = 0; ASSERT( (len % (sizeof(int16_t)*2)) == 0 ); while( bw < len ) { if( mad->outleft > 0 ) { /* Ratio of input bytes to output bytes. */ const int Ratio = Channels == 1? 2:1; /* Input bytes to be copied: */ const unsigned cpysize = min( (len - bw)/Ratio, mad->outleft ); if( Channels == 1 ) mono_to_stereo( (char *) buf + bw, (const char *) mad->outbuf + mad->outpos, cpysize ); else memcpy( buf + bw, mad->outbuf + mad->outpos, cpysize ); bw += cpysize * Ratio; mad->outpos += cpysize; mad->outleft -= cpysize; continue; } /* Decode more from the MP3 stream. */ int ret = do_mad_frame_decode(); if( ret == 0 ) return bw; if( ret == -1 ) return -1; mad->outpos = 0; synth_output(); } return bw; } bool RageSoundReader_MP3::MADLIB_rewind() { this->file.Seek(0); mad_frame_mute(&mad->Frame); mad_synth_mute(&mad->Synth); mad_timer_reset(&mad->Timer); mad->outpos = mad->outleft = 0; mad_stream_finish(&mad->Stream); mad_stream_init(&mad->Stream); mad_stream_buffer(&mad->Stream, NULL, 0); mad->inbuf_filepos = 0; /* Be careful. We need to leave header_bytes alone, so if we try to SetPosition_estimate * immediately after this, we still know the header size. However, we need to set first_frame * to true, since the first frame is handled specially in do_mad_frame_decode; if we don't * set it, then we'll be desynced by a frame after an accurate seek. */ // mad->header_bytes = 0; mad->first_frame = true; mad->Stream.this_frame = NULL; return true; } /* Methods of seeking: * * 1. We can jump based on a TOC. We potentially have two; the Xing TOC and our * own index. The Xing TOC is only accurate to 1/256th of the file size, * so it's unsuitable for precise seeks. Our own TOC is byte-accurate. * (SetPosition_toc) * * 2. We can jump based on the bitrate. This is fast, but not accurate. * (SetPosition_estimate) * * 3. We can seek from any position to any higher position by decoding headers. * (SetPosition_hard) * * Both 1 and 2 will leave the position behind the actual requested position; * combine them with 3 to catch up. Never do 3 alone in "fast" mode, since it's * slow if it ends up seeking from the beginning of the file. Never do 2 in * "precise" mode. */ /* Returns actual position on success, 0 if past EOF, -1 on error. */ int RageSoundReader_MP3::SetPosition_toc( int ms, bool Xing ) { int percent; ASSERT( !Xing || mad->has_xing ); ASSERT( mad->length != -1 ); /* This leaves our timer accurate if we're using our own TOC, and inaccurate * if we're using Xing. */ mad->timer_accurate = !Xing; /* We can speed up the seek using the XING tag. First, figure * out what percentage the requested position falls in. */ percent = ms * 100 / mad->length; if(percent >= 0) { int bytepos = -1; if( Xing ) { if( percent < 100 ) { int jump = mad->xingtag.toc[percent]; bytepos = mad->filesize * jump / 256; } else bytepos = 2000000000; /* force EOF */ } else if( percent < 200 ) { /* Find the last entry <= percent that we actually have an entry for; * this will get us as close as possible. */ while( percent >= 0 && mad->toc[percent] == -1 ) percent--; if( percent == -1 ) return ms; /* don't have any info */ bytepos = mad->toc[percent]; } if( bytepos != -1 ) { /* Seek backwards up to 4k. */ const int seekpos = max( 0, bytepos - 1024*4 ); seek_stream_to_byte( seekpos ); do { int ret = do_mad_frame_decode(); if( ret <= 0 ) return ret; /* it set the error */ } while( get_this_frame_byte(mad) < bytepos ); synth_output(); mad_timer_set( &mad->Timer, 0, percent * mad->length, 100 ); } } return ms; } int RageSoundReader_MP3::SetPosition_hard( int ms ) { mad_timer_t desired; mad_timer_set( &desired, 0, ms, 1000 ); /* This seek doesn't change the accuracy of our timer. */ /* If we're already exactly at the requested position, OK. */ if( mad_timer_compare(mad->Timer, desired) == 0 ) return 0; /* If we're already past the requested position, rewind. */ if(mad_timer_compare(mad->Timer, desired) > 0) MADLIB_rewind(); /* We always come in here with data synthed. Be careful not to synth the * same frame twice. */ bool synthed=true; /* Decode frames until the current frame contains the desired offset. */ while(1) { /* If desired < next_frame_timer, this frame contains the position. Since we've * already decoded the frame, synth it, too. */ mad_timer_t next_frame_timer = mad->Timer; mad_timer_add( &next_frame_timer, mad->framelength ); if( mad_timer_compare(desired, next_frame_timer) < 0 ) { if( !synthed ) synth_output(); /* We just synthed data starting at mad->Timer, containing the desired offset. * Skip (desired - mad->Timer) worth of frames in the output to line up. */ mad_timer_t skip = desired; mad_timer_sub( &skip, mad->Timer ); int samples = mad_timer_count( skip, (mad_units) SampleRate ); /* Skip 'samples' samples; each sample is 2 * channels bytes, since * we're currently always using AUDIO_S16SYS. */ mad->outpos = samples * 2 * this->Channels; mad->outleft -= samples * 2 * this->Channels; return ms; } /* Otherwise, if the desired time will be in the *next* decode, then synth * this one, too. */ mad_timer_t next_next_frame_timer = next_frame_timer; mad_timer_add( &next_next_frame_timer, mad->framelength ); if( mad_timer_compare(desired, next_next_frame_timer) < 0 && !synthed ) { synth_output(); synthed = true; } int ret = do_mad_frame_decode(); if( ret <= 0 ) return ret; /* it set the error */ synthed = false; } } /* Do a seek based on the bitrate. */ int RageSoundReader_MP3::SetPosition_estimate( int ms ) { /* This doesn't leave us accurate. */ mad->timer_accurate = 0; mad_timer_t seekamt; mad_timer_set( &seekamt, 0, ms, 1000 ); { /* We're going to skip ahead two samples below, so seek earlier than * we were asked to. */ mad_timer_t back_len = mad->framelength; mad_timer_multiply(&back_len, -2); mad_timer_add(&seekamt, back_len); if( mad_timer_compare(seekamt, mad_timer_zero) < 0 ) seekamt = mad_timer_zero; } int seekpos = mad_timer_count( seekamt, MAD_UNITS_MILLISECONDS ) * (mad->bitrate / 8 / 1000); seekpos += mad->header_bytes; seek_stream_to_byte( seekpos ); /* We've jumped across the file, so the decoder is currently desynced. * Don't use resync(); it's slow. Just decode a few frames. */ for( int i = 0; i < 2; ++i ) { int ret = do_mad_frame_decode(); if( ret <= 0 ) return ret; } /* Throw out one synth. */ synth_output(); mad->outleft = 0; /* Find out where we really seeked to. */ ms = (get_this_frame_byte(mad) - mad->header_bytes) / (mad->bitrate / 8 / 1000); mad_timer_set(&mad->Timer, 0, ms, 1000); return ms; } int RageSoundReader_MP3::SetPosition_Accurate( int ms ) { /* Seek using our own internal (accurate) TOC. */ int ret = SetPosition_toc( ms, false ); if( ret <= 0 ) return ret; /* it set the error */ /* Align exactly. */ return SetPosition_hard( ms ); } int RageSoundReader_MP3::SetPosition_Fast( int ms ) { /* Rewinding is always fast and accurate, and SetPosition_estimate is bad at 0. */ if( !ms ) { MADLIB_rewind(); return 0; /* ok */ } /* We can do a fast jump in VBR with Xing with more accuracy than without Xing. */ if( mad->has_xing ) return SetPosition_toc( ms, true ); /* Guess. This is only remotely accurate when we're not VBR, but also * do it if we have no Xing tag. */ return SetPosition_estimate( ms ); } int RageSoundReader_MP3::GetLengthInternal( bool fast ) { if( mad->has_xing && mad->length != -1 ) return mad->length; /* should be accurate */ /* Check to see if a frame in the middle of the file is the same * bitrate as the first frame. If it is, assume the file is really CBR. */ seek_stream_to_byte( mad->filesize / 2 ); /* XXX use mad_header_decode and check more than one frame */ if(mad->length != -1 && do_mad_frame_decode() && mad->bitrate == (int) mad->Frame.header.bitrate) { return mad->length; } if( !MADLIB_rewind() ) return 0; /* Worst-case: vbr && !xing. We've made a guess at the length, but let's actually * scan the size, since the guess is probably wrong. */ if( fast ) { SetError("Can't estimate file length"); return -1; } MADLIB_rewind(); while(1) { int ret = do_mad_frame_decode( true ); if( ret == -1 ) return -1; /* it set the error */ if( ret == 0 ) /* EOF */ break; } /* mad->Timer is the timestamp of the current frame; find the timestamp of * the very end. */ mad_timer_t end = mad->Timer; mad_timer_add( &end, mad->framelength ); /* Count milliseconds. */ return mad_timer_count( end, MAD_UNITS_MILLISECONDS ); } int RageSoundReader_MP3::GetLengthConst( bool fast ) const { RageSoundReader_MP3 *cpy = (RageSoundReader_MP3 *) Copy(); int length = cpy->GetLengthInternal( fast ); delete cpy; return length; } /* begin XING code ripped out of madplay */ /* * NAME: xing->init() * DESCRIPTION: initialize Xing structure */ void xing_init(struct xing *xing) { xing->flags = 0; } /* * NAME: xing->parse() * DESCRIPTION: parse a Xing VBR header */ int xing_parse(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen) { const unsigned XING_MAGIC = (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g'); const unsigned INFO_MAGIC = (('I' << 24) | ('n' << 16) | ('f' << 8) | 'o'); unsigned data; if (bitlen < 64) goto fail; data = mad_bit_read(&ptr, 32); if( data == XING_MAGIC ) xing->type = xing::XING; else if( data == INFO_MAGIC ) xing->type = xing::INFO; else goto fail; xing->flags = mad_bit_read(&ptr, 32); bitlen -= 64; if (xing->flags & XING_FRAMES) { if (bitlen < 32) goto fail; xing->frames = mad_bit_read(&ptr, 32); bitlen -= 32; } if (xing->flags & XING_BYTES) { if (bitlen < 32) goto fail; xing->bytes = mad_bit_read(&ptr, 32); bitlen -= 32; } if (xing->flags & XING_TOC) { if (bitlen < 800) goto fail; for (int i = 0; i < 100; ++i) xing->toc[i] = (unsigned char) mad_bit_read(&ptr, 8); bitlen -= 800; } if (xing->flags & XING_SCALE) { if (bitlen < 32) goto fail; xing->scale = mad_bit_read(&ptr, 32); bitlen -= 32; } return 0; fail: xing->flags = 0; return -1; } /* end XING code ripped out of madplay */ /* * Copyright (c) 2003-2004 Glenn Maynard * Copyright (c) 2000-2003 Robert Leslie (Xing code from madplay) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */