Optimizations 1

This commit is contained in:
Dejvino
2026-03-01 10:42:04 +01:00
parent aaeaa9986e
commit ad0fb039fc
5 changed files with 246 additions and 222 deletions
+5 -117
View File
@@ -1,5 +1,6 @@
#include "synth_engine.h"
#include <math.h>
#include <string.h>
// A simple sine lookup table for the sine oscillator
const int SINE_TABLE_SIZE = 256;
@@ -37,14 +38,6 @@ SynthEngine::SynthEngine(uint32_t sampleRate)
}
SynthEngine::~SynthEngine() {
for (int x = 0; x < GRID_W; ++x) {
for (int y = 0; y < GRID_H; ++y) {
if (grid[x][y].buffer) {
delete[] grid[x][y].buffer;
grid[x][y].buffer = nullptr;
}
}
}
}
void SynthEngine::exportGrid(uint8_t* buffer) {
@@ -62,6 +55,7 @@ void SynthEngine::exportGrid(uint8_t* buffer) {
void SynthEngine::importGrid(const uint8_t* buffer) {
SynthLockGuard<SynthMutex> lock(gridMutex);
size_t idx = 0;
for(int y=0; y<GRID_H; ++y) {
for(int x=0; x<GRID_W; ++x) {
@@ -71,15 +65,7 @@ void SynthEngine::importGrid(const uint8_t* buffer) {
uint8_t r = buffer[idx++];
GridCell::Type newType = (GridCell::Type)t;
if (c.type != newType) {
if (c.buffer) { delete[] c.buffer; c.buffer = nullptr; c.buffer_size = 0; }
c.type = newType;
if (c.type == GridCell::DELAY || c.type == GridCell::REVERB || c.type == GridCell::PITCH_SHIFTER) {
c.buffer_size = 2 * _sampleRate;
c.buffer = new float[c.buffer_size]();
c.write_idx = 0;
}
}
c.type = newType;
c.param = (float)p / 255.0f;
c.rotation = r;
}
@@ -93,11 +79,6 @@ void SynthEngine::clearGrid() {
GridCell& c = grid[x][y];
if (c.type == GridCell::SINK) continue;
if (c.buffer) {
delete[] c.buffer;
c.buffer = nullptr;
c.buffer_size = 0;
}
c.type = GridCell::EMPTY;
c.param = 0.5f;
c.rotation = 0;
@@ -257,8 +238,8 @@ float SynthEngine::_random() {
float SynthEngine::processGridStep() {
// Double buffer for values to handle feedback loops gracefully (1-sample delay)
float next_values[GRID_W][GRID_H];
static 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 >= GRID_W || from_y < 0 || from_y >= GRID_H) return false;
GridCell& n = grid[from_x][from_y];
@@ -545,50 +526,6 @@ float SynthEngine::processGridStep() {
// Mix between original and rectified based on param
float rect = fabsf(in);
val = in * (1.0f - c.param) + rect * c.param;
} else if (c.type == GridCell::PITCH_SHIFTER) {
float in = getInputFromTheBack(x, y, c);
if (c.buffer && c.buffer_size > 0) {
c.buffer[c.write_idx] = in;
// Granular pitch shift
// Pitch ratio: 0.5 to 2.0
float pitchRatio = 0.5f + c.param * 1.5f;
// Delay rate change: 1.0 - pitchRatio
// If pitch=1, rate=0 (delay constant). If pitch=2, rate=-1 (delay decreases).
float rate = 1.0f - pitchRatio;
c.phase += rate;
// Wrap phase within window (e.g. 4096 samples)
float windowSize = 4096.0f;
if (c.phase >= windowSize) c.phase -= windowSize;
if (c.phase < 0.0f) c.phase += windowSize;
// Read from buffer
// Simple crossfade windowing would be better, but for now just a single tap with moving delay
// To reduce clicks, we really need 2 taps. Let's stick to single tap for simplicity in this grid context, or maybe just a vibrato if rate is LFO?
// Actually, let's implement the 2-tap crossfade for quality.
// Tap 1
float p1 = c.phase;
float p2 = c.phase + windowSize * 0.5f;
if (p2 >= windowSize) p2 -= windowSize;
// Window function (Triangle)
auto getWindow = [&](float p) -> float {
return 1.0f - fabsf(2.0f * (p / windowSize) - 1.0f);
};
// Read indices
int r1 = (int)c.write_idx - (int)p1;
if (r1 < 0) r1 += c.buffer_size;
int r2 = (int)c.write_idx - (int)p2;
if (r2 < 0) r2 += c.buffer_size;
val = c.buffer[r1] * getWindow(p1) + c.buffer[r2] * getWindow(p2);
c.write_idx = (c.write_idx + 1) % c.buffer_size;
} else {
val = 0.0f;
}
} else if (c.type == GridCell::GLITCH) {
float in = getInputFromTheBack(x, y, c);
// Param controls probability of glitch
@@ -601,55 +538,6 @@ float SynthEngine::processGridStep() {
} else {
val = in;
}
} else if (c.type == GridCell::DELAY) {
// Input is from the "Back" (rot+2)
float input_val = getInputFromTheBack(x, y, c);
if (c.buffer && c.buffer_size > 0) {
// Write current input to buffer
c.buffer[c.write_idx] = input_val;
// Calculate read index based on parameter. Max delay is buffer_size.
uint32_t delay_samples = c.param * (c.buffer_size - 1);
// Using modulo for wraparound. Need to handle negative result from subtraction.
int read_idx = (int)c.write_idx - (int)delay_samples;
if (read_idx < 0) {
read_idx += c.buffer_size;
}
// Read delayed value for output
val = c.buffer[read_idx];
// Increment write index
c.write_idx = (c.write_idx + 1) % c.buffer_size;
} else {
val = 0.0f; // No buffer, no output
}
} else if (c.type == GridCell::REVERB) {
// Input is from the "Back" (rot+2)
float input_val = getInputFromTheBack(x, y, c);
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) {
// Gather inputs
float inputs[4];