Repository: ErikBjare/gptme
File: gptme/tools/tts.py (Python)

Code before:
            f"  [{i}] {dev['name']} (in: {dev['max_input_channels']}, "
            f"out: {dev['max_output_channels']}, hostapi: {dev['hostapi']})"
        )

    # Try using system default output device, preferring virtual devices
    try:
        default_output = sd.default.device[1]
        if default_output is not None:
            device_info = sd.query_devices(default_output)
            device_name = device_info["name"].lower()

            # Check if it's a virtual device (PulseAudio/PipeWire)
            if device_info["max_output_channels"] > 0 and device_name in [
                "pulse",
                "pipewire",
            ]:
                log.debug(f"Using virtual audio device: {device_info['name']}")
                return default_output, int(device_info["default_samplerate"])
            else:
                log.debug(f"Skipping non-virtual default device: {device_info['name']}")
    except Exception as e:
        log.debug(f"Could not use default device: {e}")

    # Second try: platform-specific defaults
    # - On macOS: prefer CoreAudio (hostapi == 2)
    # - On Linux: prefer PulseAudio/PipeWire virtual devices
    output_device = next(
        (
            i
            for i, d in enumerate(devices)
            if d["max_output_channels"] > 0
            and (
                # macOS: CoreAudio devices
                (d["hostapi"] == 2)
                or
                # Linux: PulseAudio/PipeWire virtual devices
                d["name"].lower() in ["pulse", "pipewire"]
            )
        ),
        None,
    )

    # Third try: any device with output channels
    if output_device is None:
        output_device = next(
            (i for i, d in enumerate(devices) if d["max_output_channels"] > 0),
            None,
        )

    if output_device is None:
        raise RuntimeError(

Review comment (suggestion):
```suggestion
    # Second try: on macOS, prefer CoreAudio (hostapi == 2)
```

Code after:
            f"  [{i}] {dev['name']} (in: {dev['max_input_channels']}, "
            f"out: {dev['max_output_channels']}, hostapi: {dev['hostapi']})"
        )

    # Try using system default output device, preferring virtual devices
    try:
        default_output = sd.default.device[1]
        if default_output is not None:
            device_info = sd.query_devices(default_output)
            device_name = device_info["name"].lower()

            # Check if it's a virtual device (PulseAudio/PipeWire)
            if device_info["max_output_channels"] > 0 and device_name in [
                "pulse",
                "pipewire",
            ]:
                log.debug(f"Using virtual audio device: {device_info['name']}")
                return default_output, int(device_info["default_samplerate"])
            else:
                log.debug(f"Skipping non-virtual default device: {device_info['name']}")
    except Exception as e:
        log.debug(f"Could not use default device: {e}")

    # on macOS, prefer CoreAudio (hostapi == 2)
    output_device = next(
        (
            i
            for i, d in enumerate(devices)
            if d["max_output_channels"] > 0 and (d["hostapi"] == 2)
        ),
        None,
    )

    # Third try: any device with output channels
    if output_device is None:
        output_device = next(
            (i for i, d in enumerate(devices) if d["max_output_channels"] > 0),
            None,
        )

    if output_device is None:
        raise RuntimeError(
            "No suitable audio output device found. "
            "Available devices:\n"
            + "\n".join(f"  {i}: {d['name']}" for i, d in enumerate(devices))
        )

    device_info = sd.query_devices(output_device)
    device_sr = int(device_info["default_samplerate"])

    log.debug(f"Selected output device: {output_device} ({device_info['name']})")