medfall

A super great game engine
Log | Files | Refs

mixer.cc (3155B)


      1 #include "intrinsics.h"
      2 #include "memory_arena.h"
      3 #include "int_conversions.h"
      4 #include "mixer.h"
      5 #include "pool.h"
      6 
      7 struct PlayingSound {
      8 	PlayingSoundID id;
      9 	SoundData data;
     10 	s32 num_played_samples;
     11 	float volume;
     12 	bool loop;
     13 };
     14 
     15 enum MixerCommandType {
     16 	MIXER_PLAY,
     17 	MIXER_VOLUME,
     18 	MIXER_STOP,
     19 	MIXER_STOP_ALL,
     20 };
     21 
     22 struct MixerCommand {
     23 	MixerCommandType type;
     24 	SoundData sound_to_play;
     25 	PlayingSoundID sound_id;
     26 	s32 start_pos;
     27 	float volume; // TODO: per channel volume?
     28 	bool loop;
     29 };
     30 
     31 #define RESET_SOUND_COUNTER 0
     32 STATIC_ASSERT( RESET_SOUND_COUNTER != INVALID_SOUND_ID );
     33 
     34 static Pool< PlayingSound, MAX_CONCURRENT_SOUNDS > playing_sounds;
     35 static PlayingSoundID sound_counter;
     36 
     37 // TODO: this should really use a hash index/pool with handles
     38 static PlayingSound * find_playing_sound( PlayingSoundID sound_id ) {
     39 	for( PlayingSound & ps : playing_sounds ) {
     40 		if( ps.id == sound_id ) {
     41 			return &ps;
     42 		}
     43 	}
     44 
     45 	return NULL;
     46 }
     47 
     48 static void mixer_stop_all_impl() {
     49 	playing_sounds.clear();
     50 }
     51 
     52 static void mixer_mix_sound( PlayingSound * ps, float * samples, size_t num_samples ) {
     53 	// TODO: simd
     54 
     55 	s32 samples_to_write = checked_cast< s32 >( num_samples );
     56 	if( !ps->loop ) {
     57 		samples_to_write = min( samples_to_write, checked_cast< s32 >( ps->data.num_samples ) - ps->num_played_samples );
     58 	}
     59 
     60 	for( s32 i = 0; i < samples_to_write; i++ ) {
     61 		s32 j = ( ps->num_played_samples + i ) % ps->data.num_samples;
     62 		samples[ i ] += dequantize11s( ps->data.samples[ j ], 16 ) * ps->volume;
     63 	}
     64 
     65 	ps->num_played_samples += samples_to_write;
     66 	if( ps->loop ) {
     67 		ps->num_played_samples %= ps->data.num_samples;
     68 	}
     69 	else if( ps->num_played_samples == checked_cast< s32 >( ps->data.num_samples ) ) {
     70 		playing_sounds.release( ps );
     71 	}
     72 }
     73 
     74 void mixer_init() {
     75 	mixer_stop_all();
     76 }
     77 
     78 void mixer_mix( AudioBuffer * buffer, MemoryArena * arena, u32 samples_to_mix, u32 mix_ahead ) {
     79 	MEMARENA_SCOPED_CHECKPOINT( arena );
     80 
     81 	float * float_samples = alloc_many< float >( arena, samples_to_mix );
     82 	for( u32 i = 0; i < samples_to_mix; i++ ) {
     83 		float_samples[ i ] = 0.0f;
     84 	}
     85 
     86 	for( PlayingSound & ps : playing_sounds ) {
     87 		mixer_mix_sound( &ps, float_samples, samples_to_mix );
     88 	}
     89 
     90 	u32 cursor = load_acquire( &buffer->cursor );
     91 	for( u32 i = 0; i < samples_to_mix; i++ ) {
     92 		s16 q = checked_cast< s16 >( quantize11s( clamp11( float_samples[ i ] ), 16 ) );
     93 		buffer->samples[ ( i + cursor ) % ARRAY_COUNT( buffer->samples ) ] = q;
     94 	}
     95 }
     96 
     97 PlayingSoundID mixer_play( SoundData sound, MixerLoopBool loop, s32 start_pos ) {
     98 	PlayingSound * ps = playing_sounds.acquire();
     99 	if( ps == NULL )
    100 		return INVALID_SOUND_ID;
    101 
    102 	ps->id = sound_counter;
    103 	ps->data = sound;
    104 	ps->num_played_samples = start_pos;
    105 	ps->volume = 1.0f;
    106 	ps->loop = loop;
    107 
    108 	sound_counter++;
    109 	if( sound_counter == INVALID_SOUND_ID ) {
    110 		sound_counter = RESET_SOUND_COUNTER;
    111 	}
    112 
    113 	return ps->id;
    114 }
    115 
    116 void mixer_volume( PlayingSoundID sound_id, float volume ) {
    117 	PlayingSound * ps = find_playing_sound( sound_id );
    118 	if( ps != NULL ) {
    119 		ps->volume = volume;
    120 	}
    121 }
    122 
    123 void mixer_stop( PlayingSoundID sound_id ) {
    124 	playing_sounds.release( find_playing_sound( sound_id ) );
    125 }
    126 
    127 void mixer_stop_all() {
    128 	playing_sounds.clear();
    129 }