Sunday, April 06, 2008

Arduino Beats

Today i have been working on a sample-based drum machine for the Arduino. At the moment, bars and patterns have to be programmed into the code manually. However, i am hoping to expand upon this basic idea by adding a small and simple physical interface for real-time pattern sequencing.

Currently, the code contains four drum sounds (kick, snare, hi-hat and cymbal). More sounds can be added easily. Each sound is 128 samples long with a depth of 8 bits. The samples are stored in separate arrays. Thus, the present sketch is only 3228 bytes long.

The code itself is pretty inefficient and will have to be rewritten, but this can be worked on later as well. A pot changes the sample playback rate (and therefore tempo and pitch). One-bar phrases with a resolution of 1/16th notes are held inside arrays which are then referenced within the main loop of the sketch.

A quick sound demo can be heard here:
http://milkcrate.com.au/_other/downloads/mp3s/drums.mp3

Arduino Code

/* Arduino Beats

by Sebastian Tomczak

6 April 2008

*/


// Working Variables


byte byteA;

byte byteB;

int delayTime = 100;

int barLength = 16;


// Beats


byte bar1[] = {

1, 0, 1, 0, 2, 4, 1, 2, 1, 0, 1, 0, 2, 1, 3, 0

};


byte bar2[] = {

1, 0, 1, 0, 2, 0, 1, 2, 1, 0, 1, 0, 2, 2, 2, 2

};


byte bar3[] = {

1, 3, 1, 3, 2, 3, 1, 2, 1, 3, 1, 2, 1, 1, 4, 4

};


byte bar4[] = {

1, 0, 1, 0, 2, 2, 0, 2, 2, 1, 1, 4, 1, 1, 4, 4

};


// 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 = DDRD | B11111100;

}

void loop() {



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar2[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar3[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar2[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar1[j]);

}



for(int j = 0; j < barLength; j ++) {

playBeat(bar4[j]);

}




}


// 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;

}

9 comments:

Anonymous said...

Wonderful. And you have left us with the simple and fun ability to create our own sounds.

Anonymous said...

Whats up Sebastian!
Dude, how do you integrate the sounds with MIDI ?
I saw a video that you were playing arduino with a MIDI controller.

My email is badcluster.cleber@gmail.com

Sebastian Tomczak said...

Hi there, there is a really good thread on the Arduino forum which explains how to handle MIDI input. Basically, all you need is a few resistors and a 4n25 or 4n28 (I use a 4n28 I think).

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1187962258/

Anonymous said...

yes, thanks for the code. it really sounds like something. hope i'll finde the time these days to start working on my stuff again.

talking about midi: i wrote a short how-to on the wiki playgound over at arduino.cc for midi-in (midi-out is there as well). i mention that it works using a 4n28. still i think this optopoupler is slightly below midi specs (speedwise). so if you're going to start at buying parts you should look into digital optos with a darlington transistor.

since MAKE seems to heavily push the "arduino beat scene" these days. i feel a little pushed to work on and publish some plans for "drool" now... i think i'll try to include your samples for a low-entry version without the solenoids if you're fine with that :-)


keep it up, kuk

Sebastian Tomczak said...

Kuk! I must say, I am a fan of yours. I have read your MIDI in thread on the forums. Excellent, excellent work. Well done.

Have you a suggestion for a more suitable opto?

In regards to your question about using the samples - absolutely, as long as you give credit where due, I have no problems with you using my samples.

Thanks for your comment! ;-)

Sebastian

Anonymous said...

I'm a beginner and just can't understand how to make sound with your arduino beats project.
Should I use a piezo Eletric or something in some ports to make some noise?
Hope you can help me.

Thanks.

Ric said...

How did you get the sample data?

Sebastian Tomczak said...

I made it using a max/msp patch which converts wav files to int text files at a chosen bit depth and sample rate.

------------ said...

Great work! I was able to take your sound arrays and play them on an Atmel Attiny 84. I am trying to convert my own sounds with a program written in Processing, but I am unsure of exactly what to do. I am using this library http://sonia.pitaru.com/reference/Sample_read_.html . It seems like the wav file amplitudes have a range between -1 and 1. How can I convert this to a range between 0 and 127 or 0 and 15? Do I just straight take the ratio, or is there more to it than this? Would it be possible for you to share your program? Thank you! -Richard