From 11e5f50717af85f775491a5d2a2867a7e0f9c45f Mon Sep 17 00:00:00 2001 From: spl3g Date: Fri, 7 Nov 2025 15:20:41 +0300 Subject: Improve the wave screen --- src/defines.h | 2 +- src/main.c | 35 +++++++++----- src/sounds.c | 150 ++++++++++++++++++++++++++++++++++++++++++---------------- src/sounds.h | 45 ------------------ 4 files changed, 132 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/defines.h b/src/defines.h index 88e5c46..3d4bc31 100644 --- a/src/defines.h +++ b/src/defines.h @@ -4,7 +4,7 @@ #define MESSAGE_QUEUE_SIZE 128 #define SAMPLE_RATE 48000 #define PERIOD_SIZE 480 -#define DISPLAY_SAMPLES 960 +#define DISPLAY_SAMPLES (PERIOD_SIZE * 2) static const int SCREEN_FPS = 60; static const int SCREEN_TICKS_PER_FRAME = 1000 / SCREEN_FPS; diff --git a/src/main.c b/src/main.c index f254074..1b398d1 100644 --- a/src/main.c +++ b/src/main.c @@ -223,7 +223,7 @@ int init_ui(app_state *state) { return 0; } -KeyState keys[12] = { +static KeyState keys[] = { {'Z', SDL_SCANCODE_Z, 0, 0}, {'S', SDL_SCANCODE_S, 0, 0}, {'X', SDL_SCANCODE_X, 0, 0}, {'D', SDL_SCANCODE_D, 0, 0}, {'C', SDL_SCANCODE_C, 0, 0}, @@ -231,6 +231,14 @@ KeyState keys[12] = { {'B', SDL_SCANCODE_B, 0, 0}, {'H', SDL_SCANCODE_H, 0, 0}, {'N', SDL_SCANCODE_N, 0, 0}, {'J', SDL_SCANCODE_J, 0, 0}, {'M', SDL_SCANCODE_M, 0, 0}, + + {'Q', SDL_SCANCODE_Q, 0, 0}, {'2', SDL_SCANCODE_2, 0, 0}, + {'W', SDL_SCANCODE_W, 0, 0}, {'3', SDL_SCANCODE_3, 0, 0}, + {'E', SDL_SCANCODE_E, 0, 0}, + {'R', SDL_SCANCODE_R, 0, 0}, {'6', SDL_SCANCODE_5, 0, 0}, + {'T', SDL_SCANCODE_T, 0, 0}, {'7', SDL_SCANCODE_6, 0, 0}, + {'Y', SDL_SCANCODE_Y, 0, 0}, {'8', SDL_SCANCODE_7, 0, 0}, + {'U', SDL_SCANCODE_U, 0, 0}, }; SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { @@ -241,7 +249,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { memset(state, 0, sizeof(app_state)); *appstate = state; state->keys = keys; - state->keys_amount = 12; + state->keys_amount = sizeof(keys)/sizeof(keys[0]); if (init_ui(state) != 0) { printf("Couldn't initialize UI: %s", SDL_GetError()); @@ -255,6 +263,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { return SDL_APP_CONTINUE; } +float old_wave_buffer[DISPLAY_SAMPLES]; + SDL_AppResult SDL_AppIterate(void *appstate) { app_state *state = appstate; Arena *arena = &state->frame_arena; @@ -279,18 +289,17 @@ SDL_AppResult SDL_AppIterate(void *appstate) { } } - // clamp cycle length if it goes past the end - size_t cycle_len = SAMPLE_RATE / state->wave_data.freq; - size_t display_len = cycle_len; - if (buffer_start + cycle_len > DISPLAY_SAMPLES) { - display_len = DISPLAY_SAMPLES - buffer_start; + if (buffer_start < 100) { + ui_data->wave_buffer = &wave_buffer[buffer_start]; + ui_data->wave_buffer_size = DISPLAY_SAMPLES - 100; + memcpy(old_wave_buffer, &wave_buffer[buffer_start], (DISPLAY_SAMPLES - 100) * sizeof(float)); + } else { + ui_data->wave_buffer = old_wave_buffer; + ui_data->wave_buffer_size = DISPLAY_SAMPLES - 100; } - - ui_data->wave_buffer = &wave_buffer[buffer_start]; - ui_data->wave_buffer_size = display_len; ui_data->keys = state->keys; - ui_data->keys_amount = 12; + ui_data->keys_amount = state->keys_amount; ui_data->knob_settings = &state->knob_settings; ui_data->scale = dimensions.width / DEFAULT_DIMENSIONS_WIDTH; @@ -332,7 +341,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { return SDL_APP_SUCCESS; } case SDL_EVENT_KEY_DOWN: { - for (int i = 0; i < 12; i++) { + for (size_t i = 0; i < state->keys_amount; i++) { if (event->key.scancode == state->keys[i].keycode) { if (state->keys[i].keyboard_pressed) { break; @@ -349,7 +358,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { } case SDL_EVENT_KEY_UP: { - for (int i = 0; i < 12; i++) { + for (size_t i = 0; i < state->keys_amount; i++) { if (event->key.scancode == state->keys[i].keycode) { if (!state->keys[i].keyboard_pressed) { break; diff --git a/src/sounds.c b/src/sounds.c index 904b76b..89da2b0 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1,8 +1,60 @@ #define MIDI_FREQS_LIST #include "sounds.h" +typedef enum { + ENV_OFF, + ENV_ATTACK, + ENV_DECAY, + ENV_SUSTAIN, + ENV_RELEASE, +} EnvelopeState; + +typedef struct { + int attack_time; + int decay_time; + float sustain_level; + int release_time; +} EnvelopeParams; + +typedef struct { + EnvelopeState state; + int counter; + float current_inc; + float current_value; + EnvelopeParams params; +} Envelope; + +typedef struct { + bool active; + float freq; + float phase; + float phase_inc; + Envelope envelope; +} SynthVoice; + +typedef struct { + SynthVoice *buffer; + size_t size; +} SynthVoices; + +typedef struct { + OscilatorType oscilator_type; + float master_volume; + EnvelopeParams envelope_params; +} SynthParams; + +typedef struct { + SynthParams params; + SynthVoice *last_voice; + + MessageQueue *queue; + WaveData *wave_data; + SynthVoices *voices; +} SynthState; + +typedef float (*OscilatorFunc)(float phase); + float envelope_next(Envelope *env) { - float value; bool next_state = false; env->counter++; @@ -17,10 +69,10 @@ float envelope_next(Envelope *env) { } if (env->current_inc == 0) { - env->current_inc = (1.0 - env->release_value) / (float)env->params.attack_time; + env->current_inc = (1.0 - env->current_value) / (float)env->params.attack_time; } - value = env->release_value + env->current_inc * (float)env->counter; + env->current_value += env->current_inc; break; } case ENV_DECAY: { @@ -32,26 +84,31 @@ float envelope_next(Envelope *env) { env->current_inc = (1.0 - env->params.sustain_level) / (float)env->params.decay_time; } - value = 1.0 - env->current_inc * (float)env->counter; + env->current_value -= env->current_inc; break; } case ENV_SUSTAIN: { - value = env->params.sustain_level; + env->current_value = env->params.sustain_level; break; } case ENV_RELEASE: { if (env->counter >= env->params.release_time) { env->state = ENV_OFF; - env->counter = env->current_inc = 0; + env->counter = env->current_inc = env->current_value = 0; return 0; } if (env->current_inc == 0) { - env->current_inc = env->params.sustain_level / (float)env->params.release_time; + float from_level; + if (env->params.sustain_level > env->current_value) { + from_level = (env->params.sustain_level - env->current_value); + }else { + from_level = env->current_value; + } + env->current_inc = from_level / (float)env->params.release_time; } - value = env->params.sustain_level - env->current_inc * (float)env->counter; - env->release_value = value; + env->current_value -= env->current_inc; break; } } @@ -60,18 +117,20 @@ float envelope_next(Envelope *env) { env->counter = env->current_inc = 0; env->state++; } - return value; + return env->current_value; } void envelope_note_on(EnvelopeParams params, Envelope *env) { env->state = ENV_ATTACK; env->counter = 0; + env->current_inc = 0; env->params = params; } void envelope_note_off(Envelope *env) { env->state = ENV_RELEASE; env->counter = 0; + env->current_inc = 0; } float osc_sine_get(float phase) { @@ -105,7 +164,6 @@ void set_note_on(SynthParams *params, SynthVoices *voices, size_t note_id) { } envelope_note_on(params->envelope_params, &voices->buffer[note_id].envelope); voices->buffer[note_id].active = true; - params->last_freq = voices->buffer[note_id].freq; } void set_note_off(SynthVoices *voices, size_t note_id) { @@ -210,8 +268,28 @@ void prepare_output(float *scratch_buffer, short *output_buffer, } } -void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data, SynthVoices *voices) { - SynthParams params; +void play_output_buffer(snd_pcm_t *pcm, short *output_buffer) { + int period_size = PERIOD_SIZE; + short *ptr = output_buffer; + + while (period_size > 0) { + snd_pcm_sframes_t written = snd_pcm_writei(pcm, ptr, period_size); + if (written < 0) { + printf("xrun\n"); + snd_pcm_prepare(pcm); // recover from xrun + break; + } + ptr += written; + period_size -= written; + } +} + +void sound_loop(snd_pcm_t *pcm, SynthState *state) { + MessageQueue *queue = state->queue; + WaveData *wave_data = state->wave_data; + SynthVoices *voices = state->voices; + SynthParams params = state->params; + short output_buffer[PERIOD_SIZE]; float scratch_buffer[PERIOD_SIZE]; float display_buffer[DISPLAY_SAMPLES]; @@ -224,6 +302,7 @@ void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data, case MSG_NOTE_ON: { size_t note_id = msg.note.note_id; set_note_on(¶ms, voices, note_id); + state->last_voice = &voices->buffer[note_id]; break; } case MSG_NOTE_OFF: { @@ -255,33 +334,15 @@ void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data, post_process(¶ms, scratch_buffer, PERIOD_SIZE); prepare_output(scratch_buffer, output_buffer, PERIOD_SIZE); - if (display_write_pos + PERIOD_SIZE > DISPLAY_SAMPLES) { - size_t diff = DISPLAY_SAMPLES - display_write_pos; - memcpy(&display_buffer[display_write_pos], scratch_buffer, diff * sizeof(float)); - memcpy(display_buffer, &scratch_buffer[diff], (PERIOD_SIZE - diff) * sizeof(float)); - } else { - memcpy(&display_buffer[display_write_pos], scratch_buffer, PERIOD_SIZE * sizeof(float)); - } - + memcpy(&display_buffer[display_write_pos], scratch_buffer, PERIOD_SIZE * sizeof(float)); display_write_pos = (display_write_pos + PERIOD_SIZE) % DISPLAY_SAMPLES; - memcpy(wave_data->buffers[wave_data->write_index], display_buffer, DISPLAY_SAMPLES * sizeof(float)); - atomic_store(&wave_data->write_index, 1 - atomic_load(&wave_data->write_index)); - wave_data->freq = params.last_freq; - - int period_size = PERIOD_SIZE; - short *ptr = output_buffer; - - while (period_size > 0) { - snd_pcm_sframes_t written = snd_pcm_writei(pcm, ptr, period_size); - if (written < 0) { - printf("xrun\n"); - snd_pcm_prepare(pcm); // recover from xrun - break; - } - ptr += written; - period_size -= written; + if (display_write_pos == 0) { + memcpy(wave_data->buffers[atomic_load(&wave_data->write_index)], display_buffer, DISPLAY_SAMPLES * sizeof(float)); + atomic_store(&wave_data->write_index, 1 - atomic_load(&wave_data->write_index)); } + + play_output_buffer(pcm, output_buffer); } stop: return; @@ -302,15 +363,22 @@ void fill_voices(SynthVoice *voices, MidiNote *freqs, size_t freqs_amount) { void *sound_thread_start(void *ptr) { SoundThreadMeta *meta = ptr; - SynthVoice buffer[12]; - fill_voices(buffer, &midi_freqs[49], 12); + SynthVoice buffer[24]; + fill_voices(buffer, &midi_freqs[48], 24); SynthVoices voices = { .buffer = buffer, - .size = 12, + .size = 24, + }; + + SynthState state = { + .params = {0}, + .queue = meta->queue, + .wave_data = meta->wave_data, + .voices = &voices, }; - sound_loop_start(meta->pcm, meta->queue, meta->wave_data, &voices); + sound_loop(meta->pcm, &state); check(snd_pcm_drop(meta->pcm)); return NULL; diff --git a/src/sounds.h b/src/sounds.h index 639ce80..0a61487 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -27,51 +27,6 @@ typedef struct { WaveData *wave_data; } SoundThreadMeta; -typedef enum { - ENV_OFF, - ENV_ATTACK, - ENV_DECAY, - ENV_SUSTAIN, - ENV_RELEASE, -} EnvelopeState; - -typedef struct { - int attack_time; - int decay_time; - float sustain_level; - int release_time; -} EnvelopeParams; - -typedef struct { - EnvelopeState state; - int counter; - float current_inc; - float release_value; - EnvelopeParams params; -} Envelope; - -typedef struct { - bool active; - float freq; - float phase; - float phase_inc; - Envelope envelope; -} SynthVoice; - -typedef struct { - SynthVoice *buffer; - size_t size; -} SynthVoices; - -typedef struct { - OscilatorType oscilator_type; - float master_volume; - EnvelopeParams envelope_params; - float last_freq; -} SynthParams; - -typedef float (*OscilatorFunc)(float phase); - void *sound_thread_start(void *ptr); int set_hw_params(snd_pcm_t *pcm); -- cgit v1.2.3