/* * test_audio_routing.c * * External test program for the looper application. * Connects to JACK, sends a test tone to the application's audio_in ports, * captures the output from audio_out ports, and verifies that audio is flowing. * * Compile with: * gcc -o test_audio_routing test_audio_routing.c -ljack -lm * * Run after the looper application is running and JACK is active. */ #include #include #include #include #include #include #define TEST_DURATION_SECONDS 2 #define SAMPLE_RATE 48000 #define NUM_CHANNELS 2 // test first two channels static jack_client_t *client = NULL; static jack_port_t *test_out_ports[NUM_CHANNELS]; static jack_port_t *test_in_ports[NUM_CHANNELS]; static float *captured_output[NUM_CHANNELS]; static jack_nframes_t captured_frames = 0; static volatile int running = 1; /* Generate a sine wave sample */ static float sine_sample(jack_nframes_t frame, float freq, float sample_rate) { return sinf(2.0f * M_PI * freq * frame / sample_rate); } /* Process callback: generate test tone on output ports, capture input ports */ static int process(jack_nframes_t nframes, void *arg) { (void)arg; /* Generate test tone on each output port */ for (int ch = 0; ch < NUM_CHANNELS; ch++) { jack_default_audio_sample_t *out = (jack_default_audio_sample_t *)jack_port_get_buffer(test_out_ports[ch], nframes); for (jack_nframes_t i = 0; i < nframes; i++) { /* 440 Hz sine wave, amplitude 0.5 */ out[i] = 0.5f * sine_sample(captured_frames + i, 440.0f, SAMPLE_RATE); } } /* Capture input ports */ for (int ch = 0; ch < NUM_CHANNELS; ch++) { jack_default_audio_sample_t *in = (jack_default_audio_sample_t *)jack_port_get_buffer(test_in_ports[ch], nframes); /* Append to captured buffer */ if (captured_output[ch]) { memcpy(captured_output[ch] + captured_frames, in, nframes * sizeof(float)); } } captured_frames += nframes; return 0; } /* Shutdown callback */ static void shutdown(void *arg) { (void)arg; running = 0; } int main(void) { jack_status_t status; const char *client_name = "test_audio_routing"; client = jack_client_open(client_name, JackNullOption, &status, NULL); if (!client) { fprintf(stderr, "Failed to open JACK client, status = 0x%2.0x\n", status); return 1; } /* Register ports */ char port_name[64]; for (int ch = 0; ch < NUM_CHANNELS; ch++) { snprintf(port_name, sizeof(port_name), "test_out_%d", ch); test_out_ports[ch] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (!test_out_ports[ch]) { fprintf(stderr, "Failed to register output port %d\n", ch); jack_client_close(client); return 1; } snprintf(port_name, sizeof(port_name), "test_in_%d", ch); test_in_ports[ch] = jack_port_register(client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (!test_in_ports[ch]) { fprintf(stderr, "Failed to register input port %d\n", ch); jack_client_close(client); return 1; } } /* Set process callback */ jack_set_process_callback(client, process, NULL); jack_on_shutdown(client, shutdown, NULL); /* Allocate capture buffers */ jack_nframes_t sample_rate = jack_get_sample_rate(client); jack_nframes_t total_frames = sample_rate * TEST_DURATION_SECONDS; for (int ch = 0; ch < NUM_CHANNELS; ch++) { captured_output[ch] = (float *)calloc(total_frames, sizeof(float)); if (!captured_output[ch]) { fprintf(stderr, "Memory allocation failed\n"); jack_client_close(client); return 1; } } /* Activate client */ if (jack_activate(client) != 0) { fprintf(stderr, "Failed to activate JACK client\n"); jack_client_close(client); return 1; } /* Connect to the looper application's ports */ const char *app_client_name = "looper"; /* adjust if your app uses a different name */ for (int ch = 0; ch < NUM_CHANNELS; ch++) { char src_port[128], dst_port[128]; snprintf(src_port, sizeof(src_port), "%s:test_out_%d", client_name, ch); snprintf(dst_port, sizeof(dst_port), "%s:audio_in_%d", app_client_name, ch); if (jack_connect(client, src_port, dst_port) != 0) { fprintf(stderr, "Warning: could not connect %s -> %s\n", src_port, dst_port); } snprintf(src_port, sizeof(src_port), "%s:audio_out_%d", app_client_name, ch); snprintf(dst_port, sizeof(dst_port), "%s:test_in_%d", client_name, ch); if (jack_connect(client, src_port, dst_port) != 0) { fprintf(stderr, "Warning: could not connect %s -> %s\n", src_port, dst_port); } } /* Run for the test duration */ printf("Running test for %d seconds...\n", TEST_DURATION_SECONDS); sleep(TEST_DURATION_SECONDS); /* Deactivate and close */ jack_deactivate(client); jack_client_close(client); /* Analyze captured output */ int pass = 1; for (int ch = 0; ch < NUM_CHANNELS; ch++) { float max_val = 0.0f; for (jack_nframes_t i = 0; i < total_frames; i++) { float abs_val = fabsf(captured_output[ch][i]); if (abs_val > max_val) max_val = abs_val; } printf("Channel %d: max output amplitude = %f\n", ch, max_val); if (max_val < 0.001f) { printf("FAIL: Channel %d output is near zero (no audio flowing)\n", ch); pass = 0; } } /* Cleanup */ for (int ch = 0; ch < NUM_CHANNELS; ch++) { free(captured_output[ch]); } if (pass) { printf("PASS: Audio routing is working correctly.\n"); return 0; } else { printf("FAIL: Audio routing test failed.\n"); return 1; } }