WiiFlow_Lite/source/music/gui_sound.cpp
fix94.1 706e18c6c0 -internal wii game and channel banner playback, still work in
progress, thanks alot to dimok and the whole usb loader gx team 
for help and the code for it!
To zoom the banner to fullscreen click the back button, to zoom out
the back button again, known bugs:
-some games fuck up the textures of buttons (e.g. wii play motion)
-if no wii game or channel was selected and you try to launch a game
or exit wiiflow it will most likely codedump
2012-07-03 16:26:49 +00:00

500 lines
8.8 KiB
C++

/***************************************************************************
* Copyright (C) 2010
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* for WiiXplorer 2010
***************************************************************************/
#include <unistd.h>
#include <string.h>
#include "SoundHandler.hpp"
#include "gui_sound.h"
#include "musicplayer.h"
#include "WavDecoder.hpp"
#include "loader/sys.h"
#define MAX_SND_VOICES 16
using namespace std;
static bool VoiceUsed[MAX_SND_VOICES] =
{
true, false, false, false, false, false,
false, false, false, false, false, false,
false, false, false, false
};
static inline int GetFirstUnusedVoice()
{
for(int i = 1; i < MAX_SND_VOICES; i++)
{
if(VoiceUsed[i] == false)
return i;
}
gprintf("ALL VOICES USED UP!!\n");
return -1;
}
extern "C" void SoundCallback(s32 voice)
{
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return;
if(decoder->IsBufferReady())
{
if(ASND_AddVoice(voice, decoder->GetBuffer(), decoder->GetBufferSize()) == SND_OK)
{
decoder->LoadNext();
SoundHandler::Instance()->ThreadSignal();
}
}
else if(decoder->IsEOF())
ASND_StopVoice(voice);
else
SoundHandler::Instance()->ThreadSignal();
}
GuiSound::GuiSound()
{
voice = -1;
Init();
}
GuiSound::GuiSound(string filepath, int v)
{
voice = v;
Init();
Load(filepath.c_str());
}
GuiSound::GuiSound(const u8 * snd, u32 len, string name, bool isallocated, int v)
{
voice = v;
Init();
Load(snd, len, isallocated);
this->filepath = name;
}
GuiSound::GuiSound(GuiSound *g)
{
voice = -1;
Init();
if (g == NULL)
return;
if (g->sound != NULL)
{
u8 *snd = (u8 *)MEM1_alloc(g->length);
memcpy(snd, g->sound, length);
Load(snd, g->length, true);
}
else
Load(g->filepath.c_str());
}
GuiSound::~GuiSound()
{
FreeMemory();
VoiceUsed[voice] = false;
}
void GuiSound::Init()
{
sound = NULL;
length = 0;
if (voice == -1)
voice = GetFirstUnusedVoice();
if(voice > 0)
VoiceUsed[voice] = true;
volume = 255;
SoundEffectLength = 0;
loop = false;
allocated = false;
}
void GuiSound::FreeMemory()
{
Stop();
// Prevent reinitialization of SoundHandler since we're exiting
if (!Sys_Exiting())
SoundHandler::Instance()->RemoveDecoder(voice);
if(allocated)
{
MEM1_free(sound);
allocated = false;
}
filepath = "";
SoundEffectLength = 0;
}
bool GuiSound::Load(const char * filepath)
{
FreeMemory();
if(!filepath || filepath[strlen(filepath)-1] == '/' || strlen(filepath) < 4)
return false;
FILE * f = fopen(filepath, "rb");
if(!f)
{
gprintf("Failed to load file %s!!\n", filepath);
return false;
}
u32 magic;
fread(&magic, 1, 4, f);
fclose(f);
SoundHandler::Instance()->AddDecoder(voice, filepath);
gprintf("Loading %s using voice %d\n", filepath, voice);
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
{
gprintf("No Decoder!!!\n");
return false;
}
if(!decoder->IsBufferReady())
{
gprintf("Buffer not ready!!\n");
SoundHandler::Instance()->RemoveDecoder(voice);
return false;
}
this->filepath = filepath;
SetLoop(loop);
return true;
}
bool GuiSound::Load(const u8 * snd, u32 len, bool isallocated)
{
FreeMemory();
this->voice = voice;
if(!snd)
return false;
if(!isallocated && *((u32 *) snd) == 'RIFF')
return LoadSoundEffect(snd, len);
if(*((u32 *) snd) == 'IMD5')
UncompressSoundbin(snd, len, isallocated);
else
{
sound = (u8 *) snd;
length = len;
allocated = isallocated;
}
SoundHandler::Instance()->AddDecoder(this->voice, sound, length);
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return false;
if(!decoder->IsBufferReady())
{
SoundHandler::Instance()->RemoveDecoder(voice);
return false;
}
SetLoop(loop);
return true;
}
bool GuiSound::LoadSoundEffect(const u8 * snd, u32 len)
{
FreeMemory();
WavDecoder decoder(snd, len);
decoder.Rewind();
u32 done = 0;
sound = (u8 *)MEM1_alloc(4096);
memset(sound, 0, 4096);
while(1)
{
u8 * tmpsnd = (u8 *)MEM1_realloc(sound, done+4096);
if(!tmpsnd)
{
MEM1_free(sound);
return false;
}
sound = tmpsnd;
int read = decoder.Read(sound+done, 4096, done);
if(read <= 0)
break;
done += read;
}
sound = (u8 *)MEM1_realloc(sound, done);
SoundEffectLength = done;
allocated = true;
return true;
}
void GuiSound::Play(int vol, bool restart)
{
if(SoundEffectLength > 0)
{
ASND_StopVoice(voice);
ASND_SetVoice(voice, VOICE_MONO_16BIT, 22050, 0, sound, SoundEffectLength, vol, vol, NULL);
return;
}
if(IsPlaying() && !restart)
return;
if(voice < 0 || voice >= 16)
return;
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return;
ASND_StopVoice(voice);
if(decoder->IsEOF())
{
decoder->ClearBuffer();
decoder->Rewind();
decoder->Decode();
}
u8 * curbuffer = decoder->GetBuffer();
int bufsize = decoder->GetBufferSize();
decoder->LoadNext();
SoundHandler::Instance()->ThreadSignal();
ASND_SetVoice(voice, decoder->GetFormat(), decoder->GetSampleRate(), 0, curbuffer, bufsize, vol, vol, SoundCallback);
}
void GuiSound::Play()
{
Play(volume);
}
void GuiSound::Stop()
{
if (!IsPlaying()) return;
if(voice < 0 || voice >= 16)
return;
ASND_StopVoice(voice);
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return;
decoder->ClearBuffer();
Rewind();
SoundHandler::Instance()->ThreadSignal();
}
void GuiSound::Pause()
{
if(voice < 0 || voice >= 16)
return;
ASND_StopVoice(voice);
}
void GuiSound::Resume()
{
Play();
}
bool GuiSound::IsPlaying()
{
if(voice < 0 || voice >= 16)
return false;
int result = ASND_StatusVoice(voice);
if(result == SND_WORKING || result == SND_WAITING)
return true;
return false;
}
int GuiSound::GetVolume()
{
return volume;
}
void GuiSound::SetVolume(int vol)
{
if(voice < 0 || voice >= 16)
return;
if(vol < 0)
return;
volume = vol;
ASND_ChangeVolumeVoice(voice, volume, volume);
}
void GuiSound::SetLoop(u8 l)
{
loop = l;
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return;
decoder->SetLoop(l == 1);
}
void GuiSound::Rewind()
{
SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice);
if(!decoder)
return;
decoder->Rewind();
}
struct _LZ77Info
{
u16 length : 4;
u16 offset : 12;
} __attribute__((packed));
typedef struct _LZ77Info LZ77Info;
u8 * uncompressLZ77(const u8 *inBuf, u32 inLength, u32 * size)
{
u8 *buffer = NULL;
if (inLength <= 0x8 || *((const u32 *)inBuf) != 0x4C5A3737 /*"LZ77"*/ || inBuf[4] != 0x10)
return NULL;
u32 uncSize = le32(((const u32 *)inBuf)[1] << 8);
if(uncSize <= 0) return 0;
const u8 *inBufEnd = inBuf + inLength;
inBuf += 8;
buffer = (u8 *)MEM1_alloc(uncSize);
if (!buffer)
return buffer;
u8 *bufCur = buffer;
u8 *bufEnd = buffer + uncSize;
while (bufCur < bufEnd && inBuf < inBufEnd)
{
u8 flags = *inBuf;
++inBuf;
int i = 0;
for (i = 0; i < 8 && bufCur < bufEnd && inBuf < inBufEnd; ++i)
{
if ((flags & 0x80) != 0)
{
const LZ77Info * info = (const LZ77Info *)inBuf;
inBuf += sizeof (LZ77Info);
int length = info->length + 3;
if (bufCur - info->offset - 1 < buffer || bufCur + length > bufEnd)
return buffer;
memcpy(bufCur, bufCur - info->offset - 1, length);
bufCur += length;
}
else
{
*bufCur = *inBuf;
++inBuf;
++bufCur;
}
flags <<= 1;
}
}
*size = uncSize;
return buffer;
}
void GuiSound::UncompressSoundbin(const u8 * snd, u32 len, bool isallocated)
{
const u8 * file = snd+32;
length = len-32;
if (length <= 0)
return;
if(*((u32 *) file) == 'LZ77')
{
u32 size = 0;
sound = uncompressLZ77(file, length, &size);
if (!sound)
{
length = 0;
return;
}
length = size;
}
else
{
sound = (u8 *)MEM1_alloc(length);
if (!sound)
{
length = 0;
return;
}
memcpy(sound, file, length);
}
if(isallocated)
{
void *p = (void *)snd;
free(p);
}
allocated = true;
}
void soundInit(void)
{
ASND_Init();
ASND_Pause(0);
}
void soundDeinit(void)
{
ASND_Pause(1);
ASND_End();
}