TinyML Arduino Audio Capture: Detect When Inference Falls Behind the Sample History
A speech sketch can keep returning successful audio reads after inference has fallen behind capture. The requested time range may already have been overwritten, while modulo indexing still returns plausible PCM values from the same addresses.
This analysis is limited to the Nano 33 BLE provider in the archived TensorFlow Arduino examples at the pinned 2023 commit. The repository is archived; it is a concrete maintenance case, not a recommendation to adopt that library for a new product.
Interpret the timestamp as a sample-history coordinate
The examined Arduino audio provider increments its timestamp from the number of samples received. Capture positions and requested read positions are derived from milliseconds multiplied by samples per millisecond, then reduced modulo the history capacity.
Its backing array contains DEFAULT_PDM_BUFFER_SIZE * 16 int16 elements. The callback reads DEFAULT_PDM_BUFFER_SIZE bytes, equivalent to half that many int16 samples. Do not confuse the array's element count with the byte count passed to the PDM driver.
The audio abstraction promises neither an absolute clock origin nor exact timestamp granularity. Treat this coordinate as the provider's capture-progress estimate, not a wall-clock measurement of when sound reached the microphone.
A successful copy does not establish retained history
The provider's GetAudioSamples copies the requested interval through modulo indexing and returns success. It has no explicit check that the requested beginning is still retained or that the requested end has already arrived. Its comment assumes the consumer runs quickly enough.
A defensible wrapper needs a retained-range test before indexing. Express that test with a sufficiently wide sample counter: if L is the next completed sample position and N is the history capacity, valid samples lie in [max(0, L-N), L). A request beginning at S with count C must fit entirely inside that interval.
For an illustrative 4096-sample history at 16 kHz, 256 milliseconds are retained. At capture progress 1000 milliseconds, the oldest retained coordinate is 744 milliseconds. A request covering 730–760 milliseconds is invalid even though all its modulo addresses are inside the array. These are hypothetical values, not the archived board's measured capacity.

Carry failure into the feature consumer
The archived feature provider derives new slice positions from earlier processing time. Capping the number of generated slices does not itself rebase an obsolete starting coordinate to the retained history.
More importantly, that caller invokes GetAudioSamples without checking its returned status. It checks the reported sample count afterward. Adding an overrun error only inside the provider would therefore leave the consumer's control flow incomplete. Update both sides so that an unavailable interval prevents feature generation and classification from proceeding as though continuity were intact.
The model settings require 30-millisecond slices at 16 kHz: 480 samples. Also validate requested length against the output buffer and reject invalid or negative time inputs before conversion.
Test overwrite during the copy, not only before it
Capture continues while the main code copies history. A retained-range check made before a long copy can become stale during that copy. Add an ownership or sequence-validation scheme appropriate to the target; an unsynchronized timestamp read alone does not prove the copied samples form one coherent interval.
Exercise delays shorter than, near and longer than retention, and verify explicit gap reporting. Test missing future samples separately from overwritten past samples. After a gap, choose a deliberate restart or feature-history policy rather than splicing unrelated audio into a seemingly continuous spectrogram.
The archived provider's short PDM read path instead logs and halts indefinitely, which is a different failure from consumer lag. Keep the two diagnoses separate. No microphone or delayed-inference experiment was performed here; the proposed change is explicit time-range validity through the entire provider-to-feature call chain.