Add method to obtain precise audio sample rate based on hardware clocks
Background
Currently libDaisy's getter methods to obtain the current audio codec sample rate as a float return the target/idealized sample rate – e.g. 48000.0 for 48kHz – when in fact the actual codec sample rate will be slightly different from this due to hardware limitations.
The SAI peripheral clock must be divided by an integer (this is all initialized via the HAL) and thus the actual operating frame clock / LR clock (aka sample rate) for the I2S codec ends up being slightly off from the target sample rate. For example, at 48kHz target sample rate the actual operating sample rate comes out to ~48014.324.
In many DSP applications this error doesn't matter. Oscillator tuning will be off only by a cent or so usually, and delay times etc are off by a marginal amount. However, for certain specific applications, the exact value is important, and it's good to have the option anyway. One such application is a looping delay (100% feedback delay line) which will drift over time if not using the correct value.
Changes
This PR adds a new method GetPreciseSampleRate() to SAI handle (also accessible via callthrough from AudioHandle) which provides the exact operating sample rate derived from the corresponding SAI peripheral clock and the HAL-determined MCK divider – Mckdiv in the HAL init struct, as updated via the call to HAL_SAI_InitProtocol(...)
I also have my IDE setup to trim trailing whitespace and run clang-format (which uses the libDaisy configuration file) so there's a good amount of formatting changes here too.
Seeking Feedback:
- Should this instead replace the implementation of the current
GetSampleRate()methods? This would not be an API-breaking change but it may have slight impact to existing applications so I didn't go that route.- The most significant behavior change is probably that the current method returns hard-coded values so it works before audio system init, and this method does not.
- Is optionally passing the
sai_idxas an integer in theAudioHandlemethod reasonable? It would also be easy, albeit more verbose, to useSAIHandle::Config::Peripheralinstead.
Example Usage
Typical usage:
// The exact sample rate is accessible from `AudioHandle`
// This must happen AFTER audio system is initialized
float sr_exact = seed.audio_handle.GetPreciseSampleRate();
osc.Init(sr_exact);
If the hardware is using an additional codec on the second SAI peripheral you can also specify this by passing 1 as an argument for sai_index.
float sr_exact = seed.audio_handle.GetPreciseSampleRate(1);
Test Results
151 tests ±0 151 :white_check_mark: ±0 0s :stopwatch: ±0s 16 suites ±0 0 :zzz: ±0 1 files ±0 0 :x: ±0
Results for commit 11b5b841. ± Comparison against base commit f7727edb.
I don't know why clang-format in the github CI action always disagrees with my local run so I'm not going to try to fix the style check errors here for now. I would love to figure out why that happens.