From c7aa020f670466ba09741484ba24f489d32de888 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Sun, 1 Mar 2026 15:19:36 +0100 Subject: [PATCH] fix initial grid state --- simulator/main.cpp | 137 ++++++++++++++++++++++++++++++++++++++------- synth_engine.cpp | 4 ++ synth_engine.h | 1 + 3 files changed, 122 insertions(+), 20 deletions(-) diff --git a/simulator/main.cpp b/simulator/main.cpp index c863aa4..a726586 100644 --- a/simulator/main.cpp +++ b/simulator/main.cpp @@ -45,7 +45,7 @@ bool auto_melody_enabled = false; Uint32 auto_melody_next_event_time = 0; const int c_major_scale[] = {0, 2, 4, 5, 7, 9, 11, 12}; // Semitones from root int current_preset = 0; - +int current_patch_slot = 0; // 0-7 float note_to_freq(int octave, int semitone_offset); @@ -54,6 +54,43 @@ float note_to_freq(int octave, int semitone_offset); // The audio callback needs access to our synth, so we make it global. SynthEngine engine(SAMPLE_RATE); +void savePatch(int slot) { + char filename[64]; + snprintf(filename, sizeof(filename), "noicesynth_patch_%d.dat", slot); + FILE* f = fopen(filename, "wb"); + if (f) { + uint8_t buf[SynthEngine::MAX_SERIALIZED_GRID_SIZE]; + size_t size = engine.exportGrid(buf); + fwrite(buf, 1, size, f); + fclose(f); + printf("Saved patch to slot %d (%s)\n", slot, filename); + } else { + printf("Failed to save patch to slot %d\n", slot); + } +} + +void loadPatch(int slot) { + char filename[64]; + snprintf(filename, sizeof(filename), "noicesynth_patch_%d.dat", slot); + FILE* f = fopen(filename, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + if (size > 0 && size <= (long)SynthEngine::MAX_SERIALIZED_GRID_SIZE) { + uint8_t buf[SynthEngine::MAX_SERIALIZED_GRID_SIZE]; + fread(buf, 1, size, f); + engine.importGrid(buf, size); + printf("Loaded patch from slot %d (%s)\n", slot, filename); + } + fclose(f); + } else { + printf("Failed to load patch from slot %d (file not found)\n", slot); + } +} + + /** * @brief The audio callback function that miniaudio will call. * @@ -923,6 +960,9 @@ void randomizeGrid() { } } + // 5. Update processing order for simulation + engine.updateGraph(); + // 6. Run Simulation engine.setGate(true); float oldFreq = engine.getFrequency(); @@ -1105,6 +1145,8 @@ int main(int argc, char* argv[]) { SDL_Event e; bool exportButtonPressed = false; bool importButtonPressed = false; + bool saveButtonPressed = false; + bool loadButtonPressed = false; while (!quit) { checkSerialInput(serialPort); @@ -1206,6 +1248,18 @@ int main(int argc, char* argv[]) { my >= importButtonRect.y && my <= importButtonRect.y + importButtonRect.h) { importButtonPressed = true; } + + SDL_Rect saveButtonRect = {270, 535, 80, 30}; + if (synthX >= saveButtonRect.x && synthX <= saveButtonRect.x + saveButtonRect.w && + my >= saveButtonRect.y && my <= saveButtonRect.y + saveButtonRect.h) { + saveButtonPressed = true; + } + + SDL_Rect loadButtonRect = {450, 535, 80, 30}; + if (synthX >= loadButtonRect.x && synthX <= loadButtonRect.x + loadButtonRect.w && + my >= loadButtonRect.y && my <= loadButtonRect.y + loadButtonRect.h) { + loadButtonPressed = true; + } } } else if (e.type == SDL_MOUSEWHEEL) { SDL_Keymod modState = SDL_GetModState(); @@ -1231,26 +1285,34 @@ int main(int argc, char* argv[]) { // Synth Scroll int synthX = mx - GRID_PANEL_WIDTH; - if (synthX < SYNTH_PANEL_WIDTH / 2) { // Left knob (Octave) - if (e.wheel.y > 0) current_octave++; - else if (e.wheel.y < 0) current_octave--; + if (my < 500) { + if (synthX < SYNTH_PANEL_WIDTH / 2) { // Left knob (Octave) + if (e.wheel.y > 0) current_octave++; + else if (e.wheel.y < 0) current_octave--; - if (current_octave < 0) current_octave = 0; - if (current_octave > 8) current_octave = 8; + if (current_octave < 0) current_octave = 0; + if (current_octave > 8) current_octave = 8; - // If a note is being held, update its frequency to the new octave - if (!auto_melody_enabled && current_key_scancode != 0) { - engine.setFrequency(note_to_freq(current_octave, key_to_note_map[ (SDL_Scancode)current_key_scancode ])); + // If a note is being held, update its frequency to the new octave + if (!auto_melody_enabled && current_key_scancode != 0) { + engine.setFrequency(note_to_freq(current_octave, key_to_note_map[ (SDL_Scancode)current_key_scancode ])); + } + } else { // Right knob (volume) + float volStep = fineTune ? 0.01f : 0.05f; + if (e.wheel.y > 0) knob_vol_val += volStep; + else if (e.wheel.y < 0) knob_vol_val -= volStep; + + if (knob_vol_val > 1.0f) knob_vol_val = 1.0f; + if (knob_vol_val < 0.0f) knob_vol_val = 0.0f; + engine.setVolume(knob_vol_val); + } + } else { + // Patch Slot Knob + if (e.wheel.y > 0) current_patch_slot++; + else if (e.wheel.y < 0) current_patch_slot--; + if (current_patch_slot < 0) current_patch_slot = 0; + if (current_patch_slot > 7) current_patch_slot = 7; } - } else { // Right knob (volume) - float volStep = fineTune ? 0.01f : 0.05f; - if (e.wheel.y > 0) knob_vol_val += volStep; - else if (e.wheel.y < 0) knob_vol_val -= volStep; - - if (knob_vol_val > 1.0f) knob_vol_val = 1.0f; - if (knob_vol_val < 0.0f) knob_vol_val = 0.0f; - engine.setVolume(knob_vol_val); - } } } else if (e.type == SDL_KEYDOWN) { if (e.key.repeat == 0) { // Ignore key repeats @@ -1330,6 +1392,30 @@ int main(int argc, char* argv[]) { } importButtonPressed = false; } + if (saveButtonPressed) { + int mx = e.button.x; + int my = e.button.y; + int synthX = mx - GRID_PANEL_WIDTH; + SDL_Rect saveButtonRect = {270, 535, 80, 30}; + if (mx >= GRID_PANEL_WIDTH && + synthX >= saveButtonRect.x && synthX <= saveButtonRect.x + saveButtonRect.w && + my >= saveButtonRect.y && my <= saveButtonRect.y + saveButtonRect.h) { + savePatch(current_patch_slot); + } + saveButtonPressed = false; + } + if (loadButtonPressed) { + int mx = e.button.x; + int my = e.button.y; + int synthX = mx - GRID_PANEL_WIDTH; + SDL_Rect loadButtonRect = {450, 535, 80, 30}; + if (mx >= GRID_PANEL_WIDTH && + synthX >= loadButtonRect.x && synthX <= loadButtonRect.x + loadButtonRect.w && + my >= loadButtonRect.y && my <= loadButtonRect.y + loadButtonRect.h) { + loadPatch(current_patch_slot); + } + loadButtonPressed = false; + } } else if (e.type == SDL_KEYUP) { if (!auto_melody_enabled && e.key.keysym.scancode == current_key_scancode) { engine.setGate(false); @@ -1340,11 +1426,12 @@ int main(int argc, char* argv[]) { // Update window title with current values char title[256]; - snprintf(title, sizeof(title), "NoiceSynth | Vol: %.0f%% | Oct: %d | Auto(M): %s | Preset: %d", + snprintf(title, sizeof(title), "NoiceSynth | Vol: %.0f%% | Oct: %d | Auto(M): %s | Preset: %d | Slot: %d", knob_vol_val * 100.0f, current_octave, auto_melody_enabled ? "ON" : "OFF", - current_preset); + current_preset, + current_patch_slot); SDL_SetWindowTitle(window, title); // Clear screen @@ -1411,6 +1498,16 @@ int main(int argc, char* argv[]) { drawButton(renderer, 300, 435, 100, 30, "EXPORT", exportButtonPressed); drawButton(renderer, 410, 435, 100, 30, "IMPORT", importButtonPressed); + // Patch Slot Control + float normalized_slot = (float)current_patch_slot / 7.0f; + drawKnob(renderer, 400, 550, 40, normalized_slot); + char slotBuf[16]; + snprintf(slotBuf, sizeof(slotBuf), "SLOT %d", current_patch_slot); + drawString(renderer, 380, 600, 12, slotBuf); + + drawButton(renderer, 270, 535, 80, 30, "SAVE", saveButtonPressed); + drawButton(renderer, 450, 535, 80, 30, "LOAD", loadButtonPressed); + // --- Draw Grid Panel (Left) --- SDL_Rect gridViewport = {0, 0, GRID_PANEL_WIDTH, WINDOW_HEIGHT}; SDL_RenderSetViewport(renderer, &gridViewport); diff --git a/synth_engine.cpp b/synth_engine.cpp index 30563d1..6b43f0b 100644 --- a/synth_engine.cpp +++ b/synth_engine.cpp @@ -348,6 +348,10 @@ void SynthEngine::rebuildProcessingOrder() { rebuildProcessingOrder_locked(); } +void SynthEngine::updateGraph() { + rebuildProcessingOrder_locked(); +} + float SynthEngine::processGridStep() { auto isConnected = [&](int tx, int ty, int from_x, int from_y) -> bool { diff --git a/synth_engine.h b/synth_engine.h index 292d2d9..60ec861 100644 --- a/synth_engine.h +++ b/synth_engine.h @@ -118,6 +118,7 @@ public: int importGrid(const uint8_t* buffer, size_t size); void loadPreset(int preset); void rebuildProcessingOrder(); + void updateGraph(); void clearGrid(); GridCell grid[GRID_W][GRID_H];