From 9e3d75536d485acd19824b024e39138465fa09a0 Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Mon, 4 May 2026 21:51:48 +0000 Subject: [PATCH] fix: write test audio to shared buffer in JACK callback instead of directly to port Co-authored-by: aider (deepseek/deepseek-coder) --- test_audio_routing.c | 93 +++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/test_audio_routing.c b/test_audio_routing.c index c38f0d2..f65689c 100644 --- a/test_audio_routing.c +++ b/test_audio_routing.c @@ -41,7 +41,16 @@ static atomic_bool test_timeout; static atomic_int tests_passed; static atomic_int tests_failed; -// Test audio buffers (reduced size) +// Shared buffers for test audio input (written by test thread, read by JACK callback) +static float test_audio_input[MAX_CHANNELS][TEST_NFRAMES * TEST_DURATION_SECONDS]; +static atomic_size_t test_audio_input_count[MAX_CHANNELS]; +static atomic_size_t test_audio_input_read[MAX_CHANNELS]; + +// Shared MIDI buffer (written by test thread, read by JACK callback) +static uint8_t test_midi_buffer[3]; // status, note, velocity +static atomic_bool test_midi_pending; + +// Test audio output buffers (written by JACK callback, read by test thread) static float test_output_buffer[MAX_CHANNELS][TEST_NFRAMES * TEST_DURATION_SECONDS]; static atomic_size_t test_output_count[MAX_CHANNELS]; @@ -67,6 +76,24 @@ static int test_process_callback(jack_nframes_t nframes, void *arg) { (void)arg; if (!atomic_load(&test_running)) return 0; + // Write test audio to output ports (to be sent to looper) + for (int ch = 0; ch < MAX_CHANNELS; ch++) { + jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) + jack_port_get_buffer(test_audio_out[ch], nframes); + + size_t read_pos = atomic_load(&test_audio_input_read[ch]); + size_t count = atomic_load(&test_audio_input_count[ch]); + + for (jack_nframes_t i = 0; i < nframes; i++) { + if (read_pos + i < count) { + out[i] = test_audio_input[ch][read_pos + i]; + } else { + out[i] = 0.0f; + } + } + atomic_store(&test_audio_input_read[ch], read_pos + nframes); + } + // Read audio from looper outputs for (int ch = 0; ch < MAX_CHANNELS; ch++) { jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) @@ -81,6 +108,17 @@ static int test_process_callback(jack_nframes_t nframes, void *arg) { } } + // Send pending MIDI message to looper + if (atomic_exchange(&test_midi_pending, false)) { + void *midi_out_buf = jack_port_get_buffer(test_midi_out, nframes); + jack_midi_clear_buffer(midi_out_buf); + uint8_t msg[3]; + msg[0] = test_midi_buffer[0]; + msg[1] = test_midi_buffer[1]; + msg[2] = test_midi_buffer[2]; + jack_midi_event_write(midi_out_buf, 0, msg, 3); + } + // Read MIDI from looper void *midi_buf = jack_port_get_buffer(test_midi_in, nframes); jack_midi_event_t event; @@ -203,51 +241,32 @@ static void disconnect_test_client(void) { static void send_sine_wave(int channel, float frequency, float amplitude, jack_nframes_t duration) { if (!test_client) return; + if (atomic_load(&test_timeout)) return; jack_nframes_t sample_rate = jack_get_sample_rate(test_client); jack_nframes_t total_samples = (sample_rate * duration) / 1000; - jack_nframes_t processed = 0; - struct timeval start_time; - gettimeofday(&start_time, NULL); - - while (processed < total_samples && atomic_load(&test_running) && !atomic_load(&test_timeout)) { - // Check for timeout - struct timeval now; - gettimeofday(&now, NULL); - double elapsed = (now.tv_sec - start_time.tv_sec) + - (now.tv_usec - start_time.tv_usec) / 1000000.0; - if (elapsed > TEST_TIMEOUT_SECONDS) { - fprintf(stderr, "TIMEOUT: send_sine_wave exceeded %d seconds\n", TEST_TIMEOUT_SECONDS); - atomic_store(&test_timeout, true); - break; - } - - jack_nframes_t nframes = (total_samples - processed < TEST_NFRAMES) - ? (total_samples - processed) : TEST_NFRAMES; - - jack_default_audio_sample_t *buf = (jack_default_audio_sample_t *) - jack_port_get_buffer(test_audio_out[channel], nframes); - - for (jack_nframes_t i = 0; i < nframes; i++) { - buf[i] = amplitude * sinf(2.0 * M_PI * frequency * (processed + i) / sample_rate); - } - - processed += nframes; - usleep(50000); // 50ms delay to reduce CPU usage + // Fill the shared input buffer + for (jack_nframes_t i = 0; i < total_samples; i++) { + test_audio_input[channel][i] = amplitude * sinf(2.0 * M_PI * frequency * i / sample_rate); } + atomic_store(&test_audio_input_count[channel], total_samples); + atomic_store(&test_audio_input_read[channel], 0); + + // Wait for the JACK callback to consume the audio + // (the callback runs in a separate thread) + usleep(duration * 1000 + 100000); // duration ms + 100ms extra } static void send_midi_note(int note, int velocity) { if (!test_client) return; if (atomic_load(&test_timeout)) return; - jack_nframes_t nframes = TEST_NFRAMES; - void *buf = jack_port_get_buffer(test_midi_out, nframes); - jack_midi_clear_buffer(buf); - - uint8_t msg[3] = {0x90, note & 0x7F, velocity & 0x7F}; - jack_midi_event_write(buf, 0, msg, 3); + // Store MIDI message for the JACK callback to send + test_midi_buffer[0] = 0x90; // Note On, channel 0 + test_midi_buffer[1] = note & 0x7F; + test_midi_buffer[2] = velocity & 0x7F; + atomic_store(&test_midi_pending, true); usleep(100000); // 100ms to let JACK process } @@ -267,7 +286,11 @@ static void clear_output_buffers(void) { for (int ch = 0; ch < MAX_CHANNELS; ch++) { atomic_store(&test_output_count[ch], 0); memset(test_output_buffer[ch], 0, sizeof(test_output_buffer[ch])); + atomic_store(&test_audio_input_count[ch], 0); + atomic_store(&test_audio_input_read[ch], 0); + memset(test_audio_input[ch], 0, sizeof(test_audio_input[ch])); } + atomic_store(&test_midi_pending, false); } // ============================================================