got stutter fixed

This commit is contained in:
zyphlar
2026-07-04 03:28:13 -07:00
parent 0697c3d6f3
commit a385a156f2
2 changed files with 40 additions and 48 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(find /c/Users/Will/apps/mobile -type f -name *)" "Bash(find /c/Users/Will/apps/mobile -type f -name *)",
"WebSearch"
] ]
} }
} }
+38 -47
View File
@@ -207,14 +207,15 @@ uint8_t g_currentTrack = 0;
// 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];
volatile bool g_bufReady[2] = {false, false}; volatile bool g_bufReady[2] = {false, false};
uint32_t g_trackDoneMs = 0; // nonzero = millis() deadline after which we stop
uint32_t g_nextReadAddr = 0; uint32_t g_nextReadAddr = 0;
uint32_t g_playEnd = 0; uint32_t g_playEnd = 0;
// Timers // Timers
unsigned long g_lastActivity = 0; unsigned long g_lastActivity = 0;
#define AUTO_OFF_MS (30UL * 60UL * 1000UL) // 30 min auto-shutoff #define AUTO_OFF_MS (30UL * 60UL * 1000UL) // 30 min auto-shutoff
#define IDLE_SLEEP_MS (60UL * 1000UL) // 1 min idle → sleep #define IDLE_SLEEP_MS (10UL * 60UL * 1000UL) // 10 min idle → sleep
// USB connected flag // USB connected flag
volatile bool g_usbConnected = false; volatile bool g_usbConnected = false;
@@ -693,8 +694,8 @@ void setupBLE() {
// Software gain: 1=unity, 2=2x, etc. Adjustable via 'u'/'d' serial. // Software gain: 1=unity, 2=2x, etc. Adjustable via 'u'/'d' serial.
static uint8_t g_audioGain = 1; static uint8_t g_audioGain = 1;
// Read PCM from current source into g_pwmBuf[b], applying gain and scaling. // Read PCM into g_pwmBuf[b], pad tail with silence.
// Pads the tail with silence if fewer than AUDIO_BUF_SIZE bytes remain. // On the first pure-silence fill (track exhausted), arms the stop timer.
static void audioFillBuf(uint8_t b) { static void audioFillBuf(uint8_t b) {
uint8_t pcm[AUDIO_BUF_SIZE]; uint8_t pcm[AUDIO_BUF_SIZE];
uint32_t toRead = 0; uint32_t toRead = 0;
@@ -718,24 +719,23 @@ static void audioFillBuf(uint8_t b) {
g_pwmBuf[b][i] = PWM_SILENCE; g_pwmBuf[b][i] = PWM_SILENCE;
} }
g_bufReady[b] = true; g_bufReady[b] = true;
// Arm stop timer on first pure-silence fill (all audio already sent to DMA)
if (toRead == 0 && g_trackDoneMs == 0) {
g_trackDoneMs = millis() + 100; // 100ms > 3 buffer lengths (3 × 32ms)
}
} }
// PWM0 ISR — fires every 32 ms (512 samples at 16 kHz). // PWM0 ISR — fires every 32 ms (512 samples at 16 kHz).
// Just signals which buffer needs refilling; loop() does the actual I/O. // Just signals which buffer needs refilling; loop() does the actual I/O.
// ISR just chains buffers — end-of-track is handled entirely in loop()
extern "C" void PWM0_IRQHandler() { extern "C" void PWM0_IRQHandler() {
if (NRF_PWM0->EVENTS_SEQEND[0]) { if (NRF_PWM0->EVENTS_SEQEND[0]) {
NRF_PWM0->EVENTS_SEQEND[0] = 0; NRF_PWM0->EVENTS_SEQEND[0] = 0;
if (g_playing) { if (g_playing) { g_bufReady[0] = false; NRF_PWM0->TASKS_SEQSTART[1] = 1; }
g_bufReady[0] = false;
NRF_PWM0->TASKS_SEQSTART[1] = 1; // start buf 1 immediately
}
} }
if (NRF_PWM0->EVENTS_SEQEND[1]) { if (NRF_PWM0->EVENTS_SEQEND[1]) {
NRF_PWM0->EVENTS_SEQEND[1] = 0; NRF_PWM0->EVENTS_SEQEND[1] = 0;
if (g_playing) { if (g_playing) { g_bufReady[1] = false; NRF_PWM0->TASKS_SEQSTART[0] = 1; }
g_bufReady[1] = false;
NRF_PWM0->TASKS_SEQSTART[0] = 1; // start buf 0 immediately
}
} }
} }
@@ -801,6 +801,10 @@ void audioStart(uint8_t trackNum) {
g_playEnd = g_nextReadAddr + g_trackLen[trackNum]; g_playEnd = g_nextReadAddr + g_trackLen[trackNum];
#endif #endif
g_trackDoneMs = 0;
g_bufReady[0] = false;
g_bufReady[1] = false;
// Stop DMA cleanly, fill both buffers, restart // Stop DMA cleanly, fill both buffers, restart
NRF_PWM0->TASKS_STOP = 1; NRF_PWM0->TASKS_STOP = 1;
uint32_t t = millis(); uint32_t t = millis();
@@ -823,6 +827,7 @@ void audioStart(uint8_t trackNum) {
void audioStop() { void audioStop() {
g_playing = false; g_playing = false;
g_trackDoneMs = 0; // cancel any pending auto-advance
NRF_PWM0->TASKS_STOP = 1; NRF_PWM0->TASKS_STOP = 1;
#ifdef POC_INTERNAL_FLASH #ifdef POC_INTERNAL_FLASH
@@ -870,9 +875,9 @@ void buttonsInit() {
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 // Buttons pull to ground
if (analogRead(BTN_PINS[i]) < 128) { if (digitalRead(BTN_PINS[i]) == LOW) {
delay(20); // debounce delay(20); // debounce
if (analogRead(BTN_PINS[i]) < 128) { if (digitalRead(BTN_PINS[i]) == LOW) {
return i + 1; return i + 1;
} }
} }
@@ -894,7 +899,7 @@ void enterDeepSleep() {
// Don't sleep if any button is currently LOW — would wake instantly. // Don't sleep if any button is currently LOW — would wake instantly.
// Also catches floating pins that the internal pull-up isn't winning against. // Also catches floating pins that the internal pull-up isn't winning against.
for (uint8_t i = 0; i < NUM_BUTTONS; i++) { for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
if (analogRead(BTN_PINS[i]) < 128) { if (digitalRead(BTN_PINS[i]) == LOW) {
g_lastActivity = millis(); // postpone g_lastActivity = millis(); // postpone
return; return;
} }
@@ -998,23 +1003,6 @@ void setup() {
// Audio // Audio
audioInit(); audioInit();
#ifdef POC_INTERNAL_FLASH
if (g_numTracks > 0) {
Serial.println("Diag: playing track0 for 3s...");
audioStart(0);
uint32_t diagEnd = millis() + 3000;
while (millis() < diagEnd && g_playing) {
for (uint8_t b = 0; b < 2; b++) {
if (!g_bufReady[b] && g_nextReadAddr < g_playEnd) {
audioFillBuf(b);
}
}
}
audioStop();
Serial.println("Diag done");
}
#endif
// USB Mass Storage // USB Mass Storage
#ifndef POC_INTERNAL_FLASH #ifndef POC_INTERNAL_FLASH
usb_msc.setID("BabyMobile", "Audio Drive", "2.0"); usb_msc.setID("BabyMobile", "Audio Drive", "2.0");
@@ -1191,21 +1179,22 @@ void loop() {
serUploadTick(); serUploadTick();
#endif #endif
// ---- Refill audio buffers (ISR sets g_bufReady[b]=false when buffer done) ---- // ---- Refill audio buffers ----
if (g_playing) { if (g_playing) {
for (uint8_t b = 0; b < 2; b++) { for (uint8_t b = 0; b < 2; b++) {
if (!g_bufReady[b] && g_nextReadAddr < g_playEnd) { if (!g_bufReady[b]) audioFillBuf(b);
audioFillBuf(b);
}
} }
}
// Track finished — both buffers drained and no data left // ---- End of track: timer-based stop ----
if (!g_bufReady[0] && !g_bufReady[1] && g_nextReadAddr >= g_playEnd) { // g_trackDoneMs is armed 100ms after first silence fill.
audioStop(); // By then both DMA buffers have definitely cycled through silence.
if (g_numTracks > 0) { if (g_playing && g_trackDoneMs != 0 && millis() >= g_trackDoneMs) {
g_currentTrack = (g_currentTrack + 1) % g_numTracks; g_trackDoneMs = 0;
audioStart(g_currentTrack); audioStop();
} if (g_numTracks > 0) {
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
audioStart(g_currentTrack);
} }
} }
@@ -1217,18 +1206,20 @@ void loop() {
btnLastMs[btn] = millis(); btnLastMs[btn] = millis();
Serial.print("BTN"); Serial.println(btn); Serial.print("BTN"); Serial.println(btn);
g_lastActivity = millis(); g_lastActivity = millis();
/*
switch (btn) { switch (btn) {
case 1: // Play / Pause case 1: // Play / Pause
case 2: case 2:
case 3: case 3:
case 4:
case 5:
if (g_playing) { if (g_playing) {
audioStop(); audioStop();
} else if (g_numTracks > 0) { } else if (g_numTracks > 0) {
audioStart(g_currentTrack); audioStart(g_currentTrack);
} }
break; break;
/*
case 4: // Next track case 4: // Next track
if (g_numTracks > 0) { if (g_numTracks > 0) {
g_currentTrack = (g_currentTrack + 1) % g_numTracks; g_currentTrack = (g_currentTrack + 1) % g_numTracks;
@@ -1242,7 +1233,7 @@ void loop() {
if (g_playing) { audioStop(); audioStart(g_currentTrack); } if (g_playing) { audioStop(); audioStart(g_currentTrack); }
} }
break; break;
*/
case 6: // Motor toggle case 6: // Motor toggle
if (g_motorOn) motorStop(); else motorStart(g_motorSpeed); if (g_motorOn) motorStop(); else motorStart(g_motorSpeed);
break; break;
@@ -1266,7 +1257,7 @@ void loop() {
audioStop(); audioStop();
motorStop(); motorStop();
break; break;
}*/ }
waitButtonRelease(btn); waitButtonRelease(btn);
} }