sim_lib_plugin_clap/event.rs
1use std::collections::BTreeMap;
2
3use sim_lib_audio_graph_core::BlockEvent;
4
5/// A CLAP input event, mirroring the CLAP event union SIM accepts from a host.
6///
7/// Each variant carries a sample-relative `time` offset and maps to a matching
8/// `BlockEvent` via [`ClapEvent::to_block_event`].
9#[derive(Clone, Debug, PartialEq)]
10pub enum ClapEvent {
11 /// A short (up to 3-byte) raw MIDI message.
12 MidiShort {
13 /// Sample offset of the event within the block.
14 time: u32,
15 /// Raw MIDI bytes; only the first `len` are significant.
16 bytes: [u8; 3],
17 /// Number of valid bytes in `bytes`.
18 len: u8,
19 },
20 /// A note-on event.
21 NoteOn {
22 /// Sample offset of the event within the block.
23 time: u32,
24 /// MIDI channel (0-based).
25 channel: u8,
26 /// Note key number.
27 key: u8,
28 /// Note velocity, normalized to 0.0 to 1.0.
29 velocity: f32,
30 },
31 /// A note-off event.
32 NoteOff {
33 /// Sample offset of the event within the block.
34 time: u32,
35 /// MIDI channel (0-based).
36 channel: u8,
37 /// Note key number.
38 key: u8,
39 /// Release velocity, normalized to 0.0 to 1.0.
40 velocity: f32,
41 },
42 /// A parameter value change addressed by CLAP parameter id.
43 ParamValue {
44 /// Sample offset of the event within the block.
45 time: u32,
46 /// CLAP parameter id, translated through a [`ClapParamMap`].
47 clap_param_id: u32,
48 /// New parameter value.
49 value: f64,
50 },
51}
52
53impl ClapEvent {
54 /// Translates this CLAP event into the audio-graph `BlockEvent`.
55 ///
56 /// Note and MIDI events pass through unchanged; a [`ClapEvent::ParamValue`]
57 /// has its CLAP parameter id resolved to a SIM parameter id through
58 /// `params`.
59 pub fn to_block_event(&self, params: &ClapParamMap) -> BlockEvent<'_> {
60 match self {
61 Self::MidiShort { time, bytes, len } => BlockEvent::Midi {
62 offset: *time,
63 bytes: *bytes,
64 len: *len,
65 },
66 Self::NoteOn {
67 time,
68 channel,
69 key,
70 velocity,
71 } => BlockEvent::NoteOn {
72 offset: *time,
73 channel: *channel,
74 key: *key,
75 velocity: *velocity,
76 },
77 Self::NoteOff {
78 time,
79 channel,
80 key,
81 velocity,
82 } => BlockEvent::NoteOff {
83 offset: *time,
84 channel: *channel,
85 key: *key,
86 velocity: *velocity,
87 },
88 Self::ParamValue {
89 time,
90 clap_param_id,
91 value,
92 } => BlockEvent::ParamSet {
93 offset: *time,
94 param: params.sim_param_for(*clap_param_id),
95 value: *value,
96 },
97 }
98 }
99}
100
101/// A translation table from CLAP parameter ids to SIM parameter ids.
102///
103/// Unmapped CLAP ids resolve to themselves, so an empty map behaves as the
104/// identity mapping.
105#[derive(Clone, Debug, Default, PartialEq, Eq)]
106pub struct ClapParamMap {
107 clap_to_sim: BTreeMap<u32, u32>,
108}
109
110impl ClapParamMap {
111 /// Creates an empty map (identity translation for every id).
112 pub fn new() -> Self {
113 Self::default()
114 }
115
116 /// Builds a map sending each id in `ids` to itself.
117 pub fn identity(ids: impl IntoIterator<Item = u32>) -> Self {
118 let mut map = Self::new();
119 for id in ids {
120 map.insert(id, id);
121 }
122 map
123 }
124
125 /// Records that `clap_param_id` translates to `sim_param_id`.
126 pub fn insert(&mut self, clap_param_id: u32, sim_param_id: u32) {
127 self.clap_to_sim.insert(clap_param_id, sim_param_id);
128 }
129
130 /// Resolves a CLAP parameter id to its SIM parameter id.
131 ///
132 /// Returns `clap_param_id` unchanged when no mapping is recorded.
133 pub fn sim_param_for(&self, clap_param_id: u32) -> u32 {
134 self.clap_to_sim
135 .get(&clap_param_id)
136 .copied()
137 .unwrap_or(clap_param_id)
138 }
139}
140
141/// An ordered buffer of [`ClapEvent`]s for a single processing block.
142#[derive(Clone, Debug, Default, PartialEq)]
143pub struct ClapEventBuffer {
144 events: Vec<ClapEvent>,
145}
146
147impl ClapEventBuffer {
148 /// Builds a buffer from a pre-collected list of events.
149 pub fn new(events: Vec<ClapEvent>) -> Self {
150 Self { events }
151 }
152
153 /// Returns the buffered events in order.
154 pub fn events(&self) -> &[ClapEvent] {
155 &self.events
156 }
157
158 /// Appends one event to the end of the buffer.
159 pub fn push(&mut self, event: ClapEvent) {
160 self.events.push(event);
161 }
162
163 /// Translates every buffered event into an audio-graph `BlockEvent`,
164 /// resolving parameter ids through `params`.
165 pub fn to_block_events(&self, params: &ClapParamMap) -> Vec<BlockEvent<'_>> {
166 self.events
167 .iter()
168 .map(|event| event.to_block_event(params))
169 .collect()
170 }
171}