16kHz PWM EasyDMA audio, loop mode, serial p/l/r/DELETE commands, button and end-of-track fixes

This commit is contained in:
zyphlar
2026-07-04 03:28:13 -07:00
parent a385a156f2
commit 7b1dc8e118
+37 -12
View File
@@ -204,6 +204,7 @@ uint8_t g_numTracks = 0;
uint32_t g_trackStart[MAX_TRACKS]; uint32_t g_trackStart[MAX_TRACKS];
uint32_t g_trackLen[MAX_TRACKS]; uint32_t g_trackLen[MAX_TRACKS];
uint8_t g_currentTrack = 0; 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 // Audio DMA buffers — uint16_t PWM duty-cycle values for EasyDMA
static uint16_t g_pwmBuf[2][AUDIO_BUF_SIZE]; static uint16_t g_pwmBuf[2][AUDIO_BUF_SIZE];
@@ -348,6 +349,7 @@ void loadTrackTable() {
if (!f.open(fname, FILE_O_READ)) break; if (!f.open(fname, FILE_O_READ)) break;
g_trackLen[i] = f.size(); g_trackLen[i] = f.size();
f.close(); f.close();
if (g_trackLen[i] == 0) break; // stop at first empty file
g_numTracks = i + 1; g_numTracks = i + 1;
} }
Serial.print("Found "); Serial.print("Found ");
@@ -720,7 +722,7 @@ static void audioFillBuf(uint8_t b) {
} }
g_bufReady[b] = true; g_bufReady[b] = true;
// Arm stop timer on first pure-silence fill (all audio already sent to DMA) // 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) 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() { uint8_t buttonRead() {
for (uint8_t i = 0; i < NUM_BUTTONS; i++) { for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
// Buttons pull to ground if (digitalRead(BTN_PINS[i]) == LOW) return i + 1;
if (digitalRead(BTN_PINS[i]) == LOW) {
delay(20); // debounce
if (digitalRead(BTN_PINS[i]) == LOW) {
return i + 1;
}
}
} }
return 0; 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) { void waitButtonRelease(uint8_t btn) {
unsigned long deadline = millis() + 300; while (buttonRead() == btn) {
while (buttonRead() == btn && millis() < deadline) {} // 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++; if (g_serLineBuf[0] == 'u') g_audioGain++;
else if (g_audioGain > 1) g_audioGain--; else if (g_audioGain > 1) g_audioGain--;
Serial.print("GAIN "); Serial.println(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 { } else {
Serial.print("ERR bad cmd: "); Serial.print("ERR bad cmd: ");
Serial.println(g_serLineBuf); Serial.println(g_serLineBuf);
@@ -1192,7 +1217,7 @@ void loop() {
if (g_playing && g_trackDoneMs != 0 && millis() >= g_trackDoneMs) { if (g_playing && g_trackDoneMs != 0 && millis() >= g_trackDoneMs) {
g_trackDoneMs = 0; g_trackDoneMs = 0;
audioStop(); audioStop();
if (g_numTracks > 0) { if (g_loopTracks && g_numTracks > 0) {
g_currentTrack = (g_currentTrack + 1) % g_numTracks; g_currentTrack = (g_currentTrack + 1) % g_numTracks;
audioStart(g_currentTrack); audioStart(g_currentTrack);
} }