Skip to main content

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};