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//!         ctx: &RenderContext,
110//!         _control_inputs: &[T],
111//!         _clock_inputs: &[RenderContext],
112//!         _tick: &ClockTick,
113//!     ) -> ProcessResult<()> {
114//!         let two_pi = T::from_f32(2.0 * std::f32::consts::PI);
115//!         let phase_inc = self.frequency / T::from_f32(ctx.sample_rate);
116//!         let amp = self.amplitude;
117//!         
118//!         let mut temp = [T::ZERO; BUF_SIZE];
119//!         for i in 0..BUF_SIZE {
120//!             let phase_rad = self.phase * two_pi;
121//!             temp[i] = phase_rad.sin() * amp;
122//!             self.phase = self.phase + phase_inc;
123//!             if self.phase >= T::from_f32(1.0) {
124//!                 self.phase = self.phase - T::from_f32(1.0);
125//!             }
126//!         }
127//!         *self.output_port_mut(0).unwrap().buffer.as_mut_array() = temp;
128//!         Ok(())
129//!     }
130//!     
131//!     fn num_signal_outputs(&self) -> usize { 1 }
132//!     fn num_control_inputs(&self) -> usize { 0 }
133//!     fn num_clock_inputs(&self) -> usize { 1 }
134//! }
135//! ```
136
137#![warn(missing_docs)]
138#![allow(clippy::doc_lazy_continuation)]
139#![deny(unsafe_code)]
140#![cfg_attr(not(test), deny(unused))]
141#![cfg_attr(docsrs, feature(doc_cfg))]
142#![allow(deprecated)]
143
144// ============================================================================
145// Core Modules
146// ============================================================================
147
148/// Core traits for the Rill ecosystem
149pub mod traits;
150
151/// Mathematical abstractions for signal processing
152pub mod math;
153
154/// Lock-free, real-time safe signal buffers
155pub mod buffer;
156
157/// Real-time safe command queues for automation
158pub mod queues;
159
160/// Time and clock abstractions for synchronization
161pub mod time;
162
163#[doc(hidden)]
164pub use math::vector;
165
166/// Macros for node creation and boilerplate reduction
167#[macro_use]
168pub mod macros;
169
170/// Convenience prelude for importing common types
171pub mod prelude;
172
173/// Fractional-index interpolation trait for slice-like types
174pub mod interpolate;
175
176/// Generic multi-channel signal I/O abstraction
177pub mod io;
178
179/// Graph executor for driving signal processing
180pub mod executor;
181
182// ============================================================================
183// Error Types
184// ============================================================================
185
186/// Core error types for the Rill ecosystem
187mod error;
188pub use error::*;
189
190// ============================================================================
191// Re-exports for Convenience
192// ============================================================================
193
194// Re-export core traits
195pub use traits::{
196    ClockError, ClockResult, ConnectionError, ConnectionResult, Eurorack, Node, NodeCategory,
197    NodeId, NodeMetadata, NodeState, NodeTypeId, ParamMetadata, ParamRange, ParamType, ParamValue,
198    ParameterError, ParameterId, Params, Port, PortDirection, PortError, PortId, PortResult,
199    PortType, ProcessError, ProcessResult, Processor, Sink, Source,
200};
201
202// Re-export math abstractions
203pub use math::{Scalar, Transcendental};
204
205// Re-export buffer types with AtomicCell safety
206pub use buffer::{
207    AtomicCell, AtomicCellError, AtomicStats, Buffer, BufferError, BufferResult, BufferStats,
208    DelayLine, FanInBuffer, FanOutBuffer, PipeBuffer, RingBuffer,
209};
210
211// Re-export queue types (from rill-patchbay integration)
212pub use queues::{QueueError, QueueResult};
213
214// Re-export time abstractions
215pub use time::{ClockSource, ClockTick, RenderContext, SystemClock};
216
217// ============================================================================
218// Constants
219// ============================================================================
220
221/// Current version of rill-core
222pub const VERSION: &str = env!("CARGO_PKG_VERSION");
223
224/// Maximum supported sample rate
225pub const MAX_SAMPLE_RATE: f32 = 384_000.0;
226
227/// Minimum supported sample rate
228pub const MIN_SAMPLE_RATE: f32 = 8_000.0;
229
230/// Default sample rate (44.1 kHz)
231pub const DEFAULT_SAMPLE_RATE: f32 = 44_100.0;
232
233/// Default block size for signal processing
234pub const DEFAULT_BLOCK_SIZE: usize = 64;
235
236/// Maximum block size
237pub const MAX_BLOCK_SIZE: usize = 8192;
238
239/// Minimum block size
240pub const MIN_BLOCK_SIZE: usize = 16;
241
242/// Default buffer size for most use cases
243pub const DEFAULT_BUFFER_SIZE: usize = 1024;
244
245/// Maximum buffer size (2^16 = 65536 samples)
246pub const MAX_BUFFER_SIZE: usize = 65536;
247
248/// Minimum buffer size
249pub const MIN_BUFFER_SIZE: usize = 16;
250
251/// Cache line size for alignment (64 bytes on x86_64)
252pub const CACHE_LINE_SIZE: usize = 64;
253
254// ============================================================================
255// Utility Functions
256// ============================================================================
257
258/// Utility functions for common operations
259pub mod utils {
260    use crate::math::Transcendental;
261
262    /// Convert seconds to samples
263    #[inline(always)]
264    pub fn seconds_to_samples(seconds: f32, sample_rate: f32) -> usize {
265        (seconds * sample_rate) as usize
266    }
267
268    /// Convert samples to seconds
269    #[inline(always)]
270    pub fn samples_to_seconds(samples: usize, sample_rate: f32) -> f32 {
271        samples as f32 / sample_rate
272    }
273
274    /// Convert dB to linear gain
275    #[inline(always)]
276    pub fn db_to_linear<T: Transcendental>(db: T) -> T {
277        T::from_f32(10.0_f32.powf(db.to_f32() / 20.0))
278    }
279
280    /// Convert linear gain to dB
281    #[inline(always)]
282    pub fn linear_to_db<T: Transcendental>(linear: T) -> T {
283        T::from_f32(20.0 * linear.to_f32().log10())
284    }
285
286    /// Check if a value is a power of two
287    #[inline(always)]
288    pub const fn is_power_of_two(x: usize) -> bool {
289        x != 0 && (x & (x - 1)) == 0
290    }
291
292    /// Round up to the next power of two
293    #[inline(always)]
294    pub const fn next_power_of_two(x: usize) -> usize {
295        let mut n = x - 1;
296        n |= n >> 1;
297        n |= n >> 2;
298        n |= n >> 4;
299        n |= n >> 8;
300        n |= n >> 16;
301        n + 1
302    }
303}
304
305// ============================================================================
306// Version Information
307// ============================================================================
308
309/// Get detailed version information
310pub fn version_info() -> VersionInfo {
311    VersionInfo {
312        version: VERSION,
313        crate_name: env!("CARGO_PKG_NAME"),
314        authors: env!("CARGO_PKG_AUTHORS"),
315        description: env!("CARGO_PKG_DESCRIPTION"),
316        repository: env!("CARGO_PKG_REPOSITORY"),
317    }
318}
319
320/// Detailed version information for the rill-core crate.
321#[derive(Debug, Clone)]
322pub struct VersionInfo {
323    /// Crate version string (from `CARGO_PKG_VERSION`).
324    pub version: &'static str,
325    /// Crate name (from `CARGO_PKG_NAME`).
326    pub crate_name: &'static str,
327    /// Author list (from `CARGO_PKG_AUTHORS`).
328    pub authors: &'static str,
329    /// Crate description (from `CARGO_PKG_DESCRIPTION`).
330    pub description: &'static str,
331    /// Repository URL (from `CARGO_PKG_REPOSITORY`).
332    pub repository: &'static str,
333}
334
335// ============================================================================
336// Tests
337// ============================================================================
338
339#[cfg(test)]
340mod tests {
341    use super::prelude::*;
342    use super::utils;
343
344    #[test]
345    fn test_constants() {
346        assert!(!VERSION.is_empty());
347        const {
348            assert!(MAX_SAMPLE_RATE > MIN_SAMPLE_RATE);
349            assert!(MAX_BLOCK_SIZE > MIN_BLOCK_SIZE);
350        }
351        assert_eq!(DEFAULT_BLOCK_SIZE, 64);
352        assert_eq!(DEFAULT_SAMPLE_RATE, 44100.0);
353        assert_eq!(CACHE_LINE_SIZE, 64);
354    }
355
356    #[test]
357    fn test_utils() {
358        assert_eq!(utils::seconds_to_samples(1.0, 44100.0), 44100);
359        assert!((utils::samples_to_seconds(44100, 44100.0) - 1.0).abs() < 1e-6);
360
361        let linear = utils::db_to_linear(0.0f32);
362        assert!((linear - 1.0).abs() < 1e-6);
363
364        let db = utils::linear_to_db(1.0f32);
365        assert!((db - 0.0).abs() < 1e-6);
366
367        assert!(utils::is_power_of_two(64));
368        assert!(!utils::is_power_of_two(63));
369        assert_eq!(utils::next_power_of_two(63), 64);
370    }
371
372    #[test]
373    fn test_atomic_cell() {
374        let cell = AtomicCell::new(42);
375        assert_eq!(cell.load(), 42);
376        cell.store(100);
377        assert_eq!(cell.load(), 100);
378    }
379}
380
381// ============================================================================
382// Documentation Tests
383// ============================================================================
384
385#[cfg(doctest)]
386mod doctests {
387    //! This module exists only to host documentation tests
388}