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