shithub: riscv

Download patch

ref: 483ff27f9d5067fd597dae09161d07a3857293b6
parent: ec628c4dfbf62891e311567034936a0b2cb584c4
author: Jacob Moody <moody@posixcafe.org>
date: Wed Aug 23 11:57:00 EDT 2023

games/voc^(enc dec): move from audio and add encoder

diff: cannot open a/sys/src/cmd/audio/vocdec//null: file does not exist: 'a/sys/src/cmd/audio/vocdec//null'
--- a/rc/bin/play
+++ b/rc/bin/play
@@ -70,7 +70,7 @@
 		case *audio/mus*
 			games/mus | midi
 		case *audio/x-voc*
-			audio/vocdec
+			games/vocdec
 		case *pls*
 			awk 'BEGIN {FS="="} /^File/{print $2}' | play1 list plain
 		case *
--- a/sys/man/1/audio
+++ b/sys/man/1/audio
@@ -1,6 +1,6 @@
 .TH AUDIO 1
 .SH NAME
-mp3dec, mp3enc, oggdec, oggenc, flacdec, flacenc, sundec, wavdec, vocdec, pcmconv, mixfs \- decode and encode audio files
+mp3dec, mp3enc, oggdec, oggenc, flacdec, flacenc, sundec, wavdec pcmconv, mixfs \- decode and encode audio files
 .SH SYNOPSIS
 .B audio/mp3dec
 [
@@ -29,8 +29,6 @@
 ]
 .br
 .B audio/sundec
-.br
-.B audio/vocdec
 .PP
 .B audio/oggenc
 .br
--- /dev/null
+++ b/sys/man/1/vocdec
@@ -1,0 +1,45 @@
+.TH VOCDEC 1
+.SH NAME
+vocdec, vocenc \- decode and encode VOC files
+.SH SYNOPSIS
+.B games/vocdec
+.br
+.B games/vocenc
+.I fmt
+.SH DESCRIPTION
+.I Vocdec
+and
+.I vocenc
+process and create Creative Voice formatted audio files.
+.PP
+.I Vocdec
+reads a VOC file on stdin and outputs a raw audio stream
+in the default format understood by
+.BR /dev/audio ,
+that is signed 16 bit little endian, two channels, 44.1kHz.
+.PP
+.I Vocenc
+reads raw audio on stdin and outputs a VOC formatted file
+on stdout. The
+.I fmt
+argument specifies both the input and output raw audio format
+using the same string format as
+.IR pcmconv (1).
+.SH EXAMPLES
+Create a VOC file from microphone input:
+.IP
+.EX
+</dev/audio audio/pcmconv -o u8c1r44100 | \\
+  games/vocenc u8c1r44100 >out.voc
+.EE
+.SH SOURCE
+.B /sys/src/games/vocdec.c
+.br
+.B /sys/src/games/vocenc.c
+.SH SEE ALSO
+.IR audio (1),
+.IR audio (3),
+.IR play (1)
+.SH HISTORY
+Vocdec first appeared in 9front (February, 2023).
+Vocenc was added later (August, 2023).
--- 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 vocdec mixfs readtags zuke scream
+PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec flacenc wavdec sundec mixfs readtags zuke scream
 #libs must be made first
 DIRS=$LIBS $PROGS
 
