Skip to main content

maolan_engine/
lib.rs

1mod audio;
2pub mod audio_codec;
3#[cfg(target_os = "macos")]
4pub mod audio_devices {
5    pub use crate::hw::coreaudio::{
6        AudioDeviceDescriptor, default_input_device_id, default_output_device_id,
7        discover_coreaudio_audio_devices,
8    };
9}
10#[cfg(target_os = "freebsd")]
11pub mod audio_devices {
12    pub use crate::hw::freebsd::{AudioDeviceDescriptor, discover_freebsd_audio_devices};
13}
14pub mod client;
15pub mod connectable;
16mod engine;
17pub use engine::Engine;
18pub mod executor;
19pub mod history;
20mod hw;
21pub mod kind;
22pub mod loudness;
23pub use loudness::LoudnessValues;
24pub mod message;
25pub mod meter;
26pub mod midi;
27pub mod modulator;
28mod osc;
29#[cfg(unix)]
30mod pitch_shift;
31mod plan_builder;
32pub mod plugins;
33pub mod render_plan;
34mod routing;
35pub mod simd;
36pub mod state;
37mod track;
38pub mod triple_buffer;
39pub mod workers;
40pub use workers::worker;
41
42pub use plugins::clap_proc;
43#[cfg(unix)]
44pub use plugins::lv2_proc;
45pub use plugins::vst3_proc;
46
47pub mod clap {
48    pub use crate::plugins::types::is_supported_clap_binary;
49    pub use crate::plugins::types::{
50        ClapMidiOutputEvent, ClapParameterInfo, ClapPluginInfo, ClapPluginState,
51    };
52}
53pub mod vst3 {
54    pub use crate::plugins::types::{Vst3PluginInfo, Vst3PluginState};
55    pub mod interfaces {
56        pub use crate::plugins::types::Vst3GuiInfo;
57    }
58    pub mod port {
59        pub use crate::plugins::types::ParameterInfo;
60    }
61    pub mod state {
62        pub use crate::plugins::types::Vst3PluginState;
63    }
64}
65#[cfg(unix)]
66pub mod lv2 {
67    pub use crate::plugins::types::Lv2PluginInfo;
68}
69
70use tokio::sync::mpsc::{Sender, channel};
71use tokio::task::JoinHandle;
72
73pub fn enable_flush_denormals_to_zero() {
74    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
75    unsafe {
76        let mut mxcsr: u32 = 0;
77        std::arch::asm!("stmxcsr [{}]", in(reg) &mut mxcsr);
78        mxcsr |= 0x8040;
79        std::arch::asm!("ldmxcsr [{}]", in(reg) &mxcsr);
80    }
81
82    #[cfg(target_arch = "aarch64")]
83    unsafe {
84        let mut fpcr: u64;
85        std::arch::asm!("mrs {0}, fpcr", out(reg) fpcr);
86        fpcr |= 1 << 24;
87        std::arch::asm!("msr fpcr, {0}", in(reg) fpcr);
88    }
89}
90
91/// RAII guard that restores the previous Windows timer resolution on drop.
92#[cfg(target_os = "windows")]
93pub struct WindowsTimerResolutionGuard;
94
95#[cfg(target_os = "windows")]
96impl Drop for WindowsTimerResolutionGuard {
97    fn drop(&mut self) {
98        #[link(name = "winmm")]
99        unsafe extern "system" {
100            fn timeEndPeriod(period: u32) -> u32;
101        }
102        unsafe {
103            timeEndPeriod(1);
104        }
105    }
106}
107
108/// Request a 1 ms Windows timer resolution so that tokio's 1 ms poll interval
109/// (and other waits) actually fire near 1 ms instead of being rounded up to
110/// the default ~15.6 ms quantum. Without this, the engine's dependent node
111/// chain is processed too slowly on Windows and WASAPI output underruns.
112#[cfg(target_os = "windows")]
113pub fn enable_windows_high_resolution_timer() -> Option<WindowsTimerResolutionGuard> {
114    #[link(name = "winmm")]
115    unsafe extern "system" {
116        fn timeBeginPeriod(period: u32) -> u32;
117    }
118    unsafe {
119        if timeBeginPeriod(1) == 0 {
120            tracing::info!("Windows timer resolution set to 1 ms");
121            Some(WindowsTimerResolutionGuard)
122        } else {
123            tracing::warn!("Failed to set Windows timer resolution to 1 ms");
124            None
125        }
126    }
127}
128
129pub type EngineInit = (
130    Sender<message::Message>,
131    JoinHandle<()>,
132    triple_buffer::TripleBufferConsumer<meter::MeterSnapshot>,
133    triple_buffer::TripleBufferConsumer<meter::TransportSnapshot>,
134    triple_buffer::TripleBufferConsumer<meter::SessionRuntimeSnapshot>,
135);
136
137pub fn init() -> EngineInit {
138    let command_queue_capacity = num_cpus::get().saturating_mul(4).max(128);
139    let (tx, rx) = channel::<message::Message>(command_queue_capacity);
140    let (meter_producer, meter_consumer) =
141        triple_buffer::triple_buffer(meter::MeterSnapshot::default());
142    let (transport_producer, transport_consumer) =
143        triple_buffer::triple_buffer(meter::TransportSnapshot::default());
144    let (session_runtime_producer, session_runtime_consumer) =
145        triple_buffer::triple_buffer(meter::SessionRuntimeSnapshot::default());
146    let mut engine = engine::Engine::new_with_snapshots(
147        rx,
148        tx.clone(),
149        meter_producer,
150        transport_producer,
151        session_runtime_producer,
152    );
153    let handle = tokio::spawn(async move {
154        engine.init().await;
155        engine.work().await;
156    });
157    (
158        tx.clone(),
159        handle,
160        meter_consumer,
161        transport_consumer,
162        session_runtime_consumer,
163    )
164}