#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_backend.h" #include #include #include #include #include #include /* Optional live test. The only route is xtc's output -> this test's input. * No ports are connected to hardware. Never changes server configuration. */ typedef struct { jack_port_t *input; unsigned int blocks, bad, low, high, lost; } probe_state; static int probe_process(jack_nframes_t frames, void *context) { probe_state *probe = context; const float *input = jack_port_get_buffer(probe->input, frames); jack_nframes_t frame; float peak = 0.0f; if (input == NULL) { __atomic_store_n(&probe->bad, 1U, __ATOMIC_RELAXED); return 0; } for (frame = 0U; frame < frames; ++frame) { float sample = input[frame]; if (!isfinite(sample) || fabsf(sample) > 0.201f) __atomic_store_n(&probe->bad, 1U, __ATOMIC_RELAXED); if (fabsf(sample) > peak) peak = fabsf(sample); } if (peak > 0.09f && peak < 0.101f) __atomic_store_n(&probe->low, 1U, __ATOMIC_RELAXED); if (peak > 0.19f) __atomic_store_n(&probe->high, 1U, __ATOMIC_RELAXED); (void)__atomic_fetch_add(&probe->blocks, 1U, __ATOMIC_RELAXED); return 0; } static void probe_shutdown(void *context) { probe_state *probe = context; __atomic_store_n(&probe->lost, 1U, __ATOMIC_RELEASE); } static xtc_result tone(float gain, xtc_graph **out_graph) { xtc_result result; size_t osc, level, output; result = xtc_graph_create(3U, out_graph); if (result != XTC_OK) return result; result = xtc_graph_add_oscillator(*out_graph, 440.0f, &osc); if (result == XTC_OK) result = xtc_graph_add_gain(*out_graph, gain, &level); if (result == XTC_OK) result = xtc_graph_add_output(*out_graph, &output); if (result == XTC_OK) result = xtc_graph_connect(*out_graph, osc, level); if (result == XTC_OK) result = xtc_graph_connect(*out_graph, level, output); return result; } static int count_port(const char *name, void *context) { (void)name; ++*(size_t *)context; return 0; } #define REQUIRE(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); goto cleanup; } } while (0) int main(int argc, char **argv) { long seconds = 5; char *end; char name[64]; xtc_backend_config config = {XTC_BACKEND_JACK, name, 4096U, 1U}; xtc_backend *backend = NULL; xtc_backend_status status; xtc_graph *low = NULL, *high = NULL; jack_client_t *probe_client = NULL; jack_status_t jack_status = 0; probe_state probe = {0}; struct timespec pause = {0, 50000000L}; size_t port_count = 0U; long tick; int result = EXIT_FAILURE; if (argc > 2) { fprintf(stderr,"Usage: %s [seconds: 1..300]\n",argv[0]); return EXIT_FAILURE; } if (argc == 2) { seconds = strtol(argv[1], &end, 10); if (*end != '\0' || seconds < 1 || seconds > 300) return EXIT_FAILURE; } (void)snprintf(name, sizeof(name), "xtc-test-%ld", (long)getpid()); REQUIRE(xtc_backend_open(&config, &backend) == XTC_OK); REQUIRE(xtc_backend_get_status(backend, &status) == XTC_OK); printf("JACK: %u Hz, %u frames, mono, max 4096 frames\n", status.sample_rate, status.buffer_frames); REQUIRE(xtc_backend_visit_ports(backend, XTC_PORT_INPUT, count_port, &port_count) == XTC_OK); REQUIRE(port_count > 0U); REQUIRE(tone(0.1f, &low) == XTC_OK && tone(0.2f, &high) == XTC_OK); probe_client = jack_client_open("xtc-test-probe", JackNoStartServer, &jack_status); REQUIRE(probe_client != NULL); probe.input = jack_port_register(probe_client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0U); REQUIRE(probe.input != NULL); REQUIRE(jack_set_process_callback(probe_client, probe_process, &probe) == 0); jack_on_shutdown(probe_client, probe_shutdown, &probe); REQUIRE(jack_activate(probe_client) == 0); REQUIRE(xtc_backend_submit(backend, low) == XTC_OK); REQUIRE(xtc_backend_start(backend) == XTC_OK); REQUIRE(xtc_backend_connect(backend, XTC_PORT_OUTPUT, jack_port_name(probe.input)) == XTC_OK); for (tick = 0; tick < seconds * 20; ++tick) { (void)nanosleep(&pause, NULL); REQUIRE(!__atomic_load_n(&probe.lost, __ATOMIC_ACQUIRE)); REQUIRE(xtc_backend_get_status(backend, &status) == XTC_OK); REQUIRE(status.running && !status.sample_rate_changed && status.process_result == XTC_OK); xtc_backend_collect(backend); if (tick % 8 == 7) { xtc_result submitted = xtc_backend_submit(backend, tick % 16 == 7 ? high : low); REQUIRE(submitted == XTC_OK || submitted == XTC_ERR_CAPACITY); } } REQUIRE(__atomic_load_n(&probe.bad, __ATOMIC_RELAXED) == 0U); REQUIRE(__atomic_load_n(&probe.low, __ATOMIC_RELAXED) && __atomic_load_n(&probe.high, __ATOMIC_RELAXED)); REQUIRE(status.processed_blocks > 0U); REQUIRE(xtc_backend_disconnect(backend, XTC_PORT_OUTPUT, jack_port_name(probe.input)) == XTC_OK); for (tick = 0; tick < 10; ++tick) { REQUIRE(xtc_backend_stop(backend) == XTC_OK); REQUIRE(xtc_backend_start(backend) == XTC_OK); (void)nanosleep(&pause, NULL); } REQUIRE(xtc_backend_stop(backend) == XTC_OK); printf("PASS: %ld seconds, %u engine blocks, %u captured blocks, both gains observed, %u xruns; 10 restart cycles\n", seconds, status.processed_blocks, __atomic_load_n(&probe.blocks, __ATOMIC_RELAXED), status.xruns); result = EXIT_SUCCESS; cleanup: if (xtc_backend_close(backend) != XTC_OK) { fprintf(stderr,"JACK client close failed\n"); result = EXIT_FAILURE; } if (probe_client != NULL && jack_client_close(probe_client) != 0) result = EXIT_FAILURE; xtc_graph_destroy(low); xtc_graph_destroy(high); return result; }