Wavetable and reverb
This commit is contained in:
parent
e86c06e05f
commit
58af6bd3dc
203
main.cpp
203
main.cpp
@ -194,6 +194,12 @@ void drawChar(SDL_Renderer* renderer, int x, int y, int size, char c) {
|
|||||||
case '>': SDL_RenderDrawLine(renderer, x, y, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w, y+h2, x, y+h); break;
|
case '>': SDL_RenderDrawLine(renderer, x, y, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w, y+h2, x, y+h); break;
|
||||||
case 'F': SDL_RenderDrawLine(renderer, x, y, x, y+h); SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x, y+h2, x+w2, y+h2); break;
|
case 'F': SDL_RenderDrawLine(renderer, x, y, x, y+h); SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x, y+h2, x+w2, y+h2); break;
|
||||||
case 'O': drawChar(renderer, x, y, size, '0'); break;
|
case 'O': drawChar(renderer, x, y, size, '0'); break;
|
||||||
|
case 'W':
|
||||||
|
SDL_RenderDrawLine(renderer, x, y, x, y+h);
|
||||||
|
SDL_RenderDrawLine(renderer, x, y+h, x+w2, y+h2);
|
||||||
|
SDL_RenderDrawLine(renderer, x+w2, y+h2, x+w, y+h);
|
||||||
|
SDL_RenderDrawLine(renderer, x+w, y+h, x+w, y);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +335,23 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
snprintf(buf, 16, "%.0fms", delay_ms);
|
snprintf(buf, 16, "%.0fms", delay_ms);
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 128, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 255, 128, 0, 255);
|
||||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||||
|
} else if (cell.type == SynthEngine::GridCell::REVERB) {
|
||||||
|
// Draw R
|
||||||
|
drawString(renderer, cx - 5, cy - 5, 12, "R");
|
||||||
|
// Input (Back)
|
||||||
|
int inDir = (cell.rotation + 2) % 4;
|
||||||
|
int idx=0, idy=0;
|
||||||
|
if(inDir==0) idy=-r; else if(inDir==1) idx=r; else if(inDir==2) idy=r; else idx=-r;
|
||||||
|
SDL_RenderDrawLine(renderer, cx+idx, cy+idy, cx, cy);
|
||||||
|
// Output (Front)
|
||||||
|
int outDir = cell.rotation;
|
||||||
|
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);
|
||||||
|
// Param (Strength)
|
||||||
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
||||||
|
SDL_SetRenderDrawColor(renderer, 200, 100, 255, 255);
|
||||||
|
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||||
} else if (cell.type == SynthEngine::GridCell::OPERATOR) {
|
} else if (cell.type == SynthEngine::GridCell::OPERATOR) {
|
||||||
SDL_Rect opRect = {cx - r, cy - r, r*2, r*2};
|
SDL_Rect opRect = {cx - r, cy - r, r*2, r*2};
|
||||||
SDL_RenderDrawRect(renderer, &opRect);
|
SDL_RenderDrawRect(renderer, &opRect);
|
||||||
@ -349,9 +372,171 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
else if (opType == 4) opChar = '<';
|
else if (opType == 4) opChar = '<';
|
||||||
else if (opType == 5) opChar = '>';
|
else if (opType == 5) opChar = '>';
|
||||||
drawChar(renderer, cx - 5, cy - 5, 12, opChar);
|
drawChar(renderer, cx - 5, cy - 5, 12, opChar);
|
||||||
|
} else if (cell.type == SynthEngine::GridCell::WAVETABLE) {
|
||||||
|
drawString(renderer, cx - 5, cy - 5, 12, "W");
|
||||||
|
// Direction line
|
||||||
|
int dx=0, dy=0;
|
||||||
|
if (cell.rotation == 0) dy = -r;
|
||||||
|
if (cell.rotation == 1) dx = r;
|
||||||
|
if (cell.rotation == 2) dy = r;
|
||||||
|
if (cell.rotation == 3) dx = -r;
|
||||||
|
SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy);
|
||||||
|
// Param (Wave index)
|
||||||
|
int idx = (int)(cell.param * 7.99f);
|
||||||
|
char buf[4];
|
||||||
|
snprintf(buf, 4, "%d", idx);
|
||||||
|
SDL_SetRenderDrawColor(renderer, 128, 128, 255, 255);
|
||||||
|
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearGrid() {
|
||||||
|
std::lock_guard<std::mutex> lock(engine.gridMutex);
|
||||||
|
for (int x = 0; x < 5; ++x) {
|
||||||
|
for (int y = 0; y < 8; ++y) {
|
||||||
|
SynthEngine::GridCell& c = engine.grid[x][y];
|
||||||
|
if (c.type == SynthEngine::GridCell::SINK) continue;
|
||||||
|
|
||||||
|
if ((c.type == SynthEngine::GridCell::DELAY || c.type == SynthEngine::GridCell::REVERB) && c.buffer) {
|
||||||
|
delete[] c.buffer;
|
||||||
|
c.buffer = nullptr;
|
||||||
|
c.buffer_size = 0;
|
||||||
|
}
|
||||||
|
c.type = SynthEngine::GridCell::EMPTY;
|
||||||
|
c.param = 0.5f;
|
||||||
|
c.rotation = 0;
|
||||||
|
c.value = 0.0f;
|
||||||
|
c.phase = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void randomizeGrid() {
|
||||||
|
std::lock_guard<std::mutex> lock(engine.gridMutex);
|
||||||
|
|
||||||
|
// Number of types to choose from (excluding SINK)
|
||||||
|
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) {
|
||||||
|
SynthEngine::GridCell& c = engine.grid[x][y];
|
||||||
|
if ((c.type == SynthEngine::GridCell::DELAY || c.type == SynthEngine::GridCell::REVERB) && c.buffer) {
|
||||||
|
delete[] c.buffer;
|
||||||
|
c.buffer = nullptr;
|
||||||
|
c.buffer_size = 0;
|
||||||
|
}
|
||||||
|
if (c.type != SynthEngine::GridCell::SINK) {
|
||||||
|
c.type = SynthEngine::GridCell::EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempts = 0;
|
||||||
|
bool connected = false;
|
||||||
|
|
||||||
|
while (!connected && attempts < 1000) {
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
// 2. Randomize (without allocation)
|
||||||
|
for (int x = 0; x < 5; ++x) {
|
||||||
|
for (int y = 0; y < 8; ++y) {
|
||||||
|
SynthEngine::GridCell& c = engine.grid[x][y];
|
||||||
|
if (c.type == SynthEngine::GridCell::SINK) continue;
|
||||||
|
|
||||||
|
c.type = (SynthEngine::GridCell::Type)(rand() % numTypes);
|
||||||
|
c.rotation = rand() % 4;
|
||||||
|
c.param = (float)rand() / (float)RAND_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check Connectivity
|
||||||
|
// BFS from SINK (2,3) backwards
|
||||||
|
bool visited[5][8] = {false};
|
||||||
|
std::vector<std::pair<int, int>> q;
|
||||||
|
|
||||||
|
q.push_back({2, 3});
|
||||||
|
visited[2][3] = true;
|
||||||
|
|
||||||
|
int head = 0;
|
||||||
|
while(head < (int)q.size()){
|
||||||
|
std::pair<int, int> curr = q[head++];
|
||||||
|
int cx = curr.first;
|
||||||
|
int cy = curr.second;
|
||||||
|
|
||||||
|
// Check neighbors to see if they output to (cx, cy)
|
||||||
|
int nx[4] = {0, 1, 0, -1};
|
||||||
|
int ny[4] = {-1, 0, 1, 0};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (visited[tx][ty]) continue;
|
||||||
|
|
||||||
|
SynthEngine::GridCell& neighbor = engine.grid[tx][ty];
|
||||||
|
bool pointsToCurr = false;
|
||||||
|
|
||||||
|
if (neighbor.type == SynthEngine::GridCell::EMPTY || neighbor.type == SynthEngine::GridCell::SINK) {
|
||||||
|
pointsToCurr = false;
|
||||||
|
} else if (neighbor.type == SynthEngine::GridCell::FORK) {
|
||||||
|
int dx = cx - tx;
|
||||||
|
int dy = cy - ty;
|
||||||
|
int dir = -1;
|
||||||
|
if (dx == 0 && dy == -1) dir = 0; // N
|
||||||
|
else if (dx == 1 && dy == 0) dir = 1; // E
|
||||||
|
else if (dx == 0 && dy == 1) dir = 2; // S
|
||||||
|
else if (dx == -1 && dy == 0) dir = 3; // W
|
||||||
|
|
||||||
|
int leftOut = (neighbor.rotation + 3) % 4;
|
||||||
|
int rightOut = (neighbor.rotation + 1) % 4;
|
||||||
|
if (dir == leftOut || dir == rightOut) pointsToCurr = true;
|
||||||
|
} else {
|
||||||
|
// Standard directional
|
||||||
|
int dx = cx - tx;
|
||||||
|
int dy = cy - ty;
|
||||||
|
int dir = -1;
|
||||||
|
if (dx == 0 && dy == -1) dir = 0; // N
|
||||||
|
else if (dx == 1 && dy == 0) dir = 1; // E
|
||||||
|
else if (dx == 0 && dy == 1) dir = 2; // S
|
||||||
|
else if (dx == -1 && dy == 0) dir = 3; // W
|
||||||
|
|
||||||
|
if (neighbor.rotation == dir) pointsToCurr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointsToCurr) {
|
||||||
|
if (neighbor.type == SynthEngine::GridCell::FIXED_OSCILLATOR ||
|
||||||
|
neighbor.type == SynthEngine::GridCell::INPUT_OSCILLATOR ||
|
||||||
|
neighbor.type == SynthEngine::GridCell::WAVETABLE ||
|
||||||
|
neighbor.type == SynthEngine::GridCell::NOISE) {
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
visited[tx][ty] = true;
|
||||||
|
q.push_back({tx, ty});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connected) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Allocate buffers for DELAYs
|
||||||
|
for (int x = 0; x < 5; ++x) {
|
||||||
|
for (int y = 0; y < 8; ++y) {
|
||||||
|
SynthEngine::GridCell& c = engine.grid[x][y];
|
||||||
|
if (c.type == SynthEngine::GridCell::DELAY || c.type == SynthEngine::GridCell::REVERB) {
|
||||||
|
c.buffer_size = 2 * SAMPLE_RATE;
|
||||||
|
c.buffer = new float[c.buffer_size]();
|
||||||
|
c.write_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Randomized in %d attempts. Connected: %s\n", attempts, connected ? "YES" : "NO");
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
(void)argc; (void)argv;
|
(void)argc; (void)argv;
|
||||||
|
|
||||||
@ -460,15 +645,17 @@ int main(int argc, char* argv[]) {
|
|||||||
SynthEngine::GridCell::Type newType = oldType;
|
SynthEngine::GridCell::Type newType = oldType;
|
||||||
|
|
||||||
if (e.button.button == SDL_BUTTON_LEFT) {
|
if (e.button.button == SDL_BUTTON_LEFT) {
|
||||||
// Cycle: EMPTY -> FIXED -> INPUT -> NOISE -> FORK -> WIRE -> OP -> DELAY -> EMPTY
|
// Cycle: EMPTY -> FIXED -> INPUT -> WAVETABLE -> NOISE -> FORK -> WIRE -> OP -> DELAY -> REVERB -> EMPTY
|
||||||
if (oldType == SynthEngine::GridCell::EMPTY) newType = SynthEngine::GridCell::FIXED_OSCILLATOR;
|
if (oldType == SynthEngine::GridCell::EMPTY) newType = SynthEngine::GridCell::FIXED_OSCILLATOR;
|
||||||
else if (oldType == SynthEngine::GridCell::FIXED_OSCILLATOR) newType = SynthEngine::GridCell::INPUT_OSCILLATOR;
|
else if (oldType == SynthEngine::GridCell::FIXED_OSCILLATOR) newType = SynthEngine::GridCell::INPUT_OSCILLATOR;
|
||||||
else if (oldType == SynthEngine::GridCell::INPUT_OSCILLATOR) newType = SynthEngine::GridCell::NOISE;
|
else if (oldType == SynthEngine::GridCell::INPUT_OSCILLATOR) newType = SynthEngine::GridCell::WAVETABLE;
|
||||||
|
else if (oldType == SynthEngine::GridCell::WAVETABLE) newType = SynthEngine::GridCell::NOISE;
|
||||||
else if (oldType == SynthEngine::GridCell::NOISE) newType = SynthEngine::GridCell::FORK;
|
else if (oldType == SynthEngine::GridCell::NOISE) newType = SynthEngine::GridCell::FORK;
|
||||||
else if (oldType == SynthEngine::GridCell::FORK) newType = SynthEngine::GridCell::WIRE;
|
else if (oldType == SynthEngine::GridCell::FORK) newType = SynthEngine::GridCell::WIRE;
|
||||||
else if (oldType == SynthEngine::GridCell::WIRE) newType = SynthEngine::GridCell::OPERATOR;
|
else if (oldType == SynthEngine::GridCell::WIRE) newType = SynthEngine::GridCell::OPERATOR;
|
||||||
else if (oldType == SynthEngine::GridCell::OPERATOR) newType = SynthEngine::GridCell::DELAY;
|
else if (oldType == SynthEngine::GridCell::OPERATOR) newType = SynthEngine::GridCell::DELAY;
|
||||||
else if (oldType == SynthEngine::GridCell::DELAY) newType = SynthEngine::GridCell::EMPTY;
|
else if (oldType == SynthEngine::GridCell::DELAY) newType = SynthEngine::GridCell::REVERB;
|
||||||
|
else if (oldType == SynthEngine::GridCell::REVERB) newType = SynthEngine::GridCell::EMPTY;
|
||||||
} else if (e.button.button == SDL_BUTTON_RIGHT) {
|
} else if (e.button.button == SDL_BUTTON_RIGHT) {
|
||||||
c.rotation = (c.rotation + 1) % 4;
|
c.rotation = (c.rotation + 1) % 4;
|
||||||
} else if (e.button.button == SDL_BUTTON_MIDDLE) {
|
} else if (e.button.button == SDL_BUTTON_MIDDLE) {
|
||||||
@ -479,14 +666,14 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (newType != oldType) {
|
if (newType != oldType) {
|
||||||
// If old type was DELAY, free its buffer
|
// If old type was DELAY, free its buffer
|
||||||
if (oldType == SynthEngine::GridCell::DELAY && c.buffer) {
|
if ((oldType == SynthEngine::GridCell::DELAY || oldType == SynthEngine::GridCell::REVERB) && c.buffer) {
|
||||||
delete[] c.buffer;
|
delete[] c.buffer;
|
||||||
c.buffer = nullptr;
|
c.buffer = nullptr;
|
||||||
c.buffer_size = 0;
|
c.buffer_size = 0;
|
||||||
}
|
}
|
||||||
c.type = newType;
|
c.type = newType;
|
||||||
// If new type is DELAY, allocate its buffer
|
// If new type is DELAY, allocate its buffer
|
||||||
if (newType == SynthEngine::GridCell::DELAY) {
|
if (newType == SynthEngine::GridCell::DELAY || newType == SynthEngine::GridCell::REVERB) {
|
||||||
c.buffer_size = 2 * SAMPLE_RATE; // Max 2 seconds delay
|
c.buffer_size = 2 * SAMPLE_RATE; // Max 2 seconds delay
|
||||||
c.buffer = new float[c.buffer_size](); // Allocate and zero-initialize
|
c.buffer = new float[c.buffer_size](); // Allocate and zero-initialize
|
||||||
c.write_idx = 0; // Reset write index
|
c.write_idx = 0; // Reset write index
|
||||||
@ -595,7 +782,11 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
} else if (e.type == SDL_KEYDOWN) {
|
} else if (e.type == SDL_KEYDOWN) {
|
||||||
if (e.key.repeat == 0) { // Ignore key repeats
|
if (e.key.repeat == 0) { // Ignore key repeats
|
||||||
if (e.key.keysym.scancode == SDL_SCANCODE_M) {
|
if (e.key.keysym.scancode == SDL_SCANCODE_INSERT) {
|
||||||
|
randomizeGrid();
|
||||||
|
} else if (e.key.keysym.scancode == SDL_SCANCODE_DELETE) {
|
||||||
|
clearGrid();
|
||||||
|
} else if (e.key.keysym.scancode == SDL_SCANCODE_M) {
|
||||||
auto_melody_enabled = !auto_melody_enabled;
|
auto_melody_enabled = !auto_melody_enabled;
|
||||||
engine.setGate(false); // Silence synth on mode change
|
engine.setGate(false); // Silence synth on mode change
|
||||||
current_key_scancode = 0;
|
current_key_scancode = 0;
|
||||||
|
|||||||
@ -118,7 +118,7 @@ float SynthEngine::processGridStep() {
|
|||||||
|
|
||||||
// Check if neighbor outputs to (tx, ty)
|
// Check if neighbor outputs to (tx, ty)
|
||||||
bool connects = false;
|
bool connects = false;
|
||||||
if (n.type == GridCell::WIRE || n.type == GridCell::FIXED_OSCILLATOR || n.type == GridCell::INPUT_OSCILLATOR || n.type == GridCell::OPERATOR || n.type == GridCell::NOISE || n.type == GridCell::DELAY) {
|
if (n.type == GridCell::WIRE || n.type == GridCell::FIXED_OSCILLATOR || n.type == GridCell::INPUT_OSCILLATOR || n.type == GridCell::WAVETABLE || n.type == GridCell::OPERATOR || n.type == GridCell::NOISE || n.type == GridCell::DELAY || n.type == GridCell::REVERB) {
|
||||||
// Check rotation
|
// Check rotation
|
||||||
// 0:N (y-1), 1:E (x+1), 2:S (y+1), 3:W (x-1)
|
// 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;
|
if (n.rotation == 0 && from_y - 1 == ty && from_x == tx) connects = true;
|
||||||
@ -184,6 +184,41 @@ float SynthEngine::processGridStep() {
|
|||||||
c.phase += inc;
|
c.phase += inc;
|
||||||
if (c.phase >= SINE_TABLE_SIZE) c.phase -= SINE_TABLE_SIZE;
|
if (c.phase >= SINE_TABLE_SIZE) c.phase -= SINE_TABLE_SIZE;
|
||||||
val = (float)sine_table[(int)c.phase] / 32768.0f;
|
val = (float)sine_table[(int)c.phase] / 32768.0f;
|
||||||
|
} else if (c.type == GridCell::WAVETABLE) {
|
||||||
|
float mod = 0.0f;
|
||||||
|
mod += getInput(x, y, x, y-1);
|
||||||
|
mod += getInput(x, y, x+1, y);
|
||||||
|
mod += getInput(x, y, x, y+1);
|
||||||
|
mod += getInput(x, y, x-1, y);
|
||||||
|
|
||||||
|
float freq = 440.0f + (mod * 500.0f); // Fixed base freq + FM
|
||||||
|
if (freq < 1.0f) freq = 1.0f;
|
||||||
|
|
||||||
|
float inc = freq * (float)SINE_TABLE_SIZE / (float)_sampleRate;
|
||||||
|
c.phase += inc;
|
||||||
|
if (c.phase >= SINE_TABLE_SIZE) c.phase -= SINE_TABLE_SIZE;
|
||||||
|
|
||||||
|
float phase_norm = c.phase / (float)SINE_TABLE_SIZE; // 0.0 to 1.0
|
||||||
|
int wave_select = (int)(c.param * 7.99f);
|
||||||
|
|
||||||
|
switch(wave_select) {
|
||||||
|
case 0: val = (float)sine_table[(int)c.phase] / 32768.0f; break;
|
||||||
|
case 1: val = (phase_norm * 2.0f) - 1.0f; break; // Saw
|
||||||
|
case 2: val = (phase_norm < 0.5f) ? 1.0f : -1.0f; break; // Square
|
||||||
|
case 3: val = (phase_norm < 0.5f) ? (phase_norm * 4.0f - 1.0f) : (3.0f - phase_norm * 4.0f); break; // Triangle
|
||||||
|
case 4: val = 1.0f - (phase_norm * 2.0f); break; // Ramp
|
||||||
|
case 5: val = (phase_norm < 0.25f) ? 1.0f : -1.0f; break; // Pulse 25%
|
||||||
|
case 6: // Distorted Sine
|
||||||
|
val = sin(phase_norm * 2.0 * M_PI) + sin(phase_norm * 4.0 * M_PI) * 0.3f;
|
||||||
|
val /= 1.3f; // Normalize
|
||||||
|
break;
|
||||||
|
case 7: // Organ-like
|
||||||
|
val = sin(phase_norm * 2.0 * M_PI) * 0.6f +
|
||||||
|
sin(phase_norm * 4.0 * M_PI) * 0.2f +
|
||||||
|
sin(phase_norm * 8.0 * M_PI) * 0.1f;
|
||||||
|
val /= 0.9f; // Normalize
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else if (c.type == GridCell::NOISE) {
|
} else if (c.type == GridCell::NOISE) {
|
||||||
float white = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f;
|
float white = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f;
|
||||||
int shade = (int)(c.param * 4.99f);
|
int shade = (int)(c.param * 4.99f);
|
||||||
@ -252,6 +287,33 @@ float SynthEngine::processGridStep() {
|
|||||||
} else {
|
} else {
|
||||||
val = 0.0f; // No buffer, no output
|
val = 0.0f; // No buffer, no output
|
||||||
}
|
}
|
||||||
|
} else if (c.type == GridCell::REVERB) {
|
||||||
|
// Input is from the "Back" (rot+2)
|
||||||
|
int inDir = (c.rotation + 2) % 4;
|
||||||
|
int dx=0, dy=0;
|
||||||
|
if(inDir==0) dy=-1; else if(inDir==1) dx=1; else if(inDir==2) dy=1; else dx=-1;
|
||||||
|
float input_val = getInput(x, y, x+dx, y+dy);
|
||||||
|
|
||||||
|
if (c.buffer && c.buffer_size > 0) {
|
||||||
|
// Fixed delay for reverb effect (e.g. 50ms)
|
||||||
|
uint32_t delay_samples = (uint32_t)(0.05f * _sampleRate);
|
||||||
|
if (delay_samples >= c.buffer_size) delay_samples = c.buffer_size - 1;
|
||||||
|
|
||||||
|
int read_idx = (int)c.write_idx - (int)delay_samples;
|
||||||
|
if (read_idx < 0) read_idx += c.buffer_size;
|
||||||
|
|
||||||
|
float delayed = c.buffer[read_idx];
|
||||||
|
// Feedback controlled by param (0.0 to 0.95)
|
||||||
|
float feedback = c.param * 0.95f;
|
||||||
|
float newValue = input_val + delayed * feedback;
|
||||||
|
|
||||||
|
c.buffer[c.write_idx] = newValue;
|
||||||
|
val = newValue;
|
||||||
|
|
||||||
|
c.write_idx = (c.write_idx + 1) % c.buffer_size;
|
||||||
|
} else {
|
||||||
|
val = 0.0f;
|
||||||
|
}
|
||||||
} else if (c.type == GridCell::OPERATOR || c.type == GridCell::SINK) {
|
} else if (c.type == GridCell::OPERATOR || c.type == GridCell::SINK) {
|
||||||
// Gather inputs
|
// Gather inputs
|
||||||
float inputs[4];
|
float inputs[4];
|
||||||
|
|||||||
@ -86,7 +86,7 @@ public:
|
|||||||
|
|
||||||
// --- Grid Synth ---
|
// --- Grid Synth ---
|
||||||
struct GridCell {
|
struct GridCell {
|
||||||
enum Type { EMPTY, FIXED_OSCILLATOR, INPUT_OSCILLATOR, NOISE, FORK, WIRE, OPERATOR, DELAY, SINK };
|
enum Type { EMPTY, FIXED_OSCILLATOR, INPUT_OSCILLATOR, WAVETABLE, NOISE, FORK, WIRE, OPERATOR, DELAY, REVERB, SINK };
|
||||||
enum Op { OP_ADD, OP_MUL, OP_SUB, OP_DIV, OP_MIN, OP_MAX };
|
enum Op { OP_ADD, OP_MUL, OP_SUB, OP_DIV, OP_MIN, OP_MAX };
|
||||||
|
|
||||||
Type type = EMPTY;
|
Type type = EMPTY;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user