1use arc_swap::ArcSwap;
2use std::cell::UnsafeCell;
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, Weak};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct MidiEvent {
8 pub frame: u32,
9 pub data: Vec<u8>,
10}
11
12impl MidiEvent {
13 pub fn new(frame: u32, data: Vec<u8>) -> Self {
14 Self { frame, data }
15 }
16}
17
18#[derive(Debug, Default)]
34#[allow(clippy::upper_case_acronyms)]
35pub struct MIDIIO {
36 sources: ArcSwap<Vec<Weak<MIDIIO>>>,
38 connections: ArcSwap<Vec<Weak<MIDIIO>>>,
41 buffer: UnsafeCell<Vec<MidiEvent>>,
42 finished: AtomicBool,
43}
44
45unsafe impl Sync for MIDIIO {}
50
51#[derive(Debug)]
55pub struct MidiBufferMut<'a> {
56 buffer: &'a mut Vec<MidiEvent>,
57}
58
59impl std::ops::Deref for MidiBufferMut<'_> {
60 type Target = Vec<MidiEvent>;
61
62 fn deref(&self) -> &Self::Target {
63 self.buffer
64 }
65}
66
67impl std::ops::DerefMut for MidiBufferMut<'_> {
68 fn deref_mut(&mut self) -> &mut Self::Target {
69 self.buffer
70 }
71}
72
73impl MIDIIO {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn connect(from: &Arc<MIDIIO>, to: &Arc<MIDIIO>) {
82 let mut conns = from.connections();
83 if !conns.iter().any(|c| Arc::ptr_eq(c, to)) {
84 conns.push(to.clone());
85 from.store_connections(conns);
86 }
87 let mut sources = to.sources();
88 if !sources.iter().any(|s| Arc::ptr_eq(s, from)) {
89 sources.push(from.clone());
90 to.store_sources(sources);
91 }
92 }
93
94 pub fn disconnect(from: &Arc<MIDIIO>, to: &Arc<MIDIIO>) -> Result<(), String> {
97 let mut removed = false;
98 let mut conns = from.connections();
99 let before = conns.len();
100 conns.retain(|c| !Arc::ptr_eq(c, to));
101 if conns.len() < before {
102 from.store_connections(conns);
103 removed = true;
104 }
105 let mut sources = to.sources();
106 sources.retain(|s| !Arc::ptr_eq(s, from));
107 to.store_sources(sources);
108 if removed {
109 Ok(())
110 } else {
111 Err("Connection not found".to_string())
112 }
113 }
114
115 pub fn add_connection(&self, to: &Arc<MIDIIO>) {
120 let mut conns = self.connections();
121 if !conns.iter().any(|c| Arc::ptr_eq(c, to)) {
122 conns.push(to.clone());
123 self.store_connections(conns);
124 }
125 }
126
127 pub fn connections(&self) -> Vec<Arc<MIDIIO>> {
130 let connections = self.connections.load_full();
131 let live = Self::live_ports(&connections);
132 if live.len() != connections.len() {
133 self.store_connections(live.clone());
134 }
135 live
136 }
137
138 pub fn sources(&self) -> Vec<Arc<MIDIIO>> {
140 let sources = self.sources.load_full();
141 let live = Self::live_ports(&sources);
142 if live.len() != sources.len() {
143 self.store_sources(live.clone());
144 }
145 live
146 }
147
148 pub unsafe fn setup(&self) {
154 unsafe { &mut *self.buffer.get() }.clear();
156 self.finished.store(false, Ordering::Release);
157 }
158
159 pub unsafe fn process(&self) {
167 let buffer = unsafe { &mut *self.buffer.get() };
169 buffer.clear();
170 let sources = self.sources.load();
171 for source in sources.iter().filter_map(Weak::upgrade) {
172 let src = unsafe { &*source.buffer.get() };
175 buffer.extend_from_slice(src);
176 }
177 buffer.sort_by_key(|e| e.frame);
178 self.finished.store(true, Ordering::Release);
179 }
180
181 pub unsafe fn buffer(&self) -> &[MidiEvent] {
187 unsafe { &*self.buffer.get() }
189 }
190
191 pub unsafe fn buffer_mut(&self) -> MidiBufferMut<'_> {
201 MidiBufferMut {
203 buffer: unsafe { &mut *self.buffer.get() },
204 }
205 }
206
207 pub fn ready(&self) -> bool {
211 let sources = self.sources.load();
212 let mut has_source = false;
213 for source in sources.iter().filter_map(Weak::upgrade) {
214 has_source = true;
215 if !source.finished.load(Ordering::Acquire) {
216 return false;
217 }
218 }
219 if has_source {
220 true
221 } else {
222 self.finished.load(Ordering::Acquire)
223 }
224 }
225
226 fn store_connections(&self, connections: Vec<Arc<MIDIIO>>) {
227 self.connections
228 .store(Arc::new(connections.iter().map(Arc::downgrade).collect()));
229 }
230
231 fn store_sources(&self, sources: Vec<Arc<MIDIIO>>) {
232 self.sources
233 .store(Arc::new(sources.iter().map(Arc::downgrade).collect()));
234 }
235
236 fn live_ports(ports: &[Weak<MIDIIO>]) -> Vec<Arc<MIDIIO>> {
237 ports.iter().filter_map(Weak::upgrade).collect()
238 }
239
240 pub fn mark_finished(&self) {
243 self.finished.store(true, Ordering::Release);
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn midi_event_new_sets_fields() {
253 let event = MidiEvent::new(42, vec![0x90, 60, 100]);
254
255 assert_eq!(event.frame, 42);
256 assert_eq!(event.data, vec![0x90, 60, 100]);
257 }
258
259 #[test]
260 fn connect_and_disconnect_manage_both_sides() {
261 let from = Arc::new(MIDIIO::new());
262 let to = Arc::new(MIDIIO::new());
263
264 MIDIIO::connect(&from, &to);
265 let from_connections = from.connections();
266 assert_eq!(from_connections.len(), 1);
267 assert!(Arc::ptr_eq(&from_connections[0], &to));
268 let to_sources = to.sources();
269 assert_eq!(to_sources.len(), 1);
270 assert!(Arc::ptr_eq(&to_sources[0], &from));
271
272 assert!(MIDIIO::disconnect(&from, &to).is_ok());
273 assert!(from.connections().is_empty());
274 assert!(to.sources().is_empty());
275 }
276
277 #[test]
278 fn disconnect_returns_error_for_missing_connection() {
279 let from = Arc::new(MIDIIO::new());
280 let to = Arc::new(MIDIIO::new());
281
282 let err = MIDIIO::disconnect(&from, &to).expect_err("missing connection should error");
283 assert_eq!(err, "Connection not found");
284 }
285
286 #[test]
287 fn disconnect_removes_all_duplicate_connections_for_same_target() {
288 let from = Arc::new(MIDIIO::new());
289 let to = Arc::new(MIDIIO::new());
290
291 MIDIIO::connect(&from, &to);
292 MIDIIO::connect(&from, &to);
293
294 assert!(MIDIIO::disconnect(&from, &to).is_ok());
295 assert!(from.connections().is_empty());
296 assert!(to.sources().is_empty());
297 }
298
299 #[test]
300 fn process_merges_and_sorts_sources() {
301 let source_a = Arc::new(MIDIIO::new());
302 let source_b = Arc::new(MIDIIO::new());
303 let consumer = Arc::new(MIDIIO::new());
304
305 unsafe {
308 source_a
309 .buffer_mut()
310 .push(MidiEvent::new(10, vec![0x90, 60, 100]));
311 source_b
312 .buffer_mut()
313 .push(MidiEvent::new(5, vec![0x80, 60, 100]));
314 }
315
316 MIDIIO::connect(&source_a, &consumer);
317 MIDIIO::connect(&source_b, &consumer);
318
319 unsafe { consumer.process() };
321
322 let events = unsafe { consumer.buffer() }.to_vec();
323 assert_eq!(events.len(), 2);
324 assert_eq!(events[0].frame, 5);
325 assert_eq!(events[1].frame, 10);
326 }
327
328 #[test]
329 fn ready_requires_all_sources_finished() {
330 let source = Arc::new(MIDIIO::new());
331 let consumer = Arc::new(MIDIIO::new());
332
333 MIDIIO::connect(&source, &consumer);
334
335 assert!(!consumer.ready());
336
337 source.mark_finished();
338 assert!(consumer.ready());
339 }
340
341 #[test]
342 fn no_source_port_ready_after_mark_finished() {
343 let io = MIDIIO::new();
344 assert!(!io.ready());
345 io.mark_finished();
346 assert!(io.ready());
347 }
348
349 #[test]
350 fn setup_clears_buffer_and_finished() {
351 let io = MIDIIO::new();
352 unsafe {
354 io.buffer_mut().push(MidiEvent::new(0, vec![0x90, 60, 100]));
355 io.mark_finished();
356 io.setup();
357 assert!(io.buffer().is_empty());
358 }
359 assert!(!io.ready());
360 }
361}