use arc_swap::ArcSwap;
use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Weak};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MidiEvent {
pub frame: u32,
pub data: Vec<u8>,
}
impl MidiEvent {
pub fn new(frame: u32, data: Vec<u8>) -> Self {
Self { frame, data }
}
}
#[derive(Debug, Default)]
#[allow(clippy::upper_case_acronyms)]
pub struct MIDIIO {
sources: ArcSwap<Vec<Weak<MIDIIO>>>,
connections: ArcSwap<Vec<Weak<MIDIIO>>>,
buffer: UnsafeCell<Vec<MidiEvent>>,
finished: AtomicBool,
}
unsafe impl Sync for MIDIIO {}
#[derive(Debug)]
pub struct MidiBufferMut<'a> {
buffer: &'a mut Vec<MidiEvent>,
}
impl std::ops::Deref for MidiBufferMut<'_> {
type Target = Vec<MidiEvent>;
fn deref(&self) -> &Self::Target {
self.buffer
}
}
impl std::ops::DerefMut for MidiBufferMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.buffer
}
}
impl MIDIIO {
pub fn new() -> Self {
Self::default()
}
pub fn connect(from: &Arc<MIDIIO>, to: &Arc<MIDIIO>) {
let mut conns = from.connections();
if !conns.iter().any(|c| Arc::ptr_eq(c, to)) {
conns.push(to.clone());
from.store_connections(conns);
}
let mut sources = to.sources();
if !sources.iter().any(|s| Arc::ptr_eq(s, from)) {
sources.push(from.clone());
to.store_sources(sources);
}
}
pub fn disconnect(from: &Arc<MIDIIO>, to: &Arc<MIDIIO>) -> Result<(), String> {
let mut removed = false;
let mut conns = from.connections();
let before = conns.len();
conns.retain(|c| !Arc::ptr_eq(c, to));
if conns.len() < before {
from.store_connections(conns);
removed = true;
}
let mut sources = to.sources();
sources.retain(|s| !Arc::ptr_eq(s, from));
to.store_sources(sources);
if removed {
Ok(())
} else {
Err("Connection not found".to_string())
}
}
pub fn add_connection(&self, to: &Arc<MIDIIO>) {
let mut conns = self.connections();
if !conns.iter().any(|c| Arc::ptr_eq(c, to)) {
conns.push(to.clone());
self.store_connections(conns);
}
}
pub fn connections(&self) -> Vec<Arc<MIDIIO>> {
let connections = self.connections.load_full();
let live = Self::live_ports(&connections);
if live.len() != connections.len() {
self.store_connections(live.clone());
}
live
}
pub fn sources(&self) -> Vec<Arc<MIDIIO>> {
let sources = self.sources.load_full();
let live = Self::live_ports(&sources);
if live.len() != sources.len() {
self.store_sources(live.clone());
}
live
}
pub unsafe fn setup(&self) {
unsafe { &mut *self.buffer.get() }.clear();
self.finished.store(false, Ordering::Release);
}
pub unsafe fn process(&self) {
let buffer = unsafe { &mut *self.buffer.get() };
buffer.clear();
let sources = self.sources.load();
for source in sources.iter().filter_map(Weak::upgrade) {
let src = unsafe { &*source.buffer.get() };
buffer.extend_from_slice(src);
}
buffer.sort_by_key(|e| e.frame);
self.finished.store(true, Ordering::Release);
}
pub unsafe fn buffer(&self) -> &[MidiEvent] {
unsafe { &*self.buffer.get() }
}
pub unsafe fn buffer_mut(&self) -> MidiBufferMut<'_> {
MidiBufferMut {
buffer: unsafe { &mut *self.buffer.get() },
}
}
pub fn ready(&self) -> bool {
let sources = self.sources.load();
let mut has_source = false;
for source in sources.iter().filter_map(Weak::upgrade) {
has_source = true;
if !source.finished.load(Ordering::Acquire) {
return false;
}
}
if has_source {
true
} else {
self.finished.load(Ordering::Acquire)
}
}
fn store_connections(&self, connections: Vec<Arc<MIDIIO>>) {
self.connections
.store(Arc::new(connections.iter().map(Arc::downgrade).collect()));
}
fn store_sources(&self, sources: Vec<Arc<MIDIIO>>) {
self.sources
.store(Arc::new(sources.iter().map(Arc::downgrade).collect()));
}
fn live_ports(ports: &[Weak<MIDIIO>]) -> Vec<Arc<MIDIIO>> {
ports.iter().filter_map(Weak::upgrade).collect()
}
pub fn mark_finished(&self) {
self.finished.store(true, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn midi_event_new_sets_fields() {
let event = MidiEvent::new(42, vec![0x90, 60, 100]);
assert_eq!(event.frame, 42);
assert_eq!(event.data, vec![0x90, 60, 100]);
}
#[test]
fn connect_and_disconnect_manage_both_sides() {
let from = Arc::new(MIDIIO::new());
let to = Arc::new(MIDIIO::new());
MIDIIO::connect(&from, &to);
let from_connections = from.connections();
assert_eq!(from_connections.len(), 1);
assert!(Arc::ptr_eq(&from_connections[0], &to));
let to_sources = to.sources();
assert_eq!(to_sources.len(), 1);
assert!(Arc::ptr_eq(&to_sources[0], &from));
assert!(MIDIIO::disconnect(&from, &to).is_ok());
assert!(from.connections().is_empty());
assert!(to.sources().is_empty());
}
#[test]
fn disconnect_returns_error_for_missing_connection() {
let from = Arc::new(MIDIIO::new());
let to = Arc::new(MIDIIO::new());
let err = MIDIIO::disconnect(&from, &to).expect_err("missing connection should error");
assert_eq!(err, "Connection not found");
}
#[test]
fn disconnect_removes_all_duplicate_connections_for_same_target() {
let from = Arc::new(MIDIIO::new());
let to = Arc::new(MIDIIO::new());
MIDIIO::connect(&from, &to);
MIDIIO::connect(&from, &to);
assert!(MIDIIO::disconnect(&from, &to).is_ok());
assert!(from.connections().is_empty());
assert!(to.sources().is_empty());
}
#[test]
fn process_merges_and_sorts_sources() {
let source_a = Arc::new(MIDIIO::new());
let source_b = Arc::new(MIDIIO::new());
let consumer = Arc::new(MIDIIO::new());
unsafe {
source_a
.buffer_mut()
.push(MidiEvent::new(10, vec![0x90, 60, 100]));
source_b
.buffer_mut()
.push(MidiEvent::new(5, vec![0x80, 60, 100]));
}
MIDIIO::connect(&source_a, &consumer);
MIDIIO::connect(&source_b, &consumer);
unsafe { consumer.process() };
let events = unsafe { consumer.buffer() }.to_vec();
assert_eq!(events.len(), 2);
assert_eq!(events[0].frame, 5);
assert_eq!(events[1].frame, 10);
}
#[test]
fn ready_requires_all_sources_finished() {
let source = Arc::new(MIDIIO::new());
let consumer = Arc::new(MIDIIO::new());
MIDIIO::connect(&source, &consumer);
assert!(!consumer.ready());
source.mark_finished();
assert!(consumer.ready());
}
#[test]
fn no_source_port_ready_after_mark_finished() {
let io = MIDIIO::new();
assert!(!io.ready());
io.mark_finished();
assert!(io.ready());
}
#[test]
fn setup_clears_buffer_and_finished() {
let io = MIDIIO::new();
unsafe {
io.buffer_mut().push(MidiEvent::new(0, vec![0x90, 60, 100]));
io.mark_finished();
io.setup();
assert!(io.buffer().is_empty());
}
assert!(!io.ready());
}
}