发布
社区首页 >问答首页 >AKPlayer在channelCount条件下从缓冲区播放时崩溃

AKPlayer在channelCount条件下从缓冲区播放时崩溃
EN

Stack Overflow用户
提问于 2018-10-28 07:05:32
回答 1查看 706关注 0票数 3

我很难使下面的场景像预期的那样工作(下面将提供代码)。

  1. 记录我的麦克风输入并将一个AVAudioPCMBuffer存储在内存中,这是用AVAudioPCMBuffer扩展方法copy(from buffer: AVAudioPCMBuffer, readOffset: AVAudioFrameCount = default, frames: AVAudioFrameCount = default)完成的。--我确实在录音的末尾得到了缓冲器。
  2. 当记录结束时,将缓冲区传递给AKPlayer并播放。这里有一个代码片段来演示我所做的事情(我知道这不是完整的应用程序代码,如果需要的话,我可以分享):

代码语言:javascript
代码运行次数:0
复制
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输出。

当我检查和调试应用程序时,我可以看到缓冲区的长度是正确的,并且我的所有输出/输入设置都使用相同的处理格式(采样速率、通道、比特率等)以及记录的缓冲区,但我的应用程序仍然在这一行崩溃:

代码语言:javascript
代码运行次数:0
复制
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代码时,我可以看到断线在AKPlayer+Playback.swift上的line 162上,在方法:playerNode.scheduleBuffer上。

更多可能有用的信息:

  • 记录的缓冲区长达16秒。
  • 当我试图将缓冲区直接传递给点击方法中的播放器节点时,我确实听到了从麦克风到扬声器的延迟,但它确实回放了。
  • 我试着在调用游戏方法之前对玩家进行呼叫准备,没有任何帮助。

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-11-02 05:58:30

好吧,这是超级不酷的调试会话。我必须研究AVAudioEngine以及如何在那里实现这种场景,当然,这不是我正在寻找的最终结果。这个探索帮助我理解了如何用AudioKit来解决这个问题(我的应用程序的一半是使用AudioKit的工具实现的,所以用AVFoundation重写它是没有意义的)。

AFFoundation 解决方案:

代码语言:javascript
代码运行次数:0
复制
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函数:

代码语言:javascript
代码运行次数:0
复制
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对象上调用这些行。请注意,这应该在您实际启动引擎之前完成。

代码语言:javascript
代码运行次数:0
复制
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中所做的非常相似,而是在节点(麦克风或其他节点)上安装一个点击,并记录PCM示例的缓冲区。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53029148

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档