Use different VAD thresholds for before and during voice command (#129848)

* Use two VAD thresholds

* Fix VoiceActivityTimeout class

* Update homeassistant/components/assist_pipeline/audio_enhancer.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Michael Hansen
2024-11-05 08:01:45 -06:00
committed by GitHub
co-authored by Joost Lekkerkerker
parent 6caa4baa00
commit 69e3348cd7
4 changed files with 108 additions and 60 deletions
+52 -28
View File
@@ -16,7 +16,7 @@ def test_silence() -> None:
segmenter = VoiceCommandSegmenter()
# True return value indicates voice command has not finished
assert segmenter.process(_ONE_SECOND * 3, False)
assert segmenter.process(_ONE_SECOND * 3, 0.0)
assert not segmenter.in_command
@@ -26,15 +26,15 @@ def test_speech() -> None:
segmenter = VoiceCommandSegmenter()
# silence
assert segmenter.process(_ONE_SECOND, False)
assert segmenter.process(_ONE_SECOND, 0.0)
# "speech"
assert segmenter.process(_ONE_SECOND, True)
assert segmenter.process(_ONE_SECOND, 1.0)
assert segmenter.in_command
# silence
# False return value indicates voice command is finished
assert not segmenter.process(_ONE_SECOND, False)
assert not segmenter.process(_ONE_SECOND, 0.0)
assert not segmenter.in_command
@@ -112,19 +112,19 @@ def test_silence_seconds() -> None:
segmenter = VoiceCommandSegmenter(silence_seconds=1.0)
# silence
assert segmenter.process(_ONE_SECOND, False)
assert segmenter.process(_ONE_SECOND, 0.0)
assert not segmenter.in_command
# "speech"
assert segmenter.process(_ONE_SECOND, True)
assert segmenter.process(_ONE_SECOND, 1.0)
assert segmenter.in_command
# not enough silence to end
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert segmenter.in_command
# exactly enough silence now
assert not segmenter.process(_ONE_SECOND * 0.5, False)
assert not segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert not segmenter.in_command
@@ -134,27 +134,27 @@ def test_silence_reset() -> None:
segmenter = VoiceCommandSegmenter(silence_seconds=1.0, reset_seconds=0.5)
# silence
assert segmenter.process(_ONE_SECOND, False)
assert segmenter.process(_ONE_SECOND, 0.0)
assert not segmenter.in_command
# "speech"
assert segmenter.process(_ONE_SECOND, True)
assert segmenter.process(_ONE_SECOND, 1.0)
assert segmenter.in_command
# not enough silence to end
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert segmenter.in_command
# speech should reset silence detection
assert segmenter.process(_ONE_SECOND * 0.5, True)
assert segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert segmenter.in_command
# not enough silence to end
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert segmenter.in_command
# exactly enough silence now
assert not segmenter.process(_ONE_SECOND * 0.5, False)
assert not segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert not segmenter.in_command
@@ -166,23 +166,23 @@ def test_speech_reset() -> None:
)
# silence
assert segmenter.process(_ONE_SECOND, False)
assert segmenter.process(_ONE_SECOND, 0.0)
assert not segmenter.in_command
# not enough speech to start voice command
assert segmenter.process(_ONE_SECOND * 0.5, True)
assert segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert not segmenter.in_command
# silence should reset speech detection
assert segmenter.process(_ONE_SECOND, False)
assert segmenter.process(_ONE_SECOND, 0.0)
assert not segmenter.in_command
# not enough speech to start voice command
assert segmenter.process(_ONE_SECOND * 0.5, True)
assert segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert not segmenter.in_command
# exactly enough speech now
assert segmenter.process(_ONE_SECOND * 0.5, True)
assert segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert segmenter.in_command
@@ -193,18 +193,18 @@ def test_timeout() -> None:
# not enough to time out
assert not segmenter.timed_out
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert not segmenter.timed_out
# enough to time out
assert not segmenter.process(_ONE_SECOND * 0.5, True)
assert not segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert segmenter.timed_out
# flag resets with more audio
assert segmenter.process(_ONE_SECOND * 0.5, True)
assert segmenter.process(_ONE_SECOND * 0.5, 1.0)
assert not segmenter.timed_out
assert not segmenter.process(_ONE_SECOND * 0.5, False)
assert not segmenter.process(_ONE_SECOND * 0.5, 0.0)
assert segmenter.timed_out
@@ -215,14 +215,38 @@ def test_command_seconds() -> None:
command_seconds=3, speech_seconds=1, silence_seconds=1, reset_seconds=1
)
assert segmenter.process(_ONE_SECOND, True)
assert segmenter.process(_ONE_SECOND, 1.0)
# Silence counts towards total command length
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
# Enough to finish command now
assert segmenter.process(_ONE_SECOND, True)
assert segmenter.process(_ONE_SECOND * 0.5, False)
assert segmenter.process(_ONE_SECOND, 1.0)
assert segmenter.process(_ONE_SECOND * 0.5, 0.0)
# Silence to finish
assert not segmenter.process(_ONE_SECOND * 0.5, False)
assert not segmenter.process(_ONE_SECOND * 0.5, 0.0)
def test_speech_thresholds() -> None:
"""Test before/in command speech thresholds."""
segmenter = VoiceCommandSegmenter(
before_command_speech_threshold=0.2,
in_command_speech_threshold=0.5,
command_seconds=2,
speech_seconds=1,
silence_seconds=1,
)
# Not high enough probability to trigger command
assert segmenter.process(_ONE_SECOND, 0.1)
assert not segmenter.in_command
# Triggers command
assert segmenter.process(_ONE_SECOND, 0.3)
assert segmenter.in_command
# Now that same probability is considered silence.
# Finishes command.
assert not segmenter.process(_ONE_SECOND, 0.3)