Switch DoA to AUDIO_MGR_SELECTED_AZIMUTHS (auto-select beam)
DOA_VALUE on GPO resource was sluggish/cached. The beamformer-level AUDIO_MGR_SELECTED_AZIMUTHS on resource 35 tracks the active speaker in real time. Falls back to simple DOA_VALUE when both azimuths are NaN. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
43
xvf3800.py
43
xvf3800.py
@@ -33,8 +33,15 @@ GPO_RESID = 20
|
|||||||
|
|
||||||
# Command IDs (wValue in USB control transfer)
|
# Command IDs (wValue in USB control transfer)
|
||||||
# Verified against official xvf_host.py WriteCMD output
|
# Verified against official xvf_host.py WriteCMD output
|
||||||
DOA_VALUE_CMD = 18 # returns (angle 0-359, vad 0/1)
|
DOA_VALUE_CMD = 18 # returns (angle 0-359, vad 0/1) — simple, sluggish
|
||||||
LED_EFFECT_CMD = 12 # 0=off, 1=breath, 2=rainbow, 3=solid, 4=doa, 5=ring
|
LED_EFFECT_CMD = 12 # 0=off, 1=breath, 2=rainbow, 3=solid, 4=doa, 5=ring
|
||||||
|
|
||||||
|
# AEC resource (resid=33) — beamformer-level commands, more responsive
|
||||||
|
AEC_RESID = 33
|
||||||
|
AEC_AZIMUTH_CMD = 75 # 4 floats: beam1, beam2, free-running, auto-select (radians)
|
||||||
|
AEC_SPENERGY_CMD = 80 # 4 floats: speech energy per beam (>0 = speech)
|
||||||
|
AUDIO_MGR_RESID = 35
|
||||||
|
AUDIO_MGR_SELECTED_AZ_CMD = 11 # 2 floats: processed DoA, auto-select DoA (radians)
|
||||||
LED_BRIGHTNESS_CMD = 14
|
LED_BRIGHTNESS_CMD = 14
|
||||||
LED_COLOR_CMD = 16 # single uint32 color (confirmed: xvf_host LED_COLOR cmdid=16)
|
LED_COLOR_CMD = 16 # single uint32 color (confirmed: xvf_host LED_COLOR cmdid=16)
|
||||||
LED_DOA_COLOR_CMD = 17 # two uint32 values: base + indicator
|
LED_DOA_COLOR_CMD = 17 # two uint32 values: base + indicator
|
||||||
@@ -94,13 +101,35 @@ class XVF3800:
|
|||||||
# --- DoA ---
|
# --- DoA ---
|
||||||
|
|
||||||
def read_doa(self) -> tuple[int, bool]:
|
def read_doa(self) -> tuple[int, bool]:
|
||||||
"""Read Direction of Arrival. Returns (angle 0-359, vad True/False).
|
"""Read Direction of Arrival using the auto-select beam azimuth.
|
||||||
Response format: 1 status byte + 2 uint16 words (angle, vad)."""
|
Returns (angle 0-359 degrees, vad True/False).
|
||||||
data = self._read_uint16(GPO_RESID, DOA_VALUE_CMD, 2)
|
Uses AUDIO_MGR_SELECTED_AZIMUTHS which tracks the active speaker."""
|
||||||
if len(data) < 5:
|
import math
|
||||||
|
|
||||||
|
# Read auto-select beam azimuth (2 floats: processed_doa, auto_select_doa)
|
||||||
|
data = self._read_float(AUDIO_MGR_RESID, AUDIO_MGR_SELECTED_AZ_CMD, 2)
|
||||||
|
if len(data) < 9: # 1 status + 2*4 bytes
|
||||||
return 0, False
|
return 0, False
|
||||||
angle, vad = struct.unpack_from("<HH", data, 1) # skip status byte
|
|
||||||
return angle % 360, bool(vad)
|
processed_doa, auto_select_doa = struct.unpack_from("<ff", data, 1)
|
||||||
|
|
||||||
|
# Use auto-select beam (index 1), fall back to processed (index 0)
|
||||||
|
if not math.isnan(auto_select_doa):
|
||||||
|
angle_deg = math.degrees(auto_select_doa) % 360
|
||||||
|
vad = True
|
||||||
|
elif not math.isnan(processed_doa):
|
||||||
|
angle_deg = math.degrees(processed_doa) % 360
|
||||||
|
vad = True
|
||||||
|
else:
|
||||||
|
# Both NaN — no speech detected
|
||||||
|
# Fall back to simple DOA_VALUE
|
||||||
|
data2 = self._read_uint16(GPO_RESID, DOA_VALUE_CMD, 2)
|
||||||
|
if len(data2) >= 5:
|
||||||
|
angle, vad_flag = struct.unpack_from("<HH", data2, 1)
|
||||||
|
return angle % 360, bool(vad_flag)
|
||||||
|
return 0, False
|
||||||
|
|
||||||
|
return int(angle_deg) % 360, vad
|
||||||
|
|
||||||
# --- LEDs ---
|
# --- LEDs ---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user