Is there an API that will notify me when a MIDI device has been connected to iOS device? I have been trying to find it among the CoreMidi APIs, but I wasn't successful. I am only able to list all connected devices at a given moment.
I would like to avoid polling if possible, so this would be beneficial.
Yes, you can use the MIDIClient
API. Specifically, here's a simple, self-contained program that will print messages when devices are added, removed, or have their properties changed:
import Cocoa
import CoreMIDI
var client = MIDIClientRef()
let clientName = "MyMIDIClient" as CFString
let err = MIDIClientCreateWithBlock(clientName, &client) { (notificationPtr: UnsafePointer<MIDINotification>) in
let notification = notificationPtr.pointee
switch notification.messageID {
case .msgSetupChanged: // Can ignore, really
break
case .msgObjectAdded:
let rawPtr = UnsafeRawPointer(notificationPtr)
let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee
print("MIDI \(message.childType) added: \(message.child)")
case .msgObjectRemoved:
let rawPtr = UnsafeRawPointer(notificationPtr)
let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee
print("MIDI \(message.childType) removed: \(message.child)")
case .msgPropertyChanged:
let rawPtr = UnsafeRawPointer(notificationPtr)
let message = rawPtr.assumingMemoryBound(to: MIDIObjectPropertyChangeNotification.self).pointee
print("MIDI \(message.object) property \(message.propertyName.takeUnretainedValue()) changed.")
case .msgThruConnectionsChanged:
fallthrough
case .msgSerialPortOwnerChanged:
print("MIDI Thru connection was created or destroyed")
case .msgIOError:
let rawPtr = UnsafeRawPointer(notificationPtr)
let message = rawPtr.assumingMemoryBound(to: MIDIIOErrorNotification.self).pointee
print("MIDI I/O error \(message.errorCode) occurred")
default:
break
}
}
if err != noErr {
print("Error creating MIDI client: \(err)")
}
let rl = RunLoop.current
while true {
rl.run(mode: .default, before: .distantFuture)
}
A few notes:
Unsafe(Raw)Pointers
, rebinding memory, etc. I think it still makes more sense to just use it from Objective-C, and do so in my own projects.MIDIDeviceRef
). Some use a driver that creates virtual endpoints, and those just show up as (unrelated) source and destination endpoints. You have to figure out how to deal with these on your own. Native Instruments' devices are one common example of this behavior.var midiDevicesObserver: NSKeyValueObservation?
let deviceManager = MIKMIDIDeviceManager.shared
midiDevicesObserver = deviceManager.observe(\.availableDevices) { (dm, _) in
print("Available MIDI devices changed: \(dm.availableDevices)")
}
or use the also-available MIKMIDIDeviceWasAddedNotification
and related notifications. It also handles automatically coalescing source/endpoint pairs into devices, allows you to KVO device properties (name, etc.), and a bunch of other stuff.
Disclaimer: I'm the primary author and maintainer of MIKMIDI.