rill_core/executor/mod.rs
1//! Graph executor for driving signal processing using the topology graph and active ports.
2//!
3//! The `GraphExecutor` coordinates clock ticks, pulls data from input ports,
4//! calls node processing, and pushes data to output ports.
5//!
6//! This module provides a prototype implementation that demonstrates the active ports flow.
7//! A real implementation would integrate with a concrete graph (e.g., `rill_graph::SignalGraph`).
8//!
9//! # Example
10//! ```
11//! use rill_core::executor::GraphExecutor;
12//! use rill_core::math::Transcendental;
13//!
14//! // Create a graph executor (graph omitted for prototype).
15//! // let mut executor = GraphExecutor::new(graph);
16//! // executor.process_block().unwrap();
17//! ```
18
19use crate::math::Transcendental;
20use crate::traits::ActivePort;
21
22/// Executor for a signal graph that processes nodes in topological order.
23///
24/// This is a prototype that outlines the structure. In a real implementation,
25/// the executor would hold a concrete graph and iterate over its nodes.
26pub struct GraphExecutor<T: Transcendental, const BUF_SIZE: usize> {
27 // Placeholder for the signal graph.
28 // In reality, this would be something like `graph: rill_graph::SignalGraph<T, BUF_SIZE>`.
29 _phantom: std::marker::PhantomData<T>,
30}
31
32impl<T: Transcendental, const BUF_SIZE: usize> Default for GraphExecutor<T, BUF_SIZE> {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl<T: Transcendental, const BUF_SIZE: usize> GraphExecutor<T, BUF_SIZE> {
39 /// Create a new executor from an existing graph.
40 pub fn new() -> Self {
41 Self {
42 _phantom: std::marker::PhantomData,
43 }
44 }
45
46 /// Process one block of audio.
47 ///
48 /// This advances the clock, obtains topological order, processes each node
49 /// by pulling inputs, calling appropriate processing method, and pushing outputs.
50 ///
51 /// # Returns
52 /// `Ok(())` if processing succeeded, or an error if something went wrong.
53 ///
54 /// # Algorithm (conceptual)
55 /// 1. Advance the clock (if the graph provides a clock source).
56 /// 2. Obtain topological order (if the graph provides it).
57 /// 3. For each node in order:
58 /// a. Gather input data by calling `pull` on each input port (ActivePort trait).
59 /// b. Determine node type (Source/Processor/Sink) via metadata.
60 /// c. Call the appropriate processing method (generate/process/consume).
61 /// d. Push output data by calling `push` on each output port.
62 /// 4. Handle errors (disconnected ports, missing data).
63 pub fn process_block(&mut self) -> Result<(), Box<dyn std::error::Error>> {
64 // This is a stub implementation. A real executor would need access to
65 // the graph's nodes and connections, which is beyond the scope of this prototype.
66 Ok(())
67 }
68}
69
70/// Example of using ActivePort trait to pull data from an input port.
71/// This function illustrates the intended pattern.
72pub fn demonstrate_pull<T: Transcendental, const BUF_SIZE: usize>(
73 port: &mut dyn ActivePort<T, BUF_SIZE>,
74) -> Option<[T; BUF_SIZE]> {
75 // Pull data from the port (if connected).
76 port.pull()
77}
78
79/// Example of pushing data to an output port.
80pub fn demonstrate_push<T: Transcendental, const BUF_SIZE: usize>(
81 port: &mut dyn ActivePort<T, BUF_SIZE>,
82 data: [T; BUF_SIZE],
83) -> Result<(), crate::traits::PortError> {
84 port.push(data)
85}