Skip to main content

Multitrack Recorder App

About 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.

In this example we will create the skeleton of a simple studio recorder app which will record the dry (clean), the wet (processed) and the monitor mix of the incoming audio. You can check out the available effects on the extensions page.

Visualizing the audio graph first makes the implementation super easy.


Code Example

import SwitchboardSDK
import SwitchboardSuperpowered

class MultitrackRecorderExample {

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

private let micInput = SBAudioSourceNode()
private let micDrySplitter = SBBusSplitterNode()
private let micDryRecorder = SBRecorderNode()
private let micCompressor = SBCompressorNode()
private let micReverb = SBReverbNode()
private let micWetSplitter = SBBusSplitterNode()
private let micWetRecorder = SBRecorderNode()
private let micGain = SBGainNode()

private let guitarInput = SBAudioSourceNode()
private let guitarDrySplitter = SBBusSplitterNode()
private let guitarDryRecorder = SBRecorderNode()
private let guitarFlanger = SBFlangerNode()
private let guitarWetSplitter = SBBusSplitterNode()
private let guitarWetRecorder = SBRecorderNode()
private let guitarGain = SBGainNode()

private let drumsInput = SBAudioSourceNode()
private let drumsDrySplitter = SBBusSplitterNode()
private let drumsDryRecorder = SBRecorderNode()
private let drumsBitcrusher = SBBitcrusherNode()
private let drumsWetSplitter = SBBusSplitterNode()
private let drumsWetRecorder = SBRecorderNode()
private let drumsGain = SBGainNode()

private let monitorMixer = SBMixerNode()
private let monitorSplitter = SBBusSplitterNode()
private let monitorRecorder = SBRecorderNode()

private let format: SBCodec = .wav

init() {
audioGraph.addNode(micInput)
audioGraph.addNode(micDrySplitter)
audioGraph.addNode(micDryRecorder)
audioGraph.addNode(micCompressor)
audioGraph.addNode(micReverb)
audioGraph.addNode(micWetSplitter)
audioGraph.addNode(micWetRecorder)
audioGraph.addNode(micGain)
audioGraph.addNode(guitarInput)
audioGraph.addNode(guitarDrySplitter)
audioGraph.addNode(guitarDryRecorder)
audioGraph.addNode(guitarFlanger)
audioGraph.addNode(guitarWetSplitter)
audioGraph.addNode(guitarWetRecorder)
audioGraph.addNode(guitarGain)
audioGraph.addNode(drumsInput)
audioGraph.addNode(drumsDrySplitter)
audioGraph.addNode(drumsDryRecorder)
audioGraph.addNode(drumsBitcrusher)
audioGraph.addNode(drumsWetSplitter)
audioGraph.addNode(drumsWetRecorder)
audioGraph.addNode(drumsGain)
audioGraph.addNode(monitorMixer)
audioGraph.addNode(monitorSplitter)
audioGraph.addNode(monitorRecorder)

audioGraph.connect(micInput, to: micDrySplitter)
audioGraph.connect(micDrySplitter, to: micDryRecorder)
audioGraph.connect(micDrySplitter, to: micCompressor)
audioGraph.connect(micCompressor, to: micReverb)
audioGraph.connect(micReverb, to: micWetSplitter)
audioGraph.connect(micWetSplitter, to: micWetRecorder)
audioGraph.connect(micWetSplitter, to: micGain)

audioGraph.connect(guitarInput, to: guitarDrySplitter)
audioGraph.connect(guitarDrySplitter, to: guitarDryRecorder)
audioGraph.connect(guitarDrySplitter, to: guitarFlanger)
audioGraph.connect(guitarFlanger, to: guitarWetSplitter)
audioGraph.connect(guitarWetSplitter, to: guitarWetRecorder)
audioGraph.connect(guitarWetSplitter, to: guitarGain)

audioGraph.connect(drumsInput, to: drumsDrySplitter)
audioGraph.connect(drumsDrySplitter, to: drumsDryRecorder)
audioGraph.connect(drumsDrySplitter, to: drumsBitcrusher)
audioGraph.connect(drumsBitcrusher, to: drumsWetSplitter)
audioGraph.connect(drumsWetSplitter, to: drumsWetRecorder)
audioGraph.connect(drumsWetSplitter, to: drumsGain)

audioGraph.connect(micGain, to: monitorMixer)
audioGraph.connect(guitarGain, to: monitorMixer)
audioGraph.connect(drumsGain, to: monitorMixer)
audioGraph.connect(monitorMixer, to: monitorSplitter)
audioGraph.connect(monitorSplitter, to: monitorRecorder)
audioGraph.connect(monitorSplitter, to: audioGraph.outputNode)

micCompressor.isEnabled = true
micReverb.isEnabled = true
guitarFlanger.isEnabled = true
drumsBitcrusher.isEnabled = true

audioEngine.start(audioGraph)
}

func startRecording() {
micDryRecorder.start()
micWetRecorder.start()
guitarDryRecorder.start()
guitarWetRecorder.start()
drumsDryRecorder.start()
drumsWetRecorder.start()
monitorRecorder.start()
}

func stopRecording(filePath: String) {
micDryRecorder.stop(filePath + "microphone_dry" + format.fileExtension,
withFormat: format)
micWetRecorder.stop(filePath + "microphone_wet" + format.fileExtension,
withFormat: format)
guitarDryRecorder.stop(filePath + "guitar_dry" + format.fileExtension,
withFormat: format)
guitarWetRecorder.stop(filePath + "guitar_wet" + format.fileExtension,
withFormat: format)
drumsDryRecorder.stop(filePath + "drums_dry" + format.fileExtension,
withFormat: format)
drumsWetRecorder.stop(filePath + "drums_wet" + format.fileExtension,
withFormat: format)
monitorRecorder.stop(filePath + "monitor_mix" + format.fileExtension,
withFormat: format)
}
}