2010-02-09 06:58:27 +01:00
|
|
|
#include "cMP3Decoder.h"
|
2010-02-13 00:11:26 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2010-02-09 06:58:27 +01:00
|
|
|
|
2010-02-13 00:11:26 +01:00
|
|
|
cMP3Decoder::cMP3Decoder(IDataSource* stream) : IAudioDecoder(stream), Context(0x0),
|
|
|
|
Valid(false), DataOffset(0), NumChannels(0), Frequency(0)
|
2010-02-09 06:58:27 +01:00
|
|
|
{
|
2010-02-13 00:11:26 +01:00
|
|
|
if(Stream && Stream->isValid() && Stream->getSize() > 0)
|
|
|
|
{
|
|
|
|
CurrentPacket.data = new unsigned char[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
|
2010-02-09 06:58:27 +01:00
|
|
|
|
2010-02-13 00:11:26 +01:00
|
|
|
if(!CurrentPacket.data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Context = new MPAuDecContext();
|
|
|
|
|
|
|
|
if(!Context || mpaudec_init(Context) < 0)
|
|
|
|
{
|
2010-02-13 05:47:59 +01:00
|
|
|
delete Context;
|
|
|
|
Context = 0x0;
|
2010-02-13 00:11:26 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check to see if we need to skip the idv3 header
|
|
|
|
char idv3Header[10];
|
|
|
|
int amountRead = Stream->read(idv3Header, 10);
|
2010-02-13 05:47:59 +01:00
|
|
|
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
|
|
|
|
{
|
2010-02-13 00:11:26 +01:00
|
|
|
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;
|
|
|
|
}
|
2010-02-09 06:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cMP3Decoder::~cMP3Decoder()
|
|
|
|
{
|
2010-02-13 05:47:59 +01:00
|
|
|
if (Context)
|
|
|
|
{
|
|
|
|
mpaudec_clear(Context);
|
|
|
|
delete Context;
|
2010-02-13 00:11:26 +01:00
|
|
|
}
|
2010-02-09 06:58:27 +01:00
|
|
|
|
2010-02-13 00:11:26 +01:00
|
|
|
if(CurrentPacket.data)
|
|
|
|
delete CurrentPacket.data;
|
2010-02-09 06:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioFormats cMP3Decoder::getFormat()
|
|
|
|
{
|
2010-02-13 00:11:26 +01:00
|
|
|
if(Valid)
|
|
|
|
{
|
|
|
|
if(NumChannels == 1)
|
|
|
|
{
|
|
|
|
return EAF_16BIT_MONO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return EAF_16BIT_STEREO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EAF_8BIT_MONO;
|
2010-02-09 06:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int cMP3Decoder::getFrequency()
|
|
|
|
{
|
2010-02-13 00:11:26 +01:00
|
|
|
if(Valid)
|
|
|
|
{
|
|
|
|
return Frequency;
|
|
|
|
}
|
|
|
|
|
2010-02-09 06:58:27 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cMP3Decoder::isSeekingSupported()
|
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
return true;
|
2010-02-09 06:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cMP3Decoder::isValid()
|
|
|
|
{
|
2010-02-13 00:11:26 +01:00
|
|
|
return Valid;
|
2010-02-09 06:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int cMP3Decoder::readAudioData(void* output, int amount)
|
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
if(Valid)
|
2010-02-13 00:11:26 +01:00
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
//Check to see if we have an empty packet and fill it
|
|
|
|
if(CurrentPacket.size <= CurrentPacket.read)
|
2010-02-13 00:11:26 +01:00
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
//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);
|
|
|
|
}
|
2010-02-13 00:11:26 +01:00
|
|
|
}
|
|
|
|
|
2010-02-13 01:37:10 +01:00
|
|
|
//If we have data left since last time, put it in if we have space
|
|
|
|
if( CurrentPacket.size > CurrentPacket.read )
|
2010-02-13 00:11:26 +01:00
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
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;
|
|
|
|
}
|
2010-02-13 00:11:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-09 06:58:27 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cMP3Decoder::setPosition(int position, bool relative)
|
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
if(Valid)
|
2010-02-13 00:11:26 +01:00
|
|
|
{
|
2010-02-13 01:37:10 +01:00
|
|
|
if(relative)
|
|
|
|
position = Stream->getCurrentPos() + position;
|
|
|
|
|
|
|
|
if(position < DataOffset)
|
|
|
|
position = DataOffset;
|
|
|
|
|
2010-02-13 00:11:26 +01:00
|
|
|
//Just reload from scratch
|
|
|
|
Stream->seek(DataOffset, false);
|
|
|
|
|
2010-02-13 05:47:59 +01:00
|
|
|
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;
|
2010-02-13 00:11:26 +01:00
|
|
|
Context->sample_rate = oldContext.sample_rate;
|
|
|
|
|
2010-02-13 01:37:10 +01:00
|
|
|
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);
|
|
|
|
|
2010-02-13 00:11:26 +01:00
|
|
|
return true;
|
|
|
|
}
|
2010-02-09 06:58:27 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cMP3Decoder::seek(float seconds, bool relative)
|
2010-02-13 01:37:10 +01:00
|
|
|
{
|
|
|
|
if(Valid)
|
|
|
|
{
|
|
|
|
int amountToSeek = seconds * (float)(Context->bit_rate / 8);
|
|
|
|
return setPosition(amountToSeek, relative);
|
|
|
|
}
|
2010-02-09 06:58:27 +01:00
|
|
|
return false;
|
|
|
|
}
|