Skip to main content

vst_audio_ring/
events.rs

1//! Bounded, pointer-free realtime events and transport snapshots.
2pub const MAX_EVENTS: usize = 256;
3pub const MAX_SYSEX_BYTES: usize = 256;
4pub(crate) const EVENT_WORDS: usize = 72;
5pub(crate) const CONTEXT_WORDS: usize = 32;
6pub(crate) const PACKET_WORDS: usize = 2 + MAX_EVENTS * EVENT_WORDS;
7pub(crate) const METADATA_WORDS: usize = CONTEXT_WORDS + 2 * PACKET_WORDS;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct Transport {
11    pub sample_position: i64,
12    pub continuous_position: i64,
13    pub ppq_position: f64,
14    pub tempo: f64,
15    pub bar_start: f64,
16    pub cycle_start: f64,
17    pub cycle_end: f64,
18    pub numerator: u16,
19    pub denominator: u16,
20    pub playing: bool,
21    pub recording: bool,
22    pub cycling: bool,
23}
24impl Default for Transport {
25    fn default() -> Self {
26        Self {
27            sample_position: 0,
28            continuous_position: 0,
29            ppq_position: 0.0,
30            tempo: 120.0,
31            bar_start: 0.0,
32            cycle_start: 0.0,
33            cycle_end: 4.0,
34            numerator: 4,
35            denominator: 4,
36            playing: true,
37            recording: false,
38            cycling: false,
39        }
40    }
41}
42impl Transport {
43    pub fn valid(self) -> bool {
44        [
45            self.ppq_position,
46            self.tempo,
47            self.bar_start,
48            self.cycle_start,
49            self.cycle_end,
50        ]
51        .iter()
52        .all(|v| v.is_finite())
53            && self.tempo > 0.0
54            && self.tempo <= 1_000_000.0
55            && (1..=1024).contains(&self.numerator)
56            && self.denominator.is_power_of_two()
57            && self.denominator <= 1024
58            && (!self.cycling || self.cycle_end > self.cycle_start)
59    }
60    pub fn advanced(mut self, frames: usize, rate: u32) -> Self {
61        let frames_i64 = i64::try_from(frames).unwrap_or(i64::MAX);
62        self.continuous_position = self.continuous_position.saturating_add(frames_i64);
63        if self.playing {
64            self.sample_position = self.sample_position.saturating_add(frames_i64);
65            self.ppq_position += frames as f64 * self.tempo / (60.0 * f64::from(rate.max(1)));
66        }
67        self
68    }
69    pub(crate) fn words(self) -> [u32; CONTEXT_WORDS] {
70        let mut words = [0; CONTEXT_WORDS];
71        words[0] = u32::from(self.playing)
72            | (u32::from(self.recording) << 1)
73            | (u32::from(self.cycling) << 2);
74        words[1] = u32::from(self.numerator) | (u32::from(self.denominator) << 16);
75        for (i, value) in [
76            self.sample_position as u64,
77            self.continuous_position as u64,
78            self.ppq_position.to_bits(),
79            self.tempo.to_bits(),
80            self.bar_start.to_bits(),
81            self.cycle_start.to_bits(),
82            self.cycle_end.to_bits(),
83        ]
84        .into_iter()
85        .enumerate()
86        {
87            words[2 + i * 2] = value as u32;
88            words[3 + i * 2] = (value >> 32) as u32;
89        }
90        words
91    }
92    pub(crate) fn from_words(words: &[u32]) -> Option<Self> {
93        if words.len() < CONTEXT_WORDS || words[0] & !7 != 0 {
94            return None;
95        }
96        let wide = |i: usize| u64::from(words[2 + i * 2]) | (u64::from(words[3 + i * 2]) << 32);
97        let value = Self {
98            sample_position: wide(0) as i64,
99            continuous_position: wide(1) as i64,
100            ppq_position: f64::from_bits(wide(2)),
101            tempo: f64::from_bits(wide(3)),
102            bar_start: f64::from_bits(wide(4)),
103            cycle_start: f64::from_bits(wide(5)),
104            cycle_end: f64::from_bits(wide(6)),
105            numerator: words[1] as u16,
106            denominator: (words[1] >> 16) as u16,
107            playing: words[0] & 1 != 0,
108            recording: words[0] & 2 != 0,
109            cycling: words[0] & 4 != 0,
110        };
111        value.valid().then_some(value)
112    }
113}
114
115#[derive(Clone, Copy, Debug, PartialEq)]
116pub enum EventData {
117    /// MIDI 1 channel message. Unused bytes in 2-byte messages must be zero.
118    Midi([u8; 3]),
119    SysEx {
120        len: u16,
121        bytes: [u8; MAX_SYSEX_BYTES],
122    },
123    Parameter {
124        id: u32,
125        value: f64,
126    },
127    /// An in-block transport change. Processing splits at this sample offset.
128    Transport(Transport),
129}
130#[derive(Clone, Copy, Debug, PartialEq)]
131pub struct Event {
132    pub sample_offset: u32,
133    pub bus: u16,
134    pub data: EventData,
135}
136impl Default for Event {
137    fn default() -> Self {
138        Self {
139            sample_offset: 0,
140            bus: 0,
141            data: EventData::Midi([0x80, 0, 0]),
142        }
143    }
144}
145impl Event {
146    pub fn midi(sample_offset: u32, bus: u16, data: [u8; 3]) -> Self {
147        Self {
148            sample_offset,
149            bus,
150            data: EventData::Midi(data),
151        }
152    }
153    pub fn valid(self, frames: u32) -> bool {
154        self.sample_offset < frames
155            && self.bus < 32
156            && match self.data {
157                EventData::Midi([status, a, b]) => {
158                    (0x80..=0xef).contains(&status)
159                        && a < 128
160                        && b < 128
161                        && (!matches!(status & 0xf0, 0xc0 | 0xd0) || b == 0)
162                }
163                EventData::SysEx { len, .. } => usize::from(len) <= MAX_SYSEX_BYTES && len > 0,
164                EventData::Parameter { value, .. } => {
165                    self.bus == 0 && value.is_finite() && (0.0..=1.0).contains(&value)
166                }
167                EventData::Transport(context) => self.bus == 0 && context.valid(),
168            }
169    }
170    pub(crate) fn words(self) -> [u32; EVENT_WORDS] {
171        let mut words = [0; EVENT_WORDS];
172        words[1] = self.sample_offset;
173        words[2] = u32::from(self.bus);
174        match self.data {
175            EventData::Midi([a, b, c]) => {
176                words[0] = 1;
177                words[3] = u32::from_le_bytes([a, b, c, 0]);
178            }
179            EventData::Parameter { id, value } => {
180                words[0] = 2;
181                words[3] = id;
182                words[4] = value.to_bits() as u32;
183                words[5] = (value.to_bits() >> 32) as u32;
184            }
185            EventData::Transport(context) => {
186                words[0] = 3;
187                words[4..4 + CONTEXT_WORDS].copy_from_slice(&context.words());
188            }
189            EventData::SysEx { len, bytes } => {
190                words[0] = 4;
191                words[3] = u32::from(len);
192                for (to, from) in words[4..68].iter_mut().zip(bytes.as_chunks::<4>().0.iter()) {
193                    *to = u32::from_le_bytes(*from);
194                }
195            }
196        }
197        words
198    }
199    pub(crate) fn from_words(words: &[u32], frames: u32) -> Option<Self> {
200        if words.len() != EVENT_WORDS || words[2] >= 32 {
201            return None;
202        }
203        let data = match words[0] {
204            1 => {
205                let [a, b, c, d] = words[3].to_le_bytes();
206                if d != 0 {
207                    return None;
208                }
209                EventData::Midi([a, b, c])
210            }
211            2 => EventData::Parameter {
212                id: words[3],
213                value: f64::from_bits(u64::from(words[4]) | (u64::from(words[5]) << 32)),
214            },
215            3 => EventData::Transport(Transport::from_words(&words[4..4 + CONTEXT_WORDS])?),
216            4 => {
217                if words[3] == 0 || words[3] > MAX_SYSEX_BYTES as u32 {
218                    return None;
219                }
220                let mut bytes = [0; MAX_SYSEX_BYTES];
221                for (to, from) in bytes
222                    .as_chunks_mut::<4>()
223                    .0
224                    .iter_mut()
225                    .zip(words[4..68].iter())
226                {
227                    to.copy_from_slice(&from.to_le_bytes());
228                }
229                EventData::SysEx {
230                    len: words[3] as u16,
231                    bytes,
232                }
233            }
234            _ => return None,
235        };
236        let event = Self {
237            sample_offset: words[1],
238            bus: words[2] as u16,
239            data,
240        };
241        event.valid(frames).then_some(event)
242    }
243}
244
245#[derive(Clone, Debug)]
246pub struct EventBuffer {
247    events: [Event; MAX_EVENTS],
248    len: usize,
249    pub dropped: u32,
250}
251impl Default for EventBuffer {
252    fn default() -> Self {
253        Self {
254            events: [Event::default(); MAX_EVENTS],
255            len: 0,
256            dropped: 0,
257        }
258    }
259}
260impl EventBuffer {
261    pub fn clear(&mut self) {
262        self.len = 0;
263        self.dropped = 0;
264    }
265    pub fn sort_by_offset(&mut self) {
266        for i in 1..self.len {
267            let mut j = i;
268            while j > 0 && self.events[j - 1].sample_offset > self.events[j].sample_offset {
269                self.events.swap(j - 1, j);
270                j -= 1;
271            }
272        }
273    }
274    pub fn as_slice(&self) -> &[Event] {
275        &self.events[..self.len]
276    }
277    pub fn push(&mut self, event: Event) -> bool {
278        if self.len == MAX_EVENTS {
279            self.dropped = self.dropped.saturating_add(1);
280            return false;
281        }
282        self.events[self.len] = event;
283        self.len += 1;
284        true
285    }
286}