2026-10-27 00:31:03 +01:00
|
|
|
#define _POSIX_C_SOURCE 200809L
|
2026-10-26 23:32:04 +01:00
|
|
|
/* JACK ABI fake: deterministic fault injection, linked without libjack.
|
|
|
|
|
* Processing is driven synchronously here; realtime concurrency is tested
|
|
|
|
|
* separately by test_realtime. No fake header replaces installed declarations. */
|
|
|
|
|
#include "xtc/xtc_backend.h"
|
2026-10-27 01:06:57 +01:00
|
|
|
#include "xtc/xtc_mixer.h"
|
2026-10-26 23:32:04 +01:00
|
|
|
#include <jack/jack.h>
|
2026-10-27 00:53:41 +01:00
|
|
|
#include <jack/midiport.h>
|
2026-10-26 23:32:04 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
2026-10-27 00:31:03 +01:00
|
|
|
#include <unistd.h>
|
2026-10-26 23:32:04 +01:00
|
|
|
#define CHECK(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); return 1; } } while (0)
|
|
|
|
|
static JackProcessCallback process_cb;
|
2026-10-27 00:53:41 +01:00
|
|
|
static JackPortConnectCallback connection_cb;
|
2026-10-26 23:32:04 +01:00
|
|
|
static JackSampleRateCallback rate_cb;
|
|
|
|
|
static JackBufferSizeCallback size_cb;
|
|
|
|
|
static JackXRunCallback xrun_cb;
|
|
|
|
|
static JackShutdownCallback shutdown_cb;
|
|
|
|
|
static void *context;
|
2026-10-27 01:06:57 +01:00
|
|
|
static float input[128], output[128], output_right[128];
|
|
|
|
|
static int client_token, input_token, output_token, right_token, midi_token;
|
2026-10-27 00:53:41 +01:00
|
|
|
static unsigned int midi_count;
|
|
|
|
|
static jack_midi_event_t midi_events[256];
|
2026-10-26 23:32:04 +01:00
|
|
|
static int open_fail, register_fail, callback_fail, activate_fail, deactivate_fail, close_fail;
|
|
|
|
|
static int active, closed, port_calls, null_buffer;
|
|
|
|
|
static unsigned int server_frames = 32U;
|
|
|
|
|
jack_client_t *jack_client_open(const char *name, jack_options_t options, jack_status_t *status, ...)
|
|
|
|
|
{
|
|
|
|
|
(void)name; (void)status;
|
|
|
|
|
if (options != JackNoStartServer || open_fail) return NULL;
|
|
|
|
|
port_calls = 0;
|
|
|
|
|
process_cb = NULL; rate_cb = NULL; size_cb = NULL; xrun_cb = NULL; shutdown_cb = NULL;
|
|
|
|
|
return (jack_client_t *)&client_token;
|
|
|
|
|
}
|
|
|
|
|
int jack_client_close(jack_client_t *client)
|
|
|
|
|
{ (void)client; if (close_fail) return -1; active = 0; ++closed; return 0; }
|
|
|
|
|
int jack_client_name_size(void) { return 64; }
|
|
|
|
|
jack_nframes_t jack_get_sample_rate(jack_client_t *client) { (void)client; return 48000U; }
|
|
|
|
|
jack_nframes_t jack_get_buffer_size(jack_client_t *client) { (void)client; return server_frames; }
|
|
|
|
|
jack_port_t *jack_port_register(jack_client_t *client, const char *name, const char *type,
|
|
|
|
|
unsigned long flags, unsigned long buffer_size)
|
|
|
|
|
{
|
|
|
|
|
(void)client; (void)name; (void)type; (void)buffer_size;
|
|
|
|
|
++port_calls;
|
|
|
|
|
if (register_fail == port_calls) return NULL;
|
2026-10-27 01:06:57 +01:00
|
|
|
if (!strcmp(name, "output_right")) return (jack_port_t *)&right_token;
|
2026-10-27 00:53:41 +01:00
|
|
|
if (!strcmp(type, JACK_DEFAULT_MIDI_TYPE)) return (jack_port_t *)&midi_token;
|
2026-10-26 23:32:04 +01:00
|
|
|
return (jack_port_t *)(flags == JackPortIsInput ? &input_token : &output_token);
|
|
|
|
|
}
|
2026-10-27 00:53:41 +01:00
|
|
|
int jack_set_port_connect_callback(jack_client_t *client, JackPortConnectCallback callback, void *arg)
|
|
|
|
|
{ (void)client; (void)arg; connection_cb = callback; return 0; }
|
|
|
|
|
jack_port_t *jack_port_by_id(jack_client_t *client, jack_port_id_t id)
|
|
|
|
|
{ (void)client; return id == 3 ? (jack_port_t *)&midi_token : NULL; }
|
2026-10-26 23:32:04 +01:00
|
|
|
int jack_set_process_callback(jack_client_t *client, JackProcessCallback callback, void *arg)
|
|
|
|
|
{ (void)client; process_cb = callback; context = arg; return callback_fail ? -1 : 0; }
|
|
|
|
|
int jack_set_sample_rate_callback(jack_client_t *client, JackSampleRateCallback callback, void *arg)
|
|
|
|
|
{ (void)client; (void)arg; rate_cb = callback; return 0; }
|
|
|
|
|
int jack_set_buffer_size_callback(jack_client_t *client, JackBufferSizeCallback callback, void *arg)
|
|
|
|
|
{ (void)client; (void)arg; size_cb = callback; return 0; }
|
|
|
|
|
int jack_set_xrun_callback(jack_client_t *client, JackXRunCallback callback, void *arg)
|
|
|
|
|
{ (void)client; (void)arg; xrun_cb = callback; return 0; }
|
|
|
|
|
void jack_on_shutdown(jack_client_t *client, JackShutdownCallback callback, void *arg)
|
|
|
|
|
{ (void)client; (void)arg; shutdown_cb = callback; }
|
|
|
|
|
int jack_activate(jack_client_t *client)
|
|
|
|
|
{ (void)client; if (activate_fail) return -1; active = 1; return 0; }
|
|
|
|
|
int jack_deactivate(jack_client_t *client)
|
|
|
|
|
{ (void)client; if (deactivate_fail) return -1; active = 0; return 0; }
|
|
|
|
|
void *jack_port_get_buffer(jack_port_t *port, jack_nframes_t frames)
|
2026-10-27 01:06:57 +01:00
|
|
|
{ (void)frames; if (null_buffer) return NULL; if (port == (jack_port_t *)&midi_token) return &midi_token; if (port == (jack_port_t *)&right_token) return output_right; return port == (jack_port_t *)&input_token ? input : output; }
|
2026-10-27 00:53:41 +01:00
|
|
|
uint32_t jack_midi_get_event_count(void *buffer) { (void)buffer; return midi_count; }
|
|
|
|
|
int jack_midi_event_get(jack_midi_event_t *event, void *buffer, uint32_t index)
|
|
|
|
|
{ (void)buffer; if (index >= midi_count || index >= 256) return -1; *event = midi_events[index]; return 0; }
|
2026-10-26 23:32:04 +01:00
|
|
|
const char *jack_port_name(const jack_port_t *port)
|
2026-10-27 01:06:57 +01:00
|
|
|
{ if (port == (jack_port_t *)&right_token) return "test:output_right"; if (port == (jack_port_t *)&midi_token) return "test:midi_in"; return port == (jack_port_t *)&input_token ? "test:input" : "test:output"; }
|
2026-10-26 23:32:04 +01:00
|
|
|
const char **jack_get_ports(jack_client_t *client, const char *pattern, const char *type, unsigned long flags)
|
|
|
|
|
{
|
|
|
|
|
const char **list = malloc(2U * sizeof(*list));
|
|
|
|
|
(void)client; (void)pattern; (void)type; (void)flags;
|
|
|
|
|
if (list != NULL) { list[0] = "fake:audio"; list[1] = NULL; }
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
void jack_free(void *pointer) { free(pointer); }
|
|
|
|
|
int jack_connect(jack_client_t *client, const char *source, const char *dest)
|
|
|
|
|
{ (void)client; return strstr(source, "missing") || strstr(dest, "missing") ? -1 : EEXIST; }
|
|
|
|
|
int jack_disconnect(jack_client_t *client, const char *source, const char *dest)
|
|
|
|
|
{ (void)client; (void)source; (void)dest; return 0; }
|
|
|
|
|
static int visit(const char *name, void *arg)
|
|
|
|
|
{ if (strcmp(name, "fake:audio") == 0) ++*(int *)arg; return 0; }
|
|
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
|
{
|
|
|
|
|
xtc_backend_config config = {XTC_BACKEND_JACK, "test", 64U, 1U};
|
|
|
|
|
xtc_backend *backend = NULL;
|
|
|
|
|
xtc_backend_status status;
|
2026-10-26 23:55:35 +01:00
|
|
|
xtc_graph *graph = NULL, *invalid_graph = NULL;
|
2026-10-26 23:32:04 +01:00
|
|
|
size_t node, i;
|
|
|
|
|
const char *name;
|
|
|
|
|
int visits = 0, iteration;
|
|
|
|
|
CHECK(xtc_backend_open(NULL, &backend) == XTC_ERR_INVALID_ARGUMENT && backend == NULL);
|
2026-10-27 01:06:57 +01:00
|
|
|
config.channel_count = 3U;
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_INVALID_ARGUMENT);
|
|
|
|
|
config.channel_count = 1U;
|
|
|
|
|
config.kind = (xtc_backend_kind)999;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_UNSUPPORTED);
|
|
|
|
|
config.kind = XTC_BACKEND_JACK;
|
|
|
|
|
open_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
|
|
|
|
|
open_fail = 0;
|
|
|
|
|
server_frames = 128U;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_CAPACITY && backend == NULL);
|
|
|
|
|
server_frames = 32U;
|
|
|
|
|
register_fail = 2;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
|
2026-10-27 00:53:41 +01:00
|
|
|
register_fail = 3;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
|
2026-10-26 23:32:04 +01:00
|
|
|
register_fail = 0;
|
|
|
|
|
callback_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
|
|
|
|
|
close_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend != NULL);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
close_fail = 0;
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
callback_fail = 0;
|
|
|
|
|
CHECK(xtc_graph_create(1U, &graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_graph_add_gain(graph, 0.5f, &node) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
2026-10-26 23:55:35 +01:00
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK); /* Stopped edits replace pending plan. */
|
|
|
|
|
CHECK(xtc_graph_create(1U, &invalid_graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_graph_add_oscillator(invalid_graph, 25000.0f, &node) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, invalid_graph) == XTC_ERR_INVALID_ARGUMENT);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.submitted_revision == 1U && status.active_revision == 0U);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(xtc_backend_port_name(backend, XTC_PORT_OUTPUT, &name) == XTC_OK && strcmp(name,"test:output") == 0);
|
|
|
|
|
CHECK(xtc_backend_visit_ports(backend, XTC_PORT_INPUT, visit, &visits) == XTC_OK && visits == 1);
|
|
|
|
|
CHECK(xtc_backend_connect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_ERR_INVALID_STATE);
|
|
|
|
|
activate_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_ERR_IO);
|
|
|
|
|
activate_fail = 0;
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_seek(backend, 1000U) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 1000U);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_seek(backend, 0) == XTC_ERR_INVALID_STATE);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
CHECK(xtc_backend_connect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_connect(backend, XTC_PORT_INPUT, "missing:out") == XTC_ERR_IO);
|
|
|
|
|
CHECK(xtc_backend_disconnect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_OK);
|
|
|
|
|
for (i = 0U; i < 128U; ++i) input[i] = 1.0f;
|
|
|
|
|
CHECK(process_cb(32U, context) == 0 && output[0] == 0.5f);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.active_revision == status.submitted_revision && status.sample_position == 1032U);
|
2026-10-26 23:55:35 +01:00
|
|
|
CHECK(xtc_backend_submit(backend, invalid_graph) == XTC_ERR_INVALID_ARGUMENT);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_ERR_CAPACITY);
|
|
|
|
|
CHECK(process_cb(32U, context) == 0 && output[0] == 0.5f);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(size_cb(128U, context) == 0);
|
|
|
|
|
CHECK(process_cb(128U, context) == 0 && output[127] == 0.0f);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_ERR_CAPACITY && status.sample_position == 1064U);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(size_cb(16U, context) == 0);
|
|
|
|
|
CHECK(process_cb(16U, context) == 0 && output[15] == 0.5f);
|
|
|
|
|
CHECK(xrun_cb(context) == 0);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.xruns == 1U && status.buffer_frames == 16U && status.sample_position == 1080U);
|
2026-10-26 23:32:04 +01:00
|
|
|
null_buffer = 1;
|
|
|
|
|
CHECK(process_cb(16U, context) == 0);
|
|
|
|
|
null_buffer = 0;
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_ERR_IO);
|
|
|
|
|
CHECK(rate_cb(44100U, context) == 0);
|
|
|
|
|
CHECK(process_cb(16U, context) == 0 && output[0] == 0.0f);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_rate_changed && status.sample_rate == 44100U);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
deactivate_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_ERR_IO);
|
|
|
|
|
deactivate_fail = 0;
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK && !active);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
for (iteration = 0; iteration < 100; ++iteration) {
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(32U, context) == 0);
|
|
|
|
|
if (iteration % 2 == 0) {
|
|
|
|
|
shutdown_cb(context);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.server_lost && !status.running);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
} else {
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 32U);
|
|
|
|
|
CHECK(xtc_backend_seek(backend, 24000U) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
2026-10-27 00:10:45 +01:00
|
|
|
CHECK(process_cb(32U, context) == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 24032U);
|
2026-10-26 23:32:04 +01:00
|
|
|
}
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == (iteration % 2 == 0 ? XTC_ERR_INVALID_STATE : XTC_OK));
|
|
|
|
|
close_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_ERR_IO);
|
|
|
|
|
close_fail = 0;
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
}
|
2026-10-27 00:31:03 +01:00
|
|
|
{
|
|
|
|
|
char directory[] = "/tmp/xtc-backend-audio-XXXXXX", take[256], song[256];
|
|
|
|
|
xtc_project *p = NULL, *q = NULL;
|
|
|
|
|
xtc_recorder *recorder = NULL;
|
|
|
|
|
uint64_t track, clip, captured;
|
|
|
|
|
CHECK(mkdtemp(directory) != NULL);
|
|
|
|
|
snprintf(take, sizeof(take), "%s/take.wav", directory);
|
|
|
|
|
snprintf(song, sizeof(song), "%s/song.xtc", directory);
|
|
|
|
|
CHECK(xtc_project_create(&p) == XTC_OK && xtc_project_create(&q) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_track(p, "Take", &track) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_prepare_project(backend, p) == XTC_OK);
|
2026-10-27 03:46:09 +01:00
|
|
|
CHECK(xtc_recorder_create_replace(take, 48000, 256, 1, &recorder) == XTC_OK);
|
2026-10-27 00:31:03 +01:00
|
|
|
CHECK(xtc_backend_set_recorder(backend, recorder) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_recorder(backend, NULL) == XTC_ERR_INVALID_STATE);
|
2026-10-27 03:37:19 +01:00
|
|
|
CHECK(xtc_backend_request_stop(backend) == XTC_ERR_INVALID_STATE);
|
2026-10-27 00:31:03 +01:00
|
|
|
CHECK(xtc_backend_prepare_project(backend, p) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
for (i = 0; i < 32; ++i) input[i] = i % 2 ? -0.5f : 0.25f;
|
|
|
|
|
CHECK(process_cb(32, context) == 0);
|
|
|
|
|
deactivate_fail = 1;
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_ERR_IO);
|
|
|
|
|
CHECK(process_cb(32, context) == 0);
|
|
|
|
|
deactivate_fail = 0;
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_recorder(backend, NULL) == XTC_OK);
|
|
|
|
|
CHECK(xtc_recorder_finish(recorder) == XTC_OK);
|
|
|
|
|
CHECK(xtc_recorder_status(recorder, &captured) == XTC_OK && captured == 64);
|
|
|
|
|
xtc_recorder_destroy(recorder);
|
2026-10-27 03:46:09 +01:00
|
|
|
CHECK(xtc_recorder_create_replace(take, 48000, 256, 1, &recorder) == XTC_OK);
|
2026-10-27 00:31:03 +01:00
|
|
|
CHECK(xtc_backend_set_recorder(backend, recorder) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
null_buffer = 1; CHECK(process_cb(32, context) == 0); null_buffer = 0;
|
|
|
|
|
CHECK(process_cb(32, context) == 0); /* Never silently resume after missing callback data. */
|
|
|
|
|
CHECK(xtc_recorder_status(recorder, &captured) == XTC_ERR_IO && captured == 0);
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK && xtc_backend_set_recorder(backend, NULL) == XTC_OK);
|
|
|
|
|
CHECK(xtc_recorder_finish(recorder) == XTC_ERR_IO);
|
|
|
|
|
xtc_recorder_destroy(recorder); /* Prior destination take must survive. */
|
|
|
|
|
CHECK(xtc_project_import_audio(p, track, take, 0, &clip) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_save(p, song) == XTC_OK && xtc_project_load(q, song) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_prepare_project(backend, q) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_seek(backend, 0) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(unlink(take) == 0); /* Immutable plan is already loaded. */
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && memcmp(input, output, 32 * sizeof(float)) == 0);
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_prepare_project(backend, q) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.asset_issues == 1);
|
|
|
|
|
CHECK(strcmp(xtc_backend_asset_issue(backend), take) == 0);
|
|
|
|
|
CHECK(xtc_backend_seek(backend, 0) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[0] == 0.0f);
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
xtc_project_destroy(p); xtc_project_destroy(q);
|
|
|
|
|
CHECK(unlink(song) == 0 && rmdir(directory) == 0);
|
|
|
|
|
}
|
2026-10-27 00:53:41 +01:00
|
|
|
{
|
|
|
|
|
xtc_project *p = NULL;
|
|
|
|
|
uint64_t track, clip;
|
|
|
|
|
unsigned char on[3] = {0x90, 69, 127}, off[3] = {0x90, 69, 0}, bad[3] = {0x90, 255, 127};
|
|
|
|
|
CHECK(xtc_project_create(&p) == XTC_OK && xtc_project_add_track(p, "MIDI", &track) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_midi_clip(p, track, 0, 3840, &clip) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK && xtc_backend_prepare_project(backend, p) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_midi_port_name(backend, &name) == XTC_OK && strcmp(name, "test:midi_in") == 0);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
midi_count = 1; midi_events[0].time = 4; midi_events[0].size = 3; midi_events[0].buffer = on;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[0] == 0 && output[4] == 0 && output[5] > 0);
|
|
|
|
|
midi_events[0].time = 8; midi_events[0].buffer = off;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[0] != 0 && output[8] == 0 && output[31] == 0);
|
|
|
|
|
midi_events[0].time = 0; midi_events[0].buffer = on;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 0;
|
|
|
|
|
CHECK(xtc_backend_panic(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[0] == 0 && output[31] == 0);
|
|
|
|
|
midi_count = 1;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 257;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.midi_dropped == 257);
|
|
|
|
|
midi_count = 1; midi_events[0].buffer = bad;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.midi_dropped == 258);
|
|
|
|
|
midi_events[0].buffer = on;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 0;
|
|
|
|
|
connection_cb(999, 3, 0, context);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
midi_count = 1;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 0; CHECK(xrun_cb(context) == 0);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
midi_count = 1;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 0; null_buffer = 1; CHECK(process_cb(32, context) == 0); null_buffer = 0;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
midi_count = 1;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] > 0);
|
|
|
|
|
midi_count = 0;
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK && xtc_backend_seek(backend, 0) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0);
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
xtc_project_destroy(p);
|
|
|
|
|
}
|
2026-10-27 01:06:57 +01:00
|
|
|
{
|
|
|
|
|
xtc_project *p = NULL; uint64_t track, clip, note;
|
|
|
|
|
config.channel_count = 2;
|
|
|
|
|
register_fail = 4;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
|
|
|
|
|
register_fail = 0;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_output_name(backend, 1, &name) == XTC_OK && strcmp(name, "test:output_right") == 0);
|
|
|
|
|
CHECK(xtc_project_create(&p) == XTC_OK && xtc_project_add_track(p, "Right", &track) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_midi_clip(p, track, 0, 3840, &clip) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_note(p, clip, 0, 960, 69, 127, 0, ¬e) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_set_mix(p, track, 0.5f, 1, 0, 0) == XTC_OK);
|
2026-10-27 16:15:34 +01:00
|
|
|
uint64_t bus;
|
|
|
|
|
CHECK(xtc_project_add_bus(p, "Bus", &bus) == XTC_OK && xtc_project_route_track(p, track, bus) == XTC_OK);
|
|
|
|
|
CHECK(xtc_project_set_mix(p, bus, 2, 0, 0, 0) == XTC_OK);
|
2026-10-27 01:06:57 +01:00
|
|
|
CHECK(xtc_backend_prepare_project(backend, p) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[1] == 0 && output_right[1] > 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.peak_left == 0 && status.peak_right > 0);
|
2026-10-27 16:15:34 +01:00
|
|
|
xtc_meter_snapshot meters;
|
|
|
|
|
CHECK(xtc_backend_read_meters(backend, &meters) == XTC_OK && meters.count == 2);
|
|
|
|
|
CHECK(meters.tracks[0].id == track && meters.tracks[0].left == 0 && meters.tracks[0].right > 0);
|
|
|
|
|
CHECK(meters.tracks[1].id == bus && meters.tracks[1].left == 0 && meters.tracks[1].right > meters.tracks[0].right);
|
|
|
|
|
CHECK(meters.master_left == 0 && meters.master_right == meters.tracks[1].right);
|
|
|
|
|
CHECK(xtc_backend_read_meters(backend, &meters) == XTC_OK && meters.master_right == 0 && meters.tracks[0].right == 0);
|
2026-10-27 01:06:57 +01:00
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.peak_right == 0);
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK); xtc_project_destroy(p);
|
|
|
|
|
}
|
2026-10-27 03:37:19 +01:00
|
|
|
{
|
|
|
|
|
uint64_t held;
|
|
|
|
|
config.channel_count = 2;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_transition_frames(backend, 65537) == XTC_ERR_INVALID_ARGUMENT);
|
|
|
|
|
CHECK(xtc_backend_set_transition_frames(backend, 4) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
|
|
|
|
for (i = 0; i < 128; ++i) input[i] = 1;
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_transition_frames(backend, 4) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
CHECK(process_cb(4, context) == 0);
|
|
|
|
|
CHECK(output[0] == 0.125f && output[3] == 0.5f && output_right[3] == output[3]);
|
|
|
|
|
CHECK(xtc_backend_request_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(process_cb(2, context) == 0 && output[0] == 0.375f && output[1] == 0.25f);
|
|
|
|
|
CHECK(xtc_backend_request_stop(backend) == XTC_OK); /* Must not restart ramp. */
|
|
|
|
|
CHECK(process_cb(2, context) == 0 && output[0] == 0.125f && output[1] == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.running && status.stopping);
|
|
|
|
|
held = status.sample_position;
|
|
|
|
|
CHECK(process_cb(32, context) == 0 && output[0] == 0 && output[31] == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == held);
|
|
|
|
|
deactivate_fail = 1;
|
|
|
|
|
xtc_backend_collect(backend);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.running && status.process_result == XTC_ERR_IO);
|
|
|
|
|
deactivate_fail = 0;
|
|
|
|
|
xtc_backend_collect(backend);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && !status.running && !status.stopping);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK && process_cb(4, context) == 0 && output[3] == 0.5f);
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK); /* Immediate escape stays available. */
|
|
|
|
|
CHECK(xtc_backend_set_transition_frames(backend, 0) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK && xtc_backend_request_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && !status.running);
|
|
|
|
|
CHECK(xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
}
|
2026-10-28 10:37:01 +01:00
|
|
|
{
|
|
|
|
|
config.channel_count = 1;config.max_frames = 128;
|
|
|
|
|
CHECK(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_submit(backend, graph) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_loop(backend, 100, 163, 1) == XTC_ERR_INVALID_ARGUMENT);
|
|
|
|
|
CHECK(xtc_backend_set_loop(backend, 100, 164, 1) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_loop(backend, 0, 64, 0) == XTC_ERR_INVALID_STATE);
|
|
|
|
|
CHECK(process_cb(32, context) == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 132);
|
|
|
|
|
CHECK(process_cb(32, context) == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 100);
|
|
|
|
|
CHECK(process_cb(17, context) == 0 && process_cb(32, context) == 0 && process_cb(32, context) == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 117);
|
|
|
|
|
for(i=0;i<128;++i)input[i]=(float)i;
|
|
|
|
|
CHECK(process_cb(128,context)==0);
|
|
|
|
|
for(i=0;i<128;++i)CHECK(output[i]==input[i]*0.5f);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend,&status)==XTC_OK && status.sample_position==117);
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_loop(backend, 100, 164, 0) == XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend) == XTC_OK && process_cb(32, context) == 0);
|
|
|
|
|
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_position == 149);
|
|
|
|
|
CHECK(xtc_backend_stop(backend) == XTC_OK && xtc_backend_close(backend) == XTC_OK);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
xtc_project *p = NULL;uint64_t track,clip,note;
|
|
|
|
|
config.max_frames=128;config.channel_count=1;
|
|
|
|
|
CHECK(xtc_backend_open(&config,&backend)==XTC_OK);
|
|
|
|
|
CHECK(xtc_project_create(&p)==XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_track(p,"Loop instrument",&track)==XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_midi_clip(p,track,0,3840,&clip)==XTC_OK);
|
|
|
|
|
CHECK(xtc_project_add_note(p,clip,0,960,69,100,0,¬e)==XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_prepare_project(backend,p)==XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_set_loop(backend,0,64,1)==XTC_OK);
|
|
|
|
|
CHECK(xtc_backend_start(backend)==XTC_OK && process_cb(128,context)==0);
|
|
|
|
|
CHECK(output[1]!=0);
|
|
|
|
|
for(i=0;i<64;++i)CHECK(output[i]==output[i+64]);
|
|
|
|
|
CHECK(xtc_backend_stop(backend)==XTC_OK && xtc_backend_close(backend)==XTC_OK);
|
|
|
|
|
xtc_project_destroy(p);
|
|
|
|
|
}
|
2026-10-26 23:32:04 +01:00
|
|
|
CHECK(closed >= 104);
|
|
|
|
|
CHECK(xtc_backend_close(NULL) == XTC_OK);
|
2026-10-26 23:55:35 +01:00
|
|
|
xtc_graph_destroy(invalid_graph);
|
2026-10-26 23:32:04 +01:00
|
|
|
xtc_graph_destroy(graph);
|
|
|
|
|
puts("JACK backend fault-injection tests passed.");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|