From e4beec5db8b01479a0accf8a5044fe6019347ba4 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Mon, 26 Feb 2024 20:46:52 +0100 Subject: Drag & drop in web browser --- source/saw/main.c | 248 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 180 insertions(+), 68 deletions(-) (limited to 'source') diff --git a/source/saw/main.c b/source/saw/main.c index 4f4d2ae..721eb76 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -58,11 +58,9 @@ enum { // TODO - // - // Use 28224000 for time rate, divisible by common sample rates - // like 192000, 44100 etc. - // - // Dynamic buffer size. + // - Use 28224000 for time rate, divisible by common sample rates + // like 192000, 44100 etc. + // - Dynamic buffer size. CHANNEL_COUNT = 2, SAMPLE_RATE = 44100, @@ -193,6 +191,7 @@ typedef struct { saw_envelope_t envelope; } saw_oscillator_t; +typedef DA(u8) saw_da_u8_t; typedef DA(f32) saw_da_f32_t; typedef struct { @@ -239,49 +238,71 @@ typedef struct { // // Global state // +// NOTE +// At some point we want to move all global data into a struct +// saw_context_t. +// // ================================================================ +// Graphics +// + static struct NVGcontext *saw_nvg; -static ma_device saw_audio_device; +static i32 saw_font_text = -1; +static i32 saw_font_icons = -1; + +// Audio +// + +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_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; -static i64 saw_playback_lookahead = 0; -static i64 saw_playback_offset_read = 0; -static i64 saw_playback_offset_write = 0; -static f32 saw_playback_buffer[BUFFER_SIZE] = { 0.f }; -static f32 saw_playback_temp[BUFFER_SIZE]; +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; + +// Playback +// + +static b8 saw_playback_on = 0; +static i64 saw_playback_frame = 0; +static i64 saw_playback_lookahead = 0; +static i64 saw_playback_offset_read = 0; +static i64 saw_playback_offset_write = 0; static mtx_t saw_playback_mutex; -static i64 saw_current_track = 0; -static i64 saw_current_roll = 0; -static i64 saw_edit_mode = EDIT_MODE_HAND; +// Buffers +// + +static f32 saw_playback_buffer[BUFFER_SIZE]; +static f32 saw_playback_temp[BUFFER_SIZE]; +static b8 saw_key_pressed[512]; +static c8 saw_drop_file_name[4096]; +static saw_da_u8_t saw_drop_file_data; + +// Project state +// -static char saw_project_file_buf[4096]; -static str_t saw_project_file; -static i32 saw_font_text = -1; -static i32 saw_font_icons = -1; +static i64 saw_current_track = 0; +static i64 saw_current_roll = 0; +static i64 saw_edit_mode = EDIT_MODE_HAND; static mt64_state_t saw_rng_mt64; +static c8 saw_project_file_buf[4096]; +static str_t saw_project_file; + static saw_voice_t saw_voices[VOICE_COUNT] = { 0 }; static saw_roll_t saw_rolls[ROLL_COUNT]; static saw_track_t saw_tracks[TRACK_COUNT]; @@ -326,6 +347,10 @@ static f64 saw_ui_input_buffer = 0.; // // Sound // +// NOTE +// When music and signal processing procedures become stable enough +// we will separate them into a library and add tests. +// // ================================================================ #ifdef __GNUC__ @@ -869,6 +894,9 @@ static void saw_sampler_cleanup(saw_sampler_t *sampler) { // // UI // +// TODO +// - UI library and tests. +// // ================================================================ static void saw_ui_begin(void) { @@ -1782,7 +1810,7 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, i64 w = (width * 5) / 6; i64 sample_height = text_height * 4; - if (saw_drop_file[0] != '\0') { + if (saw_drop_file_data.size != 0) { // Load the audio sample from a file // @@ -1790,8 +1818,9 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, u32 sample_rate; drwav_uint64 count; - f32 *samples = drwav_open_file_and_read_pcm_frames_f32( - saw_drop_file, &channels, &sample_rate, &count, NULL); + f32 *samples = drwav_open_memory_and_read_pcm_frames_f32( + saw_drop_file_data.values, saw_drop_file_data.size, &channels, + &sample_rate, &count, NULL); if (samples == NULL) { printf("drwav_open_file_and_read_pcm_frames_f32 failed.\n"); @@ -1856,12 +1885,7 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); nvgText(saw_nvg, x0 + width / 2, frame_height - y0 - height + sample_height / 2, -#ifdef __EMSCRIPTEN__ - "Not implemented for the web yet", -#else - "Drop a WAV file here", -#endif - NULL); + "Drop a WAV file here", NULL); } saw_ui_value_float(x, y0 + height - sample_height - text_height, w, @@ -2509,6 +2533,15 @@ static void saw_init_audio(void) { } static void saw_init(void) { + // Init globals + // + + memset(saw_key_pressed, 0, sizeof saw_key_pressed); + memset(saw_drop_file_name, 0, sizeof saw_drop_file_name); + memset(&saw_drop_file_data, 0, sizeof saw_drop_file_data); + memset(saw_playback_buffer, 0, sizeof saw_playback_buffer); + memset(saw_playback_temp, 0, sizeof saw_playback_temp); + // Init RNG // @@ -2868,8 +2901,8 @@ static void saw_init(void) { static void saw_frame(void) { // TODO - // Check how much time passed to see if we need to adjust the - // buffer size. + // - Check how much time passed to see if we need to adjust the + // buffer size. (void) saw_ui_value_int; @@ -3002,7 +3035,11 @@ static void saw_frame(void) { saw_ctrl_on = 0; memset(saw_key_pressed, 0, sizeof saw_key_pressed); - memset(saw_drop_file, 0, sizeof saw_drop_file); + + if (saw_drop_file_data.size != 0) { + memset(saw_drop_file_name, 0, sizeof saw_drop_file_name); + DA_DESTROY(saw_drop_file_data); + } } } @@ -3167,6 +3204,23 @@ static void saw_cleanup(void) { saw_sampler_cleanup(&saw_tracks[i].sampler); } +#ifdef __EMSCRIPTEN__ +static void saw_fetch_drop( + sapp_html5_fetch_response const *response) { + assert(response != NULL); + + if (response == NULL || !response->succeeded) { + printf("Unable to fetch the dropped file\n"); + fflush(stdout); + return; + } + + assert(saw_drop_file_data.size >= response->data.size); + + DA_RESIZE(saw_drop_file_data, response->data.size); +} +#endif + static void saw_event(sapp_event const *event) { #ifdef __EMSCRIPTEN__ // In browser, resume the audio only after a user action. @@ -3289,20 +3343,80 @@ static void saw_event(sapp_event const *event) { break; case SAPP_EVENTTYPE_FILES_DROPPED: { + // Get the dropped file name + // + + i32 drop_count = sapp_get_num_dropped_files(); + if (drop_count <= 0) + break; + + c8 const *file_name = sapp_get_dropped_file_path(0); + + i64 len = strlen(file_name); + + assert(len > 0); + if (len <= 0) + break; + + if (len >= sizeof saw_drop_file_name) + len = sizeof saw_drop_file_name - 1; + + memcpy(saw_drop_file_name, file_name, len); + saw_drop_file_name[len] = '\0'; + + // Read the file data into the buffer + // + #ifdef __EMSCRIPTEN__ - (void) saw_drop_file; + i64 size = sapp_html5_get_dropped_file_size(0); #else - i32 n = sapp_get_num_dropped_files(); + i64 size = file_info(str(len, file_name)).size; +#endif - if (n > 0) { - char const *file_name = sapp_get_dropped_file_path(0); - i64 len = strlen(file_name); + assert(size > 0); + if (size <= 0) + break; - if (len > 0 && len < sizeof saw_drop_file) { - memcpy(saw_drop_file, file_name, len); - saw_drop_file[len] = '\0'; - } + if (saw_drop_file_data.size > 0) + DA_DESTROY(saw_drop_file_data); + + DA_INIT(saw_drop_file_data, size, NULL); + + assert(saw_drop_file_data.size == size); + if (saw_drop_file_data.size != size) { + printf("Bad alloc.\n"); + fflush(stdout); + break; } + +#ifdef __EMSCRIPTEN__ + sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request) { + .dropped_file_index = 0, + .callback = saw_fetch_drop, + .buffer = { .ptr = saw_drop_file_data.values, + .size = size }, + }); +#else + FILE *f = fopen(file_name, "rb"); + + assert(f != NULL); + if (f == NULL) { + printf("Unable to open file `%s`\n", file_name); + fflush(stdout); + break; + } + + size = fread(saw_drop_file_data.values, 1, size, f); + fclose(f); + + assert(size > 0); + if (size <= 0) { + printf("Unable to read file `%s`\n", file_name); + fflush(stdout); + break; + } + + DA_RESIZE(saw_drop_file_data, size); #endif } break; @@ -3368,17 +3482,15 @@ sapp_desc sokol_main(i32 argc, char **argv) { exit(0); return (sapp_desc) { - .window_title = "Saw", - .width = 1280, - .height = 720, -#ifndef __EMSCRIPTEN__ + .window_title = "Saw", + .width = 1280, + .height = 720, .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_, + .max_dropped_file_path_length = sizeof saw_drop_file_name, + .init_cb = saw_init, + .frame_cb = saw_frame, + .cleanup_cb = saw_cleanup, + .event_cb = saw_event, + .logger.func = log_, }; } -- cgit v1.2.3