Arduino VS1003 Drum Machine

I picked up some cheap matrix button boards a while back and wanted to use them as some kind of MIDI controller, so linked up with the Arduino MIDI VS1003 Synth I thought I’d make a self-contained simple drum machine.

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

The Circuit

2021-01-13 22.24.18

This makes use of a 4×4 matrix of buttons as shown above.  I picked these up from an online auction site from overseas. In theory you could make the same circuit using a breadboard.  The basic idea is the same as used in my Keyboard Matrix Decode project, so I won’t go over the theory again.

There are full instructions in the Arduino Keypad tutorial too, so I refer you there for more.

These circuits are wired up so that four of the pins represent the rows of buttons, and four of the pins represent the columns.  The exact layout and connections of boards will differ, so you’ll have to see how your own keypad matrix pans out.  The “CustomKeypad” example application will help you here, by allowing you to put in your row/column pins and seeing which keys activate which key codes.

By far the simplest way to build this is as I have done – linking it to my Arduino MIDI VS1003 Shield ideally with some additional controls as described in Arduino MIDI VS1003 Shield – Part 2.

2021-01-13 22.23.12

I was even able to reuse the LED that I added as a “start of pattern” indicator.

However it is perfectly ok to build the same circuit up using solderless breadboard, but you’ll have to figure out how to do that yourself.  These are the connections I’ve used though:

  • Arduino D7 to D13, 5V, GND to the VS1003 (as described previously).
  • Arduino A0 and A1, 5V, GND to potentiometers (optional).
  • Arduino D6 to an LED via its resistor (optional).
  • Arduino D2 to D5 for the switch matrix rows.
  • Arduino A5 to A2 for the switch matrix columns.

The VS1003 phone jack will need plugging into some kind of amplification or headphones.

The Code

The basic requirements for the code are as follows:

  • Pots for volume and tempo.
  • The 4×4 matrix to correspond to four drums being played on four beats.

The drum pattern is stored in a two dimensional array.  One dimension represents the beats and the other each of the potential drums.  If a MIDI drum note is present, the drum will play on that beat.  If it isn’t, it won’t.

The beat is generated by letting the main loop() free run, but comparing a counter with the return of the millis() call.  When the counter says the right amount of time has passed for a beat, then it will play any configured drums for that beat.

The main loop thus looks something like this.

loop()
  timenow = millis()
  IF timenow > nextsteptime THEN
    Play one beat and move the sequence on to the next one
  ENDIF

  Every time through the loop, handle one of the following:
     a) The keypad
     b) The tempo pot
     c) The volume pot

The time checking and playing of the beat is handled first, so that when the timer is up, the beat is pretty accurate.  I’ve split up the handling of the buttons and pots so that the time spent in the loop between time measurements is kept to a minimum, which again helps keep the beats accurate.

The two pots are scaled down to 8-bit values, so read 0 to 255.  For the tempo, I add 20 giving a range of tempo from 20 to 275 beats per minute.  This means the fastest tempo has 60000/275 = approx 220 milliseconds between each beat, or just over four beats a second, which makes sense – four beats a second would be 240.

The Keypad library is provided “out of the box” with the Arduino environment so there is no additional installing required.  As stated above it is worth putting in your row and column pin numbers into the CustomKeypad example to test out the library and pin connections.

For the orientation I wanted (wires at the top) and pinouts listed above, my keypad matrix looks like this in code:

char keys[KP_ROWS][KP_COLS] = {
{0x14,0x24,0x34,0x44}, // Drum 4, beats 1,2,3,4
{0x13,0x23,0x33,0x43}, // Drum 3, beats 1,2,3,4
{0x12,0x22,0x32,0x42}, // Drum 2, beats 1,2,3,4
{0x11,0x21,0x31,0x41}, // Drum 1, beats 1,2,3,4
};
byte rowPins[KP_ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[KP_COLS] = {A5, A4, A3, A2}; //connect to the column pinouts of the keypad

Now you may well be wondering why I have such odd values in the keys[] matrix rather than then usual ‘1’, ‘2’, etc.  Well this is because I want to use each key to set or reset a drum note on a specific beat in the pattern.

I already mentioned that the pattern is a two-dimensional array.  These magic numbers will automatically tell me which beat (the first digit) and which drum (the second digit) each switch corresponds too.  The only quirk is that 0 is reserved for NO_KEY, so I’ve had to use 1-based indexing.  But calculating the index for both beat and drum is relatively straightforward.  This is the code that runs if a key press is detected.

int drumidx = (key & 0x0F) - 1;
int beatidx = (key >> 4) - 1;

// So toggle this drum
if (pattern[beatidx][drumidx] != 0) {
  pattern[beatidx][drumidx] = 0;
} else {
  pattern[beatidx][drumidx] = drums[drumidx];
}

Basically, if a key is detected, work out the two index values and if the pattern for that beat and drum is zero, then store the drum note number to play there, but if the pattern is non-zero, then it already has a drum stored, so clear it by setting it to zero.

Then, when playing a specific beat, the play code just looks for any non-zero entries in the pattern array for that beat and sends MIDI noteOn messages to the VS1003.

I initially thought I’d send out MIDI noteOff messages using the previous step, but then realised that there could be race conditions where a drum is played, but then the keypad disables that drum/beat, and the noteOff is never sent. For drum patterns this probably doesn’t matter, but feels very untidy.  So in the end I just send a MIDI noteOff for all drums regardless of their status in the pattern prior to sending the noteOn messages.

This results in superfluous MIDI noteOff messages, but I thought that was better than situations where notes are never terminated.

The last point to note is that although this is sending MIDI to the VS1003, it is using the SPI bus as in all the other examples so far – so this doesn’t actually need to use the Arduino MIDI library.

I think that is about it, other than to mention that the pots/LED parts of the code are optional via statements, and this should all work just as well on a VS1053 fine too.

Find it on GitHub here.

Closing Thoughts

Some variations for this could include:

  • Using the second pot to change drum sets rather than volume.  The VS1003 has 13 defined percussion sounds.
  • Including some way of showing which drum/beat combinations are active using a 4×4 matrix of LEDs or other display.
  • Including some selectable built-in drum patterns.
  • More switches!  Although I’ve already run out of IO pins, and would need to use a multiplexer.
  • Produce an actual MIDI out signal rather than drive the VS1003.

Kevin

Leave a comment