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/Gong/Core
directory are straightforward, unopinionated wrappers around CoreMIDI's C APIs. - The files outside of the
/Sources/Gong/Core
directory 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
MIDIClient
and subscribes toMIDINotice
events (e.g., MIDI device connections and disconnections). - Creates a
MIDIInput
, connects it to all availableMIDISource
instances and subscribes toMIDIPacket
events (e.g., MIDI note or control change messages). - Creates a
MIDIOutput
, which you can use to sendMIDIPackets
to devices. - Implements an observer pattern so classes implementing the
MIDIObserver
protocol can recieveMIDINotice
andMIDIPacket
messages. - Wraps any CoreMIDI wrapper calls that might
throw
intry ~ catch
blocks 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.
The entire library:
pod 'Gong', '~> 1.3'
Just the CoreMIDI wrapper:
pod 'Gong/Core', '~> 1.3'
Just the CoreMIDI wrapper, plus MIDINote
events:
pod 'Gong/Events', '~> 1.3'
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 {
private var observerTokens = [MIDIObserverTokens]()
override func viewWillAppear() {
super.viewWillAppear()
observerTokens.append(MIDI.addObserver(self))
}
override func viewDidDisappear() {
super.viewDidDisappear()
for observerTokens in observerTokens {
MIDI.removeObserver(observerTokens)
}
}
}
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
}
}
}