Cross-platform audio device hotplug detection with debouncing for Windows, macOS, and Linux.
- π Cross-platform: Works on Windows, macOS, and Linux
- β‘ Debouncing: Coalesces rapid device changes to prevent callback storms
- π Async-ready: Supports both sync and async callbacks
- π― Focused: Does one thing well - detects audio device topology changes
- πͺΆ Lightweight: Minimal dependencies, platform-specific imports only when needed
# Install with uv
uv pip install audio-hotplug
# Or with pip
pip install audio-hotplugPlatform-specific dependencies are installed automatically based on your OS.
import asyncio
from audio_hotplug import create_monitor
def on_audio_devices_changed():
print("Audio devices changed!")
async def main():
loop = asyncio.get_running_loop()
# Create monitor with 200ms debounce
monitor = create_monitor(loop=loop, debounce_ms=200)
if monitor:
monitor.start(on_audio_devices_changed)
# Your application code here...
await asyncio.sleep(60)
monitor.stop()
asyncio.run(main())from audio_hotplug import create_monitor
def handle_change():
# Refresh your audio device list here
print("Device list changed!")
monitor = create_monitor()
if monitor:
monitor.start(handle_change)import asyncio
from audio_hotplug import create_monitor
async def handle_change():
# Async operations supported
await notify_websocket_clients()
print("Device list changed!")
async def main():
loop = asyncio.get_running_loop()
monitor = create_monitor(loop=loop)
if monitor:
monitor.start(handle_change)
await asyncio.sleep(3600)
monitor.stop()
asyncio.run(main())# Adjust debounce time (milliseconds)
monitor = create_monitor(debounce_ms=500) # Wait 500ms after last changeimport logging
logger = logging.getLogger("my_app.audio")
monitor = create_monitor(logger=logger)The library monitors OS-level audio device notifications:
- Windows: Uses Core Audio API (
IMMNotificationClient) viapycaw - macOS: Uses CoreAudio property listeners via
pyobjc-framework-CoreAudio - Linux: Uses
udevsubsystem monitoring viapyudev
When devices are added, removed, or change state, the library:
- Detects the OS notification
- Triggers the debouncer
- Coalesces multiple rapid changes
- Invokes your callback once after the debounce period
Creates a platform-specific audio device monitor.
Parameters:
loop(optional):asyncio.AbstractEventLoop- Event loop for callback schedulingdebounce_ms(optional):int- Milliseconds to wait before invoking callback (default: 200)logger(optional):logging.Logger- Custom logger instance
Returns:
AudioDeviceMonitorinstance orNoneif platform unsupported
Abstract base class for platform monitors.
Methods:
start(on_change: Callback)- Start monitoring, callon_changewhen devices changestop()- Stop monitoring (safe to call multiple times)
Callback signature:
# Sync callback
def on_change() -> None: ...
# Or async callback
async def on_change() -> None: ...| Platform | Dependency | Auto-installed |
|---|---|---|
| Windows | pycaw, comtypes |
β |
| macOS | pyobjc-framework-CoreAudio |
β |
| Linux | pyudev |
β |
Platform-specific dependencies are only installed on the relevant OS using package markers.
This project uses uv for development:
# Clone the repository
git clone https://github.com/LedFx/audio-hotplug.git
cd audio-hotplug
# Install with dev dependencies
uv sync --extra dev
# Run tests
uv run pytest
# Run example
uv run python examples/monitor_print.py
# Build package
uv run python -m build# Run all tests
uv run pytest -v
# Run with coverage
uv run pytest --cov=audio_hotplug --cov-report=html
# Run specific test
uv run pytest tests/test_debounce.pyContributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
MIT License - see LICENSE file for details.
Extracted from LedFx to provide a reusable, focused library for audio device hotplug detection.
- LedFx - Real-time LED visualization system
- sounddevice - Audio I/O library
- PortAudio - Cross-platform audio I/O library