From 02a9be36329d161064040387d7b2ef18da4cd7fe Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 30 Sep 2023 14:33:41 +0200 Subject: Track looping --- TODO | 2 +- source/saw/main.c | 115 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 92 insertions(+), 25 deletions(-) diff --git a/TODO b/TODO index 45c00b7..d739785 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ To-Do list - Grid size changing -- Track looping - UI: Playback controls - UI: Effects stack - UI: Volume control @@ -52,3 +51,4 @@ Done - UI: Track composing - UI: Instrument settings - State load and store +- Track looping diff --git a/source/saw/main.c b/source/saw/main.c index 43c9f3f..2a62e80 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -87,6 +87,7 @@ typedef struct { saw_roll_note_t notes[SHEET_SIZE]; i64 time; i64 duration; + i64 loop_duration; i64 offset_x; i64 offset_y; @@ -98,6 +99,7 @@ typedef struct { i32 grid_time; i8 offset_x_input; i8 offset_y_input; + i8 loop_input; } saw_roll_t; typedef struct { @@ -247,13 +249,21 @@ static void saw_playback(i32 frame_count) { saw_playback_frame >= roll->time + roll->duration) continue; + i64 play_frame = roll->loop_duration == 0 + ? saw_playback_frame + : saw_playback_frame - + ((saw_playback_frame + frame_count - + 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 frame = roll->time + note->time; - if (saw_playback_frame + frame_count <= frame || - saw_playback_frame > frame) + i64 note_frame = roll->time + note->time; + if (play_frame + frame_count <= note_frame || + play_frame > note_frame) continue; saw_play_voice(saw_tracks + roll->track, roll, note->pitch, @@ -510,6 +520,7 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { .notes = { 0 }, .time = frame, .duration = (6 * SAW_SAMPLE_RATE) / grid_rate, + .loop_duration = 0, .offset_x = 0, .offset_y = ROLL_DEFAULT_OFFSET_Y, @@ -519,6 +530,7 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { .grid_time = 0, .offset_x_input = 0, .offset_y_input = 0, + .loop_input = 0, }; saw_current_roll = n; @@ -527,7 +539,7 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { } } - // Offset input + // Panning input // if (saw_mbutton_click) { @@ -878,12 +890,13 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i32 frame_height = sapp_height(); i32 text_height = 35; + i32 header_height = 35; i32 pianokey_height = 35; i32 pianokey_width = 100; i32 border = 2; i32 sheet_offset = 40; - i32 sheet_scale = (20 * SAW_SAMPLE_RATE) / (10000 * roll->rate); + i32 sheet_scale = (40 * SAW_SAMPLE_RATE) / (10000 * roll->rate); // Title text // @@ -903,6 +916,40 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, title.values, title.values + title.size); nvgFill(saw_nvg); + // Loop control + // + { + i64 x = x0 + pianokey_width + sheet_offset; + i64 y = frame_height - y0 - height + text_height; + i64 w = width - pianokey_width - sheet_offset; + i64 h = header_height / 5; + + nvgBeginPath(saw_nvg); + nvgRect(saw_nvg, x, y, w, h); + nvgRect(saw_nvg, x, y + h * 4, w, h); + nvgFillColor(saw_nvg, nvgRGBA(80, 60, 50, 160)); + nvgFill(saw_nvg); + + if (saw_mouse_x >= x && saw_mouse_y >= y && saw_mouse_x < x + w && + saw_mouse_y < y + header_height && !roll->loop_input && + saw_lbutton_click) + roll->loop_input = 1; + + if (roll->loop_input && saw_lbutton_down) { + i32 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset - + roll->offset_x + sheet_scale / 2) / + sheet_scale; + + if (t <= 0) + roll->loop_duration = 0; + else + roll->loop_duration = (t * SAW_SAMPLE_RATE) / roll->rate; + } + } + + if (!saw_lbutton_down) + roll->loop_input = 0; + // Piano roll // @@ -917,7 +964,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (y > frame_height - y0 - pianokey_height) continue; - if (y < frame_height - y0 - height + text_height) + if (y < frame_height - y0 - height + text_height + header_height) break; nvgBeginPath(saw_nvg); @@ -954,7 +1001,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (!hover_any) roll->last_index = -1; - // Offset input + // Panning input // if (saw_mbutton_click) { @@ -982,7 +1029,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (y > frame_height - y0 - pianokey_height) continue; - if (y < frame_height - y0 - height + text_height) + if (y < frame_height - y0 - height + text_height + header_height) break; i32 h = pianokey_height; @@ -1023,12 +1070,16 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, nvgRect(saw_nvg, x + border, y + border, w - border * 2, h - border * 2); + i8 turned_off = roll->pitch_turned_off[pitch] || + (roll->loop_duration > 0 && + t >= (roll->loop_duration * roll->rate) / + SAW_SAMPLE_RATE); + i8 has_cursor = !roll->grid_input && saw_mouse_x >= x && saw_mouse_x < x + w && saw_mouse_y >= y && saw_mouse_y < y + h; - nvgFillColor(saw_nvg, roll->pitch_turned_off[pitch] - ? nvgRGBA(160, 150, 120, 100) + nvgFillColor(saw_nvg, turned_off ? nvgRGBA(160, 150, 120, 100) : has_cursor ? nvgRGBA(180, 180, 220, 160) : nvgRGBA(170, 160, 140, 150)); @@ -1037,8 +1088,8 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, // Empty cell input // - if (!roll->grid_input && !roll->pitch_turned_off[pitch] && - has_cursor && saw_lbutton_click) { + if (!roll->grid_input && !turned_off && has_cursor && + saw_lbutton_click) for (i32 n = 0; n < SHEET_SIZE; n++) if (!roll->notes[n].enabled) { roll->notes[n] = (saw_roll_note_t) { @@ -1052,7 +1103,6 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, roll->grid_time = t; break; } - } } } @@ -1069,7 +1119,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (y + pianokey_height > frame_height - y0) continue; - if (y < frame_height - y0 - height + text_height) + if (y < frame_height - y0 - height + text_height + header_height) continue; i64 x = x0 + pianokey_width + sheet_offset + roll->offset_x + @@ -1090,10 +1140,16 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, // Draw note // + i64 frame = saw_playback_frame; + + if (frame >= roll->time && frame < roll->time + roll->duration && + roll->loop_duration > 0) + frame -= ((frame - roll->time) / roll->loop_duration) * + roll->loop_duration; + i8 is_playing = saw_playback_on && - saw_playback_frame >= roll->time + note->time && - saw_playback_frame < - roll->time + note->time + note->duration; + frame >= roll->time + note->time && + frame < roll->time + note->time + note->duration; i32 underflow = (x0 + pianokey_width + sheet_offset + sheet_scale - 1 - x) / @@ -1186,10 +1242,16 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, // Playback indicator // { + i64 frame = saw_playback_frame; + + if (frame >= roll->time && frame < roll->time + roll->duration && + roll->loop_duration > 0) + frame -= ((frame - roll->time) / roll->loop_duration) * + roll->loop_duration; + i32 x = x0 + pianokey_width + sheet_offset + roll->offset_x - border * 2 + - ((saw_playback_frame - roll->time) * roll->rate * - sheet_scale + + ((frame - roll->time) * roll->rate * sheet_scale + SAW_SAMPLE_RATE / 2) / SAW_SAMPLE_RATE; i32 w = border * 4; @@ -1271,13 +1333,16 @@ static void saw_init(void) { .notes = { 0 }, .time = 0, .duration = (48 * SAW_SAMPLE_RATE) / 6, - .grid_input = 0, - .grid_note = 0, - .grid_time = 0, - .offset_x_input = 0, - .offset_y_input = 0, + .loop_duration = 0, .offset_x = 0, .offset_y = ROLL_DEFAULT_OFFSET_Y, + + .grid_input = 0, + .grid_note = 0, + .grid_time = 0, + .offset_x_input = 0, + .offset_y_input = 0, + .loop_input = 0, }; } @@ -1408,6 +1473,7 @@ static void saw_init(void) { SCAN_(" time %lld", 1, &roll->time); SCAN_(" duration %lld", 1, &roll->duration); + SCAN_(" loop_duration %lld", 1, &roll->loop_duration); SCAN_(" offset %lld %lld", 2, &roll->offset_x, &roll->offset_y); } @@ -1565,6 +1631,7 @@ static void saw_cleanup(void) { fprintf(f, "time %lld\n", roll->time); fprintf(f, "duration %lld\n", roll->duration); + fprintf(f, "loop_duration %lld\n", roll->loop_duration); fprintf(f, "offset %lld %lld\n\n", roll->offset_x, roll->offset_y); } -- cgit v1.2.3