Этап 3: голосовой ввод по горячей клавише
- Глобальная горячая клавиша через RegisterHotKey (по умолчанию Win+Alt+Space) - Запись с микрофона, конец фразы по паузе через Silero VAD, звуковые сигналы - Распознавание faster-whisper large-v3-turbo на GPU, CUDA-библиотеки из pip-пакетов nvidia-* - Состояния «слушаю» и «распознаю», кнопка «Говорить» в чате и пункт в трее - Голосовая команда прерывает текущий ответ и озвучку - Тесты горячей клавиши, детектора конца фразы и сеанса голосового ввода Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fec52dd1c2
commit
513d010c19
@@ -0,0 +1,43 @@
|
||||
from agr_assistent.audio.recorder import UtteranceDetector, UtteranceStatus
|
||||
|
||||
# Частота 100 отсчётов в секунду, окно — 10 отсчётов (0.1 с): так проще считать
|
||||
_WINDOW = 10
|
||||
|
||||
|
||||
def _detector(**overrides: float) -> UtteranceDetector:
|
||||
settings = dict(threshold=0.5, start_timeout_seconds=1, silence_seconds=0.3, max_seconds=5)
|
||||
settings.update(overrides)
|
||||
return UtteranceDetector(sample_rate=100, **settings)
|
||||
|
||||
|
||||
def _feed(detector: UtteranceDetector, probabilities: list[float]) -> list[UtteranceStatus]:
|
||||
return [detector.update(p, _WINDOW) for p in probabilities]
|
||||
|
||||
|
||||
def test_phrase_completes_after_silence() -> None:
|
||||
statuses = _feed(_detector(), [0.1, 0.9, 0.8, 0.2, 0.1, 0.1])
|
||||
|
||||
assert statuses[:-1] == [UtteranceStatus.CONTINUE] * 5
|
||||
assert statuses[-1] is UtteranceStatus.COMPLETE
|
||||
|
||||
|
||||
def test_short_pause_does_not_end_phrase() -> None:
|
||||
statuses = _feed(_detector(), [0.9, 0.1, 0.1, 0.9, 0.1, 0.1])
|
||||
|
||||
assert UtteranceStatus.COMPLETE not in statuses
|
||||
|
||||
|
||||
def test_no_speech_before_timeout() -> None:
|
||||
statuses = _feed(_detector(), [0.1] * 10)
|
||||
|
||||
assert statuses[-1] is UtteranceStatus.NO_SPEECH
|
||||
assert statuses[:-1] == [UtteranceStatus.CONTINUE] * 9
|
||||
|
||||
|
||||
def test_max_duration_completes_ongoing_speech() -> None:
|
||||
detector = _detector(max_seconds=0.5)
|
||||
|
||||
statuses = _feed(detector, [0.9] * 5)
|
||||
|
||||
assert statuses[-1] is UtteranceStatus.COMPLETE
|
||||
assert detector.speech_detected
|
||||
Reference in New Issue
Block a user