Skip to main content

Karaoke App

This example demonstrates how to build a simple karaoke app from Switchboard SDK components.

We need to create an AudioPlayerNode which plays the base song and a RecorderNode which records our voice. When we are done with the recording we can mix together the base song and the recording using a MixerNode, and we can also apply a wide range of effects, like Autotune, Echo, etc. Refer to our FX Chains example to see an example implementation of audio effects usage building on top of this example. To check the list of available effects please visit out our extensions page.

We can render the result of the AudioGraph into an audio file, which will contain the base song mixed with our recording.

To play this newly created audio file, create a new AudioGraph with an AudioPlayerNode, and pass it to the AudioEngine start method.

We can visualize the audio graph to get a grasp on how the nodes are connected. Let's take a look at the AudioGraph which does the recording while playing the base song:

The graph that does the mixing of the recording with the base song looks as follows:

The graph that plays the base song mixed with the recorded voice is a pretty simple one, since it contains just an AudioPlayerNode so we don't need to visualize that.

import SwitchboardSDK

class KaraokeExample {

let audioEngine = SBAudioEngine()
let recorderNode: SBRecorderNode
let baseSongAudioPlayerNode = SBAudioPlayerNode()
let mixedSongAudioPlayerNode = SBAudioPlayerNode()
let audioGraph = SBAudioGraph()
let mixedSongAudioGraph = SBAudioGraph()

let recordedFileName: String = "recording"
let mixedSongFilePath: String = "mixed_song_path"
let currentFormat: SBCodec = .wav

init() {
recorderNode = SBRecorderNode(
name: "TestRecorderNode",
recordingSampleRate: 48000,
numberOfRecordedChannels: 2
)

audioGraph.addNode(recorderNode)
audioGraph.addNode(baseSongAudioPlayerNode)
audioGraph.connect(audioGraph.inputNode, to: recorderNode)
audioGraph.connect(baseSongAudioPlayerNode, to: audioGraph.outputNode)

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

func loadBaseSong(path: String, codec: SBCodec) {
baseSongAudioPlayerNode.load(path, withFormat: codec)
}

func playBaseSongAndRecord() {
baseSongAudioPlayerNode.play()
recorderNode.start()
}

func stopBaseSongAndRecording() {
recorderNode.stop(recordedFileName, withFormat: currentFormat)
baseSongAudioPlayerNode.stop()
}

// This call could be time consuming, should not be called on the UI thread.
func mixBaseSongWithRecording(outputFilePath: String) {
let mixAudioGraph = SBAudioGraph()
let recordedVoicePlayer = SBAudioPlayerNode()
let mixerNode = SBMixerNode()

let autotuneNode = SBAutotuneNode()
autotuneNode.scale = SBAutotuneNode.TunerScale.CMAJOR
autotuneNode.range = SBAutotuneNode.TunerRange.ALTO
autotuneNode.speed = SBAutotuneNode.TunerSpeed.EXTREME
autotuneNode.frequencyOfA = 440
autotuneNode.isEnabled = true

let echoNode = SBEchoNode()
echoNode.isEnabled = true

recordedVoicePlayer.load(recorderNode.getFilePath(), withFormat: currentFormat)

mixAudioGraph.addNode(recordedVoicePlayer)
mixAudioGraph.addNode(autotuneNode)
mixAudioGraph.addNode(echoNode)
mixAudioGraph.addNode(baseSongAudioPlayerNode)
mixAudioGraph.addNode(mixerNode)

mixAudioGraph.connect(recordedVoicePlayer, to: autotuneNode)
mixAudioGraph.connect(autotuneNode, to: echoNode)
mixAudioGraph.connect(echoNode, to: mixerNode)
mixAudioGraph.connect(baseSongAudioPlayerNode, to: mixerNode)
mixAudioGraph.connect(mixerNode, to: audioGraph.outputNode)

recordedVoicePlayer.play()
baseSongAudioPlayerNode.play()

mixAudioGraph.renderToFile(mixedSongFilePath)

mixedSongAudioPlayerNode.load(outputFilePath, withFormat: currentFormat)
}

func initMixedSongGraph() {
mixedSongAudioGraph.addNode(mixedSongAudioPlayerNode)
mixedSongAudioGraph.connect(mixedSongAudioPlayerNode, to: mixedSongAudioGraph.outputNode)
audioEngine.start(mixedSongAudioGraph)

mixedSongAudioPlayerNode.load(mixedSongFilePath, withFormat: currentFormat)
}

func playMixedSong() {
mixedSongAudioPlayerNode.play()
}

func pauseMixedSong() {
mixedSongAudioPlayerNode.pause()
}

func stopMixedSong() {
mixedSongAudioPlayerNode.stop()
}

func close() {
audioEngine.close()
audioGraph.close()
recorderNode.close()
baseSongAudioPlayerNode.close()
}
}