From c44e75708b045131967ab8b1dc78917b5e87f412 Mon Sep 17 00:00:00 2001
From: John Sauter <John_Sauter@systemeyescomputerstore.com>
Date: Fri, 1 Jan 2010 16:36:22 -0500
Subject: [PATCH - aplay extensions for audio surveilance 1/1] aplay - add features for audio surveilance.

When doing audio surveilance, you want to record
all the sound, but occasionally close the old
output file and open a new one, so you can listen
to a captured event without disturbing the
recording of future sounds.  I have added three
new options and a new signal to facilitate this:

--max-file-time specifies the maximum time to
record in a file before closing it and opening
a new one.

--process-id-file specifies the file into which to
write the process ID, so a controlling program can
send signals to aplay.

Signal SIGUSR1 causes aplay to close the current
output file and open a new one.

--use-strftime causes the file name parameter to
be passed through strftime each time a file is
opened.  The important strftime codes for this
application are as follows: %Y is the year,
%m is the month, %d is the day of the month,
%H is the hour, %M the minute, %S the second.
In addition, %v is the file number.  Using
strftime replaces the default file naming
mechanism, which places the file number after
the file name, if there is more than one
output file.  Using strftime also causes
intermediate directories to be created for
the output files.  This allows for names like
sounds/%Y/%m/%d/%Y-%m-%d--%H_%M_%S.wav.

I have updated the manual file to include
all the old options and signals in addition to
those listed above.

Signed-Off-By: John Sauter <John_Sauter@systemeyescomputerstore.com>
---
 aplay/aplay.1 |   91 ++++++++++++++++--
 aplay/aplay.c |  308 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 367 insertions(+), 32 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),
diff --git a/aplay/aplay.c b/aplay/aplay.c
index 22a5fe0..8566d03 100644
--- a/aplay/aplay.c
+++ b/aplay/aplay.c
@@ -111,11 +111,19 @@ 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;
 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 +202,11 @@ _("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"
+"    --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) {
@@ -345,9 +357,17 @@ static void signal_handler(int sig)
 		snd_pcm_close(handle);
 		handle = NULL;
 	}
+	if (pidfile_written) remove (pidfile_name);
 	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,
@@ -358,7 +378,10 @@ enum {
 	OPT_DISABLE_SOFTVOL,
 	OPT_TEST_POSITION,
 	OPT_TEST_COEF,
-	OPT_TEST_NOWAIT
+	OPT_TEST_NOWAIT,
+	OPT_MAX_FILE_TIME,
+	OPT_PROCESS_ID_FILE,
+	OPT_USE_STRFTIME
 };
 
 int main(int argc, char *argv[])
@@ -399,6 +422,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";
@@ -493,6 +519,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);
+					if (pidfile_written) remove (pidfile_name);
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -588,6 +615,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;
@@ -643,10 +679,24 @@ 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);
 	signal(SIGABRT, signal_handler);
+	signal(SIGUSR1, signal_handler_recycle);
 	if (interleaved) {
 		if (optind > argc - 1) {
 			if (stream == SND_PCM_STREAM_PLAYBACK)
@@ -674,6 +724,7 @@ int main(int argc, char *argv[])
       __end:
 	snd_output_close(log);
 	snd_config_update_free_global();
+	if (pidfile_written) remove (pidfile_name);
 	return EXIT_SUCCESS;
 }
 
@@ -699,7 +750,7 @@ static ssize_t safe_read(int fd, void *buf, size_t count)
 
 /*
  * Test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
- *                                       < 0 if not 
+ *				       < 0 if not 
  */
 static int test_vocfile(void *buffer)
 {
@@ -725,6 +776,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);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	return *size = reqsize;
@@ -735,13 +787,14 @@ 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"));		  \
+			if (pidfile_written) remove (pidfile_name);  \
 			exit(EXIT_FAILURE); \
 		} \
 	}
 
 /*
  * test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.)
- *                            == 0 if not
+ *			    == 0 if not
  * Value returned is bytes to be discarded.
  */
 static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size)
