record.c revision 1.55 1 1.55 riastrad /* $NetBSD: record.c,v 1.55 2021/06/01 21:08:48 riastradh Exp $ */
2 1.2 mrg
3 1.2 mrg /*
4 1.48 mrg * Copyright (c) 1999, 2002, 2003, 2005, 2010 Matthew R. Green
5 1.2 mrg * All rights reserved.
6 1.2 mrg *
7 1.2 mrg * Redistribution and use in source and binary forms, with or without
8 1.2 mrg * modification, are permitted provided that the following conditions
9 1.2 mrg * are met:
10 1.2 mrg * 1. Redistributions of source code must retain the above copyright
11 1.2 mrg * notice, this list of conditions and the following disclaimer.
12 1.2 mrg * 2. Redistributions in binary form must reproduce the above copyright
13 1.2 mrg * notice, this list of conditions and the following disclaimer in the
14 1.2 mrg * documentation and/or other materials provided with the distribution.
15 1.2 mrg *
16 1.2 mrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 1.2 mrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 1.2 mrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 1.2 mrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 1.2 mrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 1.2 mrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 1.2 mrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 1.2 mrg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 1.2 mrg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.2 mrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.2 mrg * SUCH DAMAGE.
27 1.2 mrg */
28 1.2 mrg
29 1.1 mrg /*
30 1.1 mrg * SunOS compatible audiorecord(1)
31 1.1 mrg */
32 1.33 agc #include <sys/cdefs.h>
33 1.33 agc
34 1.33 agc #ifndef lint
35 1.55 riastrad __RCSID("$NetBSD: record.c,v 1.55 2021/06/01 21:08:48 riastradh Exp $");
36 1.33 agc #endif
37 1.33 agc
38 1.1 mrg
39 1.48 mrg #include <sys/param.h>
40 1.1 mrg #include <sys/audioio.h>
41 1.1 mrg #include <sys/ioctl.h>
42 1.1 mrg #include <sys/time.h>
43 1.1 mrg #include <sys/uio.h>
44 1.1 mrg
45 1.1 mrg #include <err.h>
46 1.1 mrg #include <fcntl.h>
47 1.1 mrg #include <paths.h>
48 1.1 mrg #include <signal.h>
49 1.1 mrg #include <stdio.h>
50 1.1 mrg #include <stdlib.h>
51 1.1 mrg #include <string.h>
52 1.1 mrg #include <unistd.h>
53 1.44 lukem #include <util.h>
54 1.1 mrg
55 1.1 mrg #include "libaudio.h"
56 1.19 mrg #include "auconv.h"
57 1.1 mrg
58 1.51 joerg static audio_info_t info, oinfo;
59 1.51 joerg static const char *device;
60 1.53 mrg static int audiofd;
61 1.53 mrg static int aflag, fflag;
62 1.1 mrg int verbose;
63 1.51 joerg static int monitor_gain, omonitor_gain;
64 1.51 joerg static int gain;
65 1.51 joerg static int balance;
66 1.51 joerg static int port;
67 1.51 joerg static char *encoding_str;
68 1.54 mrg static struct track_info ti;
69 1.51 joerg static struct timeval record_time;
70 1.51 joerg static struct timeval start_time;
71 1.51 joerg
72 1.51 joerg static void (*conv_func) (u_char *, int);
73 1.51 joerg
74 1.51 joerg static void usage (void) __dead;
75 1.51 joerg static int timeleft (struct timeval *, struct timeval *);
76 1.51 joerg static void cleanup (int) __dead;
77 1.51 joerg static void rewrite_header (void);
78 1.1 mrg
79 1.1 mrg int
80 1.51 joerg main(int argc, char *argv[])
81 1.1 mrg {
82 1.43 mrg u_char *buffer;
83 1.48 mrg size_t len, bufsize = 0;
84 1.55 riastrad ssize_t nread;
85 1.8 mrg int ch, no_time_limit = 1;
86 1.24 augustss const char *defdevice = _PATH_SOUND;
87 1.1 mrg
88 1.53 mrg /*
89 1.54 mrg * Initialise the track_info.
90 1.53 mrg */
91 1.54 mrg ti.format = AUDIO_FORMAT_DEFAULT;
92 1.54 mrg ti.total_size = -1;
93 1.53 mrg
94 1.48 mrg while ((ch = getopt(argc, argv, "ab:B:C:F:c:d:e:fhi:m:P:p:qt:s:Vv:")) != -1) {
95 1.1 mrg switch (ch) {
96 1.1 mrg case 'a':
97 1.1 mrg aflag++;
98 1.1 mrg break;
99 1.1 mrg case 'b':
100 1.1 mrg decode_int(optarg, &balance);
101 1.1 mrg if (balance < 0 || balance > 63)
102 1.30 grant errx(1, "balance must be between 0 and 63");
103 1.1 mrg break;
104 1.48 mrg case 'B':
105 1.48 mrg bufsize = strsuftoll("read buffer size", optarg,
106 1.48 mrg 1, UINT_MAX);
107 1.48 mrg break;
108 1.1 mrg case 'C':
109 1.23 jdolecek /* Ignore, compatibility */
110 1.1 mrg break;
111 1.17 mrg case 'F':
112 1.54 mrg ti.format = audio_format_from_str(optarg);
113 1.54 mrg if (ti.format < 0)
114 1.27 mrg errx(1, "Unknown audio format; supported "
115 1.27 mrg "formats: \"sun\", \"wav\", and \"none\"");
116 1.17 mrg break;
117 1.1 mrg case 'c':
118 1.54 mrg decode_int(optarg, &ti.channels);
119 1.54 mrg if (ti.channels < 0 || ti.channels > 16)
120 1.30 grant errx(1, "channels must be between 0 and 16");
121 1.1 mrg break;
122 1.1 mrg case 'd':
123 1.1 mrg device = optarg;
124 1.1 mrg break;
125 1.1 mrg case 'e':
126 1.1 mrg encoding_str = optarg;
127 1.1 mrg break;
128 1.1 mrg case 'f':
129 1.1 mrg fflag++;
130 1.1 mrg break;
131 1.1 mrg case 'i':
132 1.54 mrg ti.header_info = optarg;
133 1.1 mrg break;
134 1.1 mrg case 'm':
135 1.17 mrg decode_int(optarg, &monitor_gain);
136 1.17 mrg if (monitor_gain < 0 || monitor_gain > 255)
137 1.30 grant errx(1, "monitor volume must be between 0 and 255");
138 1.1 mrg break;
139 1.1 mrg case 'P':
140 1.54 mrg decode_int(optarg, &ti.precision);
141 1.54 mrg if (ti.precision != 4 && ti.precision != 8 &&
142 1.54 mrg ti.precision != 16 && ti.precision != 24 &&
143 1.54 mrg ti.precision != 32)
144 1.15 minoura errx(1, "precision must be between 4, 8, 16, 24 or 32");
145 1.1 mrg break;
146 1.1 mrg case 'p':
147 1.1 mrg len = strlen(optarg);
148 1.1 mrg
149 1.1 mrg if (strncmp(optarg, "mic", len) == 0)
150 1.1 mrg port |= AUDIO_MICROPHONE;
151 1.1 mrg else if (strncmp(optarg, "cd", len) == 0 ||
152 1.1 mrg strncmp(optarg, "internal-cd", len) == 0)
153 1.1 mrg port |= AUDIO_CD;
154 1.1 mrg else if (strncmp(optarg, "line", len) == 0)
155 1.1 mrg port |= AUDIO_LINE_IN;
156 1.1 mrg else
157 1.1 mrg errx(1,
158 1.1 mrg "port must be `cd', `internal-cd', `mic', or `line'");
159 1.1 mrg break;
160 1.1 mrg case 'q':
161 1.54 mrg ti.qflag++;
162 1.1 mrg break;
163 1.1 mrg case 's':
164 1.54 mrg decode_int(optarg, &ti.sample_rate);
165 1.54 mrg if (ti.sample_rate < 0 || ti.sample_rate > 48000 * 2) /* XXX */
166 1.30 grant errx(1, "sample rate must be between 0 and 96000");
167 1.1 mrg break;
168 1.1 mrg case 't':
169 1.8 mrg no_time_limit = 0;
170 1.1 mrg decode_time(optarg, &record_time);
171 1.1 mrg break;
172 1.1 mrg case 'V':
173 1.1 mrg verbose++;
174 1.1 mrg break;
175 1.1 mrg case 'v':
176 1.17 mrg decode_int(optarg, &gain);
177 1.17 mrg if (gain < 0 || gain > 255)
178 1.30 grant errx(1, "volume must be between 0 and 255");
179 1.1 mrg break;
180 1.1 mrg /* case 'h': */
181 1.1 mrg default:
182 1.1 mrg usage();
183 1.1 mrg /* NOTREACHED */
184 1.1 mrg }
185 1.1 mrg }
186 1.1 mrg argc -= optind;
187 1.1 mrg argv += optind;
188 1.1 mrg
189 1.39 mrg if (argc != 1)
190 1.39 mrg usage();
191 1.39 mrg
192 1.1 mrg /*
193 1.40 mrg * convert the encoding string into a value.
194 1.40 mrg */
195 1.40 mrg if (encoding_str) {
196 1.54 mrg ti.encoding = audio_enc_to_val(encoding_str);
197 1.54 mrg if (ti.encoding == -1)
198 1.40 mrg errx(1, "unknown encoding, bailing...");
199 1.40 mrg }
200 1.40 mrg
201 1.40 mrg /*
202 1.40 mrg * open the output file
203 1.40 mrg */
204 1.42 gson if (argv[0][0] != '-' || argv[0][1] != '\0') {
205 1.40 mrg /* intuit the file type from the name */
206 1.54 mrg if (ti.format == AUDIO_FORMAT_DEFAULT)
207 1.40 mrg {
208 1.40 mrg size_t flen = strlen(*argv);
209 1.40 mrg const char *arg = *argv;
210 1.40 mrg
211 1.40 mrg if (strcasecmp(arg + flen - 3, ".au") == 0)
212 1.54 mrg ti.format = AUDIO_FORMAT_SUN;
213 1.40 mrg else if (strcasecmp(arg + flen - 4, ".wav") == 0)
214 1.54 mrg ti.format = AUDIO_FORMAT_WAV;
215 1.40 mrg }
216 1.54 mrg ti.outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666);
217 1.54 mrg if (ti.outfd < 0)
218 1.40 mrg err(1, "could not open %s", *argv);
219 1.40 mrg } else
220 1.54 mrg ti.outfd = STDOUT_FILENO;
221 1.40 mrg
222 1.40 mrg /*
223 1.34 mrg * open the audio device
224 1.1 mrg */
225 1.6 kleink if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL &&
226 1.6 kleink (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
227 1.24 augustss device = defdevice;
228 1.1 mrg
229 1.1 mrg audiofd = open(device, O_RDONLY);
230 1.24 augustss if (audiofd < 0 && device == defdevice) {
231 1.22 augustss device = _PATH_SOUND0;
232 1.28 uwe audiofd = open(device, O_RDONLY);
233 1.22 augustss }
234 1.1 mrg if (audiofd < 0)
235 1.1 mrg err(1, "failed to open %s", device);
236 1.1 mrg
237 1.1 mrg /*
238 1.1 mrg * work out the buffer size to use, and allocate it. also work out
239 1.1 mrg * what the old monitor gain value is, so that we can reset it later.
240 1.1 mrg */
241 1.23 jdolecek if (ioctl(audiofd, AUDIO_GETINFO, &oinfo) < 0)
242 1.1 mrg err(1, "failed to get audio info");
243 1.49 jmcneill if (bufsize == 0) {
244 1.48 mrg bufsize = oinfo.record.buffer_size;
245 1.49 jmcneill if (bufsize < 32 * 1024)
246 1.49 jmcneill bufsize = 32 * 1024;
247 1.49 jmcneill }
248 1.17 mrg omonitor_gain = oinfo.monitor_gain;
249 1.1 mrg
250 1.1 mrg buffer = malloc(bufsize);
251 1.1 mrg if (buffer == NULL)
252 1.1 mrg err(1, "couldn't malloc buffer of %d size", (int)bufsize);
253 1.1 mrg
254 1.1 mrg /*
255 1.17 mrg * set up audio device for recording with the speified parameters
256 1.17 mrg */
257 1.17 mrg AUDIO_INITINFO(&info);
258 1.17 mrg
259 1.17 mrg /*
260 1.17 mrg * for these, get the current values for stuffing into the header
261 1.17 mrg */
262 1.53 mrg #define SETINFO2(x, y) if (x) \
263 1.53 mrg info.record.y = x; \
264 1.40 mrg else \
265 1.53 mrg info.record.y = x = oinfo.record.y;
266 1.54 mrg #define SETINFO(x) SETINFO2(ti.x, x)
267 1.53 mrg
268 1.40 mrg SETINFO (sample_rate)
269 1.40 mrg SETINFO (channels)
270 1.40 mrg SETINFO (precision)
271 1.40 mrg SETINFO (encoding)
272 1.53 mrg SETINFO2 (gain, gain)
273 1.53 mrg SETINFO2 (port, port)
274 1.53 mrg SETINFO2 (balance, balance)
275 1.17 mrg #undef SETINFO
276 1.53 mrg #undef SETINFO2
277 1.17 mrg
278 1.17 mrg if (monitor_gain)
279 1.17 mrg info.monitor_gain = monitor_gain;
280 1.17 mrg else
281 1.17 mrg monitor_gain = oinfo.monitor_gain;
282 1.1 mrg
283 1.1 mrg info.mode = AUMODE_RECORD;
284 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
285 1.29 mrg err(1, "failed to set audio info");
286 1.1 mrg
287 1.1 mrg signal(SIGINT, cleanup);
288 1.53 mrg
289 1.54 mrg ti.total_size = 0;
290 1.53 mrg
291 1.54 mrg write_header(&ti);
292 1.54 mrg if (ti.format == AUDIO_FORMAT_NONE)
293 1.53 mrg errx(1, "unable to determine audio format");
294 1.54 mrg conv_func = write_get_conv_func(&ti);
295 1.1 mrg
296 1.27 mrg if (verbose && conv_func) {
297 1.27 mrg const char *s = NULL;
298 1.27 mrg
299 1.27 mrg if (conv_func == swap_bytes)
300 1.27 mrg s = "swap bytes (16 bit)";
301 1.27 mrg else if (conv_func == swap_bytes32)
302 1.27 mrg s = "swap bytes (32 bit)";
303 1.27 mrg else if (conv_func == change_sign16_be)
304 1.27 mrg s = "change sign (big-endian, 16 bit)";
305 1.27 mrg else if (conv_func == change_sign16_le)
306 1.27 mrg s = "change sign (little-endian, 16 bit)";
307 1.27 mrg else if (conv_func == change_sign32_be)
308 1.27 mrg s = "change sign (big-endian, 32 bit)";
309 1.27 mrg else if (conv_func == change_sign32_le)
310 1.27 mrg s = "change sign (little-endian, 32 bit)";
311 1.27 mrg else if (conv_func == change_sign16_swap_bytes_be)
312 1.27 mrg s = "change sign & swap bytes (big-endian, 16 bit)";
313 1.27 mrg else if (conv_func == change_sign16_swap_bytes_le)
314 1.27 mrg s = "change sign & swap bytes (little-endian, 16 bit)";
315 1.27 mrg else if (conv_func == change_sign32_swap_bytes_be)
316 1.27 mrg s = "change sign (big-endian, 32 bit)";
317 1.27 mrg else if (conv_func == change_sign32_swap_bytes_le)
318 1.27 mrg s = "change sign & swap bytes (little-endian, 32 bit)";
319 1.27 mrg
320 1.27 mrg if (s)
321 1.27 mrg fprintf(stderr, "%s: converting, using function: %s\n",
322 1.27 mrg getprogname(), s);
323 1.27 mrg else
324 1.27 mrg fprintf(stderr, "%s: using unnamed conversion "
325 1.27 mrg "function\n", getprogname());
326 1.27 mrg }
327 1.27 mrg
328 1.27 mrg if (verbose)
329 1.27 mrg fprintf(stderr,
330 1.27 mrg "sample_rate=%d channels=%d precision=%d encoding=%s\n",
331 1.27 mrg info.record.sample_rate, info.record.channels,
332 1.27 mrg info.record.precision,
333 1.27 mrg audio_enc_from_val(info.record.encoding));
334 1.32 mrg
335 1.32 mrg if (!no_time_limit && verbose)
336 1.32 mrg fprintf(stderr, "recording for %lu seconds, %lu microseconds\n",
337 1.32 mrg (u_long)record_time.tv_sec, (u_long)record_time.tv_usec);
338 1.27 mrg
339 1.1 mrg (void)gettimeofday(&start_time, NULL);
340 1.8 mrg while (no_time_limit || timeleft(&start_time, &record_time)) {
341 1.55 riastrad if ((nread = read(audiofd, buffer, bufsize)) == -1)
342 1.1 mrg err(1, "read failed");
343 1.55 riastrad if (nread == 0)
344 1.55 riastrad errx(1, "read eof");
345 1.55 riastrad if ((size_t)nread != bufsize)
346 1.55 riastrad errx(1, "invalid read");
347 1.18 mrg if (conv_func)
348 1.19 mrg (*conv_func)(buffer, bufsize);
349 1.54 mrg if ((size_t)write(ti.outfd, buffer, bufsize) != bufsize)
350 1.1 mrg err(1, "write failed");
351 1.54 mrg ti.total_size += bufsize;
352 1.1 mrg }
353 1.1 mrg cleanup(0);
354 1.1 mrg }
355 1.1 mrg
356 1.1 mrg int
357 1.51 joerg timeleft(struct timeval *start_tvp, struct timeval *record_tvp)
358 1.1 mrg {
359 1.1 mrg struct timeval now, diff;
360 1.1 mrg
361 1.1 mrg (void)gettimeofday(&now, NULL);
362 1.7 dmcmahil timersub(&now, start_tvp, &diff);
363 1.7 dmcmahil timersub(record_tvp, &diff, &now);
364 1.1 mrg
365 1.1 mrg return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0));
366 1.1 mrg }
367 1.1 mrg
368 1.1 mrg void
369 1.51 joerg cleanup(int signo)
370 1.1 mrg {
371 1.1 mrg
372 1.1 mrg rewrite_header();
373 1.54 mrg close(ti.outfd);
374 1.17 mrg if (omonitor_gain) {
375 1.1 mrg AUDIO_INITINFO(&info);
376 1.17 mrg info.monitor_gain = omonitor_gain;
377 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
378 1.1 mrg err(1, "failed to reset audio info");
379 1.1 mrg }
380 1.23 jdolecek close(audiofd);
381 1.44 lukem if (signo != 0) {
382 1.44 lukem (void)raise_default_signal(signo);
383 1.44 lukem }
384 1.1 mrg exit(0);
385 1.1 mrg }
386 1.1 mrg
387 1.51 joerg static void
388 1.51 joerg rewrite_header(void)
389 1.1 mrg {
390 1.1 mrg
391 1.1 mrg /* can't do this here! */
392 1.54 mrg if (ti.outfd == STDOUT_FILENO)
393 1.1 mrg return;
394 1.1 mrg
395 1.54 mrg if (lseek(ti.outfd, (off_t)0, SEEK_SET) == (off_t)-1)
396 1.1 mrg err(1, "could not seek to start of file for header rewrite");
397 1.54 mrg write_header(&ti);
398 1.1 mrg }
399 1.1 mrg
400 1.51 joerg static void
401 1.51 joerg usage(void)
402 1.1 mrg {
403 1.14 cgd
404 1.14 cgd fprintf(stderr, "Usage: %s [-afhqV] [options] {files ...|-}\n",
405 1.14 cgd getprogname());
406 1.4 mrg fprintf(stderr, "Options:\n\t"
407 1.50 wiz "-B buffer size\n\t"
408 1.4 mrg "-b balance (0-63)\n\t"
409 1.4 mrg "-c channels\n\t"
410 1.4 mrg "-d audio device\n\t"
411 1.4 mrg "-e encoding\n\t"
412 1.35 wiz "-F format\n\t"
413 1.4 mrg "-i header information\n\t"
414 1.4 mrg "-m monitor volume\n\t"
415 1.35 wiz "-P precision (4, 8, 16, 24, or 32 bits)\n\t"
416 1.4 mrg "-p input port\n\t"
417 1.4 mrg "-s sample rate\n\t"
418 1.4 mrg "-t recording time\n\t"
419 1.4 mrg "-v volume\n");
420 1.9 kleink exit(EXIT_FAILURE);
421 1.1 mrg }
422