Skip to main content

maolan_engine/
lib.rs

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