Фикс произношения дат и температуры + wheather -лишние файлы
This commit is contained in:
@@ -20,6 +20,8 @@ from ..core.config import TTS_SPEAKER, TTS_EN_SPEAKER, TTS_SAMPLE_RATE
|
||||
# Подавляем предупреждения Silero о длинном тексте (мы сами его режем)
|
||||
warnings.filterwarnings("ignore", message="Text string is longer than 1000 symbols")
|
||||
|
||||
_EN_WORD_RE = re.compile(r"[A-Za-z][A-Za-z0-9'-]*")
|
||||
|
||||
|
||||
class TextToSpeech:
|
||||
"""Класс синтеза речи с поддержкой прерывания."""
|
||||
@@ -109,19 +111,52 @@ class TextToSpeech:
|
||||
|
||||
return [c for c in chunks if c]
|
||||
|
||||
def speak(self, text: str, check_interrupt=None, language: str = "ru") -> bool:
|
||||
def _split_mixed_language(self, text: str) -> list[tuple[str, str]]:
|
||||
"""
|
||||
Основная функция: генерирует аудио и воспроизводит его.
|
||||
|
||||
Args:
|
||||
text: Текст для озвучки.
|
||||
check_interrupt: Функция, возвращающая True, если надо прерваться (например, check_wakeword_once).
|
||||
language: "ru" или "en".
|
||||
|
||||
Returns:
|
||||
True, если договорил до конца.
|
||||
False, если был прерван.
|
||||
Разбивает текст на сегменты русского и английского текста.
|
||||
Английские слова (латиница) будут озвучены английской моделью.
|
||||
"""
|
||||
matches = list(_EN_WORD_RE.finditer(text))
|
||||
if not matches:
|
||||
return [(text, "ru")]
|
||||
|
||||
segments = []
|
||||
idx = 0
|
||||
for match in matches:
|
||||
if match.start() > idx:
|
||||
segments.append((text[idx : match.start()], "ru"))
|
||||
segments.append((match.group(0), "en"))
|
||||
idx = match.end()
|
||||
|
||||
if idx < len(text):
|
||||
segments.append((text[idx:], "ru"))
|
||||
|
||||
# Склеиваем соседние сегменты и прикрепляем чистую пунктуацию к предыдущему.
|
||||
merged = []
|
||||
for segment, lang in segments:
|
||||
if not segment:
|
||||
continue
|
||||
if not any(ch.isalnum() for ch in segment):
|
||||
if merged:
|
||||
merged[-1] = (merged[-1][0] + segment, merged[-1][1])
|
||||
else:
|
||||
merged.append((segment, lang))
|
||||
continue
|
||||
if merged and merged[-1][1] == lang:
|
||||
merged[-1] = (merged[-1][0] + segment, lang)
|
||||
else:
|
||||
merged.append((segment, lang))
|
||||
|
||||
if merged and not any(ch.isalnum() for ch in merged[0][0]) and len(merged) > 1:
|
||||
merged[1] = (merged[0][0] + merged[1][0], merged[1][1])
|
||||
merged = merged[1:]
|
||||
|
||||
return merged
|
||||
|
||||
def _speak_single_language(
|
||||
self, text: str, check_interrupt=None, language: str = "ru"
|
||||
) -> bool:
|
||||
"""Озвучивание текста одной моделью языка."""
|
||||
if not text.strip():
|
||||
return True
|
||||
|
||||
@@ -197,6 +232,45 @@ class TextToSpeech:
|
||||
else:
|
||||
return False
|
||||
|
||||
def _speak_mixed(
|
||||
self, segments: list[tuple[str, str]], check_interrupt=None
|
||||
) -> bool:
|
||||
"""Озвучивание текста с переключением RU/EN по сегментам."""
|
||||
for segment, lang in segments:
|
||||
if not segment.strip():
|
||||
continue
|
||||
completed = self._speak_single_language(
|
||||
segment, check_interrupt=check_interrupt, language=lang
|
||||
)
|
||||
if not completed:
|
||||
return False
|
||||
return True
|
||||
|
||||
def speak(self, text: str, check_interrupt=None, language: str = "ru") -> bool:
|
||||
"""
|
||||
Основная функция: генерирует аудио и воспроизводит его.
|
||||
|
||||
Args:
|
||||
text: Текст для озвучки.
|
||||
check_interrupt: Функция, возвращающая True, если надо прерваться (например, check_wakeword_once).
|
||||
language: "ru" или "en".
|
||||
|
||||
Returns:
|
||||
True, если договорил до конца.
|
||||
False, если был прерван.
|
||||
"""
|
||||
if not text.strip():
|
||||
return True
|
||||
|
||||
if language == "ru":
|
||||
segments = self._split_mixed_language(text)
|
||||
if any(lang == "en" for _, lang in segments):
|
||||
return self._speak_mixed(segments, check_interrupt=check_interrupt)
|
||||
|
||||
return self._speak_single_language(
|
||||
text, check_interrupt=check_interrupt, language=language
|
||||
)
|
||||
|
||||
def _check_interrupt_worker(self, check_interrupt):
|
||||
"""
|
||||
Фоновая функция для потока: постоянно опрашивает check_interrupt.
|
||||
|
||||
@@ -22,7 +22,6 @@ class WakeWordDetector:
|
||||
self.pa = None
|
||||
self._stream_closed = True # Флаг состояния потока (закрыт/открыт)
|
||||
self._last_hit_ts = 0.0
|
||||
self._hit_streak = 0
|
||||
|
||||
def initialize(self):
|
||||
"""Инициализация Porcupine и PyAudio."""
|
||||
@@ -136,16 +135,11 @@ class WakeWordDetector:
|
||||
keyword_index = self.porcupine.process(pcm)
|
||||
if keyword_index >= 0:
|
||||
now = time.time()
|
||||
if now - self._last_hit_ts < 0.6:
|
||||
self._hit_streak += 1
|
||||
else:
|
||||
self._hit_streak = 1
|
||||
if now - self._last_hit_ts < 0.4:
|
||||
return False
|
||||
self._last_hit_ts = now
|
||||
|
||||
if self._hit_streak >= 2:
|
||||
self._hit_streak = 0
|
||||
print("🛑 Wake word подтвержден во время ответа!")
|
||||
return True
|
||||
print("🛑 Wake word обнаружен во время ответа!")
|
||||
return True
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user