AudioUnitRender 生成空音频缓冲区

问题描述 投票:0回答:1

我正在尝试使用音频单元混合来自几个音频源的音频。结构非常简单,我有两个输入,一个混音器音频单元和一个通用输出。问题是输出

AudioUnit
的缓冲区仅填充了零。

  • 输入通过
    AURenderCallbackStruct
    直接提供给混音器。
  • 混合器子类型是
    kAudioUnitSubType_MultiChannelMixer
  • 输出子类型是
    kAudioUnitSubType_GenericOutput
    ,手动拉出混合音频而不播放它。输出通过
    AudioUnitConnection
    连接以避免弃用
    AUGraph
    AVAudioSession
    也不能用于
    ReplayKit
    兼容性。

我删除了所有

OSStatus
检查以提供较短版本的代码;但是,所有音频单元方法都会返回
noErr
。设置方法是:

private func setupAudioUnits() {
    // Setting up mixer unit
    var mixerComponentDescription = AudioComponentDescription(
        componentType: kAudioUnitType_Mixer,
        componentSubType: kAudioUnitSubType_MultiChannelMixer,
        componentManufacturer: kAudioUnitManufacturer_Apple,
        componentFlags: 0,
        componentFlagsMask: 0)
    let mixerComponent = AudioComponentFindNext(nil, &mixerComponentDescription)
    AudioComponentInstanceNew(mixerComponent!, &mixerAudioUnit)
    guard let mixerAudioUnit else { fatalError() }

    var streamFormat = outputFormat.streamDescription.pointee
    AudioUnitSetProperty(mixerAudioUnit,
                         kAudioUnitProperty_StreamFormat,
                         kAudioUnitScope_Output,
                         0,
                         &streamFormat,
                         UInt32(MemoryLayout<AudioStreamBasicDescription>.size))                                
                            
    AudioUnitInitialize(mixerAudioUnit)

    var busCount = UInt32(inputs)
    AudioUnitSetProperty(mixerAudioUnit,
                         kAudioUnitProperty_ElementCount,
                         kAudioUnitScope_Input,
                         0,
                         &busCount,
                         UInt32(MemoryLayout<UInt32>.size))

    // Setting up mixer's inputs
    for input in 0..<inputs {
        var callbackStruct = AURenderCallbackStruct(inputProc: inputRenderCallback, 
                                                    inputProcRefCon: Unmanaged.passUnretained(self).toOpaque())
        AudioUnitSetProperty(mixerAudioUnit,
                             kAudioUnitProperty_SetRenderCallback,
                             kAudioUnitScope_Input,
                             UInt32(input),
                             &callbackStruct,
                             UInt32(MemoryLayout<AURenderCallbackStruct>.size))
    }

    // Setting up output unit
    var outputDescription = AudioComponentDescription(
          componentType: kAudioUnitType_Output,
          componentSubType: kAudioUnitSubType_GenericOutput,
          componentManufacturer: kAudioUnitManufacturer_Apple,
          componentFlags: 0,
          componentFlagsMask: 0)
    let outputComponent = AudioComponentFindNext(nil, &outputDescription)
    AudioComponentInstanceNew(outputComponent!, &outputAudioUnit)
    guard let outputAudioUnit else { fatalError() }

    AudioUnitSetProperty(outputAudioUnit,
                         kAudioUnitProperty_StreamFormat,
                         kAudioUnitScope_Input,
                         0,
                         &streamFormat,
                         UInt32(MemoryLayout<AudioStreamBasicDescription>.size))

    AudioUnitInitialize(outputAudioUnit)

    // Setting up a connection between the mixer and output units
    var connection = AudioUnitConnection(sourceAudioUnit: mixerAudioUnit,
                                         sourceOutputNumber: 0,
                                         destInputNumber: 0)

    AudioUnitSetProperty(outputAudioUnit,
                         kAudioUnitProperty_MakeConnection,
                         kAudioUnitScope_Input,
                         0,
                         &connection,
                         UInt32(MemoryLayout<AudioUnitConnection>.size))

    AudioOutputUnitStart(outputAudioUnit)
}

这里是启动渲染的部分。我为

nil
提供
mData
以使用音频单元的内部缓冲区。

private func render(numberOfFrames: AVAudioFrameCount) {
    timeStamp.mFlags = .sampleTimeValid
    timeStamp.mSampleTime = Float64(sampleTime)

    let channelCount = outputFormat.channelCount
    let audioBufferList = AudioBufferList.allocate(maximumBuffers: Int(channelCount))
    for i in 0..<Int(channelCount) {
        audioBufferList[i] = AudioBuffer(mNumberChannels: 1,
                                          mDataByteSize: outputFormat.streamDescription.pointee.mBytesPerFrame,
                                          mData: nil)
    }

    // always returns noErr
    AudioUnitRender(outputAudioUnit,
                    nil,
                    &timeStamp,
                    0,
                    numberOfFrames,
                    audioBufferList.unsafeMutablePointer)

    for channel in 0..<Int(channelCount) {
        let outputBufferData = audioBufferList[channel].mData?.assumingMemoryBound(to: Float.self)
        // Inspecting the data
        // Audio buffer has only zeros
    }

    sampleTime += Int64(numberOfFrames)
}

AudioUnitRender
始终返回
noErr
并且缓冲区具有基于提供的
AVAudioFormat
的正确大小。正在调用输入回调并且
ioData
已填充数据:

但是,输出缓冲区始终填充零:

我的输出音频单元设置或连接有问题吗?或者混合器可能没有向前传递数据?如有任何帮助,我们将不胜感激。

ios macos avfoundation core-audio audiounit
1个回答
0
投票

所以主要问题是

kAudioUnitSubType_MultiChannelMixer
在 macOS 上总是产生空缓冲区。不确定这是否是一个错误,但用
kAudioUnitSubType_StereoMixer
替换子类型解决了问题:

let mixerSubType: OSType
#if os(macOS)
mixerSubType = kAudioUnitSubType_StereoMixer
#else
mixerSubType = kAudioUnitSubType_MultiChannelMixer
#endif

还有一些其他值得一提的事情:

  • 如果您使用 kAudioUnitProperty_SetRenderCallback 将渲染回调设置为
    output
    单元,则
    AudioRenderUnit
    始终会生成空缓冲区。
  • 每个混音器输入都需要使用
    kAudioUnitProperty_StreamFormat
    设置正确的音频格式。
© www.soinside.com 2019 - 2024. All rights reserved.