在 iOS 上观察 MIDI 设备连接通知
Observe MIDI device connection notification on iOS
是否有 API 会在 MIDI 设备连接到 iOS 设备时通知我?我一直试图在 CoreMidi API 中找到它,但我没有成功。我只能在给定时刻列出所有连接的设备。
我想尽可能避免轮询,所以这会很有用。
是的,可以使用MIDIClient
API。具体来说,这是一个简单的自包含程序,它会在添加、删除设备或更改其属性时打印消息:
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)
}
一些注意事项:
- 在所有 Apple 系统 API 中,CoreMIDI 可能是 Swift 中使用最差的一个。它是纯 C API,大量使用指针、CoreFoundation 类型、回调过程、类型仅在运行时已知的结构等。您在此处看到
Unsafe(Raw)Pointers
的使用,重新绑定内存等。我认为从 Objective-C 开始使用它仍然更有意义,并在我自己的项目中这样做。
- 并非所有 MIDI 设备实际上都显示为 MIDI 设备(即
MIDIDeviceRef
)。有些使用创建虚拟端点的驱动程序,而那些只是显示为(不相关的)源端点和目标端点。您必须自己弄清楚如何处理这些问题。 Native Instruments 的设备就是这种行为的一个常见示例。
- 您可以查看 MIKMIDI,这会使所有这些变得容易得多。具体来说,您只需执行以下操作:
var midiDevicesObserver: NSKeyValueObservation?
let deviceManager = MIKMIDIDeviceManager.shared
midiDevicesObserver = deviceManager.observe(\.availableDevices) { (dm, _) in
print("Available MIDI devices changed: \(dm.availableDevices)")
}
或使用也可用的 MIKMIDIDeviceWasAddedNotification
和相关通知。它还处理自动将 source/endpoint 对合并到设备中,允许您 KVO 设备属性(名称等),以及一堆其他东西。
免责声明:我是 MIKMIDI 的主要作者和维护者。
是否有 API 会在 MIDI 设备连接到 iOS 设备时通知我?我一直试图在 CoreMidi API 中找到它,但我没有成功。我只能在给定时刻列出所有连接的设备。
我想尽可能避免轮询,所以这会很有用。
是的,可以使用MIDIClient
API。具体来说,这是一个简单的自包含程序,它会在添加、删除设备或更改其属性时打印消息:
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)
}
一些注意事项:
- 在所有 Apple 系统 API 中,CoreMIDI 可能是 Swift 中使用最差的一个。它是纯 C API,大量使用指针、CoreFoundation 类型、回调过程、类型仅在运行时已知的结构等。您在此处看到
Unsafe(Raw)Pointers
的使用,重新绑定内存等。我认为从 Objective-C 开始使用它仍然更有意义,并在我自己的项目中这样做。 - 并非所有 MIDI 设备实际上都显示为 MIDI 设备(即
MIDIDeviceRef
)。有些使用创建虚拟端点的驱动程序,而那些只是显示为(不相关的)源端点和目标端点。您必须自己弄清楚如何处理这些问题。 Native Instruments 的设备就是这种行为的一个常见示例。 - 您可以查看 MIKMIDI,这会使所有这些变得容易得多。具体来说,您只需执行以下操作:
var midiDevicesObserver: NSKeyValueObservation?
let deviceManager = MIKMIDIDeviceManager.shared
midiDevicesObserver = deviceManager.observe(\.availableDevices) { (dm, _) in
print("Available MIDI devices changed: \(dm.availableDevices)")
}
或使用也可用的 MIKMIDIDeviceWasAddedNotification
和相关通知。它还处理自动将 source/endpoint 对合并到设备中,允许您 KVO 设备属性(名称等),以及一堆其他东西。
免责声明:我是 MIKMIDI 的主要作者和维护者。