I am making an attempt to combine audio from a few audio sources utilizing Audio Models. The construction is kind of easy, I’ve two inputs, a mixer audio unit and a generic output. The issue is that the buffers from the output AudioUnit
are solely crammed with zeros.
- Inputs are offered via
AURenderCallbackStruct
on to the mixer. - Mixer subtype is
kAudioUnitSubType_MultiChannelMixer
. - Output subtype is
kAudioUnitSubType_GenericOutput
to drag the blended audio manually with out enjoying it. Output is related viaAudioUnitConnection
to keep away from deprecatedAUGraph
.AVAudioSession
can’t be used both forReplayKit
compatibility.
I eliminated all OSStatus
checks to offer shorter model of the code; nevertheless, all Audio Unit strategies return noErr
. The setup technique is:
personal func setupAudioUnits() {
// Organising 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>.dimension))
AudioUnitInitialize(mixerAudioUnit)
var busCount = UInt32(inputs)
AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_ElementCount,
kAudioUnitScope_Input,
0,
&busCount,
UInt32(MemoryLayout<UInt32>.dimension))
// Organising mixer's inputs
for enter in 0..<inputs {
var callbackStruct = AURenderCallbackStruct(inputProc: inputRenderCallback,
inputProcRefCon: Unmanaged.passUnretained(self).toOpaque())
AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
UInt32(enter),
&callbackStruct,
UInt32(MemoryLayout<AURenderCallbackStruct>.dimension))
}
// Organising 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>.dimension))
AudioUnitInitialize(outputAudioUnit)
// Organising a connection between the mixer and output items
var connection = AudioUnitConnection(sourceAudioUnit: mixerAudioUnit,
sourceOutputNumber: 0,
destInputNumber: 0)
AudioUnitSetProperty(outputAudioUnit,
kAudioUnitProperty_MakeConnection,
kAudioUnitScope_Input,
0,
&connection,
UInt32(MemoryLayout<AudioUnitConnection>.dimension))
AudioOutputUnitStart(outputAudioUnit)
}
Right here is the half that initiates the rendering. I am offering nil
for mData
to make use of the inner buffer of the audio unit.
personal 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)
}
// at all times 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 info
// Audio buffer has solely zeros
}
sampleTime += Int64(numberOfFrames)
}
AudioUnitRender
at all times returns noErr
and the buffers have right dimension that’s primarily based on the offered AVAudioFormat
. Enter callback is being known as and ioData
is crammed with information:
Nevertheless, the output buffer is at all times crammed with zeros:
Is there one thing improper with my output audio unit setup or with the connection? Or possibly the mixer is just not passing the info ahead? Any assistance is appreciated.