From e97e5240bbaf9ad3f0311e443ea7945a45ca776e Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Tue, 13 Feb 2024 22:36:05 +0100 Subject: Rendering optimizations --- source/saw/main.c | 231 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 136 insertions(+), 95 deletions(-) (limited to 'source') diff --git a/source/saw/main.c b/source/saw/main.c index 38d292f..7bcc6d7 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -58,17 +58,26 @@ enum { CHANNEL_COUNT = 2, SAMPLE_RATE = 44100, #ifdef __EMSCRIPTEN__ - BUFFER_SIZE = 2048 * 32, + BUFFER_SIZE = 1024 * 16, #else - BUFFER_SIZE = 2048 * 4, + BUFFER_SIZE = 1024 * 2, #endif +#ifdef __EMSCRIPTEN__ + TRACK_COUNT = 8, + ROLL_COUNT = 16, + PITCH_COUNT = 80, + VOICE_COUNT = 16, + UNISON_COUNT = 100, + SHEET_SIZE = 200, +#else TRACK_COUNT = 16, ROLL_COUNT = 32, PITCH_COUNT = 80, VOICE_COUNT = 32, UNISON_COUNT = 100, SHEET_SIZE = 200, +#endif ROLL_DEFAULT_RATE = 8, ROLL_DEFAULT_UI_OFFSET_Y = 710, @@ -212,6 +221,7 @@ static i64 saw_playback_lookahead = 0; static i64 saw_playback_offset_read = 0; static i64 saw_playback_offset_write = 0; static f32 saw_playback_buffer[BUFFER_SIZE] = { 0.f }; +static f32 saw_playback_temp[BUFFER_SIZE]; static mtx_t saw_playback_mutex; static i64 saw_current_track = 0; @@ -338,105 +348,134 @@ static void saw_audio_render(void) { // FIXME // Improve performance. - if (mtx_lock(&saw_playback_mutex) != thrd_success) { - assert(0); - return; - } - i64 frame_count = (BUFFER_SIZE / CHANNEL_COUNT) - saw_playback_lookahead; - for (i64 i = 0; i < frame_count; i++) { - if (saw_playback_on) { - // Note triggers - // + if (frame_count > 0) { + memset(saw_playback_temp, 0, + frame_count * CHANNEL_COUNT * sizeof *saw_playback_temp); - for (i32 k = 0; k < ROLL_COUNT; k++) { - saw_roll_t *roll = saw_rolls + k; - if (!roll->enabled || saw_playback_frame + 1 <= roll->time || - saw_playback_frame + 1 > roll->time + roll->duration) - continue; + for (i64 i = 0; i < frame_count; i++) { + if (saw_playback_on) { + // Note triggers + // - i64 play_frame = roll->loop_duration == 0 - ? saw_playback_frame - : saw_playback_frame - - ((saw_playback_frame + 1 - - roll->time) / - roll->loop_duration) * - roll->loop_duration; - - for (i32 i = 0; i < SHEET_SIZE; i++) { - saw_roll_note_t *note = roll->notes + i; - if (!note->enabled) - continue; - i64 note_frame = roll->time + note->time; - if (play_frame + 1 <= note_frame || play_frame > note_frame) + for (i32 k = 0; k < ROLL_COUNT; k++) { + saw_roll_t *roll = saw_rolls + k; + if (!roll->enabled || + saw_playback_frame + 1 <= roll->time || + saw_playback_frame + 1 > roll->time + roll->duration) continue; - saw_play_voice(saw_tracks + roll->track, roll, note->pitch, - note->duration); + i64 play_frame = roll->loop_duration == 0 + ? saw_playback_frame + : saw_playback_frame - + ((saw_playback_frame + 1 - + roll->time) / + roll->loop_duration) * + roll->loop_duration; + + for (i32 i = 0; i < SHEET_SIZE; i++) { + saw_roll_note_t *note = roll->notes + i; + if (!note->enabled) + continue; + i64 note_frame = roll->time + note->time; + if (play_frame + 1 <= note_frame || + play_frame > note_frame) + continue; + + saw_play_voice(saw_tracks + roll->track, roll, + note->pitch, note->duration); + } } + + ++saw_playback_frame; } - ++saw_playback_frame; + // Oscillators + // + { + for (i32 n = 0; n < VOICE_COUNT; n++) { + if (!saw_voices[n].enabled) + continue; + + saw_track_t *track = saw_tracks + saw_voices[n].track; + + i32 wave_type = track->instrument; + f64 warp = track->warp; + f64 frequency = saw_voices[n].frequency; + f64 amplitude = saw_voices[n].amplitude; + f64 phase_l = track->phase + saw_voices[n].phase[0]; + f64 phase_r = track->phase + saw_voices[n].phase[1]; + + f64 attack = track->envelope.attack; + f64 decay = track->envelope.decay; + f64 sustain = track->envelope.sustain; + f64 duration = saw_voices[n].duration; + f64 release = track->envelope.release; + + f64 t = (f64) saw_voices[n].time / (f64) SAMPLE_RATE; + f64 a = amplitude * saw_envelope(t, attack, decay, sustain, + duration, release); + + saw_playback_temp[i * CHANNEL_COUNT] += + (f32) (saw_oscillator(wave_type, frequency, phase_l, + warp, t) * + a); + saw_playback_temp[i * CHANNEL_COUNT + 1] += + (f32) (saw_oscillator(wave_type, frequency, phase_r, + warp, t) * + a); + + saw_voices[n].time++; + + if (t > duration + release) + saw_voices[n].enabled = 0; + } + } } - // Oscillators - // - { - i64 k = (saw_playback_offset_write + i * CHANNEL_COUNT) % - BUFFER_SIZE; + i64 n0 = frame_count < (BUFFER_SIZE - saw_playback_offset_write) / + CHANNEL_COUNT + ? frame_count + : (BUFFER_SIZE - saw_playback_offset_write) / + CHANNEL_COUNT; + i64 n1 = frame_count - n0; - saw_playback_buffer[k] = 0.f; - saw_playback_buffer[k + 1] = 0.f; + if (mtx_lock(&saw_playback_mutex) != thrd_success) { + assert(0); + return; + } - for (i32 n = 0; n < VOICE_COUNT; n++) { - if (!saw_voices[n].enabled) - continue; + if (n0 > 0) + memcpy(saw_playback_buffer + saw_playback_offset_write, + saw_playback_temp, + n0 * CHANNEL_COUNT * sizeof *saw_playback_temp); - saw_track_t *track = saw_tracks + saw_voices[n].track; - - i32 wave_type = track->instrument; - f64 warp = track->warp; - f64 frequency = saw_voices[n].frequency; - f64 amplitude = saw_voices[n].amplitude; - f64 phase_l = track->phase + saw_voices[n].phase[0]; - f64 phase_r = track->phase + saw_voices[n].phase[1]; - - f64 attack = track->envelope.attack; - f64 decay = track->envelope.decay; - f64 sustain = track->envelope.sustain; - f64 duration = saw_voices[n].duration; - f64 release = track->envelope.release; - - f64 t = (f64) saw_voices[n].time / (f64) SAMPLE_RATE; - f64 a = amplitude * saw_envelope(t, attack, decay, sustain, - duration, release); - - saw_playback_buffer[k] += (f32) (saw_oscillator( - wave_type, frequency, - phase_l, warp, t) * - a); - saw_playback_buffer[k + 1] += (f32) (saw_oscillator( - wave_type, frequency, - phase_r, warp, t) * - a); - - saw_voices[n].time++; - - if (t > duration + release) - saw_voices[n].enabled = 0; - } - } - } + if (n1 > 0) + memcpy(saw_playback_buffer, + saw_playback_temp + (n0 * CHANNEL_COUNT), + n1 * CHANNEL_COUNT * sizeof *saw_playback_temp); - saw_playback_offset_write = (saw_playback_offset_write + - frame_count * CHANNEL_COUNT) % - BUFFER_SIZE; + saw_playback_offset_write = (saw_playback_offset_write + + frame_count * CHANNEL_COUNT) % + BUFFER_SIZE; - saw_playback_lookahead += frame_count; + saw_playback_lookahead += frame_count; - mtx_unlock(&saw_playback_mutex); + mtx_unlock(&saw_playback_mutex); + } + +#ifndef __EMSCRIPTEN__ + if (frame_count == 0 && !saw_playback_on) { + // Sleep for 1/5 of the buffer duration + thrd_sleep( + &(struct timespec) { + .tv_nsec = (200000000ll * BUFFER_SIZE / CHANNEL_COUNT) / + SAMPLE_RATE }, + NULL); + } +#endif } static void saw_audio_callback(ma_device *device, void *void_out_, @@ -458,12 +497,20 @@ static void saw_audio_callback(ma_device *device, void *void_out_, ? frame_count : saw_playback_lookahead; - for (i64 i = 0; i < n; i++) { - i64 k = (saw_playback_offset_read + i * CHANNEL_COUNT) % - BUFFER_SIZE; - out[i * 2] = saw_playback_buffer[k]; - out[i * 2 + 1] = saw_playback_buffer[k + 1]; - } + i64 n0 = n < (BUFFER_SIZE - saw_playback_offset_read) / + CHANNEL_COUNT + ? n + : (BUFFER_SIZE - saw_playback_offset_read) / + CHANNEL_COUNT; + i64 n1 = n - n0; + + if (n0 > 0) + memcpy(out, saw_playback_buffer + saw_playback_offset_read, + n0 * CHANNEL_COUNT * sizeof *out); + + if (n1 > 0) + memcpy(out + (n0 * CHANNEL_COUNT), saw_playback_buffer, + n1 * CHANNEL_COUNT * sizeof *out); saw_playback_offset_read = (saw_playback_offset_read + n * CHANNEL_COUNT) % @@ -1946,12 +1993,6 @@ static void saw_init(void) { } static void saw_frame(void) { - // TODO - // Adjust sleep depending on how much compute we need - - // Sleep to prevent high CPU load - thrd_sleep(&(struct timespec) { .tv_nsec = 10000000 }, NULL); - saw_audio_render(); i64 frame_width = sapp_width(); -- cgit v1.2.3