xref: /netbsd-src/usr.bin/audio/common/wav.c (revision eb1fa7eb4ae6fc2d7292d5740199a587709f7021)
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