Files
itgmania212121/src/RageSoundReader_MP3.cpp
T
2013-04-19 20:34:11 -07:00

1178 lines
29 KiB
C++

/* MAD is available from: http://www.underbit.com/products/mad/ */
#include "global.h"
#include "RageSoundReader_MP3.h"
#include "RageLog.h"
#include "RageUtil.h"
#include <cstdio>
#include <cerrno>
#include <map>
#if defined(_WINDOWS) || defined(MACOSX)
#include "../extern/mad-0.15.1b/mad.h"
#ifdef _MSC_VER
#pragma comment(lib, "libmad.lib")
#endif //_MSC_VER
#else
#include <mad.h>
#endif // _WINDOWS
// ID3 code from libid3:
enum tagtype {
TAGTYPE_NONE = 0,
TAGTYPE_ID3V1,
TAGTYPE_ID3V2,
TAGTYPE_ID3V2_FOOTER
};
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
{
madlib_t()
{
outpos = 0;
outleft = 0;
memset( inbuf, 0, sizeof(inbuf) );
memset( outbuf, 0, sizeof(outbuf) );
memset( &Stream, 0, sizeof(Stream) );
memset( &Frame, 0, sizeof(Frame) );
memset( &Synth, 0, sizeof(Synth) );
Timer = mad_timer_zero;
timer_accurate = false;
inbuf_filepos = 0;
filesize = 0;
header_bytes = 0;
first_frame = 0;
has_xing = 0;
memset( &xingtag, 0, sizeof(xingtag) );
length = 0;
framelength = mad_timer_zero;
bitrate = 0;
}
uint8_t inbuf[16384];
float 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 constructed
* as we read the file, so we can quickly seek back later.
*/
struct mad_timer_compare_lt
{
bool operator() ( const mad_timer_t &timer1, const mad_timer_t &timer2 ) const
{
return mad_timer_compare( timer1, timer2 ) < 0;
}
};
typedef map<mad_timer_t, int, mad_timer_compare_lt> tocmap_t;
tocmap_t tocmap;
/* 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 float scale(mad_fixed_t sample)
{
return float( double(sample) / (1<<MAD_F_FRACBITS) );
}
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 = m_pFile->AtEOF();
int rc = m_pFile->Read( mad->inbuf + inbytes, sizeof(mad->inbuf)-inbytes-MAD_BUFFER_GUARD );
if( rc < 0 )
{
SetError( m_pFile->GetError() );
return -1;
}
if ( m_pFile->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 pos;
/* Only update the frame cache if our timer is consistent. */
if(!mad->timer_accurate) return;
/* the frame we just decoded: */
pos = get_this_frame_byte(mad);
/* Fill in the TOC percent. */
if( !mad->tocmap.empty() )
{
/* Don't add an entry if one already exists near this time. */
madlib_t::tocmap_t::iterator it = mad->tocmap.upper_bound( mad->Timer );
if( it != mad->tocmap.begin() )
{
--it;
int iDiffSeconds = mad->Timer.seconds - it->first.seconds;
if( iDiffSeconds < 5 )
return;
}
}
mad->tocmap[mad->Timer] = 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;
}
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 = mad->outpos = 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)
{
float Sample = scale(mad->Synth.pcm.samples[chan][i]);
mad->outbuf[mad->outleft] = Sample;
++mad->outleft;
}
}
}
/* 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( m_pFile->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->tocmap.empty() )
mad->first_frame = ( byte <= mad->tocmap.begin()->second );
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;
m_bAccurateSync = false;
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;
}
RageSoundReader_MP3::~RageSoundReader_MP3()
{
mad_synth_finish( &mad->Synth );
mad_frame_finish( &mad->Frame );
mad_stream_finish( &mad->Stream );
delete mad;
}
RageSoundReader_FileReader::OpenResult RageSoundReader_MP3::Open( RageFileBasic *pFile )
{
m_pFile = pFile;
mad->filesize = m_pFile->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;
}
RageSoundReader_MP3 *RageSoundReader_MP3::Copy() const
{
RageSoundReader_MP3 *ret = new RageSoundReader_MP3;
ret->m_pFile = m_pFile->Copy();
ret->m_pFile->Seek( 0 );
ret->m_bAccurateSync = m_bAccurateSync;
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;
}
int RageSoundReader_MP3::Read( float *buf, int iFrames )
{
int iFramesWritten = 0;
while( iFrames > 0 )
{
if( mad->outleft > 0 )
{
int iFramesToCopy = min( iFrames, int(mad->outleft / GetNumChannels()) );
const int iSamplesToCopy = iFramesToCopy * GetNumChannels();
const int iBytesToCopy = iSamplesToCopy * sizeof(float);
memcpy( buf, mad->outbuf + mad->outpos, iBytesToCopy );
buf += iSamplesToCopy;
iFrames -= iFramesToCopy;
iFramesWritten += iFramesToCopy;
mad->outpos += iSamplesToCopy;
mad->outleft -= iSamplesToCopy;
continue;
}
/* Decode more from the MP3 stream. */
int ret = do_mad_frame_decode();
if( ret == 0 )
return END_OF_FILE;
if( ret == -1 )
return ERROR;
synth_output();
}
return iFramesWritten;
}
bool RageSoundReader_MP3::MADLIB_rewind()
{
m_pFile->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 iFrame, bool Xing )
{
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;
int bytepos = -1;
if( Xing )
{
/* We can speed up the seek using the XING tag. First, figure
* out what percentage the requested position falls in. */
ASSERT( SampleRate != 0 );
int ms = int( (iFrame * 1000LL) / SampleRate );
ASSERT( mad->length != 0 );
int percent = ms * 100 / mad->length;
if( percent < 100 )
{
int jump = mad->xingtag.toc[percent];
bytepos = mad->filesize * jump / 256;
}
else
bytepos = 2000000000; /* force EOF */
mad_timer_set( &mad->Timer, 0, percent * mad->length, 100000 );
}
else
{
mad_timer_t desired;
mad_timer_set( &desired, 0, iFrame, SampleRate );
if( mad->tocmap.empty() )
return 1; /* don't have any info */
/* Find the last entry <= iFrame that we actually have an entry for;
* this will get us as close as possible. */
madlib_t::tocmap_t::iterator it = mad->tocmap.upper_bound( desired );
if( it == mad->tocmap.begin() )
return 1; /* don't have any info */
--it;
mad->Timer = it->first;
bytepos = it->second;
}
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();
}
return 1;
}
int RageSoundReader_MP3::SetPosition_hard( int iFrame )
{
mad_timer_t desired;
mad_timer_set( &desired, 0, iFrame, mad->Frame.header.samplerate );
/* 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 1;
/* We always come in here with data synthed. Be careful not to synth the
* same frame twice. */
bool synthed=true;
/* If we're already past the requested position, rewind. */
if(mad_timer_compare(mad->Timer, desired) > 0)
{
MADLIB_rewind();
do_mad_frame_decode();
synthed = false;
}
/* 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();
}
else
{
mad->outleft += mad->outpos;
mad->outpos = 0;
}
/* 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. */
mad->outpos = samples * this->Channels;
mad->outleft -= samples * this->Channels;
return 1;
}
/* 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 )
{
mad->outleft = mad->outpos = 0;
return ret; /* it set the error */
}
synthed = false;
}
}
/* Do a seek based on the bitrate. */
int RageSoundReader_MP3::SetPosition_estimate( int iFrame )
{
/* This doesn't leave us accurate. */
mad->timer_accurate = 0;
mad_timer_t seekamt;
mad_timer_set( &seekamt, 0, iFrame, mad->Frame.header.samplerate );
{
/* 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. */
int ms = (get_this_frame_byte(mad) - mad->header_bytes) / (mad->bitrate / 8 / 1000);
mad_timer_set(&mad->Timer, 0, ms, 1000);
return 1;
}
int RageSoundReader_MP3::SetPosition( int iFrame )
{
if( m_bAccurateSync )
{
/* Seek using our own internal (accurate) TOC. */
int ret = SetPosition_toc( iFrame, false );
if( ret <= 0 )
return ret; /* it set the error */
/* Align exactly. */
return SetPosition_hard( iFrame );
}
else
{
/* Rewinding is always fast and accurate, and SetPosition_estimate is bad at 0. */
if( !iFrame )
{
MADLIB_rewind();
return 1; /* ok */
}
/* We can do a fast jump in VBR with Xing with more accuracy than without Xing. */
if( mad->has_xing )
return SetPosition_toc( iFrame, 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( iFrame );
}
}
bool RageSoundReader_MP3::SetProperty( const RString &sProperty, float fValue )
{
if( sProperty == "AccurateSync" )
{
m_bAccurateSync = (fValue > 0.001f);
return true;
}
return RageSoundReader_FileReader::SetProperty( sProperty, fValue );
}
int RageSoundReader_MP3::GetNextSourceFrame() const
{
int iFrame = mad_timer_count( mad->Timer, mad_units(mad->Frame.header.samplerate) );
iFrame += mad->outpos / this->Channels;
return iFrame;
}
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 *pCopy = this->Copy();
int iLength = pCopy->GetLengthInternal( fast );
delete pCopy;
return iLength;
}
/* 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
*/