Auduino MIDI Granular Synthesis

I wanted to return to the Auduino project and add MIDI functionality.  This project describes how to add some basic MIDI receiving functionality.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments or gear!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

The Circuit

AuduinoMIDI_bb

This is the same Auduiono standard circuit but the “sync frequency” potentiometer isn’t used and a MIDI receive circuit is hooked up to the Arduino RX pin.

The four potentiometers are used as follows:

  • Grain 1 frequency
  • Grain 1 decay
  • Grain 2 frequency
  • Grain 2 decay

In principle you could hook up a loudspeaker (via a suitable resistor) to the output pin directly, but it would be better to connect it to some (suitably sacrificial in case of problems) amplification.  I’ve used my Arduino PWM Output Filter Circuit as the output stage.

Once again I’ve used my six-potentiometer board I put together for the Arduino Multi-pot Mozzi FM Synthesis project even though I only need four for this project.

The Code

To add MIDI functionality to the original Auduino code base is relatively straight forward.  I’ve included the Arduino MIDI Library and initialised it as follows:

#include <MIDI.h>

#define MIDI_CHANNEL 1
MIDI_CREATE_DEFAULT_INSTANCE();
int midiNote;
int numMidiNotes;
#define MIDI_NOTE_START 24 // 24=C1

There are some additional global variables that will be used later too.

I’ve opted for a simple four-potentiometer setup, so configured them as follows:

// Map Analogue channels
//#define SYNC_CONTROL (4)
#define GRAIN_FREQ_CONTROL (0)
#define GRAIN_DECAY_CONTROL (1)
#define GRAIN2_FREQ_CONTROL (2)
#define GRAIN2_DECAY_CONTROL (3)

In the setup() function, we need to initialise the MIDI library:

MIDI.begin(MIDI_CHANNEL);
MIDI.setHandleNoteOn(midiNoteOn);
MIDI.setHandleNoteOff(midiNoteOff);
numMidiNotes = sizeof(midiTable)/sizeof(midiTable[0]);

Notice how I calculate the number of notes in the (already provided in the original code) midiTable.  Together with MIDI_START_NOTE, this give the range of recoginsed MIDI notes.

And in the loop() function we need to make a call to the MIDI read function and update the “syncPhaseInc” frequency based on the currently playing MIDI note:

MIDI.read();

if (midiNote == 0) {
  syncPhaseInc = 0;
} else {
  syncPhaseInc = mapMidi (midiNote-MIDI_NOTE_START);
}

This uses the pre-existing mapMidi function that already converts MIDI notes into frequencies suitable for use with the Auduino synthesis engine. I’ve scaled back the note number so that the first recognised MIDI note (defined by MIDI_NOTE_START) yields an index for mapMidi of zero.

There was one issue though.  As written the mapMidi function assumes an input value from a potentometer – so 0 to 1023 – and scales the input to fit.  This needs updating to accept an index based on the MIDI note number instead.  This is shown below (the original code is left in as a comment):

uint16_t mapMidi(uint16_t input) {
  // return (midiTable[(1023-input) >> 3]);
  return (midiTable[input]);
}

Then it just a matter of setting up the MIDI functions to handle the noteOn and noteOff messages.  The basic idea is simply to set the midiNote global variable to whatever the MIDI message says should be playing next.  This is then picked up in the main loop and turned into the appropriate frequency for the Auduino synthesis engine.

void midiNoteOn(byte channel, byte pitch, byte velocity)
{
  if (velocity == 0) {
    midiNoteOff(channel, pitch, velocity);
    return;
  }

  if ((pitch < MIDI_NOTE_START) || (pitch >= MIDI_NOTE_START+numMidiNotes)) {
    return;
  }

  midiNote = pitch;
}

void midiNoteOff(byte channel, byte pitch, byte velocity)
{
  if (pitch == midiNote) {
    midiNote = 0;
  }
}

It probably goes without saying that this is monophonic.  As implemented it is a “last note played is the current playing note” system.

Closing Thoughts

This is a simplistic implementation of MIDI and deliberately so.  The Notes and Volts code includes additional functionality (such as supporting pitch bend for example), but I was after the basics here.

The video shows it in action though driven by my Toy Keyboard USB Matrix Decode with the Pi Pico.

I need to spend a bit of time tweaking the parameters now to get something that gives me a good range of sound generating possibilities.

At some point I’d like to create a version of the Auduino code that can be run on my Universal Synthesizer Panel but that will need some messing around with the ATmega328 timers as I’d need the audio output on pin 9 rather than pin 3.

Kevin

Leave a comment