mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-07 09:12:44 +02:00
redid commit e0e011e and also added Vendor ESP32-audioI2S into lib/ with ES8311 APLL + set-once I2S edits
This commit is contained in:
@@ -2841,6 +2841,7 @@ void loop() {
|
||||
digitalWrite(41, LOW);
|
||||
#endif
|
||||
notifTonePlaying = false;
|
||||
ui_task.setNotifAudioActive(false);
|
||||
notifFadingOut = false;
|
||||
notifMuted = false;
|
||||
notifDurationMs = 0;
|
||||
@@ -2943,9 +2944,10 @@ void loop() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ramp volume up to max
|
||||
audio->setVolume(21);
|
||||
// Ramp volume up to playback level
|
||||
audio->setVolume(17);
|
||||
notifTonePlaying = true;
|
||||
ui_task.setNotifAudioActive(true);
|
||||
notifFadingOut = false;
|
||||
notifMuted = false;
|
||||
notifStartMs = millis();
|
||||
|
||||
@@ -376,8 +376,61 @@ private:
|
||||
return String(AB_BOOKMARK_FOLDER) + "/" + base + ".bmk";
|
||||
}
|
||||
|
||||
// True when the current folder is the music library (/audiobooks/music or any
|
||||
// subfolder). Music files are played without bookmarks: no position is saved
|
||||
// or restored, and any stale bookmark for the file is deleted when it opens.
|
||||
bool isMusicPath() const {
|
||||
String musicRoot = String(AUDIOBOOKS_FOLDER) + "/music";
|
||||
return _currentPath == musicRoot || _currentPath.startsWith(musicRoot + "/");
|
||||
}
|
||||
|
||||
// Compute WAV playback duration (seconds) from the file header by reading the
|
||||
// fmt chunk's byte rate and the data chunk's size. Returns 0 if the header
|
||||
// can't be parsed (caller falls back to the during-playback bitrate estimate).
|
||||
uint32_t parseWavDurationSec(File& f) {
|
||||
if (!f) return 0;
|
||||
f.seek(0);
|
||||
uint8_t hdr[12];
|
||||
if (f.read(hdr, 12) != 12) return 0;
|
||||
if (memcmp(hdr, "RIFF", 4) != 0 || memcmp(hdr + 8, "WAVE", 4) != 0) return 0;
|
||||
|
||||
uint32_t byteRate = 0;
|
||||
uint32_t dataSize = 0;
|
||||
while (f.available() >= 8) {
|
||||
uint8_t ch[8];
|
||||
if (f.read(ch, 8) != 8) break;
|
||||
uint32_t sz = (uint32_t)ch[4] | ((uint32_t)ch[5] << 8) |
|
||||
((uint32_t)ch[6] << 16) | ((uint32_t)ch[7] << 24);
|
||||
if (memcmp(ch, "fmt ", 4) == 0) {
|
||||
uint8_t fmt[16];
|
||||
uint32_t toRead = sz < 16 ? sz : 16;
|
||||
if ((uint32_t)f.read(fmt, toRead) != toRead) break;
|
||||
// byteRate is bytes 8-11 of the fmt body (after format, channels, rate)
|
||||
byteRate = (uint32_t)fmt[8] | ((uint32_t)fmt[9] << 8) |
|
||||
((uint32_t)fmt[10] << 16) | ((uint32_t)fmt[11] << 24);
|
||||
if (sz > toRead) f.seek(f.position() + (sz - toRead));
|
||||
if (sz & 1) f.seek(f.position() + 1); // chunks are word-aligned
|
||||
} else if (memcmp(ch, "data", 4) == 0) {
|
||||
dataSize = sz;
|
||||
break; // have what we need
|
||||
} else {
|
||||
f.seek(f.position() + sz + (sz & 1)); // skip other chunks (word-aligned)
|
||||
}
|
||||
}
|
||||
if (byteRate == 0 || dataSize == 0) return 0;
|
||||
return dataSize / byteRate;
|
||||
}
|
||||
|
||||
void loadBookmark() {
|
||||
String path = getBookmarkPath(_currentFile);
|
||||
if (isMusicPath()) {
|
||||
// Music files don't use bookmarks — delete any stale one and start at 0.
|
||||
if (SD.exists(path.c_str())) {
|
||||
SD.remove(path.c_str());
|
||||
digitalWrite(SDCARD_CS, HIGH);
|
||||
}
|
||||
return;
|
||||
}
|
||||
File f = SD.open(path.c_str(), FILE_READ);
|
||||
if (!f) return;
|
||||
|
||||
@@ -394,6 +447,7 @@ private:
|
||||
|
||||
void saveBookmark() {
|
||||
if (!_bookOpen || _currentFile.length() == 0) return;
|
||||
if (isMusicPath()) return; // Music files don't use bookmarks
|
||||
|
||||
if (!SD.exists(AB_BOOKMARK_FOLDER)) {
|
||||
SD.mkdir(AB_BOOKMARK_FOLDER);
|
||||
@@ -973,6 +1027,11 @@ private:
|
||||
int dot = base.lastIndexOf('.');
|
||||
if (dot > 0) base = base.substring(0, dot);
|
||||
strncpy(_metadata.title, base.c_str(), M4B_MAX_TITLE - 1);
|
||||
// WAV: exact duration is in the header, so show it at open.
|
||||
if (lower.endsWith(".wav")) {
|
||||
uint32_t wavSec = parseWavDurationSec(file);
|
||||
if (wavSec > 0) _durationSec = wavSec;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
@@ -1626,7 +1685,10 @@ public:
|
||||
// auto-refresh once per minute (for time/progress updates). Key presses
|
||||
// always trigger immediate refresh regardless of this interval.
|
||||
// When paused or stopped, refresh every 5s as normal.
|
||||
if (_isPlaying && !_isPaused) return 60000; // 1 min between auto-refreshes
|
||||
if (_isPlaying && !_isPaused) {
|
||||
if (_durationSec == 0) return 1000; // refresh quickly until duration resolves
|
||||
return 60000; // then back to once-a-minute (avoids stutter)
|
||||
}
|
||||
return 5000;
|
||||
}
|
||||
|
||||
|
||||
@@ -2019,7 +2019,9 @@ if (curr) curr->poll();
|
||||
// Defer display refresh while BLE is actively transferring contacts.
|
||||
// E-ink partial update blocks for ~820ms, stalling the BLE send queue
|
||||
// and adding ~1.6s of dead time to a full contact sync.
|
||||
if (_serial != NULL && _serial->hasPendingData()) {
|
||||
if (_notifAudioActive) {
|
||||
_next_refresh = millis() + 200; // Defer e-ink refresh during notif tone (SPI bus contention)
|
||||
} else if (_serial != NULL && _serial->hasPendingData()) {
|
||||
_next_refresh = millis() + 500; // Re-check in 500ms
|
||||
} else {
|
||||
// Sync dark mode with prefs (settings toggle takes effect here)
|
||||
|
||||
@@ -51,6 +51,7 @@ class UITask : public AbstractUITask {
|
||||
GenericVibration vibration;
|
||||
#endif
|
||||
unsigned long _next_refresh, _auto_off;
|
||||
bool _notifAudioActive = false; // True while a notification tone plays; defers e-ink refresh (SPI bus contention)
|
||||
unsigned long _kb_flash_off_at; // Keyboard flash turn-off timer
|
||||
#ifdef HAS_4G_MODEM
|
||||
bool _incomingCallRinging; // Currently ringing (incoming call)
|
||||
@@ -221,6 +222,7 @@ public:
|
||||
#endif
|
||||
void showAlert(const char* text, int duration_millis) override;
|
||||
void forceRefresh() override { _next_refresh = 100; }
|
||||
void setNotifAudioActive(bool active) { _notifAudioActive = active; }
|
||||
void showBootHint(bool immediate = false); // Show navigation hint overlay on first boot
|
||||
void dismissBootHint(); // Dismiss hint and save preference
|
||||
bool isHintActive() const { return _hintActive; }
|
||||
|
||||
@@ -183,7 +183,6 @@ build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter}
|
||||
lib_deps =
|
||||
${LilyGo_TDeck_Pro_Max.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6
|
||||
bitbank2/JPEGDEC
|
||||
|
||||
; MAX + WiFi companion (WiFi app bridging — no BLE, higher contact limit)
|
||||
@@ -212,7 +211,6 @@ build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter}
|
||||
lib_deps =
|
||||
${LilyGo_TDeck_Pro_Max.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6
|
||||
bitbank2/JPEGDEC
|
||||
|
||||
; MAX standalone (no BLE/WiFi — maximum battery life, LoRa mesh only)
|
||||
|
||||
Reference in New Issue
Block a user