Skip to content

[CoreMidi] Add missing CoreMIDI bindings and MidiEventList support.#24738

Draft
rolfbjarne wants to merge 2 commits intomainfrom
dev/rolf/coremidi-copilot
Draft

[CoreMidi] Add missing CoreMIDI bindings and MidiEventList support.#24738
rolfbjarne wants to merge 2 commits intomainfrom
dev/rolf/coremidi-copilot

Conversation

@rolfbjarne
Copy link
Member

Bind the following missing CoreMIDI APIs:

  • MIDIDeviceCreate/MIDIDeviceDispose
  • MIDIExternalDeviceCreate
  • MIDISetupAddDevice/MIDISetupRemoveDevice
  • MIDISetupAddExternalDevice/MIDISetupRemoveExternalDevice
  • MIDIEntityAddOrRemoveEndpoints
  • MIDIDeviceRemoveEntity
  • MIDIEndPointGetRefCons/MIDIEndPointSetRefCons
  • MIDIDriverEnableMonitoring
  • MIDIGetDriverDeviceList/MIDIGetDriverIORunLoop
  • MIDISendSysex/MIDISendUMPSysex
  • MIDIDestinationCreateWithProtocol
  • MIDISourceCreateWithProtocol
  • MIDIInputPortCreateWithProtocol
  • MIDIClientCreateWithBlock
  • MIDIEventPacketSysexBytesForGroup
  • MIDI 2.0 structs (MIDI2DeviceManufacturer, MIDI2DeviceRevisionLevel,
    MIDICIProfileID, MIDISysexSendRequest, MIDISysexSendRequestUMP)
  • MidiDriver abstract class for implementing custom MIDI drivers

Add MidiEventList and MidiEventPacket classes for MIDI 2.0 Universal MIDI
Packet (UMP) support (MIDIEventList/MIDIEventPacket structs).

Add comprehensive tests including a Happy Birthday melody test.

There's a sample project in progress here: dotnet/macios-samples#10.

Fixes #4452
Fixes #12489

Bind the following missing CoreMIDI APIs:
- MIDIDeviceCreate/MIDIDeviceDispose
- MIDIExternalDeviceCreate
- MIDISetupAddDevice/MIDISetupRemoveDevice
- MIDISetupAddExternalDevice/MIDISetupRemoveExternalDevice
- MIDIEntityAddOrRemoveEndpoints
- MIDIDeviceRemoveEntity
- MIDIEndPointGetRefCons/MIDIEndPointSetRefCons
- MIDIDriverEnableMonitoring
- MIDIGetDriverDeviceList/MIDIGetDriverIORunLoop
- MIDISendSysex/MIDISendUMPSysex
- MIDIDestinationCreateWithProtocol
- MIDISourceCreateWithProtocol
- MIDIInputPortCreateWithProtocol
- MIDIClientCreateWithBlock
- MIDIEventPacketSysexBytesForGroup
- MIDI 2.0 structs (MIDI2DeviceManufacturer, MIDI2DeviceRevisionLevel,
  MIDICIProfileID, MIDISysexSendRequest, MIDISysexSendRequestUMP)
- MidiDriver abstract class for implementing custom MIDI drivers

Add MidiEventList and MidiEventPacket classes for MIDI 2.0 Universal MIDI
Packet (UMP) support (MIDIEventList/MIDIEventPacket structs).

Add comprehensive tests including a Happy Birthday melody test.

There's a sample project in progress here: dotnet/macios-samples#10.

Fixes #4452
Fixes #12489
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive CoreMIDI bindings for missing APIs and implements MIDI 2.0 Universal MIDI Packet (UMP) support, addressing issues #4452 and #12489. The changes enable developers to work with MIDI devices at a lower level, including custom driver implementation, and provide full support for the modern MIDI 2.0 protocol.

