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;