Skip to content

Commit af80545

Browse files
committed
misc
1 parent d1b342f commit af80545

File tree

4 files changed

+79
-28
lines changed

4 files changed

+79
-28
lines changed

src/arpeggiator.c

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ void arpeggiator_init(Arpeggiator *arp) {
2727
arp->add_9 = 0;
2828
arp->voicing = 0;
2929

30+
// Initialize gate parameters
31+
arp->gate_length = 0.8f; // Default 80% gate length
32+
3033
for (int i = 0; i < 16; ++i) {
3134
arp->active_arpeggiated_notes[i].active = 0;
3235
}
@@ -73,6 +76,8 @@ void arpeggiator_set_param(Arpeggiator *arp, const char *param, float value, str
7376
arp->add_9 = (int)value;
7477
else if (!strcmp(param, "voicing"))
7578
arp->voicing = (int)value;
79+
else if (!strcmp(param, "gate_length"))
80+
arp->gate_length = value;
7681
}
7782

7883
void arpeggiator_note_on(Arpeggiator *arp, int note) {
@@ -180,12 +185,12 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
180185
break;
181186
}
182187
}
183-
if (free_slot != -1) {
184-
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
185-
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time; // Note duration is one step
186-
arp->active_arpeggiated_notes[free_slot].active = 1;
187-
synth_note_on(synth, note_to_play, 0.8f);
188-
note_count++;
188+
if (free_slot != -1) {
189+
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
190+
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time * arp->gate_length; // Gate length controls note duration
191+
arp->active_arpeggiated_notes[free_slot].active = 1;
192+
synth_note_on(synth, note_to_play, 0.8f);
193+
note_count++;
189194
}
190195
}
191196
} else {
@@ -216,12 +221,12 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
216221
break;
217222
}
218223
}
219-
if (free_slot != -1) {
220-
arp->active_arpeggiated_notes[free_slot].note = notes_to_play[i];
221-
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time;
222-
arp->active_arpeggiated_notes[free_slot].active = 1;
223-
synth_note_on(synth, notes_to_play[i], 0.8f);
224-
}
224+
if (free_slot != -1) {
225+
arp->active_arpeggiated_notes[free_slot].note = notes_to_play[i];
226+
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time * arp->gate_length;
227+
arp->active_arpeggiated_notes[free_slot].active = 1;
228+
synth_note_on(synth, notes_to_play[i], 0.8f);
229+
}
225230
}
226231
}
227232
break;
@@ -240,6 +245,26 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
240245
case ARP_RANDOM:
241246
idx = rand() % arp->held_count;
242247
break;
248+
case ARP_PENDULUM:
249+
// Pendulum mode: play up then down, creating a bounce pattern
250+
// For held notes [n0, n1, n2, n3], play: n0, n1, n2, n3, n2, n1, n0, n1, n2, n3...
251+
qsort(notes, arp->held_count, sizeof(int), order_compare);
252+
{
253+
int cycle_length = (arp->held_count * 2) - 2; // n0,n1,n2,n3,n2,n1 = 5 for 4 notes
254+
if (arp->held_count == 1) {
255+
cycle_length = 1; // Single note just repeats
256+
}
257+
int step_in_cycle = arp->step % cycle_length;
258+
259+
if (step_in_cycle < arp->held_count) {
260+
// Ascending part
261+
idx = step_in_cycle;
262+
} else {
263+
// Descending part
264+
idx = (arp->held_count - 1) - (step_in_cycle - arp->held_count + 1);
265+
}
266+
}
267+
break;
243268
default:
244269
idx = 0;
245270
}
@@ -255,12 +280,12 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
255280
break;
256281
}
257282
}
258-
if (free_slot != -1) {
259-
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
260-
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time; // Note duration is one step
261-
arp->active_arpeggiated_notes[free_slot].active = 1;
262-
synth_note_on(synth, note_to_play, 0.8f);
263-
}
283+
if (free_slot != -1) {
284+
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
285+
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time * arp->gate_length; // Gate length controls note duration
286+
arp->active_arpeggiated_notes[free_slot].active = 1;
287+
synth_note_on(synth, note_to_play, 0.8f);
288+
}
264289
}
265290
}
266291

