Skip to content

Instantly share code, notes, and snippets.

@JRHeaton
Last active May 6, 2023 16:41
Show Gist options
  • Save JRHeaton/b3b1b1b7fc191afdc01a to your computer and use it in GitHub Desktop.
Save JRHeaton/b3b1b1b7fc191afdc01a to your computer and use it in GitHub Desktop.
elegant midi message model w/ swift
import CoreMIDI
enum ChannelMessage {
case NoteOn(key: UInt8, velocity: UInt8)
case NoteOff(key: UInt8)
case ControlChange(controller: UInt8, value: UInt8)
func bytesForChannel(channel: Int) -> [UInt8] {
let status = { $0 | (UInt8(channel) & 0x0F) }
let removeMSB = { $0 & UInt8(0x7F) }
switch self {
case .NoteOn(let key, let velocity):
return [status(0x90), removeMSB(key), removeMSB(velocity)]
case .NoteOff(let key):
return [status(0x80), removeMSB(key), 0]
case .ControlChange(let controller, let value):
return [status(0xB0), removeMSB(controller), removeMSB(value)]
}
}
}
func •>> (left: ChannelMessage, right: ChannelMessage) -> [ChannelMessage] {
return [left, right]
}
func •>> (left: ChannelMessage, right: [ChannelMessage]) -> [ChannelMessage] {
return [left] + right
}
struct MIDICollection: CollectionType {
var startIndex: Int = 0
var endIndex: Int {
return count()
}
let count: () -> Int
let atIndex: Int -> MIDIObjectRef
init(count: () -> Int, atIndex: Int -> MIDIObjectRef) {
self.count = count
self.atIndex = atIndex
}
subscript (position: Int) -> MIDIObjectRef {
return atIndex(position)
}
func generate() -> GeneratorOf<MIDIObjectRef> {
var index = 0
return GeneratorOf {
let ref = self.atIndex(index++)
if ref == 0 {
return nil
}
return ref
}
}
}
let c = MIDICollection(count: MIDIGetNumberOfDestinations, atIndex: MIDIGetDestination)
func property(propertyName: CFStringRef)(_ obj: MIDIObjectRef) -> String {
var property: Unmanaged<CFString>?
MIDIObjectGetStringProperty(obj, propertyName, &property)
if let prop = property?.takeUnretainedValue() as? String {
property?.release()
return prop
}
return ""
}
func MIDIClientCreate(name: String) -> MIDIClientRef {
var ret: MIDIClientRef = 0
MIDIClientCreate(name, nil, nil, &ret)
return ret
}
func MIDIOutputPortCreate(name: String, client: MIDIClientRef) -> MIDIPortRef {
var ret: MIDIPortRef = 0
MIDIOutputPortCreate(client, name, &ret)
return ret
}
let client = MIDIClientCreate("my client")
let out = MIDIOutputPortCreate("my output", client)
enum LPMessage {
enum ColorIntensity: UInt8 {
case Off = 0
case Min = 1
case Mid = 2
case Max = 3
}
case Reset
case LEDTest
case PadOn(red: ColorIntensity, green: ColorIntensity, x: Int, y: Int)
var channelMessage: ChannelMessage {
switch self {
case .Reset:
return ChannelMessage.ControlChange(controller: 0, value: 0)
case .LEDTest:
return ChannelMessage.ControlChange(controller: 0, value: 0x7F)
case .PadOn(let red, let green, let x, let y):
return ChannelMessage.NoteOn(key: UInt8(0x10 * y + x), velocity: (green.rawValue << 4) | red.rawValue)
}
}
}
let commands = [LPMessage.PadOn(red: .Max, green: .Off, x: 1, y: 1),
LPMessage.PadOn(red: .Off, green: .Max, x: 0, y: 0)].flatMap { $0.channelMessage.bytesForChannel(0) }
for dest in c {
if property(kMIDIPropertyName)(dest) == "Launchpad S" {
sendMIDIBytes(dest, out, commands, UInt16(commands.count))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment