I'm working on an app that does audio recording and playback of recorded audio. I'm using AVAudioSession
to change the category to playAndRecord
as well as passing it to defaultToSpeaker
.
My problem is, if I launch the app, play an earlier recording, it plays through the bottom (louder) speaker as I want it to and expected BUT if I launch the app and start recording a new memo then play it back, no matter what I do, it will always use the quieter (phone call) speaker that's next to front face camera. I need it to play from the louder speaker (actually all speakers if possible).
NOTE: I'm running iOS 14.2.1, my phone is set to silent mode (usually) though I tried other settings as well. I'm testing on an iPhone 12. Ideally, I'm not really looking for a 3rd party SDK or library solution as I want the playback to be simple and done natively.
This is how I set the audioSession in my viewModel (that's initialized from a viewController):
private func setupAudioSession() {
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth])
try session.setActive(true)
session.requestRecordPermission({ [weak self] allowed in
guard let self = self else { return }
if allowed == false {
self.settingsAlert?()
}
})
} catch {
debugPrint("error setting up AVAudioSession")
}
}
This is how I do recording:
func startRecording() {
guard let audioFileDir = getAudioFilePath(songId: songModel!.id) else { return }
let audioURLString = audioFileDir.absoluteString + ".m4a"
guard let audioURL = URL(string: audioURLString) else { return }
let recordingSettings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatLinearPCM),
AVLinearPCMIsNonInterleaved: false,
AVSampleRateKey: 44_100.0,
AVNumberOfChannelsKey: isStereoSupported ? 2 : 1,
AVLinearPCMBitDepthKey: 16
]
do {
audioRecorder = try AVAudioRecorder(url: audioURL, settings: recordingSettings)
audioUrl = audioURL
audioRecorder?.delegate = self
audioRecorder?.prepareToRecord()
audioRecorder?.record()
} catch {
audioRecorder = nil
debugPrint("Problem recording audio: \(error.localizedDescription)")
}
}
I even included some code from WWDC 2020 for stereo recording and that's what isStereoSupported does.
And this is how I do playback
do {
if let data = songDoc?.data?.audioData {
audioPlayer = try AVAudioPlayer(data: data)
} else if let url = audioUrl {
audioPlayer = try AVAudioPlayer(contentsOf: url)
}
audioPlayer?.delegate = self
audioPlayer?.play()
isPlaying = true
} catch {
audioPlayer = nil
debugPrint("playback error: \(error.localizedDescription)")
}
Of course I have tried adding things like:
try session.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
as well as setting the AVAudioSession each time I play or record. I've tried bunch of other similar things to fix this issue but nothing seems to work.
You should set audio session mode to AVAudioSessionModeVideoRecording and set session category options to AVAudioSessionCategoryOptionDefaultToSpeaker
[session setMode:AVAudioSessionModeVideoRecording error:&e]