From 5e7b446a6bf176b8c9634b6ffcbbd4c01bfbf16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Qui=C3=B1ones?= Date: Wed, 6 Sep 2023 14:20:04 +0200 Subject: [PATCH] Updated to RtMidi v6.0 --- RtMidi.Net/Enums/MidiApi.cs | 4 +- RtMidi.Net/InteropServices/RtMidiBase.cs | 11 + RtMidi.Net/InteropServices/RtMidiInterop.cs | 531 ++++++++++---------- RtMidi.Net/MidiManager.cs | 2 +- RtMidi.Net/RtMidi.Net.csproj | 7 +- WorkerTest/Worker.cs | 12 +- WorkerTest/WorkerTest.csproj | 2 + 7 files changed, 297 insertions(+), 272 deletions(-) diff --git a/RtMidi.Net/Enums/MidiApi.cs b/RtMidi.Net/Enums/MidiApi.cs index 6a255d3..9b89f62 100644 --- a/RtMidi.Net/Enums/MidiApi.cs +++ b/RtMidi.Net/Enums/MidiApi.cs @@ -7,6 +7,8 @@ public enum MidiApi : byte LinuxAlsa, UnixJack, WindowsMultimediaMidi, - WindowsKernelStreaming, RtMidiDummy, + WebMidi, + WindowsUwp, + Android, } \ No newline at end of file diff --git a/RtMidi.Net/InteropServices/RtMidiBase.cs b/RtMidi.Net/InteropServices/RtMidiBase.cs index d0e0977..75fe21c 100644 --- a/RtMidi.Net/InteropServices/RtMidiBase.cs +++ b/RtMidi.Net/InteropServices/RtMidiBase.cs @@ -203,6 +203,17 @@ public static string GetApiDisplayName(MidiApi api) return name ?? string.Empty; } + /// + /// Return the version of the RtMidi library. + /// + /// The version of the RtMidi library + public static string GetRtMidiVersion() + { + var ptr = RtMidiInterop.rtmidi_get_version(); + var name = Marshal.PtrToStringUTF8(ptr); + return name ?? string.Empty; + } + /// /// Return the compiled MIDI API having the given name. /// diff --git a/RtMidi.Net/InteropServices/RtMidiInterop.cs b/RtMidi.Net/InteropServices/RtMidiInterop.cs index d0e381d..23bdc95 100644 --- a/RtMidi.Net/InteropServices/RtMidiInterop.cs +++ b/RtMidi.Net/InteropServices/RtMidiInterop.cs @@ -1,5 +1,5 @@ -using System.Runtime.InteropServices; -using RtMidi.Net.Enums; +using RtMidi.Net.Enums; +using System.Runtime.InteropServices; namespace RtMidi.Net.InteropServices; @@ -7,264 +7,271 @@ namespace RtMidi.Net.InteropServices; internal static class RtMidiInterop { - private const string RtMidiLibrary = "rtmidi"; - - #region RtMidi - - /// - /// Determine the available compiled MIDI APIs. - /// - /// If the given `apis` parameter is null, returns the number of available APIs. - /// Otherwise, fill the given apis array with the values. - /// - /// - /// An array or a null value. - /// Number of elements pointed to by apis - /// - /// Number of items needed for apis array if apis==NULL, or - /// number of items written to apis array otherwise. A negative - /// return value indicates an error. - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int rtmidi_get_compiled_api (IntPtr apis, uint apisSize); - - /// - /// Return the name of a specified compiled MIDI API. - /// - /// This obtains a short lower-case name used for identification purposes. - /// This value is guaranteed to remain identical across library versions. - /// - /// - /// The API - /// - /// The API name - /// If the API is unknown, this function will return the empty string. - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr rtmidi_api_name (MidiApi api); - - /// - /// Return the display name of a specified compiled MIDI API. - /// - /// This obtains a long name used for display purposes. - /// - /// - /// The API - /// - /// The API display name - /// If the API is unknown, this function will return the empty string. - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr rtmidi_api_display_name (MidiApi api); - - /// - /// Return the compiled MIDI API having the given name. - /// - /// A case insensitive comparison will check the specified name - /// against the list of compiled APIs. - /// - /// - /// - /// - /// Return the one which matches. - /// On failure, the function returns . - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern MidiApi rtmidi_compiled_api_by_name (string name); - - /// - /// Open a MIDI port. - /// - /// Pointer to object - /// Must be greater than 0 - /// Name for the application port. - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern void rtmidi_open_port (RtMidiPtr rtMidiObject, uint portNumber, string portName); - - /// - /// Creates a virtual MIDI port to which other software applications can connect. - /// - /// Pointer to object - /// Name for the application port. - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern void rtmidi_open_virtual_port (RtMidiPtr rtMidiObject, string portName); - - /// - /// Close a MIDI connection. - /// - /// Pointer to object - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_close_port (RtMidiPtr rtMidiObject); - - /// - /// Return the number of available MIDI ports. - /// - /// Pointer to object - /// This function returns the number of MIDI ports of the selected API. - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint rtmidi_get_port_count (RtMidiPtr rtMidiObject); - - /// - /// Access a string identifier for the specified MIDI port number. - /// - /// To prevent memory leaks a char buffer must be passed to this function. - /// NULL can be passed as bufOut parameter, and that will write the required buffer length in the bufLen. - /// - /// - /// Pointer to object - /// MIDI port number - /// OUT value with the name - /// OUT value with the name size - /// - /// The name of the port with the given Id is returned. - /// - /// An empty string is returned if an invalid port specifier - /// is provided. User code should assume a UTF-8 encoding. - /// - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int rtmidi_get_port_name (RtMidiPtr rtMidiObject, uint portNumber, IntPtr bufOut, ref uint bufLen); - - #endregion - - #region RtMidiIn - - /// - /// Create a default RtMidiInPtr value, with no initialization. - /// - /// The RtMidiPtr created - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern RtMidiPtr rtmidi_in_create_default (); - - /// - /// Create a RtMidiInPtr value, with given api, clientName and queueSizeLimit. - /// - /// An optional API id can be specified. - /// - /// An optional client name can be specified. This - /// will be used to group the ports that are created - /// by the application. - /// - /// An optional size of the MIDI input queue can be - /// specified. - /// The RtMidiPtr created - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern RtMidiPtr rtmidi_in_create (MidiApi api, string clientName, uint queueSizeLimit); - - /// - /// Free the given RtMidiInPtr - /// - /// Pointer to object - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_in_free (RtMidiPtr rtMidiObject); - - /// - /// Returns the MIDI API specifier for the given instance of RtMidiIn. - /// - /// Pointer to object - /// The value - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern MidiApi rtmidi_in_get_current_api (RtMidiPtr rtMidiObject); - - /// - /// Set a callback function to be invoked for incoming MIDI messages. - /// - /// Pointer to object - /// A callback function must be given. - /// Optionally, a pointer to additional data can be - /// passed to the callback function whenever it is called. - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_in_set_callback (RtMidiPtr rtMidiObject, RtMidiCallback callback, byte[]? userData); - - /// - /// Cancel use of the current callback function (if one exists). - /// - /// Pointer to object - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_in_cancel_callback (RtMidiPtr rtMidiObject); - - /// - /// Specify whether certain MIDI message types should be queued or ignored during input. - /// - /// By default, MIDI timing and active sensing messages are ignored - /// during message input because of their relative high data rates. - /// MIDI sysex messages are ignored by default as well. Variable - /// values of "true" imply that the respective message type will be - /// ignored. - /// - /// - /// Pointer to object - /// Ignore midi Sysex messages? - /// Ignore midi Time messages? - /// Ignore midi Sense messages? - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_in_ignore_types (RtMidiPtr rtMidiObject, bool midiSysex, bool midiTime, bool midiSense); - - /// - /// Fill the user-provided array with the data bytes for the next available - /// MIDI message in the input queue and return the event delta-time in seconds. - /// - /// Pointer to object - /// Must point to a char* that is already allocated. - /// SYSEX messages maximum size being 1024, a statically - /// allocated array could be sufficient. - /// Is used to return the size of the message obtained. - /// Must be set to the size of when calling. - /// Timestamp of messages - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern double rtmidi_in_get_message (RtMidiPtr rtMidiObject, IntPtr message, ref int size); - - #endregion - - #region RtMidiOut - - /// - /// Create a default RtMidiOutPtr value, with no initialization. - /// - /// The RtMidiOutPtr created - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern RtMidiPtr rtmidi_out_create_default (); - - /// - /// Create a RtMidiOutPtr value, with given and clientName. - /// - /// An optional API id can be specified. - /// An optional client name can be specified. This - /// will be used to group the ports that are created - /// by the application. - /// The RtMidiOutPtr created - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern RtMidiPtr rtmidi_out_create (MidiApi api, string clientName); - - /// - /// Free the given RtMidiOutPtr. - /// - /// Pointer to object - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void rtmidi_out_free (RtMidiPtr rtMidiObject); - - /// - /// Returns the MIDI API specifier for the given instance of RtMidiOut. - /// - /// Pointer to object - /// The value - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern MidiApi rtmidi_out_get_current_api (RtMidiPtr rtMidiObject); - - /// - /// Immediately send a single message out an open MIDI output port. - /// - /// - /// An exception is thrown if an error occurs during output or an - /// output connection was not previously established. - /// - /// Pointer to object - /// A pointer to the MIDI message as raw bytes - /// Length of the MIDI message in bytes - /// - [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int rtmidi_out_send_message (RtMidiPtr rtMidiObject, byte[] message, int length); - - #endregion - + private const string RtMidiLibrary = "rtmidi"; + + #region RtMidi + + /// + /// Determine the available compiled MIDI APIs. + /// + /// If the given `apis` parameter is null, returns the number of available APIs. + /// Otherwise, fill the given apis array with the values. + /// + /// + /// An array or a null value. + /// Number of elements pointed to by apis + /// + /// Number of items needed for apis array if apis==NULL, or + /// number of items written to apis array otherwise. A negative + /// return value indicates an error. + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int rtmidi_get_compiled_api(IntPtr apis, uint apisSize); + + /// + /// Return the name of a specified compiled MIDI API. + /// + /// This obtains a short lower-case name used for identification purposes. + /// This value is guaranteed to remain identical across library versions. + /// + /// + /// The API + /// + /// The API name + /// If the API is unknown, this function will return the empty string. + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rtmidi_api_name(MidiApi api); + + /// + /// Return the display name of a specified compiled MIDI API. + /// + /// This obtains a long name used for display purposes. + /// + /// + /// The API + /// + /// The API display name + /// If the API is unknown, this function will return the empty string. + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rtmidi_api_display_name(MidiApi api); + + /// + /// Return the current RtMidi version. + /// + /// RtMidi version. + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rtmidi_get_version(); + + /// + /// Return the compiled MIDI API having the given name. + /// + /// A case insensitive comparison will check the specified name + /// against the list of compiled APIs. + /// + /// + /// + /// + /// Return the one which matches. + /// On failure, the function returns . + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern MidiApi rtmidi_compiled_api_by_name(string name); + + /// + /// Open a MIDI port. + /// + /// Pointer to object + /// Must be greater than 0 + /// Name for the application port. + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern void rtmidi_open_port(RtMidiPtr rtMidiObject, uint portNumber, string portName); + + /// + /// Creates a virtual MIDI port to which other software applications can connect. + /// + /// Pointer to object + /// Name for the application port. + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern void rtmidi_open_virtual_port(RtMidiPtr rtMidiObject, string portName); + + /// + /// Close a MIDI connection. + /// + /// Pointer to object + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_close_port(RtMidiPtr rtMidiObject); + + /// + /// Return the number of available MIDI ports. + /// + /// Pointer to object + /// This function returns the number of MIDI ports of the selected API. + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint rtmidi_get_port_count(RtMidiPtr rtMidiObject); + + /// + /// Access a string identifier for the specified MIDI port number. + /// + /// To prevent memory leaks a char buffer must be passed to this function. + /// NULL can be passed as bufOut parameter, and that will write the required buffer length in the bufLen. + /// + /// + /// Pointer to object + /// MIDI port number + /// OUT value with the name + /// OUT value with the name size + /// + /// The name of the port with the given Id is returned. + /// + /// An empty string is returned if an invalid port specifier + /// is provided. User code should assume a UTF-8 encoding. + /// + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern int rtmidi_get_port_name(RtMidiPtr rtMidiObject, uint portNumber, IntPtr bufOut, ref uint bufLen); + + #endregion + + #region RtMidiIn + + /// + /// Create a default RtMidiInPtr value, with no initialization. + /// + /// The RtMidiPtr created + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern RtMidiPtr rtmidi_in_create_default(); + + /// + /// Create a RtMidiInPtr value, with given api, clientName and queueSizeLimit. + /// + /// An optional API id can be specified. + /// + /// An optional client name can be specified. This + /// will be used to group the ports that are created + /// by the application. + /// + /// An optional size of the MIDI input queue can be + /// specified. + /// The RtMidiPtr created + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern RtMidiPtr rtmidi_in_create(MidiApi api, string clientName, uint queueSizeLimit); + + /// + /// Free the given RtMidiInPtr + /// + /// Pointer to object + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_in_free(RtMidiPtr rtMidiObject); + + /// + /// Returns the MIDI API specifier for the given instance of RtMidiIn. + /// + /// Pointer to object + /// The value + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern MidiApi rtmidi_in_get_current_api(RtMidiPtr rtMidiObject); + + /// + /// Set a callback function to be invoked for incoming MIDI messages. + /// + /// Pointer to object + /// A callback function must be given. + /// Optionally, a pointer to additional data can be + /// passed to the callback function whenever it is called. + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_in_set_callback(RtMidiPtr rtMidiObject, RtMidiCallback callback, byte[]? userData); + + /// + /// Cancel use of the current callback function (if one exists). + /// + /// Pointer to object + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_in_cancel_callback(RtMidiPtr rtMidiObject); + + /// + /// Specify whether certain MIDI message types should be queued or ignored during input. + /// + /// By default, MIDI timing and active sensing messages are ignored + /// during message input because of their relative high data rates. + /// MIDI sysex messages are ignored by default as well. Variable + /// values of "true" imply that the respective message type will be + /// ignored. + /// + /// + /// Pointer to object + /// Ignore midi Sysex messages? + /// Ignore midi Time messages? + /// Ignore midi Sense messages? + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_in_ignore_types(RtMidiPtr rtMidiObject, bool midiSysex, bool midiTime, bool midiSense); + + /// + /// Fill the user-provided array with the data bytes for the next available + /// MIDI message in the input queue and return the event delta-time in seconds. + /// + /// Pointer to object + /// Must point to a char* that is already allocated. + /// SYSEX messages maximum size being 1024, a statically + /// allocated array could be sufficient. + /// Is used to return the size of the message obtained. + /// Must be set to the size of when calling. + /// Timestamp of messages + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern double rtmidi_in_get_message(RtMidiPtr rtMidiObject, IntPtr message, ref int size); + + #endregion + + #region RtMidiOut + + /// + /// Create a default RtMidiOutPtr value, with no initialization. + /// + /// The RtMidiOutPtr created + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern RtMidiPtr rtmidi_out_create_default(); + + /// + /// Create a RtMidiOutPtr value, with given and clientName. + /// + /// An optional API id can be specified. + /// An optional client name can be specified. This + /// will be used to group the ports that are created + /// by the application. + /// The RtMidiOutPtr created + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + internal static extern RtMidiPtr rtmidi_out_create(MidiApi api, string clientName); + + /// + /// Free the given RtMidiOutPtr. + /// + /// Pointer to object + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void rtmidi_out_free(RtMidiPtr rtMidiObject); + + /// + /// Returns the MIDI API specifier for the given instance of RtMidiOut. + /// + /// Pointer to object + /// The value + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern MidiApi rtmidi_out_get_current_api(RtMidiPtr rtMidiObject); + + /// + /// Immediately send a single message out an open MIDI output port. + /// + /// + /// An exception is thrown if an error occurs during output or an + /// output connection was not previously established. + /// + /// Pointer to object + /// A pointer to the MIDI message as raw bytes + /// Length of the MIDI message in bytes + /// + [DllImport(RtMidiLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int rtmidi_out_send_message(RtMidiPtr rtMidiObject, byte[] message, int length); + + #endregion + } \ No newline at end of file diff --git a/RtMidi.Net/MidiManager.cs b/RtMidi.Net/MidiManager.cs index 720cf01..6e60b65 100644 --- a/RtMidi.Net/MidiManager.cs +++ b/RtMidi.Net/MidiManager.cs @@ -7,8 +7,8 @@ public static class MidiManager { public static IReadOnlyCollection GetAvailableApis() => RtMidiBase.GetCompiledApi(); public static string GetApiName(MidiApi api) => RtMidiBase.GetApiName(api); - public static string GetApiDisplayName(MidiApi api) => RtMidiBase.GetApiDisplayName(api); + public static string GetRtMidiVersion() => RtMidiBase.GetRtMidiVersion(); public static List GetAvailableDevices() { diff --git a/RtMidi.Net/RtMidi.Net.csproj b/RtMidi.Net/RtMidi.Net.csproj index 8a94757..276064b 100644 --- a/RtMidi.Net/RtMidi.Net.csproj +++ b/RtMidi.Net/RtMidi.Net.csproj @@ -5,6 +5,7 @@ enable LICENSE.md + README.md https://github.com/Daniel127/RtMidi.Net https://github.com/Daniel127/RtMidi.Net git @@ -18,9 +19,7 @@ - - True - - + + diff --git a/WorkerTest/Worker.cs b/WorkerTest/Worker.cs index d9c2319..ba19229 100644 --- a/WorkerTest/Worker.cs +++ b/WorkerTest/Worker.cs @@ -23,13 +23,17 @@ public Worker(ILogger logger) public override async Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Starting"); + var rtMidiVersion = MidiManager.GetRtMidiVersion(); + _logger.LogInformation("RtMidi version: {rtMidiVersion}", rtMidiVersion); var devices = MidiManager.GetAvailableDevices(); - foreach (var d in devices) - { - _logger.LogInformation($"{d.Port}) {d.Name} - {d.Type}"); - } if (devices.Any()) { + _logger.LogInformation("Available devices:"); + foreach (var d in devices.OrderBy(info => info.Port)) + { + _logger.LogInformation("{port}) {name} - {type}", d.Port, d.Name, d.Type); + } + var devicePort = 1u; //Change device to test var device = MidiManager.GetDeviceInfo(devicePort, MidiDeviceType.Input); _midiInputClient = new MidiInputClient(device); diff --git a/WorkerTest/WorkerTest.csproj b/WorkerTest/WorkerTest.csproj index f8c5662..b513fa7 100644 --- a/WorkerTest/WorkerTest.csproj +++ b/WorkerTest/WorkerTest.csproj @@ -4,6 +4,8 @@ net7.0 enable enable + AnyCPU + linux-arm dotnet-WorkerTest-B41C8CE5-6C97-4BF3-90FB-7FB7452FA332