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

Aug 5, 2016

I wanted an AUGenericView for handemade v3 AudioUnit

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

As shown in the title, I wanted to get an AUGenericView of my own v3 AudioUnit.
I could not get the view from an AUAudioUnit object because AUGenericView's initializer requires the instance of type 'AudioUnit'.
As far as I know, there was no way to get an AudioUnit instance from an AUAudioUnit object.

While, I could get an AudioUnit instance from AVAudioUnit object, which was supplied by AVFoundation framework.

So I tried to make a host app and made the AUGenericView of my own AU.
The code is following:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    AudioComponentDescription desc;
    desc.componentType = 'aufx';
    desc.componentSubType = 'vbrt';
    desc.componentManufacturer = 'Symi';
    [AVAudioUnit instantiateWithComponentDescription:desc
                                             options:0
                                   completionHandler:^(__kindof AVAudioUnit * _Nullable audioUnit, NSError * _Nullable error) {
                                       self.unit = audioUnit;
                                       AudioUnit unit = [audioUnit audioUnit];
                                       AUGenericView *view = [[AUGenericView allocinitWithAudioUnit:unit];
                                       view.frame = self.window.contentView.frame;
                                       [self.window.contentView addSubview:view];
                                   }];
}
As a result:


Altough I could get the view, no parameters were displayed.
By comparing my AU with FilterDemo, I found that it was needed to give kAudioUnitParameterFlag_IsReadable and kAudioUnitParameterFlag_IsWritableAUParameter flags when creating AUParameter for my AU as follows:
AUParameter *freqParameter = [AUParameterTree createParameterWithIdentifier:VibratoParameterIdentifierFrequency
                                                                           name:VibratoParameterNameFrequency
                                                                        address:VibratoParameterAddressFrequency
                                                                            min:0
                                                                            max:.01
                                                                           unit:kAudioUnitParameterUnit_Hertz
                                                                       unitName:nil
                                                                          flags:kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_IsWritable
                                                                   valueStrings:nil
                                                            dependentParameters:nil];
I again ran the host app, then
YES! I could finally get a view like Apple's AUs!

By the way, when I set only a writable flag to Depth and only a readable flag to Frequency,
the generic view were:
Depth was adjustable, but Frequency was not.
Hmm...

Summary:
  • When you want to get an AUGenericView for v3 AU in your host app, you can get an 'AudioUnit' instance from an AVAudioUnit object.
  • As for your own AU, you need to set kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_IsWritable flags when creating AUParameter.
That's all for today.



Aug 4, 2016

Display a custom UI of my own v3 AudioUnit in host applications

This is the sequel article of previous one.

I was looking for the reason why my v3 AudioUnit can not display a custom UI in host apps such as AUv3Host or Hosting AU...

