Arduino Mozzi Sample Drum Machine

There is still a lot to explore with the Mozzi synthesis library, so today I wanted to look at the “sample” options – getting the library to play pre-recording samples of sounds.

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

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

  • Arduino Uno
  • 4x push buttons
  • 8 ohm speaker or old headphone speaker (or external amplification)
  • 1x 220Ω resistor (for the speaker)
  • Breadboard and jumper wires

The Circuit

ArduinoMozziSampleDrums_bb

There are two sections to this circuit: the four switches as digital inputs; and the audio output.  The four switches are connected to digital pins 2, 3, 4, and 5 all configured in INPUT_PULLUP mode.  This means that they will all read HIGH unless one of the switches is pressed connecting the pin to GND to make it read LOW.

The audio output is a simple resistor to speaker connection picking up the PWM output on pin 9.  You can add in a filter here if you want better quality audio and a coupling capacitor if you want to remove the DC offset, but as shown, this will give a simple 0 to 5V level PWM signal through the speaker.

IMG_5197

It isn’t particularly loud though so you may prefer to send the audio output into some kind of external amplification, in which case a Mozzi output circuit would be recommended (and do re-read the warning above).

IMG_5196

The Code

The Mozzi library should be installed in the usual way.  The basic principles are described in the “08.Samples” – “Sample” example.  The sound to be played has to be recorded into a specific format – in this case a series of signed, 8-bit numbers (so -127 to 128) – which Mozzi will “play” when required by sending them to the audio output as part of the “audioOutput” sequence.

If you want to play multiple sounds, you simply sum the samples together in the updateAudio function.

Here is the initialisation code:

Sample <BD_NUM_CELLS, AUDIO_RATE> aBD(BD_DATA);
Sample <SD_NUM_CELLS, AUDIO_RATE> aSD(SD_DATA);
Sample <CH_NUM_CELLS, AUDIO_RATE> aCH(CH_DATA);
Sample <OH_NUM_CELLS, AUDIO_RATE> aOH(OH_DATA);

When one of the switches is pressed it will start the appropriate sound using the .start() function as follows:

aBD.start();

And to play them, the updateAudio function adds them together:

AudioOutput_t updateAudio(){
  int16_t d_sample = aBD.next() + aSD.next() + aCH.next() + aOH.next();
  return MonoOutput::fromNBit(10, d_sample);
}

We have to remember we are potentially adding together four -127 to 128 signals together so in theory the worst case range of values seen (when all four peaks align) would be -508 to +512.  This is why a int16_t is used for the intermediate value and the return value is reduced from “10 bits” (0 to 1023 or -511 to +512).  This is an absolute worst case though and to be honest unlikely to occur, so there are options in the code to adjust this so it isn’t unnecessarily cut down volume/resolution/sample wise.  But I’ll come back to that in a moment.

Preparing the Samples

One of the most tricky parts of this project is getting a set of numbers into the Arduino for Mozzi to play in the first place.  This is how I did it:

    1. Find some samples.  I used the set of drum samples from this link and then picked out four that I liked.
    2. These need turning into audio files with the following specification:
      • “RAW” audio files (.snd)
      • 8-bit, signed PCM
      • Mono
      • 16384 sample rate
    3. I used Goldwave to open each file, resample it with a 16384 sample rate and save it as a RAW audio file with the above attributes.
    4. Mozzi comes with a Python script that will take a RAW audio file and turn it into a C header file with all the required numbers in.  You can find it in extras\python\char2mozzi.py.  Of course you’ll have to install python if you don’t already have it (details for Windows users here).
c:\Users\Kevin>cd Arduino\libraries\mozzi\extras\python
c:\Users\Kevin\Arduino\libraries\mozzi\extras\python>py char2mozzi.py bassdrum.snd bassdrum.h BD 16384
c:\Users\Kevin\Arduino\libraries\mozzi\extras\python>

I amalgamated the four resulting header files from the above into a single “drumkit.h” file and copied it over to my Arduino src project area, but this is optional – you could keep them as four separate files if you’d rather.

Here is a section of one of the sample definitions.

#define D_SAMPLERATE 16384
#define BD_NUM_CELLS 2046
CONSTTABLE_STORAGE(int8_t) BD_DATA [] = {
0, 0, 0, 0, 0, 9, 6, 7, 4, 12, 12, 8,
14, 8, 18, 20, 13, 16, 26, 21, 19, 22, 25, 28,
// ... and so on for a total of 2046 different values
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

These are stored in the Arduino’s PROGMEM “program memory” so it isn’t possible to read them out directly, but you have to use the pgm_read_xxx_near functions.  Thankfully Mozzi sorts all that out for us anyway.

In case you’re wondering, CONSTTABLE_STORAGE is some Mozzi magic that allows this to work on non-AVR Arduino’s too – but I’m using an Uno so that means PROGMEM for me.  If you are not planning on doing anything with the samples themselves once they are included, then you are largely ignore this bit!

Calibration

Rather than looking through all the numbers in the samples to work out the highest and lowest values used, I’ve adding in a “calibration mode” via a .  If this is enabled the code will print out the maximum and minimum values for each sample to the serial console.

#ifdef CALIBRATE
void calcMaxMin (int drum) {
  int8_t max=0;
  int8_t min=0;
  int num_cells;
  int8_t *pData;
  switch (drum) {
    case D_BD:
      num_cells = BD_NUM_CELLS;
      pData = (int8_t *)&BD_DATA[0];
      break;
    // Repeats for other samples
    default:
      return;
  }
  for (int i=0; i<num_cells; i++) {
    int8_t val = pgm_read_byte_near (pData+i);
    if (val > max) max = val;
    if (val < min) min = val;
  }
  // Then print out max/min to serial port
}
#endif

I can then use this for a particular set of samples to print out the maximum and minimum values in the dataset and use that to choose the most suitable “bit resolution” when it comes to calling the FromNBits() function in updateAudio.

Playing the Drums

In terms of detecting when the switches are pressed to play the samples, I’m using a “trigger” indication and a loop to read the digital switches as follows.

FOR EACH drum switch:
  IF switch is pressed THEN
    IF NOT triggered already THEN
      start the drum sample for that switch
      set the triggered flag for this drum switch
  ELSE
    clear the triggered flag for this drum switch

This all goes on in the Mozzi updateControl function.

Find it on GitHub here.

Closing Thoughts

The volume with the speaker isn’t very loud.  In fact in the video, you’ll struggle to hear the “drums” over the sound of the clicks of the switches!  But with an audio output circuit and fed into some amplification it is fine.

I’d like to hook this up to something like the Analog Keypad Tone Controller and build another rhythm sequencer and it will be interesting to see how many samples I can fit in.

Kevin

Leave a comment