Skip to main content

sim_lib_plugin_vst3/
event.rs

1use std::collections::BTreeMap;
2
3use sim_kernel::{Error, Result};
4use sim_lib_audio_graph_core::BlockEvent;
5
6/// A VST3 input event for one processing block.
7///
8/// Mirrors the VST3 event surface; [`to_block_event`](Vst3Event::to_block_event)
9/// translates each variant into a graph `BlockEvent`, remapping parameter ids
10/// through a [`Vst3ParamMap`].
11#[derive(Clone, Debug, PartialEq)]
12pub enum Vst3Event {
13    /// A note-on event.
14    NoteOn {
15        /// The sample offset of the event within the block.
16        sample_offset: u32,
17        /// The MIDI channel.
18        channel: u8,
19        /// The note pitch (MIDI key number).
20        pitch: u8,
21        /// The note-on velocity.
22        velocity: f32,
23    },
24    /// A note-off event.
25    NoteOff {
26        /// The sample offset of the event within the block.
27        sample_offset: u32,
28        /// The MIDI channel.
29        channel: u8,
30        /// The note pitch (MIDI key number).
31        pitch: u8,
32        /// The note-off (release) velocity.
33        velocity: f32,
34    },
35    /// A raw MIDI event of up to three bytes.
36    Midi {
37        /// The sample offset of the event within the block.
38        sample_offset: u32,
39        /// The MIDI message bytes.
40        bytes: [u8; 3],
41        /// The number of valid bytes in `bytes`.
42        len: u8,
43    },
44    /// A normalized parameter value change.
45    ParamValue {
46        /// The sample offset of the event within the block.
47        sample_offset: u32,
48        /// The VST3 parameter id, remapped to a SIM id on conversion.
49        vst3_param_id: u32,
50        /// The normalized parameter value.
51        normalized: f64,
52    },
53}
54
55impl Vst3Event {
56    /// Translates this event into a graph `BlockEvent`, remapping any parameter
57    /// id through `params`.
58    pub fn to_block_event(&self, params: &Vst3ParamMap) -> BlockEvent<'_> {
59        match self {
60            Self::NoteOn {
61                sample_offset,
62                channel,
63                pitch,
64                velocity,
65            } => BlockEvent::NoteOn {
66                offset: *sample_offset,
67                channel: *channel,
68                key: *pitch,
69                velocity: *velocity,
70            },
71            Self::NoteOff {
72                sample_offset,
73                channel,
74                pitch,
75                velocity,
76            } => BlockEvent::NoteOff {
77                offset: *sample_offset,
78                channel: *channel,
79                key: *pitch,
80                velocity: *velocity,
81            },
82            Self::Midi {
83                sample_offset,
84                bytes,
85                len,
86            } => BlockEvent::Midi {
87                offset: *sample_offset,
88                bytes: *bytes,
89                len: *len,
90            },
91            Self::ParamValue {
92                sample_offset,
93                vst3_param_id,
94                normalized,
95            } => BlockEvent::ParamSet {
96                offset: *sample_offset,
97                param: params.sim_param_for(*vst3_param_id),
98                value: *normalized,
99            },
100        }
101    }
102
103    /// Translates this event after checking that its sample offset is inside
104    /// the current processing block.
105    pub fn try_to_block_event(&self, params: &Vst3ParamMap, frames: u32) -> Result<BlockEvent<'_>> {
106        let event = self.to_block_event(params);
107        let offset = block_event_offset(&event);
108        if offset >= frames {
109            return Err(Error::Eval(format!(
110                "VST3 event offset {offset} is outside block frames 0..{frames}"
111            )));
112        }
113        Ok(event)
114    }
115}
116
117fn block_event_offset(event: &BlockEvent<'_>) -> u32 {
118    match *event {
119        BlockEvent::Midi { offset, .. }
120        | BlockEvent::MidiLong { offset, .. }
121        | BlockEvent::ParamSet { offset, .. }
122        | BlockEvent::NoteOn { offset, .. }
123        | BlockEvent::NoteOff { offset, .. } => offset,
124    }
125}
126
127/// A mapping from host-facing VST3 parameter ids to SIM parameter ids.
128///
129/// Unmapped ids pass through unchanged, so an empty map behaves as the identity.
130#[derive(Clone, Debug, Default, PartialEq, Eq)]
131pub struct Vst3ParamMap {
132    vst3_to_sim: BTreeMap<u32, u32>,
133}
134
135impl Vst3ParamMap {
136    /// Creates an empty map (every id passes through unchanged).
137    pub fn new() -> Self {
138        Self::default()
139    }
140
141    /// Creates a map where each id in `ids` maps to itself.
142    pub fn identity(ids: impl IntoIterator<Item = u32>) -> Self {
143        let mut map = Self::new();
144        for id in ids {
145            map.insert(id, id);
146        }
147        map
148    }
149
150    /// Records that `vst3_param_id` maps to `sim_param_id`.
151    pub fn insert(&mut self, vst3_param_id: u32, sim_param_id: u32) {
152        self.vst3_to_sim.insert(vst3_param_id, sim_param_id);
153    }
154
155    /// Returns the SIM parameter id for `vst3_param_id`, or the input id itself
156    /// when it is unmapped.
157    pub fn sim_param_for(&self, vst3_param_id: u32) -> u32 {
158        self.vst3_to_sim
159            .get(&vst3_param_id)
160            .copied()
161            .unwrap_or(vst3_param_id)
162    }
163}
164
165/// An ordered buffer of VST3 events for one processing block.
166#[derive(Clone, Debug, Default, PartialEq)]
167pub struct Vst3EventBuffer {
168    events: Vec<Vst3Event>,
169}
170
171impl Vst3EventBuffer {
172    /// Creates a buffer from an existing `events` vector.
173    pub fn new(events: Vec<Vst3Event>) -> Self {
174        Self { events }
175    }
176
177    /// Returns the buffered events in order.
178    pub fn events(&self) -> &[Vst3Event] {
179        &self.events
180    }
181
182    /// Appends `event` to the buffer.
183    pub fn push(&mut self, event: Vst3Event) {
184        self.events.push(event);
185    }
186
187    /// Translates every buffered event into a graph `BlockEvent`, remapping
188    /// parameter ids through `params`.
189    pub fn to_block_events(&self, params: &Vst3ParamMap) -> Vec<BlockEvent<'_>> {
190        self.events
191            .iter()
192            .map(|event| event.to_block_event(params))
193            .collect()
194    }
195
196    /// Translates buffered events after checking every event offset against the
197    /// current processing block.
198    pub fn try_to_block_events(
199        &self,
200        params: &Vst3ParamMap,
201        frames: u32,
202    ) -> Result<Vec<BlockEvent<'_>>> {
203        self.events
204            .iter()
205            .map(|event| event.try_to_block_event(params, frames))
206            .collect()
207    }
208}