r/speechtech

TTS/STT can't tell "wind" from "wind" — how do you handle heteronyms in a pronunciation-teaching app?

I'm building a vocabulary-learning app in Flutter where hearing and saying the word correctly is the product, not a nice-to-have. I've hit a problem I can't design around and I'd rather ask than keep patching.

The stack

  • Flutter, ~1,600 words live across EN/ES/PT/IT/FR
  • TTS: ElevenLabs (eleven_multilingual_v2) called through a Supabase Edge Function so the key never ships in the client
  • Every clip cached server-side once per (text, language), shared across all users — so a given string is synthesized exactly once, ever
  • Cached again on-device (150MB LRU) so replays are instant and offline
  • flutter_tts as fallback behind a 2.5s timeout so playback never goes silent
  • STT: speech_to_text for a pronunciation-practice screen — hear the word, say it, get graded

The problem: heteronyms, in both directions

Output. "Wind" (moving air) and "wind" (to coil) are the same string and different sounds. TTS picks one reading and commits. My word library actually knows which sense is on screen — every entry carries a part of speech — but there's no API surface to hand that over. ElevenLabs pronunciation dictionaries are exact-string, case-sensitive, and have no POS or context scoping, so one spelling gets one entry and the second sense is unreachable. Phoneme tags do exist, but per the docs only on eleven_flash_v2 and v3 — not the multilingual model I'm on, and switching models means re-synthesizing the whole cache and losing voice identity across five languages.

Input. This is the part that actually bothers me. The practice screen normalizes the transcript and Levenshtein-scores it against the target. But STT returns orthography — say either reading of "wind" and the transcript is "wind" either way. A learner who mispronounces it scores full marks. The feature is structurally incapable of catching the error it exists to catch.

What I've tried

Respelling the audio-only string before it reaches the engine — the screen text is never touched. wind(noun) → winned, wind(verb) → wined, read(past) → red, and so on. This is basically ElevenLabs' own recommended "alias" workaround and it works for the ~8 vowel-shift pairs I've mapped. Side benefit: since my cache key is a hash of (lang + text), two senses naturally get two cache entries.

It fails in three ways:

  1. Stress-shift pairs. REcord/reCORD, PREsent/preSENT, CONtent/conTENT. Respelling can't encode stress, and I haven't found a trick spelling that does.
  2. Monolingual. It's an English orthography hack. Nothing about it transfers to ES/PT/IT/FR, all of which have their own homographs.
  3. Manual. Hand-curated table. Doesn't scale to a few thousand words.

What I'm actually asking

  1. Is there a TTS API that accepts a sense/POS hint, or per-request phonemes, on a multilingual model? Or does everyone route heteronyms to a separate English-only model and eat the voice mismatch?
  2. If IPA is the only real answer — has anyone found v3-class IPA reliable enough in production? The docs quote 80–90% consistency, which for a teaching app means the wrong pronunciation ships to a learner one time in eight.
  3. For stress-shift specifically: any orthographic trick that works, or is phoneme-level control genuinely the only path?
  4. On the STT side — is there a mobile-viable way to get phonemes rather than words? I've looked at wav2vec2 phoneme-CTC or a forced aligner with GOP scoring via ONNX on-device, but I don't know if that's realistic on a mid-range phone or if I'm about to spend a month learning that it isn't. Whisper doesn't help; it also returns orthography.
  5. The unglamorous option: detect heteronyms and simply disable pronunciation scoring for them, with an honest note to the user. Is that what shipped apps actually do?

If you've built pronunciation feedback into anything real, I'd love to know where you drew the line between "graded properly" and "good enough." Happy to share code for any of the above.

reddit.com
u/Fair_Expression_3291 — 2 days ago
▲ 77 r/speechtech+12 crossposts

I created a RecognitionService that handles system-wide voice input fully on-device (no Google, no network)

Most voice input on Android - SpeechRecognizer.createSpeechRecognizer(context) calls — gets routed to Google's network-backed recognizer. I wanted that path to run locally, so I wrote one.

The service hooks the framework's SpeechRecognizer API. Once it's set as the default, any app calling createSpeechRecognizer(context) (no ComponentName) ends up in our pipeline and gets back transcription that never left the device. Pipeline is Silero VAD + Parakeet TDT v3 (114 languages, ~890 MB INT8) on ONNX Runtime with NNAPI.

Honest caveat: Gboard, Samsung Keyboard, and Google Assistant ship their own recognizers and skip the system default. So the default-IME voice button on most phones won't go through this. What does: accessibility tools, custom dictation UIs, and anything calling the framework API directly.

Models download on first use (~1.2 GB) via a foreground WorkManager job so it survives backgrounding. After that, fully offline.

Setup + demo APK: github.com/soniqo/speech-android

audio.soniqo:speech:0.0.9 on Maven Central

Library:

Happy to answer questions about the binder lifecycle, the foreground worker setup, or why SpeechRecognizer is such a tarpit of edge cases.

u/ivan_digital — 6 days ago

Faster alternatives to Pyannote on Whisper?

