Skip to content

fix(mobile): dictated speech is no longer lost on iOS - #60

Merged
pseudoseed merged 7 commits into
mainfrom
fix/dictation-after-typing
Nov 10, 2026
Merged

pseudoseed merged 7 commits into
mainfrom
fix/dictation-after-typing

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Problem

On TestFlight build 49, dictated speech was lost on iOS. A 30 second dictation came out as 3 words, text typed between dictations did not reach the draft, and once a dictation ended its recording was deleted, so nothing could be recovered.

Fix

  • Typing after dictation. Takes upstream's fix for typed text not reaching the draft after an in-app dictation, plus a test for a remounted composer editor.
  • Voice filter cut the speaker's own words. A stretch is now cut only when the voiceprint says it is someone else, it is more than 6 dB quieter than the speaker, and the speaker was heard 1.25 times as long as everything cut. A voice as loud as the speaker's stays.
  • Recording start. Recording no longer waits for Apple's speech model to prepare.
  • Interruptions. A call, Siri, a recorder error or backgrounding now transcribes what was captured instead of discarding it.
  • Recordings are kept. Every recording that started is moved to Caches/dictation-recordings when the recorder stops, before transcription. The last 5 stay, canceled ones included.
  • Transcribe again. A failed transcription offers "Transcribe again" on the floating pill and the composer banner, which runs the kept recording.
  • Recent recordings. Settings > Microphone > Model lists the kept recordings. Tapping one runs it through the model in use without the voice filter and shows the text with a Copy action.
  • Edited drafts. A transcript is dropped only when its draft is gone or belongs to another owner. A draft that was edited gets the words at its end.
  • 5 minute limit. Reaching the limit adds the words up to it and says so.

Fork only. Android keeps recordings and "Transcribe again" through the shared controller; it has no Recent recordings list.

Verification

  • vp run check:changed exited 0 on the head commit.
  • Voice filter harness (19 LibriSpeech readers, real models): speaker's words lost 53 of 2674 (2.0%) with a second talker and 0 alone; stranger's words kept 44 of 899 (4.9%).
  • iPhone 18 Pro simulator (iOS 27), dev client: a finished recording and a canceled one both land in the kept folder; after 6 recordings 5 remain; Recent recordings transcribed a real speech clip placed there and Copy worked; with a forced transcription failure, "Transcribe again" on the banner and on the pill re-ran the kept file and added the words to the draft.

Not verified: real speech through a microphone, a physical iPhone, an interruption or the 5 minute limit on a device, AirPods route changes, Android. Screenshots from the simulator pass are kept locally and are not attached.

Known limits

  • Kept recordings live in Caches, which iOS may purge when storage is tight.
  • "Transcribe again" tapped within milliseconds of the failure can report that another recording is active; a second tap works.
  • The voice filter can still cut the speaker's own words when all 3 conditions hold. Recent recordings recovers them.

Model: Claude Fable 5. Harness: Claude Code in PseudoCode.

🤖 Generated with Claude Code

wojas and others added 7 commits November 10, 2026 12:17
… iOS (pingdotgg#30139)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: maria-rcks <maria@kuuro.net>
(cherry picked from commit bd30817)
… holds

Co-Authored-By: Claude Code <noreply@anthropic.com>
…a headset

The filter judged every stretch of speech against the voice profile alone.
The profile is recorded once, somewhere quiet, so noise or another
microphone pushed the user's own speech past the limit and it was cut as
someone else's. A short opening stretch has a looser limit, so in the worst
case only the first words of a dictation survived.

A stretch the profile rejects is now also compared with the stretches it
accepted in the same recording, and kept when it matches them.

Measured on 8 Mini LibriSpeech speakers, half-minute recordings, own words
lost with the filter on: noise at 5 dB 9.2% before, 1.8% after; narrowband
3.4% to 0%; echo with noise 2.7% to 0%. Strangers' words kept are unchanged
(11.5%, 1.0%, 6.4%), and the clean mixed-voice numbers are unchanged (2.4%
own words lost, 4.8% strangers' words kept, 19 speakers).

Also adds a test that half a minute of speech in the phone's recording
format (AAC, 44.1 kHz, stereo) is transcribed in full by all 3 models.

Co-Authored-By: Claude Code <noreply@anthropic.com>
In a long dictation the filter cut stretches of the user's own speech
whenever their voiceprint drifted from the profile. A voiceprint alone
now never cuts anything. A stretch goes only when it also is more than
6 dB quieter than the user (further from the phone) and the user was
heard for at least 1.25 times as long as everything cut. A voice as
loud as the user's stays.

Measured on 19 LibriSpeech readers: the speaker's words lost when
speaking alone are 0 in noise, narrowband and echo (was up to 41.5% for
one reader), and a stranger 12 dB under the speaker keeps 4.9% of their
words.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The microphone waited for Apple to prepare its model before it recorded,
so words spoken in that gap were never captured. Preparation now runs
while the user talks and is awaited when the recording is transcribed.
A language Apple cannot take is reported then; a downloaded model that
was still loading takes the recording instead.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A call, Siri, a recorder error or leaving the app threw the whole
recording away. expo-audio also pauses the recorder for an audio
interruption without an event and never resumes it, so the app kept
showing a recording that captured nothing more.

An interrupted recording is now transcribed and its words are added,
with a notice that it stopped early. The metering loop notices a
recorder that stopped capturing and finishes the same way.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…overed

Every recording was deleted the moment its dictation ended, so a failed,
empty or wrong transcript could not be recovered, and a draft that changed
during the dictation dropped the whole transcript.

- The recorder's file is moved to a kept folder as soon as recording stops,
  before transcription. The last 5 stay, canceled ones included.
- A transcription that throws offers "Transcribe again", which runs the kept
  file and adds it to the draft.
- Settings > Microphone > Model lists the recent recordings. Each runs
  through the model in use without the voice filter, with a copy button.
- A transcript for an edited draft goes at its end. It is dropped only when
  the draft is gone.
- The 5-minute limit says so, and a notice after a commit offers no retry
  at an old caret.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Nov 10, 2026
@pseudoseed
pseudoseed merged commit e7acffd into main Nov 10, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants