diff --git a/linux/blitztext/config.py b/linux/blitztext/config.py index a17f50c..35fbc6f 100644 --- a/linux/blitztext/config.py +++ b/linux/blitztext/config.py @@ -80,6 +80,8 @@ class Config: wakeword_enabled: bool = False wakeword_uri: str = "tcp://127.0.0.1:10400" wakeword_model: str = "okay_computer" + wakeword_sound_detected: str = "" # WAV played when the wakeword fires (speak now) + wakeword_sound_done: str = "" # WAV played when the command is captured # workflows workflows: list[Workflow] = field(default_factory=list) @@ -172,6 +174,8 @@ def load(path: Path = CONFIG_PATH) -> Config: wakeword_enabled=bool(ww.get("enabled", False)), wakeword_uri=ww.get("uri", "tcp://127.0.0.1:10400"), wakeword_model=ww.get("model", "okay_computer"), + wakeword_sound_detected=ww.get("sound_detected", ""), + wakeword_sound_done=ww.get("sound_done", ""), ) for entry in data.get("workflow", []): @@ -279,6 +283,8 @@ def save(cfg: Config, path: Path = CONFIG_PATH) -> None: "enabled": cfg.wakeword_enabled, "uri": cfg.wakeword_uri, "model": cfg.wakeword_model, + "sound_detected": cfg.wakeword_sound_detected, + "sound_done": cfg.wakeword_sound_done, }, "stt": {"active": cfg.stt_active}, "stt_engine": [ @@ -398,6 +404,11 @@ threshold = 0.82 # 0..1 fuzzy-match strictness (higher = stricter) enabled = false uri = "tcp://127.0.0.1:10400" model = "okay_computer" +# Optional WAV cues for hands-free use (override the [sounds] cues when the +# wakeword triggers): played when the wakeword fires (speak now) and when the +# command is captured. +sound_detected = "" +sound_done = "" # ---------------------------------------------------------------------------- # Speech-to-text engines (presets). The active one is used for transcription. diff --git a/linux/blitztext/daemon.py b/linux/blitztext/daemon.py index 38f00af..8b54b59 100644 --- a/linux/blitztext/daemon.py +++ b/linux/blitztext/daemon.py @@ -147,12 +147,15 @@ class Daemon: sound.play(fallback=sound_name) def _play_cue(self, cue: str) -> None: - """Play the user's WAV for 'before'/'after', else a built-in system sound.""" + """Play an audio cue. Hands-free (wakeword) sessions prefer the wakeword + sounds, then the general [sounds] cues, then a built-in system sound.""" from . import sound if cue == "before": - sound.play(self.cfg.sound_before, fallback="device-added") + custom = (self.cfg.wakeword_sound_detected if self._session_silent else "") or self.cfg.sound_before + sound.play(custom, fallback="device-added") else: - sound.play(self.cfg.sound_after, fallback="complete") + custom = (self.cfg.wakeword_sound_done if self._session_silent else "") or self.cfg.sound_after + sound.play(custom, fallback="complete") # -- recording control ---------------------------------------------------- def start_dictation(self, workflow: Workflow | None = None) -> None: diff --git a/linux/blitztext/gtksettings.py b/linux/blitztext/gtksettings.py index c651e44..bdc432a 100644 --- a/linux/blitztext/gtksettings.py +++ b/linux/blitztext/gtksettings.py @@ -765,8 +765,15 @@ class SettingsDialog: box = Gtk.Box(spacing=10); box.pack_start(self.ww_test_btn, False, False, 0); box.pack_start(self.ww_test_lbl, False, False, 0) _labeled(page, "", box) + self.ww_snd_detected = self._sound_field( + page, "Sound: detected", self.cfg.wakeword_sound_detected, + "Played right after the wakeword is recognised — your cue that Blitztext is listening, speak now.") + self.ww_snd_done = self._sound_field( + page, "Sound: captured", self.cfg.wakeword_sound_done, + "Played when your command is captured (silence or stop) — confirms your input was taken.") + page.pack_start(Gtk.Separator(), False, False, 8) - page.pack_start(Gtk.Label(label="Audio cues", xalign=0.0), False, False, 2) + page.pack_start(Gtk.Label(label="Audio cues (manual dictation)", xalign=0.0), False, False, 2) self.snd_before = self._sound_field( page, "Play before", self.cfg.sound_before, "Sound played when recording starts — your confirmation that Blitztext is listening.") @@ -1118,6 +1125,8 @@ class SettingsDialog: c.wakeword_enabled = self.ww_enabled.get_active() c.wakeword_uri = self.ww_uri.get_text().strip() c.wakeword_model = _combo_text(self.ww_model) + c.wakeword_sound_detected = self.ww_snd_detected.get_filename() or "" + c.wakeword_sound_done = self.ww_snd_done.get_filename() or "" c.mic = self._selected_mic_name() c.output = self.gen_output.get_active_text() or "type" c.language = self.gen_lang.get_text().strip()