audio: add audio/pcmconv program

instead of duplicating resampling and pcm format
conversion code, put it in the new pcmconv program.
This commit is contained in:
cinap_lenrek 2012-12-08 08:26:50 +01:00
parent 9507061986
commit 0d84343fbc
11 changed files with 589 additions and 392 deletions

View file

@ -1,6 +1,6 @@
.TH AUDIO 1
.SH NAME
mp3dec, mp3enc, oggdec, oggenc, flacdec, wavdec \- decode and encode audio files
mp3dec, mp3enc, oggdec, oggenc, flacdec, wavdec, pcmconv \- decode and encode audio files
.SH SYNOPSIS
.B audio/mp3dec
[
@ -39,6 +39,18 @@ q ] [
.I "long or silly options"
]
.PP
.B audio/pcmconv
[
.B -i
.I fmt
] [
.B -o
.I fmt
] [
.B -l
.I length
]
.PP
.SH DESCRIPTION
These programs decode and encode various audio formats from and to
16-bit stereo PCM (little endian). The decoders read the compressed
@ -59,6 +71,7 @@ but decode OGG Vorbis, FLAC lossless audio and PCM Wave.
.PP
The encoders read PCM on standard input and produce compressed audio
on standard output.
.PP
.I Oggenc
and
.I mp3enc
@ -195,7 +208,48 @@ disable VBR histogram display
.TP
.BI --voice
experimental voice mode
.
.PP
.I Pcmconv
is a helper program used to convert various PCM sample formats. The
.B -i
and
.B -o
options specify the input and output format
.I fmt
of the conversion.
.I Fmt
is a concatinated string of the following parts:
.TP
.BI s #
sample format is little endian signed integer where
.I #
specifies the number of bits
.TP
.BI u #
unsigned little endian integer format
.TP
.BI f #
floating point format where
.I #
has to be 32 or 64 for single or double precisition
.TP
.BI c #
specifies the number of channels
.TP
.BI r #
gives the samplerate in Hz
.PP
The program reads samples from standard
input converting the data and writes the result to standard output
until it reached end of file or, if
.B -l
was given, a number of
.I length
bytes have been consumed from input.
.SH EXAMPLE
Play back an
.L .mp3

View file

