summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--TODO5
-rw-r--r--source/saw/main.c231
-rw-r--r--source/saw/profiler.h2
4 files changed, 202 insertions, 38 deletions
diff --git a/README b/README
index 06efed4..c6e8422 100644
--- a/README
+++ b/README
@@ -4,10 +4,10 @@ Music sequencer and audio editor
Tech
- kit - https://guattari.tech/kit.git
+- stb - https://github.com/nothings/stb.git
- sokol_app - https://github.com/floooh/sokol.git
- nanovg - https://github.com/memononen/nanovg.git
- miniaudio - https://github.com/mackron/miniaudio.git
-- portmidi - https://github.com/PortMidi/portmidi.git
Algorithms
diff --git a/TODO b/TODO
index 1434ad6..795ab11 100644
--- a/TODO
+++ b/TODO
@@ -8,6 +8,11 @@ To-Do list
- Build: WebAssembly
- Sound: Kick, snare, cymbal
- UI: Track composing
+- Biquad filter
+- Fourier transform
+- UI: Spectrum view
+- UI: Wave view
+- Improve X11 startup time
Done
diff --git a/source/saw/main.c b/source/saw/main.c
index 298eb2a..e65b5b9 100644
--- a/source/saw/main.c
+++ b/source/saw/main.c
@@ -29,10 +29,23 @@ enum {
SAW_CHANNEL_COUNT = 2,
SAW_SAMPLE_RATE = 44100,
- VOICE_COUNT = 16,
- PIANOROLL_SIZE = 40
+ VOICE_COUNT = 16,
+ ROLL_SIZE = 40,
+ SHEET_SIZE = 200
};
+typedef struct {
+ i8 enabled;
+ i64 time;
+ i64 duration;
+ i64 pitch;
+} saw_sheet_note_t;
+
+typedef struct {
+ i64 rate;
+ saw_sheet_note_t notes[SHEET_SIZE];
+} saw_sheet_t;
+
static struct NVGcontext *saw_nvg;
static ma_device saw_ma;
@@ -41,13 +54,21 @@ static i32 saw_mouse_y = 0;
static i8 saw_lbutton_click = 0;
static i8 saw_lbutton_down = 0;
static i8 saw_rbutton_click = 0;
+static i8 saw_rbutton_down = 0;
+
+static i8 saw_voice_on[VOICE_COUNT] = { 0 };
+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 i8 saw_voice_on[VOICE_COUNT] = { 0 };
-static i32 saw_voice_pitch[VOICE_COUNT] = { 0 };
-static i64 saw_voice_time[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 = 4, .notes = { 0 } };
-static i32 saw_pianoroll_last_index = -1;
-static i8 saw_pianoroll_turned_off[PIANOROLL_SIZE] = { 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;
#ifdef __GNUC__
# pragma GCC diagnostic push
@@ -90,19 +111,28 @@ static void saw_audio(ma_device *device, void *void_out_,
continue;
f64 period = M_PI * 2.;
- f64 frequency = pow(2., 7 + saw_voice_pitch[n] / 12.);
+ f64 frequency = pow(2., 7.3 + saw_voice_pitch[n] / 12.);
+ f64 amplitude = .2;
+
+ // envelope
+ f64 attack = .007;
+ f64 decay = .2;
+ f64 sustain = .2;
+ f64 duration = saw_voice_duration[n];
+ f64 release = .4;
for (i64 i = 0; i < frame_count; i++) {
- f64 t = (f64) saw_voice_time[n] / (f64) SAW_SAMPLE_RATE;
- f64 amplitude = .4 * saw_envelope(t, .007, .2, .2, .6, .4);
- f64 k = period * frequency;
+ f64 t = (f64) saw_voice_time[n] / (f64) SAW_SAMPLE_RATE;
+ f64 a = amplitude * saw_envelope(t, attack, decay, sustain,
+ duration, release);
+ f64 k = period * frequency;
- out[i * 2] += (f32) (sin(k * t) * amplitude);
- out[i * 2 + 1] += (f32) (sin(k * t) * amplitude);
+ out[i * 2] += (f32) (sin(k * t) * a);
+ out[i * 2 + 1] += (f32) (sin(k * t) * a);
saw_voice_time[n]++;
- if (t > 1.0)
+ if (t > duration + release)
saw_voice_on[n] = 0;
}
}
@@ -151,7 +181,7 @@ static void saw_frame(void) {
nvgBeginFrame(saw_nvg, width, height, sapp_dpi_scale());
- // Draw piano roll
+ // Piano roll
//
i32 x0 = 20;
@@ -159,15 +189,18 @@ static void saw_frame(void) {
i32 pianokey_height = 40;
i32 pianokey_width = 100;
- i32 pianokey_border = 2;
+ i32 roll_border = 2;
+
+ i32 sheet_offset = 40;
+ i32 sheet_scale = 20;
i8 hover_any = 0;
- for (i32 i = 0; i < PIANOROLL_SIZE; i++) {
- i32 x = x0 + pianokey_border;
- i32 y = height - y0 - (i + 1) * pianokey_height;
- i32 w = pianokey_width - pianokey_border * 2;
- i32 h = pianokey_height - pianokey_border * 2;
+ for (i32 pitch = 0; pitch < ROLL_SIZE; pitch++) {
+ i32 x = x0 + roll_border;
+ i32 y = 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)
continue;
@@ -180,39 +213,164 @@ 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_pianoroll_turned_off[i]
+ nvgFillColor(saw_nvg, saw_roll_turned_off[pitch]
? nvgRGBA(220, 220, 220, 160)
: has_cursor ? nvgRGBA(200, 200, 255, 255)
: nvgRGBA(220, 220, 220, 255));
nvgFill(saw_nvg);
+ // Piano roll input
+ //
+
if (has_cursor) {
hover_any = 1;
- if (!saw_pianoroll_turned_off[i] &&
+ if (!saw_roll_turned_off[pitch] &&
(saw_lbutton_click ||
- (saw_lbutton_down && saw_pianoroll_last_index != i)) &&
+ (saw_lbutton_down && saw_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];
- saw_voice_pitch[n] = saw_voice_pitch[n - 1];
- saw_voice_time[n] = saw_voice_time[n - 1];
+ saw_voice_on[n] = saw_voice_on[n - 1];
+ saw_voice_pitch[n] = saw_voice_pitch[n - 1];
+ saw_voice_time[n] = saw_voice_time[n - 1];
+ saw_voice_duration[n] = saw_voice_duration[n - 1];
}
- saw_voice_on[0] = 1;
- saw_voice_pitch[0] = i;
- saw_voice_time[0] = 0;
+ saw_voice_on[0] = 1;
+ saw_voice_pitch[0] = pitch;
+ saw_voice_time[0] = 0;
+ saw_voice_duration[0] = .6;
}
if (saw_rbutton_click)
- saw_pianoroll_turned_off[i] = !saw_pianoroll_turned_off[i];
+ saw_roll_turned_off[pitch] = !saw_roll_turned_off[pitch];
- saw_pianoroll_last_index = i;
+ saw_roll_last_index = pitch;
}
}
if (!hover_any)
- saw_pianoroll_last_index = -1;
+ saw_roll_last_index = -1;
+
+ // Draw music sheet
+ //
+
+ for (i32 pitch = 0; pitch < ROLL_SIZE; pitch++) {
+ i32 y = height - y0 - (pitch + 1) * pianokey_height;
+
+ if (y > height - pianokey_height)
+ continue;
+ if (y < 0)
+ break;
+
+ i32 h = pianokey_height;
+
+ for (i32 t = 0;; t++) {
+ i32 x = x0 + pianokey_width + sheet_offset + t * sheet_scale;
+
+ if (x >= 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;
+ if (p->enabled && p->pitch == pitch && t >= p->time &&
+ t < p->time + p->duration) {
+ note = n;
+ break;
+ }
+ }
+
+ i8 has_cursor = 0;
+
+ if (note == -1) {
+ // Draw empty cell
+ //
+
+ i32 w = sheet_scale;
+
+ nvgBeginPath(saw_nvg);
+ nvgRect(saw_nvg, x + roll_border, y + roll_border,
+ w - roll_border * 2, h - roll_border * 2);
+
+ 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]
+ ? nvgRGBA(180, 180, 180, 120)
+ : has_cursor
+ ? nvgRGBA(180, 180, 220, 160)
+ : nvgRGBA(180, 180, 180, 160));
+ nvgFill(saw_nvg);
+
+ // Empty cell input
+ //
+
+ if (!saw_grid_input && !saw_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) {
+ .enabled = 1, .time = t, .duration = 1, .pitch = pitch
+ };
+ saw_grid_input = 1;
+ saw_grid_note = n;
+ saw_grid_pitch = pitch;
+ saw_grid_time = t;
+ break;
+ }
+ }
+ } else {
+ i32 w = sheet_scale * saw_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) {
+ // Draw note
+ //
+
+ nvgBeginPath(saw_nvg);
+ nvgRect(saw_nvg, x + roll_border, y + roll_border,
+ w - roll_border * 2, h - roll_border * 2);
+
+ nvgFillColor(saw_nvg, has_cursor
+ ? nvgRGBA(180, 180, 220, 255)
+ : nvgRGBA(180, 180, 180, 255));
+ nvgFill(saw_nvg);
+
+ // Note input
+ //
+
+ if (has_cursor && saw_rbutton_down)
+ saw_roll_sheet.notes[note].enabled = 0;
+ }
+ }
+ }
+
+ // Note stretching
+ //
+
+ if (saw_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;
+ } else {
+ p->time = t;
+ p->duration = saw_grid_time - t;
+ }
+ }
+ } else
+ saw_grid_input = 0;
+ }
+ }
nvgEndFrame(saw_nvg);
@@ -241,13 +399,15 @@ static void saw_event(sapp_event const *event) {
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
-
switch (event->mouse_button) {
case SAPP_MOUSEBUTTON_LEFT:
saw_lbutton_down = 1;
saw_lbutton_click = 1;
break;
- case SAPP_MOUSEBUTTON_RIGHT: saw_rbutton_click = 1; break;
+ case SAPP_MOUSEBUTTON_RIGHT:
+ saw_rbutton_down = 1;
+ saw_rbutton_click = 1;
+ break;
default:;
}
break;
@@ -255,6 +415,7 @@ static void saw_event(sapp_event const *event) {
case SAPP_EVENTTYPE_MOUSE_UP:
switch (event->mouse_button) {
case SAPP_MOUSEBUTTON_LEFT: saw_lbutton_down = 0; break;
+ case SAPP_MOUSEBUTTON_RIGHT: saw_rbutton_down = 0; break;
default:;
}
break;
diff --git a/source/saw/profiler.h b/source/saw/profiler.h
index a4e0f9f..850b2e8 100644
--- a/source/saw/profiler.h
+++ b/source/saw/profiler.h
@@ -1,8 +1,6 @@
#ifndef SAW_PROFILER_H
#define SAW_PROFILER_H
-extern long long profiler_time_;
-
void profile_frame(char const *s);
#endif