From 0cecb0504413db68e16d78f7008531f4b68e08bf Mon Sep 17 00:00:00 2001 From: Dejvino Date: Sat, 28 Feb 2026 19:41:35 +0100 Subject: [PATCH] DX7 capability --- main.cpp | 233 ++++++++++++++++++++++++++++++++++++++--------- synth_engine.cpp | 22 ++--- synth_engine.h | 7 +- 3 files changed, 207 insertions(+), 55 deletions(-) diff --git a/main.cpp b/main.cpp index 8e70817..c6fd4e5 100644 --- a/main.cpp +++ b/main.cpp @@ -14,10 +14,11 @@ // --- Configuration --- const uint32_t SAMPLE_RATE = 44100; const uint32_t CHANNELS = 1; // Mono -const int GRID_PANEL_WIDTH = 400; +const int CELL_SIZE = 60; +const int GRID_PANEL_WIDTH = 12 * CELL_SIZE; // 720 const int SYNTH_PANEL_WIDTH = 800; const int WINDOW_WIDTH = GRID_PANEL_WIDTH + SYNTH_PANEL_WIDTH; // 1200 -const int WINDOW_HEIGHT = 640; +const int WINDOW_HEIGHT = 12 * CELL_SIZE; // 720 // --- Visualization Buffer --- const size_t VIS_BUFFER_SIZE = 8192; @@ -36,6 +37,7 @@ int current_key_scancode = 0; // 0 for none 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; float note_to_freq(int octave, int semitone_offset); @@ -243,6 +245,45 @@ void drawParamBar(SDL_Renderer* renderer, int x, int y, int size, float value, u SDL_RenderFillRect(renderer, &fg); } +void drawDirectionArrow(SDL_Renderer* renderer, int cx, int cy, int size, int rotation) { + int r = size / 2 - 2; + int tipX = cx; + int tipY = cy; + + switch(rotation) { + case 0: tipY -= r; break; // N + case 1: tipX += r; break; // E + case 2: tipY += r; break; // S + case 3: tipX -= r; break; // W + } + + int arrowSize = 5; + int x1, y1, x2, y2, x3, y3; + + if (rotation == 0) { // N + x1 = tipX; y1 = tipY; + x2 = tipX - arrowSize; y2 = tipY + arrowSize; + x3 = tipX + arrowSize; y3 = tipY + arrowSize; + } else if (rotation == 1) { // E + x1 = tipX; y1 = tipY; + x2 = tipX - arrowSize; y2 = tipY - arrowSize; + x3 = tipX - arrowSize; y3 = tipY + arrowSize; + } else if (rotation == 2) { // S + x1 = tipX; y1 = tipY; + x2 = tipX - arrowSize; y2 = tipY - arrowSize; + x3 = tipX + arrowSize; y3 = tipY - arrowSize; + } else { // W + x1 = tipX; y1 = tipY; + x2 = tipX + arrowSize; y2 = tipY - arrowSize; + x3 = tipX + arrowSize; y3 = tipY + arrowSize; + } + + SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); + SDL_RenderDrawLine(renderer, x1, y1, x2, y2); + SDL_RenderDrawLine(renderer, x2, y2, x3, y3); + SDL_RenderDrawLine(renderer, x3, y3, x1, y1); +} + void drawTypeLabel(SDL_Renderer* renderer, int x, int y, char c) { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); drawChar(renderer, x + 3, y + 3, 8, c); @@ -280,11 +321,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); - // Param (Fading) - char buf[16]; snprintf(buf, 16, "%.2f", cell.param); - SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); - drawString(renderer, x + 5, y + size - 18, 10, buf); - drawParamBar(renderer, x, y, size, cell.param, 0, 255, 0); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawTypeLabel(renderer, x, y, '-'); } else if (cell.type == SynthEngine::GridCell::FIXED_OSCILLATOR) { DrawCircle(renderer, cx, cy, r); @@ -295,6 +332,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Freq) char buf[16]; snprintf(buf, 16, "%.0f", 10.0f + cell.param*990.0f); @@ -312,6 +350,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Octave) char buf[16]; snprintf(buf, 16, "O%d", 1 + (int)(cell.param * 4.99f)); SDL_SetRenderDrawColor(renderer, 255, 200, 0, 255); @@ -330,6 +369,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Color) const char* colors[] = {"BRN", "PNK", "WHT", "YEL", "GRN"}; @@ -347,6 +387,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawString(renderer, cx - 8, cy - 5, 12, "LFO"); // Param (Freq) char buf[16]; snprintf(buf, 16, "%.1f", 0.1f + cell.param * 19.9f); @@ -354,12 +395,6 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G drawString(renderer, x + 5, y + size - 18, 10, buf); drawParamBar(renderer, x, y, size, cell.param, 0, 255, 255); drawTypeLabel(renderer, x, y, 'L'); - } else if (cell.type == SynthEngine::GridCell::GATE) { - SDL_Rect box = {cx - r, cy - r, r*2, r*2}; - SDL_RenderDrawRect(renderer, &box); - if (cell.value > 0.5f) SDL_RenderFillRect(renderer, &box); - drawString(renderer, cx - 8, cy - 5, 12, "GAT"); - drawTypeLabel(renderer, x, y, '!'); } else if (cell.type == SynthEngine::GridCell::GATE_INPUT) { SDL_Rect box = {cx - r, cy - r, r*2, r*2}; SDL_RenderDrawRect(renderer, &box); @@ -377,6 +412,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255); drawTypeLabel(renderer, x, y, 'A'); @@ -391,6 +427,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255); drawTypeLabel(renderer, x, y, 'D'); @@ -405,6 +442,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255); drawTypeLabel(renderer, x, y, 'S'); @@ -419,6 +457,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255); drawTypeLabel(renderer, x, y, 'E'); @@ -436,6 +475,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param char buf[16]; snprintf(buf, 16, "%.2f", cell.param); SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); @@ -456,6 +496,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param char buf[16]; snprintf(buf, 16, "%.2f", cell.param); SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); @@ -474,6 +515,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param char buf[16]; snprintf(buf, 16, "%.2f", cell.param); SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); @@ -492,6 +534,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); char buf[16]; snprintf(buf, 16, "%.2f", cell.param); SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255); drawString(renderer, x + 5, y + size - 18, 10, buf); @@ -509,6 +552,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 150, 0); drawTypeLabel(renderer, x, y, '|'); } else if (cell.type == SynthEngine::GridCell::PITCH_SHIFTER) { @@ -521,6 +565,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 100, 255, 100); drawTypeLabel(renderer, x, y, '^'); } else if (cell.type == SynthEngine::GridCell::GLITCH) { @@ -533,6 +578,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); drawParamBar(renderer, x, y, size, cell.param, 255, 0, 0); drawTypeLabel(renderer, x, y, 'G'); @@ -553,6 +599,8 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if(rDir==0) rdy=-r; else if(rDir==1) rdx=r; else if(rDir==2) rdy=r; else rdx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+ldx, cy+ldy); SDL_RenderDrawLine(renderer, cx, cy, cx+rdx, cy+rdy); + drawDirectionArrow(renderer, cx, cy, size, lDir); + drawDirectionArrow(renderer, cx, cy, size, rDir); // Param (Balance) char buf[16]; snprintf(buf, 16, "%.1f", cell.param); @@ -573,6 +621,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(outDir==0) ody=-r; else if(outDir==1) odx=r; else if(outDir==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Delay time in ms) char buf[16]; @@ -595,6 +644,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G int odx=0, ody=0; if(outDir==0) ody=-r; else if(outDir==1) odx=r; else if(outDir==2) ody=r; else odx=-r; SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Strength) char buf[16]; snprintf(buf, 16, "%.2f", cell.param); SDL_SetRenderDrawColor(renderer, 200, 100, 255, 255); @@ -610,6 +660,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Draw Op Symbol char opChar = '?'; @@ -632,6 +683,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G if (cell.rotation == 2) dy = r; if (cell.rotation == 3) dx = -r; SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy); + drawDirectionArrow(renderer, cx, cy, size, cell.rotation); // Param (Wave index) int idx = (int)(cell.param * 7.99f); char buf[4]; @@ -645,8 +697,8 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G void clearGrid() { std::lock_guard lock(engine.gridMutex); - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { SynthEngine::GridCell& c = engine.grid[x][y]; if (c.type == SynthEngine::GridCell::SINK) continue; @@ -671,8 +723,8 @@ void randomizeGrid() { const int numTypes = (int)SynthEngine::GridCell::SINK; // 1. Clear existing buffers first - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { SynthEngine::GridCell& c = engine.grid[x][y]; if (c.buffer) { delete[] c.buffer; @@ -687,14 +739,14 @@ void randomizeGrid() { int attempts = 0; bool inputOscillatorReachable = false; - bool visited[5][8]; + bool visited[SynthEngine::GRID_W][SynthEngine::GRID_H]; while (!inputOscillatorReachable && attempts < 1000) { attempts++; // 2. Randomize (without allocation) - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { SynthEngine::GridCell& c = engine.grid[x][y]; if (c.type == SynthEngine::GridCell::SINK) continue; @@ -705,12 +757,12 @@ void randomizeGrid() { } // 3. Check Connectivity - // BFS from SINK (2,3) backwards + // BFS from SINK backwards memset(visited, 0, sizeof(visited)); std::vector> q; - q.push_back({2, 3}); - visited[2][3] = true; + q.push_back({SynthEngine::GRID_W / 2, SynthEngine::GRID_H - 1}); + visited[SynthEngine::GRID_W / 2][SynthEngine::GRID_H - 1] = true; int head = 0; while(head < (int)q.size()) { @@ -725,7 +777,7 @@ void randomizeGrid() { for(int i=0; i<4; ++i) { int tx = cx + nx[i]; int ty = cy + ny[i]; - if (tx >= 0 && tx < 5 && ty >= 0 && ty < 8 && !visited[tx][ty]) { + if (tx >= 0 && tx < SynthEngine::GRID_W && ty >= 0 && ty < SynthEngine::GRID_H && !visited[tx][ty]) { SynthEngine::GridCell& neighbor = engine.grid[tx][ty]; bool pointsToCurr = false; @@ -766,8 +818,8 @@ void randomizeGrid() { } // After BFS, check if an input oscillator is reachable - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { if (visited[x][y] && engine.grid[x][y].type == SynthEngine::GridCell::INPUT_OSCILLATOR) { inputOscillatorReachable = true; break; @@ -779,8 +831,8 @@ void randomizeGrid() { // 4. Prune unreachable elements if a valid grid was found if (inputOscillatorReachable) { - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { if (!visited[x][y]) { engine.grid[x][y].type = SynthEngine::GridCell::EMPTY; engine.grid[x][y].param = 0.5f; @@ -791,8 +843,8 @@ void randomizeGrid() { } // 5. Allocate buffers for DELAYs and REVERBs that are still present - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < SynthEngine::GRID_W; ++x) { + for (int y = 0; y < SynthEngine::GRID_H; ++y) { SynthEngine::GridCell& c = engine.grid[x][y]; if (c.type == SynthEngine::GridCell::DELAY || c.type == SynthEngine::GridCell::REVERB || c.type == SynthEngine::GridCell::PITCH_SHIFTER) { c.buffer_size = 2 * SAMPLE_RATE; @@ -805,6 +857,97 @@ void randomizeGrid() { printf("Randomized in %d attempts. Input Osc reachable: %s\n", attempts, inputOscillatorReachable ? "YES" : "NO"); } +void loadPreset(int preset) { + clearGrid(); + std::lock_guard lock(engine.gridMutex); + + auto placeOp = [&](int x, int y, float ratio, float att, float rel) { + // Layout: + // (x, y) : G-IN (South) + // (x, y+1) : WIRE (East) + // (x+1, y+1): ATT (East) + // (x+2, y+1): REL (East) + // (x+3, y+1): VCA (South) + // (x+3, y) : OSC (South) + + engine.grid[x][y].type = SynthEngine::GridCell::GATE_INPUT; engine.grid[x][y].rotation = 2; // S + engine.grid[x][y+1].type = SynthEngine::GridCell::WIRE; engine.grid[x][y+1].rotation = 1; // E + + engine.grid[x+1][y+1].type = SynthEngine::GridCell::ADSR_ATTACK; engine.grid[x+1][y+1].rotation = 1; // E + engine.grid[x+1][y+1].param = att; + + engine.grid[x+2][y+1].type = SynthEngine::GridCell::ADSR_RELEASE; engine.grid[x+2][y+1].rotation = 1; // E + engine.grid[x+2][y+1].param = rel; + + engine.grid[x+3][y+1].type = SynthEngine::GridCell::VCA; engine.grid[x+3][y+1].rotation = 2; // S + engine.grid[x+3][y+1].param = 0.0f; // Controlled by Env + + engine.grid[x+3][y].type = SynthEngine::GridCell::INPUT_OSCILLATOR; engine.grid[x+3][y].rotation = 2; // S + engine.grid[x+3][y].param = (ratio > 1.0f) ? 0.5f : 0.0f; + }; + + int sinkX = SynthEngine::GRID_W / 2; + int sinkY = SynthEngine::GRID_H - 1; + + if (preset == 1) { + // Algo 32: Parallel Operators + // 6 Ops in parallel feeding the sink + // We'll place 3, and wire them + placeOp(0, 0, 1.0f, 0.01f, 0.5f); // Op 1 + placeOp(4, 0, 1.0f, 0.05f, 0.3f); // Op 2 + placeOp(8, 0, 2.0f, 0.01f, 0.2f); // Op 3 + + // Wire outputs to sink + // Op1 Out at (3, 1) -> South + // VCA is at (x+3, y+1). For Op1(0,0) -> (3,1). + engine.grid[3][2].type = SynthEngine::GridCell::WIRE; engine.grid[3][2].rotation = 2; + engine.grid[3][3].type = SynthEngine::GridCell::WIRE; engine.grid[3][3].rotation = 1; // E + engine.grid[4][3].type = SynthEngine::GridCell::WIRE; engine.grid[4][3].rotation = 1; // E + engine.grid[5][3].type = SynthEngine::GridCell::WIRE; engine.grid[5][3].rotation = 1; // E + engine.grid[6][3].type = SynthEngine::GridCell::WIRE; engine.grid[6][3].rotation = 2; // S + + // Op2 Out at (7, 1) -> South + engine.grid[7][2].type = SynthEngine::GridCell::WIRE; engine.grid[7][2].rotation = 2; + engine.grid[7][3].type = SynthEngine::GridCell::WIRE; engine.grid[7][3].rotation = 3; // W + + // Op3 Out at (11, 1) -> South + engine.grid[11][2].type = SynthEngine::GridCell::WIRE; engine.grid[11][2].rotation = 2; + engine.grid[11][3].type = SynthEngine::GridCell::WIRE; engine.grid[11][3].rotation = 3; // W + engine.grid[10][3].type = SynthEngine::GridCell::WIRE; engine.grid[10][3].rotation = 3; // W + engine.grid[9][3].type = SynthEngine::GridCell::WIRE; engine.grid[9][3].rotation = 3; // W + engine.grid[8][3].type = SynthEngine::GridCell::WIRE; engine.grid[8][3].rotation = 3; // W + + // Funnel down to sink + for(int y=4; y= 0 && gx < 5 && gy >= 0 && gy < 8) { + int gx = mx / CELL_SIZE; + int gy = my / CELL_SIZE; + if (gx >= 0 && gx < SynthEngine::GRID_W && gy >= 0 && gy < SynthEngine::GRID_H) { std::lock_guard lock(engine.gridMutex); SynthEngine::GridCell& c = engine.grid[gx][gy]; if (c.type != SynthEngine::GridCell::SINK) { @@ -998,9 +1140,9 @@ int main(int argc, char* argv[]) { if (mx < GRID_PANEL_WIDTH) { // Grid Scroll float step = fineTune ? 0.01f : 0.05f; - int gx = mx / 80; - int gy = my / 80; - if (gx >= 0 && gx < 5 && gy >= 0 && gy < 8) { + int gx = mx / CELL_SIZE; + int gy = my / CELL_SIZE; + if (gx >= 0 && gx < SynthEngine::GRID_W && gy >= 0 && gy < SynthEngine::GRID_H) { std::lock_guard lock(engine.gridMutex); SynthEngine::GridCell& c = engine.grid[gx][gy]; if (e.wheel.y > 0) c.param += step; @@ -1048,6 +1190,12 @@ int main(int argc, char* argv[]) { randomizeGrid(); } else if (e.key.keysym.scancode == SDL_SCANCODE_DELETE) { clearGrid(); + } else if (e.key.keysym.scancode == SDL_SCANCODE_PAGEUP) { + current_preset = (current_preset + 1) % 3; + loadPreset(current_preset); + } else if (e.key.keysym.scancode == SDL_SCANCODE_PAGEDOWN) { + current_preset = (current_preset - 1 + 3) % 3; + loadPreset(current_preset); } else if (e.key.keysym.scancode == SDL_SCANCODE_M) { auto_melody_enabled = !auto_melody_enabled; engine.setGate(false); // Silence synth on mode change @@ -1075,10 +1223,11 @@ 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", + snprintf(title, sizeof(title), "NoiceSynth | Vol: %.0f%% | Oct: %d | Auto(M): %s | Preset: %d", knob_vol_val * 100.0f, current_octave, - auto_melody_enabled ? "ON" : "OFF"); + auto_melody_enabled ? "ON" : "OFF", + current_preset); SDL_SetWindowTitle(window, title); // Clear screen @@ -1153,9 +1302,9 @@ int main(int argc, char* argv[]) { { // Lock only for reading state to draw std::lock_guard lock(engine.gridMutex); - for(int x=0; x<5; ++x) { - for(int y=0; y<8; ++y) { - drawGridCell(renderer, x*80, y*80, 80, engine.grid[x][y]); + for(int x=0; x < SynthEngine::GRID_W; ++x) { + for(int y=0; y < SynthEngine::GRID_H; ++y) { + drawGridCell(renderer, x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, engine.grid[x][y]); } } } diff --git a/synth_engine.cpp b/synth_engine.cpp index f666fa3..38794ff 100644 --- a/synth_engine.cpp +++ b/synth_engine.cpp @@ -33,7 +33,7 @@ SynthEngine::SynthEngine(uint32_t sampleRate) setFrequency(440.0f); // Initialize SINK - grid[2][3].type = GridCell::SINK; + grid[GRID_W / 2][GRID_H - 1].type = GridCell::SINK; } SynthEngine::~SynthEngine() { @@ -82,14 +82,14 @@ float SynthEngine::_random() { float SynthEngine::processGridStep() { // Double buffer for values to handle feedback loops gracefully (1-sample delay) - float next_values[5][8]; + float next_values[GRID_W][GRID_H]; auto isConnected = [&](int tx, int ty, int from_x, int from_y) -> bool { - if (from_x < 0 || from_x >= 5 || from_y < 0 || from_y >= 8) return false; + if (from_x < 0 || from_x >= GRID_W || from_y < 0 || from_y >= GRID_H) return false; GridCell& n = grid[from_x][from_y]; bool connects = false; - if (n.type == GridCell::WIRE || n.type == GridCell::FIXED_OSCILLATOR || n.type == GridCell::INPUT_OSCILLATOR || n.type == GridCell::WAVETABLE || n.type == GridCell::NOISE || n.type == GridCell::LFO || n.type == GridCell::GATE || n.type == GridCell::GATE_INPUT || n.type == GridCell::ADSR_ATTACK || n.type == GridCell::ADSR_DECAY || n.type == GridCell::ADSR_SUSTAIN || n.type == GridCell::ADSR_RELEASE || n.type == GridCell::LPF || n.type == GridCell::HPF || n.type == GridCell::VCA || n.type == GridCell::BITCRUSHER || n.type == GridCell::DISTORTION || n.type == GridCell::RECTIFIER || n.type == GridCell::PITCH_SHIFTER || n.type == GridCell::GLITCH || n.type == GridCell::OPERATOR || n.type == GridCell::DELAY || n.type == GridCell::REVERB) { + if (n.type == GridCell::WIRE || n.type == GridCell::FIXED_OSCILLATOR || n.type == GridCell::INPUT_OSCILLATOR || n.type == GridCell::WAVETABLE || n.type == GridCell::NOISE || n.type == GridCell::LFO || n.type == GridCell::GATE_INPUT || n.type == GridCell::ADSR_ATTACK || n.type == GridCell::ADSR_DECAY || n.type == GridCell::ADSR_SUSTAIN || n.type == GridCell::ADSR_RELEASE || n.type == GridCell::LPF || n.type == GridCell::HPF || n.type == GridCell::VCA || n.type == GridCell::BITCRUSHER || n.type == GridCell::DISTORTION || n.type == GridCell::RECTIFIER || n.type == GridCell::PITCH_SHIFTER || n.type == GridCell::GLITCH || n.type == GridCell::OPERATOR || n.type == GridCell::DELAY || n.type == GridCell::REVERB) { // Check rotation // 0:N (y-1), 1:E (x+1), 2:S (y+1), 3:W (x-1) if (n.rotation == 0 && from_y - 1 == ty && from_x == tx) connects = true; @@ -171,8 +171,8 @@ float SynthEngine::processGridStep() { return hasSide ? gain : 1.0f; }; - for (int x = 0; x < 5; ++x) { - for (int y = 0; y < 8; ++y) { + for (int x = 0; x < GRID_W; ++x) { + for (int y = 0; y < GRID_H; ++y) { GridCell& c = grid[x][y]; float val = 0.0f; @@ -279,7 +279,7 @@ float SynthEngine::processGridStep() { } else if (c.type == GridCell::FORK) { // Sum inputs from "Back" (Input direction) val = getInputFromTheBack(x, y, c); - } else if (c.type == GridCell::GATE || c.type == GridCell::GATE_INPUT) { + } else if (c.type == GridCell::GATE_INPUT) { // Outputs 1.0 when gate is open (key pressed), 0.0 otherwise val = _isGateOpen ? 1.0f : 0.0f; } else if (c.type == GridCell::ADSR_ATTACK) { @@ -311,7 +311,7 @@ float SynthEngine::processGridStep() { } else if (c.type == GridCell::WIRE) { // Sum inputs from all neighbors that point to me float sum = getSummedInput(x, y, c); - val = sum * c.param; // Fading + val = sum; } else if (c.type == GridCell::LPF) { // Input from Back float in = getInputFromTheBack(x, y, c); @@ -514,13 +514,13 @@ float SynthEngine::processGridStep() { } // Update state - for(int x=0; x<5; ++x) { - for(int y=0; y<8; ++y) { + for(int x=0; x < GRID_W; ++x) { + for(int y=0; y < GRID_H; ++y) { grid[x][y].value = next_values[x][y]; } } - return grid[2][3].value; + return grid[GRID_W / 2][GRID_H - 1].value; } void SynthEngine::process(int16_t* buffer, uint32_t numFrames) { diff --git a/synth_engine.h b/synth_engine.h index e84eabb..3355564 100644 --- a/synth_engine.h +++ b/synth_engine.h @@ -70,7 +70,7 @@ public: // --- Grid Synth --- struct GridCell { - enum Type { EMPTY, FIXED_OSCILLATOR, INPUT_OSCILLATOR, WAVETABLE, NOISE, LFO, GATE, GATE_INPUT, ADSR_ATTACK, ADSR_DECAY, ADSR_SUSTAIN, ADSR_RELEASE, FORK, WIRE, LPF, HPF, VCA, BITCRUSHER, DISTORTION, RECTIFIER, PITCH_SHIFTER, GLITCH, OPERATOR, DELAY, REVERB, SINK }; + enum Type { EMPTY, FIXED_OSCILLATOR, INPUT_OSCILLATOR, WAVETABLE, NOISE, LFO, GATE_INPUT, ADSR_ATTACK, ADSR_DECAY, ADSR_SUSTAIN, ADSR_RELEASE, FORK, WIRE, LPF, HPF, VCA, BITCRUSHER, DISTORTION, RECTIFIER, PITCH_SHIFTER, GLITCH, OPERATOR, DELAY, REVERB, SINK }; enum Op { OP_ADD, OP_MUL, OP_SUB, OP_DIV, OP_MIN, OP_MAX }; Type type = EMPTY; @@ -83,7 +83,10 @@ public: uint32_t write_idx = 0; // For Delay }; - GridCell grid[5][8]; + static const int GRID_W = 12; + static const int GRID_H = 12; + + GridCell grid[GRID_W][GRID_H]; std::mutex gridMutex; // Helper to process one sample step of the grid