Homemade tone() function not working properly

I couldn't find a library that could run a buzzer quartet so I decided to just make my own function for it with VirtualDelay. The problem is I can't really do that...but I got close! The only issue is that it's out of tune (at least a half step flat) and I'm not sure why. Here's the code:

#include <avdweb_VirtualDelay.h>

//stores every note frequency by note from O0 - O8
float C[9] = {16.35, 32.70, 65.41, 130.81, 261.63, 523.25, 1046.50, 2093, 4186.01};
float Db[9] = {17.32, 34.65, 69.30, 138.59, 277.18, 554.37, 1108.73, 2217.46, 4434.92};
float D[9] = {18.35, 36.71,	73.42, 146.83, 293.66, 587.33,	1174.66, 2349.32, 4698.63};
float Eb[9] = {19.45, 38.89, 77.78, 155.56, 311.13, 622.25, 1244.51, 2489.02, 4978.03};
float E[9] = {20.60, 41.20, 82.41, 164.81, 329.63, 659.25, 1318.51, 2637.02, 5274.04};
float F[9] = {21.83, 43.65, 87.31, 174.61, 349.23, 698.46, 1396.91, 2793.83, 5587.65};
float Gb[9] = {23.12, 46.25, 92.50, 185, 369.99, 739.99, 1479.98, 2959.96, 5919.91};
float G[9] = {24.50, 49, 98, 196, 392, 783.99, 1567.98, 3135.96, 6271.93};
float Ab[9] = {25.96, 51.91, 103.83, 207.65, 415.30, 830.61, 1661.22, 3322.44, 6644.88};
float A[9] = {27.50, 55, 110, 220, 440, 880, 1760, 3520, 7040};
float Bb[9] = {29.14, 58.27, 116.54, 233.08, 466.16, 932.33, 1864.66, 3729.31, 7458.62};
float B[9] = {30.87, 61.74, 123.47, 246.94, 493.88, 987.77, 1975.53, 3951.07, 7902.13};

float q = 1000; //quarter note in milliseconds
VirtualDelay d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21;
VirtualDelay m[8] {(micros), (micros), (micros), (micros), (micros), (micros), (micros), (micros)};

int buzzer[4] = {3, 5, 6, 9};

void setup()
{
  pinMode(buzzer[0], OUTPUT);
  pinMode(buzzer[1], OUTPUT);
  pinMode(buzzer[2], OUTPUT);
  pinMode(buzzer[3], OUTPUT);
}

void loop()
{
  /* I'm testing how the note() function sounds different
  from the tone() function by playing them one after another */
  tone(3, A[4]);
  delay(1000);
  noTone(3);
  delay(1000);
  note(1, A[4], 1000, d1);
  delay(1000);
}

void note(int buzz, float freq, int length, VirtualDelay l)
{
  bool first = true;
  int delay1 = (buzz*2)- 1;
  int delay2 = (buzz*2)- 2;
  l.start(length);
  m[delay1].start(1);
  while (!l.elapsed())
  {
    if (first)
    {
      if (m[delay1].elapsed())
      {
        digitalWrite(buzzer[buzz-1], HIGH);
        m[delay2].start(500000/freq);
        first = false;
      }
    }
    else 
    {
      if (m[delay2].elapsed())
      {
        digitalWrite(buzzer[buzz-1], LOW);
        m[delay1].start(500000/freq);
        first = true;
      }
    }
  }
  digitalWrite(buzzer[buzz-1], LOW);
}

Edit: Some users pointed out some missing code so I added it in, I'm not sure how I missed it

What is the type of VirtualDelay?
It seems to be a class, but it is not defined in your code...

Where in note() is freq used to set the frequency?

Do not expect a truely clear pitch. The timers can only be set with limited precision. The normal tone function goes in steps if 1 Hz (and at lower frequencies that can be off to such an extent that most people will hear).

Do you mean a musical quartet with four independent parts playing simultaneously, or something else such as four buzzers or four sequential notes?

2 Likes

Maybe I’m blind, but I can’t see where the array A is defined.

And A[4] looks like it is meant to be the frequency. Maybe it's magic shorthand for 440.0.

Added: nevermind, too little attention.

And why woukd

  int delay1 = (buzz*2)- 1;
  int delay2 = (buzz*2)- 2;

the waveform be dependent on the buzzer index rather than the frequency?

a7

Do you mean a musical quartet with four independent parts playing simultaneously

Yeah, I'm trying to play a short barbershop tag

I must've forgotten to paste that in, it's just an array with all of the different frequencies of A notes from A0 - A8

Where in note() is freq used to set the frequency?

I changed it for testing and forgot to change it back, it's there now

Found it.
Seems your solution with VirtualDelay will give a better tone than tone().
You might have 4 of your tones playing on 4 different outputs and mix them with 5 resistors...
You might add a small cap to filter the (in principal) infinite high frequencies of the block wave you produce. This approach would need a non blocking setup that keeps running the 4 different tone functions simultaneously...
Also, a block wave has only uneven harmonics and will not mix that well with other 'singers'.
Somewhere in between a block and a sine will sound better. You could dedicate one channel to the high voice and another to the low voice and add different capacitors (or an analog tone regulatin with a adjustable resistor). You will need to experiment to see what you like best.

If you want a better quality- look for Julian Illett’s “penny organ” video series on you tube - look at the episodes where he is using the DCOs