summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2023-09-30 12:15:02 +0200
committerMitya Selivanov <automainint@guattari.tech>2023-09-30 12:15:02 +0200
commit5a38fcf873c9a507b79ab799edf264c2e0654460 (patch)
tree43bfa854c5af9f6aef1328d9e3db4175985bdf8f
parent17bc2c6cec21dd77a2ca68390b417b2cd7af55b6 (diff)
downloadsaw-5a38fcf873c9a507b79ab799edf264c2e0654460.zip
Refactor piano roll time calculation
-rw-r--r--source/saw/main.c338
1 files 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);
}