The Gameduino's sound system is a 64-voice synthesizer. Each voice generates a pure sine wave at a certain pitch and volume.
Using the sound processing package Loris, you can analyze a one-note instrument sample to generate a graph of how its key frequencies (or "partials") develop over time. This graph shows the 16 strongest frequencies in a single :download:`piano note <piano.wav>`, and how their volume peaks at the beginning of the note, then decays over the next second or so.
A little more code (Loris has a Python interface, fortunately) turns this note data into a form that the Gameduino can play directly through its 64-voice synthesizer. There are several of advantages of doing this instead of using a straight sample:
- control over pitch: to play at a different pitch, just scale the frequencies - duration is unaffected
- control over duration: by updating the amplitude values faster or slower, it is simple to lengthen or shorten a note
- reduced memory - the sample above would be about 10Kbytes as an 8-bit sample, as an instrument it takes about 600 bytes
The encoding is done by this Python program (analyze-instruments.zip) - it reads the note samples and writes Arduino data into a header file for each instrument. The format of this instrument data is:
- number of voices (1 byte)
- duration of note (1 byte)
- frequencies for all voices (voices * 2 bytes)
- amplitudes for all voices for all time steps (voices * duration bytes)
To play back the note, the Arduino sets up the frequencies for all used voices, then updates the voice amplitudes for each time step:
PROGMEM prog_uchar *instr = piano;
byte voices = pgm_read_byte(instr++);
byte duration = pgm_read_byte(instr++);
for (byte i = 0; i < voices; i++) {
GD.voice(i, 0, (pgm_read_word(instr) * 184L) >> 10, 0, 0);
instr += 2;
}
for (;;) {
prog_uchar *amps = instr;
for (byte i = 0; i < duration; i++) {
for (byte j = 0; j < voices; j++) {
byte v = pgm_read_word(amps++);
GD.wr(VOICES + 4 * j + 2, v);
GD.wr(VOICES + 4 * j + 3, v);
}
GD.waitvblank();
GD.waitvblank();
}
}