feat: inline timestamp markers for offline audio (firmware)#5571
feat: inline timestamp markers for offline audio (firmware)#5571
Conversation
Split from #4851 - firmware changes only. Adds inline 0xFF timestamp markers in the offline audio stream, exposes recording start time via BLE, and flushes storage buffer on shutdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR injects inline Key issues found:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant MIC as Microphone / TX Queue
participant PUSH as pusher thread
participant SD as SD Worker Thread
participant BLE as BLE Client
MIC->>PUSH: read_from_tx_queue()
alt BLE connected & subscribed
PUSH->>PUSH: needs_timestamp_marker = true
PUSH->>BLE: push_to_gatt()
else No BLE connection (offline mode)
alt needs_timestamp_marker == true
PUSH->>PUSH: write_timestamp_to_storage()<br/>[0xFF | UTC epoch 4B]
PUSH->>PUSH: needs_timestamp_marker = false
end
PUSH->>PUSH: set_recording_start_time(now)
PUSH->>PUSH: write_to_storage() → storage_temp_data[]
alt buffer_offset == MAX_WRITE_SIZE
PUSH->>SD: write_to_file(storage_temp_data, MAX_WRITE_SIZE)
end
end
note over BLE: BLE storage characteristic read
BLE->>SD: storage_read_characteristic()
SD-->>BLE: [file_size: u32 | offset: u32 | recording_start_time: u32]
note over PUSH,SD: On shutdown (button press)
PUSH->>SD: storage_flush_buffer() → write_to_file(remaining bytes)
PUSH->>SD: app_sd_off()
Last reviewed commit: 048ca41 |
| if (needs_timestamp_marker) { | ||
| write_timestamp_to_storage(); | ||
| needs_timestamp_marker = false; | ||
| } |
There was a problem hiding this comment.
Timestamp marker flag cleared even when write is skipped
needs_timestamp_marker is set to false unconditionally, even though write_timestamp_to_storage() returns early (without writing anything) when get_utc_time() == 0.
If the RTC has not been synced at the moment recording starts, the marker is silently skipped and the flag is cleared. When time becomes available on a later audio frame, the flag remains false so no marker is ever inserted into that recording session. This means the entire offline recording could go without any timestamp markers, defeating the purpose of the feature.
The flag should only be cleared after a marker is actually written:
| if (needs_timestamp_marker) { | |
| write_timestamp_to_storage(); | |
| needs_timestamp_marker = false; | |
| } | |
| if (needs_timestamp_marker) { | |
| if (get_utc_time() != 0) { | |
| write_timestamp_to_storage(); | |
| needs_timestamp_marker = false; | |
| } | |
| } |
Alternatively, have write_timestamp_to_storage() return a bool indicating success and only clear the flag on true.
| /** | ||
| * @brief Flush any pending offline storage buffer to SD card | ||
| */ | ||
| void storage_flush_buffer(void); |
There was a problem hiding this comment.
Linker error when CONFIG_OMI_ENABLE_OFFLINE_STORAGE is disabled
storage_flush_buffer() is declared here unconditionally and called in button.c unconditionally (line 389), but its definition in transport.c is inside the #ifdef CONFIG_OMI_ENABLE_OFFLINE_STORAGE block. Any build configuration that does not enable offline storage will fail with an undefined reference at link time.
The declaration should be guarded to match the definition:
| /** | |
| * @brief Flush any pending offline storage buffer to SD card | |
| */ | |
| void storage_flush_buffer(void); | |
| #ifdef CONFIG_OMI_ENABLE_OFFLINE_STORAGE | |
| /** | |
| * @brief Flush any pending offline storage buffer to SD card | |
| */ | |
| void storage_flush_buffer(void); | |
| #else | |
| static inline void storage_flush_buffer(void) {} | |
| #endif |
The call site in button.c can then remain as-is (the inline no-op is compiled away when offline storage is disabled).
| k_msleep(100); | ||
| #endif | ||
|
|
||
| storage_flush_buffer(); |
There was a problem hiding this comment.
Unsynchronised access to pusher thread's buffer
storage_flush_buffer() reads and zeroes buffer_offset and reads storage_temp_data, which are static variables in transport.c exclusively owned by the pusher thread (write_to_storage / write_timestamp_to_storage). This call is issued from the button handler without any lock or stopping the pusher first. If the pusher thread is still active (processing the last audio frames from the TX queue), a concurrent write to storage_temp_data / buffer_offset results in a data race that can corrupt the final flush.
Consider signaling pusher_stop_flag (or an equivalent mechanism) and waiting for the pusher thread to idle before calling storage_flush_buffer(), or guard the buffer accesses with a mutex/atomic check that is also respected inside write_to_storage.
#5572) ## Summary - Parses inline `0xFF` timestamp markers from the offline audio data stream so each recording segment has an exact UTC start time - Fixes WAL timing bugs: chunk size now uses actual codec FPS, and WAL duration derives from real frame count instead of a hardcoded 60-second assumption - Splits audio into correctly-timed WAL segments based on timestamp markers Split from #4851 — app changes only. See #5571 for the firmware counterpart. ### App changes - **sdcard_wal_sync.dart**: Gates all timestamp marker logic on firmware version `>= 3.0.16` using `_supportsTimestampMarkers()`. Old firmware uses the original legacy parsing path unchanged. New firmware uses marker-aware parsing in both BLE and WiFi sync paths, splitting audio into correctly-timed WAL segments. Also fixes chunk size from `sdcardChunkSizeSecs * 100` to `sdcardChunkSizeSecs * codec.getFramesPerSecond()` (new firmware path only). ### Firmware version gating - **Old firmware (< 3.0.16)**: Takes the `_readStorageBytesToFileLegacy()` path — identical to current `main` branch behavior. No marker detection, no new chunking logic. - **New firmware (>= 3.0.16)**: Takes the `_readStorageBytesToFileWithMarkers()` path — parses `0xFF` markers, splits segments by timestamp boundaries, uses device-provided recording start time. ## Test plan - [ ] Connect with old firmware (< 3.0.16) → verify BLE sync works identically to current behavior - [ ] Connect with new firmware (>= 3.0.16) → disconnect BLE → record ~1 min offline → reconnect → sync - [ ] Verify app shows WAL segments with correct timestamps from device-provided epochs - [ ] Test WiFi sync path with both old and new firmware 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
0xFFtimestamp markers in the offline audio data stream so each recording segment (between BLE disconnects or power cycles) has an exact UTC start timeinfo.txton SD card from 4 to 8 bytes to persist a recording start time alongside the read offsetSplit from #4851 — firmware changes only. See #5572 for the app counterpart.
Firmware changes
write_timestamp_to_storage()injects a 5-byte marker (0xFF+ 4-byte LE epoch) before the first offline audio frame of each segment.storage_flush_buffer()flushes pending data on shutdown.needs_timestamp_markerflag resets on each BLE connect.recording_start_timepersisted ininfo.txt(backward-compatible 8-byte format)[file_size, offset, recording_start_time]storage_flush_buffer()beforeapp_sd_off()inturnoff_all()Backward compatibility
Test plan
🤖 Generated with Claude Code