The Doof Machine - Arduino Drum Sequencer - Simple & Customizable
1036 Views, 5 Favorites, 0 Comments
The Doof Machine - Arduino Drum Sequencer - Simple & Customizable
Hey there!
Welcome to PTV04 "The Doof Machine". Please come in! Sit down and make yourself comfortable. Can I get you a drink or something?
Basically, I wanted something that would be pretty easy to make, yet be fun/useful enough to make it worth doing; and allow for some customization too.
Inspired by Gavin Davies' work https://gavd.co.uk/2022/03/arduino-based-drum-machine/
The Doof Machine is a 16 step drum sequencer that plays 1, 2 or a blend of 2 samples at any one time. 2 dials for each sample (one controls how many steps in each loop/sequence [up to 16], and the other controls how many of those steps will be playing a sample [up to 16 depending on what you've set your "number of steps" dial to]) and 1 dial for tempo control. You can have 4 samples loaded into each sample bank (8 samples in total) and that seems to allow for a pretty decent amount of variation in sounds and beats without making to project too complex. Check out the video! See what you think.
I will go through in detail how to make your own samples and adjust the code.
The Doof Machine is meant to be an entry level safe project and I will do my best to present it in a way that makes it accessible for someone that has very little or no prior knowledge. An interest in computers, electronics and music is highly recommended though!
It's pretty easy to put together and doesn't require too many parts but I think it's a satisfying enough project to make it worth having a go at. Hope you enjoy!
Supplies
Here's a list of components and tools you'll need to bring your own Doof Machine to life!:
Materials:
- An Arduino board! You'll need an Arduino board as the brain of your Doof Machine. An Arduino Uno or Arduino Nano (or equivalent aftermarket version) is recommended for this project. I have used an aftermarket Arduino Nano in this Instructable but an Uno will also work fine without having to edit any code.
- A USB cable that matches your Arduino board!
- A PC to run your Arduino IDE and upload the code!
- A full size breadboard! 5.5 cm x 17 cm (2.2" x 7"), the row numbers should go up to about 65.
- 5 linear dial potentiometers! Pretty much any rating should be fine, but make sure that they are linear B type potentiometers. Otherwise you may get some unbalanced results. In this Instructable I used 5 B50K pots that I upcycled from an old mixing desk. However I'm currently making a circuit board version with some newly purchased B10Ks. Higher ratings should work fine too.
- 8 push buttons! Basic single pole, single throw (SPST) buttons.
- 6.3mm (1/4") female mono audio output jack! For connecting The Doof Machine to a guitar amp or some other audio output.
- A bunch of wires! About 15 long ones and about 24 shorter ones.
- A guitar amp and lead! or something similar to connect your new instrument into.
- A soldering iron and 60/40 rosin core solder! (optional) To solder the pins onto the Arduino Nano and connect wires to the output jack (if necessary).
- A sincere urge to succeed and flood the world with fantastic electronic noise!
Install Arduino IDE, Check Your Arduino, Install Mozzi & Grab Samples!
In this step, you'll set up your development environment by installing the Arduino Integrated Development Environment (IDE) . It's also a good time to make sure that your Arduino's working too!
- To begin with, let's get that Arduino IDE on your system if you don't already have it! Please follow the link below or do a search for something like "Arduino IDE" in your browser. https://support.arduino.cc/hc/en-us/articles/360019833020-Download-and-install-Arduino-IDE
- Choose your appropriate operating system and download the installation file!
- Instaaaaall!
- Once you've got your copy of the Arduino IDE up and running, we need to make sure that your Arduino board and the IDE are talking to each other. Open up the Arduino IDE and plug your Arduino board into the USB on your PC. https://www.tutorialspoint.com/arduino/arduino_installation.htm
- Here is a link to the official guide section. https://www.arduino.cc/en/Guide
- It should just work, but if it doesn't, you may have a channel 340 issue. Official boards may be easier to get up and running than aftermarket Arduinos. If your board doesn't want to connect straight away, don't dismay, It might not be that hard to sort out. https://www.hackster.io/engtong82/setting-up-the-compatible-uno-ch340-for-the-first-time-51da13
- Once you've managed to get the usb noticing the arduino board, it's time to make sure this puppy's working! https://learn.adafruit.com/adafruit-arduino-lesson-1-blink/loading-the-blink-example
- As instructed in the above Arduino guide, I really recommend playing around with the blink program a little, just to make sure that everything is happy and talking to each other nicely before proceeding.
- Time to install the Mozzi Library! please navigate over to the Mozzi website https://sensorium.github.io/Mozzi/ and have a bit of a read of what's going on! There are some pretty amazing things that can be done on an Arduino thanks to Mozzi, check it out! But to begin with, we need to jump onto the Download link in dive further in github https://github.com/sensorium/Mozzi
- So what we gotta do is download the Mozzi library zip file, then install it into the Arduino IDE. Here is a link to how to go about installing/adding libraries to the Arduino IDE https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries
- Once you've installed the Mozzi library, it couldn't hurt to close down and reopen the Arduino just to make sure the changes have taken effect.
- Once that's done and good to go, you'll need to jump into your file explorer window (or finder or equivalent) and get into the Mozzi directory within libraries in the Arduino IDE (which is usually in Documents). **For Example** C:\Users\Peter\Documents\Arduino\libraries\Mozzi
- Once in, we'll need to make a new folder called "sfx" (C:\Users\Peter\Documents\Arduino\libraries\Mozzi\sfx) This "sfx" folder is where we'll keep all of our samples for The Doof Machine. I suppose they could have gone into the already existing "samples" folder, but I thought that this way would keep everything nice and neat.
- Please note that the code assumes that you have an "sfx" folder in your Mozzi library!
- Then it's just a matter of downloading the below samples into the "sfx" folder and getting ourselves ready to start putting the board together!
Wiring the Circuit!
Okay, got all your bits and pieces? Let's do this!
Please refer to the below table for ease of connections with your project. There are copies of the above diagrams for your reference that include the used notations.
B=Buttons!
D=Digital Arduino Pins!
P=Potentiometers!
A=Analogue Arduino Pins!
B1 >to> D3
B2 >to> D4
B3 >to> D5
B4 >to> D6
B5 >to> D7
B6 >to> D8
B7 >to> D10
B8 >to> D11
P1 >to> A3
P2 >to> A4
P3 >to> A1
P4 >to> A5
P5 >to> A2
+ Wire on 1/4" Audio Socket >to> D9 (PWM)
Upload the Code and Try It Out!
Okay, without any more messing around. Here's the code!
(There is also a .ino file available).
So what are you waiting for!? Jump into your Arduino IDE, upload the code and get things running!
/*
PTV04 The Doof Machine - Instructables Edition
Peter Polmeartagami - PTV ArtSpace 2023
Create more music machines! Create more music!
Inspired by Gavin Davies' work https://gavd.co.uk/2022/03/arduino-based-drum-machine/
*/
#include <MozziGuts.h>
#include <Sample.h>
#include "sfx/doof01.h"
#include "sfx/doof02.h"
#include "sfx/doof03.h"
#include "sfx/tsh04.h"
#include "sfx/tsh01.h"
#include "sfx/tsh02.h"
#include "sfx/tsh03.h"
#include "sfx/doof04.h"
#include <EventDelay.h>
#include <mozzi_rand.h>
#define CONTROL_RATE 128
#define CELLS_PER_SAMPLE 2048
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aDoof01(DOOF01_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aDoof02(DOOF02_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aDoof03(DOOF03_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aTsh04(TSH04_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aTsh01(TSH01_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aTsh02(TSH02_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aTsh03(TSH03_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aDoof04(DOOF04_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE>* sample1 = &aDoof01;
Sample<CELLS_PER_SAMPLE, AUDIO_RATE>* sample2 = &aTsh01;
EventDelay kTriggerDelay;
const byte MAX_STEP_COUNT = 16;
byte cursor1 = 0;
byte cursor2 = 0;
void setup() {
pinMode(2, INPUT_PULLUP); // ON/OFF switch if you want to add one.
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(8, INPUT_PULLUP);
pinMode(9, OUTPUT);
pinMode(10, INPUT_PULLUP);
pinMode(11, INPUT_PULLUP);
startMozzi();
aDoof01.setFreq((float)DOOF01_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aDoof02.setFreq((float)DOOF02_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aDoof03.setFreq((float)DOOF03_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aTsh04.setFreq((float)TSH04_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aTsh01.setFreq((float)TSH01_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aTsh02.setFreq((float)TSH02_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aTsh03.setFreq((float)TSH03_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aDoof04.setFreq((float)DOOF04_SAMPLERATE / (float)CELLS_PER_SAMPLE);
kTriggerDelay.set(10);
}
bool shouldPlay(byte steps, byte beats, byte i) {
float divisor = ((float)beats / (float)steps);
bool shouldPlay;
if (i == 0) {
shouldPlay = (beats != 0);
} else {
byte score = i * divisor;
byte prevScore = (i - 1) * divisor;
shouldPlay = (score > prevScore);
}
return shouldPlay;
}
void updateControl() { // ON/OFF switch if you want to add one.
if (digitalRead(2) == LOW) {
return;
}
if (digitalRead(3) == LOW) {
sample1 = &aDoof01;
} else if (digitalRead(4) == LOW) {
sample1 = &aDoof02;
} else if (digitalRead(5) == LOW) {
sample1 = &aDoof03;
} else if (digitalRead(6) == LOW) {
sample1 = &aTsh04;
} else {
// You can assign different samples or set sample1 to nullptr if you want no sound.
}
if (digitalRead(7) == LOW) {
sample2 = &aTsh01;
} else if (digitalRead(8) == LOW) {
sample2 = &aTsh02;
} else if (digitalRead(10) == LOW) {
sample2 = &aTsh03;
} else if (digitalRead(11) == LOW) {
sample2 = &aDoof04;
} else {
// You can assign different samples or set sample2 to nullptr if you want no sound.
}
byte steps1 = (byte)map(mozziAnalogRead(4), 0, 1023, 1, MAX_STEP_COUNT);
byte beats1 = (byte)map(mozziAnalogRead(1), 0, 1023, 0, steps1);
byte steps2 = (byte)map(mozziAnalogRead(5), 0, 1023, 1, MAX_STEP_COUNT);
byte beats2 = (byte)map(mozziAnalogRead(2), 0, 1023, 0, steps2);
if (kTriggerDelay.ready()) {
if (cursor1 >= steps1) {
cursor1 = 0;
}
if (cursor2 >= steps2) {
cursor2 = 0;
}
if (shouldPlay(steps1, beats1, cursor1)) {
(*sample1).start();
}
if (shouldPlay(steps2, beats2, cursor2)) {
(*sample2).start();
}
cursor1++;
cursor2++;
kTriggerDelay.start(map(mozziAnalogRead(3), 0, 1023, 300, 50));
}
}
int updateAudio() {
int asig = (int)(((long)(*sample1).next() + (*sample2).next()) * 127);
if (asig > 127) {
asig = 127;
} else if (asig < -128) {
asig = -128;
}
return asig;
}
void loop() {
audioHook();
}
Okay! You can call that complete now if you want to! Well done!
However.... If you would like to dig deeper and learn how to customise your own sounds relatively easily,
please continue!
Downloads
Creating and Uploading Your Own Samples!
Okay, so you wanna customise this little puppy, aye? Well let's do it then!
THIS SECTION ASSUMES A SOMEWHAT INTERMEDIATE LEVEL OF PC SAVVY! (I'm sure you'll be fine)
Before anything else, I highly recommend printing out a copy of The Doof Machine's code in it's entirety. It'll make any check work much quicker and easier on the eyes/brain.
Although not the only way to do it, this is the way that I have done it. And I have done it this way because it was (after a lot of scratching my head) the easiest and most cost effective way I've been able to find so far given my current skill level.
I hope it helps you in some way!
Software Used
- Audacity https://www.audacityteam.org/
- Waveform (free version) https://www.tracktion.com/products/waveform-free
- Chat GPT (free version) https://chat.openai.com/
- Google Colab https://colab.google/
Make Your Samples!
What we want from this exercise is 8 new (short length!) samples in a .wav format.
Apologies if this and the next video seem a little long-winded. Feel free to skip through once you understand the process!
Prepare Your Samples!
What we want from this exercise is 8 samples in a (headerless) .RAW format, trimmed to a length of 2048 samples (for the code) at a 16,384 Hz sample rate (for Mozzi) in a signed 8-bit PCM format.
Convert Your Samples!
Using the below Google Colab Notebook, we can quickly convert all 8 of our (or any number of) samples all at once into Mozzi and Arduino friendly .h files complete with all required effects. This notebook should be useful for many other Mozzi sample applications. It can handle samples of any length.
Please feel free to make use of and distribute this code in any way you see fit. Make more music machines!
Updating The Code To Run Your New Samples!
Chat GPT can make this chore much easier! Although sometimes it plays nicer than other times. This step is why a printout of the original code is useful!
Recommended Chat GPT Prompt:
Can you please rewrite the above code with
xxx.h replacing doof01.h,
xxx.h replacing doof02.h,
xxx.h replacing doof03.h,
xxx.h replacing doof04.h,
xxx.h replacing tsh01.h,
xxx.h replacing tsh02.h,
xxx.h replacing tsh03.h,
xxx.h replacing tsh04.h?
/*
PTV04 The Doof Machine - Instructables Edition
Peter Polmeartagami - PTV ArtSpace 2023
Create more music machines! Create more music!
Inspired by Gavin Davies' work https://gavd.co.uk/2022/03/arduino-based-drum-machine/
*/
#include <MozziGuts.h>
#include <Sample.h>
#include "sfx/bom01.h"
#include "sfx/bom02.h"
#include "sfx/bom03.h"
#include "sfx/chi04.h"
#include "sfx/chi01.h"
#include "sfx/chi02.h"
#include "sfx/chi03.h"
#include "sfx/bom04.h"
#include <EventDelay.h>
#include <mozzi_rand.h>
#define CONTROL_RATE 128
#define CELLS_PER_SAMPLE 2048
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aBom01(BOM01_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aBom02(BOM02_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aBom03(BOM03_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aChi04(CHI04_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aChi01(CHI01_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aChi02(CHI02_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aChi03(CHI03_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE> aBom04(BOM04_DATA);
Sample<CELLS_PER_SAMPLE, AUDIO_RATE>* sample1 = &aBom01;
Sample<CELLS_PER_SAMPLE, AUDIO_RATE>* sample2 = &aChi01;
EventDelay kTriggerDelay;
const byte MAX_STEP_COUNT = 16;
byte cursor1 = 0;
byte cursor2 = 0;
void setup() {
pinMode(2, INPUT_PULLUP); // ON/OFF switch if you want to add one.
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(8, INPUT_PULLUP);
pinMode(9, OUTPUT);
pinMode(10, INPUT_PULLUP);
pinMode(11, INPUT_PULLUP);
startMozzi();
aBom01.setFreq((float)BOM01_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aBom02.setFreq((float)BOM02_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aBom03.setFreq((float)BOM03_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aChi04.setFreq((float)CHI04_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aChi01.setFreq((float)CHI01_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aChi02.setFreq((float)CHI02_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aChi03.setFreq((float)CHI03_SAMPLERATE / (float)CELLS_PER_SAMPLE);
aBom04.setFreq((float)BOM04_SAMPLERATE / (float)CELLS_PER_SAMPLE);
kTriggerDelay.set(10);
}
bool shouldPlay(byte steps, byte beats, byte i) {
float divisor = ((float)beats / (float)steps);
bool shouldPlay;
if (i == 0) {
shouldPlay = (beats != 0);
} else {
byte score = i * divisor;
byte prevScore = (i - 1) * divisor;
shouldPlay = (score > prevScore);
}
return shouldPlay;
}
void updateControl() { // ON/OFF switch if you want to add one.
if (digitalRead(2) == LOW) {
return;
}
if (digitalRead(3) == LOW) {
sample1 = &aBom01;
} else if (digitalRead(4) == LOW) {
sample1 = &aBom02;
} else if (digitalRead(5) == LOW) {
sample1 = &aBom03;
} else if (digitalRead(6) == LOW) {
sample1 = &aChi04;
} else {
// You can assign different samples or set sample1 to nullptr if you want no sound.
}
if (digitalRead(7) == LOW) {
sample2 = &aChi01;
} else if (digitalRead(8) == LOW) {
sample2 = &aChi02;
} else if (digitalRead(10) == LOW) {
sample2 = &aChi03;
} else if (digitalRead(11) == LOW) {
sample2 = &aBom04;
} else {
// You can assign different samples or set sample2 to nullptr if you want no sound.
}
byte steps1 = (byte)map(mozziAnalogRead(4), 0, 1023, 1, MAX_STEP_COUNT);
byte beats1 = (byte)map(mozziAnalogRead(1), 0, 1023, 0, steps1);
byte steps2 = (byte)map(mozziAnalogRead(5), 0, 1023, 1, MAX_STEP_COUNT);
byte beats2 = (byte)map(mozziAnalogRead(2), 0, 1023, 0, steps2);
if (kTriggerDelay.ready()) {
if (cursor1 >= steps1) {
cursor1 = 0;
}
if (cursor2 >= steps2) {
cursor2 = 0;
}
if (shouldPlay(steps1, beats1, cursor1)) {
(*sample1).start();
}
if (shouldPlay(steps2, beats2, cursor2)) {
(*sample2).start();
}
cursor1++;
cursor2++;
kTriggerDelay.start(map(mozziAnalogRead(3), 0, 1023, 300, 50));
}
}
int updateAudio() {
int asig = (int)(((long)(*sample1).next() + (*sample2).next()) * 127);
if (asig > 127) {
asig = 127;
} else if (asig < -128) {
asig = -128;
}
return asig;
}
void loop() {
audioHook();
}
Thank you so much! I hope you enjoyed!
Peter Polmeartagami
PTV (Polmeartagami Ventures)