Skip to main content

Multitrack Recorder App

Multitrack recording is a technique used in audio production that allows for the separate recording and mixing of individual audio tracks. In multitrack recording, each instrument or vocal track is recorded onto a separate track, which can then be mixed together with other tracks to create a final mix.

Using the components of the SwitchboardSDK we can simply create an app like this. We need to create an AudioGraph with multiple recorders, and mix the recordings together. We can even apply various effects on the different tracks. To check the list of available effects please visit our extensions page. Let's say we want to have three different tracks:

  • our voice with applied compressor effect on it
  • a guitar track with reverb effect
  • a piano track with flanger effect

The sketch of the audio graph will help us understand how the nodes will be connected:


Now let's take a look at code:
import SwitchboardSDK

class MultitrackRecorderExample {

let audioEngine = SBAudioEngine()
let audioGraph = SBAudioGraph()

let voiceRecorderNode: SBRecorderNode
let guitarRecorderNode: SBRecorderNode
let pianoRecorderNode: SBRecorderNode

let voicePlayerNode = SBAudioPlayerNode()
let guitarPlayerNode = SBAudioPlayerNode()
let pianoPlayerNode = SBAudioPlayerNode()

let compressorNode = SBCompressorNode()
let reverbNode = SBReverbNode()
let flangerNode = SBFlangerNode()

let mixerNode = SBMixerNode()

let currentFormat: SBCodec = .wav

init() {

voiceRecorderNode = SBRecorderNode(
name: "UniqueID1",
recordingSampleRate: 48000,
numberOfRecordedChannels: 2
)

guitarRecorderNode = SBRecorderNode(
name: "UniqueID2",
recordingSampleRate: 48000,
numberOfRecordedChannels: 2
)

pianoRecorderNode = SBRecorderNode(
name: "UniqueID3",
recordingSampleRate: 48000,
numberOfRecordedChannels: 2
)

audioGraph.add(voiceRecorderNode)
audioGraph.add(voiceRecorderNode)
audioGraph.add(voiceRecorderNode)

audioGraph.add(voicePlayerNode)
audioGraph.add(guitarPlayerNode)
audioGraph.add(pianoPlayerNode)

audioGraph.add(compressorNode)
audioGraph.add(reverbNode)
audioGraph.add(flangerNode)

audioGraph.add(mixerNode)

audioGraph.connect(audioGraph.inputNode, to: voiceRecorderNode)
audioGraph.connect(audioGraph.inputNode, to: guitarRecorderNode)
audioGraph.connect(audioGraph.inputNode, to: pianoRecorderNode)

audioGraph.connect(voicePlayerNode, to: compressorNode)
audioGraph.connect(guitarPlayerNode, to: reverbNode)
audioGraph.connect(pianoPlayerNode, to: flangerNode)

audioGraph.connect(compressorNode, to: mixerNode)
audioGraph.connect(reverbNode, to: mixerNode)
audioGraph.connect(flangerNode, to: mixerNode)

audioGraph.connect(mixerNode, to: audioGraph.outputNode)

compressorNode.isEnabled = true
reverbNode.isEnabled = true
flangerNode.isEnabled = true

audioEngine.microphoneEnabled = true
audioEngine.start(audioGraph)
}

func startRecording(typeId: String) {
switch typeId {
case "voice": voiceRecorderNode.start()
case "guitar": guitarRecorderNode.start()
case "piano": pianoRecorderNode.start()
}
}

func stopRecording(typeId: String, fileName: String) {
switch typeId {
case "voice": voiceRecorderNode.stop(fileName, withFormat: currentFormat)
case "guitar": guitarRecorderNode.stop(fileName, withFormat: currentFormat)
case "piano": pianoRecorderNode.stop(fileName, withFormat: currentFormat)
}
}

func loadRecordingIntoAudioPlayer(path: String, typeId: String) {
switch typeId {
case "voice": voicePlayerNode.load(path, withFormat: currentFormat)
case "guitar": guitarPlayerNode.load(path, withFormat: currentFormat)
case "piano": pianoPlayerNode.load(path, withFormat: currentFormat)
}
}

func startAudioPlayer(typeId: String) {
switch typeId {
case "voice": voicePlayerNode.play()
case "guitar": guitarPlayerNode.play()
case "piano": pianoPlayerNode.play()
}
}

func stopAudioPlayer(typeId: String) {
switch typeId {
case "voice": voicePlayerNode.stop()
case "guitar": guitarPlayerNode.stop()
case "piano": pianoPlayerNode.stop()
}
}
}