commit d0f8e7ece417864f414a0d44c875a9f4da7058f7 parent a4dfd4026db9e82618357b40643214035d4d18ac Author: Michael Savage <mikejsavage@gmail.com> Date: Wed Dec 28 21:15:33 +0200 Make the wave decoder use a ReadStream Diffstat:
audio.cc | | | 7 | ++++--- |
wave.cc | | | 134 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
wave.h | | | 2 | +- |
diff --git a/audio.cc b/audio.cc @@ -37,10 +37,11 @@ int main( int argc, char ** argv ) { AudioOutputDevice output_device; audio_output_open( &output_device, mixer_mix ); - u8 * wave = file_get_contents( "02 - Unbreakable.wav" ); + size_t wave_len; + u8 * wave = file_get_contents( "02 - Unbreakable.wav", &wave_len ); SoundData sound; - bool ok = wave_decode( &arena, wave, &sound ); - assert( ok ); + bool ok = wave_decode( &arena, wave, wave_len, &sound ); + ASSERT( ok ); // mixer_play( make_sin_wave( sound.sample_rate, 200 ), MIXER_LOOP ); diff --git a/wave.cc b/wave.cc @@ -1,97 +1,111 @@ #include "intrinsics.h" #include "memory_arena.h" #include "assets.h" +#include "stream.h" +#include "log.h" #include "wave.h" // TODO: this only works on a little endian machine #define WAVEID( a, b, c, d ) ( d << 24 ) | ( c << 16 ) | ( b << 8 ) | a -enum WaveID { - WAVEID_RIFF = WAVEID( 'R', 'I', 'F', 'F' ), - WAVEID_WAVE = WAVEID( 'W', 'A', 'V', 'E' ), - WAVEID_fmt = WAVEID( 'f', 'm', 't', ' ' ), - WAVEID_data = WAVEID( 'd', 'a', 't', 'a' ), -}; - -enum WaveFormat { - WAVEFORMAT_PCM = 1, -}; - -struct WaveHeader { - u32 id_riff; - u32 length; - u32 id_wave; -}; - -struct WaveChunk { - u32 id; - u32 length; -}; - -struct WaveFMT { - u32 id; - u32 length; - - u16 format; - u16 num_channels; - u32 sample_rate; - u32 data_rate; - u16 block_align; - u16 bits_per_sample; -}; - -static bool wave_supported_format( const WaveFMT * fmt ) { - if( fmt->format != WAVEFORMAT_PCM ) return false; - if( fmt->num_channels != 1 && fmt->num_channels != 2 ) return false; - if( fmt->bits_per_sample != 16 ) return false; +static const u32 WAVEID_RIFF = WAVEID( 'R', 'I', 'F', 'F' ); +static const u32 WAVEID_WAVE = WAVEID( 'W', 'A', 'V', 'E' ); +static const u32 WAVEID_fmt = WAVEID( 'f', 'm', 't', ' ' ); +static const u32 WAVEID_data = WAVEID( 'd', 'a', 't', 'a' ); + +static const u32 WAVEFORMAT_PCM = 1; + +static bool supported_format( u16 format, u16 num_channels, u16 bits_per_sample ) { + if( format != WAVEFORMAT_PCM ) return false; + if( num_channels != 1 && num_channels != 2 ) return false; + if( bits_per_sample != 16 ) return false; return true; } -bool wave_decode( MemoryArena * arena, u8 * data, SoundData * sound ) { - WaveHeader * header = ( WaveHeader * ) data; +bool wave_decode( MemoryArena * arena, u8 * data, size_t data_len, SoundData * sound ) { + ReadStream r( data, data_len ); - if( header->id_riff != WAVEID_RIFF || header->id_wave != WAVEID_WAVE ) { - printf( "bad header\n" ); + // decode wave header + u32 id_riff = read_u32( &r ); + u32 length = read_u32( &r ); + u32 id_wave = read_u32( &r ); + + if( !r.ok ) { + WARN( "stream error" ); + return false; + } + if( id_riff != WAVEID_RIFF || id_wave != WAVEID_WAVE ) { + WARN( "not a WAVE file" ); + return false; + } + if( length != data_len - 2 * sizeof( u32 ) ) { + WARN( "WAVE length and data_len don't match up" ); return false; } - u32 pos = sizeof( WaveHeader ); u32 data_length = 0; sound->samples = NULL; - while( pos < header->length ) { - WaveChunk * chunk = ( WaveChunk * ) ( data + pos ); + while( r.ok && !r.done() ) { + u32 chunk_id = read_u32( &r ); + u32 chunk_length = read_u32( &r ); - switch( chunk->id ) { + switch( chunk_id ) { case WAVEID_fmt: { - if( chunk->length != 16 ) return false; - - WaveFMT * fmt = ( WaveFMT * ) chunk; - if( !wave_supported_format( fmt ) ) return false; - - sound->num_channels = fmt->num_channels; - sound->sample_rate = fmt->sample_rate; - + if( chunk_length != 16 ) { + WARN( "invalid WAVE format chunk" ); + return false; + } + + u16 format = read_u16( &r ); + u16 num_channels = read_u16( &r ); + u32 sample_rate = read_u32( &r ); + /* u32 data_rate = */ read_u32( &r ); + /* u16 block_align = */ read_u16( &r ); + u16 bits_per_sample = read_u16( &r ); + + if( !r.ok ) { + WARN( "stream error" ); + return false; + } + if( !supported_format( format, num_channels, bits_per_sample ) ) { + WARN( "unsupported WAVE format" ); + return false; + } + + sound->num_channels = num_channels; + sound->sample_rate = sample_rate; } break; case WAVEID_data: { - sound->samples = ( s16 * ) ( chunk + 1 ); - data_length = chunk->length; + if( data_length != 0 ) { + WARN( "WAVE contains more than one data section" ); + return false; + } + + // TODO: this should really copy data out, if + // only to make sure the samples are aligned + sound->samples = ( s16 * ) r.cursor; + data_length = chunk_length; + for( u32 i = 0; i < align2( chunk_length ); i++ ) { + read_u8( &r ); + } } break; default: { - printf( "wtf is this\n" ); + WARN( "unrecognised WAVE chunk: %u", chunk_id ); return false; } } - - pos += sizeof( WaveChunk ) + align2( chunk->length ); } - if( sound->samples == NULL ) return false; + if( sound->samples == NULL ) { + WARN( "WAVE contains no data section" ); + return false; + } sound->num_samples = data_length / ( sound->num_channels * sizeof( s16 ) ); diff --git a/wave.h b/wave.h @@ -5,6 +5,6 @@ #include "memory_arena.h" #include "assets.h" -bool wave_decode( MemoryArena * arena, u8 * data, SoundData * sound ); +bool wave_decode( MemoryArena * arena, u8 * data, size_t len, SoundData * sound ); #endif // _WAVE_H_