5.4 KiB
04 Diktat (Whisper im Browser)
Überall dort, wo längerer Text entsteht – Notiz am Kontakt, Notizen an Lead und Unternehmen, Projektnotizen, Ausschreibungstext – sitzt ein Mikrofon-Knopf. Die Erkennung läuft vollständig im Browser des Benutzers.
Das Backend ist daran nicht beteiligt und sieht keine Audiodaten: kein API-Schlüssel, keine Kosten pro Minute, kein Datenabfluss. Aufnahmen werden nirgends gespeichert.
Die Umsetzung ist aus dem perinco CRM übernommen und technisch identisch – wer das eine kennt, findet sich im anderen zurecht.
Weg der Aufnahme
flowchart LR
MIC(("Mikrofon")) --> MR["MediaRecorder<br/>webm / ogg / mp4"]
MR --> AC["AudioContext + OfflineAudioContext<br/>16 kHz Mono PCM"]
AC --> W["Web Worker"]
W --> ONNX["Whisper als ONNX<br/>WebGPU, sonst WASM"]
ONNX --> T["Text"]
T --> F["Formularfeld<br/>angehaengt, nie ersetzt"]
HF["Hugging-Face-CDN"] -.->|"einmalig ~300 MB"| CACHE[("Cache-Speicher<br/>der Seite")]
CACHE --> ONNX
Die Erkennung liegt im Web Worker (src/lib/whisper.worker.ts), damit die
Oberfläche währenddessen nicht einfriert. Die ONNX-Laufzeit wird mit
ausgeliefert (Vite bündelt die .wasm, rund 21 MB unter dist/assets/), nur
die Modelldateien kommen beim ersten Laden vom CDN und bleiben danach im
Cache-Speicher der Seite.
Festlegungen
Es gibt bewusst nichts einzustellen (src/lib/whisper.tsx):
| Festlegung | Wert | Warum |
|---|---|---|
| Modell | onnx-community/whisper-large-v3-turbo |
beste Erkennung im Deutschen, auch bei Fachbegriffen („S4 HANA"); small verliert genau dort, tiny ist unbrauchbar |
| Genauigkeit | q4 |
q8 liefert auf der Grafikkarte Unsinn, fp16 blockiert den Tab, fp32 passt nicht in einen Speicherblock |
| Rechenweg | WebGPU, sonst WASM | automatisch; ohne Grafikbeschleunigung dauert es länger, funktioniert aber |
| Sprache | Deutsch | fest |
Lebenszyklus des Modells
stateDiagram-v2
[*] --> idle: Anmeldung
idle --> loading: "Sprachmodell laden" oder Cache vorhanden
loading --> ready: Worker meldet "ready"
loading --> error: Speicher, Abbruch, 2 min ohne Regung
ready --> idle: "Entladen"
ready --> idle: "Sprachmodell loeschen" (Cache geleert)
error --> loading: erneuter Versuch
- Liegt das Modell im Cache, stellt es sich beim Anmelden von selbst bereit. Einmal herunterladen genügt also.
- Ohne Cache passiert nichts von allein – ein Download von 300 MB startet nie unaufgefordert.
- Der Selbstversuch läuft genau einmal je Seitenaufruf: wer das Modell absichtlich löscht oder entlädt, will es nicht sofort wiederhaben.
- Vor dem Download bittet die Anwendung um dauerhaften Speicher
(
navigator.storage.persist). Chrome darf ablehnen; dann funktioniert alles genauso, nur ohne diese Zusicherung.
Hänger werden erkannt
Zwei Fristen, beide mit verwertbarer Meldung statt endlosem „Lädt …":
| Frist | Wann |
|---|---|
| 120 s ohne Regung des Workers | beim Laden – meist scheitert das Anlegen der ONNX-Sitzung am Speicher |
| max(45 s, 15 × Aufnahmedauer), höchstens 300 s | bei einer Erkennung |
In beiden Fällen wird der Worker beendet; ein neuer Versuch bekommt einen frischen.
Bedienung
Einstellungen → Diktat (für jede Rolle sichtbar, denn jeder richtet sein Gerät selbst ein):
- Sprachmodell laden – zeigt die tatsächliche Downloadgröße, bei Hugging
Face erfragt (
src/lib/whisperModels.ts), und einen Fortschrittsbalken. - Probe sprechen – kurz etwas diktieren und den erkannten Text ansehen.
- Sprachmodell löschen – entfernt es aus diesem Browser.
Danach steht in den Formularen ein Mikrofon-Knopf. Während der Aufnahme läuft die Zeit, daneben liegt ein Kreuz zum Verwerfen.
Der Knopf zeigt drei Zustände:
| Zustand | Was zu sehen ist |
|---|---|
| kein Mikrofon / kein HTTPS | gar nichts – ein toter Knopf wäre schlechter |
| Modell fehlt | Diktat einrichten, führt direkt in den Reiter |
| bereit | Mikrofon, dann laufende Zeit, dann Erkenne … |
Erkannter Text wird angehängt, nie ersetzt: in den WYSIWYG-Feldern als eigener Absatz am Ende, in den Textfeldern hinter dem Vorhandenen. Ins DOM des Editors zu schreiben wäre die Alternative – dann hinge das Ergebnis aber an der Schreibmarke, die während der Aufnahme längst woanders stehen kann.
Dateien
| Datei | Aufgabe |
|---|---|
src/lib/whisper.tsx |
Kontext: Zustand, Fortschritt, Cache-Erkennung, Fähigkeiten des Geräts |
src/lib/whisper.worker.ts |
Pipeline, Erkennung, Fehler auch außerhalb der await-Kette |
src/lib/dictation.tsx |
useRecorder – Mikrofon, Dauer, Abbruch |
src/lib/audio.ts |
webm/ogg/mp4 → 16 kHz Mono Float32 |
src/lib/whisperModels.ts |
Downloadgröße, Formatierung |
src/components/ui/Dictate.tsx |
DictateButton, DictateTextarea, appendParagraph |
src/components/settings/WhisperSettings.tsx |
der Reiter |
Abhängigkeit: @huggingface/transformers (^3.8.1).
Störungssuche
| Symptom | Ursache |
|---|---|
| Kein Mikrofon-Knopf zu sehen | kein MediaRecorder oder keine sichere Verbindung (HTTPS) |
| „Kein Zugriff aufs Mikrofon" | Berechtigung im Browser verweigert |
| Erkennung sehr langsam | keine WebGPU – der Reiter sagt es ausdrücklich |
| Modell nach Neustart wieder weg | privates Fenster oder Speicherbeschränkung; der Reiter weist darauf hin |
| „Der Speicher reicht … nicht" | zu wenig Arbeitsspeicher für die ONNX-Sitzung |