aplay - Audio Surveilance

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I have added some features to aplay to make audio surveillance easier:
--process-id-file writes the PID to a file so another program can signal
aplay to close its capture file and open another one without missing any
sound.  --max-file-time causes the close and re-open to happen
automatically.  --use-strftime allows capture files to be named for
their start times.  Details are in the man file.

For ease of review, this patch is divided into three parts.  Part 1 is
just --process-id-file, which is intrusive because it must intercept the
exit points to delete the file.  Part 2 is the remainder of the new
features in aplay.c.  Part 3 updates the man file.
    John Sauter (John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx)

From 2615ace193a0c31605be1ef3c67e0382d6eb84a8 Mon Sep 17 00:00:00 2001
From: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 8 Jan 2010 16:21:07 -0500
Subject: [PATCH - aplay 1/3] aplay - add option --process-id-file

Write the process ID to a file so other programs can
signal aplay.  When aplay exits, delete the file.

Signed-off-by: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
---
 aplay/aplay.c |  105 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/aplay/aplay.c b/aplay/aplay.c
index e1f7c4d..ddb766d 100644
--- a/aplay/aplay.c
+++ b/aplay/aplay.c
@@ -116,6 +116,10 @@ static int fd = -1;
 static off64_t pbrec_count = LLONG_MAX, fdcount;
 static int vocmajor, vocminor;
 
+static char *pidfile_name = NULL;
+FILE *pidf = NULL;
+static int pidfile_written = 0;
+
 /* needed prototypes */
 
 static void playback(char *filename);
@@ -194,7 +198,8 @@ _("Usage: %s [OPTION]... [FILE]...\n"
 "    --test-position     test ring buffer position\n"
 "    --test-coef=#	 test coeficient for ring buffer position (default 8)\n"
 "                        expression for validation is: coef * (buffer_size / 2)\n"
-"    --test-nowait       do not wait for ring buffer - eats whole CPU\n")
+"    --test-nowait       do not wait for ring buffer - eats whole CPU\n"
+"    --process-id-file   write the process ID here\n")
 		, command);
 	printf(_("Recognized sample formats are:"));
 	for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
@@ -324,6 +329,15 @@ static void version(void)
 	printf("%s: version " SND_UTIL_VERSION_STR " by Jaroslav Kysela <perex@xxxxxxxx>\n", command);
 }
 
+/*
+ *	Subroutine to clean up before exit.
+ */
+static void prg_exit () 
+{
+	if (pidfile_written)
+		remove (pidfile_name);
+}
+
 static void signal_handler(int sig)
 {
 	if (verbose==2)
@@ -345,6 +359,7 @@ static void signal_handler(int sig)
 		snd_pcm_close(handle);
 		handle = NULL;
 	}
+	prg_exit();
 	exit(EXIT_FAILURE);
 }
 
@@ -358,7 +373,8 @@ enum {
 	OPT_DISABLE_SOFTVOL,
 	OPT_TEST_POSITION,
 	OPT_TEST_COEF,
-	OPT_TEST_NOWAIT
+	OPT_TEST_NOWAIT,
+	OPT_PROCESS_ID_FILE
 };
 
 int main(int argc, char *argv[])
@@ -399,6 +415,7 @@ int main(int argc, char *argv[])
 		{"test-position", 0, 0, OPT_TEST_POSITION},
 		{"test-coef", 1, 0, OPT_TEST_COEF},
 		{"test-nowait", 0, 0, OPT_TEST_NOWAIT},
+		{"process-id-file", 1, 0, OPT_PROCESS_ID_FILE},
 		{0, 0, 0, 0}
 	};
 	char *pcm_name = "default";
@@ -493,6 +510,7 @@ int main(int argc, char *argv[])
 				rhwparams.format = snd_pcm_format_value(optarg);
 				if (rhwparams.format == SND_PCM_FORMAT_UNKNOWN) {
 					error(_("wrong extended format '%s'"), optarg);
+					prg_exit();
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -588,6 +606,9 @@ int main(int argc, char *argv[])
 		case OPT_TEST_NOWAIT:
 			test_nowait = 1;
 			break;
+		case OPT_PROCESS_ID_FILE:
+			pidfile_name = optarg;
+			break;
 		default:
 			fprintf(stderr, _("Try `%s --help' for more information.\n"), command);
 			return 1;
@@ -643,6 +664,19 @@ int main(int argc, char *argv[])
 		readn_func = snd_pcm_readn;
 	}
 
