let c_port_name = match CString::new(port_name) {
Ok(c_port_name) => c_port_name,
Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self))
};
let threadbuilder = Builder::new(); let name = format!("midir ALSA input handler (port '{}')", port_name); let threadbuilder = threadbuilder.name(name); let thread = match threadbuilder.spawn(move || { letmut d = data; let h = handle_input(handler_data, &mut d);
(h, d) // return both the handler data and the user data
}) {
Ok(handle) => handle,
Err(_) => { //unsafe { snd_seq_unsubscribe_port(self.seq.as_mut_ptr(), sub.as_ptr()) }; return Err(ConnectError::other("could not start ALSA input handler thread", self));
}
};
pubfn create_virtual<F, T: Send>( mutself, port_name: &str, callback: F, data: T
) -> Result<MidiInputConnection<T>, ConnectError<Self>> where F: FnMut(u64, &[u8], &mut T) + Send + 'static { let trigger_fds = matchself.init_trigger() {
Ok(fds) => fds,
Err(()) => { return Err(ConnectError::other("could not create communication pipe for ALSA handler", self)); }
};
let queue_id = self.init_queue();
let c_port_name = match CString::new(port_name) {
Ok(c_port_name) => c_port_name,
Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self))
};
let threadbuilder = Builder::new(); let thread = match threadbuilder.spawn(move || { letmut d = data; let h = handle_input(handler_data, &mut d);
(h, d) // return both the handler data and the user data
}) {
Ok(handle) => handle,
Err(_) => { //unsafe { snd_seq_unsubscribe_port(self.seq.as_mut_ptr(), sub.as_ptr()) }; return Err(ConnectError::other("could not start ALSA input handler thread", self));
}
};
/// This must only be called if the handler thread has not yet been shut down fn close_internal(&mutself) -> (HandlerData<T>, T) { // Request the thread to stop. let _res = unsafe { self::libc::write(self.trigger_send_fd, &falseas *const bool as *const _, mem::size_of::<bool>() asself::libc::size_t) };
let thread = self.thread.take().unwrap(); // Join the thread to get the handler_data back let (handler_data, user_data) = match thread.join() {
Ok(data) => data, // TODO: handle this more gracefully?
Err(e) => { iflet Some(e) = e.downcast_ref::<&'static str>() {
panic!("Error when joining ALSA thread: {}", e);
} else {
panic!("Unknown error when joining ALSA thread: {:?}", e);
}
}
};
// TODO: find out why snd_seq_unsubscribe_port takes a long time if there was not yet any input message iflet Some(ref subscription) = self.subscription { let _ = handler_data.seq.unsubscribe_port(subscription.get_sender(), subscription.get_dest());
}
// Close the trigger fds (TODO: make sure that these are closed even in the presence of panic in thread) unsafe { self::libc::close(handler_data.trigger_rcv_fd); self::libc::close(self.trigger_send_fd);
}
// Stop and free the input queue if !cfg!(feature = "avoid_timestamping") { let _ = handler_data.seq.control_queue(handler_data.queue_id, EventType::Stop, 0, None); let _ = handler_data.seq.drain_output(); let _ = handler_data.seq.free_queue(handler_data.queue_id);
}
// Delete the port let _ = handler_data.seq.delete_port(self.vport);
(handler_data, user_data)
}
}
impl<T> Drop for MidiInputConnection<T> { fn drop(&mutself) { // Use `self.thread` as a flag whether the connection has already been dropped ifself.thread.is_some() { self.close_internal();
}
}
}
pubstruct MidiOutput {
seq: Option<Seq>, // TODO: if `Seq` is marked as non-zero, this should just be pointer-sized
}
let c_port_name = match CString::new(port_name) {
Ok(c_port_name) => c_port_name,
Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self))
};
pubfn send(&mutself, message: &[u8]) -> Result<(), SendError> { let nbytes = message.len();
assert!(nbytes <= u32::max_value() as usize);
if nbytes > self.coder.get_buffer_size() as usize { ifself.coder.resize_buffer(nbytes as u32).is_err() { return Err(SendError::Other("could not resize ALSA encoding buffer"));
}
}
// ALSA documentation says: // The required buffer size for a sequencer event it as most 12 bytes, except for System Exclusive events (which we handle separately) letmut buffer = [0; 12];
{ // open scope where we can borrow data.seq letmut seq_input = data.seq.input();
letmut do_input = true; while do_input { iflet Ok(0) = seq_input.event_input_pending(true) { // No data pending if helpers::poll(&mut poll_fds, -1) >= 0 { // Read from our "channel" whether we should stop the thread if poll_fds[0].revents & self::libc::POLLIN != 0 { let _res = unsafe { self::libc::read(poll_fds[0].fd, mem::transmute(&mut do_input), mem::size_of::<bool>() asself::libc::size_t) };
}
} continue;
}
// This is a bit weird, but we now have to decode an ALSA MIDI // event (back) into MIDI bytes. We'll ignore non-MIDI types.
// The ALSA sequencer has a maximum buffer size for MIDI sysex // events of 256 bytes. If a device sends sysex messages larger // than this, they are segmented into 256 byte chunks. So, // we'll watch for this and concatenate sysex chunks into a // single sysex message if necessary. // // TODO: Figure out if this is still true (seems to not be the case) // If not (i.e., each event represents a complete message), we can // call the user callback with the byte buffer directly, without the // copying to `message.bytes` first. if !continue_sysex { message.bytes.clear() }
let ignore_flags = data.ignore_flags;
// If here, there should be data. letmut ev = match seq_input.event_input() {
Ok(ev) => ev,
Err(ref e) if e.errno() == alsa::nix::errno::Errno::ENOSPC => { let _ = writeln!(stderr(), "\nError in handle_input: ALSA MIDI input buffer overrun!\n"); continue;
},
Err(ref e) if e.errno() == alsa::nix::errno::Errno::EAGAIN => { let _ = writeln!(stderr(), "\nError in handle_input: no input event from ALSA MIDI input buffer!\n"); continue;
},
Err(ref e) => { let _ = writeln!(stderr(), "\nError in handle_input: unknown ALSA MIDI input error ({})!\n", e); //perror("System reports"); continue;
}
};
let do_decode = match ev.get_type() {
EventType::PortSubscribed => { if cfg!(debug) { println!("Notice from handle_input: ALSA port connection made!") }; false
},
EventType::PortUnsubscribed => { if cfg!(debug) { let _ = writeln!(stderr(), "Notice from handle_input: ALSA port connection has closed!"); let connect = ev.get_data::<Connect>().unwrap(); let _ = writeln!(stderr(), "sender = {}:{}, dest = {}:{}",
connect.sender.client,
connect.sender.port,
connect.dest.client,
connect.dest.port
);
} false
},
EventType::Qframe => { // MIDI time code
!ignore_flags.contains(Ignore::Time)
},
EventType::Tick => { // 0xF9 ... MIDI timing tick
!ignore_flags.contains(Ignore::Time)
},
EventType::Clock => { // 0xF8 ... MIDI timing (clock) tick
!ignore_flags.contains(Ignore::Time)
},
EventType::Sensing => { // Active sensing
!ignore_flags.contains(Ignore::ActiveSense)
},
EventType::Sysex => { if !ignore_flags.contains(Ignore::Sysex) { // Directly copy the data from the external buffer to our message
message.bytes.extend_from_slice(ev.get_ext().unwrap());
continue_sysex = *message.bytes.last().unwrap() != 0xF7;
} false// don't ever decode sysex messages (it would unnecessarily copy the message content to another buffer)
},
_ => true
};
// NOTE: SysEx messages have already been "decoded" at this point! if do_decode { iflet Ok(nbytes) = coder.get_wrapped().decode(&mut buffer, & style='color:red'>mut ev) { if nbytes > 0 {
message.bytes.extend_from_slice(&buffer[0..nbytes]);
}
}
}
if message.bytes.len() == 0 || continue_sysex { continue; }
// Calculate the time stamp: // Use the ALSA sequencer event time data. // (thanks to Pedro Lopez-Cabanillas!). let alsa_time = ev.get_time().unwrap(); let secs = alsa_time.as_secs(); let nsecs = alsa_time.subsec_nanos();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.