diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2023-09-30 10:03:09 +0200 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2023-09-30 10:03:09 +0200 |
commit | 564c8381ac5d7d7713480689a8dba3e3d3f7d3cc (patch) | |
tree | d198b62302b999542549c4f8337629f705152c96 | |
parent | 1bec45aa796ac286df6848f5f77b0e43ebfe6d82 (diff) | |
download | saw-564c8381ac5d7d7713480689a8dba3e3d3f7d3cc.zip |
State load and store
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | source/saw/main.c | 290 |
2 files changed, 270 insertions, 22 deletions
@@ -7,7 +7,6 @@ To-Do list - Sound: Simple tonal synth - Sound: Kick, snare, cymbal - UI: Catalog -- State load and store - Data: WAV export - Sound: Buffering - Build: WebAssembly @@ -51,3 +50,4 @@ Done - UI: Piano roll panning - UI: Track composing - UI: Instrument settings +- State load and store diff --git a/source/saw/main.c b/source/saw/main.c index b5f46be..e505e4f 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -6,6 +6,7 @@ #include "../kit/string_ref.h" #include "../kit/mersenne_twister_64.h" #include "../kit/secure_random.h" +#include "../kit/file.h" #ifdef __EMSCRIPTEN__ # include <GLES3/gl3.h> @@ -76,19 +77,22 @@ typedef struct { typedef struct { i8 enabled; i64 track; - i32 last_index; i8 pitch_turned_off[PITCH_COUNT]; i64 rate; saw_roll_note_t notes[SHEET_SIZE]; - i64 frame; i64 size; - i8 grid_input; - i32 grid_note; - i32 grid_time; - i8 offset_x_input; - i8 offset_y_input; i64 offset_x; i64 offset_y; + + // dynamic properties + // + i32 last_index; + i64 frame; + i8 grid_input; + i32 grid_note; + i32 grid_time; + i8 offset_x_input; + i8 offset_y_input; } saw_roll_t; typedef struct { @@ -107,12 +111,18 @@ typedef struct { f64 stereo_width; f64 volume; saw_envelope_t envelope; - i32 value_input; - f64 value_buffer; + + // dynamic properties + // + i32 value_input; + f64 value_buffer; } saw_track_t; typedef struct { i64 rolls[ROLL_COUNT]; + + // dynamic properties + // i8 grid_input; i32 grid_roll; i32 grid_cell; @@ -124,8 +134,9 @@ typedef struct { static struct NVGcontext *saw_nvg; static ma_device saw_ma; -static i32 saw_font = -1; - +static char saw_project_file_buf[4096]; +static str_t saw_project_file; +static i32 saw_font = -1; static mt64_state_t saw_mt64; static i32 saw_mouse_x = 0; @@ -470,19 +481,20 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { saw_rolls[n] = (saw_roll_t) { .enabled = 1, .track = track, - .last_index = -1, .pitch_turned_off = { 0 }, .rate = 6, .notes = { 0 }, - .frame = frame, .size = 6 / grid_rate, - .grid_input = 0, - .grid_note = 0, - .grid_time = 0, - .offset_x_input = 0, - .offset_y_input = 0, .offset_x = 0, .offset_y = ROLL_DEFAULT_OFFSET_Y, + + .last_index = -1, + .frame = frame, + .grid_input = 0, + .grid_note = 0, + .grid_time = 0, + .offset_x_input = 0, + .offset_y_input = 0, }; saw_current_roll = n; @@ -738,9 +750,10 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0, nvgText(saw_nvg, x0 + border * 2, frame_height - y0 - height + next_y - border * 2, "Instrument", 0); - nvgText(saw_nvg, x0 + column_width + border * 2, - frame_height - y0 - height + next_y - border * 2, - buf_instr[track->instrument], 0); + if (track->instrument >= 0 && track->instrument <= INSTRUMENT_SINE) + nvgText(saw_nvg, x0 + column_width + border * 2, + frame_height - y0 - height + next_y - border * 2, + buf_instr[track->instrument], 0); next_y += text_height; char buf[100]; @@ -1222,6 +1235,146 @@ static void saw_init(void) { }, .value_input = TRACK_INPUT_NONE, }; + + // Determine the project file name + // + + if (saw_project_file.size == 0) { + char abc[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" + "Z0123456789"; + + char arena_buf[10000]; + kit_allocator_t arena = kit_alloc_buffer(sizeof arena_buf, + arena_buf); + + // No need to deallocate memory with arena. + str_builder_t cache = path_join(WRAP_STR(path_cache(&arena)), + SZ("saw"), &arena); + + kit_status_t s = folder_create_recursive(WRAP_STR(cache)); + + if (s != KIT_OK) + printf("Failed to create cache folder: %s\n (code %d)", + BS(cache), (i32) s); + else { + for (i32 index = 0;; ++index) { + memcpy(saw_project_file_buf, cache.values, cache.size); + saw_project_file_buf[cache.size] = PATH_DELIM_C; + + for (i32 i = 0; i < 16; i++) + saw_project_file_buf[cache.size + 1 + i] = + abc[mt64_generate(&saw_mt64) % (sizeof abc - 1)]; + + sprintf(saw_project_file_buf + cache.size + 17, "%d", index); + + saw_project_file.size = strlen(saw_project_file_buf); + saw_project_file.values = saw_project_file_buf; + + if (path_type(saw_project_file) == PATH_NONE) + break; + } + + printf("Project file: %s\n", saw_project_file_buf); + } + } + + // Load the project from a file + // + + if (path_type(saw_project_file) == PATH_FILE) { + printf("Open project: %s\n", BS(saw_project_file)); + + FILE *f = fopen(BS(saw_project_file), "rb"); + + if (f == NULL) { + printf("Failed to read file: %s\n", BS(saw_project_file)); + return; + } + +#define SCAN_(format_, num_, ...) \ + do { \ + if (fscanf(f, format_, __VA_ARGS__) != num_) { \ + printf("Invalid syntax at \"%s\"\n", format_); \ + fclose(f); \ + return; \ + } \ + } while (0) + + i32 total_rolls; + SCAN_(" compose_rolls %d", 1, &total_rolls); + for (i64 i = 0; i < total_rolls; i++) + SCAN_(" %lld", 1, saw_compose.rolls + i); + + 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; + + SCAN_(" track %lld", 1, &roll->track); + + i32 pitch_count; + SCAN_(" pitch_turned_off %d", 1, &pitch_count); + for (i64 pitch = 0; pitch < PITCH_COUNT; pitch++) { + i32 flag; + SCAN_(" %d", 1, &flag); + roll->pitch_turned_off[pitch] = flag ? 1 : 0; + } + + SCAN_(" rate %lld", 1, &roll->rate); + + i32 sheet_size; + SCAN_(" notes %d", 1, &sheet_size); + 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_(" size %lld", 1, &roll->size); + SCAN_(" offset %lld %lld", 2, &roll->offset_x, &roll->offset_y); + } + + i32 total_tracks; + SCAN_(" tracks %d", 1, &total_tracks); + + for (i64 i = 0; i < total_tracks; i++) { + saw_track_t *track = saw_tracks + i; + + i64 warp, phase, spread, stereo_width, volume, sustain, attack, + decay, release; + + SCAN_(" instrument %d", 1, &track->instrument); + SCAN_(" warp %lld", 1, &warp); + SCAN_(" phase %lld", 1, &phase); + SCAN_(" unison %d", 1, &track->unison); + SCAN_(" spread %lld", 1, &spread); + 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); + + track->warp = warp * 0.0001 - 1.; + track->phase = phase * 0.0001; + track->spread = spread * 0.0001; + track->stereo_width = stereo_width * 0.0001; + track->volume = volume * 0.0001; + track->envelope.sustain = sustain * 0.0001; + track->envelope.attack = attack * 0.0001; + track->envelope.decay = decay * 0.0001; + track->envelope.release = release * 0.0001; + } + +#undef SCAN_ + + fclose(f); + } } static void saw_frame(void) { @@ -1281,6 +1434,99 @@ static void saw_cleanup(void) { #else nvgDeleteGLES3(saw_nvg); #endif + + // Save the project to a file + // + + if (saw_project_file.size == 0) + return; + + printf("Save project: %s\n", BS(saw_project_file)); + + FILE *f = fopen(BS(saw_project_file), "wb"); + + if (f == NULL) { + printf("Failed to write file: %s\n", BS(saw_project_file)); + return; + } + + // Save the compose + // + + fprintf(f, "compose_rolls %d", ROLL_COUNT); + for (i64 i = 0; i < ROLL_COUNT; i++) + fprintf(f, " %lld", saw_compose.rolls[i]); + fprintf(f, "\n\n"); + + // 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, "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, "size %lld\n", roll->size); + fprintf(f, "offset %lld %lld\n\n", roll->offset_x, + roll->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); + fprintf(f, "warp %lld\n", + (i64) ((1. + track->warp) * 10000 + .5)); + fprintf(f, "phase %lld\n", + (i64) (track->phase * 10000 + .5)); + fprintf(f, "unison %d\n", track->unison); + fprintf(f, "spread %lld\n", + (i64) (track->spread * 10000 + .5)); + fprintf(f, "stereo_width %lld\n", + (i64) (track->stereo_width * 10000 + .5)); + fprintf(f, "volume %lld\n", + (i64) (track->volume * 10000 + .5)); + fprintf(f, "sustain %lld\n", + (i64) (track->envelope.sustain * 10000 + .5)); + fprintf(f, "attack %lld\n", + (i64) (track->envelope.attack * 10000 + .5)); + fprintf(f, "decay %lld\n", + (i64) (track->envelope.decay * 10000 + .5)); + fprintf(f, "release %lld\n\n", + (i64) (track->envelope.release * 10000 + .5)); + } + + fclose(f); } static void saw_event(sapp_event const *event) { @@ -1472,6 +1718,8 @@ sapp_desc sokol_main(i32 argc, char **argv) { for (i32 i = 0; i < argc; i++) if (strcmp(argv[i], "--version") == 0) print_version = 1; + else if (i > 0 && saw_project_file.size == 0) + saw_project_file = kit_str(strlen(argv[i]), argv[i]); else if (i > 0) printf("Unknown command line argument: \"%s\"\n", argv[i]); |