AKPlayer падает при воспроизведении из буфера при условии channelCount - PullRequest
0 голосов
/ 28 октября 2018

Я изо всех сил стараюсь, чтобы следующий сценарий работал как положено (код будет предоставлен ниже).

  1. Запишите мой микрофонный вход и сохраните AVAudioPCMBuffer в памяти, это делается с помощьюAVAudioPCMBuffer метод расширения copy(from buffer: AVAudioPCMBuffer, readOffset: AVAudioFrameCount = default, frames: AVAudioFrameCount = default). Я действительно получаю буфер в конце моей записи .

  2. Когда запись заканчивается, передайте буфер на 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.

Когда я проверяю и отлаживаю приложение, я вижу, что буфер имеет правильную длину,и все мои настройки вывода / ввода используют один и тот же формат обработки (частота дискретизации, каналы, битрейт и т. д.), а также записанный буфер, но все равно мое приложение вылетает в этой строке:

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 секунд.
  • Когда я попытался передать буфер прямо на узел проигрывателя в методе касания, похоже, что он работал, я услышал задержку от микрофона к динамику, но он действительно воспроизводился.
  • Iпопытка вызова подготовить на плеере до вызова метода воспроизведения, без помощи

спасибо!

1 Ответ

0 голосов
/ 02 ноября 2018

Хорошо, это была супер-не крутая сессия отладки.Я должен был исследовать 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, вы устанавливаете касание на свой узел (микрофон или другоеузел) и записать буфер образцов PCM.

...