Skip to main content

rill_core/
lib.rs

1//! # Rill Core
2//!
3//! The core of the Rill ecosystem. Provides fundamental traits, types,
4//! and utilities for building real-time signal processing applications.
5//!
6//! ## Architecture Overview
7//!
8//! ```text
9//! rill-core/
10//! ├── traits/           # Core traits (Node, Source, Processor, Sink, etc.)
11//! ├── math/             # Mathematical abstractions (Scalar, Transcendental, Vector)
12//! │   └── vector/       # Vector types, SIMD abstractions, slice operations
13//! ├── buffer/           # Lock-free signal buffers with AtomicCell safety
14//! ├── queues/           # Real-time safe command queues
15//! ├── time/             # Time and clock abstractions (ClockTick, SystemClock)
16//! ├── io/               # Generic I/O backend trait (IoBackend)
17//! ├── macros/           # Node creation macros (source_node!, processor_node!, etc.)
18//! ├── prelude           # Convenience prelude for common imports
19//! ├── interpolate       # Fractional-index interpolation trait
20//! └── executor/         # Graph executor for driving signal processing
21//! ```
22//!
23//! ## Key Concepts
24//!
25//! - **Scalar**: Base numeric trait for any type (floats and integers)
26//! - **Transcendental**: Float numeric abstraction with sin/cos/sqrt
27//! - **AtomicCell**: Safe atomic wrapper for lock-free data structures
28//! - **Node**: Base trait for all nodes in the signal graph
29//! - **Source**: Active generators (oscillators, file readers)
30//! - **Processor**: Passive processors (filters, effects)
31//! - **Sink**: Active outputs (I/O devices, file writers)
32//! - **PipeBuffer**: Zero-copy connections between nodes
33//! - **CommandQueue**: Real-time safe parameter automation
34//! - **ClockTick**: Sample-accurate timing for synchronization
35//!
36//! ## Example
37//!
38//! ```rust
39//! use rill_core::prelude::*;
40//! use rill_core::Port;
41//! use rill_core::traits::node;
42//!
43//! // Create a simple sine source
44//! struct MySine<T: Transcendental, const BUF_SIZE: usize> {
45//!     frequency: T,
46//!     amplitude: T,
47//!     phase: T,
48//!     sample_rate: T,
49//! }
50//!
51//! impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for MySine<T, BUF_SIZE> {
52//!     fn metadata(&self) -> NodeMetadata {
53//!         NodeMetadata {
54//!             name: "Sine".to_string(),
55//!             type_name: None,
56//!             category: NodeCategory::Source,
57//!             description: "Sine wave oscillator".to_string(),
58//!             author: "Rill".to_string(),
59//!             version: env!("CARGO_PKG_VERSION").to_string(),
60//!             signal_inputs: 0,
61//!             signal_outputs: 1,
62//!             control_inputs: 0,
63//!             control_outputs: 0,
64//!             clock_inputs: 1,
65//!             clock_outputs: 0,
66//!             feedback_ports: 0,
67//!             parameters: vec![],
68//!         }
69//!     }
70//!     
71//!     fn init(&mut self, sample_rate: f32) {
72//!         self.sample_rate = T::from_f32(sample_rate);
73//!     }
74//!     
75//!     fn reset(&mut self) {
76//!         self.phase = T::ZERO;
77//!     }
78//!     
79//!     fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
80//!         None
81//!     }
82//!     
83//!     fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
84//!         Ok(())
85//!     }
86//!     
87//!     fn id(&self) -> NodeId { NodeId(0) }
88//!     fn set_id(&mut self, _id: NodeId) {}
89//!     
90//!     fn input_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
91//!     fn input_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
92//!     fn output_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
93//!     fn output_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
94//!     fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
95//!     fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
96//!     
97//!     fn state(&self) -> &node::NodeState<T,BUF_SIZE> {
98//!         unimplemented!()
99//!     }
100//!     
101//!     fn state_mut(&mut self) -> &mut node::NodeState<T,BUF_SIZE> {
102//!         unimplemented!()
103//!     }
104//! }
105//!
106//! impl<T: Transcendental, const BUF_SIZE: usize> Source<T, BUF_SIZE> for MySine<T, BUF_SIZE> {
107//!     fn generate(
108//!         &mut self,
109//!         clock: &ClockTick,
110//!         _control_inputs: &[T],
111//!         _clock_inputs: &[ClockTick],
112//!     ) -> ProcessResult<()> {
113//!         let two_pi = T::from_f32(2.0 * std::f32::consts::PI);
114//!         let phase_inc = self.frequency / T::from_f32(clock.sample_rate);
115//!         let amp = self.amplitude;
116//!         
117//!         let mut temp = [T::ZERO; BUF_SIZE];
118//!         for i in 0..BUF_SIZE {
119//!             let phase_rad = self.phase * two_pi;
120//!             temp[i] = phase_rad.sin() * amp;
121//!             self.phase = self.phase + phase_inc;
122//!             if self.phase >= T::from_f32(1.0) {
123//!                 self.phase = self.phase - T::from_f32(1.0);
124//!             }
125//!         }
126//!         *self.output_port_mut(0).unwrap().buffer.as_mut_array() = temp;
127//!         Ok(())
128//!     }
129//!     
130//!     fn num_signal_outputs(&self) -> usize { 1 }
131//!     fn num_control_inputs(&self) -> usize { 0 }
132//!     fn num_clock_inputs(&self) -> usize { 1 }
133//! }
134//! ```
135
136#![warn(missing_docs)]
137#![allow(clippy::doc_lazy_continuation)]
138#![deny(unsafe_code)]
139#![cfg_attr(not(test), deny(unused))]
140#![cfg_attr(docsrs, feature(doc_cfg))]
141#![allow(deprecated)]
142
143// ============================================================================
144// Core Modules
145// ============================================================================
146
147/// Core traits for the Rill ecosystem
148pub mod traits;
149
150/// Mathematical abstractions for signal processing
151pub mod math;
152
153/// Lock-free, real-time safe signal buffers
154pub mod buffer;
155
156/// Real-time safe command queues for automation
157pub mod queues;
158
159/// Time and clock abstractions for synchronization
160pub mod time;
161
162#[doc(hidden)]
163pub use math::vector;
164
165/// Macros for node creation and boilerplate reduction
166#[macro_use]
167pub mod macros;
168
169/// Convenience prelude for importing common types
170pub mod prelude;
171
172/// Fractional-index interpolation trait for slice-like types
173pub mod interpolate;
174
175/// Generic multi-channel signal I/O abstraction
176pub mod io;
177
178/// Graph executor for driving signal processing
179pub mod executor;
180
181// ============================================================================
182// Error Types
183// ============================================================================
184
185/// Core error types for the Rill ecosystem
186mod error;
187pub use error::*;
188
189// ============================================================================
190// Re-exports for Convenience
191// ============================================================================
192
193// Re-export core traits
194pub use traits::{
195    ClockError, ClockResult, ConnectionError, ConnectionResult, Eurorack, Node, NodeCategory,
196    NodeId, NodeMetadata, NodeState, NodeTypeId, ParamMetadata, ParamRange, ParamType, ParamValue,
197    ParameterError, ParameterId, Params, Port, PortDirection, PortError, PortId, PortResult,
198    PortType, ProcessError, ProcessResult, Processor, Sink, Source,
199};
200
201// Re-export math abstractions
202pub use math::{Scalar, Transcendental};
203
204// Re-export buffer types with AtomicCell safety
205pub use buffer::{
206    AtomicCell, AtomicCellError, AtomicStats, Buffer, BufferError, BufferResult, BufferStats,
207    DelayLine, FanInBuffer, FanOutBuffer, PipeBuffer, RingBuffer,
208};
209
210// Re-export queue types (from rill-patchbay integration)
211pub use queues::{QueueError, QueueResult};
212
213// Re-export time abstractions
214pub use time::{ClockSource, ClockTick, SystemClock};
215
216// ============================================================================
217// Constants
218// ============================================================================
219
220/// Current version of rill-core
221pub const VERSION: &str = env!("CARGO_PKG_VERSION");
222
223/// Maximum supported sample rate
224pub const MAX_SAMPLE_RATE: f32 = 384_000.0;
225
226/// Minimum supported sample rate
227pub const MIN_SAMPLE_RATE: f32 = 8_000.0;
228
229/// Default sample rate (44.1 kHz)
230pub const DEFAULT_SAMPLE_RATE: f32 = 44_100.0;
231
232/// Default block size for signal processing
233pub const DEFAULT_BLOCK_SIZE: usize = 64;
234
235/// Maximum block size
236pub const MAX_BLOCK_SIZE: usize = 8192;
237
238/// Minimum block size
239pub const MIN_BLOCK_SIZE: usize = 16;
240
241/// Default buffer size for most use cases
242pub const DEFAULT_BUFFER_SIZE: usize = 1024;
243
244/// Maximum buffer size (2^16 = 65536 samples)
245pub const MAX_BUFFER_SIZE: usize = 65536;
246
247/// Minimum buffer size
248pub const MIN_BUFFER_SIZE: usize = 16;
249
250/// Cache line size for alignment (64 bytes on x86_64)
251pub const CACHE_LINE_SIZE: usize = 64;
252
253// ============================================================================
254// Utility Functions
255// ============================================================================
256
257/// Utility functions for common operations
258pub mod utils {
259    use crate::math::Transcendental;
260
261    /// Convert seconds to samples
262    #[inline(always)]
263    pub fn seconds_to_samples(seconds: f32, sample_rate: f32) -> usize {
264        (seconds * sample_rate) as usize
265    }
266
267    /// Convert samples to seconds
268    #[inline(always)]
269    pub fn samples_to_seconds(samples: usize, sample_rate: f32) -> f32 {
270        samples as f32 / sample_rate
271    }
272
273    /// Convert dB to linear gain
274    #[inline(always)]
275    pub fn db_to_linear<T: Transcendental>(db: T) -> T {
276        T::from_f32(10.0_f32.powf(db.to_f32() / 20.0))
277    }
278
279    /// Convert linear gain to dB
280    #[inline(always)]
281    pub fn linear_to_db<T: Transcendental>(linear: T) -> T {
282        T::from_f32(20.0 * linear.to_f32().log10())
283    }
284
285    /// Check if a value is a power of two
286    #[inline(always)]
287    pub const fn is_power_of_two(x: usize) -> bool {
288        x != 0 && (x & (x - 1)) == 0
289    }
290
291    /// Round up to the next power of two
292    #[inline(always)]
293    pub const fn next_power_of_two(x: usize) -> usize {
294        let mut n = x - 1;
295        n |= n >> 1;
296        n |= n >> 2;
297        n |= n >> 4;
298        n |= n >> 8;
299        n |= n >> 16;
300        n + 1
301    }
302}
303
304// ============================================================================
305// Version Information
306// ============================================================================
307
308/// Get detailed version information
309pub fn version_info() -> VersionInfo {
310    VersionInfo {
311        version: VERSION,
312        crate_name: env!("CARGO_PKG_NAME"),
313        authors: env!("CARGO_PKG_AUTHORS"),
314        description: env!("CARGO_PKG_DESCRIPTION"),
315        repository: env!("CARGO_PKG_REPOSITORY"),
316    }
317}
318
319/// Detailed version information for the rill-core crate.
320#[derive(Debug, Clone)]
321pub struct VersionInfo {
322    /// Crate version string (from `CARGO_PKG_VERSION`).
323    pub version: &'static str,
324    /// Crate name (from `CARGO_PKG_NAME`).
325    pub crate_name: &'static str,
326    /// Author list (from `CARGO_PKG_AUTHORS`).
327    pub authors: &'static str,
328    /// Crate description (from `CARGO_PKG_DESCRIPTION`).
329    pub description: &'static str,
330    /// Repository URL (from `CARGO_PKG_REPOSITORY`).
331    pub repository: &'static str,
332}
333
334// ============================================================================
335// Tests
336// ============================================================================
337
338#[cfg(test)]
339mod tests {
340    use super::prelude::*;
341    use super::utils;
342
343    #[test]
344    fn test_constants() {
345        assert!(!VERSION.is_empty());
346        assert!(MAX_SAMPLE_RATE > MIN_SAMPLE_RATE);
347        assert!(MAX_BLOCK_SIZE > MIN_BLOCK_SIZE);
348        assert_eq!(DEFAULT_BLOCK_SIZE, 64);
349        assert_eq!(DEFAULT_SAMPLE_RATE, 44100.0);
350        assert_eq!(CACHE_LINE_SIZE, 64);
351    }
352
353    #[test]
354    fn test_utils() {
355        assert_eq!(utils::seconds_to_samples(1.0, 44100.0), 44100);
356        assert!((utils::samples_to_seconds(44100, 44100.0) - 1.0).abs() < 1e-6);
357
358        let linear = utils::db_to_linear(0.0f32);
359        assert!((linear - 1.0).abs() < 1e-6);
360
361        let db = utils::linear_to_db(1.0f32);
362        assert!((db - 0.0).abs() < 1e-6);
363
364        assert!(utils::is_power_of_two(64));
365        assert!(!utils::is_power_of_two(63));
366        assert_eq!(utils::next_power_of_two(63), 64);
367    }
368
369    #[test]
370    fn test_atomic_cell() {
371        let cell = AtomicCell::new(42);
372        assert_eq!(cell.load(), 42);
373        cell.store(100);
374        assert_eq!(cell.load(), 100);
375    }
376}
377
378// ============================================================================
379// Documentation Tests
380// ============================================================================
381
382#[cfg(doctest)]
383mod doctests {
384    //! This module exists only to host documentation tests
385}