/**************************************************************************** * 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 );