Swift CoreMIDI without Objective-C (Updated for Swift 3)

Update: Code has been added for Swift 3 and XCode 8

These examples do little to no error checking and it is assumed your system has at least one source and one destination. Run them in a macOS Playground.

List the MIDI sources and destinations (Swift 3)

//
// Swift MIDI Playground : Matt Grippaldi 10/17/2016
//
// Sources & Destinations
//
// Updated for Swift 3 / XCode 8
//
import Cocoa
import CoreMIDI
import PlaygroundSupport

func getDisplayName(_ obj: MIDIObjectRef) -> String
{
    var param: Unmanaged<CFString>?
    var name: String = "Error"
    
    let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &param)
    if err == OSStatus(noErr)
    {
        name =  param!.takeRetainedValue() as String
    }
    
    return name
}

func getDestinationNames() -> [String]
{
    var names:[String] = [];
    
    let count: Int = MIDIGetNumberOfDestinations();
    for i in 0..<count {
        let endpoint:MIDIEndpointRef = MIDIGetDestination(i);
        
        if (endpoint != 0)
        {
            names.append(getDisplayName(endpoint));
        }
    }
    return names;
}

func getSourceNames() -> [String]
{
    var names:[String] = [];
    
    let count: Int = MIDIGetNumberOfSources();
    for i in 0..<count {
        let endpoint:MIDIEndpointRef = MIDIGetSource(i);
        if (endpoint != 0)
        {
            names.append(getDisplayName(endpoint));
        }
    }
    return names;
}

let destNames = getDestinationNames();

print("Number of MIDI Destinations: \(destNames.count)");
for destName in destNames
{
    print("  Destination: \(destName)");
}

let sourceNames = getSourceNames();

print("\nNumber of MIDI Sources: \(sourceNames.count)");
for sourceName in sourceNames
{
    print("  Source: \(sourceName)");
}

 

List the MIDI sources and destinations (Swift 2)

//
// Swift MIDI Playground : Matt Grippaldi 1/1/2016
//
import Cocoa
import CoreMIDI

func getDisplayName(obj: MIDIObjectRef) -> String
{
    var param: Unmanaged?
    var name: String = "Error";
    
    let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &param)
    if err == OSStatus(noErr)
    {
        name =  param!.takeRetainedValue() as String
    }
    
    return name;
}

func getDestinationNames() -> [String]
{
    var names:[String] = [String]();
    
    let count: Int = MIDIGetNumberOfDestinations();
    for (var i=0; i < count; ++i) { let endpoint:MIDIEndpointRef = MIDIGetDestination(i); if (endpoint != 0) { names.append(getDisplayName(endpoint)); } } return names; } func getSourceNames() -> [String]
{
    var names:[String] = [String]();
    
    let count: Int = MIDIGetNumberOfSources();
    for (var i=0; i < count; ++i)
    {
        let endpoint:MIDIEndpointRef = MIDIGetSource(i);
        if (endpoint != 0)
        {
            names.append(getDisplayName(endpoint));
        }
    }
    return names;
}

let destNames = getDestinationNames();
for destName in destNames
{
     print("Destination: \(destName)");
}

let sourceNames = getSourceNames();
for sourceName in sourceNames
{
    print("Source: \(sourceName)");
}

 

Create a client, connect a destination, and play a note (Swift 3)

//
//
// Swift MIDI Playground : Matt Grippaldi 10/17/2016
//
// Sources & Destinations
//
// Updated for Swift 3 / XCode 8
// Added code to display destinations (11/25/2016)
//
import Cocoa
import CoreMIDI
import PlaygroundSupport

func getDisplayName(_ obj: MIDIObjectRef) -> String
{
    var param: Unmanaged?
    var name: String = "Error";
    
    let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &param)
    if err == OSStatus(noErr)
    {
        name =  param!.takeRetainedValue() as String
    }
    
    return name;
}

func getDestinationNames() -> [String]
{
    var names:[String] = [String]();
    
    let count: Int = MIDIGetNumberOfDestinations();
    for i in 0 ..< count
    {
        let endpoint:MIDIEndpointRef = MIDIGetDestination(i);
        if (endpoint != 0)
        {
            names.append(getDisplayName(endpoint));
        }
    }
    return names;
}

