Step 6 of 8

Network Synchronization

One timeline, several machines, under 5 ms apart. Namespace: OwnaudioNET

One device is the server, the others are clients. Every client plays its own copy of the audio on its own clock, like a standalone player, and follows the server's transport: when the server plays, the client goes in where the server is; when it stops, the client stops where it stands; when it jumps, the client jumps after it. What the network adds is a tiny tempo trim (at most ±0.5%) that keeps the two heard positions together, and — rarely — one seek when they get more than 80 ms apart.

â„šī¸

The network never writes the client's clock. Losing the server is just a trim of 1.0 — nothing jumps, and with allowOfflinePlayback the music simply plays on. Create and start the AudioMixer before starting the server or client; the timeline tracks it follows are the mixer's FileSource and GroupSource sources.

How it stays together

Server Mode

The server answers the clients' pings, sends them its heard position, and announces itself once a second on each network card's broadcast address (port + 1) so clients can find it. Control playback on the server as usual — the clients follow.

C#
// Prerequisites: Initialize + Start + Create AudioMixer
await OwnaudioNet.InitializeAsync();
OwnaudioNet.Start();
var mixer = new AudioMixer(OwnaudioNet.Engine!.UnderlyingEngine);
mixer.Start();

var track = new FileSource("backing.mp3");
mixer.AddSource(track);

await OwnaudioNet.StartNetworkSyncServerAsync(port: 9876);

// From this point, control playback normally — clients follow play, pause, stop and seek:
track.Play();
mixer.Seek(42.0);

OwnaudioNet.StopNetworkSync();

Client Mode

The client loads the same song on its side and leaves the transport to the server. It does not need to call Play().

C#
await OwnaudioNet.InitializeAsync();
OwnaudioNet.Start();
var mixer = new AudioMixer(OwnaudioNet.Engine!.UnderlyingEngine);
mixer.Start();

var clientTrack = new FileSource("backing.mp3");
mixer.AddSource(clientTrack);

await OwnaudioNet.StartNetworkSyncClientAsync(
    serverAddress: null,             // null = find the server by its announcement, or "192.168.1.100"
    port: 9876,
    allowOfflinePlayback: true);     // keep playing if the server is lost
â„šī¸

The trim needs the Rust-native chain (the default): it rides on the native tracks' tempo, on top of whatever Tempo you set. source.Tempo itself never shows it.

Stop Network Sync

C#
OwnaudioNet.StopNetworkSync();

Events & commands

Transport needs no messages. Anything else the clients should do — a tempo change, a song change — the server sends with BroadcastCommand; each command is sent three times and arrives once.

C#
// Client
OwnaudioNet.NetworkSyncConnectionChanged += (_, e) =>
    Console.WriteLine($"Network sync state: {e.OldState} → {e.NewState}");

OwnaudioNet.NetworkSyncCommandReceived += (_, e) =>
{
    if (e.Command.Type == NetworkSyncProtocol.CommandType.Tempo)
        clientTrack.Tempo = e.Command.TempoValue;   // raised on a sync thread
};

// Server
var tempo = NetworkSyncProtocol.CreateTempoCommand(0, 1.05f, useSmooth: false);
OwnaudioNet.BroadcastCommand(ref tempo);

// Either side
var status = OwnaudioNet.GetNetworkSyncStatus();
Console.WriteLine($"{status.ConnectionState}, one-way {status.AverageLatency:F1} ms, off by {status.SyncDrift:F1} ms");

A following client also writes one [NetSync] log line a minute — average and worst gap, the share within 5 ms, trims, seeks, jitter and round trip.

Use Cases

🎤
Live Performance
Backing track server controls the click track; musicians' tablets follow automatically.
🔊
Multi-Room Audio
One server controls multiple zone players across a venue.
🎧
DJ Setup
Sync two machines for seamless handoffs or redundant playback.
đŸ›ī¸
Installation Art
Synchronized audio-visual installations with multiple playback nodes.

API Parameters

StartNetworkSyncServerAsync

ParameterTypeDefaultDescription
portint9876UDP port the clients ping; announcements go to port + 1.
useLocalTimeOnlybooltrueKept for compatibility, no effect — the sync never needs the internet.

StartNetworkSyncClientAsync

ParameterTypeDefaultDescription
serverAddressstring?nullServer host or IP address. null = find it by its announcements.
portint9876The server's UDP port. The client itself binds a free port.
allowOfflinePlaybackbooltrueKeep playing when the server is lost; false pauses.

Next