/**************************************************************************** * 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::vector 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::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 );