Phase 2.
This commit is contained in:
parent
c0ca1ff396
commit
577d80f018
16 changed files with 1100 additions and 41 deletions
|
|
@ -1,11 +1,28 @@
|
|||
# Integration tests
|
||||
|
||||
Planned integration coverage:
|
||||
- deterministic offline render;
|
||||
- repeated engine initialization and shutdown;
|
||||
- graph replacement while processing;
|
||||
- project save/load round trip;
|
||||
- backend disconnect/reconnect;
|
||||
- MIDI panic and transport stop.
|
||||
`make test` is hardware/server independent: offline render tests, concurrent
|
||||
plan-replacement stress, and (when JACK headers are present) JACK ABI fault
|
||||
injection. The fake links against installed declarations without linking
|
||||
libjack. It does not claim to reproduce JACK scheduling; the separate
|
||||
threaded stress test exercises publication/reclamation.
|
||||
|
||||
Do not mark these tests as passing until they exist and run.
|
||||
`make test-jack` explicitly runs a five-second live JACK loopback test. Requires
|
||||
an existing JACK or PipeWire JACK-compatible server; does not start a server,
|
||||
change its sample rate/block size, or connect physical outputs. Source output
|
||||
is connected only to the test's capture client, then disconnected and removed.
|
||||
It checks finite/bounded captured samples at two gains, active plan replacement,
|
||||
status and ten restart cycles. For a longer run:
|
||||
|
||||
```sh
|
||||
make build/test_jack
|
||||
./build/test_jack 30
|
||||
```
|
||||
|
||||
On the inspected system the 30-second test passed at 48000 Hz (initial 1024
|
||||
frames): 704 engine blocks, 716 probe blocks, both gains observed, zero xruns,
|
||||
and ten restart cycles. This is a limited software-loopback result, not an
|
||||
endurance or physical-device qualification. No live server termination or global
|
||||
format change is performed on the shared server. Those paths, missing server,
|
||||
port/callback setup failure, activation/deactivation/close failure, null buffers,
|
||||
invalid configuration, oversize buffers, and reopen are tested with injected
|
||||
JACK callbacks/results in `tests/unit/test_backend.c`.
|
||||
|
|
|
|||
119
tests/integration/test_jack.c
Normal file
119
tests/integration/test_jack.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_backend.h"
|
||||
#include <jack/jack.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
172
tests/unit/test_backend.c
Normal file
172
tests/unit/test_backend.c
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/* 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"
|
||||
#include <jack/jack.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); return 1; } } while (0)
|
||||
static JackProcessCallback process_cb;
|
||||
static JackSampleRateCallback rate_cb;
|
||||
static JackBufferSizeCallback size_cb;
|
||||
static JackXRunCallback xrun_cb;
|
||||
static JackShutdownCallback shutdown_cb;
|
||||
static void *context;
|
||||
static float input[128], output[128];
|
||||
static int client_token, input_token, output_token;
|
||||
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;
|
||||
return (jack_port_t *)(flags == JackPortIsInput ? &input_token : &output_token);
|
||||
}
|
||||
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)
|
||||
{ (void)frames; if (null_buffer) return NULL; return port == (jack_port_t *)&input_token ? input : output; }
|
||||
const char *jack_port_name(const jack_port_t *port)
|
||||
{ return port == (jack_port_t *)&input_token ? "test:input" : "test:output"; }
|
||||
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;
|
||||
xtc_graph *graph = NULL;
|
||||
size_t node, i;
|
||||
const char *name;
|
||||
int visits = 0, iteration;
|
||||
CHECK(xtc_backend_open(NULL, &backend) == XTC_ERR_INVALID_ARGUMENT && backend == NULL);
|
||||
config.channel_count = 2U;
|
||||
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);
|
||||
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);
|
||||
CHECK(xtc_backend_submit(backend, graph) == XTC_ERR_CAPACITY);
|
||||
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;
|
||||
CHECK(xtc_backend_start(backend) == XTC_OK);
|
||||
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);
|
||||
CHECK(size_cb(128U, context) == 0);
|
||||
CHECK(process_cb(128U, context) == 0 && output[127] == 0.0f);
|
||||
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_ERR_CAPACITY);
|
||||
CHECK(size_cb(16U, context) == 0);
|
||||
CHECK(process_cb(16U, context) == 0 && output[15] == 0.5f);
|
||||
CHECK(xrun_cb(context) == 0);
|
||||
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.xruns == 1U && status.buffer_frames == 16U);
|
||||
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);
|
||||
CHECK(xtc_backend_start(backend) == XTC_OK);
|
||||
}
|
||||
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);
|
||||
}
|
||||
CHECK(closed >= 104);
|
||||
CHECK(xtc_backend_close(NULL) == XTC_OK);
|
||||
xtc_graph_destroy(graph);
|
||||
puts("JACK backend fault-injection tests passed.");
|
||||
return 0;
|
||||
}
|
||||
80
tests/unit/test_realtime.c
Normal file
80
tests/unit/test_realtime.c
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_realtime.h"
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); return 1; } } while (0)
|
||||
|
||||
typedef struct { xtc_realtime *engine; unsigned int stop, failed; } worker_state;
|
||||
static void *worker(void *context)
|
||||
{
|
||||
worker_state *state = context;
|
||||
float input[32], output[32];
|
||||
size_t i;
|
||||
for (i = 0U; i < 32U; ++i) input[i] = 1.0f;
|
||||
while (!__atomic_load_n(&state->stop, __ATOMIC_ACQUIRE)) {
|
||||
if (xtc_realtime_process(state->engine, input, output, 32U) != XTC_OK)
|
||||
__atomic_store_n(&state->failed, 1U, __ATOMIC_RELAXED);
|
||||
/* A whole block must come from one plan. No partially edited gains. */
|
||||
for (i = 0U; i < 32U; ++i)
|
||||
if ((output[i] != 0.0f && output[i] != 1.0f && output[i] != 2.0f) || output[i] != output[0])
|
||||
__atomic_store_n(&state->failed, 1U, __ATOMIC_RELAXED);
|
||||
sched_yield();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static int run(void)
|
||||
{
|
||||
xtc_realtime *engine = NULL;
|
||||
xtc_graph *one = NULL, *two = NULL;
|
||||
xtc_realtime_status status;
|
||||
size_t node, i;
|
||||
float input[5] = {1,1,1,1,1}, output[5] = {9,9,9,9,9};
|
||||
pthread_t thread;
|
||||
worker_state state;
|
||||
CHECK(xtc_realtime_create(48000.0, 32U, &engine) == XTC_OK);
|
||||
CHECK(xtc_realtime_process(engine, input, output, 4U) == XTC_OK && output[0] == 0.0f);
|
||||
CHECK(xtc_graph_create(1U, &one) == XTC_OK);
|
||||
CHECK(xtc_graph_add_gain(one, 1.0f, &node) == XTC_OK);
|
||||
CHECK(xtc_graph_create(1U, &two) == XTC_OK);
|
||||
CHECK(xtc_graph_add_gain(two, 2.0f, &node) == XTC_OK);
|
||||
CHECK(xtc_realtime_submit(engine, one) == XTC_OK);
|
||||
CHECK(xtc_realtime_submit(engine, two) == XTC_ERR_CAPACITY);
|
||||
CHECK(xtc_realtime_process(engine, input, output, 4U) == XTC_OK && output[0] == 1.0f);
|
||||
CHECK(xtc_realtime_submit(engine, two) == XTC_OK);
|
||||
CHECK(xtc_realtime_process(engine, input, output, 4U) == XTC_OK && output[0] == 2.0f);
|
||||
xtc_realtime_collect(engine);
|
||||
CHECK(xtc_realtime_process(engine, input, output, 0U) == XTC_OK && output[0] == 2.0f);
|
||||
CHECK(xtc_realtime_get_status(engine, &status) == XTC_OK && status.processed_blocks == 3U);
|
||||
state.engine = engine; state.stop = 0U; state.failed = 0U;
|
||||
CHECK(pthread_create(&thread, NULL, worker, &state) == 0);
|
||||
for (i = 0U; i < 2000U; ++i) {
|
||||
xtc_result result;
|
||||
do {
|
||||
result = xtc_realtime_submit(engine, i % 2U ? one : two);
|
||||
if (result == XTC_ERR_CAPACITY) sched_yield();
|
||||
} while (result == XTC_ERR_CAPACITY);
|
||||
CHECK(result == XTC_OK);
|
||||
}
|
||||
__atomic_store_n(&state.stop, 1U, __ATOMIC_RELEASE);
|
||||
CHECK(pthread_join(thread, NULL) == 0);
|
||||
CHECK(state.failed == 0U);
|
||||
CHECK(xtc_realtime_get_status(engine, &status) == XTC_OK && status.processed_blocks > 3U);
|
||||
xtc_realtime_destroy(engine); /* Also cleans pending and retired plans. */
|
||||
xtc_graph_destroy(one); xtc_graph_destroy(two);
|
||||
for (i = 0U; i < 100U; ++i) {
|
||||
CHECK(xtc_realtime_create(48000.0, 4U, &engine) == XTC_OK);
|
||||
CHECK(xtc_realtime_process(engine, input, output, 5U) == XTC_ERR_CAPACITY);
|
||||
CHECK(output[0] == 0.0f && output[4] == 0.0f);
|
||||
CHECK(xtc_realtime_get_status(engine, &status) == XTC_OK && status.last_result == XTC_ERR_CAPACITY);
|
||||
xtc_realtime_destroy(engine);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int main(void)
|
||||
{
|
||||
CHECK(run() == 0);
|
||||
puts("Realtime handoff and concurrent replacement tests passed.");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue