summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2023-09-30 14:33:41 +0200
committerMitya Selivanov <automainint@guattari.tech>2023-09-30 14:33:41 +0200
commit02a9be36329d161064040387d7b2ef18da4cd7fe (patch)
treeb5a0c2e776cbcdc7e9b5f6ea1e2237a0ff3c6c66
parentca7a3b138ed70d67ceb3a9033f4feb4c8acca4cc (diff)
downloadsaw-02a9be36329d161064040387d7b2ef18da4cd7fe.zip
Track looping
-rw-r--r--TODO2
-rw-r--r--source/saw/main.c115
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);
}