Да, вы можете использовать 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)
}
Несколько замечаний:
- Из Из всех системных API Apple, CoreMIDI может быть просто худшим из Swift. Это чистый C API, интенсивно использующий указатели, типы CoreFoundation, процедуры обратного вызова, структуры, типы которых известны только во время выполнения и т. Д. c. Вы видите, что здесь используется
Unsafe(Raw)Pointers
, перепрограммирование памяти и т. Д. c. Я думаю, что все еще имеет смысл просто использовать его из Objective- C и делать это в моих собственных проектах. - Не все MIDI-устройства фактически представлены как MIDI-устройства (ie.
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
и связанные уведомления. Он также обрабатывает автоматическое объединение пар «источник / конечная точка» в устройства, позволяет вам задавать свойства устройства KVO (name, et c.) И кучу других вещей.
Отказ от ответственности: я основной автор и сопровождающий МИКМИДИ.