Changes:

  • Added MidiEventList and MidiEventPacket classes for MIDI 2.0 UMP support, enabling Universal MIDI Packet handling
  • Implemented missing CoreMIDI device/entity management APIs (Create, Dispose, AddOrRemoveEndpoints, etc.) and sysex sending functionality
  • Introduced experimental MidiDriver abstract class for implementing custom MIDI drivers with full lifecycle management

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tests/xtro-sharpie/api-annotations-dotnet/*.ignore Removed API ignore entries for newly bound CoreMIDI APIs across all platforms
tests/cecil-tests/Documentation.cs Added FunctionPointerType handling to return empty doc IDs for unmanaged function pointers
tests/cecil-tests/Documentation.KnownFailures.txt Removed known failures for newly documented CoreMIDI types and fixed function pointer doc IDs
tests/monotouch-test/CoreMidi/MidiEventPacketTest.cs Comprehensive unit tests for MidiEventPacket struct including bounds checking and data manipulation
tests/monotouch-test/CoreMidi/MidiEventListTest.cs Unit tests for MidiEventList covering construction, adding packets, iteration, and edge cases
tests/monotouch-test/CoreMidi/MidiEndpointTest.cs Added tests for GetRefCons/SetRefCons endpoint reference management APIs
tests/monotouch-test/CoreMidi/MidiDeviceTest.cs Tests for external device creation and MidiSetup add/remove operations
tests/monotouch-test/CoreMidi/MidiComprehensiveTest.cs Extensive integration tests including a complete "Happy Birthday" melody demonstration
src/frameworks.sources Registered new CoreMidi and CoreFoundation source files in the build system
src/coremidi.cs Added MidiDriverProperty enum for driver-specific properties
src/CoreMidi/MidiThruConnectionParams.cs Moved enum definitions outside TVOS conditional to fix tvOS build issues
src/CoreMidi/MidiStructs.cs Implemented MIDI 2.0 structs (Midi2DeviceManufacturer, Midi2DeviceRevisionLevel, MidiCIProfileId variants, MidiSysexSendRequest)
src/CoreMidi/MidiServices.cs Added device/entity creation, external device support, sysex async sending, and protocol-aware port/endpoint creation methods
src/CoreMidi/MidiEventPacket.cs Implemented MidiEventPacket struct with 64-word capacity, indexer, and GetSysexBytes method
src/CoreMidi/MidiEventList.cs Implemented MidiEventList as IDisposable wrapper with Add, Send, Receive, and iteration support
src/CoreMidi/MidiDriverInterface.cs Created experimental MidiDriver abstract class with COM-style interface for custom driver implementation
src/CoreMidi/MidiBluetoothDriver.cs Removed TVOS conditional and added XML documentation for Bluetooth driver methods
src/CoreFoundation/CFUuidBytes.cs Added CFUuidBytes struct for CoreMIDI driver interface QueryInterface method
docs/preview-apis.md Documented MidiDriver as experimental (APL0004) until .NET 12

Comment on lines +35 to +54
public void CtorTest_Size ()
{
Exception ex;

Assert.Multiple (() => {
ex = Assert.Throws<ArgumentOutOfRangeException> (() => new MidiEventList (MidiProtocolId.Protocol_1_0, int.MinValue), "AOORE int.MinValue");
Assert.That (ex.Message, Does.Contain ("size must be at least 276."), "AOORE msg int.MinValue");
ex = Assert.Throws<ArgumentOutOfRangeException> (() => new MidiEventList (MidiProtocolId.Protocol_1_0, -1), "AOORE -1");
Assert.That (ex.Message, Does.Contain ("size must be at least 276."), "AOORE msg -1");
ex = Assert.Throws<ArgumentOutOfRangeException> (() => new MidiEventList (MidiProtocolId.Protocol_1_0, 0), "AOORE 0");
Assert.That (ex.Message, Does.Contain ("size must be at least 276."), "AOORE msg 0");
ex = Assert.Throws<ArgumentOutOfRangeException> (() => new MidiEventList (MidiProtocolId.Protocol_1_0, 275), "AOORE 275");
Assert.That (ex.Message, Does.Contain ("size must be at least 276."), "AOORE msg 275");

var obj = new MidiEventList (MidiProtocolId.Protocol_1_0, 276);
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_1_0), "Protocol");
Assert.That (obj.PacketCount, Is.EqualTo (0), "PacketCount");
var packets = obj.ToArray ();
Assert.That (packets.Length, Is.EqualTo (0), "ToArray ().Length");
});
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instances created in these tests are not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, these should be wrapped in using statements to ensure proper cleanup. For example: using var obj = new MidiEventList (MidiProtocolId.Protocol_1_0, 276);

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +76
public void AddTest ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol");
Assert.That (obj.PacketCount, Is.EqualTo (0), "PacketCount");

var rv = obj.Add (123, new uint [] { 1, 2, 3 });
Assert.That (rv, Is.EqualTo (true), "Add B");
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol B");
Assert.That (obj.PacketCount, Is.EqualTo (1), "PacketCount B");

var packets = obj.ToArray ();
Assert.That (packets.Length, Is.EqualTo (1), "ToArray ().Length");
Assert.That (packets [0].Timestamp, Is.EqualTo (123), "Item[0].Timestamp");
Assert.That (packets [0].WordCount, Is.EqualTo (3), "Item[0].WordCount");
Assert.That (packets [0].Words, Is.EqualTo (new uint [] { 1, 2, 3 }), "Item[0].Words");
});
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instance created in this test is not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, this should be wrapped in a using statement to ensure proper cleanup. For example: using var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +90
[Test]
public void AddTest_ManyWords ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol");
Assert.That (obj.PacketCount, Is.EqualTo (0), "PacketCount");

var manyWords = Enumerable.Range (1, 65).Select (v => (uint) v).ToArray ();
var rv = obj.Add (123, manyWords);
Assert.That (rv, Is.EqualTo (false), "Add B");
});
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instance created in this test is not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, this should be wrapped in a using statement to ensure proper cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +108
[Test]
public void AddTest_NotEnoughSpace ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol");
Assert.That (obj.PacketCount, Is.EqualTo (0), "PacketCount");

var fitsTwice = Enumerable.Range (1, 24).Select (v => (uint) v).ToArray ();
var rv = obj.Add (123, fitsTwice);
Assert.That (rv, Is.EqualTo (true), "Add B");
rv = obj.Add (456, fitsTwice);
Assert.That (rv, Is.EqualTo (true), "Add C");
rv = obj.Add (789, fitsTwice);
Assert.That (rv, Is.EqualTo (false), "Add C");
});
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instance created in this test is not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, this should be wrapped in a using statement to ensure proper cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +126
[Test]
public void EnumeratorTest ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);
var rv = obj.Add (789, new uint [] { 4, 5, 6 });
Assert.That (rv, Is.EqualTo (true), "Add B");
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol B");
Assert.That (obj.PacketCount, Is.EqualTo (1), "PacketCount B");

var packets = obj.ToArray ();
Assert.That (packets.Length, Is.EqualTo (1), "ToArray ().Length");
Assert.That (packets [0].Timestamp, Is.EqualTo (789), "Item[0].Timestamp");
Assert.That (packets [0].WordCount, Is.EqualTo (3), "Item[0].WordCount");
Assert.That (packets [0].Words, Is.EqualTo (new uint [] { 4, 5, 6 }), "Item[0].Words");
});
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instance created in this test is not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, this should be wrapped in a using statement to ensure proper cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +153
[Test]
public void IteratorTest ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_2_0);
var rv = obj.Add (456, new uint [] { 1, 2, 3, 4, 5, 6 });
Assert.That (rv, Is.EqualTo (true), "Add B");
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_2_0), "Protocol B");
Assert.That (obj.PacketCount, Is.EqualTo (1), "PacketCount B");

var packets = obj.ToArray ();
Assert.That (packets.Length, Is.EqualTo (1), "ToArray ().Length");
Assert.That (packets [0].Timestamp, Is.EqualTo (456), "Item[0].Timestamp");
Assert.That (packets [0].WordCount, Is.EqualTo (6), "Item[0].WordCount");
Assert.That (packets [0].Words, Is.EqualTo (new uint [] { 1, 2, 3, 4, 5, 6 }), "Item[0].Words");

var packetList = new List<MidiEventPacket> ();
obj.Iterate ((ref MidiEventPacket packet) => {
packetList.Add (packet);
});
Assert.That (packetList.Count, Is.EqualTo (1), "packetList.Length");
Assert.That (packetList [0].Timestamp, Is.EqualTo (456), "packetList[0].Timestamp");
Assert.That (packetList [0].WordCount, Is.EqualTo (6), "packetList[0].WordCount");
Assert.That (packetList [0].Words, Is.EqualTo (new uint [] { 1, 2, 3, 4, 5, 6 }), "packetList[0].Words");
});
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instance created in this test is not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, this should be wrapped in a using statement to ensure proper cleanup.

Copilot uses AI. Check for mistakes.
}
#endif

/// <summary>Add a new <see cref="MidiEventPacket" /> to this lis.</summary>
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in the XML documentation: "lis" should be "list".

Suggested change
/// <summary>Add a new <see cref="MidiEventPacket" /> to this lis.</summary>
/// <summary>Add a new <see cref="MidiEventPacket" /> to this list.</summary>

Copilot uses AI. Check for mistakes.
}

/// <summary>Distribute the packets from the specified <paramref name="source" />.</summary>
/// <param name="source">The endpoint where the packates come from.</param>
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in the XML documentation: "packates" should be "packets".

Suggested change
/// <param name="source">The endpoint where the packates come from.</param>
/// <param name="source">The endpoint where the packets come from.</param>

Copilot uses AI. Check for mistakes.
return 0;
}

static List<MidiDriver> strongReferences = new List<MidiDriver> ();
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static List<MidiDriver> field should be initialized as readonly to prevent accidental reassignment. Consider: static readonly List&lt;MidiDriver&gt; strongReferences = new List&lt;MidiDriver&gt; ();

Suggested change
static List<MidiDriver> strongReferences = new List<MidiDriver> ();
static readonly List<MidiDriver> strongReferences = new List<MidiDriver> ();

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +31
public void CtorTest ()
{
Assert.Multiple (() => {
var obj = new MidiEventList (MidiProtocolId.Protocol_1_0);
Assert.That (obj.Protocol, Is.EqualTo (MidiProtocolId.Protocol_1_0), "Protocol");
Assert.That (obj.PacketCount, Is.EqualTo (0), "PacketCount");
var packets = obj.ToArray ();
Assert.That (packets.Length, Is.EqualTo (0), "ToArray ().Length");
});
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MidiEventList instances created in these tests are not being disposed. Since MidiEventList allocates unmanaged memory when owns=true, these should be wrapped in using statements to ensure proper cleanup. For example: using var obj = new MidiEventList (MidiProtocolId.Protocol_1_0);

Copilot uses AI. Check for mistakes.
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [CI Build #9db33b7] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 9db33b797ea11470d8a6aa2e8a37f1f35dad0514 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [PR Build #9db33b7] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 9db33b797ea11470d8a6aa2e8a37f1f35dad0514 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 9db33b797ea11470d8a6aa2e8a37f1f35dad0514 [PR build]

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [CI Build #9db33b7] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 9db33b797ea11470d8a6aa2e8a37f1f35dad0514 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

🔥 [CI Build #9db33b7] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

0 tests crashed, 4 tests failed, 152 tests passed.

Failures

❌ linker tests [attempt 2]

1 tests failed, 43 tests passed.

Failed tests

  • link all/iOS - simulator/Release: LaunchFailure

Html Report (VSDrops) Download

❌ monotouch tests (iOS) [attempt 2]

1 tests failed, 10 tests passed.

Failed tests

  • monotouch-test/iOS - simulator/Release (all optimizations): Crashed

Html Report (VSDrops) Download

❌ monotouch tests (MacCatalyst) [attempt 2]

2 tests failed, 13 tests passed.

Failed tests

  • monotouch-test/Mac Catalyst/Release (NativeAOT): Failed (Test run failed.
    Tests run: 3774 Passed: 3532 Inconclusive: 11 Failed: 15 Ignored: 227)
  • monotouch-test/Mac Catalyst/Debug (interpreter): Failed (Test run failed.
    Tests run: 3777 Passed: 3556 Inconclusive: 10 Failed: 15 Ignored: 206)

Html Report (VSDrops) Download

Successes

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 6 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 12 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 11 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Monterey (12): All 5 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ Tests on macOS Ventura (13): All 5 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ Tests on macOS Sonoma (14): All 5 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. [attempt 2] Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. [attempt 2] Html Report (VSDrops) Download

Pipeline on Agent
Hash: 9db33b797ea11470d8a6aa2e8a37f1f35dad0514 [PR build]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[AudioToolbox] Some AudioToolbox APIs not yet implemented in Xcode13 Old CoreMIDI APIs are missing

2 participants

Comments