From c0ca1ff396afc57077aef0bacb2c65d43dc00ef4 Mon Sep 17 00:00:00 2001 From: hanez Date: Mon, 26 Oct 2026 23:20:49 +0100 Subject: [PATCH] Phase 1 forgot some files. --- src/graph/graph_internal.h | 14 ++++ tests/unit/test_offline.c | 136 +++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/graph/graph_internal.h create mode 100644 tests/unit/test_offline.c diff --git a/src/graph/graph_internal.h b/src/graph/graph_internal.h new file mode 100644 index 0000000..87ee743 --- /dev/null +++ b/src/graph/graph_internal.h @@ -0,0 +1,14 @@ +#ifndef XTC_GRAPH_INTERNAL_H +#define XTC_GRAPH_INTERNAL_H +#include "xtc/xtc_engine.h" +#define XTC_MAX_NODES 256U +#define XTC_MAX_FRAMES 65536U +typedef struct { xtc_node_type type; float value; } xtc_node; +typedef struct { size_t source; size_t destination; } xtc_edge; +struct xtc_graph { + size_t max_nodes, node_count, edge_count; + xtc_node *nodes; + xtc_edge *edges; +}; +xtc_result xtc_graph_order(const xtc_graph *graph, size_t *order); +#endif diff --git a/tests/unit/test_offline.c b/tests/unit/test_offline.c new file mode 100644 index 0000000..7a757ae --- /dev/null +++ b/tests/unit/test_offline.c @@ -0,0 +1,136 @@ +#include "xtc/xtc_engine.h" +#include +#include +#include +#define CHECK(c) do { if (!(c)) { fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); return 1; } } while (0) +#define NEAR(a,b) (fabsf((a) - (b)) < 0.00001f) + +static int oscillator(void) +{ + xtc_graph *graph = NULL; + xtc_plan *plan = NULL, *other = NULL; + size_t osc, gain, meter, output, unused; + float whole[19], split[19], peak; + const float expected[4] = {0.0f, 0.25f, 0.0f, -0.25f}; + size_t i; + CHECK(xtc_graph_create(5U, &graph) == XTC_OK); + /* Reverse insertion order forces real dependency scheduling. */ + CHECK(xtc_graph_add_output(graph, &output) == XTC_OK); + CHECK(xtc_graph_add_meter(graph, &meter) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 0.25f, &gain) == XTC_OK); + CHECK(xtc_graph_add_oscillator(graph, 12000.0f, &osc) == XTC_OK); + CHECK(xtc_graph_connect(graph, osc, gain) == XTC_OK); + CHECK(xtc_graph_connect(graph, gain, meter) == XTC_OK); + CHECK(xtc_graph_connect(graph, meter, output) == XTC_OK); + CHECK(xtc_graph_compile(graph, 48000.0, 19U, &plan) == XTC_OK); + CHECK(xtc_graph_compile(graph, 48000.0, 19U, &other) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 999.0f, &unused) == XTC_OK); + xtc_graph_destroy(graph); /* Plans own their snapshot and lifetime. */ + CHECK(xtc_plan_process(plan, NULL, whole, 19U) == XTC_OK); + for (i = 0U; i < 19U; ++i) CHECK(NEAR(whole[i], expected[i % 4U])); + CHECK(xtc_plan_meter_peak(plan, meter, &peak) == XTC_OK && NEAR(peak, 0.25f)); + CHECK(xtc_plan_process(other, NULL, split, 3U) == XTC_OK); + CHECK(xtc_plan_process(other, NULL, split + 3U, 7U) == XTC_OK); + CHECK(xtc_plan_process(other, NULL, split + 10U, 9U) == XTC_OK); + CHECK(memcmp(whole, split, sizeof(whole)) == 0); + CHECK(xtc_plan_reset(other) == XTC_OK); + CHECK(xtc_plan_meter_peak(other, meter, &peak) == XTC_OK && peak == 0.0f); + split[0] = 42.0f; + CHECK(xtc_plan_process(other, NULL, split, 20U) == XTC_ERR_CAPACITY); + CHECK(split[0] == 42.0f); + CHECK(xtc_plan_process(other, NULL, NULL, 1U) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_plan_process(other, NULL, split, 0U) == XTC_OK && split[0] == 42.0f); + CHECK(xtc_plan_process(other, NULL, split, 19U) == XTC_OK); + CHECK(memcmp(whole, split, sizeof(whole)) == 0); + CHECK(xtc_plan_meter_peak(other, gain, &peak) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_plan_meter_peak(other, SIZE_MAX, &peak) == XTC_ERR_NOT_FOUND); + xtc_plan_destroy(plan); + xtc_plan_destroy(other); + return 0; +} + +static int routing(void) +{ + xtc_graph *graph = NULL; + xtc_plan *plan = NULL; + size_t output, a, b, root, disconnected; + float samples[4] = {1.0f, -1.0f, 0.5f, 0.0f}; + CHECK(xtc_graph_create(5U, &graph) == XTC_OK); + CHECK(xtc_graph_add_output(graph, &output) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 2.0f, &a) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 3.0f, &b) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 0.5f, &root) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 100.0f, &disconnected) == XTC_OK); + CHECK(xtc_graph_connect(graph, root, a) == XTC_OK); + CHECK(xtc_graph_connect(graph, root, b) == XTC_OK); + CHECK(xtc_graph_connect(graph, a, output) == XTC_OK); + CHECK(xtc_graph_connect(graph, b, output) == XTC_OK); + CHECK(xtc_graph_compile(graph, 48000.0, 4U, &plan) == XTC_OK); + CHECK(xtc_plan_process(plan, samples, samples, 4U) == XTC_OK); + CHECK(NEAR(samples[0], 2.5f) && NEAR(samples[1], -2.5f) && NEAR(samples[2], 1.25f)); + CHECK(xtc_plan_process(plan, NULL, samples, 4U) == XTC_OK); + CHECK(samples[0] == 0.0f && samples[3] == 0.0f); + samples[0] = NAN; + samples[1] = INFINITY; + CHECK(xtc_plan_process(plan, samples, samples, 4U) == XTC_OK); + CHECK(samples[0] == 0.0f && samples[1] == 0.0f); + xtc_plan_destroy(plan); + xtc_graph_destroy(graph); + return 0; +} + +static int validation(void) +{ + xtc_graph *graph = NULL; + xtc_plan *plan = NULL; + xtc_port_info port; + size_t a, b, osc, output, unchanged = 77U; + size_t iteration; + CHECK(xtc_graph_create(SIZE_MAX, &graph) == XTC_ERR_INVALID_ARGUMENT && graph == NULL); + CHECK(xtc_graph_create(0U, &graph) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_create(4U, &graph) == XTC_OK); + CHECK(xtc_graph_compile(graph, 48000.0, 4U, &plan) == XTC_ERR_INVALID_STATE && plan == NULL); + CHECK(xtc_graph_add_gain(graph, NAN, &unchanged) == XTC_ERR_INVALID_ARGUMENT && unchanged == 77U); + CHECK(xtc_graph_add_oscillator(graph, -1.0f, &unchanged) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_add_gain(graph, 1.0f, &a) == XTC_OK); + CHECK(xtc_graph_add_gain(graph, 1.0f, &b) == XTC_OK); + CHECK(xtc_graph_add_oscillator(graph, 24000.0f, &osc) == XTC_OK); + CHECK(xtc_graph_add_output(graph, &output) == XTC_OK); + CHECK(xtc_graph_add_meter(graph, &unchanged) == XTC_ERR_CAPACITY && unchanged == 77U); + CHECK(xtc_graph_connect(graph, a, a) == XTC_ERR_GRAPH_CYCLE); + CHECK(xtc_graph_connect(graph, a, b) == XTC_OK); + CHECK(xtc_graph_connect(graph, b, a) == XTC_ERR_GRAPH_CYCLE); + CHECK(xtc_graph_connect(graph, a, b) == XTC_ERR_INVALID_STATE); + CHECK(xtc_graph_connect(graph, SIZE_MAX, b) == XTC_ERR_NOT_FOUND); + CHECK(xtc_graph_connect_ports(graph, a, 0U, b, 0U) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_connect_ports(graph, a, 1U, b, 1U) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_connect_ports(graph, a, 2U, b, 0U) == XTC_ERR_NOT_FOUND); + CHECK(xtc_graph_connect(graph, a, osc) == XTC_ERR_NOT_FOUND); + CHECK(xtc_graph_connect(graph, output, a) == XTC_ERR_NOT_FOUND); + CHECK(xtc_graph_get_port(graph, osc, 1U, &port) == XTC_OK); + CHECK(port.direction == XTC_PORT_OUTPUT && port.signal_type == XTC_SIGNAL_AUDIO && port.channel_count == 1U); + CHECK(xtc_graph_validate(graph) == XTC_OK); + CHECK(xtc_graph_compile(graph, NAN, 4U, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_compile(graph, 0.0, 4U, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_compile(graph, 44100.0, 4U, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_compile(graph, 48000.0, SIZE_MAX, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_compile(graph, 48000.0, 0U, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_graph_compile(NULL, 48000.0, 4U, &plan) == XTC_ERR_INVALID_ARGUMENT); + CHECK(xtc_plan_reset(NULL) == XTC_ERR_INVALID_ARGUMENT); + for (iteration = 0U; iteration < 100U; ++iteration) { + CHECK(xtc_graph_compile(graph, 48000.0, 4U, &plan) == XTC_OK); + xtc_plan_destroy(plan); + } + xtc_graph_destroy(graph); + xtc_graph_destroy(NULL); + xtc_plan_destroy(NULL); + return 0; +} +int main(void) +{ + CHECK(oscillator() == 0); + CHECK(routing() == 0); + CHECK(validation() == 0); + puts("All offline graph tests passed."); + return 0; +}