@ -1,92 +1,8 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "FLAC/stream_decoder.h"
int rate = 44100;
typedef unsigned long ulong;
typedef unsigned char uchar;
typedef long long vlong;
typedef struct Chan Chan;
struct Chan
{
ulong phase;
FLAC__int32 last;
FLAC__int32 rand;
};
enum
{
OutBits = 16,
Max = 32767,
Min = -32768,
};
#define PRNG(x) (((x)*0x19660dL + 0x3c6ef35fL) & 0xffffffffL)
static uchar*
resample(Chan *c, FLAC__int32 *src, uchar *dst, int mono, ulong delta, ulong count, ulong bps)
{
FLAC__int32 last, val, out, rand;
ulong phase, pos, scale, lowmask, lowmask2;
vlong v;
scale = 0;
if(bps > OutBits){
scale = bps - OutBits;
lowmask = (1<<scale)-1;
lowmask2 = lowmask/2;
}
rand = c->rand;
last = c->last;
phase = c->phase;
pos = phase >> 16;
while(pos < count){
val = src[pos];
if(pos)
last = src[pos-1];
/* interpolate */
v = val;
v -= last;
v *= (phase & 0xFFFF);
out = last + (v >> 16);
/* scale / dithering */
if(scale){
out += (rand & lowmask) - lowmask2;
rand = PRNG(rand);
out >>= scale;
}
/* cliping */
if(out > Max)
out = Max;
else if(out < Min)
out = Min;
*dst++ = out;
*dst++ = out >> 8;
if(mono){
*dst++ = out;
*dst++ = out >> 8;
} else
dst += 2;
phase += delta;
pos = phase >> 16;
}
c->rand = rand;
c->last = val;
if(delta < 0x10000)
c->phase = phase & 0xFFFF;
else
c->phase = phase - (count << 16);
return dst;
}
static FLAC__StreamDecoderReadStatus
decinput(FLAC__StreamDecoder *dec, FLAC__byte buffer[], unsigned *bytes, void *client_data)
{
@ -105,24 +21,80 @@ decinput(FLAC__StreamDecoder *dec, FLAC__byte buffer[], unsigned *bytes, void *c
static FLAC__StreamDecoderWriteStatus
decoutput(FLAC__StreamDecoder *dec, FLAC__Frame *frame, FLAC__int32 *buffer[], void *client_data)
{
static uchar *buf;
static int nbuf;
static Chan c1, c0;
ulong length, n, delta, bps;
uchar *p;
static int rate, chans, bits;
static unsigned char *buf;
static int nbuf, ifd = -1;
FLAC__int32 *s, v;
unsigned char *p;
int i, j, n, b, len;
bps = frame->header.bits_per_sample;
length = frame->header.blocksize;
delta = (frame->header.sample_rate << 16) / rate;
n = 4 * (frame->header.sample_rate + length * rate) / frame->header.sample_rate;
/* start converter if format changed */
if(rate != frame->header.sample_rate
|| chans != frame->header.channels
|| bits != frame->header.bits_per_sample){
int pid, pfd[2];
char fmt[32];
rate = frame->header.sample_rate;
chans = frame->header.channels;
bits = frame->header.bits_per_sample;
sprintf(fmt, "s%dr%dc%d", bits, rate, chans);
if(ifd >= 0)
close(ifd);
if(pipe(pfd) < 0){
fprintf(stderr, "Error creating pipe\n");
exit(1);
}
pid = fork();
if(pid < 0){
fprintf(stderr, "Error forking\n");
exit(1);
}
if(pid == 0){
dup2(pfd[1], 0);
close(pfd[1]);
close(pfd[0]);
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, 0);
fprintf(stderr, "Error executing converter\n");
exit(1);
}
close(pfd[1]);
ifd = pfd[0];
}
len = frame->header.blocksize;
b = (bits+7)/8;
n = b * chans * len;
if(n > nbuf){
nbuf = n;
buf = realloc(buf, nbuf);
if(buf == NULL){
fprintf(stderr, "Error allocating memory\n");
exit(1);
}
}
if(frame->header.channels == 2)
resample(&c1, buffer[1], buf+2, 0, delta, length, bps);
p = resample(&c0, buffer[0], buf, frame->header.channels == 1, delta, length, bps);
fwrite(buf, p-buf, 1, stdout);
p = buf;
for(j=0; j < chans; j++){
s = buffer[j];
p = buf + j*b;
for(i=0; i < len; i++){
n = 0;
v = *s++;
switch(b){
case 4:
p[n++] = v, v>>=8;
case 3:
p[n++] = v, v>>=8;
case 2:
p[n++] = v, v>>=8;
case 1:
p[n] = v;
}
p += chans*b;
}
}
if(p > buf)
write(ifd, buf, p - buf);
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

View file

@ -396,7 +396,7 @@ typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *deco
* \retval FLAC__StreamDecoder*
* \c NULL if there was an error allocating memory, else the new instance.
*/
FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new();
FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void);
/** Free a decoder instance. Deletes the object pointed to by \a decoder.
*

View file

@ -1,7 +1,7 @@
</$objtype/mkfile
LIBS=libogg libvorbis libFLAC
PROGS=oggdec oggenc mp3dec mp3enc flacdec wavdec
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec
#libs must be made first
DIRS=$LIBS $PROGS

View file

@ -7,14 +7,14 @@
/* Current input file */
vlong offset;
int rate = 44100;
int debug = 0;
int ifd = -1;
static enum mad_flow
input(void *, struct mad_stream *stream)
{
int fd, n, m;
static uchar buf[32768];
int fd, n, m;
n = stream->bufend - stream->next_frame;
memmove(buf, stream->next_frame, n);
@ -29,99 +29,67 @@ input(void *, struct mad_stream *stream)
return MAD_FLOW_CONTINUE;
}
typedef struct Chan Chan;
struct Chan
{
ulong phase;
mad_fixed_t last;
mad_fixed_t rand;
};
#define PRNG(x) (((x)*0x19660dL + 0x3c6ef35fL) & 0xffffffffL)
enum
{
FracBits = MAD_F_FRACBITS,
OutBits = 16,
ScaleBits = FracBits + 1 - OutBits,
LowMask = (1<<ScaleBits) - 1,
Min = -MAD_F_ONE,
Max = MAD_F_ONE - 1,
};
static uchar*
resample(Chan *c, mad_fixed_t *src, uchar *dst, int mono, ulong delta, ulong count)
{
mad_fixed_t last, val, out, rand;
ulong phase, pos;
vlong v;
rand = c->rand;
last = c->last;
phase = c->phase;
pos = phase >> 16;
while(pos < count){
val = src[pos];
if(pos)
last = src[pos-1];
/* interpolate */
v = val;
v -= last;
v *= (phase & 0xFFFF);
out = last + (v >> 16);
/* dithering */
out += (rand & LowMask) - LowMask/2;
rand = PRNG(rand);
/* cliping */
if(out > Max)
out = Max;
else if(out < Min)
out = Min;
out >>= ScaleBits;
*dst++ = out;
*dst++ = out >> 8;
if(mono){
*dst++ = out;
*dst++ = out >> 8;
} else
dst += 2;
phase += delta;
pos = phase >> 16;
}
c->rand = rand;
c->last = val;
if(delta < 0x10000)
c->phase = phase & 0xFFFF;
else
c->phase = phase - (count << 16);
return dst;
}
static enum mad_flow
output(void *, struct mad_header const* header, struct mad_pcm *pcm)
{
static int rate, chans;
static uchar *buf;
static int nbuf;
static Chan c1, c0;
ulong n, delta;
mad_fixed_t v, *s;
int i, j, n;
uchar *p;
delta = (pcm->samplerate << 16) / rate;
n = 4 * (pcm->samplerate + pcm->length * rate) / pcm->samplerate;
/* start converter if format changed */
if(rate != pcm->samplerate || chans != pcm->channels){
int pid, pfd[2];
char fmt[32];
rate = pcm->samplerate;
chans = pcm->channels;
snprint(fmt, sizeof(fmt), "s32r%dc%d", rate, chans);
if(ifd >= 0){
close(ifd);
waitpid();
}
if(pipe(pfd) < 0)
sysfatal("pipe: %r");
pid = fork();
if(pid < 0)
sysfatal("fork: %r");
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]);
ifd = pfd[0];
}
n = 4 * chans * pcm->length;
if(n > nbuf){
nbuf = n;
buf = realloc(buf, nbuf);
if(buf == nil)
sysfatal("realloc: %r");
}
if(pcm->channels == 2)
resample(&c1, pcm->samples[1], buf+2, 0, delta, pcm->length);
p = resample(&c0, pcm->samples[0], buf, pcm->channels == 1, delta, pcm->length);
write(1, buf, p-buf);
p = buf;
for(j=0; j < chans; j++){
s = pcm->samples[j];
n = pcm->length;
p = buf + j*4;
for(i=0; i < n; i++){
v = *s++;
p[0] = v, v>>=8;
p[1] = v, v>>=8;
p[2] = v, v>>=8;
p[3] = v;
p += chans*4;
}
}
if(p > buf)
write(ifd, buf, p - buf);
return MAD_FLOW_CONTINUE;
}
@ -175,5 +143,11 @@ main(int argc, char **argv)
mad_decoder_init(&decoder, nil, input, nil, nil, output, error, nil);
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
mad_decoder_finish(&decoder);
if(ifd >= 0){
close(ifd);
waitpid();
}
exits(0);
}

