Skip to main content

Python library wrapping aiosendspin and sounddevice for programmatic audio playback

Project description

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 (via resolve_audio_device())
    • Wraps SendspinClient from aiosendspin
    • Updates AppState based on server events
    • Delegates audio chunks to AudioStreamHandler
    • Sends media commands to server via SendspinClient

AudioStreamHandler

  • Purpose: Manages audio stream lifecycle and format changes
  • Responsibilities:
    • Handles stream start/end/clear events
    • Routes audio chunks to AudioPlayer
    • Manages AudioPlayer initialization and reconfiguration
    • Clears audio queue on stream events
  • Interactions:
    • Creates and manages AudioPlayer instances
    • Receives audio chunks from SendspinAudioClient
    • Receives stream events from SendspinAudioClient
    • Calls AudioPlayer.submit() for audio chunks
    • Calls AudioPlayer.clear() on stream events

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() and compute_server_time() for time sync
    • Outputs audio via sounddevice
    • Provides timing metrics to SendspinAudioClient

AppState

  • Purpose: Mirrors server state for client presentation
  • Responsibilities:
    • Stores playback state, metadata, volume, group info
    • Tracks progress with interpolation support
    • Provides update_metadata() and describe() methods
  • Interactions:
    • Updated by SendspinAudioClient event handlers
    • Read by SendspinAudioClient query methods
    • Used for progress interpolation calculations

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 SendspinAudioClient via resolve_audio_device() helper
    • Provides AudioDevice instances for device selection
    • Static method list_audio_devices() for public API

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 DiscoveredServer instances with server information
    • Class method discover_servers() for one-time discovery

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

  1. Delegation: SendspinAudioClient delegates audio handling to AudioStreamHandler, which delegates playback to AudioPlayer
  2. State Management: AppState centralizes all state, updated by event handlers, read by query methods
  3. Event-Driven: Server events trigger handlers that update state and call user callbacks
  4. 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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiosendspin_sounddevice-0.3.0.tar.gz (35.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiosendspin_sounddevice-0.3.0-py3-none-any.whl (32.8 kB view details)

Uploaded Python 3

File details

Details for the file aiosendspin_sounddevice-0.3.0.tar.gz.

File metadata

  • Download URL: aiosendspin_sounddevice-0.3.0.tar.gz
  • Upload date:
  • Size: 35.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for aiosendspin_sounddevice-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d62854bf93b0ffe0dd0048df4170b4c16afc69dfe62f392f7f6e5b9b98befad4
MD5 4f6e40bcb391a6c9bce436fefe9fe5b8
BLAKE2b-256 7587a2c0b37216f7fc956c8d06e36ad5145301c426eccd0558f628261f6a5418

See more details on using hashes here.

File details

Details for the file aiosendspin_sounddevice-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for aiosendspin_sounddevice-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62f9e98db1eacd25abe95fdd89ef057108c8fe5866a6fa2562e341338eed46bf
MD5 6a184887816e3bed6e71e55cac0dd96a
BLAKE2b-256 123748990fec13e6fb0a6f489932bd4bb8e51d26986314148c582e99be3c3146

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page