audio_processor_standalone_midi/
vst.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23use std::cmp::{max, min};
24
25use vst::api::{Event, Events, MidiEvent};
26
27use crate::constants::MIDI_BUFFER_CAPACITY;
28use crate::host::MidiMessageEntry;
29
30/// This is an unsafe converter from MIDI events received from [`midir`] into the `rust-vst` VST api.
31///
32/// It's unsafe because it must do manual memory allocation & management to interface with the VST
33/// C-style API.
34///
35/// Pre-allocates buffers of MIDI events up-front. Capacity is set to 100 MIDI messages by default.
36///
37/// More than 100 midi messages being passed into it in a single buffer tick will result in dropped
38/// messages.
39///
40/// The collecting phase of the audio-thread should collect at most a limit of messages.
41///
42/// Threshold can be changed in the future to include more.
43pub struct MidiVSTConverter {
44    events: Box<Events>,
45    /// Events list here for freeing manually allocated memory.
46    #[allow(dead_code, clippy::vec_box)]
47    events_lst: Vec<Box<Event>>,
48    capacity: usize,
49}
50
51impl Default for MidiVSTConverter {
52    fn default() -> Self {
53        Self::new(MIDI_BUFFER_CAPACITY)
54    }
55}
56
57impl MidiVSTConverter {
58    /// Create a new MidiVSTConverter with capacity.
59    ///
60    /// Will pre-allocate buffers.
61    pub fn new(capacity: usize) -> Self {
62        unsafe {
63            let events_ptr = MidiVSTConverter::allocate_events(capacity);
64            let event_ptrs = std::slice::from_raw_parts_mut(
65                &mut (*events_ptr).events[0] as *mut *mut _ as *mut *mut _,
66                capacity,
67            );
68            let mut events_lst = Vec::with_capacity(capacity);
69            for event_ptr_cell in event_ptrs.iter_mut().take(capacity) {
70                let event_ptr = MidiVSTConverter::allocate_event();
71                *event_ptr_cell = event_ptr;
72                events_lst.push(Box::from_raw(*event_ptr_cell));
73            }
74
75            Self {
76                events: Box::from_raw(events_ptr),
77                events_lst,
78                capacity,
79            }
80        }
81    }
82
83    /// Pushes MIDI messages onto a pre-allocated `Events` struct. Returns a reference to it.
84    ///
85    /// This should be real-time safe.
86    ///
87    /// The `vst::api::Events` returned may be passed into a VST plugin instance.
88    pub fn accept(&mut self, midi_message_buffer: &[MidiMessageEntry]) -> &Events {
89        self.events.num_events = min(self.capacity as i32, midi_message_buffer.len() as i32);
90
91        for (i, message) in midi_message_buffer.iter().enumerate() {
92            if i >= self.capacity {
93                log::trace!("MIDI Message was dropped");
94                break;
95            }
96
97            unsafe {
98                log::trace!("Forwarding message {:?}", message.message_data);
99                let event = MidiEvent {
100                    event_type: vst::api::EventType::Midi,
101                    byte_size: std::mem::size_of::<MidiEvent>() as i32,
102                    delta_frames: 0,
103                    flags: 0,
104                    note_length: 0,
105                    note_offset: 0,
106                    midi_data: message.message_data,
107                    _midi_reserved: 0,
108                    detune: 0,
109                    note_off_velocity: 0,
110                    _reserved1: 0,
111                    _reserved2: 0,
112                };
113
114                let ptr = std::slice::from_raw_parts_mut(
115                    &mut self.events.events[0] as *mut *mut Event,
116                    self.events.num_events as usize,
117                );
118                let ptr_event_ref: *mut Event = ptr[i];
119                let midi_event: *mut MidiEvent = std::mem::transmute(ptr_event_ref);
120                *midi_event = event;
121            }
122        }
123
124        &self.events
125    }
126
127    /// Get a reference to the events
128    pub fn events(&self) -> &Events {
129        &self.events
130    }
131
132    /// Allocates a simple event. We're only using MIDI events for now, but should create enough
133    /// space for any message type.
134    unsafe fn allocate_event() -> *mut Event {
135        let (event_size, event_align) = (
136            max(
137                std::mem::size_of::<vst::api::SysExEvent>(),
138                max(
139                    std::mem::size_of::<Event>(),
140                    std::mem::size_of::<MidiEvent>(),
141                ),
142            ),
143            std::mem::align_of::<Event>(),
144        );
145        let event_layout = std::alloc::Layout::from_size_align_unchecked(event_size, event_align);
146
147        std::alloc::alloc(event_layout) as *mut Event
148    }
149
150    /// Allocates the `Events` struct. This is a C struct with a trailing array of events.
151    /// The Rust declaration sizes this array as 2 elements ; here we append space for another
152    /// capacity elements after it.
153    unsafe fn allocate_events(capacity: usize) -> *mut Events {
154        let event_ptr_size = std::mem::size_of::<*mut Event>();
155        let events_layout = std::alloc::Layout::from_size_align_unchecked(
156            std::mem::size_of::<Events>() + event_ptr_size * capacity,
157            std::mem::align_of::<Events>(),
158        );
159
160        std::alloc::alloc(events_layout) as *mut Events
161    }
162}
163
164#[cfg(test)]
165mod test {
166    use basedrop::Owned;
167
168    use audio_processor_traits::MidiMessageLike;
169
170    use crate::host::MidiMessageWrapper;
171    use assert_no_alloc::assert_no_alloc;
172
173    use super::*;
174
175    #[test]
176    fn test_create_converter() {
177        let _midi_vst_converter = MidiVSTConverter::new(10);
178    }
179
180    #[test]
181    fn test_allocate_event() {
182        // Just leak the event ptr
183        let _event_ptr = unsafe { MidiVSTConverter::allocate_event() };
184    }
185
186    #[test]
187    fn test_accept_events() {
188        let mut converter = MidiVSTConverter::new(10);
189        let buffer = [
190            MidiMessageEntry(Owned::new(
191                audio_garbage_collector::handle(),
192                MidiMessageWrapper {
193                    timestamp: 0,
194                    message_data: [10, 20, 30],
195                },
196            )),
197            MidiMessageEntry(Owned::new(
198                audio_garbage_collector::handle(),
199                MidiMessageWrapper {
200                    timestamp: 10,
201                    message_data: [30, 40, 50],
202                },
203            )),
204        ];
205
206        let events = assert_no_alloc(|| converter.accept(&buffer));
207        assert_eq!(events.num_events, 2);
208
209        let event = events.events[0];
210        assert_eq!(event.is_midi(), true);
211        let msg = [10_u8, 20, 30];
212        assert_eq!(event.bytes(), Some(&msg as &[u8]));
213        let event = events.events[1];
214        assert_eq!(event.is_midi(), true);
215        let msg = [30_u8, 40, 50];
216        assert_eq!(event.bytes(), Some(&msg as &[u8]));
217    }
218}