Whew! It took me days of relentless fiddling, but I finally found the problem. It seems my internalRenderBlock was returning early with kAudioUnitErr_TooManyFramesToProcess.
It appears Logic Pro X (and Garage Band on macOS too, for that matter) requests different buffer sizes for selected and unselected tracks.
Selected tracks are the I/O Buffer Size as per the Logic settings (eg. 256).
Unselected tracks are always 1024 frames, regardless of the I/O Buffer Size setting. (lower render priority than the selected track, I suppose?)
Since the Audio Unit Extension template stubs out
language cpp
AUAudioFrameCount maxFramesToRender = 512;
that makes it so that render fails, but only on unselected tracks. And when internalRenderBlock returns early, it never reaches processWithEvents, which in turn calls handleMIDIEvents, and MIDI events aren't processed (neither is audio, possibly resulting in periodic audio glitches).
Setting maxFramesToRender = 1024; fixes that problem in my case.
Now, I can't help but feel this default maxFramesToRender = 512; is setting newbies like me up for failure, since as far as I can tell it necessarily results in Logic (presumably the most obvious test host) failing to render in a highly elusive manner. But I'm just a newbie programmer, and I'm sure there must be a reason for that 512 not being higher that I just happen to not see. (theories on the reason for that default size of 512 are very welcome!)
Anyway, that's one reason less to lose sleep over. Whew!
Post
Replies
Boosts
Views
Activity
I'm just replying to say THANK YOU for the detailed explanation of how you got it to work. You definitely saved me many, many hours of headaches figuring all that out by myself!
To other people who possibly find this thread and find that AUHostMusicalContextBlock is always nil: (I hope Leonardo and jfjs already got it, since it's been 3 years...)
since AUHostMusicalContextBlock is apparently only invoked during render time, it seems it's always nil unless you poll it from the render block. Mine ended up looking like this:
Objective-C (AUInternalRenderBlock)internalRenderBlock {
//return _kernelAdapter.internalRenderBlock;
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *timestamp,
AVAudioFrameCount frameCount,
NSInteger outputBusNumber,
AudioBufferList *outputData,
const AURenderEvent *realtimeEventListHead,
AURenderPullInputBlock __unsafe_unretained pullInputBlock) {
// get tempo from host
double currentTempo;
if ( _musicalContext ) { // only use this if the host supports it...
if (_musicalContext( &currentTempo, NULL, NULL, NULL, NULL, NULL ) ) {
[self-_kernelAdapter setTempo: currentTempo];
}
}
return self-_kernelAdapter.internalRenderBlock(actionFlags, timestamp, frameCount, outputBusNumber, outputData, realtimeEventListHead, pullInputBlock);
};
}
(I hope I'm doing this right, I don't really understand exactly how Objective C blocks work, as you can very possibly tell...)