Skip to content

Commit 403c8fa

Browse files
committed
misc
1 parent 1265127 commit 403c8fa

File tree

3 files changed

+204
-17
lines changed

3 files changed

+204
-17
lines changed

src/app.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ void app_update(App *app) {
359359

360360
char title[256];
361361
snprintf(title, sizeof(title),
362-
"sdl2-synth - Voices %d/%d | CPU %.1f%% | FPS %.1f | Comp: %.1fdB | Arp: %s | Oct: %d | Octaves: %d",
362+
"Voices %d/%d | CPU %.1f%% | FPS %.1f | Comp: %.1fdB | Arp: %s | Oct: %d | Octaves: %d",
363363
synth_active_voices(&app->synth), app->synth.max_voices,
364364
synth_cpu_usage(&app->synth), app->fps,
365365
app->synth.mixer.compressor.gain_reduction,

src/arpeggiator.c

Lines changed: 196 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ void arpeggiator_init(Arpeggiator *arp) {
99
memset(arp->pattern, 0, sizeof(arp->pattern)); // Initialize pattern to all zeros
1010
arp->step = 0;
1111
arp->tempo = 120.0f;
12+
arp->rate = RATE_EIGHTH;
1213
arp->step_phase = 0.0f;
1314
arp->held_count = 0;
1415
memset(arp->held_notes, 0, sizeof(arp->held_notes));
@@ -17,6 +18,15 @@ void arpeggiator_init(Arpeggiator *arp) {
1718
arp->hold = 1;
1819
arp->octave = 0;
1920
arp->octaves = 3;
21+
22+
// Initialize chord generation parameters
23+
arp->chord_type = CHORD_MAJOR;
24+
arp->add_6 = 0;
25+
arp->add_m7 = 0;
26+
arp->add_M7 = 0;
27+
arp->add_9 = 0;
28+
arp->voicing = 0;
29+
2030
for (int i = 0; i < 16; ++i) {
2131
arp->active_arpeggiated_notes[i].active = 0;
2232
}
@@ -35,6 +45,8 @@ void arpeggiator_set_param(Arpeggiator *arp, const char *param, float value, str
3545
arp->mode = (ArpMode)((int)value);
3646
else if (!strcmp(param, "tempo"))
3747
arp->tempo = value;
48+
else if (!strcmp(param, "rate"))
49+
arp->rate = (ArpRate)((int)value);
3850
else if (!strcmp(param, "polyphonic"))
3951
arp->polyphonic = (int)value;
4052
else if (!strcmp(param, "hold")) {
@@ -49,6 +61,18 @@ void arpeggiator_set_param(Arpeggiator *arp, const char *param, float value, str
4961
arp->octave = (int)value;
5062
else if (!strcmp(param, "octaves"))
5163
arp->octaves = (int)value;
64+
else if (!strcmp(param, "chord_type"))
65+
arp->chord_type = (ChordType)((int)value);
66+
else if (!strcmp(param, "add_6"))
67+
arp->add_6 = (int)value;
68+
else if (!strcmp(param, "add_m7"))
69+
arp->add_m7 = (int)value;
70+
else if (!strcmp(param, "add_M7"))
71+
arp->add_M7 = (int)value;
72+
else if (!strcmp(param, "add_9"))
73+
arp->add_9 = (int)value;
74+
else if (!strcmp(param, "voicing"))
75+
arp->voicing = (int)value;
5276
}
5377

5478
void arpeggiator_note_on(Arpeggiator *arp, int note) {
@@ -159,6 +183,41 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
159183
} else {
160184
// Monophonic behavior (existing logic)
161185
switch (arp->mode) {
186+
case ARP_CHORD: {
187+
// Generate chord from the first held note (root)
188+
if (arp->held_count > 0) {
189+
int chord_notes[8];
190+
int note_count = arpeggiator_generate_chord(arp, notes[0], chord_notes, 8);
191+
192+
// Apply octave spread if requested
193+
int notes_to_play[8];
194+
int actual_count = 0;
195+
for (int i = 0; i < note_count && actual_count < 8; ++i) {
196+
int base_note = chord_notes[i];
197+
for (int oct = 0; oct < arp->octaves && actual_count < 8; ++oct) {
198+
notes_to_play[actual_count++] = base_note + (arp->octave * 12) + (oct * 12);
199+
}
200+
}
201+
202+
// Play all chord notes simultaneously
203+
for (int i = 0; i < actual_count; ++i) {
204+
int free_slot = -1;
205+
for (int j = 0; j < 16; ++j) {
206+
if (!arp->active_arpeggiated_notes[j].active) {
207+
free_slot = j;
208+
break;
209+
}
210+
}
211+
if (free_slot != -1) {
212+
arp->active_arpeggiated_notes[free_slot].note = notes_to_play[i];
213+
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time;
214+
arp->active_arpeggiated_notes[free_slot].active = 1;
215+
synth_note_on(synth, notes_to_play[i], 0.8f);
216+
}
217+
}
218+
}
219+
break;
220+
}
162221
case ARP_UP:
163222
qsort(notes, arp->held_count, sizeof(int), order_compare);
164223
idx = arp->step % arp->held_count;
@@ -176,38 +235,160 @@ int arpeggiator_step(Arpeggiator *arp, float samplerate, struct Synth *synth) {
176235
default:
177236
idx = 0;
178237
}
179-
int note_to_play = notes[idx];
180238

181-
// Find a free slot for the new note
182-
int free_slot = -1;
183-
for (int j = 0; j < 16; ++j) {
184-
if (!arp->active_arpeggiated_notes[j].active) {
185-
free_slot = j;
186-
break;
239+
if (arp->mode != ARP_CHORD) {
240+
int note_to_play = notes[idx];
241+
242+
// Find a free slot for the new note
243+
int free_slot = -1;
244+
for (int j = 0; j < 16; ++j) {
245+
if (!arp->active_arpeggiated_notes[j].active) {
246+
free_slot = j;
247+
break;
248+
}
249+
}
250+
if (free_slot != -1) {
251+
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
252+
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time; // Note duration is one step
253+
arp->active_arpeggiated_notes[free_slot].active = 1;
254+
synth_note_on(synth, note_to_play, 0.8f);
187255
}
188-
}
189-
if (free_slot != -1) {
190-
arp->active_arpeggiated_notes[free_slot].note = note_to_play;
191-
arp->active_arpeggiated_notes[free_slot].remaining_duration = step_time; // Note duration is one step
192-
arp->active_arpeggiated_notes[free_slot].active = 1;
193-
synth_note_on(synth, note_to_play, 0.8f);
194256
}
195257
}
196258

197259
arp->step++;
198260
return 0; // Return 0 to indicate success
199261
}
200262

263+
int arpeggiator_generate_chord(const Arpeggiator *arp, int root_note, int *chord_notes, int max_notes) {
264+
if (!chord_notes || max_notes < 8) return 0;
265+
266+
int note_count = 0;
267+
int root_in_octave = root_note % 12;
268+
int octave = root_note / 12;
269+
270+
// Basic chord tones based on chord type
271+
switch (arp->chord_type) {
272+
case CHORD_MAJOR:
273+
chord_notes[note_count++] = root_note;
274+
chord_notes[note_count++] = root_note + 4; // Major third
275+
chord_notes[note_count++] = root_note + 7; // Perfect fifth
276+
break;
277+
case CHORD_MINOR:
278+
chord_notes[note_count++] = root_note;
279+
chord_notes[note_count++] = root_note + 3; // Minor third
280+
chord_notes[note_count++] = root_note + 7; // Perfect fifth
281+
break;
282+
case CHORD_SUS:
283+
chord_notes[note_count++] = root_note;
284+
chord_notes[note_count++] = root_note + 5; // Perfect fourth
285+
chord_notes[note_count++] = root_note + 7; // Perfect fifth
286+
break;
287+
case CHORD_DIM:
288+
chord_notes[note_count++] = root_note;
289+
chord_notes[note_count++] = root_note + 3; // Minor third
290+
chord_notes[note_count++] = root_note + 6; // Diminished fifth
291+
break;
292+
}
293+
294+
// Add extensions if enabled
295+
if (arp->add_6 && note_count < max_notes) {
296+
chord_notes[note_count++] = root_note + 9; // Sixth
297+
}
298+
if (arp->add_m7 && note_count < max_notes) {
299+
chord_notes[note_count++] = root_note + 10; // Minor seventh
300+
}
301+
if (arp->add_M7 && note_count < max_notes) {
302+
chord_notes[note_count++] = root_note + 11; // Major seventh
303+
}
304+
if (arp->add_9 && note_count < max_notes) {
305+
chord_notes[note_count++] = root_note + 14; // Ninth (one octave above second)
306+
}
307+
308+
// Apply voicing/inversion if specified
309+
if (arp->voicing > 0 && note_count > 1) {
310+
int inversion = arp->voicing % note_count;
311+
for (int i = 0; i < inversion; ++i) {
312+
int bass_note = chord_notes[0];
313+
// Move bass note up one octave
314+
for (int j = 0; j < note_count - 1; ++j) {
315+
chord_notes[j] = chord_notes[j + 1];
316+
}
317+
chord_notes[note_count - 1] = bass_note + 12;
318+
}
319+
}
320+
321+
return note_count;
322+
}
323+
324+
const char *arpeggiator_rate_str(const Arpeggiator *arp) {
325+
switch (arp->rate) {
326+
case RATE_QUARTER:
327+
return "1/4";
328+
case RATE_EIGHTH:
329+
return "1/8";
330+
case RATE_SIXTEENTH:
331+
return "1/16";
332+
case RATE_THIRTYSECOND:
333+
return "1/32";
334+
default:
335+
return "-";
336+
}
337+
}
338+
339+
const char *arpeggiator_chord_str(const Arpeggiator *arp) {
340+
switch (arp->chord_type) {
341+
case CHORD_MAJOR:
342+
return "MAJ";
343+
case CHORD_MINOR:
344+
return "MIN";
345+
case CHORD_SUS:
346+
return "SUS";
347+
case CHORD_DIM:
348+
return "DIM";
349+
default:
350+
return "-";
351+
}
352+
}
353+
201354
const char *arpeggiator_mode_str(const Arpeggiator *arp) {
202355
switch (arp->mode) {
356+
case ARP_OFF:
357+
return "OFF";
358+
case ARP_CHORD:
359+
return "CHORD";
203360
case ARP_UP:
204361
return "UP";
205362
case ARP_DOWN:
206363
return "DOWN";
207-
case ARP_ORDER:
208-
return "ORDER";
364+
case ARP_UP_DOWN:
365+
return "UP_DOWN";
366+
case ARP_PENDULUM:
367+
return "PENDULUM";
368+
case ARP_CONVERGE:
369+
return "CONVERGE";
370+
case ARP_DIVERGE:
371+
return "DIVERGE";
372+
case ARP_LEAPFROG:
373+
return "LEAPFROG";
374+
case ARP_THUMB_UP:
375+
return "THUMB_UP";
376+
case ARP_THUMB_DOWN:
377+
return "THUMB_DOWN";
378+
case ARP_PINKY_UP:
379+
return "PINKY_UP";
380+
case ARP_PINKY_DOWN:
381+
return "PINKY_DOWN";
382+
case ARP_REPEAT:
383+
return "REPEAT";
209384
case ARP_RANDOM:
210385
return "RANDOM";
386+
case ARP_RANDOM_WALK:
387+
return "RANDOM_WALK";
388+
case ARP_SHUFFLE:
389+
return "SHUFFLE";
390+
case ARP_ORDER:
391+
return "ORDER";
211392
default:
212393
return "-";
213394
}

src/synth.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,13 @@ int synth_init(Synth *synth, int samplerate, int buffer_size, int voices) {
251251
return 1;
252252
}
253253

254-
void synth_shutdown(Synth *synth) { midi_shutdown(&synth->midi); }
254+
void synth_shutdown(Synth *synth) {
255+
#ifndef __EMSCRIPTEN__
256+
// Save current settings to default config before shutdown
257+
synth_save_default_config(synth);
258+
#endif
259+
midi_shutdown(&synth->midi);
260+
}
255261

256262
void synth_set_bpm(Synth *synth, float bpm) {
257263
fx_set_bpm(&synth->fx, bpm);

0 commit comments

Comments
 (0)