xref: /netbsd-src/usr.bin/audio/common/audio.c (revision cca8938d5bda48f3624cdbf92c8d239ca49a033a)
1*cca8938dSgson /*	$NetBSD: audio.c,v 1.27 2024/02/27 21:05:34 gson Exp $	*/
2e0c321f2Smrg 
3e0c321f2Smrg /*
495676db7Smrg  * Copyright (c) 1999, 2013, 2015, 2019 Matthew R. Green
5e0c321f2Smrg  * All rights reserved.
6e0c321f2Smrg  *
7e0c321f2Smrg  * Redistribution and use in source and binary forms, with or without
8e0c321f2Smrg  * modification, are permitted provided that the following conditions
9e0c321f2Smrg  * are met:
10e0c321f2Smrg  * 1. Redistributions of source code must retain the above copyright
11e0c321f2Smrg  *    notice, this list of conditions and the following disclaimer.
12e0c321f2Smrg  * 2. Redistributions in binary form must reproduce the above copyright
13e0c321f2Smrg  *    notice, this list of conditions and the following disclaimer in the
14e0c321f2Smrg  *    documentation and/or other materials provided with the distribution.
15e0c321f2Smrg  *
16e0c321f2Smrg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e0c321f2Smrg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18e0c321f2Smrg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19e0c321f2Smrg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20e0c321f2Smrg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21e0c321f2Smrg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22e0c321f2Smrg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23e0c321f2Smrg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24e0c321f2Smrg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25e0c321f2Smrg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26e0c321f2Smrg  * SUCH DAMAGE.
27e0c321f2Smrg  */
28e0c321f2Smrg 
29dfe35feaSmrg /*
30dfe35feaSmrg  * XXX this is slightly icky in places...
31dfe35feaSmrg  */
32c0db2196Sagc #include <sys/cdefs.h>
33c0db2196Sagc 
34c0db2196Sagc #ifndef lint
35*cca8938dSgson __RCSID("$NetBSD: audio.c,v 1.27 2024/02/27 21:05:34 gson Exp $");
36c0db2196Sagc #endif
37c0db2196Sagc 
38dfe35feaSmrg 
39140c9e18Smrg #include <sys/types.h>
40140c9e18Smrg #include <sys/audioio.h>
41140c9e18Smrg #include <sys/ioctl.h>
42140c9e18Smrg #include <sys/time.h>
430c2e0646Smrg #include <sys/uio.h>
44140c9e18Smrg 
450c2e0646Smrg #include <unistd.h>
46140c9e18Smrg #include <ctype.h>
47140c9e18Smrg #include <err.h>
48140c9e18Smrg #include <stdio.h>
49140c9e18Smrg #include <stdlib.h>
50140c9e18Smrg #include <string.h>
51140c9e18Smrg 
52140c9e18Smrg #include "libaudio.h"
530c2e0646Smrg #include "auconv.h"
54140c9e18Smrg 
5537188d08Smrg /* what format am i? */
5637188d08Smrg 
57c36a7298Sjoerg static const struct {
58e7fdf182Smrg 	const char *fname;
5937188d08Smrg 	int fno;
6037188d08Smrg } formats[] = {
6137188d08Smrg 	{ "sunau",		AUDIO_FORMAT_SUN },
6237188d08Smrg 	{ "au",			AUDIO_FORMAT_SUN },
6337188d08Smrg 	{ "sun",		AUDIO_FORMAT_SUN },
6437188d08Smrg 	{ "wav",		AUDIO_FORMAT_WAV },
6537188d08Smrg 	{ "wave",		AUDIO_FORMAT_WAV },
6637188d08Smrg 	{ "riff",		AUDIO_FORMAT_WAV },
6737188d08Smrg 	{ "no",			AUDIO_FORMAT_NONE },
6837188d08Smrg 	{ "none",		AUDIO_FORMAT_NONE },
6937188d08Smrg 	{ NULL, -1 }
7037188d08Smrg };
7137188d08Smrg 
720c2e0646Smrg char	audio_default_info[8] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
730c2e0646Smrg 
7437188d08Smrg int
audio_format_from_str(char * str)75c36a7298Sjoerg audio_format_from_str(char *str)
7637188d08Smrg {
7737188d08Smrg 	int	i;
7837188d08Smrg 
7937188d08Smrg 	for (i = 0; formats[i].fname; i++)
8037188d08Smrg 		if (strcasecmp(formats[i].fname, str) == 0)
8137188d08Smrg 			break;
8237188d08Smrg 	return (formats[i].fno);
8337188d08Smrg }
8437188d08Smrg 
8537188d08Smrg 
8637188d08Smrg 
87140c9e18Smrg /* back and forth between encodings */
88c36a7298Sjoerg static const struct {
89e7fdf182Smrg 	const char *ename;
90140c9e18Smrg 	int eno;
91140c9e18Smrg } encs[] = {
92140c9e18Smrg 	{ AudioEmulaw,		AUDIO_ENCODING_ULAW },
93140c9e18Smrg 	{ "ulaw",		AUDIO_ENCODING_ULAW },
94140c9e18Smrg 	{ AudioEalaw, 		AUDIO_ENCODING_ALAW },
95140c9e18Smrg 	{ AudioEslinear,	AUDIO_ENCODING_SLINEAR },
96140c9e18Smrg 	{ "linear",		AUDIO_ENCODING_SLINEAR },
97140c9e18Smrg 	{ AudioEulinear,	AUDIO_ENCODING_ULINEAR },
98140c9e18Smrg 	{ AudioEadpcm,		AUDIO_ENCODING_ADPCM },
99140c9e18Smrg 	{ "ADPCM",		AUDIO_ENCODING_ADPCM },
100140c9e18Smrg 	{ AudioEslinear_le,	AUDIO_ENCODING_SLINEAR_LE },
101140c9e18Smrg 	{ "linear_le",		AUDIO_ENCODING_SLINEAR_LE },
102140c9e18Smrg 	{ AudioEulinear_le,	AUDIO_ENCODING_ULINEAR_LE },
103140c9e18Smrg 	{ AudioEslinear_be,	AUDIO_ENCODING_SLINEAR_BE },
104140c9e18Smrg 	{ "linear_be",		AUDIO_ENCODING_SLINEAR_BE },
105140c9e18Smrg 	{ AudioEulinear_be,	AUDIO_ENCODING_ULINEAR_BE },
106140c9e18Smrg 	{ AudioEmpeg_l1_stream,	AUDIO_ENCODING_MPEG_L1_STREAM },
107140c9e18Smrg 	{ AudioEmpeg_l1_packets,AUDIO_ENCODING_MPEG_L1_PACKETS },
108140c9e18Smrg 	{ AudioEmpeg_l1_system,	AUDIO_ENCODING_MPEG_L1_SYSTEM },
109140c9e18Smrg 	{ AudioEmpeg_l2_stream,	AUDIO_ENCODING_MPEG_L2_STREAM },
110140c9e18Smrg 	{ AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS },
111140c9e18Smrg 	{ AudioEmpeg_l2_system,	AUDIO_ENCODING_MPEG_L2_SYSTEM },
112a123dfbeSjmcneill 	{ AudioEac3,		AUDIO_ENCODING_AC3 },
11395676db7Smrg 	{ "ieee_float32",	AUDIO_ENCODING_LIBAUDIO_FLOAT32 },
11495676db7Smrg 	{ "ieee_float64",	AUDIO_ENCODING_LIBAUDIO_FLOAT64 },
115feac6b28Stron 	{ NULL, -1 }
116140c9e18Smrg };
117140c9e18Smrg 
118140c9e18Smrg 
119e7fdf182Smrg const char *
audio_enc_from_val(int val)120c36a7298Sjoerg audio_enc_from_val(int val)
121140c9e18Smrg {
122140c9e18Smrg 	int	i;
123140c9e18Smrg 
124140c9e18Smrg 	for (i = 0; encs[i].ename; i++)
125140c9e18Smrg 		if (encs[i].eno == val)
126140c9e18Smrg 			break;
127140c9e18Smrg 	return (encs[i].ename);
128140c9e18Smrg }
129140c9e18Smrg 
130140c9e18Smrg int
audio_enc_to_val(const char * enc)131c36a7298Sjoerg audio_enc_to_val(const char *enc)
132140c9e18Smrg {
133140c9e18Smrg 	int	i;
134140c9e18Smrg 
135140c9e18Smrg 	for (i = 0; encs[i].ename; i++)
136140c9e18Smrg 		if (strcmp(encs[i].ename, enc) == 0)
137140c9e18Smrg 			break;
138140c9e18Smrg 	if (encs[i].ename)
139140c9e18Smrg 		return (encs[i].eno);
140140c9e18Smrg 	else
141140c9e18Smrg 		return (AUDIO_ENOENT);
142140c9e18Smrg }
143140c9e18Smrg 
144140c9e18Smrg /*
145140c9e18Smrg  * decode a string into an encoding value.
146140c9e18Smrg  */
147140c9e18Smrg void
decode_encoding(const char * arg,int * encp)148c36a7298Sjoerg decode_encoding(const char *arg, int *encp)
149140c9e18Smrg {
150140c9e18Smrg 	size_t	len;
151140c9e18Smrg 	int i;
152140c9e18Smrg 
153140c9e18Smrg 	len = strlen(arg);
154140c9e18Smrg 	for (i = 0; encs[i].ename; i++)
155140c9e18Smrg 		if (strncmp(encs[i].ename, arg, len) == 0) {
156140c9e18Smrg 			*encp = encs[i].eno;
157140c9e18Smrg 			return;
158140c9e18Smrg 		}
159140c9e18Smrg 	errx(1, "unknown encoding `%s'", arg);
160140c9e18Smrg }
161140c9e18Smrg 
162c36a7298Sjoerg static const char *const audio_errlist[] = {
163cc78535bSmrg 	"error zero",				/* nothing? */
164cc78535bSmrg 	"no audio entry",			/* AUDIO_ENOENT */
165cc78535bSmrg 	"short header",				/* AUDIO_ESHORTHDR */
166cc78535bSmrg 	"unsupported WAV format",		/* AUDIO_EWAVUNSUPP */
167cc78535bSmrg 	"bad (unsupported) WAV PCM format",	/* AUDIO_EWAVBADPCM */
168cc78535bSmrg 	"no WAV audio data",			/* AUDIO_EWAVNODATA */
16937188d08Smrg 	"internal error",			/* AUDIO_EINTERNAL */
170140c9e18Smrg };
171140c9e18Smrg 
172140c9e18Smrg const char *
audio_errstring(int errval)173c36a7298Sjoerg audio_errstring(int errval)
174140c9e18Smrg {
175140c9e18Smrg 
176140c9e18Smrg 	errval = -errval;
177140c9e18Smrg 	if (errval < 1 || errval > AUDIO_MAXERRNO)
178140c9e18Smrg 		return "Invalid error";
179140c9e18Smrg 	return audio_errlist[errval];
180140c9e18Smrg }
1810c2e0646Smrg 
1820c2e0646Smrg void
write_header(struct track_info * ti)183a0e3e391Smrg write_header(struct track_info *ti)
1840c2e0646Smrg {
1850c2e0646Smrg 	struct iovec iv[3];
1860c2e0646Smrg 	int veclen, left, tlen;
1870c2e0646Smrg 	void *hdr;
1880c2e0646Smrg 	size_t hdrlen;
1890c2e0646Smrg 
190a0e3e391Smrg 	switch (ti->format) {
1910c2e0646Smrg 	case AUDIO_FORMAT_DEFAULT:
1920c2e0646Smrg 	case AUDIO_FORMAT_SUN:
193a0e3e391Smrg 		if (sun_prepare_header(ti, &hdr, &hdrlen, &left) != 0)
1940c2e0646Smrg 			return;
1950c2e0646Smrg 		break;
1960c2e0646Smrg 	case AUDIO_FORMAT_WAV:
197a0e3e391Smrg 		if (wav_prepare_header(ti, &hdr, &hdrlen, &left) != 0)
1980c2e0646Smrg 			return;
1990c2e0646Smrg 		break;
2000c2e0646Smrg 	case AUDIO_FORMAT_NONE:
2010c2e0646Smrg 		return;
2020c2e0646Smrg 	default:
2030c2e0646Smrg 		errx(1, "unknown audio format");
2040c2e0646Smrg 	}
2050c2e0646Smrg 
2060c2e0646Smrg 	veclen = 0;
2070c2e0646Smrg 	tlen = 0;
2080c2e0646Smrg 
2090c2e0646Smrg 	if (hdrlen != 0) {
2100c2e0646Smrg 		iv[veclen].iov_base = hdr;
2110c2e0646Smrg 		iv[veclen].iov_len = hdrlen;
2120c2e0646Smrg 		tlen += iv[veclen++].iov_len;
2130c2e0646Smrg 	}
214a0e3e391Smrg 	if (ti->header_info) {
215a0e3e391Smrg 		iv[veclen].iov_base = ti->header_info;
216a0e3e391Smrg 		iv[veclen].iov_len = (int)strlen(ti->header_info) + 1;
2170c2e0646Smrg 		tlen += iv[veclen++].iov_len;
2180c2e0646Smrg 	}
2190c2e0646Smrg 	if (left) {
2200c2e0646Smrg 		iv[veclen].iov_base = audio_default_info;
2210c2e0646Smrg 		iv[veclen].iov_len = left;
2220c2e0646Smrg 		tlen += iv[veclen++].iov_len;
2230c2e0646Smrg 	}
2240c2e0646Smrg 
2250c2e0646Smrg 	if (tlen == 0)
2260c2e0646Smrg 		return;
2270c2e0646Smrg 
228a0e3e391Smrg 	if (writev(ti->outfd, iv, veclen) != tlen)
2290c2e0646Smrg 		err(1, "could not write audio header");
2300c2e0646Smrg }
2310c2e0646Smrg 
2320c2e0646Smrg write_conv_func
write_get_conv_func(struct track_info * ti)233a0e3e391Smrg write_get_conv_func(struct track_info *ti)
2340c2e0646Smrg {
2350c2e0646Smrg 
236a0e3e391Smrg 	switch (ti->format) {
2370c2e0646Smrg 	case AUDIO_FORMAT_DEFAULT:
2380c2e0646Smrg 	case AUDIO_FORMAT_SUN:
239a0e3e391Smrg 		return sun_write_get_conv_func(ti);
2400c2e0646Smrg 	case AUDIO_FORMAT_WAV:
241a0e3e391Smrg 		return wav_write_get_conv_func(ti);
2420c2e0646Smrg 	case AUDIO_FORMAT_NONE:
2430c2e0646Smrg 		return NULL;
2440c2e0646Smrg 	default:
2450c2e0646Smrg 		errx(1, "unknown audio format");
2460c2e0646Smrg 	}
2470c2e0646Smrg }
248