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