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