Direct answer
A capture timestamp and a buffer boundary describe different events. AudioRecord.getTimestamp returns an AudioTimestamp representing frame-delivery information at the earliest point available in the capture pipeline. AudioTimestamp.framePosition is a frame position relative to an assumed stream; nanoTime is the estimated capture time for that frame in the selected timebase. It is not defined as the end time of the most recently returned read buffer, so attaching it directly to a chunk without a frame-coordinate relation can shift every segment. AudioRecord.getTimestamp is API 24 and may return ERROR_INVALID_OPERATION when a timestamp is unavailable; update anchors only on success. AudioTimestamp.framePosition/nanoTime fields date from API 19, while MONOTONIC/BOOTTIME selection is API 24. Android maps those bases to System.nanoTime and elapsedRealtimeNanos, respectively. startRecording after stop resets the frame count; start a new epoch then. Record route or format changes, but do not assume they reset the counter. Read overloads return bytes, shorts, or floats, not frame counts. Convert successful data using the configured encoding and channel count; record incomplete tails or errors instead of dropping them silently. Maintain cumulative app-read frame ranges per chunk. A frame contains one sample per configured channel, but this app coordinate is not automatically AudioTimestamp's capture coordinate. A local time estimate is valid only after a capture-coordinate association is verified. Given a successful timestamp anchor (P, T), a chunk frame coordinate q that is proven to use the same capture origin, one uninterrupted recorder epoch, a matching clock base, and a stable sample rate R, the local estimate is t_est = T + (q - P) * 1e9 / R. This is a derived estimate, not an accuracy guarantee. If the first accepted read does not have a verified offset to capture framePosition, retain q and t_est as unknown instead of assuming the first read starts at frame zero. Timestamp/read deltas are diagnostic only when mapping and tolerance are known. Capture buffering, delayed reads, discarded data, and transforms can cause differences; one poll does not identify the cause. Compare repeated successful anchors with accepted read counts, return codes, route/recorder events, and controlled discontinuities. Distinguish scheduler delay, short read, route interruption, app discard, and unknown mismatch. Keep input-frame and transformed-output coordinates separate. Use two fixture tiers: pure PCM plus synthetic (P,T)/read ranges tests arithmetic only. A real-mic pulse replay can test capture, but its source-file frame label is not AudioRecord.framePosition; record the captured marker, route, method, and uncertainty. Use only a separately verified read-origin offset, never the nearest poll by default. If origin cannot be verified, timing stays unknown. Do not log real audio. COVE-ASR-02 describes sequential segmenting/merge, not timestamp support; no Cove implementation or device result is claimed.
A transcript segment needs a defensible media offset
An Android developer splits a live AudioRecord capture into PCM chunks for speech processing and later needs each transcript segment to point back to its approximate location in the recording. One test shows a time gap after a short read, but the app has only a display duration and does not know whether capture frames were delayed, dropped, or merely read later.
Decision
Pair successful AudioTimestamp anchors with a separately counted app-read frame timeline only when their origin, epoch, sample rate, and timebase have been verified. Treat unproven offsets as unknown and keep transform output frames separate.
Expected outcome
The report can map tested chunks to a bounded capture-time estimate or identify why mapping is unavailable. It does not turn a mismatch into proof of dropped audio or a timestamp-accuracy claim.
Evidence to collect
The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:
- Successful frame/time anchors with epoch and clock base
- Read-overload units and separate chunk frame ranges
- Controlled origin mapping, reset and unknown-offset fixtures
A release claim is valid only for the recorded configuration and acceptance set. Generalization requires additional records.
Implementation workflow
- Define the timestamp question and epoch. Record purpose, recorder ID, start/stop events, API level, sample format/channels, and the chosen MONOTONIC or BOOTTIME base. Create a new epoch only after a counter reset; log route/format changes without assuming reset.
- Capture only successful timestamp anchors. Poll getTimestamp at known points and store status, framePosition, nanoTime, and timebase together. Update the anchor only on SUCCESS; a failed query means unavailable, not permission to reuse stale fields as current time.
- Count frames from successful reads. Record each read overload and unit: bytes, shorts, or floats. Convert successful counts to complete frames using configured encoding/channels; mark short reads, errors, or incomplete tails. Keep cumulative app-read ranges separate from capture framePosition.
- Prove the read-to-capture offset before estimating. Establish an association between app frame coordinate q and capture position P for the same recorder epoch, stable sample rate, and clock base. Do not use the nearest timestamp poll as an unmeasured origin. Only then may t_est = T + (q - P) * 1e9 / R be used locally; otherwise keep mapping unknown.
- Investigate discontinuities with independent evidence. Compare repeated successful anchors with accepted read frames under a declared tolerance. Correlate mismatches with read results, route events, restart, resampling, and deliberate discards. One mismatch does not prove loss or identify its cause.
- Validate fixture tiers and privacy. Use two tiers: pure PCM plus synthetic P/T and read ranges tests arithmetic only. For real-mic pulse playback, record the captured marker, route, method, and uncertainty; source-file frame labels are not capture framePosition. Without verified origin, mark unknown. Never log real PCM or transcripts.
Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.
Acceptance criteria
- Each anchor records AudioRecord session epoch, getTimestamp status, framePosition, nanoTime, selected timebase, and sample-format context; failed queries never reuse stale timestamp fields. Epochs change only on verified counter resets, while route/format changes are logged separately.
- Successful read counts are converted from their actual byte/short/float units to complete frames using the configured format; app-read frame indices remain separate from capture framePosition until an offset is verified.
- Any frame-to-time formula is limited to a verified common capture origin, epoch, stable rate, and matching clock domain; otherwise the time mapping is unknown.
- A pure PCM/synthetic tuple test is arithmetic-only; source-file frame labels are not hardware capture positions. Real-mic mapping records its captured marker, route, method, and uncertainty, and remains unknown without verified origin evidence. A timestamp/read delta is not called a dropped-frame count without independent evidence.
Decision table
| Dimension | Choice or evidence | How to interpret it |
|---|---|---|
| Task duration clock | Monotonic or boottime endpoints for a named elapsed interval | Answers how much time passed; it does not locate an individual sample frame in the capture stream. |
| AudioTimestamp anchor | A successful framePosition/nanoTime pair in one capture epoch | Associates an estimated capture time with a frame coordinate; it is not a read-buffer boundary or exact device-accuracy promise. |
| App-read chunk index | Cumulative frames derived from successful AudioRecord read counts | Describes data delivered to the application; buffering or discarded data can make its origin differ from capture framePosition. |
| Transcription segment order | Sequential chunk IDs and merged text results | Preserves processing order but does not by itself retain source-time offsets. |
Failure patterns to prevent
- Attaching the latest timestamp to a read-buffer boundary
- Calling a timestamp/read mismatch a dropped-frame count
- Treating synthetic source frames as verified capture coordinates
Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.
Minimum reproducibility record
| Layer | Record |
|---|---|
| Device | Manufacturer, model, chipset, RAM class, operating-system build |
| Software | Application version and git commit |
| Model | Family, variant, revision, format, file length, hash, quantization |
| Runtime | Name, revision, requested backend, observed backend evidence |
| Workload | Fixture revision, input hash, prompt hash, output policy |
| Outcome | Completed, failed, cancelled, fallback, and privacy-safe diagnostics |
Release checklist
- ☐ The primary query is answered without an unsupported number.
- ☐ Every artifact and runtime is pinned.
- ☐ The representative task and failure policy are explicit.
- ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
- ☐ User-content and network boundaries are documented.
- ☐ Result wording applies only to the recorded configuration.
- ☐ The page links to raw method or evidence when results are added.
Sources and related evidence
- Cove Voice transcription
- Cove benchmark method
- Android AudioRecord API: getTimestamp and read operations
- Android AudioTimestamp API: framePosition, nanoTime, and timebases
- Android AudioFormat API: sample encoding and channel configuration
- Android SystemClock API: monotonic and boottime clocks
Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.
Frequently asked questions
Does AudioRecord.getTimestamp give the end time of my latest read buffer?
No. Android says the timestamp reflects frame delivery at the earliest point available in the capture pipeline; AudioTimestamp.nanoTime estimates capture time for framePosition. Establish the read-buffer offset separately.
Can I set the first successful read to frame zero?
Not without evidence. A pure PCM fixture with synthetic P/T values checks arithmetic only; its source-frame label is not a real capture position. If playing and recording a synthetic pulse, record the captured marker, route, method, and uncertainty. Do not calibrate to the nearest timestamp poll by default; if origin is unverified, leave mapping unknown.
Does a mismatch between timestamp and read counts prove frames were dropped?
No. It can reflect buffering, delayed reads, recorder resets, route changes, transformation, or data loss. Compare repeated successful anchors with read results and independent event records before classifying a gap.
Can I compare AudioTimestamp nanoTime to wall-clock dates?
Not directly. The call selects MONOTONIC or BOOTTIME, which correspond to documented monotonic Android clock domains. Store the base and compare only values with a compatible origin and lifetime; keep calendar time as a separate field.