--- a/sys/src/cmd/audio/vocdec/mkfile
+++ /dev/null
@@ -1,8 +1,0 @@
-</$objtype/mkfile
-<../config
-
-OFILES=vocdec.$O
-
-TARG=vocdec
-
-</sys/src/cmd/mkone
--- a/sys/src/cmd/audio/vocdec/vocdec.c
+++ /dev/null
@@ -1,149 +1,0 @@
-#include <u.h>
-#include <libc.h>
-
-uchar
-get(void)
-{
-	uchar b;
-
-	if(read(0, &b, 1) != 1)
-		sysfatal("read: %r");
-	return b;
-}
-
-uint
-get2(void)
-{
-	uchar b[2];
-
-	if(readn(0, b, 2) != 2)
-		sysfatal("read: %r");
-	return (b[0]<<0) | (b[1]<<8);
-}
-
-uint
-get3(void)
-{
-	uchar b[3];
-
-	if(readn(0, b, 3) != 3)
-		sysfatal("read: %r");
-	return (b[0]<<0) | (b[1]<<8) | (b[2]<<16);
-}
-
-uint
-get4(void)
-{
-	uchar b[4];
-
-	if(readn(0, b, 4) != 4)
-		sysfatal("read: %r");
-	return (b[0]<<0) | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
-}
-
-typedef struct Block Block;
-struct Block {
-	uchar type;
-	uint size;
-	uint freq;
-	uint codec;
-	uchar chan;
-	uint bits;
-};
-
-char*
-codec(int c)
-{
-	switch(c){
-	case 0x00:
-		return "u8";
-	case 0x04:
-		return "s16";
-	case 0x06:
-		return "µ8";
-	case 0x07:
-		return "a8";
-	default:
-		sysfatal("unsupported");
-	}
-	return nil;
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: %s", argv0);
-	exits("usage");
-}
-
-void
-main(int argc, char **argv)
-{
-	Block b;
-	static uchar buf[2048];
-	char fmt[32], size[32];
-	ushort chksum, ver;
-	int n;
-
-	ARGBEGIN{
-	default:
-		usage();
-		break;
-	}ARGEND;
-
-	if(readn(0, buf, 20) != 20 || memcmp(buf, "Creative Voice File\x1a", 20) != 0)
-		sysfatal("not a voc file");
-
-	get2(); /* gulp */
-	ver = get2();
-	chksum = get2();
-	if(~ver + 0x1234 != chksum)
-		sysfatal("invalid checksum");
-
-	memset(&b, 0, sizeof b);
-	for(;;){
-		/* files may end without a proper block */
-		if(read(0, &b.type, 1) != 1 || b.type == 0)
-			break;
-		b.size = get3();
-
-		switch(b.type){
-		case 1:
-			b.freq = 1000000 / (256 - get());
-			b.codec = get();
-			b.chan = 1;
-			b.size -= 2;
-			break;
-		case 2:
-			if(b.freq == 0)
-				sysfatal("block 2 without defined codec");
-			break;
-		case 9:
-			b.freq = get4();
-			b.bits = get();
-			b.chan = get();
-			b.codec = get2();
-			get4(); /* reserved */
-			b.size -= 4+1+1+2+4;
-			break;
-		default:
-			while(b.size != 0){
-				n = b.size;
-				if(n > sizeof buf)
-					n = sizeof buf;
-				if(readn(0, buf, n) != n)
-					break;
-				b.size -= n;
-			}
-			break;
-		}
-
-		snprint(fmt, sizeof fmt, "%sc%dr%d", codec(b.codec), b.chan, b.freq);
-		snprint(size, sizeof size, "%d", b.size);
-		if(fork() == 0){
-			execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", size, nil);
-			sysfatal("exec: %r");
-		}
-		waitpid();
-	}
-}
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -21,6 +21,8 @@
 	dpic\
 	todpic\
 	turtle\
+	vocenc\
+	vocdec\
 
 OFILES=
 HFILES=
