Sunday, April 13, 2008

Arduino Drum Machine

Demo video: http://youtube.com/watch?v=I7CMI_imM68

Today i have made a physical interface for the Arduino drum samples (as seen in a previous post). Two 8-way DIP switches control the beat, a pot controls the sampling rate (and therefore pitch) and another pot controls the time between notes.

The two dip switches work in tandem so that each 1/8th note can have four possibilities (two bits) worth). If the position on the right switch is 0 and the position on the left switch is 0, then nothing is played. If the setup is 1 and 0, then a kick sample is played. If the setup is 0 and 1, then a snare sample is played. If the setup is 1 and 1, then a hi-hat sample is played.

So, for example, the following setup:

___SWITCH__LEFT___ ___SWITCH__RIGHT__
|0 0 1 0 1 0 1 0 | |1 0 1 0 0 0 1 0 |
------------------ ------------------
would give the following drum pattern:
KICK, TACET, HI-HAT, TACET, SNARE, TACET, HI-HAT, TACET
(because on the first 1/8th, the left switch is set to 0 and the right switch is set to 1 etc).

The two DIP switches are connected to the Arduino using 2 x 4021 shift registers. The sketch uses the a modified version of the code found in the ShiftIn tutorial by Carolyn Maw and Tom Igoe. The sound is output via an 8 bit R2R DAC. In fact, I seem to be using these quite a bit so I made myself a semi-hardwired one this morning. This is the board that is connected to PORTD (digital pins 0 - 7) on the Arduino. This is so much easier to deal with than scrounging around for suitable resistor values all the time, and then having to place them on the board.

Additionally, 2 LEDs keep track of timing -- one lights up on every 1/4 beat, and the other lights up at the start of every bar.




Schematic
Notes:
• PORTD pins 1 - 8 refers to digital pins 0 to 7
• PORTB pins 1 - 6 refers to digital pins 8 - 13
• PORTC pins 1 - 6 refers to analog pins 0 - 5



Arduino Code


/* Arduino Drum Machine

by Sebastian Tomczak

13 April 2008

*/

// 4021 Pins


int latchPin = 9;

int dataPin = 10;

int clockPin = 8;


// Working Variables


int delayTime;

byte dipSwitch1;

byte dipSwitch2;

byte beatByte;

int LED1 = 11;

int LED2 = 12;

int LED1_start = 0;

int LED1_end = 7;


// Samples


byte kick[] =

{

127, 80, 42, 5, 165, 242, 241, 233, 128, 73, 48, 22, 127, 69, 55, 113, 151, 183, 209, 217, 223, 228, 233, 215, 161, 117, 91, 76, 65, 49, 37, 31, 31, 48, 83, 120, 146, 166, 183, 198, 206, 210, 209, 199, 178, 145, 111, 88, 78, 73, 69, 67, 72, 80, 88, 97, 109, 124, 137, 150, 163, 171, 174, 172, 168, 160, 144, 125, 114, 110, 108, 104, 104, 106, 109, 110, 112, 117, 124, 129, 135, 142, 145, 145, 143, 140, 137, 132, 128, 125, 122, 119, 118, 119, 119, 119, 118, 118, 120, 124, 126, 129, 132, 135, 137, 137, 135, 132, 131, 130, 129, 128, 126, 126, 124, 123, 121, 120, 120, 122, 123, 124, 126, 128, 129, 130, 130, 131, 131, 131, 130, 130, 130, 129, 129, 128, 126, 125, 125, 124, 124, 124, 124, 125, 126, 126, 128, 128, 128, 129, 129, 129, 129, 129, 128, 128, 128, 128, 126, 126, 126, 126, 126, 126, 126, 126, 126, 128, 127, 126, 128, 128, 128, 128, 128, 128, 128, 128, 126, 126, 126, 126, 126, 126, 126, 126, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126

};


byte snare[] =

