I am using the following code to receive MIDI events in a Swift Playground:
import Cocoa
import CoreMIDI
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
func notifyCallback(message:UnsafePointer<MIDINotification>,refCon:UnsafeMutablePointer<Void>)
{
println("MIDI Notify")
}
func eventCallback(pktlist:UnsafePointer<MIDIPacketList>, refCon:UnsafeMutablePointer<Void>, connRefCon:UnsafeMutablePointer<Void>)
{
println("MIDI Read")
}
var client = MIDIClientRef()
MIDIClientCreate("Core MIDI Callback Demo" as NSString, MIDINotifyProc(COpaquePointer([notifyCallback])), nil, &client)
var inPort = MIDIPortRef()
MIDIInputPortCreate(client, "Input port",MIDIReadProc(COpaquePointer([eventCallback])), nil, &inPort)
let sourceCount = MIDIGetNumberOfSources()
for var count:UInt = 0; count < sourceCount; ++count
{
let src:MIDIEndpointRef = MIDIGetSource(count)
MIDIPortConnectSource(inPort, src, nil)
}
I got this by translating working Objective-C code into what I think would be the correct Swift version.
It compiles and runs fine until one of the callbacks fires, e.g. when I unplug the MIDI device or hit one of its keys. I always get a BAD_EXEC.
Any ideas how to make this work or is Swift just not ready as some blog posts on the web state. Anything official from Apple that I overlooked that clearly states Swift is not ready yet for CoreMIDI callbacks?
Update 2015-03-10: The corresponding Objective-C code can be found at http://www.digital-aud.io/blog/2015/03/10/on-coremidi-callbacks/
Update 2021-06-25: I have written a programming tutorial on how to receive MIDI messages on Apple devices https://twissmueller.medium.com/midi-listener-in-swift-b6e5fb277406. There is a link in the tutorial where a complete Xcode-project with a sample application can be purchased.
Swift3 seems to have better support for CoreMIDI than earlier releases. The example playground demo shown below shows some working rudimentary CoreMIDI calls.
import Cocoa
import CoreMIDI
import PlaygroundSupport
// helper method to extract the display name from a MIDIObjectRef
func midiObjectDisplayName(_ obj: MIDIObjectRef) -> String {
var param: Unmanaged<CFString>?
var capturedName = "Error"
let err = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, ¶m)
if err == OSStatus(noErr) {
capturedName = param!.takeRetainedValue() as String
}
return capturedName
}
// method to collect display names of available MIDI destinations
func midiDestinationNames() -> [String] {
var names:[String] = []
let count:Int = MIDIGetNumberOfDestinations()
for i in 0..<count {
let endpoint:MIDIEndpointRef = MIDIGetDestination(i)
if endpoint != 0 {
names.append(midiObjectDisplayName(endpoint))
}
}
return names
}
let destinationNames = midiDestinationNames()
// check if we have any available MIDI destinations.
if destinationNames.count > 0 {
// establish a MIDI client and output port, and send a note on/off pair.
var midiClient:MIDIClientRef = 0
var outPort:MIDIPortRef = 0
MIDIClientCreate("Swift3 Test Client" as CFString, nil, nil, &midiClient)
MIDIOutputPortCreate(midiClient, "Swift3 Test OutPort" as CFString, &outPort)
let destNum = 0
let destName = destinationNames[destNum]
var dest:MIDIEndpointRef = MIDIGetDestination(destNum)
var midiPacket:MIDIPacket = MIDIPacket()
midiPacket.timeStamp = 0
midiPacket.length = 3
midiPacket.data.0 = 0x90 + 0 // Note On event channel 1
midiPacket.data.1 = 0x3D // Note Db
midiPacket.data.2 = 100 // Velocity
var packetList:MIDIPacketList = MIDIPacketList(numPackets: 1, packet: midiPacket)
print("Sending note on to \(destName)")
MIDISend(outPort, dest, &packetList)
midiPacket.data.0 = 0x80 + 0 // Note off event channel 1
midiPacket.data.2 = 0 // Velocity
sleep(1)
packetList = MIDIPacketList(numPackets: 1, packet: midiPacket)
MIDISend(outPort, dest, &packetList)
print("Note off sent to \(destName)")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With