我需要播放基于输出设备的采样率生成的任意音调。用户需要连接有线或无线耳机才能收听音频。
由于现代耳机可以具有 44100 或 48000 的原生采样率,我需要以某种方式重新初始化我AVAudioPlayerNode
的不同AVAudioFormat
. 不做任何事情,音频开始听起来失真。但是,在尝试attach
、或. 有时它会在我第一次尝试切换耳机时锁定执行(我同时连接了 2 个耳机,一个 SR 为 44100 的通用耳机和 SR 为 48000 的 Airpods),并且很少会在第二个时冻结尝试。detach
connect
disconnectNodeOutput
这是我的代码:
private let engine = AVAudioEngine()
private let audioUnit = MyAudioUnit()
init() {
let audioSession = AVAudioSession.sharedInstance()
let sampleRate = audioSession.sampleRate
format = AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: sampleRate,
channels: AVAudioChannelCount(audioSession.outputNumberOfChannels),
interleaved: false
)!
engine.attach(audioUnit)
engine.connect(
audioUnit,
to: engine.mainMixerNode,
format: format
)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.handleInterruption),
name: Notification.Name.AVAudioEngineConfigurationChange,
object: engine
)
}
@objc
private func handleInterruption(_ notification: Notification) {
DispatchQueue.main.async {
let audioSession = AVAudioSession.sharedInstance()
let sampleRate = audioSession.sampleRate
self.format = AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: sampleRate,
channels: AVAudioChannelCount(audioSession.outputNumberOfChannels),
interleaved: false
)!
self.engine.detach(self.audioUnit)
self.engine.attach(self.audioUnit)
self.engine.connect(
self.audioUnit,
to: self.engine.mainMixerNode,
format: self.format
)
}
}
// method called on main thread only
// BTW, using audioUnit.stop() instead of pause() would also introduce a deadlock
func setActive(_ active: Bool) {
...
if active {
try! engine.start()
audioUnit.play()
} else {
audioUnit.pause()
engine.stop()
engine.reset()
}
}
我已经尝试了许多“重新连接”的变体,AVAudioEngine
但它们都陷入了以下僵局:
我也试过把它放在后台线程上,但这没关系。一旦应用程序尝试AVAudioEngine
再次使用,一切都会卡住。
那么,是否有适当的方法重新连接AVAudioPlayerNode
以更新采样率?或者也许可以不依赖耳机的原生采样率,而让音频正常地以静态采样率发声?提前致谢!