µlaw audio support (from erik quanstro)
This commit is contained in:
parent
0d84343fbc
commit
7148847ff0
7 changed files with 386 additions and 4 deletions
|
@ -52,6 +52,8 @@ fn play1 {
|
||||||
audio/wavdec
|
audio/wavdec
|
||||||
case *flac*
|
case *flac*
|
||||||
audio/flacdec
|
audio/flacdec
|
||||||
|
case *µlaw* *audio/basic*
|
||||||
|
audio/µlawdec
|
||||||
case *pls*
|
case *pls*
|
||||||
awk 'BEGIN {FS="="} /^File/{print $2}' | play1 plain
|
awk 'BEGIN {FS="="} /^File/{print $2}' | play1 plain
|
||||||
case *
|
case *
|
||||||
|
|
|
@ -12,6 +12,8 @@ mp3dec, mp3enc, oggdec, oggenc, flacdec, wavdec, pcmconv \- decode and encode au
|
||||||
.B audio/flacdec
|
.B audio/flacdec
|
||||||
.br
|
.br
|
||||||
.B audio/wavdec
|
.B audio/wavdec
|
||||||
|
.br
|
||||||
|
.B audio/µlawdec
|
||||||
.PP
|
.PP
|
||||||
.B audio/oggenc
|
.B audio/oggenc
|
||||||
.br
|
.br
|
||||||
|
@ -62,12 +64,13 @@ decodes MPEG audio (layer 1, 2 and 3). The
|
||||||
.B -d
|
.B -d
|
||||||
option enables debug output to standard error.
|
option enables debug output to standard error.
|
||||||
.I Oggdec,
|
.I Oggdec,
|
||||||
.I flacdec
|
.I flacdec,
|
||||||
|
.I µlawdec
|
||||||
and
|
and
|
||||||
.I wavdec
|
.I wavdec
|
||||||
are like
|
are like
|
||||||
.I mp3dec
|
.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
|
.PP
|
||||||
The encoders read PCM on standard input and produce compressed audio
|
The encoders read PCM on standard input and produce compressed audio
|
||||||
on standard output.
|
on standard output.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
|
|
||||||
LIBS=libogg libvorbis libFLAC
|
LIBS=libogg libvorbis libFLAC
|
||||||
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec
|
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec µlawdec
|
||||||
#libs must be made first
|
#libs must be made first
|
||||||
DIRS=$LIBS $PROGS
|
DIRS=$LIBS $PROGS
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ main(int argc, char *argv[])
|
||||||
Chan ch[8];
|
Chan ch[8];
|
||||||
Desc i, o;
|
Desc i, o;
|
||||||
ulong delta;
|
ulong delta;
|
||||||
int k, r, n, m, p;
|
int k, r, n, m;
|
||||||
vlong l;
|
vlong l;
|
||||||
|
|
||||||
void (*oconv)(int *, uchar *, int, int, int) = nil;
|
void (*oconv)(int *, uchar *, int, int, int) = nil;
|
||||||
|
|
8
sys/src/cmd/audio/µlawdec/mkfile
Normal file
8
sys/src/cmd/audio/µlawdec/mkfile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
</$objtype/mkfile
|
||||||
|
<../config
|
||||||
|
|
||||||
|
OFILES=µlawdec.$O
|
||||||
|
|
||||||
|
TARG=µlawdec
|
||||||
|
|
||||||
|
</sys/src/cmd/mkone
|
368
sys/src/cmd/audio/µlawdec/µlawdec.c
Normal file
368
sys/src/cmd/audio/µlawdec/µlawdec.c
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -816,6 +816,7 @@ struct FILE_STRING
|
||||||
"\033Lua", "Lua bytecode", 4, OCTET,
|
"\033Lua", "Lua bytecode", 4, OCTET,
|
||||||
"ID3", "mp3 audio with id3", 3, "audio/mpeg",
|
"ID3", "mp3 audio with id3", 3, "audio/mpeg",
|
||||||
"OggS", "ogg audio", 4, "audio/ogg",
|
"OggS", "ogg audio", 4, "audio/ogg",
|
||||||
|
".snd", "µlaw audio", 4, "audio/basic",
|
||||||
"\211PNG", "PNG image", 4, "image/png",
|
"\211PNG", "PNG image", 4, "image/png",
|
||||||
"P3\n", "ppm", 3, "image/ppm",
|
"P3\n", "ppm", 3, "image/ppm",
|
||||||
"P6\n", "ppm", 3, "image/ppm",
|
"P6\n", "ppm", 3, "image/ppm",
|
||||||
|
|
Loading…
Reference in a new issue