View file

@ -4,7 +4,7 @@
TARGET=oggdec
CC=pcc
CFLAGS=-I../libvorbis -I../libogg
CFLAGS=-I../libvorbis -I../libogg -D_POSIX_SOURCE
%.$O: %.c
$CC $CFLAGS -c $stem.c

View file

@ -22,85 +22,72 @@
/* Note that this is POSIX, not ANSI code */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <vorbis/codec.h>
int rate = 44100;
enum {
Max = 32767,
Min = -32768,
};
typedef unsigned long ulong;
typedef unsigned char uchar;
typedef struct Chan Chan;
struct Chan
{
unsigned long phase;
float last;
};
static uchar*
resample(Chan *c, float *src, uchar *dst, int mono, ulong delta, ulong count)
{
float f, last, val;
ulong phase, pos;
int out;
last = c->last;
phase = c->phase;
pos = phase >> 16;
while(pos < count){
val = src[pos];
if(pos)
last = src[pos-1];
f = (float)(phase&0xFFFF)/0x10000;
out = (last + (val - last) * f) * 32767.f;
/* cliping */
if(out > Max)
out = Max;
else if(out < Min)
out = Min;
*dst++ = out;
*dst++ = out >> 8;
if(mono){
*dst++ = out;
*dst++ = out >> 8;
} else
dst += 2;
phase += delta;
pos = phase >> 16;
}
c->last = val;
if(delta < 0x10000)
c->phase = phase & 0xFFFF;
else
c->phase = phase - (count << 16);
return dst;
}
static void
output(float **pcm, int samples, vorbis_info *vi)
{
static uchar *buf;
static int nbuf;
static Chan c1, c0;
ulong n, delta;
uchar *p;
static int rate, chans;
static unsigned char *buf;
static int nbuf, ifd = -1;
unsigned char *p;
int i, j, n, v;
float *s;
delta = ((ulong)vi->rate << 16) / rate;
n = 4 * ((ulong)vi->rate + samples * rate) / (ulong)vi->rate;
/* start converter if format changed */
if(rate != vi->rate || chans != vi->channels){
int pid, pfd[2];
char fmt[32];
rate = vi->rate;
chans = vi->channels;
sprintf(fmt, "f%dr%dc%d", sizeof(float)*8, rate, chans);
if(ifd >= 0)
close(ifd);
if(pipe(pfd) < 0){
fprintf(stderr, "Error creating pipe\n");
exit(1);
}
pid = fork();
if(pid < 0){
fprintf(stderr, "Error forking\n");
exit(1);
}
if(pid == 0){
dup2(pfd[1], 0);
close(pfd[1]);
close(pfd[0]);
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, 0);
fprintf(stderr, "Error executing converter\n");
exit(1);
}
close(pfd[1]);
ifd = pfd[0];
}
n = sizeof(float) * chans * samples;
if(n > nbuf){
nbuf = n;
buf = realloc(buf, nbuf);
if(buf == NULL){
fprintf(stderr, "Error allocating memory\n");
exit(1);
}
}
if(vi->channels == 2)
resample(&c1, pcm[1], buf+2, 0, delta, samples);
p = resample(&c0, pcm[0], buf, vi->channels == 1, delta, samples);
fwrite(buf, p-buf, 1, stdout);
p = buf;
for(j=0; j < chans; j++){
s = pcm[j];
p = buf + j*sizeof(float);
for(i=0; i < samples; i++){
*((float*)p) = *s++;
p += chans*sizeof(float);
}
}
if(p > buf)
write(ifd, buf, p - buf);
}
int main(){

View file

@ -0,0 +1,8 @@
</$objtype/mkfile
<../config
OFILES=pcmconv.$O
TARG=pcmconv
</sys/src/cmd/mkone

View file

@ -0,0 +1,328 @@
#include <u.h>
#include <libc.h>
typedef struct Desc Desc;
typedef struct Chan Chan;
struct Desc
{
int rate;
int channels;
int framesz;
int bits;
int fmt;
};
struct Chan
{
ulong phase;
int last;
};
int*
resample(Chan *c, int *src, int *dst, ulong delta, ulong count)
{
int last, val;
ulong phase, pos;
vlong v;
if(delta == 0x10000){
/* same frequency */
memmove(dst, src, count*sizeof(int));
return dst + count;
}
val = 0;
last = c->last;
phase = c->phase;
pos = phase >> 16;
while(pos < count){
val = src[pos];
if(pos)
last = src[pos-1];
/* interpolate */
v = val;
v -= last;
v *= (phase & 0xFFFF);
v >>= 16;
v += last;
/* clipping */
if(v > 0x7fffffffLL)
v = 0x7fffffff;
else if(v < -0x80000000LL)
v = -0x80000000;
*dst++ = v;
phase += delta;
pos = phase >> 16;
}
c->last = val;
if(delta < 0x10000)
c->phase = phase & 0xFFFF;
else
c->phase = phase - (count << 16);
return dst;
}
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 = b;
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)
{
int i, s, b;
uint v;
b = (bits+7)/8;
s = sizeof(uint)*8-bits;
while(count--){
v = 0;
i = b;
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)
{
while(count--){
if(bits == 32)
*dst++ = *((float*)src) * 2147483647.f;
else
*dst++ = *((double*)src) * 2147483647.f;
src += skip;
}
}
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 = 0;
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)
{
int i, s, b;
uint v;
b = (bits+7)/8;
s = sizeof(uint)*8-bits;
while(count--){
v = ((~0UL>>1) + *src++) >> s;
i = 0;
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)
{
while(count--){
if(bits == 32)
*((float*)dst) = *src++ / 2147483647.f;
else
*((double*)dst) = *src++ / 2147483647.f;
dst += skip;
}
}
Desc
mkdesc(char *f)
{
Desc d;
int c;
char *p;
memset(&d, 0, sizeof(d));
p = f;
while(c = *p++){
switch(c){
case 'r':
d.rate = strtol(p, &p, 10);
break;
case 'c':
d.channels = strtol(p, &p, 10);
break;
case 's':
case 'u':
case 'f':
d.fmt = c;
d.bits = strtol(p, &p, 10);
break;
default:
goto Bad;
}
}
if(d.rate <= 0)
goto Bad;
if(d.fmt == 'f'){
if(d.bits != 32 && d.bits != 64)
goto Bad;
} else if(d.bits <= 0 || d.bits > 32)
goto Bad;
d.framesz = ((d.bits+7)/8) * d.channels;
if(d.framesz <= 0)
goto Bad;
return d;
Bad:
sysfatal("bad format: %s", f);
return d;
}
void
usage(void)
{
fprint(2, "usage: %s [-i fmt] [-o fmt] [-l length]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
uchar ibuf[8*1024], *obuf;
int *out, *in;
Chan ch[8];
Desc i, o;
ulong delta;
int k, r, n, m, p;
vlong l;
void (*oconv)(int *, uchar *, int, int, int) = nil;
void (*iconv)(int *, uchar *, int, int, int) = nil;
o = mkdesc("s16c2r44100");
i = o;
l = -1LL;
ARGBEGIN {
case 'i':
i = mkdesc(EARGF(usage()));
break;
case 'o':
o = mkdesc(EARGF(usage()));
break;
case 'l':
l = atoll(EARGF(usage()));
break;
default:
usage();
} ARGEND;
switch(i.fmt){
case 's': iconv = siconv; break;
case 'u': iconv = uiconv; break;
case 'f': iconv = ficonv; break;
}
switch(o.fmt){
case 's': oconv = soconv; break;
case 'u': oconv = uoconv; break;
case 'f': oconv = foconv; break;
}
delta = ((uvlong)i.rate << 16) / o.rate;
memset(ch, 0, sizeof(ch));
n = (sizeof(ibuf)-i.framesz)/i.framesz;
r = n*i.framesz;
m = (i.rate + n*o.rate)/i.rate;
in = sbrk(sizeof(int) * n);
out = sbrk(sizeof(int) * m);
obuf = sbrk(o.framesz * m);
if(in == nil || out == nil || obuf == nil)
sysfatal("out of memory");
for(;;){
if(l >= 0 && l < r)
r = l;
n = read(0, ibuf, r);
if(n < 0)
sysfatal("read: %r");
if(n == 0)
break;
if(l > 0)
l -= n;
n /= i.framesz;
(*iconv)(in, ibuf, i.bits, i.framesz, n);
m = resample(&ch[0], in, out, delta, n) - out;
if(m < 1)
continue;
(*oconv)(out, obuf, o.bits, o.framesz, m);
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);
resample(&ch[k], in, out, delta, n);
(*oconv)(out, obuf + k*((o.bits+7)/8), o.bits, o.framesz, m);
}
} else {
for(k=1; k<o.channels; k++)
(*oconv)(out, obuf + k*((o.bits+7)/8), o.bits, o.framesz, m);
}
m *= o.framesz;
write(1, obuf, m);
}
exits(0);
}