var midiClient: MIDIClientRef = 0;
var outPort:MIDIPortRef = 0;

MIDIClientCreate("MidiTestClient" as CFString, nil, nil, &midiClient);
MIDIOutputPortCreate(midiClient, "MidiTest_OutPort" as CFString, &outPort);

var packet1:MIDIPacket = MIDIPacket();
packet1.timeStamp = 0;
packet1.length = 3;
packet1.data.0 = 0x90 + 0; // Note On event channel 1
packet1.data.1 = 0x3C; // Note C3
packet1.data.2 = 100; // Velocity

var packetList:MIDIPacketList = MIDIPacketList(numPackets: 1, packet: packet1);

let destinationNames = getDestinationNames()
for (index,destName) in destinationNames.enumerated()
{
    print("Destination #\(index): \(destName)")
}

let destNum = 0
print("Using destination #\(destNum)")

var dest:MIDIEndpointRef = MIDIGetDestination(destNum);
print("Playing note for 1 second on channel 1")
MIDISend(outPort, dest, &packetList);
packet1.data.0 = 0x80 + 0; // Note Off event channel 1
packet1.data.2 = 0; // Velocity
sleep(1);
packetList = MIDIPacketList(numPackets: 1, packet: packet1);
MIDISend(outPort, dest, &packetList);
print("Note off sent")

 

Create a client, connect a destination, and play a note (Swift 2)

//
// Swift MIDI Playground : Matt Grippaldi 1/1/2016
//
import Cocoa
import CoreMIDI

var midiClient: MIDIClientRef = 0;
var outPort:MIDIPortRef = 0;

MIDIClientCreate("MidiTestClient", nil, nil, &midiClient);
MIDIOutputPortCreate(midiClient, "MidiTest_OutPort", &outPort);

var packet1:MIDIPacket = MIDIPacket();
packet1.timeStamp = 0;
packet1.length = 3;
packet1.data.0 = 0x90 + 0; // Note On event channel 1
packet1.data.1 = 0x3C; // Note C3
packet1.data.2 = 100; // Velocity

var packetList:MIDIPacketList = MIDIPacketList(numPackets: 1, packet: packet1);

// Get the first destination
var dest:MIDIEndpointRef = MIDIGetDestination(0);
MIDISend(outPort, dest, &packetList);
packet1.data.0 = 0x80 + 0; // Note Off event channel 1
packet1.data.2 = 0; // Velocity
sleep(1);
packetList = MIDIPacketList(numPackets: 1, packet: packet1);
MIDISend(outPort, dest, &packetList);

 

Connect an input source and receive data via a callback (Swift 3)

//
// Swift MIDI Playground : Matt Grippaldi 10/17/2016
//
// MIDI Callbacks
// 
// Updated for Swift 3 / XCode 8
//
import Cocoa
import CoreMIDI
import PlaygroundSupport

func getDisplayName(_ obj: MIDIObjectRef) -> String
{
    var param: Unmanaged<CFString>?
    var name: String = "Error"
    
    let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &param)
    if err == OSStatus(noErr)
    {
        name =  param!.takeRetainedValue() as String
    }
    
    return name
}

func MyMIDIReadProc(pktList: UnsafePointer<MIDIPacketList>,
                    readProcRefCon: UnsafeMutableRawPointer?, srcConnRefCon: UnsafeMutableRawPointer?) -> Void
{
    let packetList:MIDIPacketList = pktList.pointee
    let srcRef:MIDIEndpointRef = srcConnRefCon!.load(as: MIDIEndpointRef.self)

    print("MIDI Received From Source: \(getDisplayName(srcRef))")
    
    var packet:MIDIPacket = packetList.packet
    for _ in 1...packetList.numPackets
    {
        let bytes = Mirror(reflecting: packet.data).children
        var dumpStr = ""
        
        // bytes mirror contains all the zero values in the ridiulous packet data tuple
        // so use the packet length to iterate.
        var i = packet.length
        for (_, attr) in bytes.enumerated()
        {
            dumpStr += String(format:"$%02X ", attr.value as! UInt8)
            i -= 1
            if (i <= 0)
            {
                break
            }
        }
        
        print(dumpStr)
        packet = MIDIPacketNext(&packet).pointee
    }
}

