sensorium / Mozzi

sound synthesis library for Arduino

Home Page:https://sensorium.github.io/Mozzi/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RP2040 is out of tune

tomcombriat opened this issue · comments

Making a small drone synth to play along, I noticed that a simple setup, based on a RP2040 with PWM output, seems to be out of tune (looks like something similar than #67 ).

Here are some measurements ("Sinewave" sketch, CPU speed: 133MHz (this does not seem to affect):

Asked frequency (Hz) Measured frequency (Hz) Deviation (%)
440 448 1.82
220 224 1.82
1000 1017 1.7
2000 2035 1.75
10000 10173 1.73

Looks fairly stable, hence the timer is probably off by that amount.

Looking more closely at the code, I think the problem comes from:

micros_per_update = 1000000l / MOZZI_AUDIO_RATE;
  do {
    next_audio_update = make_timeout_time_us(micros_per_update);

(from MozziGuts_impl_RP2040.hpp l. 246). As (1000000/32768 - int(1000000/32768)) / 1000000/32768 = 1.696%

Probable way out: https://arduino-pico.readthedocs.io/en/latest/pwm.html (using the PWM Audio class)

Note that it might remove the possibility to have an accurate EXTERNAL_TIMED output mode…

Yes, that looks useful in its own right, but also, yes, we need to fix the timers anyway (for external timed).

One approach to fixing could be to internally keep next_audio_update at a higher resolution. IIRC, absolute_time_t is essentially just a 64 bit count of microseconds. For all practical purposes, we could turn that and micros_per_update into, say, a UFix<56,8>, and simply perform an appropriate shift when passing it to hardware_alarm_set_target().

I am not sure I am following you on this. Whatever we do, the alarms on the RP2040 have a microsecond precision (only systick is up to the processor cycle, but the associated irq is already taken) whereas we are trying to set up something at 1/32768=30.51us… The .51 is where the shift comes from but I do not see how to get out without an precision improved alarm (or oscillating between 30 and 31…).

My plan so far is to reimplement the default output with PWM audio (which is DMA buffered and so on ;) !). I will try to keep the external timed as it is now, knowing that it is not on tune.

My plan so far is to reimplement the default output with PWM audio

Which, again, will be useful in it's own right, so by all means, do.

Whatever we do, the alarms on the RP2040 have a microsecond precision [...] but I do not see how to get out without an precision improved alarm (or oscillating between 30 and 31…).

In effect, I'm aiming for the latter:

1/32768=30.51us

So we'll try to output the first sample at 30us, the second at 61, then 91, 122, etc, and sample no 100 at 3051us. In an ideal world we wouldn't have any jitter at all, but I do think this will be much less noticeable than being 1.6% off on the frequency.