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
|
.TH AUDIO 1
|
||||||
.SH NAME
|
.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
|
.SH SYNOPSIS
|
||||||
.B audio/mp3dec
|
.B audio/mp3dec
|
||||||
[
|
[
|
||||||
|
@ -39,6 +39,18 @@ q ] [
|
||||||
.I "long or silly options"
|
.I "long or silly options"
|
||||||
]
|
]
|
||||||
.PP
|
.PP
|
||||||
|
.B audio/pcmconv
|
||||||
|
[
|
||||||
|
.B -i
|
||||||
|
.I fmt
|
||||||
|
] [
|
||||||
|
.B -o
|
||||||
|
.I fmt
|
||||||
|
] [
|
||||||
|
.B -l
|
||||||
|
.I length
|
||||||
|
]
|
||||||
|
.PP
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
These programs decode and encode various audio formats from and to
|
These programs decode and encode various audio formats from and to
|
||||||
16-bit stereo PCM (little endian). The decoders read the compressed
|
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
|
.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.
|
||||||
|
.PP
|
||||||
.I Oggenc
|
.I Oggenc
|
||||||
and
|
and
|
||||||
.I mp3enc
|
.I mp3enc
|
||||||
|
@ -195,7 +208,48 @@ disable VBR histogram display
|
||||||
.TP
|
.TP
|
||||||
.BI --voice
|
.BI --voice
|
||||||
experimental voice mode
|
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
|
.SH EXAMPLE
|
||||||
Play back an
|
Play back an
|
||||||
.L .mp3
|
.L .mp3
|
||||||
|
|
|
@ -1,92 +1,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "FLAC/stream_decoder.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
|
static FLAC__StreamDecoderReadStatus
|
||||||
decinput(FLAC__StreamDecoder *dec, FLAC__byte buffer[], unsigned *bytes, void *client_data)
|
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
|
static FLAC__StreamDecoderWriteStatus
|
||||||
decoutput(FLAC__StreamDecoder *dec, FLAC__Frame *frame, FLAC__int32 *buffer[], void *client_data)
|
decoutput(FLAC__StreamDecoder *dec, FLAC__Frame *frame, FLAC__int32 *buffer[], void *client_data)
|
||||||
{
|
{
|
||||||
static uchar *buf;
|
static int rate, chans, bits;
|
||||||
static int nbuf;
|
static unsigned char *buf;
|
||||||
static Chan c1, c0;
|
static int nbuf, ifd = -1;
|
||||||
ulong length, n, delta, bps;
|
FLAC__int32 *s, v;
|
||||||
uchar *p;
|
unsigned char *p;
|
||||||
|
int i, j, n, b, len;
|
||||||
|
|
||||||
bps = frame->header.bits_per_sample;
|
/* start converter if format changed */
|
||||||
length = frame->header.blocksize;
|
if(rate != frame->header.sample_rate
|
||||||
delta = (frame->header.sample_rate << 16) / rate;
|
|| chans != frame->header.channels
|
||||||
n = 4 * (frame->header.sample_rate + length * rate) / frame->header.sample_rate;
|
|| 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){
|
if(n > nbuf){
|
||||||
nbuf = n;
|
nbuf = n;
|
||||||
buf = realloc(buf, nbuf);
|
buf = realloc(buf, nbuf);
|
||||||
|
if(buf == NULL){
|
||||||
|
fprintf(stderr, "Error allocating memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(frame->header.channels == 2)
|
p = buf;
|
||||||
resample(&c1, buffer[1], buf+2, 0, delta, length, bps);
|
for(j=0; j < chans; j++){
|
||||||
p = resample(&c0, buffer[0], buf, frame->header.channels == 1, delta, length, bps);
|
s = buffer[j];
|
||||||
fwrite(buf, p-buf, 1, stdout);
|
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;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -396,7 +396,7 @@ typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *deco
|
||||||
* \retval FLAC__StreamDecoder*
|
* \retval FLAC__StreamDecoder*
|
||||||
* \c NULL if there was an error allocating memory, else the new instance.
|
* \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.
|
/** Free a decoder instance. Deletes the object pointed to by \a decoder.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
|
|
||||||
LIBS=libogg libvorbis libFLAC
|
LIBS=libogg libvorbis libFLAC
|
||||||
PROGS=oggdec oggenc mp3dec mp3enc flacdec wavdec
|
PROGS=pcmconv oggdec oggenc mp3dec mp3enc flacdec wavdec
|
||||||
#libs must be made first
|
#libs must be made first
|
||||||
DIRS=$LIBS $PROGS
|
DIRS=$LIBS $PROGS
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
/* Current input file */
|
/* Current input file */
|
||||||
vlong offset;
|
vlong offset;
|
||||||
int rate = 44100;
|
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
|
int ifd = -1;
|
||||||
|
|
||||||
static enum mad_flow
|
static enum mad_flow
|
||||||
input(void *, struct mad_stream *stream)
|
input(void *, struct mad_stream *stream)
|
||||||
{
|
{
|
||||||
int fd, n, m;
|
|
||||||
static uchar buf[32768];
|
static uchar buf[32768];
|
||||||
|
int fd, n, m;
|
||||||
|
|
||||||
n = stream->bufend - stream->next_frame;
|
n = stream->bufend - stream->next_frame;
|
||||||
memmove(buf, stream->next_frame, n);
|
memmove(buf, stream->next_frame, n);
|
||||||
|
@ -29,99 +29,67 @@ input(void *, struct mad_stream *stream)
|
||||||
return MAD_FLOW_CONTINUE;
|
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
|
static enum mad_flow
|
||||||
output(void *, struct mad_header const* header, struct mad_pcm *pcm)
|
output(void *, struct mad_header const* header, struct mad_pcm *pcm)
|
||||||
{
|
{
|
||||||
|
static int rate, chans;
|
||||||
static uchar *buf;
|
static uchar *buf;
|
||||||
static int nbuf;
|
static int nbuf;
|
||||||
static Chan c1, c0;
|
mad_fixed_t v, *s;
|
||||||
ulong n, delta;
|
int i, j, n;
|
||||||
uchar *p;
|
uchar *p;
|
||||||
|
|
||||||
delta = (pcm->samplerate << 16) / rate;
|
/* start converter if format changed */
|
||||||
n = 4 * (pcm->samplerate + pcm->length * rate) / pcm->samplerate;
|
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){
|
if(n > nbuf){
|
||||||
nbuf = n;
|
nbuf = n;
|
||||||
buf = realloc(buf, nbuf);
|
buf = realloc(buf, nbuf);
|
||||||
|
if(buf == nil)
|
||||||
|
sysfatal("realloc: %r");
|
||||||
}
|
}
|
||||||
if(pcm->channels == 2)
|
p = buf;
|
||||||
resample(&c1, pcm->samples[1], buf+2, 0, delta, pcm->length);
|
for(j=0; j < chans; j++){
|
||||||
p = resample(&c0, pcm->samples[0], buf, pcm->channels == 1, delta, pcm->length);
|
s = pcm->samples[j];
|
||||||
write(1, buf, p-buf);
|
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;
|
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_init(&decoder, nil, input, nil, nil, output, error, nil);
|
||||||
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
|
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
|
||||||
mad_decoder_finish(&decoder);
|
mad_decoder_finish(&decoder);
|
||||||
|
|
||||||
|
if(ifd >= 0){
|
||||||
|
close(ifd);
|
||||||
|
waitpid();
|
||||||
|
}
|
||||||
|
|
||||||
exits(0);
|
exits(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
TARGET=oggdec
|
TARGET=oggdec
|
||||||
|
|
||||||
CC=pcc
|
CC=pcc
|
||||||
CFLAGS=-I../libvorbis -I../libogg
|
CFLAGS=-I../libvorbis -I../libogg -D_POSIX_SOURCE
|
||||||
|
|
||||||
%.$O: %.c
|
%.$O: %.c
|
||||||
$CC $CFLAGS -c $stem.c
|
$CC $CFLAGS -c $stem.c
|
||||||
|
|
|
@ -22,85 +22,72 @@
|
||||||
/* Note that this is POSIX, not ANSI code */
|
/* Note that this is POSIX, not ANSI code */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <vorbis/codec.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
|
static void
|
||||||
output(float **pcm, int samples, vorbis_info *vi)
|
output(float **pcm, int samples, vorbis_info *vi)
|
||||||
{
|
{
|
||||||
static uchar *buf;
|
static int rate, chans;
|
||||||
static int nbuf;
|
static unsigned char *buf;
|
||||||
static Chan c1, c0;
|
static int nbuf, ifd = -1;
|
||||||
ulong n, delta;
|
unsigned char *p;
|
||||||
uchar *p;
|
int i, j, n, v;
|
||||||
|
float *s;
|
||||||
|
|
||||||
delta = ((ulong)vi->rate << 16) / rate;
|
/* start converter if format changed */
|
||||||
n = 4 * ((ulong)vi->rate + samples * rate) / (ulong)vi->rate;
|
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){
|
if(n > nbuf){
|
||||||
nbuf = n;
|
nbuf = n;
|
||||||
buf = realloc(buf, nbuf);
|
buf = realloc(buf, nbuf);
|
||||||
|
if(buf == NULL){
|
||||||
|
fprintf(stderr, "Error allocating memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(vi->channels == 2)
|
p = buf;
|
||||||
resample(&c1, pcm[1], buf+2, 0, delta, samples);
|
for(j=0; j < chans; j++){
|
||||||
p = resample(&c0, pcm[0], buf, vi->channels == 1, delta, samples);
|
s = pcm[j];
|
||||||
fwrite(buf, p-buf, 1, stdout);
|
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(){
|
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
|
TARG=wavdec
|
||||||
|
|
||||||
</sys/src/cmd/mkone
|
</sys/src/cmd/mkone
|
||||||
|
|
||||||
CFLAGS=-FVp
|
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
#include <u.h>
|
#include <u.h>
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
|
|
||||||
int debug = 0;
|
|
||||||
int rate = 44100;
|
|
||||||
|
|
||||||
typedef struct Wave Wave;
|
typedef struct Wave Wave;
|
||||||
typedef struct Chan Chan;
|
|
||||||
|
|
||||||
struct Chan
|
|
||||||
{
|
|
||||||
ulong phase;
|
|
||||||
int last;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Wave
|
struct Wave
|
||||||
{
|
{
|
||||||
int rate;
|
int rate;
|
||||||
|
@ -42,106 +31,22 @@ get4(void)
|
||||||
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
|
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
|
||||||
}
|
}
|
||||||
|
|
||||||
uchar*
|
char*
|
||||||
getcc(uchar tag[4])
|
getcc(char tag[4])
|
||||||
{
|
{
|
||||||
if(readn(0, tag, 4) != 4)
|
if(readn(0, tag, 4) != 4)
|
||||||
sysfatal("read: %r");
|
sysfatal("read: %r");
|
||||||
return tag;
|
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
|
void
|
||||||
conv(int *dst, uchar *src, int bits, int skip, int n)
|
main(int, char *argv[])
|
||||||
{
|
{
|
||||||
int i, v;
|
char buf[8*1024], fmt[32];
|
||||||
|
ulong len, n;
|
||||||
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;
|
|
||||||
Wave wav;
|
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)
|
if(memcmp(getcc(buf), "RIFF", 4) != 0)
|
||||||
sysfatal("no riff format");
|
sysfatal("no riff format");
|
||||||
get4();
|
get4();
|
||||||
|
@ -177,39 +82,10 @@ main(int argc, char *argv[])
|
||||||
|
|
||||||
if(wav.fmt != 1)
|
if(wav.fmt != 1)
|
||||||
sysfatal("compressed format (0x%x) not supported", wav.fmt);
|
sysfatal("compressed format (0x%x) not supported", wav.fmt);
|
||||||
if(wav.framesz <= 0 || wav.bits <= 0 || wav.framesz != wav.channels*wav.bits/8)
|
snprint(fmt, sizeof(fmt), "%c%dr%dc%d",
|
||||||
sysfatal("bad format");
|
wav.bits == 8 ? 'u' : 's', wav.bits,
|
||||||
if(debug)
|
wav.rate,
|
||||||
fprint(2, "wave: PCM %d Hz, %d ch, %d bits\n",
|
wav.channels);
|
||||||
wav.rate, wav.channels, wav.bits);
|
snprint(buf, sizeof(buf), "%lud", len);
|
||||||
|
execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, "-l", buf, 0);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue