From 8c4da16b90daa31ebfea0f1a09981f7579dea549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Bir=C3=B3?= Date: Fri, 3 Jan 2025 21:11:18 +0200 Subject: [PATCH 1/3] Migrate to CMake, fix Windows builds, bump RtMidi to v6 --- .gitmodules | 3 + Cargo.toml | 10 ++- build.rs | 155 +++++++++++++++++++++++++++---------- rtmidi | 1 + src/api.rs | 32 ++++---- src/bindings.rs | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ffi.rs | 54 +------------ src/midi.rs | 43 ++++++++--- src/midi_in.rs | 6 +- src/midi_out.rs | 4 +- wrapper.h | 3 - 11 files changed, 383 insertions(+), 130 deletions(-) create mode 100644 .gitmodules create mode 160000 rtmidi create mode 100644 src/bindings.rs delete mode 100644 wrapper.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7b45afc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rtmidi"] + path = rtmidi + url = https://github.com/thestk/rtmidi diff --git a/Cargo.toml b/Cargo.toml index e65501f..02ece99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rtmidi" -version = "0.2.0" -authors = ["Rob Hardwick "] +version = "0.3.0" +authors = ["Rob Hardwick ", "Dani Biro "] edition = "2018" description = "Safe wrapper for RtMidi, realtime MIDI input/output" repository = "https://github.com/robhardwick/rtmidi-rs" @@ -11,5 +11,7 @@ categories = ["multimedia::audio", "api-bindings"] license = "MIT" [build-dependencies] -bindgen = "0.57.0" -pkg-config = "0.3.19" +cmake = "0.1" + +[target.'cfg(any(target_os="linux"))'.build-dependencies] +pkg-config = "0.3" diff --git a/build.rs b/build.rs index 47344c9..e392a9a 100644 --- a/build.rs +++ b/build.rs @@ -1,47 +1,118 @@ -use std::env; -use std::path::PathBuf; +extern crate cmake; + +#[cfg(any(target_os = "linux"))] +extern crate pkg_config; fn main() { - println!("cargo:rustc-link-lib=rtmidi"); - println!("cargo:rerun-if-changed=wrapper.h"); + // Build the static library with CMake. + let mut config = cmake::Config::new("rtmidi"); + config.define("BUILD_SHARED_LIBS", "OFF"); + config.define("RTMIDI_BUILD_STATIC_LIBS", "ON"); + + #[cfg(target_os = "linux")] + { + println!("cargo:rustc-link-lib=dylib=stdc++"); + + #[cfg(feature = "alsa")] + { + config.define("RTMIDI_API_ALSA", "ON"); + + match pkg_config::Config::new().statik(false).probe("alsa") { + Err(pkg_config::Error::Failure { command, output }) => panic!( + "Pkg-config failed - usually this is because alsa development headers are not installed.\n\n\ + For Fedora users:\n# dnf install alsa-lib-devel\n\n\ + For Debian/Ubuntu users:\n# apt-get install libasound2-dev\n\n\ + pkg_config details:\n{}\n", pkg_config::Error::Failure { command, output }), + Err(e) => panic!("{}", e), + Ok(alsa_library) => { + for lib in alsa_library.libs { + println!("cargo:rustc-link-lib={}", lib); + } + } + }; + } + #[cfg(not(feature = "alsa"))] + config.define("RTMIDI_API_ALSA", "OFF"); + + #[cfg(feature = "jack_linux")] + { + config.define("RTMIDI_API_JACK", "ON"); + + match pkg_config::Config::new().statik(false).probe("jack") { + Err(pkg_config::Error::Failure { command, output }) => panic!( + "Pkg-config failed - usually this is because jack development headers are not installed.\n\n\ + For Debian/Ubuntu users:\n# apt-get install libjack-dev\n\n\ + pkg_config details:\n{}\n", pkg_config::Error::Failure { command, output }), + Err(e) => panic!("{}", e), + Ok(jack_library) => { + for lib in jack_library.libs { + println!("cargo:rustc-link-lib={}", lib); + } + } + }; + } + #[cfg(not(feature = "jack_linux"))] + config.define("RTMIDI_API_JACK", "OFF"); + } + + #[cfg(target_os = "macos")] + { + println!("cargo:rustc-link-lib=dylib=c++"); - let (version, include_args) = match pkg_config::Config::new() - .statik(false) - .atleast_version("3.0.0") - .probe("rtmidi") + #[cfg(feature = "coreaudio")] + { + config.define("RTMIDI_API_CORE", "ON"); + + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=CoreAudio"); + } + #[cfg(not(feature = "coreaudio"))] + config.define("RTMIDI_API_CORE", "OFF"); + + // TODO: Jack support on MacOS + // How do you install and link the Jack library files? + config.define("RTMIDI_API_JACK", "OFF"); + /* + #[cfg(feature = "jack_macos")] + config.define("RTMIDI_API_JACK", "ON"); + #[cfg(not(feature = "jack_macos"))] + config.define("RTMIDI_API_JACK", "OFF"); + */ + } + + #[cfg(target_os = "windows")] { - Err(_) => ("4.0.0".to_string(), vec![]), - Ok(library) => ( - library.version, - library - .include_paths - .iter() - .map(|include_path| { - format!( - "-I{}", - include_path.to_str().expect("include path was not UTF-8") - ) - }) - .collect::>(), - ), - }; - - let feature = match version.as_ref() { - "4.0.0" => "v4_0_0", - "3.0.0" => "v3_0_0", - version => panic!("Unsupported RtMidi version '{}'", version), - }; - println!("cargo:rustc-cfg=rtmidi_version=\"{}\"", feature); - - let bindings = bindgen::Builder::default() - .header("wrapper.h") - .clang_args(include_args) - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .generate() - .expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); + println!("cargo:rustc-link-lib=winmm"); + println!("cargo:rustc-link-lib=ole32"); + println!("cargo:rustc-link-lib=user32"); + + #[cfg(feature = "winmm")] + config.define("RTMIDI_API_WINMM", "ON"); + #[cfg(not(feature = "winmm"))] + config.define("RTMIDI_API_WINMM", "OFF"); + + // https://github.com/rust-lang/rust/issues/39016 + config.profile("Release"); + } + + let dst = config.build(); + + // Sometimes the path can be called lib64 + let libdir_path = ["lib", "lib64"] + .iter() + .map(|dir| dst.clone().join(dir)) + .find(|path| path.exists()) + .unwrap_or_else(|| { + panic!( + "Could not find rtmidi static lib path. Check `target/debug/build/rtmidi-sys-*/out` for a lib or lib64 folder." + ); + }); + + // Tell cargo to link to the compiled library. + println!( + "cargo:rustc-link-search=native={}", + libdir_path.to_str().unwrap() + ); + + println!("cargo:rustc-link-lib=static=rtmidi"); } diff --git a/rtmidi b/rtmidi new file mode 160000 index 0000000..24b3a3b --- /dev/null +++ b/rtmidi @@ -0,0 +1 @@ +Subproject commit 24b3a3bf99ddbeec2288629155c511b65f937049 diff --git a/src/api.rs b/src/api.rs index 56b86e0..5884b09 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,26 +4,26 @@ use std::fmt; use crate::ffi; /// MIDI API specifier -#[repr(u32)] +#[repr(i32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RtMidiApi { - Unspecified = ffi::RtMidiApi_RTMIDI_API_UNSPECIFIED, - MacOSXCore = ffi::RtMidiApi_RTMIDI_API_MACOSX_CORE, - LinuxALSA = ffi::RtMidiApi_RTMIDI_API_LINUX_ALSA, - UnixJack = ffi::RtMidiApi_RTMIDI_API_UNIX_JACK, - WindowsMM = ffi::RtMidiApi_RTMIDI_API_WINDOWS_MM, - RtMidiDummy = ffi::RtMidiApi_RTMIDI_API_RTMIDI_DUMMY, + Unspecified = ffi::RTMIDI_API_UNSPECIFIED, + MacOSXCore = ffi::RTMIDI_API_MACOSX_CORE, + LinuxALSA = ffi::RTMIDI_API_LINUX_ALSA, + UnixJack = ffi::RTMIDI_API_UNIX_JACK, + WindowsMM = ffi::RTMIDI_API_WINDOWS_MM, + RtMidiDummy = ffi::RTMIDI_API_RTMIDI_DUMMY, } -impl From for RtMidiApi { - fn from(api: u32) -> Self { +impl From for RtMidiApi { + fn from(api: i32) -> Self { match api { - ffi::RtMidiApi_RTMIDI_API_UNSPECIFIED => RtMidiApi::Unspecified, - ffi::RtMidiApi_RTMIDI_API_MACOSX_CORE => RtMidiApi::MacOSXCore, - ffi::RtMidiApi_RTMIDI_API_LINUX_ALSA => RtMidiApi::LinuxALSA, - ffi::RtMidiApi_RTMIDI_API_UNIX_JACK => RtMidiApi::UnixJack, - ffi::RtMidiApi_RTMIDI_API_WINDOWS_MM => RtMidiApi::WindowsMM, - ffi::RtMidiApi_RTMIDI_API_RTMIDI_DUMMY => RtMidiApi::RtMidiDummy, + ffi::RTMIDI_API_UNSPECIFIED => RtMidiApi::Unspecified, + ffi::RTMIDI_API_MACOSX_CORE => RtMidiApi::MacOSXCore, + ffi::RTMIDI_API_LINUX_ALSA => RtMidiApi::LinuxALSA, + ffi::RTMIDI_API_UNIX_JACK => RtMidiApi::UnixJack, + ffi::RTMIDI_API_WINDOWS_MM => RtMidiApi::WindowsMM, + ffi::RTMIDI_API_RTMIDI_DUMMY => RtMidiApi::RtMidiDummy, _ => panic!("Invalid API value"), } } @@ -31,7 +31,7 @@ impl From for RtMidiApi { impl fmt::Display for RtMidiApi { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let display_name = unsafe { CStr::from_ptr(ffi::rtmidi_api_display_name(*self as u32)) }; + let display_name = unsafe { CStr::from_ptr(ffi::rtmidi_api_display_name(*self as i32)) }; write!(f, "{}", display_name.to_str().map_err(|_| fmt::Error)?) } } diff --git a/src/bindings.rs b/src/bindings.rs new file mode 100644 index 0000000..34eabc2 --- /dev/null +++ b/src/bindings.rs @@ -0,0 +1,202 @@ +use std::os::raw::{c_char, c_int, c_uchar, c_uint}; + +#[doc = "! \\brief Wraps an RtMidi object for C function return statuses."] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RtMidiWrapper { + #[doc = "! The wrapped RtMidi object."] + pub ptr: *mut c_void, + pub data: *mut c_void, + #[doc = "! True when the last function call was OK."] + pub ok: bool, + #[doc = "! If an error occurred (ok != true), set to an error message."] + pub msg: *const c_char, +} + +#[doc = "! \\brief Typedef for a generic RtMidi pointer."] +pub type RtMidiPtr = *mut RtMidiWrapper; + +#[doc = "! \\brief Typedef for a generic RtMidiIn pointer."] +pub type RtMidiInPtr = *mut RtMidiWrapper; + +#[doc = "! \\brief Typedef for a generic RtMidiOut pointer."] +pub type RtMidiOutPtr = *mut RtMidiWrapper; + +#[doc = "< Search for a working compiled API."] +pub const RTMIDI_API_UNSPECIFIED: RtMidiApi = 0; + +#[doc = "< Macintosh OS-X CoreMIDI API."] +pub const RTMIDI_API_MACOSX_CORE: RtMidiApi = 1; + +#[doc = "< The Advanced Linux Sound Architecture API."] +pub const RTMIDI_API_LINUX_ALSA: RtMidiApi = 2; + +#[doc = "< The Jack Low-Latency MIDI Server API."] +pub const RTMIDI_API_UNIX_JACK: RtMidiApi = 3; + +#[doc = "< The Microsoft Multimedia MIDI API."] +pub const RTMIDI_API_WINDOWS_MM: RtMidiApi = 4; + +#[doc = "< A compilable but non-functional API."] +pub const RTMIDI_API_RTMIDI_DUMMY: RtMidiApi = 5; + +#[doc = "< W3C Web MIDI API."] +pub const RTMIDI_API_WEB_MIDI_API: RtMidiApi = 6; + +#[doc = "< The Microsoft Universal Windows Platform MIDI API."] +pub const RTMIDI_API_WINDOWS_UWP: RtMidiApi = 7; + +#[doc = "< The Android MIDI API."] +pub const RTMIDI_API_ANDROID: RtMidiApi = 8; + +#[doc = "< Number of values in this enum."] +pub const RTMIDI_API_NUM: RtMidiApi = 9; + +#[doc = "! \\brief MIDI API specifier arguments. See \\ref RtMidi::Api."] +pub type RtMidiApi = c_int; + +#[doc = "< A non-critical error."] +pub const RTMIDI_ERROR_WARNING: RtMidiErrorType = 0; + +#[doc = "< A non-critical error which might be useful for debugging."] +pub const RTMIDI_ERROR_DEBUG_WARNING: RtMidiErrorType = 1; + +#[doc = "< The default, unspecified error type."] +pub const RTMIDI_ERROR_UNSPECIFIED: RtMidiErrorType = 2; + +#[doc = "< No devices found on system."] +pub const RTMIDI_ERROR_NO_DEVICES_FOUND: RtMidiErrorType = 3; + +#[doc = "< An invalid device ID was specified."] +pub const RTMIDI_ERROR_INVALID_DEVICE: RtMidiErrorType = 4; + +#[doc = "< An error occurred during memory allocation."] +pub const RTMIDI_ERROR_MEMORY_ERROR: RtMidiErrorType = 5; + +#[doc = "< An invalid parameter was specified to a function."] +pub const RTMIDI_ERROR_INVALID_PARAMETER: RtMidiErrorType = 6; + +#[doc = "< The function was called incorrectly."] +pub const RTMIDI_ERROR_INVALID_USE: RtMidiErrorType = 7; + +#[doc = "< A system driver error occurred."] +pub const RTMIDI_ERROR_DRIVER_ERROR: RtMidiErrorType = 8; + +#[doc = "< A system error occurred."] +pub const RTMIDI_ERROR_SYSTEM_ERROR: RtMidiErrorType = 9; + +#[doc = "< A thread error occurred."] +pub const RTMIDI_ERROR_THREAD_ERROR: RtMidiErrorType = 10; + +#[doc = "! \\brief Defined RtMidiError types. See \\ref RtMidiError::Type."] +pub type RtMidiErrorType = c_int; + +#[doc = " \\brief The type of a RtMidi callback function.\n\n \\param timeStamp The time at which the message has been received.\n \\param message The midi message.\n \\param userData Additional user data for the callback.\n\n See \\ref RtMidiIn::RtMidiCallback."] +pub type RtMidiCCallback = ::std::option::Option< + unsafe extern "C" fn( + timeStamp: f64, + message: *const c_uchar, + messageSize: usize, + userData: *mut c_void, + ), +>; + +unsafe extern "C" { + #[doc = " \\brief Return the current RtMidi version.\n See \\ref RtMidi::getVersion()."] + pub fn rtmidi_get_version() -> *const c_char; + + #[doc = " \\brief Determine the available compiled MIDI APIs.\n\n If the given `apis` parameter is null, returns the number of available APIs.\n Otherwise, fill the given apis array with the RtMidi::Api values.\n\n \\param apis An array or a null value.\n \\param apis_size Number of elements pointed to by apis\n \\return number of items needed for apis array if apis==NULL, or\n number of items written to apis array otherwise. A negative\n return value indicates an error.\n\n See \\ref RtMidi::getCompiledApi()."] + pub fn rtmidi_get_compiled_api(apis: *mut RtMidiApi, apis_size: c_uint) -> c_int; + + #[doc = "! \\brief Return the name of a specified compiled MIDI API.\n! See \\ref RtMidi::getApiName()."] + pub fn rtmidi_api_name(api: RtMidiApi) -> *const c_char; + + #[doc = "! \\brief Return the display name of a specified compiled MIDI API.\n! See \\ref RtMidi::getApiDisplayName()."] + pub fn rtmidi_api_display_name(api: RtMidiApi) -> *const c_char; + + #[doc = "! \\brief Return the compiled MIDI API having the given name.\n! See \\ref RtMidi::getCompiledApiByName()."] + pub fn rtmidi_compiled_api_by_name(name: *const c_char) -> RtMidiApi; + + #[doc = "! \\internal Report an error."] + pub fn rtmidi_error(type_: RtMidiErrorType, errorString: *const c_char); + + #[doc = " \\brief Open a MIDI port.\n\n \\param port Must be greater than 0\n \\param portName Name for the application port.\n\n See RtMidi::openPort()."] + pub fn rtmidi_open_port(device: RtMidiPtr, portNumber: c_uint, portName: *const c_char); + + #[doc = " \\brief Creates a virtual MIDI port to which other software applications can\n connect.\n\n \\param portName Name for the application port.\n\n See RtMidi::openVirtualPort()."] + pub fn rtmidi_open_virtual_port(device: RtMidiPtr, portName: *const c_char); + + #[doc = " \\brief Close a MIDI connection.\n See RtMidi::closePort()."] + pub fn rtmidi_close_port(device: RtMidiPtr); + + #[doc = " \\brief Return the number of available MIDI ports.\n See RtMidi::getPortCount()."] + pub fn rtmidi_get_port_count(device: RtMidiPtr) -> c_uint; + + #[doc = " \\brief Access a string identifier for the specified MIDI input port number.\n\n To prevent memory leaks a char buffer must be passed to this function.\n NULL can be passed as bufOut parameter, and that will write the required buffer length in the bufLen.\n\n See RtMidi::getPortName()."] + pub fn rtmidi_get_port_name( + device: RtMidiPtr, + portNumber: c_uint, + bufOut: *mut c_char, + bufLen: *mut c_int, + ) -> c_int; + + #[doc = "! \\brief Create a default RtMidiInPtr value, with no initialization."] + pub fn rtmidi_in_create_default() -> RtMidiInPtr; + + #[doc = " \\brief Create a RtMidiInPtr value, with given api, clientName and queueSizeLimit.\n\n \\param api An optional API id can be specified.\n \\param clientName An optional client name can be specified. This\n will be used to group the ports that are created\n by the application.\n \\param queueSizeLimit An optional size of the MIDI input queue can be\n specified.\n\n See RtMidiIn::RtMidiIn()."] + pub fn rtmidi_in_create( + api: RtMidiApi, + clientName: *const c_char, + queueSizeLimit: c_uint, + ) -> RtMidiInPtr; + + #[doc = "! \\brief Free the given RtMidiInPtr."] + pub fn rtmidi_in_free(device: RtMidiInPtr); + + #[doc = "! \\brief Returns the MIDI API specifier for the given instance of RtMidiIn.\n! See \\ref RtMidiIn::getCurrentApi()."] + pub fn rtmidi_in_get_current_api(device: RtMidiPtr) -> RtMidiApi; + + #[doc = "! \\brief Set a callback function to be invoked for incoming MIDI messages.\n! See \\ref RtMidiIn::setCallback()."] + pub fn rtmidi_in_set_callback( + device: RtMidiInPtr, + callback: RtMidiCCallback, + userData: *mut c_void, + ); + + #[doc = "! \\brief Cancel use of the current callback function (if one exists).\n! See \\ref RtMidiIn::cancelCallback()."] + pub fn rtmidi_in_cancel_callback(device: RtMidiInPtr); + + #[doc = "! \\brief Specify whether certain MIDI message types should be queued or ignored during input.\n! See \\ref RtMidiIn::ignoreTypes()."] + pub fn rtmidi_in_ignore_types( + device: RtMidiInPtr, + midiSysex: bool, + midiTime: bool, + midiSense: bool, + ); + + #[doc = " Fill the user-provided array with the data bytes for the next available\n MIDI message in the input queue and return the event delta-time in seconds.\n\n \\param message Must point to a char* that is already allocated.\n SYSEX messages maximum size being 1024, a statically\n allocated array could\n be sufficient.\n \\param size Is used to return the size of the message obtained.\n Must be set to the size of \\ref message when calling.\n\n See RtMidiIn::getMessage()."] + pub fn rtmidi_in_get_message( + device: RtMidiInPtr, + message: *mut c_uchar, + size: *mut usize, + ) -> f64; + + #[doc = "! \\brief Create a default RtMidiInPtr value, with no initialization."] + pub fn rtmidi_out_create_default() -> RtMidiOutPtr; + + #[doc = " \\brief Create a RtMidiOutPtr value, with given and clientName.\n\n \\param api An optional API id can be specified.\n \\param clientName An optional client name can be specified. This\n will be used to group the ports that are created\n by the application.\n\n See RtMidiOut::RtMidiOut()."] + pub fn rtmidi_out_create(api: RtMidiApi, clientName: *const c_char) -> RtMidiOutPtr; + + #[doc = "! \\brief Free the given RtMidiOutPtr."] + pub fn rtmidi_out_free(device: RtMidiOutPtr); + + #[doc = "! \\brief Returns the MIDI API specifier for the given instance of RtMidiOut.\n! See \\ref RtMidiOut::getCurrentApi()."] + pub fn rtmidi_out_get_current_api(device: RtMidiPtr) -> RtMidiApi; + + #[doc = "! \\brief Immediately send a single message out an open MIDI output port.\n! See \\ref RtMidiOut::sendMessage()."] + pub fn rtmidi_out_send_message( + device: RtMidiOutPtr, + message: *const c_uchar, + length: c_int, + ) -> c_int; +} diff --git a/src/ffi.rs b/src/ffi.rs index 517b0a9..fe7668e 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -3,23 +3,22 @@ #![allow(non_snake_case)] #![allow(dead_code)] -#[cfg(rtmidi_version = "v4_0_0")] mod lib { use std::ffi::c_void; use std::slice; - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + include!("bindings.rs"); pub fn create_callback( f: F, ) -> ( - unsafe extern "C" fn(f64, *const u8, u64, *mut c_void), + unsafe extern "C" fn(f64, *const u8, usize, *mut c_void), *mut F, ) { unsafe extern "C" fn trampoline( timestamp: f64, data: *const u8, - size: u64, + size: usize, func: *mut c_void, ) { let messages = slice::from_raw_parts(data, size as usize); @@ -29,51 +28,4 @@ mod lib { } } -#[cfg(rtmidi_version = "v4_0_0")] pub use lib::*; - -#[cfg(rtmidi_version = "v3_0_0")] -mod lib { - use std::ffi::c_void; - use std::os::raw::{c_char, c_uchar}; - use std::ptr; - use std::slice; - - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - - pub const RtMidiApi_RTMIDI_API_UNSPECIFIED: RtMidiApi = RtMidiApi_RT_MIDI_API_UNSPECIFIED; - pub const RtMidiApi_RTMIDI_API_MACOSX_CORE: RtMidiApi = RtMidiApi_RT_MIDI_API_MACOSX_CORE; - pub const RtMidiApi_RTMIDI_API_LINUX_ALSA: RtMidiApi = RtMidiApi_RT_MIDI_API_LINUX_ALSA; - pub const RtMidiApi_RTMIDI_API_UNIX_JACK: RtMidiApi = RtMidiApi_RT_MIDI_API_UNIX_JACK; - pub const RtMidiApi_RTMIDI_API_WINDOWS_MM: RtMidiApi = RtMidiApi_RT_MIDI_API_WINDOWS_MM; - pub const RtMidiApi_RTMIDI_API_RTMIDI_DUMMY: RtMidiApi = RtMidiApi_RT_MIDI_API_RTMIDI_DUMMY; - - pub fn rtmidi_api_display_name(_api: u32) -> *const c_char { - ptr::null() - } - - pub fn create_callback( - f: F, - ) -> (unsafe extern "C" fn(f64, *const u8, *mut c_void), *mut F) { - unsafe extern "C" fn trampoline( - timestamp: f64, - data: *const u8, - func: *mut c_void, - ) { - let messages = slice::from_raw_parts(data, 3); - (*(func as *mut F))(timestamp, messages) - } - (trampoline::, Box::into_raw(Box::new(f))) - } - - pub unsafe fn wrap_rtmidi_in_get_message( - device: RtMidiInPtr, - mut message: *mut c_uchar, - size: *mut size_t, - ) -> f64 { - rtmidi_in_get_message(device, &mut message, size) - } -} - -#[cfg(rtmidi_version = "v3_0_0")] -pub use lib::{wrap_rtmidi_in_get_message as rtmidi_in_get_message, *}; diff --git a/src/midi.rs b/src/midi.rs index dc35caa..3d62a8a 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -45,14 +45,39 @@ pub fn port_count(ptr: *mut ffi::RtMidiWrapper) -> Result( ptr: *mut ffi::RtMidiWrapper, port_number: RtMidiPort, -) -> Result<&'a str, RtMidiError> { - let port_name = unsafe { ffi::rtmidi_get_port_name(ptr, port_number) }; - match unsafe { Result::<(), RtMidiError>::from(*ptr) } { - Ok(_) if port_name.is_null() => Err(RtMidiError::NullPointer), - Ok(_) => { - let port_name = unsafe { CStr::from_ptr(port_name) }; - Ok(port_name.to_str()?) - } - Err(e) => Err(e), +) -> Result { + let mut buffer_len: i32 = 0; + let size_res = unsafe { + ffi::rtmidi_get_port_name(ptr, port_number, std::ptr::null_mut(), &mut buffer_len) + }; + + if size_res != 0 || buffer_len <= 0 { + return Err(RtMidiError::Error( + "Error getting port name length".to_string(), + )); + } + + let mut buffer = vec![0u8; buffer_len as usize]; + + let res = unsafe { + ffi::rtmidi_get_port_name( + ptr, + port_number, + buffer.as_mut_ptr() as *mut i8, + &mut buffer_len, + ) + }; + + if res != buffer_len - 1 { + return Err(RtMidiError::Error(format!( + "Error getting port name: {}", + res + ))); + } + + let c_str = unsafe { CStr::from_bytes_with_nul_unchecked(&buffer) }; + match c_str.to_str() { + Ok(s) => Ok(s.to_owned()), + Err(e) => Err(RtMidiError::Utf8(e)), } } diff --git a/src/midi_in.rs b/src/midi_in.rs index ac61b07..6b68745 100644 --- a/src/midi_in.rs +++ b/src/midi_in.rs @@ -80,7 +80,7 @@ impl RtMidiIn { pub fn new(args: RtMidiInArgs) -> Result { let client_name = CString::new(args.client_name)?; let ptr = unsafe { - ffi::rtmidi_in_create(args.api as u32, client_name.as_ptr(), args.queue_size_limit) + ffi::rtmidi_in_create(args.api as i32, client_name.as_ptr(), args.queue_size_limit) }; match unsafe { Result::<(), RtMidiError>::from(*ptr) } { Ok(_) => Ok(RtMidiIn(ptr)), @@ -124,7 +124,7 @@ impl RtMidiIn { } /// Return a string identifier for the specified MIDI input port number - pub fn port_name(&self, port_number: RtMidiPort) -> Result<&str, RtMidiError> { + pub fn port_name(&self, port_number: RtMidiPort) -> Result { midi::port_name(self.0, port_number) } @@ -179,7 +179,7 @@ impl RtMidiIn { /// message is indicated by a non-zero vector size. An exception is thrown if an error occurs /// during message retrieval or an input connection was not previously established. pub fn message(&self) -> Result<(f64, Vec), RtMidiError> { - let mut length = 0u64; + let mut length = 0usize; let mut message = Vec::with_capacity(1024); let ptr = message.as_mut_ptr(); let timestamp = unsafe { ffi::rtmidi_in_get_message(self.0, ptr, &mut length) }; diff --git a/src/midi_out.rs b/src/midi_out.rs index af2d873..d8816fb 100644 --- a/src/midi_out.rs +++ b/src/midi_out.rs @@ -67,7 +67,7 @@ impl RtMidiOut { /// order of use is ALSA, JACK (Linux) and CORE, JACK (macOS). pub fn new(args: RtMidiOutArgs) -> Result { let client_name = CString::new(args.client_name)?; - let ptr = unsafe { ffi::rtmidi_out_create(args.api as u32, client_name.as_ptr()) }; + let ptr = unsafe { ffi::rtmidi_out_create(args.api as i32, client_name.as_ptr()) }; match unsafe { Result::<(), RtMidiError>::from(*ptr) } { Ok(_) => Ok(RtMidiOut(ptr)), Err(e) => Err(e), @@ -111,7 +111,7 @@ impl RtMidiOut { } /// Return a string identifier for the specified MIDI output port number - pub fn port_name(&self, port_number: RtMidiPort) -> Result<&str, RtMidiError> { + pub fn port_name(&self, port_number: RtMidiPort) -> Result { midi::port_name(self.0, port_number) } diff --git a/wrapper.h b/wrapper.h deleted file mode 100644 index d9cb291..0000000 --- a/wrapper.h +++ /dev/null @@ -1,3 +0,0 @@ -typedef struct RtMidiWrapper RtMidiWrapper; - -#include \ No newline at end of file From ba5afaf5feaffbca36ef84b7a0a8c1a4098c3f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Bir=C3=B3?= Date: Fri, 3 Jan 2025 21:30:02 +0200 Subject: [PATCH 2/3] Add missing features --- Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 02ece99..7d14530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,13 @@ keywords = ["midi", "audio", "music", "sound"] categories = ["multimedia::audio", "api-bindings"] license = "MIT" +[features] +default = ["coreaudio", "alsa", "winmm"] +coreaudio = [] +alsa = [] +jack_linux = [] +winmm = [] + [build-dependencies] cmake = "0.1" From cdbe4eadb9c53c112dc13031d85efef1935e8f7b Mon Sep 17 00:00:00 2001 From: Dani Date: Fri, 10 Jan 2025 16:14:15 +0200 Subject: [PATCH 3/3] Fix MacOS build --- Cargo.toml | 4 ++-- build.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7d14530..51b095f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ categories = ["multimedia::audio", "api-bindings"] license = "MIT" [features] -default = ["coreaudio", "alsa", "winmm"] -coreaudio = [] +default = ["coremidi", "alsa", "winmm"] +coremidi = [] alsa = [] jack_linux = [] winmm = [] diff --git a/build.rs b/build.rs index e392a9a..263e3c4 100644 --- a/build.rs +++ b/build.rs @@ -59,14 +59,14 @@ fn main() { { println!("cargo:rustc-link-lib=dylib=c++"); - #[cfg(feature = "coreaudio")] + #[cfg(feature = "coremidi")] { config.define("RTMIDI_API_CORE", "ON"); println!("cargo:rustc-link-lib=framework=CoreFoundation"); - println!("cargo:rustc-link-lib=framework=CoreAudio"); + println!("cargo:rustc-link-lib=framework=CoreMidi"); } - #[cfg(not(feature = "coreaudio"))] + #[cfg(not(feature = "coremidi"))] config.define("RTMIDI_API_CORE", "OFF"); // TODO: Jack support on MacOS