Small background story:
In any audio streaming system, the receiving end needs to sync up to the sample pace that the source is transmitting. Between transmitter and receiver we expect to have TCP/IP infrastructure - that will results in unknown delay over time, but a buffer in the receiving end typical overcome that issue.
But we still need to sync to the source sample rate, if not we run out of buffer space or samples.
There are two ways to handle that. Skip or add samples. If that happens at a low rate it can be masked by the audio signal. But as tx/rx pace gets to far form each other, it will start to be audible.
Alternative the clock at receiver end can be locked to the transmitted sample rate. That is where the audio PLL come into play.
First of all we need a high resolution PLL to get exactly the clock frequency we want, secondly we need low clock jitter. Until the now we have used a fractional PLL to generate the master clock for the I2S audio sub system. The fractional PLL has limited resolution and lots of clock jitter.
Here we call the init_i2s function call to setup a 44100 samples pr second system. The driver calculate the best match from the fractional PLL and in this setup it will do a 44642 samples pr second output rate. Each second we consume 542 samples that the source did has not provide on average. On top of that a fractional PLL has a lot of clock to clock jitter on the generated i2s_bck aka audio bit clock.
Codec/integrated class D amplifier are more or less insensitive to audio bit clock jitter. But to do a god job here the Codec/Amplifier require a higher clock or its own wide band pll to clean up the clock. If not, the audio bit clock jitter turns into phase noise in the audio spectra.
Until now my work has been on rev_0 chips and been working late night at very low volume setting due to daytime job and a sleeping family. In such conditions phase noise floor is very audible and will not pass in commercial products.
My end goal is to do a high performing audio streaming system using esp32 with our own designed fully integrated audio amplifier. ESP32 is the sweep spot on price, performance and size. And our amplifier has the flexibility to cover use cases from small battery driven portable speakers to battery powered PA systems that will keep the party rocking all night long. Check out what http://soundboks.com made using our technology.
ESP32 Audio PLL
The ESP32 Audio PLL is a 350-500MHz oscillator with a wide bandwidth phase look loop around. Slow lock-in, high resolution and low clock to clock jitter. To use the APLL as clock source for the i2s subsystem one must do:
1. Setup sdm0,sdm1,sdm2 and odir in the clock system APLL section to generate the wanted frequency for i2s module.
2. In i2s module select APLL as clock source and bypass fractional N divider.
Starting with 2. Select APLL and bypass fractional N divider:
Code: Select all
// 20.10.2017/JKJ: Dirty hack to use APLL for i2s audio clock I2S[i2s_num]->clkm_conf.clka_en = 1; // was 0 I2S[i2s_num]->clkm_conf.clkm_div_a = 1; // was 63; I2S[i2s_num]->clkm_conf.clkm_div_b = 0; // was clkmDecimals; I2S[i2s_num]->clkm_conf.clkm_div_num = 1; // was clkmInteger;
With a review of figure 50 from the hardware ref. we see that APLL_CLK is passed select and passed direct to I2Sn_CLK. We keep M = 4 and get i2SnO_BCK_out to be 4 times less then APLL_SCK. So for 32 bits stereo 44100 samples pr sec:
clock APLL = (2*32*44100) * 4 Hz = 2822400 * 4 Hz = 11289600 Hz
For step 2. Setup APLL
Here we need to review section 3.2.7 in hardware ref. and call rtc_clk_apll_enable(...) to enable and set sdm and divider parameter correctly. One approach is to calculate back from clock APLL = 11289600 Hz to somewhere in the 350-500 MHz range of the APLL oscillator.
Was not able to get the calculation correct - need odir value of 8 to work and doing so I am miss a magic factor (mf) 2 :
clock APLL oscillator = clock APLL * mf * 2 * ( odir + 2 )) = 11,288,960 * 2 * 2 * ( 8 + 2 ) = 451,584,000 Hz
clk xtal * (sdm2 + sdm1/256 + sdm0/(256*256) + 4 ) = 40 Mhz * mf * ( 7 + 73/256 + 0/(256*256) + 4 ) = 445,700,000 Hz
Code: Select all
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM, &pin_config); rtc_clk_apll_enable(1,0,73,7,8); // enable, sdm0 = 0, sdm1 = 73, sdm2 = 7, odir = 8
Clock jitter is gone, I have more then enough resolution to get the i2c_bck I want. The noise floor on our amplifier is around 25 dB lower compared to fracN clock and it just sounds awesome.
Now i just need some one to help figuring out why my magic factor is needed. When understood, I suggest that we patch the i2s driver to use the APLL if on rev_1 chips. And we all can start to do cool new awesome sounding audio application with ESP32 and cheap class D amplifiers. (please let me know if you want to look in to our technology)
Well done at Espressif and a big thanks to all the hang-around hackers