I am thinking about trying to make an Arduino synthesizer using the true analog output on the Due. However I would like to be able to play multiple notes at the same time on a single buzzer. Is this possible and how would I do it?
2 Answers
You just have to add the waveforms. For example, if you want to play pure tones (the simplest and most boring timbre), you generate a sine wave for each of them and add them together:
struct Note {
float frequency;
float phase;
bool is_active;
} notes[MAX_NOTES];
void output_sample()
{
static uint32_t previous_time;
uint32_t now = micros();
uint32_t elapsed_time = now - previous_time;
previous_time = now;
float output = 0;
for (int i = 0; i < MAX_NOTES; i++) {
Note *note = ¬es[i];
if (note->is_active) {
output += cos(note->phase);
note->phase += 2*M_PI*note->frequency*1e-6*elapsed_time;
if (note->phase >= 2*M_PI) {
note->phase -= 2*M_PI;
}
}
}
analogWrite(DAC_PIN, 2048 + round(output * (2047.0 / MAX_NOTES)));
}
The function output_sample()
should be called as frequently as
possible. You can change the notes
array any time to add, remove or
change notes.
There is some room for optimization here, like:
storing
2*M_PI*note->frequency*1e-6
as the “frequency” in order to save some calculationsusing a wave table instead of the
cos()
function (I don't know how good is the Due at transcendental math)replacing
elapsed_time
by a constant if you can manage to output the samples at a fixed rate.
Edit: I wrote “You can change the notes
array any time to add,
remove or change notes.” Here is how to start or stop a note, i.e. to
add or remove it from the list of active notes:
// Start a note. Returns an index to be used with stop_note(), or -1 if
// we couldn't start the note because we are already outputting the
// maximum number of simultaneous notes.
int start_note(float frequency)
{
int i; // index of the note in the notes[] array
// Find a free slot in the array.
for (i = 0; i < MAX_NOTES; i++)
if (!notes[i].is_active) break; // found
// Couldn't find a free slot?
if (i == MAX_NOTES) return -1;
// Setup the note parameters.
notes[i].frequency = frequency;
notes[i].phase = 0;
notes[i].is_active = true;
return i;
}
// Stop the note with the given index.
void stop_note(int index)
{
if (index >=0 && index < MAX_NOTES)
notes[index].is_active = false;
}
-
How would I add notes here? I am not extremely familiar with structs.– SomeoneCommented Nov 29, 2019 at 18:36
-
@Someone: You find a cell in the array where
is_active
isfalse
, and you set the desired frequency, and setis_active
totrue
. Commented Nov 29, 2019 at 19:17 -
Sound is nothing else than a multiple simple waves combined in to one. So, theoretically you can play as many sounds (tones) as you like on a single pin. If you want to go true analog than you probably want to use a sin wave to generate sound. However, in that case you can do the following: Either make a function that would be capable of generating a sin wave of your desired frequency. In case of true analog this would probably be done by calculating actual sin values and using the map() function mapping 0 - 1 to 0 - 1024 and writing to the pin. The frequency can be adjusted by changing the delay between each step of changing the voltage or DAC value. However, this will not allow for multiple frequencies. Alternatively for multiple frequencies to be played at the same time you would need to calculate the Fourier inversion of all the waves you want to play and send the results of that to the DAC.
-
And you would need to use a speaker instead of a buzzer, since they can produce a single tone at a time. Commented Oct 26, 2019 at 19:10
-
1“Fourier inversion” is needless math jargon for what is simply the sum of the tones. Commented Oct 26, 2019 at 19:32
-
-
-
1Mixing sounds is not as simple as summing them mathematically (nor summing and averaging to bring the level back down). I made that mistake once years ago, and the results were crap. Once I found a proper sound mixing function I never looked back...– MajenkoCommented Oct 26, 2019 at 21:30
buzzer
? .... is it able to produce different tones?