Grid synth engine
This commit is contained in:
@@ -14,8 +14,10 @@
|
||||
// --- Configuration ---
|
||||
const uint32_t SAMPLE_RATE = 44100;
|
||||
const uint32_t CHANNELS = 1; // Mono
|
||||
const int WINDOW_WIDTH = 800;
|
||||
const int WINDOW_HEIGHT = 600;
|
||||
const int GRID_PANEL_WIDTH = 400;
|
||||
const int SYNTH_PANEL_WIDTH = 800;
|
||||
const int WINDOW_WIDTH = GRID_PANEL_WIDTH + SYNTH_PANEL_WIDTH; // 1200
|
||||
const int WINDOW_HEIGHT = 640;
|
||||
|
||||
// --- Visualization Buffer ---
|
||||
const size_t VIS_BUFFER_SIZE = 8192;
|
||||
@@ -25,9 +27,6 @@ std::atomic<size_t> vis_write_index{0};
|
||||
// --- Control State ---
|
||||
int current_octave = 4; // C4 is middle C
|
||||
float knob_vol_val = 0.5f;
|
||||
SynthEngine::Waveform current_waveform = SynthEngine::SAWTOOTH;
|
||||
const char* waveform_names[] = {"Saw", "Square", "Sine"};
|
||||
|
||||
// ADSR (A, D, R in seconds, S in 0-1)
|
||||
float adsr_vals[4] = {0.05f, 0.2f, 0.6f, 0.5f};
|
||||
float filter_vals[2] = {1.0f, 0.0f}; // LP (1.0=Open), HP (0.0=Open)
|
||||
@@ -128,31 +127,6 @@ void drawKnob(SDL_Renderer* renderer, int x, int y, int radius, float value) {
|
||||
SDL_RenderDrawLine(renderer, x, y, x2, y2);
|
||||
}
|
||||
|
||||
void drawWaveformIcon(SDL_Renderer* renderer, int x, int y, int w, int h, SynthEngine::Waveform wf) {
|
||||
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
|
||||
switch(wf) {
|
||||
case SynthEngine::SAWTOOTH:
|
||||
SDL_RenderDrawLine(renderer, x, y+h, x+w, y); // Ramp up
|
||||
SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); // Drop down
|
||||
break;
|
||||
case SynthEngine::SQUARE:
|
||||
SDL_RenderDrawLine(renderer, x, y+h, x, y); // Rise
|
||||
SDL_RenderDrawLine(renderer, x, y, x+w, y); // High
|
||||
SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); // Drop
|
||||
break;
|
||||
case SynthEngine::SINE: {
|
||||
int prev_x = x, prev_y = y + h/2;
|
||||
for (int i = 1; i <= w; ++i) {
|
||||
int px = x + i;
|
||||
int py = y + h/2 - (int)(sin(i * 2.0 * M_PI / w) * h/2.0f);
|
||||
SDL_RenderDrawLine(renderer, prev_x, prev_y, px, py);
|
||||
prev_x = px; prev_y = py;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawToggle(SDL_Renderer* renderer, int x, int y, int size, bool active) {
|
||||
// Draw box
|
||||
SDL_Rect rect = {x - size/2, y - size/2, size, size};
|
||||
@@ -193,6 +167,191 @@ void drawSlider(SDL_Renderer* renderer, int x, int y, int w, int h, float val, c
|
||||
SDL_RenderDrawRect(renderer, &handle);
|
||||
}
|
||||
|
||||
// --- Simple Vector Font ---
|
||||
void drawChar(SDL_Renderer* renderer, int x, int y, int size, char c) {
|
||||
int w = size * 0.6;
|
||||
int h = size;
|
||||
int h2 = h / 2;
|
||||
int w2 = w / 2;
|
||||
|
||||
switch(c) {
|
||||
case '0': SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); SDL_RenderDrawLine(renderer, x+w, y+h, x, y+h); SDL_RenderDrawLine(renderer, x, y+h, x, y); break;
|
||||
case '1': SDL_RenderDrawLine(renderer, x+w2, y, x+w2, y+h); break;
|
||||
case '2': SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w, y+h2, x, y+h2); SDL_RenderDrawLine(renderer, x, y+h2, x, y+h); SDL_RenderDrawLine(renderer, x, y+h, x+w, y+h); break;
|
||||
case '3': SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); SDL_RenderDrawLine(renderer, x, y+h, x+w, y+h); break;
|
||||
case '4': SDL_RenderDrawLine(renderer, x, y, x, y+h2); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); break;
|
||||
case '5': SDL_RenderDrawLine(renderer, x+w, y, x, y); SDL_RenderDrawLine(renderer, x, y, x, y+h2); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w, y+h2, x+w, y+h); SDL_RenderDrawLine(renderer, x+w, y+h, x, y+h); break;
|
||||
case '6': SDL_RenderDrawLine(renderer, x+w, y, x, y); SDL_RenderDrawLine(renderer, x, y, x, y+h); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); SDL_RenderDrawLine(renderer, x, y+h, x+w, y+h); SDL_RenderDrawLine(renderer, x+w, y+h2, x+w, y+h); break;
|
||||
case '7': SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x, y+h); break;
|
||||
case '8': SDL_RenderDrawLine(renderer, x, y, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x+w, y+h); SDL_RenderDrawLine(renderer, x+w, y+h, x, y+h); SDL_RenderDrawLine(renderer, x, y+h, x, y); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); break;
|
||||
case '9': SDL_RenderDrawLine(renderer, x+w, y+h, x+w, y); SDL_RenderDrawLine(renderer, x+w, y, x, y); SDL_RenderDrawLine(renderer, x, y, x, y+h2); SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); break;
|
||||
case '.': SDL_RenderDrawPoint(renderer, x+w2, y+h-2); break;
|
||||
case '+': SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); SDL_RenderDrawLine(renderer, x+w2, y, x+w2, y+h); break;
|
||||
case '-': SDL_RenderDrawLine(renderer, x, y+h2, x+w, y+h2); break;
|
||||
case '*': SDL_RenderDrawLine(renderer, x, y, x+w, y+h); SDL_RenderDrawLine(renderer, x+w, y, x, y+h); break; // X
|
||||
case '/': SDL_RenderDrawLine(renderer, x+w, y, x, y+h); break;
|
||||
case '<': SDL_RenderDrawLine(renderer, x+w, y, x, y+h2); SDL_RenderDrawLine(renderer, x, y+h2, x+w, 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 'O': drawChar(renderer, x, y, size, '0'); break;
|
||||
}
|
||||
}
|
||||
|
||||
void drawString(SDL_Renderer* renderer, int x, int y, int size, const char* str) {
|
||||
int cursor = x;
|
||||
while (*str) {
|
||||
drawChar(renderer, cursor, y, size, *str);
|
||||
cursor += (size * 0.6) + 4;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Grid UI Helpers ---
|
||||
void drawGridCell(SDL_Renderer* renderer, int x, int y, int size, SynthEngine::GridCell& cell) {
|
||||
// Background
|
||||
SDL_Rect rect = {x, y, size, size};
|
||||
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
|
||||
SDL_RenderDrawRect(renderer, &rect);
|
||||
|
||||
int cx = x + size/2;
|
||||
int cy = y + size/2;
|
||||
int r = size/3;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
||||
|
||||
if (cell.type == SynthEngine::GridCell::EMPTY) {
|
||||
SDL_RenderDrawPoint(renderer, cx, cy);
|
||||
} else if (cell.type == SynthEngine::GridCell::SINK) {
|
||||
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
|
||||
SDL_Rect sinkRect = {cx - r, cy - r, r*2, r*2};
|
||||
SDL_RenderFillRect(renderer, &sinkRect);
|
||||
} else if (cell.type == SynthEngine::GridCell::WIRE) {
|
||||
// Draw center
|
||||
SDL_RenderDrawPoint(renderer, cx, cy);
|
||||
// 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 (Fading)
|
||||
char buf[16]; snprintf(buf, 16, "%.2f", cell.param);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||
} else if (cell.type == SynthEngine::GridCell::FIXED_OSCILLATOR) {
|
||||
DrawCircle(renderer, cx, cy, r);
|
||||
// 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 (Freq)
|
||||
char buf[16];
|
||||
snprintf(buf, 16, "%.0f", 10.0f + cell.param*990.0f);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||
} else if (cell.type == SynthEngine::GridCell::INPUT_OSCILLATOR) {
|
||||
DrawCircle(renderer, cx, cy, r);
|
||||
DrawCircle(renderer, cx, cy, r/2); // Inner circle to distinguish
|
||||
// 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 (Octave)
|
||||
char buf[16]; snprintf(buf, 16, "O%d", 1 + (int)(cell.param * 4.99f));
|
||||
SDL_SetRenderDrawColor(renderer, 255, 200, 0, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||
} else if (cell.type == SynthEngine::GridCell::NOISE) {
|
||||
// Draw static/noise pattern
|
||||
for(int i=0; i<20; ++i) {
|
||||
SDL_RenderDrawPoint(renderer, x + rand()%size, y + rand()%size);
|
||||
}
|
||||
// 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 (Color)
|
||||
const char* colors[] = {"BRN", "PNK", "WHT", "YEL", "GRN"};
|
||||
int idx = (int)(cell.param * 4.99f);
|
||||
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, colors[idx]);
|
||||
} else if (cell.type == SynthEngine::GridCell::FORK) {
|
||||
// Draw Y shape based on rotation
|
||||
// Center
|
||||
SDL_RenderDrawPoint(renderer, cx, cy);
|
||||
// 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, cy, cx+idx, cy+idy);
|
||||
// Outputs (Left/Right)
|
||||
int lDir = (cell.rotation + 3) % 4;
|
||||
int rDir = (cell.rotation + 1) % 4;
|
||||
int ldx=0, ldy=0, rdx=0, rdy=0;
|
||||
if(lDir==0) ldy=-r; else if(lDir==1) ldx=r; else if(lDir==2) ldy=r; else ldx=-r;
|
||||
if(rDir==0) rdy=-r; else if(rDir==1) rdx=r; else if(rDir==2) rdy=r; else rdx=-r;
|
||||
SDL_RenderDrawLine(renderer, cx, cy, cx+ldx, cy+ldy);
|
||||
SDL_RenderDrawLine(renderer, cx, cy, cx+rdx, cy+rdy);
|
||||
|
||||
// Param (Balance)
|
||||
char buf[16]; snprintf(buf, 16, "%.1f", cell.param);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||
} else if (cell.type == SynthEngine::GridCell::DELAY) {
|
||||
// Draw D
|
||||
drawString(renderer, cx - 5, cy - 5, 12, "D");
|
||||
// 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 (Delay time in ms)
|
||||
char buf[16];
|
||||
float delay_ms = cell.param * 2000.0f; // Max 2 seconds
|
||||
snprintf(buf, 16, "%.0fms", delay_ms);
|
||||
SDL_SetRenderDrawColor(renderer, 255, 128, 0, 255);
|
||||
drawString(renderer, x + 5, y + size - 15, 10, buf);
|
||||
} else if (cell.type == SynthEngine::GridCell::OPERATOR) {
|
||||
SDL_Rect opRect = {cx - r, cy - r, r*2, r*2};
|
||||
SDL_RenderDrawRect(renderer, &opRect);
|
||||
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);
|
||||
|
||||
// Draw Op Symbol
|
||||
char opChar = '?';
|
||||
int opType = (int)(cell.param * 5.99f);
|
||||
if (opType == 0) opChar = '+';
|
||||
else if (opType == 1) opChar = '*';
|
||||
else if (opType == 2) opChar = '-';
|
||||
else if (opType == 3) opChar = '/';
|
||||
else if (opType == 4) opChar = '<';
|
||||
else if (opType == 5) opChar = '>';
|
||||
drawChar(renderer, cx - 5, cy - 5, 12, opChar);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
(void)argc; (void)argv;
|
||||
|
||||
@@ -204,7 +363,7 @@ int main(int argc, char* argv[]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_Window* window = SDL_CreateWindow("NoiceSynth Scope", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
|
||||
SDL_Window* window = SDL_CreateWindow("NoiceSynth Integrated", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
|
||||
if (!window) return -1;
|
||||
|
||||
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
@@ -280,11 +439,101 @@ int main(int argc, char* argv[]) {
|
||||
while (SDL_PollEvent(&e) != 0) {
|
||||
if (e.type == SDL_QUIT) {
|
||||
quit = true;
|
||||
} else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||
quit = true; // Close app if any window closes
|
||||
}
|
||||
|
||||
// --- Mouse Handling ---
|
||||
// Check if event is in Grid Panel (Left) or Synth Panel (Right)
|
||||
|
||||
if (e.type == SDL_MOUSEBUTTONDOWN) {
|
||||
int mx = e.button.x;
|
||||
int my = e.button.y;
|
||||
if (mx < GRID_PANEL_WIDTH) {
|
||||
int gx = mx / 80;
|
||||
int gy = my / 80;
|
||||
if (gx >= 0 && gx < 5 && gy >= 0 && gy < 8) {
|
||||
std::lock_guard<std::mutex> lock(engine.gridMutex);
|
||||
SynthEngine::GridCell& c = engine.grid[gx][gy];
|
||||
if (c.type != SynthEngine::GridCell::SINK) {
|
||||
SynthEngine::GridCell::Type oldType = c.type;
|
||||
SynthEngine::GridCell::Type newType = oldType;
|
||||
|
||||
if (e.button.button == SDL_BUTTON_LEFT) {
|
||||
// Cycle: EMPTY -> FIXED -> INPUT -> NOISE -> FORK -> WIRE -> OP -> DELAY -> EMPTY
|
||||
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::INPUT_OSCILLATOR) newType = SynthEngine::GridCell::NOISE;
|
||||
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::WIRE) newType = SynthEngine::GridCell::OPERATOR;
|
||||
else if (oldType == SynthEngine::GridCell::OPERATOR) newType = SynthEngine::GridCell::DELAY;
|
||||
else if (oldType == SynthEngine::GridCell::DELAY) newType = SynthEngine::GridCell::EMPTY;
|
||||
} else if (e.button.button == SDL_BUTTON_RIGHT) {
|
||||
c.rotation = (c.rotation + 1) % 4;
|
||||
} else if (e.button.button == SDL_BUTTON_MIDDLE) {
|
||||
newType = SynthEngine::GridCell::EMPTY;
|
||||
c.param = 0.5f;
|
||||
c.rotation = 0;
|
||||
}
|
||||
|
||||
if (newType != oldType) {
|
||||
// If old type was DELAY, free its buffer
|
||||
if (oldType == SynthEngine::GridCell::DELAY && c.buffer) {
|
||||
delete[] c.buffer;
|
||||
c.buffer = nullptr;
|
||||
c.buffer_size = 0;
|
||||
}
|
||||
c.type = newType;
|
||||
// If new type is DELAY, allocate its buffer
|
||||
if (newType == SynthEngine::GridCell::DELAY) {
|
||||
c.buffer_size = 2 * SAMPLE_RATE; // Max 2 seconds delay
|
||||
c.buffer = new float[c.buffer_size](); // Allocate and zero-initialize
|
||||
c.write_idx = 0; // Reset write index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Synth Panel Click
|
||||
int synthX = mx - GRID_PANEL_WIDTH;
|
||||
// Check Toggle Click
|
||||
int toggleX = 580;
|
||||
int toggleY = 450;
|
||||
int toggleSize = 30;
|
||||
|
||||
if (synthX >= toggleX - toggleSize/2 && synthX <= toggleX + toggleSize/2 &&
|
||||
my >= toggleY - toggleSize/2 && my <= toggleY + toggleSize/2) {
|
||||
|
||||
auto_melody_enabled = !auto_melody_enabled;
|
||||
engine.setGate(false); // Silence synth on mode change
|
||||
current_key_scancode = 0;
|
||||
if (auto_melody_enabled) {
|
||||
auto_melody_next_event_time = SDL_GetTicks(); // Start immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (e.type == SDL_MOUSEWHEEL) {
|
||||
int mouseX, mouseY;
|
||||
SDL_GetMouseState(&mouseX, &mouseY);
|
||||
int mx, my;
|
||||
SDL_GetMouseState(&mx, &my);
|
||||
|
||||
if (mouseX < WINDOW_WIDTH / 2) { // Left knob (Octave)
|
||||
if (mx < GRID_PANEL_WIDTH) {
|
||||
// Grid Scroll
|
||||
int gx = mx / 80;
|
||||
int gy = my / 80;
|
||||
if (gx >= 0 && gx < 5 && gy >= 0 && gy < 8) {
|
||||
std::lock_guard<std::mutex> lock(engine.gridMutex);
|
||||
SynthEngine::GridCell& c = engine.grid[gx][gy];
|
||||
if (e.wheel.y > 0) c.param += 0.05f;
|
||||
else c.param -= 0.05f;
|
||||
if (c.param > 1.0f) c.param = 1.0f;
|
||||
if (c.param < 0.0f) c.param = 0.0f;
|
||||
}
|
||||
} else {
|
||||
// Synth Scroll
|
||||
int synthX = mx - GRID_PANEL_WIDTH;
|
||||
|
||||
if (synthX < SYNTH_PANEL_WIDTH / 2) { // Left knob (Octave)
|
||||
if (e.wheel.y > 0) current_octave++;
|
||||
else if (e.wheel.y < 0) current_octave--;
|
||||
|
||||
@@ -303,40 +552,15 @@ int main(int argc, char* argv[]) {
|
||||
if (knob_vol_val < 0.0f) knob_vol_val = 0.0f;
|
||||
engine.setVolume(knob_vol_val);
|
||||
}
|
||||
} else if (e.type == SDL_MOUSEBUTTONDOWN) {
|
||||
int mouseX, mouseY;
|
||||
SDL_GetMouseState(&mouseX, &mouseY);
|
||||
|
||||
// Check Toggle Click
|
||||
int toggleX = 580;
|
||||
int toggleY = 450;
|
||||
int toggleSize = 30;
|
||||
|
||||
if (mouseX >= toggleX - toggleSize/2 && mouseX <= toggleX + toggleSize/2 &&
|
||||
mouseY >= toggleY - toggleSize/2 && mouseY <= toggleY + toggleSize/2) {
|
||||
|
||||
auto_melody_enabled = !auto_melody_enabled;
|
||||
engine.setGate(false); // Silence synth on mode change
|
||||
current_key_scancode = 0;
|
||||
if (auto_melody_enabled) {
|
||||
auto_melody_next_event_time = SDL_GetTicks(); // Start immediately
|
||||
}
|
||||
} else if (e.button.button == SDL_BUTTON_LEFT) {
|
||||
// Check Left Knob (Octave) or Waveform Icon
|
||||
// Knob: 100, 450, r=40. Waveform: 75, 500, 50x20
|
||||
bool clickedKnob = (pow(mouseX - 100, 2) + pow(mouseY - 450, 2)) <= pow(40, 2);
|
||||
bool clickedWave = (mouseX >= 75 && mouseX <= 125 && mouseY >= 500 && mouseY <= 520);
|
||||
if (clickedKnob || clickedWave) {
|
||||
// Left knob click emulates encoder switch: cycle waveform
|
||||
current_waveform = (SynthEngine::Waveform)(((int)current_waveform + 1) % 3);
|
||||
engine.setWaveform(current_waveform);
|
||||
}
|
||||
}
|
||||
} else if (e.type == SDL_MOUSEMOTION) {
|
||||
if (e.motion.state & SDL_BUTTON_LMASK) {
|
||||
int mouseX = e.motion.x;
|
||||
int mouseY = e.motion.y;
|
||||
|
||||
if (mouseX >= GRID_PANEL_WIDTH) {
|
||||
int synthX = mouseX - GRID_PANEL_WIDTH;
|
||||
|
||||
// Handle Sliders
|
||||
// ADSR: x=200, 250, 300, 350. y=380, h=150
|
||||
// Filters: x=450, 500.
|
||||
@@ -345,7 +569,7 @@ int main(int argc, char* argv[]) {
|
||||
int sliderW = 30;
|
||||
|
||||
auto checkSlider = [&](int idx, int sx, float* val) {
|
||||
if (mouseX >= sx && mouseX <= sx + sliderW && mouseY >= sliderY - 20 && mouseY <= sliderY + sliderH + 20) {
|
||||
if (synthX >= sx && synthX <= sx + sliderW && mouseY >= sliderY - 20 && mouseY <= sliderY + sliderH + 20) {
|
||||
*val = 1.0f - (float)(mouseY - sliderY) / (float)sliderH;
|
||||
if (*val < 0.0f) *val = 0.0f;
|
||||
if (*val > 1.0f) *val = 1.0f;
|
||||
@@ -367,6 +591,7 @@ int main(int argc, char* argv[]) {
|
||||
float hpFreq = 20.0f + pow(filter_vals[1], 2.0f) * 19980.0f;
|
||||
engine.setFilter(lpFreq, hpFreq);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.repeat == 0) { // Ignore key repeats
|
||||
@@ -397,10 +622,8 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// Update window title with current values
|
||||
char title[256];
|
||||
snprintf(title, sizeof(title), "NoiceSynth | Freq: %.1f Hz | Vol: %.0f%% | Wave: %s | Oct: %d | Auto(M): %s",
|
||||
engine.getFrequency(),
|
||||
snprintf(title, sizeof(title), "NoiceSynth | Vol: %.0f%% | Oct: %d | Auto(M): %s",
|
||||
knob_vol_val * 100.0f,
|
||||
waveform_names[(int)current_waveform],
|
||||
current_octave,
|
||||
auto_melody_enabled ? "ON" : "OFF");
|
||||
SDL_SetWindowTitle(window, title);
|
||||
@@ -409,8 +632,11 @@ int main(int argc, char* argv[]) {
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
// --- Draw Synth Panel (Right) ---
|
||||
SDL_Rect synthViewport = {GRID_PANEL_WIDTH, 0, SYNTH_PANEL_WIDTH, WINDOW_HEIGHT};
|
||||
SDL_RenderSetViewport(renderer, &synthViewport);
|
||||
|
||||
// --- Draw Waveform (Oscilloscope) ---
|
||||
// Draw in the top half of the window
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Green
|
||||
|
||||
// Determine read position (snapshot atomic write index)
|
||||
@@ -438,7 +664,7 @@ int main(int argc, char* argv[]) {
|
||||
int prev_x = -1;
|
||||
int prev_y = -1;
|
||||
|
||||
for (int x = 0; x < WINDOW_WIDTH; ++x) {
|
||||
for (int x = 0; x < SYNTH_PANEL_WIDTH; ++x) {
|
||||
int16_t sample = vis_buffer[read_idx];
|
||||
read_idx = (read_idx + 1) % VIS_BUFFER_SIZE;
|
||||
|
||||
@@ -458,7 +684,6 @@ int main(int argc, char* argv[]) {
|
||||
// Knobs moved to edges
|
||||
float normalized_octave = (float)current_octave / 8.0f; // Max octave 8
|
||||
drawKnob(renderer, 100, 450, 40, normalized_octave);
|
||||
drawWaveformIcon(renderer, 100 - 25, 450 + 50, 50, 20, current_waveform);
|
||||
|
||||
drawKnob(renderer, 700, 450, 40, knob_vol_val);
|
||||
|
||||
@@ -472,6 +697,23 @@ int main(int argc, char* argv[]) {
|
||||
drawSlider(renderer, 450, 380, 30, 150, filter_vals[0], "LP");
|
||||
drawSlider(renderer, 500, 380, 30, 150, filter_vals[1], "HP");
|
||||
|
||||
// --- Draw Grid Panel (Left) ---
|
||||
SDL_Rect gridViewport = {0, 0, GRID_PANEL_WIDTH, WINDOW_HEIGHT};
|
||||
SDL_RenderSetViewport(renderer, &gridViewport);
|
||||
|
||||
// Draw separator line
|
||||
SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
|
||||
SDL_RenderDrawLine(renderer, GRID_PANEL_WIDTH - 1, 0, GRID_PANEL_WIDTH - 1, WINDOW_HEIGHT);
|
||||
|
||||
{
|
||||
// Lock only for reading state to draw
|
||||
std::lock_guard<std::mutex> lock(engine.gridMutex);
|
||||
for(int x=0; x<5; ++x) {
|
||||
for(int y=0; y<8; ++y) {
|
||||
drawGridCell(renderer, x*80, y*80, 80, engine.grid[x][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user