#include "audio.h" #include "enginestate.h" #include void audio_cb(p_audio_buffer *buffer); void audio_init() { audio_player *player = &engine.audio; player->track_count = 0; player->track_capacity = 32; player->tracks = (audio_track *) p_alloc(player->track_capacity * sizeof(audio_track)); player->next_id = 0; player->sample_rate = p_audio_sample_rate(); p_audio_register_data_callback(audio_cb); } track_id audio_add_track(p_audio_buffer *data, u32 flags) { // @Correctness: fix track if it has a different sample rate audio_player *player = &engine.audio; audio_track track; track.id = player->next_id; player->next_id++; track.data = *data; track.progress = 0; track.playing = !(flags & AUDIO_PAUSED); track.loop = !!(flags & AUDIO_LOOP); track.free_on_finish = !!(flags & AUDIO_FREE_ON_FINISH); track.volume = 1.0; if (player->track_count + 1 < player->track_capacity) { u64 index = player->track_count; player->tracks[index] = track; player->track_count++; } else { u32 replace_index = 0; for(u32 i = 0; i < player->track_count; i++) { audio_track *current = &player->tracks[i]; if(track.loop == current->loop) { replace_index = i; break; } } player->tracks[replace_index] = track; } return track.id; } void audio_remove_track(track_id id) { audio_player *player = &engine.audio; for(int i = 0; i < player->track_count; i++) { audio_track *current = &player->tracks[i]; if(id == current->id) { u32 move_count = player->track_count - (i + 1); if(current->free_on_finish) p_free(current->data.samples); memmove(current, current + 1, move_count * sizeof(audio_track)); player->track_count--; break; } } } void audio_pause_track(track_id id) { audio_track *track = audio_track_from_id(id); if(track) track->playing = false; } void audio_play_track(track_id id) { audio_track *track = audio_track_from_id(id); if(track) track->playing = true; } void audio_change_track_volume(track_id id, f32 volume) { audio_track *track = audio_track_from_id(id); if(track) track->volume = volume; } audio_track *audio_track_from_id(track_id id) { audio_player *player = &engine.audio; for(int i = 0; i < player->track_count; i++) { audio_track *current = &player->tracks[i]; if(id == current->id) return current; } return NULL; } // @Correctness: audio cb modifies the array of tracks asynchronously. All r/w operations on tracks should be protected by a mutex. void audio_cb(p_audio_buffer *buffer) { audio_player *player = &engine.audio; buffer->size = minimum(buffer->size, 2048); // Low latency for(u64 i = 0; i < buffer->size; i++) { buffer->samples[i].left = 0; buffer->samples[i].right = 0; } for(int track_i = 0; track_i < player->track_count; track_i++) { audio_track *current = &player->tracks[track_i]; if(current->playing) { u64 remaining = current->data.size - current->progress; u64 copy_count = minimum(remaining, buffer->size); if(current->loop) copy_count = buffer->size; for(u64 i = 0; i < copy_count; i++) { buffer->samples[i].left += current->volume * current->data.samples[current->progress].left; buffer->samples[i].right += current->volume * current->data.samples[current->progress].right; current->progress = (current->progress + 1) % current->data.size; } if(!current->loop && current->progress == 0) // current->progress == 0 because we finished and then we looped around { // Finished playing. Remove track u32 move_count = player->track_count - (track_i + 1); if(current->free_on_finish) p_free(current->data.samples); // @Bug: double free when the same sound is played 2 times. Find a way to signal we reached the end of the track so the user code can make its own free (if needed) memmove(current, current + 1, move_count * sizeof(audio_track)); track_i--; player->track_count--; } } } }