1

I have an AUGraph setup with a couple of File Player audio units feeding into a MultiChannelMixer unit, which then feeds into a Remote I/O output. This setup has worked just fine.

Now I've been struggling to add a callback in such a way that I can calculate the sound level of the individual file players.

private let meteringCallback: AURenderCallback = { (
    inRefCon,
    ioActionFlags,
    inTimeStamp,
    inBusNumber,
    frameCount,
    ioData ) -> OSStatus in

    var status = noErr

    var track: AUTrack = unsafeBitCast(inRefCon, to: AUTrack.self)

    status = AudioUnitRender(track.fileAU!,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             frameCount,
                             ioData!);

    var samples = [Float]()
    let ptr = ioData!.pointee.mBuffers.mData?.assumingMemoryBound(to: Float.self)
    samples.append(contentsOf: UnsafeBufferPointer(start: ptr, count: Int(frameCount)))

    // ... fancy algorithm calculating DB value ...
}

AUTrack simple holds information about that particular track. Passing the entire class instance to the callback (as is usually done in examples) would not make sense here because there are multiple file players.

Now I need to set this callback somewhere, so I can capture the value off each of the file players feeding into the mixer. Yet I keep getting a -10877 (invalid element) error when I try to do that.

I try to setup the metering callback with this.

// Set metering callback
var meteringCallbackStruct = AURenderCallbackStruct(inputProc: meteringCallback,
                                                    inputProcRefCon: &self.tracks[1])
status = AudioUnitSetProperty(self.tracks[1].fileAU!,
                          kAudioUnitProperty_SetRenderCallback,
                          kAudioUnitScope_Output,
                          0,
                          &meteringCallbackStruct,
                     UInt32(MemoryLayout<AURenderCallbackStruct>.size))

I'm not quite sure how to solve this.

And wouldn't this callback "eat" the samples unless I somehow pass them back to the mixer unit?

Leonhard Printz
  • 355
  • 2
  • 16

1 Answers1

1

You shouldn't be doing the callback in Swift. Render thread processing should only be done in C/C++.

You can use a render notify:

AudioUnitAddRenderNotify(mixer, my_C_callback, nil) //my_C_callback should not reference Swift objects, or be a Swift callback.

It uses the same function signature as the render callback. It gets called pre and post render, you want to process post render. You get this information from the ioActionFlags.

int isPostRender = ioActionFlags & kAudioUnitRenderAction_PostRender;

However, since you're using the multi-channel mixer, input level monitoring is built in, so you won't need the callback.

You first enable it like this.

//AudioUnit mixer; kAudioUnitSubType_MultiChannelMixer 

//Call Before AudioUnitInitialize()
UInt32 meteringMode = 1;
AudioUnitSetProperty(mixer, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Input, 0, &meteringMode, sizeof(meteringMode));

Then during processing you can get the levels by reading parameters.

int channel = 0;

AudioUnitParameterValue averageDecibles;
AudioUnitGetParameter(mixer, kMultiChannelMixerParam_PreAveragePower, kAudioUnitScope_Input, channel, &averageDecibles);

AudioUnitParameterValue peakHoldDecibles;
AudioUnitGetParameter(mixer, kMultiChannelMixerParam_PrePeakHoldLevel, kAudioUnitScope_Input, channel, &peakHoldDecibles);

Swift:

var meteringMode: UInt32 = 1;
let propSize = UInt32(MemoryLayout<UInt32>.size)
AudioUnitSetProperty(mixer, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Input, 0, &meteringMode, propSize);

var averageDecibles: AudioUnitParameterValue = 0
AudioUnitGetParameter(mixer, kMultiChannelMixerParam_PreAveragePower, kAudioUnitScope_Input, channel, &averageDecibles);

var peakHoldDecibles: AudioUnitParameterValue = 0
AudioUnitGetParameter(mixer, kMultiChannelMixerParam_PrePeakHoldLevel, kAudioUnitScope_Input, channel, &peakHoldDecibles);
dave234
  • 4,793
  • 1
  • 14
  • 29
  • 1
    Thank you so much. This is a great help for me, I was halfway into writing my own mixer. I can't believe I missed these parameters in the header files. :D – Leonhard Printz Jan 02 '18 at 08:45