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