//************************************************************************** //** //** ## ## ## ## ## #### #### ### ### //** ## ## ## ## ## ## ## ## ## ## #### #### //** ## ## ## ## ## ## ## ## ## ## ## ## ## ## //** ## ## ######## ## ## ## ## ## ## ## ### ## //** ### ## ## ### ## ## ## ## ## ## //** # ## ## # #### #### ## ## //** //** $Id: s_mp3.cpp 1583 2006-06-27 19:05:42Z dj_jl $ //** //** Copyright (C) 1999-2006 Jānis Legzdiņš //** //** This program is free software; you can redistribute it and/or //** modify it under the terms of the GNU General Public License //** as published by the Free Software Foundation; either version 2 //** of the License, or (at your option) any later version. //** //** This program is distributed in the hope that it will be useful, //** but WITHOUT ANY WARRANTY; without even the implied warranty of //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //** GNU General Public License for more details. //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include #include "gamedefs.h" #include "s_local.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- class VMp3AudioCodec : public VAudioCodec { public: enum { INPUT_BUFFER_SIZE = 5 * 8192 }; VStream* Strm; int BytesLeft; bool Initialised; mad_stream Stream; mad_frame Frame; mad_synth Synth; byte InputBuffer[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD]; int FramePos; bool HaveFrame; VMp3AudioCodec(VStream* InStrm); ~VMp3AudioCodec(); bool Init(); int Decode(short* Data, int NumSamples); int ReadData(); bool Finished(); void Restart(); static VAudioCodec* Create(VStream* InStrm); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ IMPLEMENT_AUDIO_CODEC(VMp3AudioCodec, "MP3"); // CODE -------------------------------------------------------------------- //========================================================================== // // VMp3AudioCodec::VMp3AudioCodec // //========================================================================== VMp3AudioCodec::VMp3AudioCodec(VStream* InStrm) : Strm(InStrm) , Initialised(false) { guard(VMp3AudioCodec::VMp3AudioCodec); // Seek file to the begining BytesLeft = Strm->TotalSize(); Strm->Seek(0); // Initialise structures used by libmad mad_stream_init(&Stream); mad_frame_init(&Frame); mad_synth_init(&Synth); unguard; } //========================================================================== // // VMp3AudioCodec::~VMp3AudioCodec // //========================================================================== VMp3AudioCodec::~VMp3AudioCodec() { guard(VMp3AudioCodec::~VMp3AudioCodec); if (Initialised) { // Close file only if decoder has been initialised succesfully. Strm->Close(); delete Strm; Strm = NULL; } // Clear structs used by libmad. mad_synth_finish(&Synth); mad_frame_finish(&Frame); mad_stream_finish(&Stream); unguard; } //========================================================================== // // VMp3AudioCodec::Init // //========================================================================== bool VMp3AudioCodec::Init() { guard(VMp3AudioCodec::Init); // Check for ID3v2 header. byte Id3Hdr[10]; int SavedPos = Strm->Tell(); Strm->Serialise(Id3Hdr, 10); if (Id3Hdr[0] == 'I' && Id3Hdr[1] == 'D' && Id3Hdr[2] == '3') { // It's a ID3v3 header, skip it. int HdrSize = Id3Hdr[9] + (Id3Hdr[8] << 7) + (Id3Hdr[7] << 14) + (Id3Hdr[6] << 21); if (HdrSize + 10 > BytesLeft) return false; Strm->Seek(Strm->Tell() + HdrSize); BytesLeft -= 10 + HdrSize; } else { // Not a ID3v3 header, seek back to saved position. Strm->Seek(SavedPos); } // Read some data. ReadData(); // Decode first frame. If this fails we assume it's not a MP3 file. if (mad_frame_decode(&Frame, &Stream)) { // Not a valid stream. return false; } // We are ready to read data. mad_synth_frame(&Synth, &Frame); FramePos = 0; HaveFrame = true; // Everything's OK. SampleRate = Frame.header.samplerate; Initialised = true; return true; unguard; } //========================================================================== // // VMp3AudioCodec::Decode // //========================================================================== int VMp3AudioCodec::Decode(short* Data, int NumSamples) { guard(VMp3AudioCodec::Decode); int CurSample = 0; do { if (HaveFrame) { // Convert stream from fixed point to short. for (; FramePos < Synth.pcm.length; FramePos++) { // Left channel short Sample; mad_fixed_t Fixed = Synth.pcm.samples[0][FramePos]; if (Fixed >= MAD_F_ONE) Sample = 0x7fff; else if (Fixed <= -MAD_F_ONE) Sample = -0x7fff; else Sample = Fixed >> (MAD_F_FRACBITS - 15); Data[CurSample * 2] = Sample; // Right channel. If the decoded stream is monophonic then // the right output channel is the same as the left one. if (MAD_NCHANNELS(&Frame.header) == 2) { Fixed = Synth.pcm.samples[1][FramePos]; if (Fixed >= MAD_F_ONE) Sample = 0x7fff; else if (Fixed <= -MAD_F_ONE) Sample = -0x7fff; else Sample = Fixed >> (MAD_F_FRACBITS - 15); } Data[CurSample * 2 + 1] = Sample; CurSample++; // Check if we already have decoded enough. if (CurSample >= NumSamples) return CurSample; } // We are done with the frame. HaveFrame = false; } // Fill in input buffer if it becomes empty. if (Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) { if (!ReadData()) break; } // Decode the next frame. if (mad_frame_decode(&Frame, &Stream)) { if (MAD_RECOVERABLE(Stream.error) || Stream.error==MAD_ERROR_BUFLEN) { continue; } else { break; } } // Once decoded the frame is synthesized to PCM samples. mad_synth_frame(&Synth, &Frame); FramePos = 0; HaveFrame = true; } while(1); return CurSample; unguard; } //========================================================================== // // VMp3AudioCodec::ReadData // //========================================================================== int VMp3AudioCodec::ReadData() { guard(VMp3AudioCodec::ReadData); int ReadSize; int Remaining; byte* ReadStart; // If there are some bytes left, move them to the beginning of the // buffer. if (Stream.next_frame != NULL) { Remaining = Stream.bufend - Stream.next_frame; memmove(InputBuffer, Stream.next_frame, Remaining); ReadStart = InputBuffer + Remaining; ReadSize = INPUT_BUFFER_SIZE - Remaining; } else { ReadSize = INPUT_BUFFER_SIZE; ReadStart = InputBuffer; Remaining = 0; } // Fill-in the buffer. if (ReadSize > BytesLeft) ReadSize = BytesLeft; if (!ReadSize) return 0; Strm->Serialise(ReadStart, ReadSize); BytesLeft -= ReadSize; // When decoding the last frame of a file, it must be followed by // MAD_BUFFER_GUARD zero bytes if one wants to decode that last frame. if (!BytesLeft) { memset(ReadStart + ReadSize + Remaining, 0, MAD_BUFFER_GUARD); ReadSize += MAD_BUFFER_GUARD; } // Pipe the new buffer content to libmad's stream decoder facility. mad_stream_buffer(&Stream, InputBuffer, ReadSize + Remaining); Stream.error = MAD_ERROR_NONE; return ReadSize + Remaining; unguard; } //========================================================================== // // VMp3AudioCodec::Finished // //========================================================================== bool VMp3AudioCodec::Finished() { guard(VMp3AudioCodec::Finished); // We are done if there's no more data and last frame has been decoded. return !BytesLeft && !HaveFrame; unguard; } //========================================================================== // // VMp3AudioCodec::Restart // //========================================================================== void VMp3AudioCodec::Restart() { guard(VMp3AudioCodec::Restart); // Seek to the beginning of the file. Strm->Seek(0); BytesLeft = Strm->TotalSize(); unguard; } //========================================================================== // // VMp3AudioCodec::Create // //========================================================================== VAudioCodec* VMp3AudioCodec::Create(VStream* InStrm) { guard(VMp3AudioCodec::Create); VMp3AudioCodec* Codec = new VMp3AudioCodec(InStrm); if (!Codec->Init()) { delete Codec; return NULL; } return Codec; unguard; }