Skip to content

Commit ca1b56b

Browse files
committed
add config for audio devices
1 parent a26fcba commit ca1b56b

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

examples/wakeword_agent_dispatch/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ LIVEKIT_ROOM=wakeword-preconnect
1515
LIVEKIT_AGENT_NAME=test-agent
1616
LIVEKIT_AGENT_METADATA=
1717

18+
# Optional PortAudio device indices. Leave blank to use system defaults.
19+
LIVEKIT_AUDIO_INPUT_DEVICE=
20+
LIVEKIT_AUDIO_OUTPUT_DEVICE=
21+
1822
# Wake word model settings
1923
LIVEKIT_WAKEWORD_MODEL=./models/hey_livekit.onnx
2024
LIVEKIT_WAKEWORD_NAME=hey_livekit

examples/wakeword_agent_dispatch/client.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class Config:
4343
preconnect_debug_wav: Path | None
4444
agent_metadata: str
4545
agent_wait_timeout: float
46+
audio_input_device: int | None
47+
audio_output_device: int | None
4648

4749
@staticmethod
4850
def from_env() -> "Config":
@@ -78,8 +80,44 @@ def from_env() -> "Config":
7880
),
7981
agent_metadata=os.getenv("LIVEKIT_AGENT_METADATA", ""),
8082
agent_wait_timeout=float(os.getenv("LIVEKIT_AGENT_WAIT_TIMEOUT", "30.0")),
83+
audio_input_device=Config._optional_int_from_env(
84+
"LIVEKIT_AUDIO_INPUT_DEVICE"
85+
),
86+
audio_output_device=Config._optional_int_from_env(
87+
"LIVEKIT_AUDIO_OUTPUT_DEVICE"
88+
),
8189
)
8290

91+
@staticmethod
92+
def _optional_int_from_env(name: str) -> int | None:
93+
raw = os.getenv(name)
94+
if raw is None or not raw.strip():
95+
return None
96+
97+
try:
98+
return int(raw)
99+
except ValueError:
100+
raise RuntimeError(f"{name} must be an integer device index") from None
101+
102+
103+
def _describe_audio_device(device_index: int | None, kind: str) -> str:
104+
import sounddevice as sd # type: ignore[import-not-found, import-untyped]
105+
106+
selected_index = device_index
107+
if selected_index is None:
108+
default_device = sd.default.device
109+
if isinstance(default_device, (list, tuple)):
110+
selected_index = default_device[0 if kind == "input" else 1]
111+
112+
if selected_index is None:
113+
return "system default"
114+
115+
try:
116+
device = sd.query_devices(selected_index)
117+
return f"{selected_index} ({device['name']})"
118+
except Exception:
119+
return str(selected_index)
120+
83121

84122
@dataclass(frozen=True)
85123
class MicChunk:
@@ -573,17 +611,25 @@ async def main() -> None:
573611
shutdown_event = asyncio.Event()
574612

575613
model = WakeWordModel(models=[str(config.wakeword_model)])
576-
room = rtc.Room()
577-
lkapi = api.LiveKitAPI(config.url, config.api_key, config.api_secret)
578614
audio_queue: asyncio.Queue[MicChunk] = asyncio.Queue(maxsize=100)
579615
devices = rtc.MediaDevices(
580616
input_sample_rate=SAMPLE_RATE,
581617
output_sample_rate=OUTPUT_SAMPLE_RATE,
582618
num_channels=NUM_CHANNELS,
583619
blocksize=OUTPUT_FRAME_SAMPLES,
584620
)
585-
mic = devices.open_input(enable_aec=True)
586-
player = devices.open_output()
621+
logger.info(
622+
"using audio devices input=%s output=%s",
623+
_describe_audio_device(config.audio_input_device, "input"),
624+
_describe_audio_device(config.audio_output_device, "output"),
625+
)
626+
mic = devices.open_input(
627+
enable_aec=True,
628+
input_device=config.audio_input_device,
629+
)
630+
player = devices.open_output(output_device=config.audio_output_device)
631+
room = rtc.Room()
632+
lkapi = api.LiveKitAPI(config.url, config.api_key, config.api_secret)
587633
background_tasks: set[asyncio.Task[None]] = set()
588634

589635
def _create_background_task(coro) -> None:

0 commit comments

Comments
 (0)