Skip to main content

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}