2009-11-10 00:03:13 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* libwiigui
|
|
|
|
*
|
|
|
|
* Tantric 2009
|
|
|
|
*
|
|
|
|
* gui_sound_plugin_wav.cpp
|
|
|
|
*
|
|
|
|
* by ardi 2009
|
|
|
|
*
|
|
|
|
* Decoder for WAVE PCM
|
|
|
|
*
|
|
|
|
* GUI class definitions
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <asndlib.h>
|
|
|
|
#include <map>
|
|
|
|
#include <vector>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "gui_sound_decoder.h"
|
|
|
|
|
2010-02-09 11:59:55 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
u32 cueID;
|
|
|
|
u32 len;
|
|
|
|
u32 loops;
|
2009-11-10 00:03:13 +01:00
|
|
|
}plst_t;
|
2010-02-09 11:59:55 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
const u8 *start;
|
|
|
|
const u8 *end;
|
|
|
|
u32 loops;
|
2009-11-10 00:03:13 +01:00
|
|
|
}playlist_t;
|
2009-11-10 23:03:52 +01:00
|
|
|
|
2010-02-09 11:59:55 +01:00
|
|
|
class GuiSoundDecoderWAV : public GuiSoundDecoder
|
|
|
|
{
|
2009-11-10 00:03:13 +01:00
|
|
|
protected:
|
2010-02-09 11:59:55 +01:00
|
|
|
GuiSoundDecoderWAV(const u8 * snd, u32 len, bool snd_is_allocated)
|
|
|
|
{
|
|
|
|
sound = snd;
|
|
|
|
is_running = false;
|
|
|
|
is_allocated = snd_is_allocated;
|
|
|
|
|
|
|
|
const u8 *in_ptr = sound;
|
|
|
|
|
|
|
|
if(be32inc(in_ptr) != 0x52494646 /*'RIFF' (WAV)*/) throw("Not a WAV");
|
|
|
|
|
|
|
|
u32 riffsize = le32inc(in_ptr);
|
|
|
|
if(riffsize > (len-8)) throw("Wrong size");
|
|
|
|
|
|
|
|
if(be32inc(in_ptr) != 0x57415645 /*'WAVE'*/) throw("No WAVE-Tag");
|
|
|
|
|
|
|
|
if(be32inc(in_ptr) != 0x666D7420 /*'fmt '*/) throw("No fmt-Tag");
|
|
|
|
|
|
|
|
u32 fmtLen = le32inc(in_ptr);
|
|
|
|
|
|
|
|
if(le16inc(in_ptr) != 1) throw("Not PCM data");
|
|
|
|
|
|
|
|
channelCount = le16inc(in_ptr);
|
|
|
|
if(channelCount < 1 || channelCount > 2) throw("only mono or stereo");
|
|
|
|
|
|
|
|
sampleRate = le32inc(in_ptr);
|
|
|
|
|
|
|
|
in_ptr += 6; // skip <bytes/second> and <block align>
|
|
|
|
|
|
|
|
bytePerSample = (le16inc(in_ptr)+7)/8;
|
|
|
|
if(bytePerSample < 1 || bytePerSample > 2) throw("only 1-16 bit/Sample");
|
|
|
|
|
|
|
|
in_ptr += fmtLen-16;
|
|
|
|
|
|
|
|
if(be32inc(in_ptr) != 0x64617461 /*'data'*/) throw("No data-Tag");
|
|
|
|
|
|
|
|
|
|
|
|
soundDataStart = in_ptr+4;
|
|
|
|
soundDataEnd = soundDataStart + le32(in_ptr);
|
|
|
|
|
|
|
|
in_ptr = soundDataEnd;
|
|
|
|
|
|
|
|
std::map<u32, u32> cue;
|
|
|
|
std::vector<plst_t>plst;
|
|
|
|
|
|
|
|
if(((u32)in_ptr) & 0x0001UL) in_ptr++;
|
|
|
|
while((in_ptr+4) < (sound + riffsize))
|
|
|
|
{
|
|
|
|
u32 tag = be32inc(in_ptr);
|
|
|
|
switch(tag)
|
|
|
|
{
|
|
|
|
case 0x63756520 /*'cue '*/:
|
|
|
|
in_ptr += 4; // skip size
|
|
|
|
for(u32 count = le32inc(in_ptr); count>0; count--)
|
|
|
|
{
|
|
|
|
u32 ID = be32inc(in_ptr);
|
|
|
|
in_ptr += 4; // skip dwPosition
|
|
|
|
if(be32inc(in_ptr) == 0x64617461 /*'data'*/)
|
|
|
|
{
|
|
|
|
in_ptr += 8; // skip chunkStart - dwBlockStart
|
|
|
|
cue[ID] = le32inc(in_ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
in_ptr += 12; // skip chunkStart - SammpleOffset
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x706C7374 /*' plst'*/:
|
|
|
|
in_ptr += 4; // skip size
|
|
|
|
for(u32 count = le32inc(in_ptr); count>0; count--)
|
|
|
|
plst.push_back((plst_t){le32inc(in_ptr), le32inc(in_ptr), le32inc(in_ptr)});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
in_ptr -= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(std::vector<plst_t>::iterator i = plst.begin(); i != plst.end(); ++i)
|
|
|
|
{
|
|
|
|
const u8 *start = soundDataStart + cue[i->cueID];
|
|
|
|
const u8 *end = soundDataStart + (i->len*bytePerSample*channelCount);
|
|
|
|
u32 loops = i->loops;
|
|
|
|
playlist.push_back((playlist_t){start,end,loops});
|
|
|
|
}
|
|
|
|
if(playlist.size() == 0)
|
|
|
|
{
|
|
|
|
playlist.push_back((playlist_t){soundDataStart, soundDataEnd, 1});
|
|
|
|
}
|
|
|
|
Rewind();
|
|
|
|
}
|
2009-11-10 00:03:13 +01:00
|
|
|
public:
|
2010-02-09 11:59:55 +01:00
|
|
|
~GuiSoundDecoderWAV()
|
|
|
|
{
|
|
|
|
while(is_running) usleep(50);
|
|
|
|
if(is_allocated) delete [] sound;
|
|
|
|
}
|
|
|
|
static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated)
|
|
|
|
{
|
|
|
|
if(snd && len>4 && snd[0]=='R' && snd[1]=='I' && snd[2]=='F' && snd[3]=='F'
|
|
|
|
&& snd[8]=='W' && snd[9]=='A' && snd[10]=='V' && snd[11]=='E')
|
|
|
|
return new GuiSoundDecoderWAV(snd, len, snd_is_allocated);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
s32 GetFormat()
|
|
|
|
{
|
|
|
|
if(bytePerSample == 2)
|
|
|
|
return channelCount==2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT;
|
|
|
|
else
|
|
|
|
return channelCount==2 ? VOICE_STEREO_8BIT : VOICE_MONO_8BIT;
|
|
|
|
}
|
|
|
|
s32 GetSampleRate()
|
|
|
|
{
|
|
|
|
return sampleRate;
|
|
|
|
}
|
|
|
|
/* Read reads data from stream to buffer
|
|
|
|
return: >0 = readed bytes;
|
|
|
|
0 = EOF;
|
|
|
|
<0 = Error;
|
|
|
|
*/
|
|
|
|
int Read(u8 * buffer, int buffer_size)
|
|
|
|
{
|
|
|
|
is_running = true;
|
|
|
|
u8 *write_pos = buffer;
|
|
|
|
u8 *write_end = buffer+buffer_size;
|
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
while(currentPos < currentEnd)
|
|
|
|
{
|
|
|
|
if(write_pos >= write_end)
|
|
|
|
{
|
|
|
|
is_running = false;
|
|
|
|
return write_pos-buffer;
|
|
|
|
}
|
|
|
|
if(bytePerSample == 2)
|
|
|
|
{
|
|
|
|
*((s16*)write_pos) = le16inc(currentPos);
|
|
|
|
write_pos+=2;
|
|
|
|
if(channelCount==2) // stereo
|
|
|
|
{
|
|
|
|
*((s16*)write_pos) = le16inc(currentPos);
|
|
|
|
write_pos+=2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*write_pos++ = *currentPos++;
|
|
|
|
if(channelCount==2) // stereo
|
|
|
|
*write_pos++ = *currentPos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(currentLoops>1)
|
|
|
|
{
|
|
|
|
currentLoops--;
|
|
|
|
currentPos = currentStart;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(currentPlaylist != playlist.end())
|
|
|
|
currentPlaylist++;
|
|
|
|
if(currentPlaylist != playlist.end())
|
|
|
|
{
|
|
|
|
currentStart = currentPos = currentPlaylist->start;
|
|
|
|
currentEnd = currentPlaylist->end;
|
|
|
|
currentLoops = currentPlaylist->loops;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
is_running = false;
|
|
|
|
return write_pos-buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int Rewind()
|
|
|
|
{
|
|
|
|
currentPlaylist = playlist.begin();
|
|
|
|
currentStart = currentPos = currentPlaylist->start;
|
|
|
|
currentEnd = currentPlaylist->end;
|
|
|
|
currentLoops = currentPlaylist->loops;
|
|
|
|
return 0;
|
|
|
|
}
|
2009-11-10 00:03:13 +01:00
|
|
|
private:
|
2010-02-09 11:59:55 +01:00
|
|
|
const u8 *sound;
|
|
|
|
bool is_allocated;
|
|
|
|
bool is_running;
|
|
|
|
|
|
|
|
u16 channelCount;
|
|
|
|
u32 sampleRate;
|
|
|
|
u16 bytePerSample;
|
|
|
|
const u8 *soundDataStart;
|
|
|
|
const u8 *soundDataEnd;
|
|
|
|
std::vector<playlist_t> playlist;
|
|
|
|
std::vector<playlist_t>::iterator currentPlaylist;
|
|
|
|
const u8 *currentStart;
|
|
|
|
const u8 *currentEnd;
|
|
|
|
u32 currentLoops;
|
|
|
|
const u8 *currentPos;
|
|
|
|
|
2009-11-10 00:03:13 +01:00
|
|
|
};
|
|
|
|
REGISTER_GUI_SOUND_DECODER(GuiSoundDecoderWAV);
|