Let’s build a basic synth with AudioKit – part II

Let’s build a basic synth with AudioKit –
Part 2: Project creation

(If you missed Part I, please read that section first).

In the previous episode, we inspected from a macroscopic perspective, what’s under the hood in a virtual synth. Now we are ready to apply these concepts and port them into a fully working project. Let’s start.

Create the XCode iOS Project

We are assuming that you have properly installed AudioKit, if not please download/install it by following the official guidelines.

First thing first, let’s open XCode and create a new Single View iOS Project

Single View iOS Project
Single View iOS Project

Next, select default settings, select your company organization and the other stuff, press Next, in the next popup window select the directory where to save the project, eventually press Ok to finish the setup process.

Now, you should include your AudioKit framework to your project, you can do this i.e. by using CocoaPods, or manually embedding the framework, please read the official guidelines, as above.

If you’ve done anything correctly, pressing ⌘B should lead to a successfully project building, without errors. Otherwise, repeat installation/embedding instructions.

Now, we are ready to get our hands dirty with the code. Open your ViewController.swift file. We will do all our work into this ViewController, also we will not use Storyboard, but instead we will create our GUI properties programmatically, in order to speed up things. Code is self-commented so it should not be tricky to understand even if you have minimum iOS programming and Swift knowledge.

Create the Oscillator

First, we will create an Oscillator to generate the sound. AudioKit gives us many type of Oscillators, for simplicity purposes we will generate an Oscillator that will produce a simple Sawtooth waveform (due the fact that Sawtooth waveform has a big amount of harmonics, that we will subtract after with our filter, in order to get a more audible result).
For this purpose,  we will use AKOscillator class. There are other types of Oscillators, but for now we will go straight with this (simple) one; in the next series we will learn to use other Oscillator types and even to write down our own Oscillators. Let’s create the variable, we will call the class’ init() method later.

Create the Filter

Next block is our Filter section. We will use a simple Lowpass Filter, in this case AKLowPassFilter class will be our friend.
A lowpass filter (at its basic implementation) has two parameters:

  • Cutoff Frequency -> is the frequency beyond which the filter will start to subtract frequencies
  • Resonance (or Q factor) -> is a dimensionless parameters that in this case represents the amount of resonance of the filter in its cutoff frequency

For simplicity purposes, when we will modify the “filter” value using our GUI Slider, we will automatically set the Resonance value according to Cufott Frequency value.

Create the ADSR Envelope

Last but not least, we’re going to create our third and final block, for this basic project: the ADSR Envelope. We will use the AKAmplitudeEnvelope class, that provides us with Attack, Decay, Sustain and Release settings.

Override viewDidLoad() method

In order to make things clean, we will split our work into two macro functions: and setupAudio() and setupUI()

Create private function setupAudio()

We will first instantiate our AKOscillator property with a sawtooth AKTable (waveform). This is our “Oscillator node”.

Next, we will setup our AKLowPassFilter. We pass into its constructor 3 parameters: an AKNode (all generators/effects nodes inherit from AKNode) that is our oscillator and its initial values for Cutoff Frequency and Resonance. Human beings are capable of perceiving frequencies up to ~22 kHz (in the reality, if a 40 yo. man can perceive a 16 kHz frequency he could say he’s lucky!), so applying a Cutoff Frequency of 22kHz would mean “Do not subtract nothing audible”, and for a sampling rate of 44100 kHz it would preserve almost the entire frequency spectrum (as stated by Shannon-Nyquist sampling theorem).

Eventually, we will instantiate our ADSR Envelope, accepting as input node our filter and settings its initial values: 0.01 ms for the Attack, 0.1 ms for the Decay, 1.0 as Sustain value and 0.1 ms as Release.

Now it’s time to tell AudioKit which node will be used as its output.
So, our chain acts as following:

OSCILLATOR -> flows into FILTER -> flows into ADSR ENVELOPE

