code: plan9front

Download patch

ref: cff0ebade5fb37b3d5614ae9ff76513e4b0e4640
parent: 48f53e57be61f7cee021fdb21849d4759770f722
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Dec 5 10:46:27 EST 2022

audio/scream: multicast audio protocol

diff: cannot open b/sys/src/cmd/audio/scream//null: file does not exist: 'b/sys/src/cmd/audio/scream//null'
--- /dev/null
+++ b/sys/man/1/scream
@@ -1,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
+
--- a/sys/src/cmd/audio/mkfile
+++ b/sys/src/cmd/audio/mkfile
@@ -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
 
--- /dev/null
+++ b/sys/src/cmd/audio/scream/mkfile
@@ -1,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
--- /dev/null
+++ b/sys/src/cmd/audio/scream/screamdec.c
@@ -1,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);
+}
--- /dev/null
+++ b/sys/src/cmd/audio/scream/screamenc.c
@@ -1,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);
+}
--- /dev/null
+++ b/sys/src/cmd/audio/scream/screamrecv
@@ -1,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'
--- /dev/null
+++ b/sys/src/cmd/audio/scream/screamsend
@@ -1,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