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//! ```
21//!
22//! ## Key Concepts
23//!
24//! - **Scalar**: Base numeric trait for any type (floats and integers)
25//! - **Transcendental**: Float numeric abstraction with sin/cos/sqrt
26//! - **AtomicCell**: Safe atomic wrapper for lock-free data structures
27//! - **Node**: Base trait for all nodes in the signal graph
28//! - **Source**: Active generators (oscillators, file readers)
29//! - **Processor**: Passive processors (filters, effects)
30//! - **Sink**: Active outputs (I/O devices, file writers)
31//! - **PipeBuffer**: Zero-copy connections between nodes
32//! - **CommandQueue**: Real-time safe parameter automation
33//! - **ClockTick**: Sample-accurate timing for synchronization
34//!
35//! ## Example
36//!
37//! ```rust
38//! use rill_core::prelude::*;
39//! use rill_core::Port;
40//! use rill_core::traits::node;
41//!
42//! // Create a simple sine source
43//! struct MySine<T: Transcendental, const BUF_SIZE: usize> {
44//!     frequency: T,
45//!     amplitude: T,
46//!     phase: T,
47//!     sample_rate: T,
48//! }
49//!
50//! impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for MySine<T, BUF_SIZE> {
51//!     fn metadata(&self) -> NodeMetadata {
52//!         NodeMetadata {
53//!             name: "Sine".to_string(),
54//!             type_name: None,
55//!             category: NodeCategory::Source,
56//!             description: "Sine wave oscillator".to_string(),
57//!             author: "Rill".to_string(),
58//!             version: env!("CARGO_PKG_VERSION").to_string(),
59//!             signal_inputs: 0,
60//!             signal_outputs: 1,
61//!             control_inputs: 0,
62//!             control_outputs: 0,
63//!             clock_inputs: 1,
64//!             clock_outputs: 0,
65//!             feedback_ports: 0,
66//!             parameters: vec![],
67//!         }
68//!     }
69//!     
70//!     fn init(&mut self, sample_rate: f32) {
71//!         self.sample_rate = T::from_f32(sample_rate);
72//!     }
73//!     
74//!     fn reset(&mut self) {
75//!         self.phase = T::ZERO;
76//!     }
77//!     
78//!     fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
79//!         None
80//!     }
81//!     
82//!     fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
83//!         Ok(())
84//!     }
85//!     
86//!     fn id(&self) -> NodeId { NodeId(0) }
87//!     fn set_id(&mut self, _id: NodeId) {}
88//!     
89//!     fn input_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
90//!     fn input_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
91//!     fn output_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
92//!     fn output_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
93//!     fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> { None }
94//!     fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> { None }
95//!     
96//!     fn state(&self) -> &node::NodeState<T,BUF_SIZE> {
97//!         unimplemented!()
98//!     }
99//!     
100//!     fn state_mut(&mut self) -> &mut node::NodeState<T,BUF_SIZE> {
101//!         unimplemented!()
102//!     }
103//! }
104//!
105//! impl<T: Transcendental, const BUF_SIZE: usize> Source<T, BUF_SIZE> for MySine<T, BUF_SIZE> {
106//!     fn generate(
107//!         &mut self,
108//!         ctx: &RenderContext,
109//!         _control_inputs: &[T],
110//!         _clock_inputs: &[RenderContext],
111//!         _tick: &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(ctx.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().write() = 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// ============================================================================
179// Error Types
180// ============================================================================
181
182/// Core error types for the Rill ecosystem
183mod error;
184pub use error::*;
185
186// ============================================================================
187// Re-exports for Convenience
188// ============================================================================
189
190// Re-export core traits
191pub use traits::{
192    ClockError, ClockResult, ConnectionError, ConnectionResult, Eurorack, Node, NodeCategory,
193    NodeId, NodeMetadata, NodeState, NodeTypeId, ParamMetadata, ParamRange, ParamType, ParamValue,
194    ParameterError, ParameterId, Params, Port, PortDirection, PortError, PortId, PortResult,
195    PortType, ProcessError, ProcessResult, Processor, Sink, Source,
196};
197
198// Re-export math abstractions
199pub use math::{Scalar, Transcendental};
200
201// Re-export buffer types with AtomicCell safety
202pub use buffer::{
203    AtomicCell, AtomicCellError, AtomicStats, Buffer, BufferError, BufferResult, BufferStats,
204    DelayLine, FanInBuffer, FanOutBuffer, PipeBuffer, RingBuffer,
205};
206
207// Re-export queue types (from rill-patchbay integration)
208pub use queues::{QueueError, QueueResult};
209
210// Re-export time abstractions
211pub use time::{ClockSource, ClockTick, RenderContext, SystemClock};
212
213// ============================================================================
214// Constants
215// ============================================================================
216
217/// Current version of rill-core
218pub const VERSION: &str = env!("CARGO_PKG_VERSION");
219
220/// Maximum supported sample rate
221pub const MAX_SAMPLE_RATE: f32 = 384_000.0;
222
223/// Minimum supported sample rate
224pub const MIN_SAMPLE_RATE: f32 = 8_000.0;
225
226/// Default sample rate (44.1 kHz)
227pub const DEFAULT_SAMPLE_RATE: f32 = 44_100.0;
228
229/// Default block size for signal processing
230pub const DEFAULT_BLOCK_SIZE: usize = 64;
231
232/// Maximum block size
233pub const MAX_BLOCK_SIZE: usize = 8192;
234
235/// Minimum block size
236pub const MIN_BLOCK_SIZE: usize = 16;
237
238/// Default buffer size for most use cases
239pub const DEFAULT_BUFFER_SIZE: usize = 1024;
240
241/// Maximum buffer size (2^16 = 65536 samples)
242pub const MAX_BUFFER_SIZE: usize = 65536;
243
244/// Minimum buffer size
245pub const MIN_BUFFER_SIZE: usize = 16;
246
247/// Cache line size for alignment (64 bytes on x86_64)
248pub const CACHE_LINE_SIZE: usize = 64;
249
250// ============================================================================
251// Utility Functions
252// ============================================================================
253
254/// Utility functions for common operations
255pub mod utils {
256    use crate::math::Transcendental;
257
258    /// Convert seconds to samples
259    #[inline(always)]
260    pub fn seconds_to_samples(seconds: f32, sample_rate: f32) -> usize {
261        (seconds * sample_rate) as usize
262    }
263
264    /// Convert samples to seconds
265    #[inline(always)]
266    pub fn samples_to_seconds(samples: usize, sample_rate: f32) -> f32 {
267        samples as f32 / sample_rate
268    }
269
270    /// Convert dB to linear gain
271    #[inline(always)]
272    pub fn db_to_linear<T: Transcendental>(db: T) -> T {
273        T::from_f32(10.0_f32.powf(db.to_f32() / 20.0))
274    }
275
276    /// Convert linear gain to dB
277    #[inline(always)]
278    pub fn linear_to_db<T: Transcendental>(linear: T) -> T {
279        T::from_f32(20.0 * linear.to_f32().log10())
280    }
281
282    /// Check if a value is a power of two
283    #[inline(always)]
284    pub const fn is_power_of_two(x: usize) -> bool {
285        x != 0 && (x & (x - 1)) == 0
286    }
287
288    /// Round up to the next power of two
289    #[inline(always)]
290    pub const fn next_power_of_two(x: usize) -> usize {
291        let mut n = x - 1;
292        n |= n >> 1;
293        n |= n >> 2;
294        n |= n >> 4;
295        n |= n >> 8;
296        n |= n >> 16;
297        n + 1
298    }
299}
300
301// ============================================================================
302// Version Information
303// ============================================================================
304
305/// Get detailed version information
306pub fn version_info() -> VersionInfo {
307    VersionInfo {
308        version: VERSION,
309        crate_name: env!("CARGO_PKG_NAME"),
310        authors: env!("CARGO_PKG_AUTHORS"),
311        description: env!("CARGO_PKG_DESCRIPTION"),
312        repository: env!("CARGO_PKG_REPOSITORY"),
313    }
314}
315
316/// Detailed version information for the rill-core crate.
317#[derive(Debug, Clone)]
318pub struct VersionInfo {
319    /// Crate version string (from `CARGO_PKG_VERSION`).
320    pub version: &'static str,
321    /// Crate name (from `CARGO_PKG_NAME`).
322    pub crate_name: &'static str,
323    /// Author list (from `CARGO_PKG_AUTHORS`).
324    pub authors: &'static str,
325    /// Crate description (from `CARGO_PKG_DESCRIPTION`).
326    pub description: &'static str,
327    /// Repository URL (from `CARGO_PKG_REPOSITORY`).
328    pub repository: &'static str,
329}
330
331// ============================================================================
332// Tests
333// ============================================================================
334
335#[cfg(test)]
336mod tests {
337    use super::prelude::*;
338    use super::utils;
339
340    #[test]
341    fn test_constants() {
342        assert!(!VERSION.is_empty());
343        const {
344            assert!(MAX_SAMPLE_RATE > MIN_SAMPLE_RATE);
345            assert!(MAX_BLOCK_SIZE > MIN_BLOCK_SIZE);
346        }
347        assert_eq!(DEFAULT_BLOCK_SIZE, 64);
348        assert_eq!(DEFAULT_SAMPLE_RATE, 44100.0);
349        assert_eq!(CACHE_LINE_SIZE, 64);
350    }
351
352    #[test]
353    fn test_utils() {
354        assert_eq!(utils::seconds_to_samples(1.0, 44100.0), 44100);
355        assert!((utils::samples_to_seconds(44100, 44100.0) - 1.0).abs() < 1e-6);
356
357        let linear = utils::db_to_linear(0.0f32);
358        assert!((linear - 1.0).abs() < 1e-6);
359
360        let db = utils::linear_to_db(1.0f32);
361        assert!((db - 0.0).abs() < 1e-6);
362
363        assert!(utils::is_power_of_two(64));
364        assert!(!utils::is_power_of_two(63));
365        assert_eq!(utils::next_power_of_two(63), 64);
366    }
367
368    #[test]
369    fn test_atomic_cell() {
370        let cell = AtomicCell::new(42);
371        assert_eq!(cell.load(), 42);
372        cell.store(100);
373        assert_eq!(cell.load(), 100);
374    }
375}
376
377// ============================================================================
378// Documentation Tests
379// ============================================================================
380
381#[cfg(doctest)]
382mod doctests {
383    //! This module exists only to host documentation tests
384}