mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
audio/scream: multicast audio protocol
This commit is contained in:
parent
48f53e57be
commit
cff0ebade5
7 changed files with 228 additions and 1 deletions
77
sys/man/1/scream
Normal file
77
sys/man/1/scream
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
21
sys/src/cmd/audio/scream/mkfile
Normal file
21
sys/src/cmd/audio/scream/mkfile
Normal 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
|
71
sys/src/cmd/audio/scream/screamdec.c
Normal file
71
sys/src/cmd/audio/scream/screamdec.c
Normal 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);
|
||||
}
|
32
sys/src/cmd/audio/scream/screamenc.c
Normal file
32
sys/src/cmd/audio/scream/screamenc.c
Normal 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);
|
||||
}
|
13
sys/src/cmd/audio/scream/screamrecv
Executable file
13
sys/src/cmd/audio/scream/screamrecv
Executable 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'
|
13
sys/src/cmd/audio/scream/screamsend
Executable file
13
sys/src/cmd/audio/scream/screamsend
Executable 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
|
Loading…
Reference in a new issue