Files

72 lines
4.2 KiB
Markdown
Raw Permalink Normal View History

---
type: Workflow
title: Aufnahme-Zyklus (Push-to-Talk)
description: "Der Kern-Workflow von whisper-local: Hotkey drücken → Audio aufnehmen → Medien pausieren → Hotkey loslassen → transkribieren → Text einfügen → Medien fortsetzen. Sowie Mikrofon-Überwachung und Config-Reload."
tags: [workflow, push-to-talk, recording, transcription, media-pause, microphone-monitor]
---
# Aufnahme-Zyklus (Push-to-Talk)
Der zentrale Workflow wird durch die `App`-Klasse in [`__main__.py`](../../whisper_local/__main__.py) orchestriert. Die beiden Callbacks `on_press` und `on_release` bilden den vollständigen Zyklus.
## Push-to-Talk-Flow
```
Hotkey gedrückt (on_press)
1. Tray.set_state(RECORDING) — Icon wird rot
2. await media.pause() — alle Playing-Sessions pausieren
3. recorder.start() — sounddevice.InputStream öffnet
Hotkey losgelassen (on_release)
1. recorder.stop() — Stream schließen, Audio-Array zurück
2. await media.resume() — pausierte Sessions fortsetzen (im finally-Block)
3. Wenn audio == None → Tray WAITING, return
4. Tray.set_state(TRANSCRIBING) — Icon wird gelb
5. transcriber.transcribe(audio) — faster-whisper transkribiert lokal
6. await inserter.insert(text) — Text ins aktive Textfeld einfügen
7. Tray.set_state(WAITING) — Icon wird grau
```
### Mindestdauer-Filter
`Recorder.stop()` verwirft Aufnahmen kürzer als `min_duration` (Standard: 0,5 s). In diesem Fall gibt `stop()` `None` zurück und der Zyklus wird abgebrochen — keine Transkription, kein Einfügen. Das verhindert versehentliche Trigger bei kurzen Hotkey-Berührungen.
### Text-Einfügung mit Clipboard-Restaurierung
Beide Inserter-Backends (Wayland und Win32) folgen demselben Muster:
1. Alten Clipboard-Inhalt sichern.
2. Transkribierten Text ins Clipboard setzen.
3. `Ctrl+V` simulieren (ydotool unter Wayland, pynput unter Windows).
4. Kurz warten (`PASTE_DELAY = 0.2s`).
5. Alten Clipboard-Inhalt wiederherstellen (im `finally`-Block).
Dadurch bleibt der bisherige Clipboard-Inhalt des Nutzers erhalten. Details siehe [Plattform-Abstraktionen → Inserter](../domain/platform-abstractions.md#inserter).
## Medien-Pause während Aufnahme
Wenn `pause_media_during_recording = true` (Standard), pausiert der [MediaController](../domain/platform-abstractions.md#mediacontroller) alle aktiv wiedergebenden Sessions bei Aufnahmestart und setzt nur diejenigen fort, die er selbst pausiert hat. Der Circuit-Breaker deaktiviert die Funktion dauerhaft, wenn D-Bus (Linux) oder SMTC (Windows) nicht verfügbar ist.
## Mikrofon-Überwachung
Der `MicrophoneMonitor` läuft als Hintergrund-Task und überwacht Audio-Geräte-Änderungen:
- **Gerät entfernt**: Wenn das konfigurierte Mikrofon getrennt wird, wechselt `App._on_configured_microphone_missing` auf das Standard-Gerät, zeigt eine Desktop-Benachrichtigung via [notify-py](../../whisper_local/tray/_notification.py) und setzt eine Tray-Warnung (`set_warning("Mikrofon nicht gefunden")`).
- **Gerät hinzugefügt**: Wenn das konfigurierte Mikrofon wieder verfügbar wird, stellt `App._on_microphone_added` es wieder her, benachrichtigt den Nutzer und entfernt die Tray-Warnung.
- **Start-Check**: Beim Start meldet `PollMonitor` sofort, wenn das konfigurierte Gerät nicht gefunden wird.
Windows verwendet `IMMNotificationClient` (COM Core Audio API) für Event-basierte Benachrichtigungen mit automatischem Polling-Fallback. Linux verwendet den `PollMonitor` (2,5 s Intervall). Details siehe [Plattform-Abstraktionen → Mikrofon-Monitor](../domain/platform-abstractions.md#mikrofon-monitor).
## Config-Reload zur Laufzeit
`App._on_config_reload` wird vom [Einstellungs-Dialog](../../whisper_local/tray/_settings.py) aufgerufen und tauscht alle dynamischen Komponenten ohne Neustart aus:
1. Neue Config übernehmen.
2. Recorder neu instanziieren (neues Mikrofon/Sample-Rate).
3. Mikrofon-Monitor stoppen und neu starten.
4. Tray-Warnung zurücksetzen.
5. Alten Media-Controller fortsetzen, neuen erstellen.
6. Hotkey-Listener stoppen und neu starten (`_restart_hotkey`).
Der Hotkey-Restart benötigt ein kurzes `await asyncio.sleep(0.1)` zwischen Stop und Start, um den alten Listener sauber abzubauen.