From b5bd5840744a0a3e36b1be8f9ab95492d1005bca Mon Sep 17 00:00:00 2001 From: spl3g Date: Sat, 1 Nov 2025 19:34:45 +0300 Subject: Show the waves! (badly) --- src/clay_renderer_SDL3.c | 32 ++++++++++++++++++++++++++++++-- src/clay_renderer_SDL3.h | 8 ++++++++ src/main.c | 41 +++++++++++++++++++++++++++++------------ src/messages.h | 10 ++++++++-- src/sounds.c | 37 +++++++++++++++++++++++++++++++++++-- src/sounds.h | 6 +++--- src/ui.c | 30 ++++++++++++++++++++++++------ src/ui.h | 7 ++++++- 8 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/clay_renderer_SDL3.c b/src/clay_renderer_SDL3.c index 147c836..7e13859 100644 --- a/src/clay_renderer_SDL3.c +++ b/src/clay_renderer_SDL3.c @@ -1,4 +1,5 @@ #include "clay_renderer_SDL3.h" +#include /* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with * no AA or low resolution might make it appear as jagged curves) */ @@ -131,7 +132,7 @@ static void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FP } } -int SDL_Clay_RenderCircle(SDL_Renderer *renderer, float x, float y, float width, float height, float start_angle, float end_angle, const Clay_Color _color) { +void SDL_Clay_RenderCircle(SDL_Renderer *renderer, float x, float y, float width, float height, float start_angle, float end_angle, const Clay_Color _color) { const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 }; float center_x = x + width / 2; float center_y = y + width / 2; @@ -172,7 +173,28 @@ int SDL_Clay_RenderCircle(SDL_Renderer *renderer, float x, float y, float width, } SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount); - return 0; +} + +void SDL_Clay_RenderWaveScreen(SDL_Renderer *renderer, float x, float y, float width, float height, const Clay_Color color, float *point_buffer, size_t buffer_len) { + SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); + float samples_per_px = (float)buffer_len / width; + + for (int px = 0; px < (int)width; px++) { + size_t start = (size_t)(px * samples_per_px); + size_t end = (size_t)((px + 1) * samples_per_px); + if (end >= buffer_len) end = buffer_len - 1; + + float minv = 1.0f, maxv = -1.0f; + for (size_t i = start; i <= end; i++) { + if (point_buffer[i] < minv) minv = point_buffer[i]; + if (point_buffer[i] > maxv) maxv = point_buffer[i]; + } + + float y_min = y + height * (0.5f - 0.5f * maxv); + float y_max = y + height * (0.5f - 0.5f * minv); + + SDL_RenderLine(renderer, x + px, y_min, x + px, y_max); + } } SDL_Rect currentClippingRectangle; @@ -309,6 +331,12 @@ void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Clay_Rende SDL_Clay_RenderCircle(rendererData->renderer, bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height, start_angle, end_angle, config.color); break; } + case CUSTOM_ELEMENT_TYPE_WAVE_SCREEN: { + WaveScreenData config = custom_element->wave_screen; + SDL_Clay_RenderWaveScreen(rendererData->renderer, bounding_box.x, bounding_box.y, + bounding_box.width, bounding_box.height, config.color, + config.point_buffer, config.buffer_len); + } } break; } diff --git a/src/clay_renderer_SDL3.h b/src/clay_renderer_SDL3.h index 6ece289..5ba1104 100644 --- a/src/clay_renderer_SDL3.h +++ b/src/clay_renderer_SDL3.h @@ -14,6 +14,7 @@ typedef struct { typedef enum { CUSTOM_ELEMENT_TYPE_CIRCLE, + CUSTOM_ELEMENT_TYPE_WAVE_SCREEN, } CustomElementType; typedef struct { @@ -22,11 +23,18 @@ typedef struct { Clay_Color color; } CircleData; +typedef struct { + float *point_buffer; + size_t buffer_len; + Clay_Color color; +} WaveScreenData; + typedef struct { CustomElementType type; union { CircleData circle; + WaveScreenData wave_screen; }; } CustomElementData; diff --git a/src/main.c b/src/main.c index a2f8296..580b271 100644 --- a/src/main.c +++ b/src/main.c @@ -18,15 +18,6 @@ #define ARENA_IMPLEMENTATION #include "arena.h" -static const int SCREEN_FPS = 60; -static const int SCREEN_TICKS_PER_FRAME = 1000 / SCREEN_FPS; - -static const int FONT_ID = 0; -static const Clay_Dimensions DEFAULT_DIMENSIONS = { - .width = 1280, - .height = 720, -}; - typedef struct { bool pressed; @@ -50,6 +41,7 @@ typedef struct { snd_pcm_t *sound_device; message_queue msg_queue; + WaveData wave_data; pthread_t sound_thread; } app_state; @@ -74,6 +66,7 @@ int init_sounds(app_state *state) { sound_thread_meta *sound_thread_params = malloc(sizeof(sound_thread_meta)); sound_thread_params->pcm = state->sound_device; sound_thread_params->queue = &state->msg_queue; + sound_thread_params->wave_data = &state->wave_data; pthread_t sound_thread; pthread_create(&sound_thread, NULL, sound_thread_start, sound_thread_params); @@ -193,7 +186,7 @@ int init_ui(app_state *state) { return 1; } - if (!SDL_CreateWindowAndRenderer("crynth", DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS, &state->window, &state->renderer_data.renderer)) { + if (!SDL_CreateWindowAndRenderer("crynth", DEFAULT_DIMENSIONS_WIDTH, DEFAULT_DIMENSIONS_HEIGHT, SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS, &state->window, &state->renderer_data.renderer)) { return 1; } @@ -272,12 +265,36 @@ SDL_AppResult SDL_AppIterate(void *appstate) { Clay_Dimensions dimensions = Clay_GetCurrentContext()->layoutDimensions; UIData *ui_data = arena_alloc(arena, sizeof(UIData)); + ui_data->arena = arena; ui_data->msg_queue = &state->msg_queue; + + int read_index = 1 - atomic_load(&state->wave_data.write_index); + float *wave_buffer = state->wave_data.buffers[read_index]; + + size_t buffer_start = 0; + for (size_t i = 1; i < DISPLAY_SAMPLES; i++) { + if (wave_buffer[i-1] < 0 && wave_buffer[i] >= 0) { + buffer_start = i; + break; + } + } + + // 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; + } + + 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->arena = arena; + ui_data->knob_settings = &state->knob_settings; - ui_data->scale = dimensions.width / DEFAULT_DIMENSIONS.width; + ui_data->scale = dimensions.width / DEFAULT_DIMENSIONS_WIDTH; + Clay_SetPointerState((Clay_Vector2){ state->pointer.position_x, state->pointer.position_y }, state->pointer.pressed); Clay_UpdateScrollContainers(true, (Clay_Vector2){ state->pointer.pending_scroll_delta_x, state->pointer.pending_scroll_delta_y }, 0.016f); diff --git a/src/messages.h b/src/messages.h index 6dc0d44..edbc32e 100644 --- a/src/messages.h +++ b/src/messages.h @@ -3,8 +3,8 @@ #include #include - -#define MESSAGE_QUEUE_SIZE 128 +#include +#include "defines.h" typedef enum { PARAM_OSC, @@ -55,6 +55,12 @@ typedef struct { pthread_mutex_t lock; } message_queue; +typedef struct { + float freq; + float buffers[2][DISPLAY_SAMPLES]; + atomic_int write_index; +} WaveData; + int mqueue_get(message_queue *q, synth_message *msg); int mqueue_push(message_queue *q, synth_message msg); int mqueue_push_many(message_queue *q, synth_message *msg, size_t count); diff --git a/src/sounds.c b/src/sounds.c index 692555c..d1eb875 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -105,6 +105,7 @@ void set_note_on(synth_params *params, synth_voices *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(synth_voices *voices, size_t note_id) { @@ -179,6 +180,22 @@ void generate_voices(synth_voices *voices, synth_params *params, float *buffer, } } +void print_wave(oscilator_func osc, float freq, size_t size) { + float phase = 0; + float phase_inc = 2 * M_PI * freq / SAMPLE_RATE; + + printf("float wave_buffer[%d] = {\n", size); + for (size_t j = 0; j < size; j++) { + printf(" %f,\n", osc(phase)); + + phase += phase_inc; + if (phase >= 2 * M_PI) + phase -= 2 * M_PI; + + } + printf("}\n"); +} + void post_process(synth_params *params, float *scratch_buffer, size_t buffer_size) { for (size_t i = 0; i < buffer_size; i++) { @@ -193,10 +210,12 @@ void prepare_output(float *scratch_buffer, short *output_buffer, } } -void sound_loop_start(snd_pcm_t *pcm, message_queue *queue, synth_voices *voices) { +void sound_loop_start(snd_pcm_t *pcm, message_queue *queue, WaveData *wave_data, synth_voices *voices) { synth_params params; short output_buffer[PERIOD_SIZE]; float scratch_buffer[PERIOD_SIZE]; + float display_buffer[DISPLAY_SAMPLES]; + size_t display_write_pos = 0; while (true) { synth_message msg; @@ -236,6 +255,20 @@ void sound_loop_start(snd_pcm_t *pcm, message_queue *queue, synth_voices *voices 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)); + } + + 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; @@ -277,7 +310,7 @@ void *sound_thread_start(void *ptr) { .size = 12, }; - sound_loop_start(meta->pcm, meta->queue, &voices); + sound_loop_start(meta->pcm, meta->queue, meta->wave_data, &voices); check(snd_pcm_drop(meta->pcm)); return NULL; diff --git a/src/sounds.h b/src/sounds.h index 290a0b0..2d185d6 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -9,6 +9,7 @@ #include "messages.h" #include "midi_freqs.h" +#include "defines.h" #define check(ret) \ do { \ @@ -20,12 +21,10 @@ } \ } while (0) -#define SAMPLE_RATE 48000 -#define PERIOD_SIZE 480 - typedef struct { snd_pcm_t *pcm; message_queue *queue; + WaveData *wave_data; } sound_thread_meta; typedef enum { @@ -68,6 +67,7 @@ typedef struct { oscilator_type oscilator_type; float master_volume; envelope_params envelope_params; + float last_freq; } synth_params; typedef float (*oscilator_func)(float phase); diff --git a/src/ui.c b/src/ui.c index 5d2297e..f7a0ea7 100644 --- a/src/ui.c +++ b/src/ui.c @@ -291,6 +291,29 @@ void draw_knob(Clay_ElementId id, UIData *ui_data, KnobInfo* knob_info, Clay_Siz } } +void draw_screen(UIData *ui_data) { + + CustomElementData *wave = arena_alloc(ui_data->arena, sizeof(CustomElementData)); + wave->type = CUSTOM_ELEMENT_TYPE_WAVE_SCREEN; + wave->wave_screen = (WaveScreenData){ + .point_buffer = ui_data->wave_buffer, + .buffer_len = ui_data->wave_buffer_size, + .color = COLOR_FG, + }; + + CLAY(CLAY_ID("wave_screen"), { + .layout = { + .sizing = {CLAY_SIZING_FIXED(200 * ui_data->scale), CLAY_SIZING_FIXED(100 * ui_data->scale)}, + }, + .border = { .width = {1, 1, 1, 1, 0}, .color = COLOR_FG }, + .custom = { + .customData = wave, + }, + }) { + + }; +} + void draw_panel(UIData *ui_data) { CLAY(CLAY_ID("panel_container"), { .layout = { @@ -303,12 +326,7 @@ void draw_panel(UIData *ui_data) { draw_knob(CLAY_ID("volume_knob"), ui_data, &ui_data->knob_settings->volume, CLAY_SIZING_FIXED(85 * ui_data->scale), CLAY_SIZING_FIXED(45 * ui_data->scale)); } - CLAY(CLAY_ID("wave_screen"), { - .layout = { - .sizing = {CLAY_SIZING_FIXED(200 * ui_data->scale), CLAY_SIZING_FIXED(100 * ui_data->scale)}, - }, - .border = { .width = {1, 1, 1, 1, 0}, .color = COLOR_FG }, - }); + draw_screen(ui_data); CLAY(CLAY_ID("envelope_knobs_container"), { .layout = { diff --git a/src/ui.h b/src/ui.h index daf4596..e6316f8 100644 --- a/src/ui.h +++ b/src/ui.h @@ -14,6 +14,8 @@ #include "clay.h" #include "messages.h" +#include "defines.h" + static const Clay_Color COLOR_BG = (Clay_Color){45, 53, 59, 255}; static const Clay_Color COLOR_BG_INTER = (Clay_Color){52, 63, 68, 255}; static const Clay_Color COLOR_FG = (Clay_Color){211, 198, 170, 255}; @@ -43,11 +45,14 @@ typedef struct { } KnobSettings; typedef struct { + Arena *arena; message_queue *msg_queue; + float *wave_buffer; + size_t wave_buffer_size; + KeyState *keys; size_t keys_amount; KnobSettings *knob_settings; - Arena *arena; float scale; } UIData; -- cgit v1.2.3