Last active
May 6, 2023 16:41
-
-
Save JRHeaton/b3b1b1b7fc191afdc01a to your computer and use it in GitHub Desktop.
elegant midi message model w/ swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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