Frobulator was written with the aid of Michael Tyson’s Amazing Audio Engine, which furnishes a lot of the infrastructure needed for working with the iOS audio environment, and for integrating with Audiobus — which was also developed by Tyson in collaboration with Sebastian Dittman. MIDI CCs are implemented using the the MidiBus library by Nic Grant of Audeonic, which takes care of most of the messy business of identifying and interacting with the various possible MIDI sources on iOS.
The app is coded in C, Objective-C and Swift. The part of the code that actually runs on the real-time audio thread is (as Michael sternly admonishes) vanilla C. Some of the simpler audio processing is actually performed in this code, but mostly it just stashes the audio in a buffer for processing elsewhere, and then reads back results that were processed in a previous cycle. The rest of the processing is done periodically on the main thread, in much larger chunks than the audio thread is dealing with. Doing it directly on the audio thread would hold things up for an unacceptably long time.
The periodic main thread audio duties are handled in Objective-C. The bulk of what that code does is call functions from the vDSP library, part of Apple’s Accelerate framework. These provide various useful vectorized — and hence faster — mathematical operations, as well as the all-important FFTs.
Swift is used for most of the UI code and the application lifecycle, and also for control and modulation of the audio code. This stuff doesn’t do any audio processing itself, but it asynchronously adjusts parameters that are used by the audio functions. So far this appears to be an effective division of labour, although there are a few annoying cases where Swift just can’t cope and a bit of kludgy Objective-C glue is required.
In outline, Frobulator consists of two parallel “FrobFilter” instances in series with a bog-standard second-order IIR filter.
The latter is basically just a wrapper around the Accelerate function vDSP_deq22, with a tiny bit of buffering to carry end samples from one call to the next, and some other administrative scaffolding. Coefficient calculations for the different filter modes are based on the recipes in Robert Bristow-Johnson's Audio EQ Cookbook.
The FrobFilters operate on fixed size blocks, with a different block size for each instance. They transform each block to the frequency domain, and then perform some or all of the following steps:
Back in the time domain, the processed block is overlapped and added to the previous one and a shorter delay block is also mixed in on output.
The parameters controlling all these steps, as well as the filters and the panning of the various outputs, are not set directly by the user, but are instead driven by modulators. (The only parameter for which the knob value is used completely unmodified is mix.)
In normal mode, everything is essentially modulated by sine wave LFOs. The basic sine waves are multiplied and added to get various compound signals, but they're all still pretty straightfowardly wavy. The wave frequencies and mixing proportions are controlled by the lower knobs: orbit, vexity and doubt.
Such sine wave modulations also contribute (with somewhat different values) in fail mode, but they are also supplemented by a pair of chaotic maps (based on the Standard map). The latter give rise to much choppier, quasi-chaotic behaviour. It’s not clear that this is actually any better than using a basic pseudo-random number generator, but it was a mildly interesting experiment. As with the LFOs, the map parameters are also affected by the various knob settings.