usbloadergx/source/libwiigui/gui_sound.cpp

447 lines
12 KiB
C++
Raw Normal View History

/****************************************************************************
* libwiigui
*
* Tantric 2009
*
* gui_sound.cpp
*
* decoder modification by ardi 2009
*
* GUI class definitions
***************************************************************************/
#include "gui.h"
#include <unistd.h>
#include "gecko.h"
#include "gui_sound_decoder.h"
#define BUFFER_SIZE 8192
/***************************************************************
*
* D E C O D E R <EFBFBD> L I S T
*
*
***************************************************************/
GuiSoundDecoder::DecoderListEntry *GuiSoundDecoder::DecoderList = NULL;
2010-09-24 02:48:03 +02:00
GuiSoundDecoder::DecoderListEntry &GuiSoundDecoder::RegisterDecoder(DecoderListEntry &Decoder,
GuiSoundDecoderCreate fnc)
{
2010-09-24 02:48:03 +02:00
if (Decoder.fnc != fnc)
{
Decoder.fnc = fnc;
Decoder.next = DecoderList;
DecoderList = &Decoder;
}
return Decoder;
}
2010-09-24 02:48:03 +02:00
GuiSoundDecoder *GuiSoundDecoder::GetDecoder(const u8 * snd, u32 len, bool snd_is_allocated)
{
2010-09-24 02:48:03 +02:00
for (DecoderListEntry *de = DecoderList; de; de = de->next)
{
GuiSoundDecoder *d = NULL;
2010-09-24 02:48:03 +02:00
try
{
d = de->fnc(snd, len, snd_is_allocated);
}
catch (const char *error)
{
gprintf("%s", error);
}
catch (...)
{
}
2010-09-24 02:48:03 +02:00
if (d) return d;
}
return NULL;
}
/***************************************************************
*
* D E C O D E R <EFBFBD> T H R E A D
*
*
***************************************************************/
2010-09-24 02:48:03 +02:00
static GuiSound *GuiSoundPlayer[16] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL };
2010-09-24 02:48:03 +02:00
static lwp_t GuiSoundDecoderThreadHandle = LWP_THREAD_NULL;
static bool GuiSoundDecoderThreadRunning = false;
static bool GuiSoundDecoderDataRquested = false;
2010-09-24 02:48:03 +02:00
void *GuiSoundDecoderThread(void *args)
{
GuiSoundDecoderThreadRunning = true;
do
{
2010-09-24 02:48:03 +02:00
if (GuiSoundDecoderDataRquested)
{
GuiSoundDecoderDataRquested = false;
GuiSound **players = GuiSoundPlayer;
2010-09-24 02:48:03 +02:00
for (int i = 0; i < 16; ++i, ++players)
{
GuiSound *player = *players;
2010-09-24 02:48:03 +02:00
if (player) player->DecoderCallback();
}
}
2010-09-24 02:48:03 +02:00
if (!GuiSoundDecoderDataRquested) usleep(50);
} while (GuiSoundDecoderThreadRunning);
return 0;
}
/***************************************************************
*
* A S N D <EFBFBD> C A L L B A C K
*
*
***************************************************************/
2010-09-24 02:48:03 +02:00
void GuiSoundPlayerCallback(s32 Voice)
{
2010-09-24 02:48:03 +02:00
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)
*
***************************************************************/
2010-09-24 02:48:03 +02:00
class GuiSoundDecoderRAW: public GuiSoundDecoder
{
protected:
2010-09-24 02:48:03 +02:00
GuiSoundDecoderRAW(const u8 * snd, u32 len, bool snd_is_allocated)
{
2010-09-24 02:48:03 +02:00
pcm_start = snd;
is_allocated = snd_is_allocated;
pcm_end = pcm_start + len;
pos = pcm_start;
is_running = false;
}
public:
~GuiSoundDecoderRAW()
{
2010-09-24 02:48:03 +02:00
while (is_running)
usleep(50);
if (is_allocated) delete[] pcm_start;
}
2010-09-24 02:48:03 +02:00
static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated)
{
2010-09-24 02:48:03 +02:00
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
2010-09-24 02:48:03 +02:00
return: >0 = readed bytes;
0 = EOF;
<0 = Error;
*/
int Read(u8 * buffer, int buffer_size)
{
2010-09-24 02:48:03 +02:00
if (pos >= pcm_end) return 0; // EOF
is_running = true;
2010-09-24 02:48:03 +02:00
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:
2010-09-24 02:48:03 +02:00
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.
*/
2010-09-24 02:48:03 +02:00
GuiSound::GuiSound(const u8 *s, int l, int v/*=100*/, bool r/*=true*/, bool a/*=false*/)
{
2010-09-24 02:48:03 +02:00
if (GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL)
{
2010-09-24 02:48:03 +02:00
LWP_CreateThread(&GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32768, 80);
}
voice = -1;
2010-09-24 02:48:03 +02:00
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);
}
2010-09-24 02:48:03 +02:00
bool GuiSound::Load(const u8 *s, int l, bool r/*=false*/, bool a/*=false*/)
{
Stop();
2010-09-24 02:48:03 +02:00
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;
}
2010-09-24 02:48:03 +02:00
else if (a) delete[] s;
return false;
}
2010-09-24 02:48:03 +02:00
GuiSound::GuiSound(const char *p, int v/*=100*/)
{
2010-09-24 02:48:03 +02:00
if (GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL)
{
2010-09-24 02:48:03 +02:00
LWP_CreateThread(&GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32 * 1024, 80);
}
voice = -1;
2010-09-24 02:48:03 +02:00
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);
}
2010-09-24 02:48:03 +02:00
bool GuiSound::Load(const char *p)
{
2010-09-24 02:48:03 +02:00
Stop(); // stop playing
if (!play_buffer[0]) return false;
bool ret = false;
2010-09-24 02:48:03 +02:00
voice = -2; // -2 marks loading from file
u32 filesize = 0;
u8 *buffer = NULL;
size_t result;
2010-09-24 02:48:03 +02:00
FILE * pFile = fopen(p, "rb");
if (pFile)
{
// get file size:
2010-09-24 02:48:03 +02:00
fseek(pFile, 0, SEEK_END);
filesize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
// allocate memory to contain the whole file:
2010-09-24 02:48:03 +02:00
buffer = new (std::nothrow) u8[filesize];
if (buffer)
{
// copy the file into the buffer:
2010-09-24 02:48:03 +02:00
result = fread(buffer, 1, filesize, pFile);
if (result == filesize)
ret = Load(buffer, filesize, false, true);
else delete[] buffer;
}
2010-09-24 02:48:03 +02:00
fclose(pFile);
}
return ret;
}
/**
* Destructor for the GuiSound class.
*/
GuiSound::~GuiSound()
{
2010-09-24 02:48:03 +02:00
if (!loop) while (voice >= 0)
usleep(50);
Stop();
2010-09-24 02:48:03 +02:00
if (--GuiSoundCount == 0 && GuiSoundDecoderThreadHandle != LWP_THREAD_NULL)
{
GuiSoundDecoderThreadRunning = false;
2010-09-24 02:48:03 +02:00
LWP_JoinThread(GuiSoundDecoderThreadHandle, NULL);
GuiSoundDecoderThreadHandle = LWP_THREAD_NULL;
}
delete decoder;
2010-09-24 02:48:03 +02:00
free(play_buffer[0]);
}
void GuiSound::Play()
{
2010-09-24 02:48:03 +02:00
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
2010-09-24 02:48:03 +02:00
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();
2010-09-24 02:48:03 +02:00
if (voice >= 0)
{
2010-09-24 02:48:03 +02:00
s32 vol = (255 * volume) / 100;
s32 format = decoder->GetFormat();
s32 samplerate = decoder->GetSampleRate();
s32 first_pos = buffer_pos;
// switch to next buffer
2010-09-24 02:48:03 +02:00
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
2010-09-24 02:48:03 +02:00
ASND_SetVoice(voice, format, samplerate, 0, play_buffer[0], first_pos, vol, vol, GuiSoundPlayerCallback);
}
}
/*
2010-09-24 02:48:03 +02:00
int GuiSound::PlayOggFile(char * path)
{
if(Load(path))
Play();
return 1;
}
*/
void GuiSound::Stop()
{
2010-09-24 02:48:03 +02:00
if (voice < 0) return;
GuiSoundPlayer[voice] = NULL; // disable Callbacks
2010-09-24 02:48:03 +02:00
SND_StopVoice(voice);
voice = -1;
}
void GuiSound::Pause()
{
2010-09-24 02:48:03 +02:00
if (voice < 0) return;
ASND_PauseVoice(voice, 1);
}
void GuiSound::Resume()
{
2010-09-24 02:48:03 +02:00
if (voice < 0) return;
ASND_PauseVoice(voice, 0);
}
bool GuiSound::IsPlaying()
{
return voice >= 0;
}
2010-09-24 02:48:03 +02:00
void GuiSound::SetVolume(int vol)
{
volume = vol;
2010-09-24 02:48:03 +02:00
if (voice < 0) return;
int newvol = 255 * (volume / 100.0);
ASND_ChangeVolumeVoice(voice, newvol, newvol);
}
2010-09-24 02:48:03 +02:00
void GuiSound::SetLoop(bool l)
{
loop = l;
}
void GuiSound::DecoderCallback()
{
2010-09-24 02:48:03 +02:00
if (buffer_ready || buffer_eof) // if buffer ready or EOF -> nothing
return;
bool error = false;
2010-09-24 02:48:03 +02:00
while (buffer_pos < BUFFER_SIZE)
{
2010-09-24 02:48:03 +02:00
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
{
2010-09-24 02:48:03 +02:00
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;
}
2010-09-24 02:48:03 +02:00
else if (ret < 0) // an ERROR
{
2010-09-24 02:48:03 +02:00
if (buffer_pos)
break; // has data in buffer -> play the buffer
else if (loop)
{
2010-09-24 02:48:03 +02:00
if (!error) // if no prev error
{
2010-09-24 02:48:03 +02:00
decoder->Rewind(); // if loop -> rewind
error = true; // set error-state
continue; // and fill the buffer more
}
2010-09-24 02:48:03 +02:00
buffer_eof = true; // has prev error -> error in first block -> return EOF
return;
}
else
{
2010-09-24 02:48:03 +02:00
buffer_eof = true; // no loop -> return EOF
return;
}
}
2010-09-24 02:48:03 +02:00
error = false; // clear error-state
}
buffer_ready = true;
}
void GuiSound::PlayerCallback()
{
2010-09-24 02:48:03 +02:00
if (buffer_eof) // if EOF
{
2010-09-24 02:48:03 +02:00
if (ASND_TestPointer(voice, play_buffer[(buffer_nr + 2) % 3]) == 0) // test prev. Buffer
Stop();
}
2010-09-24 02:48:03 +02:00
else if (buffer_ready) // if buffer ready
{
2010-09-24 02:48:03 +02:00
if (ASND_AddVoice(voice, play_buffer[buffer_nr], buffer_pos) == SND_OK) // add buffer
{
// next buffer
2010-09-24 02:48:03 +02:00
buffer_nr = (buffer_nr + 1) % 3;
buffer_pos = 0;
buffer_ready = false;
2010-09-24 02:48:03 +02:00
buffer_eof = false;
}
}
}