earlephilhower / ESP8266Audio

Arduino library to play MOD, WAV, FLAC, MIDI, RTTTL, MP3, and AAC files on I2S DACs or with a software emulated delta-sigma DAC on the ESP8266 and ESP32
GNU General Public License v3.0
2.01k stars 432 forks source link

AudioOutputI2SNoDAC Active pop/click filter #367

Closed jgrulich closed 3 years ago

jgrulich commented 3 years ago

Hi all, Hence the NoDAC major pain are pops/clicks at the beginning and end of each played file and software solution was not found yet, I've spent some time to find simple, but working hardware solution and this is the result. Hope it will be useful. NoDAC_Active_LPF_Small

@earlephilhower If you find it useful, you may use it in doc.

jgrulich commented 3 years ago

For those, who want to know how it works: R3 and C1 is low-pass filter to transform PWM into audio signal, C2 and R4 is DC filter to separate DC averaged voltage from audio signal and across R4 is clear audio signal referenced to ground which may be directly connected to any audio amplifier. D1, R1, R2 and Q2 works as pulse detector and as a integrating capacitor is used gate capacity of Q2. Q1 is active switch which grounds audio output whenever there is missing PWM signal. R5 preserve RXD from floating. Board used: ESP01S with 3.3V Vcc, UART only for TX use, RX not connected to UART board. Output audio amplitude Vpp<1V not to open body diode of Q1, for example out->SetGain(0.3). For higher volume need to be used 2xQ1 in series connected with source together.

JakubKohoutek commented 3 years ago

Hi @jgrulich, thank you for sharing this - the circuit looks interesting (especially given there are not many similar circuits for pop and click removal), but I have a question regarding Q1 and Q2 - do you have links to their data sheet? Are they MOSFETs? I wanted to build and try the circuit, but I can't get SB108, so I am interested if there is some alternative that would be more available.

jgrulich commented 3 years ago

@JakubKohoutek SB108 is some noname copy of BS108 (ordered on Ali BS108, received SB108), with higher GS capacity, which is even better for this circuit. May be used any MOSFET with GS capacity 150-200pF, or any other with the lower capacity and add some external cap to total of 150pF and more.

jgrulich commented 3 years ago

For better signal-noise performance may be used: R3 = 22k, R4 = 47k, C1=1nF and out->SetGain(0.8).

JakubKohoutek commented 3 years ago

Thank you @jgrulich, I will definitely give it a try 👍 If it works, it might be worth including the circuit into the official documentation as many people are dealing with the same issue - @earlephilhower WDYT?

JakubKohoutek commented 3 years ago

I was playing with the circuit and eventually I ended up with its slightly modified version using BS170 as Q1 and Q2 MOSFETs as those were much easier to get (should be equivalent to 2N7000). They have much lower gate-source capacitance, so two additional capacitors C3 and C4 were needed. I was experimenting with different values of those capacitors and 10nF and 1nF were giving me the best results. I also added a pull-down resistor between the gate and source of Q1. Last change - I used 1K resistor instead of 2K2 as R3. The sound is now reasonably good, yet at this point, using DAC would be probably easier 😂 click_and_pop_filter_pwm_audio

jgrulich commented 3 years ago

@JakubKohoutek Yes, your circuit was starting point for my evaluation and components reduction. I've already tested BS170 with the GS RC networks. But the reason for SB108 was just reduction or the GS RC networks and that SB108 has 1V gate threshold compared to 2V for BS170. This additionally double the time constants. My target was to have as simple circuit as possible with minimum components. If you need longer time constant for Q2, as in your circuit it is 3-times, you may simply increase R2 to 27k. About the R3 and C1, this is low pass and integrating RC network for audio signal. Don't suppose to use this as HiFi signal source and therefor I used low cutting frequency to eliminate the background noise from ESP. For sure, you may play with this circuitry a bit more and use AC detector, symmetrical output switch and much more, but all this adds additional components and my goal was to have as less components as possible. If someone finds some simpler solution, it will be very welcome.

jgrulich commented 3 years ago

In case the SB108(BS108) is hard to get, BSS123(MMFTN123), or BSS138 is better choice than BS170. Both are logic level with low gate threshold. In this case is used: R2 = 47k, C4 = 100pF. C3 and R6 not used.

jgrulich commented 3 years ago

This is the updated version.

NoDAC_Active_LPF

jgrulich commented 3 years ago

It's important to keep C4 as small as possible to have small time constant of C4 + R1 which gives the mute-on time to discard end-of-file pop. Even better is C4 = 100pF and R5 not used. The AC PWM sensing is better in case that the RX pin is defined as UART input and there is 3.3V which caused the mute disable with the previous version of circuit. Instead of D1 + D3 may be used some series double diode in one package (BAV99) to save component count and board space. BAV99 may be used at position D2 too. It's even better that 1N4148 because it has 5x lower leakage current which gives the time constant with C4 when not used R5. C4 may even be removed at all too.

ericowhadi commented 2 years ago

