Commit ·
feebc51
1
Parent(s): 23c59a7
修复: 语音助手唤醒时暂停Sendspin和音乐播放
Browse files- 添加pause_sendspin()/resume_sendspin()方法
- duck()时暂停Sendspin音频流,unduck()时恢复
- 改进pause()方法,真正停止音频输出
- 防止语音助手交互时的音频冲突
reachy_mini_ha_voice/audio_player.py
CHANGED
|
@@ -102,6 +102,7 @@ class AudioPlayer:
|
|
| 102 |
# Audio buffer for Sendspin playback
|
| 103 |
self._sendspin_audio_format: Optional["PCMFormat"] = None
|
| 104 |
self._sendspin_playback_started = False
|
|
|
|
| 105 |
|
| 106 |
def set_reachy_mini(self, reachy_mini) -> None:
|
| 107 |
"""Set the Reachy Mini instance."""
|
|
@@ -124,6 +125,28 @@ class AudioPlayer:
|
|
| 124 |
"""Get current Sendspin server URL."""
|
| 125 |
return self._sendspin_url
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
async def start_sendspin_discovery(self) -> None:
|
| 128 |
"""Start mDNS discovery for Sendspin servers.
|
| 129 |
|
|
@@ -269,10 +292,16 @@ class AudioPlayer:
|
|
| 269 |
|
| 270 |
Plays the audio through Reachy Mini's speaker using push_audio_sample().
|
| 271 |
Resamples audio if needed (Reachy Mini uses 16kHz).
|
|
|
|
|
|
|
| 272 |
"""
|
| 273 |
if self.reachy_mini is None:
|
| 274 |
return
|
| 275 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
try:
|
| 277 |
# Store format for potential use
|
| 278 |
self._sendspin_audio_format = fmt
|
|
@@ -515,11 +544,21 @@ class AudioPlayer:
|
|
| 515 |
_LOGGER.exception("Unexpected error running done callback")
|
| 516 |
|
| 517 |
def pause(self) -> None:
|
| 518 |
-
"""Pause playback.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
self.is_playing = False
|
| 520 |
|
| 521 |
def resume(self) -> None:
|
| 522 |
-
"""Resume playback."""
|
|
|
|
| 523 |
if self._playlist:
|
| 524 |
self._play_next()
|
| 525 |
|
|
|
|
| 102 |
# Audio buffer for Sendspin playback
|
| 103 |
self._sendspin_audio_format: Optional["PCMFormat"] = None
|
| 104 |
self._sendspin_playback_started = False
|
| 105 |
+
self._sendspin_paused = False # Pause Sendspin when voice assistant is active
|
| 106 |
|
| 107 |
def set_reachy_mini(self, reachy_mini) -> None:
|
| 108 |
"""Set the Reachy Mini instance."""
|
|
|
|
| 125 |
"""Get current Sendspin server URL."""
|
| 126 |
return self._sendspin_url
|
| 127 |
|
| 128 |
+
def pause_sendspin(self) -> None:
|
| 129 |
+
"""Pause Sendspin audio playback.
|
| 130 |
+
|
| 131 |
+
Called when voice assistant is activated to prevent audio conflicts.
|
| 132 |
+
Incoming Sendspin audio chunks will be dropped until resumed.
|
| 133 |
+
"""
|
| 134 |
+
if self._sendspin_paused:
|
| 135 |
+
return
|
| 136 |
+
self._sendspin_paused = True
|
| 137 |
+
_LOGGER.debug("Sendspin audio paused (voice assistant active)")
|
| 138 |
+
|
| 139 |
+
def resume_sendspin(self) -> None:
|
| 140 |
+
"""Resume Sendspin audio playback.
|
| 141 |
+
|
| 142 |
+
Called when voice assistant returns to idle state.
|
| 143 |
+
"""
|
| 144 |
+
if not self._sendspin_paused:
|
| 145 |
+
return
|
| 146 |
+
self._sendspin_paused = False
|
| 147 |
+
self._logged_resample = False # Reset resample log flag for new stream
|
| 148 |
+
_LOGGER.debug("Sendspin audio resumed")
|
| 149 |
+
|
| 150 |
async def start_sendspin_discovery(self) -> None:
|
| 151 |
"""Start mDNS discovery for Sendspin servers.
|
| 152 |
|
|
|
|
| 292 |
|
| 293 |
Plays the audio through Reachy Mini's speaker using push_audio_sample().
|
| 294 |
Resamples audio if needed (Reachy Mini uses 16kHz).
|
| 295 |
+
|
| 296 |
+
Note: Audio is dropped when Sendspin is paused (e.g., during voice assistant interaction).
|
| 297 |
"""
|
| 298 |
if self.reachy_mini is None:
|
| 299 |
return
|
| 300 |
|
| 301 |
+
# Drop audio when paused (voice assistant is active)
|
| 302 |
+
if self._sendspin_paused:
|
| 303 |
+
return
|
| 304 |
+
|
| 305 |
try:
|
| 306 |
# Store format for potential use
|
| 307 |
self._sendspin_audio_format = fmt
|
|
|
|
| 544 |
_LOGGER.exception("Unexpected error running done callback")
|
| 545 |
|
| 546 |
def pause(self) -> None:
|
| 547 |
+
"""Pause playback.
|
| 548 |
+
|
| 549 |
+
Stops current audio output but preserves playlist for resume.
|
| 550 |
+
"""
|
| 551 |
+
self._stop_flag.set()
|
| 552 |
+
if self.reachy_mini is not None:
|
| 553 |
+
try:
|
| 554 |
+
self.reachy_mini.media.stop_playing()
|
| 555 |
+
except Exception:
|
| 556 |
+
pass
|
| 557 |
self.is_playing = False
|
| 558 |
|
| 559 |
def resume(self) -> None:
|
| 560 |
+
"""Resume playback from where it was paused."""
|
| 561 |
+
self._stop_flag.clear()
|
| 562 |
if self._playlist:
|
| 563 |
self._play_next()
|
| 564 |
|
reachy_mini_ha_voice/satellite.py
CHANGED
|
@@ -398,10 +398,14 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 398 |
def duck(self) -> None:
|
| 399 |
_LOGGER.debug("Ducking music")
|
| 400 |
self.state.music_player.duck()
|
|
|
|
|
|
|
| 401 |
|
| 402 |
def unduck(self) -> None:
|
| 403 |
_LOGGER.debug("Unducking music")
|
| 404 |
self.state.music_player.unduck()
|
|
|
|
|
|
|
| 405 |
|
| 406 |
def _tts_finished(self) -> None:
|
| 407 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|
|
|
|
| 398 |
def duck(self) -> None:
|
| 399 |
_LOGGER.debug("Ducking music")
|
| 400 |
self.state.music_player.duck()
|
| 401 |
+
# Pause Sendspin to prevent audio conflicts during voice interaction
|
| 402 |
+
self.state.tts_player.pause_sendspin()
|
| 403 |
|
| 404 |
def unduck(self) -> None:
|
| 405 |
_LOGGER.debug("Unducking music")
|
| 406 |
self.state.music_player.unduck()
|
| 407 |
+
# Resume Sendspin audio
|
| 408 |
+
self.state.tts_player.resume_sendspin()
|
| 409 |
|
| 410 |
def _tts_finished(self) -> None:
|
| 411 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|