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!
Topic:
Developer Tools & Services
SubTopic:
Xcode
Tags: