caudio/plugins/mp3Decoder/cMP3Decoder.cpp

256 lines
5.9 KiB
C++

#include "cMP3Decoder.h"
#include <stdlib.h>
#include <string.h>
cMP3Decoder::cMP3Decoder(IDataSource* stream) : IAudioDecoder(stream), Context(0x0),
Valid(false), DataOffset(0), NumChannels(0), Frequency(0)
{
if(Stream && Stream->isValid() && Stream->getSize() > 0)
{
CurrentPacket.data = new unsigned char[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
if(!CurrentPacket.data)
return;
Context = new MPAuDecContext();
if(!Context || mpaudec_init(Context) < 0)
{
delete Context;
Context = 0x0;
return;
}
//Check to see if we need to skip the idv3 header
char idv3Header[10];
int amountRead = Stream->read(idv3Header, 10);
if (amountRead == 10 && idv3Header[0] == 'I' && idv3Header[1] == 'D' && idv3Header[2] == '3')
{
int versionMajor = idv3Header[3];
int versionMinor = idv3Header[4];
int flags = idv3Header[5];
int size = 0;
size = (idv3Header[6] & 0x7f) << (3*7);
size |= (idv3Header[7] & 0x7f) << (2*7);
size |= (idv3Header[8] & 0x7f) << (1*7);
size |= (idv3Header[9] & 0x7f) ;
size += 10;
DataOffset = size;
Stream->seek(DataOffset, false);
}
else
{
Stream->seek(0, false);
}
//Now, read the header for our information
Context->parse_only = 1;
unsigned char tempBuffer[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
int outputSize = 0;
unsigned char inputBuffer[4096];
int inputSize = Stream->read(inputBuffer, 4096);
int rv = mpaudec_decode_frame( Context, tempBuffer, &outputSize, inputBuffer, inputSize);
//Oops, error...
if(rv < 0)
return;
NumChannels = Context->channels;
Frequency = Context->sample_rate;
Context->parse_only = 0;
Stream->seek(DataOffset, false);
Valid = true;
}
}
cMP3Decoder::~cMP3Decoder()
{
if (Context)
{
mpaudec_clear(Context);
delete Context;
}
if(CurrentPacket.data)
delete CurrentPacket.data;
}
AudioFormats cMP3Decoder::getFormat()
{
if(Valid)
{
if(NumChannels == 1)
{
return EAF_16BIT_MONO;
}
else
{
return EAF_16BIT_STEREO;
}
}
return EAF_8BIT_MONO;
}
int cMP3Decoder::getFrequency()
{
if(Valid)
{
return Frequency;
}
return 0;
}
bool cMP3Decoder::isSeekingSupported()
{
return true;
}
bool cMP3Decoder::isValid()
{
return Valid;
}
int cMP3Decoder::readAudioData(void* output, int amount)
{
if(Valid)
{
//Check to see if we have an empty packet and fill it
if(CurrentPacket.size <= CurrentPacket.read)
{
//Empty packet, grab a new one.
CurrentPacket.reset();
memset(CurrentPacket.data, 0, MPAUDEC_MAX_AUDIO_FRAME_SIZE);
unsigned char inputBuffer[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
int oldPos = Stream->getCurrentPos();
int inputSize = Stream->read(inputBuffer, Context->frame_size > MPAUDEC_MAX_AUDIO_FRAME_SIZE ? MPAUDEC_MAX_AUDIO_FRAME_SIZE : Context->frame_size);
if(inputSize == 0)
return 0;
int rv = mpaudec_decode_frame( Context, CurrentPacket.data, &CurrentPacket.size, inputBuffer, inputSize);
Stream->seek(oldPos + rv, false);
//Oops, error...
if(rv < 0)
return -1;
//OpenAL really hates it if you try to change format in the middle
if(NumChannels != Context->channels)
return -1;
if(Frequency != Context->sample_rate)
return -1;
//Error from reading, but we need to try to move ahead to avoid stopping the feed since it may be recoverable
if(CurrentPacket.size < 0)
{
CurrentPacket.size = Context->frame_size;
memset(CurrentPacket.data, 0, CurrentPacket.size);
}
}
//If we have data left since last time, put it in if we have space
if( CurrentPacket.size > CurrentPacket.read )
{
int amountLeft = CurrentPacket.size - CurrentPacket.read;
if(amountLeft < amount)
{
//Dump in all our data
memcpy(output, CurrentPacket.data + CurrentPacket.read, amountLeft);
CurrentPacket.read += amountLeft;
return amountLeft;
}
else
{
//Not enough space for all our data, put in a little bit
memcpy(output, CurrentPacket.data + CurrentPacket.read, amount);
CurrentPacket.read += amount;
return amount;
}
}
}
return 0;
}
bool cMP3Decoder::setPosition(int position, bool relative)
{
if(Valid)
{
if(relative)
position = Stream->getCurrentPos() + position;
if(position < DataOffset)
position = DataOffset;
//Just reload from scratch
Stream->seek(DataOffset, false);
MPAuDecContext oldContext = *Context;
mpaudec_clear(Context);
mpaudec_init(Context);
Context->bit_rate = oldContext.bit_rate;
Context->channels = oldContext.channels;
Context->frame_size = oldContext.frame_size;
Context->sample_rate = oldContext.sample_rate;
Context->parse_only = 1;
int lastFrameSize = 0;
while(Stream->getCurrentPos() < position)
{
//Eat up frames till we reach about the right spot
unsigned char inputBuffer[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
int oldPos = Stream->getCurrentPos();
int inputSize = Stream->read(inputBuffer, Context->frame_size > MPAUDEC_MAX_AUDIO_FRAME_SIZE ? MPAUDEC_MAX_AUDIO_FRAME_SIZE : Context->frame_size);
if(inputSize == 0)
{
Stream->seek(DataOffset, false);
return false;
}
unsigned char tempBuffer[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
int outputSize = 0;
int rv = mpaudec_decode_frame( Context, tempBuffer, &outputSize, inputBuffer, inputSize);
Stream->seek(oldPos + rv, false);
//Oops, error...
if(rv < 0)
{
Stream->seek(DataOffset, false);
return false;
}
lastFrameSize = rv;
}
Context->parse_only = 0;
Stream->seek(-lastFrameSize, true);
return true;
}
return false;
}
bool cMP3Decoder::seek(float seconds, bool relative)
{
if(Valid)
{
int amountToSeek = seconds * (float)(Context->bit_rate / 8);
return setPosition(amountToSeek, relative);
}
return false;
}