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 }