-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update I2S base frequency and frequency dividers to real ones #4031
Conversation
Relevant discussion here: Also please merge the new I2S features and fixes provided by other users, this part of the SDK is lacking a lot of stuff, and I'm waiting on it so that I can finish a library I'm writing |
@Crypter in general, proposed I2S features will be left for 2.5.0 release, meaning the intent is to merge post 2.4.0 release. This does not apply to bug fixes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
It's an interesting start, but it looks to me like more changed needed to pull in the work from the NONOS-MP3 player.
@@ -754,7 +754,7 @@ extern uint8_t esp8266_gpioToFn[16]; | |||
#define i2c_bbpll_en_audio_clock_out_msb 7 | |||
#define i2c_bbpll_en_audio_clock_out_lsb 7 | |||
#define I2S_CLK_ENABLE() i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1) | |||
#define I2SBASEFREQ (12000000L) | |||
#define I2SBASEFREQ (160000000L) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the CPU is not in 2x mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@earlephilhower I'm glad you asked all of this.
Cnlohr confirmed small part of this in one of his projects/videos, there was confirmation in one of the links above, and I just confirmed it experimentaly in my room using a microphone and a speaker attached to the I2S output pins.
Apparently there's internal PLL running on 160MHz, completely independent from the CPU clock. That is divided by two deviders, the first one is the so-called clock divider, and the later is bck (word select) divider. All of that naming doesn't make sense, because the first divider just divides the clock to a lower, closer to what we need, number, and the second one actually gives us the clock speed of the I2S bus. ( lines 215, 216 in the file, uint32_t i2s_clock_div = THIS_NUMBER & I2SCDM; uint8_t i2s_bck_div = AND_THIS_NUMBER & I2SBDM; )
For example, on 160MHz, if you set the first divider to 19, and the second one to 6 you'll get ~1403508.7 Hz I2S clock speed, from which later is derived the Word select clock by dividing that to 32(bits), which gives us ~43859.64 Hz Word select clock. This is the closest you can get to 44.1KHz.
When I set the dividers to their maximum of 63 (64 overflows), I got ~40312 Hz clock speed, which when I attached a speaker to it and recorded that on 96KHz sample rate (max of my soundcard), I found the peak very clearly on my audio spectral analyzer. And the corresponding WS clock at 1259-1260Hz too, which is very audible also.
I encourage you (reader) to test this on oscilloscope, because I don't own one, but I'm 100% sure in my findings and calculations.
Which follows that the audio coming out of this chip using I2S would be somewhat slower and with lower pitch, but that's a completely different topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and we'll have to find a more decent way to set the sample rate more accurately because this doesn't work at all. I suggest a precomputed look up table with the most common sample rates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thanks a lot for the info and add'l update! I'll give the existing change a pull and run it against my audio stack this week. I need to pore over the existing code because it still seems that such a large constant change would have a correspondingly large frequency change, but it'll be simple enough to test once I get some headphones! I'll also see if my DMM has a freq counter option.
I would caution against a hard-coded table because when the I2S is used (abused) as a software sigma-delta DAC you can do 64x and 96x oversampling to get more effective bits. My own AAC/MP3/etc. audio stack does this now. You end up requesting an output frequency of (baserate*oversample/32) so you end up with requests for, say, 88.2Khz for a 44.1 @ 64x oversamples.
So you're thinking of the existing setFreq(x hz) and an optional setDividers(div1, div2) call? If setFreq does its job, there's not much more that setDividers can do, no?
Thx!
-EFP3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you VERY much! for such a detailed analysis!
Would you be able to do the brute force as a change? I thought that was already being done in the MP3 example, so it might simply be a cut-n-paste. I'd stick with lockbits=1 so only try 16 bits, because the SW DAC doesn't actually generate those other bits and you'll introduce a bias in the output signal by putting in 0s, so it comes to (64-2)*(64-5) iterations:
for (bckdiv=2; bckdiv<64; bckdiv++) {
for (clkmdiv=5; clkmdiv<64; clkmdiv++) {
tstfreq=BASEFREQ/(bckdiv*clkmdiv*16*2);
if (ABS(rate-tstfreq)<ABS(rate-bestfreq)) {
bestfreq=tstfreq;
bestclkmdiv=clkmdiv;
bestbckdiv=bckdiv;
}
}
}
```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if you can add a getActualFreq call, a user application can actually correct for this difference between requested and generated via simple dropping our replicating samples, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory - yes. In practice it takes some interpolation and dithering to achieve better results. This can be a problem only during playback of streams for longer periods due to buffer overrun or underruns by different feed and playback rates. In practice the minor network connection issues (jitter and packet loss) will solve this issue by forcing re-buffering of the stream once in a while. We replace our issue with a better issue. Networking rocks!
Also, once upon a time 4% difference in speed was acceptable... :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, all of the above applies only and only if the ESP8266 playing the stream and the server serving the stream have zero relative drift in their clocks. Funny how imperfect technology is...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for such a fast turnaround!
I'm in the middle of rocket surgery tonight, actually, working with @tueddy porting the ESP8266Audio library to the ESP32 (just plugged in after about 3 months sitting under my keyboard), but I promise to look at this by the weekend and get it into the merge window for 2.4.1 or 2.5.0 (whichever comes next)!
cores/esp8266/core_esp8266_i2s.c
Outdated
@@ -218,7 +218,7 @@ void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ | |||
|
|||
//!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right | |||
I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); | |||
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((i2s_bck_div-1) << I2SBD) | ((i2s_clock_div-1) << I2SCD); | |||
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((i2s_bck_div) << I2SBD) | ((i2s_clock_div) << I2SCD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the math in this function vs. what's in the MP3_decoder vs. what's there now, I can't see how increasing I2SBASEFREQ by 13.33x and then incrementing the divider (aka div-by-2) would work. Assuming it works at all, wouldn't this just increase the output frequency by 13.33/2 or 13.33/4?
The MP3 decoder reference has a series of configs on the clock source for I2S and then a large for-loop to determine the closest divider with their (different, I believe) clock inputs.
https://github.com/espressif/ESP8266_MP3_DECODER/blob/7552a62d425598d64ebc255d32fa4f20220e92f6/mp3/driver/i2s_freertos.c#L250
Have you run this with a frequency counter on LRCLK for different CPU freqs and sample rates? The existing logic is close enough for most sample outputs I've seen for multi-hour AAC and MP3 streams, so seeing such a large change in clocking requested here really puzzles me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I2S fix applied, can now be tested.
Hi earlephilhower, |
@Crypter Thanks again! |
Values taken from:
https://github.com/espressif/ESP8266_MP3_DECODER/blob/master/mp3/driver/i2s_freertos.c
Reference:
espressif/ESP8266_MP3_DECODER@75cd867