From d7edc7ee544fbf7fe38d0149afb3563f21f450ff Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Thu, 15 Feb 2024 02:39:09 +0100 Subject: Sampler UI --- .clang-format | 6 +- source/saw/_dep.c | 5 + source/saw/main.c | 1144 +++++++++++++++++++++++++++++++++-------------------- 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 #include @@ -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_, }; } -- cgit v1.2.3