iOS 16 中的 AVAudioSession 路由问题 - 输入始终为内置麦克风。设置首选输入法不起作用



AVAudioSessionOS16InpitIssue

这是关于:

我有一个iOS";吉他效果";该应用程序从输入中获取音频信号,对其进行处理,并通过输出将结果音频播放回用户。该应用程序无法与iOS设备的内置麦克风配合使用(因为反馈(-用户必须通过特殊设备连接吉他:模拟设备如iRig,数字设备如iRigHD。

TL;DR:从iOS 16开始,我面临AVAudioSession的一个奇怪行为,它破坏了我的应用程序。在iOS 16中,AVAudioSession Route的输入始终为microphone BuiltIn-无论我是否连接任何外部麦克风,如iRig设备或带麦克风的耳机。即使我试图通过为AVAudioSession分配preferredInput手动切换到外部麦克风,它也不会改变路线——输入始终是MicrophoneBuiltIn。在iOS 15及更早版本中,iOS会自动更改连接到iOS设备的任何外部麦克风的路由输入。您可以通过为AVAudioSession分配preferredInput属性来控制输入。

这是一个重现该问题的最小示例项目。

项目结构:

这是一个非常小的项目,旨在重现这个问题。所有代码都在ViewController类中。

  1. 我创建了一个playAndRecord AVAudioSession并订阅routeChangeNotification通知:
NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange), name: AVAudioSession.routeChangeNotification, object: nil)
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playAndRecord, options: .mixWithOthers)
try audioSession.setActive(true, options: [])
} catch {
print("AVAudioSession init error: (error)")
}
  1. 当我收到通知时,我打印可用音频输入、首选输入和当前音频路径的列表:
@objc func handleRouteChange(notification: Notification) {
print("nHANDLE ROUTE CHANGE")
print("AVAILABLE INPUTS: (AVAudioSession.sharedInstance().availableInputs ?? [])")
print("PREFERRED INPUT: (String(describing: AVAudioSession.sharedInstance().preferredInput))")
print("CURRENT ROUTE: (AVAudioSession.sharedInstance().currentRoute)n")
}
  1. 我有一个按钮,它显示一个带有所有可用音频输入列表的警报,并提供将每个输入设置为首选的方法:
@IBAction func selectPreferredInputClick(_ sender: UIButton) {
let inputs = AVAudioSession.sharedInstance().availableInputs ?? []
let title = "Select Preferred Input"
let message = "Current Preferred Input: (String(describing: AVAudioSession.sharedInstance().preferredInput?.portName))nCurrent Route Input (String(describing: AVAudioSession.sharedInstance().currentRoute.inputs.first?.portName))"
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for input in inputs {
alert.addAction(UIAlertAction(title: input.portName, style: .default) {_ in
print("n(title)")
print("(message) New Preferred Input: (input.portName)n")
do {
try AVAudioSession.sharedInstance().setPreferredInput(input)
} catch {
print("Set Preferred Input Error: (error)")
}
})
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(alert, animated: true)
}

iOS 16行为:

当我在没有连接任何外部麦克风的情况下启动应用程序并启动AVAudioSession时,我有以下日志:

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2837101e0, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>]
PREFERRED INPUT: nil
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x283710a80, 
inputs = (
"<AVAudioSessionPortDescription: 0x283710a50, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x283710600, type = Receiver; name = Receiver; UID = Built-In Receiver; selectedDataSource = (null)>"
)>

