Update __main__.py
Wrong version uploaded.
This commit is contained in:
@@ -134,10 +134,9 @@ async def main() -> None:
|
|||||||
finally:
|
finally:
|
||||||
# --- 10. Cleanup ---
|
# --- 10. Cleanup ---
|
||||||
_LOGGER.debug("Shutting down...")
|
_LOGGER.debug("Shutting down...")
|
||||||
state.shutdown = True
|
state.shutdown = True # Signal audio thread to stop
|
||||||
audio_thread.join()
|
audio_thread.join()
|
||||||
|
|
||||||
# Check for and stop the MQTT controller (set in _init_controllers)
|
|
||||||
if hasattr(state, "mqtt_controller") and state.mqtt_controller:
|
if hasattr(state, "mqtt_controller") and state.mqtt_controller:
|
||||||
_LOGGER.debug("Stopping MQTT controller...")
|
_LOGGER.debug("Stopping MQTT controller...")
|
||||||
state.mqtt_controller.stop()
|
state.mqtt_controller.stop()
|
||||||
@@ -404,93 +403,96 @@ def _process_audio(state: ServerState, mic, block_size: int):
|
|||||||
has_oww = False
|
has_oww = False
|
||||||
last_active: Optional[float] = None
|
last_active: Optional[float] = None
|
||||||
|
|
||||||
is_muted_flag = False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_LOGGER.debug("Opening audio input device: %s", mic.name)
|
while True:
|
||||||
with mic.recorder(samplerate=16000, channels=1, blocksize=block_size) as mic_in:
|
# --- THIS IS THE FIX ---
|
||||||
while True:
|
# Wait while muted. This loop will spin until unmuted.
|
||||||
if state.shutdown:
|
if state.mic_muted:
|
||||||
_LOGGER.debug("Shutdown signal received, stopping audio thread.")
|
_LOGGER.debug("Audio thread muted.")
|
||||||
break
|
while state.mic_muted:
|
||||||
|
if state.shutdown:
|
||||||
if state.mic_muted:
|
_LOGGER.debug("Shutdown signal received, stopping audio thread.")
|
||||||
if not is_muted_flag:
|
return # Exit thread
|
||||||
_LOGGER.debug("Audio thread muted.")
|
|
||||||
is_muted_flag = True
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
|
||||||
|
|
||||||
if is_muted_flag:
|
|
||||||
_LOGGER.debug("Audio thread unmuted, clearing buffers.")
|
|
||||||
micro_features.reset()
|
|
||||||
if oww_features is not None:
|
|
||||||
oww_features.reset()
|
|
||||||
last_active = time.monotonic()
|
|
||||||
|
|
||||||
is_muted_flag = False
|
|
||||||
|
|
||||||
_LOGGER.debug("Flushing stale audio buffer...")
|
|
||||||
for _ in range(8):
|
|
||||||
mic_in.record(block_size)
|
|
||||||
_LOGGER.debug("Stale audio buffer cleared.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
audio_chunk_array = mic_in.record(block_size).reshape(-1)
|
# --- Just unmuted ---
|
||||||
audio_chunk = (
|
_LOGGER.debug("Audio thread unmuted, clearing buffers.")
|
||||||
(np.clip(audio_chunk_array, -1.0, 1.0) * 32767.0)
|
micro_features.reset()
|
||||||
.astype("<i2")
|
if oww_features is not None:
|
||||||
.tobytes()
|
oww_features.reset()
|
||||||
)
|
last_active = time.monotonic() # Start refractory period
|
||||||
|
|
||||||
|
# Re-open microphone to flush OS buffer
|
||||||
|
_LOGGER.debug("Re-opening microphone to flush OS buffers...")
|
||||||
|
with mic.recorder(samplerate=16000, channels=1, blocksize=block_size) as mic_in:
|
||||||
|
mic_in.flush() # Discard any stale data
|
||||||
|
_LOGGER.debug("Microphone flushed.")
|
||||||
|
# Fall through to the main recording loop
|
||||||
|
# --- END FIX ---
|
||||||
|
|
||||||
|
_LOGGER.debug("Opening audio input device: %s", mic.name)
|
||||||
|
with mic.recorder(samplerate=16000, channels=1, blocksize=block_size) as mic_in:
|
||||||
|
# --- This is now the main recording loop ---
|
||||||
|
while not state.mic_muted:
|
||||||
|
if state.shutdown:
|
||||||
|
_LOGGER.debug("Shutdown signal received, stopping audio thread.")
|
||||||
|
return # Exit thread
|
||||||
|
|
||||||
|
audio_chunk_array = mic_in.record(block_size).reshape(-1)
|
||||||
|
audio_chunk = (
|
||||||
|
(np.clip(audio_chunk_array, -1.0, 1.0) * 32767.0)
|
||||||
|
.astype("<i2")
|
||||||
|
.tobytes()
|
||||||
|
)
|
||||||
|
|
||||||
if state.satellite is None:
|
if state.satellite is None:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (not wake_words) or (state.wake_words_changed):
|
|
||||||
state.wake_words_changed = False
|
|
||||||
wake_words = [
|
|
||||||
ww for ww in state.wake_words.values()
|
|
||||||
if ww.id in state.active_wake_words
|
|
||||||
]
|
|
||||||
has_oww = any(isinstance(ww, OpenWakeWord) for ww in wake_words)
|
|
||||||
|
|
||||||
if has_oww and (oww_features is None):
|
if (not wake_words) or (state.wake_words_changed):
|
||||||
_LOGGER.debug("Initializing OpenWakeWord features...")
|
state.wake_words_changed = False
|
||||||
oww_features = OpenWakeWordFeatures.from_builtin()
|
wake_words = [
|
||||||
|
ww for ww in state.wake_words.values()
|
||||||
try:
|
if ww.id in state.active_wake_words
|
||||||
micro_inputs.clear(); micro_inputs.extend(micro_features.process_streaming(audio_chunk))
|
]
|
||||||
|
has_oww = any(isinstance(ww, OpenWakeWord) for ww in wake_words)
|
||||||
if has_oww:
|
|
||||||
assert oww_features is not None
|
|
||||||
oww_inputs.clear(); oww_inputs.extend(oww_features.process_streaming(audio_chunk))
|
|
||||||
|
|
||||||
for wake_word in wake_words:
|
|
||||||
activated = False
|
|
||||||
if isinstance(wake_word, MicroWakeWord):
|
|
||||||
if any(wake_word.process_streaming(mi) for mi in micro_inputs): activated = True
|
|
||||||
elif isinstance(wake_word, OpenWakeWord):
|
|
||||||
if any(p > 0.5 for oi in oww_inputs for p in wake_word.process_streaming(oi)): activated = True
|
|
||||||
|
|
||||||
if activated:
|
if has_oww and (oww_features is None):
|
||||||
now = time.monotonic()
|
_LOGGER.debug("Initializing OpenWakeWord features...")
|
||||||
if (last_active is None) or ((now - last_active) > state.refractory_seconds):
|
oww_features = OpenWakeWordFeatures.from_builtin()
|
||||||
state.loop.call_soon_threadsafe(state.satellite.wakeup, wake_word)
|
|
||||||
last_active = now
|
|
||||||
|
|
||||||
stopped = False
|
try:
|
||||||
for micro_input in micro_inputs:
|
micro_inputs.clear(); micro_inputs.extend(micro_features.process_streaming(audio_chunk))
|
||||||
if state.stop_word.process_streaming(micro_input):
|
|
||||||
stopped = True
|
|
||||||
|
|
||||||
if stopped and (state.stop_word.id in state.active_wake_words):
|
|
||||||
state.loop.call_soon_threadsafe(state.satellite.stop)
|
|
||||||
|
|
||||||
state.loop.call_soon_threadsafe(state.satellite.handle_audio, audio_chunk)
|
if has_oww:
|
||||||
|
assert oww_features is not None
|
||||||
|
oww_inputs.clear(); oww_inputs.extend(oww_features.process_streaming(audio_chunk))
|
||||||
|
|
||||||
|
for wake_word in wake_words:
|
||||||
|
activated = False
|
||||||
|
if isinstance(wake_word, MicroWakeWord):
|
||||||
|
if any(wake_word.process_streaming(mi) for mi in micro_inputs): activated = True
|
||||||
|
elif isinstance(wake_word, OpenWakeWord):
|
||||||
|
if any(p > 0.5 for oi in oww_inputs for p in wake_word.process_streaming(oi)): activated = True
|
||||||
|
|
||||||
|
if activated:
|
||||||
|
now = time.monotonic()
|
||||||
|
if (last_active is None) or ((now - last_active) > state.refractory_seconds):
|
||||||
|
state.loop.call_soon_threadsafe(state.satellite.wakeup, wake_word)
|
||||||
|
last_active = now
|
||||||
|
|
||||||
|
stopped = False
|
||||||
|
for micro_input in micro_inputs:
|
||||||
|
if state.stop_word.process_streaming(micro_input):
|
||||||
|
stopped = True
|
||||||
|
|
||||||
except Exception:
|
if stopped and (state.stop_word.id in state.active_wake_words):
|
||||||
_LOGGER.exception("Unexpected error handling audio")
|
state.loop.call_soon_threadsafe(state.satellite.stop)
|
||||||
|
|
||||||
|
state.loop.call_soon_threadsafe(state.satellite.handle_audio, audio_chunk)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
_LOGGER.exception("Unexpected error handling audio")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.critical("A soundcard error occurred: %s", e)
|
_LOGGER.critical("A soundcard error occurred: %s", e)
|
||||||
|
|||||||
Reference in New Issue
Block a user