Playing Despacito on an Arduino

Today's pointless coronavirus-fueled project is playing Despacito on an Arduino. Yes, I know the meme is dead at this point.

Can't play the video? Download it (right click, save as) (28MB)

How it works

The idea is to use a piezo buzzer for the melody and a relay for the drums, but there are some technical problems to face:

  • We need to play the melody and the drums in parallel, but the Arduino can only do 1 thing at a time, so we need some trick to do that
  • We don't have enough memory to store a MIDI file and the program to play it, we need something simpler

To solve this problem in the easiest way possible, I decided to make a very simple tracker. The time is divided into intervals of 160ms (that's the length of a 1/8th note in Despacito), the song is stored as 3 arrays of equal length (64 for this song), a pointer is moved forward after each interval and points at what should be played in the next interval. The 3 arrays are

  • LEADTK: the notes to play. To conserve memory, instead of storing them as a list of frequencies (which requires 16 bits per note, we store them as a list of 8 bit offsets to be used to access a FREQUENCIES array, which maps each offset to the actual frequency to play). 0 indicates that no note is played on that interval
  • LEADLN: the duration of the notes to play in each interval. 0 indicates that no note is played on that interval. To separate identical notes, NOTE_SPACING (16ms) is subtracted from the calculated duration in ms
  • DRUMTK: how long the relay should be kept high in ms. 0 indicates that the relay is not clicked in that interval. Cannot be higher than the length of an interval. I used 30ms for the loud click and 5ms for the quiet click.

The loop simply iterates through this array at a constant rate determined by the value of INTERVAL, and at each iteration it reads the 3 arrays to determine what to do.

To play a note, we use the tone function of the Arduino. This was originally a library but was merged into the core library of Arduino a few years ago. This function uses one of the timers to pulse a square wave at a very precise timing to create a tone. This is done asynchronously so while the tone is playing we can click the relay. Technically we have enough timers to add a second tone and make it even better, but that will be a project for another day.

Schematic

Schematic for breadboard

Parts used:

  • Arduino Uno or cheap clone
  • Generic breadboard
  • Generic piezo speaker (passive buzzer)
  • Songle SRD-05VDC-SL-C relay

The schematic is very simple, but there are 2 things worthy of notice:

  • The 220ohm resistor is there to lower the volume of the piezo, which was very loud. You can replace it or remove it if you want
  • The pins connected to the coil of the relay are reversed, that is, one is connected to the Arduino 5V output and the other to the digital pin. This was done because the digital pin cannot output quite enough current to drive the relay reliably and the Arduino would freeze quite often while playing the song. The 5V pin can output more current and the digital pin is used as ground (power consumption is a bit higher, but it won't break your board)

Code

#define SPEAKER 3
#define DRUMS 5
#define INTERVAL 160
#define NOTE_SPACING 16
const uint16_t FREQUENCIES[]={0,31,33,35,37,39,41,44,46,49,52,55,58,62,65,69,73,78,82,87,93,98,104,110,117,123,131,139,147,156,165,175,185,196,208,220,233,247,262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659,698,740,784,831,880,932,988,1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976,2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,3951,4186,4435,4699,4978};
const uint16_t SONG_LENGTH=64;
const uint8_t LEADTK[]={52,0,0,0,51,0,0,0,49,0,44,0,44,44,44,44,44,49,49,49,49,0,47,49,0,0,45,0,45,45,45,45,45,49,49,49,49,0,51,52,0,0,47,0,47,47,47,47,52,51,52,51,52,0,54,54,0,51,0,0,0,0,0,0};
const uint8_t LEADLN[]={4,0,0,0,4,0,0,0,2,0,1,0,1,1,1,1,1,1,1,1,2,0,1,1,0,0,1,0,1,1,1,1,1,1,1,1,2,0,1,1,0,0,1,0,1,1,1,1,1,1,1,1,2,0,1,2,0,3,0,0,0,0,0};
const uint8_t DRUMTK[]={0,0,0,0,0,0,0,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0,30,0,0,5,5,0,5,0};

void setup() {
  pinMode(SPEAKER,OUTPUT);
  pinMode(DRUMS,OUTPUT);
}

uint16_t ctr=0,t=0;
void loop() {
  t=millis();
  if(LEADLN[ctr]!=0){
    noTone(SPEAKER);
    if(LEADTK[ctr]!=0) tone(SPEAKER,FREQUENCIES[LEADTK[ctr]],LEADLN[ctr]*INTERVAL-NOTE_SPACING);
  }
  if(DRUMTK[ctr]!=0){
    digitalWrite(DRUMS,HIGH);
    delay(DRUMTK[ctr]);
    digitalWrite(DRUMS,LOW);
  }
  ctr=(ctr+1)%SONG_LENGTH;
  delay(INTERVAL-(millis()-t));
}

Download

Download project for Arduino IDE and Fritzing
You are free to do what you want with this code and schematic.

Share this article

Comments