AVAudioEngine: воспроизведение потока сэмплов Int16 PCM

Я получаю поток 16-битных / 48 кГц стерео сэмплов PCM в формате Int16 и пытаюсь воспроизвести их с помощью AVAudioEngine, однако вообще ничего не слышу. Я думаю, это как-то связано с тем, как я настроил плеер, или, может быть, с тем, как я помещаю данные в буфер.

Я много читал об альтернативных решениях с использованием Audio Queue Services, однако весь пример кода, который я смог найти, написан либо на Objective-C, либо только на iOS.

Если бы у меня были какие-то проблемы с размером кадра или что-то в этом роде, разве я не мог бы хотя бы слышать мусор, исходящий из моих динамиков?

Это мой код:


import Foundation
import AVFoundation

class VoicePlayer {
    
    var engine: AVAudioEngine
    
    let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 48000.0, channels: 2, interleaved: true)!
    let playerNode: AVAudioPlayerNode!
    var audioSession: AVCaptureSession = AVCaptureSession()
    
    init() {
        
        self.audioSession = AVCaptureSession()
        
        self.engine = AVAudioEngine()
        self.playerNode = AVAudioPlayerNode()
        
        self.engine.attach(self.playerNode)
        //engine.connect(self.playerNode, to: engine.mainMixerNode, format:AVAudioFormat.init(standardFormatWithSampleRate: 48000, channels: 2))
        /* If I set my custom format here, AVFoundation complains about the format not being available */
        engine.connect(self.playerNode, to: engine.outputNode, format:AVAudioFormat.init(standardFormatWithSampleRate: 48000, channels: 2))
        engine.prepare()
        try! engine.start()
        self.playerNode.play()
        
    }
    
    
    
    
    func play(buffer: [Int16]) {
        let interleavedChannelCount = 2
        let frameLength = buffer.count / interleavedChannelCount
        let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(frameLength))!
        print("audio buffer size in frames is \(AVAudioFrameCount(frameLength))")
        // buffer contains 2 channel interleaved data
        // audioBuffer contains 2 channel interleaved data
        var buf = buffer
        let size = MemoryLayout<Int16>.stride * interleavedChannelCount * frameLength
        
        
        memcpy(audioBuffer.mutableAudioBufferList.pointee.mBuffers.mData, &buf, size)
        audioBuffer.frameLength = AVAudioFrameCount(frameLength)
        
        /* Implemented an AVAudioConverter for testing
         Input: 16 bit PCM 48kHz stereo interleaved
         Output: whatever the standard format for the system is
         
         Maybe this is somehow needed as my audio interface doesn't directly support 16 bit audio and can only run at 24 bit?
         */
         let normalBuffer = AVAudioPCMBuffer(pcmFormat: AVAudioFormat.init(standardFormatWithSampleRate: 48000, channels: 2)!, frameCapacity: AVAudioFrameCount(frameLength))
         normalBuffer?.frameLength = AVAudioFrameCount(frameLength)
         let converter = AVAudioConverter(from: format, to: AVAudioFormat.init(standardFormatWithSampleRate: 48000, channels: 2)!)
         var gotData = false
         
         let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
         
         if gotData {
         outStatus.pointee = .noDataNow
         return nil
         }
         gotData = true
         outStatus.pointee = .haveData
         return audioBuffer
         }
         
         var error: NSError? = nil
         let status: AVAudioConverterOutputStatus = converter!.convert(to: normalBuffer!, error: &error, withInputFrom: inputBlock);
         
        // Play the output buffer, in this case the audioBuffer, otherwise the normalBuffer
        // Playing the raw audio buffer causes an EXEC_BAD_ACCESS on playback, playing back the buffer from the converter doesn't, but it still doesn't sound anything like a human voice
        self.playerNode.scheduleBuffer(audioBuffer) {
        print("Played")
        }
        
        
    }
    
    
}

Любая помощь будет принята с благодарностью.


person Tobias Timpe    schedule 24.12.2020    source источник


Ответы (1)


После того, как вы скопируете данные в AVAudioPCMBuffer, вам нужно установить его свойство frameLength, чтобы указать, сколько действительного звука он содержит.

func play(buffer: [Int16]) {
    let interleavedChannelCount = 2
    let frameLength = buffer.count / interleavedChannelCount
    let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(frameLength))!

    // buffer contains 2 channel interleaved data
    // audioBuffer contains 2 channel interleaved data

    var buf = buffer
    memcpy(audioBuffer.mutableAudioBufferList.pointee.mBuffers.mData, &buf, MemoryLayout<Int16>.stride * interleavedChannelCount * frameLength)

    audioBuffer.frameLength = AVAudioFrameCount(frameLength)

    self.playerNode.scheduleBuffer(audioBuffer) {
        print("Played")
    }
}

Изменить: обновлено для изменений в вопросе. Старая, (теперь) неактуальная часть:

Часть проблемы заключается в несоответствии ваших форматов. format объявлен как не чередующийся, но buffer представляет собой единый массив Int16, поэтому предположительно представляет чередующиеся данные. Копировать одно в другое напрямую, вероятно, неправильно.

person sbooth    schedule 25.12.2020
comment
О, мне очень жаль, я на самом деле совершенно неправильно использовал чередующуюся часть. Все переплетено в моей звуковой цепочке. Ваше решение подняло это. Я пытался настроить его безуспешно. Я обновил код выше. - person Tobias Timpe; 26.12.2020
comment
Это имеет большое значение. Я обновил ответ. - person sbooth; 27.12.2020
comment
Спасибо, это кажется намного проще. Однако теперь я получаю EXEC_BAD_ACCESS при вызове memcpy. Вот мои размеры: размер: 7680 буфер 3840 значений frameLength: 1920 AVAudioFrameCount of frameLength 1920. Является ли мой буфер, который я пытаюсь воспроизвести, слишком длинным или что-то не так с расчетом размера? - person Tobias Timpe; 28.12.2020
comment
Эти цифры, кажется, имеют смысл для 16-битного стереозвука; 2 байта на выборку * 2 выборки на кадр = 4 байта на кадр, поэтому 1920 кадров для 7680 байт кажется правильным. Пожалуйста, опубликуйте полный (не)рабочий пример, чтобы помочь сузить проблему. - person sbooth; 28.12.2020
comment
Я обновил свой код выше. - person Tobias Timpe; 29.12.2020