mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-03 18:15:06 +01:00
9e79c9d99b
* code cleanup
215 lines
7.4 KiB
C++
215 lines
7.4 KiB
C++
/****************************************************************************
|
|
* 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 <unistd.h>
|
|
#include <limits.h>
|
|
#include <asndlib.h>
|
|
#include <mad.h>
|
|
#include <string.h>
|
|
#include <new>
|
|
|
|
|
|
#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 );
|