/***************************************************************************** * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: libmad.c 543 2006-01-07 22:06:24Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/ #include "../common/common.h" #include "libmad.h" #include "libmad/frame.h" #include "libmad/synth.h" typedef struct libmad { codec Codec; buffer Buffer; struct mad_stream Stream; struct mad_frame Frame; struct mad_synth Synth; int BufferAlign; int AdjustBytes; // adjust (rewind) RefTime bool_t Skip; // skip next frame int AdjustTime; int FormatSet; } libmad; static INLINE void UpdateAdjustTime( libmad* p ) { if (p->Codec.In.Format.ByteRate) p->AdjustTime = (TICKSPERSEC * 4096) / p->Codec.In.Format.ByteRate; else p->AdjustTime = 0; } static int UpdateInput( libmad* p ) { BufferClear(&p->Buffer); mad_frame_finish(&p->Frame); mad_stream_finish(&p->Stream); mad_synth_finish(&p->Synth); if (p->Codec.In.Format.Type == PACKET_AUDIO) { PacketFormatPCM(&p->Codec.Out.Format,&p->Codec.In.Format,MAD_F_FRACBITS+1); p->Codec.Out.Format.Format.Audio.Flags = PCM_PLANES; p->FormatSet = 0; p->AdjustBytes = 0; UpdateAdjustTime(p); mad_stream_init(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); } return ERR_NONE; } static int Process( libmad* p, const packet* Packet, const flowstate* State ) { if (Packet) { // remove processed bytes from buffer BufferPack(&p->Buffer,p->Stream.next_frame - p->Stream.buffer); // set new reftime if (Packet->RefTime >= 0) { p->Codec.Packet.RefTime = Packet->RefTime; p->AdjustBytes = p->Buffer.WritePos - p->Buffer.ReadPos; } // add new packet to buffer BufferWrite(&p->Buffer,Packet->Data[0],Packet->Length,p->BufferAlign); mad_stream_buffer(&p->Stream, p->Buffer.Data+p->Buffer.ReadPos, p->Buffer.WritePos-p->Buffer.ReadPos); } else p->Codec.Packet.RefTime = TIME_UNKNOWN; for (;;) { while (mad_frame_decode(&p->Frame, &p->Stream) == -1) { if (p->Stream.error == MAD_ERROR_BUFLEN || p->Stream.error == MAD_ERROR_BUFPTR) return ERR_NEED_MORE_DATA; if (p->Stream.error == MAD_ERROR_NOMEM) { BufferDrop(&p->Buffer); return ERR_OUT_OF_MEMORY; } } if (p->Skip) { // the first frame, it may be corrupt p->Skip--; continue; } mad_synth_frame(&p->Synth,&p->Frame); // handle stereo streams with random mono frames (is this really a good mp3?) if (p->Codec.Out.Format.Format.Audio.Channels==2 && p->Synth.pcm.channels==1) { p->Synth.pcm.channels = 2; memcpy(p->Synth.pcm.samples[1],p->Synth.pcm.samples[0], p->Synth.pcm.length * sizeof(mad_fixed_t)); } if (p->Codec.In.Format.ByteRate != (int)(p->Frame.header.bitrate >> 3)) { p->Codec.In.Format.ByteRate = (p->Frame.header.bitrate >> 3); UpdateAdjustTime(p); } // adjust RefTime with AdjustBytes (now that we know the bitrate) if (p->Codec.Packet.RefTime >= 0) p->Codec.Packet.RefTime -= (p->AdjustBytes * p->AdjustTime) >> 12; // output format setup needed? if (p->Codec.Out.Format.Format.Audio.SampleRate != (int)p->Synth.pcm.samplerate || p->Codec.Out.Format.Format.Audio.Channels != p->Synth.pcm.channels) { if (p->FormatSet) { p->FormatSet--; continue; // probably a bad frame, drop it } // set new output format p->Codec.In.Format.Format.Audio.SampleRate = p->Codec.Out.Format.Format.Audio.SampleRate = p->Synth.pcm.samplerate; p->Codec.In.Format.Format.Audio.Channels = p->Codec.Out.Format.Format.Audio.Channels = p->Synth.pcm.channels; ConnectionUpdate(&p->Codec.Node,CODEC_OUTPUT,p->Codec.Out.Pin.Node,p->Codec.Out.Pin.No); } p->FormatSet = 16; // reset countdown break; } // build output packet p->Codec.Packet.Length = p->Synth.pcm.length * sizeof(mad_fixed_t); p->Codec.Packet.Data[0] = p->Synth.pcm.samples[0]; p->Codec.Packet.Data[1] = p->Synth.pcm.samples[1]; return ERR_NONE; } static int Flush( libmad* p ) { BufferDrop(&p->Buffer); p->Skip = 1; mad_frame_finish(&p->Frame); mad_synth_finish(&p->Synth); mad_stream_finish(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); mad_stream_init(&p->Stream); return ERR_NONE; } #ifdef BUILDFIXED #include extern struct fixedfloat { unsigned long mantissa : 27; unsigned long exponent : 5; } rq_table[8207]; #endif static int Create( libmad* p ) { p->Codec.Process = (packetprocess)Process; p->Codec.UpdateInput = (nodefunc)UpdateInput; p->Codec.Flush = (nodefunc)Flush; p->BufferAlign = Context()->LowMemory?4096:16384; return ERR_NONE; } #define XING_FRAMES 0x01 #define XING_BYTES 0x02 #define XING_TOC 0x04 #define XING_SCALE 0x08 typedef struct mp3 { rawaudio RawAudio; int Frames; int Bytes; bool_t TOC; uint8_t TOCTable[100]; int VBRITableSize; int VBRIEntryFrames; int* VBRITable; } mp3; static int Read(format_reader* Reader,int n) { int v=0; while (n--) { v = v << 8; v += Reader->Read8(Reader); } return v; } static int InitMP3(mp3* p) { static const int RateTable[4] = { 44100, 48000, 32000, 99999 }; format_reader* Reader; int i,SampleRate,Id,Mode,Layer,SamplePerFrame; int Result = RawAudioInit(&p->RawAudio); p->TOC = 0; p->Bytes = p->RawAudio.Format.FileSize - p->RawAudio.Head; p->Frames = 0; p->RawAudio.VBR = 0; Reader = p->RawAudio.Format.Reader; for (i=0;i<2048;++i) if (Reader->Read8(Reader) == 0xFF) { filepos_t Frame; i = Reader->Read8(Reader); if ((i & 0xE0) != 0xE0) continue; Id = (i >> 3) & 3; Layer = (i >> 1) & 3; SampleRate = RateTable[(Reader->Read8(Reader) >> 2) & 3]; if (Id==2) SampleRate >>= 1; // MPEG2 if (Id==0) SampleRate >>= 2; // MPEG2.5 Mode = (Reader->Read8(Reader) >> 6) & 3; SamplePerFrame = (Layer == 3)?384:1152; Frame = Reader->FilePos; //Xing offset if (Id==3) { // MPEG1 Reader->Skip(Reader,Mode==3 ? 17:32); } else { // MPEG2/2.5 Reader->Skip(Reader,Mode==3 ? 9:17); if (Layer == 1) // layer-3 SamplePerFrame = 576; } if (Reader->ReadLE32(Reader) == FOURCCLE('X','i','n','g')) { int Flags = Reader->ReadBE32(Reader); if (Flags & XING_FRAMES) p->Frames = Reader->ReadBE32(Reader); if (Flags & XING_BYTES) p->Bytes = Reader->ReadBE32(Reader); if (Flags & XING_TOC) { p->TOC = 1; Reader->Read(Reader,p->TOCTable,100); } } else { Reader->Seek(Reader,Frame+32,SEEK_SET); if (Reader->ReadLE32(Reader) == FOURCCLE('V','B','R','I')) { int Scale,EntryBytes; Reader->Skip(Reader,2+2+2); //Version,Delay,Quality p->Bytes= Reader->ReadBE32(Reader); p->Frames = Reader->ReadBE32(Reader); p->VBRITableSize = Reader->ReadBE16(Reader)+1; Scale = Reader->ReadBE16(Reader); EntryBytes = Reader->ReadBE16(Reader); p->VBRIEntryFrames = Reader->ReadBE16(Reader); p->VBRITable = malloc(sizeof(int)*p->VBRITableSize); if (p->VBRITable) for (i=0;iVBRITableSize;++i) p->VBRITable[i] = Read(Reader,EntryBytes)*Scale; } } if (p->Frames>0) { p->RawAudio.VBR = 1; p->RawAudio.Format.Duration = Scale(p->Frames,TICKSPERSEC*SamplePerFrame,SampleRate); if (p->Bytes>0) p->RawAudio.Format.Streams[0]->Format.ByteRate = Scale(p->Bytes,TICKSPERSEC,p->RawAudio.Format.Duration); } break; } Reader->Seek(Reader,p->RawAudio.Head,SEEK_SET); return Result; } static void DoneMP3(mp3* p) { if (p->VBRITable) { free(p->VBRITable); p->VBRITable = NULL; } } static int SeekMP3(mp3* p, tick_t Time, filepos_t FilePos,bool_t PrevKey) { if (FilePos < 0 && Time > 0 && p->RawAudio.Format.Duration > 0) { int i,a,b; if (Time > p->RawAudio.Format.Duration) Time = p->RawAudio.Format.Duration; if (p->VBRITable) { int i; tick_t Left; tick_t EntryTime = p->RawAudio.Format.Duration / p->VBRITableSize; FilePos = p->RawAudio.Head; Left = Time; for (i=0;Left>0 && iVBRITableSize;++i) { FilePos += p->VBRITable[i]; Left -= EntryTime; } if (i>0) FilePos += Scale(Left,p->VBRITable[i-1],EntryTime); } else if (p->TOC && p->Bytes>0) { i = Scale(Time,100,p->RawAudio.Format.Duration); if (i>99) i=99; a = p->TOCTable[i]; if (i==99) b = 256; else b = p->TOCTable[i+1]; a <<= 10; b <<= 10; a += Scale(Time - Scale(i,p->RawAudio.Format.Duration,100),b-a,p->RawAudio.Format.Duration); FilePos = p->RawAudio.Head + Scale(a,p->Bytes,256*1024); } } return RawAudioSeek(&p->RawAudio,Time,FilePos,PrevKey); } static int CreateMP3( mp3* p ) { p->RawAudio.Format.Init = (fmtfunc)InitMP3; p->RawAudio.Format.Done = (fmtvoid)DoneMP3; p->RawAudio.Format.Seek = (fmtseek)SeekMP3; return ERR_NONE; } static const nodedef MP3 = { sizeof(mp3), MP3_ID, RAWAUDIO_CLASS, PRI_DEFAULT-5, (nodecreate)CreateMP3, }; static const nodedef LibMad = { sizeof(libmad), LIBMAD_ID, CODEC_CLASS, PRI_DEFAULT-5, (nodecreate)Create, }; void LibMad_Init() { #ifdef BUILDFIXED int x; struct fixedfloat* p=rq_table; for (x=0;x<8207;++x,++p) { int exp; double v = frexp(pow(x,4./3.),&exp); if (exp) ++exp; p->exponent = (unsigned short)exp; p->mantissa = (int)(0x10000000 * (v*0.5)); } #endif NodeRegisterClass(&LibMad); NodeRegisterClass(&MP3); } void LibMad_Done() { NodeUnRegisterClass(LIBMAD_ID); NodeUnRegisterClass(MP3_ID); }