170 lines
4.7 KiB
C++
170 lines
4.7 KiB
C++
|
#include "nsv_vlb.h"
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// NSV VLB Decoder
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
VLB_Decoder::VLB_Decoder()
|
||
|
{
|
||
|
aacdec = new CAacDecoderApi( &datain );
|
||
|
fused = 0;
|
||
|
needsync = 1;
|
||
|
packets_in_since_flush = 0;
|
||
|
packets_decoded_since_flush = 0;
|
||
|
}
|
||
|
|
||
|
VLB_Decoder::~VLB_Decoder()
|
||
|
{
|
||
|
delete aacdec;
|
||
|
}
|
||
|
|
||
|
void VLB_Decoder::flush()
|
||
|
{
|
||
|
datain.Empty();
|
||
|
dataout.Empty();
|
||
|
|
||
|
//OutputDebugString("FLUSH\n");
|
||
|
|
||
|
|
||
|
// JF> this seems to be necessary for me at least, to have aacdec's internal buffer and state get reset :/
|
||
|
// (especially for AAC files, but I got it to do weird stuff on VLB files too :/)
|
||
|
// might be cleaner to see if we can just clear it somehow manually.. hmm..
|
||
|
delete aacdec;
|
||
|
aacdec = new CAacDecoderApi( &datain );
|
||
|
fused = 0;
|
||
|
needsync = 1;
|
||
|
packets_in_since_flush = 0;
|
||
|
packets_decoded_since_flush = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int VLB_Decoder::decode( void *in, int in_len,
|
||
|
void *out, int *out_len,
|
||
|
unsigned int out_fmt[8] )
|
||
|
{
|
||
|
// This function gets called with 1 nsv frame's worth of audio data.
|
||
|
// That could mean 1 OR MORE audio packets. (or zero?)
|
||
|
// Just process the smallest amount (1 packet) and return 0 if you
|
||
|
// finished processing this big chunk, or 1 if you need to work on
|
||
|
// extracting more audio (from this same nsv frame) on the next call.
|
||
|
|
||
|
// RETURN VALUES:
|
||
|
// 1: call me again w/same buffer (and contents) next time
|
||
|
// 0: give me a new buffer (w/new frame contents) next time
|
||
|
|
||
|
AUDIO_FORMATINFO info;
|
||
|
|
||
|
int rval = 1;
|
||
|
|
||
|
if (!dataout.BytesAvail())
|
||
|
{
|
||
|
int l = datain.GetInputFree(); // the # of bytes that datain still has room for
|
||
|
if ( l > in_len - fused ) l = in_len - fused;
|
||
|
|
||
|
if ( l > 0)
|
||
|
{
|
||
|
datain.Fill( (unsigned char *)in + fused, l );
|
||
|
fused += l;
|
||
|
}
|
||
|
|
||
|
/*********************************/
|
||
|
if (in_len > 0)
|
||
|
packets_in_since_flush++;
|
||
|
#define PACKETS_TO_PREBUFFER_BEFORE_SYNCHRONIZE 2
|
||
|
if (needsync && packets_in_since_flush < PACKETS_TO_PREBUFFER_BEFORE_SYNCHRONIZE)
|
||
|
{
|
||
|
// Don't allow ourselves to call Synchronize() until we've actually received
|
||
|
// TWO audio packets. We need two because Synchronize() peeks ahead beyond
|
||
|
// the first packet, and throws an exception if the second one isn't also available.
|
||
|
// (note that if we were worried about getting partial frames from the nsv decoder,
|
||
|
// we'd want to set PACKETS_TO_PREBUFFER==3...)
|
||
|
fused = 0;
|
||
|
*out_len = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
/*********************************/
|
||
|
|
||
|
if (!datain.GetSize())
|
||
|
{
|
||
|
if ( fused >= in_len ) rval = fused = 0; // get more data
|
||
|
*out_len = 0;
|
||
|
return rval;
|
||
|
}
|
||
|
|
||
|
if (needsync)
|
||
|
{
|
||
|
int status = aacdec->Synchronize( ¶ms ); // returns # of bytes skipped forward through 'datain' in params.frame_length
|
||
|
if (status)
|
||
|
{
|
||
|
// ERROR
|
||
|
*out_len = 0;
|
||
|
if ( fused >= in_len ) rval = fused = 0; // get more data
|
||
|
return rval;
|
||
|
}
|
||
|
|
||
|
needsync = 0;
|
||
|
|
||
|
// NOTE: as long as the NSV file was encoded properly (i.e. all vlb audio packets were
|
||
|
// sent to the encoder intact, never split among two nsv frames), we don't have to worry
|
||
|
// about ever getting back a partial frame from the NSV decoder; it will sync for us,
|
||
|
// and hand us the 1st complete audio packet it can find.
|
||
|
// In short: Synchronize() should always return with params.frame_length==0.
|
||
|
/*
|
||
|
int bytes_skipped = params.frame_length;
|
||
|
if (bytes_skipped)
|
||
|
{
|
||
|
// here we assume that the first packet was partially or entirely skipped through
|
||
|
// (but that the 2nd and 3rd packets were 100% okay), so we won't try to decode
|
||
|
// that first packet.
|
||
|
packets_in_since_flush--;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
info.ucNChannels = (unsigned char) params.num_channels;
|
||
|
info.uiSampleRate = params.sampling_frequency;
|
||
|
|
||
|
dataout.SetFormatInfo( &info );
|
||
|
}
|
||
|
|
||
|
while (packets_decoded_since_flush < packets_in_since_flush)
|
||
|
{
|
||
|
int status = aacdec->DecodeFrame( &dataout, ¶ms ); // returns # of bytes consumed from 'datain' in params.frame_length
|
||
|
packets_decoded_since_flush++;
|
||
|
if ( status > ERR_NO_ERROR && status != ERR_END_OF_FILE)
|
||
|
{
|
||
|
// ERROR
|
||
|
flush();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (packets_decoded_since_flush > 64)
|
||
|
{
|
||
|
// avoid overflow, but don't let either of these vars drop back to 0, 1, or 2!
|
||
|
packets_in_since_flush -= 32;
|
||
|
packets_decoded_since_flush -= 32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int l = dataout.BytesAvail();
|
||
|
if (l > 0)
|
||
|
{
|
||
|
if ( l > *out_len ) l = *out_len;
|
||
|
else *out_len = l;
|
||
|
|
||
|
dataout.PullBytes( (unsigned char *)out, l );
|
||
|
|
||
|
info = dataout.GetFormatInfo();
|
||
|
|
||
|
out_fmt[0] = NSV_MAKETYPE( 'P', 'C', 'M', ' ' );
|
||
|
out_fmt[1] = info.uiSampleRate;
|
||
|
out_fmt[2] = info.ucNChannels;
|
||
|
out_fmt[3] = 16;
|
||
|
out_fmt[4] = params.bitrate;
|
||
|
}
|
||
|
else *out_len = 0;
|
||
|
|
||
|
return rval;
|
||
|
}
|