+	if (pidfile_name) {
+		errno = 0;
+		pidf = fopen (pidfile_name, "w");
+		if (pidf) {
+			(void)fprintf (pidf, "%d\n", getpid());
+			fclose(pidf);
+			pidfile_written = 1;
+		} else {
+			error(_("Cannot create process ID file %s: %s"), 
+				pidfile_name, strerror (errno));
+			return 1;
+		}
+	}
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
@@ -674,6 +708,7 @@ int main(int argc, char *argv[])
       __end:
 	snd_output_close(log);
 	snd_config_update_free_global();
+	prg_exit ();
 	return EXIT_SUCCESS;
 }
 
@@ -725,6 +760,7 @@ static size_t test_wavefile_read(int fd, u_char *buffer, size_t *size, size_t re
 		return *size;
 	if ((size_t)safe_read(fd, buffer + *size, reqsize - *size) != reqsize - *size) {
 		error(_("read error (called from line %i)"), line);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	return *size = reqsize;
@@ -735,6 +771,7 @@ static size_t test_wavefile_read(int fd, u_char *buffer, size_t *size, size_t re
 		blimit = len; \
 		if ((buffer = realloc(buffer, blimit)) == NULL) { \
 			error(_("not enough memory"));		  \
+			prg_exit ();  \
 			exit(EXIT_FAILURE); \
 		} \
 	}
@@ -784,6 +821,7 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
 	if (len < sizeof(WaveFmtBody)) {
 		error(_("unknown length of 'fmt ' chunk (read %u, should be %u at least)"),
 		      len, (u_int)sizeof(WaveFmtBody));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	check_wavefile_space(buffer, len, blimit);
@@ -794,10 +832,12 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
 		if (len < sizeof(WaveFmtExtensibleBody)) {
 			error(_("unknown length of extensible 'fmt ' chunk (read %u, should be %u at least)"),
 					len, (u_int)sizeof(WaveFmtExtensibleBody));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		if (memcmp(fe->guid_tag, WAV_GUID_TAG, 14) != 0) {
 			error(_("wrong format tag in extensible 'fmt ' chunk"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		f->format = fe->guid_format;
@@ -805,10 +845,12 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
         if (LE_SHORT(f->format) != WAV_FMT_PCM &&
             LE_SHORT(f->format) != WAV_FMT_IEEE_FLOAT) {
                 error(_("can't play WAVE-file format 0x%04x which is not PCM or FLOAT encoded"), LE_SHORT(f->format));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (LE_SHORT(f->channels) < 1) {
 		error(_("can't play WAVE-files with %d tracks"), LE_SHORT(f->channels));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	hwparams.channels = LE_SHORT(f->channels);
@@ -842,6 +884,7 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
 		default:
 			error(_(" can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)"),
 			      LE_SHORT(f->bit_p_spl), LE_SHORT(f->byte_p_spl), hwparams.channels);
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		break;
@@ -854,6 +897,7 @@ static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
 	default:
 		error(_(" can't play WAVE-files with sample %d bits wide"),
 		      LE_SHORT(f->bit_p_spl));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	hwparams.rate = LE_INT(f->sample_fq);
@@ -936,6 +980,7 @@ static int test_au(int fd, void *buffer)
 		return -1;
 	if ((size_t)safe_read(fd, buffer + sizeof(AuHeader), BE_INT(ap->hdr_size) - sizeof(AuHeader)) != BE_INT(ap->hdr_size) - sizeof(AuHeader)) {
 		error(_("read error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	return 0;
@@ -966,6 +1011,7 @@ static void set_params(void)
 	err = snd_pcm_hw_params_any(handle, params);
 	if (err < 0) {
 		error(_("Broken configuration for this PCM: no configurations available"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (mmap_flag) {
@@ -983,17 +1029,20 @@ static void set_params(void)
 						   SND_PCM_ACCESS_RW_NONINTERLEAVED);
 	if (err < 0) {
 		error(_("Access type not available"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	err = snd_pcm_hw_params_set_format(handle, params, hwparams.format);
 	if (err < 0) {
 		error(_("Sample format non available"));
 		show_available_sample_formats(params);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels);
 	if (err < 0) {
 		error(_("Channels count non available"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 
@@ -1052,6 +1101,7 @@ static void set_params(void)
 	if (err < 0) {
 		error(_("Unable to install hw params:"));
 		snd_pcm_hw_params_dump(params, log);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
@@ -1059,6 +1109,7 @@ static void set_params(void)
 	if (chunk_size == buffer_size) {
 		error(_("Can't use period equal to buffer size (%lu == %lu)"),
 		      chunk_size, buffer_size);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	snd_pcm_sw_params_current(handle, swparams);
@@ -1090,6 +1141,7 @@ static void set_params(void)
 	if (snd_pcm_sw_params(handle, swparams) < 0) {
 		error(_("unable to install sw params:"));
 		snd_pcm_sw_params_dump(swparams, log);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 
@@ -1102,6 +1154,7 @@ static void set_params(void)
 	audiobuf = realloc(audiobuf, chunk_bytes);
 	if (audiobuf == NULL) {
 		error(_("not enough memory"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
@@ -1120,6 +1173,7 @@ static void set_params(void)
 		err = snd_pcm_mmap_begin(handle, &areas, &offset, &size);
 		if (err < 0) {
 			error("snd_pcm_mmap_begin problem: %s", snd_strerror(err));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		for (i = 0; i < hwparams.channels; i++)
@@ -1164,6 +1218,7 @@ static void xrun(void)
 	snd_pcm_status_alloca(&status);
 	if ((res = snd_pcm_status(handle, status))<0) {
 		error(_("status error: %s"), snd_strerror(res));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
@@ -1176,8 +1231,6 @@ static void xrun(void)
 			fprintf(stderr, _("%s!!! (at least %.3f ms long)\n"),
 				stream == SND_PCM_STREAM_PLAYBACK ? _("underrun") : _("overrun"),
 				diff.tv_sec * 1000 + diff.tv_nsec / 10000000.0);
-			if (verbose)
-				snd_pcm_dump(handle, log);
 #else
 			fprintf(stderr, "%s !!!\n", _("underrun"));
 #endif
@@ -1196,6 +1249,7 @@ static void xrun(void)
 		}
 		if ((res = snd_pcm_prepare(handle))<0) {
 			error(_("xrun: prepare error: %s"), snd_strerror(res));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		return;		/* ok, data should be accepted again */
@@ -1208,6 +1262,7 @@ static void xrun(void)
 			fprintf(stderr, _("capture stream format change? attempting recover...\n"));
 			if ((res = snd_pcm_prepare(handle))<0) {
 				error(_("xrun(DRAINING): prepare error: %s"), snd_strerror(res));
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 			return;
@@ -1218,6 +1273,7 @@ static void xrun(void)
 		snd_pcm_status_dump(status, log);
 	}
 	error(_("read/write error, state = %s"), snd_pcm_state_name(snd_pcm_status_get_state(status)));
+	prg_exit ();
 	exit(EXIT_FAILURE);
 }
 
@@ -1235,6 +1291,7 @@ static void suspend(void)
 			fprintf(stderr, _("Failed. Restarting stream. ")); fflush(stderr);
 		if ((res = snd_pcm_prepare(handle)) < 0) {
 			error(_("suspend: prepare error: %s"), snd_strerror(res));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -1540,6 +1597,7 @@ static ssize_t pcm_write(u_char *data, size_t count)
 			suspend();
 		} else if (r < 0) {
 			error(_("write error: %s"), snd_strerror(r));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1586,6 +1644,7 @@ static ssize_t pcm_writev(u_char **data, unsigned int channels, size_t count)
 			suspend();
 		} else if (r < 0) {
 			error(_("writev error: %s"), snd_strerror(r));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1629,6 +1688,7 @@ static ssize_t pcm_read(u_char *data, size_t rcount)
 			suspend();
 		} else if (r < 0) {
 			error(_("read error: %s"), snd_strerror(r));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1672,6 +1732,7 @@ static ssize_t pcm_readv(u_char **data, unsigned int channels, size_t rcount)
 			suspend();
 		} else if (r < 0) {
 			error(_("readv error: %s"), snd_strerror(r));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1729,6 +1790,7 @@ static void voc_write_silence(unsigned x)
 			l = chunk_size;
 		if (voc_pcm_write(buf, l) != (ssize_t)l) {
 			error(_("write error"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		x -= l;
@@ -1771,6 +1833,7 @@ static void voc_play(int fd, int ofs, char *name)
 	buffer_pos = 0;
 	if (data == NULL) {
 		error(_("malloc error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (!quiet_mode) {
@@ -1780,6 +1843,7 @@ static void voc_play(int fd, int ofs, char *name)
 	while (ofs > (ssize_t)chunk_bytes) {
 		if ((size_t)safe_read(fd, buf, chunk_bytes) != chunk_bytes) {
 			error(_("read error"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		ofs -= chunk_bytes;
@@ -1787,6 +1851,7 @@ static void voc_play(int fd, int ofs, char *name)
 	if (ofs) {
 		if (safe_read(fd, buf, ofs) != ofs) {
 			error(_("read error"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -1811,6 +1876,7 @@ static void voc_play(int fd, int ofs, char *name)
 				nextblock = buf[0] = 0;
 				if (l == -1) {
 					perror(name);
+					prg_exit ();
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -1955,11 +2021,13 @@ static void voc_play(int fd, int ofs, char *name)
 			if (output && !quiet_mode) {
 				if (write(2, data, l) != l) {	/* to stderr */
 					error(_("write error"));
+					prg_exit ();
 					exit(EXIT_FAILURE);
 				}
 			} else {
 				if (voc_pcm_write(data, l) != l) {
 					error(_("write error"));
+					prg_exit ();
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -2007,6 +2075,7 @@ static void begin_voc(int fd, size_t cnt)
 
 	if (write(fd, &vh, sizeof(VocHeader)) != sizeof(VocHeader)) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (hwparams.channels > 1) {
@@ -2016,6 +2085,7 @@ static void begin_voc(int fd, size_t cnt)
 		bt.datalen_m = bt.datalen_h = 0;
 		if (write(fd, &bt, sizeof(VocBlockType)) != sizeof(VocBlockType)) {
 			error(_("write error"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 		eb.tc = LE_SHORT(65536 - 256000000L / (hwparams.rate << 1));
@@ -2023,6 +2093,7 @@ static void begin_voc(int fd, size_t cnt)
 		eb.mode = 1;
 		if (write(fd, &eb, sizeof(VocExtBlock)) != sizeof(VocExtBlock)) {
 			error(_("write error"));
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -2033,12 +2104,14 @@ static void begin_voc(int fd, size_t cnt)
 	bt.datalen_h = (u_char) ((cnt & 0xFF0000) >> 16);
 	if (write(fd, &bt, sizeof(VocBlockType)) != sizeof(VocBlockType)) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	vd.tc = (u_char) (256 - (1000000 / hwparams.rate));
 	vd.pack = 0;
 	if (write(fd, &vd, sizeof(VocVoiceData)) != sizeof(VocVoiceData)) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2075,6 +2148,7 @@ static void begin_wave(int fd, size_t cnt)
 		break;
 	default:
 		error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	h.magic = WAV_RIFF;
@@ -2111,6 +2185,7 @@ static void begin_wave(int fd, size_t cnt)
 	    write(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) ||
 	    write(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2135,12 +2210,14 @@ static void begin_au(int fd, size_t cnt)
 		break;
 	default:
 		error(_("Sparc Audio doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	ah.sample_rate = BE_INT(hwparams.rate);
 	ah.channels = BE_INT(hwparams.channels);
 	if (write(fd, &ah, sizeof(AuHeader)) != sizeof(AuHeader)) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2155,6 +2232,7 @@ static void end_voc(int fd)
 
 	if (write(fd, &dummy, 1) != 1) {
 		error(_("write error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	length_seek = sizeof(VocHeader);
@@ -2263,6 +2341,7 @@ static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *n
 			r = safe_read(fd, audiobuf + l, c);
 			if (r < 0) {
 				perror(name);
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 			fdcount += r;
@@ -2302,6 +2381,7 @@ static void playback(char *name)
 	} else {
 		if ((fd = open64(name, O_RDONLY, 0)) == -1) {
 			perror(name);
+			prg_exit ();
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -2309,6 +2389,7 @@ static void playback(char *name)
 	dta = sizeof(AuHeader);
 	if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
 		error(_("read error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if (test_au(fd, audiobuf) >= 0) {
@@ -2321,6 +2402,7 @@ static void playback(char *name)
 	if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader),
 		 dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) {
 		error(_("read error"));
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 	if ((ofs = test_vocfile(audiobuf)) >= 0) {
@@ -2430,6 +2512,7 @@ static void capture(char *orig_name)
 			remove(name);
 			if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) {
 				perror(name);
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 			filecount++;
@@ -2453,6 +2536,7 @@ static void capture(char *orig_name)
 				break;
 			if (write(fd, audiobuf, c) != c) {
 				perror(name);
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 			count -= c;
@@ -2500,11 +2584,13 @@ static void playbackv_go(int* fds, unsigned int channels, size_t loaded, off64_t
 			r = safe_read(fds[0], bufs[0], expected);
 			if (r < 0) {
 				perror(names[channel]);
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 			for (channel = 1; channel < channels; ++channel) {
 				if (safe_read(fds[channel], bufs[channel], r) != r) {
 					perror(names[channel]);
+					prg_exit ();
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -2552,6 +2638,7 @@ static void capturev_go(int* fds, unsigned int channels, off64_t count, int rtyp
 		for (channel = 0; channel < channels; ++channel) {
 			if ((size_t)write(fds[channel], bufs[channel], rv) != rv) {
 				perror(names[channel]);
+				prg_exit ();
 				exit(EXIT_FAILURE);
 			}
 		}
@@ -2585,6 +2672,7 @@ static void playbackv(char **names, unsigned int count)
 		alloced = 1;
 	} else if (count != channels) {
 		error(_("You need to specify %d files"), channels);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 
@@ -2610,8 +2698,10 @@ static void playbackv(char **names, unsigned int count)
 	}
 	if (alloced)
 		free(names);
-	if (ret)
+	if (ret) {
+		prg_exit ();
 		exit(ret);
+	}
 }
 
 static void capturev(char **names, unsigned int count)
@@ -2638,6 +2728,7 @@ static void capturev(char **names, unsigned int count)
 		alloced = 1;
 	} else if (count != channels) {
 		error(_("You need to specify %d files"), channels);
+		prg_exit ();
 		exit(EXIT_FAILURE);
 	}
 
@@ -2663,6 +2754,8 @@ static void capturev(char **names, unsigned int count)
 	}
 	if (alloced)
 		free(names);
-	if (ret)
+	if (ret) {
+		prg_exit ();
 		exit(ret);
+	}
 }
-- 
1.6.3.3

From 5d8ab60e14ba7846a49ec6735eeab74f9e0fd364 Mon Sep 17 00:00:00 2001
From: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 8 Jan 2010 16:24:33 -0500
Subject: [PATCH - aplay 2/3] aplay -- add features for audio surveilance

Add signal SIGUSR1 to turn over the output file,
--max-file-time to cause the output file to turn over automatically,
and --use-strftime to create output files based on the current time.

Signed-off-by: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
---
 aplay/aplay.c |  189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 181 insertions(+), 8 deletions(-)

diff --git a/aplay/aplay.c b/aplay/aplay.c
index ddb766d..02322f1 100644
--- a/aplay/aplay.c
+++ b/aplay/aplay.c
@@ -111,6 +111,10 @@ static int test_position = 0;
 static int test_coef = 8;
 static int test_nowait = 0;
 static snd_output_t *log;
+static long long max_file_size = 0;
+static int max_file_time = 0;
+static int use_strftime = 0;
+volatile static int recycle_capture_file = 0;
 
 static int fd = -1;
 static off64_t pbrec_count = LLONG_MAX, fdcount;
@@ -199,7 +203,10 @@ _("Usage: %s [OPTION]... [FILE]...\n"
 "    --test-coef=#	 test coeficient for ring buffer position (default 8)\n"
 "                        expression for validation is: coef * (buffer_size / 2)\n"
 "    --test-nowait       do not wait for ring buffer - eats whole CPU\n"
-"    --process-id-file   write the process ID here\n")
+"    --max-file-time=#   start another output file when the old file has recorded\n"
+"                        for this many seconds\n"
+"    --process-id-file   write the process ID here\n"
+"    --use-strftime      apply the strftime facility to the output file name\n")
 		, command);
 	printf(_("Recognized sample formats are:"));
 	for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
@@ -363,6 +370,13 @@ static void signal_handler(int sig)
 	exit(EXIT_FAILURE);
 }
 
+/* call on SIGUSR1 signal. */
+static void signal_handler_recycle (int sig)
+{
+	/* flag the capture loop to start a new output file */
+	recycle_capture_file = 1;
+}
+
 enum {
 	OPT_VERSION = 1,
 	OPT_PERIOD_SIZE,
@@ -374,7 +388,9 @@ enum {
 	OPT_TEST_POSITION,
 	OPT_TEST_COEF,
 	OPT_TEST_NOWAIT,
-	OPT_PROCESS_ID_FILE
+	OPT_MAX_FILE_TIME,
+	OPT_PROCESS_ID_FILE,
+	OPT_USE_STRFTIME
 };
 
 int main(int argc, char *argv[])
@@ -415,7 +431,9 @@ int main(int argc, char *argv[])
 		{"test-position", 0, 0, OPT_TEST_POSITION},
 		{"test-coef", 1, 0, OPT_TEST_COEF},
 		{"test-nowait", 0, 0, OPT_TEST_NOWAIT},
+		{"max-file-time", 1, 0, OPT_MAX_FILE_TIME},
 		{"process-id-file", 1, 0, OPT_PROCESS_ID_FILE},
+		{"use-strftime", 0, 0, OPT_USE_STRFTIME},
 		{0, 0, 0, 0}
 	};
 	char *pcm_name = "default";
@@ -606,9 +624,15 @@ int main(int argc, char *argv[])
 		case OPT_TEST_NOWAIT:
 			test_nowait = 1;
 			break;
+		case OPT_MAX_FILE_TIME:
+			max_file_time = strtol(optarg, NULL, 0);
+			break;
 		case OPT_PROCESS_ID_FILE:
 			pidfile_name = optarg;
 			break;
+		case OPT_USE_STRFTIME:
+			use_strftime = 1;
+			break;
 		default:
 			fprintf(stderr, _("Try `%s --help' for more information.\n"), command);
 			return 1;
@@ -681,6 +705,7 @@ int main(int argc, char *argv[])
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 	signal(SIGABRT, signal_handler);
+	signal(SIGUSR1, signal_handler_recycle);
 	if (interleaved) {
 		if (optind > argc - 1) {
 			if (stream == SND_PCM_STREAM_PLAYBACK)
@@ -2425,13 +2450,100 @@ static void playback(char *name)
 		close(fd);
 }
 
+/**
+ * mystrftime
+ *
+ *   Variant of strftime(3) that supports additional format
+ *   specifiers in the format string.
+ *
+ * Parameters:
+ *
+ *   s	  - destination string
+ *   max	- max number of bytes to write
+ *   userformat - format string
+ *   tm	 - time information
+ *   filenumber - the number of the file, starting at 1
+ *
+ * Returns: number of bytes written to the string s
+ */
+size_t mystrftime(char *s, size_t max, const char *userformat,
+		  const struct tm *tm, const int filenumber)
+{
+	char formatstring[PATH_MAX] = "";
+	char tempstring[PATH_MAX] = "";
+	char *format, *tempstr;
+	const char *pos_userformat;
+
+	format = formatstring;
+
+	/* if mystrftime is called with userformat = NULL we return a zero length string */
+	if (userformat == NULL) {
+		*s = '\0';
+		return 0;
+	}
+
+	for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
+		if (*pos_userformat == '%') {
+			tempstr = tempstring;
+			tempstr[0] = '\0';
+			switch (*++pos_userformat) {
+
+				case '\0': // end of string
+					--pos_userformat;
+					break;
+
+				case 'v': // file number 
+					sprintf(tempstr, "%02d", filenumber);
+					break;
+
+				default: // All other codes will be handled by strftime
+					*format++ = '%';
+					*format++ = *pos_userformat;
+					continue;
+			}
+
+			/* If a format specifier was found and used, copy the result. */
+			if (tempstr[0]) {
+				while ((*format = *tempstr++) != '\0')
+					++format;
+				continue;
+			}
+		}
+
+		/* For any other character than % we simply copy the character */
+		*format++ = *pos_userformat;
+	}
+
+	*format = '\0';
+	format = formatstring;
+	return strftime(s, max, format, tm);
+}
+
 static int new_capture_file(char *name, char *namebuf, size_t namelen,
 			    int filecount)
 {
-	/* get a copy of the original filename */
 	char *s;
 	char buf[PATH_MAX+1];
+	time_t t;
+	struct tm *tmp;
+
+	if (use_strftime) {
+		t = time(NULL);
+		tmp = localtime(&t);
+		if (tmp == NULL) {
+			perror ("localtime");
+			prg_exit ();
+			exit (EXIT_FAILURE);
+		}
+		if (mystrftime (namebuf, namelen, name, tmp, filecount+1) == 0) {
+			prg_exit ();
+			fprintf (stderr, "mystrftime returned 0");
+			exit (EXIT_FAILURE);
+		}
+		return filecount;
+	}
 
+	/* get a copy of the original filename */
 	strncpy(buf, name, sizeof(buf));
 
 	/* separate extension from filename */
@@ -2463,6 +2575,44 @@ static int new_capture_file(char *name, char *namebuf, size_t namelen,
 	return filecount;
 }
 
+/**
+ * create_path
+ *
+ *   This function creates a file path, like mkdir -p. 
+ *
+ * Parameters:
+ *
+ *   path - the path to create
+ *
+ * Returns: 0 on success, -1 on failure
+ * On failure, a message has been printed to stderr.
+ */
+int create_path(const char *path)
+{
+	char *start;
+	mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+	if (path[0] == '/')
+		start = strchr(path + 1, '/');
+	else
+		start = strchr(path, '/');
+
+	while (start) {
+		char *buffer = strdup(path);
+		buffer[start-path] = 0x00;
+
+		if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
+			fprintf (stderr, "Problem creating directory %s", buffer);
+			perror (" ");
+			free(buffer);
+			return -1;
+		}
+		free(buffer);
+		start = strchr(start + 1, '/');
+	}
+	return 0;
+}
+
 static void capture(char *orig_name)
 {
 	int tostdout=0;		/* boolean which describes output stream */
@@ -2475,6 +2625,8 @@ static void capture(char *orig_name)
 	count = calc_count();
 	if (count == 0)
 		count = LLONG_MAX;
+	/* compute the number of bytes per file */
+	max_file_size = max_file_time * snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
 	/* WAVE-file should be even (I'm not sure), but wasting one byte
 	   isn't a problem (this can only be in 8 bit mono) */
 	if (count < LLONG_MAX)
@@ -2501,7 +2653,7 @@ static void capture(char *orig_name)
 		/* open a file to write */
 		if(!tostdout) {
 			/* upon the second file we start the numbering scheme */
-			if (filecount) {
+			if (filecount || use_strftime) {
 				filecount = new_capture_file(orig_name, namebuf,
 							     sizeof(namebuf),
 							     filecount);
@@ -2511,9 +2663,22 @@ static void capture(char *orig_name)
 			/* open a new file */
 			remove(name);
 			if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) {
-				perror(name);
-				prg_exit ();
-				exit(EXIT_FAILURE);
+				if ((errno == ENOENT) && use_strftime) {
+					if (create_path (name) == 0) {
+						if ((fd = open64 (name, O_WRONLY | O_CREAT, 0644)) == -1) {
+							perror (name);
+							prg_exit ();
+							exit (EXIT_FAILURE);
+						} 
+					} else {
+						prg_exit ();
+						exit (EXIT_FAILURE);
+					}
+				} else {
+					perror(name);
+					prg_exit ();
+					exit(EXIT_FAILURE);
+				}
 			}
 			filecount++;
 		}
@@ -2521,6 +2686,8 @@ static void capture(char *orig_name)
 		rest = count;
 		if (rest > fmt_rec_table[file_type].max_filesize)
 			rest = fmt_rec_table[file_type].max_filesize;
+		if (max_file_size && (rest > max_file_size)) 
+			rest = max_file_size;
 
 		/* setup sample header */
 		if (fmt_rec_table[file_type].start)
@@ -2528,7 +2695,7 @@ static void capture(char *orig_name)
 
 		/* capture */
 		fdcount = 0;
-		while (rest > 0) {
+		while ((rest > 0) && (recycle_capture_file == 0)) {
 			size_t c = (rest <= (off64_t)chunk_bytes) ?
 				(size_t)rest : chunk_bytes;
 			size_t f = c * 8 / bits_per_frame;
@@ -2544,6 +2711,12 @@ static void capture(char *orig_name)
 			fdcount += c;
 		}
 
+		/* re-enable SIGUSR1 signal */
+		if (recycle_capture_file) {
+			recycle_capture_file = 0;
+			signal(SIGUSR1, signal_handler_recycle);
+		}
+
 		/* finish sample container */
 		if (fmt_rec_table[file_type].end && !tostdout) {
 			fmt_rec_table[file_type].end(fd);
-- 
1.6.3.3

From 1005710f0ea0bbfe3bb675357b042d1503bd04d7 Mon Sep 17 00:00:00 2001
From: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 8 Jan 2010 16:28:57 -0500
Subject: [PATCH - aplay 3/3] aplay -- update the man file

Bring the man file up to date, documenting the signals and all the
options, including those added for audio surveilance.

Signed-off-by: John Sauter <John_Sauter@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
---
 aplay/aplay.1 |   91 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/aplay/aplay.1 b/aplay/aplay.1
index 7759347..b6caf0b 100644
--- a/aplay/aplay.1
+++ b/aplay/aplay.1
@@ -1,4 +1,4 @@
-.TH APLAY 1 "2 August 2001"
+.TH APLAY 1 "1 January 2010"
 .SH NAME
 arecord, aplay \- command-line sound recorder and player for ALSA 
 soundcard driver
@@ -46,6 +46,7 @@ If this parameter is omitted the WAVE format is used.
 \fI\-c, \-\-channels=#\fP
 The number of channels.
 The default is one channel.
+Valid values are 1 through 32.
 .TP
 \fI\-f \-\-format=FORMAT\fP
 Sample format
@@ -53,19 +54,23 @@ Sample format
 Recognized sample formats are: S8 U8 S16_LE S16_BE U16_LE U16_BE S24_LE
 S24_BE U24_LE U24_BE S32_LE S32_BE U32_LE U32_BE FLOAT_LE FLOAT_BE
 FLOAT64_LE FLOAT64_BE IEC958_SUBFRAME_LE IEC958_SUBFRAME_BE MU_LAW
-A_LAW IMA_ADPCM MPEG GSM
+A_LAW IMA_ADPCM MPEG GSM SPECIAL S24_3LE S24_3BE U24_3LE U24_3BE S20_3LE
+S20_3BE U20_3LE U20_3BE S18_3LE S18_3BE U18_3LE
 .br
 Some of these may not be available on selected hardware
 .br
-There are also two format shortcuts available:
+The available format shortcuts are:
 .nf
-\-f cd (16 bit little endian, 44100, stereo [\-f S16_LE \-c2 \-r44100]
+\-f cd (16 bit little endian, 44100, stereo) [\-f S16_LE \-c2 \-r44100]
+\-f cdr (16 bit big endian, 44100, stereo) [\-f S16_BE \-c2 \-f44100]
 \-f dat (16 bit little endian, 48000, stereo) [\-f S16_LE \-c2 \-r48000]
 .fi
 If no format is given U8 is used.
 .TP
 \fI\-r, \-\-rate=#<Hz>\fP
 Sampling rate in Hertz. The default rate is 8000 Hertz.
+If the value specified is less than 300, it is taken as the rate in kilohertz.
+Valid values are 2000 through 192000 Hertz.
 .TP
 \fI\-d, \-\-duration=#\fP
 Interrupt after # seconds.
@@ -120,10 +125,69 @@ The stereo VU-meter is available only for 2-channel stereo samples
 with interleaved format.
 .TP
 \fI\-I, \-\-separate\-channels\fP 
-One file for each channel
+One file for each channel.  This option disables max\-file\-time
+and use\-strftime, and ignores SIGUSR1.  The stereo VU meter is
+not available with separate channels.
+.TP
+\fI\-P\fP
+Playback.  This is the default if the program is invoked
+by typing aplay.
+.TP
+\fI\-C\fP
+Record.  This is the default if the program is invoked
+by typing arecord.
+.TP
+\fI\-\-disable\-resample\fP
+Disable automatic rate resample.
+.TP
+\fI\-\-disable\-channels\fP
+Disable automatic channel conversions.
+.TP
+\fI\-\-disable\-format\fP
+Disable automatic format conversions.
+.TP
+\fI\-\-disable\-softvol\fP
+Disable software volume control (softvol).
+.TP
+\fI\-\-test\-position\fP
+Test ring buffer position.
+.TP
+\fI\-\-test\-coef=<coef>\fP
+Test coefficient for ring buffer position; default is 8.
+Expression for validation is: coef * (buffer_size / 2).
+Minimum value is 1.
+.TP
+\fI\-\-test\-nowait\fP
+Do not wait for the ring buffer--eats the whole CPU.
+.TP
+\fI\-\-max\-file\-time\fP
+While recording, when the output file has been accumulating
+sound for this long,
+close it and open a new output file.  Default is the maximum
+size supported by the file format: 2 GiB for WAV files.
+This option has no effect if  \-\-separate\-channels is
+specified.
+.TP
+\fI\-\-process\-id\-file <file name>\fP
+aplay writes its process ID here, so other programs can
+send signals to it.
+.TP
+\fI\-\-use\-strftime\fP
+When recording, interpret %-codes in the file name parameter using
+the strftime facility whenever the output file is opened.  The
+important strftime codes are: %Y is the year, %m month, %d day of
+the month, %H hour, %M minute and %S second.  In addition, %v is
+the file number, starting at 1.  When this option is specified,
+intermediate directories for the output file are created automatically.
+This option has no effect if \-\-separate\-channels is specified.
 
-.SS
-Example:
+.SH SIGNALS
+When recording, SIGINT, SIGTERM and SIGABRT will close the output 
+file and exit.  SIGUSR1 will close the output file, open a new one,
+and continue recording.  However, SIGUSR1 does not work with
+\-\-separate\-channels.
+
+.SH EXAMPLES
 
 .TP
 \fBaplay \-c 1 \-t raw \-r 22050 \-f mu_law foobar\fR
@@ -144,6 +208,19 @@ pcm.copy {
 }
 .fi
 
+.TP
+\fBarecord \-t wav \-max-file_time 30 mon.wav\fP
+Record from the default audio source in monaural, 8,000 samples
+per second, 8 bits per sample.  Start a new file every
+30 seconds.  File names are mon-nn.wav, where nn increases
+from 01.  The file after mon-99.wav is mon-100.wav.
+
+.TP
+\fBarecord \-f cd \-t wav \-max-file-time 3600 --use-strftime %Y/%m/%d/listen-%H-%M-%v.wav\fP
+Record in stereo from the default audio source.  Create a new file
+every hour.  The files are placed in directories based on their start dates
+and have names which include their start times and file numbers.
+
 .SH SEE ALSO
 \fB
 alsamixer(1),
-- 
1.6.3.3

Attachment: smime.p7s
Description: S/MIME cryptographic signature

_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux