summaryrefslogtreecommitdiffstats
path: root/third_party/rust/midir/src/backend/jack
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/midir/src/backend/jack
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/midir/src/backend/jack')
-rw-r--r--third_party/rust/midir/src/backend/jack/mod.rs364
-rw-r--r--third_party/rust/midir/src/backend/jack/wrappers.rs250
2 files changed, 614 insertions, 0 deletions
diff --git a/third_party/rust/midir/src/backend/jack/mod.rs b/third_party/rust/midir/src/backend/jack/mod.rs
new file mode 100644
index 0000000000..92cc81a295
--- /dev/null
+++ b/third_party/rust/midir/src/backend/jack/mod.rs
@@ -0,0 +1,364 @@
+extern crate jack_sys;
+extern crate libc;
+
+use self::jack_sys::jack_nframes_t;
+use self::libc::c_void;
+
+use std::{mem, slice};
+use std::ffi::CString;
+
+mod wrappers;
+use self::wrappers::*;
+
+use ::{Ignore, MidiMessage};
+use ::errors::*;
+
+const OUTPUT_RINGBUFFER_SIZE: usize = 16384;
+
+struct InputHandlerData<T> {
+ port: Option<MidiPort>,
+ ignore_flags: Ignore,
+ callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>,
+ user_data: Option<T>
+}
+
+pub struct MidiInput {
+ ignore_flags: Ignore,
+ client: Option<Client>,
+}
+
+#[derive(Clone, PartialEq)]
+pub struct MidiInputPort {
+ name: CString
+}
+
+pub struct MidiInputConnection<T> {
+ handler_data: Box<InputHandlerData<T>>,
+ client: Option<Client>
+}
+
+impl MidiInput {
+ pub fn new(client_name: &str) -> Result<Self, InitError> {
+ let client = match Client::open(client_name, JackOpenOptions::NoStartServer) {
+ Ok(c) => c,
+ Err(_) => { return Err(InitError); } // TODO: maybe add message that Jack server might not be running
+ };
+
+ Ok(MidiInput {
+ ignore_flags: Ignore::None,
+ client: Some(client),
+ })
+ }
+
+ pub fn ignore(&mut self, flags: Ignore) {
+ self.ignore_flags = flags;
+ }
+
+ pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> {
+ let ports = self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsOutput);
+ let mut result = Vec::with_capacity(ports.count());
+ for i in 0..ports.count() {
+ result.push(::common::MidiInputPort {
+ imp: MidiInputPort {
+ name: ports.get_c_name(i).into()
+ }
+ })
+ }
+ result
+ }
+
+ pub fn port_count(&self) -> usize {
+ self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsOutput).count()
+ }
+
+ pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
+ Ok(port.name.to_string_lossy().into())
+ }
+
+ fn activate_callback<F, T: Send>(&mut self, callback: F, data: T)
+ -> Box<InputHandlerData<T>>
+ where F: FnMut(u64, &[u8], &mut T) + Send + 'static
+ {
+ let handler_data = Box::new(InputHandlerData {
+ port: None,
+ ignore_flags: self.ignore_flags,
+ callback: Box::new(callback),
+ user_data: Some(data)
+ });
+
+ let data_ptr = unsafe { mem::transmute_copy::<_, *mut InputHandlerData<T>>(&handler_data) };
+
+ self.client.as_mut().unwrap().set_process_callback(handle_input::<T>, data_ptr as *mut c_void);
+ self.client.as_mut().unwrap().activate();
+ handler_data
+ }
+
+ pub fn connect<F, T: Send>(
+ mut self, port: &MidiInputPort, port_name: &str, callback: F, data: T
+ ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>>
+ where F: FnMut(u64, &[u8], &mut T) + Send + 'static {
+
+ let mut handler_data = self.activate_callback(callback, data);
+
+ // Create port ...
+ let dest_port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsInput) {
+ Ok(p) => p,
+ Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); }
+ };
+
+ // ... and connect it to the output
+ if let Err(_) = self.client.as_mut().unwrap().connect(&port.name, dest_port.get_name()) {
+ return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self));
+ }
+
+ handler_data.port = Some(dest_port);
+
+ Ok(MidiInputConnection {
+ handler_data: handler_data,
+ client: self.client.take()
+ })
+ }
+
+ pub fn create_virtual<F, T: Send>(
+ mut self, port_name: &str, callback: F, data: T
+ ) -> Result<MidiInputConnection<T>, ConnectError<Self>>
+ where F: FnMut(u64, &[u8], &mut T) + Send + 'static {
+
+ let mut handler_data = self.activate_callback(callback, data);
+
+ // Create port
+ let port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsInput) {
+ Ok(p) => p,
+ Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); }
+ };
+
+ handler_data.port = Some(port);
+
+ Ok(MidiInputConnection {
+ handler_data: handler_data,
+ client: self.client.take()
+ })
+ }
+}
+
+impl<T> MidiInputConnection<T> {
+ pub fn close(mut self) -> (MidiInput, T) {
+ self.close_internal();
+
+ (MidiInput {
+ client: self.client.take(),
+ ignore_flags: self.handler_data.ignore_flags,
+ }, self.handler_data.user_data.take().unwrap())
+ }
+
+ fn close_internal(&mut self) {
+ let port = self.handler_data.port.take().unwrap();
+ self.client.as_mut().unwrap().unregister_midi_port(port);
+ self.client.as_mut().unwrap().deactivate();
+ }
+}
+
+impl<T> Drop for MidiInputConnection<T> {
+ fn drop(&mut self) {
+ if self.client.is_some() {
+ self.close_internal();
+ }
+ }
+}
+
+extern "C" fn handle_input<T>(nframes: jack_nframes_t, arg: *mut c_void) -> i32 {
+ let data: &mut InputHandlerData<T> = unsafe { &mut *(arg as *mut InputHandlerData<T>) };
+
+ // Is port created?
+ if let Some(ref port) = data.port {
+ let buff = port.get_midi_buffer(nframes);
+
+ let mut message = MidiMessage::new(); // TODO: create MidiMessage once and reuse its buffer for every handle_input call
+
+ // We have midi events in buffer
+ let evcount = buff.get_event_count();
+ let mut event = mem::MaybeUninit::uninit();
+
+ for j in 0..evcount {
+ message.bytes.clear();
+ unsafe { buff.get_event(event.as_mut_ptr(), j) };
+ let event = unsafe { event.assume_init() };
+
+ for i in 0..event.size {
+ message.bytes.push(unsafe { *event.buffer.offset(i as isize) });
+ }
+
+ message.timestamp = Client::get_time(); // this is in microseconds
+ (data.callback)(message.timestamp, &message.bytes, data.user_data.as_mut().unwrap());
+ }
+ }
+
+ return 0;
+}
+
+struct OutputHandlerData {
+ port: Option<MidiPort>,
+ buff_size: Ringbuffer,
+ buff_message: Ringbuffer,
+}
+
+pub struct MidiOutput {
+ client: Option<Client>,
+}
+
+#[derive(Clone, PartialEq)]
+pub struct MidiOutputPort {
+ name: CString
+}
+
+pub struct MidiOutputConnection {
+ handler_data: Box<OutputHandlerData>,
+ client: Option<Client>
+}
+
+impl MidiOutput {
+ pub fn new(client_name: &str) -> Result<Self, InitError> {
+ let client = match Client::open(client_name, JackOpenOptions::NoStartServer) {
+ Ok(c) => c,
+ Err(_) => { return Err(InitError); } // TODO: maybe add message that Jack server might not be running
+ };
+
+ Ok(MidiOutput {
+ client: Some(client),
+ })
+ }
+
+ pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> {
+ let ports = self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsInput);
+ let mut result = Vec::with_capacity(ports.count());
+ for i in 0..ports.count() {
+ result.push(::common::MidiOutputPort {
+ imp: MidiOutputPort {
+ name: ports.get_c_name(i).into()
+ }
+ })
+ }
+ result
+ }
+
+ pub fn port_count(&self) -> usize {
+ self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsInput).count()
+ }
+
+ pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
+ Ok(port.name.to_string_lossy().into())
+ }
+
+ fn activate_callback(&mut self) -> Box<OutputHandlerData> {
+ let handler_data = Box::new(OutputHandlerData {
+ port: None,
+ buff_size: Ringbuffer::new(OUTPUT_RINGBUFFER_SIZE),
+ buff_message: Ringbuffer::new(OUTPUT_RINGBUFFER_SIZE)
+ });
+
+ let data_ptr = unsafe { mem::transmute_copy::<_, *mut OutputHandlerData>(&handler_data) };
+
+ self.client.as_mut().unwrap().set_process_callback(handle_output, data_ptr as *mut c_void);
+ self.client.as_mut().unwrap().activate();
+ handler_data
+ }
+
+ pub fn connect(mut self, port: &MidiOutputPort, port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> {
+ let mut handler_data = self.activate_callback();
+
+ // Create port ...
+ let source_port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsOutput) {
+ Ok(p) => p,
+ Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); }
+ };
+
+ // ... and connect it to the input
+ if let Err(_) = self.client.as_mut().unwrap().connect(source_port.get_name(), &port.name) {
+ return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self));
+ }
+
+ handler_data.port = Some(source_port);
+
+ Ok(MidiOutputConnection {
+ handler_data: handler_data,
+ client: self.client.take()
+ })
+ }
+
+ pub fn create_virtual(
+ mut self, port_name: &str
+ ) -> Result<MidiOutputConnection, ConnectError<Self>> {
+ let mut handler_data = self.activate_callback();
+
+ // Create port
+ let port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsOutput) {
+ Ok(p) => p,
+ Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); }
+ };
+
+ handler_data.port = Some(port);
+
+ Ok(MidiOutputConnection {
+ handler_data: handler_data,
+ client: self.client.take()
+ })
+ }
+}
+
+impl MidiOutputConnection {
+ pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> {
+ let nbytes = message.len();
+
+ // Write full message to buffer
+ let written = self.handler_data.buff_message.write(message);
+ debug_assert!(written == nbytes, "not enough bytes written to ALSA ringbuffer `message`");
+ let nbytes_slice = unsafe { slice::from_raw_parts(&nbytes as *const usize as *const u8, mem::size_of_val(&nbytes)) };
+ let written = self.handler_data.buff_size.write(nbytes_slice);
+ debug_assert!(written == mem::size_of_val(&nbytes), "not enough bytes written to ALSA ringbuffer `size`");
+ Ok(())
+ }
+
+ pub fn close(mut self) -> MidiOutput {
+ self.close_internal();
+
+ MidiOutput {
+ client: self.client.take(),
+ }
+ }
+
+ fn close_internal(&mut self) {
+ let port = self.handler_data.port.take().unwrap();
+ self.client.as_mut().unwrap().unregister_midi_port(port);
+ self.client.as_mut().unwrap().deactivate();
+ }
+}
+
+impl Drop for MidiOutputConnection {
+ fn drop(&mut self) {
+ if self.client.is_some() {
+ self.close_internal();
+ }
+ }
+}
+
+extern "C" fn handle_output(nframes: jack_nframes_t, arg: *mut c_void) -> i32 {
+ let data: &mut OutputHandlerData = unsafe { mem::transmute(arg) };
+
+ // Is port created?
+ if let Some(ref port) = data.port {
+ let mut space: usize = 0;
+
+ let mut buff = port.get_midi_buffer(nframes);
+ buff.clear();
+
+ while data.buff_size.get_read_space() > 0 {
+ let read = data.buff_size.read(&mut space as *mut usize as *mut u8, mem::size_of::<usize>());
+ debug_assert!(read == mem::size_of::<usize>(), "not enough bytes read from `size` ringbuffer");
+ let midi_data = buff.event_reserve(0, space);
+ let read = data.buff_message.read(midi_data, space);
+ debug_assert!(read == space, "not enough bytes read from `message` ringbuffer");
+ }
+ }
+
+ return 0;
+}
diff --git a/third_party/rust/midir/src/backend/jack/wrappers.rs b/third_party/rust/midir/src/backend/jack/wrappers.rs
new file mode 100644
index 0000000000..ef00e2d73f
--- /dev/null
+++ b/third_party/rust/midir/src/backend/jack/wrappers.rs
@@ -0,0 +1,250 @@
+#![allow(non_upper_case_globals, dead_code)]
+
+use std::{ptr, slice, str};
+use std::ffi::{CStr, CString};
+use std::ops::Index;
+
+use super::libc::{c_void, size_t};
+
+use super::jack_sys::{
+ jack_get_time,
+ jack_client_t,
+ jack_client_open,
+ jack_client_close,
+ jack_get_ports,
+ jack_port_t,
+ jack_port_register,
+ jack_port_unregister,
+ jack_port_name,
+ jack_free,
+ jack_activate,
+ jack_deactivate,
+ jack_nframes_t,
+ jack_set_process_callback,
+ jack_connect,
+ jack_port_get_buffer,
+ jack_midi_data_t,
+ jack_midi_get_event_count,
+ jack_midi_event_get,
+ jack_midi_event_t,
+ jack_midi_clear_buffer,
+ jack_midi_event_reserve,
+ jack_ringbuffer_t,
+ jack_ringbuffer_create,
+ jack_ringbuffer_read_space,
+ jack_ringbuffer_read,
+ jack_ringbuffer_free,
+ jack_ringbuffer_write,
+};
+
+pub const JACK_DEFAULT_MIDI_TYPE: &[u8] = b"8 bit raw midi\0";
+
+bitflags! {
+ pub struct JackOpenOptions: u32 {
+ const NoStartServer = 1;
+ const UseExactName = 2;
+ const ServerName = 4;
+ const SessionID = 32;
+ }
+}
+
+bitflags! {
+ pub struct PortFlags: u32 {
+ const PortIsInput = 1;
+ const PortIsOutput = 2;
+ const PortIsPhysical = 4;
+ const PortCanMonitor = 8;
+ const PortIsTerminal = 16;
+ }
+}
+
+// TODO: hide this type
+pub type ProcessCallback = extern "C" fn(nframes: jack_nframes_t, arg: *mut c_void) -> i32;
+
+pub struct Client {
+ p: *mut jack_client_t
+}
+
+unsafe impl Send for Client {}
+
+impl Client {
+ pub fn get_time() -> u64 {
+ unsafe { jack_get_time() }
+ }
+
+ pub fn open(name: &str, options: JackOpenOptions) -> Result<Client, ()> {
+ let c_name = CString::new(name).ok().expect("client name must not contain null bytes");
+ let result = unsafe { jack_client_open(c_name.as_ptr(), options.bits(), ptr::null_mut()) };
+ if result.is_null() {
+ Err(())
+ } else {
+ Ok(Client { p: result })
+ }
+ }
+
+ pub fn get_midi_ports(&self, flags: PortFlags) -> PortInfos {
+ let ports_ptr = unsafe { jack_get_ports(self.p, ptr::null_mut(), JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const i8, flags.bits() as u64) };
+ let slice = if ports_ptr.is_null() {
+ &[]
+ } else {
+ unsafe {
+ let count = (0isize..).find(|i| (*ports_ptr.offset(*i)).is_null()).unwrap() as usize;
+ slice::from_raw_parts(ports_ptr, count)
+ }
+ };
+ PortInfos { p: slice }
+ }
+
+ pub fn register_midi_port(&mut self, name: &str, flags: PortFlags) -> Result<MidiPort, ()> {
+ let c_name = CString::new(name).ok().expect("port name must not contain null bytes");
+ let result = unsafe { jack_port_register(self.p, c_name.as_ptr(), JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const i8, flags.bits() as u64, 0) };
+ if result.is_null() {
+ Err(())
+ } else {
+ Ok(MidiPort { p: result })
+ }
+ }
+
+ /// This can not be implemented in Drop, because it needs a reference
+ /// to the client. But it consumes the MidiPort.
+ pub fn unregister_midi_port(&mut self, client: MidiPort) {
+ unsafe { jack_port_unregister(self.p, client.p) };
+ }
+
+ pub fn activate(&mut self) {
+ unsafe { jack_activate(self.p) };
+ }
+
+ pub fn deactivate(&mut self) {
+ unsafe { jack_deactivate(self.p) };
+ }
+
+ /// The code in the supplied function must be suitable for real-time
+ /// execution. That means that it cannot call functions that might block
+ /// for a long time. This includes all I/O functions (disk, TTY, network),
+ /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select,
+ /// pthread_join, pthread_cond_wait, etc, etc.
+ pub fn set_process_callback(&mut self, callback: ProcessCallback, data: *mut c_void) {
+ unsafe { jack_set_process_callback(self.p, Some(callback), data) };
+ }
+
+ pub fn connect(&mut self, source_port: &CStr, destination_port: &CStr) -> Result<(), ()> {
+ let rc = unsafe { jack_connect(self.p, source_port.as_ptr(), destination_port.as_ptr()) };
+ if rc == 0 {
+ Ok(())
+ } else {
+ Err(()) // TODO: maybe handle EEXIST explicitly
+ }
+ }
+}
+
+impl Drop for Client {
+ fn drop(&mut self) {
+ unsafe { jack_client_close(self.p) };
+ }
+}
+
+pub struct PortInfos<'a> {
+ p: &'a[*const i8],
+}
+
+unsafe impl<'a> Send for PortInfos<'a> {}
+
+impl<'a> PortInfos<'a> {
+ pub fn count(&self) -> usize {
+ self.p.len()
+ }
+
+ pub fn get_c_name(&self, index: usize) -> &CStr {
+ let ptr = self.p[index];
+ unsafe { CStr::from_ptr(ptr) }
+ }
+}
+
+impl<'a> Index<usize> for PortInfos<'a> {
+ type Output = str;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ let slice = self.get_c_name(index).to_bytes();
+ str::from_utf8(slice).ok().expect("Error converting port name to UTF8")
+ }
+}
+
+impl<'a> Drop for PortInfos<'a> {
+ fn drop(&mut self) {
+ if self.p.len() > 0 {
+ unsafe { jack_free(self.p.as_ptr() as *mut _) }
+ }
+ }
+}
+
+pub struct MidiPort {
+ p: *mut jack_port_t
+}
+
+unsafe impl Send for MidiPort {}
+
+impl MidiPort {
+ pub fn get_name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(jack_port_name(self.p)) }
+ }
+
+ pub fn get_midi_buffer(&self, nframes: jack_nframes_t) -> MidiBuffer {
+ let buf = unsafe { jack_port_get_buffer(self.p, nframes) };
+ MidiBuffer { p: buf }
+ }
+}
+
+pub struct MidiBuffer {
+ p: *mut c_void
+}
+
+impl MidiBuffer {
+ pub fn get_event_count(&self) -> u32 {
+ unsafe { jack_midi_get_event_count(self.p) }
+ }
+
+ pub unsafe fn get_event(&self, ev: *mut jack_midi_event_t, index: u32) {
+ jack_midi_event_get(ev, self.p, index);
+ }
+
+ pub fn clear(&mut self) {
+ unsafe { jack_midi_clear_buffer(self.p) }
+ }
+
+ pub fn event_reserve(&mut self, time: jack_nframes_t, data_size: usize) -> *mut jack_midi_data_t {
+ unsafe { jack_midi_event_reserve(self.p, time, data_size as size_t) }
+ }
+}
+
+pub struct Ringbuffer {
+ p: *mut jack_ringbuffer_t
+}
+
+unsafe impl Send for Ringbuffer {}
+
+impl Ringbuffer {
+ pub fn new(size: usize) -> Ringbuffer {
+ let result = unsafe { jack_ringbuffer_create(size as size_t) };
+ Ringbuffer { p: result }
+ }
+
+ pub fn get_read_space(&self) -> usize {
+ unsafe { jack_ringbuffer_read_space(self.p) as usize }
+ }
+
+ pub fn read(&mut self, destination: *mut u8, count: usize) -> usize {
+ let bytes_read = unsafe { jack_ringbuffer_read(self.p, destination as *mut i8, count as size_t) };
+ bytes_read as usize
+ }
+
+ pub fn write(&mut self, source: &[u8]) -> usize {
+ unsafe { jack_ringbuffer_write(self.p, source.as_ptr() as *const i8, source.len() as size_t) as usize }
+ }
+}
+
+impl Drop for Ringbuffer{
+ fn drop(&mut self) {
+ unsafe { jack_ringbuffer_free(self.p) }
+ }
+} \ No newline at end of file