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