Play STT start sound after wake word

This commit is contained in:
2026-03-15 16:27:02 +03:00
parent 3df24e27ae
commit 4b442795f8
4 changed files with 81 additions and 9 deletions

View File

@@ -40,6 +40,9 @@ PORCUPINE_SENSITIVITY=0.8
# AUDIO_INPUT_DEVICE_INDEX=2 # AUDIO_INPUT_DEVICE_INDEX=2
# AUDIO_OUTPUT_DEVICE_NAME=pulse # AUDIO_OUTPUT_DEVICE_NAME=pulse
# AUDIO_OUTPUT_DEVICE_INDEX=5 # AUDIO_OUTPUT_DEVICE_INDEX=5
# STT start sound (played after wake word before listening)
# STT_START_SOUND_PATH=~/Music/alisa-golosovoj-pomoschnik.mp3
# STT_START_SOUND_VOLUME=0.25
TTS_EN_SPEAKER=en_0 TTS_EN_SPEAKER=en_0
WEATHER_LAT=63.56 WEATHER_LAT=63.56
WEATHER_LON=53.69 WEATHER_LON=53.69

View File

@@ -156,6 +156,8 @@ python run.py
| `AUDIO_INPUT_DEVICE_INDEX` | Нет | auto | Индекс PortAudio для микрофона (приоритетнее `AUDIO_INPUT_DEVICE_NAME`) | | `AUDIO_INPUT_DEVICE_INDEX` | Нет | auto | Индекс PortAudio для микрофона (приоритетнее `AUDIO_INPUT_DEVICE_NAME`) |
| `AUDIO_OUTPUT_DEVICE_NAME` | Нет | auto | Подстрока имени динамика/выхода (например `pulse`) | | `AUDIO_OUTPUT_DEVICE_NAME` | Нет | auto | Подстрока имени динамика/выхода (например `pulse`) |
| `AUDIO_OUTPUT_DEVICE_INDEX` | Нет | auto | Индекс PortAudio для вывода (приоритетнее `AUDIO_OUTPUT_DEVICE_NAME`) | | `AUDIO_OUTPUT_DEVICE_INDEX` | Нет | auto | Индекс PortAudio для вывода (приоритетнее `AUDIO_OUTPUT_DEVICE_NAME`) |
| `STT_START_SOUND_PATH` | Нет | `~/Music/alisa-golosovoj-pomoschnik.mp3` | Короткий звук после wake word и перед стартом STT (wav/mp3) |
| `STT_START_SOUND_VOLUME` | Нет | `0.25` | Громкость звука старта STT (0..1) |
| `TTS_EN_SPEAKER` | Нет | `en_0` | Английский голос TTS | | `TTS_EN_SPEAKER` | Нет | `en_0` | Английский голос TTS |
| `WEATHER_LAT` | Нет | - | Широта города по умолчанию | | `WEATHER_LAT` | Нет | - | Широта города по умолчанию |
| `WEATHER_LON` | Нет | - | Долгота города по умолчанию | | `WEATHER_LON` | Нет | - | Долгота города по умолчанию |

View File

@@ -125,6 +125,20 @@ os.environ["TZ"] = "Europe/Moscow"
time.tzset() time.tzset()
# --- Настройки синтеза речи (TTS) --- # --- Настройки синтеза речи (TTS) ---
# --- Sound effects (SFX) ---
# Короткий "beep" после wake word и перед запуском STT, чтобы пользователь понял:
# можно начинать говорить. Поддерживает wav/mp3 (если pygame mixer поддерживает mp3),
# иначе будет использован mpg123 как fallback.
_stt_sfx_default = Path.home() / "Music" / "alisa-golosovoj-pomoschnik.mp3"
STT_START_SOUND_PATH = os.getenv("STT_START_SOUND_PATH", "").strip() or str(
_stt_sfx_default
)
try:
STT_START_SOUND_VOLUME = float(os.getenv("STT_START_SOUND_VOLUME", "0.25"))
except Exception:
STT_START_SOUND_VOLUME = 0.25
STT_START_SOUND_VOLUME = max(0.0, min(1.0, STT_START_SOUND_VOLUME))
# Голос для русского языка (eugene - мужской голос) # Голос для русского языка (eugene - мужской голос)
TTS_SPEAKER = "eugene" # Доступные (ru): aidar, baya, kseniya, xenia, eugene TTS_SPEAKER = "eugene" # Доступные (ru): aidar, baya, kseniya, xenia, eugene
# Голос для английского языка # Голос для английского языка

