/**************************************************************************** * libwiigui * * Tantric 2009 * * gui_sound_plugin_mpg.cpp * * by ardi 2009 * * Decoder for MPEG-Audio Mpeg-1/-2 Layer I,II and III with libmad * * GUI class definitions ***************************************************************************/ #include #include #include #include #include #include #include "gui_sound_decoder.h" static inline s16 FixedToShort(mad_fixed_t Fixed) { /* Clipping */ if(Fixed>=MAD_F_ONE) return(SHRT_MAX); if(Fixed<=-MAD_F_ONE) return(-SHRT_MAX); Fixed=Fixed>>(MAD_F_FRACBITS-15); return((s16)Fixed); } #define ADMA_BUFFERSIZE (8192) #define DATABUFFER_SIZE (32768) // http://www.fr-an.de/fragen/v06/02_01.htm class GuiSoundDecoderMPG : public GuiSoundDecoder { protected: GuiSoundDecoderMPG(const u8 * snd, u32 len, bool snd_is_allocated) { sound = snd; length = len; is_allocated = snd_is_allocated; // Init mad-structures mad_stream_init(&madStream); mad_stream_buffer(&madStream, sound, length); mad_frame_init(&madFrame); mad_synth_init(&madSynth); madSynthPcmPos = 0; mad_timer_reset(&madTimer); guardBuffer = NULL; is_running = false; // decode first Frame if(DecodeFirstFrame()) { mad_synth_finish(&madSynth); mad_frame_finish(&madFrame); mad_stream_finish(&madStream); throw("Stream Error"); } } public: ~GuiSoundDecoderMPG() { while(is_running) usleep(50); mad_synth_finish(&madSynth); mad_frame_finish(&madFrame); mad_stream_finish(&madStream); delete [] guardBuffer; if(is_allocated) delete [] sound; } static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) { struct mad_stream madStream; struct mad_header madHeader; mad_stream_init(&madStream); mad_stream_buffer(&madStream, snd, len); mad_header_init(&madHeader); s32 ret = mad_header_decode(&madHeader, &madStream); if(ret == 0 || madStream.error==MAD_ERROR_LOSTSYNC) // LOSTSYNC in first call is ok { int i; for(i=0; i<4 && mad_header_decode(&madHeader, &madStream)==0; i++); if( i == 4 ) { mad_header_finish(&madHeader); mad_stream_finish(&madStream); return new GuiSoundDecoderMPG(snd, len, snd_is_allocated); } } mad_header_finish(&madHeader); mad_stream_finish(&madStream); return NULL; } s32 GetFormat() { return MAD_NCHANNELS(&madFrame.header)==2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; } s32 GetSampleRate() { return madFrame.header.samplerate; } /* Read reads data from stream to buffer return: >0 = readed bytes; 0 = EOF; <0 = Error; */ int Read(u8 * buffer, int buffer_size) { is_running = true; if(MAD_NCHANNELS(&madFrame.header)==2) // stereo buffer_size &= ~0x0003; // make size to a kind of 4 else buffer_size &= ~0x0001; // make size to a kind of 2 u8 *write_pos = buffer; u8 *write_end = buffer+buffer_size; for(;;) { for(;madSynthPcmPos < madSynth.pcm.length; ++madSynthPcmPos) { if(write_pos >= write_end) { is_running = false; return write_pos-buffer; } *((s16*)write_pos) = FixedToShort(madSynth.pcm.samples[0][madSynthPcmPos]); write_pos+=2; if(MAD_NCHANNELS(&madFrame.header)==2) // stereo { *((s16*)write_pos) = FixedToShort(madSynth.pcm.samples[1][madSynthPcmPos]); write_pos+=2; } } madStream.error = MAD_ERROR_NONE; if(mad_frame_decode(&madFrame,&madStream)) { if(MAD_RECOVERABLE(madStream.error)) { if(madStream.error!=MAD_ERROR_LOSTSYNC || !guardBuffer) continue; } else if(madStream.error==MAD_ERROR_BUFLEN) { if(!guardBuffer) { u32 guardLen = (madStream.bufend-madStream.next_frame); guardBuffer = new(std::nothrow) u8[guardLen + MAD_BUFFER_GUARD]; if(guardBuffer) { memcpy(guardBuffer, madStream.next_frame, guardLen); memset(guardBuffer+guardLen, 0, MAD_BUFFER_GUARD); mad_stream_buffer(&madStream, guardBuffer, guardLen + MAD_BUFFER_GUARD); continue; } } } break; } mad_timer_add(&madTimer,madFrame.header.duration); mad_synth_frame(&madSynth,&madFrame); madSynthPcmPos = 0; } is_running = false; return write_pos-buffer; } int Rewind() { while(is_running) usleep(50); delete [] guardBuffer; guardBuffer = NULL; mad_stream_buffer(&madStream, sound, length); mad_synth_finish(&madSynth); mad_synth_init(&madSynth); madSynthPcmPos = 0; mad_timer_reset(&madTimer); // decode first Frame return DecodeFirstFrame(); } private: int DecodeFirstFrame() { for(;;) { madStream.error = MAD_ERROR_NONE; if(mad_frame_decode(&madFrame,&madStream)) { if(MAD_RECOVERABLE(madStream.error)) continue; else return -1; } mad_timer_add(&madTimer,madFrame.header.duration); mad_synth_frame(&madSynth,&madFrame); return 0; } } const u8 *sound; u32 length; bool is_allocated; struct mad_stream madStream; struct mad_frame madFrame; struct mad_synth madSynth; u16 madSynthPcmPos; mad_timer_t madTimer; u8 *guardBuffer; bool is_running; }; REGISTER_GUI_SOUND_DECODER(GuiSoundDecoderMPG);