Addressing code review comments
This commit is contained in:
@@ -116,6 +116,18 @@ class BaseTurnAnalyzer(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_vad_start_secs_updated(self, vad_start_secs: float):
|
||||||
|
"""Invoked when the VAD start trigger time is updated.
|
||||||
|
|
||||||
|
The turn analyzer may choose to change its buffer size depending
|
||||||
|
on this value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vad_start_secs (float): The number of seconds of voice activity
|
||||||
|
before triggering the user speaking event.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Reset the turn analyzer to its initial state."""
|
"""Reset the turn analyzer to its initial state."""
|
||||||
|
|||||||
@@ -35,15 +35,11 @@ class SmartTurnParams(BaseTurnParams):
|
|||||||
Parameters:
|
Parameters:
|
||||||
stop_secs: Maximum silence duration in seconds before ending turn.
|
stop_secs: Maximum silence duration in seconds before ending turn.
|
||||||
pre_speech_ms: Milliseconds of audio to include before speech starts.
|
pre_speech_ms: Milliseconds of audio to include before speech starts.
|
||||||
vad_start_secs: Seconds VAD waits before confirming speech start (e.g. VAD STARTING window).
|
|
||||||
This is added to `pre_speech_ms` at inference slicing time so Smart Turn can include
|
|
||||||
the initial audio that occurred while VAD was still confirming speech.
|
|
||||||
max_duration_secs: Maximum duration in seconds for audio segments.
|
max_duration_secs: Maximum duration in seconds for audio segments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
stop_secs: float = STOP_SECS
|
stop_secs: float = STOP_SECS
|
||||||
pre_speech_ms: float = PRE_SPEECH_MS
|
pre_speech_ms: float = PRE_SPEECH_MS
|
||||||
vad_start_secs: float = 0.0
|
|
||||||
max_duration_secs: float = MAX_DURATION_SECONDS
|
max_duration_secs: float = MAX_DURATION_SECONDS
|
||||||
|
|
||||||
|
|
||||||
@@ -82,6 +78,7 @@ class BaseSmartTurn(BaseTurnAnalyzer):
|
|||||||
# Thread executor that will run the model. We only need one thread per
|
# Thread executor that will run the model. We only need one thread per
|
||||||
# analyzer because one analyzer just handles one audio stream.
|
# analyzer because one analyzer just handles one audio stream.
|
||||||
self._executor = ThreadPoolExecutor(max_workers=1)
|
self._executor = ThreadPoolExecutor(max_workers=1)
|
||||||
|
self._vad_start_secs: float = 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def speech_triggered(self) -> bool:
|
def speech_triggered(self) -> bool:
|
||||||
@@ -165,6 +162,9 @@ class BaseSmartTurn(BaseTurnAnalyzer):
|
|||||||
logger.debug(f"End of Turn result: {state}")
|
logger.debug(f"End of Turn result: {state}")
|
||||||
return state, result
|
return state, result
|
||||||
|
|
||||||
|
def on_vad_start_secs_updated(self, vad_start_secs: float):
|
||||||
|
self._vad_start_secs = vad_start_secs
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Reset the turn analyzer to its initial state."""
|
"""Reset the turn analyzer to its initial state."""
|
||||||
self._clear(EndOfTurnState.COMPLETE)
|
self._clear(EndOfTurnState.COMPLETE)
|
||||||
@@ -185,7 +185,7 @@ class BaseSmartTurn(BaseTurnAnalyzer):
|
|||||||
return state, None
|
return state, None
|
||||||
|
|
||||||
# Extract recent audio segment for prediction
|
# Extract recent audio segment for prediction
|
||||||
effective_pre_speech_ms = self._params.pre_speech_ms + (self._params.vad_start_secs * 1000)
|
effective_pre_speech_ms = self._params.pre_speech_ms + (self._vad_start_secs * 1000)
|
||||||
start_time = self._speech_start_time - (effective_pre_speech_ms / 1000)
|
start_time = self._speech_start_time - (effective_pre_speech_ms / 1000)
|
||||||
start_index = 0
|
start_index = 0
|
||||||
for i, (t, _) in enumerate(audio_buffer):
|
for i, (t, _) in enumerate(audio_buffer):
|
||||||
|
|||||||
@@ -108,20 +108,12 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy):
|
|||||||
async def _handle_speech_control_params(self, frame: SpeechControlParamsFrame):
|
async def _handle_speech_control_params(self, frame: SpeechControlParamsFrame):
|
||||||
"""Sync Smart Turn pre-speech buffering with VAD start delay.
|
"""Sync Smart Turn pre-speech buffering with VAD start delay.
|
||||||
|
|
||||||
In the new user-turn-strategies pipeline, `VADUserStartedSpeakingFrame`
|
`VADUserStartedSpeakingFrame` is emitted only once VAD has confirmed speech
|
||||||
is emitted only once VAD has *confirmed* speech (after `vad_params.start_secs`).
|
(after `vad_params.start_secs`). Smart Turn should still include the initial
|
||||||
Smart Turn should still include the initial audio collected during that
|
audio collected during that confirmation window, so we let the analyzer know
|
||||||
confirmation window, so we record it in `SmartTurnParams.vad_start_secs` and
|
when this value has changed.
|
||||||
add it at inference slicing time (preserving `pre_speech_ms` semantics).
|
|
||||||
"""
|
"""
|
||||||
if not frame.vad_params:
|
self._turn_analyzer.on_vad_start_secs_updated(frame.vad_params.start_secs)
|
||||||
return
|
|
||||||
|
|
||||||
params = self._turn_analyzer.params
|
|
||||||
if not isinstance(params, SmartTurnParams):
|
|
||||||
return
|
|
||||||
|
|
||||||
params.vad_start_secs = frame.vad_params.start_secs
|
|
||||||
|
|
||||||
async def _handle_input_audio(self, frame: InputAudioRawFrame):
|
async def _handle_input_audio(self, frame: InputAudioRawFrame):
|
||||||
"""Handle input audio to check if the turn is completed."""
|
"""Handle input audio to check if the turn is completed."""
|
||||||
|
|||||||
Reference in New Issue
Block a user