fix: write test audio to shared buffer in JACK callback instead of directly to port
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
@@ -41,7 +41,16 @@ static atomic_bool test_timeout;
|
|||||||
static atomic_int tests_passed;
|
static atomic_int tests_passed;
|
||||||
static atomic_int tests_failed;
|
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 float test_output_buffer[MAX_CHANNELS][TEST_NFRAMES * TEST_DURATION_SECONDS];
|
||||||
static atomic_size_t test_output_count[MAX_CHANNELS];
|
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;
|
(void)arg;
|
||||||
if (!atomic_load(&test_running)) return 0;
|
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
|
// Read audio from looper outputs
|
||||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||||
jack_default_audio_sample_t *in = (jack_default_audio_sample_t *)
|
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
|
// Read MIDI from looper
|
||||||
void *midi_buf = jack_port_get_buffer(test_midi_in, nframes);
|
void *midi_buf = jack_port_get_buffer(test_midi_in, nframes);
|
||||||
jack_midi_event_t event;
|
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) {
|
static void send_sine_wave(int channel, float frequency, float amplitude, jack_nframes_t duration) {
|
||||||
if (!test_client) return;
|
if (!test_client) return;
|
||||||
|
if (atomic_load(&test_timeout)) return;
|
||||||
|
|
||||||
jack_nframes_t sample_rate = jack_get_sample_rate(test_client);
|
jack_nframes_t sample_rate = jack_get_sample_rate(test_client);
|
||||||
jack_nframes_t total_samples = (sample_rate * duration) / 1000;
|
jack_nframes_t total_samples = (sample_rate * duration) / 1000;
|
||||||
jack_nframes_t processed = 0;
|
|
||||||
|
|
||||||
struct timeval start_time;
|
// Fill the shared input buffer
|
||||||
gettimeofday(&start_time, NULL);
|
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);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
atomic_store(&test_audio_input_count[channel], total_samples);
|
||||||
|
atomic_store(&test_audio_input_read[channel], 0);
|
||||||
|
|
||||||
jack_nframes_t nframes = (total_samples - processed < TEST_NFRAMES)
|
// Wait for the JACK callback to consume the audio
|
||||||
? (total_samples - processed) : TEST_NFRAMES;
|
// (the callback runs in a separate thread)
|
||||||
|
usleep(duration * 1000 + 100000); // duration ms + 100ms extra
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_midi_note(int note, int velocity) {
|
static void send_midi_note(int note, int velocity) {
|
||||||
if (!test_client) return;
|
if (!test_client) return;
|
||||||
if (atomic_load(&test_timeout)) return;
|
if (atomic_load(&test_timeout)) return;
|
||||||
|
|
||||||
jack_nframes_t nframes = TEST_NFRAMES;
|
// Store MIDI message for the JACK callback to send
|
||||||
void *buf = jack_port_get_buffer(test_midi_out, nframes);
|
test_midi_buffer[0] = 0x90; // Note On, channel 0
|
||||||
jack_midi_clear_buffer(buf);
|
test_midi_buffer[1] = note & 0x7F;
|
||||||
|
test_midi_buffer[2] = velocity & 0x7F;
|
||||||
uint8_t msg[3] = {0x90, note & 0x7F, velocity & 0x7F};
|
atomic_store(&test_midi_pending, true);
|
||||||
jack_midi_event_write(buf, 0, msg, 3);
|
|
||||||
|
|
||||||
usleep(100000); // 100ms to let JACK process
|
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++) {
|
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||||
atomic_store(&test_output_count[ch], 0);
|
atomic_store(&test_output_count[ch], 0);
|
||||||
memset(test_output_buffer[ch], 0, sizeof(test_output_buffer[ch]));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user