Home | History | Annotate | Line # | Download | only in record
record.c revision 1.53
      1  1.53       mrg /*	$NetBSD: record.c,v 1.53 2013/08/30 20:57:26 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.53       mrg __RCSID("$NetBSD: record.c,v 1.53 2013/08/30 20:57:26 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.53       mrg static struct write_info wi;
     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.53       mrg 	 * Initialise the write_info.
     89  1.53       mrg 	 */
     90  1.53       mrg 	wi.format = AUDIO_FORMAT_DEFAULT;
     91  1.53       mrg 	wi.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.53       mrg 			wi.format = audio_format_from_str(optarg);
    112  1.53       mrg 			if (wi.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.53       mrg 			decode_int(optarg, &wi.channels);
    118  1.53       mrg 			if (wi.channels < 0 || wi.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.53       mrg 			wi.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.53       mrg 			decode_int(optarg, &wi.precision);
    140  1.53       mrg 			if (wi.precision != 4 && wi.precision != 8 &&
    141  1.53       mrg 			    wi.precision != 16 && wi.precision != 24 &&
    142  1.53       mrg 			    wi.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.53       mrg 			wi.qflag++;
    161   1.1       mrg 			break;
    162   1.1       mrg 		case 's':
    163  1.53       mrg 			decode_int(optarg, &wi.sample_rate);
    164  1.53       mrg 			if (wi.sample_rate < 0 || wi.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.53       mrg 		wi.encoding = audio_enc_to_val(encoding_str);
    196  1.53       mrg 		if (wi.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.53       mrg 		if (wi.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.53       mrg 				wi.format = AUDIO_FORMAT_SUN;
    212  1.40       mrg 			else if (strcasecmp(arg + flen - 4, ".wav") == 0)
    213  1.53       mrg 				wi.format = AUDIO_FORMAT_WAV;
    214  1.40       mrg 		}
    215  1.53       mrg 		wi.outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666);
    216  1.53       mrg 		if (wi.outfd < 0)
    217  1.40       mrg 			err(1, "could not open %s", *argv);
    218  1.40       mrg 	} else
    219  1.53       mrg 		wi.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.53       mrg #define SETINFO(x)	SETINFO2(wi.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.53       mrg 	wi.total_size = 0;
    289  1.53       mrg 
    290  1.53       mrg 	write_header(&wi);
    291  1.53       mrg 	if (wi.format == AUDIO_FORMAT_NONE)
    292  1.53       mrg 		errx(1, "unable to determine audio format");
    293  1.53       mrg 	conv_func = write_get_conv_func(&wi);
    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.53       mrg 		if ((size_t)write(wi.outfd, buffer, bufsize) != bufsize)
    345   1.1       mrg 			err(1, "write failed");
    346  1.53       mrg 		wi.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.53       mrg 	close(wi.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.53       mrg 	if (wi.outfd == STDOUT_FILENO)
    388   1.1       mrg 		return;
    389   1.1       mrg 
    390  1.53       mrg 	if (lseek(wi.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.53       mrg 	write_header(&wi);
    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