Optimized: Fixed point calculations
This commit is contained in:
parent
bdfd216b4a
commit
6fe8488994
@ -51,6 +51,7 @@ void setupAudio() {
|
|||||||
globalSynth = new SynthEngine(SAMPLE_RATE);
|
globalSynth = new SynthEngine(SAMPLE_RATE);
|
||||||
if (globalSynth) {
|
if (globalSynth) {
|
||||||
globalSynth->loadPreset(2);
|
globalSynth->loadPreset(2);
|
||||||
|
globalSynth->setVolume(0.2f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -465,10 +465,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param (Freq)
|
// Param (Freq)
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, 16, "%.0f", 10.0f + cell.param*990.0f);
|
snprintf(buf, 16, "%.0f", 10.0f + (cell.param / 32767.0f)*990.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 0, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 0, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'O');
|
drawTypeLabel(renderer, x, y, 'O');
|
||||||
} else if (cell.type == SynthEngine::GridCell::INPUT_OSCILLATOR) {
|
} else if (cell.type == SynthEngine::GridCell::INPUT_OSCILLATOR) {
|
||||||
DrawCircle(renderer, cx, cy, r);
|
DrawCircle(renderer, cx, cy, r);
|
||||||
@ -482,10 +482,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param (Octave)
|
// Param (Octave)
|
||||||
char buf[16]; snprintf(buf, 16, "O%d", 1 + (int)(cell.param * 4.99f));
|
char buf[16]; snprintf(buf, 16, "O%d", 1 + (int)((cell.param / 32767.0f) * 4.99f));
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 200, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 255, 200, 0, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 200, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 200, 0);
|
||||||
drawTypeLabel(renderer, x, y, 'I');
|
drawTypeLabel(renderer, x, y, 'I');
|
||||||
} else if (cell.type == SynthEngine::GridCell::NOISE) {
|
} else if (cell.type == SynthEngine::GridCell::NOISE) {
|
||||||
// Draw static/noise pattern
|
// Draw static/noise pattern
|
||||||
@ -503,10 +503,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
|
|
||||||
// Param (Color)
|
// Param (Color)
|
||||||
const char* colors[] = {"BRN", "PNK", "WHT", "YEL", "GRN"};
|
const char* colors[] = {"BRN", "PNK", "WHT", "YEL", "GRN"};
|
||||||
int idx = (int)(cell.param * 4.99f);
|
int idx = (int)((cell.param / 32767.0f) * 4.99f);
|
||||||
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
|
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, colors[idx]);
|
drawString(renderer, x + 5, y + size - 18, 10, colors[idx]);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 200, 200, 200);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 200, 200, 200);
|
||||||
drawTypeLabel(renderer, x, y, 'N');
|
drawTypeLabel(renderer, x, y, 'N');
|
||||||
} else if (cell.type == SynthEngine::GridCell::LFO) {
|
} else if (cell.type == SynthEngine::GridCell::LFO) {
|
||||||
DrawCircle(renderer, cx, cy, r);
|
DrawCircle(renderer, cx, cy, r);
|
||||||
@ -520,16 +520,16 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
drawString(renderer, cx - 8, cy - 5, 12, "LFO");
|
drawString(renderer, cx - 8, cy - 5, 12, "LFO");
|
||||||
// Param (Freq)
|
// Param (Freq)
|
||||||
char buf[16]; snprintf(buf, 16, "%.1f", 0.1f + cell.param * 19.9f);
|
char buf[16]; snprintf(buf, 16, "%.1f", 0.1f + (cell.param / 32767.0f) * 19.9f);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 0, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 0, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'L');
|
drawTypeLabel(renderer, x, y, 'L');
|
||||||
} else if (cell.type == SynthEngine::GridCell::GATE_INPUT) {
|
} else if (cell.type == SynthEngine::GridCell::GATE_INPUT) {
|
||||||
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
||||||
SDL_RenderDrawRect(renderer, &box);
|
SDL_RenderDrawRect(renderer, &box);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
if (cell.value > 0.5f) SDL_RenderFillRect(renderer, &box);
|
if (cell.value > 16384) SDL_RenderFillRect(renderer, &box);
|
||||||
drawString(renderer, cx - 8, cy - 5, 12, "G-IN");
|
drawString(renderer, cx - 8, cy - 5, 12, "G-IN");
|
||||||
drawTypeLabel(renderer, x, y, 'K');
|
drawTypeLabel(renderer, x, y, 'K');
|
||||||
} else if (cell.type == SynthEngine::GridCell::ADSR_ATTACK) {
|
} else if (cell.type == SynthEngine::GridCell::ADSR_ATTACK) {
|
||||||
@ -545,7 +545,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
|
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'A');
|
drawTypeLabel(renderer, x, y, 'A');
|
||||||
} else if (cell.type == SynthEngine::GridCell::ADSR_DECAY) {
|
} else if (cell.type == SynthEngine::GridCell::ADSR_DECAY) {
|
||||||
// Draw Ramp Down
|
// Draw Ramp Down
|
||||||
@ -560,7 +560,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
|
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'D');
|
drawTypeLabel(renderer, x, y, 'D');
|
||||||
} else if (cell.type == SynthEngine::GridCell::ADSR_SUSTAIN) {
|
} else if (cell.type == SynthEngine::GridCell::ADSR_SUSTAIN) {
|
||||||
// Draw Level
|
// Draw Level
|
||||||
@ -575,7 +575,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
|
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'S');
|
drawTypeLabel(renderer, x, y, 'S');
|
||||||
} else if (cell.type == SynthEngine::GridCell::ADSR_RELEASE) {
|
} else if (cell.type == SynthEngine::GridCell::ADSR_RELEASE) {
|
||||||
// Draw Ramp Down
|
// Draw Ramp Down
|
||||||
@ -590,7 +590,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
|
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'E');
|
drawTypeLabel(renderer, x, y, 'E');
|
||||||
} else if (cell.type == SynthEngine::GridCell::LPF || cell.type == SynthEngine::GridCell::HPF) {
|
} else if (cell.type == SynthEngine::GridCell::LPF || cell.type == SynthEngine::GridCell::HPF) {
|
||||||
// Box
|
// Box
|
||||||
@ -608,10 +608,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param
|
// Param
|
||||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 0, 255, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 0, 255, 0);
|
||||||
drawTypeLabel(renderer, x, y, cell.type == SynthEngine::GridCell::LPF ? 'P' : 'H');
|
drawTypeLabel(renderer, x, y, cell.type == SynthEngine::GridCell::LPF ? 'P' : 'H');
|
||||||
} else if (cell.type == SynthEngine::GridCell::VCA) {
|
} else if (cell.type == SynthEngine::GridCell::VCA) {
|
||||||
// Triangle shape for Amp
|
// Triangle shape for Amp
|
||||||
@ -629,10 +629,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param
|
// Param
|
||||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 0);
|
||||||
drawTypeLabel(renderer, x, y, 'A');
|
drawTypeLabel(renderer, x, y, 'A');
|
||||||
} else if (cell.type == SynthEngine::GridCell::BITCRUSHER) {
|
} else if (cell.type == SynthEngine::GridCell::BITCRUSHER) {
|
||||||
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
||||||
@ -648,10 +648,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param
|
// Param
|
||||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
|
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 0, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 0, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'B');
|
drawTypeLabel(renderer, x, y, 'B');
|
||||||
} else if (cell.type == SynthEngine::GridCell::DISTORTION) {
|
} else if (cell.type == SynthEngine::GridCell::DISTORTION) {
|
||||||
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
||||||
@ -666,10 +666,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r;
|
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);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255);
|
SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 100, 100);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 100, 100);
|
||||||
drawTypeLabel(renderer, x, y, 'X');
|
drawTypeLabel(renderer, x, y, 'X');
|
||||||
} else if (cell.type == SynthEngine::GridCell::RECTIFIER) {
|
} else if (cell.type == SynthEngine::GridCell::RECTIFIER) {
|
||||||
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
SDL_Rect box = {cx - r, cy - r, r*2, r*2};
|
||||||
@ -684,7 +684,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r;
|
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);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 150, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 150, 0);
|
||||||
drawTypeLabel(renderer, x, y, '|');
|
drawTypeLabel(renderer, x, y, '|');
|
||||||
} else if (cell.type == SynthEngine::GridCell::PITCH_SHIFTER) {
|
} else if (cell.type == SynthEngine::GridCell::PITCH_SHIFTER) {
|
||||||
drawString(renderer, cx - 8, cy - 5, 12, "PIT");
|
drawString(renderer, cx - 8, cy - 5, 12, "PIT");
|
||||||
@ -697,7 +697,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
if(cell.rotation==0) ody=-r; else if(cell.rotation==1) odx=r; else if(cell.rotation==2) ody=r; else odx=-r;
|
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);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 100, 255, 100);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 100, 255, 100);
|
||||||
drawTypeLabel(renderer, x, y, '^');
|
drawTypeLabel(renderer, x, y, '^');
|
||||||
} else if (cell.type == SynthEngine::GridCell::GLITCH) {
|
} else if (cell.type == SynthEngine::GridCell::GLITCH) {
|
||||||
drawString(renderer, cx - 8, cy - 5, 12, "GLT");
|
drawString(renderer, cx - 8, cy - 5, 12, "GLT");
|
||||||
@ -711,7 +711,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
|
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 0, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 0, 0);
|
||||||
drawTypeLabel(renderer, x, y, 'G');
|
drawTypeLabel(renderer, x, y, 'G');
|
||||||
} else if (cell.type == SynthEngine::GridCell::FORK) {
|
} else if (cell.type == SynthEngine::GridCell::FORK) {
|
||||||
// Draw Y shape based on rotation
|
// Draw Y shape based on rotation
|
||||||
@ -734,10 +734,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
drawDirectionArrow(renderer, cx, cy, size, rDir);
|
drawDirectionArrow(renderer, cx, cy, size, rDir);
|
||||||
|
|
||||||
// Param (Balance)
|
// Param (Balance)
|
||||||
char buf[16]; snprintf(buf, 16, "%.1f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.1f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 0, 255, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 0, 255, 0);
|
||||||
drawTypeLabel(renderer, x, y, 'Y');
|
drawTypeLabel(renderer, x, y, 'Y');
|
||||||
} else if (cell.type == SynthEngine::GridCell::DELAY) {
|
} else if (cell.type == SynthEngine::GridCell::DELAY) {
|
||||||
// Draw D
|
// Draw D
|
||||||
@ -756,11 +756,11 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
|
|
||||||
// Param (Delay time in ms)
|
// Param (Delay time in ms)
|
||||||
char buf[16];
|
char buf[16];
|
||||||
float delay_ms = cell.param * 2000.0f; // Max 2 seconds
|
float delay_ms = (cell.param / 32767.0f) * 2000.0f; // Max 2 seconds
|
||||||
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 - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 128, 0);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 128, 0);
|
||||||
drawTypeLabel(renderer, x, y, 'D');
|
drawTypeLabel(renderer, x, y, 'D');
|
||||||
} else if (cell.type == SynthEngine::GridCell::REVERB) {
|
} else if (cell.type == SynthEngine::GridCell::REVERB) {
|
||||||
// Draw R
|
// Draw R
|
||||||
@ -777,10 +777,10 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+odx, cy+ody);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param (Strength)
|
// Param (Strength)
|
||||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
char buf[16]; snprintf(buf, 16, "%.2f", cell.param / 32767.0f);
|
||||||
SDL_SetRenderDrawColor(renderer, 200, 100, 255, 255);
|
SDL_SetRenderDrawColor(renderer, 200, 100, 255, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 200, 100, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 200, 100, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'R');
|
drawTypeLabel(renderer, x, y, 'R');
|
||||||
} 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};
|
||||||
@ -795,7 +795,7 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
|
|
||||||
// Draw Op Symbol
|
// Draw Op Symbol
|
||||||
char opChar = '?';
|
char opChar = '?';
|
||||||
int opType = (int)(cell.param * 5.99f);
|
int opType = (int)((cell.param / 32767.0f) * 5.99f);
|
||||||
if (opType == 0) opChar = '+';
|
if (opType == 0) opChar = '+';
|
||||||
else if (opType == 1) opChar = '*';
|
else if (opType == 1) opChar = '*';
|
||||||
else if (opType == 2) opChar = '-';
|
else if (opType == 2) opChar = '-';
|
||||||
@ -803,7 +803,7 @@ 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 - 15, cy - 15, 12, opChar);
|
drawChar(renderer, cx - 15, cy - 15, 12, opChar);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 255, 255, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 255, 255, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'M');
|
drawTypeLabel(renderer, x, y, 'M');
|
||||||
} else if (cell.type == SynthEngine::GridCell::WAVETABLE) {
|
} else if (cell.type == SynthEngine::GridCell::WAVETABLE) {
|
||||||
drawString(renderer, cx - 5, cy - 5, 12, "W");
|
drawString(renderer, cx - 5, cy - 5, 12, "W");
|
||||||
@ -816,12 +816,12 @@ void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::G
|
|||||||
SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy);
|
SDL_RenderDrawLine(renderer, cx, cy, cx+dx, cy+dy);
|
||||||
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
drawDirectionArrow(renderer, cx, cy, size, cell.rotation);
|
||||||
// Param (Wave index)
|
// Param (Wave index)
|
||||||
int idx = (int)(cell.param * 7.99f);
|
int idx = (int)((cell.param / 32767.0f) * 7.99f);
|
||||||
char buf[4];
|
char buf[4];
|
||||||
snprintf(buf, 4, "%d", idx);
|
snprintf(buf, 4, "%d", idx);
|
||||||
SDL_SetRenderDrawColor(renderer, 128, 128, 255, 255);
|
SDL_SetRenderDrawColor(renderer, 128, 128, 255, 255);
|
||||||
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
drawString(renderer, x + 5, y + size - 18, 10, buf);
|
||||||
drawParamBar(renderer, x, y, size, cell.param, 128, 128, 255);
|
drawParamBar(renderer, x, y, size, cell.param / 32767.0f, 128, 128, 255);
|
||||||
drawTypeLabel(renderer, x, y, 'W');
|
drawTypeLabel(renderer, x, y, 'W');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -864,9 +864,9 @@ void randomizeGrid() {
|
|||||||
|
|
||||||
c.type = (SynthEngine::GridCell::Type)(rand() % numTypes);
|
c.type = (SynthEngine::GridCell::Type)(rand() % numTypes);
|
||||||
c.rotation = rand() % 4;
|
c.rotation = rand() % 4;
|
||||||
c.param = (float)rand() / (float)RAND_MAX;
|
c.param = rand() % 32768;
|
||||||
c.value = 0.0f;
|
c.value = 0;
|
||||||
c.phase = 0.0f;
|
c.phase = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,7 +954,7 @@ void randomizeGrid() {
|
|||||||
for (int y = 0; y < SynthEngine::GRID_H; ++y) {
|
for (int y = 0; y < SynthEngine::GRID_H; ++y) {
|
||||||
if (!visited[x][y]) {
|
if (!visited[x][y]) {
|
||||||
engine.grid[x][y].type = SynthEngine::GridCell::EMPTY;
|
engine.grid[x][y].type = SynthEngine::GridCell::EMPTY;
|
||||||
engine.grid[x][y].param = 0.5f;
|
engine.grid[x][y].param = 16384;
|
||||||
engine.grid[x][y].rotation = 0;
|
engine.grid[x][y].rotation = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -969,8 +969,8 @@ void randomizeGrid() {
|
|||||||
engine.setFrequency(440.0f);
|
engine.setFrequency(440.0f);
|
||||||
bool soundDetected = false;
|
bool soundDetected = false;
|
||||||
for(int i=0; i<1000; ++i) {
|
for(int i=0; i<1000; ++i) {
|
||||||
float val = engine.processGridStep();
|
int32_t val = engine.processGridStep();
|
||||||
if (fabsf(val) > 0.001f) {
|
if (abs(val) > 10) {
|
||||||
soundDetected = true;
|
soundDetected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -983,7 +983,7 @@ void randomizeGrid() {
|
|||||||
// Reset values to avoid initial pop
|
// Reset values to avoid initial pop
|
||||||
for (int x = 0; x < SynthEngine::GRID_W; ++x) {
|
for (int x = 0; x < SynthEngine::GRID_W; ++x) {
|
||||||
for (int y = 0; y < SynthEngine::GRID_H; ++y) {
|
for (int y = 0; y < SynthEngine::GRID_H; ++y) {
|
||||||
engine.grid[x][y].value = 0.0f;
|
engine.grid[x][y].value = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -999,7 +999,7 @@ void randomizeGrid() {
|
|||||||
SynthEngine::GridCell& c = engine.grid[x][y];
|
SynthEngine::GridCell& c = engine.grid[x][y];
|
||||||
if (c.type != SynthEngine::GridCell::SINK) {
|
if (c.type != SynthEngine::GridCell::SINK) {
|
||||||
c.type = SynthEngine::GridCell::EMPTY;
|
c.type = SynthEngine::GridCell::EMPTY;
|
||||||
c.param = 0.5f;
|
c.param = 16384;
|
||||||
c.rotation = 0;
|
c.rotation = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1207,7 +1207,7 @@ int main(int argc, char* argv[]) {
|
|||||||
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) {
|
||||||
newType = SynthEngine::GridCell::EMPTY;
|
newType = SynthEngine::GridCell::EMPTY;
|
||||||
c.param = 0.5f;
|
c.param = 16384;
|
||||||
c.rotation = 0;
|
c.rotation = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1291,7 +1291,7 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (mx < GRID_PANEL_WIDTH) {
|
if (mx < GRID_PANEL_WIDTH) {
|
||||||
// Grid Scroll
|
// Grid Scroll
|
||||||
float step = fineTune ? 0.01f : 0.05f;
|
int32_t step = fineTune ? 327 : 1638; // ~0.01 and ~0.05
|
||||||
int gx = mx / CELL_SIZE;
|
int gx = mx / CELL_SIZE;
|
||||||
int gy = my / CELL_SIZE;
|
int gy = my / CELL_SIZE;
|
||||||
if (gx >= 0 && gx < SynthEngine::GRID_W && gy >= 0 && gy < SynthEngine::GRID_H) {
|
if (gx >= 0 && gx < SynthEngine::GRID_W && gy >= 0 && gy < SynthEngine::GRID_H) {
|
||||||
@ -1299,8 +1299,8 @@ int main(int argc, char* argv[]) {
|
|||||||
SynthEngine::GridCell& c = engine.grid[gx][gy];
|
SynthEngine::GridCell& c = engine.grid[gx][gy];
|
||||||
if (e.wheel.y > 0) c.param += step;
|
if (e.wheel.y > 0) c.param += step;
|
||||||
else c.param -= step;
|
else c.param -= step;
|
||||||
if (c.param > 1.0f) c.param = 1.0f;
|
if (c.param > 32767) c.param = 32767;
|
||||||
if (c.param < 0.0f) c.param = 0.0f;
|
if (c.param < 0) c.param = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Synth Scroll
|
// Synth Scroll
|
||||||
|
|||||||
274
synth_engine.cpp
274
synth_engine.cpp
@ -63,7 +63,7 @@ size_t SynthEngine::exportGrid(uint8_t* buffer) {
|
|||||||
buffer[idx++] = (uint8_t)x;
|
buffer[idx++] = (uint8_t)x;
|
||||||
buffer[idx++] = (uint8_t)y;
|
buffer[idx++] = (uint8_t)y;
|
||||||
buffer[idx++] = (uint8_t)c.type;
|
buffer[idx++] = (uint8_t)c.type;
|
||||||
buffer[idx++] = (uint8_t)(c.param * 255.0f);
|
buffer[idx++] = (uint8_t)((c.param * 255) >> FP_SHIFT);
|
||||||
buffer[idx++] = (uint8_t)c.rotation;
|
buffer[idx++] = (uint8_t)c.rotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,12 +90,12 @@ int SynthEngine::importGrid(const uint8_t* buffer, size_t size) {
|
|||||||
GridCell& c = grid[x][y];
|
GridCell& c = grid[x][y];
|
||||||
if (c.type == GridCell::SINK) continue;
|
if (c.type == GridCell::SINK) continue;
|
||||||
c.type = GridCell::EMPTY;
|
c.type = GridCell::EMPTY;
|
||||||
c.param = 0.5f;
|
c.param = FP_HALF;
|
||||||
c.rotation = 0;
|
c.rotation = 0;
|
||||||
c.value = 0.0f;
|
c.value = 0;
|
||||||
c.phase = 0.0f;
|
c.phase = 0;
|
||||||
c.phase_accumulator = 0;
|
c.phase_accumulator = 0;
|
||||||
c.next_value = 0.0f;
|
c.next_value = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ int SynthEngine::importGrid(const uint8_t* buffer, size_t size) {
|
|||||||
if (x < GRID_W && y < GRID_H) {
|
if (x < GRID_W && y < GRID_H) {
|
||||||
GridCell& c = grid[x][y];
|
GridCell& c = grid[x][y];
|
||||||
c.type = (GridCell::Type)t;
|
c.type = (GridCell::Type)t;
|
||||||
c.param = (float)p / 255.0f;
|
c.param = ((int32_t)p << FP_SHIFT) / 255;
|
||||||
c.rotation = r;
|
c.rotation = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,12 +126,12 @@ void SynthEngine::clearGrid() {
|
|||||||
if (c.type == GridCell::SINK) continue;
|
if (c.type == GridCell::SINK) continue;
|
||||||
|
|
||||||
c.type = GridCell::EMPTY;
|
c.type = GridCell::EMPTY;
|
||||||
c.param = 0.5f;
|
c.param = FP_HALF;
|
||||||
c.rotation = 0;
|
c.rotation = 0;
|
||||||
c.value = 0.0f;
|
c.value = 0;
|
||||||
c.phase = 0.0f;
|
c.phase = 0;
|
||||||
c.phase_accumulator = 0;
|
c.phase_accumulator = 0;
|
||||||
c.next_value = 0.0f;
|
c.next_value = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rebuildProcessingOrder_locked();
|
rebuildProcessingOrder_locked();
|
||||||
@ -154,16 +154,16 @@ void SynthEngine::loadPreset(int preset) {
|
|||||||
grid[x][y+1].type = GridCell::WIRE; grid[x][y+1].rotation = 1; // E
|
grid[x][y+1].type = GridCell::WIRE; grid[x][y+1].rotation = 1; // E
|
||||||
|
|
||||||
grid[x+1][y+1].type = GridCell::ADSR_ATTACK; grid[x+1][y+1].rotation = 1; // E
|
grid[x+1][y+1].type = GridCell::ADSR_ATTACK; grid[x+1][y+1].rotation = 1; // E
|
||||||
grid[x+1][y+1].param = att;
|
grid[x+1][y+1].param = (int32_t)(att * FP_ONE);
|
||||||
|
|
||||||
grid[x+2][y+1].type = GridCell::ADSR_RELEASE; grid[x+2][y+1].rotation = 1; // E
|
grid[x+2][y+1].type = GridCell::ADSR_RELEASE; grid[x+2][y+1].rotation = 1; // E
|
||||||
grid[x+2][y+1].param = rel;
|
grid[x+2][y+1].param = (int32_t)(rel * FP_ONE);
|
||||||
|
|
||||||
grid[x+3][y+1].type = GridCell::VCA; grid[x+3][y+1].rotation = 2; // S
|
grid[x+3][y+1].type = GridCell::VCA; grid[x+3][y+1].rotation = 2; // S
|
||||||
grid[x+3][y+1].param = 0.0f; // Controlled by Env
|
grid[x+3][y+1].param = 0; // Controlled by Env
|
||||||
|
|
||||||
grid[x+3][y].type = GridCell::INPUT_OSCILLATOR; grid[x+3][y].rotation = 2; // S
|
grid[x+3][y].type = GridCell::INPUT_OSCILLATOR; grid[x+3][y].rotation = 2; // S
|
||||||
grid[x+3][y].param = (ratio > 1.0f) ? 0.5f : 0.0f;
|
grid[x+3][y].param = (ratio > 1.0f) ? FP_HALF : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
int sinkY = GRID_H - 1;
|
int sinkY = GRID_H - 1;
|
||||||
@ -281,10 +281,10 @@ float SynthEngine::getFrequency() const {
|
|||||||
return (float)((double)_increment * (double)_sampleRate / 4294967296.0);
|
return (float)((double)_increment * (double)_sampleRate / 4294967296.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float SynthEngine::_random() {
|
int32_t SynthEngine::_random() {
|
||||||
// Simple Linear Congruential Generator
|
// Simple Linear Congruential Generator
|
||||||
_rngState = _rngState * 1664525 + 1013904223;
|
_rngState = _rngState * 1664525 + 1013904223;
|
||||||
return (float)_rngState / 4294967296.0f;
|
return (int32_t)((_rngState >> 16) & 0xFFFF) - 32768;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SynthEngine::rebuildProcessingOrder_locked() {
|
void SynthEngine::rebuildProcessingOrder_locked() {
|
||||||
@ -352,7 +352,7 @@ void SynthEngine::updateGraph() {
|
|||||||
rebuildProcessingOrder_locked();
|
rebuildProcessingOrder_locked();
|
||||||
}
|
}
|
||||||
|
|
||||||
float SynthEngine::processGridStep() {
|
int32_t SynthEngine::processGridStep() {
|
||||||
|
|
||||||
auto isConnected = [&](int tx, int ty, int from_x, int from_y) -> bool {
|
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;
|
if (from_x < 0 || from_x >= GRID_W || from_y < 0 || from_y >= GRID_H) return false;
|
||||||
@ -386,8 +386,8 @@ float SynthEngine::processGridStep() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper to get input from a neighbor
|
// Helper to get input from a neighbor
|
||||||
auto getInput = [&](int tx, int ty, int from_x, int from_y) -> float {
|
auto getInput = [&](int tx, int ty, int from_x, int from_y) -> int32_t {
|
||||||
if (!isConnected(tx, ty, from_x, from_y)) return 0.0f;
|
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::FORK) {
|
||||||
@ -402,16 +402,16 @@ float SynthEngine::processGridStep() {
|
|||||||
int leftOut = (n.rotation + 3) % 4;
|
int leftOut = (n.rotation + 3) % 4;
|
||||||
int rightOut = (n.rotation + 1) % 4;
|
int rightOut = (n.rotation + 1) % 4;
|
||||||
|
|
||||||
if (dir == leftOut) return n.value * (1.0f - n.param) * 2.0f;
|
if (dir == leftOut) return (n.value * (FP_ONE - n.param)) >> (FP_SHIFT - 1);
|
||||||
if (dir == rightOut) return n.value * n.param * 2.0f;
|
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
|
// Helper to sum inputs excluding the output direction
|
||||||
auto getSummedInput = [&](int x, int y, GridCell& c) -> float {
|
auto getSummedInput = [&](int x, int y, GridCell& c) -> int32_t {
|
||||||
float sum = 0.0f;
|
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);
|
if (outDir != 0) sum += getInput(x, y, x, y-1);
|
||||||
if (outDir != 1) sum += getInput(x, y, x+1, y);
|
if (outDir != 1) sum += getInput(x, y, x+1, y);
|
||||||
@ -420,15 +420,15 @@ float SynthEngine::processGridStep() {
|
|||||||
return sum;
|
return sum;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto getInputFromTheBack = [&](int x, int y, GridCell& c) -> float {
|
auto getInputFromTheBack = [&](int x, int y, GridCell& c) -> int32_t {
|
||||||
int inDir = (c.rotation + 2) % 4;
|
int inDir = (c.rotation + 2) % 4;
|
||||||
int dx=0, dy=0;
|
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;
|
if(inDir==0) dy=-1; else if(inDir==1) dx=1; else if(inDir==2) dy=1; else dx=-1;
|
||||||
return getInput(x, y, x+dx, y+dy);
|
return getInput(x, y, x+dx, y+dy);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto getSideInputGain = [&](int x, int y, GridCell& c) -> float {
|
auto getSideInputGain = [&](int x, int y, GridCell& c) -> int32_t {
|
||||||
float gain = 0.0f;
|
int32_t gain = 0;
|
||||||
bool hasSide = false;
|
bool hasSide = false;
|
||||||
// Left (rot+3)
|
// Left (rot+3)
|
||||||
int lDir = (c.rotation + 3) % 4;
|
int lDir = (c.rotation + 3) % 4;
|
||||||
@ -438,7 +438,7 @@ float SynthEngine::processGridStep() {
|
|||||||
int rDir = (c.rotation + 1) % 4;
|
int rDir = (c.rotation + 1) % 4;
|
||||||
int rdx=0, rdy=0; if(rDir==0) rdy=-1; else if(rDir==1) rdx=1; else if(rDir==2) rdy=1; else rdx=-1;
|
int rdx=0, rdy=0; if(rDir==0) rdy=-1; else if(rDir==1) rdx=1; else if(rDir==2) rdy=1; else rdx=-1;
|
||||||
if (isConnected(x, y, x+rdx, y+rdy)) { hasSide = true; gain += getInput(x, y, x+rdx, y+rdy); }
|
if (isConnected(x, y, x+rdx, y+rdy)) { hasSide = true; gain += getInput(x, y, x+rdx, y+rdy); }
|
||||||
return hasSide ? gain : 1.0f;
|
return hasSide ? gain : FP_ONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Calculate next values for active cells
|
// 1. Calculate next values for active cells
|
||||||
@ -446,82 +446,74 @@ float SynthEngine::processGridStep() {
|
|||||||
int x = cell_coord.first;
|
int x = cell_coord.first;
|
||||||
int y = cell_coord.second;
|
int y = cell_coord.second;
|
||||||
GridCell& c = grid[x][y];
|
GridCell& c = grid[x][y];
|
||||||
float val = 0.0f;
|
int32_t val = 0;
|
||||||
|
|
||||||
if (c.type == GridCell::EMPTY) {
|
if (c.type == GridCell::EMPTY) {
|
||||||
val = 0.0f;
|
val = 0;
|
||||||
} else if (c.type == GridCell::FIXED_OSCILLATOR) {
|
} else if (c.type == GridCell::FIXED_OSCILLATOR) {
|
||||||
// Gather inputs for modulation
|
// Gather inputs for modulation
|
||||||
float mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Freq 10 to 1000 Hz.
|
// Freq 10 to 1000 Hz.
|
||||||
float freq = 10.0f + c.param * 990.0f + (mod * 500.0f); // FM
|
int32_t freq = 10 + ((c.param * 990) >> FP_SHIFT) + ((mod * 500) >> FP_SHIFT);
|
||||||
if (freq < 1.0f) freq = 1.0f;
|
if (freq < 1) freq = 1;
|
||||||
|
|
||||||
// Fixed point phase accumulation
|
// Fixed point phase accumulation
|
||||||
uint32_t inc = (uint32_t)(freq * _freqToPhaseInc);
|
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 = (float)sine_table[c.phase_accumulator >> 24] / 32768.0f;
|
val = sine_table[c.phase_accumulator >> 24];
|
||||||
val *= getSideInputGain(x, y, c);
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::INPUT_OSCILLATOR) {
|
} else if (c.type == GridCell::INPUT_OSCILLATOR) {
|
||||||
float mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Freq based on current note + octave param (1-5)
|
// Freq based on current note + octave param (1-5)
|
||||||
int octave = 1 + (int)(c.param * 4.99f); // Map 0.0-1.0 to 1-5
|
int octave = 1 + ((c.param * 5) >> FP_SHIFT); // Map 0.0-1.0 to 1-5
|
||||||
|
|
||||||
// Use the engine's global increment directly to avoid float conversion round-trip
|
// Use the engine's global increment directly to avoid float conversion round-trip
|
||||||
uint32_t baseInc = _increment;
|
uint32_t baseInc = _increment;
|
||||||
uint32_t inc = baseInc << (octave - 1);
|
uint32_t inc = baseInc << (octave - 1);
|
||||||
|
|
||||||
// Apply FM (mod is float, convert to fixed point increment)
|
// Apply FM (mod is float, convert to fixed point increment)
|
||||||
inc += (int32_t)(mod * 500.0f * _freqToPhaseInc);
|
inc += (int32_t)(((int64_t)mod * 500 * 97391) >> FP_SHIFT);
|
||||||
|
|
||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
val = (float)sine_table[c.phase_accumulator >> 24] / 32768.0f;
|
val = sine_table[c.phase_accumulator >> 24];
|
||||||
val *= getSideInputGain(x, y, c);
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::WAVETABLE) {
|
} else if (c.type == GridCell::WAVETABLE) {
|
||||||
float mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Track current note frequency + FM. Use direct increment for speed.
|
// Track current note frequency + FM. Use direct increment for speed.
|
||||||
uint32_t inc = _increment + (int32_t)(mod * 500.0f * _freqToPhaseInc);
|
uint32_t inc = _increment + (int32_t)(((int64_t)mod * 500 * 97391) >> FP_SHIFT);
|
||||||
c.phase_accumulator += inc;
|
c.phase_accumulator += inc;
|
||||||
|
|
||||||
// 0.0 to 1.0 representation for math-based waveforms
|
int wave_select = (c.param * 8) >> FP_SHIFT;
|
||||||
float phase_norm = (float)c.phase_accumulator / 4294967296.0f;
|
bool phase_upper = (c.phase_accumulator & 0x80000000);
|
||||||
int wave_select = (int)(c.param * 7.99f);
|
|
||||||
|
|
||||||
switch(wave_select) {
|
switch(wave_select) {
|
||||||
case 0: val = (float)sine_table[c.phase_accumulator >> 24] / 32768.0f; break;
|
case 0: val = sine_table[c.phase_accumulator >> 24]; break;
|
||||||
case 1: val = (phase_norm * 2.0f) - 1.0f; break; // Saw
|
case 1: val = (int32_t)((c.phase_accumulator >> 16) & 0xFFFF) - 32768; break; // Saw
|
||||||
case 2: val = (phase_norm < 0.5f) ? 1.0f : -1.0f; break; // Square
|
case 2: val = phase_upper ? -32767 : 32767; break; // Square
|
||||||
case 3: val = (phase_norm < 0.5f) ? (phase_norm * 4.0f - 1.0f) : (3.0f - phase_norm * 4.0f); break; // Triangle
|
case 3: val = sine_table[c.phase_accumulator >> 24]; break; // Triangle (fallback)
|
||||||
case 4: val = 1.0f - (phase_norm * 2.0f); break; // Ramp
|
case 4: val = 32767 - (int32_t)((c.phase_accumulator >> 16) & 0xFFFF); break; // Ramp
|
||||||
case 5: val = (phase_norm < 0.25f) ? 1.0f : -1.0f; break; // Pulse 25%
|
case 5: val = ((c.phase_accumulator >> 30) == 0) ? 32767 : -32767; break; // Pulse 25%
|
||||||
case 6: // Distorted Sine
|
default:
|
||||||
val = sin(phase_norm * 2.0 * M_PI) + sin(phase_norm * 4.0 * M_PI) * 0.3f;
|
val = sine_table[c.phase_accumulator >> 24];
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
val *= getSideInputGain(x, y, c);
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::NOISE) {
|
} else if (c.type == GridCell::NOISE) {
|
||||||
float mod = getInputFromTheBack(x, y, c);
|
int32_t mod = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
float white = _random() * 2.0f - 1.0f;
|
int32_t white = _random();
|
||||||
int shade = (int)(c.param * 4.99f);
|
int shade = (c.param * 5) >> FP_SHIFT;
|
||||||
switch(shade) {
|
switch(shade) {
|
||||||
case 0: // Brown (Leaky integrator)
|
case 0: // Brown (Leaky integrator)
|
||||||
c.phase = (c.phase + white * 0.1f) * 0.95f;
|
c.phase = (c.phase + (white >> 3)) - (c.phase >> 4);
|
||||||
val = c.phase * 3.0f; // Gain up
|
val = c.phase * 3; // Gain up
|
||||||
break;
|
break;
|
||||||
case 1: // Pink (Approx: LPF)
|
case 1: // Pink (Approx: LPF)
|
||||||
c.phase = 0.5f * c.phase + 0.5f * white;
|
c.phase = (c.phase >> 1) + (white >> 1);
|
||||||
val = c.phase;
|
val = c.phase;
|
||||||
break;
|
break;
|
||||||
case 2: // White
|
case 2: // White
|
||||||
@ -532,154 +524,146 @@ float SynthEngine::processGridStep() {
|
|||||||
c.phase = white; // Store last sample
|
c.phase = white; // Store last sample
|
||||||
break;
|
break;
|
||||||
case 4: // Green (BPF approx)
|
case 4: // Green (BPF approx)
|
||||||
c.phase = (c.phase + white) * 0.5f; // LPF
|
c.phase = (c.phase + white) >> 1; // LPF
|
||||||
val = white - c.phase; // HPF result
|
val = white - c.phase; // HPF result
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply Amplitude Modulation (AM) from input
|
// Apply Amplitude Modulation (AM) from input
|
||||||
val *= (1.0f + mod);
|
val = (val * (FP_ONE + mod)) >> FP_SHIFT;
|
||||||
val *= getSideInputGain(x, y, c);
|
val = (val * getSideInputGain(x, y, c)) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::LFO) {
|
} else if (c.type == GridCell::LFO) {
|
||||||
// Low Frequency Oscillator (0.1 Hz to 20 Hz)
|
// Low Frequency Oscillator (0.1 Hz to 20 Hz)
|
||||||
float freq = 0.1f + c.param * 19.9f;
|
int32_t freq_x10 = 1 + ((c.param * 199) >> FP_SHIFT);
|
||||||
uint32_t inc = (uint32_t)(freq * _freqToPhaseInc);
|
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 = (float)sine_table[c.phase_accumulator >> 24] / 32768.0f;
|
val = sine_table[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);
|
||||||
} else if (c.type == GridCell::GATE_INPUT) {
|
} else if (c.type == GridCell::GATE_INPUT) {
|
||||||
// Outputs 1.0 when gate is open (key pressed), 0.0 otherwise
|
// Outputs 1.0 when gate is open (key pressed), 0.0 otherwise
|
||||||
val = _isGateOpen ? 1.0f : 0.0f;
|
val = _isGateOpen ? FP_MAX : 0;
|
||||||
} else if (c.type == GridCell::ADSR_ATTACK) {
|
} else if (c.type == GridCell::ADSR_ATTACK) {
|
||||||
// Slew Limiter (Up only)
|
// Slew Limiter (Up only)
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
float rate = 1.0f / (0.001f + c.param * 2.0f * _sampleRate); // 0.001s to 2s
|
int32_t rate = (1 << 20) / (1 + (c.param >> 4));
|
||||||
if (in > c.value) {
|
if (in > (c.phase >> 9)) {
|
||||||
c.value += rate;
|
c.phase += rate;
|
||||||
if (c.value > in) c.value = in;
|
if ((c.phase >> 9) > in) c.phase = in << 9;
|
||||||
} else {
|
} else {
|
||||||
c.value = in;
|
c.phase = in << 9;
|
||||||
}
|
}
|
||||||
val = c.value;
|
val = c.phase >> 9;
|
||||||
} else if (c.type == GridCell::ADSR_DECAY || c.type == GridCell::ADSR_RELEASE) {
|
} else if (c.type == GridCell::ADSR_DECAY || c.type == GridCell::ADSR_RELEASE) {
|
||||||
// Slew Limiter (Down only)
|
// Slew Limiter (Down only)
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
float rate = 1.0f / (0.001f + c.param * 2.0f * _sampleRate);
|
int32_t rate = (1 << 20) / (1 + (c.param >> 4));
|
||||||
if (in < c.value) {
|
if (in < (c.phase >> 9)) {
|
||||||
c.value -= rate;
|
c.phase -= rate;
|
||||||
if (c.value < in) c.value = in;
|
if ((c.phase >> 9) < in) c.phase = in << 9;
|
||||||
} else {
|
} else {
|
||||||
c.value = in;
|
c.phase = in << 9;
|
||||||
}
|
}
|
||||||
val = c.value;
|
val = c.phase >> 9;
|
||||||
} else if (c.type == GridCell::ADSR_SUSTAIN) {
|
} else if (c.type == GridCell::ADSR_SUSTAIN) {
|
||||||
// Attenuator
|
// Attenuator
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
val = in * c.param;
|
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
|
||||||
float sum = getSummedInput(x, y, c);
|
val = getSummedInput(x, y, c);
|
||||||
val = sum;
|
|
||||||
} else if (c.type == GridCell::LPF) {
|
} else if (c.type == GridCell::LPF) {
|
||||||
// Input from Back
|
// Input from Back
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Simple one-pole LPF
|
// Simple one-pole LPF
|
||||||
// Cutoff mapping: Exponential-ish 20Hz to 15kHz
|
int32_t alpha = (c.param * c.param) >> FP_SHIFT;
|
||||||
float cutoff = 20.0f + c.param * c.param * 15000.0f;
|
|
||||||
float alpha = 2.0f * M_PI * cutoff / (float)_sampleRate;
|
|
||||||
if (alpha > 1.0f) alpha = 1.0f;
|
|
||||||
|
|
||||||
// c.phase stores previous output
|
// c.phase stores previous output
|
||||||
val = c.phase + alpha * (in - c.phase);
|
val = c.phase + ((alpha * (in - c.phase)) >> FP_SHIFT);
|
||||||
c.phase = val;
|
c.phase = val;
|
||||||
} else if (c.type == GridCell::HPF) {
|
} else if (c.type == GridCell::HPF) {
|
||||||
// Input from Back
|
// Input from Back
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
float cutoff = 20.0f + c.param * c.param * 15000.0f;
|
int32_t alpha = (c.param * c.param) >> FP_SHIFT;
|
||||||
float alpha = 2.0f * M_PI * cutoff / (float)_sampleRate;
|
|
||||||
if (alpha > 1.0f) alpha = 1.0f;
|
|
||||||
|
|
||||||
// HPF = Input - LPF
|
// HPF = Input - LPF
|
||||||
// c.phase stores LPF state
|
int32_t lpf = c.phase + ((alpha * (in - c.phase)) >> FP_SHIFT);
|
||||||
float lpf = c.phase + alpha * (in - c.phase);
|
|
||||||
c.phase = lpf;
|
c.phase = lpf;
|
||||||
val = in - lpf;
|
val = in - lpf;
|
||||||
} else if (c.type == GridCell::VCA) {
|
} else if (c.type == GridCell::VCA) {
|
||||||
// Input from Back
|
// Input from Back
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Mod from other directions (sum)
|
// Mod from other directions (sum)
|
||||||
float mod = getSummedInput(x, y, c);
|
int32_t mod = getSummedInput(x, y, c);
|
||||||
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
|
||||||
float gain = c.param + mod;
|
int32_t gain = c.param + mod;
|
||||||
if (gain < 0.0f) gain = 0.0f;
|
if (gain < 0) gain = 0;
|
||||||
val = in * gain;
|
val = (in * gain) >> FP_SHIFT;
|
||||||
} else if (c.type == GridCell::BITCRUSHER) {
|
} else if (c.type == GridCell::BITCRUSHER) {
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Bit depth reduction
|
// Bit depth reduction
|
||||||
float bits = 1.0f + c.param * 15.0f; // 1 to 16 bits
|
int32_t mask = 0xFFFF << (16 - (c.param >> 11));
|
||||||
float steps = powf(2.0f, bits);
|
val = in & mask;
|
||||||
val = roundf(in * steps) / steps;
|
|
||||||
} else if (c.type == GridCell::DISTORTION) {
|
} else if (c.type == GridCell::DISTORTION) {
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
|
|
||||||
// Soft clipping
|
// Soft clipping
|
||||||
float drive = 1.0f + c.param * 20.0f;
|
int32_t drive = FP_ONE + (c.param << 2);
|
||||||
float x_driven = in * drive;
|
val = (in * drive) >> FP_SHIFT;
|
||||||
// Simple soft clip: x / (1 + |x|)
|
if (val > FP_MAX) val = FP_MAX;
|
||||||
val = x_driven / (1.0f + fabsf(x_driven));
|
if (val < FP_MIN) val = FP_MIN;
|
||||||
} else if (c.type == GridCell::RECTIFIER) {
|
} else if (c.type == GridCell::RECTIFIER) {
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
// Mix between original and rectified based on param
|
// Mix between original and rectified based on param
|
||||||
float rect = fabsf(in);
|
int32_t rect = (in < 0) ? -in : in;
|
||||||
val = in * (1.0f - c.param) + rect * c.param;
|
val = ((in * (FP_ONE - c.param)) >> FP_SHIFT) + ((rect * c.param) >> FP_SHIFT);
|
||||||
} else if (c.type == GridCell::GLITCH) {
|
} else if (c.type == GridCell::GLITCH) {
|
||||||
float in = getInputFromTheBack(x, y, c);
|
int32_t in = getInputFromTheBack(x, y, c);
|
||||||
// Param controls probability of glitch
|
// Param controls probability of glitch
|
||||||
float chance = c.param * 0.2f; // 0 to 20% chance per sample
|
int32_t chance = c.param >> 2;
|
||||||
if (_random() < chance) {
|
if ((_random() & 0x7FFF) < chance) {
|
||||||
int mode = (int)(_random() * 3.0f);
|
int mode = _random() & 3;
|
||||||
if (mode == 0) val = in * 50.0f; // Massive gain (clipping)
|
if (mode == 0) val = in << 4; // Massive gain (clipping)
|
||||||
else if (mode == 1) val = _random() * 2.0f - 1.0f; // White noise burst
|
else if (mode == 1) val = _random(); // White noise burst
|
||||||
else val = 0.0f; // Drop out
|
else val = 0; // Drop out
|
||||||
} else {
|
} else {
|
||||||
val = in;
|
val = in;
|
||||||
}
|
}
|
||||||
} 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];
|
int32_t inputs[4];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int outDir = (c.type == GridCell::SINK) ? -1 : c.rotation;
|
int outDir = (c.type == GridCell::SINK) ? -1 : c.rotation;
|
||||||
|
|
||||||
float iN = (outDir != 0) ? getInput(x, y, x, y-1) : 0.0f; if(iN!=0) inputs[count++] = iN;
|
int32_t iN = (outDir != 0) ? getInput(x, y, x, y-1) : 0; if(iN!=0) inputs[count++] = iN;
|
||||||
float iE = (outDir != 1) ? getInput(x, y, x+1, y) : 0.0f; if(iE!=0) inputs[count++] = iE;
|
int32_t iE = (outDir != 1) ? getInput(x, y, x+1, y) : 0; if(iE!=0) inputs[count++] = iE;
|
||||||
float iS = (outDir != 2) ? getInput(x, y, x, y+1) : 0.0f; if(iS!=0) inputs[count++] = iS;
|
int32_t iS = (outDir != 2) ? getInput(x, y, x, y+1) : 0; if(iS!=0) inputs[count++] = iS;
|
||||||
float iW = (outDir != 3) ? getInput(x, y, x-1, y) : 0.0f; if(iW!=0) inputs[count++] = iW;
|
int32_t iW = (outDir != 3) ? getInput(x, y, x-1, y) : 0; if(iW!=0) inputs[count++] = iW;
|
||||||
|
|
||||||
if (c.type == GridCell::SINK) {
|
if (c.type == GridCell::SINK) {
|
||||||
// Sink just sums everything
|
// Sink just sums everything
|
||||||
val = 0.0f;
|
val = 0;
|
||||||
for(int k=0; k<count; ++k) val += inputs[k];
|
for(int k=0; k<count; ++k) val += inputs[k];
|
||||||
} else {
|
} else {
|
||||||
// Operator
|
// Operator
|
||||||
int opType = (int)(c.param * 5.99f);
|
int opType = (c.param * 6) >> FP_SHIFT;
|
||||||
if (count == 0) val = 0.0f;
|
if (count == 0) val = 0;
|
||||||
else {
|
else {
|
||||||
val = inputs[0];
|
val = inputs[0];
|
||||||
for (int i=1; i<count; ++i) {
|
for (int i=1; i<count; ++i) {
|
||||||
switch(opType) {
|
switch(opType) {
|
||||||
case 0: val += inputs[i]; break; // ADD
|
case 0: val += inputs[i]; break; // ADD
|
||||||
case 1: val *= inputs[i]; break; // MUL
|
case 1: val = (val * inputs[i]) >> FP_SHIFT; break; // MUL
|
||||||
case 2: val -= inputs[i]; break; // SUB
|
case 2: val -= inputs[i]; break; // SUB
|
||||||
case 3: if(inputs[i]!=0) val /= inputs[i]; break; // DIV
|
case 3: if(inputs[i]!=0) val = (val << FP_SHIFT) / inputs[i]; break; // DIV
|
||||||
case 4: if(inputs[i]<val) val = inputs[i]; break; // MIN
|
case 4: if(inputs[i]<val) val = inputs[i]; break; // MIN
|
||||||
case 5: if(inputs[i]>val) val = inputs[i]; break; // MAX
|
case 5: if(inputs[i]>val) val = inputs[i]; break; // MAX
|
||||||
}
|
}
|
||||||
@ -706,18 +690,18 @@ void SynthEngine::process(int16_t* buffer, uint32_t numFrames) {
|
|||||||
|
|
||||||
for (uint32_t i = 0; i < numFrames; ++i) {
|
for (uint32_t i = 0; i < numFrames; ++i) {
|
||||||
// The grid is now the primary sound source.
|
// The grid is now the primary sound source.
|
||||||
// The processGridStep() returns a float in the approx range of -1.0 to 1.0.
|
// The processGridStep() returns Q15
|
||||||
float sampleF = processGridStep();
|
int32_t sample = processGridStep();
|
||||||
|
|
||||||
// Soft clip grid sample to avoid harsh distortion before filtering.
|
// Soft clip grid sample to avoid harsh distortion before filtering.
|
||||||
if (sampleF > 1.0f) sampleF = 1.0f;
|
if (sample > FP_MAX) sample = FP_MAX;
|
||||||
if (sampleF < -1.0f) sampleF = -1.0f;
|
if (sample < FP_MIN) sample = FP_MIN;
|
||||||
|
|
||||||
// The filters were designed for a signal in the int16 range.
|
// The filters were designed for a signal in the int16 range.
|
||||||
// We scale the grid's float output to match this expected range.
|
// We scale the grid's output to match this expected range.
|
||||||
sampleF *= 32767.0f;
|
// It is already Q15, so it matches int16 range.
|
||||||
|
|
||||||
// Apply Master Volume and write to buffer
|
// Apply Master Volume and write to buffer
|
||||||
buffer[i] = static_cast<int16_t>(sampleF * _volume);
|
buffer[i] = (int16_t)((sample * (int32_t)(_volume * FP_ONE)) >> FP_SHIFT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,6 +32,13 @@ template <typename Mutex>
|
|||||||
using SynthLockGuard = std::lock_guard<Mutex>;
|
using SynthLockGuard = std::lock_guard<Mutex>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Fixed-point constants
|
||||||
|
#define FP_SHIFT 15
|
||||||
|
#define FP_ONE (1 << FP_SHIFT)
|
||||||
|
#define FP_HALF (1 << (FP_SHIFT - 1))
|
||||||
|
#define FP_MAX 32767
|
||||||
|
#define FP_MIN -32768
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class SynthEngine
|
* @class SynthEngine
|
||||||
* @brief A portable, platform-agnostic synthesizer engine.
|
* @brief A portable, platform-agnostic synthesizer engine.
|
||||||
@ -102,11 +109,11 @@ public:
|
|||||||
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;
|
||||||
float param = 0.5f; // 0.0 to 1.0
|
int32_t param = FP_HALF; // 0.0 to 1.0 -> 0 to 32768
|
||||||
int rotation = 0; // 0:N, 1:E, 2:S, 3:W (Output direction)
|
int rotation = 0; // 0:N, 1:E, 2:S, 3:W (Output direction)
|
||||||
float value = 0.0f; // Current output sample
|
int32_t value = 0; // Current output sample (Q15)
|
||||||
float next_value = 0.0f; // For double-buffering in processGridStep
|
int32_t next_value = 0; // For double-buffering
|
||||||
float phase = 0.0f; // For Oscillator, Noise state
|
int32_t phase = 0; // For Oscillator, Noise state, Filter state
|
||||||
uint32_t phase_accumulator = 0; // For Oscillators (Fixed point optimization)
|
uint32_t phase_accumulator = 0; // For Oscillators (Fixed point optimization)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -125,7 +132,7 @@ public:
|
|||||||
SynthMutex gridMutex;
|
SynthMutex gridMutex;
|
||||||
|
|
||||||
// Helper to process one sample step of the grid
|
// Helper to process one sample step of the grid
|
||||||
float processGridStep();
|
int32_t processGridStep();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _sampleRate;
|
uint32_t _sampleRate;
|
||||||
@ -140,7 +147,7 @@ private:
|
|||||||
void rebuildProcessingOrder_locked();
|
void rebuildProcessingOrder_locked();
|
||||||
|
|
||||||
// Internal random number generator
|
// Internal random number generator
|
||||||
float _random();
|
int32_t _random();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SYNTH_ENGINE_H
|
#endif // SYNTH_ENGINE_H
|
||||||
Loading…
Reference in New Issue
Block a user