Musicomatic : the random jazz machine
Here is the arduino souce code !
It sould fit an atmega8, but the tone() function bugs on it so you’ll have to use an atmega168 or 328.
/////////////////////////////////////// //////////////////THE////////////////// //////////////MUSICOMATIC////////////// ////////////// BY R0D0T ////////////// //////// http://r0d0t.tumblr.com ////// /////////////////////////////////////// // You should get a look at the video / // first to understand the code /////// http://r0d0t.tumblr.com/post/45022928402 /////////////////////////////////////// #define selfShutdownPin 4 //keep this pin HIGH, when you put it LOW it will cut the power //see the schematics in the video to better understand this //DRUMS #include <Servo.h> //include the servo library byte servoPins[3] = { //store the servo pins in an array to be able to mix them 6,7,8}; #define lowAngle 90 //the angle the servos need to reach to hit the wood #define highAngle 80 //the angle the servos need to reach to be able to hit again ;) Servo servo0; //the 3 servos objects Servo servo1; Servo servo2; //TEMPO //the fastets note duration will be the sixteenth note for the melody //so everything will be timed based on the sixteenth byte tempo; //time between two sixteenth #define tempoMin 100 //fastest tempo #define tempoMax 150 //slowest tempo //NOTES #define tonePin 0 //buzzer pin //frequencies in hertz of 2 octaves. 0 is used for muted notes unsigned int noteFreq[25] = { 0,262,277,294,311,330,349,370,392,415,440,466,494, 523,554,587,622,659,698,740,784,830,880,932,988}; byte scale[6] = { //index of the pentatonic scale in noteFreq[] 0,3,5,6,7,10}; byte scaleOffset; //apply an offset to each note of the scale to change the pitch #define scaleOffsetMin 1 #define scaleOffsetMax 15 #define pauseBetweenNotes 0.8 //to separate the notes //CHORUS RHYTHMS : define some rhythms to pick that sound good #define numberOfRhythms 7 //array containing the duration of the notes for the rhythms. //Zeros doesn't matter, it's because the arrays need to have the same size byte rhythms[numberOfRhythms][4] = { {4,0,0,0}, {1,3,0,0}, {8,0,0,0}, {6,2,0,0}, {3,1,0,0}, {1,1,2,0}, {2,1,1,0} }; //SCORES : store the notes to be played, and theyre duration byte drums[3][16]; //angles for each servo byte chorus[16]; //index of the notes in noteFreq[] byte chorusDuration[16]; //duration of the chorus's notes in ms byte impro[16]; //index of the notes in noteFreq[] byte improDuration[16]; //duration of the impro's notes in ms //SETUP void setup() { //SELF SHUTDOWN pinMode(selfShutdownPin, OUTPUT); digitalWrite(selfShutdownPin, HIGH); //Self shutdown if LOW //RANDOMIZE EVERYTHING ! randomSeed(analogRead(A0)); //pick a first random seed //but doing this, you will often have the same seed (between 0..1024) //so let get a bigger, and really random seed.... unsigned long seed; for(int i=0; i<random(0,100); i++){ seed += analogRead(A0); seed += analogRead(A1); seed += analogRead(A2); seed += analogRead(A3); seed += analogRead(A4); } randomSeed(seed); //that's a good huge seed ! //randomize tempo and scale offset once per music tempo = random(tempoMin,tempoMax); //no comment here... scaleOffset = random(scaleOffsetMin,scaleOffsetMax); //pick a random scale offset for(byte i=0; i<6; i++){ //then apply the offset to each note of the scale scale[i] += scaleOffset; } //this way the pitch of each note will increase depending on the offset //randomize servos //you shouldn't read the following lines, it's crap //its randomly attributes pins (from the servo's pin array) to the servos //but avoids to pick the same twice //I should find better way to do that, but it works... byte servoRandom[3]; servoRandom[0] = random(0,3); do{ servoRandom[1] = random(0,3); } while (servoRandom[1]==servoRandom[0]); do{ servoRandom[2] = random(0,3); } while ((servoRandom[2]==servoRandom[0]) || (servoRandom[2]==servoRandom[1])); //Attach the pins to the servos servo0.attach(servoPins[servoRandom[0]]); servo1.attach(servoPins[servoRandom[1]]); servo2.attach(servoPins[servoRandom[2]]); //then make them take them default angle servo0.write(lowAngle); servo1.write(lowAngle); servo2.write(lowAngle); //play the scale with the good tempo //initially it was done for debugging purposes, but it sounds good for (byte i=0; i<sizeof(scale); i++){ tone(tonePin, noteFreq[scale[i]], tempo*0.8); delay(tempo); } //wait a little before beginning the tune... delay(500); //PLAY THE MUSIC !!! //See below to understand what each function does... RandomizeDrums(); PlayIntro(); //introduces drums one by one for(byte thisTheme=0; thisTheme<3; thisTheme++){ //play 3 "themes" RandomizeChorus(); //change the chorus for each theme for(byte i=0; i<4; i++){ //a theme is composed of : PlayChorus(); //the chorus, the same for the whole theme RandomizeImpro(); //an impro randomized each time PlayImpro(); //play the impro } PlayInterThemes(); //between themes and at the end of the song, play drums alone } //once this marvelous music reaches it's end, make the servo take their default position servo0.write(lowAngle); servo1.write(lowAngle); servo2.write(lowAngle); delay(2*tempo); //let them time to reach their position servo0.detach(); servo1.detach(); servo2.detach(); //and then shut down ! digitalWrite(selfShutdownPin, LOW); } //LOOP void loop() { //The loop is useless as the code runs once } void PlayIntro(){ //introduces the drums one by one for(byte i=0; i<16; i++){ servo2.write(drums[2][i]); //the faster one first delay(tempo); } for(byte i=0; i<16; i++){ servo1.write(drums[1][i]); //the a slower one servo2.write(drums[2][i]); delay(tempo); } for(byte i=0; i<16; i++){ servo0.write(drums[0][i]); //and finaly the slowest servo1.write(drums[1][i]); servo2.write(drums[2][i]); delay(tempo); } } void PlayInterThemes(){ //drums between main themes and at the end of the song for(byte i=0; i<16; i++){ servo2.write(drums[2][i]); if(i>8){ servo1.write(drums[1][i]); } delay(tempo); } } void RandomizeDrums(){ for(byte thisDrum=0 ; thisDrum<3 ; thisDrum++){ for(byte i=0; i<16; i++){ //clear the array containing the drums's servos angles drums[thisDrum][i] = lowAngle; } } for(byte i=0; i<16; i++){ if((i%4)==3){ //the fastest drum drums[2][i] = highAngle; drums[2][i-1] = highAngle; //the servo needs time to reach it's position, //so you need to move it on both i and i-1 } if((i%8)==2){ //the medium one drums[1][i] = highAngle; drums[1][i-1] = highAngle; } if((i%16)==15){//the slowest drums[0][i] = highAngle; drums[0][i-1] = highAngle; } for(byte i=2; i<16; i+=2){ if(0 == random(0,12)){//and add some random hits to the slowest one drums[0][i] = highAngle; drums[0][i-1] = highAngle; } } } } void RandomizeChorus(){ for(byte i=0; i<16; i++){ chorus[i] = 0; //clear the chorus array } byte i = 0; //start from the beginning of the chorus while(i<16){ //then, while the end isn't reached byte thisRhythm = random(0, numberOfRhythms); //pick a random rhythm byte thisNote = 0; while(rhythms[thisRhythm][thisNote] !=0){ //while the end of the rhythms isn't reached //(because the rhythms arrays ends with zeroes) chorus[i] = scale[random(0,6)]; //pick a random note in the scale if(i>0){//if the current note is not the first one... if(random(0,48)==0) { //...randomly... chorus[i] = 0; //...mute it. } } chorusDuration[i] = rhythms[thisRhythm][thisNote]; //set the note duration based one the rhythm thisNote++; //go to the next note i+=chorusDuration[i]; //go forward by the note's duration if(i>=16) //if the end is reached... break; //...go to the next step ! } } } void PlayChorus(){ //play the chorus we just generated ! for(byte i=0; i<16; i++){ //for each instant if(chorus[i] > 0){ //if there is a note if(chorus[i] != chorus [i-1]){ //and this note is note the same than the previous tone(tonePin, noteFreq[chorus[i]], chorusDuration[i]*tempo*0.8); //play it } } servo0.write(drums[0][i]); //simply move each drum's servo servo1.write(drums[1][i]); servo2.write(drums[2][i]); delay(tempo); //wait until the next note ! } } void RandomizeImpro(){ //same as RandomizeChorus(), but simpler for(byte i=0; i<16; i++){ impro[i] = 0; //clear the array } byte i = 0; while(i<16){ do{ impro[i] = scale[random(0,6)]; } while (impro[i] == impro[i-1]); //avoid two notes of the same pitch improDuration[i] = random(1, 4); i+=improDuration[i]; } } void PlayImpro(){ //the same as PlayChorus()... they should have been merged for(byte i=0; i<16; i++){ if(impro[i] > 0){ if(impro[i] != impro [i-1]){ tone(tonePin, noteFreq[impro[i]], improDuration[i]*tempo); } } if(i==15){ noTone(tonePin); } servo0.write(drums[0][i]); servo1.write(drums[1][i]); servo2.write(drums[2][i]); delay(tempo); } }