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}