audio/scream: multicast audio protocol

This commit is contained in:
cinap_lenrek 2022-12-05 15:46:27 +00:00
parent 48f53e57be
commit cff0ebade5
7 changed files with 228 additions and 1 deletions

77
sys/man/1/scream Normal file
View file

@ -0,0 +1,77 @@
.TH SCREAM 1
.SH NAME
screamsend, screamrecv, screamenc, screamdec \- multicast audio protocol
.SH SYNOPSIS
.B audio/screamsend
[
.I interfaceip
]...
.br
.B audio/screamrecv
[
.I interfaceip
]...
.br
.B audio/screamenc
.br
.B audio/screamdec
.SH DESCRIPTION
.PP
Scream is a simple network protocol for transmitting PCM audio on a local network.
It sends UDP packets at a constant rate to the multicast address
.B 239.255.77.77
on port
.BR 4010 .
Each packet starts with a small 5-byte header that contains information about the
sample-rate and data format followed raw PCM data payload (maximum 1157 bytes).
.PP
.I Screamsend
reads PCM audio from
.B /dev/audio
and sends scream packets to the local network on the interface given by
.IR interfaceip.
When
.I interfaceip
is omitted, it uses first IPv4 interface ip address from
.B /net/ipselftab
as a default.
.PP
.I Screamrecv
listens for packets from the local network on the interfaces
selected by
.IR interfaceip
and writes PCM audio to
.BR /dev/audio .
When no
.I interfaceip
addresses where given, it will listen on all interfaces with an IPv4 address.
.PP
Both
.I screamsend
and
.I screamrecv
are usually run after
.I audio/mixfs
(see
.IR audio (1))
to provide loopback audio source as well as mixing for multiple senders.
.PP
.I Screamenc
reads PCM audio from standard-input and writes scream packets to standard-output.
.PP
.I Screamdec
reads scream packets from standard-input, and writes PCM audio to standard-output.
It spawns
.I audio/pcmconv
(see
.IR audio (1))
to convert the audio in case the scream packet format is not
the default of 16-bit little-endian stereo samples at 44100 Hz.
It exits when no packets have arrived for 500 milliseconds.
.SH SOURCE
.B /sys/src/cmd/audio/scream
.SH SEE ALSO
.IR audio (1).
.br
.B https://github.com/duncanthrax/scream

View file

@ -1,7 +1,7 @@
</$objtype/mkfile
LIBS=libogg libvorbis libFLAC libtags
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec flacenc wavdec sundec mixfs readtags zuke
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec flacenc wavdec sundec mixfs readtags zuke scream
#libs must be made first
DIRS=$LIBS $PROGS

View file

@ -0,0 +1,21 @@
</$objtype/mkfile
<../config
TARG=screamenc screamdec
RC=screamsend screamrecv
</sys/src/cmd/mkmany
$O.screamenc: screamenc.$O
$O.screamdec: screamdec.$O
# Override install target to install rc.
install:V:
for (i in $TARG)
mk $MKFLAGS $i.install
for (i in $RC)
mk $MKFLAGS $i.rcinstall
%.rcinstall:V:
cp $stem $BIN/$stem
chmod +x $BIN/$stem

View file

@ -0,0 +1,71 @@
#include <u.h>
#include <libc.h>
char deffmt[] = "s16c2r44100";
char fmt[64];
uchar hdr[5];
uchar buf[2048];
int pfd[2];
char*
getformat(uchar hdr[5])
{
int freq, bits, chan;
if(hdr[0] & 0x80)
freq = 44100;
else
freq = 48000;
freq *= hdr[0] & 0x7F;
bits = hdr[1];
chan = hdr[2];
snprint(fmt, sizeof(fmt), "s%dc%dr%d", bits, chan, freq);
return fmt;
}
void
main(void)
{
int n;
for(;;){
alarm(500);
n = read(0, buf, sizeof(buf));
if(n < sizeof(hdr))
break;
if(pfd[1] == 0 || memcmp(buf, hdr, sizeof(hdr)) != 0){
if(pfd[1] > 1){
close(pfd[1]);
waitpid();
}
if(strcmp(getformat(buf), deffmt) == 0){
pfd[1] = 1;
} else {
if(pipe(pfd) < 0)
sysfatal("pipe: %r");
switch(fork()){
case -1:
sysfatal("fork: %r");
case 0:
close(pfd[1]);
dup(pfd[0], 0);
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, nil);
sysfatal("exec: %r");
return;
}
close(pfd[0]);
}
memmove(hdr, buf, sizeof(hdr));
}
n -= sizeof(hdr);
if(n <= 0)
continue;
if(write(pfd[1], buf+sizeof(hdr), n) != n)
break;
}
exits(nil);
}

View file

@ -0,0 +1,32 @@
#include <u.h>
#include <libc.h>
int freq = 44100;
int chan = 2;
int bps = 16;
int delay = 5;
uchar buf[2048];
void
main(void)
{
int n, m;
if((freq % 44100) == 0){
buf[0] = 0x80 | (freq / 44100);
} else {
buf[0] = freq / 48000;
}
buf[1] = bps;
buf[2] = chan;
buf[3] = 0;
buf[4] = 0;
n = (bps/8)*chan*((delay*freq+999)/1000);
while((m = read(0, buf+5, n)) > 0){
if(write(1, buf, 5+m) < 0)
sysfatal("write: %r");
}
exits(nil);
}

View file

@ -0,0 +1,13 @@
#!/bin/rc
rfork e
lifc=()
while(~ $1 *.*.*.*){
lifc=($lifc $1)
shift
}
if(! ~ $#* 0){
echo 'Usage: audio/screamrecv [interfaceip]...' >[1=2]
exit 'usage'
}
if(~ $#lifc 0) lifc=`{awk '/4u/{print $1}' /net/ipselftab}
exec aux/listen1 -t -p4 -O^'addmulti '^$lifc udp!239.255.77.77!4010 rc -c 'exec audio/screamdec > /dev/audio'

View file

@ -0,0 +1,13 @@
#!/bin/rc
rfork e
lifc=()
while(~ $1 *.*.*.*){
lifc=($lifc $1)
shift
}
if(! ~ $#* 0){
echo 'Usage: audio/screamsend [interfaceip]...' >[1=2]
exit 'usage'
}
if(~ $#lifc 0) lifc=`{awk '/4u/{print $1}' /net/ipselftab}
exec audio/screamenc < /dev/audio | exec aux/trampoline -o^'addmulti '^$lifc udp!239.255.77.77!4010