MAX98357A Crackle and Pops #1268
-
I am surprised that this has not come up more in this forum as it is a big deal practically everywhere else. (Maybe this is good thing as it is resolved!) It is very common when using the MAX98357A to have excessive crackling during playback. There have been a number of proposed solutions but what I have found is that it is most likely cause by excessive ringing on the LRC and/or BCLK lines. This would be less of an issue with custom boards where the distance from the microcontroller and the amplifier can be very short. The modules from Aidafruit, where wire lengths can be long, do not mitigate this. As is my practice with data lines like this, I added low value resistors on those lines and that cleared up the crackling. I typically use 27 ohms but anything from 10-50 should work just fine. What I have not been able to solve is the pop sound between plays. Scouring the Internet, especially the Raspberry Pi crowd, turns up the idea that the popping is caused by BCLK not being present before LRC starts. It is suggested that if BCLK is never stopped, this would not be a problem. Not sure how that could be done. It may be at the I2S level. One workaround is to use the SD Mode line. The voltage on that line sets the mix of right and left channels but if pulled all the way down, it mutes the amplifier. Using that line and putting in mute periods of very short duration is supposed to take care of that problem. So my question is within the context of these Audio Tools, how can this be done. Or is there some other solution that I am unaware of? FYI, here is my code.... ` #include "AudioTools.h"
#include "wavBell.h"
MemoryStream Bell(wavBell_wav, wavBell_wav_len);
I2SStream i2s;
EncodedAudioStream EncAudio(&i2s, new WAVDecoder()); // output to decoder
StreamCopy copier(EncAudio, Bell); // copy in to i2s
unsigned long tmrBellInterval = 10000;
unsigned long tmrBellStart = millis();
constexpr auto pinSD = 7;
void setup() {
Serial.begin(115200);
//AudioLogger::instance().begin(Serial, AudioLogger::Info);
// begin processing
auto cfg = i2s.defaultConfig();
cfg.sample_rate = 24000;
cfg.channels = 1;
cfg.pin_data = 0;
cfg.pin_bck = 4;
cfg.pin_ws = 5;
pinMode(pinSD, OUTPUT); digitalWrite(pinSD, LOW);
i2s.begin(cfg);
EncAudio.begin();
Bell.setLoop(true);
}
void loop() {
if (Bell.available()) {
copier.copy();
}
if (millis() - tmrBellStart > tmrBellInterval) {
Bell.setLoop(false);
//stop();
}
}
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 7 replies
-
I never had any pop sounds between plays, so the first thing I would suggest, is to check your audio file: - Does is start and end with a 0 ? If not, you can adjust your file (e.g. by fading in and out) or if it is data related, a MedianFilter should also help. If the i2s configuration autoclear is set (which is the default setting), i2s automatically sends 0 if no data is available. So the case that you described can not really happen. Of cause you can always send silence when there is a pause, which would have the same effect. You can use a VolumeOutput to determine the volume or the result of the copy() call to check if you processed any data and use this information to activate/deactivate the amplifier |
Beta Was this translation helpful? Give feedback.
-
Strange: what happens if you simplify the loop and you just call copier.copy() ? |
Beta Was this translation helpful? Give feedback.
-
Same thing. The pop occurs between replays, not at the start of the first or after the last. The bell sound is attached as a .txt file, not a .h file. I don't know why this is but pasting code does not come across in one piece but here goes... #include "AudioTools.h"
#include "RRCrossingBell01A.h"
MemoryStream Bell(RRCrossingBell01A_wav, RRCrossingBell01A_wav_len);
I2SStream i2s;
EncodedAudioStream EncAudio(&i2s, new WAVDecoder()); // output to decoder
StreamCopy copier(EncAudio, Bell); // copy in to i2s
unsigned long tmrBellInterval = 5000;
unsigned long tmrBellStart = millis();
constexpr auto pinSD = 7;
bool LastPlay;
void setup() {
Serial.begin(115200);
//AudioLogger::instance().begin(Serial, AudioLogger::Info);
// begin processing
auto cfg = i2s.defaultConfig();
cfg.auto_clear = true;
cfg.sample_rate = 22050;
cfg.channels = 1;
cfg.pin_data = 0;
cfg.pin_bck = 4;
cfg.pin_ws = 5;
pinMode(pinSD, OUTPUT); digitalWrite(pinSD, LOW);
i2s.begin(cfg);
EncAudio.begin();
Bell.setLoop(false);
}
void loop() {
if (Bell.available()) {
copier.copy();
}
if (!copier.copy()) {
if (!LastPlay) {
Bell.begin();
}
else {
i2s.end();
stop();
}
}
if (millis() - tmrBellStart > tmrBellInterval) {
LastPlay = true;
}
}
|
Beta Was this translation helpful? Give feedback.
-
Your click comes from the WAV header! Solution Alternatives:
|
Beta Was this translation helpful? Give feedback.
I never had any pop sounds between plays, so the first thing I would suggest, is to check your audio file: - Does is start and end with a 0 ? If not, you can adjust your file (e.g. by fading in and out) or if it is data related, a MedianFilter should also help.
If the i2s configuration autoclear is set (which is the default setting), i2s automatically sends 0 if no data is available. So the case that you described can not really happen.
Of cause you can always send silence when there is a pause, which would have the same effect.
You can use a VolumeOutput to determine the volume or the result of the copy() call to check if you processed any data and use this information to activate/deacti…