From 5a38fcf873c9a507b79ab799edf264c2e0654460 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 30 Sep 2023 12:15:02 +0200 Subject: Refactor piano roll time calculation --- source/saw/main.c | 338 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 198 insertions(+), 140 deletions(-) diff --git a/source/saw/main.c b/source/saw/main.c index e505e4f..3c9accc 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -80,14 +80,14 @@ typedef struct { i8 pitch_turned_off[PITCH_COUNT]; i64 rate; saw_roll_note_t notes[SHEET_SIZE]; - i64 size; + i64 time; + i64 duration; i64 offset_x; i64 offset_y; // dynamic properties // i32 last_index; - i64 frame; i8 grid_input; i32 grid_note; i32 grid_time; @@ -220,7 +220,7 @@ static void saw_play_voice(saw_track_t *track, saw_roll_t *roll, saw_voices[0] = (saw_voice_t) { .enabled = 1, .time = 0, - .duration = ((f64) duration) / roll->rate, + .duration = (f64) duration / (f64) SAW_SAMPLE_RATE, .frequency = saw_pitch_frequency(pitch), .amplitude = saw_pitch_amplitude(pitch) * track->volume, .phase = { @@ -238,17 +238,15 @@ static void saw_playback(i32 frame_count) { for (i32 k = 0; k < ROLL_COUNT; k++) { saw_roll_t *roll = saw_rolls + k; if (!roll->enabled || - saw_playback_frame + frame_count <= roll->frame || - saw_playback_frame >= - roll->frame + (roll->size * SAW_SAMPLE_RATE) / roll->rate) + saw_playback_frame + frame_count <= roll->time || + saw_playback_frame >= roll->time + roll->duration) continue; for (i32 i = 0; i < SHEET_SIZE; i++) { saw_roll_note_t *note = roll->notes + i; if (!note->enabled) continue; - i64 frame = roll->frame + - (note->time * SAW_SAMPLE_RATE) / roll->rate; + i64 frame = roll->time + note->time; if (saw_playback_frame + frame_count <= frame || saw_playback_frame > frame) continue; @@ -347,8 +345,8 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { i64 top = frame_height - y0 - height + track_height; i64 bottom = frame_height - y0; i64 dx = x0 + saw_compose.offset_x; - i64 l = dx + (roll->frame * grid_scale) / SAW_SAMPLE_RATE; - i64 r = l + (roll->size * grid_scale) / roll->rate; + i64 l = dx + (roll->time * grid_scale) / SAW_SAMPLE_RATE; + i64 r = l + (roll->duration * grid_scale) / SAW_SAMPLE_RATE; i64 u = frame_height - y0 - height + track_height + saw_compose.offset_y + roll->track * track_height; i64 d = u + track_height; @@ -374,10 +372,8 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { i8 is_choosen = (saw_current_roll == i); i8 is_playing = saw_playback_on && - saw_playback_frame >= roll->frame && - saw_playback_frame < - roll->frame + (roll->size * SAW_SAMPLE_RATE) / - roll->rate; + saw_playback_frame >= roll->time && + saw_playback_frame < roll->time + roll->duration; i8 has_cursor = (saw_compose.grid_input && saw_compose.grid_roll == i) || @@ -405,16 +401,19 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { i64 cell = ((saw_mouse_x - saw_compose.offset_x) * grid_rate) / grid_scale; - i64 c0 = (roll->frame * grid_rate) / SAW_SAMPLE_RATE; - i64 c1 = c0 + (roll->size * grid_rate) / roll->rate; + i64 c0 = (roll->time * grid_rate) / SAW_SAMPLE_RATE; + i64 c1 = c0 + + (roll->duration * grid_rate) / SAW_SAMPLE_RATE; saw_compose.grid_input = 1; saw_compose.grid_roll = saw_current_roll; if (cell - c0 > c1 - cell) { saw_compose.grid_cell = c0; - roll->size = ((cell - c0 + 1) * roll->rate) / grid_rate; + roll->duration = ((cell - c0 + 1) * SAW_SAMPLE_RATE) / + grid_rate; } else { saw_compose.grid_cell = c1 - 1; - roll->size = ((c1 - cell + 1) * roll->rate) / grid_rate; + roll->duration = ((c1 - cell + 1) * SAW_SAMPLE_RATE) / + grid_rate; } } else { saw_current_roll = saw_compose.rolls[i]; @@ -484,12 +483,12 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { .pitch_turned_off = { 0 }, .rate = 6, .notes = { 0 }, - .size = 6 / grid_rate, + .time = frame, + .duration = (6 * SAW_SAMPLE_RATE) / grid_rate, .offset_x = 0, .offset_y = ROLL_DEFAULT_OFFSET_Y, .last_index = -1, - .frame = frame, .grid_input = 0, .grid_note = 0, .grid_time = 0, @@ -527,14 +526,16 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { if (cell >= 0) { if (saw_compose.grid_cell <= cell) { - p->frame = (saw_compose.grid_cell * SAW_SAMPLE_RATE) / - grid_rate; - p->size = ((1 + cell - saw_compose.grid_cell) * p->rate) / + p->time = (saw_compose.grid_cell * SAW_SAMPLE_RATE) / grid_rate; + p->duration = ((1 + cell - saw_compose.grid_cell) * + SAW_SAMPLE_RATE) / + grid_rate; } else { - p->frame = (cell * SAW_SAMPLE_RATE) / grid_rate; - p->size = ((1 + saw_compose.grid_cell - cell) * p->rate) / - grid_rate; + p->time = (cell * SAW_SAMPLE_RATE) / grid_rate; + p->duration = ((1 + saw_compose.grid_cell - cell) * + SAW_SAMPLE_RATE) / + grid_rate; } } @@ -544,28 +545,29 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { saw_roll_t *q = saw_rolls + i; if (!q->enabled || p->track != q->track) continue; - i64 q_cell = (q->frame * grid_rate) / SAW_SAMPLE_RATE; - i64 q_size = (q->size * grid_rate) / q->rate; + i64 q_cell = (q->time * grid_rate) / SAW_SAMPLE_RATE; + i64 q_size = (q->duration * grid_rate) / SAW_SAMPLE_RATE; if (saw_compose.grid_cell < q_cell && cell >= q_cell) { - cell = q_cell - 1; - p->frame = (saw_compose.grid_cell * SAW_SAMPLE_RATE) / - grid_rate; - p->size = ((q_cell - saw_compose.grid_cell) * p->rate) / + cell = q_cell - 1; + p->time = (saw_compose.grid_cell * SAW_SAMPLE_RATE) / grid_rate; + p->duration = ((q_cell - saw_compose.grid_cell) * + SAW_SAMPLE_RATE) / + grid_rate; } if (saw_compose.grid_cell > q_cell && cell < q_cell + q_size) { - cell = q_cell + q_size; - p->frame = ((q_cell + q_size) * SAW_SAMPLE_RATE) / - grid_rate; - p->size = ((1 + saw_compose.grid_cell - q_cell - q_size) * - p->rate) / - grid_rate; + cell = q_cell + q_size; + p->time = ((q_cell + q_size) * SAW_SAMPLE_RATE) / grid_rate; + p->duration = ((1 + saw_compose.grid_cell - q_cell - + q_size) * + SAW_SAMPLE_RATE) / + grid_rate; } } - if (p->size <= 0) - p->size = 1; + if (p->duration <= 0) + p->duration = SAW_SAMPLE_RATE / grid_rate; } else saw_compose.grid_input = 0; } @@ -855,7 +857,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i32 border = 2; i32 sheet_offset = 40; - i32 sheet_scale = 20; + i32 sheet_scale = (20 * SAW_SAMPLE_RATE) / (10000 * roll->rate); // Title text // @@ -959,7 +961,8 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i32 h = pianokey_height; - for (i32 t = 0; t < roll->size; t++) { + for (i32 t = 0; + t < (roll->duration * roll->rate) / SAW_SAMPLE_RATE; t++) { i32 x = x0 + pianokey_width + sheet_offset + t * sheet_scale + roll->offset_x; @@ -970,109 +973,128 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, for (i32 n = 0; n < SHEET_SIZE; n++) { saw_roll_note_t *p = roll->notes + n; - if (p->enabled && p->pitch == pitch && t >= p->time && - t < p->time + p->duration) { + if (p->enabled && p->pitch == pitch && + t >= (p->time * roll->rate) / SAW_SAMPLE_RATE && + t < ((p->time + p->duration) * roll->rate) / + SAW_SAMPLE_RATE) { note = n; break; } } - if (note == -1) { - // Draw empty cell - // + if (note != -1) + continue; - if (x < x0 + pianokey_width + sheet_offset) - continue; + // Draw empty cell + // - i32 w = sheet_scale; + if (x < x0 + pianokey_width + sheet_offset) + continue; - nvgBeginPath(saw_nvg); - nvgRect(saw_nvg, x + border, y + border, w - border * 2, - h - border * 2); - - 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) - : has_cursor - ? nvgRGBA(180, 180, 220, 160) - : nvgRGBA(170, 160, 140, 150)); - nvgFill(saw_nvg); + i32 w = sheet_scale; - // Empty cell input - // - - if (!roll->grid_input && !roll->pitch_turned_off[pitch] && - 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) { - .enabled = 1, .time = t, .duration = 1, .pitch = pitch - }; - roll->grid_input = 1; - roll->grid_note = n; - roll->grid_time = t; - break; - } - } - } else { - i32 w = sheet_scale * roll->notes[note].duration; + nvgBeginPath(saw_nvg); + nvgRect(saw_nvg, x + border, y + border, w - border * 2, + h - border * 2); - i8 has_cursor = (roll->grid_input && - roll->grid_note == note) || - (saw_mouse_x >= x && saw_mouse_x < x + w && - saw_mouse_y >= y && saw_mouse_y < y + h); + i8 has_cursor = !roll->grid_input && saw_mouse_x >= x && + saw_mouse_x < x + w && saw_mouse_y >= y && + saw_mouse_y < y + h; - if (x + w - sheet_scale < x0 + pianokey_width + sheet_offset) - continue; + nvgFillColor(saw_nvg, roll->pitch_turned_off[pitch] + ? nvgRGBA(160, 150, 120, 100) + : has_cursor + ? nvgRGBA(180, 180, 220, 160) + : nvgRGBA(170, 160, 140, 150)); + nvgFill(saw_nvg); - if (t == roll->notes[note].time) { - // Draw note - // + // Empty cell input + // + + if (!roll->grid_input && !roll->pitch_turned_off[pitch] && + 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) { + .enabled = 1, + .time = (t * SAW_SAMPLE_RATE) / roll->rate, + .duration = SAW_SAMPLE_RATE / roll->rate, + .pitch = pitch + }; + roll->grid_input = 1; + roll->grid_note = n; + roll->grid_time = t; + break; + } + } + } + } - i64 t0 = (t * SAW_SAMPLE_RATE) / roll->rate; - i64 t1 = ((t + roll->notes[note].duration) * - SAW_SAMPLE_RATE) / - roll->rate; + // Draw notes + // - i8 is_playing = saw_playback_on && - saw_playback_frame - roll->frame >= t0 && - saw_playback_frame - roll->frame < t1; + for (i32 n = 0; n < SHEET_SIZE; n++) { + saw_roll_note_t *note = roll->notes + n; + if (!note->enabled) + continue; - i32 underflow = (x0 + pianokey_width + sheet_offset + - sheet_scale - 1 - x) / - sheet_scale; - i32 overflow = (x + w + sheet_scale + 1 - x0 - width) / - sheet_scale; + i32 y = frame_height - y0 - (note->pitch + 1) * pianokey_height + + roll->offset_y; - if (underflow > 0) { - x += underflow * sheet_scale; - w -= underflow * sheet_scale; - } + if (y + border > frame_height - y0 - pianokey_height) + continue; + if (y + border < frame_height - y0 - height + text_height) + break; - if (overflow > 0) - w -= overflow * sheet_scale; + i64 x = x0 + pianokey_width + sheet_offset + roll->offset_x + + (note->time * roll->rate * sheet_scale) / SAW_SAMPLE_RATE; + i64 w = (note->duration * roll->rate * sheet_scale) / + SAW_SAMPLE_RATE; + i32 h = pianokey_height; - nvgBeginPath(saw_nvg); - nvgRect(saw_nvg, x + border, y + border, w - border * 2, - h - border * 2); + i8 has_cursor = (roll->grid_input && roll->grid_note == n) || + (saw_mouse_x >= x && saw_mouse_x < x + w && + saw_mouse_y >= y && saw_mouse_y < y + h); - nvgFillColor(saw_nvg, - is_playing ? nvgRGBA(255, 230, 200, 255) - : has_cursor ? nvgRGBA(190, 190, 230, 255) - : nvgRGBA(180, 180, 180, 255)); - nvgFill(saw_nvg); + if (x + w - sheet_scale < x0 + pianokey_width + sheet_offset) + continue; - // Note input - // + // Draw note + // - if (has_cursor && saw_rbutton_down) - roll->notes[note].enabled = 0; - } - } + i8 is_playing = saw_playback_on && + saw_playback_frame >= roll->time + note->time && + saw_playback_frame < + roll->time + note->time + note->duration; + + i32 underflow = (x0 + pianokey_width + sheet_offset + + sheet_scale - 1 - x) / + sheet_scale; + i32 overflow = (x + w + sheet_scale + 1 - x0 - width) / + sheet_scale; + + if (underflow > 0) { + x += underflow * sheet_scale; + w -= underflow * sheet_scale; } + + if (overflow > 0) + w -= overflow * sheet_scale; + + nvgBeginPath(saw_nvg); + nvgRect(saw_nvg, x + border, y + border, w - border * 2, + h - border * 2); + + nvgFillColor(saw_nvg, is_playing ? nvgRGBA(255, 230, 200, 255) + : has_cursor ? nvgRGBA(190, 190, 230, 255) + : nvgRGBA(180, 180, 180, 255)); + nvgFill(saw_nvg); + + // Note input + // + + if (has_cursor && saw_rbutton_down) + note->enabled = 0; } // Note stretching input @@ -1088,11 +1110,15 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (t >= 0) { if (roll->grid_time <= t) { - p->time = roll->grid_time; - p->duration = 1 + t - roll->grid_time; + p->time = (roll->grid_time * SAW_SAMPLE_RATE) / roll->rate; + p->duration = ((1 + t - roll->grid_time) * + SAW_SAMPLE_RATE) / + roll->rate; } else { - p->time = t; - p->duration = 1 + roll->grid_time - t; + p->time = (t * SAW_SAMPLE_RATE) / roll->rate; + p->duration = ((1 + roll->grid_time - t) * + SAW_SAMPLE_RATE) / + roll->rate; } } @@ -1102,17 +1128,26 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, saw_roll_note_t *q = roll->notes + n; if (!q->enabled || q->pitch != p->pitch) continue; - if (q->time < roll->grid_time && + if (q->time < + (roll->grid_time * SAW_SAMPLE_RATE) / roll->rate && q->time + q->duration > p->time) { p->time = q->time + q->duration; - p->duration = roll->grid_time > p->time - ? roll->grid_time - p->time + 1 - : 1; + p->duration = (roll->grid_time * SAW_SAMPLE_RATE) / + roll->rate > + p->time + ? ((roll->grid_time + 1) * + SAW_SAMPLE_RATE) / + roll->rate - + p->time + : SAW_SAMPLE_RATE / roll->rate; } - if (q->time > roll->grid_time && + if (q->time > + (roll->grid_time * SAW_SAMPLE_RATE) / roll->rate && q->time < p->time + p->duration) { - p->time = roll->grid_time; - p->duration = q->time - roll->grid_time; + p->time = (roll->grid_time * SAW_SAMPLE_RATE) / roll->rate; + p->duration = q->time - + (roll->grid_time * SAW_SAMPLE_RATE) / + roll->rate; assert(p->duration > 0); } } @@ -1125,7 +1160,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, { i32 x = x0 + pianokey_width + sheet_offset + roll->offset_x - border * 2 + - ((saw_playback_frame - roll->frame) * roll->rate * + ((saw_playback_frame - roll->time) * roll->rate * sheet_scale + SAW_SAMPLE_RATE / 2) / SAW_SAMPLE_RATE; @@ -1206,8 +1241,8 @@ static void saw_init(void) { .pitch_turned_off = { 0 }, .rate = 6, .notes = { 0 }, - .frame = 0, - .size = 48, + .time = 0, + .duration = (48 * SAW_SAMPLE_RATE) / 6, .grid_input = 0, .grid_note = 0, .grid_time = 0, @@ -1302,6 +1337,13 @@ static void saw_init(void) { i32 total_rolls; SCAN_(" compose_rolls %d", 1, &total_rolls); + + if (total_rolls < 0 || total_rolls > ROLL_COUNT) { + printf("Invalid roll count: %d\n", total_rolls); + fclose(f); + return; + } + for (i64 i = 0; i < total_rolls; i++) SCAN_(" %lld", 1, saw_compose.rolls + i); @@ -1318,7 +1360,14 @@ static void saw_init(void) { i32 pitch_count; SCAN_(" pitch_turned_off %d", 1, &pitch_count); - for (i64 pitch = 0; pitch < PITCH_COUNT; pitch++) { + + if (pitch_count < 0 || pitch_count > PITCH_COUNT) { + printf("Invalid pitch count: %d\n", pitch_count); + fclose(f); + return; + } + + for (i64 pitch = 0; pitch < pitch_count; pitch++) { i32 flag; SCAN_(" %d", 1, &flag); roll->pitch_turned_off[pitch] = flag ? 1 : 0; @@ -1328,6 +1377,13 @@ static void saw_init(void) { i32 sheet_size; SCAN_(" notes %d", 1, &sheet_size); + + if (sheet_size < 0 || sheet_size > SHEET_SIZE) { + printf("Invalid note count: %d\n", sheet_size); + fclose(f); + return; + } + for (i64 n = 0; n < sheet_size; n++) { i32 flag; SCAN_(" %d %lld %lld %lld", 4, &flag, &roll->notes[n].time, @@ -1335,7 +1391,8 @@ static void saw_init(void) { roll->notes[n].enabled = flag ? 1 : 0; } - SCAN_(" size %lld", 1, &roll->size); + SCAN_(" time %lld", 1, &roll->time); + SCAN_(" duration %lld", 1, &roll->duration); SCAN_(" offset %lld %lld", 2, &roll->offset_x, &roll->offset_y); } @@ -1491,7 +1548,8 @@ static void saw_cleanup(void) { (i32) roll->notes[n].enabled, roll->notes[n].time, roll->notes[n].duration, roll->notes[n].pitch); - fprintf(f, "size %lld\n", roll->size); + fprintf(f, "time %lld\n", roll->time); + fprintf(f, "duration %lld\n", roll->duration); fprintf(f, "offset %lld %lld\n\n", roll->offset_x, roll->offset_y); } -- cgit v1.2.3