@@ -784,6 +837,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));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	check_wavefile_space(buffer, len, blimit);
@@ -794,21 +848,25 @@ 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));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		if (memcmp(fe->guid_tag, WAV_GUID_TAG, 14) != 0) {
 			error(_("wrong format tag in extensible 'fmt ' chunk"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		f->format = fe->guid_format;
 	}
-        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));
+	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));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (LE_SHORT(f->channels) < 1) {
 		error(_("can't play WAVE-files with %d tracks"), LE_SHORT(f->channels));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	hwparams.channels = LE_SHORT(f->channels);
@@ -842,18 +900,20 @@ 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);
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		break;
 	case 32:
-                if (LE_SHORT(f->format) == WAV_FMT_PCM)
-                        hwparams.format = SND_PCM_FORMAT_S32_LE;
-                else if (LE_SHORT(f->format) == WAV_FMT_IEEE_FLOAT)
-                        hwparams.format = SND_PCM_FORMAT_FLOAT_LE;
+		if (LE_SHORT(f->format) == WAV_FMT_PCM)
+			hwparams.format = SND_PCM_FORMAT_S32_LE;
+		else if (LE_SHORT(f->format) == WAV_FMT_IEEE_FLOAT)
+			hwparams.format = SND_PCM_FORMAT_FLOAT_LE;
 		break;
 	default:
 		error(_(" can't play WAVE-files with sample %d bits wide"),
 		      LE_SHORT(f->bit_p_spl));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	hwparams.rate = LE_INT(f->sample_fq);
@@ -936,6 +996,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"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	return 0;
@@ -966,6 +1027,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"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (mmap_flag) {
@@ -983,17 +1045,20 @@ static void set_params(void)
 						   SND_PCM_ACCESS_RW_NONINTERLEAVED);
 	if (err < 0) {
 		error(_("Access type not available"));
+		if (pidfile_written) remove (pidfile_name);
 		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);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels);
 	if (err < 0) {
 		error(_("Channels count non available"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 
@@ -1052,6 +1117,7 @@ static void set_params(void)
 	if (err < 0) {
 		error(_("Unable to install hw params:"));
 		snd_pcm_hw_params_dump(params, log);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
@@ -1059,6 +1125,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);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	snd_pcm_sw_params_current(handle, swparams);
@@ -1090,6 +1157,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);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 
@@ -1102,6 +1170,7 @@ static void set_params(void)
 	audiobuf = realloc(audiobuf, chunk_bytes);
 	if (audiobuf == NULL) {
 		error(_("not enough memory"));
+		if (pidfile_written) remove (pidfile_name);
 		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 +1189,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));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		for (i = 0; i < hwparams.channels; i++)
@@ -1164,6 +1234,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));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
@@ -1194,6 +1265,7 @@ static void xrun(void)
 		}
 		if ((res = snd_pcm_prepare(handle))<0) {
 			error(_("xrun: prepare error: %s"), snd_strerror(res));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		return;		/* ok, data should be accepted again */
@@ -1206,6 +1278,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));
+				if (pidfile_written) remove (pidfile_name);
 				exit(EXIT_FAILURE);
 			}
 			return;
@@ -1216,6 +1289,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)));
+	if (pidfile_written) remove (pidfile_name);
 	exit(EXIT_FAILURE);
 }
 
