usbloadergx/source/libwiigui/gui_sound_decoder_wav.cpp

219 lines
6.7 KiB
C++

/****************************************************************************
* 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"
typedef struct {
u32 cueID;
u32 len;
u32 loops;
}plst_t;
typedef struct {
const u8 *start;
const u8 *end;
u32 loops;
}playlist_t;
class GuiSoundDecoderWAV : public GuiSoundDecoder {
protected:
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();
}
public:
~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;
}
private:
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;
};
REGISTER_GUI_SOUND_DECODER(GuiSoundDecoderWAV);