{

127, 215, 65, 212, 56, 102, 135, 122, 51, 201, 220, 46, 175, 80, 152, 95, 123, 116, 184, 155, 59, 122, 100, 161, 143, 173, 101, 155, 97, 73, 112, 98, 176, 96, 140, 77, 134, 109, 132, 149, 112, 149, 97, 161, 98, 151, 98, 155, 149, 112, 157, 103, 133, 106, 167, 97, 166, 108, 129, 124, 136, 146, 124, 136, 129, 150, 94, 130, 105, 141, 146, 128, 129, 99, 150, 121, 141, 99, 142, 116, 131, 114, 118, 143, 127, 143, 115, 144, 120, 137, 109, 129, 131, 139, 129, 113, 144, 119, 145, 117, 135, 129, 134, 136, 124, 130, 130, 139, 121, 136, 121, 132, 128, 127, 126, 122, 130, 126, 138, 120, 136, 122, 131, 123, 130, 128, 127, 128, 118, 132, 125, 131, 122, 131, 125, 131, 122, 126, 128, 126, 129, 121, 129, 123, 132, 129, 127, 131, 123, 128, 125, 130, 123, 131, 123, 128, 131, 129, 128, 126, 125, 124, 131, 121, 124, 129, 130, 126, 124, 126, 127, 130, 125, 126, 128, 126, 128, 126, 126, 126, 126, 125, 128, 126, 126, 126, 126, 126, 126, 125, 128, 126, 126, 126, 126, 126, 126, 126, 126, 128, 128, 126, 128, 126, 127, 126, 128, 125, 127, 128, 128, 126, 126, 128, 126, 126, 128, 128, 128, 128, 128, 126, 128, 126, 126, 128, 128, 126, 126, 128, 128, 126, 126, 127, 126, 128, 126, 126, 128, 128, 128, 126, 126, 126, 128, 128, 126, 126, 126, 128, 128, 126, 128, 128, 126, 126

};


byte hat[] =


{

127, 128, 225, 217, 99, 38, 61, 153, 152, 144, 133, 73, 122, 144, 65, 188, 87, 170, 164, 111, 122, 151, 114, 88, 174, 77, 140, 92, 122, 141, 156, 124, 121, 123, 126, 133, 132, 139, 119, 120, 127, 141, 130, 122, 129, 127, 132, 121, 139, 118, 130, 131, 129, 132, 130, 134, 126, 128, 130, 126, 122, 132, 129, 127, 131, 126, 128, 127, 126, 125, 127, 125, 128, 125, 128, 128, 127, 127, 126, 127, 128, 128, 128, 127, 127, 127, 127, 127, 128, 127, 127, 126, 127, 127, 128, 127, 128, 126, 127, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, 126, 126, 128, 127, 126, 127, 126, 127, 127, 126, 127, 126, 127, 127, 127, 127, 127, 126, 127, 127, 127, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 127, 127, 127, 126, 127, 127, 127, 126, 127, 127, 126, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 127, 127, 126, 127, 126, 126, 127, 126, 127, 126, 126, 126, 126, 126, 126, 126, 127, 127, 126, 127, 127, 127, 127, 126, 126, 127, 127, 127, 126, 127, 126, 127, 127, 127, 127, 127, 126, 126, 127, 127, 126, 127, 127, 127, 127, 126, 127, 127, 127, 127, 127, 127, 127, 127

};


byte crash[] =


{

127, 128, 225, 217, 99, 38, 61, 153, 152, 144, 133, 73, 122, 144, 65, 188, 87, 170, 164, 111, 122, 151, 114, 88, 174, 77, 140, 92, 122, 141, 156, 124, 121, 123, 126, 133, 132, 139, 119, 120, 127, 141, 130, 122, 129, 127, 132, 121, 139, 118, 130, 131, 129, 132, 130, 134, 126, 128, 130, 126, 122, 132, 129, 127, 131, 126, 128, 127, 126, 125, 127, 125, 128, 125, 128, 128, 127, 127, 126, 127, 128, 128, 128, 127, 127, 127, 127, 127, 128, 127, 127, 126, 127, 127, 128, 127, 128, 126, 127, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, 126, 126, 128, 127, 126, 127, 126, 127, 127, 126, 127, 126, 127, 127, 127, 127, 127, 126, 127, 127, 127, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 127, 127, 127, 126, 127, 127, 127, 126, 127, 127, 126, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 127, 127, 126, 127, 126, 126, 127, 126, 127, 126, 126, 126, 126, 126, 126, 126, 127, 127, 126, 127, 127, 127, 127, 126, 126, 127, 127, 127, 126, 127, 126, 127, 127, 127, 127, 127, 126, 126, 127, 127, 126, 127, 127, 127, 127, 126, 127, 127, 127, 127, 127, 127, 127, 127

};


void setup() {

DDRD = B00000000;

pinMode(latchPin, OUTPUT);

pinMode(clockPin, OUTPUT);

pinMode(dataPin, INPUT);

pinMode(LED1, OUTPUT);

pinMode(LED2,OUTPUT);

}


void loop() {

for(int j = 7; j >= 0; j --) {

if(j == LED1_start) {

digitalWrite(LED1, 1);

}


if(j == LED1_end)

{

digitalWrite(LED1, 0);

}

digitalWrite(LED2, 1 - (j % 2));

digitalWrite(latchPin, 1);

delayMicroseconds(20);

digitalWrite(latchPin, 0);

dipSwitch1 = shiftIn(dataPin, clockPin);

dipSwitch2 = shiftIn(dataPin, clockPin);


dipSwitch1 = (dipSwitch1 >> j) & B00000001;

dipSwitch2 = (dipSwitch2 >> j) & B00000001;

beatByte = dipSwitch1 | (dipSwitch2 << 1);

delayTime = analogRead(1) + 1;

playBeat(beatByte);

}

}