var midiClient: MIDIClientRef = 0
var inPort:MIDIPortRef = 0
var src:MIDIEndpointRef = MIDIGetSource(0)

MIDIClientCreate("MidiTestClient" as CFString, nil, nil, &midiClient)
MIDIInputPortCreate(midiClient, "MidiTest_InPort" as CFString, MyMIDIReadProc, nil, &inPort)

MIDIPortConnectSource(inPort, src, &src)

// Keep playground running
PlaygroundPage.current.needsIndefiniteExecution = true

 

Connect an input source and receive data via a callback (Swift 2)

//
// Swift MIDI Playground : Matt Grippaldi 1/1/2016
//
import Cocoa
import CoreMIDI
import XCPlayground

func getDisplayName(obj: MIDIObjectRef) -> String
{
    var param: Unmanaged?
    var name: String = "Error";
    
    let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &param)
    if err == OSStatus(noErr)
    {
        name =  param!.takeRetainedValue() as String
    }
    
    return name;
}

func MyMIDIReadProc(pktList: UnsafePointer,
    readProcRefCon: UnsafeMutablePointer, srcConnRefCon: UnsafeMutablePointer) -> Void
{
    let packetList:MIDIPacketList = pktList.memory;
    let srcRef:MIDIEndpointRef = UnsafeMutablePointer(COpaquePointer(srcConnRefCon)).memory;
    print("MIDI Received From Source: \(getDisplayName(srcRef))");
    
    var packet:MIDIPacket = packetList.packet;
    for _ in 1...packetList.numPackets
    {
        let bytes = Mirror(reflecting: packet.data).children;
        var dumpStr = "";
        
        // bytes mirror contains all the zero values in the ridiulous packet data tuple
        // so use the packet length to iterate.
        var i = packet.length;
        for (_, attr) in bytes.enumerate()
        {
             dumpStr += String(format:"$%02X ", attr.value as! UInt8);
            --i;
            if (i <= 0)
            {
               break;
            }
        }

        print(dumpStr)
        packet = MIDIPacketNext(&packet).memory;
    }
}

var midiClient: MIDIClientRef = 0;
var inPort:MIDIPortRef = 0;
var src:MIDIEndpointRef = MIDIGetSource(0);

MIDIClientCreate("MidiTestClient", nil, nil, &midiClient);
MIDIInputPortCreate(midiClient, "MidiTest_InPort", MyMIDIReadProc, nil, &inPort);

MIDIPortConnectSource(inPort, src, &src);

// Keep playground running
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true;

8 thoughts on “Swift CoreMIDI without Objective-C (Updated for Swift 3)”

  1. Hi,
    Thanks for this page, I do get an EXC_BAD_ACCESS error on the “MIDISend(outPort, dest, &packetList)” line of the “Create a client, connect a destination, and play a note” code. I can’t figure out what this is due to. Any idea?

    1. The problem was that I working with a playground for iOS and not for OS X. It works fine when working with a playground for OS X and using Cocoa.

    1. Hello,
      Thanks for this page ! i would like to send midi message, in playground everything is right, no error, but i’m trying to send midi to my DAW Ableton and they don’t receive anything, What i miss ?

  2. Hello Matt,

    very helpful, thanks.

    I have one problem. In a playground, the code for sending MIDI notes (and other events) works only, if the destination is “Internalcomm”. For all other devices (Network, an CoremMIDI compliant interface attached to the computer’s USB port etc.) there is no effect. A MIDI event monitoring program I have purchased does not show any events. There are no error messages and the code runs to completion.

    Also, if I rund the exact same code on an iOS device, no messages are sent.

    Did I miss out something?

    christoph

    1. I have no way to test on iOS but I added code to display the destinations to the example code. I set destNum to each destination on my system and they all worked. I tried to use Swift.readLine() to read the destination number from the command line but apparently it doesn’t work in playgrounds.

Comments are closed.