Introduction: MIDI Drum Machine
A drum machine is a musical instrument which plays sequences and patterns. Usually drum machines produce percussion sounds, drum beats, etc. but this one has a built-in MIDI synthesiser, amplifier and speakers so it can produce "melodic" as well as percussive sounds. It allows you to program your own rhythms and beats.
You can also attach pads that allow you to play drum sounds "live", either on top of a programmed drum beat or as a standalone performance.
The musical notes are produced by a MIDI chip - the VS1053 - which has 166 voices (i.e. allegedly-different instruments). It has a high degree of polyphony (up to 64) so it can play single notes or chords.
The patterns are programmed via a 320x240 touch screen. There are up to 8 tracks and up to a 16-step bar.
Each track can have a different "voice" and amplitude. There are 119 "melodic" voices and 47 "percussive" voices. With a melodic voice, such as a piano, each beat on the track can play a different note.
You can save and load different "Setups" and switch between them during a performance.
The whole MIDI Sequencer operates stand-alone with its own speaker and battery.
If you're going to copy my build, you will need an Arduino Nano (£1.50), a VS1053 module (£4.50), a 3.2" ILI9341 SPI display (£7.80) and a few resistors. You'll also need a case, some powered speakers and perhaps a lithium cell and a PSU but the details will depend on how you decide to build it. I got all those extras from car-boot sales and charity shops. Plus you'll need the usual electronic workshop paraphernalia.
Step 1: Controlling the VS1053
I chose the VS1053 module shown in the picture. Search eBay, Alibaba or your favourite supplier for a VS1053 module that looks like that. It works well "out of the box" without modification.
There seem to be two other VS1053 modules available on Alibaba and eBay a red one and a blue one. The blue one is the one I used in my MIDI Theremin, it too works without modification. The red one is manufactured to be in non-MIDI mode by default. You can modify the hardware or software to put it into "live" MIDI mode. Read the discussions at the bottom of my Theremin project to see how other people have got it working. Once you've got it into MIDI mode it will respond to the same messages as the module I used here. I haven't tested it.
The VS1053 is a fine chip but rather complicated. I'm only using the MIDI part of it. It's possible to control the VS1053 over a serial interface but I'm using the SPI bus as it's more convenient with an Arduino Nano. Any byte you send over the SPI bus is treated as a MIDI command.
You can download the VS1053 data sheet from the web. It's a huge document and is hard going. Section "8.9 Supported MIDI Formats" is almost all it says about MIDI. Section "10.10 Real-Time MIDI" talks about using GPIO0 and GPIO1 to enable MIDI but the board I using here doesn't require any special enabling. You can also download a list of MIDI messages (not all of which are supported by the VS1053).
Wire the VS1053 module to an Arduino Nano as shown and upload the INO file to the Arduino. I used a solderless-breadboard.
The connector of the VS1053 module is a double-row pin header. I made a lead using a ribbon cable connector from a scrapped PC.
The drumsynth0.ino sketch receives a byte from the PC over the serial line and sends the byte to the VS1053. It's a very simple program which allows you to test the VS1053. Connect the output jack socket to headphones or a computer speaker.
The Windows DrumSynth0.exe program (download ) sends commands to the VS1053. Click the "90 note vel" button to play a note. Or you could write your own Windows program. Or use one of the many terminal programs available on the web.
The VS1053 module has the following pins:
- the SPI bus has the usual MISO, MOSI and SCLK
- if XRST is low, the chip resets
- XDCS doesn't do anything in SPI mode so tie it to XCS
- XCS is Chip Select
- DREQ tells you when the chip is ready for a new command.
XCS should be set low while you're sending a byte; then high. That way, you're sure you have synchronised the first bit of each byte. Reading DREQ tells you that the chip is ready to receive a new command (I ignore it).
After the Arduino sends a byte, it must send a dummy byte so as to toggle the clock and allow the VS1053 to send a byte back in response. The SPItransfer() function shows you how.
Now you're confident you can make the VS1053 work, we'll turn it into more of a musical instrument. Attachments
Step 2: Adding a Display
The display is a 2.8" colour TFT LCD screen with a ILI9341 controller, 320x240 pixels. There are lots of screens available on eBay, for instance you might prefer to develop your instrument with a larger touch-screen. I chose a 320x240 SPI display because can be updated reasonably quickly. A larger 480x320 display would be nicer but might be too slow. I've ordered one and will try it out. SPI is slower than using a parallel interface but the Nano doesn't have many pins.
The module is sold widely on eBay - just get one that looks the same as the photo.
The LCD has the following pins:
- VCC 5V
- GND ground
- CS LCD chip select
- RESET reset
- DC data/command
- MOSI SPI bus MOSI of Arduino
- SCK SPI bus SCLK
- LED back light
- MISO SPI data out of LCD (ignored)
- T_CLK SPI bus SCLK
- T_CS touch chip select
- T_DIN MOSI of Arduino
- T_DO SPI data out of touch
- T_IRQ touch interrupt request (ignored)
The display I bought has a built-in 3V3 regulator. If you look on the back of the PCB, you'll see it connected to the VCC pin. It's a 662K chip. So it can be powered by 5V and you can connect it directly to your 5V Arduino pin. The LED power pin can also connect it directly to the 5V pin.
(Check that your display has a 3V3 regulator. If it doesn't and it requires 3.3V power then it takes around 50mA which is just at the limit of what the Arduino Nano's 3.3V regulator can provide. So it may be a mistake to power the display from the Nano's 3.3V pin. You'll should supply your own regulator.)
The logic pins of the display require 3.3V signals. You can't connect them directly to the 5V I/O pins of the Arduino. I've used 2k2 and 3k3 resistors to drop the voltage.
Adafruit very kindly publish an ILI9341 library and several other libraries are available in Github and elsewhere. I tried a few and didn't like any of them. Some simply didn't work and all were huge. You write an Arduino sketch that draws a line and some text and you find your memory if 75% full. So I wrote my own drivers. The SimpleILI9341 library can be downloaded (below).
I've decided I don't like the way the Arduino IDE uses "libraries". It makes version control difficult. So I now keep the code needed to compile a sketch together. Put all these files in the same folder:
SimpleILI9341 has a standard set of drawing commands very similar to all such libraries.
Some of the "fast" libraries you can download use special timing loops and are upset when other, maybe slower, devices are used on the same bus. SimpleILI9341 is written in C rather than assembler so isn't quite as fast as it could be but is much more portable and it shares the SPI bus politely with other devices. A Windows program can be downloaded which allows you to make your own fonts and icons.
You can download the ILI9341 data sheet from the web. You send it a command by
- set DC low
- set CS low
- send a command byte
- set CS high
and send it data by
- set DC high
- set CS low
- send zero or more data bytes
- set CS high
You can see how I do it in the tft_write functions. The data bytes might be a whole row of pixels or a setting for a control register.
The ILI9341Begin() function in the library shows you the initialisation command set I've chosen. You might want to change the commands if you choose a different ILI9341 display (e.g. with more pixels) or want a different orientation. I hope my code is easy for you to see how to change if you need to.
Two functions control the touch screen: BeginTouch() and GetTouch(). BeginTouch() sets the CD pin of the device and allows you to adjust the x.y min,max of the touch position so that the position matches the pixel coordinates. GetTouch() returns true if a touch is happening and also returns the coordinates.
Step 3: The User Interface
There is a Beat Checkbox for each of the 16 beats for each of the 8 channels. Click on a Beat Checkbox to enable a note to be played on that beat. The note will be played with the voice of that channel.
The voice for a channel can be changed by clicking the channel's Voice Button. When you click on a Voice Button, a panel appears which allows you to choose one of 166 voices and the set the amplitude of the channel.
MIDI has 128 standard voices called "General MIDI patch" numbers from 0 to 127, I only use the first 119. It also has 47 "General MIDI Percussion" numbers (from 35 to 81). The percussion voices are on channel 9: the patch (instrument) number part of the MIDI command is ignored and the note number selects a different percussion sound.
Click the Enable Checkboxes to enable or disable that whole channel.
The Beat LEDs show which beat is currently being played. If you click on one of them, the beats to the right will be disabled so you could, for instance, have just 8 beats to the bar.
The Tempo Slider sets the rate at which the beats are played.
The Run Button enables or disabled running the pattern.
The Note Keyboard appears when the Current Note belongs to a melodic voice. A melodic voice could be, for instance, a piano or xylophone. The Note Keyboard displays the note that will be played. You can change the note by clicking on the keyboard or on the left/right buttons at the sides of the keyboard.
Whenever you make a change, the pattern is saved to the current "setup". The processor can remember 6 different setups. The Setups Button allows you to load a new setup.
Step 4: Adding a Knob (optional)
Choosing a voice requires a lot of clicking - there are 166 of them - so I decided to add a rotary encoder. It's optional. The drum machine can be used without it.
If you fit an encoder, you should enable the code by defining the HasEncoder macro define:
You'll find it (commented-out) near the top of the sketch.
The encoder allows you to set
- the Tempo
- whether it is Running
- the Voice of the current channel
- the Volume of the current channel
- the Note of the current beat or channel
- the number of Beats-per-Bar
- the current Setup
- how many Channels are enabled
If you click on one of those controls, it will be highlighted in red. Turning the knob of the encoder then adjusts that value.
If you press the knob in, the next control is highlighted.
The encoder I used has 24 detente positions and for each "click" it produces a complete cycle of quadrature signals. Other encoders may produce other outputs. You may need to write your own encoder function.
The CheckEncoder() function gets input from the encoder pins and converts them into up/down counts. If your encoder is different, you'll have to rewrite CheckEncoder(). Everyone has their favourite algorithm for an encoder. The important thing is to make sure the algorithm you choose behaves well when there is contact bounce.
Step 5: Adding Drum Pads (optional)
You can optionally add drum-pads so you can play along with the pre-recorded beats or record new beats. There are lots of Instructables for making electronic drum-pads - search Instructables for "drum pad". Or you can google for "diy drum pad".
The most popular designs use a piezo sounder disc as a sensor. The piezo disc is glued to the underside of a flexible membrane and produces a voltage when the membrane it hit. The output from the piezo is connected to an ADC pin of an Arduino and the size of the resulting voltage determines how loud the Arduino plays the "drum" sound.
What's not clear from the various instructions on the web is how it works and how the disc should be attached. How big is the voltage? How long does a pulse last? Is it a single pulse or several cycles of a wave?
A piezo crystal generates electricity when it is compressed or expanded. Conversely, a voltage across the crystal causes it to compress or expand.
The compressions and expansions are tiny so you wouldn't hear the output from a piezo crystal if it was used as a sounder. A piezo sounder therefore designed to bend the brass disc when a voltage is applied. The deflection is much bigger that way. So, to use a piezo souder as a sensor, you should bend it.
If you sandwich the peizo between the flexible membrane pad and a hard surface, you will get a voltage when the stick hits exactly above the piezo and squashes it. However, even a couple of cm away and you'll get nothing. But if the membrane can flex then the piezo bends and produces a voltage.
You should design your drum-pad so that hitting it anywhere near the centre causes the membrane to bow inwards and bend the disc. If you hit it further from the centre, it should still bow in but to a smaller degree.
I happened to have some 5mm medium density closed cell foam sheet left over from another project. It was sold as a "camping mat" but was so thin you wouldn't want to sleep on it. I found that a real camping mat was higher density and much thicker so didn't give such a good result. Other people have used mouse-mats.
I used contact adhesive to glue the piezo onto the closed cell foam sheet. Then there was a layer of soft open-cell "foam rubber" to support the closed cell foam yet allow it to bend. That was glued on with PVA. Make sure you cover the "top" surface of the piezo with insulating tape - it didn't work while it was touching the PVA. Then I used PVA to glue on an old DVD.
The output when you hit the pad with a pencil is 2V to 5V and lasts up to 20mS. You should think of a piezo as a capacitor (a few tens of nF) - it produces changes in voltage, not a fixed voltage output. To stop the ADC input drifting, the sensor should therefore be connected to a fixed voltage (0V or 5V) through a big resistor. It's traditional to connect a 1M resistor across the sensor. However, to save space on the pcb, I've not used 1M resistors. Instead, I've turned on the Arduino's internal pullups on the analog pins. So the ADC reading is normally at 1023 and goes down when you hit the pad.
The internal pullups are approximately 50k ohm which is rather small. 1M would be better. You may want to turn off the pullups and have 1M resistors to ground. (And remove the "1023-" code which inverts the ADC value). Don't leave the analog pins open as noise on them will look like hits.
The polarity of the voltage that's generated by the piezo depends of which way up it is. Try swapping the red/black leads from the piezo sensors. I found that red=0V, black=ADC worked best. Your piezos may be different.
The CheckDrumpads() function of the Arduino sketch reads each ADC in turn every millisecond or so. If it sees a voltage spike above 10mV, it then watches for the maximum ADC value until about 10mS after the voltage has gone back down below 10mV. (You shouldn't use the first voltage you see as it won't be the biggest.)
You can connect between 0 and 8 drum-pads connected to pins A0 to A7. The numDrumpadPins constant specifies how many you have attached and determines how much time the Arduino devotes to checking the ADC.
When the CheckDrumpads() function recognises a hit, it plays the note. Drum-pad 0 is connected to pin A0 and hence to channel 0 (the topmost line of the display). Drum-pad 1 is connected to channel 1 (the next line) and so on.
The amplitude played depends on how hard you hit the pad. In the call to NoteOn() you'll see that the amplitude is equal to (i-mean[chan]). If your ADC gives bigger voltages, you may want to divide (i-mean[chan]) by 2 or 3.
If the channel is "enabled" (the cyan circle at the right hand end of the line is lit) then the sketch remembers that hit. It sets one of the beat checkboxes. If the channel is "disabled" (the circle is unlit) then the note is just played and forgotten.
If you decide to have Drum Pads, you could connect them via a D-connector or perhaps a RJ45 (LAN) connector or a RJ11 (phone) connector.
Step 6: Soldering It Together
I built the circuit on stripboard. I can't see the point of getting a PCB made for a one-off with just a few resistors but I realise some people don't like stripboard.
The four boards - Arduino, VS1053, display and stripboard - form a sandwich. In the image, the outline of the Arduino is yellow, the VS1053 is blue, the display is green and the stripboard is orange.
My stripboard layout is shown above. The cyan lines are the copper strips of the stripboard - make sure you put breaks in where needed (magenta circles). The straight red lines are links on the component side of the stripboard. The curved red lines are insulated flexible wire. The blue lines show the outlines of the display and VS1053 modules. The stripboard (green outline) is shown from the component side; the display and VS1053 modules are shown from the copper side.
I used sockets for the pins of the VS1053 board and the display. (The 2x5 header pins of the VS1053 board are slightly annoying on a stripboard. You'll see in my stripboard layout that I cut one of the socket pins short and cut the strips between two of the other pins.)
If you have a different VS1053 module or a different display, you can change the Arduino pins to make the layout easier:
- D2 to D10 can be used in any order you like; update the pin numbers near the start of the INO sketch
- A0 to A7 are for the drum pads.
- D11, D12, D13 are dedicated to SPI and cannot be re-assigned
- D0, D1 are dedicated to serial I/O
Step 7: Adding Speakers
I wanted the MIDI Drum Sequencer to be portable. It should include its own speakers and amplifier.
I used a PAM8403 module. They're very widely available and very cheap on eBay, Alibaba, etc. As you can see in the above photo, I soldered it to the terminals of a speaker using 20 gauge tinned copper wire. The speaker in the photo is unsuitable as it's 16ohm. The 16ohm speaker was rather quiet so I changed to an 8ohm one. The PAM8403 can drive a 4ohm to 8ohm speaker; many people think there's less distortion with 8ohm.
I added a volume control. The VS1053 has a MIDI command for the overall volume of the output which you could use instead but I thought a manual volume control would be more convenient.
Step 8: Adding a PSU
I measured the total current of the Arduino, VS1053 and display as 115mA. (That doesn't include the amplifier.)
Is it safe to power the circuit through the Arduino's Vin (Raw) pin? I cannot find the answer to that anywhere on the web. It's not in the Arduino documentation. The on-board 5V regulator will have to dissipate (Vin-5)*115 mW. What is its maximum dissipation? It seems that nobody really knows. According to its datasheet, the NCP1117 regulator in a SOT-223 package with a minimum copper pad can dissipate 650mW. So for a 115mA current,
- Vin Power
- 6V 115mW
- 7V 230mW
- 8V 345mW
- 9V 460mW
- 10V 575mW
- 11V 690mW
- 12V 805mW
To be safe, I suppose we shouldn't exceed 9V on Vin. The NCP1117 regulator's dropout is around 1V (at 115mA) so the minimum for Vin is 6V.
An external 5V PSU would be safer but I used the Arduino's regulator and it's fine.
You could power the circuit from a rechargeable Li-ion cell. My Instructable on a MIDI theremin explains how to use a a module that combines a Li-ion charger and a boost PSU.
But I chose to use AA cells. I started with 4 AA cells and found that as they discharged, their voltage soon dropped to 1.25V each - 6V in total. 6V is slightly low for the Nano regulator but the Nano worked fine and so did the display. But the touch-screen started to go crazy when its supply went below 4.7V. The battery only lasted 20mins.
So I added two more AA cells - that's 6 in total i.e. 9V - and everything was fine.
The current drawn by the PAM8403 is way too much for the Nano to supply (it peaks at 150mA) so the PAM8403 should not be powered from the 5V output of the Nano. I power the PAM8403 from the battery. The PAM8403 should not be run at more than 5.5V (absolute maximum 6V). So I drop the 9V from the battery using a 78L05 regulator. A 78L05 can dissipate 600mW; the PAM8403 takes a peak of 150mA. 150*(9-5) = 600mW so we're just at the maximum of what the 78L05 can manage. It's surviving OK and doesn't get warm but you might want to use a 9V amplifier. 600mW is the peak; the average is much lower.
Step 9: Making a Case
What case you make will depend on the materials you have to hand. I found a nice plastic box I once bought a camera in. I made a cutout for the screen and drilled holes for the speaker, a volume potentiometer and the rotary encoder.
A battery holder for 6 AA cells is hot-glued into the lid. (The photo shows only 4 cells.)
I made a "holster" for the touch-screen stylus out of polystyrene sheet..
I guess I could have 3D printed something but I prefer the old-school methods where I can adjust things as I go along. Making things is a voyage of discovery rather than "engineering".
Step 10: Future Development
How could you develop the instrument further?
You could have an output socket for the audio signal to feed it into your mixer desk. You could have an output socket for the MIDI and use your own MIDI synthesiser. You could have a MIDI input socket so you can use the VS1053 as the MIDI synthesiser for other instruments. Here's an instructable using a VS1053 and here's a list of other MIDI projects.
You could use a larger display such as a 3.5" 480x320 pxels. Near the start of the sketch is a Define:
If you un-comment it, the display will mostly work with a bigger display. I haven't tested it and it will have some mistakes but it will get you started. I chose to use a 2.8" 320x240 display because it updates twice as fast. Whatever display you choose, make sure it uses SPI.
Each beat-note could have a different amplitude so, for instance, one hit of the drum could have more emphasis. At the moment you could do that by having the same drum on different channels with different amplitudes.
MIDI has commands to set the reverb and sustain of a channel. It also has commands to change the volume and pitch of a channel as a note is playing so you could add tremulo and vibrato.
So that's it. I hope you enjoy building and using a MIDI Drum Sequencer. Let me know if you find any mistakes in my description or if you can think of any improvements.
1 Person Made This Project!
- andyfras made it!