This sound module generates a waveform by dividing a clock signal by the desired frequency. Sampled waveforms are stored in an EEPROM and then outputted through a DAC (r2r ladder). The EEPROM is programmable to allow adding new waveforms.
Waveforms
Samples
Frequencies / Formula
Range | Initial Frequency | Divided Range |
0x0 | 28912Hz | 738Hz – 147Hz |
0x1 | 57824Hz | 1476Hz – 293Hz |
0x2 | 115648Hz | 2953Hz – 587Hz |
0x3 | 1231296Hz | 5906Hz – 1175Hz |
Precision
Generally, the higher the number you choose to divide by, the more precision there will be. You will notice overlaps in the range of frequencies above.
ie. 523Hz (C5) can be reached on ranges 0x0 and 0x1. However, the one that would be most accurate is most likely 0x1.
0x1) 57824Hz / 523Hz = 0b01101110 ✓
0x0) 28912Hz / 523Hz = 0b00110111
EEPROM and Waveforms
I have decided to go with loading calculated waveforms into an EEPROM. Each sample takes up an equal amount of space, 0xFF, and uses the addresses [A0..A7]
I then use the next two bits of address space to select the range of frequencies (see above). [A8..A9]
The rest of the address lines are used for choosing which waveform I want to use. ie. Square, Triangle, Sawtooth, Noise, Sine, etc…
The output of the EEPROM, [D0..D7] feeds directly into an R2R ladder, to convert the digital signal into an analog one.
Register | Bit(s) | Value |
Meta | xxxxxx11 | Range Select |
Meta | xxxx11xx | Waveform Select |
Meta | 1111xxxx | Volume Select |
Division | 11111111 | Range Freq Divisor |
Waveforms in EEPROM
I used Javascript (NodeJs) to help me generate this data since its the language I am most comfortable with.
As you might notice, for the square wave the max value I am using is 0xEF and similarly, for the triangle, I am going from 0x80 to 0xFF. There may be a better way, but this way the output volume is almost equal across all the waveforms.
Here are some examples:
// SQUARE (1 cycle)
for (let e = 0; e <= 255; e = e + 1) {
eepromData[address] = e < 128 ? 0xef : 0x0;
address++;
}
// SAWTOOTH (1 cycle) - Ramp Up
for (let e = 0; e <= 255; e = e + 1) {
eepromData[address] = e;
address++;
}
// TRIANGLE (1 cycle)
val = 0;
for (let e = 0; e <= 254; e = e + 1) {
eepromData[address] = val;
if (e < 128) {
val = val + 2;
if (val > 255) val = 255;
} else {
val = val - 2;
}
address++;
}
Generating Music!
My goal was to be able to convert a standard MIDI file and play it back, plain and simple. To do this, first I created a small Node script that loaded the file and ran through…. TODO TODO TODO