Using a small 8ohm analogue speaker hooked up to pin 11, by strobing the pin, I generate a square wave (a kind of annoying timbre to be honest).
I generate 2 octaves of semitones starting with A-220 and index into it.
The timing is currently inaccurate as the note length for lower notes runs slightly longer (half a pulse width) than higher notes due to how the strobing currently works. It's surprisingly noticeable. I need to work on that to make it more consistent. Probably by using timer interrupts instead of delays.
To do:
Alter the volume by using analogWrite() to set the duty cycle of the pin... 255 = loud 0 = off.
I think I can use that mechanism to generate other waveforms, I'll probably try sawtooth next.
Proper ADSR waveforms.
Multichannel output (haw haw).
const int baseFreq = 220; // A
const int numHalfTones = 24; // 2 Octaves
long frequencies[numHalfTones];
long halfFreqs[numHalfTones];
/* 12 semi-tones per octave
| Bf| | | C#| Ef| | |F# |G# |
-+---+ | +---+---+ | +---+---+-
| A | B | C | D | E | F | G | A |
+---+---+---+---+---+---+---+---+
2 octaves:
0 1 2 3 4 5 6 7 8 9 10 11
A Bf B C C# D Ef E F F# G G#
12 13 14 15 16 17 18 19 20 21 22 23
*/
int seq[] = {
3, 7, 10, 12, 13, 12, 10 , 7,
3, 7, 10, 12, 13, 12, 10 , 7,
8, 12, 15, 17, 18, 17, 15, 12,
3, 7, 10, 12, 13, 12, 10 , 7,
10, 14, 17, -1, 8, 12, 15, -1
};
int notes = 40;
long getTimeHigh(int i)
{
return halfFreqs[i];
};
void ToneGen(void)
{
float baseMult;
int i;
int lastTone = baseFreq;
baseMult = pow(2.0 , 0.083333);
for(i = 0; i < numHalfTones; ++i)
{
long frequency = baseFreq;
if(i)
{
frequency = (0.5 + (frequencies[i - 1] * baseMult));
}
frequencies[i] = frequency;
halfFreqs[i] = (0.5 + (1000000.0 / frequency / 2.0));
}
};
void setup(void) {
pinMode(11, OUTPUT);
ToneGen();
Serial.begin(57600);
}
int rest_count = 100;
long duration = (60 * 1000000) / 288;
void playNote(int idx)
{
long tone = 0;
if (idx >= 0)
{
tone = getTimeHigh(idx);
}
long endTime = micros() + duration ;
if (tone > 0)
{
int val = HIGH;
while (micros() < endTime)
{
digitalWrite(11,val);
delayMicroseconds(tone);
val = !val;
}
}
else
{
delay(duration / 1000);
}
digitalWrite(11, LOW);
}
void loop(void) {
analogWrite(11, 0);
for(int i = 0; i < notes; ++i)
{
playNote(seq[i]);
delay(10);
}
}