summaryrefslogtreecommitdiffstats
path: root/third_party/rust/alsa/src/seq.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/alsa/src/seq.rs')
-rw-r--r--third_party/rust/alsa/src/seq.rs1567
1 files changed, 1567 insertions, 0 deletions
diff --git a/third_party/rust/alsa/src/seq.rs b/third_party/rust/alsa/src/seq.rs
new file mode 100644
index 0000000000..bb1e46dfa5
--- /dev/null
+++ b/third_party/rust/alsa/src/seq.rs
@@ -0,0 +1,1567 @@
+//! MIDI sequencer I/O and enumeration
+
+use libc::{c_uint, c_int, c_short, c_uchar, c_void, c_long, size_t, pollfd};
+use super::error::*;
+use crate::alsa;
+use super::{Direction, poll};
+use std::{ptr, fmt, mem, slice, time, cell};
+use std::str::{FromStr, Split};
+use std::ffi::{CStr};
+use std::borrow::Cow;
+
+// Workaround for improper alignment of snd_seq_ev_ext_t in alsa-sys
+#[repr(packed)]
+struct EvExtPacked {
+ len: c_uint,
+ ptr: *mut c_void,
+}
+
+/// [snd_seq_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___sequencer.html) wrapper
+///
+/// To access the functions `event_input`, `event_input_pending` and `set_input_buffer_size`,
+/// you first have to obtain an instance of `Input` by calling `input()`. Only one instance of
+/// `Input` may exist at any time for a given `Seq`.
+pub struct Seq(*mut alsa::snd_seq_t, cell::Cell<bool>);
+
+unsafe impl Send for Seq {}
+
+impl Drop for Seq {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_close(self.0) }; }
+}
+
+impl Seq {
+ fn check_has_input(&self) {
+ if self.1.get() { panic!("No additional Input object allowed")}
+ }
+
+ /// Opens the sequencer.
+ ///
+ /// If name is None, "default" will be used. That's almost always what you usually want to use anyway.
+ pub fn open(name: Option<&CStr>, dir: Option<Direction>, nonblock: bool) -> Result<Seq> {
+ let n2 = name.unwrap_or(unsafe { CStr::from_bytes_with_nul_unchecked(b"default\0") });
+ let mut h = ptr::null_mut();
+ let mode = if nonblock { alsa::SND_SEQ_NONBLOCK } else { 0 };
+ let streams = match dir {
+ None => alsa::SND_SEQ_OPEN_DUPLEX,
+ Some(Direction::Playback) => alsa::SND_SEQ_OPEN_OUTPUT,
+ Some(Direction::Capture) => alsa::SND_SEQ_OPEN_INPUT,
+ };
+ acheck!(snd_seq_open(&mut h, n2.as_ptr(), streams, mode))
+ .map(|_| Seq(h, cell::Cell::new(false)))
+ }
+
+ pub fn set_client_name(&self, name: &CStr) -> Result<()> {
+ acheck!(snd_seq_set_client_name(self.0, name.as_ptr())).map(|_| ())
+ }
+
+ pub fn set_client_event_filter(&self, event_type: i32) -> Result<()> {
+ acheck!(snd_seq_set_client_event_filter(self.0, event_type as c_int)).map(|_| ())
+ }
+
+ pub fn set_client_pool_output(&self, size: u32) -> Result<()> {
+ acheck!(snd_seq_set_client_pool_output(self.0, size as size_t)).map(|_| ())
+ }
+
+ pub fn set_client_pool_input(&self, size: u32) -> Result<()> {
+ acheck!(snd_seq_set_client_pool_input(self.0, size as size_t)).map(|_| ())
+ }
+
+ pub fn set_client_pool_output_room(&self, size: u32) -> Result<()> {
+ acheck!(snd_seq_set_client_pool_output_room(self.0, size as size_t)).map(|_| ())
+ }
+
+ pub fn client_id(&self) -> Result<i32> {
+ acheck!(snd_seq_client_id(self.0)).map(|q| q as i32)
+ }
+
+ pub fn drain_output(&self) -> Result<i32> {
+ acheck!(snd_seq_drain_output(self.0)).map(|q| q as i32)
+ }
+
+ pub fn get_any_client_info(&self, client: i32) -> Result<ClientInfo> {
+ let c = ClientInfo::new()?;
+ acheck!(snd_seq_get_any_client_info(self.0, client, c.0)).map(|_| c)
+ }
+
+ pub fn get_any_port_info(&self, a: Addr) -> Result<PortInfo> {
+ let c = PortInfo::new()?;
+ acheck!(snd_seq_get_any_port_info(self.0, a.client as c_int, a.port as c_int, c.0)).map(|_| c)
+ }
+
+ pub fn create_port(&self, port: &PortInfo) -> Result<()> {
+ acheck!(snd_seq_create_port(self.0, port.0)).map(|_| ())
+ }
+
+ pub fn create_simple_port(&self, name: &CStr, caps: PortCap, t: PortType) -> Result<i32> {
+ acheck!(snd_seq_create_simple_port(self.0, name.as_ptr(), caps.bits() as c_uint, t.bits() as c_uint)).map(|q| q as i32)
+ }
+
+ pub fn set_port_info(&self, port: i32, info: &mut PortInfo) -> Result<()> {
+ acheck!(snd_seq_set_port_info(self.0, port, info.0)).map(|_| ())
+ }
+
+ pub fn delete_port(&self, port: i32) -> Result<()> {
+ acheck!(snd_seq_delete_port(self.0, port as c_int)).map(|_| ())
+ }
+
+ pub fn subscribe_port(&self, info: &PortSubscribe) -> Result<()> {
+ acheck!(snd_seq_subscribe_port(self.0, info.0)).map(|_| ())
+ }
+
+ pub fn unsubscribe_port(&self, sender: Addr, dest: Addr) -> Result<()> {
+ let z = PortSubscribe::new()?;
+ z.set_sender(sender);
+ z.set_dest(dest);
+ acheck!(snd_seq_unsubscribe_port(self.0, z.0)).map(|_| ())
+ }
+
+ pub fn control_queue(&self, q: i32, t: EventType, value: i32, e: Option<&mut Event>) -> Result<()> {
+ assert!(EvQueueControl::<()>::has_data(t) || EvQueueControl::<i32>::has_data(t) || EvQueueControl::<u32>::has_data(t));
+ let p = e.map(|e| &mut e.0 as *mut _).unwrap_or(ptr::null_mut());
+ acheck!(snd_seq_control_queue(self.0, q as c_int, t as c_int, value as c_int, p)).map(|_| ())
+ }
+
+ pub fn event_output(&self, e: &mut Event) -> Result<u32> {
+ e.ensure_buf();
+ acheck!(snd_seq_event_output(self.0, &mut e.0)).map(|q| q as u32)
+ }
+
+ pub fn event_output_buffer(&self, e: &mut Event) -> Result<u32> {
+ e.ensure_buf();
+ acheck!(snd_seq_event_output_buffer(self.0, &mut e.0)).map(|q| q as u32)
+ }
+
+ pub fn event_output_direct(&self, e: &mut Event) -> Result<u32> {
+ e.ensure_buf();
+ acheck!(snd_seq_event_output_direct(self.0, &mut e.0)).map(|q| q as u32)
+ }
+
+ pub fn get_queue_tempo(&self, q: i32) -> Result<QueueTempo> {
+ let value = QueueTempo::new()?;
+ acheck!(snd_seq_get_queue_tempo(self.0, q as c_int, value.0)).map(|_| value)
+ }
+
+ pub fn set_queue_tempo(&self, q: i32, value: &QueueTempo) -> Result<()> {
+ acheck!(snd_seq_set_queue_tempo(self.0, q as c_int, value.0)).map(|_| ())
+ }
+
+ pub fn get_queue_status(&self, q: i32) -> Result<QueueStatus> {
+ let value = QueueStatus::new()?;
+ acheck!(snd_seq_get_queue_status(self.0, q as c_int, value.0)).map(|_| value)
+ }
+
+ pub fn free_queue(&self, q: i32) -> Result<()> { acheck!(snd_seq_free_queue(self.0, q)).map(|_| ()) }
+ pub fn alloc_queue(&self) -> Result<i32> { acheck!(snd_seq_alloc_queue(self.0)).map(|q| q as i32) }
+ pub fn alloc_named_queue(&self, n: &CStr) -> Result<i32> {
+ acheck!(snd_seq_alloc_named_queue(self.0, n.as_ptr())).map(|q| q as i32)
+ }
+
+ pub fn sync_output_queue(&self) -> Result<()> {
+ acheck!(snd_seq_sync_output_queue(self.0)).map(|_| ())
+ }
+
+ pub fn drop_output(&self) -> Result<()> {
+ acheck!(snd_seq_drop_output(self.0)).map(|_| ())
+ }
+
+ /// Call this function to obtain an instance of `Input` to access the functions `event_input`,
+ /// `event_input_pending` and `set_input_buffer_size`. See the documentation of `Input` for details.
+ pub fn input(&self) -> Input {
+ Input::new(self)
+ }
+
+ pub fn remove_events(&self, condition: RemoveEvents) -> Result<()> {
+ acheck!(snd_seq_remove_events(self.0, condition.0)).map(|_| ())
+ }
+}
+
+/// Struct for receiving input events from a sequencer. The methods offered by this
+/// object may modify the internal input buffer of the sequencer, which must not happen
+/// while an `Event` is alive that has been obtained from a call to `event_input` (which
+/// takes `Input` by mutable reference for this reason). This is because the event might
+/// directly reference the sequencer's input buffer for variable-length messages (e.g. Sysex).
+///
+/// Note: Only one `Input` object is allowed in scope at a time.
+pub struct Input<'a>(&'a Seq);
+
+impl<'a> Drop for Input<'a> {
+ fn drop(&mut self) { (self.0).1.set(false) }
+}
+
+impl<'a> Input<'a> {
+ fn new(s: &'a Seq) -> Input<'a> {
+ s.check_has_input();
+ s.1.set(true);
+ Input(s)
+ }
+
+ pub fn event_input(&mut self) -> Result<Event> {
+ // The returned event might reference the input buffer of the `Seq`.
+ // Therefore we mutably borrow the `Input` structure, preventing any
+ // other function call that might change the input buffer while the
+ // event is alive.
+ let mut z = ptr::null_mut();
+ acheck!(snd_seq_event_input((self.0).0, &mut z))?;
+ unsafe { Event::extract (&mut *z, "snd_seq_event_input") }
+ }
+
+ pub fn event_input_pending(&self, fetch_sequencer: bool) -> Result<u32> {
+ acheck!(snd_seq_event_input_pending((self.0).0, if fetch_sequencer {1} else {0})).map(|q| q as u32)
+ }
+
+ pub fn set_input_buffer_size(&self, size: u32) -> Result<()> {
+ acheck!(snd_seq_set_input_buffer_size((self.0).0, size as size_t)).map(|_| ())
+ }
+
+ pub fn drop_input(&self) -> Result<()> {
+ acheck!(snd_seq_drop_input((self.0).0)).map(|_| ())
+ }
+}
+
+fn polldir(o: Option<Direction>) -> c_short {
+ match o {
+ None => poll::Flags::IN | poll::Flags::OUT,
+ Some(Direction::Playback) => poll::Flags::OUT,
+ Some(Direction::Capture) => poll::Flags::IN,
+ }.bits()
+}
+
+impl<'a> poll::Descriptors for (&'a Seq, Option<Direction>) {
+
+ fn count(&self) -> usize {
+ unsafe { alsa::snd_seq_poll_descriptors_count((self.0).0, polldir(self.1)) as usize }
+ }
+
+ fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
+ let z = unsafe { alsa::snd_seq_poll_descriptors((self.0).0, p.as_mut_ptr(), p.len() as c_uint, polldir(self.1)) };
+ from_code("snd_seq_poll_descriptors", z).map(|_| z as usize)
+ }
+
+ fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
+ let mut r = 0;
+ let z = unsafe { alsa::snd_seq_poll_descriptors_revents((self.0).0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
+ from_code("snd_seq_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
+ }
+}
+
+/// [snd_seq_client_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_client.html) wrapper
+pub struct ClientInfo(*mut alsa::snd_seq_client_info_t);
+
+unsafe impl Send for ClientInfo {}
+
+impl Drop for ClientInfo {
+ fn drop(&mut self) {
+ unsafe { alsa::snd_seq_client_info_free(self.0) };
+ }
+}
+
+impl ClientInfo {
+ fn new() -> Result<Self> {
+ let mut p = ptr::null_mut();
+ acheck!(snd_seq_client_info_malloc(&mut p)).map(|_| ClientInfo(p))
+ }
+
+ // Not sure if it's useful for this one to be public.
+ fn set_client(&self, client: i32) {
+ unsafe { alsa::snd_seq_client_info_set_client(self.0, client as c_int) };
+ }
+
+ pub fn get_client(&self) -> i32 {
+ unsafe { alsa::snd_seq_client_info_get_client(self.0) as i32 }
+ }
+
+ pub fn get_name(&self) -> Result<&str> {
+ let c = unsafe { alsa::snd_seq_client_info_get_name(self.0) };
+ from_const("snd_seq_client_info_get_name", c)
+ }
+}
+
+impl fmt::Debug for ClientInfo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "ClientInfo({},{:?})", self.get_client(), self.get_name())
+ }
+}
+
+#[derive(Copy, Clone)]
+/// Iterates over clients connected to the seq API (both kernel and userspace clients).
+pub struct ClientIter<'a>(&'a Seq, i32);
+
+impl<'a> ClientIter<'a> {
+ pub fn new(seq: &'a Seq) -> Self { ClientIter(seq, -1) }
+}
+
+impl<'a> Iterator for ClientIter<'a> {
+ type Item = ClientInfo;
+ fn next(&mut self) -> Option<Self::Item> {
+ let z = ClientInfo::new().unwrap();
+ z.set_client(self.1);
+ let r = unsafe { alsa::snd_seq_query_next_client((self.0).0, z.0) };
+ if r < 0 { self.1 = -1; return None };
+ self.1 = z.get_client();
+ Some(z)
+ }
+}
+
+/// [snd_seq_port_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) wrapper
+pub struct PortInfo(*mut alsa::snd_seq_port_info_t);
+
+unsafe impl Send for PortInfo {}
+
+impl Drop for PortInfo {
+ fn drop(&mut self) {
+ unsafe { alsa::snd_seq_port_info_free(self.0) };
+ }
+}
+
+impl PortInfo {
+ fn new() -> Result<Self> {
+ let mut p = ptr::null_mut();
+ acheck!(snd_seq_port_info_malloc(&mut p)).map(|_| PortInfo(p))
+ }
+
+ /// Creates a new PortInfo with all fields set to zero.
+ pub fn empty() -> Result<Self> {
+ let z = Self::new()?;
+ unsafe { ptr::write_bytes(z.0 as *mut u8, 0, alsa::snd_seq_port_info_sizeof()) };
+ Ok(z)
+ }
+
+ pub fn get_client(&self) -> i32 {
+ unsafe { alsa::snd_seq_port_info_get_client(self.0) as i32 }
+ }
+
+ pub fn get_port(&self) -> i32 {
+ unsafe { alsa::snd_seq_port_info_get_port(self.0) as i32 }
+ }
+
+ // Not sure if it's useful for this one to be public.
+ fn set_client(&self, client: i32) {
+ unsafe { alsa::snd_seq_port_info_set_client(self.0, client as c_int) };
+ }
+
+ // Not sure if it's useful for this one to be public.
+ fn set_port(&self, port: i32) {
+ unsafe { alsa::snd_seq_port_info_set_port(self.0, port as c_int) };
+ }
+
+ pub fn get_name(&self) -> Result<&str> {
+ let c = unsafe { alsa::snd_seq_port_info_get_name(self.0) };
+ from_const("snd_seq_port_info_get_name", c)
+ }
+
+ pub fn set_name(&mut self, name: &CStr) {
+ // Note: get_name returns an interior reference, so this one must take &mut self
+ unsafe { alsa::snd_seq_port_info_set_name(self.0, name.as_ptr()) };
+ }
+
+ pub fn get_capability(&self) -> PortCap {
+ PortCap::from_bits_truncate(unsafe { alsa::snd_seq_port_info_get_capability(self.0) as u32 })
+ }
+
+ pub fn get_type(&self) -> PortType {
+ PortType::from_bits_truncate(unsafe { alsa::snd_seq_port_info_get_type(self.0) as u32 })
+ }
+
+ pub fn set_capability(&self, c: PortCap) {
+ unsafe { alsa::snd_seq_port_info_set_capability(self.0, c.bits() as c_uint) }
+ }
+
+ pub fn set_type(&self, c: PortType) {
+ unsafe { alsa::snd_seq_port_info_set_type(self.0, c.bits() as c_uint) }
+ }
+
+ /// Returns an Addr containing this PortInfo's client and port id.
+ pub fn addr(&self) -> Addr {
+ Addr {
+ client: self.get_client(),
+ port: self.get_port(),
+ }
+ }
+
+ pub fn get_midi_channels(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_midi_channels(self.0) as i32 } }
+ pub fn get_midi_voices(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_midi_voices(self.0) as i32 } }
+ pub fn get_synth_voices(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_synth_voices(self.0) as i32 } }
+ pub fn get_read_use(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_read_use(self.0) as i32 } }
+ pub fn get_write_use(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_write_use(self.0) as i32 } }
+ pub fn get_port_specified(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_port_specified(self.0) == 1 } }
+ pub fn get_timestamping(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_timestamping(self.0) == 1 } }
+ pub fn get_timestamp_real(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_timestamp_real(self.0) == 1 } }
+ pub fn get_timestamp_queue(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_timestamp_queue(self.0) as i32 } }
+
+ pub fn set_midi_channels(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_midi_channels(self.0, value as c_int) } }
+ pub fn set_midi_voices(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_midi_voices(self.0, value as c_int) } }
+ pub fn set_synth_voices(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_synth_voices(self.0, value as c_int) } }
+ pub fn set_port_specified(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_port_specified(self.0, if value { 1 } else { 0 } ) } }
+ pub fn set_timestamping(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_timestamping(self.0, if value { 1 } else { 0 } ) } }
+ pub fn set_timestamp_real(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_timestamp_real(self.0, if value { 1 } else { 0 } ) } }
+ pub fn set_timestamp_queue(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_timestamp_queue(self.0, value as c_int) } }
+}
+
+impl fmt::Debug for PortInfo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "PortInfo({}:{},{:?})", self.get_client(), self.get_port(), self.get_name())
+ }
+}
+
+#[derive(Copy, Clone)]
+/// Iterates over clients connected to the seq API (both kernel and userspace clients).
+pub struct PortIter<'a>(&'a Seq, i32, i32);
+
+impl<'a> PortIter<'a> {
+ pub fn new(seq: &'a Seq, client: i32) -> Self { PortIter(seq, client, -1) }
+}
+
+impl<'a> Iterator for PortIter<'a> {
+ type Item = PortInfo;
+ fn next(&mut self) -> Option<Self::Item> {
+ let z = PortInfo::new().unwrap();
+ z.set_client(self.1);
+ z.set_port(self.2);
+ let r = unsafe { alsa::snd_seq_query_next_port((self.0).0, z.0) };
+ if r < 0 { self.2 = -1; return None };
+ self.2 = z.get_port();
+ Some(z)
+ }
+}
+
+bitflags! {
+ /// [SND_SEQ_PORT_CAP_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) constants
+ pub struct PortCap: u32 {
+ const READ = 1<<0;
+ const WRITE = 1<<1;
+ const SYNC_READ = 1<<2;
+ const SYNC_WRITE = 1<<3;
+ const DUPLEX = 1<<4;
+ const SUBS_READ = 1<<5;
+ const SUBS_WRITE = 1<<6;
+ const NO_EXPORT = 1<<7;
+ }
+}
+
+bitflags! {
+ /// [SND_SEQ_PORT_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) constants
+ pub struct PortType: u32 {
+ const SPECIFIC = (1<<0);
+ const MIDI_GENERIC = (1<<1);
+ const MIDI_GM = (1<<2);
+ const MIDI_GS = (1<<3);
+ const MIDI_XG = (1<<4);
+ const MIDI_MT32 = (1<<5);
+ const MIDI_GM2 = (1<<6);
+ const SYNTH = (1<<10);
+ const DIRECT_SAMPLE = (1<<11);
+ const SAMPLE = (1<<12);
+ const HARDWARE = (1<<16);
+ const SOFTWARE = (1<<17);
+ const SYNTHESIZER = (1<<18);
+ const PORT = (1<<19);
+ const APPLICATION = (1<<20);
+ }
+}
+
+bitflags! {
+ /// [SND_SEQ_REMOVE_xxx](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html) constants
+ pub struct Remove: u32 {
+ const INPUT = (1<<0);
+ const OUTPUT = (1<<1);
+ const DEST = (1<<2);
+ const DEST_CHANNEL = (1<<3);
+ const TIME_BEFORE = (1<<4);
+ const TIME_AFTER = (1<<5);
+ const TIME_TICK = (1<<6);
+ const EVENT_TYPE = (1<<7);
+ const IGNORE_OFF = (1<<8);
+ const TAG_MATCH = (1<<9);
+ }
+}
+
+
+/// [snd_seq_addr_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__addr__t.html) wrapper
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
+pub struct Addr {
+ pub client: i32,
+ pub port: i32,
+}
+
+impl FromStr for Addr {
+ type Err = Box<dyn std::error::Error>;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ let mut split: Split<'_, char> = s.trim().split(':');
+ let client = split.next()
+ .ok_or("no client provided")?
+ .parse::<i32>()?;
+ let port = split.next()
+ .ok_or("no port provided")?
+ .parse::<i32>()?;
+ match split.next() {
+ Some(_) => {
+ Err("too many arguments".into())
+ },
+ None => {
+ Ok(Addr { client, port })
+ }
+ }
+ }
+}
+
+impl Addr {
+ pub fn system_timer() -> Addr { Addr { client: alsa::SND_SEQ_CLIENT_SYSTEM as i32, port: alsa::SND_SEQ_PORT_SYSTEM_TIMER as i32 } }
+ pub fn system_announce() -> Addr { Addr { client: alsa::SND_SEQ_CLIENT_SYSTEM as i32, port: alsa::SND_SEQ_PORT_SYSTEM_ANNOUNCE as i32 } }
+ pub fn broadcast() -> Addr { Addr { client: alsa::SND_SEQ_ADDRESS_BROADCAST as i32, port: alsa::SND_SEQ_ADDRESS_BROADCAST as i32 } }
+}
+
+/// [snd_seq_port_subscribe_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper
+pub struct PortSubscribe(*mut alsa::snd_seq_port_subscribe_t);
+
+unsafe impl Send for PortSubscribe {}
+
+impl Drop for PortSubscribe {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_port_subscribe_free(self.0) }; }
+}
+
+impl PortSubscribe {
+ fn new() -> Result<Self> {
+ let mut p = ptr::null_mut();
+ acheck!(snd_seq_port_subscribe_malloc(&mut p)).map(|_| PortSubscribe(p))
+ }
+
+ /// Creates a new PortSubscribe with all fields set to zero.
+ pub fn empty() -> Result<Self> {
+ let z = Self::new()?;
+ unsafe { ptr::write_bytes(z.0 as *mut u8, 0, alsa::snd_seq_port_subscribe_sizeof()) };
+ Ok(z)
+ }
+
+ pub fn get_sender(&self) -> Addr { unsafe {
+ let z = alsa::snd_seq_port_subscribe_get_sender(self.0);
+ Addr { client: (*z).client as i32, port: (*z).port as i32 }
+ } }
+
+ pub fn get_dest(&self) -> Addr { unsafe {
+ let z = alsa::snd_seq_port_subscribe_get_dest(self.0);
+ Addr { client: (*z).client as i32, port: (*z).port as i32 }
+ } }
+
+ pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_port_subscribe_get_queue(self.0) as i32 } }
+ pub fn get_exclusive(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_exclusive(self.0) == 1 } }
+ pub fn get_time_update(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_time_update(self.0) == 1 } }
+ pub fn get_time_real(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_time_real(self.0) == 1 } }
+
+ pub fn set_sender(&self, value: Addr) {
+ let z = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar };
+ unsafe { alsa::snd_seq_port_subscribe_set_sender(self.0, &z) };
+ }
+
+ pub fn set_dest(&self, value: Addr) {
+ let z = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar };
+ unsafe { alsa::snd_seq_port_subscribe_set_dest(self.0, &z) };
+ }
+
+ pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_port_subscribe_set_queue(self.0, value as c_int) } }
+ pub fn set_exclusive(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_exclusive(self.0, if value { 1 } else { 0 } ) } }
+ pub fn set_time_update(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_time_update(self.0, if value { 1 } else { 0 } ) } }
+ pub fn set_time_real(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_time_real(self.0, if value { 1 } else { 0 } ) } }
+
+}
+
+/// [snd_seq_query_subs_type_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper
+#[derive(Copy, Clone)]
+pub enum QuerySubsType {
+ READ = alsa::SND_SEQ_QUERY_SUBS_READ as isize,
+ WRITE = alsa::SND_SEQ_QUERY_SUBS_WRITE as isize,
+}
+
+/// [snd_seq_query_subscribe_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper
+//(kept private, functionality exposed by PortSubscribeIter)
+struct QuerySubscribe(*mut alsa::snd_seq_query_subscribe_t);
+
+unsafe impl Send for QuerySubscribe {}
+
+impl Drop for QuerySubscribe {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_query_subscribe_free(self.0) } }
+}
+
+impl QuerySubscribe {
+ pub fn new() -> Result<Self> {
+ let mut q = ptr::null_mut();
+ acheck!(snd_seq_query_subscribe_malloc(&mut q)).map(|_| QuerySubscribe(q))
+ }
+
+ pub fn get_index(&self) -> i32 { unsafe { alsa::snd_seq_query_subscribe_get_index(self.0) as i32 } }
+ pub fn get_addr(&self) -> Addr { unsafe {
+ let a = &(*alsa::snd_seq_query_subscribe_get_addr(self.0));
+ Addr { client: a.client as i32, port: a.port as i32 }
+ } }
+ pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_query_subscribe_get_queue(self.0) as i32 } }
+ pub fn get_exclusive(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_exclusive(self.0) == 1 } }
+ pub fn get_time_update(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_time_update(self.0) == 1 } }
+ pub fn get_time_real(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_time_real(self.0) == 1 } }
+
+ pub fn set_root(&self, value: Addr) { unsafe {
+ let a = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar};
+ alsa::snd_seq_query_subscribe_set_root(self.0, &a);
+ } }
+ pub fn set_type(&self, value: QuerySubsType) { unsafe {
+ alsa::snd_seq_query_subscribe_set_type(self.0, value as alsa::snd_seq_query_subs_type_t)
+ } }
+ pub fn set_index(&self, value: i32) { unsafe { alsa::snd_seq_query_subscribe_set_index(self.0, value as c_int) } }
+}
+
+#[derive(Copy, Clone)]
+/// Iterates over port subscriptions for a given client:port/type.
+pub struct PortSubscribeIter<'a> {
+ seq: &'a Seq,
+ addr: Addr,
+ query_subs_type: QuerySubsType,
+ index: i32
+}
+
+impl<'a> PortSubscribeIter<'a> {
+ pub fn new(seq: &'a Seq, addr: Addr, query_subs_type: QuerySubsType) -> Self {
+ PortSubscribeIter {seq, addr, query_subs_type, index: 0 }
+ }
+}
+
+impl<'a> Iterator for PortSubscribeIter<'a> {
+ type Item = PortSubscribe;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let query = QuerySubscribe::new().unwrap();
+
+ query.set_root(self.addr);
+ query.set_type(self.query_subs_type);
+ query.set_index(self.index);
+
+ let r = unsafe { alsa::snd_seq_query_port_subscribers((self.seq).0, query.0) };
+ if r < 0 {
+ self.index = 0;
+ return None;
+ }
+
+ self.index = query.get_index() + 1;
+ let vtr = PortSubscribe::new().unwrap();
+ match self.query_subs_type {
+ QuerySubsType::READ => {
+ vtr.set_sender(self.addr);
+ vtr.set_dest(query.get_addr());
+ },
+ QuerySubsType:: WRITE => {
+ vtr.set_sender(query.get_addr());
+ vtr.set_dest(self.addr);
+ }
+ };
+ vtr.set_queue(query.get_queue());
+ vtr.set_exclusive(query.get_exclusive());
+ vtr.set_time_update(query.get_time_update());
+ vtr.set_time_real(query.get_time_real());
+
+ Some(vtr)
+ }
+}
+
+/// [snd_seq_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html) wrapper
+///
+/// Fields of the event is not directly exposed. Instead call `Event::new` to set data (which can be, e g, an EvNote).
+/// Use `get_type` and `get_data` to retrieve data.
+///
+/// The lifetime parameter refers to the lifetime of an associated external buffer that might be used for
+/// variable-length messages (e.g. SysEx).
+pub struct Event<'a>(alsa::snd_seq_event_t, EventType, Option<Cow<'a, [u8]>>);
+
+unsafe impl<'a> Send for Event<'a> {}
+
+impl<'a> Event<'a> {
+ /// Creates a new event. For events that carry variable-length data (e.g. Sysex), `new_ext` has to be used instead.
+ pub fn new<D: EventData>(t: EventType, data: &D) -> Event<'static> {
+ assert!(!Event::has_ext_data(t), "event type must not carry variable-length data");
+ let mut z = Event(unsafe { mem::zeroed() }, t, None);
+ (z.0).type_ = t as c_uchar;
+ (z.0).flags |= Event::get_length_flag(t);
+ debug_assert!(D::has_data(t));
+ data.set_data(&mut z);
+ z
+ }
+
+ /// Creates a new event carrying variable-length data. This is required for event types `Sysex`, `Bounce`, and the `UsrVar` types.
+ pub fn new_ext<D: Into<Cow<'a, [u8]>>>(t: EventType, data: D) -> Event<'a> {
+ assert!(Event::has_ext_data(t), "event type must carry variable-length data");
+ let mut z = Event(unsafe { mem::zeroed() }, t, Some(data.into()));
+ (z.0).type_ = t as c_uchar;
+ (z.0).flags |= Event::get_length_flag(t);
+ z
+ }
+
+ /// Consumes this event and returns an (otherwise unchanged) event where the externally referenced
+ /// buffer for variable length messages (e.g. SysEx) has been copied into the event.
+ /// The returned event has a static lifetime, i e, it's decoupled from the original buffer.
+ pub fn into_owned(self) -> Event<'static> {
+ Event(self.0, self.1, self.2.map(|cow| Cow::Owned(cow.into_owned())))
+ }
+
+ fn get_length_flag(t: EventType) -> u8 {
+ match t {
+ EventType::Sysex => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE,
+ EventType::Bounce => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE, // not clear whether this should be VARIABLE or VARUSR
+ EventType::UsrVar0 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
+ EventType::UsrVar1 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
+ EventType::UsrVar2 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
+ EventType::UsrVar3 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
+ EventType::UsrVar4 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
+ _ => alsa::SND_SEQ_EVENT_LENGTH_FIXED
+ }
+ }
+
+ fn has_ext_data(t: EventType) -> bool {
+ Event::get_length_flag(t) != alsa::SND_SEQ_EVENT_LENGTH_FIXED
+ }
+
+ /// Extracts event type and data. Produces a result with an arbitrary lifetime, hence the unsafety.
+ unsafe fn extract<'any>(z: &mut alsa::snd_seq_event_t, func: &'static str) -> Result<Event<'any>> {
+ let t = EventType::from_c_int((*z).type_ as c_int, func)?;
+ let ext_data = if Event::has_ext_data(t) {
+ assert_ne!((*z).flags & alsa::SND_SEQ_EVENT_LENGTH_MASK, alsa::SND_SEQ_EVENT_LENGTH_FIXED);
+ Some(Cow::Borrowed({
+ let zz: &EvExtPacked = &*(&(*z).data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _);
+ slice::from_raw_parts((*zz).ptr as *mut u8, (*zz).len as usize)
+ }))
+ } else {
+ None
+ };
+ Ok(Event(ptr::read(z), t, ext_data))
+ }
+
+ /// Ensures that the ev.ext union element points to the correct resize_buffer for events
+ /// with variable length content
+ fn ensure_buf(&mut self) {
+ if !Event::has_ext_data(self.1) { return; }
+ let slice: &[u8] = match self.2 {
+ Some(Cow::Owned(ref mut vec)) => &vec[..],
+ Some(Cow::Borrowed(buf)) => buf,
+ // The following case is always a logic error in the program, thus panicking is okay.
+ None => panic!("event type requires variable-length data, but none was provided")
+ };
+ let z: &mut EvExtPacked = unsafe { &mut *(&mut self.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) };
+ z.len = slice.len() as c_uint;
+ z.ptr = slice.as_ptr() as *mut c_void;
+ }
+
+ #[inline]
+ pub fn get_type(&self) -> EventType { self.1 }
+
+ /// Extract the event data from an event.
+ /// Use `get_ext` instead for events carrying variable-length data.
+ pub fn get_data<D: EventData>(&self) -> Option<D> { if D::has_data(self.1) { Some(D::get_data(self)) } else { None } }
+
+ /// Extract the variable-length data carried by events of type `Sysex`, `Bounce`, or the `UsrVar` types.
+ pub fn get_ext(&self) -> Option<&[u8]> {
+ if Event::has_ext_data(self.1) {
+ match self.2 {
+ Some(Cow::Owned(ref vec)) => Some(&vec[..]),
+ Some(Cow::Borrowed(buf)) => Some(buf),
+ // The following case is always a logic error in the program, thus panicking is okay.
+ None => panic!("event type requires variable-length data, but none was found")
+ }
+ } else {
+ None
+ }
+ }
+
+ pub fn set_subs(&mut self) {
+ self.0.dest.client = alsa::SND_SEQ_ADDRESS_SUBSCRIBERS;
+ self.0.dest.port = alsa::SND_SEQ_ADDRESS_UNKNOWN;
+ }
+
+ pub fn set_source(&mut self, p: i32) { self.0.source.port = p as u8 }
+ pub fn set_dest(&mut self, d: Addr) { self.0.dest.client = d.client as c_uchar; self.0.dest.port = d.port as c_uchar; }
+ pub fn set_tag(&mut self, t: u8) { self.0.tag = t as c_uchar; }
+ pub fn set_queue(&mut self, q: i32) { self.0.queue = q as c_uchar; }
+
+ pub fn get_source(&self) -> Addr { Addr { client: self.0.source.client as i32, port: self.0.source.port as i32 } }
+ pub fn get_dest(&self) -> Addr { Addr { client: self.0.dest.client as i32, port: self.0.dest.port as i32 } }
+ pub fn get_tag(&self) -> u8 { self.0.tag as u8 }
+ pub fn get_queue(&self) -> i32 { self.0.queue as i32 }
+
+ pub fn schedule_real(&mut self, queue: i32, relative: bool, rtime: time::Duration) {
+ self.0.flags &= !(alsa::SND_SEQ_TIME_STAMP_MASK | alsa::SND_SEQ_TIME_MODE_MASK);
+ self.0.flags |= alsa::SND_SEQ_TIME_STAMP_REAL | (if relative { alsa::SND_SEQ_TIME_MODE_REL } else { alsa::SND_SEQ_TIME_MODE_ABS });
+ self.0.queue = queue as u8;
+ let t = unsafe { &mut self.0.time.time };
+ t.tv_sec = rtime.as_secs() as c_uint;
+ t.tv_nsec = rtime.subsec_nanos() as c_uint;
+ }
+
+ pub fn schedule_tick(&mut self, queue: i32, relative: bool, ttime: u32) {
+ self.0.flags &= !(alsa::SND_SEQ_TIME_STAMP_MASK | alsa::SND_SEQ_TIME_MODE_MASK);
+ self.0.flags |= alsa::SND_SEQ_TIME_STAMP_TICK | (if relative { alsa::SND_SEQ_TIME_MODE_REL } else { alsa::SND_SEQ_TIME_MODE_ABS });
+ self.0.queue = queue as u8;
+ let t = unsafe { &mut self.0.time.tick };
+ *t = ttime as c_uint;
+ }
+
+ pub fn set_direct(&mut self) { self.0.queue = alsa::SND_SEQ_QUEUE_DIRECT }
+
+ pub fn get_relative(&self) -> bool { (self.0.flags & alsa::SND_SEQ_TIME_MODE_REL) != 0 }
+
+ pub fn get_time(&self) -> Option<time::Duration> {
+ if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) != 0 {
+ let d = self.0.time;
+ let t = unsafe { &d.time };
+ Some(time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32))
+ } else { None }
+ }
+
+ pub fn get_tick(&self) -> Option<u32> {
+ if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) == 0 {
+ let d = self.0.time;
+ let t = unsafe { &d.tick };
+ Some(*t)
+ } else { None }
+ }
+
+ /// Returns true if the message is high priority.
+ pub fn get_priority(&self) -> bool { (self.0.flags & alsa::SND_SEQ_PRIORITY_HIGH) != 0 }
+
+ pub fn set_priority(&mut self, is_high_prio: bool) {
+ if is_high_prio { self.0.flags |= alsa::SND_SEQ_PRIORITY_HIGH; }
+ else { self.0.flags &= !alsa::SND_SEQ_PRIORITY_HIGH; }
+ }
+}
+
+impl<'a> Clone for Event<'a> {
+ fn clone(&self) -> Self { Event(unsafe { ptr::read(&self.0) }, self.1, self.2.clone()) }
+}
+
+impl<'a> fmt::Debug for Event<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut x = f.debug_tuple("Event");
+ x.field(&self.1);
+ if let Some(z) = self.get_data::<EvNote>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvCtrl>() { x.field(&z); }
+ if let Some(z) = self.get_data::<Addr>() { x.field(&z); }
+ if let Some(z) = self.get_data::<Connect>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvQueueControl<()>>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvQueueControl<i32>>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvQueueControl<u32>>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvQueueControl<time::Duration>>() { x.field(&z); }
+ if let Some(z) = self.get_data::<EvResult>() { x.field(&z); }
+ if let Some(z) = self.get_data::<[u8; 12]>() { x.field(&z); }
+ if let Some(z) = self.get_ext() { x.field(&z); }
+ x.finish()
+ }
+}
+
+/// Low level methods to set/get data on an Event. Don't use these directly, use generic methods on Event instead.
+pub trait EventData {
+ fn get_data(ev: &Event) -> Self;
+ fn has_data(e: EventType) -> bool;
+ fn set_data(&self, ev: &mut Event);
+}
+
+impl EventData for () {
+ fn get_data(_: &Event) -> Self {}
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::TuneRequest |
+ EventType::Reset |
+ EventType::Sensing |
+ EventType::None)
+ }
+ fn set_data(&self, _: &mut Event) {}
+}
+
+impl EventData for [u8; 12] {
+ fn get_data(ev: &Event) -> Self {
+ let d = unsafe { ptr::read(&ev.0.data) };
+ let z = unsafe { &d.raw8 };
+ z.d
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::Echo |
+ EventType::Oss |
+ EventType::Usr0 |
+ EventType::Usr1 |
+ EventType::Usr2 |
+ EventType::Usr3 |
+ EventType::Usr4 |
+ EventType::Usr5 |
+ EventType::Usr6 |
+ EventType::Usr7 |
+ EventType::Usr8 |
+ EventType::Usr9)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z = unsafe { &mut ev.0.data.raw8 };
+ z.d = *self;
+ }
+}
+
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
+pub struct EvNote {
+ pub channel: u8,
+ pub note: u8,
+ pub velocity: u8,
+ pub off_velocity: u8,
+ pub duration: u32,
+}
+
+impl EventData for EvNote {
+ fn get_data(ev: &Event) -> Self {
+ let z: &alsa::snd_seq_ev_note_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) };
+ EvNote { channel: z.channel as u8, note: z.note as u8, velocity: z.velocity as u8, off_velocity: z.off_velocity as u8, duration: z.duration as u32 }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::Note |
+ EventType::Noteon |
+ EventType::Noteoff |
+ EventType::Keypress)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z: &mut alsa::snd_seq_ev_note_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) };
+ z.channel = self.channel as c_uchar;
+ z.note = self.note as c_uchar;
+ z.velocity = self.velocity as c_uchar;
+ z.off_velocity = self.off_velocity as c_uchar;
+ z.duration = self.duration as c_uint;
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
+pub struct EvCtrl {
+ pub channel: u8,
+ pub param: u32,
+ pub value: i32,
+}
+
+impl EventData for EvCtrl {
+ fn get_data(ev: &Event) -> Self {
+ let z: &alsa::snd_seq_ev_ctrl_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) };
+ EvCtrl { channel: z.channel as u8, param: z.param as u32, value: z.value as i32 }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::Controller |
+ EventType::Pgmchange |
+ EventType::Chanpress |
+ EventType::Pitchbend |
+ EventType::Control14 |
+ EventType::Nonregparam |
+ EventType::Regparam |
+ EventType::Songpos |
+ EventType::Songsel |
+ EventType::Qframe |
+ EventType::Timesign |
+ EventType::Keysign)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z: &mut alsa::snd_seq_ev_ctrl_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) };
+ z.channel = self.channel as c_uchar;
+ z.param = self.param as c_uint;
+ z.value = self.value as c_int;
+ }
+}
+
+impl EventData for Addr {
+ fn get_data(ev: &Event) -> Self {
+ let z: &alsa::snd_seq_addr_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) };
+ Addr { client: z.client as i32, port: z.port as i32 }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::ClientStart |
+ EventType::ClientExit |
+ EventType::ClientChange |
+ EventType::PortStart |
+ EventType::PortExit |
+ EventType::PortChange)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z: &mut alsa::snd_seq_addr_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) };
+ z.client = self.client as c_uchar;
+ z.port = self.port as c_uchar;
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
+/// [snd_seq_connect_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__connect__t.html) wrapper
+pub struct Connect {
+ pub sender: Addr,
+ pub dest: Addr,
+}
+
+impl EventData for Connect {
+ fn get_data(ev: &Event) -> Self {
+ let d = unsafe { ptr::read(&ev.0.data) };
+ let z = unsafe { &d.connect };
+ Connect {
+ sender: Addr { client: z.sender.client as i32, port: z.sender.port as i32 },
+ dest: Addr { client: z.dest.client as i32, port: z.dest.port as i32 }
+ }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::PortSubscribed |
+ EventType::PortUnsubscribed)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z = unsafe { &mut ev.0.data.connect };
+ z.sender.client = self.sender.client as c_uchar;
+ z.sender.port = self.sender.port as c_uchar;
+ z.dest.client = self.dest.client as c_uchar;
+ z.dest.port = self.dest.port as c_uchar;
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
+/// [snd_seq_ev_queue_control_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__queue__control__t.html) wrapper
+///
+/// Note: This struct is generic, but what types of T are required for the different EvQueueControl messages is
+/// not very well documented in alsa-lib. Right now, Tempo is i32, Tick, SetposTick and SyncPos are u32, SetposTime is time::Duration,
+/// and the rest is (). If I guessed wrong, let me know.
+pub struct EvQueueControl<T> {
+ pub queue: i32,
+ pub value: T,
+}
+
+impl EventData for EvQueueControl<()> {
+ fn get_data(ev: &Event) -> Self {
+ let d = unsafe { ptr::read(&ev.0.data) };
+ let z = unsafe { &d.queue };
+ EvQueueControl { queue: z.queue as i32, value: () }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::Start |
+ EventType::Continue |
+ EventType::Stop |
+ EventType::Clock |
+ EventType::QueueSkew)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z = unsafe { &mut ev.0.data.queue };
+ z.queue = self.queue as c_uchar;
+ }
+}
+
+impl EventData for EvQueueControl<i32> {
+ fn get_data(ev: &Event) -> Self { unsafe {
+ let mut d = ptr::read(&ev.0.data);
+ let z = &mut d.queue;
+ EvQueueControl { queue: z.queue as i32, value: z.param.value as i32 }
+ } }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::Tempo)
+ }
+ fn set_data(&self, ev: &mut Event) { unsafe {
+ let z = &mut ev.0.data.queue;
+ z.queue = self.queue as c_uchar;
+ z.param.value = self.value as c_int;
+ } }
+}
+
+impl EventData for EvQueueControl<u32> {
+ fn get_data(ev: &Event) -> Self { unsafe {
+ let mut d = ptr::read(&ev.0.data);
+ let z = &mut d.queue;
+ EvQueueControl { queue: z.queue as i32, value: z.param.position as u32 }
+ } }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::SyncPos |
+ EventType::Tick |
+ EventType::SetposTick)
+ }
+ fn set_data(&self, ev: &mut Event) { unsafe {
+ let z = &mut ev.0.data.queue;
+ z.queue = self.queue as c_uchar;
+ z.param.position = self.value as c_uint;
+ } }
+}
+
+impl EventData for EvQueueControl<time::Duration> {
+ fn get_data(ev: &Event) -> Self { unsafe {
+ let mut d = ptr::read(&ev.0.data);
+ let z = &mut d.queue;
+ let t = &mut z.param.time.time;
+ EvQueueControl { queue: z.queue as i32, value: time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32) }
+ } }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::SetposTime)
+ }
+ fn set_data(&self, ev: &mut Event) { unsafe {
+ let z = &mut ev.0.data.queue;
+ z.queue = self.queue as c_uchar;
+ let t = &mut z.param.time.time;
+ t.tv_sec = self.value.as_secs() as c_uint;
+ t.tv_nsec = self.value.subsec_nanos() as c_uint;
+ } }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
+/// [snd_seq_result_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__result__t.html) wrapper
+///
+/// It's called EvResult instead of Result, in order to not be confused with Rust's Result type.
+pub struct EvResult {
+ pub event: i32,
+ pub result: i32,
+}
+
+impl EventData for EvResult {
+ fn get_data(ev: &Event) -> Self {
+ let d = unsafe { ptr::read(&ev.0.data) };
+ let z = unsafe { &d.result };
+ EvResult { event: z.event as i32, result: z.result as i32 }
+ }
+ fn has_data(e: EventType) -> bool {
+ matches!(e,
+ EventType::System |
+ EventType::Result)
+ }
+ fn set_data(&self, ev: &mut Event) {
+ let z = unsafe { &mut ev.0.data.result };
+ z.event = self.event as c_int;
+ z.result = self.result as c_int;
+ }
+}
+
+
+
+alsa_enum!(
+ /// [SND_SEQ_EVENT_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_events.html) constants
+
+ EventType, ALL_EVENT_TYPES[59],
+
+ Bounce = SND_SEQ_EVENT_BOUNCE,
+ Chanpress = SND_SEQ_EVENT_CHANPRESS,
+ ClientChange = SND_SEQ_EVENT_CLIENT_CHANGE,
+ ClientExit = SND_SEQ_EVENT_CLIENT_EXIT,
+ ClientStart = SND_SEQ_EVENT_CLIENT_START,
+ Clock = SND_SEQ_EVENT_CLOCK,
+ Continue = SND_SEQ_EVENT_CONTINUE,
+ Control14 = SND_SEQ_EVENT_CONTROL14,
+ Controller = SND_SEQ_EVENT_CONTROLLER,
+ Echo = SND_SEQ_EVENT_ECHO,
+ Keypress = SND_SEQ_EVENT_KEYPRESS,
+ Keysign = SND_SEQ_EVENT_KEYSIGN,
+ None = SND_SEQ_EVENT_NONE,
+ Nonregparam = SND_SEQ_EVENT_NONREGPARAM,
+ Note = SND_SEQ_EVENT_NOTE,
+ Noteoff = SND_SEQ_EVENT_NOTEOFF,
+ Noteon = SND_SEQ_EVENT_NOTEON,
+ Oss = SND_SEQ_EVENT_OSS,
+ Pgmchange = SND_SEQ_EVENT_PGMCHANGE,
+ Pitchbend = SND_SEQ_EVENT_PITCHBEND,
+ PortChange = SND_SEQ_EVENT_PORT_CHANGE,
+ PortExit = SND_SEQ_EVENT_PORT_EXIT,
+ PortStart = SND_SEQ_EVENT_PORT_START,
+ PortSubscribed = SND_SEQ_EVENT_PORT_SUBSCRIBED,
+ PortUnsubscribed = SND_SEQ_EVENT_PORT_UNSUBSCRIBED,
+ Qframe = SND_SEQ_EVENT_QFRAME,
+ QueueSkew = SND_SEQ_EVENT_QUEUE_SKEW,
+ Regparam = SND_SEQ_EVENT_REGPARAM,
+ Reset = SND_SEQ_EVENT_RESET,
+ Result = SND_SEQ_EVENT_RESULT,
+ Sensing = SND_SEQ_EVENT_SENSING,
+ SetposTick = SND_SEQ_EVENT_SETPOS_TICK,
+ SetposTime = SND_SEQ_EVENT_SETPOS_TIME,
+ Songpos = SND_SEQ_EVENT_SONGPOS,
+ Songsel = SND_SEQ_EVENT_SONGSEL,
+ Start = SND_SEQ_EVENT_START,
+ Stop = SND_SEQ_EVENT_STOP,
+ SyncPos = SND_SEQ_EVENT_SYNC_POS,
+ Sysex = SND_SEQ_EVENT_SYSEX,
+ System = SND_SEQ_EVENT_SYSTEM,
+ Tempo = SND_SEQ_EVENT_TEMPO,
+ Tick = SND_SEQ_EVENT_TICK,
+ Timesign = SND_SEQ_EVENT_TIMESIGN,
+ TuneRequest = SND_SEQ_EVENT_TUNE_REQUEST,
+ Usr0 = SND_SEQ_EVENT_USR0,
+ Usr1 = SND_SEQ_EVENT_USR1,
+ Usr2 = SND_SEQ_EVENT_USR2,
+ Usr3 = SND_SEQ_EVENT_USR3,
+ Usr4 = SND_SEQ_EVENT_USR4,
+ Usr5 = SND_SEQ_EVENT_USR5,
+ Usr6 = SND_SEQ_EVENT_USR6,
+ Usr7 = SND_SEQ_EVENT_USR7,
+ Usr8 = SND_SEQ_EVENT_USR8,
+ Usr9 = SND_SEQ_EVENT_USR9,
+ UsrVar0 = SND_SEQ_EVENT_USR_VAR0,
+ UsrVar1 = SND_SEQ_EVENT_USR_VAR1,
+ UsrVar2 = SND_SEQ_EVENT_USR_VAR2,
+ UsrVar3 = SND_SEQ_EVENT_USR_VAR3,
+ UsrVar4 = SND_SEQ_EVENT_USR_VAR4,
+);
+
+/// [snd_seq_queue_tempo_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html) wrapper
+pub struct QueueTempo(*mut alsa::snd_seq_queue_tempo_t);
+
+unsafe impl Send for QueueTempo {}
+
+impl Drop for QueueTempo {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_queue_tempo_free(self.0) } }
+}
+
+impl QueueTempo {
+ fn new() -> Result<Self> {
+ let mut q = ptr::null_mut();
+ acheck!(snd_seq_queue_tempo_malloc(&mut q)).map(|_| QueueTempo(q))
+ }
+
+ /// Creates a new QueueTempo with all fields set to zero.
+ pub fn empty() -> Result<Self> {
+ let q = QueueTempo::new()?;
+ unsafe { ptr::write_bytes(q.0 as *mut u8, 0, alsa::snd_seq_queue_tempo_sizeof()) };
+ Ok(q)
+ }
+
+ pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_queue_tempo_get_queue(self.0) as i32 } }
+ pub fn get_tempo(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_tempo(self.0) as u32 } }
+ pub fn get_ppq(&self) -> i32 { unsafe { alsa::snd_seq_queue_tempo_get_ppq(self.0) as i32 } }
+ pub fn get_skew(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_skew(self.0) as u32 } }
+ pub fn get_skew_base(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_skew_base(self.0) as u32 } }
+
+// pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_queue_tempo_set_queue(self.0, value as c_int) } }
+ pub fn set_tempo(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_tempo(self.0, value as c_uint) } }
+ pub fn set_ppq(&self, value: i32) { unsafe { alsa::snd_seq_queue_tempo_set_ppq(self.0, value as c_int) } }
+ pub fn set_skew(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_skew(self.0, value as c_uint) } }
+ pub fn set_skew_base(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_skew_base(self.0, value as c_uint) } }
+}
+
+/// [snd_seq_queue_status_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html) wrapper
+pub struct QueueStatus(*mut alsa::snd_seq_queue_status_t);
+
+unsafe impl Send for QueueStatus {}
+
+impl Drop for QueueStatus {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_queue_status_free(self.0) } }
+}
+
+impl QueueStatus {
+ fn new() -> Result<Self> {
+ let mut q = ptr::null_mut();
+ acheck!(snd_seq_queue_status_malloc(&mut q)).map(|_| QueueStatus(q))
+ }
+
+ /// Creates a new QueueStatus with all fields set to zero.
+ pub fn empty() -> Result<Self> {
+ let q = QueueStatus::new()?;
+ unsafe { ptr::write_bytes(q.0 as *mut u8, 0, alsa::snd_seq_queue_status_sizeof()) };
+ Ok(q)
+ }
+
+ pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_queue(self.0) as i32 } }
+ pub fn get_events(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_events(self.0) as i32 } }
+ pub fn get_tick_time(&self) -> u32 { unsafe {alsa::snd_seq_queue_status_get_tick_time(self.0) as u32 } }
+ pub fn get_real_time(&self) -> time::Duration { unsafe {
+ let t = &(*alsa::snd_seq_queue_status_get_real_time(self.0));
+ time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32)
+ } }
+ pub fn get_status(&self) -> u32 { unsafe { alsa::snd_seq_queue_status_get_status(self.0) as u32 } }
+}
+
+/// [snd_seq_remove_events_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html) wrapper
+pub struct RemoveEvents(*mut alsa::snd_seq_remove_events_t);
+
+unsafe impl Send for RemoveEvents {}
+
+impl Drop for RemoveEvents {
+ fn drop(&mut self) { unsafe { alsa::snd_seq_remove_events_free(self.0) } }
+}
+
+impl RemoveEvents {
+ pub fn new() -> Result<Self> {
+ let mut q = ptr::null_mut();
+ acheck!(snd_seq_remove_events_malloc(&mut q)).map(|_| RemoveEvents(q))
+ }
+
+ pub fn get_condition(&self) -> Remove { unsafe {
+ Remove::from_bits_truncate(alsa::snd_seq_remove_events_get_condition(self.0) as u32)
+ } }
+ pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_remove_events_get_queue(self.0) as i32 } }
+ pub fn get_time(&self) -> time::Duration { unsafe {
+ let d = ptr::read(alsa::snd_seq_remove_events_get_time(self.0));
+ let t = &d.time;
+
+ time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32)
+ } }
+ pub fn get_dest(&self) -> Addr { unsafe {
+ let a = &(*alsa::snd_seq_remove_events_get_dest(self.0));
+
+ Addr { client: a.client as i32, port: a.port as i32 }
+ } }
+ pub fn get_channel(&self) -> i32 { unsafe { alsa::snd_seq_remove_events_get_channel(self.0) as i32 } }
+ pub fn get_event_type(&self) -> Result<EventType> { unsafe {
+ EventType::from_c_int(alsa::snd_seq_remove_events_get_event_type(self.0), "snd_seq_remove_events_get_event_type")
+ } }
+ pub fn get_tag(&self) -> u8 { unsafe { alsa::snd_seq_remove_events_get_tag(self.0) as u8 } }
+
+
+ pub fn set_condition(&self, value: Remove) { unsafe {
+ alsa::snd_seq_remove_events_set_condition(self.0, value.bits() as c_uint);
+ } }
+ pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_remove_events_set_queue(self.0, value as c_int) } }
+ pub fn set_time(&self, value: time::Duration) { unsafe {
+ let mut d: alsa::snd_seq_timestamp_t = mem::zeroed();
+ let mut t = &mut d.time;
+
+ t.tv_sec = value.as_secs() as c_uint;
+ t.tv_nsec = value.subsec_nanos() as c_uint;
+
+ alsa::snd_seq_remove_events_set_time(self.0, &d);
+ } }
+ pub fn set_dest(&self, value: Addr) { unsafe {
+ let a = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar};
+
+ alsa::snd_seq_remove_events_set_dest(self.0, &a);
+ } }
+ pub fn set_channel(&self, value: i32) { unsafe { alsa::snd_seq_remove_events_set_channel(self.0, value as c_int) } }
+ pub fn set_event_type(&self, value: EventType) { unsafe { alsa::snd_seq_remove_events_set_event_type(self.0, value as i32); } }
+ pub fn set_tag(&self, value: u8) { unsafe { alsa::snd_seq_remove_events_set_tag(self.0, value as c_int) } }
+}
+
+/// [snd_midi_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___m_i_d_i___event.html) Wrapper
+///
+/// Sequencer event <-> MIDI byte stream coder
+pub struct MidiEvent(*mut alsa::snd_midi_event_t);
+
+impl Drop for MidiEvent {
+ fn drop(&mut self) { unsafe { alsa::snd_midi_event_free(self.0) } }
+}
+
+impl MidiEvent {
+ pub fn new(bufsize: u32) -> Result<MidiEvent> {
+ let mut q = ptr::null_mut();
+ acheck!(snd_midi_event_new(bufsize as size_t, &mut q)).map(|_| MidiEvent(q))
+ }
+
+ pub fn resize_buffer(&self, bufsize: u32) -> Result<()> { acheck!(snd_midi_event_resize_buffer(self.0, bufsize as size_t)).map(|_| ()) }
+
+ /// Note: this corresponds to snd_midi_event_no_status, but on and off are switched.
+ ///
+ /// Alsa-lib is a bit confusing here. Anyhow, set "enable" to true to enable running status.
+ pub fn enable_running_status(&self, enable: bool) { unsafe { alsa::snd_midi_event_no_status(self.0, if enable {0} else {1}) } }
+
+ /// Resets both encoder and decoder
+ pub fn init(&self) { unsafe { alsa::snd_midi_event_init(self.0) } }
+
+ pub fn reset_encode(&self) { unsafe { alsa::snd_midi_event_reset_encode(self.0) } }
+
+ pub fn reset_decode(&self) { unsafe { alsa::snd_midi_event_reset_decode(self.0) } }
+
+ pub fn decode(&self, buf: &mut [u8], ev: &mut Event) -> Result<usize> {
+ ev.ensure_buf();
+ acheck!(snd_midi_event_decode(self.0, buf.as_mut_ptr() as *mut c_uchar, buf.len() as c_long, &ev.0)).map(|r| r as usize)
+ }
+
+ /// In case of success, returns a tuple of (bytes consumed from buf, found Event).
+ pub fn encode<'a>(&'a mut self, buf: &[u8]) -> Result<(usize, Option<Event<'a>>)> {
+ // The ALSA documentation clearly states that the event will be valid as long as the Encoder
+ // is not messed with (because the data pointer for sysex events may point into the Encoder's
+ // buffer). We make this safe by taking self by unique reference and coupling it to
+ // the event's lifetime.
+ let mut ev = unsafe { mem::zeroed() };
+ let r = acheck!(snd_midi_event_encode(self.0, buf.as_ptr() as *const c_uchar, buf.len() as c_long, &mut ev))?;
+ let e = if ev.type_ == alsa::SND_SEQ_EVENT_NONE as u8 {
+ None
+ } else {
+ Some(unsafe { Event::extract(&mut ev, "snd_midi_event_encode") }?)
+ };
+ Ok((r as usize, e))
+ }
+}
+
+#[test]
+fn print_seqs() {
+ use std::ffi::CString;
+ let s = super::Seq::open(None, None, false).unwrap();
+ s.set_client_name(&CString::new("rust_test_print_seqs").unwrap()).unwrap();
+ let clients: Vec<_> = ClientIter::new(&s).collect();
+ for a in &clients {
+ let ports: Vec<_> = PortIter::new(&s, a.get_client()).collect();
+ println!("{:?}: {:?}", a, ports);
+ }
+}
+
+#[test]
+fn seq_subscribe() {
+ use std::ffi::CString;
+ let s = super::Seq::open(None, None, false).unwrap();
+ s.set_client_name(&CString::new("rust_test_seq_subscribe").unwrap()).unwrap();
+ let timer_info = s.get_any_port_info(Addr { client: 0, port: 0 }).unwrap();
+ assert_eq!(timer_info.get_name().unwrap(), "Timer");
+ let info = PortInfo::empty().unwrap();
+ let _port = s.create_port(&info);
+ let subs = PortSubscribe::empty().unwrap();
+ subs.set_sender(Addr { client: 0, port: 0 });
+ subs.set_dest(Addr { client: s.client_id().unwrap(), port: info.get_port() });
+ s.subscribe_port(&subs).unwrap();
+}
+
+#[test]
+fn seq_loopback() {
+ use std::ffi::CString;
+ let s = super::Seq::open(Some(&CString::new("default").unwrap()), None, false).unwrap();
+ s.set_client_name(&CString::new("rust_test_seq_loopback").unwrap()).unwrap();
+
+ // Create ports
+ let sinfo = PortInfo::empty().unwrap();
+ sinfo.set_capability(PortCap::READ | PortCap::SUBS_READ);
+ sinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
+ s.create_port(&sinfo).unwrap();
+ let sport = sinfo.get_port();
+ let dinfo = PortInfo::empty().unwrap();
+ dinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE);
+ dinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
+ s.create_port(&dinfo).unwrap();
+ let dport = dinfo.get_port();
+
+ // Connect them
+ let subs = PortSubscribe::empty().unwrap();
+ subs.set_sender(Addr { client: s.client_id().unwrap(), port: sport });
+ subs.set_dest(Addr { client: s.client_id().unwrap(), port: dport });
+ s.subscribe_port(&subs).unwrap();
+ println!("Connected {:?} to {:?}", subs.get_sender(), subs.get_dest());
+
+ // Send a note!
+ let note = EvNote { channel: 0, note: 64, duration: 100, velocity: 100, off_velocity: 64 };
+ let mut e = Event::new(EventType::Noteon, &note);
+ e.set_subs();
+ e.set_direct();
+ e.set_source(sport);
+ println!("Sending {:?}", e);
+ s.event_output(&mut e).unwrap();
+ s.drain_output().unwrap();
+
+ // Receive the note!
+ let mut input = s.input();
+ let e2 = input.event_input().unwrap();
+ println!("Receiving {:?}", e2);
+ assert_eq!(e2.get_type(), EventType::Noteon);
+ assert_eq!(e2.get_data(), Some(note));
+}
+
+#[test]
+fn seq_encode_sysex() {
+ let mut me = MidiEvent::new(16).unwrap();
+ let sysex = &[0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7];
+ let (s, ev) = me.encode(sysex).unwrap();
+ assert_eq!(s, 9);
+ let ev = ev.unwrap();
+ let v = ev.get_ext().unwrap();
+ assert_eq!(&*v, sysex);
+}
+
+#[test]
+fn seq_decode_sysex() {
+ let sysex = [0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7];
+ let mut ev = Event::new_ext(EventType::Sysex, &sysex[..]);
+ let me = MidiEvent::new(0).unwrap();
+ let mut buffer = vec![0; sysex.len()];
+ assert_eq!(me.decode(&mut buffer[..], &mut ev).unwrap(), sysex.len());
+ assert_eq!(buffer, sysex);
+}
+
+#[test]
+#[should_panic]
+fn seq_get_input_twice() {
+ use std::ffi::CString;
+ let s = super::Seq::open(None, None, false).unwrap();
+ s.set_client_name(&CString::new("rust_test_seq_get_input_twice").unwrap()).unwrap();
+ let input1 = s.input();
+ let input2 = s.input(); // this should panic
+ let _ = (input1, input2);
+}
+
+#[test]
+fn seq_has_data() {
+ for v in EventType::all() {
+ let v = *v;
+ let mut i = 0;
+ if <() as EventData>::has_data(v) { i += 1; }
+ if <[u8; 12] as EventData>::has_data(v) { i += 1; }
+ if Event::has_ext_data(v) { i += 1; }
+ if EvNote::has_data(v) { i += 1; }
+ if EvCtrl::has_data(v) { i += 1; }
+ if Addr::has_data(v) { i += 1; }
+ if Connect::has_data(v) { i += 1; }
+ if EvResult::has_data(v) { i += 1; }
+ if EvQueueControl::<()>::has_data(v) { i += 1; }
+ if EvQueueControl::<u32>::has_data(v) { i += 1; }
+ if EvQueueControl::<i32>::has_data(v) { i += 1; }
+ if EvQueueControl::<time::Duration>::has_data(v) { i += 1; }
+ if i != 1 { panic!("{:?}: {} has_data", v, i) }
+ }
+}
+
+#[test]
+fn seq_remove_events() -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let info = RemoveEvents::new()?;
+
+
+ info.set_condition(Remove::INPUT | Remove::DEST | Remove::TIME_BEFORE | Remove::TAG_MATCH);
+ info.set_queue(123);
+ info.set_time(time::Duration::new(456, 789));
+ info.set_dest(Addr { client: 212, port: 121 });
+ info.set_channel(15);
+ info.set_event_type(EventType::Noteon);
+ info.set_tag(213);
+
+ assert_eq!(info.get_condition(), Remove::INPUT | Remove::DEST | Remove::TIME_BEFORE | Remove::TAG_MATCH);
+ assert_eq!(info.get_queue(), 123);
+ assert_eq!(info.get_time(), time::Duration::new(456, 789));
+ assert_eq!(info.get_dest(), Addr { client: 212, port: 121 });
+ assert_eq!(info.get_channel(), 15);
+ assert_eq!(info.get_event_type()?, EventType::Noteon);
+ assert_eq!(info.get_tag(), 213);
+
+ Ok(())
+}
+
+#[test]
+fn seq_portsubscribeiter() {
+ let s = super::Seq::open(None, None, false).unwrap();
+
+ // Create ports
+ let sinfo = PortInfo::empty().unwrap();
+ sinfo.set_capability(PortCap::READ | PortCap::SUBS_READ);
+ sinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
+ s.create_port(&sinfo).unwrap();
+ let sport = sinfo.get_port();
+ let dinfo = PortInfo::empty().unwrap();
+ dinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE);
+ dinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
+ s.create_port(&dinfo).unwrap();
+ let dport = dinfo.get_port();
+
+ // Connect them
+ let subs = PortSubscribe::empty().unwrap();
+ subs.set_sender(Addr { client: s.client_id().unwrap(), port: sport });
+ subs.set_dest(Addr { client: s.client_id().unwrap(), port: dport });
+ s.subscribe_port(&subs).unwrap();
+
+ // Query READ subs from sport's point of view
+ let read_subs: Vec<PortSubscribe> = PortSubscribeIter::new(&s,
+ Addr {client: s.client_id().unwrap(), port: sport },
+ QuerySubsType::READ).collect();
+ assert_eq!(read_subs.len(), 1);
+ assert_eq!(read_subs[0].get_sender(), subs.get_sender());
+ assert_eq!(read_subs[0].get_dest(), subs.get_dest());
+
+ let write_subs: Vec<PortSubscribe> = PortSubscribeIter::new(&s,
+ Addr {client: s.client_id().unwrap(), port: sport },
+ QuerySubsType::WRITE).collect();
+ assert_eq!(write_subs.len(), 0);
+
+ // Now query WRITE subs from dport's point of view
+ let write_subs: Vec<PortSubscribe> = PortSubscribeIter::new(&s,
+ Addr {client: s.client_id().unwrap(), port: dport },
+ QuerySubsType::WRITE).collect();
+ assert_eq!(write_subs.len(), 1);
+ assert_eq!(write_subs[0].get_sender(), subs.get_sender());
+ assert_eq!(write_subs[0].get_dest(), subs.get_dest());
+}