record.c revision 1.54 1 1.54 mrg /* $NetBSD: record.c,v 1.54 2015/08/05 06:54:39 mrg 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.54 mrg __RCSID("$NetBSD: record.c,v 1.54 2015/08/05 06:54:39 mrg 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.8 mrg int ch, no_time_limit = 1;
85 1.24 augustss const char *defdevice = _PATH_SOUND;
86 1.1 mrg
87 1.53 mrg /*
88 1.54 mrg * Initialise the track_info.
89 1.53 mrg */
90 1.54 mrg ti.format = AUDIO_FORMAT_DEFAULT;
91 1.54 mrg ti.total_size = -1;
92 1.53 mrg
93 1.48 mrg while ((ch = getopt(argc, argv, "ab:B:C:F:c:d:e:fhi:m:P:p:qt:s:Vv:")) != -1) {
94 1.1 mrg switch (ch) {
95 1.1 mrg case 'a':
96 1.1 mrg aflag++;
97 1.1 mrg break;
98 1.1 mrg case 'b':
99 1.1 mrg decode_int(optarg, &balance);
100 1.1 mrg if (balance < 0 || balance > 63)
101 1.30 grant errx(1, "balance must be between 0 and 63");
102 1.1 mrg break;
103 1.48 mrg case 'B':
104 1.48 mrg bufsize = strsuftoll("read buffer size", optarg,
105 1.48 mrg 1, UINT_MAX);
106 1.48 mrg break;
107 1.1 mrg case 'C':
108 1.23 jdolecek /* Ignore, compatibility */
109 1.1 mrg break;
110 1.17 mrg case 'F':
111 1.54 mrg ti.format = audio_format_from_str(optarg);
112 1.54 mrg if (ti.format < 0)
113 1.27 mrg errx(1, "Unknown audio format; supported "
114 1.27 mrg "formats: \"sun\", \"wav\", and \"none\"");
115 1.17 mrg break;
116 1.1 mrg case 'c':
117 1.54 mrg decode_int(optarg, &ti.channels);
118 1.54 mrg if (ti.channels < 0 || ti.channels > 16)
119 1.30 grant errx(1, "channels must be between 0 and 16");
120 1.1 mrg break;
121 1.1 mrg case 'd':
122 1.1 mrg device = optarg;
123 1.1 mrg break;
124 1.1 mrg case 'e':
125 1.1 mrg encoding_str = optarg;
126 1.1 mrg break;
127 1.1 mrg case 'f':
128 1.1 mrg fflag++;
129 1.1 mrg break;
130 1.1 mrg case 'i':
131 1.54 mrg ti.header_info = optarg;
132 1.1 mrg break;
133 1.1 mrg case 'm':
134 1.17 mrg decode_int(optarg, &monitor_gain);
135 1.17 mrg if (monitor_gain < 0 || monitor_gain > 255)
136 1.30 grant errx(1, "monitor volume must be between 0 and 255");
137 1.1 mrg break;
138 1.1 mrg case 'P':
139 1.54 mrg decode_int(optarg, &ti.precision);
140 1.54 mrg if (ti.precision != 4 && ti.precision != 8 &&
141 1.54 mrg ti.precision != 16 && ti.precision != 24 &&
142 1.54 mrg ti.precision != 32)
143 1.15 minoura errx(1, "precision must be between 4, 8, 16, 24 or 32");
144 1.1 mrg break;
145 1.1 mrg case 'p':
146 1.1 mrg len = strlen(optarg);
147 1.1 mrg
148 1.1 mrg if (strncmp(optarg, "mic", len) == 0)
149 1.1 mrg port |= AUDIO_MICROPHONE;
150 1.1 mrg else if (strncmp(optarg, "cd", len) == 0 ||
151 1.1 mrg strncmp(optarg, "internal-cd", len) == 0)
152 1.1 mrg port |= AUDIO_CD;
153 1.1 mrg else if (strncmp(optarg, "line", len) == 0)
154 1.1 mrg port |= AUDIO_LINE_IN;
155 1.1 mrg else
156 1.1 mrg errx(1,
157 1.1 mrg "port must be `cd', `internal-cd', `mic', or `line'");
158 1.1 mrg break;
159 1.1 mrg case 'q':
160 1.54 mrg ti.qflag++;
161 1.1 mrg break;
162 1.1 mrg case 's':
163 1.54 mrg decode_int(optarg, &ti.sample_rate);
164 1.54 mrg if (ti.sample_rate < 0 || ti.sample_rate > 48000 * 2) /* XXX */
165 1.30 grant errx(1, "sample rate must be between 0 and 96000");
166 1.1 mrg break;
167 1.1 mrg case 't':
168 1.8 mrg no_time_limit = 0;
169 1.1 mrg decode_time(optarg, &record_time);
170 1.1 mrg break;
171 1.1 mrg case 'V':
172 1.1 mrg verbose++;
173 1.1 mrg break;
174 1.1 mrg case 'v':
175 1.17 mrg decode_int(optarg, &gain);
176 1.17 mrg if (gain < 0 || gain > 255)
177 1.30 grant errx(1, "volume must be between 0 and 255");
178 1.1 mrg break;
179 1.1 mrg /* case 'h': */
180 1.1 mrg default:
181 1.1 mrg usage();
182 1.1 mrg /* NOTREACHED */
183 1.1 mrg }
184 1.1 mrg }
185 1.1 mrg argc -= optind;
186 1.1 mrg argv += optind;
187 1.1 mrg
188 1.39 mrg if (argc != 1)
189 1.39 mrg usage();
190 1.39 mrg
191 1.1 mrg /*
192 1.40 mrg * convert the encoding string into a value.
193 1.40 mrg */
194 1.40 mrg if (encoding_str) {
195 1.54 mrg ti.encoding = audio_enc_to_val(encoding_str);
196 1.54 mrg if (ti.encoding == -1)
197 1.40 mrg errx(1, "unknown encoding, bailing...");
198 1.40 mrg }
199 1.40 mrg
200 1.40 mrg /*
201 1.40 mrg * open the output file
202 1.40 mrg */
203 1.42 gson if (argv[0][0] != '-' || argv[0][1] != '\0') {
204 1.40 mrg /* intuit the file type from the name */
205 1.54 mrg if (ti.format == AUDIO_FORMAT_DEFAULT)
206 1.40 mrg {
207 1.40 mrg size_t flen = strlen(*argv);
208 1.40 mrg const char *arg = *argv;
209 1.40 mrg
210 1.40 mrg if (strcasecmp(arg + flen - 3, ".au") == 0)
211 1.54 mrg ti.format = AUDIO_FORMAT_SUN;
212 1.40 mrg else if (strcasecmp(arg + flen - 4, ".wav") == 0)
213 1.54 mrg ti.format = AUDIO_FORMAT_WAV;
214 1.40 mrg }
215 1.54 mrg ti.outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666);
216 1.54 mrg if (ti.outfd < 0)
217 1.40 mrg err(1, "could not open %s", *argv);
218 1.40 mrg } else
219 1.54 mrg ti.outfd = STDOUT_FILENO;
220 1.40 mrg
221 1.40 mrg /*
222 1.34 mrg * open the audio device
223 1.1 mrg */
224 1.6 kleink if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL &&
225 1.6 kleink (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
226 1.24 augustss device = defdevice;
227 1.1 mrg
228 1.1 mrg audiofd = open(device, O_RDONLY);
229 1.24 augustss if (audiofd < 0 && device == defdevice) {
230 1.22 augustss device = _PATH_SOUND0;
231 1.28 uwe audiofd = open(device, O_RDONLY);
232 1.22 augustss }
233 1.1 mrg if (audiofd < 0)
234 1.1 mrg err(1, "failed to open %s", device);
235 1.1 mrg
236 1.1 mrg /*
237 1.1 mrg * work out the buffer size to use, and allocate it. also work out
238 1.1 mrg * what the old monitor gain value is, so that we can reset it later.
239 1.1 mrg */
240 1.23 jdolecek if (ioctl(audiofd, AUDIO_GETINFO, &oinfo) < 0)
241 1.1 mrg err(1, "failed to get audio info");
242 1.49 jmcneill if (bufsize == 0) {
243 1.48 mrg bufsize = oinfo.record.buffer_size;
244 1.49 jmcneill if (bufsize < 32 * 1024)
245 1.49 jmcneill bufsize = 32 * 1024;
246 1.49 jmcneill }
247 1.17 mrg omonitor_gain = oinfo.monitor_gain;
248 1.1 mrg
249 1.1 mrg buffer = malloc(bufsize);
250 1.1 mrg if (buffer == NULL)
251 1.1 mrg err(1, "couldn't malloc buffer of %d size", (int)bufsize);
252 1.1 mrg
253 1.1 mrg /*
254 1.17 mrg * set up audio device for recording with the speified parameters
255 1.17 mrg */
256 1.17 mrg AUDIO_INITINFO(&info);
257 1.17 mrg
258 1.17 mrg /*
259 1.17 mrg * for these, get the current values for stuffing into the header
260 1.17 mrg */
261 1.53 mrg #define SETINFO2(x, y) if (x) \
262 1.53 mrg info.record.y = x; \
263 1.40 mrg else \
264 1.53 mrg info.record.y = x = oinfo.record.y;
265 1.54 mrg #define SETINFO(x) SETINFO2(ti.x, x)
266 1.53 mrg
267 1.40 mrg SETINFO (sample_rate)
268 1.40 mrg SETINFO (channels)
269 1.40 mrg SETINFO (precision)
270 1.40 mrg SETINFO (encoding)
271 1.53 mrg SETINFO2 (gain, gain)
272 1.53 mrg SETINFO2 (port, port)
273 1.53 mrg SETINFO2 (balance, balance)
274 1.17 mrg #undef SETINFO
275 1.53 mrg #undef SETINFO2
276 1.17 mrg
277 1.17 mrg if (monitor_gain)
278 1.17 mrg info.monitor_gain = monitor_gain;
279 1.17 mrg else
280 1.17 mrg monitor_gain = oinfo.monitor_gain;
281 1.1 mrg
282 1.1 mrg info.mode = AUMODE_RECORD;
283 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
284 1.29 mrg err(1, "failed to set audio info");
285 1.1 mrg
286 1.1 mrg signal(SIGINT, cleanup);
287 1.53 mrg
288 1.54 mrg ti.total_size = 0;
289 1.53 mrg
290 1.54 mrg write_header(&ti);
291 1.54 mrg if (ti.format == AUDIO_FORMAT_NONE)
292 1.53 mrg errx(1, "unable to determine audio format");
293 1.54 mrg conv_func = write_get_conv_func(&ti);
294 1.1 mrg
295 1.27 mrg if (verbose && conv_func) {
296 1.27 mrg const char *s = NULL;
297 1.27 mrg
298 1.27 mrg if (conv_func == swap_bytes)
299 1.27 mrg s = "swap bytes (16 bit)";
300 1.27 mrg else if (conv_func == swap_bytes32)
301 1.27 mrg s = "swap bytes (32 bit)";
302 1.27 mrg else if (conv_func == change_sign16_be)
303 1.27 mrg s = "change sign (big-endian, 16 bit)";
304 1.27 mrg else if (conv_func == change_sign16_le)
305 1.27 mrg s = "change sign (little-endian, 16 bit)";
306 1.27 mrg else if (conv_func == change_sign32_be)
307 1.27 mrg s = "change sign (big-endian, 32 bit)";
308 1.27 mrg else if (conv_func == change_sign32_le)
309 1.27 mrg s = "change sign (little-endian, 32 bit)";
310 1.27 mrg else if (conv_func == change_sign16_swap_bytes_be)
311 1.27 mrg s = "change sign & swap bytes (big-endian, 16 bit)";
312 1.27 mrg else if (conv_func == change_sign16_swap_bytes_le)
313 1.27 mrg s = "change sign & swap bytes (little-endian, 16 bit)";
314 1.27 mrg else if (conv_func == change_sign32_swap_bytes_be)
315 1.27 mrg s = "change sign (big-endian, 32 bit)";
316 1.27 mrg else if (conv_func == change_sign32_swap_bytes_le)
317 1.27 mrg s = "change sign & swap bytes (little-endian, 32 bit)";
318 1.27 mrg
319 1.27 mrg if (s)
320 1.27 mrg fprintf(stderr, "%s: converting, using function: %s\n",
321 1.27 mrg getprogname(), s);
322 1.27 mrg else
323 1.27 mrg fprintf(stderr, "%s: using unnamed conversion "
324 1.27 mrg "function\n", getprogname());
325 1.27 mrg }
326 1.27 mrg
327 1.27 mrg if (verbose)
328 1.27 mrg fprintf(stderr,
329 1.27 mrg "sample_rate=%d channels=%d precision=%d encoding=%s\n",
330 1.27 mrg info.record.sample_rate, info.record.channels,
331 1.27 mrg info.record.precision,
332 1.27 mrg audio_enc_from_val(info.record.encoding));
333 1.32 mrg
334 1.32 mrg if (!no_time_limit && verbose)
335 1.32 mrg fprintf(stderr, "recording for %lu seconds, %lu microseconds\n",
336 1.32 mrg (u_long)record_time.tv_sec, (u_long)record_time.tv_usec);
337 1.27 mrg
338 1.1 mrg (void)gettimeofday(&start_time, NULL);
339 1.8 mrg while (no_time_limit || timeleft(&start_time, &record_time)) {
340 1.47 lukem if ((size_t)read(audiofd, buffer, bufsize) != bufsize)
341 1.1 mrg err(1, "read failed");
342 1.18 mrg if (conv_func)
343 1.19 mrg (*conv_func)(buffer, bufsize);
344 1.54 mrg if ((size_t)write(ti.outfd, buffer, bufsize) != bufsize)
345 1.1 mrg err(1, "write failed");
346 1.54 mrg ti.total_size += bufsize;
347 1.1 mrg }
348 1.1 mrg cleanup(0);
349 1.1 mrg }
350 1.1 mrg
351 1.1 mrg int
352 1.51 joerg timeleft(struct timeval *start_tvp, struct timeval *record_tvp)
353 1.1 mrg {
354 1.1 mrg struct timeval now, diff;
355 1.1 mrg
356 1.1 mrg (void)gettimeofday(&now, NULL);
357 1.7 dmcmahil timersub(&now, start_tvp, &diff);
358 1.7 dmcmahil timersub(record_tvp, &diff, &now);
359 1.1 mrg
360 1.1 mrg return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0));
361 1.1 mrg }
362 1.1 mrg
363 1.1 mrg void
364 1.51 joerg cleanup(int signo)
365 1.1 mrg {
366 1.1 mrg
367 1.1 mrg rewrite_header();
368 1.54 mrg close(ti.outfd);
369 1.17 mrg if (omonitor_gain) {
370 1.1 mrg AUDIO_INITINFO(&info);
371 1.17 mrg info.monitor_gain = omonitor_gain;
372 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
373 1.1 mrg err(1, "failed to reset audio info");
374 1.1 mrg }
375 1.23 jdolecek close(audiofd);
376 1.44 lukem if (signo != 0) {
377 1.44 lukem (void)raise_default_signal(signo);
378 1.44 lukem }
379 1.1 mrg exit(0);
380 1.1 mrg }
381 1.1 mrg
382 1.51 joerg static void
383 1.51 joerg rewrite_header(void)
384 1.1 mrg {
385 1.1 mrg
386 1.1 mrg /* can't do this here! */
387 1.54 mrg if (ti.outfd == STDOUT_FILENO)
388 1.1 mrg return;
389 1.1 mrg
390 1.54 mrg if (lseek(ti.outfd, (off_t)0, SEEK_SET) == (off_t)-1)
391 1.1 mrg err(1, "could not seek to start of file for header rewrite");
392 1.54 mrg write_header(&ti);
393 1.1 mrg }
394 1.1 mrg
395 1.51 joerg static void
396 1.51 joerg usage(void)
397 1.1 mrg {
398 1.14 cgd
399 1.14 cgd fprintf(stderr, "Usage: %s [-afhqV] [options] {files ...|-}\n",
400 1.14 cgd getprogname());
401 1.4 mrg fprintf(stderr, "Options:\n\t"
402 1.50 wiz "-B buffer size\n\t"
403 1.4 mrg "-b balance (0-63)\n\t"
404 1.4 mrg "-c channels\n\t"
405 1.4 mrg "-d audio device\n\t"
406 1.4 mrg "-e encoding\n\t"
407 1.35 wiz "-F format\n\t"
408 1.4 mrg "-i header information\n\t"
409 1.4 mrg "-m monitor volume\n\t"
410 1.35 wiz "-P precision (4, 8, 16, 24, or 32 bits)\n\t"
411 1.4 mrg "-p input port\n\t"
412 1.4 mrg "-s sample rate\n\t"
413 1.4 mrg "-t recording time\n\t"
414 1.4 mrg "-v volume\n");
415 1.9 kleink exit(EXIT_FAILURE);
416 1.1 mrg }
417