I am running Faster Whisper on CPU only and get good running times with about 2.5 min for 60 min sound with Whisper Base. With Pyannote for diarization the rate is about 0.9 times the sound length, aka 54 min for 60 min sound.

That is terribly slow compared to the transcription without Payannote.

Are there any faster alternatives out there, or hacks to make Payannote run faster with Whisper?

reddit.com
u/bidutree — 6 days ago
▲ 1.3k r/speechtech+1 crossposts

Hacked and debloated an Echo Dot 2 (local LLM + local Speech recognition)

Code and instructions available here: https://github.com/albertoZurini/echo-dot-2-playground

Hello there!

After a few days of playing around, with a lot of help from Gemini and GPT, I was able to successfully debloat an Echo Dot 2 from most of Amazon's services and let it run speech to text locally with Sherpa-ONNX and a small LLM through llama.cpp.

The first step was rooting it by following the procedure on XDA. There is a link to the XDA thread in the GitHub repo along with a step-by-step guide for the rest of the setup. Once I had root access I pulled the APKs of some of the system applications, especially SpeechInteractionManager which contains the wake word and speech interaction code, plus the native audio and wake word libraries it loads.

At first I wanted to do something similar to what the Wyoming team did, which was intercepting the wake word from FireOS services through logcat. That worked as a quick proof of concept but it gave me a lot of limitations. The Amazon speech service would still be running in the background and would still own parts of the audio pipeline. Volume handling was also not just a normal Android volume-key event. The FireOS services receive the hardware button events, change the audio stream volume and control the LED ring feedback. This meant that even if I reacted to the wake word from logcat, I was still relying on a large part of Amazon's stack underneath.

That is why I decided to do a more complete reverse engineering pass. The APK included native `.so` libraries such as `libwakewordmanager.so`, `libwakewordmanageraudiostream.so` and the newer `libwakewordserver_jni.so`. Some of the older libraries turned out to be compatibility stubs on this firmware. The useful path was the newer native wake word server, which loads Pryon and creates the native audio recorder. I also decompiled the Java code around `AudioStreamProviderService`, `AudioRecordStrategy` and `NativeWakeWordServiceCore` to understand how the pieces connect.

The main reason for doing the full reverse was to understand how these apps connect to the hardware and communicate with each other. It turns out there are two separate pieces that matter: one service for the native wake word detector and another service for speech processing and the rest of the assistant. I ended up running my own wake word service alongside my custom assistant. They communicate through an explicit Android service intent. Logcat is only used for diagnostics now.

I had done some prior research and saw that with 512 MB of RAM and a theoretical maximum memory bandwidth of about 5 GB/s this device might be able to run small LLMs locally. It turns out it can. The original TinyStories 28M model was not directly usable with llama.cpp because of its architecture, so I used an architecturally equivalent 25M LLaMA2 TinyStories model in GGUF format. Through llama.cpp it reaches around 7 tokens per second during prefill and around 4 tokens per second during decoding. This is roughly the kind of model that has also been used in ESP32 experiments.

I also tried the larger `MobileLLM-125M-Q4_K_M.gguf` model but prefill plus generation took around 20 seconds which is too slow for this device. Even with the smaller model I was able to trigger some simple tools by voice, for example turning on the light or playing a sound.

When the wake word is detected I can stop the detector process while the assistant is processing the request to save CPU. The detector uses around 20% CPU when it is active. This would have been much harder to control cleanly if I had kept the original FireOS speech services running.

I am also able to intercept all the hardware buttons. I assigned playing a sample WAV file to the action button. The volume keys change the music stream volume, play a tone and show visual feedback using the LEDs. The mute button works through the hardware microphone mute integration as well.

There is still a lot to improve, especially around making the assistant more useful and reducing the startup and response time, but it is pretty interesting to see this little device running an entirely local voice pipeline after removing most of the original services. I also want to try running openWakeWord/Porcupine to see if they'd be using less CPU than the stock Amazon's library.

The code and the reverse engineering notes are here: https://github.com/albertoZurini/echo-dot-2-playground

I would be interested to hear if anyone has tried something similar with other Echo devices or with the older FireOS speech components.

u/alberto_zurini — 13 days ago
▲ 13 r/speechtech+3 crossposts

EdgeSpeech: local speech processing for React Native

A cool tool for adding speech to your React Native app without having to touch low-level code.

github.com
u/trolleycrash — 9 days ago
▲ 18 r/speechtech+3 crossposts

I launched a YouTube Transcript API for AI/video apps

I built a REST API that extracts YouTube transcripts, metadata, available languages, and supports batch processing.

It is mainly for developers building:
- AI video summarizers
- YouTube-to-blog tools
- SEO/content research tools
- EdTech products
- RAG pipelines using video content

Endpoints include:
- GET /api/transcript
- GET /api/metadata
- GET /api/languages
- POST /api/batch

It is published on RapidAPI with a free plan:
https://rapidapi.com/dtech4099/api/youtube-transcript27

Docs:
https://youtube-trascript-api.vercel.app/docs

I’m looking for feedback from builders who work with YouTube/video content.
u/Significant_Sail_722 — 14 days ago

