#include "cMP3Decoder.h" #include #include 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; }