got stutter fixed
This commit is contained in:
@@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,13 +208,14 @@ 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,23 +1179,24 @@ 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.
|
||||||
|
// By then both DMA buffers have definitely cycled through silence.
|
||||||
|
if (g_playing && g_trackDoneMs != 0 && millis() >= g_trackDoneMs) {
|
||||||
|
g_trackDoneMs = 0;
|
||||||
audioStop();
|
audioStop();
|
||||||
if (g_numTracks > 0) {
|
if (g_numTracks > 0) {
|
||||||
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
|
g_currentTrack = (g_currentTrack + 1) % g_numTracks;
|
||||||
audioStart(g_currentTrack);
|
audioStart(g_currentTrack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Handle buttons ----
|
// ---- Handle buttons ----
|
||||||
// Per-button cooldown prevents phantom/stuck pins from re-firing
|
// Per-button cooldown prevents phantom/stuck pins from re-firing
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user