So we will ask AudioKit to set our ENVELOPE node as its output.
If we would route our audio signal *before* entering into envelope stage, we could ask AudioKit to set our FILTER node as its output, in example.

Next, last thing to do is to start our engine, via AudioKit.start() method.

Create private function setupUI()

In the next and final part we will create our custom controls with Paintcode, meanwhile we will use default sliders and keyboard that AudioKit provides us.

I assume that you are familiar with concepts like “Delegate methods” and “Closures”, if not this is time for you to get back into iOS/Swift programming.

We will use AudioKit’s AKKeyboardView. It provides us with a standard keyboard, we can select numbers of octave, also. For our basic implementation, this sounds pretty good.
Next, we apply some constraints in order to take advantage of XCode’s AutoLayout and placing the keyboard at the bottom of the iDevice’s screen.

We will user AKKeyboardDelegate in order to get notified when a key is pressed or released. These methods will be triggered, so we use extensions to extend our ViewController with AKKeyboardDelegate functionalities (aka: functions). We could implement them directly in our ViewController class and implement the interface during class signature/declaration, but I’d rather prefer using extensions, I think it’s a more elegant pattern to follow. But this is totally up to you.

Ok, now meet AKPropertySlider. This “straight-out-of-the-box” class gives us a nice way to hook a AKNode’s property and set its value. It’s a horizontal slider, let’s analyze its properties:

  • property -> is the name we want to assign to the property
  • format: -> is the format style we want to do to the visualized value. “%0.2f ms” in example means we want use 2 decimal cyphers and we will add “ms” after the value
  • value: -> is the property we want to hook. So the initial value of the slider will reflect the initial value of the property
  • closure -> every time we change the slider’s value, a closure will be triggered, capturing a value (its type will be equal to property value type) and permitting us to “do things” with that value. This is right time to set up our property’s value, and/or other properties’ values that depends from this slider

After declaring one slider for our ADSR Envelope’s Attack, Decay, Sustain, Release and for our Filter’s Cutoff Frequency, we will setup their constraints, as well as we did for the keyboard.

We will activate our constraints and we are done.

Listening events triggered from the Keyboard

The AKKeyboardDelegate gives us 2 functions:
noteOn(note:) and noteOff(note:)

In both functions, the “note” parameter tells us *which* note is pressed/released. In our case, we are building a monophonic synth, so this not really matters. But in the case of a polyphonic synth, you will know which note has been triggered or released. 

When a note is triggered, we will grab its midi-to-frequency value (AKKeyboardView will trigger “midi-like” notes like 62, 75 etc.), next we will tell our oscillator to set up oscillating frequency according to this value. At last, we start both our envelope and the oscillator. Starting only the oscillator would produce no sound, due the fact that its amplitude will be constantly 0. Oscillators have a parameter, amplitude itself, that in our case is automatically hooked to our ADSR envelope, when we specifies that our oscillator should be used as input AKNode for our ADSR Envelope. To say it in a nut, we control our Oscillator’s amplitude via our ADSR Envelope.

When the key is released, we may be tempted to stop our Oscillator. And this will effectively stop the sound, but this would prevent our release duration from being respected, due the fact AKOscillator is a very basic oscillator and it does not provides us with its own ADSR values, like instead other oscillator types do, like AKOscillatorBank in example.

So we will use a little trick, dispatching oscillator.stop() when our release duration will be reached. It’s not 100% precise, but for this tutorial’s purpose it’s okay. After this happens, we can safely stop our oscillator *AND* our ADSR Envelope (if we don’t stop also the Envelope, its next start() wouldn’t work properly). 

Basic Synth

Build the project and test it

Full project is available here 
Don’t forget to add your AudioKit framework to the project.

You can build the project and run it without get any problem. Just play notes on the keyboard and feel free to play around with the sliders

In the next episode, we will create our custom controls in Paintcode.

Cheers,
Mike

To be continued in Part III…

Related Posts

Leave a comment