src/arpeggiator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ typedef struct {
7171
int add_9; // Add 9th extension
7272
int voicing; // Voicing/inversion level (0-16)
7373

74+
// Gate parameters
75+
float gate_length; // Gate length (0.0-1.0) - proportion of step time note should sound
76+
7477
ActiveArpeggiatedNote active_arpeggiated_notes[16]; // Max 16 notes in a polyphonic step
7578
} Arpeggiator;
7679

src/gui.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -575,17 +575,10 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
575575
draw_list->AddRect(rms_bar_pos,
576576
ImVec2(rms_bar_pos.x + bar_width, rms_bar_pos.y + bar_height),
577577
ImColor(1.0f, 1.0f, 1.0f, 1.0f)); // White border
578-
579-
ImGui::Separator();
580-
ImGui::Text("Status:");
581-
if (synth->mixer.dc_filter_enabled)
582-
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "[DC Filter ON]");
583-
if (synth->mixer.soft_clip_enabled)
584-
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "[Soft Clip ON]");
585-
if (synth->mixer.auto_gain_enabled)
586-
ImGui::TextColored(ImVec4(0.0f, 0.5f, 1.0f, 1.0f), "[Auto Gain ON]");
587-
}
588578

579+
ImGui::Separator();
580+
}
581+
589582
// Arpeggiator
590583
if (ImGui::CollapsingHeader("ArpeggiatorSection", ImGuiTreeNodeFlags_DefaultOpen)) {
591584
ImGui::Checkbox("Enabled##arp", (bool*)&synth->arp.enabled);
@@ -640,6 +633,13 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
640633
}
641634
}
642635

636+
ImGui::Separator();
637+
ImGui::Text("Gate & Timing:");
638+
ImGui::SliderFloat("Gate Length", &synth->arp.gate_length, 0.1f, 1.0f, "%.2f");
639+
if (ImGui::IsItemHovered()) {
640+
ImGui::SetTooltip("Controls note duration (0.1=10%%, 0.5=50%%, 1.0=100%% of step time)");
641+
}
642+
643643
ImGui::Separator();
644644
ImGui::Text("Presets:");
645645
ImGui::Columns(4, "arp_presets", true);
@@ -650,6 +650,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
650650
synth->arp.octaves = 2;
651651
synth->arp.polyphonic = 0;
652652
synth->arp.hold = 0;
653+
synth->arp.gate_length = 0.8f;
653654
}
654655
ImGui::SameLine(); ImGui::Text("8th notes");
655656

@@ -659,6 +660,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
659660
synth->arp.octaves = 2;
660661
synth->arp.polyphonic = 0;
661662
synth->arp.hold = 0;
663+
synth->arp.gate_length = 0.8f;
662664
}
663665
ImGui::SameLine(); ImGui::Text("Descending");
664666

@@ -668,6 +670,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
668670
synth->arp.octaves = 3;
669671
synth->arp.polyphonic = 1;
670672
synth->arp.hold = 1;
673+
synth->arp.gate_length = 0.7f;
671674
}
672675
ImGui::SameLine(); ImGui::Text("Pop style");
673676

@@ -677,6 +680,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
677680
synth->arp.octaves = 1;
678681
synth->arp.polyphonic = 0;
679682
synth->arp.hold = 0;
683+
synth->arp.gate_length = 0.9f;
680684
}
681685
ImGui::SameLine(); ImGui::Text("Sequential");
682686

@@ -689,6 +693,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
689693
synth->arp.octaves = 4;
690694
synth->arp.polyphonic = 1;
691695
synth->arp.hold = 1;
696+
synth->arp.gate_length = 0.4f;
692697
}
693698
ImGui::SameLine(); ImGui::Text("16th notes");
694699

@@ -698,6 +703,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
698703
synth->arp.octaves = 3;
699704
synth->arp.polyphonic = 0;
700705
synth->arp.hold = 0;
706+
synth->arp.gate_length = 0.6f;
701707
}
702708
ImGui::SameLine(); ImGui::Text("Unpredictable");
703709

@@ -707,6 +713,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
707713
synth->arp.octaves = 2;
708714
synth->arp.polyphonic = 1;
709715
synth->arp.hold = 1;
716+
synth->arp.gate_length = 0.8f;
710717
}
711718
ImGui::SameLine(); ImGui::Text("Progressive");
712719