--- /dev/null
+++ b/sys/src/games/vocdec.c
@@ -1,0 +1,118 @@
+#include <u.h>
+#include <libc.h>
+
+#define GET16(p) ((u16int)(p)[0] | (u16int)(p)[1]<<8)
+#define GET24(p) ((u32int)(p)[0] | (u32int)(p)[1]<<8 | (u32int)(p)[2]<<16)
+#define GET32(p) ((u32int)(p)[0] | (u32int)(p)[1]<<8 | (u32int)(p)[2]<<16 | (u32int)(p)[3]<<24)
+
+typedef struct Block Block;
+struct Block {
+	uchar type;
+	uint size;
+	uint freq;
+	uint codec;
+	uchar chan;
+	uint bits;
+};
+
+char*
+codec(int c)
+{
+	switch(c){
+	case 0x00:
+		return "u8";
+	case 0x04:
+		return "s16";
+	case 0x06:
+		return "µ8";
+	case 0x07:
+		return "a8";
+	default:
+		sysfatal("unsupported");
+	}
+	return nil;
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s", argv0);
+	exits("usage");
+}
+
+Block b;
+
+void
+main(int argc, char **argv)
+{
+	uchar buf[8192];
+	char fmt[32], size[32];
+	ushort chksum, ver;
+	int n;
+
+	ARGBEGIN{
+	default:
+		usage();
+		break;
+	}ARGEND;
+
+	if(readn(0, buf, 20+2+2+2) != 20+2+2+2 || memcmp(buf, "Creative Voice File\x1a", 20) != 0)
+		sysfatal("not a voc file");
+
+	ver = GET16(buf+20+2);
+	chksum = GET16(buf+20+2+2);
+	if(~ver + 0x1234 != chksum)
+		sysfatal("invalid checksum");
+
+	for(;;){
+		if(readn(0, buf, 4) != 4)
+			break;
+		b.type = buf[0];
+		b.size = GET24(buf+1);
+
+		switch(b.type){
+		case 0:
+			exits(nil);
+		case 1:
+			if(readn(0, buf, 2) != 2)
+				sysfatal("truncated block");
+			b.codec = buf[0];
+			b.freq = 1000000 / (256 - buf[1]);
+			b.chan = 1;
+			b.size -= 2;
+			break;
+		case 2:
+			if(b.freq == 0)
+				sysfatal("block 2 without defined codec");
+			break;
+		case 9:
+			if(readn(0, buf, 4+1+1+2+4) != 4+1+1+2+4)
+				sysfatal("truncated block");
+			b.freq = GET32(buf);
+			b.bits = buf[4];
+			b.chan = buf[5];
+			b.codec = buf[6];
+			b.size -= 4+1+1+2+4;
+			break;
+		default:
+			while(b.size != 0){
+				n = b.size;
+				if(n > sizeof buf)
+					n = sizeof buf;
+				if(read(0, buf, n) <= 0)
+					sysfatal("truncated block");
+				b.size -= n;
+			}
+			break;
+		}
+
+		snprint(fmt, sizeof fmt, "%sc%dr%d", codec(b.codec), b.chan, b.freq);
+		snprint(size, sizeof size, "%d", b.size);
+		if(fork() == 0){
+			execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", size, nil);
+			sysfatal("exec: %r");
+		}
+		waitpid();
+	}
+	exits(nil);
+}
--- /dev/null
+++ b/sys/src/games/vocenc.c
@@ -1,0 +1,124 @@
+#include <u.h>
+#include <libc.h>
+
+#define PUT16(p, u) ((p)[1] = (u)>>8, (p)[0] = (u))
+#define PUT24(p, u) ((p)[2] = (u)>>16, (p)[1] = (u)>>8, (p)[0] = (u))
+#define PUT32(p, u) ((p)[3] = (u)>>24, (p)[2] = (u)>>16, (p)[1] = (u)>>8, (p)[0] = (u))
+
+typedef struct Desc Desc;
+struct Desc
+{
+	int	rate;
+	int	channels;
+	int	bits;
+	Rune	fmt;
+};
+
+Desc
+mkdesc(char *f)
+{
+	Desc d;
+	Rune r;
+	char *p;
+
+	memset(&d, 0, sizeof(d));
+	p = f;
+	while(*p != 0){
+		p += chartorune(&r, p);
+		switch(r){
+		case L'r':
+			d.rate = strtol(p, &p, 10);
+			break;
+		case L'c':
+			d.channels = strtol(p, &p, 10);
+			break;
+		case L'm':
+			r = L'µ';
+		case L's':
+		case L'u':
+		case L'a':
+		case L'µ':
+			d.fmt = r;
+			d.bits = strtol(p, &p, 10);
+			break;
+		default:
+			goto Bad;
+		}
+	}
+	if(d.rate <= 0)
+		goto Bad;
+	if(d.bits <= 0 || d.bits > 16)
+		goto Bad;
+	if(d.bits == 16 && d.fmt != 's')
+		goto Bad;
+	return d;
+Bad:
+	sysfatal("bad format: %s", f);
+	return d;
+}
+
+int
+codec(Rune r)
+{
+	switch(r){
+	case 'u':
+		return 0x0;
+	case 's':
+		return 0x04;
+	case L'µ':
+		return 0x06;
+	case 'a':
+		return 0x07;
+	default:
+		sysfatal("bad format");
+	}
+	return -1;
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s fmt\n", argv0);
+	sysfatal("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	Desc o;
+	char magic[] = "Creative Voice File\x1a";
+	uchar buf[16+8192];
+	long n;
+	int c;
+	enum{ hdrsz = 0x1a, ver = 0x010a };
+
+	ARGBEGIN{
+	default:
+		usage();
+		break;
+	}ARGEND;
+	if(argc < 1)
+		usage();
+
+	o = mkdesc(argv[0]);
+	write(1, magic, sizeof magic - 1);
+	PUT16(buf, hdrsz);
+	PUT16(buf+2, ver);
+	PUT16(buf+4, ~ver + 0x1234);
+	write(1, buf, 2+2+2);
+
+	while((n = read(0, buf+16, sizeof buf-16)) > 0){
+		buf[0] = 0x9;
+		PUT24(buf+1, n+12);
+		PUT32(buf+4, o.rate);
+		buf[8] = o.bits;
+		buf[9] = o.channels;
+		c = codec(o.fmt);
+		PUT16(buf+10, c);
+		PUT32(buf+12, 0x0);
+		write(1, buf, 16+n);
+	}
+	buf[0] = 0;
+	write(1, buf, 1);
+	exits(nil);
+}