/**************************************************************************** * libwiigui * * Tantric 2009 * * gui_sound_plugin_wav.cpp * * by ardi 2009 * * Decoder for WAVE PCM * * GUI class definitions ***************************************************************************/ #include #include #include #include #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 and 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 cue; std::vectorplst; 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::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; std::vector::iterator currentPlaylist; const u8 *currentStart; const u8 *currentEnd; u32 currentLoops; const u8 *currentPos; }; REGISTER_GUI_SOUND_DECODER(GuiSoundDecoderWAV);