11#include "app.h"
22#include <stdio.h>
33#include <string.h>
4+ #include <stdlib.h>
45#include "oscilloscope.h"
56#include "gui.h"
67#include "arpeggiator.h"
78
9+ static App * g_app_for_toggle = NULL ;
10+ static void toggle_chord_progression (void );
11+ static void generate_random_chord_progression (App * app );
12+ static void play_chord (App * app , int root_note , float velocity );
13+ static void stop_active_notes (App * app );
14+
815int app_init (App * app ) {
916
1017 memset (app , 0 , sizeof (App ));
@@ -15,6 +22,19 @@ int app_init(App *app) {
1522 app -> frame_count = 0 ;
1623 app -> fps = 0.0f ;
1724
25+ // Initialize chord progression
26+ app -> chord_progression_enabled = 0 ;
27+ app -> current_chord_index = 0 ;
28+ app -> chord_progression_timer = 0.0f ;
29+ app -> chord_duration = 2.0f ;
30+ app -> chord_progression_length = 0 ;
31+ app -> rhythm_pattern_length = 0 ;
32+ app -> current_rhythm_step = 0 ;
33+ app -> rhythm_step_timer = 0.0f ;
34+ app -> active_chord_note_count = 0 ;
35+ app -> humanize_velocity_amount = 0.15f ;
36+ app -> humanize_timing_amount = 0.03f ;
37+ app -> bpm = 120.0f ;
1838
1939 if (SDL_Init (SDL_INIT_AUDIO | SDL_INIT_VIDEO ) < 0 ) {
2040 return 0 ;
@@ -66,6 +86,8 @@ int app_init(App *app) {
6686 }
6787
6888 gui_init (app -> window , app -> gl_context );
89+ g_app_for_toggle = app ;
90+ gui_set_humanize_vars (& app -> chord_progression_enabled , & app -> humanize_velocity_amount , & app -> humanize_timing_amount , & app -> bpm , toggle_chord_progression , & app -> synth );
6991
7092 oscilloscope_init ();
7193
@@ -149,6 +171,103 @@ static void handle_keyboard_note(App *app, SDL_Keycode key, int is_down) {
149171 }
150172}
151173
174+ static void toggle_chord_progression (void ) {
175+ if (g_app_for_toggle ) {
176+ g_app_for_toggle -> chord_progression_enabled = !g_app_for_toggle -> chord_progression_enabled ;
177+ if (g_app_for_toggle -> chord_progression_enabled ) {
178+ generate_random_chord_progression (g_app_for_toggle );
179+ play_chord (g_app_for_toggle , g_app_for_toggle -> chord_progression [0 ],
180+ g_app_for_toggle -> rhythm_pattern [0 ]);
181+ } else {
182+ stop_active_notes (g_app_for_toggle );
183+ }
184+ }
185+ }
186+
187+ static void generate_random_chord_progression (App * app ) {
188+ int scale [] = {0 , 2 , 4 , 5 , 7 , 9 , 11 };
189+ int progression_patterns [][8 ] = {
190+ {0 , 5 , 3 , 4 , 2 , 6 , 1 , 0 },
191+ {0 , 3 , 5 , 4 , 2 , 1 , 6 , 5 },
192+ {0 , 4 , 5 , 3 , 1 , 6 , 2 , 0 },
193+ {0 , 5 , 3 , 4 , 0 , 5 , 3 , 6 },
194+ {0 , 3 , 4 , 5 , 1 , 2 , 6 , 0 },
195+ {0 , 4 , 3 , 5 , 6 , 1 , 2 , 4 },
196+ {0 , 6 , 3 , 5 , 4 , 1 , 2 , 0 },
197+ {0 , 2 , 5 , 3 , 6 , 4 , 1 , 0 },
198+ };
199+
200+ int pattern_idx = rand () % 8 ;
201+ int root = 48 + (rand () % 12 );
202+
203+ app -> chord_progression_length = 8 ;
204+ for (int i = 0 ; i < 8 ; i ++ ) {
205+ app -> chord_progression [i ] = root + scale [progression_patterns [pattern_idx ][i ]];
206+ }
207+
208+ // Generate 128-step rhythm pattern by repeating 16-step patterns
209+ float base_patterns [][16 ] = {
210+ {1.0f , 0.0f , 0.5f , 0.0f , 0.75f , 0.0f , 0.5f , 0.0f , 1.0f , 0.0f , 0.5f , 0.0f , 0.75f , 0.0f , 0.5f , 0.0f },
211+ {1.0f , 0.0f , 0.0f , 0.5f , 0.0f , 0.75f , 0.5f , 0.0f , 1.0f , 0.0f , 0.5f , 0.0f , 0.75f , 0.5f , 0.0f , 0.0f },
212+ {1.0f , 0.5f , 0.5f , 0.0f , 0.75f , 0.0f , 0.5f , 0.5f , 1.0f , 0.0f , 0.5f , 0.5f , 0.75f , 0.0f , 0.5f , 0.0f },
213+ {1.0f , 0.0f , 0.0f , 0.0f , 0.5f , 0.0f , 0.75f , 0.0f , 1.0f , 0.0f , 0.5f , 0.0f , 0.5f , 0.75f , 0.0f , 0.0f },
214+ {1.0f , 0.5f , 0.0f , 0.5f , 0.75f , 0.5f , 0.0f , 0.5f , 1.0f , 0.5f , 0.0f , 0.5f , 0.75f , 0.5f , 0.0f , 0.5f },
215+ };
216+
217+ int rhythm_idx = rand () % 5 ;
218+ app -> rhythm_pattern_length = 128 ;
219+
220+ // Fill 128 steps by repeating the 16-step pattern 8 times
221+ for (int i = 0 ; i < 128 ; i ++ ) {
222+ app -> rhythm_pattern [i ] = base_patterns [rhythm_idx ][i % 16 ];
223+ }
224+
225+ app -> current_chord_index = 0 ;
226+ app -> chord_progression_timer = 0.0f ;
227+ app -> current_rhythm_step = 0 ;
228+ app -> rhythm_step_timer = 0.0f ;
229+ }
230+
231+ static float humanize_velocity (float velocity , float amount ) {
232+ float random = ((float )rand () / RAND_MAX ) * 2.0f - 1.0f ;
233+ velocity += random * amount ;
234+ if (velocity < 0.1f ) velocity = 0.1f ;
235+ if (velocity > 1.0f ) velocity = 1.0f ;
236+ return velocity ;
237+ }
238+
239+ static void stop_active_notes (App * app ) {
240+ for (int i = 0 ; i < app -> active_chord_note_count ; i ++ ) {
241+ synth_note_off (& app -> synth , app -> active_chord_notes [i ]);
242+ }
243+ app -> active_chord_note_count = 0 ;
244+ }
245+
246+ static void play_chord (App * app , int root_note , float velocity ) {
247+ if (velocity <= 0.0f ) {
248+ return ;
249+ }
250+
251+ velocity = humanize_velocity (velocity , app -> humanize_velocity_amount );
252+
253+ int chord_notes [8 ];
254+ chord_notes [0 ] = root_note ;
255+ chord_notes [1 ] = root_note + 3 ;
256+ chord_notes [2 ] = root_note + 4 ;
257+ chord_notes [3 ] = root_note + 7 ;
258+ chord_notes [4 ] = root_note + 10 ;
259+ chord_notes [5 ] = root_note + 12 ;
260+ chord_notes [6 ] = root_note + 15 ;
261+ chord_notes [7 ] = root_note + 16 ;
262+
263+ app -> active_chord_note_count = 0 ;
264+ for (int i = 0 ; i < 8 ; i ++ ) {
265+ float note_velocity = humanize_velocity (velocity , app -> humanize_velocity_amount );
266+ synth_note_on (& app -> synth , chord_notes [i ], note_velocity );
267+ app -> active_chord_notes [app -> active_chord_note_count ++ ] = chord_notes [i ];
268+ }
269+ }
270+
152271void app_poll_events (App * app ) {
153272 SDL_Event e ;
154273 int event_count = 0 ;
@@ -160,7 +279,13 @@ void app_poll_events(App *app) {
160279 }
161280 if (e .type == SDL_KEYDOWN ) {
162281 if (e .key .keysym .sym == SDLK_F1 ) {
163- app -> show_help = !app -> show_help ;
282+ app -> chord_progression_enabled = !app -> chord_progression_enabled ;
283+ if (app -> chord_progression_enabled ) {
284+ generate_random_chord_progression (app );
285+ play_chord (app , app -> chord_progression [0 ], app -> rhythm_pattern [0 ]);
286+ } else {
287+ stop_active_notes (app );
288+ }
164289 } else if (app -> show_help ) {
165290 app -> show_help = 0 ;
166291 }
@@ -184,6 +309,37 @@ void app_poll_events(App *app) {
184309
185310void app_update (App * app ) {
186311
312+ // Update chord progression with rhythm
313+ if (app -> chord_progression_enabled && app -> chord_progression_length > 0 && app -> rhythm_pattern_length > 0 ) {
314+ float delta_time = app -> target_frame_time / 1000.0f ;
315+ float seconds_per_beat = 60.0f / app -> bpm ;
316+ float step_duration = seconds_per_beat * 4.0f / app -> rhythm_pattern_length ;
317+
318+ float timing_variation = ((float )rand () / RAND_MAX - 0.5f ) * 2.0f * app -> humanize_timing_amount ;
319+ step_duration += timing_variation ;
320+ if (step_duration < 0.01f ) step_duration = 0.01f ;
321+
322+ app -> rhythm_step_timer += delta_time ;
323+
324+ if (app -> rhythm_step_timer >= step_duration ) {
325+ app -> rhythm_step_timer -= step_duration ;
326+
327+ stop_active_notes (app );
328+
329+ app -> current_rhythm_step = (app -> current_rhythm_step + 1 ) % app -> rhythm_pattern_length ;
330+
331+ if (app -> current_rhythm_step == 0 ) {
332+ app -> current_chord_index = (app -> current_chord_index + 1 ) % app -> chord_progression_length ;
333+ }
334+
335+ float velocity = app -> rhythm_pattern [app -> current_rhythm_step ];
336+ play_chord (app , app -> chord_progression [app -> current_chord_index ], velocity );
337+ }
338+ }
339+
340+ // Sync FX BPM with chord progression BPM
341+ synth_set_bpm (& app -> synth , app -> bpm );
342+
187343 // Calculate FPS
188344 app -> frame_count ++ ;
189345 Uint32 current_time = SDL_GetTicks ();
@@ -213,7 +369,10 @@ void app_update(App *app) {
213369}
214370
215371void app_render (App * app ) {
216- gui_draw (& app -> synth , app -> window , app -> gl_context );
372+ gui_draw (& app -> synth , app -> window , app -> gl_context ,
373+ app -> chord_progression_enabled , app -> current_chord_index ,
374+ app -> chord_progression_length , app -> current_rhythm_step ,
375+ app -> rhythm_pattern_length );
217376 SDL_GL_SwapWindow (app -> window );
218377}
219378
0 commit comments