@@ -716,6 +723,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
716723
synth->arp.octaves = 1;
717724
synth->arp.polyphonic = 1;
718725
synth->arp.hold = 1;
726+
synth->arp.gate_length = 1.0f;
719727
}
720728
ImGui::SameLine(); ImGui::Text("Chordal");
721729

@@ -727,6 +735,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
727735
synth->arp.octaves = 2;
728736
synth->arp.polyphonic = 0;
729737
synth->arp.hold = 0;
738+
synth->arp.gate_length = 0.3f;
730739
}
731740
ImGui::SameLine(); ImGui::Text("Driving");
732741

@@ -736,6 +745,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
736745
synth->arp.octaves = 1;
737746
synth->arp.polyphonic = 0;
738747
synth->arp.hold = 0;
748+
synth->arp.gate_length = 0.7f;
739749
}
740750
ImGui::SameLine(); ImGui::Text("Reggae");
741751

@@ -746,6 +756,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
746756
synth->arp.octaves = 2;
747757
synth->arp.polyphonic = 1;
748758
synth->arp.hold = 0;
759+
synth->arp.gate_length = 0.2f;
749760
}
750761
ImGui::SameLine(); ImGui::Text("Fast");
751762

@@ -755,6 +766,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
755766
synth->arp.octaves = 2;
756767
synth->arp.polyphonic = 0;
757768
synth->arp.hold = 0;
769+
synth->arp.gate_length = 0.5f;
758770
}
759771
ImGui::SameLine(); ImGui::Text("8-bit");
760772

@@ -766,6 +778,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
766778
synth->arp.octaves = 4;
767779
synth->arp.polyphonic = 0;
768780
synth->arp.hold = 0;
781+
synth->arp.gate_length = 0.8f;
769782
}
770783
ImGui::SameLine(); ImGui::Text("Classic");
771784

@@ -775,12 +788,14 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
775788
synth->arp.octaves = 3;
776789
synth->arp.polyphonic = 1;
777790
synth->arp.hold = 1;
791+
synth->arp.gate_length = 0.9f;
778792
}
779793
ImGui::SameLine(); ImGui::Text("Ethereal");
780794

781795
if (ImGui::Button("Industrial")) {
782796
synth->arp.mode = ARP_RANDOM;
783797
synth->arp.tempo = 160.0f;
798+
synth->arp.gate_length = 0.2f; // Staccato industrial feel
784799
synth->arp.octaves = 2;
785800
synth->arp.polyphonic = 0;
786801
synth->arp.hold = 0;
@@ -793,6 +808,7 @@ void gui_draw(Synth *synth, SDL_Window *window, SDL_GLContext gl_context,
793808
synth->arp.octaves = 2;
794809
synth->arp.polyphonic = 0;
795810
synth->arp.hold = 0;
811+
synth->arp.gate_length = 0.6f; // Medium gate for swing feel
796812
}
797813
ImGui::SameLine(); ImGui::Text("Bouncing");
798814

src/synth.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,9 @@ char* synth_save_preset_json(const Synth *synth) {
511511
cJSON_AddBoolToObject(arp, "add_9", synth->arp.add_9);
512512
cJSON_AddNumberToObject(arp, "voicing", synth->arp.voicing);
513513

514+
// Gate parameters
515+
cJSON_AddNumberToObject(arp, "gate_length", synth->arp.gate_length);
516+
514517
cJSON_AddItemToObject(root, "arpeggiator", arp);
515518

516519
char *json_string = cJSON_Print(root);
@@ -723,6 +726,10 @@ void synth_load_preset_json(Synth *synth, const char *json_string) {
723726
if (cJSON_IsNumber(voicing)) {
724727
synth_set_param(synth, "arp.voicing", (float)voicing->valuedouble);
725728
}
729+
cJSON *gate_length = cJSON_GetObjectItemCaseSensitive(arp, "gate_length");
730+
if (cJSON_IsNumber(gate_length)) {
731+
synth_set_param(synth, "arp.gate_length", (float)gate_length->valuedouble);
732+
}
726733
}
727734

728735
cJSON_Delete(root);

0 commit comments

Comments
 (0)