From 7148847ff04a0b49ff8ca4c8ba92f9024acf49cd Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 8 Dec 2012 09:18:44 +0100 Subject: [PATCH] =?UTF-8?q?=C2=B5law=20audio=20support=20(from=20erik=20qu?= =?UTF-8?q?anstro)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rc/bin/play | 2 + sys/man/1/audio | 7 +- sys/src/cmd/audio/mkfile | 2 +- sys/src/cmd/audio/pcmconv/pcmconv.c | 2 +- sys/src/cmd/audio/µlawdec/mkfile | 8 + sys/src/cmd/audio/µlawdec/µlawdec.c | 368 ++++++++++++++++++++++++++++ sys/src/cmd/file.c | 1 + 7 files changed, 386 insertions(+), 4 deletions(-) create mode 100644 sys/src/cmd/audio/µlawdec/mkfile create mode 100644 sys/src/cmd/audio/µlawdec/µlawdec.c diff --git a/rc/bin/play b/rc/bin/play index 2acbdf8ba..e79b8568c 100755 --- a/rc/bin/play +++ b/rc/bin/play @@ -52,6 +52,8 @@ fn play1 { audio/wavdec case *flac* audio/flacdec + case *µlaw* *audio/basic* + audio/µlawdec case *pls* awk 'BEGIN {FS="="} /^File/{print $2}' | play1 plain case * diff --git a/sys/man/1/audio b/sys/man/1/audio index ba9dfd883..9c50c9171 100644 --- a/sys/man/1/audio +++ b/sys/man/1/audio @@ -12,6 +12,8 @@ mp3dec, mp3enc, oggdec, oggenc, flacdec, wavdec, pcmconv \- decode and encode au .B audio/flacdec .br .B audio/wavdec +.br +.B audio/µlawdec .PP .B audio/oggenc .br @@ -62,12 +64,13 @@ decodes MPEG audio (layer 1, 2 and 3). The .B -d option enables debug output to standard error. .I Oggdec, -.I flacdec +.I flacdec, +.I µlawdec and .I wavdec are like .I mp3dec -but decode OGG Vorbis, FLAC lossless audio and PCM Wave. +but decode OGG Vorbis, FLAC lossless audio, Sun µlaw audio and PCM Wave. .PP The encoders read PCM on standard input and produce compressed audio on standard output. diff --git a/sys/src/cmd/audio/mkfile b/sys/src/cmd/audio/mkfile index b85ff65c2..ddc4af14d 100644 --- a/sys/src/cmd/audio/mkfile +++ b/sys/src/cmd/audio/mkfile @@ -1,7 +1,7 @@ +#include +#include + +enum { + Qmask = 0xf, /* quantization mask */ + Nsegs = 8, /* A-law segments */ + Segshift = 4, + Segmask = 0x70, +}; + +static short segend[Nsegs] = { + 0xff, 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff +}; + +/* copy from CCITT G.711 specifications */ +static uchar u2a[128] = { /* μ- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128 +}; + +static uchar a2u[128] = { /* A- to μ-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 +}; + +/* speed doesn't matter. table has 8 entires */ +static int +search(int val, short *table, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return i; + } + return size; +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +uchar +linear2alaw(int pcm) /* 2's complement (16-bit range) */ +{ + uchar aval; + int mask, seg; + + if (pcm >= 0) { + mask = 0xd5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm = -pcm - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm, segend, 8); + + /* Combine the sign, segment, and quantization bits. */ + if (seg >= 8) + /* out of range, return maximum value. */ + return 0x7f ^ mask; + else { + aval = seg << Segshift; + if (seg < 2) + aval |= pcm>>4 & Qmask; + else + aval |= pcm>>(seg + 3) & Qmask; + return aval ^ mask; + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +alaw2linear(uchar a) +{ + int t, seg; + + a ^= 0x55; + + t = (a & Qmask) << 4; + seg = (a & Segmask) >> Segshift; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return (a & 0x80) ? t : -t; +} + +enum { + Bias = 0x84, /* Bias for linear code. */ +}; + +/* + * linear2μlaw() - Convert a linear PCM value to μ-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +uchar +linear2μlaw(int pcm) +{ + int mask, seg; + uchar uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm < 0) { + pcm = Bias - pcm; + mask = 0x7f; + } else { + pcm += Bias; + mask = 0xff; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm, segend, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) + /* out of range, return maximum value. */ + return 0x7f ^ mask; + else { + uval = seg<< 4 | ((pcm >> (seg + 3)) & 0xf); + return uval ^ mask; + } +} + +/* + * μlaw2linear() - Convert a μ-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int +μlaw2linear(uchar μ) +{ + int t; + + /* Complement to obtain normal μ-law value. */ + μ = ~μ; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((μ & Qmask) << 3) + Bias; + t <<= (μ & Segmask) >> Segshift; + + return μ & 0x80? Bias - t: t - Bias; +} + +/* A-law to μ-law conversion */ +uchar +alaw2μlaw(uchar a) +{ +// a &= 0xff; + if(a & 0x80) + return 0xff ^ a2u[a ^ 0xd5]; + else + return 0x7f ^ a2u[a ^ 0x55]; +} + +/* μ-law to A-law conversion */ +uchar +μlaw2alaw(uchar μ) +{ +// μ &= 0xff; + if(μ & 0x80) + return 0xd5 ^ u2a[0xff ^ μ] - 1; + else + return 0x55 ^ u2a[0x7f ^ μ] - 1; +} + +static void +setrate(int rate) +{ + int fd; + + fd = open("/dev/volume", OWRITE); + if(fd == -1){ + fprint(2, "μlawdec: can't set rate %d: open: %r\n", rate); + return; + } + if(fprint(fd, "speed %d", rate) == -1) + fprint(2, "μlawdec: can't set rate %d: fprint: %r\n", rate); + else + fprint(2, "μlawdec: rate %d\n", rate); + close(fd); +} + +enum { + Sig = 0, + Offset = 1, + Enc = 3, + Rate = 4, + Nchan = 5, +}; + +u32int +μlawword(uchar *buf, int w) +{ + buf += 4*w; + return buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; +} + +int +pcmconv(char *fmt) +{ + int pid, pfd[2]; + + if(pipe(pfd) < 0) + return -1; + pid = fork(); + if(pid < 0){ + close(pfd[0]); + close(pfd[1]); + return -1; + } + if(pid == 0){ + dup(pfd[1], 0); + close(pfd[1]); + close(pfd[0]); + execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, 0); + sysfatal("exec: %r"); + } + close(pfd[1]); + return pfd[0]; +} + +void +main(void) +{ + int fd, n; + uchar buf[5*4]; + char fmt[32]; + vlong off; + Biobuf i, o; + + if(Binit(&i, 0, OREAD) == -1) + sysfatal("μlawdec: Binit: %r"); + if(Bread(&i, buf, sizeof buf) != sizeof buf) + sysfatal("μlawdec: Bread: %r"); + off = μlawword(buf, Offset); + if(off < 24) + sysfatal("μlawdec: bad offset: %lld", off); + if(μlawword(buf, Sig) != 0x2e736e64) + sysfatal("μlawdec: not .au file"); + if(μlawword(buf, Enc) != 1) + sysfatal("μlawdec: not μlaw"); + + snprint(fmt, sizeof(fmt), "s16c1r%d", μlawword(buf, Rate)); + fd = pcmconv(fmt); + if(fd < 0) + sysfatal("μlawdec: pcmconv: %r"); + + if(Binit(&o, fd, OWRITE) == -1) + sysfatal("μlawdec: Binit: %r"); + + while(off > 0){ + n = sizeof(buf); + if(off < n) + n = off; + if(Bread(&i, buf, n) != n) + sysfatal("μlawdec: Bread: %r"); + off -= n; + } + + for(;;){ + switch(Bread(&i, buf, 1)){ + case 0: + goto done; + case -1: + sysfatal("μlawdec: Bread: %r"); + } + n = μlaw2linear(buf[0]); + buf[0] = n&0xff; + buf[1] = (n&0xff00)>>8; + if(Bwrite(&o, buf, 2) != 2) + sysfatal("μlawdec: Bwrite: %r"); + } +done: + Bterm(&o); + Bterm(&i); +} diff --git a/sys/src/cmd/file.c b/sys/src/cmd/file.c index 826902646..90490d310 100644 --- a/sys/src/cmd/file.c +++ b/sys/src/cmd/file.c @@ -816,6 +816,7 @@ struct FILE_STRING "\033Lua", "Lua bytecode", 4, OCTET, "ID3", "mp3 audio with id3", 3, "audio/mpeg", "OggS", "ogg audio", 4, "audio/ogg", + ".snd", "µlaw audio", 4, "audio/basic", "\211PNG", "PNG image", 4, "image/png", "P3\n", "ppm", 3, "image/ppm", "P6\n", "ppm", 3, "image/ppm",