DX7 capability
This commit is contained in:
parent
7ff85048df
commit
0cecb05044
233
main.cpp
233
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<std::mutex> 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<std::pair<int, int>> 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<std::mutex> 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<sinkY; ++y) {
|
||||
engine.grid[6][y].type = SynthEngine::GridCell::WIRE; engine.grid[6][y].rotation = 2;
|
||||
}
|
||||
|
||||
} else if (preset == 2) {
|
||||
// Algo 1: Stack (FM)
|
||||
// Op 2 Modulates Op 1
|
||||
// Op 1 is Carrier
|
||||
|
||||
// Modulator (Top)
|
||||
placeOp(4, 0, 2.0f, 0.01f, 0.2f); // VCA at (7, 1)
|
||||
|
||||
// Carrier (Bottom)
|
||||
placeOp(4, 2, 1.0f, 0.01f, 0.8f); // VCA at (7, 3). Osc at (7, 2).
|
||||
|
||||
// Connect Modulator to Carrier
|
||||
// Mod VCA (7, 1) South.
|
||||
// Carrier Osc (7, 2) South. Back is North (7, 1).
|
||||
// Direct connection! No wire needed.
|
||||
|
||||
// Carrier Output to Sink
|
||||
// Carrier VCA (7, 3) South.
|
||||
engine.grid[7][4].type = SynthEngine::GridCell::WIRE; engine.grid[7][4].rotation = 3; // W
|
||||
engine.grid[6][4].type = SynthEngine::GridCell::WIRE; engine.grid[6][4].rotation = 2; // S
|
||||
for(int y=5; y<sinkY; ++y) {
|
||||
engine.grid[6][y].type = SynthEngine::GridCell::WIRE; engine.grid[6][y].rotation = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
(void)argc; (void)argv;
|
||||
|
||||
@ -872,7 +1015,6 @@ int main(int argc, char* argv[]) {
|
||||
SynthEngine::GridCell::WAVETABLE,
|
||||
SynthEngine::GridCell::NOISE,
|
||||
SynthEngine::GridCell::LFO,
|
||||
SynthEngine::GridCell::GATE,
|
||||
SynthEngine::GridCell::GATE_INPUT,
|
||||
SynthEngine::GridCell::ADSR_ATTACK,
|
||||
SynthEngine::GridCell::ADSR_DECAY,
|
||||
@ -928,9 +1070,9 @@ int main(int argc, char* argv[]) {
|
||||
int mx = e.button.x;
|
||||
int my = e.button.y;
|
||||
if (mx < GRID_PANEL_WIDTH) {
|
||||
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<std::mutex> 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<std::mutex> 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<std::mutex> 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user