Skip to main content

sim_lib_stream_host/
midi_live_eval_site.rs

1//! Live MIDI stream evaluation-site adapter.
2
3use std::convert::Infallible;
4
5use sim_kernel::{Error, Result, Symbol};
6use sim_lib_midi_core::{MidiSink, MidiSource};
7use sim_lib_midi_live::{LiveMidiDirection, LiveMidiError, LiveMidiSession};
8
9use crate::eval_site::StreamEvalSite;
10use crate::placement::{DeviceDirection, DeviceKind, DeviceRecord, Placement};
11
12/// Live MIDI evaluation site opened from a cataloged device record.
13pub struct MidiLiveEvalSite {
14    record: DeviceRecord,
15    session: LiveMidiSession,
16    site: Box<dyn StreamEvalSite>,
17}
18
19impl MidiLiveEvalSite {
20    /// Wraps an opened catalog site in a live MIDI session.
21    pub fn from_eval_site(id: &Symbol, site: Box<dyn StreamEvalSite>) -> Result<Self> {
22        let record = site.device_record().clone();
23        if record.kind != DeviceKind::Midi {
24            site.close()?;
25            return Err(Error::Eval(format!("open_live: device '{id}' is not MIDI")));
26        }
27        let direction = live_direction(record.direction);
28        let session = match site.live_midi_session() {
29            Some(Ok(session)) => session,
30            Some(Err(error)) => {
31                site.close()?;
32                return Err(error);
33            }
34            None => LiveMidiSession::modeled(direction).map_err(live_error)?,
35        };
36        if session.direction() != direction {
37            site.close()?;
38            return Err(Error::Eval(format!(
39                "open_live: device '{id}' live session direction does not match catalog record"
40            )));
41        }
42        Ok(Self {
43            record,
44            session,
45            site,
46        })
47    }
48
49    /// Returns the live MIDI source side.
50    pub fn source_mut(&mut self) -> &mut dyn MidiSource<Err = Infallible> {
51        self.session.source_mut()
52    }
53
54    /// Returns the live MIDI sink side when this site supports output.
55    pub fn sink_mut(&mut self) -> Option<&mut dyn MidiSink<Err = Infallible>> {
56        self.session.sink_mut()
57    }
58}
59
60impl StreamEvalSite for MidiLiveEvalSite {
61    fn placement(&self) -> &Placement {
62        &self.record.placement
63    }
64
65    fn device_record(&self) -> &DeviceRecord {
66        &self.record
67    }
68
69    fn close(self: Box<Self>) -> Result<()> {
70        let Self { session, site, .. } = *self;
71        session.close().map_err(live_error)?;
72        site.close()
73    }
74}
75
76fn live_direction(direction: DeviceDirection) -> LiveMidiDirection {
77    match direction {
78        DeviceDirection::Input => LiveMidiDirection::Source,
79        DeviceDirection::Output => LiveMidiDirection::Sink,
80        DeviceDirection::Duplex => LiveMidiDirection::Duplex,
81    }
82}
83
84fn live_error(err: LiveMidiError) -> Error {
85    Error::Eval(format!("midi live: {err}"))
86}