diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c index 02e082f..3b5dfda 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c @@ -189,5 +189,5 @@ int main(int argc, char *argv[]) } snd_config_update_free_global(); - return res < 0 ? res : 0; + return res < 0 ? -res : 0; } diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h index 89ad295..be90efb 100644 --- a/alsactl/alsactl.h +++ b/alsactl/alsactl.h @@ -34,16 +34,16 @@ extern char *statefile; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) #define cerror(cond, ...) do {\ - if (cond) { \ - fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ + if (cond || debugflag) { \ + fprintf(stderr, "%s%s: %s:%d: ", debugflag ? "WARNING: " : "", command, __FUNCTION__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ putc('\n', stderr); \ } \ } while (0) #else #define cerror(cond, args...) do {\ - if (cond) { \ - fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \ + if (cond || debugflag) { \ + fprintf(stderr, "%s%s: %s:%d: ", debugflag ? "WARNING: " : "", command, __FUNCTION__, __LINE__); \ fprintf(stderr, ##args); \ putc('\n', stderr); \ } \ @@ -78,7 +78,7 @@ int generate_names(const char *cfgfile); int file_map(const char *filename, char **buf, size_t *bufsize); void file_unmap(void *buf, size_t bufsize); size_t line_width(const char *buf, size_t bufsize, size_t pos); -void initfailed(int cardnumber, const char *reason); +void initfailed(int cardnumber, const char *reason, int exitcode); static inline int hextodigit(int c) { diff --git a/alsactl/state.c b/alsactl/state.c index 635a999..e70c6f9 100644 --- a/alsactl/state.c +++ b/alsactl/state.c @@ -1117,7 +1117,6 @@ static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info, } snd_ctl_elem_value_set_byte(ctl, idx, val); return 1; - break; default: break; } @@ -1404,6 +1403,7 @@ static int set_controls(int card, snd_config_t *top, int doit) snd_ctl_card_info_alloca(&info); sprintf(name, "hw:%d", card); + dbg("device='%s', doit=%i", name, doit); err = snd_ctl_open(&handle, name, 0); if (err < 0) { error("snd_ctl_open error: %s", snd_strerror(err)); @@ -1415,6 +1415,7 @@ static int set_controls(int card, snd_config_t *top, int doit) goto _close; } id = snd_ctl_card_info_get_id(info); + dbg("card-info-id: '%s'", id); err = snd_config_searchv(top, &control, "state", id, "control", 0); if (err < 0) { if (force_restore) { @@ -1440,24 +1441,25 @@ static int set_controls(int card, snd_config_t *top, int doit) goto _close; } + dbg("maxnumid=%i", maxnumid); /* check if we have additional controls in driver */ /* in this case we should go through init procedure */ if (!doit && maxnumid >= 0) { - snd_ctl_elem_id_t *id; snd_ctl_elem_info_t *info; - snd_ctl_elem_id_alloca(&id); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_info_set_numid(info, maxnumid+1); if (snd_ctl_elem_info(handle, info) == 0) { /* not very informative */ /* but value is used for check only */ err = -EAGAIN; + dbg("more controls than maxnumid?"); goto _close; } } _close: snd_ctl_close(handle); + dbg("result code: %i", err); return err; } @@ -1582,9 +1584,9 @@ int load_state(const char *file, const char *initfile, const char *cardname, err = init(initfile, cardname1); if (err < 0) { finalerr = err; - initfailed(card, "init"); + initfailed(card, "init", err); } - initfailed(card, "restore"); + initfailed(card, "restore", -ENOENT); } if (first) finalerr = 0; /* no cards, no error code */ @@ -1617,14 +1619,14 @@ int load_state(const char *file, const char *initfile, const char *cardname, sprintf(cardname1, "%i", card); err = init(initfile, cardname1); if (err < 0) { - initfailed(card, "init"); + initfailed(card, "init", err); finalerr = err; } } if ((err = set_controls(card, config, 1))) { if (!force_restore) finalerr = err; - initfailed(card, "restore"); + initfailed(card, "restore", err); } } } else { @@ -1639,12 +1641,12 @@ int load_state(const char *file, const char *initfile, const char *cardname, if (do_init && set_controls(cardno, config, 0)) { err = init(initfile, cardname); if (err < 0) { - initfailed(cardno, "init"); - return err; + initfailed(cardno, "init", err); + finalerr = err; } } if ((err = set_controls(cardno, config, 1))) { - initfailed(cardno, "restore"); + initfailed(cardno, "restore", err); if (!force_restore) return err; } diff --git a/alsactl/utils.c b/alsactl/utils.c index ab4dbd4..a27eb6e 100644 --- a/alsactl/utils.c +++ b/alsactl/utils.c @@ -79,19 +79,23 @@ size_t line_width(const char *buf, size_t bufsize, size_t pos) return count - pos; } -void initfailed(int cardnumber, const char *reason) +void initfailed(int cardnumber, const char *reason, int exitcode) { int fp; char *str; + char sexitcode[16]; if (statefile == NULL) return; if (snd_card_get_name(cardnumber, &str) < 0) return; + sprintf(sexitcode, "%i", exitcode); fp = open(statefile, O_WRONLY|O_CREAT|O_APPEND, 0644); write(fp, str, strlen(str)); write(fp, ":", 1); write(fp, reason, strlen(reason)); + write(fp, ":", 1); + write(fp, sexitcode, strlen(sexitcode)); write(fp, "\n", 1); close(fp); free(str); diff --git a/alsamixer/mixer_display.c b/alsamixer/mixer_display.c index 9eadcc9..20d6d6a 100644 --- a/alsamixer/mixer_display.c +++ b/alsamixer/mixer_display.c @@ -390,6 +390,15 @@ static void display_string_centered_in_control(int y, int col, const char *s, in display_string_in_field(y, x, s, width, ALIGN_CENTER); } +static long clamp(long value, long min, long max) +{ + if (value < min) + return min; + if (value > max) + return max; + return value; +} + static void display_control(unsigned int control_index) { struct control *control; @@ -462,6 +471,10 @@ static void display_control(unsigned int control_index) err = snd_mixer_selem_get_capture_volume_range(control->elem, &min, &max); if (err < 0) return; + if (min >= max) + max = min + 1; + volumes[0] = clamp(volumes[0], min, max); + volumes[1] = clamp(volumes[1], min, max); if (control->flags & IS_ACTIVE) wattrset(mixer_widget.window, 0); diff --git a/amidi/amidi.c b/amidi/amidi.c index 2e970ae..cedf18c 100644 --- a/amidi/amidi.c +++ b/amidi/amidi.c @@ -95,122 +95,63 @@ static void *my_malloc(size_t size) return p; } -static int is_input(snd_ctl_t *ctl, int card, int device, int sub) -{ - snd_rawmidi_info_t *info; - int err; - - snd_rawmidi_info_alloca(&info); - snd_rawmidi_info_set_device(info, device); - snd_rawmidi_info_set_subdevice(info, sub); - snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); - - if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0 && err != -ENXIO) - return err; - else if (err == 0) - return 1; - - return 0; -} - -static int is_output(snd_ctl_t *ctl, int card, int device, int sub) -{ - snd_rawmidi_info_t *info; - int err; - - snd_rawmidi_info_alloca(&info); - snd_rawmidi_info_set_device(info, device); - snd_rawmidi_info_set_subdevice(info, sub); - snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); - - if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0 && err != -ENXIO) - return err; - else if (err == 0) - return 1; - - return 0; -} - static void list_device(snd_ctl_t *ctl, int card, int device) { snd_rawmidi_info_t *info; const char *name; const char *sub_name; int subs, subs_in, subs_out; - int sub, in, out; + int sub; int err; snd_rawmidi_info_alloca(&info); snd_rawmidi_info_set_device(info, device); snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); - snd_ctl_rawmidi_info(ctl, info); - subs_in = snd_rawmidi_info_get_subdevices_count(info); + err = snd_ctl_rawmidi_info(ctl, info); + if (err >= 0) + subs_in = snd_rawmidi_info_get_subdevices_count(info); + else + subs_in = 0; + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); - snd_ctl_rawmidi_info(ctl, info); - subs_out = snd_rawmidi_info_get_subdevices_count(info); - subs = subs_in > subs_out ? subs_in : subs_out; + err = snd_ctl_rawmidi_info(ctl, info); + if (err >= 0) + subs_out = snd_rawmidi_info_get_subdevices_count(info); + else + subs_out = 0; - sub = 0; - in = out = 0; - if ((err = is_output(ctl, card, device, sub)) < 0) { - error("cannot get rawmidi information %d:%d: %s", - card, device, snd_strerror(err)); + subs = subs_in > subs_out ? subs_in : subs_out; + if (!subs) return; - } else if (err) - out = 1; - if (err == 0) { - if ((err = is_input(ctl, card, device, sub)) < 0) { - error("cannot get rawmidi information %d:%d: %s", - card, device, snd_strerror(err)); + for (sub = 0; sub < subs; ++sub) { + snd_rawmidi_info_set_stream(info, sub < subs_in ? + SND_RAWMIDI_STREAM_INPUT : + SND_RAWMIDI_STREAM_OUTPUT); + snd_rawmidi_info_set_subdevice(info, sub); + err = snd_ctl_rawmidi_info(ctl, info); + if (err < 0) { + error("cannot get rawmidi information %d:%d:%d: %s\n", + card, device, sub, snd_strerror(err)); return; } - } else if (err) - in = 1; - - if (err == 0) - return; - - name = snd_rawmidi_info_get_name(info); - sub_name = snd_rawmidi_info_get_subdevice_name(info); - if (sub_name[0] == '\0') { - if (subs == 1) { - printf("%c%c hw:%d,%d %s\n", - in ? 'I' : ' ', out ? 'O' : ' ', + name = snd_rawmidi_info_get_name(info); + sub_name = snd_rawmidi_info_get_subdevice_name(info); + if (sub == 0 && sub_name[0] == '\0') { + printf("%c%c hw:%d,%d %s", + sub < subs_in ? 'I' : ' ', + sub < subs_out ? 'O' : ' ', card, device, name); - } else - printf("%c%c hw:%d,%d %s (%d subdevices)\n", - in ? 'I' : ' ', out ? 'O' : ' ', - card, device, name, subs); - } else { - sub = 0; - for (;;) { + if (subs > 1) + printf(" (%d subdevices)", subs); + putchar('\n'); + break; + } else { printf("%c%c hw:%d,%d,%d %s\n", - in ? 'I' : ' ', out ? 'O' : ' ', + sub < subs_in ? 'I' : ' ', + sub < subs_out ? 'O' : ' ', card, device, sub, sub_name); - if (++sub >= subs) - break; - - in = is_input(ctl, card, device, sub); - out = is_output(ctl, card, device, sub); - snd_rawmidi_info_set_subdevice(info, sub); - if (out) { - snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); - if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0) { - error("cannot get rawmidi information %d:%d:%d: %s", - card, device, sub, snd_strerror(err)); - break; - } - } else { - snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); - if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0) { - error("cannot get rawmidi information %d:%d:%d: %s", - card, device, sub, snd_strerror(err)); - break; - } - } - sub_name = snd_rawmidi_info_get_subdevice_name(info); } } } diff --git a/amixer/amixer.c b/amixer/amixer.c index 9620721..c9ea572 100644 --- a/amixer/amixer.c +++ b/amixer/amixer.c @@ -534,6 +534,26 @@ static void decode_tlv(unsigned int spaces, unsigned int *tlv, unsigned int tlv_ } break; #endif +#ifdef SND_CTL_TLVT_DB_MINMAX + case SND_CTL_TLVT_DB_MINMAX: + case SND_CTL_TLVT_DB_MINMAX_MUTE: + if (type == SND_CTL_TLVT_DB_MINMAX_MUTE) + printf("dBminmaxmute-"); + else + printf("dBminmax-"); + if (size != 2 * sizeof(unsigned int)) { + while (size > 0) { + printf("0x%08x,", tlv[idx++]); + size -= sizeof(unsigned int); + } + } else { + printf("min="); + print_dB(tlv[2]); + printf(",max="); + print_dB(tlv[3]); + } + break; +#endif default: printf("unk-%i-", type); while (size > 0) { @@ -602,6 +622,8 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, break; } if (level & LEVEL_BASIC) { + if (!snd_ctl_elem_info_is_readable(info)) + goto __skip_read; if ((err = snd_hctl_elem_read(elem, control)) < 0) { error("Control %s element read error: %s\n", card, snd_strerror(err)); return err; @@ -638,6 +660,7 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, } } printf("\n"); + __skip_read: if (!snd_ctl_elem_info_is_tlv_readable(info)) goto __skip_tlv; tlv = malloc(4096); 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=#\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=\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 \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..e1d8e6a 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) { @@ -324,6 +336,18 @@ static void version(void) printf("%s: version " SND_UTIL_VERSION_STR " by Jaroslav Kysela \n", command); } +/* + * Subroutine to clean up before exit. + */ +static void prg_exit(int code) +{ + if (handle) + snd_pcm_close(handle); + if (pidfile_written) + remove (pidfile_name); + exit(code); +} + static void signal_handler(int sig) { if (verbose==2) @@ -345,7 +369,14 @@ static void signal_handler(int sig) snd_pcm_close(handle); handle = NULL; } - exit(EXIT_FAILURE); + prg_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 { @@ -358,7 +389,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 +433,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,7 +530,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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } break; @@ -588,6 +625,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 +689,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) @@ -670,10 +730,13 @@ int main(int argc, char *argv[]) if (verbose==2) putchar('\n'); snd_pcm_close(handle); + handle = NULL; free(audiobuf); __end: snd_output_close(log); snd_config_update_free_global(); + prg_exit(EXIT_SUCCESS); + /* avoid warning */ return EXIT_SUCCESS; } @@ -725,7 +788,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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } return *size = reqsize; } @@ -735,7 +798,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")); \ - exit(EXIT_FAILURE); \ + prg_exit(EXIT_FAILURE); \ } \ } @@ -784,7 +847,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } check_wavefile_space(buffer, len, blimit); test_wavefile_read(fd, buffer, &size, len, __LINE__); @@ -794,22 +857,22 @@ 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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (memcmp(fe->guid_tag, WAV_GUID_TAG, 14) != 0) { error(_("wrong format tag in extensible 'fmt ' chunk")); - exit(EXIT_FAILURE); + prg_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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (LE_SHORT(f->channels) < 1) { error(_("can't play WAVE-files with %d tracks"), LE_SHORT(f->channels)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } hwparams.channels = LE_SHORT(f->channels); switch (LE_SHORT(f->bit_p_spl)) { @@ -842,7 +905,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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } break; case 32: @@ -854,7 +917,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } hwparams.rate = LE_INT(f->sample_fq); @@ -936,7 +999,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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } return 0; } @@ -966,7 +1029,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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (mmap_flag) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); @@ -983,18 +1046,18 @@ static void set_params(void) SND_PCM_ACCESS_RW_NONINTERLEAVED); if (err < 0) { error(_("Access type not available")); - exit(EXIT_FAILURE); + prg_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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels); if (err < 0) { error(_("Channels count non available")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } #if 0 @@ -1052,14 +1115,14 @@ static void set_params(void) if (err < 0) { error(_("Unable to install hw params:")); snd_pcm_hw_params_dump(params, log); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { error(_("Can't use period equal to buffer size (%lu == %lu)"), chunk_size, buffer_size); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); if (avail_min < 0) @@ -1090,7 +1153,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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (verbose) @@ -1102,7 +1165,7 @@ static void set_params(void) audiobuf = realloc(audiobuf, chunk_bytes); if (audiobuf == NULL) { error(_("not enough memory")); - exit(EXIT_FAILURE); + prg_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,7 +1183,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } for (i = 0; i < hwparams.channels; i++) fprintf(stderr, "mmap_area[%i] = %p,%u,%u (%u)\n", i, areas[i].addr, areas[i].first, areas[i].step, snd_pcm_format_physical_width(hwparams.format)); @@ -1164,7 +1227,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { if (monotonic) { @@ -1194,7 +1257,7 @@ static void xrun(void) } if ((res = snd_pcm_prepare(handle))<0) { error(_("xrun: prepare error: %s"), snd_strerror(res)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } return; /* ok, data should be accepted again */ } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) { @@ -1206,7 +1269,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } return; } @@ -1216,7 +1279,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))); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } /* I/O suspend handler */ @@ -1233,7 +1296,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } if (!quiet_mode) @@ -1538,7 +1601,7 @@ static ssize_t pcm_write(u_char *data, size_t count) suspend(); } else if (r < 0) { error(_("write error: %s"), snd_strerror(r)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (r > 0) { if (vumeter) @@ -1584,7 +1647,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (r > 0) { if (vumeter) { @@ -1627,7 +1690,7 @@ static ssize_t pcm_read(u_char *data, size_t rcount) suspend(); } else if (r < 0) { error(_("read error: %s"), snd_strerror(r)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (r > 0) { if (vumeter) @@ -1670,7 +1733,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (r > 0) { if (vumeter) { @@ -1727,7 +1790,7 @@ static void voc_write_silence(unsigned x) l = chunk_size; if (voc_pcm_write(buf, l) != (ssize_t)l) { error(_("write error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } x -= l; } @@ -1769,7 +1832,7 @@ static void voc_play(int fd, int ofs, char *name) buffer_pos = 0; if (data == NULL) { error(_("malloc error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (!quiet_mode) { fprintf(stderr, _("Playing Creative Labs Channel file '%s'...\n"), name); @@ -1778,14 +1841,14 @@ 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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } ofs -= chunk_bytes; } if (ofs) { if (safe_read(fd, buf, ofs) != ofs) { error(_("read error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } hwparams.format = DEFAULT_FORMAT; @@ -1809,7 +1872,7 @@ static void voc_play(int fd, int ofs, char *name) nextblock = buf[0] = 0; if (l == -1) { perror(name); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } } @@ -1953,12 +2016,12 @@ 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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } else { if (voc_pcm_write(data, l) != l) { error(_("write error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } COUNT(l); @@ -2005,7 +2068,7 @@ static void begin_voc(int fd, size_t cnt) if (write(fd, &vh, sizeof(VocHeader)) != sizeof(VocHeader)) { error(_("write error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (hwparams.channels > 1) { /* write an extended block */ @@ -2014,14 +2077,14 @@ 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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } eb.tc = LE_SHORT(65536 - 256000000L / (hwparams.rate << 1)); eb.pack = 0; eb.mode = 1; if (write(fd, &eb, sizeof(VocExtBlock)) != sizeof(VocExtBlock)) { error(_("write error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } bt.type = 1; @@ -2031,13 +2094,13 @@ 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")); - exit(EXIT_FAILURE); + prg_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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } @@ -2073,7 +2136,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)); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } h.magic = WAV_RIFF; tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8; @@ -2109,7 +2172,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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } @@ -2133,13 +2196,13 @@ 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)); - exit(EXIT_FAILURE); + prg_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")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } @@ -2153,7 +2216,7 @@ static void end_voc(int fd) if (write(fd, &dummy, 1) != 1) { error(_("write error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } length_seek = sizeof(VocHeader); if (hwparams.channels > 1) @@ -2261,7 +2324,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); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } fdcount += r; if (r == 0) @@ -2300,14 +2363,14 @@ static void playback(char *name) } else { if ((fd = open64(name, O_RDONLY, 0)) == -1) { perror(name); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } /* read the file header */ dta = sizeof(AuHeader); if ((size_t)safe_read(fd, audiobuf, dta) != dta) { error(_("read error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } if (test_au(fd, audiobuf) >= 0) { rhwparams.format = hwparams.format; @@ -2319,7 +2382,7 @@ static void playback(char *name) if ((size_t)safe_read(fd, audiobuf + sizeof(AuHeader), dta - sizeof(AuHeader)) != dta - sizeof(AuHeader)) { error(_("read error")); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE);; } if ((ofs = test_vocfile(audiobuf)) >= 0) { pbrec_count = calc_count(); @@ -2341,13 +2404,98 @@ 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_FAILURE); + } + if (mystrftime(namebuf, namelen, name, tmp, filecount+1) == 0) { + fprintf(stderr, "mystrftime returned 0"); + prg_exit(EXIT_FAILURE); + } + return filecount; + } + /* get a copy of the original filename */ strncpy(buf, name, sizeof(buf)); /* separate extension from filename */ @@ -2379,6 +2527,58 @@ 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 int safe_open(const char *name) +{ + int fd; + + fd = open64(name, O_WRONLY | O_CREAT, 0644); + if (fd == -1) { + if (errno != ENOENT || !use_strftime) + return -1; + if (create_path(name) == 0) + fd = open64(name, O_WRONLY | O_CREAT, 0644); + } + return fd; +} + static void capture(char *orig_name) { int tostdout=0; /* boolean which describes output stream */ @@ -2391,6 +2591,10 @@ 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 +2621,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); @@ -2426,9 +2630,10 @@ static void capture(char *orig_name) /* open a new file */ remove(name); - if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) { + fd = safe_open(name); + if (fd < 0) { perror(name); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } filecount++; } @@ -2436,6 +2641,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) @@ -2443,7 +2650,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,13 +2658,19 @@ static void capture(char *orig_name) break; if (write(fd, audiobuf, c) != c) { perror(name); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } count -= c; rest -= c; 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,12 +2711,12 @@ 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]); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } for (channel = 1; channel < channels; ++channel) { if (safe_read(fds[channel], bufs[channel], r) != r) { perror(names[channel]); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } if (r == 0) @@ -2550,7 +2763,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]); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } } r = r * bits_per_frame / 8; @@ -2583,7 +2796,7 @@ static void playbackv(char **names, unsigned int count) alloced = 1; } else if (count != channels) { error(_("You need to specify %d files"), channels); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } for (channel = 0; channel < channels; ++channel) { @@ -2609,7 +2822,7 @@ static void playbackv(char **names, unsigned int count) if (alloced) free(names); if (ret) - exit(ret); + prg_exit(ret); } static void capturev(char **names, unsigned int count) @@ -2636,7 +2849,7 @@ static void capturev(char **names, unsigned int count) alloced = 1; } else if (count != channels) { error(_("You need to specify %d files"), channels); - exit(EXIT_FAILURE); + prg_exit(EXIT_FAILURE); } for (channel = 0; channel < channels; ++channel) { @@ -2662,5 +2875,5 @@ static void capturev(char **names, unsigned int count) if (alloced) free(names); if (ret) - exit(ret); + prg_exit(ret); } diff --git a/speaker-test/speaker-test.c b/speaker-test/speaker-test.c index 053ed3b..d8d68e2 100644 --- a/speaker-test/speaker-test.c +++ b/speaker-test/speaker-test.c @@ -689,6 +689,7 @@ static int write_loop(snd_pcm_t *handle, int channel, int periods, uint8_t *fram double phase = 0; int err, n; + fflush(stdout); if (test_type == TEST_WAV) { int bufsize = snd_pcm_frames_to_bytes(handle, period_size); n = 0;