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

Note: This package is not yet available on PyPI. 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]"

Documentation

API documentation is available in the docs/ directory. To build the documentation:

cd docs
make html

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.2.0.tar.gz (35.7 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.2.0-py3-none-any.whl (32.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: aiosendspin_sounddevice-0.2.0.tar.gz
  • Upload date:
  • Size: 35.7 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.2.0.tar.gz
Algorithm Hash digest
SHA256 fce723b99a34e9c96ce58654ed380bb74250efefef8287ad76052d53f157d623
MD5 7715f5aaed877b4fb7c8b72287944df0
BLAKE2b-256 64cae59d51fbe31e14ac618e941b9cc2497d59b2e25100f70bad9296f6afbbea

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for aiosendspin_sounddevice-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 08de488cc2d28b7fc0b8ee41422d148994f1f50943502df61c06833391c821f6
MD5 9adba77a314f15727a88215fd23122c4
BLAKE2b-256 26bc290a48830d5054cf0c2cbc35210022d01273e32ea91e57af5e94def732b0

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