snd-udpplay - Play UDP (broadcast) audio frames with frame rate compensation. This program has been tested on a Raspberry Pi (install libasound2-dev and ctags for this) with a Behringer UCA 202 USB audio device in a somewhat larger setup with a websocket based server program for broadcasting UDP audio frames (not yet included here). The server should transmit UDP/IP packets to port 13012 containing: - 32 bit sequence number, incremented for every packet. - 350 frames of 2 channel 16 bit little endian audio samples with 48kHz frame rate. A significant sequence number jump will cause snd-udpplay to resynchronize its stream. Duplicates and lost packets are handled gracefully. Incoming packets are not reordered. There is no return traffic to the server. snd-udpplay can run on multiple boxes providing synchronous audio playback via Ethernet. The frame rate at the server and in all snd-udpplay instances is basically 48 kHz but the corresponding oscillators in hardware may deviate slightly. Every time when an UDP packet is received (on average every 7.3ms) the program will examine the amount of free ALSA buffer space. After 100 packets the lowest free ALSA buffer space seen is used to decide if we should very slowly insert or remove a few audio frames, until the next 100 packets have been received for a repeat. This serves two purposes: - avoid ALSA buffer underrun/overrun because of small 48kHz frame rate differences, i.e. frame rate compensation. - maintain identical audio delay everywhere. The delay is a function of snd-udpplay buffersize and audio hardware so it's best to use the same ALSA playback device everywhere. Do not use Pulse-Audio or anything else between snd-udpplay and the ALSA playback device. Example "snd-udpplay -v" output illustrating frame rate compensation at work: - Every line covers 0.73s (100 packets of 350 frames). - err=-59 means we should synthesize 59 frames to catch up with the server frame rate. A positive number means we should drop frames. - adj=0 means the delta was considered too small to compensate for. The thresholds are dynamic. - '+' means frame inserted, '-' means frame deleted. Only once per packet of 350 frames and with multiple packets in between. The server and ALSA frame rates are assumed to be very close to 48kHz after all. err=-9 -> adj=0 err=-9 -> adj=0 err=-21 -> adj=0 err=-9 -> adj=0 err=-21 -> adj=0 err=-59 -> adj=59 ++++++++++++++++++ err=-18 -> adj=0 err=-3 -> adj=0 err=9 -> adj=0 err=9 -> adj=0 err=-3 -> adj=0 err=-41 -> adj=41 +++++++++++++++++ err=-1 -> adj=0 err=26 -> adj=-26 ------------------ err=13 -> adj=0 err=-4 -> adj=0 err=-4 -> adj=0 err=-42 -> adj=42 +++++++++++++++++ err=-2 -> adj=0 err=13 -> adj=0 err=13 -> adj=0 err=13 -> adj=0 err=13 -> adj=0 err=-25 -> adj=25 ++++++++++++++++++ err=16 -> adj=0 err=31 -> adj=-31 ----------------- err=22 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=-24 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=-24 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=-24 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=-24 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=14 -> adj=0 err=-24 -> adj=0 err=-24 -> adj=0