/
/
/
1# Sendspin Source
2
3Exposes Sendspin clients that implement the [source role](https://github.com/Sendspin-Protocol/spec)
4(line-in, turntable preamp, microphone, Bluetooth receiver) as Music Assistant
5audio sources, playable on any player or group.
6
7## Why a separate provider?
8
9Music Assistant assigns each provider one type. The main Sendspin provider is a
10player provider, so it cannot also expose captured inputs through the plugin
11AudioSource interface. This separate plugin provides that interface while
12reusing the main provider's client connections.
13
14## How it works
15
16Every connected Sendspin client whose negotiated roles include the `source`
17family shows up as one AudioSource under Live Inputs. The source role only
18activates on paired connections, so an unpaired device never appears here.
19
20When a user plays a source, the provider sends the client a
21`server/command: start`. The client announces its native stream format,
22streams timestamped encoded audio up to the server, and the Sendspin server
23library decodes it back to PCM. This provider feeds that PCM into a clock
24bridge (`aiosendspin.audio.AsrcSourceBridge`) and serves Music Assistant a
25steady 48 kHz / 16-bit / stereo stream.
26
27## Design notes
28
29- **Fixed output format.** A source's native format is only known once the
30 client starts streaming, but Music Assistant needs the stream format before
31 that. The bridge converts whatever the client sends to the fixed declared
32 format, so format discovery never blocks stream setup.
33- **Clock bridge.** The client's capture clock (its ADC) and the consuming
34 player's clock drift relative to each other, and capture timestamps can be
35 gappy. The bridge holds a configurable target latency and folds drift
36 correction into a phase-continuous variable-rate resample (soxr).
37- **Silence-hold.** A real line-in goes silent when unplugged, it does not
38 stop. This provider mirrors that: when source audio stops flowing (stream
39 end, disconnect, client unavailable) the stream keeps playing silence rather
40 than ending. After the source timeout, the stream ends. This intentionally
41 differs from providers like Spotify Connect, which end the stream immediately
42 on pause: those have an upstream transport state to mirror, a line-in does not.
43- **Reconnect recovery.** A reconnect clears the client's start request, so the
44 provider re-sends it. The existing Music Assistant stream remains open with
45 silence and resumes live audio after the client reconnects and rebuilds its
46 latency buffer, provided audio returns before the source timeout. Audio
47 captured during the disconnect is lost. Recovery is scoped to reconnects: a
48 client that reports itself unavailable also has its start request cleared, but
49 announces nothing when it returns, so that stream runs out the source timeout
50 instead.
51- **Server-initiated streaming only.** Per the Sendspin spec, a source client
52 must not stream until the server asks. Streaming starts on source selection
53 and stops on unselection, so no bandwidth is spent while nobody listens.
54
55## Autostart
56
57Clients that advertise the `line_sense` feature report whether a signal is
58present on their input. Per the spec the server decides what to do with that,
59and this provider turns it into playback: pick a target under the device's own
60settings and the source starts there when a signal appears, and stops again
61after the signal has been gone for a minute. The target list covers everything
62that renders audio, groups and stereo pairs included, and a device that is a
63player itself defaults to playing its own line-in.
64
65Only transitions act, and the first report after a connect is recorded
66without acting, so a restart with the needle already down starts nothing. A
67source that is already streaming is never re-targeted, so moving it by hand
68survives the next transition. Autostop also closes a gap that exists without
69line-sense: a client keeps streaming silence after a record ends, so the
7030-second no-audio timeout never fires and the queue would otherwise sit
71playing silence indefinitely.
72
73Devices without `line_sense` have no trigger to offer, so they get no setting
74and stay manual.
75
76## Out of scope (for now)
77
78- Per-source latency overrides; the target latency is a provider-level
79 setting.
80