2012-01-21 21:57:41 +01:00
|
|
|
/***************************************************************************
|
|
|
|
* 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>
|
2013-01-01 18:42:46 +01:00
|
|
|
#include <gccore.h>
|
2012-01-21 21:57:41 +01:00
|
|
|
#include "SoundHandler.hpp"
|
|
|
|
#include "Mp3Decoder.hpp"
|
|
|
|
#include "OggDecoder.hpp"
|
|
|
|
#include "WavDecoder.hpp"
|
|
|
|
#include "AifDecoder.hpp"
|
|
|
|
#include "BNSDecoder.hpp"
|
2012-12-08 17:17:35 +01:00
|
|
|
#include "gecko/gecko.hpp"
|
2012-08-05 15:48:15 +02:00
|
|
|
#include "memory/mem2.hpp"
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-10-15 21:16:14 +02:00
|
|
|
SoundHandler SoundHandle;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2014-03-17 18:38:32 +01:00
|
|
|
u8 SoundHandler::SoundStack[32768] ATTRIBUTE_ALIGN(32);
|
|
|
|
const u32 SoundHandler::SoundStackSize = 32768;
|
|
|
|
|
2012-10-15 21:16:14 +02:00
|
|
|
void SoundHandler::Init()
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
Decoding = false;
|
|
|
|
ExitRequested = false;
|
2012-01-21 21:57:41 +01:00
|
|
|
for(u32 i = 0; i < MAX_DECODERS; ++i)
|
2012-05-17 14:47:27 +02:00
|
|
|
DecoderList[i] = NULL;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2019-02-18 21:40:07 +01:00
|
|
|
LWP_CreateThread(&SoundThread, UpdateThread, this, SoundStack, SoundStackSize, 64);
|
2018-08-01 14:27:12 +02:00
|
|
|
gprintf("Running sound thread\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
2012-10-15 21:16:14 +02:00
|
|
|
void SoundHandler::Cleanup()
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2018-08-01 14:27:12 +02:00
|
|
|
gprintf("Stopping sound thread\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
ExitRequested = true;
|
|
|
|
ThreadSignal();
|
|
|
|
LWP_JoinThread(SoundThread, NULL);
|
|
|
|
SoundThread = LWP_THREAD_NULL;
|
|
|
|
|
|
|
|
ClearDecoderList();
|
2018-08-01 14:27:12 +02:00
|
|
|
gprintf("Stopped sound thread\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundHandler::AddDecoder(int voice, const char * filepath)
|
|
|
|
{
|
2012-05-16 23:52:14 +02:00
|
|
|
if(voice < 0 || voice >= MAX_DECODERS)
|
|
|
|
return;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-16 23:52:14 +02:00
|
|
|
if(DecoderList[voice] != NULL)
|
|
|
|
RemoveDecoder(voice);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-16 23:52:14 +02:00
|
|
|
DecoderList[voice] = GetSoundDecoder(filepath);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundHandler::AddDecoder(int voice, const u8 * snd, int len)
|
|
|
|
{
|
2012-05-16 23:52:14 +02:00
|
|
|
if(voice < 0 || voice >= MAX_DECODERS)
|
2012-07-27 19:26:49 +02:00
|
|
|
{
|
|
|
|
gprintf("SoundHandler: Invalid voice!\n");
|
2012-05-16 23:52:14 +02:00
|
|
|
return;
|
2012-07-27 19:26:49 +02:00
|
|
|
}
|
2012-05-16 23:52:14 +02:00
|
|
|
|
2012-07-27 19:26:49 +02:00
|
|
|
if(snd == NULL || len == 0)
|
|
|
|
{
|
|
|
|
gprintf("SoundHandler: Invalid sound!\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
return;
|
2012-07-27 19:26:49 +02:00
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-16 23:52:14 +02:00
|
|
|
if(DecoderList[voice] != NULL)
|
2012-07-27 19:26:49 +02:00
|
|
|
RemoveDecoder(voice);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-16 23:52:14 +02:00
|
|
|
DecoderList[voice] = GetSoundDecoder(snd, len);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundHandler::RemoveDecoder(int voice)
|
|
|
|
{
|
2012-05-16 23:52:14 +02:00
|
|
|
if(voice < 0 || voice >= MAX_DECODERS)
|
|
|
|
return;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-18 20:13:49 +02:00
|
|
|
if(DecoderList[voice] != NULL)
|
|
|
|
{
|
2012-05-17 02:43:42 +02:00
|
|
|
(*DecoderList[voice]).ClearBuffer();
|
2012-05-18 20:13:49 +02:00
|
|
|
if(DecoderList[voice]->GetSoundType() == SOUND_OGG)
|
|
|
|
delete((OggDecoder *)DecoderList[voice]);
|
|
|
|
else if(DecoderList[voice]->GetSoundType() == SOUND_MP3)
|
|
|
|
delete((Mp3Decoder *)DecoderList[voice]);
|
|
|
|
else if(DecoderList[voice]->GetSoundType() == SOUND_WAV)
|
|
|
|
delete((WavDecoder *)DecoderList[voice]);
|
|
|
|
else if(DecoderList[voice]->GetSoundType() == SOUND_AIF)
|
|
|
|
delete((AifDecoder *)DecoderList[voice]);
|
|
|
|
else if(DecoderList[voice]->GetSoundType() == SOUND_BNS)
|
|
|
|
delete((BNSDecoder *)DecoderList[voice]);
|
|
|
|
else
|
|
|
|
delete DecoderList[voice];
|
2012-05-16 23:52:14 +02:00
|
|
|
DecoderList[voice] = NULL;
|
2012-05-18 20:13:49 +02:00
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundHandler::ClearDecoderList()
|
|
|
|
{
|
2012-05-16 23:52:14 +02:00
|
|
|
for(u32 i = 0; i < MAX_DECODERS; ++i)
|
|
|
|
RemoveDecoder(i);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool CheckMP3Signature(const u8 * buffer)
|
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
const char MP3_Magic[][3] =
|
|
|
|
{
|
|
|
|
{'I', 'D', '3'}, //'ID3'
|
|
|
|
{0xff, 0xfe}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xff}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xfa}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xfb}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf2}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf3}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf4}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf5}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf6}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xf7}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xe2}, //'MPEG ADTS, layer III, v2.5 [protected]', 'mp3', 'audio/mpeg'),
|
|
|
|
{0xff, 0xe3}, //'MPEG ADTS, layer III, v2.5', 'mp3', 'audio/mpeg'),
|
|
|
|
};
|
|
|
|
|
|
|
|
if(buffer[0] == MP3_Magic[0][0] && buffer[1] == MP3_Magic[0][1] &&
|
|
|
|
buffer[2] == MP3_Magic[0][2])
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
for(int i = 1; i < 13; i++)
|
|
|
|
{
|
|
|
|
if(buffer[0] == MP3_Magic[i][0] && buffer[1] == MP3_Magic[i][1])
|
|
|
|
return true;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
return false;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SoundDecoder * SoundHandler::GetSoundDecoder(const char * filepath)
|
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
u32 magic;
|
|
|
|
CFile f(filepath, "rb");
|
|
|
|
if(f.size() == 0)
|
|
|
|
return NULL;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
f.read((u8 *) &magic, 1);
|
|
|
|
}
|
|
|
|
while(((u8 *) &magic)[0] == 0 && f.tell() < f.size());
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
if(f.tell() == f.size())
|
|
|
|
return NULL;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
f.seek(f.tell()-1, SEEK_SET);
|
|
|
|
f.read((u8 *) &magic, 4);
|
|
|
|
f.close();
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
/* gprintf("SHND: Searching decoder for magic\n");
|
|
|
|
ghexdump((u8 *) &magic, 4); */
|
|
|
|
|
2012-10-10 19:09:23 +02:00
|
|
|
if(memcmp(&magic, "OggS", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new OggDecoder(filepath);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic, "RIFF", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new WavDecoder(filepath);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic, "BNS ", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new BNSDecoder(filepath);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic, "FORM", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new AifDecoder(filepath);
|
|
|
|
else if(CheckMP3Signature((u8 *) &magic) == true)
|
|
|
|
return new Mp3Decoder(filepath);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
return new SoundDecoder(filepath);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SoundDecoder * SoundHandler::GetSoundDecoder(const u8 * sound, int length)
|
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
const u8 * check = sound;
|
|
|
|
int counter = 0;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
while(check[0] == 0 && counter < length)
|
|
|
|
{
|
|
|
|
check++;
|
|
|
|
counter++;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
if(counter >= length)
|
|
|
|
return NULL;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-10-10 19:09:23 +02:00
|
|
|
u32 * magic = (u32 *)check;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-10-10 19:09:23 +02:00
|
|
|
if(memcmp(&magic[0], "OggS", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new OggDecoder(sound, length);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic[0], "RIFF", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new WavDecoder(sound, length);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic[0], "BNS ", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new BNSDecoder(sound, length);
|
2012-10-10 19:09:23 +02:00
|
|
|
else if(memcmp(&magic[0], "FORM", 4) == 0)
|
2012-05-17 14:47:27 +02:00
|
|
|
return new AifDecoder(sound, length);
|
|
|
|
else if(CheckMP3Signature(check) == true)
|
|
|
|
return new Mp3Decoder(sound, length);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
return new SoundDecoder(sound, length);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void * SoundHandler::UpdateThread(void *arg)
|
|
|
|
{
|
|
|
|
((SoundHandler *) arg)->InternalSoundUpdates();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundHandler::InternalSoundUpdates()
|
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
u16 i = 0;
|
2012-01-21 21:57:41 +01:00
|
|
|
LWP_InitQueue(&ThreadQueue);
|
2012-05-17 14:47:27 +02:00
|
|
|
while(!ExitRequested)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-05-17 14:47:27 +02:00
|
|
|
LWP_ThreadSleep(ThreadQueue);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
for(i = 0; i < MAX_DECODERS; ++i)
|
|
|
|
{
|
|
|
|
if(DecoderList[i] == NULL)
|
|
|
|
continue;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-05-17 14:47:27 +02:00
|
|
|
Decoding = true;
|
|
|
|
DecoderList[i]->Decode();
|
|
|
|
}
|
|
|
|
Decoding = false;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
2012-05-17 14:47:27 +02:00
|
|
|
LWP_CloseQueue(ThreadQueue);
|
|
|
|
ThreadQueue = LWP_TQUEUE_NULL;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|