/**************************************************************************** * libwiigui * * Tantric 2009 * * gui_sound.cpp * * decoder modification by ardi 2009 * * GUI class definitions ***************************************************************************/ #include "gui.h" #include #include "gecko.h" #include "gui_sound_decoder.h" #define BUFFER_SIZE 8192 /*************************************************************** * * D E C O D E R – L I S T * * ***************************************************************/ GuiSoundDecoder::DecoderListEntry *GuiSoundDecoder::DecoderList = NULL; GuiSoundDecoder::DecoderListEntry &GuiSoundDecoder::RegisterDecoder( DecoderListEntry &Decoder, GuiSoundDecoderCreate fnc ) { if ( Decoder.fnc != fnc ) { Decoder.fnc = fnc; Decoder.next = DecoderList; DecoderList = &Decoder; } return Decoder; } GuiSoundDecoder *GuiSoundDecoder::GetDecoder( const u8 * snd, u32 len, bool snd_is_allocated ) { for ( DecoderListEntry *de = DecoderList; de; de = de->next ) { GuiSoundDecoder *d = NULL; try { d = de->fnc( snd, len, snd_is_allocated ); } catch ( const char *error ) { gprintf( "%s", error ); } catch ( ... ) {} if ( d ) return d; } return NULL; } /*************************************************************** * * D E C O D E R – T H R E A D * * ***************************************************************/ static GuiSound *GuiSoundPlayer[16] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static lwp_t GuiSoundDecoderThreadHandle = LWP_THREAD_NULL; static bool GuiSoundDecoderThreadRunning = false; static bool GuiSoundDecoderDataRquested = false; void *GuiSoundDecoderThread( void *args ) { GuiSoundDecoderThreadRunning = true; do { if ( GuiSoundDecoderDataRquested ) { GuiSoundDecoderDataRquested = false; GuiSound **players = GuiSoundPlayer; for ( int i = 0; i < 16; ++i , ++players ) { GuiSound *player = *players; if ( player ) player->DecoderCallback(); } } if ( !GuiSoundDecoderDataRquested ) usleep( 50 ); } while ( GuiSoundDecoderThreadRunning ); return 0; } /*************************************************************** * * A S N D – C A L L B A C K * * ***************************************************************/ void GuiSoundPlayerCallback( s32 Voice ) { if ( Voice >= 0 && Voice < 16 && GuiSoundPlayer[Voice] ) { GuiSoundPlayer[Voice]->PlayerCallback(); GuiSoundDecoderDataRquested = true; } } /*************************************************************** * * R A W - D E C O D E R * Decoder for Raw-PCM-Datas (16bit Stereo 48kHz) * ***************************************************************/ class GuiSoundDecoderRAW : public GuiSoundDecoder { protected: GuiSoundDecoderRAW( const u8 * snd, u32 len, bool snd_is_allocated ) { pcm_start = snd; is_allocated = snd_is_allocated; pcm_end = pcm_start + len; pos = pcm_start; is_running = false; } public: ~GuiSoundDecoderRAW() { while ( is_running ) usleep( 50 ); if ( is_allocated ) delete [] pcm_start; } static GuiSoundDecoder *Create( const u8 * snd, u32 len, bool snd_is_allocated ) { try { return new GuiSoundDecoderRAW( snd, len, snd_is_allocated ); } catch ( ... ) {} return NULL; } s32 GetFormat() { return VOICE_STEREO_16BIT; } s32 GetSampleRate() { return 48000; } /* Read reads data from stream to buffer return: >0 = readed bytes; 0 = EOF; <0 = Error; */ int Read( u8 * buffer, int buffer_size ) { if ( pos >= pcm_end ) return 0; // EOF is_running = true; if ( pos + buffer_size > pcm_end ) buffer_size = pcm_end - pos; memcpy( buffer, pos, buffer_size ); pos += buffer_size; is_running = false; return buffer_size; } int Rewind() { pos = pcm_start; return 0; } private: const u8 *pcm_start; const u8 *pcm_end; bool is_allocated; const u8 *pos; bool is_running; }; /*************************************************************** * * G u i S o u n d * * ***************************************************************/ #define GuiSoundBufferReady 0x01 #define GuiSoundBufferEOF 0x02 #define GuiSoundFinish 0x04 static int GuiSoundCount = 0; /** * Constructor for the GuiSound class. */ GuiSound::GuiSound( const u8 *s, int l, int v/*=100*/, bool r/*=true*/, bool a/*=false*/ ) { if ( GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL ) { LWP_CreateThread( &GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32768, 80 ); } voice = -1; play_buffer[0] = ( u8* )memalign( 32, BUFFER_SIZE * 3 ); // tripple-buffer first is played play_buffer[1] = play_buffer[0] + BUFFER_SIZE; // second is waiting play_buffer[2] = play_buffer[1] + BUFFER_SIZE; // third is decoding buffer_nr = 0; // current playbuffer buffer_pos = 0; // current idx to write in buffer buffer_ready = false; buffer_eof = false; loop = false; // play looped volume = v; // volume decoder = NULL; if ( play_buffer[0] ) // playbuffer ok Load( s, l, r, a ); } bool GuiSound::Load( const u8 *s, int l, bool r/*=false*/, bool a/*=false*/ ) { Stop(); if ( !play_buffer[0] ) return false; GuiSoundDecoder *newDecoder = GuiSoundDecoder::GetDecoder( s, l, a ); if ( !newDecoder && r ) newDecoder = GuiSoundDecoderRAW::Create( s, l, a ); if ( newDecoder ) { delete decoder; decoder = newDecoder; return true; } else if ( a ) delete [] s; return false; } GuiSound::GuiSound( const char *p, int v/*=100*/ ) { if ( GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL ) { LWP_CreateThread( &GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32*1024, 80 ); } voice = -1; play_buffer[0] = ( u8* )memalign( 32, BUFFER_SIZE * 3 ); // tripple-buffer first is played play_buffer[1] = play_buffer[0] + BUFFER_SIZE; // second is waiting play_buffer[2] = play_buffer[1] + BUFFER_SIZE; // third is decoding buffer_nr = 0; // current playbuffer buffer_pos = 0; // current idx to write in buffer buffer_ready = false; buffer_eof = false; loop = false; // play looped volume = v; // volume decoder = NULL; if ( play_buffer[0] ) // playbuffer ok Load( p ); } bool GuiSound::Load( const char *p ) { Stop(); // stop playing if ( !play_buffer[0] ) return false; bool ret = false; voice = -2; // -2 marks loading from file u32 filesize = 0; u8 *buffer = NULL; size_t result; FILE * pFile = fopen ( p, "rb" ); if ( pFile ) { // get file size: fseek ( pFile , 0 , SEEK_END ); filesize = ftell ( pFile ); fseek ( pFile , 0 , SEEK_SET ); // allocate memory to contain the whole file: buffer = new( std::nothrow ) u8[filesize]; if ( buffer ) { // copy the file into the buffer: result = fread ( buffer, 1, filesize, pFile ); if ( result == filesize ) ret = Load( buffer, filesize, false, true ); else delete [] buffer; } fclose ( pFile ); } return ret; } /** * Destructor for the GuiSound class. */ GuiSound::~GuiSound() { if ( !loop ) while ( voice >= 0 ) usleep( 50 ); Stop(); if ( --GuiSoundCount == 0 && GuiSoundDecoderThreadHandle != LWP_THREAD_NULL ) { GuiSoundDecoderThreadRunning = false; LWP_JoinThread( GuiSoundDecoderThreadHandle, NULL ); GuiSoundDecoderThreadHandle = LWP_THREAD_NULL; } delete decoder; free( play_buffer[0] ); } void GuiSound::Play() { Stop(); // stop playing if it played if ( !play_buffer[0] ) return; if ( !decoder ) return; // no decoder or no play_buffer -> no playing // initialize the buffer buffer_nr = 0; // allways starts with buffer 0 buffer_pos = 0; // reset position buffer_ready = false; buffer_eof = false; decoder->Rewind(); // play from begin DecoderCallback(); // fill first buffer; if ( !buffer_ready || buffer_eof ) // if first buffer not ready -> no play return; voice = ASND_GetFirstUnusedVoice(); if ( voice >= 0 ) { s32 vol = ( 255 * volume ) / 100; s32 format = decoder->GetFormat(); s32 samplerate = decoder->GetSampleRate(); s32 first_pos = buffer_pos; // switch to next buffer buffer_nr = 1; buffer_pos = 0; buffer_ready = false; buffer_eof = false; DecoderCallback(); // fill second buffer; GuiSoundPlayer[voice] = this; // activate Callbacks for this voice // Play the voice ASND_SetVoice( voice, format, samplerate, 0, play_buffer[0], first_pos, vol, vol, GuiSoundPlayerCallback ); } } /* int GuiSound::PlayOggFile(char * path) { if(Load(path)) Play(); return 1; } */ void GuiSound::Stop() { if ( voice < 0 ) return ; GuiSoundPlayer[voice] = NULL; // disable Callbacks SND_StopVoice( voice ); voice = -1; } void GuiSound::Pause() { if ( voice < 0 ) return ; ASND_PauseVoice( voice, 1 ); } void GuiSound::Resume() { if ( voice < 0 ) return ; ASND_PauseVoice( voice, 0 ); } bool GuiSound::IsPlaying() { return voice >= 0; } void GuiSound::SetVolume( int vol ) { volume = vol; if ( voice < 0 ) return ; int newvol = 255 * ( volume / 100.0 ); ASND_ChangeVolumeVoice( voice, newvol, newvol ); } void GuiSound::SetLoop( bool l ) { loop = l; } void GuiSound::DecoderCallback() { if ( buffer_ready || buffer_eof ) // if buffer ready or EOF -> nothing return; bool error = false; while ( buffer_pos < BUFFER_SIZE ) { int ret = decoder->Read( &play_buffer[buffer_nr][buffer_pos], BUFFER_SIZE - buffer_pos ); if ( ret > 0 ) buffer_pos += ret; // ok -> fill the buffer more else if ( ret == 0 ) // EOF from decoder { if ( loop ) decoder->Rewind(); // if loop -> rewind and fill the buffer more else if ( buffer_pos ) break; // has data in buffer -> play the buffer else buffer_eof = true; // no data in buffer -> return EOF return; } else if ( ret < 0 ) // an ERROR { if ( buffer_pos ) break; // has data in buffer -> play the buffer else if ( loop ) { if ( !error ) // if no prev error { decoder->Rewind(); // if loop -> rewind error = true; // set error-state continue; // and fill the buffer more } buffer_eof = true; // has prev error -> error in first block -> return EOF return; } else { buffer_eof = true; // no loop -> return EOF return; } } error = false; // clear error-state } buffer_ready = true; } void GuiSound::PlayerCallback() { if ( buffer_eof ) // if EOF { if ( ASND_TestPointer( voice, play_buffer[( buffer_nr+2 )%3] ) == 0 ) // test prev. Buffer Stop(); } else if ( buffer_ready ) // if buffer ready { if ( ASND_AddVoice( voice, play_buffer[buffer_nr], buffer_pos ) == SND_OK ) // add buffer { // next buffer buffer_nr = ( buffer_nr + 1 ) % 3; buffer_pos = 0; buffer_ready = false; buffer_eof = false; } } }