As as result, It is seemed that my AU needs to set preferredContentSize property.
In FilterDemo, the property seems to be set in viewDidLoad in FilterDemoViewController.
So, I also set the property as follows:
- (void) viewDidLoad {
    [super viewDidLoad];
    
    self.preferredContentSize = NSMakeSize(480272);
}
By the way, in the code which was generated when we added AudioUnit App Extension,
viewDidLoad implementation was
- (void) viewDidLoad {
    [super viewDidLoad];
    
    if (!audioUnit) {
        return;
    }
    
    // Get the parameter tree and add observers for any parameters that the UI needs to keep in sync with the AudioUnit
}
However,  because the audioUnit instance was always nil, the program immediately returns from viewDidLoad.
The audioUnit object is set in createAudioUnitWithComponentDescription:error:, which conforms to AUAudioUnitFactory protocol as follows:
- (AUAudioUnit *)createAudioUnitWithComponentDescription:(AudioComponentDescription)desc error:(NSError **)error {
    audioUnit = [[MyAudioUnit allocinitWithComponentDescription:desc error:error];
    
    return audioUnit;
}
But, because viewDidLoad is called earlier than when createAudioUnitWithComponentDescription:error: is called, audioUnit property remains nil in viewDidLoad.

I doubt that codes automatically generated by AudioUnit App Extension target are strange (not qualified as standard AudioUnit).

I have wasted a time because I suspected auto layout or other factors.

Next, I want to add UIs to the custom view.






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!!

Aug 2, 2016

pass through input data to output on v3 effect AudioUnit

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

My v3 AudioUnit now got to be able to pass input data to output.

By the way, I use AUv3Host.app as a host application by building a sample code at AudioUnitV3Example: A Basic AudioUnit Extension and Host Implementation.

When I built an AudioUnit App Extension with codes, which were generated by a template target in Xcode without any modifications,
the AudioUnit did not work with an error in AUv3Host.app:
AVAudioNode.mm:747: AUSetFormat: error -10877
It was seemed that a custom AudioUnit class (MyAudioUnit) needs to override two methods: inputBusses and outputBusses.

For now,  I overrode these methods by reference to FilterDemo in AudioUnitv3Example as
- (AUAudioUnitBusArray *)inputBusses {
    AUAudioUnitBusArray *buses = [[AUAudioUnitBusArray allocinitWithAudioUnit:self
                                                                        busType:AUAudioUnitBusTypeInput
                                                                         busses:@[self.inputBus]];
    return buses;
}

- (AUAudioUnitBusArray *)outputBusses {
    AUAudioUnitBusArray *buses = [[AUAudioUnitBusArray allocinitWithAudioUnit:self
                                                                        busType:AUAudioUnitBusTypeOutput
                                                                         busses:@[self.outputBus]];
    return buses;
}
where,  inputBus and outputBus were defined as
@property (nonatomicAUAudioUnitBus *inputBus;
@property (nonatomicAUAudioUnitBus *outputBus;
and initialized in initWithComponentDescription:options:error: as follows.
const UInt32 numCh = 2;
    
AVAudioFormat *defaultFormat = [[AVAudioFormat allocinitStandardFormatWithSampleRate:44100.0 channels:numCh];
NSError *error = nil;
self.inputBus = [[AUAudioUnitBus allocinitWithFormat:defaultFormat
                                                     error:&error];
if (error) {
    NSLog(@"failed to make input bus. error:%@", error);
    return nil;
}
    
self.outputBus = [[AUAudioUnitBus allocinitWithFormat:defaultFormat
                                                     error:&error];
if (error) {
   NSLog(@"failed to make output bus. error:%@", error);
    return nil;
}
As a result, the error was got to not occured in AUv3Host.app.

However, no sound was output.
Next, it is seemed to need to set data to output buffer by overriding internalRenderBlock.

Because my first objective is to pass input data to output, I firstly implemented as
AUAudioUnitStatus status = pullInputBlock(actionFlags, timestamp, frameCount, 0, outputData);
But, my AU did not work as I expected.
I don't know the reason yet. (Because debugging an app exntension is to annoying!)

By referring FilterDemo again, I overrode internalRenderBlock as
- (AUInternalRenderBlock)internalRenderBlock {
    return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock) {
        
        AUAudioUnitStatus status = pullInputBlock(actionFlags, timestamp, frameCount, 0self.list);
        
        if (status != noErr)
            return status;
        
        for (UInt32 b = 0; b < outputData->mNumberBuffers; b++) {
            if (outputData->mBuffers[b].mDataByteSize < sizeof(float) * frameCount) {
                NSLog(@"invalid output data size");
                return kAudioUnitErr_TooManyFramesToProcess;
            }
        
            if (self.list->mBuffers[b].mDataByteSize < sizeof(float) * frameCount) {
                NSLog(@"invalid input data size");
                return kAudioUnitErr_TooManyFramesToProcess;
            }

            memcpy(outputData->mBuffers[b].mDataself.list->mBuffers[0].mDatasizeof(float) * frameCount);
        }
        return noErr;
    };
}
where, the definition of list is
@property (nonatomicAudioBufferList *list;
and the list is also initialized in initWithComponentDescription:options:error: as
self.list = malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (numCh - 1));
    
const UInt32 numMaxFrames = 1024;
for (UInt32 ch = 0; ch < numCh; ch++) {
        self.list->mNumberBuffers = numCh;
        self.list->mBuffers[ch].mNumberChannels = 1;
        self.list->mBuffers[ch].mDataByteSize = numMaxFrames * sizeof(float);
        self.list->mBuffers[ch].mData = malloc(numMaxFrames * sizeof(float));
}
which is to say allocating buffers.

Because I set default fomats(canonical, non-interleaved, float) to input/output busses,
I allocated extra AudioBuffers for the number of channel - 1, and I hard-coded the type of buffer as float.

Finaly, my AudioUnit got to be able to pass input audio data to output.

By the way, I could not figure out how/when to use allocateRenderResourcesAndReturnError: and deallocateRenderResources.
I could not the differences between init... and dealloc.

To figure out that, I will take a look at FilterDemo.