Voice agent throws away underlying tone and speaker-features, how's that accounted and handled downstream? if it's not captured.

The moment you transcribe to text, you lose how it was said. "I think… yeah, I can pay the 4,500 by the 15th" becomes clean text, but the hesitation before the yes, the stress in the voice, and whether it's even the same speaker are gone. Those are the signals that tell you whether to trust the commitment, escalate, or verify identity. Is anyone keeping the paralinguistic layer (hesitation, emotion, speaker identity) as structured data instead of dropping it at the mic, and what do you do with it downstream?

Moreover end-2-end Duplex models limits it to trained data scenarios without no transparency.

reddit.com
u/Working_Hat5120 — 10 days ago
▲ 7 r/speechtech+2 crossposts

I built VoxFlow: A free, 100% local on-device Wispr Flow alternative for macOS (Open Source)

Like many of you, I loved the concept of AI voice dictation tools like Wispr Flow, but I didn't want my microphone audio sent to third-party cloud servers or pay a monthly subscription.

So I built VoxFlow — a native, private macOS menu bar app that transcribes your speech locally and automatically pastes formatted, grammar-cleaned text into whichever app you are using.

Key Features

  • 100% Private & Offline: Transcribes locally using Apple Speech and cleans up text using Apple Intelligence (FoundationModels). Zero cloud API keys required.
  • Global Hotkey Triggers: Double-tap the Fn (Globe) key or press Option + Space anywhere on macOS to start dictating.
  • Hands-Free Auto-Paste: Pausing for 1.5 seconds automatically stops recording, formats the text, and pastes it into your focused text field.
  • Non-Activating Floating HUD: Displays real-time audio waveform and streaming transcript without stealing focus from your active document.
  • 100% Free & Open Source: No subscriptions, no ads, no telemetry ($0 forever).

Downloads & Links

System Requirements

  • macOS 26.0 or later (Apple Silicon M1/M2/M3/M4+)
  • Apple Intelligence enabled in System Settings

I'd love your feedback, bug reports, or feature requests!

u/AdditionSoft2109 — 10 days ago
▲ 2 r/speechtech+1 crossposts

fix dogshit latency and robotic wrapper behaviour

voice implementations rn generally fall into two buckets:

  1. laggy and robotic api wrappers
  2. speech models that are fast, but lack memory and state controls

by building a cascaded stack (deepgram nova-3 → claude haiku 4.5 → elevenlabs flash v2.5), you can keep full control over tool calls and memory, allowing latency reduction. some techniques ive used in my side projects:

  • pre-warm anthropic's ephemeral prompt cache while the phone rings
  • persistent websocket handshakes and http/2 pool priming on ring
  • neural turn-detection with false-interruption resumption (a cough won't kill the tts buffer)
  • dual-store memory (sql facts + temporal graph) mapped into a ~300-token prompt snapshot
  • proactive outbound scheduling that wakes a killed ios app via apns voip push -> callkit

synthetic ci gates hit p50 ≈ 973ms, though live networks push us to ~3.7s right now (stt and tts ttfb are the real boss fights). Judge our results yourself at getfriendo.app/launch

reddit.com
u/crashcody — 11 days ago

parakeet.wgsl – Fast, accurate ASR in the browser, via raw WebGPU & SIMD WASM

High-performance inference of NVIDIA's Parakeet TDT 0.6B V2 English transcription model, in the browser.

Check out the live demo: https://parakeet.narcotic.sh/

A fully custom, dependancy-free implementation with raw WebGPU compute shaders and SIMD WebAssembly audio frontend.

1 hour of audio transcribed in 20 seconds (Apple M5, Google Chrome 151.0.7922.72).

Check out the source and use in your own projects!

https://github.com/narcotic-sh/parakeet.wgsl

https://www.npmjs.com/package/parakeet.wgsl

This might be the first instance of fast & accurate transcription running locally in the browser. So long as your device has a GPU and can run a WebGPU-capable browser, parakeet.wgsl will run on it.

And as WebGPU transpiles to practically any GPU, this also opens up the path to getting parakeet.wgsl running offline / outside of the browser, via Dawn or wgpu, so that fast, accurate transcription can be brought to offline programs, with GPU acceleration and support for nearly all hardware/devices.

Interested in what you guys think about about this project.

And lastly, I'm looking for a job. If you like my work and think I'd be a good fit for your team, I'd love to chat. I've done some other work too that you can find at https://hamzaq.com

Cheers, everyone. Enjoy fast, cross-platform GPU-accelerated local transcription, right in the browser!

reddit.com
u/hamza_q_ — 12 days ago
▲ 3 r/speechtech+1 crossposts

Anyone aware of a commercially-viable retrain of Omnivoice?

So, Omnivoice's abilities are incredible, but given the training set is CC BY NC, the model is not actually usable for commercial use which is very annoying.

I've noticed some orgs doings retrains on commercially viable datasets for other models.

Interested if anyone is busy doing one of these for Omnivoice? It's quite a pricey exercise so hoping the cool kids are on it

reddit.com
u/deepaurorasky — 14 days ago