Skip to content

Commit

Permalink
Use objects instead of indices as port identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
Boddlnagg committed Sep 14, 2019
1 parent b156ef5 commit 7b865ed
Show file tree
Hide file tree
Showing 17 changed files with 602 additions and 284 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ bitflags = "0.3"
memalloc = "0.1.0"
jack-sys = { version = "0.1.0", optional = true }
libc = { version = "0.2.21", optional = true }
winrt = { version = "0.4.0", features = ["windows-devices", "windows-storage"], optional = true}
winrt = { version = "0.5.0", features = ["windows-devices", "windows-storage"], optional = true}

[target.'cfg(target_os = "linux")'.dependencies]
alsa = "0.2"
Expand Down
16 changes: 10 additions & 6 deletions examples/test_forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,27 @@ fn run() -> Result<(), Box<Error>> {
let midi_out = MidiOutput::new("midir forwarding output")?;

println!("Available input ports:");
for i in 0..midi_in.port_count() {
println!("{}: {}", i, midi_in.port_name(i)?);
let midi_in_ports = midi_in.ports();
for (i, p) in midi_in_ports.iter().enumerate() {
println!("{}: {}", i, midi_in.port_name(p)?);
}
print!("Please select input port: ");
stdout().flush()?;
stdin().read_line(&mut input)?;
let in_port: usize = input.trim().parse()?;
let in_port = midi_in_ports.get(input.trim().parse::<usize>()?)
.ok_or("Invalid port number")?;

println!("\nAvailable output ports:");
for i in 0..midi_out.port_count() {
println!("{}: {}", i, midi_out.port_name(i)?);
let midi_out_ports = midi_out.ports();
for (i, p) in midi_out_ports.iter().enumerate() {
println!("{}: {}", i, midi_out.port_name(p)?);
}
print!("Please select output port: ");
stdout().flush()?;
input.clear();
stdin().read_line(&mut input)?;
let out_port: usize = input.trim().parse()?;
let out_port = midi_out_ports.get(input.trim().parse::<usize>()?)
.ok_or("Invalid port number")?;

println!("\nOpening connections");
let in_port_name = midi_in.port_name(in_port)?;
Expand Down
8 changes: 4 additions & 4 deletions examples/test_list_ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ fn run() -> Result<(), Box<Error>> {

loop {
println!("Available input ports:");
for i in 0..midi_in.port_count() {
println!("{}: {}", i, midi_in.port_name(i)?);
for (i, p) in midi_in.ports().iter().enumerate() {
println!("{}: {}", i, midi_in.port_name(p)?);
}

println!("\nAvailable output ports:");
for i in 0..midi_out.port_count() {
println!("{}: {}", i, midi_out.port_name(i)?);
for (i, p) in midi_out.ports().iter().enumerate() {
println!("{}: {}", i, midi_out.port_name(p)?);
}

// run in endless loop if "--loop" parameter is specified
Expand Down
18 changes: 11 additions & 7 deletions examples/test_play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::time::Duration;
use std::io::{stdin, stdout, Write};
use std::error::Error;

use midir::MidiOutput;
use midir::{MidiOutput, MidiOutputPort};

fn main() {
match run() {
Expand All @@ -18,22 +18,24 @@ fn run() -> Result<(), Box<Error>> {
let midi_out = MidiOutput::new("My Test Output")?;

// Get an output port (read from console if multiple are available)
let out_port = match midi_out.port_count() {
let out_ports = midi_out.ports();
let out_port: &MidiOutputPort = match out_ports.len() {
0 => return Err("no output port found".into()),
1 => {
println!("Choosing the only available output port: {}", midi_out.port_name(0).unwrap());
0
println!("Choosing the only available output port: {}", midi_out.port_name(&out_ports[0]).unwrap());
&out_ports[0]
},
_ => {
println!("\nAvailable output ports:");
for i in 0..midi_out.port_count() {
println!("{}: {}", i, midi_out.port_name(i).unwrap());
for (i, p) in out_ports.iter().enumerate() {
println!("{}: {}", i, midi_out.port_name(p).unwrap());
}
print!("Please select output port: ");
stdout().flush()?;
let mut input = String::new();
stdin().read_line(&mut input)?;
input.trim().parse()?
out_ports.get(input.trim().parse::<usize>()?)
.ok_or("invalid output port selected")?
}
};

Expand All @@ -51,6 +53,8 @@ fn run() -> Result<(), Box<Error>> {
sleep(Duration::from_millis(duration * 150));
let _ = conn_out.send(&[NOTE_OFF_MSG, note, VELOCITY]);
};

sleep(Duration::from_millis(4 * 150));

play_note(66, 4);
play_note(65, 3);
Expand Down
14 changes: 8 additions & 6 deletions examples/test_read_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,24 @@ fn run() -> Result<(), Box<Error>> {
midi_in.ignore(Ignore::None);

// Get an input port (read from console if multiple are available)
let in_port = match midi_in.port_count() {
let in_ports = midi_in.ports();
let in_port = match in_ports.len() {
0 => return Err("no input port found".into()),
1 => {
println!("Choosing the only available input port: {}", midi_in.port_name(0).unwrap());
0
println!("Choosing the only available input port: {}", midi_in.port_name(&in_ports[0]).unwrap());
&in_ports[0]
},
_ => {
println!("\nAvailable input ports:");
for i in 0..midi_in.port_count() {
println!("{}: {}", i, midi_in.port_name(i).unwrap());
for (i, p) in in_ports.iter().enumerate() {
println!("{}: {}", i, midi_in.port_name(p).unwrap());
}
print!("Please select input port: ");
stdout().flush()?;
let mut input = String::new();
stdin().read_line(&mut input)?;
input.trim().parse()?
in_ports.get(input.trim().parse::<usize>()?)
.ok_or("invalid input port selected")?
}
};

Expand Down
16 changes: 10 additions & 6 deletions examples/test_reuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@ fn run() -> Result<(), Box<Error>> {
let mut midi_out = MidiOutput::new("My Test Output")?;

println!("Available input ports:");
for i in 0..midi_in.port_count() {
println!("{}: {}", i, midi_in.port_name(i).unwrap());
let midi_in_ports = midi_in.ports();
for (i, p) in midi_in_ports.iter().enumerate() {
println!("{}: {}", i, midi_in.port_name(p)?);
}
print!("Please select input port: ");
stdout().flush()?;
stdin().read_line(&mut input)?;
let in_port: usize = input.trim().parse()?;
let in_port = midi_in_ports.get(input.trim().parse::<usize>()?)
.ok_or("Invalid port number")?;

println!("\nAvailable output ports:");
for i in 0..midi_out.port_count() {
println!("{}: {}", i, midi_out.port_name(i).unwrap());
let midi_out_ports = midi_out.ports();
for (i, p) in midi_out_ports.iter().enumerate() {
println!("{}: {}", i, midi_out.port_name(p)?);
}
print!("Please select output port: ");
stdout().flush()?;
input.clear();
stdin().read_line(&mut input)?;
let out_port: usize = input.trim().parse()?;
let out_port = midi_out_ports.get(input.trim().parse::<usize>()?)
.ok_or("Invalid port number")?;

// This shows how to reuse input and output objects:
// Open/close the connections twice using the same MidiInput/MidiOutput objects
Expand Down
7 changes: 5 additions & 2 deletions examples/test_sysex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ pub fn run() -> Result<(), Box<Error>> {

assert_eq!(midi_out.port_count(), previous_count + 1);

println!("Connecting to port '{}' ...", midi_out.port_name(previous_count).unwrap());
let mut conn_out = midi_out.connect(previous_count, "midir-test")?;
let out_ports = midi_out.ports();
let new_port = out_ports.last().unwrap();
println!("Connecting to port '{}' ...", midi_out.port_name(&new_port).unwrap());
let mut conn_out = midi_out.connect(&new_port, "midir-test")?;
println!("Starting to send messages ...");
//sleep(Duration::from_millis(2000));
println!("Sending NoteOn message");
Expand All @@ -53,6 +55,7 @@ pub fn run() -> Result<(), Box<Error>> {
assert_eq!(v.len(), LARGE_SYSEX_SIZE);
conn_out.send(&v)?;
sleep(Duration::from_millis(200));
// FIXME: the following doesn't seem to work with ALSA
println!("Sending large SysEx message (chunked)...");
for ch in v.chunks(4) {
conn_out.send(ch)?;
Expand Down
67 changes: 46 additions & 21 deletions src/backend/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,37 @@ use ::{MidiMessage, Ignore};
use ::errors::*;

mod helpers {
use super::alsa::seq::{Seq, ClientIter, PortIter, PortInfo, PortCap, MidiEvent, MIDI_GENERIC, SYNTH, APPLICATION};
use super::alsa::seq::{Seq, Addr, ClientIter, PortIter, PortInfo, PortCap, MidiEvent, MIDI_GENERIC, SYNTH, APPLICATION};
use ::errors::PortInfoError;

pub fn poll(fds: &mut [super::libc::pollfd], timeout: i32) -> i32 {
unsafe { super::libc::poll(fds.as_mut_ptr(), fds.len() as super::libc::nfds_t, timeout) }
}

#[inline]
pub fn get_port_count(s: &Seq, capability: PortCap) -> usize {
pub fn get_ports<F, T>(s: &Seq, capability: PortCap, f: F) -> Vec<T> where F: Fn(PortInfo) -> T {
ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client()))
.filter(|p| p.get_type().intersects(MIDI_GENERIC | SYNTH | APPLICATION))
.filter(|p| p.get_capability().intersects(capability))
.count()
.map(f)
.collect()
}

#[inline]
pub fn get_port_info(s: &Seq, capability: PortCap, port_number: usize) -> Option<PortInfo> {
pub fn get_port_count(s: &Seq, capability: PortCap) -> usize {
ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client()))
.filter(|p| p.get_type().intersects(MIDI_GENERIC | SYNTH | APPLICATION))
.filter(|p| p.get_capability().intersects(capability))
.nth(port_number)
.count()
}

#[inline]
pub fn get_port_name(s: &Seq, capability: PortCap, port_number: usize) -> Result<String, PortInfoError> {
pub fn get_port_name(s: &Seq, addr: Addr) -> Result<String, PortInfoError> {
use std::fmt::Write;

let pinfo = match get_port_info(s, capability, port_number) {
Some(p) => p,
None => return Err(PortInfoError::PortNumberOutOfRange)
let pinfo = match s.get_any_port_info(addr) {
Ok(p) => p,
Err(_) => return Err(PortInfoError::InvalidPort)
};

let cinfo = s.get_any_client_info(pinfo.get_client()).map_err(|_| PortInfoError::CannotRetrievePortName)?;
Expand Down Expand Up @@ -120,6 +121,10 @@ pub struct MidiInput {
seq: Option<Seq>,
}

pub struct MidiInputPort {
addr: Addr
}

pub struct MidiInputConnection<T: 'static> {
subscription: Option<PortSubscribe>,
thread: Option<JoinHandle<(HandlerData<T>, T)>>,
Expand Down Expand Up @@ -154,13 +159,21 @@ impl MidiInput {
pub fn ignore(&mut self, flags: Ignore) {
self.ignore_flags = flags;
}

pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> {
helpers::get_ports(self.seq.as_ref().unwrap(), READ | SUBS_READ, |p| ::common::MidiInputPort {
imp: MidiInputPort {
addr: Addr { client: p.get_client(), port: p.get_port() }
}
})
}

pub fn port_count(&self) -> usize {
helpers::get_port_count(self.seq.as_ref().unwrap(), READ | SUBS_READ)
}

pub fn port_name(&self, port_number: usize) -> Result<String, PortInfoError> {
helpers::get_port_name(self.seq.as_ref().unwrap(), READ | SUBS_READ, port_number)
pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
helpers::get_port_name(self.seq.as_ref().unwrap(), port.addr)
}

fn init_queue(&mut self) -> i32 {
Expand Down Expand Up @@ -221,7 +234,7 @@ impl MidiInput {
}

pub fn connect<F, T: Send>(
mut self, port_number: usize, port_name: &str, callback: F, data: T
mut self, port: &MidiInputPort, port_name: &str, callback: F, data: T
) -> Result<MidiInputConnection<T>, ConnectError<Self>>
where F: FnMut(u64, &[u8], &mut T) + Send + 'static {

Expand All @@ -232,9 +245,9 @@ impl MidiInput {

let queue_id = self.init_queue();

let src_pinfo = match helpers::get_port_info(self.seq.as_ref().unwrap(), READ | SUBS_READ, port_number) {
Some(p) => p,
None => return Err(ConnectError::new(ConnectErrorKind::PortNumberOutOfRange, self))
let src_pinfo = match self.seq.as_ref().unwrap().get_any_port_info(port.addr) {
Ok(p) => p,
Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self))
};

let c_port_name = match CString::new(port_name) {
Expand Down Expand Up @@ -418,6 +431,10 @@ pub struct MidiOutput {
seq: Option<Seq>, // TODO: if `Seq` is marked as non-zero, this should just be pointer-sized
}

pub struct MidiOutputPort {
addr: Addr
}

pub struct MidiOutputConnection {
seq: Option<Seq>,
vport: i32,
Expand All @@ -439,19 +456,27 @@ impl MidiOutput {
seq: Some(seq),
})
}

pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> {
helpers::get_ports(self.seq.as_ref().unwrap(), WRITE | SUBS_WRITE, |p| ::common::MidiOutputPort {
imp: MidiOutputPort {
addr: Addr { client: p.get_client(), port: p.get_port() }
}
})
}

pub fn port_count(&self) -> usize {
helpers::get_port_count(self.seq.as_ref().unwrap(), WRITE | SUBS_WRITE)
}

pub fn port_name(&self, port_number: usize) -> Result<String, PortInfoError> {
helpers::get_port_name(self.seq.as_ref().unwrap(), WRITE | SUBS_WRITE, port_number)
pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
helpers::get_port_name(self.seq.as_ref().unwrap(), port.addr)
}

pub fn connect(mut self, port_number: usize, port_name: &str) -> Result<MidiOutputConnection, ConnectError<Self>> {
let pinfo = match helpers::get_port_info(self.seq.as_ref().unwrap(), WRITE | SUBS_WRITE, port_number) {
Some(p) => p,
None => return Err(ConnectError::new(ConnectErrorKind::PortNumberOutOfRange, self))
pub fn connect(mut self, port: &MidiOutputPort, port_name: &str) -> Result<MidiOutputConnection, ConnectError<Self>> {
let pinfo = match self.seq.as_ref().unwrap().get_any_port_info(port.addr) {
Ok(p) => p,
Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self))
};

let c_port_name = match CString::new(port_name) {
Expand Down
Loading

0 comments on commit 7b865ed

Please sign in to comment.