16kHz PWM EasyDMA audio, loop mode, serial p/l/r/DELETE commands, button and end-of-track fixes
This commit is contained in:
@@ -204,6 +204,7 @@ uint8_t g_numTracks = 0;
|
||||
uint32_t g_trackStart[MAX_TRACKS];
|
||||
uint32_t g_trackLen[MAX_TRACKS];
|
||||
uint8_t g_currentTrack = 0;
|
||||
bool g_loopTracks = false; // false = play once and stop; true = auto-advance through all tracks
|
||||
|
||||
// Audio DMA buffers — uint16_t PWM duty-cycle values for EasyDMA
|
||||
static uint16_t g_pwmBuf[2][AUDIO_BUF_SIZE];
|
||||
@@ -348,6 +349,7 @@ void loadTrackTable() {
|
||||
if (!f.open(fname, FILE_O_READ)) break;
|
||||
g_trackLen[i] = f.size();
|
||||
f.close();
|
||||
if (g_trackLen[i] == 0) break; // stop at first empty file
|
||||
g_numTracks = i + 1;
|
||||
}
|
||||
Serial.print("Found ");
|
||||
@@ -720,7 +722,7 @@ static void audioFillBuf(uint8_t b) {
|
||||
}
|
||||
g_bufReady[b] = true;
|
||||
// Arm stop timer on first pure-silence fill (all audio already sent to DMA)
|
||||
if (toRead == 0 && g_trackDoneMs == 0) {
|
||||
if (toRead == 0 && g_trackDoneMs == 0 && g_playing) {
|
||||
g_trackDoneMs = millis() + 100; // 100ms > 3 buffer lengths (3 × 32ms)
|
||||
}
|
||||
}
|
||||
@@ -871,23 +873,26 @@ void buttonsInit() {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns 1-8, or 0 if none pressed
|
||||
// Returns 1-4, or 0 if none pressed. No blocking delay — safe to call every loop().
|
||||
uint8_t buttonRead() {
|
||||
for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
|
||||
// Buttons pull to ground
|
||||
if (digitalRead(BTN_PINS[i]) == LOW) {
|
||||
delay(20); // debounce
|
||||
if (digitalRead(BTN_PINS[i]) == LOW) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
if (digitalRead(BTN_PINS[i]) == LOW) return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Wait until the button is physically released. No timeout — prevents the 800ms
|
||||
// cooldown from expiring while the button is still held and re-triggering.
|
||||
void waitButtonRelease(uint8_t btn) {
|
||||
unsigned long deadline = millis() + 300;
|
||||
while (buttonRead() == btn && millis() < deadline) {}
|
||||
while (buttonRead() == btn) {
|
||||
// Keep DMA buffers fed while waiting
|
||||
if (g_playing) {
|
||||
for (uint8_t b = 0; b < 2; b++) {
|
||||
if (!g_bufReady[b]) audioFillBuf(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(20); // debounce after release
|
||||
}
|
||||
|
||||
|
||||
@@ -1102,6 +1107,26 @@ static void serUploadTick() {
|
||||
if (g_serLineBuf[0] == 'u') g_audioGain++;
|
||||
else if (g_audioGain > 1) g_audioGain--;
|
||||
Serial.print("GAIN "); Serial.println(g_audioGain);
|
||||
} else if (g_serLineLen == 1 && g_serLineBuf[0] == 'p') {
|
||||
if (g_playing) { audioStop(); Serial.println("STOP"); }
|
||||
else if (g_numTracks > 0) { audioStart(g_currentTrack); Serial.println("PLAY"); }
|
||||
else Serial.println("ERR no tracks");
|
||||
} else if (g_serLineLen == 1 && g_serLineBuf[0] == 'l') {
|
||||
g_loopTracks = !g_loopTracks;
|
||||
Serial.print("LOOP "); Serial.println(g_loopTracks ? "ON" : "OFF");
|
||||
} else if (g_serLineLen == 1 && g_serLineBuf[0] == 'r') {
|
||||
Serial.println("REBOOT");
|
||||
delay(10);
|
||||
NVIC_SystemReset();
|
||||
} else if (sscanf(g_serLineBuf, "DELETE %u", &utrk) == 1) {
|
||||
char fname[16];
|
||||
pocFilename((uint8_t)utrk, fname);
|
||||
if (InternalFS.remove(fname)) {
|
||||
Serial.print("DELETED "); Serial.println(fname);
|
||||
} else {
|
||||
Serial.print("ERR no file "); Serial.println(fname);
|
||||
}
|
||||
loadTrackTable();
|
||||
} else {
|
||||
Serial.print("ERR bad cmd: ");
|
||||
Serial.println(g_serLineBuf);
|
||||
@@ -1192,7 +1217,7 @@ void loop() {
|
||||
if (g_playing && g_trackDoneMs != 0 && millis() >= g_trackDoneMs) {
|
||||
g_trackDoneMs = 0;
|
||||
audioStop();
|
||||
if (g_numTracks > 0) {
|
||||
if (g_loopTracks && g_numTracks > 0) {
|
||||
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
|
||||
audioStart(g_currentTrack);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user