这很好。然后我连接iRig设备(基本上是外部麦克风(,我有以下日志:

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x283718630, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>, <AVAudioSessionPortDescription: 0x283718500, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>]
PREFERRED INPUT: nil
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x283700140, 
inputs = (
"<AVAudioSessionPortDescription: 0x283700160, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2837001f0, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)>

如您所见,MicrophoneWired出现在可用输入列表中,但路线的输入仍然是MicrophoneBuiltIn。然后我试着把AVAudioSession的首选输入先改为MicrophoneWired,然后改为Microphone BuiltIn,然后再改为MicrohoneWired:

Select Preferred Input
Current Preferred Input: nil
Current Route Input Optional("iPhone Microphone") New Preferred Input: Headset Microphone

Select Preferred Input
Current Preferred Input: Optional("Headset Microphone")
Current Route Input Optional("iPhone Microphone") New Preferred Input: iPhone Microphone

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x28299da70, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>, <AVAudioSessionPortDescription: 0x28299d930, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>]
PREFERRED INPUT: Optional(<AVAudioSessionPortDescription: 0x282994330, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>)
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2829912d0, 
inputs = (
"<AVAudioSessionPortDescription: 0x282991820, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x282991740, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)>

Select Preferred Input
Current Preferred Input: Optional("iPhone Microphone")
Current Route Input Optional("iPhone Microphone") New Preferred Input: Headset Microphone

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x28299d7c0, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>, <AVAudioSessionPortDescription: 0x28299d8c0, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>]
PREFERRED INPUT: Optional(<AVAudioSessionPortDescription: 0x2829918e0, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>)
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x28299d530, 
inputs = (
"<AVAudioSessionPortDescription: 0x28299d510, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x28299d6d0, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)>

无论首选什么输入AudioSession路由的输入设备都是内置在中的麦克风

iOS 15行为:

iOS 15中的一切都不一样(而且好多了(。当我在没有连接任何外部麦克风的情况下启动应用程序并启动AVAudioSession时,我的日志与iOS 16:上的日志相同

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2813cc930, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>]
PREFERRED INPUT: nil
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2813cc9c0, 
inputs = (
"<AVAudioSessionPortDescription: 0x2813cc9b0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2813cc6b0, type = Speaker; name = Speaker; UID = Speaker; selectedDataSource = (null)>"
)>

然后我连接iRig设备(基本上是外部麦克风(,我有以下日志:

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2813d0450, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>, <AVAudioSessionPortDescription: 0x2813d04a0, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>]
PREFERRED INPUT: nil
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2813e40f0, 
inputs = (
"<AVAudioSessionPortDescription: 0x2813e4110, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2813e4150, type = Headphones; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:1; selectedDataSource = (null)>"
)>

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2813e40e0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>, <AVAudioSessionPortDescription: 0x2813e4160, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>]
PREFERRED INPUT: nil
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2813dc1c0, 
inputs = (
"<AVAudioSessionPortDescription: 0x2813dc1e0, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2813dc220, type = Headphones; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:1; selectedDataSource = (null)>"
)>

这里有两个主要区别:

  1. routeChangeNotification被调用了两次
  2. AVAudioSession路由的输入是MicrophoneWired然后,我尝试更改AVAudioSession的首选输入,并获得以下日志:
Select Preferred Input
Current Preferred Input: nil
Current Route Input Optional("YC136 USB AUDIO") New Preferred Input: iPad Microphone

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2813c8db0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>, <AVAudioSessionPortDescription: 0x2813c8e00, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>]
PREFERRED INPUT: Optional(<AVAudioSessionPortDescription: 0x2813d8ad0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>)
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2813c0c40, 
inputs = (
"<AVAudioSessionPortDescription: 0x2813c1300, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2813c10b0, type = Headphones; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:1; selectedDataSource = (null)>"
)>

Select Preferred Input
Current Preferred Input: Optional("iPad Microphone")
Current Route Input Optional("iPad Microphone") New Preferred Input: YC136 USB AUDIO

HANDLE ROUTE CHANGE
AVAILABLE INPUTS: [<AVAudioSessionPortDescription: 0x2813c0d50, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Top>, <AVAudioSessionPortDescription: 0x2813c0a20, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>]
PREFERRED INPUT: Optional(<AVAudioSessionPortDescription: 0x2813e4140, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>)
CURRENT ROUTE: <AVAudioSessionRouteDescription: 0x2813cdaa0, 
inputs = (
"<AVAudioSessionPortDescription: 0x2813cdad0, type = MicrophoneWired; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:2; selectedDataSource = (null)>"
); 
outputs = (
"<AVAudioSessionPortDescription: 0x2813cdeb0, type = Headphones; name = YC136 USB AUDIO; UID = AppleUSBAudioEngine:Generic:YC136 USB AUDIO:20170726905926:1; selectedDataSource = (null)>"
)>

如您所见,路线的输入与AVAudioSession 的首选输入相匹配

结论:

如果有任何方法可以使iOS 16的行为与iOS 15及以下版本的行为相同,请告诉我。我搜索了iOS 16的发布说明,没有发现任何关于AVAudioSession的内容。如果没有办法做到这一点,请让我知道什么是管理AVAudioSession路由输入源的正确方法。如有任何建议,不胜感激。

苹果发布了iOS 16.1,看起来这个问题在中得到了解决

最新更新