1use crate::{
4 audio::{AudioBuffers, AudioBusLayout, AudioLevels, BusAudioBuffers},
5 error::{Error, Result},
6 midi::{
7 MidiChannel, MidiEvent, PluginEvent, PluginEventData, MAX_EVENT_PAYLOAD_BYTES,
8 MAX_EVENT_TEXT_UNITS,
9 },
10 parameters::{Parameter, ParameterUpdate},
11};
12use crossbeam_queue::ArrayQueue;
13use std::sync::{Arc, Mutex};
14
15#[derive(Clone)]
27pub struct OutputMidiConsumer {
28 queue: Arc<ArrayQueue<PluginEvent>>,
29}
30
31impl OutputMidiConsumer {
32 pub(crate) fn from_queue(queue: Arc<ArrayQueue<PluginEvent>>) -> Self {
33 Self { queue }
34 }
35
36 pub fn pop(&self) -> Option<MidiEvent> {
38 while let Some(event) = self.queue.pop() {
39 if let Some(midi) = event.to_midi() {
40 return Some(midi);
41 }
42 }
43 None
44 }
45
46 pub fn drain(&self) -> Vec<MidiEvent> {
50 let mut out = Vec::new();
51 while let Some(event) = self.pop() {
52 out.push(event);
53 }
54 out
55 }
56}
57
58#[derive(Clone)]
60pub struct OutputEventConsumer {
61 queue: Arc<ArrayQueue<PluginEvent>>,
62}
63
64impl OutputEventConsumer {
65 pub(crate) fn from_queue(queue: Arc<ArrayQueue<PluginEvent>>) -> Self {
66 Self { queue }
67 }
68
69 pub fn pop(&self) -> Option<PluginEvent> {
71 self.queue.pop()
72 }
73
74 pub fn drain(&self) -> Vec<PluginEvent> {
76 let mut out = Vec::new();
77 while let Some(event) = self.pop() {
78 out.push(event);
79 }
80 out
81 }
82}
83
84#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
86pub struct PluginInfo {
87 pub path: std::path::PathBuf,
89 pub name: String,
91 pub vendor: String,
93 pub version: String,
95 pub category: String,
97 pub uid: String,
99 pub audio_inputs: u32,
101 pub audio_outputs: u32,
103 pub has_midi_input: bool,
105 pub has_midi_output: bool,
107 pub has_gui: bool,
109}
110
111#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
117pub struct PluginPreset {
118 pub uid: String,
120 pub plugin_name: String,
122 pub state: Vec<u8>,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
139pub enum StateContext {
140 #[default]
145 Project,
146 Preset {
157 path: Option<String>,
167 },
168}
169
170impl StateContext {
171 pub fn preset() -> Self {
173 Self::Preset { path: None }
174 }
175
176 pub fn preset_from_path(path: impl AsRef<std::path::Path>) -> Self {
178 Self::Preset {
179 path: Some(path.as_ref().to_string_lossy().into_owned()),
180 }
181 }
182
183 pub fn file_path(&self) -> Option<&std::path::Path> {
185 match self {
186 Self::Project => None,
187 Self::Preset { path } => path.as_deref().map(std::path::Path::new),
188 }
189 }
190}
191
192pub(crate) struct StateSnapshot {
198 pub component: Vec<u8>,
199 pub controller: Option<Vec<u8>>,
200}
201
202const STATE_SNAPSHOT_MAGIC: &[u8; 16] = b"VST3HOST_STATE\0\0";
203const STATE_SNAPSHOT_VERSION: u32 = 1;
204const STATE_SNAPSHOT_HEADER_SIZE: usize = 16 + 4 + 4 + 4;
205const NO_CONTROLLER_STATE: u32 = u32::MAX;
206const MAX_STATE_SNAPSHOT_PAYLOAD_BYTES: usize =
209 crate::internal::com_implementations::MAX_STREAM_BYTES;
210pub(crate) const MAX_STATE_SNAPSHOT_BYTES: usize =
211 STATE_SNAPSHOT_HEADER_SIZE + MAX_STATE_SNAPSHOT_PAYLOAD_BYTES;
212
213pub(crate) fn encode_state_snapshot(snapshot: &StateSnapshot) -> Result<Vec<u8>> {
214 let component_len = u32::try_from(snapshot.component.len())
215 .map_err(|_| Error::Other("component state is too large".to_string()))?;
216 let controller_len = match snapshot.controller.as_ref() {
217 Some(state) => u32::try_from(state.len())
218 .map_err(|_| Error::Other("controller state is too large".to_string()))?,
219 None => NO_CONTROLLER_STATE,
220 };
221 let payload_size = snapshot
222 .component
223 .len()
224 .checked_add(snapshot.controller.as_ref().map_or(0, Vec::len))
225 .ok_or_else(|| Error::Other("plugin state size overflow".to_string()))?;
226 let total = STATE_SNAPSHOT_HEADER_SIZE
227 .checked_add(payload_size)
228 .ok_or_else(|| Error::Other("plugin state size overflow".to_string()))?;
229 if payload_size > MAX_STATE_SNAPSHOT_PAYLOAD_BYTES {
230 return Err(Error::Other(format!(
231 "combined plugin state payload is too large ({payload_size} bytes, maximum \
232 {MAX_STATE_SNAPSHOT_PAYLOAD_BYTES})"
233 )));
234 }
235
236 let mut out = Vec::with_capacity(total);
237 out.extend_from_slice(STATE_SNAPSHOT_MAGIC);
238 out.extend_from_slice(&STATE_SNAPSHOT_VERSION.to_le_bytes());
239 out.extend_from_slice(&component_len.to_le_bytes());
240 out.extend_from_slice(&controller_len.to_le_bytes());
241 out.extend_from_slice(&snapshot.component);
242 if let Some(controller) = snapshot.controller.as_ref() {
243 out.extend_from_slice(controller);
244 }
245 Ok(out)
246}
247
248pub(crate) fn decode_state_snapshot(data: &[u8]) -> Result<StateSnapshot> {
249 if !data.starts_with(STATE_SNAPSHOT_MAGIC) {
250 if data.len() > MAX_STATE_SNAPSHOT_PAYLOAD_BYTES {
251 return Err(Error::Other(format!(
252 "legacy component state is too large ({} bytes, maximum \
253 {MAX_STATE_SNAPSHOT_PAYLOAD_BYTES})",
254 data.len()
255 )));
256 }
257 return Ok(StateSnapshot {
258 component: data.to_vec(),
259 controller: None,
260 });
261 }
262 if data.len() < STATE_SNAPSHOT_HEADER_SIZE {
263 return Err(Error::Other(
264 "truncated vst3-host state snapshot header".to_string(),
265 ));
266 }
267 let version = read_snapshot_u32(&data[16..20]);
268 if version != STATE_SNAPSHOT_VERSION {
269 return Err(Error::Other(format!(
270 "unsupported vst3-host state snapshot version {version}"
271 )));
272 }
273 let component_len = read_snapshot_u32(&data[20..24]) as usize;
274 let encoded_controller_len = read_snapshot_u32(&data[24..28]);
275 let controller_len =
276 (encoded_controller_len != NO_CONTROLLER_STATE).then_some(encoded_controller_len as usize);
277 let payload_size = component_len
278 .checked_add(controller_len.unwrap_or(0))
279 .ok_or_else(|| Error::Other("plugin state size overflow".to_string()))?;
280 let expected = STATE_SNAPSHOT_HEADER_SIZE
281 .checked_add(payload_size)
282 .ok_or_else(|| Error::Other("plugin state size overflow".to_string()))?;
283 if expected != data.len() || expected > MAX_STATE_SNAPSHOT_BYTES {
284 return Err(Error::Other(format!(
285 "invalid vst3-host state snapshot size (header describes {expected} bytes, got {})",
286 data.len()
287 )));
288 }
289 let component_start = STATE_SNAPSHOT_HEADER_SIZE;
290 let component_end = component_start + component_len;
291 Ok(StateSnapshot {
292 component: data[component_start..component_end].to_vec(),
293 controller: controller_len.map(|len| data[component_end..component_end + len].to_vec()),
294 })
295}
296
297fn read_snapshot_u32(bytes: &[u8]) -> u32 {
298 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
299}
300
301#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
306pub struct PluginUnit {
307 pub id: i32,
309 pub parent_id: i32,
311 pub name: String,
313 pub program_list_id: Option<i32>,
315 pub programs: Vec<String>,
317}
318
319#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
321pub struct ProgramPitchName {
322 pub midi_pitch: i16,
324 pub name: String,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
330pub enum AutomationState {
331 Off,
333 Read,
335 Write,
337 ReadWrite,
339}
340
341#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
349pub enum ParameterEditKind {
350 BeginGesture,
352 ValueChange,
355 EndGesture,
357}
358
359#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
365pub struct ParameterEdit {
366 pub id: u32,
368 pub kind: ParameterEditKind,
370 pub value: Option<f64>,
373}
374
375#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
377pub enum ProgressKind {
378 AsyncStateRestoration,
380 UiBackgroundTask,
382 Other(u32),
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
391pub struct ProgressValue(u64);
392
393impl ProgressValue {
394 pub fn new(value: f64) -> Option<Self> {
396 (value.is_finite() && (0.0..=1.0).contains(&value)).then(|| Self(value.to_bits()))
397 }
398
399 pub fn get(self) -> f64 {
401 f64::from_bits(self.0)
402 }
403}
404
405#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
411pub struct ContextMenuItem {
412 pub item_id: u32,
414 pub name: String,
416 pub tag: i32,
418 pub flags: i32,
420}
421
422impl ContextMenuItem {
423 pub fn is_separator(&self) -> bool {
425 self.flags & vst3::Steinberg::Vst::IContextMenuItem_::Flags_::kIsSeparator as i32 != 0
426 }
427
428 pub fn is_disabled(&self) -> bool {
430 self.flags & vst3::Steinberg::Vst::IContextMenuItem_::Flags_::kIsDisabled as i32 != 0
431 }
432
433 pub fn is_checked(&self) -> bool {
435 self.flags & vst3::Steinberg::Vst::IContextMenuItem_::Flags_::kIsChecked as i32 != 0
436 }
437
438 pub fn is_group_start(&self) -> bool {
440 let group_start = vst3::Steinberg::Vst::IContextMenuItem_::Flags_::kIsGroupStart as i32;
441 self.flags & group_start == group_start
442 }
443
444 pub fn is_group_end(&self) -> bool {
446 let group_end = vst3::Steinberg::Vst::IContextMenuItem_::Flags_::kIsGroupEnd as i32;
447 self.flags & group_end == group_end
448 }
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
457pub struct DataExchangeBlock {
458 pub queue_id: u32,
460 pub user_context_id: u32,
462 pub block_id: u32,
464 #[serde(with = "crate::process_isolation::state_codec")]
466 pub data: Vec<u8>,
467}
468
469#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
480pub enum HostNotification {
481 BusActivationRequested {
484 media_type: crate::audio::MediaType,
486 direction: crate::audio::BusDirection,
488 bus_index: i32,
490 active: bool,
492 },
493 DirtyChanged(bool),
495 OpenEditorRequested {
497 name: Option<String>,
499 },
500 GroupEditStarted,
502 GroupEditFinished,
504 UnitSelectionChanged {
506 unit_id: i32,
508 },
509 ProgramListChanged {
511 list_id: i32,
513 program_index: Option<i32>,
515 },
516 UnitByBusChanged,
518 ProgressStarted {
520 id: u64,
522 kind: ProgressKind,
524 description: Option<String>,
526 },
527 ProgressUpdated {
529 id: u64,
531 value: ProgressValue,
533 },
534 ProgressFinished {
536 id: u64,
538 },
539 ContextMenuRequested {
545 menu_id: u64,
547 parameter_id: Option<u32>,
549 x: i32,
551 y: i32,
553 items: Vec<ContextMenuItem>,
555 },
556}
557
558impl HostNotification {
559 pub(crate) fn invalidates_unit_cache(&self) -> bool {
560 matches!(
561 self,
562 Self::ProgramListChanged { .. } | Self::UnitByBusChanged
563 )
564 }
565}
566
567#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
584pub struct RestartFlags(i32);
585
586impl RestartFlags {
587 pub(crate) fn from_bits(bits: i32) -> Self {
589 Self(bits)
590 }
591
592 pub fn bits(self) -> i32 {
594 self.0
595 }
596
597 pub fn is_empty(self) -> bool {
599 self.0 == 0
600 }
601
602 fn has(self, flag: i32) -> bool {
603 self.0 & flag != 0
604 }
605
606 pub fn param_values_changed(self) -> bool {
608 self.has(vst3::Steinberg::Vst::RestartFlags_::kParamValuesChanged)
609 }
610
611 pub fn reload_component(self) -> bool {
613 self.has(vst3::Steinberg::Vst::RestartFlags_::kReloadComponent)
614 }
615
616 pub fn param_titles_changed(self) -> bool {
618 self.has(vst3::Steinberg::Vst::RestartFlags_::kParamTitlesChanged)
619 }
620
621 pub fn latency_changed(self) -> bool {
623 self.has(vst3::Steinberg::Vst::RestartFlags_::kLatencyChanged)
624 }
625
626 pub fn io_changed(self) -> bool {
628 self.has(vst3::Steinberg::Vst::RestartFlags_::kIoChanged)
629 }
630
631 pub fn midi_cc_assignment_changed(self) -> bool {
633 self.has(vst3::Steinberg::Vst::RestartFlags_::kMidiCCAssignmentChanged)
634 }
635
636 pub fn note_expression_changed(self) -> bool {
638 self.has(vst3::Steinberg::Vst::RestartFlags_::kNoteExpressionChanged)
639 }
640
641 pub fn io_titles_changed(self) -> bool {
643 self.has(vst3::Steinberg::Vst::RestartFlags_::kIoTitlesChanged)
644 }
645
646 pub fn prefetchable_support_changed(self) -> bool {
648 self.has(vst3::Steinberg::Vst::RestartFlags_::kPrefetchableSupportChanged)
649 }
650
651 pub fn routing_info_changed(self) -> bool {
653 self.has(vst3::Steinberg::Vst::RestartFlags_::kRoutingInfoChanged)
654 }
655
656 pub fn keyswitch_changed(self) -> bool {
658 self.has(vst3::Steinberg::Vst::RestartFlags_::kKeyswitchChanged)
659 }
660
661 pub fn param_id_mapping_changed(self) -> bool {
663 self.has(vst3::Steinberg::Vst::RestartFlags_::kParamIDMappingChanged)
664 }
665}
666
667#[cfg(test)]
668mod restart_flag_tests {
669 use super::RestartFlags;
670 use vst3::Steinberg::Vst::RestartFlags_ as Flags;
671
672 #[test]
673 fn exposes_every_vst3_restart_flag() {
674 let bits = Flags::kReloadComponent
675 | Flags::kIoChanged
676 | Flags::kParamValuesChanged
677 | Flags::kLatencyChanged
678 | Flags::kParamTitlesChanged
679 | Flags::kMidiCCAssignmentChanged
680 | Flags::kNoteExpressionChanged
681 | Flags::kIoTitlesChanged
682 | Flags::kPrefetchableSupportChanged
683 | Flags::kRoutingInfoChanged
684 | Flags::kKeyswitchChanged
685 | Flags::kParamIDMappingChanged;
686 let flags = RestartFlags::from_bits(bits);
687 assert!(flags.reload_component());
688 assert!(flags.io_changed());
689 assert!(flags.param_values_changed());
690 assert!(flags.latency_changed());
691 assert!(flags.param_titles_changed());
692 assert!(flags.midi_cc_assignment_changed());
693 assert!(flags.note_expression_changed());
694 assert!(flags.io_titles_changed());
695 assert!(flags.prefetchable_support_changed());
696 assert!(flags.routing_info_changed());
697 assert!(flags.keyswitch_changed());
698 assert!(flags.param_id_mapping_changed());
699 }
700}
701
702#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
706pub enum ProcessMode {
707 #[default]
709 Realtime,
710 Prefetch,
715 Offline,
717}
718
719#[allow(clippy::type_complexity)] pub struct Plugin {
722 pub(crate) info: PluginInfo,
724 pub(crate) compatibility: Vec<crate::discovery::ClassCompatibility>,
725 pub(crate) is_processing: bool,
726 pub(crate) sample_rate: f64,
728 pub(crate) block_size: usize,
730 pub(crate) audio_levels: Arc<Mutex<AudioLevels>>,
731 pub(crate) parameter_change_callback: Option<Box<dyn Fn(u32, f64) + Send + 'static>>,
732 pub(crate) audio_callback: Option<Box<dyn Fn(&AudioLevels) + Send + 'static>>,
733
734 pub(crate) internal: Option<Box<dyn PluginInternal>>,
736}
737
738#[derive(Debug, Clone, Copy, Default, PartialEq, serde::Serialize, serde::Deserialize)]
741pub struct TransportPosition {
742 pub samples: i64,
744 pub quarter_notes: f64,
746}
747
748pub(crate) trait PluginInternal: Send {
750 fn midi_controller_assignments(
751 &self,
752 _direction: crate::audio::BusDirection,
753 ) -> Result<Vec<crate::midi::MidiControllerAssignment>> {
754 Ok(Vec::new())
755 }
756 fn send_midi_controller_at(
757 &mut self,
758 _bus: i32,
759 _channel: MidiChannel,
760 _controller: crate::midi::MidiController,
761 _value: f64,
762 _offset: i32,
763 ) -> Result<()> {
764 Ok(())
765 }
766 fn notify_live_midi_controller(
767 &mut self,
768 _bus: i32,
769 _channel: MidiChannel,
770 _controller: crate::midi::MidiController,
771 ) -> Result<bool> {
772 Ok(false)
773 }
774 fn send_live_midi_event(&mut self, event: MidiEvent) -> Result<()> {
775 self.send_midi_event(event)
776 }
777 fn keyswitches(
778 &self,
779 _bus: i32,
780 _channel: MidiChannel,
781 ) -> Result<Vec<crate::midi::KeyswitchInfo>> {
782 Ok(Vec::new())
783 }
784
785 fn set_transport_position(&mut self, _position: TransportPosition) -> Result<()> {
786 Ok(())
787 }
788 fn transport_position(&self) -> Result<TransportPosition> {
789 Ok(TransportPosition::default())
790 }
791
792 fn set_parameter(&mut self, id: u32, value: f64) -> Result<()>;
793 fn set_parameter_at(&mut self, id: u32, value: f64, _sample_offset: i32) -> Result<()> {
797 self.set_parameter(id, value)
798 }
799 fn queue_processor_parameter_at(
805 &mut self,
806 id: u32,
807 value: f64,
808 sample_offset: i32,
809 ) -> Result<()> {
810 self.set_parameter_at(id, value, sample_offset)
811 }
812 fn get_parameter(&self, id: u32) -> Result<f64>;
813 fn get_all_parameters(&self) -> Result<Vec<Parameter>>;
814 fn format_parameter(&self, id: u32, normalized: f64) -> Result<String>;
815 fn process(&mut self, buffers: &mut AudioBuffers) -> Result<()>;
816 fn audio_bus_layout(&self) -> Result<AudioBusLayout> {
818 Err(Error::Other(
819 "bus-aware audio processing is not supported for this plugin".to_string(),
820 ))
821 }
822 fn process_buses(&mut self, _buffers: &mut BusAudioBuffers) -> Result<()> {
824 Err(Error::Other(
825 "bus-aware audio processing is not supported for this plugin".to_string(),
826 ))
827 }
828 fn reconfigure(&mut self, _sample_rate: f64, _block_size: usize) -> Result<()> {
831 Err(Error::Other(
832 "runtime reconfigure is not supported for this plugin".to_string(),
833 ))
834 }
835 fn set_process_mode(&mut self, _mode: crate::plugin::ProcessMode) -> Result<()> {
838 Err(Error::Other(
839 "process mode switching is not supported for this plugin".to_string(),
840 ))
841 }
842 fn bus_arrangements(&self) -> Result<crate::audio::BusArrangements> {
844 Err(Error::Other(
845 "bus arrangement query is not supported for this plugin".to_string(),
846 ))
847 }
848 fn set_bus_arrangements(
851 &mut self,
852 _inputs: &[crate::audio::SpeakerArrangement],
853 _outputs: &[crate::audio::SpeakerArrangement],
854 ) -> Result<()> {
855 Err(Error::Other(
856 "bus arrangement negotiation is not supported for this plugin".to_string(),
857 ))
858 }
859 fn set_bus_active(
862 &mut self,
863 _media_type: crate::audio::MediaType,
864 _direction: crate::audio::BusDirection,
865 _bus_index: i32,
866 _active: bool,
867 ) -> Result<()> {
868 Err(Error::Other(
869 "bus activation is not supported for this plugin".to_string(),
870 ))
871 }
872 fn set_tempo(&mut self, _bpm: f64) -> Result<()> {
876 Err(Error::Other(
877 "runtime transport mutation is not supported for this plugin".to_string(),
878 ))
879 }
880 fn set_time_signature(&mut self, _numerator: i32, _denominator: i32) -> Result<()> {
884 Err(Error::Other(
885 "runtime transport mutation is not supported for this plugin".to_string(),
886 ))
887 }
888 fn set_playing(&mut self, _playing: bool) -> Result<()> {
891 Err(Error::Other(
892 "runtime transport mutation is not supported for this plugin".to_string(),
893 ))
894 }
895 fn send_midi_event(&mut self, event: MidiEvent) -> Result<()>;
896 fn send_midi_event_at(&mut self, event: MidiEvent, _sample_offset: i32) -> Result<()> {
900 self.send_midi_event(event)
901 }
902 fn send_plugin_event(&mut self, _event: PluginEvent) -> Result<()> {
904 Err(Error::Other(
905 "owned VST3 events are not supported for this plugin".to_string(),
906 ))
907 }
908 fn midi_panic(&mut self) -> Result<()> {
910 for i in 0..16 {
911 if let Some(channel) = MidiChannel::from_index(i) {
912 for controller in [123, 120, 121] {
913 self.send_midi_event(MidiEvent::ControlChange {
914 channel,
915 controller,
916 value: 0,
917 })?;
918 }
919 }
920 }
921 Ok(())
922 }
923 fn note_on(
926 &mut self,
927 _channel: MidiChannel,
928 _note: u8,
929 _velocity: u8,
930 _sample_offset: i32,
931 ) -> Result<crate::midi::NoteId> {
932 Err(Error::Other(
933 "per-note expression is not supported for this plugin".to_string(),
934 ))
935 }
936 fn note_off(&mut self, _id: crate::midi::NoteId, _sample_offset: i32) -> Result<()> {
938 Err(Error::Other(
939 "per-note expression is not supported for this plugin".to_string(),
940 ))
941 }
942 fn send_note_expression(
944 &mut self,
945 _id: crate::midi::NoteId,
946 _kind: crate::midi::NoteExpressionType,
947 _value: f64,
948 _sample_offset: i32,
949 ) -> Result<()> {
950 Err(Error::Other(
951 "per-note expression is not supported for this plugin".to_string(),
952 ))
953 }
954 fn note_expressions(
957 &self,
958 _bus: i32,
959 _channel: i16,
960 ) -> Result<Vec<crate::midi::NoteExpressionInfo>> {
961 Ok(Vec::new())
962 }
963 fn start_processing(&mut self) -> Result<()>;
964 fn stop_processing(&mut self) -> Result<()>;
965 fn has_editor(&self) -> bool;
966 fn open_editor(&mut self, parent: *mut std::ffi::c_void) -> Result<()>;
967 fn close_editor(&mut self) -> Result<()>;
968 fn get_editor_size(&self) -> Result<(i32, i32)>;
969 fn is_editor_open(&self) -> bool {
971 false
972 }
973 fn attached_editor_size(&self) -> Result<Option<(i32, i32)>> {
975 Ok(None)
976 }
977 fn attached_editor_can_resize(&self) -> Option<bool> {
979 None
980 }
981 fn editor_can_resize(&self) -> bool {
983 false
984 }
985 fn resize_editor(&mut self, _width: i32, _height: i32) -> Result<(i32, i32)> {
988 Err(Error::Other("plugin editor is not resizable".to_string()))
989 }
990 fn set_editor_scale_factor(&mut self, _factor: f32) -> Result<bool> {
993 Ok(false)
994 }
995 fn service_run_loop(&mut self) {}
999 fn get_parameter_changes(&self) -> Vec<(u32, f64)>;
1000 fn take_parameter_edits(&mut self) -> Vec<ParameterEdit> {
1004 Vec::new()
1005 }
1006 fn take_host_notifications(&mut self) -> Vec<HostNotification> {
1008 Vec::new()
1009 }
1010 fn take_data_exchange_blocks(&mut self) -> Vec<DataExchangeBlock> {
1013 Vec::new()
1014 }
1015 fn execute_context_menu_item(&mut self, _menu_id: u64, _item_id: u32) -> Result<()> {
1017 Err(Error::Other(
1018 "plugin context menus are not supported".to_string(),
1019 ))
1020 }
1021 fn dismiss_context_menu(&mut self, _menu_id: u64) -> Result<()> {
1023 Err(Error::Other(
1024 "plugin context menus are not supported".to_string(),
1025 ))
1026 }
1027 fn take_restart_flags(&mut self) -> RestartFlags {
1030 RestartFlags::default()
1031 }
1032 fn service_host_requests(&mut self) -> Result<RestartFlags> {
1034 Ok(self.take_restart_flags())
1035 }
1036 fn take_output_events(&self) -> Vec<PluginEvent> {
1039 Vec::new()
1040 }
1041 fn output_midi_handle(&self) -> Option<OutputMidiConsumer> {
1044 None
1045 }
1046 fn output_event_handle(&self) -> Option<OutputEventConsumer> {
1047 None
1048 }
1049 fn get_units(&self) -> Result<Vec<PluginUnit>> {
1052 Ok(Vec::new())
1053 }
1054 fn select_program(&mut self, _unit_id: i32, _program_index: i32) -> Result<()> {
1058 Err(Error::Other(
1059 "program selection is not supported for this plugin".to_string(),
1060 ))
1061 }
1062 fn selected_unit(&self) -> Result<Option<i32>> {
1063 Ok(None)
1064 }
1065 fn select_unit(&mut self, _unit_id: i32) -> Result<()> {
1066 Err(Error::Other(
1067 "unit selection is not supported for this plugin".to_string(),
1068 ))
1069 }
1070 fn program_pitch_names(
1071 &self,
1072 _program_list_id: i32,
1073 _program_index: i32,
1074 ) -> Result<Vec<ProgramPitchName>> {
1075 Ok(Vec::new())
1076 }
1077 fn get_program_data(
1078 &self,
1079 _program_list_id: i32,
1080 _program_index: i32,
1081 ) -> Result<Option<Vec<u8>>> {
1082 Ok(None)
1083 }
1084 fn set_program_data(
1085 &mut self,
1086 _program_list_id: i32,
1087 _program_index: i32,
1088 _data: &[u8],
1089 ) -> Result<()> {
1090 Err(Error::Other(
1091 "program data is not supported for this plugin".to_string(),
1092 ))
1093 }
1094 fn get_unit_data(&self, _unit_id: i32) -> Result<Option<Vec<u8>>> {
1095 Ok(None)
1096 }
1097 fn set_unit_data(&mut self, _unit_id: i32, _data: &[u8]) -> Result<()> {
1098 Err(Error::Other(
1099 "unit data is not supported for this plugin".to_string(),
1100 ))
1101 }
1102 fn begin_host_edit(&mut self, _parameter_id: u32) -> Result<()> {
1103 Err(Error::Other(
1104 "host edit sessions are not supported for this plugin".to_string(),
1105 ))
1106 }
1107 fn end_host_edit(&mut self, _parameter_id: u32) -> Result<()> {
1108 Err(Error::Other(
1109 "host edit sessions are not supported for this plugin".to_string(),
1110 ))
1111 }
1112 fn send_midi_learn(&mut self, _bus: i32, _channel: i16, _controller: u16) -> Result<()> {
1113 Err(Error::Other(
1114 "MIDI learn is not supported for this plugin".to_string(),
1115 ))
1116 }
1117 fn set_automation_state(&mut self, _state: AutomationState) -> Result<()> {
1118 Err(Error::Other(
1119 "automation state is not supported for this plugin".to_string(),
1120 ))
1121 }
1122 fn remap_parameter_id(&self, _old_plugin_uid: &str, _old_param_id: u32) -> Result<Option<u32>> {
1124 Ok(None)
1125 }
1126 fn latency_samples(&self) -> u32 {
1128 0
1129 }
1130 fn tail_samples(&self) -> u32 {
1132 0
1133 }
1134 fn midi_cc_to_parameter(&self, _bus: i32, _channel: i16, _cc: u16) -> Option<u32> {
1137 None
1138 }
1139 fn save_state(&self) -> Result<Vec<u8>> {
1141 Err(Error::Other(
1142 "state save/restore is not supported".to_string(),
1143 ))
1144 }
1145 fn load_state_with_context(&mut self, _data: &[u8], _context: &StateContext) -> Result<()> {
1151 Err(Error::Other(
1152 "state save/restore is not supported".to_string(),
1153 ))
1154 }
1155 fn load_state(&mut self, data: &[u8]) -> Result<()> {
1158 self.load_state_with_context(data, &StateContext::Project)
1159 }
1160 fn helper_pid(&self) -> Option<u32> {
1162 None
1163 }
1164 fn recovery_count(&self) -> u64 {
1167 0
1168 }
1169 fn recover(&mut self) -> Result<()> {
1172 Err(Error::Other(
1173 "recovery is only supported for process-isolated plugins".to_string(),
1174 ))
1175 }
1176 fn take_editor_resize_request(&self) -> Option<(i32, i32)> {
1178 None
1179 }
1180 fn output_channel_count(&self) -> usize {
1182 2
1183 }
1184}
1185
1186impl Plugin {
1187 pub fn info(&self) -> &PluginInfo {
1189 &self.info
1190 }
1191
1192 pub fn class_compatibility(&self) -> &[crate::discovery::ClassCompatibility] {
1197 &self.compatibility
1198 }
1199
1200 pub fn replaced_class_ids(&self) -> &[String] {
1202 self.compatibility
1203 .iter()
1204 .find(|mapping| {
1205 crate::internal::utils::class_uid_matches(&mapping.new_class_id, &self.info.uid)
1206 })
1207 .map_or(&[], |mapping| mapping.old_class_ids.as_slice())
1208 }
1209
1210 pub fn sample_rate(&self) -> f64 {
1212 self.sample_rate
1213 }
1214
1215 pub fn block_size(&self) -> usize {
1217 self.block_size
1218 }
1219
1220 pub fn reconfigure(&mut self, sample_rate: f64, block_size: usize) -> Result<()> {
1229 if self.is_processing {
1230 return Err(Error::Other(
1231 "cannot reconfigure while processing; call stop_processing() first".to_string(),
1232 ));
1233 }
1234 if !(sample_rate.is_finite() && sample_rate > 0.0) {
1235 return Err(Error::InvalidParameter(format!(
1236 "sample rate must be finite and positive, got {sample_rate}"
1237 )));
1238 }
1239 if block_size == 0 || block_size > i32::MAX as usize {
1240 return Err(Error::InvalidParameter(format!(
1241 "block size must be in 1..={}, got {block_size}",
1242 i32::MAX
1243 )));
1244 }
1245
1246 self.internal
1247 .as_mut()
1248 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1249 .reconfigure(sample_rate, block_size)?;
1250
1251 self.sample_rate = sample_rate;
1252 self.block_size = block_size;
1253 Ok(())
1254 }
1255
1256 pub fn set_process_mode(&mut self, mode: ProcessMode) -> Result<()> {
1264 if self.is_processing {
1265 return Err(Error::Other(
1266 "cannot set process mode while processing; call stop_processing() first"
1267 .to_string(),
1268 ));
1269 }
1270 self.internal
1271 .as_mut()
1272 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1273 .set_process_mode(mode)
1274 }
1275
1276 pub fn bus_arrangements(&self) -> Result<crate::audio::BusArrangements> {
1279 self.internal
1280 .as_ref()
1281 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1282 .bus_arrangements()
1283 }
1284
1285 pub fn set_bus_arrangements(
1294 &mut self,
1295 inputs: &[crate::audio::SpeakerArrangement],
1296 outputs: &[crate::audio::SpeakerArrangement],
1297 ) -> Result<()> {
1298 if self.is_processing {
1299 return Err(Error::Other(
1300 "cannot set bus arrangements while processing; call stop_processing() first"
1301 .to_string(),
1302 ));
1303 }
1304 self.internal
1305 .as_mut()
1306 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1307 .set_bus_arrangements(inputs, outputs)
1308 }
1309
1310 pub fn set_bus_active(
1328 &mut self,
1329 media_type: crate::audio::MediaType,
1330 direction: crate::audio::BusDirection,
1331 bus_index: i32,
1332 active: bool,
1333 ) -> Result<()> {
1334 if self.is_processing {
1335 return Err(Error::Other(
1336 "cannot activate a bus while processing; call stop_processing() first".to_string(),
1337 ));
1338 }
1339 self.internal
1340 .as_mut()
1341 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1342 .set_bus_active(media_type, direction, bus_index, active)
1343 }
1344
1345 pub fn get_parameters(&self) -> Result<Vec<Parameter>> {
1347 self.internal
1348 .as_ref()
1349 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1350 .get_all_parameters()
1351 }
1352
1353 pub fn set_parameter(&mut self, id: u32, value: f64) -> Result<()> {
1355 if !(0.0..=1.0).contains(&value) {
1356 return Err(Error::InvalidParameter(format!(
1357 "Value {} is out of range [0.0, 1.0]",
1358 value
1359 )));
1360 }
1361
1362 self.internal
1363 .as_mut()
1364 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1365 .set_parameter(id, value)?;
1366
1367 if let Some(ref callback) = self.parameter_change_callback {
1369 callback(id, value);
1370 }
1371
1372 Ok(())
1373 }
1374
1375 pub fn midi_controller_assignments(
1379 &self,
1380 direction: crate::audio::BusDirection,
1381 ) -> Result<Vec<crate::midi::MidiControllerAssignment>> {
1382 self.internal
1383 .as_ref()
1384 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1385 .midi_controller_assignments(direction)
1386 }
1387
1388 pub fn send_midi_controller_at(
1391 &mut self,
1392 bus: i32,
1393 channel: MidiChannel,
1394 controller: crate::midi::MidiController,
1395 value: f64,
1396 sample_offset: i32,
1397 ) -> Result<()> {
1398 if bus < 0 || !controller.is_valid() || !(0.0..=1.0).contains(&value) {
1399 return Err(Error::InvalidParameter(
1400 "invalid controller address or normalized value".into(),
1401 ));
1402 }
1403 self.internal
1404 .as_mut()
1405 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1406 .send_midi_controller_at(bus, channel, controller, value, sample_offset.max(0))
1407 }
1408
1409 pub fn notify_live_midi_controller(
1412 &mut self,
1413 bus: i32,
1414 channel: MidiChannel,
1415 controller: crate::midi::MidiController,
1416 ) -> Result<bool> {
1417 if bus < 0 || !controller.is_valid() {
1418 return Err(Error::InvalidParameter("invalid controller address".into()));
1419 }
1420 self.internal
1421 .as_mut()
1422 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1423 .notify_live_midi_controller(bus, channel, controller)
1424 }
1425
1426 pub fn send_live_midi_event(&mut self, event: MidiEvent) -> Result<()> {
1430 validate_midi_event(&event)?;
1431 self.internal
1432 .as_mut()
1433 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1434 .send_live_midi_event(event)
1435 }
1436
1437 pub fn keyswitches(
1440 &self,
1441 bus: i32,
1442 channel: MidiChannel,
1443 ) -> Result<Vec<crate::midi::KeyswitchInfo>> {
1444 if bus < 0 {
1445 return Err(Error::InvalidParameter(
1446 "event bus must be nonnegative".into(),
1447 ));
1448 }
1449 self.internal
1450 .as_ref()
1451 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1452 .keyswitches(bus, channel)
1453 }
1454
1455 pub fn set_transport_position(&mut self, position: TransportPosition) -> Result<()> {
1458 if !position.quarter_notes.is_finite() {
1459 return Err(Error::InvalidParameter(
1460 "transport musical position must be finite".into(),
1461 ));
1462 }
1463 self.internal
1464 .as_mut()
1465 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1466 .set_transport_position(position)
1467 }
1468
1469 pub fn transport_position(&self) -> Result<TransportPosition> {
1471 self.internal
1472 .as_ref()
1473 .ok_or_else(|| Error::Other("Plugin not initialized".into()))?
1474 .transport_position()
1475 }
1476
1477 pub fn set_parameter_at(&mut self, id: u32, value: f64, sample_offset: i32) -> Result<()> {
1489 if !(0.0..=1.0).contains(&value) {
1490 return Err(Error::InvalidParameter(format!(
1491 "Value {} is out of range [0.0, 1.0]",
1492 value
1493 )));
1494 }
1495 self.internal
1496 .as_mut()
1497 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1498 .set_parameter_at(id, value, sample_offset)
1499 }
1500
1501 pub(crate) fn queue_processor_parameter_at(
1505 &mut self,
1506 id: u32,
1507 value: f64,
1508 sample_offset: i32,
1509 ) -> Result<()> {
1510 if !value.is_finite() || !(0.0..=1.0).contains(&value) {
1511 return Err(Error::InvalidParameter(format!(
1512 "Value {} is out of range [0.0, 1.0]",
1513 value
1514 )));
1515 }
1516 self.internal
1517 .as_mut()
1518 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1519 .queue_processor_parameter_at(id, value, sample_offset)
1520 }
1521
1522 pub fn set_tempo(&mut self, bpm: f64) -> Result<()> {
1529 if !(bpm.is_finite() && bpm > 0.0) {
1530 return Err(Error::InvalidParameter(format!(
1531 "tempo must be finite and positive, got {bpm}"
1532 )));
1533 }
1534 self.internal
1535 .as_mut()
1536 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1537 .set_tempo(bpm)
1538 }
1539
1540 pub fn set_time_signature(&mut self, numerator: i32, denominator: i32) -> Result<()> {
1548 if numerator <= 0 {
1549 return Err(Error::InvalidParameter(format!(
1550 "time signature numerator must be positive, got {numerator}"
1551 )));
1552 }
1553 if !matches!(denominator, 1 | 2 | 4 | 8 | 16) {
1554 return Err(Error::InvalidParameter(format!(
1555 "time signature denominator must be one of 1, 2, 4, 8, 16, got {denominator}"
1556 )));
1557 }
1558 self.internal
1559 .as_mut()
1560 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1561 .set_time_signature(numerator, denominator)
1562 }
1563
1564 pub fn set_playing(&mut self, playing: bool) -> Result<()> {
1573 self.internal
1574 .as_mut()
1575 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1576 .set_playing(playing)
1577 }
1578
1579 pub fn get_units(&self) -> Result<Vec<PluginUnit>> {
1584 self.internal
1585 .as_ref()
1586 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1587 .get_units()
1588 }
1589
1590 pub fn select_program(&mut self, unit_id: i32, program_index: i32) -> Result<()> {
1604 self.internal
1605 .as_mut()
1606 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1607 .select_program(unit_id, program_index)
1608 }
1609
1610 pub fn selected_unit(&self) -> Result<Option<i32>> {
1612 self.internal
1613 .as_ref()
1614 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1615 .selected_unit()
1616 }
1617
1618 pub fn select_unit(&mut self, unit_id: i32) -> Result<()> {
1620 self.internal
1621 .as_mut()
1622 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1623 .select_unit(unit_id)
1624 }
1625
1626 pub fn program_pitch_names(
1628 &self,
1629 program_list_id: i32,
1630 program_index: i32,
1631 ) -> Result<Vec<ProgramPitchName>> {
1632 self.internal
1633 .as_ref()
1634 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1635 .program_pitch_names(program_list_id, program_index)
1636 }
1637
1638 pub fn get_program_data(
1640 &self,
1641 program_list_id: i32,
1642 program_index: i32,
1643 ) -> Result<Option<Vec<u8>>> {
1644 self.internal
1645 .as_ref()
1646 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1647 .get_program_data(program_list_id, program_index)
1648 }
1649
1650 pub fn set_program_data(
1652 &mut self,
1653 program_list_id: i32,
1654 program_index: i32,
1655 data: &[u8],
1656 ) -> Result<()> {
1657 self.internal
1658 .as_mut()
1659 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1660 .set_program_data(program_list_id, program_index, data)
1661 }
1662
1663 pub fn get_unit_data(&self, unit_id: i32) -> Result<Option<Vec<u8>>> {
1665 self.internal
1666 .as_ref()
1667 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1668 .get_unit_data(unit_id)
1669 }
1670
1671 pub fn set_unit_data(&mut self, unit_id: i32, data: &[u8]) -> Result<()> {
1673 self.internal
1674 .as_mut()
1675 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1676 .set_unit_data(unit_id, data)
1677 }
1678
1679 pub fn begin_host_edit(&mut self, parameter_id: u32) -> Result<()> {
1681 self.internal
1682 .as_mut()
1683 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1684 .begin_host_edit(parameter_id)
1685 }
1686
1687 pub fn end_host_edit(&mut self, parameter_id: u32) -> Result<()> {
1689 self.internal
1690 .as_mut()
1691 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1692 .end_host_edit(parameter_id)
1693 }
1694
1695 pub fn send_midi_learn(&mut self, bus: i32, channel: i16, controller: u16) -> Result<()> {
1697 self.internal
1698 .as_mut()
1699 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1700 .send_midi_learn(bus, channel, controller)
1701 }
1702
1703 pub fn set_automation_state(&mut self, state: AutomationState) -> Result<()> {
1705 self.internal
1706 .as_mut()
1707 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1708 .set_automation_state(state)
1709 }
1710
1711 pub fn remap_parameter_id(
1718 &self,
1719 old_plugin_uid: &str,
1720 old_param_id: u32,
1721 ) -> Result<Option<u32>> {
1722 if crate::internal::utils::parse_class_uid(old_plugin_uid).is_none() {
1723 return Err(Error::InvalidParameter(
1724 "plugin UID must contain exactly 32 hexadecimal characters".to_string(),
1725 ));
1726 }
1727 self.internal
1728 .as_ref()
1729 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1730 .remap_parameter_id(old_plugin_uid, old_param_id)
1731 }
1732
1733 pub fn latency_samples(&self) -> u32 {
1738 self.internal
1739 .as_ref()
1740 .map(|i| i.latency_samples())
1741 .unwrap_or(0)
1742 }
1743
1744 pub fn tail_samples(&self) -> u32 {
1749 self.internal
1750 .as_ref()
1751 .map(|i| i.tail_samples())
1752 .unwrap_or(0)
1753 }
1754
1755 pub fn midi_cc_to_parameter(&self, bus: i32, channel: i16, cc: u16) -> Option<u32> {
1764 if cc > 129 {
1767 return None;
1768 }
1769 self.internal
1770 .as_ref()?
1771 .midi_cc_to_parameter(bus, channel, cc)
1772 }
1773
1774 pub fn get_parameter(&self, id: u32) -> Result<f64> {
1776 self.internal
1777 .as_ref()
1778 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1779 .get_parameter(id)
1780 }
1781
1782 pub fn format_parameter(&self, id: u32, normalized: f64) -> Result<String> {
1791 self.internal
1792 .as_ref()
1793 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1794 .format_parameter(id, normalized)
1795 }
1796
1797 pub fn set_parameter_by_name(&mut self, name: &str, value: f64) -> Result<()> {
1799 let params = self.get_parameters()?;
1800 let param = params
1801 .iter()
1802 .find(|p| p.name == name)
1803 .ok_or_else(|| Error::InvalidParameter(format!("Parameter '{}' not found", name)))?;
1804
1805 self.set_parameter(param.id, value)
1806 }
1807
1808 pub fn find_parameter(&self, name: &str) -> Result<Parameter> {
1810 let params = self.get_parameters()?;
1811 params
1812 .into_iter()
1813 .find(|p| p.name == name)
1814 .ok_or_else(|| Error::InvalidParameter(format!("Parameter '{}' not found", name)))
1815 }
1816
1817 pub fn send_midi_note(&mut self, note: u8, velocity: u8, channel: MidiChannel) -> Result<()> {
1819 validate_note(note)?;
1820 validate_velocity(velocity)?;
1821
1822 let event = MidiEvent::NoteOn {
1823 channel,
1824 note,
1825 velocity,
1826 };
1827 self.send_midi_event(event)
1828 }
1829
1830 pub fn send_midi_note_off(&mut self, note: u8, channel: MidiChannel) -> Result<()> {
1832 validate_note(note)?;
1833
1834 let event = MidiEvent::NoteOff {
1835 channel,
1836 note,
1837 velocity: 0,
1838 };
1839 self.send_midi_event(event)
1840 }
1841
1842 pub fn send_midi_cc(&mut self, controller: u8, value: u8, channel: MidiChannel) -> Result<()> {
1844 validate_controller(controller)?;
1845 validate_cc_value(value)?;
1846
1847 let event = MidiEvent::ControlChange {
1848 channel,
1849 controller,
1850 value,
1851 };
1852 self.send_midi_event(event)
1853 }
1854
1855 pub fn send_midi_event(&mut self, event: MidiEvent) -> Result<()> {
1862 validate_midi_event(&event)?;
1863 self.internal
1864 .as_mut()
1865 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1866 .send_midi_event(event)
1867 }
1868
1869 pub fn send_midi_event_at(&mut self, event: MidiEvent, sample_offset: i32) -> Result<()> {
1882 validate_midi_event(&event)?;
1883 self.internal
1884 .as_mut()
1885 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1886 .send_midi_event_at(event, sample_offset)
1887 }
1888
1889 pub fn send_plugin_event(&mut self, event: PluginEvent) -> Result<()> {
1895 validate_plugin_event(&event)?;
1896 self.internal
1897 .as_mut()
1898 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1899 .send_plugin_event(event)
1900 }
1901
1902 pub fn send_sysex(&mut self, bytes: Vec<u8>) -> Result<()> {
1904 self.send_plugin_event(PluginEvent::sysex(bytes))
1905 }
1906
1907 pub fn send_sysex_at(&mut self, bytes: Vec<u8>, sample_offset: i32) -> Result<()> {
1909 self.send_plugin_event(PluginEvent::sysex(bytes).at(sample_offset))
1910 }
1911
1912 pub fn note_on(
1924 &mut self,
1925 channel: MidiChannel,
1926 note: u8,
1927 velocity: u8,
1928 ) -> Result<crate::midi::NoteId> {
1929 self.note_on_at(channel, note, velocity, 0)
1930 }
1931
1932 pub fn note_on_at(
1937 &mut self,
1938 channel: MidiChannel,
1939 note: u8,
1940 velocity: u8,
1941 sample_offset: i32,
1942 ) -> Result<crate::midi::NoteId> {
1943 validate_note(note)?;
1944 validate_velocity(velocity)?;
1945 self.internal
1946 .as_mut()
1947 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1948 .note_on(channel, note, velocity, sample_offset)
1949 }
1950
1951 pub fn note_off(&mut self, id: crate::midi::NoteId) -> Result<()> {
1953 self.note_off_at(id, 0)
1954 }
1955
1956 pub fn note_off_at(&mut self, id: crate::midi::NoteId, sample_offset: i32) -> Result<()> {
1958 self.internal
1959 .as_mut()
1960 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1961 .note_off(id, sample_offset)
1962 }
1963
1964 pub fn send_note_expression(
1969 &mut self,
1970 id: crate::midi::NoteId,
1971 kind: crate::midi::NoteExpressionType,
1972 value: f64,
1973 ) -> Result<()> {
1974 self.send_note_expression_at(id, kind, value, 0)
1975 }
1976
1977 pub fn send_note_expression_at(
1979 &mut self,
1980 id: crate::midi::NoteId,
1981 kind: crate::midi::NoteExpressionType,
1982 value: f64,
1983 sample_offset: i32,
1984 ) -> Result<()> {
1985 if !(0.0..=1.0).contains(&value) {
1986 return Err(Error::InvalidParameter(format!(
1987 "note-expression value {value} out of range [0.0, 1.0]"
1988 )));
1989 }
1990 self.internal
1991 .as_mut()
1992 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
1993 .send_note_expression(id, kind, value, sample_offset)
1994 }
1995
1996 pub fn note_expressions(&self) -> Result<Vec<crate::midi::NoteExpressionInfo>> {
2000 self.internal
2001 .as_ref()
2002 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2003 .note_expressions(0, 0)
2004 }
2005
2006 pub fn start_processing(&mut self) -> Result<()> {
2008 if self.is_processing {
2009 return Ok(());
2010 }
2011
2012 self.internal
2013 .as_mut()
2014 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2015 .start_processing()?;
2016
2017 self.is_processing = true;
2018 Ok(())
2019 }
2020
2021 pub fn stop_processing(&mut self) -> Result<()> {
2023 if !self.is_processing {
2024 return Ok(());
2025 }
2026
2027 self.internal
2028 .as_mut()
2029 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2030 .stop_processing()?;
2031
2032 self.is_processing = false;
2033 Ok(())
2034 }
2035
2036 pub fn process_audio(&mut self, buffers: &mut AudioBuffers) -> Result<()> {
2043 if !self.is_processing {
2044 return Err(Error::NotProcessing);
2045 }
2046
2047 self.internal
2048 .as_mut()
2049 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2050 .process(buffers)?;
2051
2052 if let Ok(mut levels) = self.audio_levels.lock() {
2054 levels.update_from_buffers(&buffers.outputs);
2055
2056 if let Some(ref callback) = self.audio_callback {
2058 callback(&levels);
2059 }
2060 }
2061
2062 Ok(())
2063 }
2064
2065 pub fn audio_bus_layout(&self) -> Result<AudioBusLayout> {
2067 self.internal
2068 .as_ref()
2069 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2070 .audio_bus_layout()
2071 }
2072
2073 pub fn create_bus_audio_buffers(&self, block_size: usize) -> Result<BusAudioBuffers> {
2078 if block_size == 0 {
2079 return Err(Error::Other(
2080 "bus audio block size must be greater than zero".to_string(),
2081 ));
2082 }
2083 Ok(BusAudioBuffers::new(
2084 &self.audio_bus_layout()?,
2085 block_size,
2086 self.sample_rate,
2087 ))
2088 }
2089
2090 pub fn process_bus_audio(&mut self, buffers: &mut BusAudioBuffers) -> Result<()> {
2097 if !self.is_processing {
2098 return Err(Error::NotProcessing);
2099 }
2100 self.internal
2101 .as_mut()
2102 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2103 .process_buses(buffers)?;
2104 if let Ok(mut levels) = self.audio_levels.lock() {
2105 levels.update_from_bus_buffers(&buffers.outputs);
2106 if let Some(ref callback) = self.audio_callback {
2107 callback(&levels);
2108 }
2109 }
2110 Ok(())
2111 }
2112
2113 pub fn get_output_levels(&self) -> AudioLevels {
2119 self.audio_levels
2120 .lock()
2121 .unwrap_or_else(|poisoned| poisoned.into_inner())
2122 .clone()
2123 }
2124
2125 pub fn is_processing(&self) -> bool {
2127 self.is_processing
2128 }
2129
2130 pub fn on_parameter_change<F>(&mut self, callback: F)
2139 where
2140 F: Fn(u32, f64) + Send + 'static,
2141 {
2142 self.parameter_change_callback = Some(Box::new(callback));
2143 }
2144
2145 pub fn on_audio_process<F>(&mut self, callback: F)
2155 where
2156 F: Fn(&AudioLevels) + Send + 'static,
2157 {
2158 self.audio_callback = Some(Box::new(callback));
2159 }
2160
2161 pub fn has_editor(&self) -> bool {
2163 self.internal
2164 .as_ref()
2165 .map(|i| i.has_editor())
2166 .unwrap_or(false)
2167 }
2168
2169 pub fn open_editor(&mut self, parent: WindowHandle) -> Result<()> {
2171 self.internal
2172 .as_mut()
2173 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2174 .open_editor(parent.0)
2175 }
2176
2177 pub fn service_run_loop(&mut self) {
2184 if let Some(internal) = self.internal.as_mut() {
2185 internal.service_run_loop();
2186 }
2187 }
2188
2189 pub fn close_editor(&mut self) -> Result<()> {
2191 self.internal
2192 .as_mut()
2193 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2194 .close_editor()
2195 }
2196
2197 pub fn get_editor_size(&self) -> Result<(i32, i32)> {
2202 self.internal
2203 .as_ref()
2204 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2205 .get_editor_size()
2206 }
2207
2208 pub fn is_editor_open(&self) -> bool {
2213 self.internal
2214 .as_ref()
2215 .is_some_and(|internal| internal.is_editor_open())
2216 }
2217
2218 pub fn attached_editor_size(&self) -> Result<Option<(i32, i32)>> {
2224 self.internal
2225 .as_ref()
2226 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2227 .attached_editor_size()
2228 }
2229
2230 pub fn attached_editor_can_resize(&self) -> Option<bool> {
2236 self.internal
2237 .as_ref()
2238 .and_then(|internal| internal.attached_editor_can_resize())
2239 }
2240
2241 pub fn editor_can_resize(&self) -> bool {
2248 self.internal
2249 .as_ref()
2250 .is_some_and(|internal| internal.editor_can_resize())
2251 }
2252
2253 pub fn resize_editor(&mut self, width: i32, height: i32) -> Result<(i32, i32)> {
2257 self.internal
2258 .as_mut()
2259 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2260 .resize_editor(width, height)
2261 }
2262
2263 pub fn set_editor_scale_factor(&mut self, factor: f32) -> Result<bool> {
2267 self.internal
2268 .as_mut()
2269 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2270 .set_editor_scale_factor(factor)
2271 }
2272
2273 pub fn update_parameters<F>(&mut self, f: F) -> Result<()>
2284 where
2285 F: FnOnce(&mut ParameterUpdate) -> Result<()>,
2286 {
2287 let mut update = ParameterUpdate::new(self);
2288 f(&mut update)?;
2289 update.apply()
2290 }
2291
2292 pub fn midi_panic(&mut self) -> Result<()> {
2294 self.internal
2295 .as_mut()
2296 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2297 .midi_panic()
2298 }
2299
2300 pub fn get_parameter_changes(&self) -> Vec<(u32, f64)> {
2309 self.internal
2310 .as_ref()
2311 .map(|i| i.get_parameter_changes())
2312 .unwrap_or_default()
2313 }
2314
2315 pub fn take_parameter_edits(&mut self) -> Vec<ParameterEdit> {
2327 self.internal
2328 .as_mut()
2329 .map(|i| i.take_parameter_edits())
2330 .unwrap_or_default()
2331 }
2332
2333 pub fn take_host_notifications(&mut self) -> Vec<HostNotification> {
2335 self.internal
2336 .as_mut()
2337 .map(|i| i.take_host_notifications())
2338 .unwrap_or_default()
2339 }
2340
2341 pub fn take_data_exchange_blocks(&mut self) -> Vec<DataExchangeBlock> {
2348 self.internal
2349 .as_mut()
2350 .map(|i| i.take_data_exchange_blocks())
2351 .unwrap_or_default()
2352 }
2353
2354 pub fn execute_context_menu_item(&mut self, menu_id: u64, item_id: u32) -> Result<()> {
2361 self.internal
2362 .as_mut()
2363 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2364 .execute_context_menu_item(menu_id, item_id)
2365 }
2366
2367 pub fn dismiss_context_menu(&mut self, menu_id: u64) -> Result<()> {
2369 self.internal
2370 .as_mut()
2371 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2372 .dismiss_context_menu(menu_id)
2373 }
2374
2375 pub fn take_restart_flags(&mut self) -> RestartFlags {
2382 self.internal
2383 .as_mut()
2384 .map(|i| i.take_restart_flags())
2385 .unwrap_or_default()
2386 }
2387
2388 pub fn service_host_requests(&mut self) -> Result<RestartFlags> {
2394 self.internal
2395 .as_mut()
2396 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2397 .service_host_requests()
2398 }
2399
2400 pub fn take_output_midi(&self) -> Vec<MidiEvent> {
2411 self.internal
2412 .as_ref()
2413 .map(|i| {
2414 i.take_output_events()
2415 .into_iter()
2416 .filter_map(|event| event.to_midi())
2417 .collect()
2418 })
2419 .unwrap_or_default()
2420 }
2421
2422 pub fn take_output_events(&self) -> Vec<crate::midi::OutputEvent> {
2424 self.internal
2425 .as_ref()
2426 .map(|i| i.take_output_events())
2427 .unwrap_or_default()
2428 }
2429
2430 pub fn output_midi_handle(&self) -> Option<OutputMidiConsumer> {
2436 self.internal.as_ref().and_then(|i| i.output_midi_handle())
2437 }
2438
2439 pub fn output_event_handle(&self) -> Option<OutputEventConsumer> {
2441 self.internal.as_ref().and_then(|i| i.output_event_handle())
2442 }
2443
2444 pub fn save_state(&self) -> Result<Vec<u8>> {
2460 self.internal
2461 .as_ref()
2462 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2463 .save_state()
2464 }
2465
2466 pub fn load_state(&mut self, data: &[u8]) -> Result<()> {
2476 self.internal
2477 .as_mut()
2478 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2479 .load_state(data)
2480 }
2481
2482 pub fn load_state_with_context(&mut self, data: &[u8], context: &StateContext) -> Result<()> {
2494 self.internal
2495 .as_mut()
2496 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2497 .load_state_with_context(data, context)
2498 }
2499
2500 pub fn save_preset<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
2504 let info = self.info();
2505 let preset = PluginPreset {
2506 uid: info.uid.clone(),
2507 plugin_name: info.name.clone(),
2508 state: self.save_state()?,
2509 };
2510 let json = serde_json::to_vec_pretty(&preset)
2511 .map_err(|e| Error::Other(format!("serialize preset: {e}")))?;
2512 std::fs::write(path, json).map_err(|e| Error::Other(format!("write preset: {e}")))?;
2513 Ok(())
2514 }
2515
2516 pub fn load_preset<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<()> {
2523 let path = path.as_ref();
2524 let bytes = std::fs::read(path).map_err(|e| Error::Other(format!("read preset: {e}")))?;
2525 let preset: PluginPreset = serde_json::from_slice(&bytes)
2526 .map_err(|e| Error::Other(format!("parse preset: {e}")))?;
2527 if !self.accepts_state_class_id(&preset.uid)? {
2528 return Err(Error::Other(format!(
2529 "preset is for a different plugin ({}, expected {})",
2530 preset.plugin_name,
2531 self.info().name
2532 )));
2533 }
2534 self.load_state_with_context(&preset.state, &StateContext::preset_from_path(path))
2535 }
2536
2537 pub fn save_vstpreset<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
2546 let state = decode_state_snapshot(&self.save_state()?)?;
2547 let bytes = vstpreset::build(
2548 &self.info().uid,
2549 &state.component,
2550 state.controller.as_deref(),
2551 )?;
2552 std::fs::write(path, bytes).map_err(|e| Error::Other(format!("write vstpreset: {e}")))?;
2553 Ok(())
2554 }
2555
2556 pub fn load_vstpreset<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<()> {
2568 let path = path.as_ref();
2569 let bytes =
2570 std::fs::read(path).map_err(|e| Error::Other(format!("read vstpreset: {e}")))?;
2571 let parsed = vstpreset::parse(&bytes)?;
2572 if !self.accepts_state_class_id(&parsed.class_id)? {
2573 return Err(Error::Other(format!(
2574 "vstpreset is for a different plugin (class id {}, expected {})",
2575 parsed.class_id,
2576 self.info().uid
2577 )));
2578 }
2579 let state = encode_state_snapshot(&StateSnapshot {
2580 component: parsed.component_state,
2581 controller: parsed.controller_state,
2582 })?;
2583 self.load_state_with_context(&state, &StateContext::preset_from_path(path))
2584 }
2585
2586 fn accepts_state_class_id(&self, candidate: &str) -> Result<bool> {
2590 if crate::internal::utils::class_uid_matches(&self.info().uid, candidate) {
2591 return Ok(true);
2592 }
2593 Ok(self.compatibility.iter().any(|mapping| {
2594 crate::internal::utils::class_uid_matches(&mapping.new_class_id, &self.info().uid)
2595 && mapping
2596 .old_class_ids
2597 .iter()
2598 .any(|old| crate::internal::utils::class_uid_matches(old, candidate))
2599 }))
2600 }
2601
2602 pub fn isolation_pid(&self) -> Option<u32> {
2605 self.internal.as_ref().and_then(|i| i.helper_pid())
2606 }
2607
2608 pub fn recovery_count(&self) -> u64 {
2618 self.internal
2619 .as_ref()
2620 .map(|i| i.recovery_count())
2621 .unwrap_or(0)
2622 }
2623
2624 pub fn output_channel_count(&self) -> usize {
2629 self.internal
2630 .as_ref()
2631 .map(|i| i.output_channel_count())
2632 .unwrap_or(2)
2633 }
2634
2635 pub fn take_editor_resize_request(&self) -> Option<(i32, i32)> {
2642 self.internal
2643 .as_ref()
2644 .and_then(|i| i.take_editor_resize_request())
2645 }
2646
2647 pub fn recover(&mut self) -> Result<()> {
2659 self.internal
2660 .as_mut()
2661 .ok_or_else(|| Error::Other("Plugin not initialized".to_string()))?
2662 .recover()
2663 }
2664}
2665
2666const MIDI_DATA_MAX: u8 = 127;
2668
2669const MIDI_PITCH_BEND_MAX: u16 = 16383;
2671
2672fn validate_note(note: u8) -> Result<()> {
2673 if note > MIDI_DATA_MAX {
2674 return Err(Error::MidiError(format!("Invalid note number: {}", note)));
2675 }
2676 Ok(())
2677}
2678
2679fn validate_velocity(velocity: u8) -> Result<()> {
2680 if velocity > MIDI_DATA_MAX {
2681 return Err(Error::MidiError(format!("Invalid velocity: {}", velocity)));
2682 }
2683 Ok(())
2684}
2685
2686fn validate_controller(controller: u8) -> Result<()> {
2687 if controller > MIDI_DATA_MAX {
2688 return Err(Error::MidiError(format!(
2689 "Invalid controller number: {}",
2690 controller
2691 )));
2692 }
2693 Ok(())
2694}
2695
2696fn validate_cc_value(value: u8) -> Result<()> {
2697 if value > MIDI_DATA_MAX {
2698 return Err(Error::MidiError(format!("Invalid CC value: {}", value)));
2699 }
2700 Ok(())
2701}
2702
2703fn validate_pressure(pressure: u8) -> Result<()> {
2704 if pressure > MIDI_DATA_MAX {
2705 return Err(Error::MidiError(format!(
2706 "Invalid pressure value: {}",
2707 pressure
2708 )));
2709 }
2710 Ok(())
2711}
2712
2713fn validate_midi_event(event: &MidiEvent) -> Result<()> {
2724 match *event {
2725 MidiEvent::NoteOn { note, velocity, .. } | MidiEvent::NoteOff { note, velocity, .. } => {
2726 validate_note(note)?;
2727 validate_velocity(velocity)
2728 }
2729 MidiEvent::ControlChange {
2730 controller, value, ..
2731 } => {
2732 validate_controller(controller)?;
2733 validate_cc_value(value)
2734 }
2735 MidiEvent::ProgramChange { program, .. } => {
2736 if program > MIDI_DATA_MAX {
2737 return Err(Error::MidiError(format!(
2738 "Invalid program number: {}",
2739 program
2740 )));
2741 }
2742 Ok(())
2743 }
2744 MidiEvent::PitchBend { value, .. } => {
2745 if value > MIDI_PITCH_BEND_MAX {
2746 return Err(Error::MidiError(format!(
2747 "Invalid pitch bend value: {} (0-{})",
2748 value, MIDI_PITCH_BEND_MAX
2749 )));
2750 }
2751 Ok(())
2752 }
2753 MidiEvent::ChannelAftertouch { pressure, .. } => validate_pressure(pressure),
2754 MidiEvent::PolyAftertouch { note, pressure, .. } => {
2755 validate_note(note)?;
2756 validate_pressure(pressure)
2757 }
2758 }
2759}
2760
2761fn validate_plugin_event(event: &PluginEvent) -> Result<()> {
2762 if event.bus_index < 0 {
2763 return Err(Error::MidiError(format!(
2764 "Invalid event bus index: {}",
2765 event.bus_index
2766 )));
2767 }
2768 if event.sample_offset < 0 {
2769 return Err(Error::MidiError(format!(
2770 "Invalid event sample offset: {}",
2771 event.sample_offset
2772 )));
2773 }
2774 if !event.ppq_position.is_finite() {
2775 return Err(Error::MidiError(
2776 "Event PPQ position must be finite".to_string(),
2777 ));
2778 }
2779
2780 let validate_channel = |channel: i16| {
2781 if (0..16).contains(&channel) {
2782 Ok(())
2783 } else {
2784 Err(Error::MidiError(format!(
2785 "Invalid VST3 event channel: {channel}"
2786 )))
2787 }
2788 };
2789 let validate_pitch = |pitch: i16| {
2790 if (0..=127).contains(&pitch) {
2791 Ok(())
2792 } else {
2793 Err(Error::MidiError(format!(
2794 "Invalid VST3 event pitch: {pitch}"
2795 )))
2796 }
2797 };
2798 let validate_normalized = |name: &str, value: f64| {
2799 if value.is_finite() && (0.0..=1.0).contains(&value) {
2800 Ok(())
2801 } else {
2802 Err(Error::MidiError(format!(
2803 "{name} must be finite and normalized to [0.0, 1.0]"
2804 )))
2805 }
2806 };
2807
2808 match &event.data {
2809 PluginEventData::NoteOn {
2810 channel,
2811 pitch,
2812 tuning,
2813 velocity,
2814 length,
2815 ..
2816 } => {
2817 validate_channel(*channel)?;
2818 validate_pitch(*pitch)?;
2819 validate_normalized("note velocity", f64::from(*velocity))?;
2820 if !tuning.is_finite() || *length < 0 {
2821 return Err(Error::MidiError(
2822 "note tuning must be finite and length non-negative".to_string(),
2823 ));
2824 }
2825 Ok(())
2826 }
2827 PluginEventData::NoteOff {
2828 channel,
2829 pitch,
2830 velocity,
2831 tuning,
2832 ..
2833 } => {
2834 validate_channel(*channel)?;
2835 validate_pitch(*pitch)?;
2836 validate_normalized("note-off velocity", f64::from(*velocity))?;
2837 if !tuning.is_finite() {
2838 return Err(Error::MidiError(
2839 "note-off tuning must be finite".to_string(),
2840 ));
2841 }
2842 Ok(())
2843 }
2844 PluginEventData::Data { data_type, bytes } => {
2845 if *data_type != 0 {
2846 return Err(Error::MidiError(format!(
2847 "Unsupported VST3 data event type: {data_type}"
2848 )));
2849 }
2850 if bytes.len() > MAX_EVENT_PAYLOAD_BYTES {
2851 return Err(Error::MidiError(format!(
2852 "Event payload is {} bytes; maximum is {MAX_EVENT_PAYLOAD_BYTES}",
2853 bytes.len()
2854 )));
2855 }
2856 Ok(())
2857 }
2858 PluginEventData::PolyPressure {
2859 channel,
2860 pitch,
2861 pressure,
2862 ..
2863 } => {
2864 validate_channel(*channel)?;
2865 validate_pitch(*pitch)?;
2866 validate_normalized("poly pressure", f64::from(*pressure))
2867 }
2868 PluginEventData::NoteExpressionValue { value, .. } => {
2869 validate_normalized("note-expression value", *value)
2870 }
2871 PluginEventData::NoteExpressionText { text, .. }
2872 | PluginEventData::Chord { text, .. }
2873 | PluginEventData::Scale { text, .. } => {
2874 if text.len() > MAX_EVENT_TEXT_UNITS {
2875 return Err(Error::MidiError(format!(
2876 "Event text is {} UTF-16 units; maximum is {MAX_EVENT_TEXT_UNITS}",
2877 text.len()
2878 )));
2879 }
2880 Ok(())
2881 }
2882 PluginEventData::NoteExpressionIntValue { .. } => Ok(()),
2883 PluginEventData::LegacyMidiCcOut { .. } => Err(Error::MidiError(
2884 "Legacy MIDI CC events are plugin output only".to_string(),
2885 )),
2886 }
2887}
2888
2889pub struct WindowHandle(pub(crate) *mut std::ffi::c_void);
2891
2892impl WindowHandle {
2893 pub unsafe fn from_raw(handle: *mut std::ffi::c_void) -> Self {
2901 Self(handle)
2902 }
2903}
2904
2905unsafe impl Send for WindowHandle {}
2907
2908#[cfg(target_os = "macos")]
2909impl WindowHandle {
2910 pub unsafe fn from_nsview(view: *mut std::ffi::c_void) -> Self {
2919 Self(view)
2920 }
2921}
2922
2923#[cfg(target_os = "windows")]
2924impl WindowHandle {
2925 pub unsafe fn from_hwnd(hwnd: *mut std::ffi::c_void) -> Self {
2933 Self(hwnd)
2934 }
2935}
2936
2937#[cfg(target_os = "linux")]
2938impl WindowHandle {
2939 pub fn from_x11(window_id: u32) -> Self {
2944 Self(window_id as usize as *mut std::ffi::c_void)
2945 }
2946}
2947
2948mod vstpreset {
2960 use crate::error::{Error, Result};
2961
2962 const MAGIC: &[u8; 4] = b"VST3";
2963 const LIST_MAGIC: &[u8; 4] = b"List";
2964 const COMPONENT_CHUNK: &[u8; 4] = b"Comp";
2965 const CONTROLLER_CHUNK: &[u8; 4] = b"Cont";
2966 const VERSION: i32 = 1;
2967 const CLASS_ID_LEN: usize = 32;
2968 const HEADER_SIZE: usize = 4 + 4 + CLASS_ID_LEN + 8;
2969 const LIST_HEADER_SIZE: usize = 8;
2970 const ENTRY_SIZE: usize = 20;
2971
2972 pub(super) struct Parsed {
2974 pub class_id: String,
2976 pub component_state: Vec<u8>,
2978 pub controller_state: Option<Vec<u8>>,
2980 }
2981
2982 pub(super) fn build(
2984 class_id: &str,
2985 component_state: &[u8],
2986 controller_state: Option<&[u8]>,
2987 ) -> Result<Vec<u8>> {
2988 let class_bytes = class_id.as_bytes();
2989 if class_bytes.len() != CLASS_ID_LEN
2990 || !class_bytes.iter().all(|byte| byte.is_ascii_hexdigit())
2991 {
2992 return Err(Error::Other(format!(
2993 "vstpreset class id must be {CLASS_ID_LEN} ASCII hex chars, got {:?}",
2994 class_id
2995 )));
2996 }
2997
2998 let comp_offset = HEADER_SIZE as i64;
2999 let comp_size = component_state.len() as i64;
3000 let controller_offset = HEADER_SIZE
3001 .checked_add(component_state.len())
3002 .ok_or_else(|| Error::Other("vstpreset size overflow".to_string()))?;
3003 let list_offset = controller_offset
3004 .checked_add(controller_state.map_or(0, <[u8]>::len))
3005 .ok_or_else(|| Error::Other("vstpreset size overflow".to_string()))?;
3006 let entry_count = if controller_state.is_some() { 2 } else { 1 };
3007
3008 let mut out = Vec::with_capacity(list_offset + LIST_HEADER_SIZE + entry_count * ENTRY_SIZE);
3009 out.extend_from_slice(MAGIC);
3011 out.extend_from_slice(&VERSION.to_le_bytes());
3012 out.extend(class_bytes.iter().map(u8::to_ascii_uppercase));
3013 out.extend_from_slice(&(list_offset as i64).to_le_bytes());
3014 out.extend_from_slice(component_state);
3016 if let Some(controller) = controller_state {
3017 out.extend_from_slice(controller);
3018 }
3019 out.extend_from_slice(LIST_MAGIC);
3021 out.extend_from_slice(&(entry_count as i32).to_le_bytes());
3022 out.extend_from_slice(COMPONENT_CHUNK);
3023 out.extend_from_slice(&comp_offset.to_le_bytes());
3024 out.extend_from_slice(&comp_size.to_le_bytes());
3025 if let Some(controller) = controller_state {
3026 out.extend_from_slice(CONTROLLER_CHUNK);
3027 out.extend_from_slice(&(controller_offset as i64).to_le_bytes());
3028 out.extend_from_slice(&(controller.len() as i64).to_le_bytes());
3029 }
3030
3031 Ok(out)
3032 }
3033
3034 pub(super) fn parse(bytes: &[u8]) -> Result<Parsed> {
3036 if bytes.len() < HEADER_SIZE {
3037 return Err(Error::Other("vstpreset too short for header".to_string()));
3038 }
3039 if &bytes[0..4] != MAGIC {
3040 return Err(Error::Other(format!(
3041 "bad vstpreset magic: expected {:?}, got {:?}",
3042 MAGIC,
3043 &bytes[0..4]
3044 )));
3045 }
3046 let version = read_i32(&bytes[4..8]);
3047 if version != VERSION {
3048 return Err(Error::Other(format!(
3049 "unsupported vstpreset version {version} (expected {VERSION})"
3050 )));
3051 }
3052 let class_id = String::from_utf8(bytes[8..8 + CLASS_ID_LEN].to_vec())
3053 .map_err(|e| Error::Other(format!("vstpreset class id not UTF-8: {e}")))?;
3054 if !class_id.bytes().all(|byte| byte.is_ascii_hexdigit()) {
3055 return Err(Error::Other(
3056 "vstpreset class id is not 32 ASCII hex characters".to_string(),
3057 ));
3058 }
3059 let list_offset = read_i64(&bytes[8 + CLASS_ID_LEN..HEADER_SIZE]);
3060 if list_offset < HEADER_SIZE as i64 || list_offset as usize > bytes.len() {
3061 return Err(Error::Other(format!(
3062 "vstpreset chunk-list offset {list_offset} out of bounds (len {})",
3063 bytes.len()
3064 )));
3065 }
3066 let list = &bytes[list_offset as usize..];
3067 if list.len() < 8 || &list[0..4] != LIST_MAGIC {
3068 return Err(Error::Other(
3069 "vstpreset chunk list missing or malformed".to_string(),
3070 ));
3071 }
3072 let count = read_i32(&list[4..8]);
3073 if count < 0 {
3074 return Err(Error::Other("vstpreset negative entry count".to_string()));
3075 }
3076 let count = count as usize;
3077 let list_size = LIST_HEADER_SIZE
3078 .checked_add(
3079 count
3080 .checked_mul(ENTRY_SIZE)
3081 .ok_or_else(|| Error::Other("vstpreset entry count overflow".to_string()))?,
3082 )
3083 .ok_or_else(|| Error::Other("vstpreset list size overflow".to_string()))?;
3084 if list.len() < list_size {
3085 return Err(Error::Other(
3086 "vstpreset chunk-list entry truncated".to_string(),
3087 ));
3088 }
3089
3090 let body_end = list_offset as usize;
3091 let mut cursor = LIST_HEADER_SIZE;
3092 let mut component_state = None;
3093 let mut controller_state = None;
3094 let mut ranges: Vec<(usize, usize)> = Vec::with_capacity(count);
3095 for _ in 0..count {
3096 let id = &list[cursor..cursor + 4];
3097 let offset = read_i64(&list[cursor + 4..cursor + 12]);
3098 let size = read_i64(&list[cursor + 12..cursor + 20]);
3099 cursor += ENTRY_SIZE;
3100 if offset < HEADER_SIZE as i64 || size < 0 {
3101 return Err(Error::Other(
3102 "vstpreset chunk has invalid offset/size".to_string(),
3103 ));
3104 }
3105 let start = usize::try_from(offset)
3106 .map_err(|_| Error::Other("vstpreset chunk offset overflow".to_string()))?;
3107 let size = usize::try_from(size)
3108 .map_err(|_| Error::Other("vstpreset chunk size overflow".to_string()))?;
3109 let end = start
3110 .checked_add(size)
3111 .ok_or_else(|| Error::Other("vstpreset chunk size overflow".to_string()))?;
3112 if end > body_end {
3113 return Err(Error::Other(format!(
3114 "vstpreset chunk [{start}..{end}] is outside the payload body \
3115 [{HEADER_SIZE}..{body_end}]"
3116 )));
3117 }
3118 if ranges
3119 .iter()
3120 .any(|&(other_start, other_end)| start < other_end && other_start < end)
3121 {
3122 return Err(Error::Other("vstpreset chunk payloads overlap".to_string()));
3123 }
3124 ranges.push((start, end));
3125
3126 match id {
3127 id if id == COMPONENT_CHUNK => {
3128 if component_state.is_some() {
3129 return Err(Error::Other(
3130 "vstpreset has duplicate component chunks".to_string(),
3131 ));
3132 }
3133 component_state = Some(bytes[start..end].to_vec());
3134 }
3135 id if id == CONTROLLER_CHUNK => {
3136 if controller_state.is_some() {
3137 return Err(Error::Other(
3138 "vstpreset has duplicate controller chunks".to_string(),
3139 ));
3140 }
3141 controller_state = Some(bytes[start..end].to_vec());
3142 }
3143 _ => {}
3144 }
3145 }
3146 let component_state = component_state.ok_or_else(|| {
3147 Error::Other("vstpreset has no component (\"Comp\") chunk".to_string())
3148 })?;
3149 Ok(Parsed {
3150 class_id,
3151 component_state,
3152 controller_state,
3153 })
3154 }
3155
3156 fn read_i32(b: &[u8]) -> i32 {
3157 i32::from_le_bytes([b[0], b[1], b[2], b[3]])
3158 }
3159
3160 fn read_i64(b: &[u8]) -> i64 {
3161 i64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
3162 }
3163}
3164
3165#[cfg(test)]
3166mod public_surface_tests {
3167 use super::*;
3168
3169 fn unloaded_plugin() -> Plugin {
3172 Plugin {
3173 info: PluginInfo {
3174 path: std::path::PathBuf::from("/none.vst3"),
3175 name: "None".to_string(),
3176 vendor: String::new(),
3177 version: String::new(),
3178 category: String::new(),
3179 uid: String::new(),
3180 audio_inputs: 0,
3181 audio_outputs: 2,
3182 has_midi_input: true,
3183 has_midi_output: false,
3184 has_gui: false,
3185 },
3186 compatibility: Vec::new(),
3187 is_processing: false,
3188 sample_rate: 44_100.0,
3189 block_size: 512,
3190 audio_levels: Arc::new(Mutex::new(AudioLevels::new(2))),
3191 parameter_change_callback: None,
3192 audio_callback: None,
3193 internal: None,
3194 }
3195 }
3196
3197 #[test]
3200 fn process_audio_while_stopped_is_the_allocation_free_variant() {
3201 let mut plugin = unloaded_plugin();
3202 let mut buffers = AudioBuffers::new(0, 2, 64, 44_100.0);
3203 let err = plugin
3204 .process_audio(&mut buffers)
3205 .expect_err("processing is stopped");
3206 assert!(
3207 matches!(err, Error::NotProcessing),
3208 "expected Error::NotProcessing, got {err:?}"
3209 );
3210 }
3211
3212 #[test]
3216 fn send_midi_event_rejects_out_of_range_fields() {
3217 let mut plugin = unloaded_plugin();
3218 let bad = [
3219 MidiEvent::NoteOn {
3220 channel: MidiChannel::Ch1,
3221 note: 255,
3222 velocity: 100,
3223 },
3224 MidiEvent::NoteOn {
3225 channel: MidiChannel::Ch1,
3226 note: 60,
3227 velocity: 255,
3228 },
3229 MidiEvent::NoteOff {
3230 channel: MidiChannel::Ch1,
3231 note: 128,
3232 velocity: 0,
3233 },
3234 MidiEvent::ControlChange {
3235 channel: MidiChannel::Ch1,
3236 controller: 200,
3237 value: 0,
3238 },
3239 MidiEvent::ControlChange {
3240 channel: MidiChannel::Ch1,
3241 controller: 1,
3242 value: 200,
3243 },
3244 MidiEvent::ProgramChange {
3245 channel: MidiChannel::Ch1,
3246 program: 200,
3247 },
3248 MidiEvent::PitchBend {
3249 channel: MidiChannel::Ch1,
3250 value: 16_384,
3251 },
3252 MidiEvent::ChannelAftertouch {
3253 channel: MidiChannel::Ch1,
3254 pressure: 200,
3255 },
3256 MidiEvent::PolyAftertouch {
3257 channel: MidiChannel::Ch1,
3258 note: 200,
3259 pressure: 1,
3260 },
3261 MidiEvent::PolyAftertouch {
3262 channel: MidiChannel::Ch1,
3263 note: 60,
3264 pressure: 200,
3265 },
3266 ];
3267 for event in bad {
3268 for err in [
3269 plugin.send_midi_event(event).expect_err("out of range"),
3270 plugin
3271 .send_midi_event_at(event, 0)
3272 .expect_err("out of range"),
3273 ] {
3274 assert!(
3275 matches!(err, Error::MidiError(_)),
3276 "expected a MidiError for {event:?}, got {err:?}"
3277 );
3278 }
3279 }
3280 }
3281
3282 #[test]
3285 fn send_midi_event_accepts_in_range_fields() {
3286 let mut plugin = unloaded_plugin();
3287 let ok = [
3288 MidiEvent::NoteOn {
3289 channel: MidiChannel::Ch1,
3290 note: 127,
3291 velocity: 127,
3292 },
3293 MidiEvent::ControlChange {
3294 channel: MidiChannel::Ch1,
3295 controller: 127,
3296 value: 127,
3297 },
3298 MidiEvent::ProgramChange {
3299 channel: MidiChannel::Ch1,
3300 program: 127,
3301 },
3302 MidiEvent::PitchBend {
3303 channel: MidiChannel::Ch1,
3304 value: 16_383,
3305 },
3306 MidiEvent::ChannelAftertouch {
3307 channel: MidiChannel::Ch1,
3308 pressure: 127,
3309 },
3310 MidiEvent::PolyAftertouch {
3311 channel: MidiChannel::Ch1,
3312 note: 127,
3313 pressure: 127,
3314 },
3315 ];
3316 for event in ok {
3317 let err = plugin.send_midi_event(event).expect_err("no plugin loaded");
3318 assert!(
3319 matches!(err, Error::Other(_)),
3320 "expected the uninitialized-plugin error for {event:?}, got {err:?}"
3321 );
3322 }
3323 }
3324
3325 #[test]
3328 fn note_on_rejects_out_of_range_note_and_velocity() {
3329 let mut plugin = unloaded_plugin();
3330 for (note, velocity) in [(128, 100), (60, 128), (255, 255)] {
3331 let err = plugin
3332 .note_on(MidiChannel::Ch1, note, velocity)
3333 .expect_err("out of range");
3334 assert!(
3335 matches!(err, Error::MidiError(_)),
3336 "expected a MidiError for note {note} velocity {velocity}, got {err:?}"
3337 );
3338 let err = plugin
3339 .note_on_at(MidiChannel::Ch1, note, velocity, 0)
3340 .expect_err("out of range");
3341 assert!(matches!(err, Error::MidiError(_)), "got {err:?}");
3342 }
3343 }
3344}
3345
3346#[cfg(test)]
3347mod output_midi_consumer_tests {
3348 use super::*;
3349
3350 fn note(n: u8) -> MidiEvent {
3351 MidiEvent::NoteOn {
3352 channel: MidiChannel::Ch1,
3353 note: n,
3354 velocity: 100,
3355 }
3356 }
3357
3358 #[test]
3359 fn drains_in_order_and_drops_oldest_when_full() {
3360 let q = Arc::new(ArrayQueue::new(2));
3361 let consumer = OutputMidiConsumer::from_queue(q.clone());
3362
3363 q.force_push(note(60).into());
3365 q.force_push(note(61).into());
3366 q.force_push(note(62).into()); assert_eq!(consumer.drain(), vec![note(61), note(62)]);
3369 assert_eq!(consumer.pop(), None);
3371 assert_eq!(consumer.drain(), vec![]);
3372 }
3373
3374 #[test]
3375 fn handle_is_send_and_shares_the_queue_across_threads() {
3376 let q = Arc::new(ArrayQueue::new(8));
3377 let consumer = OutputMidiConsumer::from_queue(q.clone());
3378 let producer = q.clone();
3380 std::thread::spawn(move || {
3381 producer.force_push(note(64).into());
3382 })
3383 .join()
3384 .unwrap();
3385 assert_eq!(consumer.pop(), Some(note(64)));
3386 }
3387}
3388
3389#[cfg(test)]
3390mod vstpreset_tests {
3391 use super::{
3392 decode_state_snapshot, encode_state_snapshot, vstpreset, StateSnapshot,
3393 STATE_SNAPSHOT_MAGIC,
3394 };
3395
3396 const TEST_CLASS_ID: &str = "0123456789ABCDEF0123456789ABCDEF";
3397
3398 #[test]
3399 fn build_parse_round_trip() {
3400 let state = b"opaque plugin state \x00\x01\x02\xff bytes".to_vec();
3401 let controller = b"controller-only state".to_vec();
3402 let bytes = vstpreset::build(TEST_CLASS_ID, &state, Some(&controller)).expect("build");
3403
3404 assert_eq!(&bytes[0..4], b"VST3");
3406 assert_eq!(
3407 i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
3408 1
3409 );
3410 assert_eq!(&bytes[8..40], TEST_CLASS_ID.as_bytes());
3411
3412 let parsed = vstpreset::parse(&bytes).expect("parse");
3413 assert_eq!(parsed.class_id, TEST_CLASS_ID);
3414 assert_eq!(parsed.component_state, state);
3415 assert_eq!(parsed.controller_state, Some(controller));
3416 }
3417
3418 #[test]
3419 fn round_trip_empty_state() {
3420 let bytes = vstpreset::build(TEST_CLASS_ID, &[], None).expect("build");
3421 let parsed = vstpreset::parse(&bytes).expect("parse");
3422 assert_eq!(parsed.class_id, TEST_CLASS_ID);
3423 assert!(parsed.component_state.is_empty());
3424 assert!(parsed.controller_state.is_none());
3425 }
3426
3427 #[test]
3428 fn round_trip_empty_controller_state() {
3429 let bytes = vstpreset::build(TEST_CLASS_ID, b"component", Some(&[])).expect("build");
3430 let parsed = vstpreset::parse(&bytes).expect("parse");
3431 assert_eq!(parsed.controller_state, Some(Vec::new()));
3432 }
3433
3434 #[test]
3435 fn build_rejects_wrong_length_class_id() {
3436 assert!(vstpreset::build("short", b"x", None).is_err());
3437 assert!(vstpreset::build("Z123456789ABCDEF0123456789ABCDEF", b"x", None).is_err());
3438 }
3439
3440 #[test]
3441 fn parse_rejects_bad_magic() {
3442 let mut bytes = vstpreset::build(TEST_CLASS_ID, b"x", None).expect("build");
3443 bytes[0] = b'X';
3444 assert!(vstpreset::parse(&bytes).is_err());
3445 }
3446
3447 #[test]
3448 fn parse_rejects_truncated_header() {
3449 assert!(vstpreset::parse(b"VST3").is_err());
3450 }
3451
3452 #[test]
3453 fn parse_rejects_out_of_bounds_list_offset() {
3454 let mut bytes = vstpreset::build(TEST_CLASS_ID, b"hello", None).expect("build");
3455 let bad = (bytes.len() as i64 + 100).to_le_bytes();
3457 bytes[40..48].copy_from_slice(&bad);
3458 assert!(vstpreset::parse(&bytes).is_err());
3459 }
3460
3461 #[test]
3462 fn parser_accepts_unknown_chunk_and_controller_before_component() {
3463 let comp = b"component";
3464 let cont = b"controller";
3465 let unknown = b"metadata";
3466 let body_len = comp.len() + cont.len() + unknown.len();
3467 let list_offset = 48 + body_len;
3468 let mut bytes = Vec::new();
3469 bytes.extend_from_slice(b"VST3");
3470 bytes.extend_from_slice(&1i32.to_le_bytes());
3471 bytes.extend_from_slice(TEST_CLASS_ID.as_bytes());
3472 bytes.extend_from_slice(&(list_offset as i64).to_le_bytes());
3473 bytes.extend_from_slice(comp);
3474 bytes.extend_from_slice(cont);
3475 bytes.extend_from_slice(unknown);
3476 bytes.extend_from_slice(b"List");
3477 bytes.extend_from_slice(&3i32.to_le_bytes());
3478 for (id, offset, state) in [
3479 (b"Cont", 48 + comp.len(), cont.as_slice()),
3480 (b"Info", 48 + comp.len() + cont.len(), unknown.as_slice()),
3481 (b"Comp", 48, comp.as_slice()),
3482 ] {
3483 bytes.extend_from_slice(id);
3484 bytes.extend_from_slice(&(offset as i64).to_le_bytes());
3485 bytes.extend_from_slice(&(state.len() as i64).to_le_bytes());
3486 }
3487 let parsed = vstpreset::parse(&bytes).expect("parse");
3488 assert_eq!(parsed.component_state, comp);
3489 assert_eq!(parsed.controller_state.as_deref(), Some(cont.as_slice()));
3490 }
3491
3492 #[test]
3493 fn parser_rejects_duplicate_or_overlapping_chunks() {
3494 let mut duplicate =
3495 vstpreset::build(TEST_CLASS_ID, b"component", Some(b"controller")).expect("build");
3496 let list_offset = i64::from_le_bytes(duplicate[40..48].try_into().unwrap()) as usize;
3497 duplicate[list_offset + 28..list_offset + 32].copy_from_slice(b"Comp");
3498 assert!(vstpreset::parse(&duplicate).is_err());
3499
3500 let mut overlap =
3501 vstpreset::build(TEST_CLASS_ID, b"component", Some(b"controller")).expect("build");
3502 let list_offset = i64::from_le_bytes(overlap[40..48].try_into().unwrap()) as usize;
3503 let comp_offset = i64::from_le_bytes(
3504 overlap[list_offset + 12..list_offset + 20]
3505 .try_into()
3506 .unwrap(),
3507 );
3508 overlap[list_offset + 32..list_offset + 40]
3509 .copy_from_slice(&(comp_offset + 1).to_le_bytes());
3510 assert!(vstpreset::parse(&overlap).is_err());
3511 }
3512
3513 #[test]
3514 fn state_snapshot_round_trip_and_legacy_component_compatibility() {
3515 let snapshot = StateSnapshot {
3516 component: b"component".to_vec(),
3517 controller: Some(b"controller".to_vec()),
3518 };
3519 let bytes = encode_state_snapshot(&snapshot).expect("encode");
3520 assert!(bytes.starts_with(STATE_SNAPSHOT_MAGIC));
3521 let decoded = decode_state_snapshot(&bytes).expect("decode");
3522 assert_eq!(decoded.component, snapshot.component);
3523 assert_eq!(decoded.controller, snapshot.controller);
3524
3525 let legacy = decode_state_snapshot(b"old raw component blob").expect("legacy");
3526 assert_eq!(legacy.component, b"old raw component blob");
3527 assert!(legacy.controller.is_none());
3528 }
3529
3530 #[test]
3531 fn state_snapshot_rejects_bad_lengths_and_versions() {
3532 let snapshot = StateSnapshot {
3533 component: b"component".to_vec(),
3534 controller: Some(b"controller".to_vec()),
3535 };
3536 let mut bytes = encode_state_snapshot(&snapshot).expect("encode");
3537 bytes[16..20].copy_from_slice(&2u32.to_le_bytes());
3538 assert!(decode_state_snapshot(&bytes).is_err());
3539
3540 let mut bytes = encode_state_snapshot(&snapshot).expect("encode");
3541 bytes[20..24].copy_from_slice(&u32::MAX.to_le_bytes());
3542 assert!(decode_state_snapshot(&bytes).is_err());
3543 }
3544}