Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,46 @@ Use `--manufacturer` and `--product-name` to override the device identity report
sendspin daemon --name "Living Room" --manufacturer "Acme" --product-name "Living Room Speaker"
```

### Source Mode

Run as a **source** client to capture audio from a local input (line-in, turntable,
Bluetooth receiver, or microphone) and stream it *into* Sendspin. The server mixes
and distributes it to players like any other audio, so an analog input on one machine
can play back synchronized across the whole group.

```bash
# Capture the default input device and stream it to a discovered server
sendspin source

# Pick a specific server, input device, and codec
sendspin source --url ws://192.168.1.50:8927/sendspin --device 2 --codec flac

# Stream a 440 Hz sine test tone (no capture hardware needed)
sendspin source --input sine
```

List available capture devices:

```bash
sendspin audio-devices inputs
```

Key options:

- `--input {linein,sine}` — capture from a real input device, or generate a sine test tone. Defaults to `linein`; passing `--device` implies `linein`.
- `--device` — input device index or name (see `audio-devices inputs`).
- `--codec {pcm,opus,flac}` — codec used to encode captured audio before sending (default `pcm`). Capture is 16-bit.
- `--sample-rate` / `--channels` — capture format (default 48000 Hz, 2 channels).
- `--line-sense` — report input signal presence to the server via `client/state`; the server may use it to decide when to start/stop the source.

The **server** decides when a source streams: a source stays idle until the server
sends a `start` command, and stops on `stop` or disconnect. A device may run both the
`source` and `player` roles; when it does, it never plays its captured input locally —
it only plays back what the server distributes, staying in sync with the group.

Source-mode preferences (client id, last server, input/codec defaults) are persisted
to `~/.config/sendspin/settings-source.json`.

### Hooks

You can run external commands when audio streams start or stop. This is useful for controlling amplifiers, lighting, or other home automation:
Expand Down
32 changes: 32 additions & 0 deletions sendspin/audio_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,38 @@ def query_devices() -> list[AudioDevice]:
return result


@dataclass(slots=True)
class InputDevice:
"""Represents an audio input (capture) device."""

index: int
name: str
input_channels: int
sample_rate: float
is_default: bool


def query_input_devices() -> list[InputDevice]:
"""Query all available audio input (capture) devices."""
devices = sounddevice.query_devices()
default_input = int(sounddevice.default.device[0])

result: list[InputDevice] = []
for i in range(len(devices)):
dev = devices[i]
if dev["max_input_channels"] > 0:
result.append(
InputDevice(
index=i,
name=str(dev["name"]),
input_channels=int(dev["max_input_channels"]),
sample_rate=float(dev["default_samplerate"]),
is_default=(i == default_input),
)
)
return result


def _check_format(device: AudioDevice, rate: int, channels: int, dtype: str) -> bool:
"""Check if a specific audio format is supported by the device."""
try:
Expand Down
Loading
Loading