I want to record my voice when i am playing audio (karaoke). I use a mixer with reverb and delay to monitor my voice
- it works great with wired headphones and bluetooth
- give a high pitch feedback using the internal mic in the iphone (obviously)
how can I configure my code to:
- do not use a monitoring when using internal mic
- use monitoring when using extenal mic
- or any other idea to avoid the feedback? (use top speaker, gain to 0 etc)
I read somewhere that I need to use booster.gain = 0 or something like that, but the new audiokit doesn't have any booster at all.
Now gpt told me I needed to update or bypass my mixer, which didn't work and perplexity ai told me i needed to set the volume of the mixer to zero, which didn't work either.
How can I stop the feedback if using my internal mic?
import AudioKit
import AVFoundation
class Conductor {
static let sharedInstance = Conductor()
// Instantiate the audio engine and Mic Input node objects
let engine = AudioEngine()
var mic: AudioEngine.InputNode!
// Add effects for the Mic Input.
var delay: Delay!
var reverb: Reverb!
let mixer = Mixer()
var recorder: NodeRecorder!
// MARK: Initialize the audio engine settings.
init() {
do {
Settings.bufferLength = .medium //short //veryshort
try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(Settings.bufferLength.duration)
try AVAudioSession.sharedInstance().setCategory(.playAndRecord,
options: [.defaultToSpeaker, .mixWithOthers, .allowBluetoothA2DP])
try AVAudioSession.sharedInstance().setActive(true)
} catch let err {
print(err)
}
// The audio signal path with be:
// input > mic > delay > reverb > mixer > output
// Mic is connected to the audio engine's input...
mic = engine.input
// Mic goes into the delay...
delay = Delay(mic)
delay.time = AUValue(0.3)
delay.feedback = AUValue(7.0)
delay.dryWetMix = AUValue(7.0)
// Delay output goes into the reverb...
reverb = Reverb(delay)
reverb.loadFactoryPreset(.mediumRoom)
reverb.dryWetMix = AUValue(0.4)
// Reverb output goes into the mixer...
mixer.addInput(reverb)
// Engine output is connected to the mixer.
engine.output = mixer
// Uncomment the following method, if you don't want to Start and stop the audio engine via the SceneDelegate.
startAudioEngine()
}
// MARK: Start and stop the audio engine via the SceneDelegate
func startAudioEngine() {
do {
print("Audio engine was started.")
try engine.start()
} catch {
Log("AudioKit did not start! \(error)")
}
}
func prepareToRecord() {
updateMixerStatus() // Update mixer status before recording
//only the first time
if !engine.avEngine.isRunning {
do {
print("Audio engine was started.")
try engine.start()
} catch {
Log("AudioKit did not start! \(error)")
}
}
//this was old code in the prev implementation
do {
recorder = try NodeRecorder(node: mixer, shouldCleanupRecordings: true)
} catch {
print("error to start recording")
}
}
// Method to enable/disable mixer based on mic usage
// func updateMixerStatus() {
// if isUsingInternalMic() {
// // Bypass or disable the mixer when using internal mic
// mixer.volume = 0 // or use mixer.isBypassed = true if available
// } else {
// // Enable the mixer when not using internal mic
// mixer.volume = 1 // or use mixer.isBypassed = false if available
// }
// }
func updateMixerStatus() {
if isUsingInternalMic() {
// Bypass or disable the mixer when using internal mic
// Check if bypass is supported, if not, set the engine's output to reverb
engine.output = reverb
} else {
// Enable the mixer when not using internal mic
engine.output = mixer
}
}
func isUsingInternalMic() -> Bool {
let route = AVAudioSession.sharedInstance().currentRoute
return route.inputs.contains { $0.portType == .builtInMic }
}
func startRecording() {
do {
try recorder.record()
} catch {
print("Error starting recording: \(error)")
}
}
func stopRecording() {
recorder.stop()
if let mainMixerNode = Conductor.sharedInstance.engine.mainMixerNode {
mainMixerNode.removeInput(Conductor.sharedInstance.reverb)
mainMixerNode.removeInput(Conductor.sharedInstance.delay)
}
}
func stopAudioEngine() {
engine.stop()
print("Audio engine was stopped.")
}
}
Edit1: I read in another response that I need to add a silence input, I tried that but I still hear the big screech.
func updateMixerStatus() {
if isUsingInternalMic() {
self.silencer = Fader(engine.input!, gain: 0)
mixer.addInput(self.silencer!)
} else {
// Enable the mixer when not using internal mic
//engine.output?.start()
if mixer.hasInput(self.silencer!) {
mixer.removeInput(self.silencer!)
}
}
}
Edit2: I tried to implement a Fader as suggested here, but didn't work (not even record) https://github.com/AudioKit/Cookbook/blob/main/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/Recorder.swift
You could try setting the
setVoiceProcessingEnabledproperty of the input node totruewhen recording with the internal mic as it is supposed to cancel the output from speaker to the mic. But it's likely that the resulting mic processing will ignore the Karaoke audio playing through the phone. You could try merging it in the next step though.