Files

5.8 KiB
Raw Permalink Blame History

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: Klick auf "Sprachmodell laden" / "Diktat aktivieren"
    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
  • Es lädt nichts von selbst – weder der Download noch das Aktivieren aus dem Cache. Beides braucht einen Klick.
  • Der Grund ist gemessen, nicht vermutet: das Anlegen der ONNX-Sitzung übersetzt hunderte Shader und legt dabei Zeiger und Darstellung für einige Sekunden still. Lief das beim Anmelden von selbst, sah die ganze Anwendung aus, als hinge sie – und zwar genau dann, wenn jemand gerade eine Lead-Kachel ziehen wollte.
  • Liegt das Modell im Cache, heißt der Knopf Diktat aktivieren und der Download entfällt.
  • 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):

  1. Sprachmodell laden – zeigt die tatsächliche Downloadgröße, bei Hugging Face erfragt (src/lib/whisperModels.ts), und einen Fortschrittsbalken.
  2. Probe sprechen – kurz etwas diktieren und den erkannten Text ansehen.
  3. 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