medfall

A super great game engine
Log | Files | Refs

wave.cc (3110B)


      1 #include "intrinsics.h"
      2 #include "memory_arena.h"
      3 #include "assets.h"
      4 #include "stream.h"
      5 #include "log.h"
      6 #include "wave.h"
      7 
      8 // TODO: this only works on a little endian machine
      9 
     10 #define WAVEID( a, b, c, d ) ( d << 24 ) | ( c << 16 ) | ( b << 8 ) | a
     11 
     12 static const u32 WAVEID_RIFF = WAVEID( 'R', 'I', 'F', 'F' );
     13 static const u32 WAVEID_WAVE = WAVEID( 'W', 'A', 'V', 'E' );
     14 static const u32 WAVEID_fmt = WAVEID( 'f', 'm', 't', ' ' );
     15 static const u32 WAVEID_data = WAVEID( 'd', 'a', 't', 'a' );
     16 
     17 static const u32 WAVEFORMAT_PCM = 1;
     18 
     19 static bool supported_format( u16 format, u16 num_channels, u16 bits_per_sample ) {
     20 	if( format != WAVEFORMAT_PCM ) return false;
     21 	if( num_channels != 1 && num_channels != 2 ) return false;
     22 	if( bits_per_sample != 16 ) return false;
     23 
     24 	return true;
     25 }
     26 
     27 bool wave_decode( MemoryArena * arena, u8 * data, size_t data_len, SoundData * sound ) {
     28 	ReadStream r( data, data_len );
     29 
     30 	// decode wave header
     31 	u32 id_riff = read_u32( &r );
     32 	u32 length = read_u32( &r );
     33 	u32 id_wave = read_u32( &r );
     34 
     35 	if( !r.ok ) {
     36 		WARN( "stream error" );
     37 		return false;
     38 	}
     39 	if( id_riff != WAVEID_RIFF || id_wave != WAVEID_WAVE ) {
     40 		WARN( "not a WAVE file" );
     41 		return false;
     42 	}
     43 	if( length != data_len - 2 * sizeof( u32 ) ) {
     44 		WARN( "WAVE length and data_len don't match up" );
     45 		return false;
     46 	}
     47 
     48 	u32 data_length = 0;
     49 
     50 	sound->samples = NULL;
     51 
     52 	while( r.ok && !r.done() ) {
     53 		u32 chunk_id = read_u32( &r );
     54 		u32 chunk_length = read_u32( &r );
     55 
     56 		switch( chunk_id ) {
     57 			case WAVEID_fmt: {
     58 				if( chunk_length != 16 ) {
     59 					WARN( "invalid WAVE format chunk" );
     60 					return false;
     61 				}
     62 
     63 				u16 format = read_u16( &r );
     64 				u16 num_channels = read_u16( &r );
     65 				u32 sample_rate = read_u32( &r );
     66 				/* u32 data_rate = */ read_u32( &r );
     67 				/* u16 block_align = */ read_u16( &r );
     68 				u16 bits_per_sample = read_u16( &r );
     69 
     70 				if( !r.ok ) {
     71 					WARN( "stream error" );
     72 					return false;
     73 				}
     74 				if( !supported_format( format, num_channels, bits_per_sample ) ) {
     75 					WARN( "unsupported WAVE format" );
     76 					return false;
     77 				}
     78 
     79 				sound->num_channels = num_channels;
     80 				sound->sample_rate = sample_rate;
     81 			} break;
     82 
     83 			case WAVEID_data: {
     84 				if( data_length != 0 ) {
     85 					WARN( "WAVE contains more than one data section" );
     86 					return false;
     87 				}
     88 
     89 				sound->samples = r.nocopy< s16 >( chunk_length );
     90 				data_length = chunk_length;
     91 			} break;
     92 
     93 			default: {
     94 				WARN( "unrecognised WAVE chunk: {}", chunk_id );
     95 				return false;
     96 			}
     97 		}
     98 	}
     99 
    100 	if( sound->samples == NULL ) {
    101 		WARN( "WAVE contains no data section" );
    102 		return false;
    103 	}
    104 
    105 	sound->num_samples = data_length / ( sound->num_channels * sizeof( s16 ) );
    106 
    107 	// uninterleave the samples
    108 	if( sound->num_channels == 2 ) {
    109 		MEMARENA_SCOPED_CHECKPOINT( arena );
    110 		s16 * scratch = memarena_push_many( arena, s16, sound->num_samples * 2 );
    111 
    112 		for( u32 i = 0; i < sound->num_samples; i++ ) {
    113 			scratch[ i ] = sound->samples[ i * 2 ];
    114 			scratch[ i + sound->num_samples ] = sound->samples[ i * 2 + 1 ];
    115 		}
    116 
    117 		for( u32 i = 0; i < sound->num_samples * 2; i++ ) {
    118 			sound->samples[ i ] = scratch[ i ];
    119 		}
    120 	}
    121 
    122 	return true;
    123 }