Skip to main content

midi_io/
client.rs

1use std::sync::Arc;
2
3use crate::name::Name;
4use crate::platform::PlatformClient;
5use crate::Destination;
6use crate::DestinationChanges;
7use crate::DestinationConnection;
8use crate::Error;
9use crate::IoError;
10use crate::Source;
11use crate::SourceChanges;
12use crate::SourceConnection;
13use crate::VirtualDestination;
14use crate::VirtualSource;
15
16/// MIDI client for sending and receiving MIDI messages
17#[derive(Debug, Clone)]
18pub struct Client {
19    pub(crate) inner: Arc<PlatformClient>,
20}
21
22impl Client {
23    /// Create a new midi client.
24    ///
25    /// On macOS and iOS, CoreMIDI permanently routes hotplug notifications to
26    /// whichever thread created the first MIDI client in the process. Create this
27    /// client before anything else in the process uses CoreMIDI, or
28    /// [`source_changes`](Client::source_changes) and
29    /// [`destination_changes`](Client::destination_changes) may never receive events.
30    pub async fn new(name: &str) -> Result<Self, Error> {
31        let name = Name::try_from(name).map_err(IoError::from)?;
32        let (platform, ready_rx) = PlatformClient::new(name)?;
33        ready_rx.await.map_err(|_| IoError::BackendThreadDied)??;
34        Ok(Self {
35            inner: Arc::new(platform),
36        })
37    }
38
39    /// Enumerate the available sources from the platform.
40    pub async fn sources(&self) -> Result<Vec<Source>, Error> {
41        self.inner.sources().await
42    }
43
44    /// Enumerate the available destinations from the platform.
45    pub async fn destinations(&self) -> Result<Vec<Destination>, Error> {
46        self.inner.destinations().await
47    }
48
49    /// Subscribe to a stream of source change events.
50    #[must_use = "the source changes stream must be polled to receive source change events"]
51    pub fn source_changes(&self) -> SourceChanges {
52        SourceChanges(self.inner.source_changes_rx())
53    }
54
55    /// Subscribe to a stream of destination change events.
56    #[must_use = "the destination changes stream must be polled to receive destination change events"]
57    pub fn destination_changes(&self) -> DestinationChanges {
58        DestinationChanges(self.inner.destination_changes_rx())
59    }
60
61    /// Connect to a known source. Use the `sources` method to query currently available sources.
62    pub async fn connect_source(&self, port: &Source) -> Result<SourceConnection, Error> {
63        let (msg_rx, sx_rx, err_rx) = self.inner.connect_source(port).await?;
64        Ok(SourceConnection::new(
65            msg_rx,
66            sx_rx,
67            err_rx,
68            port.id,
69            Arc::clone(&self.inner),
70        ))
71    }
72
73    /// Connect to a known destination. Use the `destinations` method to query currently available destinations.
74    pub async fn connect_destination(
75        &self,
76        port: &Destination,
77    ) -> Result<DestinationConnection, Error> {
78        self.inner.connect_destination(port).await?;
79        Ok(DestinationConnection::new(port.id, Arc::clone(&self.inner)))
80    }
81
82    /// Creates a virtual source that can be used to send midi to destinations
83    pub async fn create_virtual_source(&self, name: &str) -> Result<VirtualSource, Error> {
84        let validated = Name::try_from(name).map_err(IoError::from)?;
85        let id = self.inner.alloc_virtual_id();
86        let port = self.inner.create_virtual_source(id, validated).await?;
87        Ok(VirtualSource::new(
88            id,
89            port,
90            name.to_string(),
91            Arc::clone(&self.inner),
92        ))
93    }
94
95    /// Creates a virtual destination that can be used to receive midi from sources
96    pub async fn create_virtual_destination(
97        &self,
98        name: &str,
99    ) -> Result<VirtualDestination, Error> {
100        let validated = Name::try_from(name).map_err(IoError::from)?;
101        let id = self.inner.alloc_virtual_id();
102        let (port, (msg_rx, sx_rx, err_rx)) =
103            self.inner.create_virtual_destination(id, validated).await?;
104        Ok(VirtualDestination::new(
105            msg_rx,
106            sx_rx,
107            err_rx,
108            id,
109            port,
110            name.to_string(),
111            Arc::clone(&self.inner),
112        ))
113    }
114}