xtc/src/graph/graph.c

122 lines
4.9 KiB
C

#include "graph_internal.h"
#include <math.h>
#include <stdlib.h>
xtc_result xtc_graph_create(size_t max_nodes, xtc_graph **out_graph)
{
xtc_graph *graph;
if (out_graph == NULL) return XTC_ERR_INVALID_ARGUMENT;
*out_graph = NULL;
if (max_nodes == 0U || max_nodes > XTC_MAX_NODES) return XTC_ERR_INVALID_ARGUMENT;
graph = calloc(1U, sizeof(*graph));
if (graph == NULL) return XTC_ERR_OUT_OF_MEMORY;
graph->nodes = calloc(max_nodes, sizeof(*graph->nodes));
graph->edges = calloc(max_nodes * max_nodes, sizeof(*graph->edges));
if (graph->nodes == NULL || graph->edges == NULL) {
xtc_graph_destroy(graph);
return XTC_ERR_OUT_OF_MEMORY;
}
graph->max_nodes = max_nodes;
*out_graph = graph;
return XTC_OK;
}
void xtc_graph_destroy(xtc_graph *graph)
{
if (graph != NULL) {
free(graph->nodes);
free(graph->edges);
free(graph);
}
}
static xtc_result add_node(xtc_graph *graph, xtc_node_type type, float value,
size_t *out_node_id)
{
if (graph == NULL || out_node_id == NULL || !isfinite(value))
return XTC_ERR_INVALID_ARGUMENT;
if (graph->node_count == graph->max_nodes) return XTC_ERR_CAPACITY;
graph->nodes[graph->node_count].type = type;
graph->nodes[graph->node_count].value = value;
*out_node_id = graph->node_count++;
return XTC_OK;
}
xtc_result xtc_graph_add_gain(xtc_graph *graph, float gain, size_t *out_node_id)
{ return add_node(graph, XTC_NODE_GAIN, gain, out_node_id); }
xtc_result xtc_graph_add_oscillator(xtc_graph *graph, float frequency, size_t *out_node_id)
{
if (frequency < 0.0f) return XTC_ERR_INVALID_ARGUMENT;
return add_node(graph, XTC_NODE_OSCILLATOR, frequency, out_node_id);
}
xtc_result xtc_graph_add_meter(xtc_graph *graph, size_t *out_node_id)
{ return add_node(graph, XTC_NODE_METER, 0.0f, out_node_id); }
xtc_result xtc_graph_add_output(xtc_graph *graph, size_t *out_node_id)
{ return add_node(graph, XTC_NODE_OUTPUT, 0.0f, out_node_id); }
xtc_result xtc_graph_get_port(const xtc_graph *graph, size_t node_id,
size_t port_id, xtc_port_info *out_port)
{
xtc_node_type type;
if (graph == NULL || out_port == NULL) return XTC_ERR_INVALID_ARGUMENT;
if (node_id >= graph->node_count) return XTC_ERR_NOT_FOUND;
type = graph->nodes[node_id].type;
if (port_id > 1U || (port_id == 0U && type == XTC_NODE_OSCILLATOR) ||
(port_id == 1U && type == XTC_NODE_OUTPUT)) return XTC_ERR_NOT_FOUND;
out_port->direction = port_id == 0U ? XTC_PORT_INPUT : XTC_PORT_OUTPUT;
out_port->signal_type = XTC_SIGNAL_AUDIO;
out_port->channel_count = 1U;
return XTC_OK;
}
xtc_result xtc_graph_order(const xtc_graph *graph, size_t *order)
{
size_t indegree[XTC_MAX_NODES] = {0U};
size_t head = 0U, tail = 0U, i;
if (graph == NULL || order == NULL) return XTC_ERR_INVALID_ARGUMENT;
for (i = 0U; i < graph->edge_count; ++i)
++indegree[graph->edges[i].destination];
for (i = 0U; i < graph->node_count; ++i)
if (indegree[i] == 0U) order[tail++] = i;
while (head < tail) {
size_t node = order[head++];
for (i = 0U; i < graph->edge_count; ++i) {
if (graph->edges[i].source == node) {
size_t dest = graph->edges[i].destination;
if (--indegree[dest] == 0U) order[tail++] = dest;
}
}
}
return tail == graph->node_count ? XTC_OK : XTC_ERR_GRAPH_CYCLE;
}
xtc_result xtc_graph_validate(const xtc_graph *graph)
{
size_t order[XTC_MAX_NODES];
return xtc_graph_order(graph, order);
}
xtc_result xtc_graph_connect_ports(xtc_graph *graph, size_t source_node,
size_t source_port, size_t destination_node,
size_t destination_port)
{
xtc_port_info source, dest;
size_t i;
xtc_result result = xtc_graph_get_port(graph, source_node, source_port, &source);
if (result != XTC_OK) return result;
result = xtc_graph_get_port(graph, destination_node, destination_port, &dest);
if (result != XTC_OK) return result;
if (source.direction != XTC_PORT_OUTPUT || dest.direction != XTC_PORT_INPUT ||
source.signal_type != dest.signal_type || source.channel_count != dest.channel_count)
return XTC_ERR_INVALID_ARGUMENT;
for (i = 0U; i < graph->edge_count; ++i)
if (graph->edges[i].source == source_node && graph->edges[i].destination == destination_node)
return XTC_ERR_INVALID_STATE;
if (graph->edge_count == graph->max_nodes * graph->max_nodes) return XTC_ERR_CAPACITY;
graph->edges[graph->edge_count].source = source_node;
graph->edges[graph->edge_count++].destination = destination_node;
result = xtc_graph_validate(graph);
if (result != XTC_OK) --graph->edge_count;
return result;
}
xtc_result xtc_graph_connect(xtc_graph *graph, size_t source_node, size_t destination_node)
{ return xtc_graph_connect_ports(graph, source_node, 1U, destination_node, 0U); }