pub struct AudioHandle { /* private fields */ }Expand description
A running audio stream driving a Plugin.
Dropping the handle stops playback (the underlying device stream is released).
While it lives, the plugin keeps running on the audio thread; use Self::lock
to send MIDI or change parameters from your control thread.
§Thread affinity
AudioHandle owns the device stream, which backends make thread-affine (cpal’s Stream
is !Send for exactly this reason: open, control and drop must happen on one thread).
So the handle is !Send and has to stay on the thread that started playback:
fn assert_send<T: Send>() {}
assert_send::<vst3_host::AudioHandle>(); // AudioHandle is deliberately not SendTo drive the plugin from another thread, move a MidiSink (Self::midi_sink) or the
shared Arc<Mutex<Plugin>> (Self::plugin) there instead — both are Send.
Implementations§
Source§impl AudioHandle
impl AudioHandle
Sourcepub fn lock(&self) -> MutexGuard<'_, Plugin>
pub fn lock(&self) -> MutexGuard<'_, Plugin>
Lock the running plugin to send MIDI, change parameters, etc.
Recovers automatically if the audio thread previously panicked while holding the lock (poisoned mutex), so control calls keep working.
Sourcepub fn try_lock(&self) -> Option<MutexGuard<'_, Plugin>>
pub fn try_lock(&self) -> Option<MutexGuard<'_, Plugin>>
Try to lock the plugin without blocking, returning None if the audio
callback currently holds the lock (it is held for the duration of each
process_audio call).
Use this on a UI/render thread for best-effort, per-frame reads (VU meters, output-MIDI drain, parameter sync): skipping a frame when the audio thread is mid-block is invisible, and it keeps the UI thread from stalling on the (unfair) mutex — which otherwise shows up as input lag.
Sourcepub fn send_midi(&self, event: MidiEvent) -> bool
pub fn send_midi(&self, event: MidiEvent) -> bool
Queue a MIDI event for the plugin without locking the audio thread.
The event is pushed onto a lock-free ring and applied at the start of the next audio
block. Prefer this over lock().send_midi_event(..) on a UI thread — it never blocks
on the audio mutex. Returns false if the ring is full (the event is dropped).
Sourcepub fn send_midi_at(&self, event: MidiEvent, sample_offset: i32) -> bool
pub fn send_midi_at(&self, event: MidiEvent, sample_offset: i32) -> bool
Queue a MIDI event scheduled at sample_offset samples into the next block, for
sample-accurate sequencing, without locking the audio thread. A negative offset is
floored to 0. Returns false if the ring is full.
Sourcepub fn midi_sink(&self) -> MidiSink
pub fn midi_sink(&self) -> MidiSink
Obtain a MidiSink: a cheap, cloneable, Send handle that can queue MIDI to this
running plugin from another thread.
Unlike AudioHandle itself (which is not Send, as it owns the device stream), the
sink can be moved into a background thread or callback — e.g. a MIDI input device
callback (see crate::midi_input). It shares the same lock-free command ring as
Self::send_midi.
Sourcepub fn set_parameter(&self, id: u32, value: f64) -> bool
pub fn set_parameter(&self, id: u32, value: f64) -> bool
Queue a normalized parameter change without locking the audio thread; applied at the
start of the next block. value must be finite and within 0.0..=1.0 — an invalid
value is rejected here (returns false) rather than queued, so the caller learns about
it instead of the audio thread silently discarding it. Returns false if the ring is
full.
§The editor catches up later
The audio thread applies the value to the plugin’s DSP, but IEditController belongs to
the main-thread domain, so the plugin’s own editor (and
Plugin::get_parameter,
format_parameter and saved state) is updated from
the control thread instead. That happens the next time the control thread touches the
plugin — reading a parameter, draining
Plugin::get_parameter_changes, or calling
Plugin::service_host_requests. A host that
polls the plugin every UI frame (the usual editor loop) never notices the gap; a host
that never calls back in will see a stale editor. The queue is bounded and drops its
oldest entry when full, so the newest value for a parameter always wins.
Sourcepub fn set_tempo(&self, bpm: f64) -> bool
pub fn set_tempo(&self, bpm: f64) -> bool
Queue a transport tempo change (BPM) without locking the audio thread; applied at the
start of the next block. bpm must be finite and greater than 0 (an invalid value is
rejected, returning false). Returns false if the ring is full.
Sourcepub fn set_time_signature(&self, numerator: i32, denominator: i32) -> bool
pub fn set_time_signature(&self, numerator: i32, denominator: i32) -> bool
Queue a transport time-signature change without locking the audio thread; applied at the
start of the next block. denominator must be one of 1, 2, 4, 8, 16 and numerator
must be positive (an invalid value is rejected, returning false). Returns false if
the ring is full.
Sourcepub fn set_playing(&self, playing: bool) -> bool
pub fn set_playing(&self, playing: bool) -> bool
Queue a transport playing-state toggle without locking the audio thread; applied at the
start of the next block. Returns false if the ring is full.
Sourcepub fn midi_panic(&self) -> bool
pub fn midi_panic(&self) -> bool
Queue an all-notes-off “panic” (CC 123/120/121 on every channel) without locking the
audio thread. Returns false if the ring is full.
Sourcepub fn output_levels(&self) -> AudioLevels
pub fn output_levels(&self) -> AudioLevels
Read the latest per-channel output peak levels without locking the audio thread.
Each channel reports the maximum peak observed since the previous call (the read resets
the accumulator), so polling at UI frame rate never misses a transient between frames.
rms is not tracked on this path and is reported as 0; peak_hold mirrors peak
(drive your own ballistics, e.g. crate::audio::PeakMeter, from the peak).
Sourcepub fn drain_output_midi(&self) -> Vec<MidiEvent>
pub fn drain_output_midi(&self) -> Vec<MidiEvent>
Drain MIDI the plugin emitted during processing (arpeggiators, MPE, …) without locking the audio thread. Returns the events queued since the last call.
Sourcepub fn drain_parameter_changes(&self) -> Vec<(u32, f64)>
pub fn drain_parameter_changes(&self) -> Vec<(u32, f64)>
Drain parameter changes the plugin made through its own editor without locking the
audio thread. Returns (id, normalized_value) pairs queued since the last call.