Fix phase cancellation

This commit is contained in:
Charlie Malmqvist 2024-08-01 16:22:50 +02:00
parent a63d76c733
commit 98113b65f3

View file

@ -54,6 +54,9 @@ typedef struct Audio_Format {
} Audio_Format;
// #Global
ogb_instance u64 next_audio_source_uid;
// Implemented per OS
ogb_instance Audio_Format audio_output_format;
ogb_instance Mutex audio_init_mutex;
@ -61,6 +64,7 @@ ogb_instance Mutex audio_init_mutex;
#if !OOGABOOGA_LINK_EXTERNAL_INSTANCE
Audio_Format audio_output_format;
Mutex audio_init_mutex;
u64 next_audio_source_uid = 0;
#endif
// I don't see a big reason for you to use anything else than WAV and OGG.
@ -114,6 +118,7 @@ typedef struct Audio_Source {
Audio_Source_Kind kind;
Audio_Format format;
u64 number_of_frames;
u64 uid;
Allocator allocator;
// For file stream
@ -542,6 +547,8 @@ bool
audio_open_source_stream_format(Audio_Source *src, string path, Audio_Format format,
Allocator allocator) {
*src = ZERO(Audio_Source);
src->uid = next_audio_source_uid;
next_audio_source_uid += 1;
mutex_init(&src->mutex_for_destroy);
@ -598,6 +605,9 @@ audio_open_source_load_format(Audio_Source *src, string path, Audio_Format forma
Allocator allocator) {
*src = ZERO(Audio_Source);
src->uid = next_audio_source_uid;
next_audio_source_uid += 1;
mutex_init(&src->mutex_for_destroy);
src->allocator = allocator;
@ -1142,7 +1152,7 @@ convert_frames(void *dst, Audio_Format dst_format,
#define AUDIO_STATE_FADE_TIME_MS 40
#define AUDIO_SMOOTH_TRANSITION_TIME_MS 40
typedef enum Audio_Player_State {
AUDIO_PLAYER_STATE_PAUSED,
@ -1241,7 +1251,7 @@ audio_player_set_state(Audio_Player *p, Audio_Player_State state) {
float64 progression = (float64)p->frame_index / (float64)p->source.number_of_frames;
float64 remaining = (1.0-progression)*full_duration;
float64 fade_seconds = min(AUDIO_STATE_FADE_TIME_MS/1000.0, remaining);
float64 fade_seconds = min(AUDIO_SMOOTH_TRANSITION_TIME_MS/1000.0, remaining);
float64 fade_factor = fade_seconds/full_duration;
@ -1635,6 +1645,8 @@ do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format,
memset(mix_buffer, 0, mix_buffer_size);
u64 *started_this_frame;
growing_array_init((void**)&started_this_frame, sizeof(u64), get_temporary_allocator());
while (block) {
@ -1714,6 +1726,24 @@ do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format,
}
// :PhaseCancellation
if (p->frame_index == 0) { // The players' source just started playing
s64 existing_index = growing_array_find_index_from_left_by_value((void**)&started_this_frame, &src.uid);
if (existing_index != -1) {
// If this source already started playing this round from another player, then we pretend that
// we're already done playing by skipping to the last frame.
// For non-looping players, this means we don't play this instance at all.
// For looping players, this means we have a slight offset between the players that start
// playing at the exact same time. I'm not sure how else to deal with phase cancellation
// in looping players.
p->frame_index = src.number_of_frames;
continue;
}
growing_array_add((void**)&started_this_frame, &src.uid);
}
p->frame_index = audio_source_sample_next_frames(
&src,
p->frame_index,