I want to create a iOS app with AVAudioEngine, main function is record sound from mic and play sound on speaker. I want to switch between loud speaker(bottom speaker) and receiver(top speaker). My code is below, this work fine on receiver. My problem is sound can not play in loud speaker. Please help me, many thanks!
import AVFoundation
import UIKit
class XViewController: UIViewController {
let audioSession = AVAudioSession.sharedInstance()
var player: AVAudioPlayerNode!
var engine: AVAudioEngine!
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
setupAudioSession()
setupNotifications()
initRecorder()
}
func setupAudioSession() {
do{
try audioSession.setPreferredSampleRate(16000)
try self.audioSession.setPreferredOutputNumberOfChannels(1)
try audioSession.setCategory(.playAndRecord, options: [])
try audioSession.overrideOutputAudioPort(.none)
try audioSession.setMode(.voiceChat)
try audioSession.setActive(true)
} catch{
print(error.localizedDescription)
}
}
func initRecorder() {
player = AVAudioPlayerNode()
engine = AVAudioEngine()
let input = engine.inputNode
engine.attach(player)
let bus = 0
let inputFormat = input.inputFormat(forBus: bus)
engine.connect(player, to: engine.outputNode, format: inputFormat)
let mixerNode = AVAudioMixerNode()
engine.attach(mixerNode)
engine.connect(input, to: mixerNode, format: nil)
input.installTap(onBus: bus, bufferSize: 2048, format: inputFormat) { (buffer, time) -> Void in
self.player.scheduleBuffer(buffer)
print(buffer)
}
}
func start() {
engine.prepare()
try! engine.start()
player.play()
}
func playToLoudSpeaker() {
do{
try self.audioSession.overrideOutputAudioPort(.speaker)
} catch{
print(error.localizedDescription)
}
}
func setupNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleRouteChange),
name: AVAudioSession.routeChangeNotification,
object: nil)
}
@objc func handleRouteChange(notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
return
}
// Switch over the route change reason.
switch reason {
case .override :
resume()
default: break
}
}
func resume() {
self.engine.stop()
self.player.stop()
self.engine.detach(player)
self.initRecorder()
engine.prepare()
try? self.engine.start()
player.play()
}
@IBAction private func startRecorderAndPlay(_ sender: UIButton) {
start()
}
@IBAction private func switchToLoudSpeaker(_ sender: UIButton) {
playToLoudSpeaker()
}
}