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