From 2a20d5a122f436f510c5a9f97ca0ed3333bf9e56 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Wed, 27 Sep 2023 05:06:58 +0200 Subject: piano roll refactor --- source/saw/main.c | 229 +++++++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 107 deletions(-) diff --git a/source/saw/main.c b/source/saw/main.c index 8250850..9945480 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -41,6 +41,16 @@ typedef struct { saw_sheet_note_t notes[SHEET_SIZE]; } saw_sheet_t; +typedef struct { + i32 last_index; + i8 turned_off[ROLL_SIZE]; + saw_sheet_t sheet; + i8 grid_input; + i32 grid_note; + i32 grid_pitch; + i32 grid_time; +} saw_roll_t; + static struct NVGcontext *saw_nvg; static ma_device saw_ma; @@ -56,16 +66,17 @@ static i32 saw_voice_pitch[VOICE_COUNT] = { 0 }; static i64 saw_voice_time[VOICE_COUNT] = { 0 }; static f64 saw_voice_duration[VOICE_COUNT] = { 0 }; -static i32 saw_roll_last_index = -1; -static i8 saw_roll_turned_off[ROLL_SIZE] = { 0 }; -static saw_sheet_t saw_roll_sheet = { .rate = 6, .notes = { 0 } }; -static i8 saw_roll_playing = 0; -static i64 saw_roll_frame = 0; +static i8 saw_playback_on = 0; +static i64 saw_playback_frame = 0; -static i8 saw_grid_input = 0; -static i32 saw_grid_note = 0; -static i32 saw_grid_pitch = 0; -static i32 saw_grid_time = 0; +static saw_roll_t saw_rolls[1] = { { .last_index = -1, + .turned_off = { 0 }, + .sheet = { .rate = 6, + .notes = { 0 } }, + .grid_input = 0, + .grid_note = 0, + .grid_pitch = 0, + .grid_time = 0 } }; #ifdef __GNUC__ # pragma GCC diagnostic push @@ -94,17 +105,19 @@ static f64 saw_envelope(f64 t, f64 attack, f64 decay, f64 sustain, return 0.; } -static void saw_roll_playback(i32 frame_count) { - if (!saw_roll_playing) +static void saw_playback(i32 frame_count) { + if (!saw_playback_on) return; + saw_roll_t *roll = saw_rolls; + for (i32 i = 0; i < SHEET_SIZE; i++) { - saw_sheet_note_t *p = saw_roll_sheet.notes + i; + saw_sheet_note_t *p = roll->sheet.notes + i; if (!p->enabled) continue; - i64 frame = (p->time * SAW_SAMPLE_RATE) / saw_roll_sheet.rate; - if (saw_roll_frame + frame_count <= frame || - saw_roll_frame > frame) + i64 frame = (p->time * SAW_SAMPLE_RATE) / roll->sheet.rate; + if (saw_playback_frame + frame_count <= frame || + saw_playback_frame > frame) continue; if (saw_voice_on[VOICE_COUNT - 1]) continue; @@ -119,15 +132,15 @@ static void saw_roll_playback(i32 frame_count) { saw_voice_on[0] = 1; saw_voice_pitch[0] = p->pitch; saw_voice_time[0] = 0; - saw_voice_duration[0] = ((f64) p->duration) / saw_roll_sheet.rate; + saw_voice_duration[0] = ((f64) p->duration) / roll->sheet.rate; } - saw_roll_frame += frame_count; + saw_playback_frame += frame_count; } static void saw_audio(ma_device *device, void *void_out_, void const *void_in_, ma_uint32 frame_count) { - saw_roll_playback(frame_count); + saw_playback(frame_count); f32 *out = (f32 *) void_out_; @@ -173,49 +186,12 @@ static void saw_audio(ma_device *device, void *void_out_, # pragma GCC diagnostic pop #endif -static void saw_init(void) { - sapp_set_window_title("saw"); - -#ifdef SOKOL_GLCORE33 - saw_nvg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); -#else - saw_nvg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); -#endif - - ma_device_config config = ma_device_config_init( - ma_device_type_playback); - - config.playback.format = ma_format_f32; - config.playback.channels = SAW_CHANNEL_COUNT; - config.sampleRate = SAW_SAMPLE_RATE; - config.dataCallback = saw_audio; - config.pUserData = NULL; - - if (ma_device_init(NULL, &config, &saw_ma) != MA_SUCCESS) { - printf("ma_device_init failed.\n"); - return; - } - - ma_device_start(&saw_ma); -} - -static void saw_frame(void) { - i32 width = sapp_width(); - i32 height = sapp_height(); - - glViewport(0, 0, width, height); - glClearColor(.23f, .19f, .16f, 1.f); - glClearDepth(1.); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | - GL_STENCIL_BUFFER_BIT); - - nvgBeginFrame(saw_nvg, width, height, sapp_dpi_scale()); - +static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, + i64 height) { // Piano roll // - i32 x0 = 20; - i32 y0 = 20; + i32 frame_height = sapp_height(); i32 pianokey_height = 35; i32 pianokey_width = 100; @@ -228,13 +204,14 @@ static void saw_frame(void) { for (i32 pitch = 0; pitch < ROLL_SIZE; pitch++) { i32 x = x0 + roll_border; - i32 y = height - y0 - (pitch + 1) * pianokey_height + roll_border; + i32 y = frame_height - y0 - (pitch + 1) * pianokey_height + + roll_border; i32 w = pianokey_width - roll_border * 2; i32 h = pianokey_height - roll_border * 2; - if (y > height - pianokey_height) + if (y > y0 + height) continue; - if (y < 0) + if (y < y0) break; nvgBeginPath(saw_nvg); @@ -243,7 +220,7 @@ static void saw_frame(void) { i8 has_cursor = saw_mouse_x >= x && saw_mouse_x < x + w && saw_mouse_y >= y && saw_mouse_y < y + h; - nvgFillColor(saw_nvg, saw_roll_turned_off[pitch] + nvgFillColor(saw_nvg, roll->turned_off[pitch] ? nvgRGBA(220, 220, 220, 160) : has_cursor ? nvgRGBA(200, 200, 255, 255) : nvgRGBA(220, 220, 220, 255)); @@ -255,9 +232,9 @@ static void saw_frame(void) { if (has_cursor) { hover_any = 1; - if (!saw_roll_turned_off[pitch] && + if (!roll->turned_off[pitch] && (saw_lbutton_click || - (saw_lbutton_down && saw_roll_last_index != pitch)) && + (saw_lbutton_down && roll->last_index != pitch)) && !saw_voice_on[VOICE_COUNT - 1]) { for (i32 n = VOICE_COUNT - 1; n > 0; --n) { saw_voice_on[n] = saw_voice_on[n - 1]; @@ -273,24 +250,24 @@ static void saw_frame(void) { } if (saw_rbutton_click) - saw_roll_turned_off[pitch] = !saw_roll_turned_off[pitch]; + roll->turned_off[pitch] = !roll->turned_off[pitch]; - saw_roll_last_index = pitch; + roll->last_index = pitch; } } if (!hover_any) - saw_roll_last_index = -1; + roll->last_index = -1; // Draw music sheet // for (i32 pitch = 0; pitch < ROLL_SIZE; pitch++) { - i32 y = height - y0 - (pitch + 1) * pianokey_height; + i32 y = frame_height - y0 - (pitch + 1) * pianokey_height; - if (y > height - pianokey_height) + if (y > y0 + height - pianokey_height) continue; - if (y < 0) + if (y < y0) break; i32 h = pianokey_height; @@ -298,13 +275,13 @@ static void saw_frame(void) { for (i32 t = 0;; t++) { i32 x = x0 + pianokey_width + sheet_offset + t * sheet_scale; - if (x >= width - roll_border) + if (x >= x0 + width - roll_border) break; i32 note = -1; for (i32 n = 0; n < SHEET_SIZE; n++) { - saw_sheet_note_t *p = saw_roll_sheet.notes + n; + saw_sheet_note_t *p = roll->sheet.notes + n; if (p->enabled && p->pitch == pitch && t >= p->time && t < p->time + p->duration) { note = n; @@ -327,7 +304,7 @@ static void saw_frame(void) { has_cursor = saw_mouse_x >= x && saw_mouse_x < x + w && saw_mouse_y >= y && saw_mouse_y < y + h; - nvgFillColor(saw_nvg, saw_roll_turned_off[pitch] + nvgFillColor(saw_nvg, roll->turned_off[pitch] ? nvgRGBA(160, 150, 120, 100) : has_cursor ? nvgRGBA(180, 180, 220, 160) @@ -337,27 +314,27 @@ static void saw_frame(void) { // Empty cell input // - if (!saw_grid_input && !saw_roll_turned_off[pitch] && + if (!roll->grid_input && !roll->turned_off[pitch] && has_cursor && saw_lbutton_click) { for (i32 n = 0; n < SHEET_SIZE; n++) - if (!saw_roll_sheet.notes[n].enabled) { - saw_roll_sheet.notes[n] = (saw_sheet_note_t) { + if (!roll->sheet.notes[n].enabled) { + roll->sheet.notes[n] = (saw_sheet_note_t) { .enabled = 1, .time = t, .duration = 1, .pitch = pitch }; - saw_grid_input = 1; - saw_grid_note = n; - saw_grid_pitch = pitch; - saw_grid_time = t; + roll->grid_input = 1; + roll->grid_note = n; + roll->grid_pitch = pitch; + roll->grid_time = t; break; } } } else { - i32 w = sheet_scale * saw_roll_sheet.notes[note].duration; + i32 w = sheet_scale * roll->sheet.notes[note].duration; has_cursor = saw_mouse_x >= x && saw_mouse_x < x + w && saw_mouse_y >= y && saw_mouse_y < y + h; - if (t == saw_roll_sheet.notes[note].time) { + if (t == roll->sheet.notes[note].time) { // Draw note // @@ -374,7 +351,7 @@ static void saw_frame(void) { // if (has_cursor && saw_rbutton_down) - saw_roll_sheet.notes[note].enabled = 0; + roll->sheet.notes[note].enabled = 0; } } } @@ -383,63 +360,101 @@ static void saw_frame(void) { // Note stretching input // - if (saw_grid_input) { + if (roll->grid_input) { if (saw_lbutton_down) { i32 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset) / sheet_scale; if (t >= 0) { - saw_sheet_note_t *p = saw_roll_sheet.notes + saw_grid_note; - if (saw_grid_time <= t) { - p->time = saw_grid_time; - p->duration = 1 + t - saw_grid_time; + saw_sheet_note_t *p = roll->sheet.notes + roll->grid_note; + if (roll->grid_time <= t) { + p->time = roll->grid_time; + p->duration = 1 + t - roll->grid_time; } else { p->time = t; - p->duration = saw_grid_time - t; + p->duration = roll->grid_time - t; } } - saw_sheet_note_t *p = saw_roll_sheet.notes + saw_grid_note; + saw_sheet_note_t *p = roll->sheet.notes + roll->grid_note; for (i32 n = 0; n < SHEET_SIZE; n++) { - if (n == saw_grid_note) + if (n == roll->grid_note) continue; - saw_sheet_note_t *q = saw_roll_sheet.notes + n; + saw_sheet_note_t *q = roll->sheet.notes + n; if (!q->enabled || q->pitch != p->pitch) continue; - if (q->time < saw_grid_time && + if (q->time < roll->grid_time && q->time + q->duration > p->time) { p->time = q->time + q->duration; - p->duration = saw_grid_time > p->time - ? saw_grid_time - p->time + p->duration = roll->grid_time > p->time + ? roll->grid_time - p->time : 1; } - if (q->time > saw_grid_time && + if (q->time > roll->grid_time && q->time < p->time + p->duration) { - p->time = saw_grid_time; - p->duration = q->time - saw_grid_time; + p->time = roll->grid_time; + p->duration = q->time - roll->grid_time; assert(p->duration > 0); } } } else - saw_grid_input = 0; + roll->grid_input = 0; } // Playback indicator // - f64 playback_time = ((f64) saw_roll_frame) / SAW_SAMPLE_RATE; + f64 playback_time = ((f64) saw_playback_frame) / SAW_SAMPLE_RATE; i32 x = x0 + pianokey_width + sheet_offset - roll_border * 2 + - (i32) (playback_time * saw_roll_sheet.rate * sheet_scale + - .5); - i32 y = 0; + (i32) (playback_time * roll->sheet.rate * sheet_scale + .5); i32 w = roll_border * 4; - i32 h = height - y0; nvgBeginPath(saw_nvg); - nvgRect(saw_nvg, x, y, w, h); + nvgRect(saw_nvg, x, y0, w, height); nvgFillColor(saw_nvg, nvgRGBA(240, 240, 80, 220)); nvgFill(saw_nvg); +} + +static void saw_init(void) { + sapp_set_window_title("saw"); + +#ifdef SOKOL_GLCORE33 + saw_nvg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); +#else + saw_nvg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); +#endif + + ma_device_config config = ma_device_config_init( + ma_device_type_playback); + + config.playback.format = ma_format_f32; + config.playback.channels = SAW_CHANNEL_COUNT; + config.sampleRate = SAW_SAMPLE_RATE; + config.dataCallback = saw_audio; + config.pUserData = NULL; + + if (ma_device_init(NULL, &config, &saw_ma) != MA_SUCCESS) { + printf("ma_device_init failed.\n"); + return; + } + + ma_device_start(&saw_ma); +} + +static void saw_frame(void) { + i32 frame_width = sapp_width(); + i32 frame_height = sapp_height(); + + glViewport(0, 0, frame_width, frame_height); + glClearColor(.23f, .19f, .16f, 1.f); + glClearDepth(1.); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); + + nvgBeginFrame(saw_nvg, frame_width, frame_height, sapp_dpi_scale()); + + saw_ui_roll(saw_rolls, 0, 0, frame_width, frame_height); nvgEndFrame(saw_nvg); @@ -493,9 +508,9 @@ static void saw_event(sapp_event const *event) { if (!event->key_repeat) { switch (event->key_code) { case SAPP_KEYCODE_SPACE: - saw_roll_playing = !saw_roll_playing; + saw_playback_on = !saw_playback_on; break; - case SAPP_KEYCODE_ENTER: saw_roll_frame = 0; break; + case SAPP_KEYCODE_ENTER: saw_playback_frame = 0; break; default:; } } -- cgit v1.2.3