diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | source/saw/main.c | 1098 |
2 files changed, 718 insertions, 382 deletions
@@ -28,6 +28,7 @@ To-Do list - Matrix view - Graph view - Custom font and localization + - File browser - Data - WAV export @@ -40,6 +41,7 @@ To-Do list - MIDI import - Drag & drop sheet files - Drag & drop project files + - Automatic serialization Bugs diff --git a/source/saw/main.c b/source/saw/main.c index 6cf0632..8d26d27 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -2,6 +2,12 @@ // // Saw main code // +// TODO +// - Use 28224000 for time rate, divisible by common sample rates +// like 192000, 44100 etc. +// - Dynamic buffer size. +// - Logging routines. +// // ================================================================ // ================================================================ @@ -16,7 +22,9 @@ #include "../kit/mersenne_twister_64.h" #include "../kit/secure_random.h" #include "../kit/file.h" +#include "../kit/input_buffer.h" #include "../kit/threads.h" +#include "../kit/status.h" #if defined(__EMSCRIPTEN__) # include <GLES3/gl3.h> @@ -61,12 +69,11 @@ #define EQUAL_TEMPERAMENT_FACTOR 1.05946309436 // 2^(1/12) #define GLOBAL_VOLUME 2.0 -enum { - // TODO - // - Use 28224000 for time rate, divisible by common sample rates - // like 192000, 44100 etc. - // - Dynamic buffer size. +#define SPACES SZ(" \t\n\r") +#define DELIM SZ(" \t\n\r}") +#define NUMS SZ(" \t\n\r+-0123456789.") +enum { CHANNEL_COUNT = 2, SAMPLE_RATE = 44100, BUFFER_SIZE = 1024 * 16, @@ -219,6 +226,11 @@ typedef struct { f32 hover[4]; } saw_ui_color_t; +typedef struct { + i8 type; + str_t data; +} saw_project_token_t; + // ================================================================ // // Global state @@ -244,10 +256,10 @@ 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 f64 saw_mouse_x = 0; +static f64 saw_mouse_y = 0; +static f64 saw_mouse_dx = 0; +static f64 saw_mouse_dy = 0; static b8 saw_lbutton_click = 0; static b8 saw_lbutton_down = 0; static b8 saw_rbutton_click = 0; @@ -340,7 +352,6 @@ static f64 saw_ui_input_buffer = 0.; #ifdef __GNUC__ # pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-function" # pragma GCC diagnostic ignored "-Wunknown-pragmas" # pragma GCC push_options # pragma GCC optimize("O3") @@ -854,6 +865,666 @@ static void saw_sampler_cleanup(saw_sampler_t *sampler) { // ================================================================ // +// Data +// +// TODO +// - Implement parsing routines in the kit library. +// - Floating point number format without data loss. +// +// ================================================================ + +static ib_token_t saw_parse_sign(ib_token_t tok, i64 *sign) { + assert(sign != NULL); + if (sign == NULL) + return (ib_token_t) { + .status = KIT_ERROR_INVALID_ARGUMENT, + }; + + ib_token_t next = ib_any(tok, SPACES); + ib_token_t plus = ib_exact(next, SZ("+")); + ib_token_t minus = ib_exact(next, SZ("-")); + + *sign = 1; + + if (plus.status == KIT_OK) + return ib_any(plus, SPACES); + + if (minus.status == KIT_OK) { + *sign = -1; + return ib_any(minus, SPACES); + } + + return next; +} + +static ib_token_t saw_parse_int(ib_token_t tok, i64 *x) { + assert(x != NULL); + if (x == NULL) + return (ib_token_t) { + .status = KIT_ERROR_INVALID_ARGUMENT, + }; + + i64 sign; + + tok = saw_parse_sign(tok, &sign); + tok = ib_any(tok, SZ("0123456789")); + + if (tok.status != KIT_OK) + return tok; + + if (tok.size == 0) { + tok.status = KIT_PARSING_FAILED; + return tok; + } + + str_t s = ib_str(tok); + + *x = 0; + + for (i64 i = 0; i < s.size; ++i) + *x = *x * 10 + (i64) (s.values[i] - '0'); + + *x *= sign; + return tok; +} + +static ib_token_t saw_parse_float(ib_token_t tok, f64 *x) { + assert(x != NULL); + if (x == NULL) + return (ib_token_t) { + .status = KIT_ERROR_INVALID_ARGUMENT, + }; + + i64 sign; + + tok = saw_parse_sign(tok, &sign); + tok = ib_any(tok, SZ("0123456789.")); + + if (tok.status != KIT_OK) + return tok; + + if (tok.size == 0) { + tok.status = KIT_PARSING_FAILED; + return tok; + } + + str_t s = ib_str(tok); + + i64 n = 0; + i64 dot = 0; + + for (i64 i = 0; i < s.size; ++i) { + if (s.values[i] == '.') + dot = i; + else + n = n * 10 + (i64) (s.values[i] - '0'); + } + + *x = (f64) n; + + if (dot < s.size - 1) + *x /= pow(10.0, s.size - dot - 1); + + *x *= sign; + return tok; +} + +static ib_token_t saw_parse_roll(ib_token_t tok, saw_roll_t *roll) { + assert(roll != NULL); + if (roll == NULL) + return (ib_token_t) { + .status = KIT_ERROR_INVALID_ARGUMENT, + }; + + // Init defaults + // + { + memset(roll, 0, sizeof *roll); + + saw_tuning_equal_temperament(roll->tuning); + roll->tuning_tag = TUNING_EQUAL_TEMPERAMENT; + roll->mark_pitch = REFERENCE_PITCH_INDEX; + roll->rate = ROLL_DEFAULT_RATE; + } + + tok = ib_any(tok, SPACES); + tok = ib_exact(tok, SZ("roll")); + tok = ib_any(tok, SPACES); + tok = ib_exact(tok, SZ("{")); + + while (tok.status == KIT_OK) { + tok = ib_any(tok, SPACES); + + // Finish parsing if we reached closing brace + // + + ib_token_t close = ib_exact(tok, SZ("}")); + + if (close.status == KIT_OK) { + tok = close; + break; + } + + // Parse property name + // + + tok = ib_any(tok, SPACES); + tok = ib_none(tok, DELIM); + + if (tok.status != KIT_OK) + break; + + if (tok.size == 0) { + tok.status = KIT_PARSING_FAILED; + break; + } + + ib_token_t name = tok; + + // Parse property values + // + + if (AR_EQUAL(ib_str(name), SZ("pitch_turned_off"))) + for (i64 pitch = 0; tok.status == KIT_OK; ++pitch) { + i64 x; + ib_token_t next = saw_parse_int(tok, &x); + + if (next.status != KIT_OK) + break; + + if (pitch >= PITCH_COUNT) { + printf("Too many roll pitch_turned_off values\n"); + tok = ib_any(next, NUMS); + break; + } + + roll->pitch_turned_off[pitch] = x ? 1 : 0; + tok = next; + } + else if (AR_EQUAL(ib_str(name), SZ("tuning"))) + for (i64 pitch = 0; tok.status == KIT_OK; ++pitch) { + f64 x; + ib_token_t next = saw_parse_float(tok, &x); + + if (next.status != KIT_OK) + break; + + if (pitch >= PITCH_COUNT) { + printf("Too many roll tuning values\n"); + tok = ib_any(next, NUMS); + break; + } + + roll->tuning[pitch] = x; + tok = next; + } + else if (AR_EQUAL(ib_str(name), SZ("notes"))) + for (i64 note = 0; tok.status == KIT_OK; ++note) { + i64 note_enabled, note_time, note_duration, note_pitch; + + ib_token_t next = saw_parse_int(tok, ¬e_enabled); + next = saw_parse_int(next, ¬e_time); + next = saw_parse_int(next, ¬e_duration); + next = saw_parse_int(next, ¬e_pitch); + + if (next.status != KIT_OK) + break; + + if (note >= SHEET_SIZE) { + printf("Too many roll notes values\n"); + tok = ib_any(next, NUMS); + break; + } + + roll->notes[note] = (saw_roll_note_t) { + .enabled = note_enabled ? 1 : 0, + .time = note_time, + .duration = note_duration, + .pitch = note_pitch, + }; + + tok = next; + } + else if (AR_EQUAL(ib_str(name), SZ("ui_offset"))) { + i64 x, y; + ib_token_t next = saw_parse_int(tok, &x); + next = saw_parse_int(next, &y); + + if (next.status == KIT_OK) { + tok = next; + roll->ui_offset_x = x; + roll->ui_offset_y = y; + } else { + printf("Ignored unknown roll property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } else { + i64 x; + ib_token_t next = saw_parse_int(tok, &x); + + if (next.status == KIT_OK) { + tok = next; + + if (AR_EQUAL(ib_str(name), SZ("enabled"))) + roll->enabled = x ? 1 : 0; + else if (AR_EQUAL(ib_str(name), SZ("track"))) + roll->track = x; + else if (AR_EQUAL(ib_str(name), SZ("tuning_tag"))) + roll->tuning_tag = x; + else if (AR_EQUAL(ib_str(name), SZ("mark_pitch"))) + roll->mark_pitch = x; + else if (AR_EQUAL(ib_str(name), SZ("rate"))) + roll->rate = x; + else if (AR_EQUAL(ib_str(name), SZ("time"))) + roll->time = x; + else if (AR_EQUAL(ib_str(name), SZ("duration"))) + roll->duration = x; + else if (AR_EQUAL(ib_str(name), SZ("loop_duration"))) + roll->loop_duration = x; + else { + printf("Ignored unknown roll property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } else { + printf("Ignored unknown roll property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } + } + + return tok; +} + +static ib_token_t saw_parse_track(ib_token_t tok, + saw_track_t *track) { + assert(track != NULL); + if (track == NULL) + return (ib_token_t) { + .status = KIT_ERROR_INVALID_ARGUMENT, + }; + + // Init defaults + // + { + memset(track, 0, sizeof *track); + track->instrument = INSTRUMENT_OSCILLATOR; + } + + tok = ib_any(tok, SPACES); + tok = ib_exact(tok, SZ("track")); + tok = ib_any(tok, SPACES); + tok = ib_exact(tok, SZ("{")); + + while (tok.status == KIT_OK) { + tok = ib_any(tok, SPACES); + + // Finish parsing if we reached closing brace + // + + ib_token_t close = ib_exact(tok, SZ("}")); + + if (close.status == KIT_OK) { + tok = close; + break; + } + + // Parse property name + // + + tok = ib_any(tok, SPACES); + tok = ib_none(tok, DELIM); + + if (tok.status != KIT_OK) + break; + + if (tok.size == 0) { + tok.status = KIT_PARSING_FAILED; + break; + } + + ib_token_t name = tok; + + // Parse property values + // + + if (AR_EQUAL(ib_str(name), SZ("data"))) + // Array values + // + + for (i64 k = 0; tok.status == KIT_OK; ++k) { + assert(track->instrument == INSTRUMENT_SAMPLER); + if (track->instrument != INSTRUMENT_SAMPLER) { + printf("Ignore unexpected track data property\n"); + tok = ib_any(tok, NUMS); + break; + } + + f64 x; + ib_token_t next = saw_parse_float(tok, &x); + + if (next.status != KIT_OK) + break; + + DA_RESIZE(track->sampler.data, k + 1); + + assert(track->sampler.data.size == k + 1); + if (track->sampler.data.size != k + 1) { + printf("Bad alloc\n"); + fflush(stdout); + tok = ib_any(tok, NUMS); + break; + } + + track->sampler.data.values[k] = x; + tok = next; + } + if (AR_EQUAL(ib_str(name), SZ("instrument")) || + AR_EQUAL(ib_str(name), SZ("wave"))) { + // Integer values + // + + i64 x; + ib_token_t next = saw_parse_int(tok, &x); + + if (next.status == KIT_OK) { + tok = next; + + if (AR_EQUAL(ib_str(name), SZ("instrument"))) { + if (track->instrument == INSTRUMENT_SAMPLER) + DA_DESTROY(track->sampler.data); + if (x == INSTRUMENT_SAMPLER) + DA_INIT(track->sampler.data, 0, NULL); + track->instrument = (i32) x; + } else if (AR_EQUAL(ib_str(name), SZ("wave")) && + track->instrument == INSTRUMENT_OSCILLATOR) + track->oscillator.wave = (i32) x; + else { + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } else { + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } else { + // Float values + // + + f64 x; + ib_token_t next = saw_parse_float(tok, &x); + + if (next.status == KIT_OK) { + tok = next; + + switch (track->instrument) { + case INSTRUMENT_OSCILLATOR: + if (AR_EQUAL(ib_str(name), SZ("warp"))) + track->oscillator.warp = x; + else if (AR_EQUAL(ib_str(name), SZ("phase"))) + track->oscillator.phase = x; + else if (AR_EQUAL(ib_str(name), SZ("stereo_width"))) + track->oscillator.stereo_width = x; + else if (AR_EQUAL(ib_str(name), SZ("volume"))) + track->oscillator.volume = x; + else if (AR_EQUAL(ib_str(name), SZ("sustain"))) + track->oscillator.envelope.sustain = x; + else if (AR_EQUAL(ib_str(name), SZ("attack"))) + track->oscillator.envelope.attack = x; + else if (AR_EQUAL(ib_str(name), SZ("decay"))) + track->oscillator.envelope.decay = x; + else if (AR_EQUAL(ib_str(name), SZ("release"))) + track->oscillator.envelope.release = x; + else { + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + break; + + case INSTRUMENT_SAMPLER: + if (AR_EQUAL(ib_str(name), SZ("begin"))) + track->sampler.begin = x; + else if (AR_EQUAL(ib_str(name), SZ("end"))) + track->sampler.end = x; + else if (AR_EQUAL(ib_str(name), SZ("crossfade"))) + track->sampler.crossfade = x; + else if (AR_EQUAL(ib_str(name), SZ("base_frequency"))) + track->sampler.base_frequency = x; + else if (AR_EQUAL(ib_str(name), SZ("volume"))) + track->sampler.volume = x; + else if (AR_EQUAL(ib_str(name), SZ("sustain"))) + track->sampler.envelope.sustain = x; + else if (AR_EQUAL(ib_str(name), SZ("attack"))) + track->sampler.envelope.attack = x; + else if (AR_EQUAL(ib_str(name), SZ("decay"))) + track->sampler.envelope.decay = x; + else if (AR_EQUAL(ib_str(name), SZ("release"))) + track->sampler.envelope.release = x; + else { + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + break; + + default: + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } else { + printf("Ignored unknown track property `%s`\n", + BS(ib_str(name))); + tok = ib_any(tok, NUMS); + } + } + } + + return tok; +} + +static void saw_project_parse_file(str_t file_name) { + FILE *f = fopen(BS(file_name), "rb"); + + if (f == NULL) { + printf("fopen failed.\n"); + fflush(stdout); + return; + } + + is_handle_t in = is_wrap_file(f, NULL); + input_buffer_t buf = ib_wrap(in, NULL); + + ib_token_t last = ib_token(&buf); + + memset(saw_rolls, 0, sizeof saw_rolls); + memset(saw_tracks, 0, sizeof saw_tracks); + + saw_current_roll = -1; + saw_current_track = 0; + + i64 roll_index = 0; + i64 track_index = 0; + + for (;;) { + ib_token_t tok; + + // Parse roll + // + { + saw_roll_t roll; + tok = saw_parse_roll(last, &roll); + + if (tok.status == KIT_OK) { + if (roll_index < ROLL_COUNT) + saw_rolls[roll_index++] = roll; + else { + printf("Too many rolls.\n"); + fflush(stdout); + break; + } + + last = tok; + continue; + } + } + + // Parse track + // + { + saw_track_t track; + tok = saw_parse_track(last, &track); + + if (tok.status == KIT_OK) { + if (track_index < TRACK_COUNT) + saw_tracks[track_index++] = track; + else { + printf("Too many tracks.\n"); + fflush(stdout); + break; + } + + last = tok; + continue; + } + } + + break; + } + + ib_destroy(&buf); + is_destroy(in); + + for (i64 i = 0; i < ROLL_COUNT; ++i) + if (saw_rolls[i].enabled) { + saw_current_roll = i; + break; + } +} + +static void saw_project_print_to_file(str_t file_name) { + printf("Save project: %s\n", BS(saw_project_file)); + fflush(stdout); + + FILE *f = fopen(BS(saw_project_file), "wb"); + + if (f == NULL) { + printf("Failed to write file: %s\n", BS(saw_project_file)); + fflush(stdout); + return; + } + + // Save rolls + // + + i32 total_rolls = 0; + + for (i64 i = 0; i < ROLL_COUNT; i++) + if (saw_rolls[i].enabled) + total_rolls = i + 1; + + for (i64 i = 0; i < total_rolls; i++) { + saw_roll_t *roll = saw_rolls + i; + + fprintf(f, "roll {\n"); + + fprintf(f, " enabled %d\n", (i32) roll->enabled); + + if (roll->enabled) { + fprintf(f, " track %lld\n", roll->track); + fprintf(f, " pitch_turned_off "); + for (i64 pitch = 0; pitch < PITCH_COUNT; ++pitch) + fprintf(f, " %d", (i32) roll->pitch_turned_off[pitch]); + fprintf(f, "\n"); + fprintf(f, " tuning "); + for (i64 pitch = 0; pitch < PITCH_COUNT; ++pitch) + fprintf(f, " %f", roll->tuning[pitch]); + fprintf(f, "\n"); + fprintf(f, " tuning_tag %lld\n", roll->tuning_tag); + fprintf(f, " mark_pitch %lld\n", roll->mark_pitch); + fprintf(f, " rate %lld\n", roll->rate); + + i32 total_notes = 0; + for (i32 n = 0; n < SHEET_SIZE; n++) + if (roll->notes[n].enabled) + total_notes = n + 1; + + fprintf(f, " notes\n"); + for (i32 n = 0; n < total_notes; n++) + fprintf(f, " %d %-6lld %-6lld %-6lld\n", + (i32) roll->notes[n].enabled, roll->notes[n].time, + roll->notes[n].duration, roll->notes[n].pitch); + + fprintf(f, " time %lld\n", roll->time); + fprintf(f, " duration %lld\n", roll->duration); + fprintf(f, " loop_duration %lld\n", roll->loop_duration); + fprintf(f, " ui_offset %lld %lld\n\n", + roll->ui_offset_x, roll->ui_offset_y); + } + + fprintf(f, "}\n"); + } + + // Save tracks + // + + for (i64 i = 0; i < TRACK_COUNT; i++) { + saw_track_t *track = saw_tracks + i; + + fprintf(f, "track {\n"); + + fprintf(f, " instrument %d\n", track->instrument); + + switch (track->instrument) { + case INSTRUMENT_OSCILLATOR: { + saw_oscillator_t *osc = &track->oscillator; + + fprintf(f, " wave %d\n", osc->wave); + fprintf(f, " warp %f\n", osc->warp); + fprintf(f, " phase %f\n", osc->phase); + fprintf(f, " stereo_width %f\n", osc->stereo_width); + fprintf(f, " volume %f\n", osc->volume); + fprintf(f, " sustain %f\n", osc->envelope.sustain); + fprintf(f, " attack %f\n", osc->envelope.attack); + fprintf(f, " decay %f\n", osc->envelope.decay); + fprintf(f, " release %f\n\n", osc->envelope.release); + } break; + + case INSTRUMENT_SAMPLER: { + saw_sampler_t *sam = &track->sampler; + + fprintf(f, " data "); + for (i64 i = 0; i < sam->data.size; i++) + fprintf(f, " %f", sam->data.values[i]); + fprintf(f, "\n"); + + fprintf(f, " begin %f\n", sam->begin); + fprintf(f, " end %f\n", sam->end); + fprintf(f, " crossfade %f\n", sam->crossfade); + fprintf(f, " base_frequency %f\n", sam->base_frequency); + fprintf(f, " volume %f\n", sam->volume); + fprintf(f, " sustain %f\n", sam->envelope.sustain); + fprintf(f, " attack %f\n", sam->envelope.attack); + fprintf(f, " decay %f\n", sam->envelope.decay); + fprintf(f, " release %f\n\n", sam->envelope.release); + } break; + + default:; + } + + fprintf(f, "}\n"); + } + + fclose(f); +} + +// ================================================================ +// // UI // // TODO @@ -1430,14 +2101,6 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { .loop_duration = 0, .ui_offset_x = 0, .ui_offset_y = ROLL_DEFAULT_UI_OFFSET_Y, - - .last_index = -1, - .grid_input = 0, - .grid_note = 0, - .grid_time = 0, - .ui_offset_x_input = 0, - .ui_offset_y_input = 0, - .loop_input = 0, }; saw_tuning_equal_temperament(saw_rolls[n].tuning); @@ -2551,6 +3214,9 @@ static void saw_init(void) { // Init Saw state // + // FIXME + // Add init routines + static f64 tuning[PITCH_COUNT]; saw_tuning_equal_temperament(tuning); @@ -2559,7 +3225,6 @@ static void saw_init(void) { saw_rolls[i] = (saw_roll_t) { .enabled = (i == 0), .track = 0, - .last_index = -1, .pitch_turned_off = { 0 }, .tuning = { 0 }, .tuning_tag = TUNING_EQUAL_TEMPERAMENT, @@ -2571,13 +3236,6 @@ static void saw_init(void) { .loop_duration = 0, .ui_offset_x = 0, .ui_offset_y = ROLL_DEFAULT_UI_OFFSET_Y, - - .grid_input = 0, - .grid_note = 0, - .grid_time = 0, - .ui_offset_x_input = 0, - .ui_offset_y_input = 0, - .loop_input = 0, }; memcpy(saw_rolls[i].tuning, tuning, sizeof tuning); @@ -2620,9 +3278,21 @@ static void saw_init(void) { BS(cache), (i32) s); fflush(stdout); } else { + c8 project_name[] = "quick.saw"; + + assert(cache.size + 1 + sizeof project_name < + sizeof saw_project_file_buf); + if (cache.size + 1 + sizeof project_name >= + sizeof saw_project_file_buf) { + printf("File name too big\n"); + fflush(stdout); + return; + } + memcpy(saw_project_file_buf, cache.values, cache.size); saw_project_file_buf[cache.size] = PATH_DELIM_C; - memcpy(saw_project_file_buf + cache.size + 1, "last", 5); + memcpy(saw_project_file_buf + cache.size + 1, project_name, + sizeof project_name); saw_project_file.size = strlen(saw_project_file_buf); saw_project_file.values = saw_project_file_buf; @@ -2635,222 +3305,7 @@ static void saw_init(void) { // Load the project from a file // - // TODO - // - More flexible project file format. - - saw_current_roll = -1; - - if (path_type(saw_project_file) == PATH_FILE) { - FILE *f = fopen(BS(saw_project_file), "rb"); - - if (f == NULL) { - printf("Failed to read file: %s\n", BS(saw_project_file)); - fflush(stdout); - return; - } - -#define SCAN_(format_, num_, ...) \ - do { \ - if (fscanf(f, format_, __VA_ARGS__) != num_) { \ - printf("Invalid syntax at \"%s\"\n", format_); \ - fflush(stdout); \ - fclose(f); \ - return; \ - } \ - } while (0) - - i32 total_rolls; - - SCAN_(" rolls %d", 1, &total_rolls); - - for (i64 i = 0; i < total_rolls; i++) { - saw_roll_t *roll = saw_rolls + i; - - i32 enabled; - SCAN_(" enabled %d", 1, &enabled); - roll->enabled = enabled ? 1 : 0; - - if (enabled && saw_current_roll == -1) - saw_current_roll = i; - - SCAN_(" track %lld", 1, &roll->track); - - i32 pitch_count; - SCAN_(" pitch_turned_off %d", 1, &pitch_count); - - if (pitch_count < 0 || pitch_count > PITCH_COUNT) { - printf("Invalid pitch count: %d\n", pitch_count); - fflush(stdout); - fclose(f); - return; - } - - for (i64 pitch = 0; pitch < pitch_count; ++pitch) { - i32 flag; - SCAN_(" %d", 1, &flag); - roll->pitch_turned_off[pitch] = flag ? 1 : 0; - } - - SCAN_(" tuning %d", 1, &pitch_count); - - if (pitch_count < 0 || pitch_count > PITCH_COUNT) { - printf("Invalid tuning count: %d\n", pitch_count); - fflush(stdout); - fclose(f); - return; - } - - for (i64 pitch = 0; pitch < pitch_count; ++pitch) { - i64 frequency; - SCAN_(" %lld", 1, &frequency); - roll->tuning[pitch] = .0001 * frequency; - } - - SCAN_(" tuning_tag %lld", 1, &roll->tuning_tag); - SCAN_(" mark_pitch %lld", 1, &roll->mark_pitch); - - SCAN_(" rate %lld", 1, &roll->rate); - - i32 sheet_size; - SCAN_(" notes %d", 1, &sheet_size); - - if (sheet_size < 0 || sheet_size > SHEET_SIZE) { - printf("Invalid note count: %d\n", sheet_size); - fflush(stdout); - fclose(f); - return; - } - - for (i64 n = 0; n < sheet_size; n++) { - i32 flag; - SCAN_(" %d %lld %lld %lld", 4, &flag, &roll->notes[n].time, - &roll->notes[n].duration, &roll->notes[n].pitch); - roll->notes[n].enabled = flag ? 1 : 0; - } - - SCAN_(" time %lld", 1, &roll->time); - SCAN_(" duration %lld", 1, &roll->duration); - SCAN_(" loop_duration %lld", 1, &roll->loop_duration); - SCAN_(" ui_offset %lld %lld", 2, &roll->ui_offset_x, - &roll->ui_offset_y); - } - - for (i64 i = total_rolls; i < ROLL_COUNT; ++i) - saw_rolls[i].enabled = false; - - i32 total_tracks; - SCAN_(" tracks %d", 1, &total_tracks); - - for (i64 i = 0; i < total_tracks; i++) { - saw_track_t *track = saw_tracks + i; - - SCAN_(" instrument %d", 1, &track->instrument); - - switch (track->instrument) { - case INSTRUMENT_OSCILLATOR: { - saw_oscillator_t *osc = &track->oscillator; - - i64 warp, phase, stereo_width, volume, sustain, attack, - decay, release; - - SCAN_(" wave %d", 1, &osc->wave); - SCAN_(" warp %lld", 1, &warp); - SCAN_(" phase %lld", 1, &phase); - - SCAN_(" stereo_width %lld", 1, &stereo_width); - SCAN_(" volume %lld", 1, &volume); - SCAN_(" sustain %lld", 1, &sustain); - SCAN_(" attack %lld", 1, &attack); - SCAN_(" decay %lld", 1, &decay); - SCAN_(" release %lld", 1, &release); - - osc->warp = warp * 0.0001 - 1.; - osc->phase = phase * 0.0001; - osc->stereo_width = stereo_width * 0.0001; - osc->volume = volume * 0.0001; - osc->envelope.sustain = sustain * 0.0001; - osc->envelope.attack = attack * 0.0001; - osc->envelope.decay = decay * 0.0001; - osc->envelope.release = release * 0.0001; - } break; - - case INSTRUMENT_SAMPLER: { - saw_sampler_t *sam = &track->sampler; - - DA_INIT(sam->data, 0, NULL); - DA_INIT(sam->outline, SAMPLER_OUTLINE_SIZE, NULL); - - if (track->sampler.outline.size != SAMPLER_OUTLINE_SIZE) { - printf("Bad alloc\n"); - fflush(stdout); - track->sampler.outline.size = 0; - } - - i64 data_size; - - SCAN_(" data %lld", 1, &data_size); - - if (data_size > 0) { - DA_RESIZE(sam->data, data_size * CHANNEL_COUNT); - - assert(sam->data.size == data_size * CHANNEL_COUNT); - - if (sam->data.size == data_size * CHANNEL_COUNT) { - for (i64 l, r, i = 0; i < data_size; i++) { - SCAN_(" %lld %lld", 2, &l, &r); - sam->data.values[i * 2] = (f32) (.0001 * l); - sam->data.values[i * 2 + 1] = (f32) (.0001 * r); - } - } else { - printf("Bad alloc\n"); - fflush(stdout); - - for (i64 l, r, i = 0; i < data_size; i++) - SCAN_(" %lld %lld", 2, &l, &r); - } - - if (sam->outline.size == SAMPLER_OUTLINE_SIZE) - for (i64 i = 0; i < SAMPLER_OUTLINE_SIZE; i++) { - f32 *v = sam->data.values; - i64 k = i * data_size / SAMPLER_OUTLINE_SIZE; - sam->outline.values[i] = fabs(v[k] + v[k + 1]) * .5; - } - } - - i64 begin, end, crossfade, bfreq, volume, sustain, attack, - decay, release; - - SCAN_(" begin %lld", 1, &begin); - SCAN_(" end %lld", 1, &end); - SCAN_(" crossfade %lld", 1, &crossfade); - SCAN_(" base_frequency %lld", 1, &bfreq); - SCAN_(" volume %lld", 1, &volume); - SCAN_(" sustain %lld", 1, &sustain); - SCAN_(" attack %lld", 1, &attack); - SCAN_(" decay %lld", 1, &decay); - SCAN_(" release %lld", 1, &release); - - sam->begin = begin * .0001; - sam->end = end * .0001; - sam->crossfade = crossfade * .0001; - sam->base_frequency = bfreq * .0001; - sam->volume = volume * .0001; - sam->envelope.sustain = sustain * .0001; - sam->envelope.attack = attack * .0001; - sam->envelope.decay = decay * .0001; - sam->envelope.release = release * .0001; - } break; - - default: - memset(track, 0, sizeof *track); - track->instrument = INSTRUMENT_NONE; - } - } - -#undef SCAN_ - - fclose(f); - } + saw_project_parse_file(saw_project_file); } static void saw_frame(void) { @@ -3011,135 +3466,8 @@ static void saw_cleanup(void) { // Save the project to a file // - if (saw_project_file.size == 0) - return; - - printf("Save project: %s\n", BS(saw_project_file)); - fflush(stdout); - - FILE *f = fopen(BS(saw_project_file), "wb"); - - if (f == NULL) { - printf("Failed to write file: %s\n", BS(saw_project_file)); - fflush(stdout); - return; - } - - // Save rolls - // - - i32 total_rolls = 0; - - for (i64 i = 0; i < ROLL_COUNT; i++) - if (saw_rolls[i].enabled) - total_rolls = i + 1; - - fprintf(f, "rolls %d\n\n", total_rolls); - - for (i64 i = 0; i < total_rolls; i++) { - saw_roll_t *roll = saw_rolls + i; - - fprintf(f, "enabled %d\n", (i32) roll->enabled); - fprintf(f, "track %lld\n", roll->track); - fprintf(f, "pitch_turned_off %d\n ", PITCH_COUNT); - for (i64 pitch = 0; pitch < PITCH_COUNT; ++pitch) - fprintf(f, " %d", (i32) roll->pitch_turned_off[pitch]); - fprintf(f, "\n"); - fprintf(f, "tuning %d\n ", PITCH_COUNT); - for (i64 pitch = 0; pitch < PITCH_COUNT; ++pitch) - fprintf(f, " %lld", - (i64) floor(roll->tuning[pitch] * 10000 + .5)); - fprintf(f, "\n"); - fprintf(f, "tuning_tag %lld\n", roll->tuning_tag); - fprintf(f, "mark_pitch %lld\n", roll->mark_pitch); - fprintf(f, "rate %lld\n", roll->rate); - - i32 total_notes = 0; - for (i32 n = 0; n < SHEET_SIZE; n++) - if (roll->notes[n].enabled) - total_notes = n + 1; - - fprintf(f, "notes %d\n", total_notes); - for (i32 n = 0; n < total_notes; n++) - fprintf(f, " %d %4lld %4lld %4lld\n", - (i32) roll->notes[n].enabled, roll->notes[n].time, - roll->notes[n].duration, roll->notes[n].pitch); - - fprintf(f, "time %lld\n", roll->time); - fprintf(f, "duration %lld\n", roll->duration); - fprintf(f, "loop_duration %lld\n", roll->loop_duration); - fprintf(f, "ui_offset %lld %lld\n\n", roll->ui_offset_x, - roll->ui_offset_y); - } - - // Save tracks - // - - fprintf(f, "tracks %d\n\n", TRACK_COUNT); - - for (i64 i = 0; i < TRACK_COUNT; i++) { - saw_track_t *track = saw_tracks + i; - - fprintf(f, "instrument %d\n", track->instrument); - - switch (track->instrument) { - case INSTRUMENT_OSCILLATOR: { - saw_oscillator_t *osc = &track->oscillator; - - fprintf(f, "wave %d\n", osc->wave); - fprintf(f, "warp %lld\n", - (i64) floor((1. + osc->warp) * 10000 + .5)); - fprintf(f, "phase %lld\n", - (i64) floor(osc->phase * 10000 + .5)); - fprintf(f, "stereo_width %lld\n", - (i64) floor(osc->stereo_width * 10000 + .5)); - fprintf(f, "volume %lld\n", - (i64) floor(osc->volume * 10000 + .5)); - fprintf(f, "sustain %lld\n", - (i64) floor(osc->envelope.sustain * 10000 + .5)); - fprintf(f, "attack %lld\n", - (i64) floor(osc->envelope.attack * 10000 + .5)); - fprintf(f, "decay %lld\n", - (i64) floor(osc->envelope.decay * 10000 + .5)); - fprintf(f, "release %lld\n\n", - (i64) floor(osc->envelope.release * 10000 + .5)); - } break; - - case INSTRUMENT_SAMPLER: { - saw_sampler_t *sam = &track->sampler; - - fprintf(f, "data %lld ", - (i64) sam->data.size / CHANNEL_COUNT); - for (i64 i = 0; i < sam->data.size; i++) - fprintf(f, " %lld", - (i64) floor(sam->data.values[i] * 10000 + .5)); - fprintf(f, "\n"); - - fprintf(f, "begin %lld\n", - (i64) floor(sam->begin * 10000 + .5)); - fprintf(f, "end %lld\n", - (i64) floor(sam->end * 10000 + .5)); - fprintf(f, "crossfade %lld\n", - (i64) floor(sam->crossfade * 10000 + .5)); - fprintf(f, "base_frequency %lld\n", - (i64) floor(sam->base_frequency * 10000 + .5)); - fprintf(f, "volume %lld\n", - (i64) floor(sam->volume * 10000 + .5)); - fprintf(f, "sustain %lld\n", - (i64) floor(sam->envelope.sustain * 10000 + .5)); - fprintf(f, "attack %lld\n", - (i64) floor(sam->envelope.attack * 10000 + .5)); - fprintf(f, "decay %lld\n", - (i64) floor(sam->envelope.decay * 10000 + .5)); - fprintf(f, "release %lld\n\n", - (i64) floor(sam->envelope.release * 10000 + .5)); - } break; - - default:; - } - } - - fclose(f); + if (saw_project_file.size != 0) + saw_project_print_to_file(saw_project_file); // Cleanup samplers // @@ -3189,10 +3517,10 @@ static void saw_event(sapp_event const *event) { 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); + saw_mouse_dx += event->mouse_dx; + saw_mouse_dy += event->mouse_dy; + saw_mouse_x = event->mouse_x; + saw_mouse_y = event->mouse_y; break; case SAPP_EVENTTYPE_MOUSE_DOWN: @@ -3256,8 +3584,8 @@ static void saw_event(sapp_event const *event) { case SAPP_EVENTTYPE_TOUCHES_BEGAN: if (event->num_touches >= 1) { - saw_mouse_x = (i64) floor(event->touches[0].pos_x + .5); - saw_mouse_y = (i64) floor(event->touches[0].pos_y + .5); + saw_mouse_x = event->touches[0].pos_x; + saw_mouse_y = event->touches[0].pos_y; saw_lbutton_down = 1; saw_lbutton_click = 1; saw_mouse_on = 1; @@ -3266,8 +3594,8 @@ static void saw_event(sapp_event const *event) { case SAPP_EVENTTYPE_TOUCHES_MOVED: if (event->num_touches >= 1) { - i64 x = (i64) floor(event->touches[0].pos_x + .5); - i64 y = (i64) floor(event->touches[0].pos_y + .5); + i64 x = event->touches[0].pos_x; + i64 y = event->touches[0].pos_y; saw_mouse_dx += x - saw_mouse_x; saw_mouse_dy += y - saw_mouse_y; saw_mouse_x = x; @@ -3277,8 +3605,8 @@ static void saw_event(sapp_event const *event) { case SAPP_EVENTTYPE_TOUCHES_ENDED: if (event->num_touches >= 1) { - i64 x = (i64) floor(event->touches[0].pos_x + .5); - i64 y = (i64) floor(event->touches[0].pos_y + .5); + i64 x = event->touches[0].pos_x; + i64 y = event->touches[0].pos_y; saw_mouse_dx += x - saw_mouse_x; saw_mouse_dy += y - saw_mouse_y; saw_mouse_x = x; @@ -3294,6 +3622,12 @@ static void saw_event(sapp_event const *event) { break; case SAPP_EVENTTYPE_FILES_DROPPED: { + // Get the drop cursor position + // + + saw_mouse_x = event->mouse_x; + saw_mouse_y = event->mouse_y; + // Get the dropped file name // |