record.c revision 1.42 1 1.42 gson /* $NetBSD: record.c,v 1.42 2006/05/09 15:55:44 gson Exp $ */
2 1.2 mrg
3 1.2 mrg /*
4 1.31 mrg * Copyright (c) 1999, 2002 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 * 3. The name of the author may not be used to endorse or promote products
16 1.2 mrg * derived from this software without specific prior written permission.
17 1.2 mrg *
18 1.2 mrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 1.2 mrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 1.2 mrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 1.2 mrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 1.2 mrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 1.2 mrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 1.2 mrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 1.2 mrg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 1.2 mrg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 1.2 mrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 1.2 mrg * SUCH DAMAGE.
29 1.2 mrg */
30 1.2 mrg
31 1.1 mrg /*
32 1.1 mrg * SunOS compatible audiorecord(1)
33 1.1 mrg */
34 1.33 agc #include <sys/cdefs.h>
35 1.33 agc
36 1.33 agc #ifndef lint
37 1.42 gson __RCSID("$NetBSD: record.c,v 1.42 2006/05/09 15:55:44 gson Exp $");
38 1.33 agc #endif
39 1.33 agc
40 1.1 mrg
41 1.1 mrg #include <sys/types.h>
42 1.1 mrg #include <sys/audioio.h>
43 1.1 mrg #include <sys/ioctl.h>
44 1.1 mrg #include <sys/time.h>
45 1.1 mrg #include <sys/uio.h>
46 1.1 mrg
47 1.1 mrg #include <err.h>
48 1.1 mrg #include <fcntl.h>
49 1.1 mrg #include <paths.h>
50 1.1 mrg #include <signal.h>
51 1.1 mrg #include <stdio.h>
52 1.1 mrg #include <stdlib.h>
53 1.1 mrg #include <string.h>
54 1.1 mrg #include <unistd.h>
55 1.1 mrg
56 1.1 mrg #include "libaudio.h"
57 1.19 mrg #include "auconv.h"
58 1.1 mrg
59 1.1 mrg audio_info_t info, oinfo;
60 1.1 mrg ssize_t total_size = -1;
61 1.21 mrg const char *device;
62 1.27 mrg int format = AUDIO_FORMAT_DEFAULT;
63 1.1 mrg char *header_info;
64 1.1 mrg char default_info[8] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
65 1.23 jdolecek int audiofd, outfd;
66 1.1 mrg int qflag, aflag, fflag;
67 1.1 mrg int verbose;
68 1.17 mrg int monitor_gain, omonitor_gain;
69 1.17 mrg int gain;
70 1.1 mrg int balance;
71 1.1 mrg int port;
72 1.1 mrg int encoding;
73 1.1 mrg char *encoding_str;
74 1.1 mrg int precision;
75 1.1 mrg int sample_rate;
76 1.1 mrg int channels;
77 1.1 mrg struct timeval record_time;
78 1.32 mrg struct timeval start_time;
79 1.1 mrg
80 1.37 mycroft void (*conv_func) (u_char *, int);
81 1.18 mrg
82 1.12 mrg void usage (void);
83 1.12 mrg int main (int, char *[]);
84 1.12 mrg int timeleft (struct timeval *, struct timeval *);
85 1.12 mrg void cleanup (int) __attribute__((__noreturn__));
86 1.17 mrg int write_header_sun (void **, size_t *, int *);
87 1.17 mrg int write_header_wav (void **, size_t *, int *);
88 1.12 mrg void write_header (void);
89 1.12 mrg void rewrite_header (void);
90 1.1 mrg
91 1.1 mrg int
92 1.1 mrg main(argc, argv)
93 1.1 mrg int argc;
94 1.1 mrg char *argv[];
95 1.1 mrg {
96 1.1 mrg char *buffer;
97 1.1 mrg size_t len, bufsize;
98 1.8 mrg int ch, no_time_limit = 1;
99 1.24 augustss const char *defdevice = _PATH_SOUND;
100 1.1 mrg
101 1.17 mrg while ((ch = getopt(argc, argv, "ab:C:F:c:d:e:fhi:m:P:p:qt:s:Vv:")) != -1) {
102 1.1 mrg switch (ch) {
103 1.1 mrg case 'a':
104 1.1 mrg aflag++;
105 1.1 mrg break;
106 1.1 mrg case 'b':
107 1.1 mrg decode_int(optarg, &balance);
108 1.1 mrg if (balance < 0 || balance > 63)
109 1.30 grant errx(1, "balance must be between 0 and 63");
110 1.1 mrg break;
111 1.1 mrg case 'C':
112 1.23 jdolecek /* Ignore, compatibility */
113 1.1 mrg break;
114 1.17 mrg case 'F':
115 1.17 mrg format = audio_format_from_str(optarg);
116 1.17 mrg if (format < 0)
117 1.27 mrg errx(1, "Unknown audio format; supported "
118 1.27 mrg "formats: \"sun\", \"wav\", and \"none\"");
119 1.17 mrg break;
120 1.1 mrg case 'c':
121 1.1 mrg decode_int(optarg, &channels);
122 1.1 mrg if (channels < 0 || channels > 16)
123 1.30 grant errx(1, "channels must be between 0 and 16");
124 1.1 mrg break;
125 1.1 mrg case 'd':
126 1.1 mrg device = optarg;
127 1.1 mrg break;
128 1.1 mrg case 'e':
129 1.1 mrg encoding_str = optarg;
130 1.1 mrg break;
131 1.1 mrg case 'f':
132 1.1 mrg fflag++;
133 1.1 mrg break;
134 1.1 mrg case 'i':
135 1.1 mrg header_info = optarg;
136 1.1 mrg break;
137 1.1 mrg case 'm':
138 1.17 mrg decode_int(optarg, &monitor_gain);
139 1.17 mrg if (monitor_gain < 0 || monitor_gain > 255)
140 1.30 grant errx(1, "monitor volume must be between 0 and 255");
141 1.1 mrg break;
142 1.1 mrg case 'P':
143 1.1 mrg decode_int(optarg, &precision);
144 1.15 minoura if (precision != 4 && precision != 8 &&
145 1.15 minoura precision != 16 && precision != 24 &&
146 1.15 minoura precision != 32)
147 1.15 minoura errx(1, "precision must be between 4, 8, 16, 24 or 32");
148 1.1 mrg break;
149 1.1 mrg case 'p':
150 1.1 mrg len = strlen(optarg);
151 1.1 mrg
152 1.1 mrg if (strncmp(optarg, "mic", len) == 0)
153 1.1 mrg port |= AUDIO_MICROPHONE;
154 1.1 mrg else if (strncmp(optarg, "cd", len) == 0 ||
155 1.1 mrg strncmp(optarg, "internal-cd", len) == 0)
156 1.1 mrg port |= AUDIO_CD;
157 1.1 mrg else if (strncmp(optarg, "line", len) == 0)
158 1.1 mrg port |= AUDIO_LINE_IN;
159 1.1 mrg else
160 1.1 mrg errx(1,
161 1.1 mrg "port must be `cd', `internal-cd', `mic', or `line'");
162 1.1 mrg break;
163 1.1 mrg case 'q':
164 1.1 mrg qflag++;
165 1.1 mrg break;
166 1.1 mrg case 's':
167 1.1 mrg decode_int(optarg, &sample_rate);
168 1.1 mrg if (sample_rate < 0 || sample_rate > 48000 * 2) /* XXX */
169 1.30 grant errx(1, "sample rate must be between 0 and 96000");
170 1.1 mrg break;
171 1.1 mrg case 't':
172 1.8 mrg no_time_limit = 0;
173 1.1 mrg decode_time(optarg, &record_time);
174 1.1 mrg break;
175 1.1 mrg case 'V':
176 1.1 mrg verbose++;
177 1.1 mrg break;
178 1.1 mrg case 'v':
179 1.17 mrg decode_int(optarg, &gain);
180 1.17 mrg if (gain < 0 || gain > 255)
181 1.30 grant errx(1, "volume must be between 0 and 255");
182 1.1 mrg break;
183 1.1 mrg /* case 'h': */
184 1.1 mrg default:
185 1.1 mrg usage();
186 1.1 mrg /* NOTREACHED */
187 1.1 mrg }
188 1.1 mrg }
189 1.1 mrg argc -= optind;
190 1.1 mrg argv += optind;
191 1.1 mrg
192 1.39 mrg if (argc != 1)
193 1.39 mrg usage();
194 1.39 mrg
195 1.1 mrg /*
196 1.40 mrg * convert the encoding string into a value.
197 1.40 mrg */
198 1.40 mrg if (encoding_str) {
199 1.40 mrg encoding = audio_enc_to_val(encoding_str);
200 1.40 mrg if (encoding == -1)
201 1.40 mrg errx(1, "unknown encoding, bailing...");
202 1.40 mrg }
203 1.40 mrg #if 0
204 1.40 mrg else
205 1.40 mrg encoding = AUDIO_ENCODING_ULAW;
206 1.40 mrg #endif
207 1.40 mrg
208 1.40 mrg /*
209 1.40 mrg * open the output file
210 1.40 mrg */
211 1.42 gson if (argv[0][0] != '-' || argv[0][1] != '\0') {
212 1.40 mrg /* intuit the file type from the name */
213 1.40 mrg if (format == AUDIO_FORMAT_DEFAULT)
214 1.40 mrg {
215 1.40 mrg size_t flen = strlen(*argv);
216 1.40 mrg const char *arg = *argv;
217 1.40 mrg
218 1.40 mrg if (strcasecmp(arg + flen - 3, ".au") == 0)
219 1.40 mrg format = AUDIO_FORMAT_SUN;
220 1.40 mrg else if (strcasecmp(arg + flen - 4, ".wav") == 0)
221 1.40 mrg format = AUDIO_FORMAT_WAV;
222 1.40 mrg }
223 1.40 mrg outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666);
224 1.40 mrg if (outfd < 0)
225 1.40 mrg err(1, "could not open %s", *argv);
226 1.40 mrg } else
227 1.40 mrg outfd = STDOUT_FILENO;
228 1.40 mrg
229 1.40 mrg /*
230 1.34 mrg * open the audio device
231 1.1 mrg */
232 1.6 kleink if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL &&
233 1.6 kleink (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
234 1.24 augustss device = defdevice;
235 1.1 mrg
236 1.1 mrg audiofd = open(device, O_RDONLY);
237 1.24 augustss if (audiofd < 0 && device == defdevice) {
238 1.22 augustss device = _PATH_SOUND0;
239 1.28 uwe audiofd = open(device, O_RDONLY);
240 1.22 augustss }
241 1.1 mrg if (audiofd < 0)
242 1.1 mrg err(1, "failed to open %s", device);
243 1.1 mrg
244 1.1 mrg /*
245 1.1 mrg * work out the buffer size to use, and allocate it. also work out
246 1.1 mrg * what the old monitor gain value is, so that we can reset it later.
247 1.1 mrg */
248 1.23 jdolecek if (ioctl(audiofd, AUDIO_GETINFO, &oinfo) < 0)
249 1.1 mrg err(1, "failed to get audio info");
250 1.1 mrg bufsize = oinfo.record.buffer_size;
251 1.1 mrg if (bufsize < 32 * 1024)
252 1.1 mrg bufsize = 32 * 1024;
253 1.17 mrg omonitor_gain = oinfo.monitor_gain;
254 1.1 mrg
255 1.1 mrg buffer = malloc(bufsize);
256 1.1 mrg if (buffer == NULL)
257 1.1 mrg err(1, "couldn't malloc buffer of %d size", (int)bufsize);
258 1.1 mrg
259 1.1 mrg /*
260 1.17 mrg * set up audio device for recording with the speified parameters
261 1.17 mrg */
262 1.17 mrg AUDIO_INITINFO(&info);
263 1.17 mrg
264 1.17 mrg /*
265 1.17 mrg * for these, get the current values for stuffing into the header
266 1.17 mrg */
267 1.40 mrg #define SETINFO(x) if (x) \
268 1.40 mrg info.record.x = x; \
269 1.40 mrg else \
270 1.40 mrg info.record.x = x = oinfo.record.x;
271 1.40 mrg SETINFO (sample_rate)
272 1.40 mrg SETINFO (channels)
273 1.40 mrg SETINFO (precision)
274 1.40 mrg SETINFO (encoding)
275 1.40 mrg SETINFO (gain)
276 1.40 mrg SETINFO (port)
277 1.40 mrg SETINFO (balance)
278 1.17 mrg #undef SETINFO
279 1.17 mrg
280 1.17 mrg if (monitor_gain)
281 1.17 mrg info.monitor_gain = monitor_gain;
282 1.17 mrg else
283 1.17 mrg monitor_gain = oinfo.monitor_gain;
284 1.1 mrg
285 1.1 mrg info.mode = AUMODE_RECORD;
286 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
287 1.29 mrg err(1, "failed to set audio info");
288 1.1 mrg
289 1.1 mrg signal(SIGINT, cleanup);
290 1.1 mrg write_header();
291 1.1 mrg total_size = 0;
292 1.1 mrg
293 1.27 mrg if (verbose && conv_func) {
294 1.27 mrg const char *s = NULL;
295 1.27 mrg
296 1.27 mrg if (conv_func == swap_bytes)
297 1.27 mrg s = "swap bytes (16 bit)";
298 1.27 mrg else if (conv_func == swap_bytes32)
299 1.27 mrg s = "swap bytes (32 bit)";
300 1.27 mrg else if (conv_func == change_sign16_be)
301 1.27 mrg s = "change sign (big-endian, 16 bit)";
302 1.27 mrg else if (conv_func == change_sign16_le)
303 1.27 mrg s = "change sign (little-endian, 16 bit)";
304 1.27 mrg else if (conv_func == change_sign32_be)
305 1.27 mrg s = "change sign (big-endian, 32 bit)";
306 1.27 mrg else if (conv_func == change_sign32_le)
307 1.27 mrg s = "change sign (little-endian, 32 bit)";
308 1.27 mrg else if (conv_func == change_sign16_swap_bytes_be)
309 1.27 mrg s = "change sign & swap bytes (big-endian, 16 bit)";
310 1.27 mrg else if (conv_func == change_sign16_swap_bytes_le)
311 1.27 mrg s = "change sign & swap bytes (little-endian, 16 bit)";
312 1.27 mrg else if (conv_func == change_sign32_swap_bytes_be)
313 1.27 mrg s = "change sign (big-endian, 32 bit)";
314 1.27 mrg else if (conv_func == change_sign32_swap_bytes_le)
315 1.27 mrg s = "change sign & swap bytes (little-endian, 32 bit)";
316 1.27 mrg
317 1.27 mrg if (s)
318 1.27 mrg fprintf(stderr, "%s: converting, using function: %s\n",
319 1.27 mrg getprogname(), s);
320 1.27 mrg else
321 1.27 mrg fprintf(stderr, "%s: using unnamed conversion "
322 1.27 mrg "function\n", getprogname());
323 1.27 mrg }
324 1.27 mrg
325 1.27 mrg if (verbose)
326 1.27 mrg fprintf(stderr,
327 1.27 mrg "sample_rate=%d channels=%d precision=%d encoding=%s\n",
328 1.27 mrg info.record.sample_rate, info.record.channels,
329 1.27 mrg info.record.precision,
330 1.27 mrg audio_enc_from_val(info.record.encoding));
331 1.32 mrg
332 1.32 mrg if (!no_time_limit && verbose)
333 1.32 mrg fprintf(stderr, "recording for %lu seconds, %lu microseconds\n",
334 1.32 mrg (u_long)record_time.tv_sec, (u_long)record_time.tv_usec);
335 1.27 mrg
336 1.1 mrg (void)gettimeofday(&start_time, NULL);
337 1.8 mrg while (no_time_limit || timeleft(&start_time, &record_time)) {
338 1.1 mrg if (read(audiofd, buffer, bufsize) != bufsize)
339 1.1 mrg err(1, "read failed");
340 1.18 mrg if (conv_func)
341 1.19 mrg (*conv_func)(buffer, bufsize);
342 1.1 mrg if (write(outfd, buffer, bufsize) != bufsize)
343 1.1 mrg err(1, "write failed");
344 1.1 mrg total_size += bufsize;
345 1.1 mrg }
346 1.1 mrg cleanup(0);
347 1.1 mrg }
348 1.1 mrg
349 1.1 mrg int
350 1.1 mrg timeleft(start_tvp, record_tvp)
351 1.1 mrg struct timeval *start_tvp;
352 1.1 mrg 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.1 mrg cleanup(signo)
365 1.1 mrg int signo;
366 1.1 mrg {
367 1.1 mrg
368 1.1 mrg rewrite_header();
369 1.1 mrg close(outfd);
370 1.17 mrg if (omonitor_gain) {
371 1.1 mrg AUDIO_INITINFO(&info);
372 1.17 mrg info.monitor_gain = omonitor_gain;
373 1.23 jdolecek if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0)
374 1.1 mrg err(1, "failed to reset audio info");
375 1.1 mrg }
376 1.23 jdolecek close(audiofd);
377 1.1 mrg exit(0);
378 1.1 mrg }
379 1.1 mrg
380 1.17 mrg int
381 1.17 mrg write_header_sun(hdrp, lenp, leftp)
382 1.17 mrg void **hdrp;
383 1.17 mrg size_t *lenp;
384 1.17 mrg int *leftp;
385 1.1 mrg {
386 1.16 mrg static int warned = 0;
387 1.17 mrg static sun_audioheader auh;
388 1.19 mrg int sunenc, oencoding = encoding;
389 1.11 mrg
390 1.27 mrg /* only perform conversions if we don't specify the encoding */
391 1.25 mrg switch (encoding) {
392 1.25 mrg case AUDIO_ENCODING_ULINEAR_LE:
393 1.25 mrg #if BYTE_ORDER == LITTLE_ENDIAN
394 1.25 mrg case AUDIO_ENCODING_ULINEAR:
395 1.25 mrg #endif
396 1.19 mrg if (precision == 16)
397 1.27 mrg conv_func = change_sign16_swap_bytes_le;
398 1.19 mrg else if (precision == 32)
399 1.27 mrg conv_func = change_sign32_swap_bytes_le;
400 1.21 mrg if (conv_func)
401 1.21 mrg encoding = AUDIO_ENCODING_SLINEAR_BE;
402 1.25 mrg break;
403 1.25 mrg
404 1.25 mrg case AUDIO_ENCODING_ULINEAR_BE:
405 1.25 mrg #if BYTE_ORDER == BIG_ENDIAN
406 1.25 mrg case AUDIO_ENCODING_ULINEAR:
407 1.25 mrg #endif
408 1.19 mrg if (precision == 16)
409 1.19 mrg conv_func = change_sign16_be;
410 1.19 mrg else if (precision == 32)
411 1.19 mrg conv_func = change_sign32_be;
412 1.21 mrg if (conv_func)
413 1.21 mrg encoding = AUDIO_ENCODING_SLINEAR_BE;
414 1.25 mrg break;
415 1.25 mrg
416 1.25 mrg case AUDIO_ENCODING_SLINEAR_LE:
417 1.25 mrg #if BYTE_ORDER == LITTLE_ENDIAN
418 1.25 mrg case AUDIO_ENCODING_SLINEAR:
419 1.25 mrg #endif
420 1.19 mrg if (precision == 16)
421 1.27 mrg conv_func = swap_bytes;
422 1.19 mrg else if (precision == 32)
423 1.27 mrg conv_func = swap_bytes32;
424 1.21 mrg if (conv_func)
425 1.21 mrg encoding = AUDIO_ENCODING_SLINEAR_BE;
426 1.25 mrg break;
427 1.25 mrg
428 1.25 mrg #if BYTE_ORDER == BIG_ENDIAN
429 1.25 mrg case AUDIO_ENCODING_SLINEAR:
430 1.25 mrg encoding = AUDIO_ENCODING_SLINEAR_BE;
431 1.25 mrg break;
432 1.25 mrg #endif
433 1.19 mrg }
434 1.19 mrg
435 1.11 mrg /* if we can't express this as a Sun header, don't write any */
436 1.11 mrg if (audio_encoding_to_sun(encoding, precision, &sunenc) != 0) {
437 1.25 mrg if (!qflag && !warned) {
438 1.25 mrg const char *s = audio_enc_from_val(oencoding);
439 1.25 mrg
440 1.25 mrg if (s == NULL)
441 1.25 mrg s = "(unknown)";
442 1.25 mrg warnx("failed to convert to sun encoding from %s "
443 1.25 mrg "(precision %d);\nSun audio header not written",
444 1.25 mrg s, precision);
445 1.25 mrg }
446 1.27 mrg format = AUDIO_FORMAT_NONE;
447 1.19 mrg conv_func = 0;
448 1.16 mrg warned = 1;
449 1.17 mrg return -1;
450 1.11 mrg }
451 1.1 mrg
452 1.1 mrg auh.magic = htonl(AUDIO_FILE_MAGIC);
453 1.1 mrg if (outfd == STDOUT_FILENO)
454 1.1 mrg auh.data_size = htonl(AUDIO_UNKNOWN_SIZE);
455 1.41 mrg else if (total_size != -1)
456 1.41 mrg auh.data_size = htonl(total_size);
457 1.1 mrg else
458 1.41 mrg auh.data_size = 0;
459 1.11 mrg auh.encoding = htonl(sunenc);
460 1.1 mrg auh.sample_rate = htonl(sample_rate);
461 1.1 mrg auh.channels = htonl(channels);
462 1.1 mrg if (header_info) {
463 1.1 mrg int len, infolen;
464 1.1 mrg
465 1.1 mrg infolen = ((len = strlen(header_info)) + 7) & 0xfffffff8;
466 1.17 mrg *leftp = infolen - len;
467 1.1 mrg auh.hdr_size = htonl(sizeof(auh) + infolen);
468 1.1 mrg } else {
469 1.17 mrg *leftp = sizeof(default_info);
470 1.17 mrg auh.hdr_size = htonl(sizeof(auh) + *leftp);
471 1.1 mrg }
472 1.17 mrg *(sun_audioheader **)hdrp = &auh;
473 1.17 mrg *lenp = sizeof auh;
474 1.17 mrg return 0;
475 1.17 mrg }
476 1.1 mrg
477 1.17 mrg int
478 1.17 mrg write_header_wav(hdrp, lenp, leftp)
479 1.17 mrg void **hdrp;
480 1.17 mrg size_t *lenp;
481 1.17 mrg int *leftp;
482 1.17 mrg {
483 1.17 mrg /*
484 1.17 mrg * WAV header we write looks like this:
485 1.17 mrg *
486 1.17 mrg * bytes purpose
487 1.17 mrg * 0-3 "RIFF"
488 1.17 mrg * 4-7 file length (minus 8)
489 1.17 mrg * 8-15 "WAVEfmt "
490 1.17 mrg * 16-19 format size
491 1.17 mrg * 20-21 format tag
492 1.17 mrg * 22-23 number of channels
493 1.17 mrg * 24-27 sample rate
494 1.17 mrg * 28-31 average bytes per second
495 1.17 mrg * 32-33 block alignment
496 1.17 mrg * 34-35 bits per sample
497 1.17 mrg *
498 1.17 mrg * then for ULAW and ALAW outputs, we have an extended chunk size
499 1.17 mrg * and a WAV "fact" to add:
500 1.17 mrg *
501 1.17 mrg * 36-37 length of extension (== 0)
502 1.17 mrg * 38-41 "fact"
503 1.17 mrg * 42-45 fact size
504 1.17 mrg * 46-49 number of samples written
505 1.17 mrg * 50-53 "data"
506 1.17 mrg * 54-57 data length
507 1.17 mrg * 58- raw audio data
508 1.17 mrg *
509 1.17 mrg * for PCM outputs we have just the data remaining:
510 1.17 mrg *
511 1.17 mrg * 36-39 "data"
512 1.17 mrg * 40-43 data length
513 1.17 mrg * 44- raw audio data
514 1.17 mrg *
515 1.17 mrg * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@
516 1.17 mrg */
517 1.17 mrg char wavheaderbuf[64], *p = wavheaderbuf;
518 1.21 mrg const char *riff = "RIFF",
519 1.21 mrg *wavefmt = "WAVEfmt ",
520 1.21 mrg *fact = "fact",
521 1.21 mrg *data = "data";
522 1.17 mrg u_int32_t filelen, fmtsz, sps, abps, factsz = 4, nsample, datalen;
523 1.17 mrg u_int16_t fmttag, nchan, align, bps, extln = 0;
524 1.17 mrg
525 1.17 mrg if (header_info)
526 1.17 mrg warnx("header information not supported for WAV");
527 1.36 fvdl *leftp = 0;
528 1.17 mrg
529 1.17 mrg switch (precision) {
530 1.17 mrg case 8:
531 1.17 mrg bps = 8;
532 1.17 mrg break;
533 1.17 mrg case 16:
534 1.17 mrg bps = 16;
535 1.17 mrg break;
536 1.17 mrg case 32:
537 1.17 mrg bps = 32;
538 1.17 mrg break;
539 1.17 mrg default:
540 1.18 mrg {
541 1.18 mrg static int warned = 0;
542 1.18 mrg
543 1.18 mrg if (warned == 0) {
544 1.30 grant warnx("can not support precision of %d", precision);
545 1.18 mrg warned = 1;
546 1.18 mrg }
547 1.17 mrg }
548 1.17 mrg return (-1);
549 1.17 mrg }
550 1.17 mrg
551 1.17 mrg switch (encoding) {
552 1.17 mrg case AUDIO_ENCODING_ULAW:
553 1.17 mrg fmttag = WAVE_FORMAT_MULAW;
554 1.17 mrg fmtsz = 18;
555 1.17 mrg align = channels;
556 1.17 mrg break;
557 1.26 mrg
558 1.17 mrg case AUDIO_ENCODING_ALAW:
559 1.17 mrg fmttag = WAVE_FORMAT_ALAW;
560 1.17 mrg fmtsz = 18;
561 1.17 mrg align = channels;
562 1.17 mrg break;
563 1.26 mrg
564 1.18 mrg /*
565 1.18 mrg * we could try to support RIFX but it seems to be more portable
566 1.18 mrg * to output little-endian data for WAV files.
567 1.18 mrg */
568 1.18 mrg case AUDIO_ENCODING_ULINEAR_BE:
569 1.26 mrg #if BYTE_ORDER == BIG_ENDIAN
570 1.26 mrg case AUDIO_ENCODING_ULINEAR:
571 1.26 mrg #endif
572 1.20 mrg if (bps == 16)
573 1.20 mrg conv_func = change_sign16_swap_bytes_be;
574 1.20 mrg else if (bps == 32)
575 1.20 mrg conv_func = change_sign32_swap_bytes_be;
576 1.20 mrg goto fmt_pcm;
577 1.26 mrg
578 1.18 mrg case AUDIO_ENCODING_SLINEAR_BE:
579 1.26 mrg #if BYTE_ORDER == BIG_ENDIAN
580 1.26 mrg case AUDIO_ENCODING_SLINEAR:
581 1.26 mrg #endif
582 1.38 mycroft if (bps == 8)
583 1.38 mycroft conv_func = change_sign8;
584 1.38 mycroft else if (bps == 16)
585 1.19 mrg conv_func = swap_bytes;
586 1.18 mrg else if (bps == 32)
587 1.19 mrg conv_func = swap_bytes32;
588 1.20 mrg goto fmt_pcm;
589 1.26 mrg
590 1.20 mrg case AUDIO_ENCODING_ULINEAR_LE:
591 1.26 mrg #if BYTE_ORDER == LITTLE_ENDIAN
592 1.26 mrg case AUDIO_ENCODING_ULINEAR:
593 1.26 mrg #endif
594 1.20 mrg if (bps == 16)
595 1.20 mrg conv_func = change_sign16_le;
596 1.20 mrg else if (bps == 32)
597 1.20 mrg conv_func = change_sign32_le;
598 1.18 mrg /* FALLTHROUGH */
599 1.26 mrg
600 1.20 mrg case AUDIO_ENCODING_SLINEAR_LE:
601 1.17 mrg case AUDIO_ENCODING_PCM16:
602 1.26 mrg #if BYTE_ORDER == LITTLE_ENDIAN
603 1.26 mrg case AUDIO_ENCODING_SLINEAR:
604 1.26 mrg #endif
605 1.38 mycroft if (bps == 8)
606 1.38 mycroft conv_func = change_sign8;
607 1.20 mrg fmt_pcm:
608 1.17 mrg fmttag = WAVE_FORMAT_PCM;
609 1.17 mrg fmtsz = 16;
610 1.17 mrg align = channels * (bps / 8);
611 1.17 mrg break;
612 1.26 mrg
613 1.17 mrg default:
614 1.18 mrg {
615 1.18 mrg static int warned = 0;
616 1.18 mrg
617 1.18 mrg if (warned == 0) {
618 1.26 mrg const char *s = wav_enc_from_val(encoding);
619 1.26 mrg
620 1.26 mrg if (s == NULL)
621 1.30 grant warnx("can not support encoding of %s", s);
622 1.26 mrg else
623 1.30 grant warnx("can not support encoding of %d", encoding);
624 1.18 mrg warned = 1;
625 1.18 mrg }
626 1.17 mrg }
627 1.27 mrg format = AUDIO_FORMAT_NONE;
628 1.17 mrg return (-1);
629 1.17 mrg }
630 1.17 mrg
631 1.17 mrg nchan = channels;
632 1.17 mrg sps = sample_rate;
633 1.17 mrg
634 1.17 mrg /* data length */
635 1.17 mrg if (outfd == STDOUT_FILENO)
636 1.17 mrg datalen = 0;
637 1.41 mrg else if (total_size != -1)
638 1.41 mrg datalen = total_size;
639 1.17 mrg else
640 1.41 mrg datalen = 0;
641 1.17 mrg
642 1.17 mrg /* file length */
643 1.17 mrg filelen = 4 + (8 + fmtsz) + (8 + datalen);
644 1.17 mrg if (fmttag != WAVE_FORMAT_PCM)
645 1.17 mrg filelen += 8 + factsz;
646 1.17 mrg
647 1.17 mrg abps = (double)align*sample_rate / (double)1 + 0.5;
648 1.17 mrg
649 1.17 mrg nsample = (datalen / bps) / sample_rate;
650 1.17 mrg
651 1.17 mrg /*
652 1.17 mrg * now we've calculated the info, write it out!
653 1.17 mrg */
654 1.17 mrg #define put32(x) do { \
655 1.17 mrg u_int32_t _f; \
656 1.17 mrg putle32(_f, (x)); \
657 1.17 mrg memcpy(p, &_f, 4); \
658 1.17 mrg } while (0)
659 1.17 mrg #define put16(x) do { \
660 1.17 mrg u_int16_t _f; \
661 1.17 mrg putle16(_f, (x)); \
662 1.17 mrg memcpy(p, &_f, 2); \
663 1.17 mrg } while (0)
664 1.17 mrg memcpy(p, riff, 4);
665 1.17 mrg p += 4; /* 4 */
666 1.17 mrg put32(filelen);
667 1.17 mrg p += 4; /* 8 */
668 1.17 mrg memcpy(p, wavefmt, 8);
669 1.17 mrg p += 8; /* 16 */
670 1.17 mrg put32(fmtsz);
671 1.17 mrg p += 4; /* 20 */
672 1.17 mrg put16(fmttag);
673 1.17 mrg p += 2; /* 22 */
674 1.17 mrg put16(nchan);
675 1.17 mrg p += 2; /* 24 */
676 1.17 mrg put32(sps);
677 1.17 mrg p += 4; /* 28 */
678 1.17 mrg put32(abps);
679 1.17 mrg p += 4; /* 32 */
680 1.17 mrg put16(align);
681 1.17 mrg p += 2; /* 34 */
682 1.17 mrg put16(bps);
683 1.17 mrg p += 2; /* 36 */
684 1.17 mrg /* NON PCM formats have an extended chunk; write it */
685 1.17 mrg if (fmttag != WAVE_FORMAT_PCM) {
686 1.17 mrg put16(extln);
687 1.17 mrg p += 2; /* 38 */
688 1.17 mrg memcpy(p, fact, 4);
689 1.17 mrg p += 4; /* 42 */
690 1.17 mrg put32(factsz);
691 1.17 mrg p += 4; /* 46 */
692 1.17 mrg put32(nsample);
693 1.17 mrg p += 4; /* 50 */
694 1.17 mrg }
695 1.17 mrg memcpy(p, data, 4);
696 1.17 mrg p += 4; /* 40/54 */
697 1.17 mrg put32(datalen);
698 1.17 mrg p += 4; /* 44/58 */
699 1.17 mrg #undef put32
700 1.17 mrg #undef put16
701 1.17 mrg
702 1.17 mrg *hdrp = wavheaderbuf;
703 1.17 mrg *lenp = (p - wavheaderbuf);
704 1.17 mrg
705 1.17 mrg return 0;
706 1.17 mrg }
707 1.17 mrg
708 1.17 mrg void
709 1.17 mrg write_header()
710 1.17 mrg {
711 1.17 mrg struct iovec iv[3];
712 1.17 mrg int veclen, left, tlen;
713 1.17 mrg void *hdr;
714 1.17 mrg size_t hdrlen;
715 1.17 mrg
716 1.17 mrg switch (format) {
717 1.27 mrg case AUDIO_FORMAT_DEFAULT:
718 1.17 mrg case AUDIO_FORMAT_SUN:
719 1.17 mrg if (write_header_sun(&hdr, &hdrlen, &left) != 0)
720 1.17 mrg return;
721 1.17 mrg break;
722 1.17 mrg case AUDIO_FORMAT_WAV:
723 1.17 mrg if (write_header_wav(&hdr, &hdrlen, &left) != 0)
724 1.17 mrg return;
725 1.17 mrg break;
726 1.17 mrg case AUDIO_FORMAT_NONE:
727 1.17 mrg return;
728 1.17 mrg default:
729 1.17 mrg errx(1, "unknown audio format");
730 1.17 mrg }
731 1.17 mrg
732 1.17 mrg veclen = 0;
733 1.17 mrg tlen = 0;
734 1.17 mrg
735 1.17 mrg if (hdrlen != 0) {
736 1.17 mrg iv[veclen].iov_base = hdr;
737 1.17 mrg iv[veclen].iov_len = hdrlen;
738 1.17 mrg tlen += iv[veclen++].iov_len;
739 1.17 mrg }
740 1.1 mrg if (header_info) {
741 1.1 mrg iv[veclen].iov_base = header_info;
742 1.17 mrg iv[veclen].iov_len = (int)strlen(header_info) + 1;
743 1.1 mrg tlen += iv[veclen++].iov_len;
744 1.1 mrg }
745 1.1 mrg if (left) {
746 1.1 mrg iv[veclen].iov_base = default_info;
747 1.1 mrg iv[veclen].iov_len = left;
748 1.1 mrg tlen += iv[veclen++].iov_len;
749 1.1 mrg }
750 1.1 mrg
751 1.17 mrg if (tlen == 0)
752 1.17 mrg return;
753 1.17 mrg
754 1.1 mrg if (writev(outfd, iv, veclen) != tlen)
755 1.1 mrg err(1, "could not write audio header");
756 1.1 mrg }
757 1.1 mrg
758 1.1 mrg void
759 1.1 mrg rewrite_header()
760 1.1 mrg {
761 1.1 mrg
762 1.1 mrg /* can't do this here! */
763 1.1 mrg if (outfd == STDOUT_FILENO)
764 1.1 mrg return;
765 1.1 mrg
766 1.1 mrg if (lseek(outfd, SEEK_SET, 0) < 0)
767 1.1 mrg err(1, "could not seek to start of file for header rewrite");
768 1.1 mrg write_header();
769 1.1 mrg }
770 1.1 mrg
771 1.1 mrg void
772 1.1 mrg usage()
773 1.1 mrg {
774 1.14 cgd
775 1.14 cgd fprintf(stderr, "Usage: %s [-afhqV] [options] {files ...|-}\n",
776 1.14 cgd getprogname());
777 1.4 mrg fprintf(stderr, "Options:\n\t"
778 1.4 mrg "-b balance (0-63)\n\t"
779 1.4 mrg "-c channels\n\t"
780 1.4 mrg "-d audio device\n\t"
781 1.4 mrg "-e encoding\n\t"
782 1.35 wiz "-F format\n\t"
783 1.4 mrg "-i header information\n\t"
784 1.4 mrg "-m monitor volume\n\t"
785 1.35 wiz "-P precision (4, 8, 16, 24, or 32 bits)\n\t"
786 1.4 mrg "-p input port\n\t"
787 1.4 mrg "-s sample rate\n\t"
788 1.4 mrg "-t recording time\n\t"
789 1.4 mrg "-v volume\n");
790 1.9 kleink exit(EXIT_FAILURE);
791 1.1 mrg }
792