在 channelCount 条件下从缓冲区播放时 AKPlayer 崩溃
AKPlayer crashes when playing from buffer on channelCount condition
我努力使以下场景按预期工作(代码将在下面提供)。
记录我的麦克风输入并在内存中存储一个 AVAudioPCMBuffer
,这是通过 AVAudioPCMBuffer
扩展方法 copy(from buffer: AVAudioPCMBuffer, readOffset: AVAudioFrameCount = default, frames: AVAudioFrameCount = default)
完成的。 我确实在录制结束时得到了缓冲区。
录制结束后将缓冲区传递给AKPlayer
并播放。这是一个代码片段来演示我所做的事情(我知道这不是完整的应用程序代码,如果需要我可以分享它):
.
private var player: AKPlayer = AKPlayer()
self.player.buffering = .always
// in the record complete callbak:
self.player.buffer = self.bufferRecorder?.pcmBuffer
self.player.volume = 1
self.player.play()
- 请注意,电镀机连接到混音器,混音器最终连接到 AudioKit 输出。
当我检查和调试应用程序时,我可以看到缓冲区的长度正确,而且我的所有 output/input 设置都使用相同的处理格式(采样率、通道、比特率等)以及缓冲区已记录,但我的应用程序仍然在此行崩溃:
2018-10-28 08:40:32.625001+0200 BeatmanApp[71037:6731884] [avae] AVAEInternal.h:70:_AVAE_Check:
required condition is false: [AVAudioPlayerNode.mm:665:ScheduleBuffer: (_outputFormat.channelCount == buffer.format.channelCount)]
当我调试并遍历 AudioKit 代码时,我可以看到中断线位于 line 162
上的 AKPlayer+Playback.swift
方法上:playerNode.scheduleBuffer
更多可能有用的信息:
- 记录的缓冲区有 16 秒长。
- 当我尝试在 tap 方法中将缓冲区直接传递给播放器节点时,它似乎起作用了,我确实听到了从麦克风到扬声器的延迟,但它确实播放了。
- 我试过在调用 play 方法之前在播放器上调用 prepare,没有帮助
谢谢!
好的,这是超级不酷的调试会话。我不得不调查 AVAudioEngine
以及如何在那里完成这种情况,这当然不是我正在寻找的最终结果。这个任务帮助我理解了如何用 AudioKit
解决它(我的应用程序有一半是使用 AudioKit
的工具实现的,所以用 AVFoundation
重写它没有意义)。
AFFoundation
解决办法:
private let engine = AVAudioEngine()
private let bufferSize = 1024
private let p: AVAudioPlayerNode = AVAudioPlayerNode()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
} catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}
let inputNode = self.engine.inputNode
engine.connect(inputNode, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))
// !!! the following lines are the key to the solution.
// !!! the player has to be attached to the engine before actually connected
engine.attach(p)
engine.connect(p, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))
do {
try engine.start()
} catch {
print("could not start engine \(error.localizedDescription)")
}
recordBufferAndPlay(duration: 4)
recordBufferAndPlay
函数:
func recordBufferAndPlay(duration: Double){
let inputNode = self.engine.inputNode
let total: Double = AVAudioSession.sharedInstance().sampleRate * duration
let totalBufferSize: UInt32 = UInt32(total)
let recordedBuffer : AVAudioPCMBuffer! = AVAudioPCMBuffer(pcmFormat: inputNode.inputFormat(forBus: 0), frameCapacity: totalBufferSize)
var alreadyRecorded = 0
inputNode.installTap(onBus: 0, bufferSize: 256, format: inputNode.inputFormat(forBus: 0)) {
(buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
recordedBuffer.copy(from: buffer) // this helper function is taken from audio kit!
alreadyRecorded = alreadyRecorded + Int(buffer.frameLength)
print(alreadyRecorded, totalBufferSize)
if(alreadyRecorded >= totalBufferSize){
inputNode.removeTap(onBus: 0)
self.p.scheduleBuffer(recordedBuffer, at: nil, options: .loops, completionHandler: {
print("completed playing")
})
self.p.play()
}
}
}
AudioKit
解决办法:
所以在 AudioKit 解决方案中,应该在您的 AKPlayer 对象上调用这些行。请注意,这应该在您实际启动引擎之前完成。
self.player.buffering = .always
AudioKit.engine.attach(self.player.playerNode)
AudioKit.engine.connect(self.player.playerNode, to: self.mixer.inputNode, format: AudioKit.engine.inputNode.outputFormat(forBus: 0))
录制的方式与您在 AVAudioEngine 中的录制方式非常相似,您可以在节点(麦克风或其他节点)上安装一个 tap 并录制 PCM 样本的缓冲区。
我努力使以下场景按预期工作(代码将在下面提供)。
记录我的麦克风输入并在内存中存储一个
AVAudioPCMBuffer
,这是通过AVAudioPCMBuffer
扩展方法copy(from buffer: AVAudioPCMBuffer, readOffset: AVAudioFrameCount = default, frames: AVAudioFrameCount = default)
完成的。 我确实在录制结束时得到了缓冲区。录制结束后将缓冲区传递给
AKPlayer
并播放。这是一个代码片段来演示我所做的事情(我知道这不是完整的应用程序代码,如果需要我可以分享它):
.
private var player: AKPlayer = AKPlayer()
self.player.buffering = .always
// in the record complete callbak:
self.player.buffer = self.bufferRecorder?.pcmBuffer
self.player.volume = 1
self.player.play()
- 请注意,电镀机连接到混音器,混音器最终连接到 AudioKit 输出。
当我检查和调试应用程序时,我可以看到缓冲区的长度正确,而且我的所有 output/input 设置都使用相同的处理格式(采样率、通道、比特率等)以及缓冲区已记录,但我的应用程序仍然在此行崩溃:
2018-10-28 08:40:32.625001+0200 BeatmanApp[71037:6731884] [avae] AVAEInternal.h:70:_AVAE_Check:
required condition is false: [AVAudioPlayerNode.mm:665:ScheduleBuffer: (_outputFormat.channelCount == buffer.format.channelCount)]
当我调试并遍历 AudioKit 代码时,我可以看到中断线位于 line 162
上的 AKPlayer+Playback.swift
方法上:playerNode.scheduleBuffer
更多可能有用的信息:
- 记录的缓冲区有 16 秒长。
- 当我尝试在 tap 方法中将缓冲区直接传递给播放器节点时,它似乎起作用了,我确实听到了从麦克风到扬声器的延迟,但它确实播放了。
- 我试过在调用 play 方法之前在播放器上调用 prepare,没有帮助
谢谢!
好的,这是超级不酷的调试会话。我不得不调查 AVAudioEngine
以及如何在那里完成这种情况,这当然不是我正在寻找的最终结果。这个任务帮助我理解了如何用 AudioKit
解决它(我的应用程序有一半是使用 AudioKit
的工具实现的,所以用 AVFoundation
重写它没有意义)。
AFFoundation
解决办法:
private let engine = AVAudioEngine()
private let bufferSize = 1024
private let p: AVAudioPlayerNode = AVAudioPlayerNode()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
} catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}
let inputNode = self.engine.inputNode
engine.connect(inputNode, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))
// !!! the following lines are the key to the solution.
// !!! the player has to be attached to the engine before actually connected
engine.attach(p)
engine.connect(p, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))
do {
try engine.start()
} catch {
print("could not start engine \(error.localizedDescription)")
}
recordBufferAndPlay(duration: 4)
recordBufferAndPlay
函数:
func recordBufferAndPlay(duration: Double){
let inputNode = self.engine.inputNode
let total: Double = AVAudioSession.sharedInstance().sampleRate * duration
let totalBufferSize: UInt32 = UInt32(total)
let recordedBuffer : AVAudioPCMBuffer! = AVAudioPCMBuffer(pcmFormat: inputNode.inputFormat(forBus: 0), frameCapacity: totalBufferSize)
var alreadyRecorded = 0
inputNode.installTap(onBus: 0, bufferSize: 256, format: inputNode.inputFormat(forBus: 0)) {
(buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
recordedBuffer.copy(from: buffer) // this helper function is taken from audio kit!
alreadyRecorded = alreadyRecorded + Int(buffer.frameLength)
print(alreadyRecorded, totalBufferSize)
if(alreadyRecorded >= totalBufferSize){
inputNode.removeTap(onBus: 0)
self.p.scheduleBuffer(recordedBuffer, at: nil, options: .loops, completionHandler: {
print("completed playing")
})
self.p.play()
}
}
}
AudioKit
解决办法:
所以在 AudioKit 解决方案中,应该在您的 AKPlayer 对象上调用这些行。请注意,这应该在您实际启动引擎之前完成。
self.player.buffering = .always
AudioKit.engine.attach(self.player.playerNode)
AudioKit.engine.connect(self.player.playerNode, to: self.mixer.inputNode, format: AudioKit.engine.inputNode.outputFormat(forBus: 0))
录制的方式与您在 AVAudioEngine 中的录制方式非常相似,您可以在节点(麦克风或其他节点)上安装一个 tap 并录制 PCM 样本的缓冲区。