Reputation: 71
I have an app that records the user's audio session while they are playing a video simultaneously. The issue is the playback from the video comes out of the speaker.
Ideally, I would like to cancel out the audio coming from the speaker and only record the user's voice through the microphone.
Example App: FaceTime Facetime cancels out the music you are playing when you are on a FaceTime call and only records the user's voice. (https://www.quora.com/How-does-an-iPhone-cancel-out-the-music-you-are-playing-when-you-FaceTime-someone)
Below is my code to add the audio session to record the user:
let audioInputDevice = AVCaptureDevice.default(
.builtInMicrophone,
for: .audio,
position: .unspecified
)
.default(for: AVMediaType.audio)!
let session = self.previewLiveCamera.cameraLayer!.session!
//TODO: fix Audio Input, do not mix with other video audio
do {
audioInput = try AVCaptureDeviceInput(device: audioInputDevice!)
session.addInput(audioInput!)
} catch {
print("failed to add audio input")
}
for output in session.outputs {
let connectionOutput = output as! AVCaptureMovieFileOutput
self.connectionOutput = connectionOutput
print("Started reacording to \(fileURL!)")
self.connectionOutput.startRecording(to: fileURL!, recordingDelegate: self)
}
Furthermore, I'm setting the videplayer for the video the user views simultaneously:
let postPlayer = AVPlayer(url: postVideoUrl!)
cell.playerLayer.player = postPlayer
Upvotes: 7
Views: 5386
Reputation: 4487
For those looking for a swift version Active Echo Cancellation [AEC] component, here is the repo I wrote: AECAudioStream
But you can no longer use high-level APIs like AVCaptureDevice
to capture audio. You have to store the AVAudioPCMBuffer
s with AVAudioFile
Here are the steps to use it:
To create a standard AudioStream without any special renderrer callback, you initialize a new instance of the AECAudioStream
class, and supply a sampling rate for the recorded Audio, as the following code shows:
/// Audio Samping at 16000Hz
let audioUnit = AECAudioStream(sampleRate: 16000)
After an AudioStream object is created, you can listen for recorded audio data by calling AECAudioStream/AECAudioStream/startAudioStream(enableAEC:)
it returns an AsyncThrowingStream
that yields AVAudioPCMBuffer
objects containing the captured audio data.
for try await pcmBuffer in audioUnit.startAudioStream(enableAEC: true) {
// here you get a ``AVAudioPCMBuffer`` data
audioFile?.write(from: pcmBuffer)
}
Upvotes: 1
Reputation: 451
After adding the input audio source you'll need to set the audio mode:
do {
audioInput = try AVCaptureDeviceInput(device: audioInputDevice!)
session.addInput(audioInput!)
} catch {
print("failed to add audio input")
}
// Add the following lines:
try! AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat)
try! AVAudioSession.sharedInstance().setActive(true)
.voiceChat will enable the device’s tonal equalization is optimized for voice and the set of allowable audio routes is reduced to only those appropriate for voice chat
https://developer.apple.com/documentation/avfoundation/avaudiosession/mode/1616455-voicechat
If the audio still has too much of the speaker audio present after setting the category you'd then need to look at enabling Active Echo Cancellation [AEC]. Here's an example of doing so under iOS:
https://github.com/twilio/video-quickstart-ios/blob/master/AudioDeviceExample/AudioDevices/ExampleAVAudioEngineDevice.m#L802 Note the VoiceProcessingIO et al.
Upvotes: 3