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:
parent
9507061986
commit
0d84343fbc
11 changed files with 589 additions and 392 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(){
|
||||
|
|
8
sys/src/cmd/audio/pcmconv/mkfile
Normal file
8
sys/src/cmd/audio/pcmconv/mkfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
</$objtype/mkfile
|
||||
<../config
|
||||
|
||||
OFILES=pcmconv.$O
|
||||
|
||||
TARG=pcmconv
|
||||
|
||||
</sys/src/cmd/mkone
|
328
sys/src/cmd/audio/pcmconv/pcmconv.c
Normal file
328
sys/src/cmd/audio/pcmconv/pcmconv.c
Normal 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);
|
||||
}
|
|
@ -6,5 +6,3 @@ OFILES=wavdec.$O
|
|||
TARG=wavdec
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
|
||||
CFLAGS=-FVp
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue