aiosendspin-sounddevice
⚠️ Work in Progress
This library is currently under active development. API might change heavily.
Python library for programmatic audio playback from Sendspin servers. Provides a clean API for connecting to Sendspin servers, receiving synchronized audio streams, and playing them through local audio devices with precise time synchronization, buffering, and drift correction.
Installation
Install from PyPI:
pip install aiosendspin-sounddevice
Or install from source:
git clone https://github.com/behesse/aiosendspin-sounddevice.git
cd aiosendspin-sounddevice
pip install .
For development with documentation support:
pip install -e ".[dev]"
The generated HTML documentation will be in docs/build/html/. See docs/README.md for more details.
Quick Start
import asyncio
from aiosendspin_sounddevice import SendspinAudioClient, SendspinAudioClientConfig
async def main():
config = SendspinAudioClientConfig(
url="ws://192.168.1.100:8080/sendspin",
client_id="my-client",
client_name="My Player",
)
client = SendspinAudioClient(config)
try:
await client.connect()
print("Connected! Playing audio... Press Ctrl+C to stop")
await client.wait_for_disconnect()
except KeyboardInterrupt:
print("\nStopping...")
finally:
await client.disconnect()
if __name__ == "__main__":
asyncio.run(main())
See examples/simple.py for a minimal working example, examples/comprehensive.py for all features, examples/discovery.py for server discovery, examples/controller.py for controller commands, or examples/tui.py for a full terminal UI example.
Architecture
Component Diagram
graph TB
SAC[SendspinAudioClient<br/>Main API - Connection, State Management, Event Handling, Controller Commands]
ASH[AudioStreamHandler<br/>Stream Lifecycle Management]
AP[AudioPlayer<br/>Audio Playback with Time Sync]
AS[AppState<br/>State Store]
ADM[AudioDeviceManager<br/>Device Discovery]
SD[ServiceDiscovery<br/>Server Discovery via mDNS]
SC[SendspinClient<br/>aiosendspin]
SAC -->|creates/manages| ASH
SAC -->|creates/manages| AS
SAC -->|uses| ADM
SAC -->|uses| SD
SAC -->|wraps| SC
ASH -->|creates/manages| AP
SAC -->|reads/updates| AS
SAC -->|sends commands| SC
ASH -->|uses| SC
AP -->|uses| SC
style SAC fill:#e1f5ff
style ASH fill:#fff4e1
style AP fill:#ffe1f5
style AS fill:#e1ffe1
style ADM fill:#f5e1ff
style SD fill:#e1f5e1
style SC fill:#ffe1e1
Component Responsibilities
SendspinAudioClient
- Purpose: Main public API for library users
- Responsibilities:
- Connection management (connect, disconnect)
- Configuration and initialization
- Event listener setup and delegation
- State query methods (get_metadata, get_playback_state, get_supported_commands, etc.)
- Controller commands (play, pause, next_track, previous_track, switch_group, toggle_play_pause)
- Volume control (set_volume)
- Timing metrics access
- Interactions:
- Creates and manages
AudioStreamHandler - Creates and manages
AppState - Uses
AudioDeviceManager(viaresolve_audio_device()) - Wraps
SendspinClientfrom aiosendspin - Updates
AppStatebased on server events - Delegates audio chunks to
AudioStreamHandler - Sends media commands to server via
SendspinClient
- Creates and manages
AudioStreamHandler
- Purpose: Manages audio stream lifecycle and format changes
- Responsibilities:
- Handles stream start/end/clear events
- Routes audio chunks to
AudioPlayer - Manages
AudioPlayerinitialization and reconfiguration - Clears audio queue on stream events
- Interactions:
- Creates and manages
AudioPlayerinstances - Receives audio chunks from
SendspinAudioClient - Receives stream events from
SendspinAudioClient - Calls
AudioPlayer.submit()for audio chunks - Calls
AudioPlayer.clear()on stream events
- Creates and manages
AudioPlayer
- Purpose: Low-level time-synchronized audio playback
- Responsibilities:
- Accepts audio chunks with server timestamps
- Time synchronization and drift correction
- Buffering and underrun prevention
- Playback speed adjustment for sync correction
- Gap/overlap detection and handling
- Volume and mute control
- Interactions:
- Receives audio chunks from
AudioStreamHandler - Uses
SendspinClient.compute_play_time()andcompute_server_time()for time sync - Outputs audio via
sounddevice - Provides timing metrics to
SendspinAudioClient
- Receives audio chunks from
AppState
- Purpose: Mirrors server state for client presentation
- Responsibilities:
- Stores playback state, metadata, volume, group info
- Tracks progress with interpolation support
- Provides
update_metadata()anddescribe()methods
- Interactions:
- Updated by
SendspinAudioClientevent handlers - Read by
SendspinAudioClientquery methods - Used for progress interpolation calculations
- Updated by
AudioDeviceManager
- Purpose: Audio device discovery and selection
- Responsibilities:
- Discovers available audio output devices
- Provides device lookup methods (by index, name)
- Caches device list
- Interactions:
- Used by
SendspinAudioClientviaresolve_audio_device()helper - Provides
AudioDeviceinstances for device selection - Static method
list_audio_devices()for public API
- Used by
ServiceDiscovery
- Purpose: mDNS-based server discovery
- Responsibilities:
- Discovers Sendspin servers on the local network via mDNS
- Tracks multiple discovered servers
- Provides continuous discovery or one-time discovery
- Interactions:
- Used independently or with
SendspinAudioClient - Provides
DiscoveredServerinstances with server information - Class method
discover_servers()for one-time discovery
- Used independently or with
Data Flow
Connection Flow
User → SendspinAudioClient.connect()
↓
SendspinClient.connect() (aiosendspin)
↓
Setup listeners → Event handlers update AppState
↓
AudioStreamHandler ready to receive chunks
Audio Playback Flow
Server → SendspinClient → SendspinAudioClient._handle_audio_chunk()
↓
AudioStreamHandler.on_audio_chunk()
↓
AudioPlayer.submit()
↓
sounddevice (audio output)
State Update Flow
Server → SendspinClient → SendspinAudioClient event handlers
↓
AppState.update_*()
↓
Optional user callbacks
↓
User query methods (get_metadata, etc.)
Event Handler Flow
Server Event → SendspinClient listener
↓
SendspinAudioClient._handle_*()
↓
AppState.update()
↓
Optional user callback (on_metadata_update, etc.)
↓
_print_event() (logs + on_event callback)
Controller Command Flow
User → SendspinAudioClient.play() / pause() / next_track() / etc.
↓
send_media_command() validates against supported_commands
↓
SendspinClient.send_group_command()
↓
Server processes command
↓
Server sends state update → Event Handler Flow
Server Discovery Flow
User → ServiceDiscovery.start() or ServiceDiscovery.discover_servers()
↓
mDNS service browser listens for _sendspin-server._tcp.local.
↓
Servers discovered → DiscoveredServer instances
↓
User selects server → SendspinAudioClient.connect(url)
Key Design Patterns
- Delegation:
SendspinAudioClientdelegates audio handling toAudioStreamHandler, which delegates playback toAudioPlayer - State Management:
AppStatecentralizes all state, updated by event handlers, read by query methods - Event-Driven: Server events trigger handlers that update state and call user callbacks
- Reference Design: All core audio playback and synchronization logic is based on the original sendspin-cli implementation.
External Dependencies
- aiosendspin.SendspinClient: WebSocket connection and protocol handling
- sounddevice: Audio output device access and playback
- numpy: Audio data processing
- zeroconf: mDNS service discovery for server discovery
Features
- ✅ Audio Playback: Time-synchronized audio playback with DAC-level precision
- ✅ Server Discovery: mDNS-based discovery of Sendspin servers on the network
- ✅ Controller Commands: Full support for media control (play, pause, next, previous, switch group)
- ✅ State Management: Real-time state tracking with metadata, progress, and volume
- ✅ Event Listeners: Optional callbacks for reactive programming
- ✅ Audio Device Management: Object-oriented audio device discovery and selection
- ✅ Progress Interpolation: Client-side progress calculation for smooth UI updates
Limitations
See FEATURE_COMPARISON.md for detailed comparison with sendspin-cli.
The library focuses on the player and controller roles. Server functionality and advanced features are not included.
License
Apache-2.0
Credits
Based on the sendspin-cli by the Sendspin Protocol authors.
Metadata
Release files for aiosendspin-sounddevice 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| aiosendspin_sounddevice-0.3.0.tar.gz | 35.9 kB | Without provenance | Details for aiosendspin_sounddevice-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| aiosendspin_sounddevice-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for aiosendspin_sounddevice-0.3.0-py3-none-any.whl |
Total release size: 68.7 kB
Release files / aiosendspin_sounddevice-0.3.0.tar.gz
| Download URL | aiosendspin_sounddevice-0.3.0.tar.gz |
|---|---|
| Size | 35.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d62854bf93b0ffe0dd0048df4170b4c16afc69dfe62f392f7f6e5b9b98befad4
|
|
BLAKE2b-256 checksum How to use checksums |
7587a2c0b37216f7fc956c8d06e36ad5145301c426eccd0558f628261f6a5418
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Release files / aiosendspin_sounddevice-0.3.0-py3-none-any.whl
| Download URL | aiosendspin_sounddevice-0.3.0-py3-none-any.whl |
|---|---|
| Size | 32.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62f9e98db1eacd25abe95fdd89ef057108c8fe5866a6fa2562e341338eed46bf
|
|
BLAKE2b-256 checksum How to use checksums |
123748990fec13e6fb0a6f489932bd4bb8e51d26986314148c582e99be3c3146
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Security for aiosendspin-sounddevice 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.