vibelang_core/lib.rs
1#![warn(clippy::unwrap_used)]
2//! # vibelang-core2
3//!
4//! A clean, trait-based runtime for VibeLang that is generic over synthesis backends.
5//!
6//! This crate provides the core audio engine for VibeLang, handling timing, scheduling,
7//! audio graph management, and communication with synthesis backends like SuperCollider.
8//!
9//! ## Design Goals
10//!
11//! - **Simplicity**: One core type ([`Runtime<B>`]) with clear responsibilities
12//! - **Modularity**: Feature traits with handler delegation for testability
13//! - **Platform-agnostic**: Works for native (scsynth) and WASM (WebAudio)
14//! - **Async-first**: Uses async channels and traits for cross-platform compatibility
15//! - **Type-safe**: Newtype IDs prevent mixing up different resource types
16//!
17//! ## Architecture Overview
18//!
19//! ```text
20//! ┌─────────────────────────────────────────────────────────────┐
21//! │ Runtime<B> │
22//! │ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
23//! │ │ Message │──│ Handlers │──│ Backend<B> │ │
24//! │ │ Channel │ │ (dispatch) │ │ (scsynth/WASM) │ │
25//! │ └─────────────┘ └──────────────┘ └───────────────────┘ │
26//! │ │ │ │ │
27//! │ ┌──────▼──────┐ ┌──────▼──────┐ ┌─────────▼─────────┐ │
28//! │ │ Transport │ │ Groups │ │ Voices │ │
29//! │ │ Clock/Tempo │ │ Routing │ │ Patterns │ │
30//! │ └─────────────┘ └─────────────┘ │ Melodies │ │
31//! │ └───────────────────┘ │
32//! └─────────────────────────────────────────────────────────────┘
33//! ```
34//!
35//! The [`Runtime`] is generic over a [`Backend`] trait, allowing different synthesis
36//! engines to be used interchangeably. All operations are performed via [`Message`]s
37//! sent through an async channel, which the runtime dispatches to feature handlers.
38//!
39//! ## Quick Start
40//!
41//! ```ignore
42//! use vibelang_core::{Runtime, Message, GroupId, VoiceId, VoiceConfig};
43//! use vibelang_core::message::{TransportMessage, GroupMessage, VoiceMessage};
44//! use vibelang_core::backends::{ScsynthBackend, ScsynthProcess, ScsynthConfig};
45//! use std::time::Duration;
46//!
47//! #[tokio::main]
48//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! // 1. Start the scsynth process
50//! let process = ScsynthProcess::spawn(ScsynthConfig::default())?;
51//! process.wait_startup(Duration::from_secs(2)).await;
52//!
53//! // 2. Connect to scsynth and create the runtime
54//! let backend = ScsynthBackend::connect(&process.addr()).await?;
55//! let mut runtime = Runtime::new(backend);
56//! let handle = runtime.handle();
57//!
58//! // 3. Spawn the runtime loop
59//! tokio::spawn(async move {
60//! runtime.run().await;
61//! });
62//!
63//! // 4. Send messages to control audio
64//! handle.send(TransportMessage::SetTempo { bpm: 128.0 }.into()).await?;
65//! handle.send(GroupMessage::Create { id: GroupId::new(1), name: "main".to_string(), parent: None }.into()).await?;
66//! handle.send(TransportMessage::Start.into()).await?;
67//!
68//! Ok(())
69//! }
70//! ```
71//!
72//! ## Feature Flags
73//!
74//! - `native` (default): Enables native platform support with scsynth backend
75//! - `midi`: Enables MIDI device enumeration and I/O via midir
76//!
77//! ## Module Overview
78//!
79//! | Module | Description |
80//! |--------|-------------|
81//! | [`types`] | Type-safe IDs ([`NodeId`], [`GroupId`], etc.), time types ([`Beat`], [`Duration`]) |
82//! | [`traits`] | Feature trait definitions ([`Transport`], [`Groups`], [`Voices`], etc.) |
83//! | [`handlers`] | Trait implementations that manage state and backend calls |
84//! | [`backends`] | Backend implementations ([`ScsynthBackend`], process management) |
85//! | [`synthdefs`] | Built-in SynthDef generation for essential audio routing |
86//!
87//! ## Key Concepts
88//!
89//! ### Messages
90//!
91//! All runtime mutations happen through [`Message`]s. Messages are organized by feature:
92//!
93//! - [`TransportMessage`](message::TransportMessage): Tempo, time signature, play/stop
94//! - [`GroupMessage`](message::GroupMessage): Audio bus groups for mixing
95//! - [`VoiceMessage`](message::VoiceMessage): Synth voice creation and triggering
96//! - [`PatternMessage`](message::PatternMessage): Step sequencer patterns
97//! - [`MelodyMessage`](message::MelodyMessage): Note sequences with timing
98//!
99//! ### Groups and Routing
100//!
101//! Audio flows through a hierarchy of groups. Each group has its own audio bus,
102//! and `system_link_audio` synths route audio from child buses to parent buses:
103//!
104//! ```text
105//! Group 1 (drums) ──┐
106//! ├──► Main Output (bus 0)
107//! Group 2 (bass) ──┘
108//! ```
109//!
110//! ### Timing
111//!
112//! The [`Beat`] type uses fixed-point arithmetic (16 fractional bits) for
113//! sample-accurate timing without floating-point drift over long sessions.
114
115// Core modules
116mod backend;
117mod clock;
118pub mod compat;
119mod error;
120mod runtime;
121mod state;
122mod transport_snapshot;
123
124// Public modules
125pub mod audio;
126pub mod backends;
127pub mod handlers;
128pub mod message;
129pub mod reload;
130pub mod synthdefs;
131pub mod traits;
132pub mod types;
133pub mod validation;
134
135#[cfg(feature = "midi")]
136pub mod midi;
137
138// Re-exports for convenience
139pub use audio::{AudioConnector, AudioError, PortDirection, PortMatcher};
140pub use backend::{AddAction, Backend, BufferInfo};
141pub use error::{Error, Result};
142
143pub use message::{
144 EffectMessage, FadeMessage, GroupMessage, MelodyMessage, Message, PatternMessage,
145 ReloadMessage, SampleMessage, SequenceMessage, SyncMessage, SynthDefMessage, TransportMessage,
146 VoiceMessage,
147};
148pub use runtime::{Runtime, RuntimeHandle};
149pub use transport_snapshot::TransportSnapshot;
150pub use state::{
151 ActiveFade, EffectState, GroupState, MelodyState, MeterLevel, PatternState, SequenceState,
152 SfzInstrumentState, SfzRegionState, State, VoiceState,
153};
154
155// Re-export the scsynth backend when available (native only)
156#[cfg(all(feature = "native", not(target_arch = "wasm32")))]
157pub use backends::{
158 setup_metering, setup_node_tracking, ProcessError, ScsynthBackend, ScsynthConfig,
159 ScsynthProcess,
160};
161
162// Re-export the web backend for WASM
163#[cfg(target_arch = "wasm32")]
164pub use backends::{WebScsynthBackend, WebScsynthError};
165
166// Re-export types at crate root for convenience
167pub use types::{
168 ids::{
169 BufferId, BusId, EffectId, GroupId, MelodyId, NodeId, PatternId, SampleId, SequenceId,
170 VoiceId,
171 },
172 params::ParamMap,
173 time::{Beat, Duration, TimeSignature},
174};
175
176#[cfg(feature = "midi")]
177pub use types::ids::MidiDeviceId;
178
179// Re-export config types (always available)
180pub use traits::{
181 Clip, FadeConfig, FadeCurve, FadeTarget, MelodyConfig, NoteEvent, PatternConfig, SampleConfig,
182 SampleInfo, SequenceConfig, SfzConfig, Step, VoiceConfig,
183};
184
185// Re-export traits at crate root (native only, as they require Send+Sync)
186#[cfg(not(target_arch = "wasm32"))]
187pub use traits::{
188 Effects, Fades, Groups, Melodies, Patterns, Samples, Sequences, SynthDefs, Transport, Voices,
189};
190
191// Re-export validation (native only)
192#[cfg(not(target_arch = "wasm32"))]
193pub use validation::{limits, Validate};
194
195#[cfg(feature = "midi")]
196pub use traits::{Midi, MidiDeviceInfo};
197
198#[cfg(feature = "midi")]
199pub use midi::{
200 decode_packed_midi, pack_cc, pack_note_off, pack_note_on, pack_pitch_bend, parse_note_name,
201 trigger_ids, CallbackData, CallbackType, CcRouteBuilder, KeyboardRouteBuilder, MidiCallbacks,
202 MidiData, MidiDeviceSender, MidiRealtimeConfig, MidiRealtimeService, MidiRealtimeStats,
203 MidiTriggerType, NoteRouteBuilder, ParameterCurve, QueuedMidiEvent, StoredCallback,
204 VelocityCurve, VelocityMapping,
205};
206
207#[cfg(all(feature = "midi", not(target_arch = "wasm32")))]
208pub use handlers::{MidiEventNotification, MidiHandler, MidiMessage};