A minimal example application demonstrating how to build a synchronized multi-room audio player using the Sendspin SDK.
This console application:
- Discovers servers - Scans the network for Sendspin-compatible servers (like Music Assistant) via mDNS
- Connects - Establishes a WebSocket connection and performs clock synchronization
- Plays audio - Receives audio streams and plays them in sync with other Sendspin players
- Shows metadata - Logs track information (title, artist) as it changes
- .NET 10 SDK (or .NET 8 with minor modifications)
- Windows (this example uses NAudio with WASAPI)
- A Sendspin-compatible server running on your network (e.g., Music Assistant with the Sendspin provider enabled)
cd SendspinExample
dotnet runinfo: Program[0]
Sendspin SDK Example - Minimal Player
info: Program[0]
======================================
info: Program[0]
Scanning for Sendspin servers (5 seconds)...
info: MdnsServerDiscovery[0]
Found server: Music Assistant at ws://192.168.1.100:8927/sendspin
info: Program[0]
Found 1 server(s)
info: Program[0]
Connecting to ws://192.168.1.100:8927/sendspin...
info: Program[0]
Connection state: Connected
info: Program[0]
Clock sync converged! Offset: 1234567890.12ms, Drift: 0.5us/s
info: Program[0]
Connected to Music Assistant!
info: Program[0]
Audio should now be playing!
info: Program[0]
Now playing: Bohemian Rhapsody by Queen
info: Program[0]
Playback: Playing, Volume: 75%
SendspinExample/
├── Program.cs # Main application with all code in one file
└── SendspinExample.csproj # Project file with NuGet references
- SimpleWasapiPlayer - Implements
IAudioPlayerusing NAudio's WASAPI output - SampleSourceProvider - Bridges the SDK's
IAudioSampleSourceto NAudio'sISampleProvider - BufferedAudioSampleSource - Bridges
ITimedAudioBuffertoIAudioSampleSource
┌────────────────────────────────────────────────────────────┐
│ This Example │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ SimpleWasapiPlayer (implements IAudioPlayer) │ │
│ │ └── NAudio WasapiOut for audio output │ │
│ └──────────────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────────────┤
│ Sendspin SDK │
│ SendspinClientService AudioPipeline TimedAudioBuffer│
│ (protocol handling) (orchestration) (sync tracking) │
├────────────────────────────────────────────────────────────┤
│ SendspinConnection KalmanClockSync OpusDecoder │
│ (WebSocket) (timing) (audio decoding) │
└────────────────────────────────────────────────────────────┘
- SDK Wiki - Full documentation
- Getting Started - Step-by-step guide
- Building a Minimal Player - Detailed tutorial
- API Reference - Interface documentation
- NuGet Package - SDK package page
For better sync accuracy, use SyncCorrectionCalculator with ReadRaw():
var correctionProvider = new SyncCorrectionCalculator(
SyncCorrectionOptions.Default,
sampleRate: 48000,
channels: 2);
// In your audio callback:
var read = buffer.ReadRaw(samples, offset, count, currentTime);
correctionProvider.UpdateFromSyncError(
buffer.SyncErrorMicroseconds,
buffer.SmoothedSyncErrorMicroseconds);Replace SimpleWasapiPlayer with your platform's audio backend:
- Linux: PulseAudio, PipeWire, or ALSA
- macOS: AVAudioEngine or AudioToolbox
- Cross-platform: SDL2
MIT License - see the main windowsSpin repository for details.