Skip to content

Commit 0da7514

Browse files
committed
misc
1 parent b283006 commit 0da7514

File tree

1 file changed

+78
-1
lines changed

1 file changed

+78
-1
lines changed

src/gui.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,26 +270,103 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
270270

271271
// ADSR Envelope
272272
if (ImGui::CollapsingHeader("ADSR Envelope", ImGuiTreeNodeFlags_DefaultOpen)) {
273-
// Single column layout - Attack, Decay, Sustain, Release stacked vertically
273+
// 4x1 layout with envelope visualization
274+
ImGui::Columns(5, "adsr_columns", true); // 4 parameters + 1 for envelope visualization
275+
276+
// Attack column
274277
ImGui::Text("Attack");
275278
if (ImGui::SliderFloat("##adsrattack", &synth->adsr.attack, 0.001f, 5.0f, "%.3f s", 0)) {
276279
synth_set_param(synth, "adsr.attack", synth->adsr.attack);
277280
}
281+
ImGui::NextColumn();
278282

283+
// Decay column
279284
ImGui::Text("Decay");
280285
if (ImGui::SliderFloat("##adsrdecay", &synth->adsr.decay, 0.001f, 5.0f, "%.3f s", 0)) {
281286
synth_set_param(synth, "adsr.decay", synth->adsr.decay);
282287
}
288+
ImGui::NextColumn();
283289

290+
// Sustain column
284291
ImGui::Text("Sustain");
285292
if (ImGui::SliderFloat("##adsrsustain", &synth->adsr.sustain, 0.0f, 1.0f, "%.2f", 0)) {
286293
synth_set_param(synth, "adsr.sustain", synth->adsr.sustain);
287294
}
295+
ImGui::NextColumn();
288296

297+
// Release column
289298
ImGui::Text("Release");
290299
if (ImGui::SliderFloat("##adsrrelease", &synth->adsr.release, 0.001f, 10.0f, "%.3f s", 0)) {
291300
synth_set_param(synth, "adsr.release", synth->adsr.release);
292301
}
302+
ImGui::NextColumn();
303+
304+
// Envelope visualization column
305+
ImGui::Text("Envelope");
306+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
307+
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
308+
ImVec2 canvas_size = ImVec2(120, 80); // Fixed size for envelope display
309+
310+
// Draw background
311+
ImVec2 bg_end = ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y);
312+
draw_list->AddRectFilled(canvas_pos, bg_end, IM_COL32(20, 25, 35, 255));
313+
draw_list->AddRect(canvas_pos, bg_end, IM_COL32(100, 100, 120, 255));
314+
315+
// Calculate envelope curve points
316+
const int num_points = 100;
317+
float total_time = synth->adsr.attack + synth->adsr.decay + synth->adsr.release + 1.0f; // Add 1 second for sustain phase
318+
float max_time = fmaxf(total_time, 2.0f); // At least 2 seconds for display
319+
320+
// Calculate envelope points and clip to canvas bounds
321+
ImVec2 points[num_points];
322+
int valid_points = 0;
323+
324+
for (int i = 0; i < num_points; i++) {
325+
float t = (float)i / (float)(num_points - 1) * max_time;
326+
float amplitude = 0.0f;
327+
328+
if (t < synth->adsr.attack) {
329+
// Attack phase: linear ramp from 0 to 1
330+
amplitude = t / synth->adsr.attack;
331+
} else if (t < synth->adsr.attack + synth->adsr.decay) {
332+
// Decay phase: exponential decay from 1 to sustain
333+
float decay_progress = (t - synth->adsr.attack) / synth->adsr.decay;
334+
amplitude = 1.0f - (1.0f - synth->adsr.sustain) * decay_progress;
335+
} else if (t < synth->adsr.attack + synth->adsr.decay + 1.0f) {
336+
// Sustain phase: constant
337+
amplitude = synth->adsr.sustain;
338+
} else {
339+
// Release phase: exponential decay from sustain to 0
340+
float release_progress = (t - synth->adsr.attack - synth->adsr.decay - 1.0f) / synth->adsr.release;
341+
amplitude = synth->adsr.sustain * (1.0f - release_progress);
342+
}
343+
344+
// Calculate screen position and clip to canvas bounds
345+
float x = canvas_pos.x + (float)i / (float)(num_points - 1) * canvas_size.x;
346+
float y = canvas_pos.y + canvas_size.y - amplitude * canvas_size.y;
347+
348+
// Clamp coordinates to canvas bounds
349+
x = fmaxf(canvas_pos.x, fminf(x, canvas_pos.x + canvas_size.x));
350+
y = fmaxf(canvas_pos.y, fminf(y, canvas_pos.y + canvas_size.y));
351+
352+
points[i] = ImVec2(x, y);
353+
valid_points++;
354+
}
355+
356+
// Draw clipped polyline using the calculated points
357+
if (valid_points > 1) {
358+
draw_list->AddPolyline(points, valid_points, IM_COL32(100, 255, 100, 255), 0, 2.0f);
359+
}
360+
361+
// Draw phase labels
362+
draw_list->AddText(ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), IM_COL32(180, 180, 200, 255), "A");
363+
draw_list->AddText(ImVec2(canvas_pos.x + canvas_size.x / 4 + 2, canvas_pos.y + canvas_size.y - 15), IM_COL32(180, 180, 200, 255), "D");
364+
draw_list->AddText(ImVec2(canvas_pos.x + canvas_size.x / 2 + 2, canvas_pos.y + canvas_size.y - 15), IM_COL32(180, 180, 200, 255), "S");
365+
draw_list->AddText(ImVec2(canvas_pos.x + 3 * canvas_size.x / 4 + 2, canvas_pos.y + canvas_size.y - 15), IM_COL32(180, 180, 200, 255), "R");
366+
367+
ImGui::Dummy(canvas_size); // Reserve space for the drawing
368+
369+
ImGui::Columns(1, "", false);
293370

294371
// Preset buttons
295372
ImGui::Separator();

0 commit comments

Comments
 (0)