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
|
.TH AUDIO 1
|
||||||
.SH NAME
|
.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
|
.SH SYNOPSIS
|
||||||
.B audio/mp3dec
|
.B audio/mp3dec
|
||||||
[
|
[
|
||||||
|
@ -13,7 +13,7 @@ mp3dec, mp3enc, oggdec, oggenc, flacdec, µlawdec, wavdec, pcmconv \- decode and
|
||||||
.br
|
.br
|
||||||
.B audio/wavdec
|
.B audio/wavdec
|
||||||
.br
|
.br
|
||||||
.B audio/µlawdec
|
.B audio/sundec
|
||||||
.PP
|
.PP
|
||||||
.B audio/oggenc
|
.B audio/oggenc
|
||||||
.br
|
.br
|
||||||
|
@ -65,12 +65,12 @@ decodes MPEG audio (layer 1, 2 and 3). The
|
||||||
option enables debug output to standard error.
|
option enables debug output to standard error.
|
||||||
.I Oggdec,
|
.I Oggdec,
|
||||||
.I flacdec,
|
.I flacdec,
|
||||||
.I µlawdec
|
.I sunwdec
|
||||||
and
|
and
|
||||||
.I wavdec
|
.I wavdec
|
||||||
are like
|
are like
|
||||||
.I mp3dec
|
.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
|
.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.
|
||||||
|
@ -226,17 +226,29 @@ is a concatinated string of the following parts:
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI s #
|
.BI s #
|
||||||
sample format is little endian signed integer where
|
sample format is little-endian signed integer where
|
||||||
.I #
|
.I #
|
||||||
specifies the number of bits
|
specifies the number of bits
|
||||||
.TP
|
.TP
|
||||||
.BI u #
|
.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
|
.TP
|
||||||
.BI f #
|
.BI f #
|
||||||
floating point format where
|
floating point format where
|
||||||
.I #
|
.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
|
.TP
|
||||||
.BI c #
|
.BI c #
|
||||||
specifies the number of channels
|
specifies the number of channels
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
|
|
||||||
LIBS=libogg libvorbis libFLAC
|
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
|
#libs must be made first
|
||||||
DIRS=$LIBS $PROGS
|
DIRS=$LIBS $PROGS
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ struct Desc
|
||||||
int rate;
|
int rate;
|
||||||
int channels;
|
int channels;
|
||||||
int framesz;
|
int framesz;
|
||||||
int bits;
|
int abits; /* bits after input conversion */
|
||||||
int fmt;
|
int bits; /* bits in input stream per sample */
|
||||||
|
Rune fmt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Chan
|
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
|
void
|
||||||
uiconv(int *dst, uchar *src, int bits, int skip, int count)
|
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
|
void
|
||||||
ficonv(int *dst, uchar *src, int bits, int skip, int count)
|
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--){
|
while(count--){
|
||||||
float f;
|
float f;
|
||||||
|
|
||||||
f = *((float*)src);
|
f = *((float*)src), src += skip;
|
||||||
if(f > 1.0)
|
if(f > 1.0)
|
||||||
*dst++ = 0x7fffffff;
|
*dst++ = 0x7fffffff;
|
||||||
else if(f < -1.0)
|
else if(f < -1.0)
|
||||||
*dst++ = -0x80000000;
|
*dst++ = -0x80000000;
|
||||||
else
|
else
|
||||||
*dst++ = f*2147483647.f;
|
*dst++ = f*2147483647.f;
|
||||||
src += skip;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while(count--){
|
while(count--){
|
||||||
double d;
|
double d;
|
||||||
|
|
||||||
d = *((double*)src);
|
d = *((double*)src), src += skip;
|
||||||
if(d > 1.0)
|
if(d > 1.0)
|
||||||
*dst++ = 0x7fffffff;
|
*dst++ = 0x7fffffff;
|
||||||
else if(d < -1.0)
|
else if(d < -1.0)
|
||||||
*dst++ = -0x80000000;
|
*dst++ = -0x80000000;
|
||||||
else
|
else
|
||||||
*dst++ = d*2147483647.f;
|
*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
|
void
|
||||||
soconv(int *src, uchar *dst, int bits, int skip, int count)
|
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
|
void
|
||||||
uoconv(int *src, uchar *dst, int bits, int skip, int count)
|
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
|
void
|
||||||
foconv(int *src, uchar *dst, int bits, int skip, int count)
|
foconv(int *src, uchar *dst, int bits, int skip, int count)
|
||||||
{
|
{
|
||||||
|
@ -362,24 +504,31 @@ Desc
|
||||||
mkdesc(char *f)
|
mkdesc(char *f)
|
||||||
{
|
{
|
||||||
Desc d;
|
Desc d;
|
||||||
int c;
|
Rune r;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
memset(&d, 0, sizeof(d));
|
memset(&d, 0, sizeof(d));
|
||||||
p = f;
|
p = f;
|
||||||
while(c = *p++){
|
while(*p != 0){
|
||||||
switch(c){
|
p += chartorune(&r, p);
|
||||||
case 'r':
|
switch(r){
|
||||||
|
case L'r':
|
||||||
d.rate = strtol(p, &p, 10);
|
d.rate = strtol(p, &p, 10);
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case L'c':
|
||||||
d.channels = strtol(p, &p, 10);
|
d.channels = strtol(p, &p, 10);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case L'm':
|
||||||
case 'u':
|
r = L'µ';
|
||||||
case 'f':
|
case L's':
|
||||||
d.fmt = c;
|
case L'S':
|
||||||
d.bits = strtol(p, &p, 10);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
goto Bad;
|
goto Bad;
|
||||||
|
@ -387,9 +536,14 @@ mkdesc(char *f)
|
||||||
}
|
}
|
||||||
if(d.rate <= 0)
|
if(d.rate <= 0)
|
||||||
goto Bad;
|
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)
|
if(d.bits != 32 && d.bits != 64)
|
||||||
goto Bad;
|
goto Bad;
|
||||||
|
d.abits = sizeof(int)*8;
|
||||||
} else if(d.bits <= 0 || d.bits > 32)
|
} else if(d.bits <= 0 || d.bits > 32)
|
||||||
goto Bad;
|
goto Bad;
|
||||||
d.framesz = ((d.bits+7)/8) * d.channels;
|
d.framesz = ((d.bits+7)/8) * d.channels;
|
||||||
|
@ -472,21 +626,31 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i.channels > nelem(ch))
|
if(i.channels > nelem(ch))
|
||||||
sysfatal("too many input channels %d", i.channels);
|
sysfatal("too many input channels: %d", i.channels);
|
||||||
|
|
||||||
switch(i.fmt){
|
switch(i.fmt){
|
||||||
case 's': iconv = siconv; break;
|
case L's': iconv = siconv; break;
|
||||||
case 'u': iconv = uiconv; break;
|
case L'S': iconv = Siconv; break;
|
||||||
case 'f': iconv = ficonv; 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){
|
switch(o.fmt){
|
||||||
case 's': oconv = soconv; break;
|
case L's': oconv = soconv; break;
|
||||||
case 'u': oconv = uoconv; break;
|
case L'S': oconv = Soconv; break;
|
||||||
case 'f': oconv = foconv; 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));
|
setfcr(getfcr() & ~(FPINVAL|FPOVFL));
|
||||||
|
|
||||||
nin = (sizeof(ibuf)-i.framesz)/i.framesz;
|
nin = (sizeof(ibuf)-i.framesz)/i.framesz;
|
||||||
|
@ -511,7 +675,7 @@ main(int argc, char *argv[])
|
||||||
l -= n;
|
l -= n;
|
||||||
n /= i.framesz;
|
n /= i.framesz;
|
||||||
(*iconv)(in, ibuf, i.bits, i.framesz, n);
|
(*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;
|
m = resample(&ch[0], in, out, n) - out;
|
||||||
if(m < 1){
|
if(m < 1){
|
||||||
if(n == 0)
|
if(n == 0)
|
||||||
|
@ -521,7 +685,7 @@ main(int argc, char *argv[])
|
||||||
if(i.channels == o.channels){
|
if(i.channels == o.channels){
|
||||||
for(k=1; k<i.channels; k++){
|
for(k=1; k<i.channels; k++){
|
||||||
(*iconv)(in, ibuf + k*((i.bits+7)/8), i.bits, i.framesz, n);
|
(*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);
|
resample(&ch[k], in, out, n);
|
||||||
if(m > 0)
|
if(m > 0)
|
||||||
(*oconv)(out, obuf + k*((o.bits+7)/8), o.bits, o.framesz, m);
|
(*oconv)(out, obuf + k*((o.bits+7)/8), o.bits, o.framesz, m);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
<../config
|
<../config
|
||||||
|
|
||||||
OFILES=µlawdec.$O
|
OFILES=sundec.$O
|
||||||
|
|
||||||
TARG=µlawdec
|
TARG=sundec
|
||||||
|
|
||||||
</sys/src/cmd/mkone
|
</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
|
void
|
||||||
main(int, char *argv[])
|
main(int, char *argv[])
|
||||||
{
|
{
|
||||||
char buf[8*1024], fmt[32];
|
char buf[1024], fmt[32];
|
||||||
ulong len, n;
|
ulong len, n;
|
||||||
Wave wav;
|
Wave wav;
|
||||||
|
|
||||||
|
@ -70,22 +70,32 @@ main(int, char *argv[])
|
||||||
len -= 2+2+4+4+2+2;
|
len -= 2+2+4+4+2+2;
|
||||||
}
|
}
|
||||||
while(len > 0){
|
while(len > 0){
|
||||||
if(len < sizeof(buf))
|
|
||||||
n = len;
|
|
||||||
else
|
|
||||||
n = sizeof(buf);
|
n = sizeof(buf);
|
||||||
if(readn(0, buf, n) != n)
|
if(len < n)
|
||||||
|
n = len;
|
||||||
|
n = read(0, buf, n);
|
||||||
|
if(n <= 0)
|
||||||
sysfatal("read: %r");
|
sysfatal("read: %r");
|
||||||
len -= n;
|
len -= n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
switch(wav.fmt){
|
||||||
if(wav.fmt != 1)
|
case 1:
|
||||||
sysfatal("compressed format (0x%x) not supported", wav.fmt);
|
snprint(fmt, sizeof(fmt), "%c%dr%dc%d", wav.bits == 8 ? 'u' : 's',
|
||||||
snprint(fmt, sizeof(fmt), "%c%dr%dc%d",
|
wav.bits, wav.rate, wav.channels);
|
||||||
wav.bits == 8 ? 'u' : 's', wav.bits,
|
break;
|
||||||
wav.rate,
|
case 3:
|
||||||
wav.channels);
|
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);
|
snprint(buf, sizeof(buf), "%lud", len);
|
||||||
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", buf, 0);
|
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…
Add table
Add a link
Reference in a new issue