View File

@@ -7,6 +7,8 @@ import signal
import sys import sys
import time import time
from collections import deque from collections import deque
from pathlib import Path
import subprocess
# Для воспроизведения звуков (mp3) # Для воспроизведения звуков (mp3)
try: try:
@@ -34,7 +36,12 @@ from .audio.wakeword import (
stop_monitoring as stop_wakeword_monitoring, stop_monitoring as stop_wakeword_monitoring,
) )
from .core.ai import ask_ai_stream, interpret_assistant_intent, translate_text from .core.ai import ask_ai_stream, interpret_assistant_intent, translate_text
from .core.config import BASE_DIR, WAKE_WORD from .core.config import (
BASE_DIR,
STT_START_SOUND_PATH,
STT_START_SOUND_VOLUME,
WAKE_WORD,
)
from .core.cleaner import clean_response from .core.cleaner import clean_response
from .core.commands import is_stop_command from .core.commands import is_stop_command
from .core.smalltalk import get_smalltalk_response from .core.smalltalk import get_smalltalk_response
@@ -221,6 +228,7 @@ def main():
# Инициализация звуковой системы для эффектов (опционально) # Инициализация звуковой системы для эффектов (опционально)
ding_sound = None ding_sound = None
stt_start_sound_path = Path(STT_START_SOUND_PATH).expanduser()
if mixer is None: if mixer is None:
print( print(
"Warning: pygame mixer not available; sound effects disabled." "Warning: pygame mixer not available; sound effects disabled."
@@ -232,12 +240,58 @@ def main():
except Exception as exc: except Exception as exc:
print(f"Warning: pygame mixer init failed; sound effects disabled. ({exc})") print(f"Warning: pygame mixer init failed; sound effects disabled. ({exc})")
else: else:
ding_sound_path = BASE_DIR / "assets" / "sounds" / "ding.wav" # Приоритет: внешний звук для старта STT (обычно в ~/Music),
if ding_sound_path.exists(): # fallback: assets/sounds/ding.wav
ding_sound = mixer.Sound(str(ding_sound_path)) candidate_paths = [
ding_sound.set_volume(0.3) stt_start_sound_path,
else: BASE_DIR / "assets" / "sounds" / "ding.wav",
print(f"⚠️ Звук {ding_sound_path} не найден") ]
for candidate in candidate_paths:
if not candidate or not Path(candidate).exists():
continue
try:
ding_sound = mixer.Sound(str(candidate))
ding_sound.set_volume(float(STT_START_SOUND_VOLUME))
break
except Exception as exc:
print(f"⚠️ Не удалось загрузить звук {candidate}: {exc}")
ding_sound = None
def _play_stt_start_sfx_fallback():
"""Fallback для систем без pygame/mixer или без поддержки mp3."""
if not stt_start_sound_path.exists():
return False
# mpg123 scale factor: 0..32768 (примерно). Делаем тихо.
scale = int(32768 * float(STT_START_SOUND_VOLUME))
scale = max(0, min(32768, scale))
try:
subprocess.run(
["mpg123", "-q", "-f", str(scale), str(stt_start_sound_path)],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return True
except FileNotFoundError:
return False
except Exception:
return False
def play_stt_start_sfx():
"""Проиграть короткий звук старта STT синхронно (чтобы не попасть в распознавание)."""
if ding_sound is not None:
try:
ch = ding_sound.play()
# Если pygame не вернул канал, ничего не ждем.
if ch is None:
return True
# Ждем завершения звука.
while ch.get_busy():
time.sleep(0.01)
return True
except Exception:
pass
return _play_stt_start_sfx_fallback()
get_recognizer().initialize() # Подключение к Deepgram get_recognizer().initialize() # Подключение к Deepgram
init_tts() # Загрузка нейросети для синтеза речи (Silero) init_tts() # Загрузка нейросети для синтеза речи (Silero)
@@ -298,8 +352,7 @@ def main():
continue continue
# Звук активации # Звук активации
if ding_sound: play_stt_start_sfx()
ding_sound.play()
# Слушаем команду # Слушаем команду
try: try: