Change Instruments in MIDI Tracks with Swift

Carlos Mbendera
CodeX
Published in
2 min readMar 29, 2024

--

Hello There! This article assumes that you already have a MiDi track set up in Swift and that you’re only interested in changing the MiDi instrument. Please read my other article if you need a refresher.

Changing Tunes: MIDI Channel Manager

Our focus here is to introduce a MIDI Channel Manager, allowing us to specify the new instrument we want for our music track. It’s a straightforward process that requires just a snippet of code:

    // Setting the instrument to Violin (program number 40) 
var programChangeMessage = MIDIChannelMessage(status: 0xC0, data1: 40, data2: 0, reserved: 0)
withUnsafePointer(to: &programChangeMessage) { ptr in
let status = MusicTrackNewMIDIChannelEvent(track!, 0, ptr)
if status != noErr {
print("Error setting program change \(status)")
}
}

Show Me The REAL Code

Below is an example of what a complete implementation using would look like. Based upon a lot of the work from my earlier article.


import Foundation
import AudioToolbox

func createPlayer(chords : [[UInt8]]){
var musicPlayer : MusicPlayer? = nil
var player = NewMusicPlayer(&musicPlayer)

player = MusicPlayerSetSequence(musicPlayer!, createMusicSequence(chords: chords))
player = MusicPlayerStart(musicPlayer!)
}

func createMusicSequence(chords: [[UInt8]] ) -> MusicSequence {

var musicSequence: MusicSequence?
var status = NewMusicSequence(&musicSequence)
if status != noErr {
print(" bad status \(status) creating sequence")
}

var tempoTrack: MusicTrack?
if MusicSequenceGetTempoTrack(musicSequence!, &tempoTrack) != noErr {
assert(tempoTrack != nil, "Cannot get tempo track")
}

//MusicTrackClear(tempoTrack, 0, 1)
if MusicTrackNewExtendedTempoEvent(tempoTrack!, 0.0, 128.0) != noErr {
print("could not set tempo")
}
if MusicTrackNewExtendedTempoEvent(tempoTrack!, 4.0, 256.0) != noErr {
print("could not set tempo")
}


// add a track
var track: MusicTrack?
status = MusicSequenceNewTrack(musicSequence!, &track)
if status != noErr {
print("error creating track \(status)")
}


// Setting the instrument to Violin (program number 40)
var programChangeMessage = MIDIChannelMessage(status: 0xC0, data1: 40, data2: 0, reserved: 0)
withUnsafePointer(to: &programChangeMessage) { ptr in
let status = MusicTrackNewMIDIChannelEvent(track!, 0, ptr)
if status != noErr {
print("Error setting program change \(status)")
}
}



// make some notes and put them on the track
var beat: MusicTimeStamp = 0.0

for batch in 0..<chords.count {
for note: UInt8 in chords[batch] {
var mess = MIDINoteMessage(channel: 0,
note: note,
velocity: 64,
releaseVelocity: 0,
duration: 1.0 )
status = MusicTrackNewMIDINoteEvent(track!, beat, &mess)
if status != noErr { print("creating new midi note event \(status)") }

}// beat changes after this
beat += 1
}

CAShow(UnsafeMutablePointer<MusicSequence>(musicSequence!))

return musicSequence!
}

✨Yay! We did it!✨

At this point, all you need to do is pass in some chords and listen to the richness of MiDi sound.

✨Congratulations✨

--

--

Carlos Mbendera
CodeX

CS Major,  WWDC23 Student Challenge Winner and Jazz Fusion Enthusiast writing about Swift and other rad stuff.