-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Update I2S base frequency and frequency dividers to real ones #4031
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
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
33a3112
Fix I2S base frequency
Crypter d1496a4
Merge pull request #1 from Crypter/hotfix/i2s_frequency
Crypter f335b28
Update frequency dividers for I2S
Crypter b07cc34
Merge branch 'master' into master
earlephilhower a380742
Merge branch 'master' into master
Crypter 941e68e
fixed i2s_set_rate, added i2s_get_real_rate() and i2s_set_dividers
b56bc49
Minimise float calculations
Crypter 6df60c4
Merge branch 'master' into master
Crypter f5d676a
Merge branch 'master' into master
earlephilhower f67cff8
Merge branch 'master' into master
earlephilhower File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I precomputed the following values:

This is the closest we can get from the real numbers. Even a simple switch-case will do the job here, I'll implement it later today. Maybe we should provide a new function that will enable us to provide the dividers directly for better control?
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:
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)!