use midir::{
InitError, MidiInput, MidiInputConnection, MidiInputPort, MidiOutput, MidiOutputConnection,
MidiOutputPort,
}; use nsstring::{nsAString, nsString}; use std::ptr; use thin_vec::ThinVec; use uuid::Uuid;
/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththisfile,
* You can obtain one at http://mozilla.org/MPL/2.0/. */ externcrate midir;
fn remove_missing_ports( self: &mut MidirWrapper,
ports: &Vec<MidiPortWrapper>,
remove_callback: RemoveCallback,
) { let old_ports = &mutself.ports; letmut i = 0; while i < old_ports.len() { if !ports
.iter()
.any(|p| p.name == old_ports[i].name && p.input() == old_ports[i].input())
{ let port = old_ports.remove(i); let id = nsString::from(&port.id); let name = nsString::from(&port.name); unsafe { remove_callback(&id, &name, port.input()) };
} else {
i += 1;
}
}
}
fn add_new_ports( self: &mut MidirWrapper,
ports: Vec<MidiPortWrapper>,
add_callback: AddCallback,
) { for port in ports { if !self.is_port_present(&port) && !Self::is_microsoft_synth_output(&port) { let id = nsString::from(&port.id); let name = nsString::from(&port.name); unsafe { add_callback(&id, &name, port.input()) }; self.ports.push(port);
}
}
}
fn open_port( self: &mut MidirWrapper,
nsid: &nsString,
timestamp: GeckoTimeStamp,
callback: unsafeextern"C"fn(
id: &nsString,
data: *const u8,
length: usize,
timestamp: &GeckoTimeStamp,
micros: u64,
),
) -> Result<(), ()> { let id = nsid.to_string(); let connections = &mutself.connections; let port = self.ports.iter_mut().find(|e| e.id.eq(&id)); iflet Some(port) = port { if port.open_count == 0 { let connection = match &port.port {
MidiPort::Input(port) => { let input = MidiInput::new("WebMIDI input").map_err(|_err| ())?; let data = CallbackData {
nsid: nsid.clone(),
open_timestamp: timestamp,
}; let connection = input
.connect(
port, "Input connection", move |stamp, message, data| unsafe {
callback(
&data.nsid,
message.as_ptr(),
message.len(),
&data.open_timestamp,
stamp,
);
},
data,
)
.map_err(|_err| ())?;
MidiConnectionWrapper {
id: id.clone(),
connection: MidiConnection::Input(connection),
}
}
MidiPort::Output(port) => { let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?; let connection = output
.connect(port, "Output connection")
.map_err(|_err| ())?;
MidiConnectionWrapper {
connection: MidiConnection::Output(connection),
id: id.clone(),
}
}
};
connections.push(connection);
}
port.open_count += 1; return Ok(());
}
Err(())
}
fn close_port(self: &mut MidirWrapper, id: &str) { let port = self.ports.iter_mut().find(|e| e.id.eq(&id)).unwrap();
port.open_count -= 1;
if port.open_count > 0 { return;
}
let connections = &mutself.connections; let index = connections.iter().position(|e| e.id.eq(id)).unwrap(); let connection_wrapper = connections.remove(index);
/// Create the C++ wrapper that will be used to talk with midir. /// /// This function will be exposed to C++ /// /// # Safety /// /// This function deliberately leaks the wrapper because ownership is /// transfered to the C++ code. Use [midir_impl_shutdown()] to free it. #[no_mangle] pubunsafeextern"C"fn midir_impl_init(callback: AddCallback) -> *mut MidirWrapper { iflet Ok(mut midir_impl) = MidirWrapper::new() {
midir_impl.refresh(callback, None);
// Gecko invokes this initialization on a separate thread from all the // other operations, so make it clear to Rust this needs to be Send. fn assert_send<T: Send>(_: &T) {}
assert_send(&midir_impl);
let midir_box = Box::new(midir_impl); // Leak the object as it will be owned by the C++ code from now on Box::leak(midir_box) as *mut _
} else {
ptr::null_mut()
}
}
/// Refresh the list of ports. /// /// This function will be exposed to C++ /// /// # Safety /// /// `wrapper` must be the pointer returned by [midir_impl_init()]. #[no_mangle] pubunsafeextern"C"fn midir_impl_refresh(
wrapper: *mut MidirWrapper,
add_callback: AddCallback,
remove_callback: RemoveCallback,
) {
(*wrapper).refresh(add_callback, Some(remove_callback))
}
/// Shutdown midir and free the C++ wrapper. /// /// This function will be exposed to C++ /// /// # Safety /// /// `wrapper` must be the pointer returned by [midir_impl_init()]. After this /// has been called the wrapper object will be destoyed and cannot be accessed /// anymore. #[no_mangle] pubunsafeextern"C"fn midir_impl_shutdown(wrapper: *mut MidirWrapper) { // The MidirImpl object will be automatically destroyed when the contents // of this box are automatically dropped at the end of the function let _midir_box = Box::from_raw(wrapper);
}
/// Open a MIDI port. /// /// This function will be exposed to C++ /// /// # Safety /// /// `wrapper` must be the pointer returned by [midir_impl_init()]. #[no_mangle] pubunsafeextern"C"fn midir_impl_open_port(
wrapper: *mut MidirWrapper,
nsid: *mut nsString,
timestamp: *mut GeckoTimeStamp,
callback: unsafeextern"C"fn(
id: &nsString,
data: *const u8,
length: usize,
timestamp: &GeckoTimeStamp,
micros: u64,
),
) -> bool {
(*wrapper)
.open_port(nsid.as_ref().unwrap(), *timestamp, callback)
.is_ok()
}
/// Close a MIDI port. /// /// This function will be exposed to C++ /// /// # Safety /// /// `wrapper` must be the pointer returned by [midir_impl_init()]. #[no_mangle] pubunsafeextern"C"fn midir_impl_close_port(wrapper: *mut MidirWrapper, id: *mut nsString) {
(*wrapper).close_port(&(*id).to_string());
}
/// Send a message over a MIDI output port. /// /// This function will be exposed to C++ /// /// # Safety /// /// `wrapper` must be the pointer returned by [midir_impl_init()]. #[no_mangle] pubunsafeextern"C"fn midir_impl_send(
wrapper: *mut MidirWrapper,
id: *const nsAString,
data: *const ThinVec<u8>,
) -> bool {
(*wrapper)
.send(&(*id).to_string(), (*data).as_slice())
.is_ok()
}
fn collect_input_ports(input: &MidiInput, wrappers: &mut Vec<MidiPortWrapper>) { let ports = input.ports(); for port in ports { let id = Uuid::new_v4()
.as_hyphenated()
.encode_lower(&mut Uuid::encode_buffer())
.to_owned(); let name = input
.port_name(&port)
.unwrap_or_else(|_| "unknown input port".to_string()); let port = MidiPortWrapper {
id,
name,
port: MidiPort::Input(port),
open_count: 0,
};
wrappers.push(port);
}
}
fn collect_output_ports(output: &MidiOutput, wrappers: &mut Vec<MidiPortWrapper>) { let ports = output.ports(); for port in ports { let id = Uuid::new_v4()
.as_hyphenated()
.encode_lower(&mut Uuid::encode_buffer())
.to_owned(); let name = output
.port_name(&port)
.unwrap_or_else(|_| "unknown input port".to_string()); let port = MidiPortWrapper {
id,
name,
port: MidiPort::Output(port),
open_count: 0,
};
wrappers.push(port);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-06-19)
¤
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.