Sotiris Kaniras
Sotiris Kaniras

Reputation: 680

How to record audio using AudioKit while playing an audio file?

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.

Upvotes: 1

Views: 239

Answers (1)

tonyK
tonyK

Reputation: 36

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.

Upvotes: 0

Related Questions