From f3ef9df7b437e6b75725b6444a30ee298b51d44e Mon Sep 17 00:00:00 2001 From: Alexandre Ratchov Date: Tue, 30 Dec 2014 18:45:05 +0100 Subject: [PATCH] add support for f32le in .wav files --- aucat/aucat.c | 46 +++++++++++++++-- aucat/wav.c | 140 ++++++++++++++++++++++++++++---------------------- aucat/wav.h | 10 +++- 3 files changed, 130 insertions(+), 66 deletions(-) diff --git a/aucat/aucat.c b/aucat/aucat.c index 69bc2ed..5df7a26 100644 --- a/aucat/aucat.c +++ b/aucat/aucat.c @@ -235,7 +235,20 @@ slot_new(char *path, int mode, struct aparams *par, int hdr, log_puts(", "); log_putu(s->wav.rate); log_puts("Hz, "); - aparams_log(&s->wav.par); + switch (s->wav.enc) { + case ENC_PCM: + aparams_log(&s->wav.par); + break; + case ENC_ULAW: + log_puts("ulaw"); + break; + case ENC_ALAW: + log_puts("alaw"); + break; + case ENC_F32LE: + log_puts("f32le"); + break; + } if (s->mode == SIO_PLAY && s->wav.endpos >= 0) { log_puts(", "); log_puti(s->wav.endpos - s->wav.startpos); @@ -294,8 +307,17 @@ slot_init(struct slot *s) s->cmin, s->cmax, 0, dev_pchan - 1, 0, dev_pchan - 1); - if (!aparams_native(&s->wav.par)) { - dec_init(&s->conv, &s->wav.par, slot_nch); + if (s->wav.enc != ENC_PCM || !aparams_native(&s->wav.par)) { + switch (s->wav.enc) { + case ENC_PCM: + dec_init(&s->conv, &s->wav.par, slot_nch); + break; + case ENC_ALAW: + case ENC_ULAW: + case ENC_F32LE: + /* nothing */ + break; + }; s->convbuf = xmalloc(s->round * slot_nch * sizeof(adata_t)); } @@ -447,8 +469,22 @@ play_filt_dec(struct slot *s, void *in, void *out, int todo) void *tmp; tmp = s->convbuf; - if (tmp) - dec_do(&s->conv, in, tmp, todo); + if (tmp) { + switch (s->wav.enc) { + case ENC_PCM: + dec_do(&s->conv, in, tmp, todo); + break; + case ENC_ULAW: + wav_dec_ulaw(in, tmp, todo * s->wav.nch); + break; + case ENC_ALAW: + wav_dec_alaw(in, tmp, todo * s->wav.nch); + break; + case ENC_F32LE: + wav_dec_f32le(in, tmp, todo * s->wav.nch); + break; + } + } return play_filt_resamp(s, tmp ? tmp : in, out, todo); } diff --git a/aucat/wav.c b/aucat/wav.c index 1616a22..158beb1 100644 --- a/aucat/wav.c +++ b/aucat/wav.c @@ -30,11 +30,11 @@ /* * Encoding IDs used in .wav headers. */ -#define WAV_ENC_PCM 1 -#define WAV_ENC_FLOAT 3 -#define WAV_ENC_ALAW 6 -#define WAV_ENC_ULAW 7 -#define WAV_ENC_EXT 0xfffe +#define WAV_FMT_PCM 1 +#define WAV_FMT_FLOAT 3 +#define WAV_FMT_ALAW 6 +#define WAV_FMT_ULAW 7 +#define WAV_FMT_EXT 0xfffe typedef struct { unsigned char ld[4]; @@ -191,24 +191,64 @@ le32_set(le32_t *p, unsigned int v) p->ld[3] = v >> 24; } -#if 0 +/* + * convert a 32-bit float to adata_t, clipping to -1:1, boundaries + * excluded + */ static inline int -f32_to_s24(unsigned int x) +f32_to_adata(unsigned int x) { unsigned int s, e, m, y; s = (x >> 31); e = (x >> 23) & 0xff; - m = (x & 0x007fffff) | 0x00800000; + m = (x << 8) | 0x80000000; if (e < 127 - 24) y = 0; else if (e > 127 - 1) - y = 0x00800000; + y = ADATA_UNIT - 1; else - y = m >> (127 - e); + y = m >> (127 + (32 - ADATA_BITS) - e); return (y ^ -s) + s; } -#endif + +/* + * convert samples from little endian ieee 754 floats to adata_t + */ +void +wav_dec_f32le(unsigned char *in, adata_t *out, int count) +{ + while (count > 0) { + *out = f32_to_adata(le32_get((le32_t *)in)); + in += 4; + out++; + count--; + } +} + +/* + * convert samples from ulaw to adata_t + */ +void +wav_dec_ulaw(unsigned char *in, adata_t *out, int count) +{ + while (count > 0) { + *out++ = (adata_t)(wav_ulawmap[*in++]) << (ADATA_BITS - 16); + count--; + } +} + +/* + * convert samples from alaw to adata_t + */ +void +wav_dec_alaw(unsigned char *in, adata_t *out, int count) +{ + while (count > 0) { + *out++ = (adata_t)(wav_alawmap[*in++]) << (ADATA_BITS - 16); + count--; + } +} static int wav_readfmt(struct wav *w, unsigned int csize) @@ -229,7 +269,7 @@ wav_readfmt(struct wav *w, unsigned int csize) } enc = le16_get(&fmt.fmt); bits = le16_get(&fmt.bits); - if (enc == WAV_ENC_EXT) { + if (enc == WAV_FMT_EXT) { if (csize != WAV_FMT_EXT_SIZE) { log_puts("missing extended format chunk in .wav file\n"); return 0; @@ -243,21 +283,6 @@ wav_readfmt(struct wav *w, unsigned int csize) enc = le16_get(&fmt.extfmt); } else bps = (bits + 7) / 8; - switch (enc) { - case WAV_ENC_PCM: - w->map = NULL; - break; - case WAV_ENC_ALAW: - w->map = wav_alawmap; - break; - case WAV_ENC_ULAW: - w->map = wav_ulawmap; - break; - default: - log_putu(enc); - log_puts(": unsupported encoding in .wav file\n"); - return 0; - } nch = le16_get(&fmt.nch); if (nch == 0) { log_puts("zero number of channels in .wav file\n"); @@ -278,22 +303,44 @@ wav_readfmt(struct wav *w, unsigned int csize) log_puts("bits larger than bytes-per-sample\n"); return 0; } - if (enc == WAV_ENC_PCM) { + switch (enc) { + case WAV_FMT_PCM: + w->enc = ENC_PCM; w->par.bps = bps; w->par.bits = bits; w->par.le = 1; w->par.sig = (bits <= 8) ? 0 : 1; /* ask microsoft why... */ w->par.msb = 1; - } else { + break; + case WAV_FMT_ALAW: + case WAV_FMT_ULAW: + w->enc = (enc == WAV_FMT_ULAW) ? ENC_ULAW : ENC_ALAW; if (bits != 8) { log_puts("mulaw/alaw encoding not 8-bit\n"); return 0; } - w->par.bits = ADATA_BITS; - w->par.bps = sizeof(adata_t); + w->par.bits = 8; + w->par.bps = 1; w->par.le = ADATA_LE; + w->par.sig = 0; + w->par.msb = 0; + break; + case WAV_FMT_FLOAT: + w->enc = ENC_F32LE; + if (bits != 32) { + log_puts("only 32-bit float supported\n"); + return 0; + } + w->par.bits = 32; + w->par.bps = 4; + w->par.le = 1; w->par.sig = 1; w->par.msb = 0; + break; + default: + log_putu(enc); + log_puts(": unsupported encoding in .wav file\n"); + return 0; } w->nch = nch; w->rate = rate; @@ -398,33 +445,12 @@ wav_writehdr(struct wav *w) return 1; } -/* - * convert ``count'' samples using the given char->short map - */ -static void -wav_conv_map(unsigned char *data, unsigned int count, short *map) -{ - unsigned int i; - unsigned char *iptr; - adata_t *optr; - - iptr = data + count; - optr = (adata_t *)data + count; - for (i = count; i > 0; i--) { - --optr; - --iptr; - *optr = (adata_t)(map[*iptr]) << (ADATA_BITS - 16); - } -} - size_t wav_read(struct wav *w, void *data, size_t count) { off_t maxread; ssize_t n; - - if (w->map) - count /= sizeof(adata_t); + if (w->endpos >= 0) { maxread = w->endpos - w->curpos; if (maxread == 0) { @@ -446,10 +472,6 @@ wav_read(struct wav *w, void *data, size_t count) return 0; } w->curpos += n; - if (w->map) { - wav_conv_map(data, n, w->map); - n *= sizeof(adata_t); - } return n; } @@ -488,8 +510,6 @@ wav_write(struct wav *w, void *data, size_t count) int wav_seek(struct wav *w, off_t pos) { - if (w->map) - pos /= sizeof(adata_t); pos += w->startpos; if (w->endpos >= 0 && pos > w->endpos) { log_puts(w->path); @@ -561,7 +581,7 @@ wav_open(struct wav *w, char *path, int hdr, int flags, } else { w->startpos = 0; w->endpos = -1; /* read until EOF */ - w->map = NULL; + w->enc = ENC_PCM; } w->curpos = w->startpos; } else if (flags == WAV_FWRITE) { diff --git a/aucat/wav.h b/aucat/wav.h index a345f8e..7d466cf 100644 --- a/aucat/wav.h +++ b/aucat/wav.h @@ -22,6 +22,11 @@ struct wav { struct aparams par; /* file params */ +#define ENC_PCM 0 /* simple integers (fixed point) */ +#define ENC_ULAW 1 /* 8-bit mu-law */ +#define ENC_ALAW 2 /* 8-bit a-law */ +#define ENC_F32LE 3 /* IEEE 754 32-bit floats */ + int enc; /* one of above */ int rate; /* file sample rate */ int nch; /* file channel count */ #define HDR_AUTO 0 @@ -36,7 +41,6 @@ struct wav { off_t startpos; /* where payload starts */ off_t endpos; /* where payload ends */ off_t maxpos; /* max allowed pos (.wav limitation) */ - short *map; /* mulaw/alaw conversions */ char *path; /* file name (debug only) */ }; @@ -46,4 +50,8 @@ size_t wav_write(struct wav *, void *, size_t); int wav_seek(struct wav *, off_t); void wav_close(struct wav *); +void wav_dec_f32le(unsigned char *, adata_t *, int); +void wav_dec_ulaw(unsigned char *, adata_t *, int); +void wav_dec_alaw(unsigned char *, adata_t *, int); + #endif /* !defined(WAV_H) */