Optimization: instant wires
This commit is contained in:
parent
6fe8488994
commit
fbf1b36652
182
synth_engine.cpp
182
synth_engine.cpp
@ -4,20 +4,48 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// A simple sine lookup table for the sine oscillator
|
// A simple sine lookup table for the sine oscillator
|
||||||
const int SINE_TABLE_SIZE = 256;
|
const int WAVE_TABLE_SIZE = 256;
|
||||||
static int16_t sine_table[SINE_TABLE_SIZE];
|
const int NUM_WAVEFORMS = 8;
|
||||||
static bool sine_table_filled = false;
|
static int16_t wave_tables[NUM_WAVEFORMS][WAVE_TABLE_SIZE];
|
||||||
|
static bool wave_tables_filled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fills the global sine table. Called once on startup.
|
* @brief Fills the global wave tables. Called once on startup.
|
||||||
*/
|
*/
|
||||||
void fill_sine_table() {
|
void fill_wave_tables() {
|
||||||
if (sine_table_filled) return;
|
if (wave_tables_filled) return;
|
||||||
for (int i = 0; i < SINE_TABLE_SIZE; ++i) {
|
for (int i = 0; i < WAVE_TABLE_SIZE; ++i) {
|
||||||
// M_PI is not standard C++, but it's common. If it fails, use 3.1415926535...
|
double phase = (double)i / (double)WAVE_TABLE_SIZE;
|
||||||
sine_table[i] = static_cast<int16_t>(sin(2.0 * M_PI * i / SINE_TABLE_SIZE) * 32767.0);
|
double pi2 = 2.0 * M_PI;
|
||||||
|
|
||||||
|
// 0: Sine
|
||||||
|
wave_tables[0][i] = (int16_t)(sin(pi2 * phase) * 32767.0);
|
||||||
|
|
||||||
|
// 1: Sawtooth (Rising)
|
||||||
|
wave_tables[1][i] = (int16_t)((2.0 * phase - 1.0) * 32767.0);
|
||||||
|
|
||||||
|
// 2: Square
|
||||||
|
wave_tables[2][i] = (int16_t)((phase < 0.5 ? 1.0 : -1.0) * 32767.0);
|
||||||
|
|
||||||
|
// 3: Triangle
|
||||||
|
double tri = (phase < 0.5) ? (4.0 * phase - 1.0) : (3.0 - 4.0 * phase);
|
||||||
|
wave_tables[3][i] = (int16_t)(tri * 32767.0);
|
||||||
|
|
||||||
|
// 4: Ramp (Falling Saw)
|
||||||
|
wave_tables[4][i] = (int16_t)((1.0 - 2.0 * phase) * 32767.0);
|
||||||
|
|
||||||
|
// 5: Pulse 25%
|
||||||
|
wave_tables[5][i] = (int16_t)((phase < 0.25 ? 1.0 : -1.0) * 32767.0);
|
||||||
|
|
||||||
|
// 6: Distorted Sine
|
||||||
|
double d = sin(pi2 * phase) + 0.3 * sin(2.0 * pi2 * phase);
|
||||||
|
wave_tables[6][i] = (int16_t)((d / 1.3) * 32767.0);
|
||||||
|
|
||||||
|
// 7: Organ
|
||||||
|
double o = 0.6 * sin(pi2 * phase) + 0.2 * sin(2.0 * pi2 * phase) + 0.1 * sin(4.0 * pi2 * phase);
|
||||||
|
wave_tables[7][i] = (int16_t)((o / 0.9) * 32767.0);
|
||||||
}
|
}
|
||||||
sine_table_filled = true;
|
wave_tables_filled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SynthEngine::SynthEngine(uint32_t sampleRate)
|
SynthEngine::SynthEngine(uint32_t sampleRate)
|
||||||
@ -31,7 +59,7 @@ SynthEngine::SynthEngine(uint32_t sampleRate)
|
|||||||
_freqToPhaseInc(0.0f),
|
_freqToPhaseInc(0.0f),
|
||||||
_rngState(12345)
|
_rngState(12345)
|
||||||
{
|
{
|
||||||
fill_sine_table();
|
fill_wave_tables();
|
||||||
// Initialize with a default frequency
|
// Initialize with a default frequency
|
||||||
setFrequency(440.0f);
|
setFrequency(440.0f);
|
||||||
|
|
||||||
@ -295,6 +323,7 @@ void SynthEngine::rebuildProcessingOrder_locked() {
|
|||||||
// Start BFS from the SINK backwards
|
// Start BFS from the SINK backwards
|
||||||
q.push_back({GRID_W / 2, GRID_H - 1});
|
q.push_back({GRID_W / 2, GRID_H - 1});
|
||||||
visited[GRID_W / 2][GRID_H - 1] = true;
|
visited[GRID_W / 2][GRID_H - 1] = true;
|
||||||
|
_processing_order.push_back({GRID_W / 2, GRID_H - 1});
|
||||||
|
|
||||||
int head = 0;
|
int head = 0;
|
||||||
while(head < (int)q.size()) {
|
while(head < (int)q.size()) {
|
||||||
@ -335,12 +364,13 @@ void SynthEngine::rebuildProcessingOrder_locked() {
|
|||||||
if (pointsToCurr) {
|
if (pointsToCurr) {
|
||||||
visited[tx][ty] = true;
|
visited[tx][ty] = true;
|
||||||
q.push_back({tx, ty});
|
q.push_back({tx, ty});
|
||||||
|
if (grid[tx][ty].type != GridCell::WIRE) {
|
||||||
|
_processing_order.push_back({tx, ty});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_processing_order = q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SynthEngine::rebuildProcessingOrder() {
|
void SynthEngine::rebuildProcessingOrder() {
|
||||||
@ -352,45 +382,45 @@ void SynthEngine::updateGraph() {
|
|||||||
rebuildProcessingOrder_locked();
|
rebuildProcessingOrder_locked();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SynthEngine::processGridStep() {
|
bool SynthEngine::isConnected(int tx, int ty, int from_x, int from_y) {
|
||||||
|
if (from_x < 0 || from_x >= GRID_W || from_y < 0 || from_y >= GRID_H) return false;
|
||||||
auto isConnected = [&](int tx, int ty, int from_x, int from_y) -> bool {
|
GridCell& n = grid[from_x][from_y];
|
||||||
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_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;
|
||||||
|
if (n.rotation == 1 && from_x + 1 == tx && from_y == ty) connects = true;
|
||||||
|
if (n.rotation == 2 && from_y + 1 == ty && from_x == tx) connects = true;
|
||||||
|
if (n.rotation == 3 && from_x - 1 == tx && from_y == ty) connects = true;
|
||||||
|
} else if (n.type == GridCell::FORK) {
|
||||||
|
// Fork outputs to Left (rot+3) and Right (rot+1) relative to its rotation
|
||||||
|
// n.rotation is "Forward"
|
||||||
|
int dx = tx - from_x;
|
||||||
|
int dy = ty - from_y;
|
||||||
|
int dir = -1;
|
||||||
|
if (dx == 0 && dy == -1) dir = 0; // N
|
||||||
|
if (dx == 1 && dy == 0) dir = 1; // E
|
||||||
|
if (dx == 0 && dy == 1) dir = 2; // S
|
||||||
|
if (dx == -1 && dy == 0) dir = 3; // W
|
||||||
|
|
||||||
bool connects = false;
|
int leftOut = (n.rotation + 3) % 4;
|
||||||
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) {
|
int rightOut = (n.rotation + 1) % 4;
|
||||||
// Check rotation
|
|
||||||
// 0:N (y-1), 1:E (x+1), 2:S (y+1), 3:W (x-1)
|
if (dir == leftOut || dir == rightOut) connects = true;
|
||||||
if (n.rotation == 0 && from_y - 1 == ty && from_x == tx) connects = true;
|
}
|
||||||
if (n.rotation == 1 && from_x + 1 == tx && from_y == ty) connects = true;
|
return connects;
|
||||||
if (n.rotation == 2 && from_y + 1 == ty && from_x == tx) connects = true;
|
}
|
||||||
if (n.rotation == 3 && from_x - 1 == tx && from_y == ty) connects = true;
|
|
||||||
} else if (n.type == GridCell::FORK) {
|
|
||||||
// Fork outputs to Left (rot+3) and Right (rot+1) relative to its rotation
|
|
||||||
// n.rotation is "Forward"
|
|
||||||
int dx = tx - from_x;
|
|
||||||
int dy = ty - from_y;
|
|
||||||
int dir = -1;
|
|
||||||
if (dx == 0 && dy == -1) dir = 0; // N
|
|
||||||
if (dx == 1 && dy == 0) dir = 1; // E
|
|
||||||
if (dx == 0 && dy == 1) dir = 2; // S
|
|
||||||
if (dx == -1 && dy == 0) dir = 3; // W
|
|
||||||
|
|
||||||
int leftOut = (n.rotation + 3) % 4;
|
|
||||||
int rightOut = (n.rotation + 1) % 4;
|
|
||||||
|
|
||||||
if (dir == leftOut || dir == rightOut) connects = true;
|
|
||||||
}
|
|
||||||
return connects;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper to get input from a neighbor
|
int32_t SynthEngine::getInput(int tx, int ty, int from_x, int from_y, int depth) {
|
||||||
auto getInput = [&](int tx, int ty, int from_x, int from_y) -> int32_t {
|
if (depth > 16) return 0; // Prevent infinite loops
|
||||||
if (!isConnected(tx, ty, from_x, from_y)) return 0;
|
if (!isConnected(tx, ty, from_x, from_y)) return 0;
|
||||||
GridCell& n = grid[from_x][from_y];
|
GridCell& n = grid[from_x][from_y];
|
||||||
|
|
||||||
if (n.type == GridCell::FORK) {
|
if (n.type == GridCell::WIRE) {
|
||||||
|
return getSummedInput(from_x, from_y, n, depth + 1);
|
||||||
|
} else if (n.type == GridCell::FORK) {
|
||||||
int dx = tx - from_x;
|
int dx = tx - from_x;
|
||||||
int dy = ty - from_y;
|
int dy = ty - from_y;
|
||||||
int dir = -1;
|
int dir = -1;
|
||||||
@ -404,21 +434,22 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
|
|
||||||
if (dir == leftOut) return (n.value * (FP_ONE - n.param)) >> (FP_SHIFT - 1);
|
if (dir == leftOut) return (n.value * (FP_ONE - n.param)) >> (FP_SHIFT - 1);
|
||||||
if (dir == rightOut) return (n.value * n.param) >> (FP_SHIFT - 1);
|
if (dir == rightOut) return (n.value * n.param) >> (FP_SHIFT - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.value;
|
return n.value;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Helper to sum inputs excluding the output direction
|
int32_t SynthEngine::getSummedInput(int x, int y, GridCell& c, int depth) {
|
||||||
auto getSummedInput = [&](int x, int y, GridCell& c) -> int32_t {
|
int32_t sum = 0;
|
||||||
int32_t sum = 0;
|
int outDir = c.rotation; // 0:N, 1:E, 2:S, 3:W
|
||||||
int outDir = c.rotation; // 0:N, 1:E, 2:S, 3:W
|
if (outDir != 0) sum += getInput(x, y, x, y-1, depth);
|
||||||
if (outDir != 0) sum += getInput(x, y, x, y-1);
|
if (outDir != 1) sum += getInput(x, y, x+1, y, depth);
|
||||||
if (outDir != 1) sum += getInput(x, y, x+1, y);
|
if (outDir != 2) sum += getInput(x, y, x, y+1, depth);
|
||||||
if (outDir != 2) sum += getInput(x, y, x, y+1);
|
if (outDir != 3) sum += getInput(x, y, x-1, y, depth);
|
||||||
if (outDir != 3) sum += getInput(x, y, x-1, y);
|
return sum;
|
||||||
return sum;
|
}
|
||||||
};
|
|
||||||
|
int32_t SynthEngine::processGridStep() {
|
||||||
|
|
||||||
auto getInputFromTheBack = [&](int x, int y, GridCell& c) -> int32_t {
|
auto getInputFromTheBack = [&](int x, int y, GridCell& c) -> int32_t {
|
||||||
int inDir = (c.rotation + 2) % 4;
|
int inDir = (c.rotation + 2) % 4;
|
||||||
@ -462,7 +493,7 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
uint32_t inc = freq * 97391;
|
uint32_t inc = freq * 97391;
|
||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
// Top 8 bits of 32-bit accumulator form the 256-entry table index
|
// Top 8 bits of 32-bit accumulator form the 256-entry table index
|
||||||
val = sine_table[c.phase_accumulator >> 24];
|
val = wave_tables[0][c.phase_accumulator >> 24];
|
||||||
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::INPUT_OSCILLATOR) {
|
} else if (c.type == GridCell::INPUT_OSCILLATOR) {
|
||||||
int32_t mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
@ -478,7 +509,7 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
inc += (int32_t)(((int64_t)mod * 500 * 97391) >> FP_SHIFT);
|
inc += (int32_t)(((int64_t)mod * 500 * 97391) >> FP_SHIFT);
|
||||||
|
|
||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
val = sine_table[c.phase_accumulator >> 24];
|
val = wave_tables[0][c.phase_accumulator >> 24];
|
||||||
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::WAVETABLE) {
|
} else if (c.type == GridCell::WAVETABLE) {
|
||||||
int32_t mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
@ -488,19 +519,8 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
|
|
||||||
int wave_select = (c.param * 8) >> FP_SHIFT;
|
int wave_select = (c.param * 8) >> FP_SHIFT;
|
||||||
bool phase_upper = (c.phase_accumulator & 0x80000000);
|
if (wave_select > 7) wave_select = 7;
|
||||||
|
val = wave_tables[wave_select][c.phase_accumulator >> 24];
|
||||||
switch(wave_select) {
|
|
||||||
case 0: val = sine_table[c.phase_accumulator >> 24]; break;
|
|
||||||
case 1: val = (int32_t)((c.phase_accumulator >> 16) & 0xFFFF) - 32768; break; // Saw
|
|
||||||
case 2: val = phase_upper ? -32767 : 32767; break; // Square
|
|
||||||
case 3: val = sine_table[c.phase_accumulator >> 24]; break; // Triangle (fallback)
|
|
||||||
case 4: val = 32767 - (int32_t)((c.phase_accumulator >> 16) & 0xFFFF); break; // Ramp
|
|
||||||
case 5: val = ((c.phase_accumulator >> 30) == 0) ? 32767 : -32767; break; // Pulse 25%
|
|
||||||
default:
|
|
||||||
val = sine_table[c.phase_accumulator >> 24];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::NOISE) {
|
} else if (c.type == GridCell::NOISE) {
|
||||||
int32_t mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
@ -538,7 +558,7 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
uint32_t inc = freq_x10 * 9739;
|
uint32_t inc = freq_x10 * 9739;
|
||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
// Output full range -1.0 to 1.0
|
// Output full range -1.0 to 1.0
|
||||||
val = sine_table[c.phase_accumulator >> 24];
|
val = wave_tables[0][c.phase_accumulator >> 24];
|
||||||
} else if (c.type == GridCell::FORK) {
|
} else if (c.type == GridCell::FORK) {
|
||||||
// Sum inputs from "Back" (Input direction)
|
// Sum inputs from "Back" (Input direction)
|
||||||
val = getInputFromTheBack(x, y, c);
|
val = getInputFromTheBack(x, y, c);
|
||||||
@ -573,7 +593,7 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
val = (in * c.param) >> FP_SHIFT;
|
val = (in * c.param) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::WIRE) {
|
} else if (c.type == GridCell::WIRE) {
|
||||||
// Sum inputs from all neighbors that point to me
|
// Sum inputs from all neighbors that point to me
|
||||||
val = getSummedInput(x, y, c);
|
val = getSummedInput(x, y, c, 0);
|
||||||
} else if (c.type == GridCell::LPF) {
|
} else if (c.type == GridCell::LPF) {
|
||||||
// Input from Back
|
// Input from Back
|
||||||
int32_t in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
@ -599,7 +619,7 @@ int32_t SynthEngine::processGridStep() {
|
|||||||
int32_t in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Mod from other directions (sum)
|
// Mod from other directions (sum)
|
||||||
int32_t mod = getSummedInput(x, y, c);
|
int32_t mod = getSummedInput(x, y, c, 0);
|
||||||
mod -= in; // Remove signal input from mod sum (it was included in getInput calls)
|
mod -= in; // Remove signal input from mod sum (it was included in getInput calls)
|
||||||
|
|
||||||
// Gain = Param + Mod
|
// Gain = Param + Mod
|
||||||
|
|||||||
@ -146,6 +146,10 @@ private:
|
|||||||
std::vector<std::pair<int, int>> _processing_order;
|
std::vector<std::pair<int, int>> _processing_order;
|
||||||
void rebuildProcessingOrder_locked();
|
void rebuildProcessingOrder_locked();
|
||||||
|
|
||||||
|
bool isConnected(int tx, int ty, int from_x, int from_y);
|
||||||
|
int32_t getInput(int tx, int ty, int from_x, int from_y, int depth = 0);
|
||||||
|
int32_t getSummedInput(int x, int y, GridCell& c, int depth = 0);
|
||||||
|
|
||||||
// Internal random number generator
|
// Internal random number generator
|
||||||
int32_t _random();
|
int32_t _random();
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user