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 -10877It 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
where, inputBus and outputBus were defined as- (AUAudioUnitBusArray *)inputBusses {AUAudioUnitBusArray *buses = [[AUAudioUnitBusArray alloc] initWithAudioUnit:selfbusType:AUAudioUnitBusTypeInputbusses:@[self.inputBus]];return buses;}- (AUAudioUnitBusArray *)outputBusses {AUAudioUnitBusArray *buses = [[AUAudioUnitBusArray alloc] initWithAudioUnit:selfbusType:AUAudioUnitBusTypeOutputbusses:@[self.outputBus]];return buses;}
and initialized in initWithComponentDescription:options:error: as follows.@property (nonatomic) AUAudioUnitBus *inputBus;@property (nonatomic) AUAudioUnitBus *outputBus;
As a result, the error was got to not occured in AUv3Host.app.const UInt32 numCh = 2;AVAudioFormat *defaultFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100.0 channels:numCh];NSError *error = nil;self.inputBus = [[AUAudioUnitBus alloc] initWithFormat:defaultFormaterror:&error];if (error) {NSLog(@"failed to make input bus. error:%@", error);return nil;}self.outputBus = [[AUAudioUnitBus alloc] initWithFormat:defaultFormaterror:&error];if (error) {NSLog(@"failed to make output bus. error:%@", error);return nil;}
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
But, my AU did not work as I expected.AUAudioUnitStatus status = pullInputBlock(actionFlags, timestamp, frameCount, 0, outputData);
I don't know the reason yet. (Because debugging an app exntension is to annoying!)
By referring FilterDemo again, I overrode internalRenderBlock as
where, the definition of list is- (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, 0, self.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].mData, self.list->mBuffers[0].mData, sizeof(float) * frameCount);}return noErr;};}
and the list is also initialized in initWithComponentDescription:options:error: as@property (nonatomic) AudioBufferList *list;
which is to say allocating buffers.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));}
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.