I am using an Adafruit PAM8302A connected to the DAC of a Particle Photon, reading .wav files from an SD card. Sounds are playing except if a .wav file finishes there is a glitch grinding sound - this doesn’t happen when skipping file to file. The only library I can find for playing sound in particle.io is the speaker library.
Can anyone see a reason this is happening? or can recommend another sound library that works with particle photon?
GitHub - monkbroc/particle-speaker: Generate audio output for a speaker with a Particle device. The glitch occurs in the example code of this library as well.
// This #include statement was automatically added by the Particle IDE.
#include <FastLED.h>
#include <speaker.h>
#include <SdFat.h>
FASTLED_USING_NAMESPACE;
#define NUM_LEDS 20
#define LED_PIN 0
#define STRIP_PIN 6
#define BUT_PIN 1
#define UPDATES_PER_SECOND 60
String file_name [17]{ "1.wav","2.wav", "3.wav", "4.wav", "5.wav", "6.wav", "7.wav", "8.wav","9.wav",
"10.wav", "11.wav", "12.wav", "13.wav","14.wav","15.wav","16.wav","17.wav"};
SerialLogHandler logHandler(LOG_LEVEL_NONE, { // Logging level for non-app messages
{ "app", LOG_LEVEL_INFO } // Logging level for application messages
});
// Define the array of leds
CRGB leds[NUM_LEDS];
typedef struct wav_header {
// RIFF Header
char riff_header[4]; // Contains "RIFF"
uint32_t wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
char wave_header[4]; // Contains "WAVE"
// Format Header
char fmt_header[4]; // Contains "fmt " (includes trailing space)
uint32_t fmt_chunk_size; // Should be 16 for PCM
uint16_t audio_format; // Should be 1 for PCM. 3 for IEEE Float
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
uint16_t sample_alignment; // num_channels * Bytes Per Sample
uint16_t bit_depth; // Number of bits per sample
// Data
char data_header[4]; // Contains "data"
uint32_t data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size
// uint8_t bytes[]; // Remainder of wave file is bytes
} WavHeader_t;
const int SD_SS = A2;
const size_t BUFFERSIZE = 1024;
uint16_t data[2][BUFFERSIZE];
SdFat sd;
File wavFile;
WavHeader_t wh;
int buttonCount = 0;
int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
static uint8_t hue;
Speaker speaker(data[0], data[1], BUFFERSIZE);
void setup() {
delay( 3000 );
Serial.begin(57600);
Serial.println("resetting");
FastLED.addLeds<NEOPIXEL, STRIP_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
//FastLED.setMaxPowerInVoltsAndMilliamps(5,10000); // limit my draw to 10A at 5v of power draw
LEDS.setBrightness(100);
pinMode(BUT_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
Particle.function("playWav", selectFile);
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(hue,255,255);
if (sd.begin(SD_SS)) Log.info("SD initialised");
else Log.warn("failed to open card");
}
//_______________________________________________________________________________
void loop() {
int reading = digitalRead(BUT_PIN);
if (reading != lastButtonState) lastDebounceTime = millis();
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
buttonCount++;
//String newSTR = buttonCount + ".wav";
selectFile(file_name[buttonCount]);
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(hue++,255,255);
}
}
}
lastButtonState = reading;
if(buttonCount > 16) buttonCount = 0;
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
if (speaker.ready()) readChunk();
}
//_______________________________________________________________________________
int selectFile(const char* filename) {
int retVal = 0;
if (!strcmp("ls", filename) || !strcmp("dir", filename)) {
sd.ls("/", LS_R);
return 0;
}
if (wavFile.isOpen()) wavFile.close();
wavFile = sd.open(filename);
if (wavFile) {
memset((uint8_t*)data, 0x80, sizeof(data)); // reset buffer to bias value 0x8080 (quicker via memset() than writing 0x8000 in a loop)
if (sizeof(wh) == wavFile.read((uint8_t*)&wh, sizeof(wh))) {
Log.printf("%s\n\r\t%.4s\r\n\tsize: %lu\r\n\t%.4s\r\n\t%.4s\r\n\tchunk size (16?): %lu\r\n\taudio format (1?): %u\r\n\tchannels: %u"
, filename
, wh.riff_header
, wh.wav_size
, wh.wave_header
, wh.fmt_header
, wh.fmt_chunk_size
, wh.audio_format
, wh.num_channels
);
// two chunks since Log only supports limited length output
Log.printf("\r\n\tsample rate: %lu\r\n\tbyte rate: %lu\r\n\tsample alignment: %u\r\n\tbit depth: %u\r\n\t%.4s\r\n\tdata bytes: %u"
, wh.sample_rate
, wh.byte_rate
, wh.sample_alignment
, wh.bit_depth
, wh.data_header
, wh.data_bytes
);
retVal = wh.data_bytes;
readChunk();
speaker.begin(wh.sample_rate);
}
}
else {
Log.error("%s not found", filename);
}
return retVal;
}
int readChunk() {
int retVal = 0;
if (wavFile.isOpen()) {
uint16_t* wav = speaker.getBuffer();
uint8_t buf[BUFFERSIZE * 2 * wh.num_channels];
int n = wavFile.read(buf, sizeof(buf));
if (n < sizeof(buf)) wavFile.close(); // when all data is read close the file
memset((uint8_t*)wav, 0x80, BUFFERSIZE); // reset buffer to bias value 0x8080
for(int b = 0; b < n; b += wh.sample_alignment) {
wav[retVal++] = *(uint16_t*)&buf[b] + 32768; // convert int16_t to uin16_t with bias 0x8000
}
} else speaker.end();
return retVal;
}
//_______________________________________________________________________________
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }