diff --git a/src/reachy_mini/media/audio_sounddevice.py b/src/reachy_mini/media/audio_sounddevice.py index 04b4128b..6660fad3 100644 --- a/src/reachy_mini/media/audio_sounddevice.py +++ b/src/reachy_mini/media/audio_sounddevice.py @@ -181,40 +181,56 @@ def _clean_up_thread() -> None: daemon=True, ).start() - def get_output_device_id(self, name_contains: str) -> int: - """Return the output device id whose name contains the given string (case-insensitive). + def _find_device_id( + self, name_contains: str, device_type: str + ) -> int: + """Find device ID by name and type with fallback logic. + + Args: + name_contains: Substring to search for in device name (case-insensitive) + device_type: Either "input" or "output" - If not found, return the default output device id. + Returns: + Device index + + Raises: + RuntimeError: If no device with appropriate channels found """ devices = sd.query_devices() + channel_key = f"max_{device_type}_channels" + # First try: Search for device by specific name (e.g., "respeaker") for idx, dev in enumerate(devices): if ( name_contains.lower() in dev["name"].lower() - and dev["max_output_channels"] > 0 + and dev.get(channel_key, 0) > 0 ): return idx - # Return default output device if not found + + # Log warning if device with specific name not found self.logger.warning( - f"No output device found containing '{name_contains}', using default." + f"No {device_type} device containing '{name_contains}' found. Using first available {device_type} device." + ) + + # Fallback: Return first device with appropriate channels + for idx, dev in enumerate(devices): + if dev.get(channel_key, 0) > 0: + return idx + + raise RuntimeError( + f"No {device_type} audio device with {device_type} channels found." ) - return int(sd.default.device[1]) + + def get_output_device_id(self, name_contains: str) -> int: + """Return the output device id whose name contains the given string (case-insensitive). + + If not found, return the first available output device. + """ + return self._find_device_id(name_contains, "output") def get_input_device_id(self, name_contains: str) -> int: """Return the input device id whose name contains the given string (case-insensitive). - If not found, return the default input device id. + If not found, return the first available input device. """ - devices = sd.query_devices() - - for idx, dev in enumerate(devices): - if ( - name_contains.lower() in dev["name"].lower() - and dev["max_input_channels"] > 0 - ): - return idx - # Return default input device if not found - self.logger.warning( - f"No input device found containing '{name_contains}', using default." - ) - return int(sd.default.device[1]) + return self._find_device_id(name_contains, "input") \ No newline at end of file