AudioUnit kAudioUnitSubType_Reverb2 and kAudioUnitType_FormatConverter

1

I have this AUGraph configuration

AudioUnitGraph 0x2505000:
  Member Nodes:
    node 1: 'aufx' 'ipeq' 'appl', instance 0x15599530 O  
    node 2: 'aufx' 'rvb2' 'appl', instance 0x1566ffd0 O  
    node 3: 'aufc' 'conv' 'appl', instance 0x15676900 O  
    node 4: 'aumx' 'mcmx' 'appl', instance 0x15676a30 O  
    node 5: 'aumx' 'mcmx' 'appl', instance 0x15677ac0 O  
    node 6: 'aumx' 'mcmx' 'appl', instance 0x15678a40 O  
    node 7: 'auou' 'rioc' 'appl', instance 0x15679a20 O  
    node 8: 'augn' 'afpl' 'appl', instance 0x1558b710 O  
  Connections:
    node   7 bus   1 => node   5 bus   0  [ 1 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   5 bus   0 => node   3 bus   0  [ 2 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   3 bus   0 => node   2 bus   0  [ 2 ch,  44100 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
    node   2 bus   0 => node   6 bus   0  [ 1 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   8 bus   0 => node   4 bus   0  [ 1 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   4 bus   0 => node   6 bus   1  [ 2 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   6 bus   0 => node   1 bus   0  [ 2 ch,  44100 Hz, 'lpcm' (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved]
    node   1 bus   0 => node   7 bus   0  [ 2 ch,      0 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
  CurrentState:
    mLastUpdateError=0, eventsToProcess=F, isInitialized=F, isRunning=F

and the error

AUGraphInitialize err = -10868

since I have connected a reverb unit between two mixer units even if I have created a converter unit:

OSStatus err  = noErr;

    UInt32 micBus               = 0;
    UInt32 filePlayerBus        = 1;

    //// ionode:1 ----> vfxNode:0 bus 0
    err =   AUGraphConnectNodeInput(processingGraph, ioNode, 1, vfxNode, 0);
    if (err) { NSLog(@"ioNode:1 ---> vfxNode:0 err = %ld", err); }

    //// vfxNode:0 ---> convertNode:0
    err = AUGraphConnectNodeInput(processingGraph, vfxNode, 0, convertNode, 0);

    //// convertNode:0 ---> vfxRevNode:0
    err = AUGraphConnectNodeInput(processingGraph, convertNode, 0, vfxRevNode, 0);

    //// vfxRevNode:0 ---> mixerNode:0
    err =   AUGraphConnectNodeInput(processingGraph, vfxRevNode, 0, mixerNode,  micBus );
    //if (err) { NSLog(@"vfxRevNode:0 ---> mixerNode:0 err = %ld", err); }

    //// vfxNode:0 ----> mixerNode:0
    //err = AUGraphConnectNodeInput(processingGraph, vfxNode, 0, mixerNode, micBus );
    if (err) { NSLog(@"vfxNode:0 ---> mixerNode:0 err = %ld", err); }

    //// audioPlayerNode:0 ----> fxNode:0
    err = AUGraphConnectNodeInput(processingGraph, audioPlayerNode, 0, fxNode, 0);
    if (err) { NSLog(@"audioPlayerNode:0 --->  fxNode:0 err = %ld", err); }

    //// fxNode:0 ----> mixerNode:1
    err = AUGraphConnectNodeInput(processingGraph, fxNode, 0, mixerNode, filePlayerBus);
    if (err) { NSLog(@"fxNode:0 ---> mixerNode:1 err = %ld", err); }

    ///// mixerNode:0 ----> eqNode:0
    err = AUGraphConnectNodeInput(processingGraph, mixerNode, 0, eqNode, 0);
    if (err) { NSLog(@"mixerNode:0 --->  eqNode:0 err = %ld", err); }

    //// eqNode:0 ----> ioNode:0
    err = AUGraphConnectNodeInput(processingGraph, eqNode, 0, ioNode, 0);
    if (err) { NSLog(@"eqNode:0 ---> ioNode:0 err = %ld", err); }

Here are the nodes:

    ////
//// EQ NODE
////
err = AUGraphAddNode(processingGraph, &EQUnitDescription, &eqNode);
if (err) { NSLog(@"eqNode err = %ld", err); }

////
//// REV NODE
////
err = AUGraphAddNode(processingGraph, &ReverbUnitDescription, &vfxRevNode);
if (err) { NSLog(@"vfxRevNode err = %ld", err); }

////
//// FORMAT CONVERTER NODE
////
err  = AUGraphAddNode (processingGraph, &convertUnitDescription, &convertNode);
if (err) { NSLog(@"convertNode err = %ld", err); }

////
//// FX NODE
////
err = AUGraphAddNode(processingGraph, &FXUnitDescription, &fxNode);
if (err) { NSLog(@"fxNode err = %ld", err); }

////
//// VFX NODE
////
err = AUGraphAddNode(processingGraph, &VFXUnitDescription, &vfxNode);
if (err) { NSLog(@"vfxNode err = %ld", err); }

///
/// MIXER NODE
///
err = AUGraphAddNode (processingGraph, &MixerUnitDescription, &mixerNode );
if (err) { NSLog(@"mixerNode err = %ld", err); }

///
/// OUTPUT NODE
///
err = AUGraphAddNode(processingGraph, &iOUnitDescription, &ioNode);
if (err) { NSLog(@"outputNode err = %ld", err); }

////
/// PLAYER NODE
///
err = AUGraphAddNode(processingGraph, &playerUnitDescription, &audioPlayerNode);
if (err) { NSLog(@"audioPlayerNode err = %ld", err); }

and the component descriptions:

OSStatus err  = noErr;

    err = NewAUGraph(&processingGraph);

    // OUTPUT unit
    AudioComponentDescription iOUnitDescription;
    iOUnitDescription.componentType = kAudioUnitType_Output;
    iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;//kAudioUnitSubType_VoiceProcessingIO;//kAudioUnitSubType_RemoteIO;
    iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
    iOUnitDescription.componentFlags = 0;
    iOUnitDescription.componentFlagsMask = 0;

    // MIXER unit
    AudioComponentDescription MixerUnitDescription;
    MixerUnitDescription.componentType          = kAudioUnitType_Mixer;
    MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
    MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    MixerUnitDescription.componentFlags         = 0;
    MixerUnitDescription.componentFlagsMask     = 0;

    // PLAYER unit
    AudioComponentDescription playerUnitDescription;
    playerUnitDescription.componentType = kAudioUnitType_Generator;
    playerUnitDescription.componentSubType = kAudioUnitSubType_AudioFilePlayer;
    playerUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;

    // EQ unit
    AudioComponentDescription EQUnitDescription;
    EQUnitDescription.componentType          = kAudioUnitType_Effect;
    EQUnitDescription.componentSubType       = kAudioUnitSubType_AUiPodEQ;
    EQUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    EQUnitDescription.componentFlags         = 0;
    EQUnitDescription.componentFlagsMask     = 0;

    // Reverb unit
    AudioComponentDescription ReverbUnitDescription;
    ReverbUnitDescription.componentType          = kAudioUnitType_Effect;
    ReverbUnitDescription.componentSubType       = kAudioUnitSubType_Reverb2;
    ReverbUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    ReverbUnitDescription.componentFlags         = 0;
    ReverbUnitDescription.componentFlagsMask     = 0;

    // Format Converter between VFX and Reverb units
    AudioComponentDescription convertUnitDescription;
    convertUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    convertUnitDescription.componentType          = kAudioUnitType_FormatConverter;
    convertUnitDescription.componentSubType       = kAudioUnitSubType_AUConverter;
    convertUnitDescription.componentFlags         = 0;
    convertUnitDescription.componentFlagsMask     = 0;

    // FX unit
    AudioComponentDescription FXUnitDescription;
    FXUnitDescription.componentType          = kAudioUnitType_Mixer;
    FXUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
    FXUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    FXUnitDescription.componentFlags         = 0;
    FXUnitDescription.componentFlagsMask     = 0;

    // VFX unit
    AudioComponentDescription VFXUnitDescription;
    VFXUnitDescription.componentType          = kAudioUnitType_Mixer;
    VFXUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
    VFXUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    VFXUnitDescription.componentFlags         = 0;
    VFXUnitDescription.componentFlagsMask     = 0;

I created the setup of the converter node to have as input the input node stream format (that is a mixer unit) and the output node stream format as output format (that is the reverb)

    OSStatus err = noErr;
err = AUGraphNodeInfo(processingGraph, convertNode, NULL, &convertUnit);
if (err) { NSLog(@"setupConverterUnit error = %ld", err); }

// set converter input format to vfxunit format

AudioStreamBasicDescription asbd = {0};
size_t bytesPerSample;
bytesPerSample = sizeof(SInt16);
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mBitsPerChannel = 8 * bytesPerSample;
asbd.mFramesPerPacket = 1;
asbd.mChannelsPerFrame = 1;
asbd.mBytesPerPacket = bytesPerSample * asbd.mFramesPerPacket;
asbd.mBytesPerFrame = bytesPerSample * asbd.mChannelsPerFrame;
asbd.mSampleRate = sampleRate;

err = AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, sizeof(asbd));
if (err) { NSLog(@"setupConverterUnit kAudioUnitProperty_StreamFormat error = %ld", err); }


// set converter output format to reverb format
UInt32 streamFormatSize = sizeof(monoStreamFormat);
err = AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &monoStreamFormat, streamFormatSize);
if (err) { NSLog(@"setupConverterUnit kAudioUnitProperty_StreamFormat error = %ld", err); }

The reverb unit is configured as follows:

    OSStatus err  = noErr;

err = AUGraphNodeInfo(processingGraph, vfxRevNode, NULL, &vfxRevUnit);
if (err) { NSLog(@"setupReverbUnit err = %ld", err); }

UInt32 size = sizeof(mReverbPresetArray);
err = AudioUnitGetProperty(vfxRevUnit, kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &mReverbPresetArray, &size);

if (err) { NSLog(@"kAudioUnitProperty_FactoryPresets err = %ld", err); }

    printf("setupReverbUnit Preset List:\n");
    UInt8 count = CFArrayGetCount(mReverbPresetArray);
    for (int i = 0; i < count; ++i) {
        AUPreset *aPreset = (AUPreset*)CFArrayGetValueAtIndex(mReverbPresetArray, i);
        CFShow(aPreset->presetName);
    }

and the input mixer unit as

    OSStatus err;
err = AUGraphNodeInfo(processingGraph, vfxNode, NULL, &vfxUnit);
if (err) { NSLog(@"setVFxUnit err = %ld", err); }

UInt32 busCount = 1;
err = AudioUnitSetProperty (
                            vfxUnit,
                            kAudioUnitProperty_ElementCount,
                            kAudioUnitScope_Input,
                            0,
                            &busCount,
                            sizeof (busCount)
                            );

AudioStreamBasicDescription asbd = {0};
size_t bytesPerSample;
bytesPerSample = sizeof(SInt16);
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mBitsPerChannel = 8 * bytesPerSample;
asbd.mFramesPerPacket = 1;
asbd.mChannelsPerFrame = 1;
asbd.mBytesPerPacket = bytesPerSample * asbd.mFramesPerPacket;
asbd.mBytesPerFrame = bytesPerSample * asbd.mChannelsPerFrame;
asbd.mSampleRate = sampleRate;

err = AudioUnitSetProperty (
                            vfxUnit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Input,
                            0,
                            &asbd,
                            sizeof (asbd)
                            );

So far no way to make it works. I need the reverb unit to be there since I need it on the mic input only before entering the next mixer units.

Where I'm wrong with AudioStreamBasicDescription on the converter ?

EDIT What happens is that there is no sound. The audio graph is not being initialized and it comes out with the error

AUGraphInitialize err = -10868

The graph described here can be depicted in this way

A mic input connected to vfxNode to get the render callback. This was connected to a mixer on bus 0. A song player node connected to another mixer where there is a second render callback that processes the audio in playback. This is connected to bus 1 of the last mixer in the chain. A eq node connects the mixer to the ouput ioNode.

When using a converter node between the fx mixer (vfxNode) and the reverb there is no sound anymore:

//// vfxNode:0 ---> convertNode:0
err = AUGraphConnectNodeInput(processingGraph, vfxNode, 0, convertNode, 0);

//// convertNode:0 ---> vfxRevNode:0
err = AUGraphConnectNodeInput(processingGraph, convertNode, 0, vfxRevNode, 0);

//// vfxRevNode:0 ---> mixerNode:0
err =   AUGraphConnectNodeInput(processingGraph, vfxRevNode, 0, mixerNode,  micBus );
//if (err) { NSLog(@"vfxRevNode:0 ---> mixerNode:0 err = %ld", err); }

Having no reverb node hence no converter node everything works properly:

//// ionode:1 ----> vfxNode:0 bus 0
err =   AUGraphConnectNodeInput(processingGraph, ioNode, 1, vfxNode, 0);
if (err) { NSLog(@"ioNode:1 ---> vfxNode:0 err = %ld", err); }

//// vfxNode:0 ----> mixerNode:0
err =   AUGraphConnectNodeInput(processingGraph, vfxNode, 0, mixerNode, micBus );
if (err) { NSLog(@"vfxNode:0 ---> mixerNode:0 err = %ld", err); }
ios
core-audio
audiounit
asked on Stack Overflow Nov 9, 2013 by loretoparisi • edited Nov 9, 2013 by loretoparisi

1 Answer

2

You haven't said what the problem is (error, no sound, etc.), but I'm willing to take a guess. Those effect units prefer to be floating point PCM, and will generally refuse to be connected to anything that isn't floating point (the non-effect units will generally default to ints or fixed point). Are you getting -50 (paramErr) when you try to start the graph?

What I've found I have to do to use effect units is to read the ASBD the effect unit defaults to (either on input or output scope), and then set that throughout the graph (the other units are generally willing to accept floating point ASBDs). Note that this is going to be about 100 lines of boilerlplate where you get the unit from each node, get the ASBD from the effect's input or output scope (don't think it matters which), and then set that as the format received or produced by any unit in your graph. Good luck.

answered on Stack Overflow Nov 9, 2013 by invalidname

User contributions licensed under CC BY-SA 3.0