Saturday 28 November 2015

Polyphonic Auduino

If you're into Arduino hacking and are also interested in synthesizers, you've probably heard of the Auduino granular synthesizer by Peter Knight of tinker.it. It's a simple noise maker built using a 16MHz Arduino board and a handful of components. And it's a lot of fun to play with.


If you want to build one, you can head over to Notes and Volts where there's a multi-part video explaining how to do it, starting with a standalone Arduino board, the actual Auduino, and then two videos describing a line level and finally a MIDI mod (links to the various parts are provided in the video).


The synthesis engine is only a few lines of code and invites hacking it ... to boldly go where no man has gone before. So, what about a polyphonic Auduino?

To generate sounds, the synth employs a method called granular synthesis. I don't know anything about granular synthesis (and haven't found a simple introduction on the intertubes), but from reading the code I was able to dissect the basic operation of the synth. There's a two-part counter called synthPhase (with an Accumulator and an Increment) that controls the pitch of the sound. It does so by determining the rate at which two triangle wave samples -- the grains that give the method its name -- are replayed (i.e. restarted). The two triangle wave oscillators that generate these samples consist each of a two-part counter grainPhase (grain2Phase, respectively, each with an Acc and Inc) and a decaying amplitude grainAmp (grain2Amp, respectively). The speed and decaying rate of these two oscillators is determined solely by the parameters read in the main loop. In other words they are quasi free-running oscillators that are independent of the note being played. All in all there is one counter for the note and two for the grains. In principle, adding another voice would mean adding another counter. Does the Arduino have enough power to allow adding more voices?

If you inspect the code (I'm talking of the midified code by David Benn of Notes and Volts) you can see that it consists of two loops. Actually, you don't, but it does. The main loop is represented by the function called loop, and the second by the timer interrupt routine. The loop function is called repeatedly by the Arduino main program and contains all time insensitive operations such as the processing of MIDI events and the reading of the potentiometers that control the sound of the synth. The second one is called repeatedly by a timer and is responsible for computing the actual sound samples at a rate of 31.25kHz. If you consider that the Arduino's processor is running at 16MHz, this leaves at most 512 cycles for the interrupt routine. Otherwise it won't be able to finish before another interrupt is initiated. That's not a lot. In order to provide, say, four independent voices we need to severely limit what needs to be computed for a sound sample.

Before starting to tinker with the code I measured how long it takes the interrupt routine to compute a sample. I built a version of the Auduino code with a free digital pin being set when the interrupt routine is entered and cleared before it is exited. A plot of the corresponding signal on an oscilloscope shows a square wave with a duty cycle of about 26%. In other words, the processor spends a bit more than a quarter of the time in the interrupt routine. Upon closer inspection, I found a few places where the compiler produces sub-optimal code, especially with regard to multiplications (a fact that has not been lost on others). By working around that and also reformulating a further two lines of code, I was able to reduce the duty cycle to less than 22% without even beginning to change the algorithm.

In order to see, how far I can push the envelope with a simplistic approach, I copied the wave generating code within the interrupt routine three more times, added some simple 4 notes on/off logic and burned the whole contraption to the Arduino. Lo and behold, I was able to play 4-note chords on the Auduino with the duty cycle only going a tad above 75%.


Spending three quarters of the time in an interrupt routine is a lot, but perhaps just about acceptable. I analyzed the code (including the generated assembly code), and I can confidently say that 4 voice polyphony is quite the limit on a 16MHz Arduino, if one wants to keep the algorithm as it is. Of course, one needn't do that. I wonder, if you can hear any difference, if the grain waves are of a different shape. After all, the ATmega contains two hardware counters (one 16 and one 8 bits) which could be used instead of the software counters to generate the grain waves.

So there you go. A 16MHz Arduino is perfectly capable of doing 4-voice polyphonic grain synthesis. Four voices is not a lot, of course. But considering that you can buy brand new professional equipment with just 4-voice polyphony, we need not be embarrassed.

2 comments:

  1. Wonderful work! Do you have the source available somewhere? I'd love to have a look at it =)

    ReplyDelete
  2. I will share my velocity sensitive code and schematic if you share yours.

    ReplyDelete