1*eb1fa7ebSmrg /* $NetBSD: wav.c,v 1.24 2024/03/20 20:18:39 mrg Exp $ */
22d530607Smrg
337188d08Smrg /*
4b4c3b934Smrg * Copyright (c) 2002, 2009, 2013, 2015, 2019, 2024 Matthew R. Green
537188d08Smrg * All rights reserved.
637188d08Smrg *
737188d08Smrg * Redistribution and use in source and binary forms, with or without
837188d08Smrg * modification, are permitted provided that the following conditions
937188d08Smrg * are met:
1037188d08Smrg * 1. Redistributions of source code must retain the above copyright
1137188d08Smrg * notice, this list of conditions and the following disclaimer.
1237188d08Smrg * 2. Redistributions in binary form must reproduce the above copyright
1337188d08Smrg * notice, this list of conditions and the following disclaimer in the
1437188d08Smrg * documentation and/or other materials provided with the distribution.
1537188d08Smrg *
1637188d08Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1737188d08Smrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1837188d08Smrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1937188d08Smrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2037188d08Smrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2137188d08Smrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2237188d08Smrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2337188d08Smrg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2437188d08Smrg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2537188d08Smrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2637188d08Smrg * SUCH DAMAGE.
2737188d08Smrg */
2837188d08Smrg
2937188d08Smrg /*
3037188d08Smrg * WAV support for the audio tools; thanks go to the sox utility for
3137188d08Smrg * clearing up issues with WAV files.
3237188d08Smrg */
33c0db2196Sagc #include <sys/cdefs.h>
34c0db2196Sagc
35c0db2196Sagc #ifndef lint
36*eb1fa7ebSmrg __RCSID("$NetBSD: wav.c,v 1.24 2024/03/20 20:18:39 mrg Exp $");
37c0db2196Sagc #endif
38c0db2196Sagc
3937188d08Smrg
4037188d08Smrg #include <sys/types.h>
4137188d08Smrg #include <sys/audioio.h>
4237188d08Smrg #include <sys/ioctl.h>
4337188d08Smrg #include <sys/time.h>
4437188d08Smrg
4537188d08Smrg #include <ctype.h>
4637188d08Smrg #include <err.h>
4737188d08Smrg #include <stdio.h>
4837188d08Smrg #include <stdlib.h>
4937188d08Smrg #include <string.h>
5095c070caSmrg #include <stdint.h>
510c2e0646Smrg #include <unistd.h>
52b4c3b934Smrg #include <stdbool.h>
5337188d08Smrg
5437188d08Smrg #include "libaudio.h"
550c2e0646Smrg #include "auconv.h"
5637188d08Smrg
57c36a7298Sjoerg static const struct {
5837188d08Smrg int wenc;
59e7fdf182Smrg const char *wname;
6037188d08Smrg } wavencs[] = {
6137188d08Smrg { WAVE_FORMAT_UNKNOWN, "Microsoft Official Unknown" },
6237188d08Smrg { WAVE_FORMAT_PCM, "Microsoft PCM" },
6337188d08Smrg { WAVE_FORMAT_ADPCM, "Microsoft ADPCM" },
6495676db7Smrg { WAVE_FORMAT_IEEE_FLOAT,"Microsoft IEEE Floating-Point" },
6537188d08Smrg { WAVE_FORMAT_ALAW, "Microsoft A-law" },
6668caa698Swiz { WAVE_FORMAT_MULAW, "Microsoft mu-law" },
6737188d08Smrg { WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" },
6837188d08Smrg { WAVE_FORMAT_DIGISTD, "Digistd format" },
6937188d08Smrg { WAVE_FORMAT_DIGIFIX, "Digifix format" },
7037188d08Smrg { -1, "?Unknown?" },
7137188d08Smrg };
7237188d08Smrg
73e7fdf182Smrg const char *
wav_enc_from_val(int encoding)74e7fdf182Smrg wav_enc_from_val(int encoding)
7537188d08Smrg {
7637188d08Smrg int i;
7737188d08Smrg
7837188d08Smrg for (i = 0; wavencs[i].wenc != -1; i++)
7937188d08Smrg if (wavencs[i].wenc == encoding)
8037188d08Smrg break;
8137188d08Smrg return (wavencs[i].wname);
8237188d08Smrg }
8337188d08Smrg
8437188d08Smrg /*
8537188d08Smrg * sample header is:
8637188d08Smrg *
8737188d08Smrg * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@
8837188d08Smrg *
8937188d08Smrg */
9037188d08Smrg /*
9137188d08Smrg * WAV format helpers
9237188d08Smrg */
93c783de97Smrg
9421fadbc4Smrg #define RIFFNAMELEN 4
9521fadbc4Smrg
96c783de97Smrg static bool
find_riff_chunk(const char * search,size_t * remainp,char ** wherep,uint32_t * partlen)9721fadbc4Smrg find_riff_chunk(const char *search, size_t *remainp, char **wherep, uint32_t *partlen)
98c783de97Smrg {
99c783de97Smrg wav_audioheaderpart part;
100c783de97Smrg
101c783de97Smrg *partlen = 0;
102c783de97Smrg
103c783de97Smrg #define ADJUST(l) do { \
104*eb1fa7ebSmrg if (l > *(remainp)) \
105c783de97Smrg return false; \
106c783de97Smrg *(wherep) += (l); \
107c783de97Smrg *(remainp) -= (l); \
108c783de97Smrg } while (0)
109c783de97Smrg
110c783de97Smrg while (*remainp >= sizeof part) {
111c783de97Smrg const char *emsg = "";
112c783de97Smrg uint32_t len;
113c783de97Smrg
114c783de97Smrg memcpy(&part, *wherep, sizeof part);
115c783de97Smrg ADJUST(sizeof part);
116c783de97Smrg len = getle32(part.len);
117c783de97Smrg if (len % 2) {
118c783de97Smrg emsg = " (odd length, adjusted)";
119c783de97Smrg len += 1;
120c783de97Smrg }
12121fadbc4Smrg if (strncmp(part.name, search, RIFFNAMELEN) == 0) {
122c783de97Smrg *partlen = len;
123c783de97Smrg if (verbose > 1)
124c783de97Smrg fprintf(stderr, "Found part %.04s length %d%s\n",
125c783de97Smrg part.name, len, emsg);
126c783de97Smrg return true;
127c783de97Smrg }
128c783de97Smrg ADJUST(len);
129c783de97Smrg if (verbose > 1)
130c783de97Smrg fprintf(stderr, "Skipping part %.04s length %d%s\n",
131c783de97Smrg part.name, len, emsg);
132c783de97Smrg }
133c783de97Smrg #undef ADJUST
134c783de97Smrg
135c783de97Smrg return false;
136c783de97Smrg }
137c783de97Smrg
13837188d08Smrg /*
13937188d08Smrg * find a .wav header, etc. returns header length on success
14037188d08Smrg */
14137188d08Smrg ssize_t
audio_wav_parse_hdr(void * hdr,size_t sz,u_int * enc,u_int * prec,u_int * sample,u_int * channels,off_t * datasize)142c36a7298Sjoerg audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec,
143a0e3e391Smrg u_int *sample, u_int *channels, off_t *datasize)
14437188d08Smrg {
145b4c3b934Smrg char *where = hdr;
14637188d08Smrg wav_audioheaderfmt fmt;
14795c070caSmrg wav_audiohdrextensible ext;
148b4c3b934Smrg size_t remain = sz;
149b1da97fdSmrg u_int newenc, newprec;
150b4c3b934Smrg uint32_t len = 0;
15195c070caSmrg u_int16_t fmttag;
152e7fdf182Smrg static const char
15321fadbc4Smrg strfmt[RIFFNAMELEN] = "fmt ",
15421fadbc4Smrg strRIFF[RIFFNAMELEN] = "RIFF",
15521fadbc4Smrg strWAVE[RIFFNAMELEN] = "WAVE",
15621fadbc4Smrg strdata[RIFFNAMELEN] = "data";
157b4c3b934Smrg bool found;
15837188d08Smrg
15937188d08Smrg if (sz < 32)
16037188d08Smrg return (AUDIO_ENOENT);
16137188d08Smrg
162b4c3b934Smrg #define ADJUST(l) do { \
163*eb1fa7ebSmrg if ((l) > remain) \
164b4c3b934Smrg return (AUDIO_ESHORTHDR); \
165b4c3b934Smrg where += (l); \
166b4c3b934Smrg remain -= (l); \
167b4c3b934Smrg } while (0)
16837188d08Smrg
169cfa155a8Smrg if (memcmp(where, strRIFF, sizeof strRIFF) != 0)
170b4c3b934Smrg return (AUDIO_ENOENT);
171cfa155a8Smrg ADJUST(sizeof strRIFF);
172cfa155a8Smrg /* XXX we ignore the RIFF length here */
173b4c3b934Smrg ADJUST(4);
174cfa155a8Smrg if (memcmp(where, strWAVE, sizeof strWAVE) != 0)
175cfa155a8Smrg return (AUDIO_ENOENT);
176cfa155a8Smrg ADJUST(sizeof strWAVE);
177b4c3b934Smrg
178c783de97Smrg found = find_riff_chunk(strfmt, &remain, &where, &len);
17995c070caSmrg
180b4c3b934Smrg /* too short ? */
181b4c3b934Smrg if (!found || remain <= sizeof fmt)
182b4c3b934Smrg return (AUDIO_ESHORTHDR);
183b4c3b934Smrg
184b4c3b934Smrg memcpy(&fmt, where, sizeof fmt);
185b4c3b934Smrg fmttag = getle16(fmt.tag);
186b4c3b934Smrg if (verbose)
187b4c3b934Smrg printf("WAVE format tag/len: %04x/%u\n", fmttag, len);
188b4c3b934Smrg
189b4c3b934Smrg if (fmttag == WAVE_FORMAT_EXTENSIBLE) {
190b4c3b934Smrg if (len < sizeof(fmt) + sizeof(ext)) {
191b4c3b934Smrg if (verbose)
192b4c3b934Smrg fprintf(stderr, "short WAVE ext fmt\n");
193b4c3b934Smrg return (AUDIO_ESHORTHDR);
194b4c3b934Smrg }
195b4c3b934Smrg if (remain <= sizeof ext + sizeof fmt) {
196b4c3b934Smrg if (verbose)
197b4c3b934Smrg fprintf(stderr, "WAVE ext truncated\n");
198b4c3b934Smrg return (AUDIO_ESHORTHDR);
199b4c3b934Smrg }
200b4c3b934Smrg memcpy(&ext, where + sizeof fmt, sizeof ext);
201b4c3b934Smrg fmttag = getle16(ext.sub_tag);
202b4c3b934Smrg uint16_t sublen = getle16(ext.len);
203b4c3b934Smrg if (verbose)
204b4c3b934Smrg printf("WAVE extensible tag/len: %04x/%u\n", fmttag, sublen);
205b4c3b934Smrg
206b4c3b934Smrg /*
207b4c3b934Smrg * XXXMRG: it may be that part.len (aka sizeof fmt + sizeof ext)
208b4c3b934Smrg * should equal sizeof fmt + sizeof ext.len + sublen? this block
209b4c3b934Smrg * is only entered for part.len == 40, where ext.len is expected
210b4c3b934Smrg * to be 22 (sizeof ext.len = 2, sizeof fmt = 16).
211b4c3b934Smrg *
212b4c3b934Smrg * warn about this, but don't consider it an error.
213b4c3b934Smrg */
214c62cdc0dSmrg if (getle16(ext.len) != 22 && verbose) {
215c62cdc0dSmrg fprintf(stderr, "warning: WAVE ext.len %u not 22\n",
216c62cdc0dSmrg getle16(ext.len));
217c62cdc0dSmrg }
218b4c3b934Smrg } else if (len < sizeof(fmt)) {
219b4c3b934Smrg if (verbose)
220b4c3b934Smrg fprintf(stderr, "WAVE fmt unsupported size %u\n", len);
221b4c3b934Smrg return (AUDIO_EWAVUNSUPP);
222b4c3b934Smrg }
223b4c3b934Smrg ADJUST(len);
224b4c3b934Smrg
22595c070caSmrg switch (fmttag) {
22637188d08Smrg default:
22737188d08Smrg return (AUDIO_EWAVUNSUPP);
22837188d08Smrg
22937188d08Smrg case WAVE_FORMAT_PCM:
23095c070caSmrg case WAVE_FORMAT_ADPCM:
23195c070caSmrg case WAVE_FORMAT_OKI_ADPCM:
23295c070caSmrg case WAVE_FORMAT_IMA_ADPCM:
23395c070caSmrg case WAVE_FORMAT_DIGIFIX:
23495c070caSmrg case WAVE_FORMAT_DIGISTD:
23537188d08Smrg switch (getle16(fmt.bits_per_sample)) {
23637188d08Smrg case 8:
23737188d08Smrg newprec = 8;
23837188d08Smrg break;
23937188d08Smrg case 16:
24037188d08Smrg newprec = 16;
24137188d08Smrg break;
24237188d08Smrg case 24:
24337188d08Smrg newprec = 24;
24437188d08Smrg break;
24537188d08Smrg case 32:
24637188d08Smrg newprec = 32;
24737188d08Smrg break;
24837188d08Smrg default:
24937188d08Smrg return (AUDIO_EWAVBADPCM);
25037188d08Smrg }
25137188d08Smrg if (newprec == 8)
25237188d08Smrg newenc = AUDIO_ENCODING_ULINEAR_LE;
25337188d08Smrg else
25437188d08Smrg newenc = AUDIO_ENCODING_SLINEAR_LE;
25537188d08Smrg break;
25637188d08Smrg case WAVE_FORMAT_ALAW:
25737188d08Smrg newenc = AUDIO_ENCODING_ALAW;
25837188d08Smrg newprec = 8;
25937188d08Smrg break;
26037188d08Smrg case WAVE_FORMAT_MULAW:
26137188d08Smrg newenc = AUDIO_ENCODING_ULAW;
26237188d08Smrg newprec = 8;
26337188d08Smrg break;
26495676db7Smrg case WAVE_FORMAT_IEEE_FLOAT:
26595676db7Smrg switch (getle16(fmt.bits_per_sample)) {
26695676db7Smrg case 32:
26795676db7Smrg newenc = AUDIO_ENCODING_LIBAUDIO_FLOAT32;
26895676db7Smrg newprec = 32;
26995676db7Smrg break;
27095676db7Smrg case 64:
27195676db7Smrg newenc = AUDIO_ENCODING_LIBAUDIO_FLOAT64;
27295676db7Smrg newprec = 32;
27395676db7Smrg break;
27495676db7Smrg default:
27595676db7Smrg return (AUDIO_EWAVBADPCM);
27695676db7Smrg }
27795676db7Smrg break;
27837188d08Smrg }
27937188d08Smrg
280c783de97Smrg found = find_riff_chunk(strdata, &remain, &where, &len);
281b4c3b934Smrg if (!found)
282c783de97Smrg return (AUDIO_EWAVNODATA);
28337188d08Smrg
28437188d08Smrg if (channels)
285b1da97fdSmrg *channels = (u_int)getle16(fmt.channels);
28637188d08Smrg if (sample)
28737188d08Smrg *sample = getle32(fmt.sample_rate);
28837188d08Smrg if (enc)
28937188d08Smrg *enc = newenc;
29037188d08Smrg if (prec)
29137188d08Smrg *prec = newprec;
29237188d08Smrg if (datasize)
293cfa155a8Smrg *datasize = (off_t)len;
294b4c3b934Smrg return (where - (char *)hdr);
295b4c3b934Smrg
296b4c3b934Smrg #undef ADJUST
29737188d08Smrg }
2980c2e0646Smrg
2990c2e0646Smrg
3000c2e0646Smrg /*
3010c2e0646Smrg * prepare a WAV header for writing; we fill in hdrp, lenp and leftp,
3020c2e0646Smrg * and expect our caller (wav_write_header()) to use them.
3030c2e0646Smrg */
3040c2e0646Smrg int
wav_prepare_header(struct track_info * ti,void ** hdrp,size_t * lenp,int * leftp)305a0e3e391Smrg wav_prepare_header(struct track_info *ti, void **hdrp, size_t *lenp, int *leftp)
3060c2e0646Smrg {
3070c2e0646Smrg /*
3080c2e0646Smrg * WAV header we write looks like this:
3090c2e0646Smrg *
3100c2e0646Smrg * bytes purpose
3110c2e0646Smrg * 0-3 "RIFF"
312b4c3b934Smrg * 4-7 RIFF chunk length (file length minus 8)
3130c2e0646Smrg * 8-15 "WAVEfmt "
3140c2e0646Smrg * 16-19 format size
3150c2e0646Smrg * 20-21 format tag
3160c2e0646Smrg * 22-23 number of channels
3170c2e0646Smrg * 24-27 sample rate
3180c2e0646Smrg * 28-31 average bytes per second
3190c2e0646Smrg * 32-33 block alignment
3200c2e0646Smrg * 34-35 bits per sample
3210c2e0646Smrg *
3220c2e0646Smrg * then for ULAW and ALAW outputs, we have an extended chunk size
3230c2e0646Smrg * and a WAV "fact" to add:
3240c2e0646Smrg *
3250c2e0646Smrg * 36-37 length of extension (== 0)
3260c2e0646Smrg * 38-41 "fact"
3270c2e0646Smrg * 42-45 fact size
3280c2e0646Smrg * 46-49 number of samples written
3290c2e0646Smrg * 50-53 "data"
3300c2e0646Smrg * 54-57 data length
3310c2e0646Smrg * 58- raw audio data
3320c2e0646Smrg *
3330c2e0646Smrg * for PCM outputs we have just the data remaining:
3340c2e0646Smrg *
3350c2e0646Smrg * 36-39 "data"
3360c2e0646Smrg * 40-43 data length
3370c2e0646Smrg * 44- raw audio data
3380c2e0646Smrg *
3390c2e0646Smrg * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@
3400c2e0646Smrg */
3410c2e0646Smrg static char wavheaderbuf[64];
3420c2e0646Smrg char *p = wavheaderbuf;
3430c2e0646Smrg const char *riff = "RIFF",
3440c2e0646Smrg *wavefmt = "WAVEfmt ",
3450c2e0646Smrg *fact = "fact",
3460c2e0646Smrg *data = "data";
3470c2e0646Smrg u_int32_t filelen, fmtsz, sps, abps, factsz = 4, nsample, datalen;
3486e28978dSchristos u_int16_t fmttag, nchan, align, extln = 0;
3490c2e0646Smrg
350a0e3e391Smrg if (ti->header_info)
3510c2e0646Smrg warnx("header information not supported for WAV");
3520c2e0646Smrg *leftp = 0;
3530c2e0646Smrg
354a0e3e391Smrg switch (ti->precision) {
3550c2e0646Smrg case 8:
3560c2e0646Smrg break;
3570c2e0646Smrg case 16:
3580c2e0646Smrg break;
3593375d27cSmlelstv case 24:
3603375d27cSmlelstv break;
3610c2e0646Smrg case 32:
3620c2e0646Smrg break;
3630c2e0646Smrg default:
3640c2e0646Smrg {
3650c2e0646Smrg static int warned = 0;
3660c2e0646Smrg
3670c2e0646Smrg if (warned == 0) {
368a0e3e391Smrg warnx("can not support precision of %d", ti->precision);
3690c2e0646Smrg warned = 1;
3700c2e0646Smrg }
3710c2e0646Smrg }
3720c2e0646Smrg return (-1);
3730c2e0646Smrg }
3740c2e0646Smrg
375a0e3e391Smrg switch (ti->encoding) {
3760c2e0646Smrg case AUDIO_ENCODING_ULAW:
3770c2e0646Smrg fmttag = WAVE_FORMAT_MULAW;
3780c2e0646Smrg fmtsz = 18;
379a0e3e391Smrg align = ti->channels;
3800c2e0646Smrg break;
3810c2e0646Smrg
3820c2e0646Smrg case AUDIO_ENCODING_ALAW:
3830c2e0646Smrg fmttag = WAVE_FORMAT_ALAW;
3840c2e0646Smrg fmtsz = 18;
385a0e3e391Smrg align = ti->channels;
3860c2e0646Smrg break;
3870c2e0646Smrg
3880c2e0646Smrg /*
3890c2e0646Smrg * we could try to support RIFX but it seems to be more portable
3900c2e0646Smrg * to output little-endian data for WAV files.
3910c2e0646Smrg */
3920c2e0646Smrg case AUDIO_ENCODING_ULINEAR_BE:
3930c2e0646Smrg case AUDIO_ENCODING_SLINEAR_BE:
3940c2e0646Smrg case AUDIO_ENCODING_ULINEAR_LE:
3950c2e0646Smrg case AUDIO_ENCODING_SLINEAR_LE:
3960c2e0646Smrg case AUDIO_ENCODING_PCM16:
3970c2e0646Smrg
3980c2e0646Smrg #if BYTE_ORDER == LITTLE_ENDIAN
3990c2e0646Smrg case AUDIO_ENCODING_ULINEAR:
4000c2e0646Smrg case AUDIO_ENCODING_SLINEAR:
4010c2e0646Smrg #endif
4020c2e0646Smrg fmttag = WAVE_FORMAT_PCM;
4030c2e0646Smrg fmtsz = 16;
404a0e3e391Smrg align = ti->channels * (ti->precision / 8);
4050c2e0646Smrg break;
4060c2e0646Smrg
4070c2e0646Smrg default:
4080c2e0646Smrg #if 0 // move into record.c, and maybe merge.c
4090c2e0646Smrg {
4100c2e0646Smrg static int warned = 0;
4110c2e0646Smrg
4120c2e0646Smrg if (warned == 0) {
413a0e3e391Smrg const char *s = wav_enc_from_val(ti->encoding);
4140c2e0646Smrg
4150c2e0646Smrg if (s == NULL)
4160c2e0646Smrg warnx("can not support encoding of %s", s);
4170c2e0646Smrg else
418a0e3e391Smrg warnx("can not support encoding of %d", ti->encoding);
4190c2e0646Smrg warned = 1;
4200c2e0646Smrg }
4210c2e0646Smrg }
4220c2e0646Smrg #endif
423a0e3e391Smrg ti->format = AUDIO_FORMAT_NONE;
4240c2e0646Smrg return (-1);
4250c2e0646Smrg }
4260c2e0646Smrg
427a0e3e391Smrg nchan = ti->channels;
428a0e3e391Smrg sps = ti->sample_rate;
4290c2e0646Smrg
4300c2e0646Smrg /* data length */
431a0e3e391Smrg if (ti->outfd == STDOUT_FILENO)
4320c2e0646Smrg datalen = 0;
433a0e3e391Smrg else if (ti->total_size != -1)
434a0e3e391Smrg datalen = ti->total_size;
4350c2e0646Smrg else
4360c2e0646Smrg datalen = 0;
4370c2e0646Smrg
4380c2e0646Smrg /* file length */
4390c2e0646Smrg filelen = 4 + (8 + fmtsz) + (8 + datalen);
4400c2e0646Smrg if (fmttag != WAVE_FORMAT_PCM)
4410c2e0646Smrg filelen += 8 + factsz;
4420c2e0646Smrg
443a0e3e391Smrg abps = (double)align*ti->sample_rate / (double)1 + 0.5;
4440c2e0646Smrg
445a0e3e391Smrg nsample = (datalen / ti->precision) / ti->sample_rate;
4460c2e0646Smrg
4470c2e0646Smrg /*
4480c2e0646Smrg * now we've calculated the info, write it out!
4490c2e0646Smrg */
4500c2e0646Smrg #define put32(x) do { \
4510c2e0646Smrg u_int32_t _f; \
4520c2e0646Smrg putle32(_f, (x)); \
4530c2e0646Smrg memcpy(p, &_f, 4); \
4540c2e0646Smrg } while (0)
4550c2e0646Smrg #define put16(x) do { \
4560c2e0646Smrg u_int16_t _f; \
4570c2e0646Smrg putle16(_f, (x)); \
4580c2e0646Smrg memcpy(p, &_f, 2); \
4590c2e0646Smrg } while (0)
4600c2e0646Smrg memcpy(p, riff, 4);
4610c2e0646Smrg p += 4; /* 4 */
4620c2e0646Smrg put32(filelen);
4630c2e0646Smrg p += 4; /* 8 */
4640c2e0646Smrg memcpy(p, wavefmt, 8);
4650c2e0646Smrg p += 8; /* 16 */
4660c2e0646Smrg put32(fmtsz);
4670c2e0646Smrg p += 4; /* 20 */
4680c2e0646Smrg put16(fmttag);
4690c2e0646Smrg p += 2; /* 22 */
4700c2e0646Smrg put16(nchan);
4710c2e0646Smrg p += 2; /* 24 */
4720c2e0646Smrg put32(sps);
4730c2e0646Smrg p += 4; /* 28 */
4740c2e0646Smrg put32(abps);
4750c2e0646Smrg p += 4; /* 32 */
4760c2e0646Smrg put16(align);
4770c2e0646Smrg p += 2; /* 34 */
478a0e3e391Smrg put16(ti->precision);
4790c2e0646Smrg p += 2; /* 36 */
4800c2e0646Smrg /* NON PCM formats have an extended chunk; write it */
4810c2e0646Smrg if (fmttag != WAVE_FORMAT_PCM) {
4820c2e0646Smrg put16(extln);
4830c2e0646Smrg p += 2; /* 38 */
4840c2e0646Smrg memcpy(p, fact, 4);
4850c2e0646Smrg p += 4; /* 42 */
4860c2e0646Smrg put32(factsz);
4870c2e0646Smrg p += 4; /* 46 */
4880c2e0646Smrg put32(nsample);
4890c2e0646Smrg p += 4; /* 50 */
4900c2e0646Smrg }
4910c2e0646Smrg memcpy(p, data, 4);
4920c2e0646Smrg p += 4; /* 40/54 */
4930c2e0646Smrg put32(datalen);
4940c2e0646Smrg p += 4; /* 44/58 */
4950c2e0646Smrg #undef put32
4960c2e0646Smrg #undef put16
4970c2e0646Smrg
4980c2e0646Smrg *hdrp = wavheaderbuf;
4990c2e0646Smrg *lenp = (p - wavheaderbuf);
5000c2e0646Smrg
5010c2e0646Smrg return 0;
5020c2e0646Smrg }
5030c2e0646Smrg
5040c2e0646Smrg write_conv_func
wav_write_get_conv_func(struct track_info * ti)505a0e3e391Smrg wav_write_get_conv_func(struct track_info *ti)
5060c2e0646Smrg {
5070c2e0646Smrg write_conv_func conv_func = NULL;
5080c2e0646Smrg
509a0e3e391Smrg switch (ti->encoding) {
5100c2e0646Smrg
5110c2e0646Smrg /*
5120c2e0646Smrg * we could try to support RIFX but it seems to be more portable
5130c2e0646Smrg * to output little-endian data for WAV files.
5140c2e0646Smrg */
5150c2e0646Smrg case AUDIO_ENCODING_ULINEAR_BE:
5160c2e0646Smrg #if BYTE_ORDER == BIG_ENDIAN
5170c2e0646Smrg case AUDIO_ENCODING_ULINEAR:
5180c2e0646Smrg #endif
519a0e3e391Smrg if (ti->precision == 16)
5200c2e0646Smrg conv_func = change_sign16_swap_bytes_be;
521a0e3e391Smrg else if (ti->precision == 32)
5220c2e0646Smrg conv_func = change_sign32_swap_bytes_be;
5230c2e0646Smrg break;
5240c2e0646Smrg
5250c2e0646Smrg case AUDIO_ENCODING_SLINEAR_BE:
5260c2e0646Smrg #if BYTE_ORDER == BIG_ENDIAN
5270c2e0646Smrg case AUDIO_ENCODING_SLINEAR:
5280c2e0646Smrg #endif
529a0e3e391Smrg if (ti->precision == 8)
5300c2e0646Smrg conv_func = change_sign8;
531a0e3e391Smrg else if (ti->precision == 16)
5320c2e0646Smrg conv_func = swap_bytes;
533a0e3e391Smrg else if (ti->precision == 32)
5340c2e0646Smrg conv_func = swap_bytes32;
5350c2e0646Smrg break;
5360c2e0646Smrg
5370c2e0646Smrg case AUDIO_ENCODING_ULINEAR_LE:
5380c2e0646Smrg #if BYTE_ORDER == LITTLE_ENDIAN
5390c2e0646Smrg case AUDIO_ENCODING_ULINEAR:
5400c2e0646Smrg #endif
541a0e3e391Smrg if (ti->precision == 16)
5420c2e0646Smrg conv_func = change_sign16_le;
543a0e3e391Smrg else if (ti->precision == 32)
5440c2e0646Smrg conv_func = change_sign32_le;
5450c2e0646Smrg break;
5460c2e0646Smrg
5470c2e0646Smrg case AUDIO_ENCODING_SLINEAR_LE:
5480c2e0646Smrg case AUDIO_ENCODING_PCM16:
5490c2e0646Smrg #if BYTE_ORDER == LITTLE_ENDIAN
5500c2e0646Smrg case AUDIO_ENCODING_SLINEAR:
5510c2e0646Smrg #endif
552a0e3e391Smrg if (ti->precision == 8)
5530c2e0646Smrg conv_func = change_sign8;
5540c2e0646Smrg break;
5550c2e0646Smrg
5560c2e0646Smrg default:
557a0e3e391Smrg ti->format = AUDIO_FORMAT_NONE;
5580c2e0646Smrg }
5590c2e0646Smrg
5600c2e0646Smrg return conv_func;
5610c2e0646Smrg }
562