audio_processor_standalone_midi/
audio_thread.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 crate::constants::MIDI_BUFFER_CAPACITY;
24use crate::host::{MidiMessageEntry, MidiMessageQueue};
25
26/// Audio-thread side of MIDI handling.
27///
28/// Pops MIDI events from the the MIDI queue & collects them on a pre-allocated fixed capacity
29/// vector.
30pub struct MidiAudioThreadHandler {
31    buffer: Vec<MidiMessageEntry>,
32    capacity: usize,
33}
34
35impl Default for MidiAudioThreadHandler {
36    fn default() -> Self {
37        Self::new(MIDI_BUFFER_CAPACITY)
38    }
39}
40
41impl MidiAudioThreadHandler {
42    pub fn new(capacity: usize) -> Self {
43        MidiAudioThreadHandler {
44            buffer: Vec::with_capacity(capacity),
45            capacity,
46        }
47    }
48
49    /// Get a reference to the message buffer
50    pub fn buffer(&self) -> &Vec<MidiMessageEntry> {
51        &self.buffer
52    }
53
54    /// Push messages onto the buffer
55    ///
56    /// This is real-time safe as long as `MidiAudioThreadHandler::clear` is called on every tick.
57    pub fn collect_midi_messages(&mut self, midi_message_queue: &MidiMessageQueue) -> usize {
58        let mut midi_message_count = 0;
59        for _i in 0..self.capacity {
60            if let Some(midi_message) = midi_message_queue.pop() {
61                self.buffer.push(midi_message);
62                midi_message_count += 1;
63            } else {
64                return midi_message_count;
65            }
66        }
67        midi_message_count
68    }
69
70    /// Clear the messages buffer. Must be called after `collect_midi_messages` on every tick.
71    pub fn clear(&mut self) {
72        self.buffer.clear();
73    }
74}
75
76#[cfg(test)]
77mod test {
78    use assert_no_alloc::assert_no_alloc;
79    use basedrop::{Collector, Owned};
80
81    use audio_processor_traits::MidiMessageLike;
82
83    use crate::host::MidiMessageWrapper;
84
85    use super::*;
86
87    #[test]
88    fn test_create_handler_and_collect_empty_messages() {
89        let mut collector = Collector::new();
90        let handle = collector.handle();
91        let queue = MidiMessageQueue::new(&handle, atomic_queue::Queue::new(MIDI_BUFFER_CAPACITY));
92
93        let mut midi_audio_thread_handler = MidiAudioThreadHandler::default();
94        let num_messages = midi_audio_thread_handler.collect_midi_messages(&queue);
95        assert_eq!(num_messages, 0);
96        let buffer = midi_audio_thread_handler.buffer();
97        assert_eq!(buffer.len(), 0);
98
99        collector.collect();
100    }
101
102    #[test]
103    fn test_create_handler_and_collect_some_messages() {
104        let mut collector = Collector::new();
105        let handle = collector.handle();
106        let queue = MidiMessageQueue::new(&handle, atomic_queue::Queue::new(MIDI_BUFFER_CAPACITY));
107        queue.push(MidiMessageEntry(Owned::new(
108            &handle,
109            MidiMessageWrapper {
110                message_data: [128, 0, 12],
111                timestamp: 0,
112            },
113        )));
114        queue.push(MidiMessageEntry(Owned::new(
115            &handle,
116            MidiMessageWrapper {
117                message_data: [129, 0, 12],
118                timestamp: 0,
119            },
120        )));
121        queue.push(MidiMessageEntry(Owned::new(
122            &handle,
123            MidiMessageWrapper {
124                message_data: [130, 0, 12],
125                timestamp: 0,
126            },
127        )));
128
129        let mut midi_audio_thread_handler = MidiAudioThreadHandler::default();
130
131        let num_messages =
132            assert_no_alloc(|| midi_audio_thread_handler.collect_midi_messages(&queue));
133
134        assert_eq!(num_messages, 3);
135        let buffer = midi_audio_thread_handler.buffer();
136        assert_eq!(buffer.len(), 3);
137        assert_eq!(buffer[0].is_midi(), true);
138        assert_eq!(buffer[0].message_data, [128, 0, 12]);
139        assert_eq!(buffer[1].message_data, [129, 0, 12]);
140        assert_eq!(buffer[2].message_data, [130, 0, 12]);
141
142        collector.collect();
143    }
144
145    #[test]
146    fn test_create_handler_and_clear() {
147        let mut collector = Collector::new();
148        let handle = collector.handle();
149        let queue = MidiMessageQueue::new(&handle, atomic_queue::Queue::new(MIDI_BUFFER_CAPACITY));
150        queue.push(MidiMessageEntry(Owned::new(
151            &handle,
152            MidiMessageWrapper {
153                message_data: [128, 0, 12],
154                timestamp: 0,
155            },
156        )));
157
158        let mut midi_audio_thread_handler = MidiAudioThreadHandler::default();
159        let num_messages =
160            assert_no_alloc(|| midi_audio_thread_handler.collect_midi_messages(&queue));
161        assert_eq!(num_messages, 1);
162        let buffer = midi_audio_thread_handler.buffer();
163        assert_eq!(buffer.len(), 1);
164        assert_eq!(buffer.capacity(), MIDI_BUFFER_CAPACITY);
165        assert_eq!(queue.is_empty(), true);
166        midi_audio_thread_handler.clear();
167        let buffer = midi_audio_thread_handler.buffer();
168        assert_eq!(buffer.len(), 0);
169        assert_eq!(buffer.capacity(), MIDI_BUFFER_CAPACITY);
170
171        collector.collect();
172    }
173}