Skip to content

Commit e064bae

Browse files
committed
misc
1 parent 1d8adde commit e064bae

File tree

7 files changed

+122
-71
lines changed

7 files changed

+122
-71
lines changed

src/gui.cpp

Lines changed: 61 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,54 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
156156
ImGui::SetNextWindowSize(ImVec2((float)window_width, (float)window_height), ImGuiCond_Always);
157157
ImGui::Begin("Synth", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus);
158158

159+
// Oscilloscope
160+
if (ImGui::CollapsingHeader("Oscilloscope", ImGuiTreeNodeFlags_DefaultOpen)) {
161+
// Set fixed height of 200 pixels, use full available width
162+
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
163+
ImGui::BeginChild("OscilloscopeChild", ImVec2(0, 200), false, ImGuiWindowFlags_NoScrollbar);
164+
ImVec2 content_size = ImGui::GetContentRegionAvail();
165+
if (content_size.x > 0 && content_size.y > 0) {
166+
if ((int)content_size.x != osc_width || 200 != osc_height) {
167+
// Recreate resources if size has changed
168+
if (g_oscilloscope_renderer) SDL_DestroyRenderer(g_oscilloscope_renderer);
169+
if (g_oscilloscope_surface) SDL_FreeSurface(g_oscilloscope_surface);
170+
if (g_oscilloscope_gl_texture) glDeleteTextures(1, &g_oscilloscope_gl_texture);
171+
172+
osc_width = (int)content_size.x;
173+
osc_height = 200; // Fixed height of 200 pixels
174+
175+
g_oscilloscope_surface = SDL_CreateRGBSurfaceWithFormat(0, osc_width, osc_height, 32, SDL_PIXELFORMAT_RGBA32);
176+
g_oscilloscope_renderer = SDL_CreateSoftwareRenderer(g_oscilloscope_surface);
177+
178+
glGenTextures(1, &g_oscilloscope_gl_texture);
179+
glBindTexture(GL_TEXTURE_2D, g_oscilloscope_gl_texture);
180+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
181+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
182+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, osc_width, osc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
183+
}
184+
185+
if (g_oscilloscope_renderer && g_oscilloscope_surface && g_oscilloscope_gl_texture && g_font) {
186+
SDL_SetRenderDrawColor(g_oscilloscope_renderer, 0, 0, 0, 0);
187+
SDL_RenderClear(g_oscilloscope_renderer);
188+
oscilloscope_draw(g_oscilloscope_renderer, synth, 0, 0, osc_width, osc_height, g_font);
189+
190+
glBindTexture(GL_TEXTURE_2D, g_oscilloscope_gl_texture);
191+
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, osc_width, osc_height, GL_RGBA, GL_UNSIGNED_BYTE, g_oscilloscope_surface->pixels);
192+
193+
ImGui::Image((void*)(intptr_t)g_oscilloscope_gl_texture, ImVec2((float)osc_width, (float)osc_height));
194+
}
195+
}
196+
ImGui::EndChild();
197+
ImGui::PopStyleVar();
198+
}
199+
159200
// Oscillators
160201
if (ImGui::CollapsingHeader("Oscillators", ImGuiTreeNodeFlags_DefaultOpen)) {
161202
ImGui::Columns(2, "osc_columns", true);
162203
for (int i = 0; i < 6; ++i) {
163204
ImGui::PushID(i);
164205
char title[32];
165206
sprintf(title, "OSC %d", i + 1);
166-
if (i == 4) sprintf(title, "BASS");
167-
if (i == 5) sprintf(title, "PERC");
168207

169208
ImGui::BeginChild(title, ImVec2(0, 200), true, 0);
170209
ImGui::Text("%s", title);
@@ -188,14 +227,21 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
188227

189228
ImGui::EndChild();
190229
ImGui::PopID();
191-
if (i % 2 != 0) {
192-
ImGui::NextColumn();
193-
}
230+
ImGui::NextColumn();
194231
}
195232
ImGui::Columns(1, "", false);
196233

234+
// Oscillator randomization buttons
235+
ImGui::Separator();
236+
237+
// Randomize All button spanning full width
238+
if (ImGui::Button("Randomize All OSCs", ImVec2(-1, 0))) {
239+
synth_randomize_parameters(synth);
240+
}
241+
197242
// Individual oscillator randomization buttons
198-
ImGui::Columns(4, "random_buttons", true);
243+
ImGui::Columns(6, "random_buttons", true);
244+
199245
if (ImGui::Button("Randomize OSC 1")) {
200246
synth_randomize_oscillator(synth, 0);
201247
}
@@ -211,11 +257,15 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
211257
if (ImGui::Button("Randomize OSC 4")) {
212258
synth_randomize_oscillator(synth, 3);
213259
}
214-
ImGui::Columns(1, "", false);
215-
216-
if (ImGui::Button("Randomize All Oscillators")) {
217-
synth_randomize_parameters(synth);
260+
ImGui::NextColumn();
261+
if (ImGui::Button("Randomize OSC 5")) {
262+
synth_randomize_oscillator(synth, 4);
263+
}
264+
ImGui::NextColumn();
265+
if (ImGui::Button("Randomize OSC 6")) {
266+
synth_randomize_oscillator(synth, 5);
218267
}
268+
ImGui::Columns(1, "", false);
219269
}
220270

221271
// ADSR Envelope
@@ -317,7 +367,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
317367
if (ImGui::CollapsingHeader("LFOs", ImGuiTreeNodeFlags_DefaultOpen)) {
318368
ImGui::Columns(3, "lfo_columns", true);
319369

320-
const char* lfo_names[] = {"Pitch LFO", "Volume LFO", "Filter LFO"};
370+
const char* lfo_names[] = {"Pitch", "Volume", "Filter"};
321371
const char* waveforms[] = {"SINE", "TRIANGLE", "SQUARE", "SAW", "RANDOM"};
322372
const char* sync_modes[] = {"Free", "Retrigger", "Keyfollow"};
323373

@@ -784,42 +834,6 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
784834
ImGui::Columns(1, "", false);
785835
}
786836

787-
// Oscilloscope
788-
ImGui::Begin("Oscilloscope");
789-
ImVec2 content_size = ImGui::GetContentRegionAvail();
790-
if (content_size.x > 0 && content_size.y > 0) {
791-
if ((int)content_size.x != osc_width || (int)content_size.y != osc_height) {
792-
// Recreate resources if size has changed
793-
if (g_oscilloscope_renderer) SDL_DestroyRenderer(g_oscilloscope_renderer);
794-
if (g_oscilloscope_surface) SDL_FreeSurface(g_oscilloscope_surface);
795-
if (g_oscilloscope_gl_texture) glDeleteTextures(1, &g_oscilloscope_gl_texture);
796-
797-
osc_width = (int)content_size.x;
798-
osc_height = (int)content_size.y;
799-
800-
g_oscilloscope_surface = SDL_CreateRGBSurfaceWithFormat(0, osc_width, osc_height, 32, SDL_PIXELFORMAT_RGBA32);
801-
g_oscilloscope_renderer = SDL_CreateSoftwareRenderer(g_oscilloscope_surface);
802-
803-
glGenTextures(1, &g_oscilloscope_gl_texture);
804-
glBindTexture(GL_TEXTURE_2D, g_oscilloscope_gl_texture);
805-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
806-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
807-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, osc_width, osc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
808-
}
809-
810-
if (g_oscilloscope_renderer && g_oscilloscope_surface && g_oscilloscope_gl_texture && g_font) {
811-
SDL_SetRenderDrawColor(g_oscilloscope_renderer, 0, 0, 0, 0);
812-
SDL_RenderClear(g_oscilloscope_renderer);
813-
oscilloscope_draw(g_oscilloscope_renderer, synth, 0, 0, osc_width, osc_height, g_font);
814-
815-
glBindTexture(GL_TEXTURE_2D, g_oscilloscope_gl_texture);
816-
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, osc_width, osc_height, GL_RGBA, GL_UNSIGNED_BYTE, g_oscilloscope_surface->pixels);
817-
818-
ImGui::Image((void*)(intptr_t)g_oscilloscope_gl_texture, ImVec2((float)osc_width, (float)osc_height));
819-
}
820-
}
821-
ImGui::End();
822-
823837
// Pattern / Melody
824838
if (ImGui::CollapsingHeader("Pattern / Melody", ImGuiTreeNodeFlags_DefaultOpen)) {
825839
ImGui::Columns(2, "pattern_columns", true);

src/lfo.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <string.h>
55
#include <stdlib.h>
66

7-
void lfo_init(LowFrequencyOscillator *lfo, float samplerate) {
7+
void lfo_init(LFO *lfo, float samplerate) {
88
lfo->waveform = LFO_SINE;
99
lfo->frequency = 1.0f;
1010
lfo->depth = 0.5f;
@@ -18,7 +18,7 @@ void lfo_init(LowFrequencyOscillator *lfo, float samplerate) {
1818
lfo->notes_pressed = 0;
1919
}
2020

21-
void lfo_set_param(LowFrequencyOscillator *lfo, const char *param, float value) {
21+
void lfo_set_param(LFO *lfo, const char *param, float value) {
2222
if (strcmp(param, "waveform") == 0)
2323
lfo->waveform = (LfoWaveform)((int)value);
2424
else if (strcmp(param, "frequency") == 0)
@@ -37,7 +37,7 @@ void lfo_set_param(LowFrequencyOscillator *lfo, const char *param, float value)
3737
lfo->enabled = (int)(value + 0.5f);
3838
}
3939

40-
float lfo_process(LowFrequencyOscillator *lfo) {
40+
float lfo_process(LFO *lfo) {
4141
if (!lfo->enabled || lfo->frequency <= 0.0f) {
4242
return 0.0f;
4343
}
@@ -75,18 +75,18 @@ float lfo_process(LowFrequencyOscillator *lfo) {
7575
return lfo->gain * output;
7676
}
7777

78-
float lfo_get_modulation_value(LowFrequencyOscillator *lfo) {
78+
float lfo_get_modulation_value(LFO *lfo) {
7979
return lfo_process(lfo) * lfo->depth;
8080
}
8181

82-
void lfo_note_on(LowFrequencyOscillator *lfo) {
82+
void lfo_note_on(LFO *lfo) {
8383
if (lfo->sync == LFO_SYNC_RETRIGGER) {
8484
lfo->phase_acc = 0.0f; // Reset phase on each note
8585
}
8686
lfo->notes_pressed++;
8787
}
8888

89-
void lfo_note_off(LowFrequencyOscillator *lfo) {
89+
void lfo_note_off(LFO *lfo) {
9090
if (lfo->notes_pressed > 0) {
9191
lfo->notes_pressed--;
9292
}

src/lfo.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ typedef enum { LFO_TARGET_FREQUENCY, LFO_TARGET_FILTER, LFO_TARGET_AMPLITUDE, LF
77

88
typedef enum { LFO_SYNC_FREE, LFO_SYNC_RETRIGGER, LFO_SYNC_KEYFOLLOW } LfoSyncMode;
99

10-
typedef struct LowFrequencyOscillator {
10+
typedef struct LFO {
1111
LfoWaveform waveform;
1212
float frequency; // LFO frequency in Hz (0.1 to 20.0)
1313
float depth; // Modulation depth (0.0 to 1.0)
@@ -20,11 +20,11 @@ typedef struct LowFrequencyOscillator {
2020
LfoSyncMode sync; // Sync mode (free, retrigger, keyfollow)
2121
int enabled; // LFO on/off
2222
int notes_pressed; // For keyfollow sync - number of notes currently pressed
23-
} LowFrequencyOscillator;
23+
} LFO;
2424

25-
void lfo_init(LowFrequencyOscillator *lfo, float samplerate);
26-
void lfo_set_param(LowFrequencyOscillator *lfo, const char *param, float value);
27-
float lfo_process(LowFrequencyOscillator *lfo);
28-
float lfo_get_modulation_value(LowFrequencyOscillator *lfo);
29-
void lfo_note_on(LowFrequencyOscillator *lfo);
30-
void lfo_note_off(LowFrequencyOscillator *lfo);
25+
void lfo_init(LFO *lfo, float samplerate);
26+
void lfo_set_param(LFO *lfo, const char *param, float value);
27+
float lfo_process(LFO *lfo);
28+
float lfo_get_modulation_value(LFO *lfo);
29+
void lfo_note_on(LFO *lfo);
30+
void lfo_note_off(LFO *lfo);

src/oscilloscope.c

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,39 @@ void oscilloscope_draw(SDL_Renderer *renderer, const struct Synth *synth,
100100
int trigger_pos = waveform_write_pos;
101101
int samples_to_display = channel_width > WAVEFORM_BUFFER_SIZE ? WAVEFORM_BUFFER_SIZE : channel_width;
102102

103+
// Find the actual signal range for auto-scaling
104+
float min_sample = 0.0f, max_sample = 0.0f;
105+
int first_sample = 1;
106+
107+
for (int i = 0; i < samples_to_display; i++) {
108+
int sample_idx = (trigger_pos + i) % WAVEFORM_BUFFER_SIZE;
109+
float sample = waveform_buffer[sample_idx];
110+
111+
if (first_sample) {
112+
min_sample = max_sample = sample;
113+
first_sample = 0;
114+
} else {
115+
if (sample < min_sample) min_sample = sample;
116+
if (sample > max_sample) max_sample = sample;
117+
}
118+
}
119+
120+
// Calculate scaling factor to fit the waveform in the available height
121+
float signal_range = max_sample - min_sample;
122+
float scale_factor;
123+
124+
if (signal_range < 0.001f) {
125+
// Signal is essentially flat, use default scaling
126+
scale_factor = (waveform_h / 2.0f) * 0.8f; // Use 80% of available range
127+
} else {
128+
// Scale to fit 90% of available height
129+
scale_factor = (waveform_h / 2.0f) * 0.9f / (signal_range / 2.0f);
130+
}
131+
132+
// Calculate center offset
133+
float signal_center = (max_sample + min_sample) / 2.0f;
134+
int center_y = y + waveform_h / 2;
135+
103136
// Draw left channel waveform (red)
104137
SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255); // Red color
105138
for (int px = 0; px < channel_width - 1; px++) {
@@ -109,9 +142,11 @@ void oscilloscope_draw(SDL_Renderer *renderer, const struct Synth *synth,
109142
float sample1 = waveform_buffer[sample_idx1];
110143
float sample2 = waveform_buffer[sample_idx2];
111144

112-
int y1 = y + waveform_h/2 - (int)(sample1 * (waveform_h/2));
113-
int y2 = y + waveform_h/2 - (int)(sample2 * (waveform_h/2));
145+
// Apply auto-scaling: center the signal and scale it to fit
146+
int y1 = center_y - (int)((sample1 - signal_center) * scale_factor);
147+
int y2 = center_y - (int)((sample2 - signal_center) * scale_factor);
114148

149+
// Clamp to drawing area
115150
if (y1 < y) y1 = y;
116151
if (y1 >= y + waveform_h) y1 = y + waveform_h - 1;
117152
if (y2 < y) y2 = y;
@@ -129,9 +164,11 @@ void oscilloscope_draw(SDL_Renderer *renderer, const struct Synth *synth,
129164
float sample1 = waveform_buffer[sample_idx1];
130165
float sample2 = waveform_buffer[sample_idx2];
131166

132-
int y1 = y + waveform_h/2 - (int)(sample1 * (waveform_h/2));
133-
int y2 = y + waveform_h/2 - (int)(sample2 * (waveform_h/2));
167+
// Apply auto-scaling: center the signal and scale it to fit
168+
int y1 = center_y - (int)((sample1 - signal_center) * scale_factor);
169+
int y2 = center_y - (int)((sample2 - signal_center) * scale_factor);
134170

171+
// Clamp to drawing area
135172
if (y1 < y) y1 = y;
136173
if (y1 >= y + waveform_h) y1 = y + waveform_h - 1;
137174
if (y2 < y) y2 = y;

src/synth.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ typedef struct {
2424

2525
typedef struct Synth {
2626
Oscillator osc[6]; // osc[0-3] for main, osc[4] for bass, osc[5] for percussion
27-
LowFrequencyOscillator lfos[3]; // lfos[0] for pitch, lfos[1] for volume, lfos[2] for filter
27+
LFO lfos[3]; // lfos[0] for pitch, lfos[1] for volume, lfos[2] for filter
2828
Mixer mixer;
2929
FX fx;
3030
RingModulator ring_mod;

src/voice.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void voice_off(Voice *v) {
2525
// Voice stays active until ADSR reaches IDLE state
2626
}
2727

28-
void voice_render(Voice *v, const Oscillator *osc, const LowFrequencyOscillator *lfo, const float *osc_gains, float *stereo, int frames) {
28+
void voice_render(Voice *v, const Oscillator *osc, const LFO *lfo, const float *osc_gains, float *stereo, int frames) {
2929
// Get ADSR envelope value for this block
3030
float adsr_value = adsr_process(&v->adsr, frames);
3131

@@ -40,9 +40,9 @@ void voice_render(Voice *v, const Oscillator *osc, const LowFrequencyOscillator
4040
float right = 0.0f;
4141

4242
// Get current LFO modulation values
43-
float pitch_mod = lfo[0].enabled ? lfo_get_modulation_value((LowFrequencyOscillator*)&lfo[0]) : 0.0f;
44-
float volume_mod = lfo[1].enabled ? lfo_get_modulation_value((LowFrequencyOscillator*)&lfo[1]) : 0.0f;
45-
float filter_mod = lfo[2].enabled ? lfo_get_modulation_value((LowFrequencyOscillator*)&lfo[2]) : 0.0f;
43+
float pitch_mod = lfo[0].enabled ? lfo_get_modulation_value((LFO*)&lfo[0]) : 0.0f;
44+
float volume_mod = lfo[1].enabled ? lfo_get_modulation_value((LFO*)&lfo[1]) : 0.0f;
45+
float filter_mod = lfo[2].enabled ? lfo_get_modulation_value((LFO*)&lfo[2]) : 0.0f;
4646

4747
// Process each oscillator with gain and panning
4848
for (int o = 0; o < 6; ++o) {

src/voice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ void voice_init(Voice *v, float samplerate);
1616
void voice_on(Voice *v, float note, float velocity,
1717
unsigned long long timestamp);
1818
void voice_off(Voice *v);
19-
void voice_render(Voice *v, const Oscillator *osc, const LowFrequencyOscillator *lfo, const float *osc_gains, float *stereo, int frames);
19+
void voice_render(Voice *v, const Oscillator *osc, const LFO *lfo, const float *osc_gains, float *stereo, int frames);
2020
int voice_is_active(const Voice *v);

0 commit comments

Comments
 (0)