[sdiy] Wavetable Design Update
Tom Wiltshire
tom at electricdruid.net
Wed Feb 2 11:42:51 CET 2011
On 2 Feb 2011, at 08:08, Matthew Smith wrote:
> Quoth Dave Manley at 02/02/11 17:38...
> ...
>> roughly:
>> note number mod 12 -> note
>> note number / 12 -> octave
>> 12 entry look up table followed by divide/mult by power of 2 for octaves
>
> Thanks for that. Unfortunately I've already cracked the MIDI note number issue (got a bit of Perl to do the sums for me.)
>
> My problem is that given a frequency f, I have to work out timer configuration values which are:
>
> * The number to count up to
> * The prescaler value, if required.
>
> ...for a given CPU clock frequency. The frequency range I need to be able to calculate is pretty massive - from 2093.00 Hz for MIDI note 0 up to 3,211,226.61 Hz for MIDI note 127. Note a) this is to clock 256 samples per cycle, b) I'm working to 2 decimal places, so really working in cHz.
>
> So, whether I calculate on-the-fly or use a lookup table, I've somehow got to find an algorithm to give me the OCR1A (timer count) and TCCR1B (prescaler) values needed to set up the counter/timer to give me my desired output frequency. Higher frequencies will need little/no prescaling, whilst lower frequencies might require different prescale values to get as close as possible to the target frequency.
>
> I *could* just spend half a day working the whole lot out by hand (trial an error on the prescale values) but it would be nice to have a means whereby I can throw an arbitrary frequency at the device and have it all calculated.
>
> NOTE: The calculation needs to be done ONCE per note request, so I'm not terribly worried about timing. However many clock cycles it takes to do the math, I can't believe it will cause a sufficient delay before the timer is set and the note starts to sound that anyone would notice.
Ok, ignore what I said about phase accumulators. If you're dividing down the CPU clock, it's more like a DCO than a PPG/DW Osc.
Like you, I did the sums first somewhere I could see. I used PHP to display an HTML table with all the note numbers, their frequencies, and the required division and prescale values, plus the error. Very similar to your perl. It looked like this:
// What prescale value are we using?
if ($note<15) {
$prescale = 8;
} else if ($note<27) {
$prescale = 4;
} else if ($note<39) {
$prescale = 2;
} else {
$prescale = 1;
}
// Find the best match for the note using frequency division of the master clk
$divide_by = round($master_clk / ($freq*$prescale));
The prescale breakpoints I determined by looking at the table of values without them in and then including them. The best match for a given freq is found by doing the sum and getting a non-integer result, then rounding to get the integer that gives least error.
I used this PHP script to built a lookup table of divider values. I used 256 entries (eg 1 entry per 1/4 tone) and then linearly interpolated between the entries. This look table used a 16-bit Pitch variable where the top 7-bits are the MIDI note, and the bottom 9 are the within-note position. The top 8-bits are the index into the lookup table, and the bottom 8-bits are used to interpolate between the table positions. You can calculate the errors in the script, but they're small. Tiny, in fact.
You/Dave are right that the errors get worse at the top end. The Roland Juno106 is the classic example in this area. It managed to get away with a 16-bit timers by using a 2-bit prescaler. This gives basically 18-bit accuracy. It used a 8MHz clock, although I think the prescaler was set to /2 as a minimum, so it was counting 4MHz pulses. This is a minimum in my opinion. If you can increase the master clock above 4MHz (your 20MHz is good!) or increase the size of the divider above 16/18 bits (the Prophet08's 32-bit dividers are good!) then you see a considerable improvement in accuracy.
HTH,
Tom
More information about the Synth-diy
mailing list