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