// Playback Functions


void playBeat(byte beat) {

if(beat == 0) {

for(int i = 0; i < 256; i ++) {

delayMicroseconds(analogRead(0) + 1);

}

}


else if(beat == 1) {

playKick();

}

else if(beat == 2) {

playSnare();

}

else if(beat == 3) {

playHat();

}

else if(beat == 4) {

playCrash();

}

delay(delayTime);

}


void playKick() {

for(int i = 0; i < 256; i ++) {

PORTD = kick[i];

delayMicroseconds(analogRead(0) + 1);

}

PORTD = 127;

}


void playSnare() {

for(int i = 0; i < 256; i ++) {

PORTD = snare[i];

delayMicroseconds(analogRead(0) + 1);

}

PORTD = 127;

}


void playHat() {

for(int i = 0; i < 256; i ++) {

PORTD = hat[i];

delayMicroseconds(analogRead(0) + 1);

}

PORTD = 127;

}


void playCrash() {

for(int i = 0; i < 256; i ++) {

PORTD = crash[i];

delayMicroseconds(analogRead(0) + 1);

}

PORTD = 127;

}


byte shiftIn(int myDataPin, int myClockPin) {

int i;

int temp = 0;

int pinState;

byte myDataIn = 0;

for (i=7; i>=0; i--)

{

digitalWrite(myClockPin, 0);

delayMicroseconds(2);

temp = digitalRead(myDataPin);

if (temp) {

pinState = 1;

myDataIn = myDataIn | (1 << i);

}

else {

pinState = 0;

}

digitalWrite(myClockPin, 1);

}

return myDataIn;

}

15 comments:

Brian Durocher said...

Seb you are doing some amazing stuff here. I would really like to make one of these myself. Have you thought of using simple serial eproms for additional samples?

Sebastian Tomczak said...

Brian! Always nice to hear from you. I have added a schematic to the post. And there is quite a bit of room left on the Arduino (> 10KB), I am going to add more samples and a pot to choose between sample 'kits'.

But yeah, I really wanted to keep this project as internal to the Arduino as possible. I wouldn't looking into an SPI-controlled recorder / playback chip. That would be kinda cool at some stage!

Hope you are well!

;-)

Ben Bonfil said...

Very nice project !

I wanted to do the same thing with a PIC microcontroller some time ago, but I never got to it.

Now I really feel like trying it :)

Sebastian Tomczak said...

Hey, thanks!

Anonymous said...

How did you figure out the waveform tables?

Sebastian Tomczak said...

CJP: I made a Max/MSP patch that converts a -1.0 - 1.0 range 44.1KHz, 16 bit sound file into an 8 bit, 0 - 255 range list of numbers with a user defineable sampling rate.

Anonymous said...

Very nice project!!! Congrats!!

Sorry to ask this, could you explain what are those numbers tables in the samples arrays?

Sebastian Tomczak said...

Hi g,
They are the waveforms of samples. I converted them from audio files using a little Max/MSP patch i wrote.

ADAM MANELLA said...

how hard would it be to add longer samples via usb? have you heard of the budda machine? would you be interested in making one? http://createdigitalmusic.com/2008/11/24/buddha-machine-2-all-in-one-25-ambient-box-gets-a-sequel/

Sebastian Tomczak said...

ADAM MANELLA: The length of the samples is really restricted by the size of arrays that Arduino can access as well as the amount of program memory. But of course we could use an external memory source to increase the sample length and simply use the Arduino to access this external memory source.

ADAM MANELLA said...

Would it be very difficult for you to have a box with 3 knobs, each with 5 different sounds. One with 5 different drum loops, another with 5 bass loops, and another with 5 sound loops. Then they would have to have an off setting as well. It seems the loops would always have to be playing but the knobs setting allows you to hear only one at a time from each. Please have a listen to some sounds we are working on. I would like sounds like this for the box. http://mp3xclusive.com/search?q=Mrr+Adm&ref=916&ref_sub=

Unknown said...

Hi:

It's been a while since you did this project, but I was wondering if you still had that Max/MSP patch to create the static samples? If not, no big deal. I've used this as a starting point/inspiration for building my own 4-bit drum machine.

http://www.youtube.com/watch?v=6y47wliQlyg&feature=channel

chuckborisnorris said...

I'm new to this stuff but i wouldn't mind giving it a go.
Just one question, on the schematic there is only one speaker terminal, where does the other wire go?

Unknown said...

Looks cool!

A couple of questions though:

1. Do I need an extra amp stage for this to run a small piezo speaker?

2. Where do the LEDs go? I can't see them on the schematic...

Flora said...

Very worthwhile piece of writing, thank you for the post.