Maintain precise sync through corruptions in Oggs. (Can't do that with MP3!)
This commit is contained in:
@@ -89,6 +89,7 @@ SoundReader_FileReader::OpenResult RageSoundReader_Vorbisfile::Open(CString file
|
|||||||
|
|
||||||
avail = 0;
|
avail = 0;
|
||||||
eof = false;
|
eof = false;
|
||||||
|
read_offset = (int) ov_pcm_tell(vf);
|
||||||
|
|
||||||
return OPEN_OK;
|
return OPEN_OK;
|
||||||
}
|
}
|
||||||
@@ -130,10 +131,93 @@ int RageSoundReader_Vorbisfile::SetPosition(int ms, bool accurate)
|
|||||||
SetError( ov_ssprintf(ret, "ogg: SetPosition failed") );
|
SetError( ov_ssprintf(ret, "ogg: SetPosition failed") );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
read_offset = (int) ov_pcm_tell(vf);
|
||||||
|
|
||||||
return ms;
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RageSoundReader_Vorbisfile::FillBuf()
|
||||||
|
{
|
||||||
|
if( avail )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
vorbis_info *vi = ov_info( vf, -1 );
|
||||||
|
ASSERT( vi != NULL );
|
||||||
|
const int bytes_per_frame = 2*vi->channels;
|
||||||
|
|
||||||
|
char tmpbuf[4096];
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
int curofs = (int) ov_pcm_tell(vf);
|
||||||
|
if( curofs < read_offset )
|
||||||
|
{
|
||||||
|
/* The timestamps moved backwards. Ignore it. This file probably
|
||||||
|
* won't sync correctly. */
|
||||||
|
LOG->Trace( "p ahead %p %i < %i, we're ahead by %i",
|
||||||
|
this, curofs, read_offset, read_offset-curofs );
|
||||||
|
read_offset = curofs;
|
||||||
|
}
|
||||||
|
else if( curofs > read_offset )
|
||||||
|
{
|
||||||
|
/* Our offset doesn't match. We have a hole in the data, or corruption.
|
||||||
|
* If we're reading with accurate syncing, insert silence to line it up.
|
||||||
|
* That way, corruptions in the file won't casue desyncs. */
|
||||||
|
|
||||||
|
/* In bytes: */
|
||||||
|
int silence = (curofs - read_offset) * bytes_per_frame;
|
||||||
|
CHECKPOINT_M( ssprintf("p %i,%i: %i frames of silence needed", curofs, read_offset, silence) );
|
||||||
|
silence = min( silence, (int) sizeof(tmpbuf) );
|
||||||
|
|
||||||
|
memset( tmpbuf, 0, silence );
|
||||||
|
ret = silence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ret == 0 )
|
||||||
|
{
|
||||||
|
int bstream;
|
||||||
|
#if defined(INTEGER_OGG)
|
||||||
|
ret = ov_read( vf, tmpbuf, sizeof(tmpbuf), &bstream );
|
||||||
|
#else // float vorbis decoder
|
||||||
|
ret = ov_read( vf, tmpbuf, sizeof(tmpbuf), (SDL_BYTEORDER == SDL_BIG_ENDIAN)?1:0, 2, 1, &bstream );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if( ret == OV_HOLE )
|
||||||
|
return true;
|
||||||
|
if( ret == OV_EBADLINK )
|
||||||
|
{
|
||||||
|
SetError( ssprintf("Read: OV_EBADLINK") );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ret == 0 )
|
||||||
|
{
|
||||||
|
eof = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read_offset += ret / bytes_per_frame;
|
||||||
|
|
||||||
|
/* If we have a different number of channels, we need to convert. */
|
||||||
|
ASSERT( vi->channels == 1 || vi->channels == 2 );
|
||||||
|
if( vi->channels == 1 )
|
||||||
|
{
|
||||||
|
Sint16 *indata = (Sint16 *)tmpbuf;
|
||||||
|
Sint16 *outdata = (Sint16 *)buffer;
|
||||||
|
int size = ret / sizeof(Sint16);
|
||||||
|
for( unsigned pos = 0; pos < unsigned(size); ++pos )
|
||||||
|
outdata[pos*2] = outdata[pos*2+1] = indata[pos];
|
||||||
|
ret *= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memcpy( buffer, tmpbuf, ret ); /* XXX optimize */
|
||||||
|
|
||||||
|
avail = ret;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int RageSoundReader_Vorbisfile::Read(char *buf, unsigned len)
|
int RageSoundReader_Vorbisfile::Read(char *buf, unsigned len)
|
||||||
{
|
{
|
||||||
if( eof )
|
if( eof )
|
||||||
@@ -142,47 +226,11 @@ int RageSoundReader_Vorbisfile::Read(char *buf, unsigned len)
|
|||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
while(len)
|
while(len)
|
||||||
{
|
{
|
||||||
if(!avail)
|
if( !FillBuf() )
|
||||||
{
|
|
||||||
vorbis_info *vi = ov_info(vf, -1);
|
|
||||||
ASSERT(vi != NULL);
|
|
||||||
|
|
||||||
char tmpbuf[4096];
|
|
||||||
int bstream;
|
|
||||||
|
|
||||||
#if defined(INTEGER_OGG)
|
|
||||||
int ret = ov_read(vf, tmpbuf, sizeof(tmpbuf), &bstream);
|
|
||||||
#else // float vorbis decoder
|
|
||||||
int ret = ov_read(vf, tmpbuf, sizeof(tmpbuf), (SDL_BYTEORDER == SDL_BIG_ENDIAN)?1:0, 2, 1, &bstream);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(ret == OV_HOLE)
|
|
||||||
continue;
|
|
||||||
if(ret == OV_EBADLINK)
|
|
||||||
{
|
|
||||||
SetError(ssprintf("Read: OV_EBADLINK"));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
if(ret == 0)
|
if( eof && !avail )
|
||||||
return bytes_read;
|
break;
|
||||||
|
|
||||||
/* If we have a different number of channels, we need to convert. */
|
|
||||||
ASSERT(vi->channels == 1 || vi->channels == 2);
|
|
||||||
if(vi->channels == 1)
|
|
||||||
{
|
|
||||||
Sint16 *indata = (Sint16 *)tmpbuf;
|
|
||||||
Sint16 *outdata = (Sint16 *)buffer;
|
|
||||||
int size = ret / sizeof(Sint16);
|
|
||||||
for(unsigned pos = 0; pos < unsigned(size); ++pos)
|
|
||||||
outdata[pos*2] = outdata[pos*2+1] = indata[pos];
|
|
||||||
ret *= 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memcpy(buffer, tmpbuf, ret); /* XXX optimize */
|
|
||||||
|
|
||||||
avail = ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned size = min(avail, len);
|
unsigned size = min(avail, len);
|
||||||
memcpy(buf, buffer, size);
|
memcpy(buf, buffer, size);
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ class RageSoundReader_Vorbisfile: public SoundReader_FileReader {
|
|||||||
unsigned avail;
|
unsigned avail;
|
||||||
bool eof;
|
bool eof;
|
||||||
int SetPosition(int ms, bool accurate);
|
int SetPosition(int ms, bool accurate);
|
||||||
|
bool FillBuf();
|
||||||
CString filename;
|
CString filename;
|
||||||
|
int read_offset;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OpenResult Open(CString filename);
|
OpenResult Open(CString filename);
|
||||||
|
|||||||
Reference in New Issue
Block a user