View file

@ -6,5 +6,3 @@ OFILES=wavdec.$O
TARG=wavdec
</sys/src/cmd/mkone
CFLAGS=-FVp

View file

@ -1,18 +1,7 @@
#include <u.h>
#include <libc.h>
int debug = 0;
int rate = 44100;
typedef struct Wave Wave;
typedef struct Chan Chan;
struct Chan
{
ulong phase;
int last;
};
struct Wave
{
int rate;
@ -42,106 +31,22 @@ get4(void)
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
}
uchar*
getcc(uchar tag[4])
char*
getcc(char tag[4])
{
if(readn(0, tag, 4) != 4)
sysfatal("read: %r");
return tag;
}
uchar*
resample(Chan *c, int *src, uchar *dst, int mono, ulong delta, ulong count)
{
int last, val, out;
ulong phase, pos;
vlong v;
last = c->last;
phase = c->phase;
pos = phase >> 16;
while(pos < count){
val = src[pos];
if(pos)
last = src[pos-1];
/* interpolate */
v = val;
v -= last;
v *= (phase & 0xFFFF);
out = (last + (v >> 16)) >> (sizeof(int)*8 - 16);
*dst++ = out;
*dst++ = out >> 8;
if(mono){
*dst++ = out;
*dst++ = out >> 8;
} else
dst += 2;
phase += delta;
pos = phase >> 16;
}
c->last = val;
if(delta < 0x10000)
c->phase = phase & 0xFFFF;
else
c->phase = phase - (count << 16);
return dst;
}
void
conv(int *dst, uchar *src, int bits, int skip, int n)
main(int, char *argv[])
{
int i, v;
while(n--){
if(bits == 8)
v = (int)src[0] - 127;
else {
v = 0;
switch(i = bits/8){
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];
}
}
v <<= sizeof(int)*8-bits;
*dst++ = v;
src += skip;
}
}
void
usage(void)
{
fprint(2, "usage: %s [ -d ]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
uchar buf[8*1024], *out, *p;
int *samples;
Chan c0, c1;
char buf[8*1024], fmt[32];
ulong len, n;
Wave wav;
ulong delta, len;
int n, z;
ARGBEGIN {
case 'd':
debug++;
break;
default:
usage();
} ARGEND;
argv0 = argv[0];
if(memcmp(getcc(buf), "RIFF", 4) != 0)
sysfatal("no riff format");
get4();
@ -177,39 +82,10 @@ main(int argc, char *argv[])
if(wav.fmt != 1)
sysfatal("compressed format (0x%x) not supported", wav.fmt);
if(wav.framesz <= 0 || wav.bits <= 0 || wav.framesz != wav.channels*wav.bits/8)
sysfatal("bad format");
if(debug)
fprint(2, "wave: PCM %d Hz, %d ch, %d bits\n",
wav.rate, wav.channels, wav.bits);
delta = (wav.rate << 16) / rate;
n = sizeof(buf)/wav.framesz;
samples = malloc(sizeof(int) * n);
out = malloc(4 * ((wav.rate + n*rate)/wav.rate));
if(samples == nil || out == nil)
sysfatal("out of memory");
while(len % wav.framesz)
--len;
while(len){
if(len < sizeof(buf))
n = len;
else
n = sizeof(buf);
while(n % wav.framesz)
--n;
if(readn(0, buf, n) != n)
sysfatal("read: %r");
len -= n;
n /= wav.framesz;
if(wav.channels == 2){
conv(samples, buf + wav.bits/8, wav.bits, wav.framesz, n);
resample(&c1, samples, out+2, 0, delta, n);
}
conv(samples, buf, wav.bits, wav.framesz, n);
p = resample(&c0, samples, out, wav.channels == 1, delta, n);
write(1, out, p-out);
}
exits(0);
snprint(fmt, sizeof(fmt), "%c%dr%dc%d",
wav.bits == 8 ? 'u' : 's', wav.bits,
wav.rate,
wav.channels);
snprint(buf, sizeof(buf), "%lud", len);
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", buf, 0);
}