Help on how to generate chords on a piano using Arduino

Hello,

so I'm doing a little experiment to see whether the project I have in mind can be pulled out or not. I have the idea of coding a simple piano, but with the ability of playing chords. Doing a little research I've come to understand (correct me if I'm wrong though, please!) that Arduino is not able to generate two squarewaves of different frequencies at a time.

With that in mind, I thought it should be possible to trick the human ear by playing two notes, one after another, but very quickly. That is, quickly enough to make our brain think both sounds are playing at the same time. I'm pretty much new into coding. Arduino has been my first approach to coding languages, so I'm not really used to thinking in the way it's needed in order not to leave any blank holes where the code doesn't know what to do, you know, those things we humans use to take for granted :smiley: such as what do we do if "x" condition is not reached. I guess it takes a little time and practice to get the hang of it.

To give a more detailed explanation of what I intend: imagine i'd like to play a simple C-E-G major chord. i'd like to implement a code that's able to:

  • Play a C4 note (262Hz) for a very short time (let's say 50ms). Then stop.
  • Automatically after that, play an E4 note (329Hz) for another 50ms. Then stop again.
  • Automatically after stopping, play a G4 note (391Hz) for another 50ms. Stop again.
  • Repeat this process until I release my finger from any of the buttons that form the C-E-G chord.

If you think about it, it can also be understood as a very fast paced arpeggio. I'd like to know if this is possible, and if so, what is the correct syntax in order to implement it. I have a little draft here where I've got a piano able to play these C, E and G notes separately. However, I've no idea how to properly implement my idea of chords. Here's the code:

//Define digital i/o 1,2 and 4 as musical notes C, E and G.
int C = 1;
int E = 2;
int G = 4;

//Define i/o 3 as the speaker.
int OUT = 3;

int i;

//Define inputs and outputs.
void setup() {
  pinMode(1, INPUT);
  pinMode(2, INPUT);
  pinMode(3, OUTPUT);
  pinMode(4, INPUT);
}

void loop() {
  while (digitalRead(C) == 1) {
    tone(OUT, 262);
  }

  while (digitalRead(E) == 1) {
    tone(OUT, 329);
  }

  while (digitalRead(G) == 1) {
    tone(OUT, 391);
  }

  while ((digitalRead(C) == 1) && (digitalRead(E) == 2)) {
    tone(OUT, 262, 50);
    tone(OUT, 329, 50);
  }

  noTone(OUT);
}

I tried using while with two variables (C and E) playing together. The way I understand it, what I'm saying is "while C and E are on a HIGH level, play C for 50ms, then play G for another 50ms, then repeat"
I have also attatched a schematic made in Proteus. It is 100% functional both there and in my Arduino Uno board (excepting the issue I'm explaining here of course). Both act the same way so I guess there are no issues with any of them :).

Thanks a lot beforehand! May you all need any other sort of information, I'll be glad to provide it.

Kind regards :smiley:

That's an interesting idea but I'm afraid it starts from a false premise. It is in fact perfectly possible for an Arduino to produce several notes at the same time. It's just that the standard tone() function can't do it.

Have a look at the Tone.h library or just Google "Arduino multiple tones".

Steve

Hi Steve, thanks for the answer.

I just did a quick experiment on my idea just to make sure what the result is and after trying this little piece of code, the sound it produces is closer to a car claxon rather than an armonious C major chord.

void setup() {
  pinMode (3, OUTPUT);
}

void loop() {
  tone(3, 262);
  delay(10);
  tone(3, 329);
  delay(10);
  tone(3, 391);
  delay (10);
}

That debunks the entire brain "trickery" concept I had in mind, so I will indeed try to use that library you suggested (though I'm having quite a hard time trying to understand many of the things explained on its github page).

Thanks again for your answer! By the way, am I allowed to bump this topic if I ever need more help with anything related to it?

The best way to bump a topic is demonstrate some progress, even if it is a seeming dead-end, rather than the usual, blood-pressure raising bare "bump"

With $35 in additional hardware you can get 64-note polyphony:

You can use chorus, flange, delay reverb and spatial effects... but then you can only play 32 notes at the same time.

If you don't need more than 16 note polyphony (with per-note volume) you can get by with a few resistors and capacitors:

Also has 128 standard General MIDI instruments.

This seems to sound OK. It would be better to use a hardware timer instead of micros().

It uses a special feature of the Arduino UNO processor: Writing a 1 into a bit of the Port Input (PIN) register for a port, toggles the corresponding output pin. Port D bit 3 is the bit for Arduino Pin 3.

// Calculate microseconds per half-cycle of each note:
const unsigned long Microseconds262 = (500000UL / 262);
const unsigned long Microseconds329 = (500000UL / 329);
const unsigned long Microseconds391 = (500000UL / 391);


unsigned long TimerA = 0;
unsigned long TimerB = 0;
unsigned long TimerC = 0;


void setup()
{
  pinMode (3, OUTPUT);
}


void loop()
{
  unsigned long currentMicros = micros();
  if (currentMicros - TimerA >= Microseconds262)
  {
    TimerA = currentMicros;
    PIND = 0x08;
  }


  if (currentMicros - TimerB >= Microseconds329)
  {
    TimerB = currentMicros;
    PIND = 0x08;
  }


  if (currentMicros - TimerC >= Microseconds391)
  {
    TimerC = currentMicros;
    PIND = 0x08;
  }
}

Thanks everyone for all this documentation :slight_smile: I will have a deep look. Cheers.

Play a C4 note (262Hz) for a very short time (let's say 50ms). Then stop.
Automatically after that, play an E4 note (329Hz) for another 50ms. Then stop again.
Automatically after stopping, play a G4 note (391Hz) for another 50ms. Stop again.
Repeat this process until I release my finger from any of the buttons that form the C-E-G chord.

Yeah ... harmonics don't work that way. You'll wind up with something that's some mishmash of harmonics of the underlying 6Hz signal - and it's going to have a lot of high frequency stuff because square wave.

What might be very instructive would be to play the arpeggio gradually faster so that you can hear how it descends into noise as the frequency of the lenghts of the notes approach the frequencies of the notes.

const byte OUT = 3;
long t;
int notelen = 333;

void setup() {
  t = millis();
  pinMode(OUT, OUTPUT);
}

void loop() {
  // get faster every second
  if(millis()-t >= 1000) { 
    notelen = (notelen * 12) / 17; // halve the length every 2 seconds
    if(notelen < 2) {
      notelen = 333; // back to 333ms
    }
    t = millis();
  }

  // play our arpeggio at the current speed

  tone(OUT, 262);
  delay(notelen);
  tone(OUT, 329);
  delay(notelen);
  tone(OUT, 391);
  delay (notelen);

}