I'm trying to record audio and apply real-time pitch shifting and write the output to an AVAudioFile, all that using AudioKit. At the same time, I want to play an audio file for the duration of the recording, using AVPlayer.
The thing is that if I play an audio file during the recording, there's a very high-pitch feedback loop echoing very loudly! If on the other hand I perform the recording simply by using AVAudioRecorder, there's zero feedback loop.
Here's how I record the processed and unprocessed audio using taps on AudioKit's engine; (you'll notice that I set both AVAudioSession's and AudioKit's Settings category.)
class PitchCorrectionAudioKitService {
private let engine = AudioEngine()
private var pitchShiftEffect: PitchShifter!
private var unprocessedAudioFile: AVAudioFile?
private var processedAudioFile: AVAudioFile?
// ------------------
@Injected private var pitchDetectionService: PitchDetectionRepository
// ------------------
init() {
guard let input = engine.input else { return }
pitchShiftEffect = PitchShifter(input)
engine.output = pitchShiftEffect
}
func start(baseNote: Note) {
handleAudioSessionCategory()
pitchDetectionService.start { data in
self.autoCorrectPitch(data, baseNote: baseNote)
}
try! engine.start()
engine.input?.avAudioNode.installTap(onBus: 0, bufferSize: 4096, format: nil) { buffer, time in
try! self.unprocessedAudioFile?.write(from: buffer)
}
engine.output?.avAudioNode.installTap(onBus: 0, bufferSize: 4096, format: nil) { buffer, time in
try! self.processedAudioFile?.write(from: buffer)
}
}
private func handleAudioSessionCategory() {
if UIDevice.primaryAudioDevice == .defaultSpeakers {
try! AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth])
try! Settings.setSession(category: .playAndRecord, with: [.defaultToSpeaker, .allowBluetooth])
} else {
try! AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .videoRecording)
try! Settings.setSession(category: .playAndRecord)
}
try! AVAudioSession.sharedInstance().setActive(true)
}
}
The moment I start recording, I also play a music track;
private var musicTrackPlayer: AVPlayer?
musicTrackPlayer?.pause()
musicTrackPlayer?.volume = areHeadphonesConnected ? 0.4 : 0.01
musicTrackPlayer?.play()
Any ideas?
EDIT; I'm using version 5.6.1.
Recording while playing may not be a sensitive solution.
However, in IOS you can use
AudioUnit(kAudioUnitSubType_VoiceProcessingIO)to split the speaker and the mic.