Commit fdde0e12 authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Kateryna Kostiuk

audio: fix audio switching

- Removes samplerate hack which fixed input but exposed requirement
  for output downsampling in the event of an core layer samplerate
  reduction. The audio session instance allows bluetooth after the
  daemon initializes, allowing the input samplerate to be correctly
  fetched.
- Sets the audio device to speakerphone or headset at app init and
  when a call is terminated so the ringtone can be heard.

Change-Id: I99ebaca55dfd7295801626b8fa4f64ea24a5abb8
Reviewed-by: Kateryna Kostiuk's avatarKateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
parent 7898b758
...@@ -89,6 +89,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -89,6 +89,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
SystemAdapter().registerConfigurationHandler() SystemAdapter().registerConfigurationHandler()
self.startDaemon() self.startDaemon()
// sets output device to whatever is currently available (either spk / headset)
self.audioService.startAVAudioSession()
// disables hardware decoding // disables hardware decoding
self.videoService.setDecodingAccelerated(withState: false) self.videoService.setDecodingAccelerated(withState: false)
...@@ -106,9 +109,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -106,9 +109,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}) })
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
// set device to headset if present
self.audioService.overrideAudioRoute(.override)
// themetize the app // themetize the app
Chameleon.setGlobalThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light) Chameleon.setGlobalThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light)
Chameleon.setRingThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light) Chameleon.setRingThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light)
......
...@@ -293,6 +293,9 @@ class CallViewModel: Stateable, ViewModel { ...@@ -293,6 +293,9 @@ class CallViewModel: Stateable, ViewModel {
} }
self.callService.hangUp(callId: call.callId) self.callService.hangUp(callId: call.callId)
.subscribe(onCompleted: { [weak self] in .subscribe(onCompleted: { [weak self] in
// switch to either spk or headset (if connected) for loud ringtone
// incase we were using rcv during the call
self?.audioService.setToRing()
self?.log.info("Call canceled") self?.log.info("Call canceled")
}, onError: { [weak self] error in }, onError: { [weak self] error in
self?.log.error("Failed to cancel the call") self?.log.error("Failed to cancel the call")
...@@ -300,6 +303,10 @@ class CallViewModel: Stateable, ViewModel { ...@@ -300,6 +303,10 @@ class CallViewModel: Stateable, ViewModel {
} }
func answerCall() -> Completable { func answerCall() -> Completable {
// switch to rcv if that's what we were last using
if !self.audioService.isHeadsetConnected.value && !self.audioService.isOutputToSpeaker.value {
self.audioService.overrideToReceiver()
}
return self.callService.accept(call: call) return self.callService.accept(call: call)
} }
...@@ -308,10 +315,9 @@ class CallViewModel: Stateable, ViewModel { ...@@ -308,10 +315,9 @@ class CallViewModel: Stateable, ViewModel {
guard let account = self.accountService.currentAccount else { guard let account = self.accountService.currentAccount else {
return return
} }
if isAudioOnly { // switch to rcv if audio only and no headset connected
if isAudioOnly && !self.audioService.isHeadsetConnected.value {
self.audioService.overrideToReceiver() self.audioService.overrideToReceiver()
} else {
self.audioService.overrideToSpeaker()
} }
self.callService.placeCall(withAccount: account, self.callService.placeCall(withAccount: account,
toRingId: uri, toRingId: uri,
......
...@@ -27,7 +27,6 @@ enum OutputPortType: Int { ...@@ -27,7 +27,6 @@ enum OutputPortType: Int {
case bluetooth = 1 case bluetooth = 1
case headphones = 2 case headphones = 2
case receiver = 3 case receiver = 3
case dummy = 4
} }
class AudioService { class AudioService {
...@@ -54,6 +53,18 @@ class AudioService { ...@@ -54,6 +53,18 @@ class AudioService {
object: nil) object: nil)
} }
func startAVAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord,
with: AVAudioSessionCategoryOptions.allowBluetooth)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
log.error("\(error)")
}
setToRing()
}
// swiftlint:disable force_cast
@objc private func audioRouteChangeListener(_ notification: Notification) { @objc private func audioRouteChangeListener(_ notification: Notification) {
let reasonRaw = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as! UInt let reasonRaw = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as! UInt
self.log.debug("Audio route change: \(reasonRaw)") self.log.debug("Audio route change: \(reasonRaw)")
...@@ -62,12 +73,13 @@ class AudioService { ...@@ -62,12 +73,13 @@ class AudioService {
} }
overrideAudioRoute(reason) overrideAudioRoute(reason)
} }
// swiftlint:enable force_cast
func overrideAudioRoute(_ reason: AVAudioSessionRouteChangeReason) { func overrideAudioRoute(_ reason: AVAudioSessionRouteChangeReason) {
let wasHeadsetConnected = isHeadsetConnected.value let wasHeadsetConnected = isHeadsetConnected.value
let bluetoothConnected = bluetoothAudioConnected() let bluetoothConnected = bluetoothAudioConnected()
let headphonesConnected = headphoneAudioConnected() let headphonesConnected = headphoneAudioConnected()
self.log.debug("Audio route status: bluetooth: \(bluetoothConnected), headphones: \(headphonesConnected)") self.log.debug("Audio route override - reason: \(reason.rawValue), status: bluetooth: \(bluetoothConnected), headphones: \(headphonesConnected)")
isHeadsetConnected.value = bluetoothConnected || headphonesConnected isHeadsetConnected.value = bluetoothConnected || headphonesConnected
if reason == .override && !isHeadsetConnected.value { if reason == .override && !isHeadsetConnected.value {
setAudioOutputDevice(port: OutputPortType.builtinspk) setAudioOutputDevice(port: OutputPortType.builtinspk)
...@@ -80,10 +92,6 @@ class AudioService { ...@@ -80,10 +92,6 @@ class AudioService {
let outputPort = isOutputToSpeaker.value ? OutputPortType.builtinspk : OutputPortType.receiver let outputPort = isOutputToSpeaker.value ? OutputPortType.builtinspk : OutputPortType.receiver
setAudioOutputDevice(port: outputPort) setAudioOutputDevice(port: outputPort)
} }
} else if reason == .categoryChange && (isHeadsetConnected.value || !isOutputToSpeaker.value) {
// Hack switch to dummy device for first call using bluetooth/headset/receiver
// allowing the samplerate for the input bus to be correctly set
setAudioOutputDevice(port: OutputPortType.dummy)
} }
} }
...@@ -98,6 +106,12 @@ class AudioService { ...@@ -98,6 +106,12 @@ class AudioService {
} }
} }
func setToRing() {
if !isHeadsetConnected.value {
setAudioOutputDevice(port: OutputPortType.builtinspk)
}
}
func overrideToSpeaker() { func overrideToSpeaker() {
isOutputToSpeaker.value = true isOutputToSpeaker.value = true
setAudioOutputDevice(port: OutputPortType.builtinspk) setAudioOutputDevice(port: OutputPortType.builtinspk)
......
...@@ -253,7 +253,7 @@ class CallsService: CallsAdapterDelegate { ...@@ -253,7 +253,7 @@ class CallsService: CallsAdapterDelegate {
let accountID = call?.accountId let accountID = call?.accountId
self.sendVCard(callID: callId, accountID: accountID!) self.sendVCard(callID: callId, accountID: accountID!)
} }
//Emit the call to the observers //Emit the call to the observers
self.currentCall.onNext(call!) self.currentCall.onNext(call!)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment