2009-11-10 00:03:13 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* libwiigui
|
|
|
|
*
|
|
|
|
* Tantric 2009
|
|
|
|
*
|
|
|
|
* gui_sound_plugin_bns.cpp
|
|
|
|
*
|
|
|
|
* by ardi 2009
|
|
|
|
*
|
|
|
|
* Decoder for Wii bns-sound
|
|
|
|
*
|
|
|
|
* GUI class definitions
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <asndlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "gui_sound_decoder.h"
|
|
|
|
#define BIG_ENDIAN_HOST 1
|
|
|
|
|
2010-02-09 11:59:55 +01:00
|
|
|
class chanel_t
|
|
|
|
{
|
2010-09-19 01:16:05 +02:00
|
|
|
public:
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
currentPos = startPos;
|
|
|
|
hist1 = hist2 = 0;
|
|
|
|
}
|
|
|
|
int DecodeNextBlock()
|
|
|
|
{
|
|
|
|
int Offset = 0;
|
2010-09-24 02:48:03 +02:00
|
|
|
if (currentPos == loopStart)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
loop_hist1 = hist1;
|
|
|
|
loop_hist2 = hist2;
|
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
if (loop && currentPos >= endPos)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
currentPos = loopStart;
|
2010-09-24 02:48:03 +02:00
|
|
|
hist1 = loop_hist1;
|
|
|
|
hist2 = loop_hist2;
|
|
|
|
Offset = loopOffset;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
if (currentPos + 8 <= endPos)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
u16 index = (*currentPos >> 4) & 0x07;
|
|
|
|
s32 scale = 1 << (*currentPos++ & 0x0F);
|
|
|
|
for (int i = 0; i < 14; i += 2)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
nibbles[i] = ((s8) *currentPos) >> 4;
|
|
|
|
nibbles[i + 1] = ((s8) ((*currentPos++) << 4)) >> 4;
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
for (int i = 0; i < 14; ++i)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
s32 sample = (scale * nibbles[i]) << 11;
|
2010-09-19 01:16:05 +02:00
|
|
|
sample += coEfficients[index * 2] * hist1;
|
|
|
|
sample += coEfficients[index * 2 + 1] * hist2;
|
|
|
|
sample += 1024;
|
|
|
|
sample = sample >> 11;
|
2010-09-24 02:48:03 +02:00
|
|
|
if (sample > 32767)
|
2010-09-19 01:16:05 +02:00
|
|
|
sample = 32767;
|
2010-09-24 02:48:03 +02:00
|
|
|
else if (sample < -32768) sample = -32768;
|
2010-09-19 01:16:05 +02:00
|
|
|
pcm[i] = sample;
|
|
|
|
|
|
|
|
hist2 = hist1;
|
|
|
|
hist1 = sample;
|
|
|
|
}
|
|
|
|
return Offset;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
const u8* startPos;
|
|
|
|
const u8* endPos;
|
|
|
|
const u8* currentPos;
|
|
|
|
s16 coEfficients[16];
|
|
|
|
s16 nibbles[14];
|
|
|
|
s16 pcm[14];
|
|
|
|
s16 hist1;
|
|
|
|
s16 hist2;
|
|
|
|
bool loop;
|
|
|
|
const u8* loopStart;
|
|
|
|
u16 loopOffset;
|
|
|
|
s16 loop_hist1;
|
|
|
|
s16 loop_hist2;
|
2009-11-10 00:03:13 +01:00
|
|
|
};
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
class GuiSoundDecoderBNS: public GuiSoundDecoder
|
2010-02-09 11:59:55 +01:00
|
|
|
{
|
2010-09-19 01:16:05 +02:00
|
|
|
protected:
|
2010-09-24 02:48:03 +02:00
|
|
|
GuiSoundDecoderBNS(const u8 * snd, u32 len, bool snd_is_allocated)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
sound = snd;
|
|
|
|
is_running = false;
|
|
|
|
is_allocated = snd_is_allocated;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
const u8 *in_ptr = sound;
|
|
|
|
|
|
|
|
/////////////////
|
|
|
|
// READ HEADER //
|
|
|
|
/////////////////
|
2010-09-24 02:48:03 +02:00
|
|
|
if (be32inc( in_ptr ) != 0x424E5320 /*'BNS '*/) throw("Not a BNS");
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 4; // skip 4 byte
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
u32 bnssize = be32inc( in_ptr );
|
2010-09-24 02:48:03 +02:00
|
|
|
if (bnssize != len) throw("Wrong size");
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 4; // skip unknown1
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
const u8* infoStart = sound + be32inc( in_ptr );
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 4; // skip const u8* infoEnd = infoStart + be32inc(in_ptr);
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
channel[0].startPos = sound + be32inc( in_ptr ) + 8;
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[0].endPos = channel[0].startPos + be32inc( in_ptr ) - 8;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
///////////////
|
|
|
|
// READ INFO //
|
|
|
|
///////////////
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr = infoStart + 8; // skip 'INFO' and Infosize
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr++; // skip u8 codeType = *in_ptr++;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
channel[0].loop = channel[1].loop = *in_ptr++; // u8 loopFlag;
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
channelCount = *in_ptr++;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr++; // skip unknown byte
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
sampleRate = be16inc( in_ptr );
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 2; // skip unknown word
|
2010-09-19 01:16:05 +02:00
|
|
|
|
|
|
|
u32 loopStart = be32inc( in_ptr );
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[0].loopStart = channel[0].startPos + ((loopStart / 14) * 8);//LoopPos to BlockStart
|
|
|
|
channel[1].loopStart = channel[1].startPos + ((loopStart / 14) * 8);
|
|
|
|
channel[0].loopOffset = channel[1].loopOffset = loopStart % 14;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 4; // skip u32 SampleCount = be32inc(in_ptr);
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 24; // skip unknown Bytes
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
if (channelCount == 2)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 4; // skip unknown long
|
2010-09-19 01:16:05 +02:00
|
|
|
u32 ChannelSplit = be32inc( in_ptr );
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 8; // skip 2x unknown long
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[1].endPos = channel[0].endPos;
|
|
|
|
channel[0].endPos = channel[1].startPos = channel[0].startPos + ChannelSplit;
|
2010-09-19 01:16:05 +02:00
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[1].loopStart = channel[1].startPos + (channel[0].loopStart - channel[0].startPos);
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
for (int a = 0; a < 16; a++)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[0].coEfficients[a] = (s16) be16inc( in_ptr );
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
if (channelCount == 2)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
in_ptr += 16; // skip 16 byte
|
|
|
|
for (int a = 0; a < 16; a++)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
channel[1].coEfficients[a] = (s16) be16inc( in_ptr );
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
channel[0].Reset();
|
|
|
|
channel[1].Reset();
|
|
|
|
currentBlockPos = 14;
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
~GuiSoundDecoderBNS()
|
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
while (is_running)
|
|
|
|
usleep(50);
|
|
|
|
if (is_allocated) delete[] sound;
|
2010-09-19 01:16:05 +02:00
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
if (snd && len > 4 && snd[0] == 'B' && snd[1] == 'N' && snd[2] == 'S' && snd[3] == ' ') return new GuiSoundDecoderBNS(
|
|
|
|
snd, len, snd_is_allocated);
|
2010-09-19 01:16:05 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
s32 GetFormat()
|
|
|
|
{
|
|
|
|
return channelCount == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT;
|
|
|
|
}
|
|
|
|
s32 GetSampleRate()
|
|
|
|
{
|
|
|
|
return sampleRate;
|
|
|
|
}
|
|
|
|
/* 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-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
is_running = true;
|
|
|
|
u8 *write_pos = buffer;
|
|
|
|
u8 *write_end = buffer + buffer_size;
|
|
|
|
|
2010-09-24 02:48:03 +02:00
|
|
|
for (;;)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
if (currentBlockPos >= 14)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
int Offset = channel[0].DecodeNextBlock();
|
2010-09-24 02:48:03 +02:00
|
|
|
if (Offset < 0 || (channelCount == 2 && channel[1].DecodeNextBlock() < 0))
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
is_running = false;
|
|
|
|
return write_pos - buffer;
|
|
|
|
}
|
|
|
|
currentBlockPos = Offset;
|
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
for (; currentBlockPos < 14; ++currentBlockPos)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
if (write_pos >= write_end)
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
|
|
|
is_running = false;
|
|
|
|
return write_pos - buffer;
|
|
|
|
}
|
2010-09-24 02:48:03 +02:00
|
|
|
*((s16*) write_pos) = channel[0].pcm[currentBlockPos];
|
2010-09-19 01:16:05 +02:00
|
|
|
write_pos += 2;
|
2010-09-24 02:48:03 +02:00
|
|
|
if (channelCount == 2) // stereo
|
2010-09-19 01:16:05 +02:00
|
|
|
{
|
2010-09-24 02:48:03 +02:00
|
|
|
*((s16*) write_pos) = channel[1].pcm[currentBlockPos];
|
2010-09-19 01:16:05 +02:00
|
|
|
write_pos += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int Rewind()
|
|
|
|
{
|
|
|
|
channel[0].Reset();
|
|
|
|
channel[1].Reset();
|
|
|
|
currentBlockPos = 14;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
private:
|
2010-09-24 02:48:03 +02:00
|
|
|
const u8 *sound;
|
|
|
|
bool is_allocated;
|
|
|
|
bool is_running;
|
|
|
|
chanel_t channel[2];
|
|
|
|
u16 currentBlockPos;
|
|
|
|
u16 channelCount;
|
|
|
|
u32 sampleRate;
|
|
|
|
// u16 loopOffset;
|
|
|
|
// u16 bytePerSample;
|
|
|
|
// const u8 *soundDataStart;
|
|
|
|
// const u8 *soundDataEnd;
|
|
|
|
// u32 soundDataLen;
|
2009-11-10 00:03:13 +01:00
|
|
|
};
|
2010-09-19 01:16:05 +02:00
|
|
|
REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderBNS );
|