Attention! For Japanese customers, my Japanese blog might help.

Aug 3, 2016

How to add parameters to your own v3 AudioUnit

NOTE: This is the English version of my Japanese article.

I'm on the way to developing a vibrato unit by referring to FilterDemo at AudioUnitV3Example: A Basic AudioUnit Extension and Host Implementation.

In this article, I tried to add parameters.
In the template code of AudioUnit App Extension, it is seemed to add "Parameter 1" parameter as follows:
// Create parameter objects.
AUParameter *param1 = [AUParameterTree createParameterWithIdentifier:@"param1" name:@"Parameter 1" address:myParam1 min:0 max:100 unit:kAudioUnitParameterUnit_Percent unitName:nil flags:0 valueStrings:nil dependentParameters:nil];
A myParam1 is defined as
// Define parameter addresses. (These needn't be static).
#define myParam1 0
In FilterDemo, the addresses are defined as enum as:
enum {
FilterParamCutoff = 0,
FilterParamResonance = 1
};
I think any unique values will be OK.

In my case, I defined the paas follows
typedef NS_ENUM(AUParameterAddress, VibratoParameterAddress) {
    VibratoParameterAddressFrequency = 0,
    VibratoParameterAddressDepth = 1
};
Then, I actually added parameters as:
AUParameter *freqParameter = [AUParameterTree createParameterWithIdentifier:VibratoParameterIdentifierFrequency
                                                                           name:VibratoParameterNameFrequency
                                                                        address:VibratoParameterAddressFrequency
                                                                            min:0
                                                                            max:1
                                                                           unit:kAudioUnitParameterUnit_Hertz
                                                                       unitName:nil
                                                                          flags:0
                                                                   valueStrings:nil
                                                            dependentParameters:nil];
freqParameter.value = .0025;
self.freq = freqParameter.value;
    
AUParameter *depthParameter = [AUParameterTree createParameterWithIdentifier:VibratoParameterIdentifierDepth
                                                                            name:VibratoParameterNameDepth
                                                                         address:VibratoParameterAddressDepth
                                                                             min:0
                                                                             max:250
                                                                            unit:kAudioUnitParameterUnit_Seconds
                                                                        unitName:nil
                                                                           flags:0
                                                                    valueStrings:nil
                                                             dependentParameters:nil];
depthParameter.value = 250;
self.depth = depthParameter.value;
    
self.parameterTree = [AUParameterTree createTreeWithChildren:@[freqParameter, depthParameter]];
where, name and identifier strings were defined as
NSString *VibratoParameterIdentifierFrequency = @"VibratoParameterIdentifierFrequency";
NSString *VibratoParameterIdentifierDepth = @"VibratoParameterIdentifierDepth";

NSString *VibratoParameterNameFrequency = @"Frequency";
NSString *VibratoParameterNameDepth = @"Depth";
By the way, as for detecting the changes in these parameters from outside, FilterDemo's implementation is
// implementorValueObserver is called when a parameter changes value.
_parameterTree.implementorValueObserver = ^(AUParameter *param, AUValue value) {
        filterKernel->setParameter(param.address, value);
};
// implementorValueProvider is called when the value needs to be refreshed.
_parameterTree.implementorValueProvider = ^(AUParameter *param) {
return filterKernel->getParameter(param.address);
};
I think this code means that blocks are executed when parameters were set/got.
In my case, I implemented as
MyAudioUnit *unit = self;
self.parameterTree.implementorValueObserver = ^(AUParameter *param, AUValue value) {
    switch (param.address) {
        case VibratoParameterAddressFrequency:
            unit.freq = value;
            break;
        case VibratoParameterAddressDepth:
            unit.depth = value;
        default:
            break;
    }
};
    
self.parameterTree.implementorValueProvider = ^(AUParameter *param) {
    switch (param.address) {
        case VibratoParameterAddressFrequency:
            return unit.freq;
        case VibratoParameterAddressDepth:
            return unit.depth;
        default:
            break;
    }
    return 0.0f;
};
By the way, it is noted that a block is needed to express these parameters as strings.
In FilterDemo, the implementations are
// A function to provide string representations of parameter values.
_parameterTree.implementorStringFromValueCallback = ^(AUParameter *param, const AUValue *__nullable valuePtr) {
AUValue value = valuePtr == nil ? param.value : *valuePtr;
switch (param.address) {
case FilterParamCutoff:
return [NSString stringWithFormat:@"%.f", value];
case FilterParamResonance:
return [NSString stringWithFormat:@"%.2f", value];
default:
return @"?";
}
};
So, I implemented as follows:
self.parameterTree.implementorStringFromValueCallback = ^(AUParameter *param, const AUValue *__nullable valuePtr) {
        AUValue value = valuePtr == nil ? param.value : *valuePtr;
        
        switch (param.address) {
            case VibratoParameterAddressFrequency:
                return [NSString stringWithFormat:@"%.f", value];
            case VibratoParameterAddressDepth:
                return [NSString stringWithFormat:@"%.0f", value];
            default:
                return @"?";
        }
    };
Finally, parameter setting will be completed.

Nextly, I tried to change the parameters from outside.
However,  my AudioUnit's UI was not displayed on host applications such as AUV3Host or Hosting AU...

For v3 AudioUnits, custom UIs may be required.
So, before checking parameter changes, I think I'm going to make a custom UI...

That's all for today!!