```
我将从一个简单的“playground”视图控制器类开始,这个类展示了我的问题:
```class AudioEnginePlaygroundViewController: UIViewController {
private var audioEngine: AVAudioEngine!
private var micTapped = false
override func viewDidLoad() {
super.viewDidLoad()
configureAudioSession()
audioEngine = AVAudioEngine()
}
@IBAction func toggleMicTap(_ sender: Any) {
guard let mic = audioEngine.inputNode else {
return
}
if micTapped {
mic.removeTap(onBus: 0)
micTapped = false
return
}
stopAudioPlayback()
let micFormat = mic.inputFormat(forBus: 0)
print("installing tap: \(micFormat.sampleRate) -- \(micFormat.channelCount)")
mic.installTap(onBus: 0, bufferSize: 2048, format: micFormat) { (buffer, when) in
print("in tap completion")
let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
}
micTapped = true
startEngine()
}
@IBAction func playAudioFile(_ sender: Any) {
stopAudioPlayback()
let playerNode = AVAudioPlayerNode()
let audioUrl = Bundle.main.url(forResource: "test_audio", withExtension: "wav")!
let audioFile = readableAudioFileFrom(url: audioUrl)
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.outputNode, format: audioFile.processingFormat)
startEngine()
playerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)
playerNode.play()
}
// MARK: Internal Methods
private func configureAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.mixWithOthers, .defaultToSpeaker])
try AVAudioSession.sharedInstance().setActive(true)
} catch { }
}
private func readableAudioFileFrom(url: URL) -> AVAudioFile {
var audioFile: AVAudioFile!
do {
try audioFile = AVAudioFile(forReading: url)
} catch { }
return audioFile
}
private func startEngine() {
guard !audioEngine.isRunning else {
return
}
do {
try audioEngine.start()
} catch { }
}
private func stopAudioPlayback() {
audioEngine.stop()
audioEngine.reset()
}
}
上述VC有一个AVAudioEngine实例和两个UIButton操作:一个是播放在硬编码URL找到的音频文件,另一个是切换在引擎的上安装/删除tap。我的目标是同时使直播麦克风捕捉和音频文件播放工作,但完全互相排斥。也就是说,我想无论当前的麦克风tap状态如何都能触发播放,反之亦然。如果在触发音频文件播放之前安装tap,则一切按预期完全正常运行。但是,如果首先播放音频文件,然后尝试安装tap,则会出现以下崩溃:
[avae] AVAEInternal.h:70:_AVAE_Check: required condition is false: [AVAEGraphNode.mm:810:CreateRecordingTap: (IsFormatSampleRateAndChannelCountValid(format))]
由于遇到问题,我通过在installTap调用上面的日志语句来检查麦克风格式的数据。果然,当我在播放之前安装tap时,我得到了预期的采样率44100.0和1个通道计数。但是当我先播放音频文件,然后再安装麦克风tap时,我的日志显示采样率为0,通道计数为2,这就引发了上述错误。
我尝试过调整AVAudioEngine的启动/重置流程,尝试了不同的AVAudioSession类别/模式组合(请参见我的configureAudioSession方法),并尝试手动创建tap格式,如下所示:
let micFormat = mic.inputFormat(forBus: 0)
var trueFormat: AVAudioFormat!
if micFormat.sampleRate == 0 {
trueFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)
} else {
trueFormat = micFormat
}
print("installing tap: \(micFormat.sampleRate) -- \(micFormat.channelCount)")
mic.installTap(onBus: 0, bufferSize: 2048, format: trueFormat) { (buffer, when) in
print("in tap completion")
let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
}
这给了我一个类似但不同的错误:
[avae] AVAEInternal.h:70:_AVAE_Check: required condition is false: [AVAudioIONodeImpl.mm:896:SetOutputFormat: (IsFormatSampleRateAndChannelCountValid(hwFormat))]
我无法理解麦克风的格式数据为什么会因为是否使用AVAudioPlayerNode而有所不同。