@@ -1233,6 +1307,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));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -1538,6 +1613,7 @@ static ssize_t pcm_write(u_char *data, size_t count)
 			suspend();
 		} else if (r < 0) {
 			error(_("write error: %s"), snd_strerror(r));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1584,6 +1660,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));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1627,6 +1704,7 @@ static ssize_t pcm_read(u_char *data, size_t rcount)
 			suspend();
 		} else if (r < 0) {
 			error(_("read error: %s"), snd_strerror(r));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1670,6 +1748,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));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		if (r > 0) {
@@ -1727,6 +1806,7 @@ static void voc_write_silence(unsigned x)
 			l = chunk_size;
 		if (voc_pcm_write(buf, l) != (ssize_t)l) {
 			error(_("write error"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		x -= l;
@@ -1769,6 +1849,7 @@ static void voc_play(int fd, int ofs, char *name)
 	buffer_pos = 0;
 	if (data == NULL) {
 		error(_("malloc error"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (!quiet_mode) {
@@ -1778,6 +1859,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"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		ofs -= chunk_bytes;
@@ -1785,6 +1867,7 @@ static void voc_play(int fd, int ofs, char *name)
 	if (ofs) {
 		if (safe_read(fd, buf, ofs) != ofs) {
 			error(_("read error"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -1809,6 +1892,7 @@ static void voc_play(int fd, int ofs, char *name)
 				nextblock = buf[0] = 0;
 				if (l == -1) {
 					perror(name);
+					if (pidfile_written) remove (pidfile_name);
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -1953,11 +2037,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"));
+					if (pidfile_written) remove (pidfile_name);
 					exit(EXIT_FAILURE);
 				}
 			} else {
 				if (voc_pcm_write(data, l) != l) {
 					error(_("write error"));
+					if (pidfile_written) remove (pidfile_name);
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -1965,8 +2051,8 @@ static void voc_play(int fd, int ofs, char *name)
 		}
 	}			/* while(1) */
       __end:
-        voc_pcm_flush();
-        free(buf);
+	voc_pcm_flush();
+	free(buf);
 }
 /* that was a big one, perhaps somebody split it :-) */
 
@@ -2005,6 +2091,7 @@ static void begin_voc(int fd, size_t cnt)
 
 	if (write(fd, &vh, sizeof(VocHeader)) != sizeof(VocHeader)) {
 		error(_("write error"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (hwparams.channels > 1) {
@@ -2014,6 +2101,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"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 		eb.tc = LE_SHORT(65536 - 256000000L / (hwparams.rate << 1));
@@ -2021,6 +2109,7 @@ static void begin_voc(int fd, size_t cnt)
 		eb.mode = 1;
 		if (write(fd, &eb, sizeof(VocExtBlock)) != sizeof(VocExtBlock)) {
 			error(_("write error"));
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -2031,12 +2120,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"));
+		if (pidfile_written) remove (pidfile_name);
 		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"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2064,7 +2155,7 @@ static void begin_wave(int fd, size_t cnt)
 		bits = 16;
 		break;
 	case SND_PCM_FORMAT_S32_LE:
-        case SND_PCM_FORMAT_FLOAT_LE:
+	case SND_PCM_FORMAT_FLOAT_LE:
 		bits = 32;
 		break;
 	case SND_PCM_FORMAT_S24_LE:
@@ -2073,6 +2164,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));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	h.magic = WAV_RIFF;
@@ -2083,10 +2175,10 @@ static void begin_wave(int fd, size_t cnt)
 	cf.type = WAV_FMT;
 	cf.length = LE_INT(16);
 
-        if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
-                f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
-        else
-                f.format = LE_SHORT(WAV_FMT_PCM);
+	if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
+		f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
+	else
+		f.format = LE_SHORT(WAV_FMT_PCM);
 	f.channels = LE_SHORT(hwparams.channels);
 	f.sample_fq = LE_INT(hwparams.rate);
 #if 0
@@ -2109,6 +2201,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"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2133,12 +2226,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));
+		if (pidfile_written) remove (pidfile_name);
 		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"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 }
@@ -2153,6 +2248,7 @@ static void end_voc(int fd)
 
 	if (write(fd, &dummy, 1) != 1) {
 		error(_("write error"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	length_seek = sizeof(VocHeader);
@@ -2261,6 +2357,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);
+				if (pidfile_written) remove (pidfile_name);
 				exit(EXIT_FAILURE);
 			}
 			fdcount += r;
@@ -2300,6 +2397,7 @@ static void playback(char *name)
 	} else {
 		if ((fd = open64(name, O_RDONLY, 0)) == -1) {
 			perror(name);
+			if (pidfile_written) remove (pidfile_name);
 			exit(EXIT_FAILURE);
 		}
 	}
@@ -2307,6 +2405,7 @@ static void playback(char *name)
 	dta = sizeof(AuHeader);
 	if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
 		error(_("read error"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if (test_au(fd, audiobuf) >= 0) {
@@ -2319,6 +2418,7 @@ static void playback(char *name)
 	if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader),
 		 dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) {
 		error(_("read error"));
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 	if ((ofs = test_vocfile(audiobuf)) >= 0) {
@@ -2341,13 +2441,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");
+			if (pidfile_written) remove (pidfile_name);
+			exit (EXIT_FAILURE);
+		}
+		if (mystrftime (namebuf, namelen, name, tmp, filecount+1) == 0) {
+			if (pidfile_written) remove (pidfile_name);
+			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 */
@@ -2379,6 +2566,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 */
@@ -2391,6 +2616,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)
@@ -2417,7 +2644,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);
@@ -2427,8 +2654,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);
-				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);
+							if (pidfile_written) remove (pidfile_name);
+							exit (EXIT_FAILURE);
+						} 
+					} else {
+						if (pidfile_written) remove (pidfile_name);
+						exit (EXIT_FAILURE);
+					}
+				} else {
+					perror(name);
+					if (pidfile_written) remove (pidfile_name);
+					exit(EXIT_FAILURE);
+				}
 			}
 			filecount++;
 		}
@@ -2436,6 +2677,7 @@ 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)
@@ -2443,7 +2685,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;
@@ -2451,6 +2693,7 @@ static void capture(char *orig_name)
 				break;
 			if (write(fd, audiobuf, c) != c) {
 				perror(name);
+				if (pidfile_written) remove (pidfile_name);
 				exit(EXIT_FAILURE);
 			}
 			count -= c;
@@ -2458,6 +2701,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);
@@ -2498,11 +2747,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]);
+				if (pidfile_written) remove (pidfile_name);
 				exit(EXIT_FAILURE);
 			}
 			for (channel = 1; channel < channels; ++channel) {
 				if (safe_read(fds[channel], bufs[channel], r) != r) {
 					perror(names[channel]);
+					if (pidfile_written) remove (pidfile_name);
 					exit(EXIT_FAILURE);
 				}
 			}
@@ -2550,6 +2801,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]);
+				if (pidfile_written) remove (pidfile_name);
 				exit(EXIT_FAILURE);
 			}
 		}
@@ -2583,6 +2835,7 @@ static void playbackv(char **names, unsigned int count)
 		alloced = 1;
 	} else if (count != channels) {
 		error(_("You need to specify %d files"), channels);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 
@@ -2608,8 +2861,10 @@ static void playbackv(char **names, unsigned int count)
 	}
 	if (alloced)
 		free(names);
-	if (ret)
+	if (ret) {
+		if (pidfile_written) remove (pidfile_name);
 		exit(ret);
+	}
 }
 
 static void capturev(char **names, unsigned int count)
@@ -2636,6 +2891,7 @@ static void capturev(char **names, unsigned int count)
 		alloced = 1;
 	} else if (count != channels) {
 		error(_("You need to specify %d files"), channels);
+		if (pidfile_written) remove (pidfile_name);
 		exit(EXIT_FAILURE);
 	}
 
@@ -2661,6 +2917,8 @@ static void capturev(char **names, unsigned int count)
 	}
 	if (alloced)
 		free(names);
-	if (ret)
+	if (ret) {
+		if (pidfile_written) remove (pidfile_name);
 		exit(ret);
+	}
 }
-- 
1.6.3.3

