audio: replace µlawdec, add big endian and µlaw audio formats to pcmconv, µlaw in wav support
to support µ-law audio embedded in wav and big endian pcm in sun audio files the µ-law and a-law and big endian integer decoding was added to pcmconv. sundec now parses the sun audio header supporting stereo now.
This commit is contained in:
parent
d7b7723c96
commit
5ceb834f0e
7 changed files with 297 additions and 417 deletions
|
@ -1,6 +1,6 @@
|
|||
.TH AUDIO 1
|
||||
.SH NAME
|
||||
mp3dec, mp3enc, oggdec, oggenc, flacdec, µlawdec, wavdec, pcmconv \- decode and encode audio files
|
||||
mp3dec, mp3enc, oggdec, oggenc, flacdec, sundec, wavdec, pcmconv \- decode and encode audio files
|
||||
.SH SYNOPSIS
|
||||
.B audio/mp3dec
|
||||
[
|
||||
|
@ -13,7 +13,7 @@ mp3dec, mp3enc, oggdec, oggenc, flacdec, µlawdec, wavdec, pcmconv \- decode and
|
|||
.br
|
||||
.B audio/wavdec
|
||||
.br
|
||||
.B audio/µlawdec
|
||||
.B audio/sundec
|
||||
.PP
|
||||
.B audio/oggenc
|
||||
.br
|
||||
|
@ -65,12 +65,12 @@ decodes MPEG audio (layer 1, 2 and 3). The
|
|||
option enables debug output to standard error.
|
||||
.I Oggdec,
|
||||
.I flacdec,
|
||||
.I µlawdec
|
||||
.I sunwdec
|
||||
and
|
||||
.I wavdec
|
||||
are like
|
||||
.I mp3dec
|
||||
but decode OGG Vorbis, FLAC lossless audio, Sun µlaw audio and PCM Wave.
|
||||
but decode OGG Vorbis, FLAC lossless audio, Sun audio and RIFF wave.
|
||||
.PP
|
||||
The encoders read PCM on standard input and produce compressed audio
|
||||
on standard output.
|
||||
|
@ -226,17 +226,29 @@ is a concatinated string of the following parts:
|
|||
|
||||
.TP
|
||||
.BI s #
|
||||
sample format is little endian signed integer where
|
||||
sample format is little-endian signed integer where
|
||||
.I #
|
||||
specifies the number of bits
|
||||
.TP
|
||||
.BI u #
|
||||
unsigned little endian integer format
|
||||
unsigned little-endian integer format
|
||||
.TP
|
||||
.BI S #
|
||||
singed big-endian integer format
|
||||
.TP
|
||||
.BI U #
|
||||
unsigned big-endian integer format
|
||||
.TP
|
||||
.BI f #
|
||||
floating point format where
|
||||
.I #
|
||||
has to be 32 or 64 for single or double precisition
|
||||
has to be 32 or 64 for single- or double-precisition
|
||||
.TP
|
||||
.B a8
|
||||
8-bit a-law format
|
||||
.TP
|
||||
.B µ8
|
||||
8-bit µ-law format
|
||||
.TP
|
||||
.BI c #
|
||||
specifies the number of channels
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
LIBS=libogg libvorbis libFLAC
|
||||
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec µlawdec
|
||||
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec sundec
|
||||
#libs must be made first
|
||||
DIRS=$LIBS $PROGS
|
||||
|
||||
|
|
|
@ -9,8 +9,9 @@ struct Desc
|
|||
int rate;
|
||||
int channels;
|
||||
int framesz;
|
||||
int bits;
|
||||
int fmt;
|
||||
int abits; /* bits after input conversion */
|
||||
int bits; /* bits in input stream per sample */
|
||||
Rune fmt;
|
||||
};
|
||||
|
||||
struct Chan
|
||||
|
@ -235,6 +236,31 @@ siconv(int *dst, uchar *src, int bits, int skip, int count)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Siconv(int *dst, uchar *src, int bits, int skip, int count)
|
||||
{
|
||||
int i, v, s, b;
|
||||
|
||||
b = (bits+7)/8;
|
||||
s = sizeof(int)*8-bits;
|
||||
while(count--){
|
||||
v = 0;
|
||||
i = 0;
|
||||
switch(b){
|
||||
case 4:
|
||||
v = src[i++];
|
||||
case 3:
|
||||
v = (v<<8) | src[i++];
|
||||
case 2:
|
||||
v = (v<<8) | src[i++];
|
||||
case 1:
|
||||
v = (v<<8) | src[i];
|
||||
}
|
||||
*dst++ = v << s;
|
||||
src += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
uiconv(int *dst, uchar *src, int bits, int skip, int count)
|
||||
{
|
||||
|
@ -261,6 +287,32 @@ uiconv(int *dst, uchar *src, int bits, int skip, int count)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Uiconv(int *dst, uchar *src, int bits, int skip, int count)
|
||||
{
|
||||
int i, s, b;
|
||||
uint v;
|
||||
|
||||
b = (bits+7)/8;
|
||||
s = sizeof(uint)*8-bits;
|
||||
while(count--){
|
||||
v = 0;
|
||||
i = 0;
|
||||
switch(b){
|
||||
case 4:
|
||||
v = src[i++];
|
||||
case 3:
|
||||
v = (v<<8) | src[i++];
|
||||
case 2:
|
||||
v = (v<<8) | src[i++];
|
||||
case 1:
|
||||
v = (v<<8) | src[i];
|
||||
}
|
||||
*dst++ = (v << s) - (~0UL>>1);
|
||||
src += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ficonv(int *dst, uchar *src, int bits, int skip, int count)
|
||||
{
|
||||
|
@ -268,31 +320,72 @@ ficonv(int *dst, uchar *src, int bits, int skip, int count)
|
|||
while(count--){
|
||||
float f;
|
||||
|
||||
f = *((float*)src);
|
||||
f = *((float*)src), src += skip;
|
||||
if(f > 1.0)
|
||||
*dst++ = 0x7fffffff;
|
||||
else if(f < -1.0)
|
||||
*dst++ = -0x80000000;
|
||||
else
|
||||
*dst++ = f*2147483647.f;
|
||||
src += skip;
|
||||
}
|
||||
} else {
|
||||
while(count--){
|
||||
double d;
|
||||
|
||||
d = *((double*)src);
|
||||
d = *((double*)src), src += skip;
|
||||
if(d > 1.0)
|
||||
*dst++ = 0x7fffffff;
|
||||
else if(d < -1.0)
|
||||
*dst++ = -0x80000000;
|
||||
else
|
||||
*dst++ = d*2147483647.f;
|
||||
src += skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
aiconv(int *dst, uchar *src, int, int skip, int count)
|
||||
{
|
||||
int t, seg;
|
||||
uchar a;
|
||||
|
||||
while(count--){
|
||||
a = *src, src += skip;
|
||||
a ^= 0x55;
|
||||
t = (a & 0xf) << 4;
|
||||
seg = (a & 0x70) >> 4;
|
||||
switch(seg){
|
||||
case 0:
|
||||
t += 8;
|
||||
break;
|
||||
case 1:
|
||||
t += 0x108;
|
||||
break;
|
||||
default:
|
||||
t += 0x108;
|
||||
t <<= seg - 1;
|
||||
}
|
||||
t = (a & 0x80) ? t : -t;
|
||||
*dst++ = t << (sizeof(int)*8 - 16);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
µiconv(int *dst, uchar *src, int, int skip, int count)
|
||||
{
|
||||
int t;
|
||||
uchar u;
|
||||
|
||||
while(count--){
|
||||
u = *src, src += skip;
|
||||
u = ~u;
|
||||
t = ((u & 0xf) << 3) + 0x84;
|
||||
t <<= (u & 0x70) >> 4;
|
||||
t = u & 0x80 ? 0x84 - t: t - 0x84;
|
||||
*dst++ = t << (sizeof(int)*8 - 16);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
soconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||
{
|
||||
|
@ -317,6 +410,30 @@ soconv(int *src, uchar *dst, int bits, int skip, int count)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Soconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||
{
|
||||
int i, v, s, b;
|
||||
|
||||
b = (bits+7)/8;
|
||||
s = sizeof(int)*8-bits;
|
||||
while(count--){
|
||||
v = *src++ >> s;
|
||||
i = b;
|
||||
switch(b){
|
||||
case 4:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 3:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 2:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 1:
|
||||
dst[--i] = v;
|
||||
}
|
||||
dst += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
uoconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||
{
|
||||
|
@ -342,6 +459,31 @@ uoconv(int *src, uchar *dst, int bits, int skip, int count)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Uoconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||
{
|
||||
int i, s, b;
|
||||
uint v;
|
||||
|
||||
b = (bits+7)/8;
|
||||
s = sizeof(uint)*8-bits;
|
||||
while(count--){
|
||||
v = ((~0UL>>1) + *src++) >> s;
|
||||
i = b;
|
||||
switch(b){
|
||||
case 4:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 3:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 2:
|
||||
dst[--i] = v, v >>= 8;
|
||||
case 1:
|
||||
dst[--i] = v;
|
||||
}
|
||||
dst += skip;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
foconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||
{
|
||||
|
@ -362,24 +504,31 @@ Desc
|
|||
mkdesc(char *f)
|
||||
{
|
||||
Desc d;
|
||||
int c;
|
||||
Rune r;
|
||||
char *p;
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
p = f;
|
||||
while(c = *p++){
|
||||
switch(c){
|
||||
case 'r':
|
||||
while(*p != 0){
|
||||
p += chartorune(&r, p);
|
||||
switch(r){
|
||||
case L'r':
|
||||
d.rate = strtol(p, &p, 10);
|
||||
break;
|
||||
case 'c':
|
||||
case L'c':
|
||||
d.channels = strtol(p, &p, 10);
|
||||
break;
|
||||
case 's':
|
||||
case 'u':
|
||||
case 'f':
|
||||
d.fmt = c;
|
||||
d.bits = strtol(p, &p, 10);
|
||||
case L'm':
|
||||
r = L'µ';
|
||||
case L's':
|
||||
case L'S':
|
||||
case L'u':
|
||||
case L'U':
|
||||
case L'f':
|
||||
case L'a':
|
||||
case L'µ':
|
||||
d.fmt = r;
|
||||
d.bits = d.abits = strtol(p, &p, 10);
|
||||
break;
|
||||
default:
|
||||
goto Bad;
|
||||
|
@ -387,9 +536,14 @@ mkdesc(char *f)
|
|||
}
|
||||
if(d.rate <= 0)
|
||||
goto Bad;
|
||||
if(d.fmt == 'f'){
|
||||
if(d.fmt == L'a' || d.fmt == L'µ'){
|
||||
if(d.bits != 8)
|
||||
goto Bad;
|
||||
d.abits = sizeof(int)*8 - 16;
|
||||
} else if(d.fmt == L'f'){
|
||||
if(d.bits != 32 && d.bits != 64)
|
||||
goto Bad;
|
||||
d.abits = sizeof(int)*8;
|
||||
} else if(d.bits <= 0 || d.bits > 32)
|
||||
goto Bad;
|
||||
d.framesz = ((d.bits+7)/8) * d.channels;
|
||||
|
@ -472,21 +626,31 @@ main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
if(i.channels > nelem(ch))
|
||||
sysfatal("too many input channels %d", i.channels);
|
||||
sysfatal("too many input channels: %d", i.channels);
|
||||
|
||||
switch(i.fmt){
|
||||
case 's': iconv = siconv; break;
|
||||
case 'u': iconv = uiconv; break;
|
||||
case 'f': iconv = ficonv; break;
|
||||
case L's': iconv = siconv; break;
|
||||
case L'S': iconv = Siconv; break;
|
||||
case L'u': iconv = uiconv; break;
|
||||
case L'U': iconv = Uiconv; break;
|
||||
case L'f': iconv = ficonv; break;
|
||||
case L'a': iconv = aiconv; break;
|
||||
case L'µ': iconv = µiconv; break;
|
||||
default:
|
||||
sysfatal("unsupported input format: %C", i.fmt);
|
||||
}
|
||||
|
||||
switch(o.fmt){
|
||||
case 's': oconv = soconv; break;
|
||||
case 'u': oconv = uoconv; break;
|
||||
case 'f': oconv = foconv; break;
|
||||
case L's': oconv = soconv; break;
|
||||
case L'S': oconv = Soconv; break;
|
||||
case L'u': oconv = uoconv; break;
|
||||
case L'U': oconv = Uoconv; break;
|
||||
case L'f': oconv = foconv; break;
|
||||
default:
|
||||
sysfatal("unsupported output format: %C", o.fmt);
|
||||
}
|
||||
|
||||
if(i.fmt == 'f' || o.fmt == 'f')
|
||||
if(i.fmt == L'f' || o.fmt == L'f')
|
||||
setfcr(getfcr() & ~(FPINVAL|FPOVFL));
|
||||
|
||||
nin = (sizeof(ibuf)-i.framesz)/i.framesz;
|
||||
|
@ -511,7 +675,7 @@ main(int argc, char *argv[])
|
|||
l -= n;
|
||||
n /= i.framesz;
|
||||
(*iconv)(in, ibuf, i.bits, i.framesz, n);
|
||||
dither(in, i.bits, o.bits, n);
|
||||
dither(in, i.abits, o.abits, n);
|
||||
m = resample(&ch[0], in, out, n) - out;
|
||||
if(m < 1){
|
||||
if(n == 0)
|
||||
|
@ -521,7 +685,7 @@ main(int argc, char *argv[])
|
|||
if(i.channels == o.channels){
|
||||
for(k=1; k<i.channels; k++){
|
||||
(*iconv)(in, ibuf + k*((i.bits+7)/8), i.bits, i.framesz, n);
|
||||
dither(in, i.bits, o.bits, n);
|
||||
dither(in, i.abits, o.abits, n);
|
||||
resample(&ch[k], in, out, n);
|
||||
if(m > 0)
|
||||
(*oconv)(out, obuf + k*((o.bits+7)/8), o.bits, o.framesz, m);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
</$objtype/mkfile
|
||||
<../config
|
||||
|
||||
OFILES=µlawdec.$O
|
||||
OFILES=sundec.$O
|
||||
|
||||
TARG=µlawdec
|
||||
TARG=sundec
|
||||
|
||||
</sys/src/cmd/mkone
|
62
sys/src/cmd/audio/sundec/sundec.c
Normal file
62
sys/src/cmd/audio/sundec/sundec.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
ulong
|
||||
get4(void)
|
||||
{
|
||||
uchar buf[4];
|
||||
|
||||
if(readn(0, buf, 4) != 4)
|
||||
sysfatal("read: %r");
|
||||
return buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
|
||||
}
|
||||
|
||||
char *fmttab[] = {
|
||||
[1] "µ8", /* 8-bit G.711 µ-law */
|
||||
[2] "S8", /* 8-bit linear PCM */
|
||||
[3] "S16", /* 16-bit linear PCM */
|
||||
[4] "S24", /* 24-bit linear PCM */
|
||||
[5] "S32", /* 32-bit linear PCM */
|
||||
[6] "f32", /* 32-bit IEEE floating point */
|
||||
[7] "f64", /* 64-bit IEEE floating point */
|
||||
[27] "a8", /* 8-bit G.711 A-law */
|
||||
};
|
||||
|
||||
void
|
||||
main(int, char *argv[])
|
||||
{
|
||||
char buf[64], fmt[32];
|
||||
ulong enc, rate, chans, len, off;
|
||||
int n;
|
||||
|
||||
argv0 = argv[0];
|
||||
if(get4() != 0x2e736e64UL)
|
||||
sysfatal("no sun format");
|
||||
off = get4();
|
||||
if(off < 24)
|
||||
sysfatal("bad data ofset");
|
||||
off -= 24;
|
||||
len = get4();
|
||||
if(len == 0xffffffffUL)
|
||||
len = 0;
|
||||
enc = get4();
|
||||
rate = get4();
|
||||
chans = get4();
|
||||
if(enc >= nelem(fmttab) || fmttab[enc] == 0)
|
||||
sysfatal("unsupported encoding: %lux", enc);
|
||||
snprint(fmt, sizeof(fmt), "%sc%ludr%lud", fmttab[enc], chans, rate);
|
||||
while(off > 0){
|
||||
n = sizeof(buf);
|
||||
if(off < n)
|
||||
n = off;
|
||||
n = read(0, buf, n);
|
||||
if(n <= 0)
|
||||
sysfatal("read: %r");
|
||||
off -= n;
|
||||
}
|
||||
if(len > 0){
|
||||
snprint(buf, sizeof(buf), "%lud", len);
|
||||
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", buf, 0);
|
||||
} else
|
||||
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, 0);
|
||||
}
|
|
@ -42,7 +42,7 @@ getcc(char tag[4])
|
|||
void
|
||||
main(int, char *argv[])
|
||||
{
|
||||
char buf[8*1024], fmt[32];
|
||||
char buf[1024], fmt[32];
|
||||
ulong len, n;
|
||||
Wave wav;
|
||||
|
||||
|
@ -70,22 +70,32 @@ main(int, char *argv[])
|
|||
len -= 2+2+4+4+2+2;
|
||||
}
|
||||
while(len > 0){
|
||||
if(len < sizeof(buf))
|
||||
n = sizeof(buf);
|
||||
if(len < n)
|
||||
n = len;
|
||||
else
|
||||
n = sizeof(buf);
|
||||
if(readn(0, buf, n) != n)
|
||||
n = read(0, buf, n);
|
||||
if(n <= 0)
|
||||
sysfatal("read: %r");
|
||||
len -= n;
|
||||
}
|
||||
}
|
||||
|
||||
if(wav.fmt != 1)
|
||||
sysfatal("compressed format (0x%x) not supported", wav.fmt);
|
||||
snprint(fmt, sizeof(fmt), "%c%dr%dc%d",
|
||||
wav.bits == 8 ? 'u' : 's', wav.bits,
|
||||
wav.rate,
|
||||
wav.channels);
|
||||
switch(wav.fmt){
|
||||
case 1:
|
||||
snprint(fmt, sizeof(fmt), "%c%dr%dc%d", wav.bits == 8 ? 'u' : 's',
|
||||
wav.bits, wav.rate, wav.channels);
|
||||
break;
|
||||
case 3:
|
||||
snprint(fmt, sizeof(fmt), "f32r%dc%d", wav.rate, wav.channels);
|
||||
break;
|
||||
case 6:
|
||||
snprint(fmt, sizeof(fmt), "a8r%dc%d", wav.rate, wav.channels);
|
||||
break;
|
||||
case 7:
|
||||
snprint(fmt, sizeof(fmt), "µ8r%dc%d", wav.rate, wav.channels);
|
||||
break;
|
||||
default:
|
||||
sysfatal("wave format (0x%lux) not supported", (ulong)wav.fmt);
|
||||
}
|
||||
snprint(buf, sizeof(buf), "%lud", len);
|
||||
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", buf, 0);
|
||||
}
|
||||
|
|
|
@ -1,368 +0,0 @@
|
|||
#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);
|
||||
}
|
Loading…
Reference in a new issue