Gong is a simple library for sending and recieving MIDI messages to and from virtual and physical devices.
Gong aims to provide a fairly transparent Swift interface to Apple's CoreMIDI library.
The library is built in two layers:
- The files in the
/Sources/Coredirectory are straightforward, unopinionated wrappers around CoreMIDI's C APIs. - The files outside of the
/Sources/Coredirectory are slightly more opinionated, but let you perform common tasks with a minimum of setup.
More specifically: there is a global MIDI singleton, which:
- Creates a
MIDIClientand subscribes toMIDINoticeevents (e.g., MIDI device connections and disconnections). - Creates a
MIDIInput, connects it to all availableMIDISourceinstances and subscribes toMIDIPacketevents (e.g., MIDI note or control change messages). - Creates a
MIDIOutput, which you can use to sendMIDIPacketsto devices. - Implements an observer pattern so classes implementing the
MIDIObserverprotocol can recieveMIDINoticeandMIDIPacketmessages. - Wraps any CoreMIDI wrapper calls that might
throwintry ~ catchblocks and prints any exceptions that get thrown.
If you prefer to write this kind of thing yourself, the CoreMIDI wrapper can be installed independently of the opinionated code.
An example project is provided to help you get started.
- Test for memory leaks
- MIDINotification/MIDINotice name issue
The entire library:
pod 'Gong', '~> 0.2'Just the CoreMIDI wrapper:
pod 'Gong/Core', '~> 0.2'Just the CoreMIDI wrapper, plus MIDINote events:
pod 'Gong/Events', '~> 0.2'One of Gong's core use cases is sending MIDI messages into DAW software such as Ableton Live.
This necessitates setting up a virtual MIDI bus. The Ableton folks have a tutorial on how to do this.
MIDIObject <----+--+ MIDIClient
|
+--+ MIDIPort <------+--+ MIDIInput
MIDINotice | |
| +--+ MIDIOutput
MIDIPacket +--+ MIDIDevice
|
+--+ MIDIEntity
MIDIError |
+--+ MIDIEndpoint <--+--+ MIDISource
|
+--+ MIDIDestination
MIDIClient MIDIDevice
creates owns
+ +
| v
| MIDIEntity
| owns
| +
v receives packets from v
MIDIInput <------------------------------+ MIDISource
and sends packets to and
MIDIOutput +---------------------------> MIDIDestination
Starting the MIDI client:
MIDI.connect()Stopping the MIDI client:
MIDI.disconnect()Listing devices:
for device in MIDIDevice.all {
print(device.name)
}Sending MIDI events:
guard let device = MIDIDevice(named: "minilogue"), let output = MIDI.output else {
return
}
let note = MIDINote(pitch: c5)
device.send(note, via: output)Receiving MIDI packets:
class ViewController: NSViewController {
override func viewWillAppear() {
super.viewWillAppear()
MIDI.addObserver(self)
}
override func viewDidDisappear() {
super.viewDidDisappear()
MIDI.removeObserver(self)
}
}
extension ViewController: MIDIObserver {
func receive(_ notice: MIDINotice) {
print(notice)
}
func receive(_ packet: MIDIPacket, from source: MIDISource) {
switch packet.message {
case .noteOn, .noteOff, .controlChange, .pitchBendChange:
print(packet.message, source)
default:
break
}
}
}