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