aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/clay_renderer_SDL3.c32
-rw-r--r--src/clay_renderer_SDL3.h8
-rw-r--r--src/main.c41
-rw-r--r--src/messages.h10
-rw-r--r--src/sounds.c37
-rw-r--r--src/sounds.h6
-rw-r--r--src/ui.c30
-rw-r--r--src/ui.h7
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 <stdio.h>
/* 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 {
@@ -23,10 +24,17 @@ typedef struct {
} 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 <stddef.h>
#include <pthread.h>
-
-#define MESSAGE_QUEUE_SIZE 128
+#include <stdatomic.h>
+#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(&params, 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;