xtc/include/xtc/xtc_engine.h

380 lines
16 KiB
C

#ifndef XTC_ENGINE_H
#define XTC_ENGINE_H
#include "xtc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct xtc_graph xtc_graph;
/* Control-thread independent model copy; out is NULL on failure. */
xtc_result xtc_graph_clone(const xtc_graph *source, xtc_graph **out);
typedef enum {
XTC_SIGNAL_AUDIO = 1,
XTC_SIGNAL_EVENT = 2,
XTC_SIGNAL_CONTROL = 3
} xtc_signal_type;
typedef enum {
XTC_PORT_INPUT = 1,
XTC_PORT_OUTPUT = 2
} xtc_port_direction;
typedef struct {
float *samples;
size_t frame_count;
size_t channel_count;
} xtc_audio_buffer;
typedef struct xtc_plan xtc_plan;
/* Single owner: set absolute automation/native-event position, no allocation. */
void xtc_plan_reset_history(xtc_plan *plan); /* Single owner; internal effects only. */
xtc_result xtc_plan_position(xtc_plan *plan, uint64_t position);
typedef enum {
XTC_NODE_GAIN = 1,
XTC_NODE_OSCILLATOR,
XTC_NODE_METER,
XTC_NODE_OUTPUT,
XTC_NODE_AUDIO, /* Timeline mix source, output only. */
XTC_NODE_INSTRUMENT, /* Internal MIDI instrument, output only. */
XTC_NODE_NATIVE, /* Trusted source/effect; see xtc_native.h. */
XTC_NODE_PLUGIN /* Persisted LADSPA source/effect; see xtc_plugin.h. */
} xtc_node_type;
typedef struct {
xtc_port_direction direction;
xtc_signal_type signal_type;
size_t channel_count;
} xtc_port_info;
/* Control-thread API. Limits: 256 nodes, one audio lane per port (stereo plans use paired lanes). IDs are stable
* monotonically assigned identifiers; removed IDs are never reused. Output
* parameters are written only on success, except create/compile/apply_edit
* which set their output pointer to NULL on failure. */
typedef struct {
size_t id;
xtc_node_type type;
float value;
} xtc_node_info;
typedef struct {
size_t source_node, source_port, destination_node, destination_port;
} xtc_connection_info;
typedef enum {
XTC_EDIT_ADD_NODE = 1,
XTC_EDIT_REMOVE_NODE,
XTC_EDIT_CONNECT,
XTC_EDIT_DISCONNECT,
XTC_EDIT_INSERT_GAIN /* Atomic wire replacement by a unity gain/bus node. */
} xtc_graph_edit_kind;
typedef struct {
xtc_graph_edit_kind kind;
union {
struct {
xtc_node_type type;
float value;
} add;
size_t node_id;
xtc_connection_info connection;
} data;
} xtc_graph_edit;
/* Control-thread routing diagnostics in dense-node order, matching get_node.
* level = longest source-to-node edge count. reaches_output follows explicit
* output nodes (or legacy terminal fallback only when no output nodes exist).
* external_input = no incoming wire and an input port: renderer uses external
* audio there, NOT silence. Reachability is structural, not an audibility test.
* No allocation/plugin loading. Failure leaves items and out_count unchanged. */
typedef struct {
size_t id, inputs, outputs, level;
int reaches_output, external_input;
} xtc_routing_info;
xtc_result xtc_graph_analyze(const xtc_graph *graph, xtc_routing_info *items, size_t capacity, size_t *out_count);
/* Read-only snapshots; indexes enumerate dense lists, distinct from stable IDs.
* No borrowed pointers into graph storage are exposed. */
/**
* @brief Copies the graph's node and connection counts.
*
* @param graph Graph model; borrowed for this call.
* @param out_nodes Receives the nodes.
* @param out_connections Receives the connections.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_get_counts(const xtc_graph *graph, size_t *out_nodes, size_t *out_connections);
/**
* @brief Copies node metadata selected by dense list index.
*
* @param graph Graph model; borrowed for this call.
* @param index Zero-based index in the dense list.
* @param out_node Receives the node.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_get_node(const xtc_graph *graph, size_t index, xtc_node_info *out_node);
/**
* @brief Copies connection metadata selected by dense list index.
*
* @param graph Graph model; borrowed for this call.
* @param index Zero-based index in the dense list.
* @param out_connection Receives the connection.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_get_connection(const xtc_graph *graph, size_t index, xtc_connection_info *out_connection);
/* Set required=1 for explicit output nodes only (none means silence).
* Default 0 retains terminal-node fallback for original offline callers. */
/**
* @brief Sets whether rendering requires explicit output nodes rather than terminal-node fallback.
*
* @param graph Graph model; borrowed for this call.
* @param required One to require explicit output nodes, zero to allow terminal-node fallback.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_require_output_nodes(xtc_graph *graph, int required);
/* Transactional command: on success caller owns a new graph; source unchanged.
* Failure sets *out_graph=NULL, leaves source and out_added_id unchanged.
* out_added_id is optional and written only for successful ADD_NODE/INSERT_GAIN.
* INSERT_GAIN uses data.connection: the existing wire is replaced by
* source -> new unity gain -> destination, without publishing intermediate states. */
/**
* @brief Applies an edit to a new graph copy, leaving the source unchanged.
*
* @param source Graph model; borrowed for this call.
* @param edit Graph edit command.
* @param out_graph Receives the newly allocated graph; caller owns it on success.
* @param out_added_id Receives the added id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_apply_edit(const xtc_graph *source, const xtc_graph_edit *edit, xtc_graph **out_graph,
size_t *out_added_id);
/**
* @brief Allocates an empty graph with the requested node capacity.
*
* @param max_nodes Maximum number of graph nodes.
* @param out_graph Receives the newly allocated graph; caller owns it on success.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_create(size_t max_nodes, xtc_graph **out_graph);
/**
* @brief Releases graph storage; accepts NULL.
*
* @param graph Graph model; borrowed for this call.
*/
void xtc_graph_destroy(xtc_graph *graph);
/**
* @brief Adds a gain node with a finite linear gain value.
*
* @param graph Graph model; borrowed for this call.
* @param gain Linear gain, 0 to 4 for track controls.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_gain(xtc_graph *graph, float gain, size_t *out_node_id);
/**
* @brief Connects a source's output port 1 to a destination's input port 0.
*
* @param graph Graph model; borrowed for this call.
* @param source_node Stable source node identifier.
* @param destination_node Stable destination node identifier.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_connect(xtc_graph *graph, size_t source_node, size_t destination_node);
/* Port 0 is input; port 1 is output. Oscillators, audio sources and instruments have only output 1;
* output nodes have only input 0. All ports carry mono audio. */
/**
* @brief Copies the direction, signal type, and channel count of a node port.
*
* @param graph Graph model; borrowed for this call.
* @param node_id Stable node identifier.
* @param port_id Node port identifier.
* @param out_port Receives the port.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_get_port(const xtc_graph *graph, size_t node_id, size_t port_id, xtc_port_info *out_port);
/**
* @brief Adds a validated audio connection while preserving an acyclic graph.
*
* @param graph Graph model; borrowed for this call.
* @param source_node Stable source node identifier.
* @param source_port Source output port identifier.
* @param destination_node Stable destination node identifier.
* @param destination_port Destination input port identifier.
* @return XTC_OK on success, or an error code describing the failure.
*/
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);
/**
* @brief Adds a sine oscillator node with a positive frequency in hertz.
*
* @param graph Graph model; borrowed for this call.
* @param frequency Oscillator frequency in hertz.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_oscillator(xtc_graph *graph, float frequency, size_t *out_node_id);
/**
* @brief Adds a pass-through node that records the last block's absolute peak.
*
* @param graph Graph model; borrowed for this call.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_meter(xtc_graph *graph, size_t *out_node_id);
/**
* @brief Adds a source node for the separately rendered MIDI instrument buffer.
*
* @param graph Graph model; borrowed for this call.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_instrument(xtc_graph *graph, size_t *out_node_id);
/**
* @brief Adds a source node for the separately rendered timeline audio buffer.
*
* @param graph Graph model; borrowed for this call.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_audio(xtc_graph *graph, size_t *out_node_id);
/**
* @brief Adds an explicit output node included in the final mix.
*
* @param graph Graph model; borrowed for this call.
* @param out_node_id Receives the node id.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_add_output(xtc_graph *graph, size_t *out_node_id);
/* Compile copies graph state; caller owns the plan. Sample rate must be
* finite and positive, oscillator frequencies <= Nyquist. max_frames: 1..65536.
* Editing/destroying the graph never changes an existing plan. */
/**
* @brief Allocates an independent execution plan with preallocated processing buffers.
*
* @param graph Graph model; borrowed for this call.
* @param sample_rate Sample rate in hertz.
* @param max_frames Maximum frames per processing block.
* @param out_plan Receives the newly allocated plan; caller owns it on success.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_compile(const xtc_graph *graph, double sample_rate, size_t max_frames, xtc_plan **out_plan);
/**
* @brief Releases a compiled plan and its processing state; accepts NULL.
*
* @param plan Compiled graph plan and its single-owner runtime state.
*/
void xtc_plan_destroy(xtc_plan *plan);
/* Single processing owner, no concurrent calls (including meter reads/reset).
* No allocation, I/O, locking or graph validation. NULL input means silence.
* output must hold frame_count floats; input may equal output. On invalid
* arguments output and DSP state are unchanged. Zero frames is a no-op.
* Sums explicit output nodes; if absent, sums terminal nodes for compatibility
* unless require_output_nodes is set. Empty graphs produce silence.
* Unconnected gain/meter/output inputs receive external input. */
/* Separate mono source buffers; NULL is silence. Existing wrappers omit MIDI. */
/* Stereo graph with paired source buffers and a mono external input duplicated
* to both sides. Output buffers must not overlap each other; either may equal a source.
* Stereo oscillator phase advances once per frame. NULL right selects mono. */
/**
* @brief Processes a block with separate stereo timeline and instrument sources.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @param input Borrowed input sample buffer.
* @param audio_left Borrowed left timeline source buffer; NULL means silence.
* @param audio_right Borrowed right timeline source buffer; NULL means silence.
* @param midi_left Borrowed left instrument source buffer; NULL means silence.
* @param midi_right Borrowed right instrument source buffer; NULL means silence.
* @param left Destination left-channel buffer.
* @param right Destination right-channel buffer; NULL selects mono rendering.
* @param frames Number of audio frames.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_process_stereo(xtc_plan * plan, const float *input, const float *audio_left, const float *audio_right,
const float *midi_left, const float *midi_right, float *left, float *right,
size_t frames);
/**
* @brief Processes a mono block with separate timeline and instrument sources.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @param input Borrowed input sample buffer.
* @param timeline Borrowed timeline source buffer; NULL means silence.
* @param instrument Borrowed instrument source buffer; NULL means silence.
* @param output Destination buffer with room for the requested frames.
* @param frames Number of audio frames.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_process_sources(xtc_plan *plan, const float *input, const float *timeline, const float *instrument,
float *output, size_t frames);
/* timeline is a separate mono input for XTC_NODE_AUDIO, NULL means silence. */
/**
* @brief Processes a mono block with timeline audio and no instrument source.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @param input Borrowed input sample buffer.
* @param timeline Borrowed timeline source buffer; NULL means silence.
* @param output Destination buffer with room for the requested frames.
* @param frames Number of audio frames.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_process_audio(xtc_plan *plan, const float *input, const float *timeline, float *output,
size_t frames);
/**
* @brief Processes a mono block with external input and no timeline or instrument sources.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @param input Borrowed input sample buffer.
* @param output Destination buffer with room for the requested frames.
* @param frame_count Number of audio frames.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_process(xtc_plan *plan, const float *input, float *output, size_t frame_count);
/* Resets oscillator phases and meters, without allocation. */
/**
* @brief Resets oscillator phases, meters, and native processor state without allocating.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_reset(xtc_plan *plan);
/* Last block absolute peak, valid for meter nodes only. */
/**
* @brief Copies the last block's absolute peak for a meter node.
*
* @param plan Compiled graph plan and its single-owner runtime state.
* @param node_id Stable node identifier.
* @param out_peak Receives the peak.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_plan_meter_peak(const xtc_plan *plan, size_t node_id, float *out_peak);
/**
* @brief Checks graph validity by attempting a topological ordering.
*
* @param graph Graph model; borrowed for this call.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_validate(const xtc_graph *graph);
/* Allocating compatibility helper: compiles at 48000 Hz and processes once.
* Requires non-NULL input/output and finite input_gain. Not for callbacks. */
/**
* @brief Compiles at 48000 Hz, applies input gain, renders once, and releases temporary state.
*
* @param graph Graph model; borrowed for this call.
* @param input Borrowed input sample buffer.
* @param output Destination buffer with room for the requested frames.
* @param frame_count Number of audio frames.
* @param input_gain Finite linear gain applied to external input before processing.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result xtc_graph_process_offline(xtc_graph *graph, const float *input, float *output, size_t frame_count,
float input_gain);
#ifdef __cplusplus
}
#endif
#endif