summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-02-15 02:39:09 +0100
committerMitya Selivanov <automainint@guattari.tech>2024-02-15 02:39:09 +0100
commitd7edc7ee544fbf7fe38d0149afb3563f21f450ff (patch)
tree919919cf6fe6f91d4d05d7703c8e643bbda7fead
parent06c55ae15d5b0ad96dd175677403c38d23f1f66b (diff)
downloadsaw-d7edc7ee544fbf7fe38d0149afb3563f21f450ff.zip
Sampler UI
-rw-r--r--.clang-format6
-rw-r--r--source/saw/_dep.c5
-rw-r--r--source/saw/main.c1144
3 files changed, 717 insertions, 438 deletions
diff --git a/.clang-format b/.clang-format
index 6df0ff7..b4483b5 100644
--- a/.clang-format
+++ b/.clang-format
@@ -45,9 +45,9 @@ AlignConsecutiveDeclarations: Consecutive
AlignEscapedNewlines: Left
AllowShortLambdasOnASingleLine: All
AllowShortFunctionsOnASingleLine: Empty
-AllowShortBlocksOnASingleLine: Always
-AllowShortCaseLabelsOnASingleLine: true
-AllowShortLoopsOnASingleLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
diff --git a/source/saw/_dep.c b/source/saw/_dep.c
index fd52289..246c934 100644
--- a/source/saw/_dep.c
+++ b/source/saw/_dep.c
@@ -34,6 +34,11 @@
# pragma GCC diagnostic pop
#endif
+// Audio formats
+//
+#define DR_WAV_IMPLEMENTATION
+#include "../dr_libs/dr_wav.h"
+
// nanovg
//
#include "../nanovg/nanovg.c"
diff --git a/source/saw/main.c b/source/saw/main.c
index d139773..246867c 100644
--- a/source/saw/main.c
+++ b/source/saw/main.c
@@ -23,9 +23,10 @@
#endif
#include "../sokol/sokol_app.h"
-#include "../miniaudio/miniaudio.h"
#include "../nanovg/nanovg.h"
#include "../nanovg/nanovg_gl.h"
+#include "../miniaudio/miniaudio.h"
+#include "../dr_libs/dr_wav.h"
#include <stdio.h>
#include <stdlib.h>
@@ -101,8 +102,6 @@ enum {
OSCILLATOR_INPUT_WAVE,
OSCILLATOR_INPUT_WARP,
OSCILLATOR_INPUT_PHASE,
- OSCILLATOR_INPUT_UNISON,
- OSCILLATOR_INPUT_SPREAD,
OSCILLATOR_INPUT_STEREO_WIDTH,
OSCILLATOR_INPUT_VOLUME,
OSCILLATOR_INPUT_SUSTAIN,
@@ -110,10 +109,15 @@ enum {
OSCILLATOR_INPUT_DECAY,
OSCILLATOR_INPUT_RELEASE,
+ SAMPLER_OUTLINE_SIZE = 64,
+
EDIT_MODE_HAND = 0,
EDIT_MODE_ERASE,
EDIT_MODE_PAN,
EDIT_MODE_CLONE,
+
+ TINT_WHITE = 0,
+ TINT_ORANGE,
};
// Data types
@@ -173,16 +177,21 @@ typedef struct {
f64 stereo_width;
f64 volume;
saw_envelope_t envelope;
-
- // dynamic properties
- //
- i32 value_input;
- f64 value_buffer;
} saw_oscillator_t;
+typedef DA(f32) saw_da_f32_t;
+
typedef struct {
- // TODO
- int _;
+ saw_da_f32_t data;
+ saw_da_f32_t outline;
+ f64 begin;
+ f64 end;
+ f64 tail;
+ f64 fade_in;
+ f64 fade_out;
+ f64 crossfade;
+ f64 base_frequency;
+ saw_envelope_t envelope;
} saw_sampler_t;
typedef struct {
@@ -207,6 +216,12 @@ typedef struct {
b8 duplicate_input;
} saw_compose_t;
+typedef struct {
+ f32 normal[4];
+ f32 active[4];
+ f32 hover[4];
+} saw_ui_color_t;
+
// ================================================================
//
// Global state
@@ -219,19 +234,21 @@ static ma_device saw_audio_device;
// Input events
//
-static i64 saw_mouse_x = 0;
-static i64 saw_mouse_y = 0;
-static i64 saw_mouse_dx = 0;
-static i64 saw_mouse_dy = 0;
-static b8 saw_lbutton_click = 0;
-static b8 saw_lbutton_down = 0;
-static b8 saw_rbutton_click = 0;
-static b8 saw_rbutton_down = 0;
-static b8 saw_mbutton_click = 0;
-static b8 saw_mbutton_down = 0;
-static b8 saw_shift_on = 0;
-static b8 saw_ctrl_on = 0;
-static b8 saw_key_pressed[512] = { 0 };
+static i64 saw_mouse_x = 0;
+static i64 saw_mouse_y = 0;
+static i64 saw_mouse_dx = 0;
+static i64 saw_mouse_dy = 0;
+static b8 saw_lbutton_click = 0;
+static b8 saw_lbutton_down = 0;
+static b8 saw_rbutton_click = 0;
+static b8 saw_rbutton_down = 0;
+static b8 saw_mbutton_click = 0;
+static b8 saw_mbutton_down = 0;
+static b8 saw_mouse_on = 0;
+static b8 saw_shift_on = 0;
+static b8 saw_ctrl_on = 0;
+static b8 saw_key_pressed[512] = { 0 };
+static char saw_drop_file[4096] = { 0 };
static b8 saw_playback_on = 0;
static i64 saw_playback_frame = 0;
@@ -267,6 +284,23 @@ static saw_compose_t saw_compose = {
.duplicate_input = 0,
};
+static saw_ui_color_t saw_ui_colors[] = {
+ {
+ .normal = { .7f, .7f, .7f, .7f },
+ .active = { .7f, .7f, .7f, 1.f },
+ .hover = { 1.f, 1.f, 1.f, 1.f },
+ },
+ {
+ .normal = { .9f, .65f, .4f, .6f },
+ .active = { .9f, .65f, .4f, 1.f },
+ .hover = { 1.f, 1.f, 1.f, 1.f },
+ },
+};
+
+static i64 saw_ui_input_index = 0;
+static i64 saw_ui_input_active = -1;
+static f64 saw_ui_input_buffer = 0.;
+
// ================================================================
//
// Sound
@@ -359,11 +393,16 @@ static f64 saw_oscillator(i32 type, f64 frequency, f64 phase,
t = t - floor(t);
switch (type) {
- case WAVE_SINE: return sin(t * (M_PI * 2));
- case WAVE_SAW_UP: return -1. + t * 2.;
- case WAVE_SAW_DOWN: return 1. - t * 2.;
- case WAVE_SQUARE_UP: return t < .5 + warp / 2. ? -1. : 1.;
- case WAVE_SQUARE_DOWN: return t < .5 + warp / 2. ? 1. : -1.;
+ case WAVE_SINE:
+ return sin(t * (M_PI * 2));
+ case WAVE_SAW_UP:
+ return -1. + t * 2.;
+ case WAVE_SAW_DOWN:
+ return 1. - t * 2.;
+ case WAVE_SQUARE_UP:
+ return t < .5 + warp / 2. ? -1. : 1.;
+ case WAVE_SQUARE_DOWN:
+ return t < .5 + warp / 2. ? 1. : -1.;
default:;
}
@@ -396,7 +435,7 @@ static void saw_audio_render(void) {
saw_roll_t *roll = saw_rolls + k;
if (!roll->enabled || saw_playback_frame < roll->time ||
- saw_playback_frame > roll->time + roll->duration)
+ saw_playback_frame >= roll->time + roll->duration)
continue;
i64 roll_frame = saw_playback_frame - roll->time;
@@ -559,6 +598,13 @@ static void saw_audio_callback(ma_device *device, void *void_out_,
mtx_unlock(&saw_playback_mutex);
}
+static void saw_sampler_cleanup(saw_sampler_t *sampler) {
+ DA_DESTROY(sampler->data);
+ DA_DESTROY(sampler->outline);
+
+ memset(sampler, 0, sizeof *sampler);
+}
+
#ifdef __GNUC__
# pragma GCC pop_options
# pragma GCC diagnostic pop
@@ -570,7 +616,331 @@ static void saw_audio_callback(ma_device *device, void *void_out_,
//
// ================================================================
-static void saw_reset_ui_offset(void) {
+static void saw_ui_begin(void) {
+ saw_ui_input_index = 0;
+}
+
+static void saw_ui_end(void) {
+ if (saw_ui_input_active != -1 && !saw_lbutton_down) {
+ assert(0);
+
+ saw_ui_input_active = -1;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(0);
+#endif
+ }
+}
+
+static b8 saw_ui_button(i64 x0, i64 y0, i64 width, i64 height,
+ i64 color_index, str_t icon, str_t label,
+ b8 is_active) {
+ i64 frame_height = sapp_height();
+ i64 y = frame_height - y0 - height;
+ b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
+ saw_mouse_y >= y && saw_mouse_y < y + height;
+ saw_ui_color_t c = saw_ui_colors[color_index];
+
+ if (has_cursor)
+ nvgFillColor(saw_nvg, nvgRGBAf(c.hover[0], c.hover[1], c.hover[2],
+ c.hover[3]));
+ else if (is_active)
+ nvgFillColor(saw_nvg, nvgRGBAf(c.active[0], c.active[1],
+ c.active[2], c.active[3]));
+ else
+ nvgFillColor(saw_nvg, nvgRGBAf(c.normal[0], c.normal[1],
+ c.normal[2], c.normal[3]));
+
+ if (icon.size > 0) {
+ nvgFontSize(saw_nvg, (height * 3) / 5);
+ nvgFontFaceId(saw_nvg, saw_font_icons);
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0 + height / 2, y + height / 2, icon.values,
+ icon.values + icon.size);
+ x0 += height;
+ width -= height * 2;
+ }
+
+ if (label.size > 0) {
+ nvgFontSize(saw_nvg, (height * 4) / 5);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0 + width / 2, y + height / 2, label.values,
+ label.values + label.size);
+ }
+
+ return saw_lbutton_click && has_cursor;
+}
+
+static void saw_ui_value_float(i64 x0, i64 y0, i64 width, i64 height,
+ i64 color_index, str_t label,
+ f64 scale, f64 min, f64 max,
+ f64 *data) {
+ assert(data != NULL && scale != .0);
+ if (data == NULL || scale == .0)
+ return;
+
+ i64 frame_height = sapp_height();
+ i64 y = frame_height - y0 - height;
+ b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
+ saw_mouse_y >= y && saw_mouse_y < y + height;
+ saw_ui_color_t c = saw_ui_colors[color_index];
+
+ // Process input
+ //
+ {
+ if (saw_ui_input_active == -1 && has_cursor &&
+ saw_lbutton_click) {
+ saw_ui_input_active = saw_ui_input_index;
+ saw_ui_input_buffer = *data * scale;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(1);
+#endif
+ }
+
+ if (saw_ui_input_active == saw_ui_input_index) {
+ if (saw_lbutton_down) {
+ saw_ui_input_buffer -= saw_shift_on ? saw_mouse_dy * 300
+ : saw_ctrl_on ? saw_mouse_dy
+ : saw_mouse_dy * 20;
+ } else {
+ saw_ui_input_active = -1;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(0);
+#endif
+ }
+
+ *data = saw_ui_input_buffer / scale;
+ }
+
+ if (*data < min)
+ *data = min;
+ if (*data > max)
+ *data = max;
+ }
+
+ // Draw UI
+ //
+ {
+ if (saw_ui_input_active == saw_ui_input_index ||
+ (saw_ui_input_active == -1 && has_cursor)) {
+ nvgBeginPath(saw_nvg);
+ nvgRect(saw_nvg, x0 + width / 2, y, width / 2, height);
+ nvgFillColor(saw_nvg, nvgRGBAf(.9f, .95f, .9f, .5f));
+ nvgFill(saw_nvg);
+ }
+
+ nvgFontSize(saw_nvg, (height * 4) / 5);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+
+ if (label.size > 0) {
+ nvgFillColor(saw_nvg, nvgRGBAf(c.normal[0], c.normal[1],
+ c.normal[2], c.normal[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0, y + (height * 5) / 8, label.values,
+ label.values + label.size);
+ x0 += width / 2;
+ }
+
+ if (has_cursor)
+ nvgFillColor(saw_nvg, nvgRGBAf(c.hover[0], c.hover[1],
+ c.hover[2], c.hover[3]));
+ else
+ nvgFillColor(saw_nvg, nvgRGBAf(c.active[0], c.active[1],
+ c.active[2], c.active[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ char buf[256];
+ sprintf(buf, "%.3f", (f32) *data);
+ nvgText(saw_nvg, x0 + width / 4, y + (height * 5) / 8, buf, NULL);
+ }
+
+ // Increment UI element input index
+ //
+
+ ++saw_ui_input_index;
+}
+
+static void saw_ui_value_int(i64 x0, i64 y0, i64 width, i64 height,
+ i64 color_index, str_t label, f64 scale,
+ i64 min, i64 max, i64 *data) {
+ assert(data != NULL && scale != .0);
+ if (data == NULL || scale == .0)
+ return;
+
+ i64 frame_height = sapp_height();
+ i64 y = frame_height - y0 - height;
+ b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
+ saw_mouse_y >= y && saw_mouse_y < y + height;
+ saw_ui_color_t c = saw_ui_colors[color_index];
+
+ // Process input
+ //
+ {
+ if (saw_ui_input_active == -1 && has_cursor &&
+ saw_lbutton_click) {
+ saw_ui_input_active = saw_ui_input_index;
+ saw_ui_input_buffer = (f64) *data * scale;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(1);
+#endif
+ }
+
+ if (saw_ui_input_active == saw_ui_input_index) {
+ if (saw_lbutton_down) {
+ saw_ui_input_buffer -= saw_shift_on ? saw_mouse_dy * 300
+ : saw_ctrl_on ? saw_mouse_dy
+ : saw_mouse_dy * 20;
+ } else {
+ saw_ui_input_active = -1;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(0);
+#endif
+ }
+
+ *data = (i64) floor(saw_ui_input_buffer / scale + .5);
+ }
+
+ if (*data < min)
+ *data = min;
+ if (*data > max)
+ *data = max;
+ }
+
+ // Draw UI
+ //
+ {
+ if (saw_ui_input_active == saw_ui_input_index ||
+ (saw_ui_input_active == -1 && has_cursor)) {
+ nvgBeginPath(saw_nvg);
+ nvgRect(saw_nvg, x0 + width / 2, y, width / 2, height);
+ nvgFillColor(saw_nvg, nvgRGBAf(.9f, .95f, .9f, .5f));
+ nvgFill(saw_nvg);
+ }
+
+ nvgFontSize(saw_nvg, (height * 4) / 5);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+
+ if (label.size > 0) {
+ nvgFillColor(saw_nvg, nvgRGBAf(c.normal[0], c.normal[1],
+ c.normal[2], c.normal[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0, y + (height * 5) / 8, label.values,
+ label.values + label.size);
+ x0 += width / 2;
+ }
+
+ if (has_cursor)
+ nvgFillColor(saw_nvg, nvgRGBAf(c.hover[0], c.hover[1],
+ c.hover[2], c.hover[3]));
+ else
+ nvgFillColor(saw_nvg, nvgRGBAf(c.active[0], c.active[1],
+ c.active[2], c.active[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ char buf[256];
+ sprintf(buf, "%lld", *data);
+ nvgText(saw_nvg, x0 + width / 4, y + (height * 5) / 8, buf, NULL);
+ }
+
+ // Increment UI element input index
+ //
+
+ ++saw_ui_input_index;
+}
+
+static void saw_ui_value_list(i64 x0, i64 y0, i64 width, i64 height,
+ i64 color_index, str_t label, f64 scale,
+ i64 size, str_t *names, i32 *data) {
+ assert(data != NULL && names != NULL && scale != .0);
+ if (data == NULL || names == NULL || scale == .0)
+ return;
+
+ i64 frame_height = sapp_height();
+ i64 y = frame_height - y0 - height;
+ b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
+ saw_mouse_y >= y && saw_mouse_y < y + height;
+ saw_ui_color_t c = saw_ui_colors[color_index];
+
+ // Process input
+ //
+ {
+ if (saw_ui_input_active == -1 && has_cursor &&
+ saw_lbutton_click) {
+ saw_ui_input_active = saw_ui_input_index;
+ saw_ui_input_buffer = (f64) *data * scale;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(1);
+#endif
+ }
+
+ if (saw_ui_input_active == saw_ui_input_index) {
+ if (saw_lbutton_down) {
+ saw_ui_input_buffer -= saw_shift_on ? saw_mouse_dy * 300
+ : saw_ctrl_on ? saw_mouse_dy
+ : saw_mouse_dy * 20;
+ } else {
+ saw_ui_input_active = -1;
+
+#ifndef __EMSCRIPTEN__
+ sapp_lock_mouse(0);
+#endif
+ }
+
+ *data = (i32) floor(saw_ui_input_buffer / scale + .5);
+ }
+
+ if (*data < 0)
+ *data = 0;
+ if (*data >= size)
+ *data = size - 1;
+ }
+
+ // Draw UI
+ //
+ {
+ if (saw_ui_input_active == saw_ui_input_index ||
+ (saw_ui_input_active == -1 && has_cursor)) {
+ nvgBeginPath(saw_nvg);
+ nvgRect(saw_nvg, x0 + width / 2, y, width / 2, height);
+ nvgFillColor(saw_nvg, nvgRGBAf(.9f, .95f, .9f, .5f));
+ nvgFill(saw_nvg);
+ }
+
+ nvgFontSize(saw_nvg, (height * 4) / 5);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+
+ if (label.size > 0) {
+ nvgFillColor(saw_nvg, nvgRGBAf(c.normal[0], c.normal[1],
+ c.normal[2], c.normal[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0, y + (height * 5) / 8, label.values,
+ label.values + label.size);
+ x0 += width / 2;
+ }
+
+ if (has_cursor)
+ nvgFillColor(saw_nvg, nvgRGBAf(c.hover[0], c.hover[1],
+ c.hover[2], c.hover[3]));
+ else
+ nvgFillColor(saw_nvg, nvgRGBAf(c.active[0], c.active[1],
+ c.active[2], c.active[3]));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ str_t s = names[*data];
+ nvgText(saw_nvg, x0 + width / 4, y + (height * 5) / 8, s.values,
+ s.values + s.size);
+ }
+
+ // Increment UI element input index
+ //
+
+ ++saw_ui_input_index;
+}
+
+static void saw_ui_reset_offset(void) {
if (saw_current_roll != -1) {
saw_rolls[saw_current_roll].ui_offset_x = 0;
saw_rolls[saw_current_roll].ui_offset_y =
@@ -585,15 +955,8 @@ static void saw_ui_header(i64 x0, u64 y0, u64 width, i64 height) {
if (height > (2 * width) / 15)
height = (2 * width) / 15;
- i64 frame_height = sapp_height();
-
- i64 border = height > 14 * 4 ? 14 : height / 4;
-
- nvgFontSize(saw_nvg, height - border * 2);
- nvgFontFaceId(saw_nvg, saw_font_icons);
-
- i64 x = x0 + border;
- i64 h = frame_height - y0 - height + height - border;
+ i64 x = x0;
+ i64 s = height;
char backward_fast[] = "\uf049";
char play[] = "\uf04b";
@@ -604,83 +967,44 @@ static void saw_ui_header(i64 x0, u64 y0, u64 width, i64 height) {
char panning[] = "\uf047";
char clone[] = "\uf24d";
- u8 color_active[] = { 220, 220, 220, 255 };
- u8 color_hover[] = { 255, 255, 220, 255 };
- u8 color_selected[] = { 255, 255, 255, 255 };
-
- b8 has_cursor = 0;
-
-#define ICON_(i_, selected_) \
- do { \
- has_cursor = saw_mouse_x >= x && saw_mouse_x < x + height && \
- saw_mouse_y >= frame_height - y0 - height && \
- saw_mouse_y < frame_height - y0; \
- if (has_cursor) \
- nvgFillColor(saw_nvg, \
- nvgRGBA(color_hover[0], color_hover[1], \
- color_hover[2], color_hover[3])); \
- else if (selected_) \
- nvgFillColor(saw_nvg, \
- nvgRGBA(color_selected[0], color_selected[1], \
- color_selected[2], color_selected[3])); \
- else \
- nvgFillColor(saw_nvg, \
- nvgRGBA(color_active[0], color_active[1], \
- color_active[2], color_active[3])); \
- nvgText(saw_nvg, x, h, (i_), (i_) + (sizeof(i_) - 1)); \
- x += height; \
- } while (0)
-
// Global actions
//
- ICON_(backward_fast, 0);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_WHITE, SZ(backward_fast),
+ SZ(""), 1))
saw_playback_frame = 0;
+ x += s;
- if (saw_playback_on)
- ICON_(stop, 0);
- else
- ICON_(play, 0);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_WHITE,
+ saw_playback_on ? SZ(stop) : SZ(play), SZ(""), 1))
saw_playback_on = !saw_playback_on;
+ x += s;
- ICON_(anchor, 0);
- if (has_cursor && saw_lbutton_click)
- saw_reset_ui_offset();
+ if (saw_ui_button(x, y0, s, s, TINT_WHITE, SZ(anchor), SZ(""), 1))
+ saw_ui_reset_offset();
+ x += s + s / 2;
// Editing mode
//
- x += height / 2;
-
- color_active[0] = 220;
- color_active[1] = 180;
- color_active[2] = 80;
- color_active[3] = 160;
-
- color_selected[0] = 220;
- color_selected[1] = 180;
- color_selected[2] = 80;
- color_selected[3] = 255;
-
- ICON_(hand_pointer, saw_edit_mode == EDIT_MODE_HAND);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_ORANGE, SZ(hand_pointer),
+ SZ(""), saw_edit_mode == EDIT_MODE_HAND))
saw_edit_mode = EDIT_MODE_HAND;
+ x += s;
- ICON_(eraser, saw_edit_mode == EDIT_MODE_ERASE);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_ORANGE, SZ(eraser), SZ(""),
+ saw_edit_mode == EDIT_MODE_ERASE))
saw_edit_mode = EDIT_MODE_ERASE;
+ x += s;
- ICON_(panning, saw_edit_mode == EDIT_MODE_PAN);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_ORANGE, SZ(panning), SZ(""),
+ saw_edit_mode == EDIT_MODE_PAN))
saw_edit_mode = EDIT_MODE_PAN;
+ x += s;
- ICON_(clone, saw_edit_mode == EDIT_MODE_CLONE);
- if (has_cursor && saw_lbutton_click)
+ if (saw_ui_button(x, y0, s, s, TINT_ORANGE, SZ(clone), SZ(""),
+ saw_edit_mode == EDIT_MODE_CLONE))
saw_edit_mode = EDIT_MODE_CLONE;
-
-#undef ICON_
}
static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) {
@@ -1031,7 +1355,7 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) {
// Draw cursor
//
- if (!hover_any && !saw_compose.grid_input &&
+ if (saw_mouse_on && !hover_any && !saw_compose.grid_input &&
saw_mouse_x >= x0 + saw_compose.ui_offset_x) {
i64 track = (saw_mouse_y - saw_compose.ui_offset_y -
frame_height + y0 + height) /
@@ -1060,7 +1384,7 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) {
// Cursor indicator
//
- if (saw_mouse_x >= x0 + saw_compose.ui_offset_x &&
+ if (saw_mouse_on && saw_mouse_x >= x0 + saw_compose.ui_offset_x &&
saw_mouse_x < x0 + width &&
saw_mouse_y >= frame_height - y0 - height &&
saw_mouse_y < frame_height - y0) {
@@ -1079,245 +1403,250 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) {
static void saw_ui_choose_instrument(saw_track_t *track, i64 x0,
i64 y0, i64 width, i64 height) {
- i64 frame_height = sapp_height();
-
i64 text_height = 40;
- i64 border = 4;
-
- nvgFontSize(saw_nvg, text_height - border);
- nvgFontFaceId(saw_nvg, saw_font_text);
-
- i64 y = frame_height - y0 - height;
+ i64 y = y0 + height - text_height;
+
+ x0 += width / 8;
+ width = (width * 3) / 4;
+
+ if (saw_ui_button(x0, y, width, text_height, TINT_ORANGE,
+ SZ("\uf83e"), SZ("Oscillator"), 1)) {
+ track->instrument = INSTRUMENT_OSCILLATOR;
+
+ track->oscillator = (saw_oscillator_t) {
+ .wave = WAVE_SINE,
+ .warp = .0,
+ .phase = .0,
+ .stereo_width = .2,
+ .volume = 1.,
+ .envelope = {
+ .sustain = .15,
+ .attack = .007,
+ .decay = .3,
+ .release = .4,
+ },
+ };
+ }
- {
- b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
- saw_mouse_y >= y && saw_mouse_y < y + text_height;
+ y -= text_height;
- if (has_cursor)
- nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
- else
- nvgFillColor(saw_nvg, nvgRGBA(200, 120, 80, 255));
- nvgText(saw_nvg, x0 + border * 2, y + text_height - border * 2,
- "Oscillator", NULL);
- y += text_height;
+ if (saw_ui_button(x0, y, width, text_height, TINT_ORANGE,
+ SZ("\uf1c7"), SZ("Sampler"), 1)) {
+ track->instrument = INSTRUMENT_SAMPLER;
- if (has_cursor && saw_lbutton_down)
- track->instrument = INSTRUMENT_OSCILLATOR;
- }
+ memset(&track->sampler, 0, sizeof track->sampler);
- {
- b8 has_cursor = saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
- saw_mouse_y >= y && saw_mouse_y < y + text_height;
+ DA_INIT(track->sampler.data, 0, NULL);
+ DA_INIT(track->sampler.outline, SAMPLER_OUTLINE_SIZE, NULL);
- if (has_cursor)
- nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
- else
- nvgFillColor(saw_nvg, nvgRGBA(200, 120, 80, 255));
- nvgText(saw_nvg, x0 + border * 2, y + text_height - border * 2,
- "Sampler", NULL);
- y += text_height;
+ if (track->sampler.outline.size != SAMPLER_OUTLINE_SIZE) {
+ printf("Bad alloc\n");
+ fflush(stdout);
+ track->sampler.outline.size = 0;
+ }
- if (has_cursor && saw_lbutton_down)
- track->instrument = INSTRUMENT_SAMPLER;
+ track->sampler.begin = 0.;
+ track->sampler.end = 1.;
+ track->sampler.tail = .2;
+ track->sampler.fade_in = 0.f;
+ track->sampler.fade_out = 0.;
+ track->sampler.crossfade = .01;
+ track->sampler.base_frequency = 440.;
+
+ track->sampler.envelope = (saw_envelope_t) {
+ .sustain = .15,
+ .attack = .007,
+ .decay = .3,
+ .release = .4,
+ };
}
}
static void saw_ui_oscillator(saw_oscillator_t *osc, i64 x0, i64 y0,
i64 width, i64 height) {
i64 frame_height = sapp_height();
+ i64 text_height = 33;
+ i64 x = x0 + width / 12;
+ i64 w = (width * 5) / 6;
- i64 text_height = 33;
- i64 header_offset = 60;
- i64 border = 2;
- i64 column_width = 200;
-
- // Values input and highlight
- //
-
- if (!saw_lbutton_down &&
- osc->value_input != OSCILLATOR_INPUT_NONE) {
- osc->value_input = OSCILLATOR_INPUT_NONE;
- osc->value_buffer = 0;
-
-#ifndef __EMSCRIPTEN__
- sapp_lock_mouse(0);
-#endif
- }
+ str_t wave_names[] = {
+ SZ("Sine"), SZ("Saw up"), SZ("Saw down"),
+ SZ("Sqr up"), SZ("Sqr down"), SZ("Kick"),
+ };
- i64 next_y = 0;
+ saw_ui_value_list(x, y0 + height - text_height, w, text_height,
+ TINT_WHITE, SZ("Wave"), 500.,
+ sizeof wave_names / sizeof *wave_names,
+ wave_names, &osc->wave);
+ saw_ui_value_float(x, y0 + height - text_height * 2, w, text_height,
+ TINT_WHITE, SZ("Warp"), 10000, -1., 1.,
+ &osc->warp);
+ // FIXME
+ // Looping phase value.
+ saw_ui_value_float(x, y0 + height - text_height * 3, w, text_height,
+ TINT_WHITE, SZ("Phase"), 10000, 0., 1.,
+ &osc->phase);
+ saw_ui_value_float(x, y0 + height - text_height * 4, w, text_height,
+ TINT_WHITE, SZ("Stereo"), 10000, 0., 2.,
+ &osc->stereo_width);
+ saw_ui_value_float(x, y0 + height - text_height * 5, w, text_height,
+ TINT_WHITE, SZ("Volume"), 10000, 0., 2.,
+ &osc->volume);
- for (i64 input_index = OSCILLATOR_INPUT_WAVE;
- input_index <= OSCILLATOR_INPUT_RELEASE; ++input_index) {
- // TODO
- // Implement Unison and Spread.
+ nvgFontSize(saw_nvg, text_height);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+ nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
+ nvgText(saw_nvg, x,
+ frame_height - y0 - height + (text_height * 13) / 2,
+ "Envelope", 0);
- if (input_index == OSCILLATOR_INPUT_UNISON ||
- input_index == OSCILLATOR_INPUT_SPREAD)
- continue;
+ saw_ui_value_float(x, y0 + height - text_height * 8, w, text_height,
+ TINT_WHITE, SZ("Sustain"), 10000, 0., 1.,
+ &osc->envelope.sustain);
+ saw_ui_value_float(x, y0 + height - text_height * 9, w, text_height,
+ TINT_WHITE, SZ("Attack"), 100000, 0., 6.,
+ &osc->envelope.attack);
+ saw_ui_value_float(x, y0 + height - text_height * 10, w,
+ text_height, TINT_WHITE, SZ("Decay"), 100000, 0.,
+ 6., &osc->envelope.decay);
+ saw_ui_value_float(x, y0 + height - text_height * 11, w,
+ text_height, TINT_WHITE, SZ("Release"), 100000,
+ 0., 6., &osc->envelope.release);
+}
- if ((osc->value_input == OSCILLATOR_INPUT_NONE ||
- osc->value_input == input_index) &&
- saw_mouse_x >= x0 && saw_mouse_x < x0 + width &&
- saw_mouse_y >= frame_height - y0 - height + next_y &&
- saw_mouse_y <
- frame_height - y0 - height + next_y + text_height) {
- i64 x = x0 + column_width;
- i64 y = frame_height - y0 - height + next_y;
- i64 w = width - column_width;
- i64 h = text_height;
-
- if (w > 0) {
- nvgBeginPath(saw_nvg);
- nvgRect(saw_nvg, x + border, y, w - border * 2, h);
- nvgFillColor(saw_nvg, nvgRGBA(200, 240, 200, 80));
- nvgFill(saw_nvg);
- }
+static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0,
+ i64 width, i64 height) {
+ i64 frame_height = sapp_height();
+ i64 text_height = 33;
+ i64 x = x0 + width / 12;
+ i64 w = (width * 5) / 6;
+ i64 sample_height = text_height * 4;
- if (saw_lbutton_click &&
- osc->value_input == OSCILLATOR_INPUT_NONE) {
+ if (saw_drop_file[0] != '\0') {
+ // Load the audio sample from a file
+ //
-#ifndef __EMSCRIPTEN__
- sapp_lock_mouse(1);
-#endif
+ u32 channels;
+ u32 sample_rate;
+ drwav_uint64 count;
- osc->value_input = input_index;
+ f32 *samples = drwav_open_file_and_read_pcm_frames_f32(
+ saw_drop_file, &channels, &sample_rate, &count, NULL);
- switch (input_index) {
- case OSCILLATOR_INPUT_WAVE:
- osc->value_buffer = osc->wave * 500;
- break;
+ if (samples == NULL) {
+ printf("drwav_open_file_and_read_pcm_frames_f32 failed.\n");
+ fflush(stdout);
+ } else {
+ DA_RESIZE(sampler->data, count * 2);
- case OSCILLATOR_INPUT_WARP:
- osc->value_buffer = osc->warp * 10000;
- break;
+ assert(sampler->data.size == count * 2);
- case OSCILLATOR_INPUT_PHASE:
- osc->value_buffer = osc->phase * 10000;
- break;
+ if (sampler->data.size != count * 2) {
+ printf("Bad alloc\n");
+ fflush(stdout);
+ sampler->data.size = 0;
+ } else {
+ if (channels == 1)
+ for (i64 i = 0; i < count; i++) {
+ sampler->data.values[i * 2] = samples[i];
+ sampler->data.values[i * 2 + 1] = samples[i];
+ }
+ else if (channels >= 2)
+ for (i64 i = 0; i < count; i++)
+ sampler->data.values[i] = samples[i];
+
+ if (sampler->outline.size == SAMPLER_OUTLINE_SIZE)
+ for (i64 i = 0; i < SAMPLER_OUTLINE_SIZE; i++) {
+ f32 *v = sampler->data.values;
+ i64 k = i * count / SAMPLER_OUTLINE_SIZE;
+ sampler->outline.values[i] = fabs(v[k] + v[k + 1]) * .5;
+ }
- case OSCILLATOR_INPUT_STEREO_WIDTH:
- osc->value_buffer = osc->stereo_width * 10000;
- break;
+ sampler->base_frequency = (440. * sample_rate) / SAMPLE_RATE;
+ }
- case OSCILLATOR_INPUT_VOLUME:
- osc->value_buffer = osc->volume * 10000;
- break;
+ drwav_free(samples, NULL);
+ }
+ }
- case OSCILLATOR_INPUT_SUSTAIN:
- osc->value_buffer = osc->envelope.sustain * 10000;
- break;
+ if (sampler->data.size > 0) {
+ // Draw the sample outline
+ //
- case OSCILLATOR_INPUT_ATTACK:
- osc->value_buffer = osc->envelope.attack * 100000;
- break;
+ if (sampler->outline.size == SAMPLER_OUTLINE_SIZE) {
+ nvgBeginPath(saw_nvg);
- case OSCILLATOR_INPUT_DECAY:
- osc->value_buffer = osc->envelope.decay * 100000;
- break;
+ f64 dw = (f64) w / SAMPLER_OUTLINE_SIZE;
+ f64 h = sample_height / 2;
+ f64 y = frame_height - y0 - height + h;
+ assert(dw > .0001);
- case OSCILLATOR_INPUT_RELEASE:
- osc->value_buffer = osc->envelope.release * 100000;
- break;
+ for (i64 i = 0; i < SAMPLER_OUTLINE_SIZE; i++)
+ nvgRect(saw_nvg, x + dw * i,
+ y - h * sampler->outline.values[i], dw,
+ h * sampler->outline.values[i] * 2);
- default:;
- }
- }
+ nvgFillColor(saw_nvg, nvgRGBAf(.8f, .4f, .0f, .7f));
+ nvgFill(saw_nvg);
}
-
- if (input_index == OSCILLATOR_INPUT_VOLUME)
- next_y += header_offset;
- next_y += text_height;
+ } else {
+ nvgFontSize(saw_nvg, text_height / 2);
+ nvgFontFaceId(saw_nvg, saw_font_text);
+ nvgFillColor(saw_nvg, nvgRGBAf(1.f, .7f, .2f, .5f));
+ nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
+ nvgText(saw_nvg, x0 + width / 2,
+ frame_height - y0 - height + sample_height / 2,
+ "Drop a WAV file here", NULL);
}
- // Draw text
- //
+ saw_ui_value_float(x, y0 + height - sample_height - text_height, w,
+ text_height, TINT_WHITE, SZ("Begin"), 100000, 0.,
+ 60., &sampler->begin);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 2,
+ w, text_height, TINT_WHITE, SZ("End"), 100000,
+ 0., 60., &sampler->end);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 3,
+ w, text_height, TINT_WHITE, SZ("Tail"), 100000,
+ 0., 60., &sampler->tail);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 4,
+ w, text_height, TINT_WHITE, SZ("Fade in"),
+ 100000, 0., 60., &sampler->fade_in);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 5,
+ w, text_height, TINT_WHITE, SZ("Fade out"),
+ 100000, 0., 60., &sampler->fade_out);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 6,
+ w, text_height, TINT_WHITE, SZ("Crossfade"),
+ 100000, 0., 60., &sampler->crossfade);
+ saw_ui_value_float(x, y0 + height - sample_height - text_height * 7,
+ w, text_height, TINT_WHITE, SZ("Base freq."),
+ 10000, 1., 44100., &sampler->base_frequency);
- next_y = text_height;
-
- nvgFontSize(saw_nvg, text_height - border);
+ nvgFontSize(saw_nvg, text_height);
nvgFontFaceId(saw_nvg, saw_font_text);
nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
-
- char buf_wave[][100] = { "Sine", "Saw up", "Saw down",
- "Sqr up", "Sqr down", "Kick" };
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Wave",
- 0);
- if (osc->wave >= 0 && osc->wave < WAVE_COUNT)
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2,
- buf_wave[osc->wave], 0);
- next_y += text_height;
-
- char buf[100];
- sprintf(buf, "%.3f", (f32) osc->warp);
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Warp",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.3f", (f32) osc->phase);
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Phase",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.3f", (f32) osc->stereo_width);
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Stereo",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.3f", (f32) osc->volume);
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Volume",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += header_offset;
-
- nvgText(saw_nvg, x0 + border * 2,
- frame_height - y0 - height + next_y - border * 2,
- "Envelope", 0);
- next_y += text_height;
-
- sprintf(buf, "%.3f", (f32) osc->envelope.sustain);
- nvgText(saw_nvg, x0 + column_width / 4 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Sustain",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.1f ms", (f32) (osc->envelope.attack * 1000));
- nvgText(saw_nvg, x0 + column_width / 4 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Attack",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.1f ms", (f32) (osc->envelope.decay * 1000));
- nvgText(saw_nvg, x0 + column_width / 4 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Decay",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
-
- sprintf(buf, "%.1f ms", (f32) (osc->envelope.release * 1000));
- nvgText(saw_nvg, x0 + column_width / 4 + border * 2,
- frame_height - y0 - height + next_y - border * 2, "Release",
- 0);
- nvgText(saw_nvg, x0 + column_width + border * 2,
- frame_height - y0 - height + next_y - border * 2, buf, 0);
- next_y += text_height;
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
+ nvgText(saw_nvg, x,
+ frame_height - y0 - height + sample_height +
+ (text_height * 17) / 2,
+ "Envelope", NULL);
+
+ saw_ui_value_float(x,
+ y0 + height - sample_height - text_height * 10,
+ w, text_height, TINT_WHITE, SZ("Sustain"), 10000,
+ 0., 1., &sampler->envelope.sustain);
+ saw_ui_value_float(x,
+ y0 + height - sample_height - text_height * 11,
+ w, text_height, TINT_WHITE, SZ("Attack"), 100000,
+ 0., 6., &sampler->envelope.attack);
+ saw_ui_value_float(x,
+ y0 + height - sample_height - text_height * 12,
+ w, text_height, TINT_WHITE, SZ("Decay"), 100000,
+ 0., 6., &sampler->envelope.decay);
+ saw_ui_value_float(x,
+ y0 + height - sample_height - text_height * 13,
+ w, text_height, TINT_WHITE, SZ("Release"),
+ 100000, 0., 6., &sampler->envelope.release);
}
static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0,
@@ -1330,6 +1659,7 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0,
nvgFontSize(saw_nvg, text_height - border);
nvgFontFaceId(saw_nvg, saw_font_text);
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
nvgText(saw_nvg, x0 + border * 2,
@@ -1357,8 +1687,12 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0,
nvgText(saw_nvg, x + border, y + s - border, xmark,
xmark + (sizeof xmark - 1));
- if (has_cursor && saw_lbutton_click)
+ if (has_cursor && saw_lbutton_click) {
+ if (track->instrument == INSTRUMENT_SAMPLER)
+ saw_sampler_cleanup(&track->sampler);
+
track->instrument = INSTRUMENT_NONE;
+ }
}
switch (track->instrument) {
@@ -1367,6 +1701,11 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0,
height - header_offset - text_height);
break;
+ case INSTRUMENT_SAMPLER:
+ saw_ui_sampler(&track->sampler, x0, y0, width,
+ height - header_offset - text_height);
+ break;
+
default:
saw_ui_choose_instrument(track, x0, y0, width,
height - header_offset - text_height);
@@ -1398,6 +1737,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width,
nvgBeginPath(saw_nvg);
nvgFontSize(saw_nvg, text_height);
nvgFontFaceId(saw_nvg, saw_font_text);
+ nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
nvgFillColor(saw_nvg, nvgRGBA(255, 255, 255, 255));
nvgText(saw_nvg, x0 + border * 2,
frame_height - y0 - height + text_height - border * 2,
@@ -1803,7 +2143,8 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width,
// Cursor indicator
//
- if (saw_mouse_x >= x0 + pianokey_width + sheet_offset &&
+ if (saw_mouse_on &&
+ saw_mouse_x >= x0 + pianokey_width + sheet_offset &&
saw_mouse_x < x0 + width &&
saw_mouse_y >= frame_height - y0 - height &&
saw_mouse_y < frame_height - y0) {
@@ -1940,7 +2281,6 @@ static void saw_init(void) {
.decay = .3,
.release = .4,
},
- .value_input = OSCILLATOR_INPUT_NONE,
}
};
@@ -2099,7 +2439,9 @@ static void saw_init(void) {
osc->envelope.release = release * 0.0001;
} break;
- default:;
+ default:
+ memset(track, 0, sizeof *track);
+ track->instrument = INSTRUMENT_NONE;
}
}
@@ -2114,6 +2456,8 @@ static void saw_frame(void) {
// Check how much time passed to see if we need to adjust the
// buffer size.
+ (void) saw_ui_value_int;
+
saw_audio_render();
i64 frame_width = sapp_width();
@@ -2137,7 +2481,7 @@ static void saw_frame(void) {
if (saw_key_pressed[SAPP_KEYCODE_ENTER])
saw_playback_frame = 0;
if (saw_key_pressed[SAPP_KEYCODE_ESCAPE])
- saw_reset_ui_offset();
+ saw_ui_reset_offset();
if (saw_key_pressed[SAPP_KEYCODE_D] ||
(saw_edit_mode == EDIT_MODE_CLONE && saw_lbutton_click))
saw_compose.duplicate_input = 1;
@@ -2154,109 +2498,6 @@ static void saw_frame(void) {
saw_compose.ui_offset_x += saw_mouse_dx;
saw_compose.ui_offset_y += saw_mouse_dy;
}
-
- if (saw_current_track != -1) {
- // Value input
- //
-
- saw_track_t *track = saw_tracks + saw_current_track;
-
- switch (saw_tracks[saw_current_track].instrument) {
- case INSTRUMENT_OSCILLATOR: {
- saw_oscillator_t *osc = &track->oscillator;
-
- if (osc->value_input == OSCILLATOR_INPUT_NONE)
- break;
-
- osc->value_buffer -= saw_shift_on ? saw_mouse_dy * 300
- : saw_ctrl_on ? saw_mouse_dy
- : saw_mouse_dy * 20;
-
- // TODO
- // Unify value input logic.
-
- // Change input value buffer for selected value.
- //
- switch (osc->value_input) {
- case OSCILLATOR_INPUT_WAVE:
- osc->wave = (i64) (osc->value_buffer * .002 + 0.5);
- if (osc->wave < 0)
- osc->wave = 0;
- if (osc->wave >= WAVE_COUNT)
- osc->wave = WAVE_COUNT - 1;
- break;
-
- case OSCILLATOR_INPUT_WARP:
- osc->warp = osc->value_buffer * .0001;
- if (osc->warp < -1.)
- osc->warp = -1.;
- if (osc->warp > 1.)
- osc->warp = 1.;
- break;
-
- case OSCILLATOR_INPUT_PHASE:
- osc->value_buffer = (f64) ((i64) osc->value_buffer %
- 10000);
- while (osc->value_buffer < 0)
- osc->value_buffer += 10000;
- osc->phase = osc->value_buffer * .0001;
- break;
-
- case OSCILLATOR_INPUT_STEREO_WIDTH:
- osc->stereo_width = osc->value_buffer * .0001;
- if (osc->stereo_width < 0.)
- osc->stereo_width = 0.;
- if (osc->stereo_width > 2.)
- osc->stereo_width = 2.;
- break;
-
- case OSCILLATOR_INPUT_VOLUME:
- osc->volume = osc->value_buffer * .0001;
- if (osc->volume < 0.)
- osc->volume = 0.;
- if (osc->volume > 2.)
- osc->volume = 2.;
- break;
-
- case OSCILLATOR_INPUT_SUSTAIN:
- osc->envelope.sustain = osc->value_buffer * .0001;
- if (osc->envelope.sustain < 0.)
- osc->envelope.sustain = 0.;
- if (osc->envelope.sustain > 1.)
- osc->envelope.sustain = 1.;
- break;
-
- case OSCILLATOR_INPUT_ATTACK:
- osc->envelope.attack = osc->value_buffer * .00001;
- if (osc->envelope.attack < 0.)
- osc->envelope.attack = 0.;
- if (osc->envelope.attack > 60.)
- osc->envelope.attack = 60.;
- break;
-
- case OSCILLATOR_INPUT_DECAY:
- osc->envelope.decay = osc->value_buffer * .00001;
- if (osc->envelope.decay < 0.)
- osc->envelope.decay = 0.;
- if (osc->envelope.decay > 60.)
- osc->envelope.decay = 60.;
- break;
-
- case OSCILLATOR_INPUT_RELEASE:
- osc->envelope.release = osc->value_buffer * .00001;
- if (osc->envelope.release < 0.)
- osc->envelope.release = 0.;
- if (osc->envelope.release > 60.)
- osc->envelope.release = 60.;
- break;
-
- default:;
- }
- } break;
-
- default:;
- }
- }
}
// Render UI
@@ -2264,6 +2505,10 @@ static void saw_frame(void) {
nvgBeginFrame(saw_nvg, frame_width, frame_height, sapp_dpi_scale());
{
+ // We have to reset intermediate state for UI input to work
+ // correctly.
+ saw_ui_begin();
+
// Adjust UI layout
//
i64 track_width = frame_width / 5;
@@ -2325,23 +2570,24 @@ static void saw_frame(void) {
kit_str(strlen(buf), buf) // label
);
}
+
+ saw_ui_end();
}
nvgEndFrame(saw_nvg);
// Cleanup input state.
//
{
- saw_mouse_dx = 0;
- saw_mouse_dy = 0;
-
+ saw_mouse_dx = 0;
+ saw_mouse_dy = 0;
saw_lbutton_click = 0;
saw_rbutton_click = 0;
saw_mbutton_click = 0;
-
- saw_shift_on = 0;
- saw_ctrl_on = 0;
+ saw_shift_on = 0;
+ saw_ctrl_on = 0;
memset(saw_key_pressed, 0, sizeof saw_key_pressed);
+ memset(saw_drop_file, 0, sizeof saw_drop_file);
}
}
@@ -2459,6 +2705,12 @@ static void saw_cleanup(void) {
}
fclose(f);
+
+ // Cleanup samplers
+ //
+ for (i64 i = 0; i < TRACK_COUNT; i++)
+ if (saw_tracks[i].instrument == INSTRUMENT_SAMPLER)
+ saw_sampler_cleanup(&saw_tracks[i].sampler);
}
static void saw_event(sapp_event const *event) {
@@ -2475,21 +2727,18 @@ static void saw_event(sapp_event const *event) {
switch (event->type) {
case SAPP_EVENTTYPE_MOUSE_MOVE:
+ saw_mouse_on = 1;
saw_shift_on = (event->modifiers & SAPP_MODIFIER_SHIFT) != 0;
saw_ctrl_on = (event->modifiers & SAPP_MODIFIER_CTRL) != 0;
-
saw_mouse_dx += (i64) floor(event->mouse_dx + .5);
saw_mouse_dy += (i64) floor(event->mouse_dy + .5);
-
saw_mouse_x = (i64) floor(event->mouse_x + .5);
saw_mouse_y = (i64) floor(event->mouse_y + .5);
-
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
saw_shift_on = (event->modifiers & SAPP_MODIFIER_SHIFT) != 0;
saw_ctrl_on = (event->modifiers & SAPP_MODIFIER_CTRL) != 0;
-
switch (event->mouse_button) {
case SAPP_MOUSEBUTTON_LEFT:
saw_lbutton_down = 1;
@@ -2505,41 +2754,41 @@ static void saw_event(sapp_event const *event) {
break;
default:;
}
-
break;
case SAPP_EVENTTYPE_MOUSE_UP:
saw_shift_on = (event->modifiers & SAPP_MODIFIER_SHIFT) != 0;
saw_ctrl_on = (event->modifiers & SAPP_MODIFIER_CTRL) != 0;
-
switch (event->mouse_button) {
- case SAPP_MOUSEBUTTON_LEFT: saw_lbutton_down = 0; break;
- case SAPP_MOUSEBUTTON_RIGHT: saw_rbutton_down = 0; break;
- case SAPP_MOUSEBUTTON_MIDDLE: saw_mbutton_down = 0; break;
+ case SAPP_MOUSEBUTTON_LEFT:
+ saw_lbutton_down = 0;
+ break;
+ case SAPP_MOUSEBUTTON_RIGHT:
+ saw_rbutton_down = 0;
+ break;
+ case SAPP_MOUSEBUTTON_MIDDLE:
+ saw_mbutton_down = 0;
+ break;
default:;
}
-
break;
case SAPP_EVENTTYPE_MOUSE_LEAVE:
+ saw_mouse_on = 0;
saw_shift_on = (event->modifiers & SAPP_MODIFIER_SHIFT) != 0;
saw_ctrl_on = (event->modifiers & SAPP_MODIFIER_CTRL) != 0;
-
saw_lbutton_down = 0;
saw_rbutton_down = 0;
saw_mbutton_down = 0;
-
break;
case SAPP_EVENTTYPE_KEY_DOWN:
saw_shift_on = (event->modifiers & SAPP_MODIFIER_SHIFT) != 0;
saw_ctrl_on = (event->modifiers & SAPP_MODIFIER_CTRL) != 0;
-
if (!event->key_repeat && event->key_code >= 0 &&
event->key_code <
sizeof saw_key_pressed / sizeof *saw_key_pressed)
saw_key_pressed[event->key_code] = 1;
-
break;
// Touch events.
@@ -2552,6 +2801,7 @@ static void saw_event(sapp_event const *event) {
saw_mouse_y = (i64) floor(event->touches[0].pos_y + .5);
saw_lbutton_down = 1;
saw_lbutton_click = 1;
+ saw_mouse_on = 1;
}
break;
@@ -2576,12 +2826,32 @@ static void saw_event(sapp_event const *event) {
saw_mouse_y = y;
}
saw_lbutton_down = 0;
+ saw_mouse_on = 0;
break;
case SAPP_EVENTTYPE_TOUCHES_CANCELLED:
saw_lbutton_down = 0;
+ saw_mouse_on = 0;
break;
+ case SAPP_EVENTTYPE_FILES_DROPPED: {
+#ifdef __EMSCRIPTEN__
+ (void) saw_drop_file;
+#else
+ i32 n = sapp_get_num_dropped_files();
+
+ if (n > 0) {
+ char const *file_name = sapp_get_dropped_file_path(0);
+ i64 len = strlen(file_name);
+
+ if (len > 0 && len < sizeof saw_drop_file) {
+ memcpy(saw_drop_file, file_name, len);
+ saw_drop_file[len] = '\0';
+ }
+ }
+#endif
+ } break;
+
default:;
}
}
@@ -2647,10 +2917,14 @@ sapp_desc sokol_main(i32 argc, char **argv) {
.window_title = "Saw",
.width = 1280,
.height = 720,
- .init_cb = saw_init,
- .frame_cb = saw_frame,
- .cleanup_cb = saw_cleanup,
- .event_cb = saw_event,
- .logger.func = log_,
+#ifndef __EMSCRIPTEN__
+ .enable_dragndrop = 1,
+ .max_dropped_file_path_length = sizeof saw_drop_file,
+#endif
+ .init_cb = saw_init,
+ .frame_cb = saw_frame,
+ .cleanup_cb = saw_cleanup,
+ .event_cb = saw_event,
+ .logger.func = log_,
};
}