I am able to get rid of the ending pop using AudioOutputI2SNoDac by software: basically I have a setup using NoDac, with a low pass filter and DC removal (2 resitors 2 capacitors). It is obvious to me that the sound is due to the step function from zero to 3V when we turn off (stop feeding the I2S). step functions have a very wide spectral response, causing that nasty noise. So the fix is to avoid that step function and go gradually over 1000 samples from 0 to the resting level of the pin. Modifying the classic loop: while (decoder->isRunning()){ if(!decoder->loop()){ for (int j=0;j<1000;j++) writeDAC(0x8000-32767*j/1000); decoder->stop();} } does the trick perfectly. my writeDac function is :

void writeDAC(uint16_t DAC) { for (uint8_t i=0;i<32;i++) { i2sACC=i2sACC<<1; if(DAC >= err) { i2sACC|=1; err += 0xFFFF-DAC; } else { err -= DAC; } } bool flag=i2s_write_sample(i2sACC); }

Having said that, I am puzzled why the same trick reversed did not successfully remove the noise at beginning. What I have observed is that the beginning noise is actually not really at the beginning, but way into my sound making me believe it is another problem. Because when I play a sound manually generated (a sine wave hand crafted), I don't have this bad noise at beginning. SO I am suspecting that there is some garbage hitting the I2S buffer at beginning of playing sound that has nothing to do with the step function handling between resting and playing. BTW I am using flac player from SPIFFS, I will experiment with other sound mode to see if this suspected garbage follow the flac player or SPIFFS handling. I am using ESP8266. hope this makes sense, Eric

javanesse commented 7 months ago

I am able to get rid of the ending pop using AudioOutputI2SNoDac by software: basically I have a setup using NoDac, with a low pass filter and DC removal (2 resitors 2 capacitors). It is obvious to me that the sound is due to the step function from zero to 3V when we turn off (stop feeding the I2S). step functions have a very wide spectral response, causing that nasty noise. So the fix is to avoid that step function and go gradually over 1000 samples from 0 to the resting level of the pin. Modifying the classic loop: while (decoder->isRunning()){ if(!decoder->loop()){ for (int j=0;j<1000;j++) writeDAC(0x8000-32767*j/1000); decoder->stop();} } does the trick perfectly. my writeDac function is :

void writeDAC(uint16_t DAC) { for (uint8_t i=0;i<32;i++) { i2sACC=i2sACC<<1; if(DAC >= err) { i2sACC|=1; err += 0xFFFF-DAC; } else { err -= DAC; } } bool flag=i2s_write_sample(i2sACC); }

Having said that, I am puzzled why the same trick reversed did not successfully remove the noise at beginning. What I have observed is that the beginning noise is actually not really at the beginning, but way into my sound making me believe it is another problem. Because when I play a sound manually generated (a sine wave hand crafted), I don't have this bad noise at beginning. SO I am suspecting that there is some garbage hitting the I2S buffer at beginning of playing sound that has nothing to do with the step function handling between resting and playing. BTW I am using flac player from SPIFFS, I will experiment with other sound mode to see if this suspected garbage follow the flac player or SPIFFS handling. I am using ESP8266. hope this makes sense, Eric

any updates about noise from the beginning yet?

ericowhadi commented 7 months ago

I am able to get rid of the ending pop using AudioOutputI2SNoDac by software: basically I have a setup using NoDac, with a low pass filter and DC removal (2 resitors 2 capacitors). It is obvious to me that the sound is due to the step function from zero to 3V when we turn off (stop feeding the I2S). step functions have a very wide spectral response, causing that nasty noise. So the fix is to avoid that step function and go gradually over 1000 samples from 0 to the resting level of the pin. Modifying the classic loop: while (decoder->isRunning()){ if(!decoder->loop()){ for (int j=0;j<1000;j++) writeDAC(0x8000-32767*j/1000); decoder->stop();} } does the trick perfectly. my writeDac function is : void writeDAC(uint16_t DAC) { for (uint8_t i=0;i<32;i++) { i2sACC=i2sACC<<1; if(DAC >= err) { i2sACC|=1; err += 0xFFFF-DAC; } else { err -= DAC; } } bool flag=i2s_write_sample(i2sACC); } Having said that, I am puzzled why the same trick reversed did not successfully remove the noise at beginning. What I have observed is that the beginning noise is actually not really at the beginning, but way into my sound making me believe it is another problem. Because when I play a sound manually generated (a sine wave hand crafted), I don't have this bad noise at beginning. SO I am suspecting that there is some garbage hitting the I2S buffer at beginning of playing sound that has nothing to do with the step function handling between resting and playing. BTW I am using flac player from SPIFFS, I will experiment with other sound mode to see if this suspected garbage follow the flac player or SPIFFS handling. I am using ESP8266. hope this makes sense, Eric

any updates about noise from the beginning yet?

Sorry no. I gave up and use an external dac.

Mir1001 commented 3 weeks ago

Sorry no. I gave up and use an external dac.

Can you provide working example of software fix. Thx.