diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /cmd/ossplay/ossplay.c | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'cmd/ossplay/ossplay.c')
-rw-r--r-- | cmd/ossplay/ossplay.c | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/cmd/ossplay/ossplay.c b/cmd/ossplay/ossplay.c new file mode 100644 index 0000000..feab4fa --- /dev/null +++ b/cmd/ossplay/ossplay.c @@ -0,0 +1,1254 @@ +/* + * Purpose: Sources for the ossplay audio player and for the ossrecord + * audio recorder shipped with OSS. + * + * Description: + * OSSPlay is a audio file player that supports most commonly used uncompressed + * audio formats (.wav, .snd, .au, .aiff). It doesn't play compressed formats + * such as MP3. + * OSSRecord is a simple file recorder. It can write simple file formats + * (.wav, .au, .aiff). + * + * This file contains the audio backend and misc. functions. + * + * This program is bit old and it uses some OSS features that may no longer be + * required. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "ossplay_decode.h" +#include "ossplay_parser.h" +#include "ossplay_wparser.h" + +#include <signal.h> +#include <strings.h> +#include <unistd.h> + +unsigned int amplification = 100; +int eflag = 0, force_speed = 0, force_fmt = 0, force_channels = 0, + overwrite = 1, verbose = 0, quiet = 0; +flag from_stdin = 0, int_conv = 0, level_meters = 0, loop = 0, + raw_file = 0, raw_mode = 0; +double seek_time = 0; +long seek_byte = 0; +off_t (*ossplay_lseek) (int, off_t, int) = lseek; + +char script[512] = ""; +unsigned int nfiles = 1; +double datalimit = 0; +fctypes_t type = WAVE_FILE; + +const format_t format_a[] = { + {"S8", AFMT_S8, CRP, AFMT_S16_NE}, + {"U8", AFMT_U8, CRP, AFMT_S16_NE}, + {"S16_LE", AFMT_S16_LE, CRP, AFMT_S16_NE}, + {"S16_BE", AFMT_S16_BE, CRP, AFMT_S16_NE}, + {"U16_LE", AFMT_U16_LE, CRP, AFMT_S16_NE}, + {"U16_BE", AFMT_U16_BE, CRP, AFMT_S16_NE}, + {"S24_LE", AFMT_S24_LE, CRP, 0}, + {"S24_BE", AFMT_S24_BE, CRP, 0}, + {"S32_LE", AFMT_S32_LE, CRP, AFMT_S32_NE}, + {"S32_BE", AFMT_S32_BE, CRP, AFMT_S32_NE}, + {"A_LAW", AFMT_A_LAW, CRP, AFMT_S16_NE}, + {"MU_LAW", AFMT_MU_LAW, CRP, AFMT_S16_NE}, + {"FLOAT32_LE", AFMT_FLOAT32_LE, CP, 0}, + {"FLOAT32_BE", AFMT_FLOAT32_BE, CP, 0}, + {"DOUBLE64_LE", AFMT_DOUBLE64_LE, CP, 0}, + {"DOUBLE64_BE", AFMT_DOUBLE64_BE, CP, 0}, + {"S24_PACKED", AFMT_S24_PACKED, CRP, 0}, + {"S24_PACKED_BE", AFMT_S24_PACKED_BE, CP, 0}, + {"IMA_ADPCM", AFMT_IMA_ADPCM, CP, 0}, + {"IMA_ADPCM_3BITS", AFMT_MS_IMA_ADPCM_3BITS,CP, 0}, + {"MS_ADPCM", AFMT_MS_ADPCM, CP, 0}, + {"CR_ADPCM_2", AFMT_CR_ADPCM_2, CP, 0}, + {"CR_ADPCM_3", AFMT_CR_ADPCM_3, CP, 0}, + {"CR_ADPCM_4", AFMT_CR_ADPCM_4, CP, 0}, + {"SPDIF_RAW", AFMT_SPDIF_RAW, CR, 0}, + {"FIBO_DELTA", AFMT_FIBO_DELTA, CP, 0}, + {"EXP_DELTA", AFMT_EXP_DELTA, CP, 0}, + {NULL, 0, CP, 0} +}; + +static const container_t container_a[] = { + {"RAW", RAW_FILE, AFMT_S16_LE, 2, 44100}, + {"WAV", WAVE_FILE, AFMT_S16_LE, 2, 48000}, + {"AU", AU_FILE, AFMT_MU_LAW, 1, 8000}, + {"AIFF", AIFF_FILE, AFMT_S16_BE, 2, 48000}, + {"CAF", CAF_FILE, AFMT_S16_NE, 2, 48000}, + {NULL, RAW_FILE, 0, 0, 0} +}; /* Order should match fctypes_t enum so that container_a[type] works */ + +static void describe_error (void); +static void find_devname (char *, const char *); +static fctypes_t select_container (const char *); +static int select_format (const char *, int); +static void ossplay_usage (const char *); +static void ossrecord_usage (const char *); +static void ossplay_getint (int); +static void print_play_verbose_info (const unsigned char *, ssize_t, void *); +static void print_record_verbose_info (const unsigned char *, ssize_t, void *); + +big_t +be_int (const unsigned char * p, int l) +{ + int i; + big_t val; + + val = 0; + + for (i = 0; i < l; i++) + { + val = (val << 8) | p[i]; + } + + return val; +} + +big_t +le_int (const unsigned char * p, int l) +{ + int i; + big_t val; + + val = 0; + + for (i = l - 1; i >= 0; i--) + { + val = (val << 8) | p[i]; + } + + return val; +} + +static void +describe_error (void) +{ + switch (errno) + { + case ENXIO: + case ENODEV: + print_msg (ERRORM, "\nThe device file was found in /dev but\n" + "there is no driver for it currently loaded.\n" + "\n" + "You can start it by executing the soundon command as\n" + "super user (root).\n"); + break; + + case ENOSPC: + print_msg (ERRORM, "\nThe soundcard driver was not installed\n" + "properly. The system is out of DMA compatible memory.\n" + "Please reboot your system and try again.\n"); + + break; + + case ENOENT: + print_msg (ERRORM, "\nThe sound device file is missing from /dev.\n" + "You should try re-installing OSS.\n"); + break; + + case EBUSY: + print_msg (ERRORM, + "\nThere is some other application using this audio device.\n" + "Exit it and try again.\n"); + print_msg (ERRORM, + "You can possibly find out the conflicting application by" + "looking\n", + "at the printout produced by command 'ossinfo -a -v1'\n"); + break; + + default:; + } +} + +static void +find_devname (char * devname, const char * num) +{ +/* + * OSS 4.0 the audio device numbering may be different from the + * legacy /dev/dsp# numbering reported by /dev/sndstat. Try to find the + * device name (devnode) that matches the given device number. + * + * Prior versions of ossplay simply used the the /dev/dsp# number. + */ + int dev; + int mixer_fd; + oss_audioinfo ai; + const char * devmixer; + + if ((devmixer = getenv("OSS_MIXERDEV")) == NULL) + devmixer = "/dev/mixer"; + + if (sscanf (num, "%d", &dev) != 1) + { + print_msg (ERRORM, "Invalid audio device number '%s'\n", num); + exit (E_SETUP_ERROR); + } + + if ((mixer_fd = open (devmixer, O_RDWR, 0)) == -1) + { + perror_msg (devmixer); + print_msg (WARNM, "Warning: Defaulting to /dev/dsp%s\n", num); + snprintf (devname, OSS_DEVNODE_SIZE, "/dev/dsp%s", num); + return; + } + + ai.dev = dev; + + if (ioctl (mixer_fd, SNDCTL_AUDIOINFO, &ai) == -1) + { + perror_msg ("SNDCTL_AUDIOINFO"); + print_msg (WARNM, "Warning: Defaulting to /dev/dsp%s\n", num); + snprintf (devname, OSS_DEVNODE_SIZE, "/dev/dsp%s", num); + close (mixer_fd); + return; + } + + strncpy (devname, ai.devnode, OSS_DEVNODE_SIZE); + + close (mixer_fd); + return; +} + +const char * +filepart (const char *name) +{ + const char * s = name; + + if (name == NULL) return ""; + + while (*name) + { + if (name[0] == '/' && name[1] != '\0') + s = name + 1; + name++; + } + + return s; +} + +float +format2bits (int format) +{ + switch (format) + { + case AFMT_CR_ADPCM_2: return 2; + case AFMT_CR_ADPCM_3: return 2.6666F; + case AFMT_MS_IMA_ADPCM_3BITS: return 3; + case AFMT_CR_ADPCM_4: + case AFMT_MAC_IMA_ADPCM: + case AFMT_MS_IMA_ADPCM: + case AFMT_IMA_ADPCM: + case AFMT_MS_ADPCM: + case AFMT_FIBO_DELTA: + case AFMT_EXP_DELTA: return 4; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: return 8; + case AFMT_VORBIS: + case AFMT_MPEG: + case AFMT_S16_LE: + case AFMT_S16_BE: + case AFMT_U16_LE: + case AFMT_U16_BE: return 16; + case AFMT_S24_PACKED: + case AFMT_S24_PACKED_BE: return 24; + case AFMT_S24_LE: + case AFMT_S24_BE: + case AFMT_SPDIF_RAW: + case AFMT_FLOAT32_LE: + case AFMT_FLOAT32_BE: + case AFMT_S32_LE: + case AFMT_S32_BE: return 32; + case AFMT_DOUBLE64_LE: + case AFMT_DOUBLE64_BE: return 64; + case AFMT_FLOAT: return sizeof (float) * 8; + case AFMT_QUERY: + default: return 0; + } +} + +void +close_device (dspdev_t * dsp) +{ + if (dsp->fd == -1) return; + close (dsp->fd); + dsp->fd = -1; +} + +void +open_device (dspdev_t * dsp) +{ + const char * devdsp; + + if (dsp->fd >= 0) + close_device (dsp); + + dsp->format = 0; dsp->channels = 0; dsp->speed = 0; + + if ((devdsp = getenv("OSS_AUDIODEV")) == NULL) + devdsp = "/dev/dsp"; + + if (raw_mode) + dsp->flags |= O_EXCL; /* Disable redirection to the virtual mixer */ + + if (dsp->dname[0] == '\0') strcpy (dsp->dname, devdsp); + + if ((dsp->fd = open (dsp->dname, dsp->flags, 0)) == -1) + { + perror_msg (dsp->dname); + describe_error (); + exit (E_SETUP_ERROR); + } + + if (raw_mode) + { + /* + * Disable sample rate/format conversions. + */ + int tmp = 0; + ioctl (dsp->fd, SNDCTL_DSP_COOKEDMODE, &tmp); + } +} + +static void +ossplay_usage (const char * prog) +{ + print_msg (HELPM, "Usage: %s [options] filename(s)\n", prog?prog:"ossplay"); + print_msg (HELPM, " Options: -v Verbose output.\n"); + print_msg (HELPM, " -q No informative printouts.\n"); + print_msg (HELPM, " -d<devname> Change output device.\n"); + print_msg (HELPM, " -g<gain> Change gain.\n"); + print_msg (HELPM, " -s<rate> Change playback rate.\n"); + print_msg (HELPM, " -f<fmt>|? Change/Query input format.\n"); + print_msg (HELPM, " -c<channels> Change number of channels.\n"); + print_msg (HELPM, " -o<playtgt>|? Select/Query output target.\n"); + print_msg (HELPM, " -l Loop playback indefinitely.\n"); + print_msg (HELPM, " -W Treat all input as raw PCM.\n"); + print_msg (HELPM, " -S<secs> Start playing from offset.\n"); + print_msg (HELPM, + " -R Open sound device in raw mode.\n"); + exit (E_USAGE); +} + +static void +ossrecord_usage (const char * prog) +{ + print_msg (HELPM, "Usage: %s [options] filename\n", prog?prog:"ossrecord"); + print_msg (HELPM, " Options: -v Verbose output.\n"); + print_msg (HELPM, " -d<device> Change input device.\n"); + print_msg (HELPM, " -c<channels> Change number of channels\n"); + print_msg (HELPM, " -L<level> Change recording level.\n"); + print_msg (HELPM, + " -g<gain> Change gain percentage.\n"); + print_msg (HELPM, " -s<rate> Change recording rate.\n"); + print_msg (HELPM, " -f<fmt|?> Change/Query sample format.\n"); + print_msg (HELPM, + " -F<cnt|?> Change/Query container format.\n"); + print_msg (HELPM, " -l Display level meters.\n"); + print_msg (HELPM, + " -i<recsrc|?> Select/Query recording source.\n"); + print_msg (HELPM, + " -m<nfiles> Repeat recording <nfiles> times.\n"); + print_msg (HELPM, + " -r<command> Run <command> after recording.\n"); + print_msg (HELPM, + " -t<maxsecs> Record no more than <maxsecs> in a" + " single recording.\n"); + print_msg (HELPM, + " -R Open sound device in raw mode.\n"); + print_msg (HELPM, " -O Do not allow overwrite.\n"); + exit (E_USAGE); +} + +const char * +sample_format_name (int sformat) +{ + int i; + + for (i = 0; format_a[i].fmt != 0; i++) + if (format_a[i].fmt == sformat) + return format_a[i].name; + + return ""; +} + +static fctypes_t +select_container (const char * optstr) +{ +/* + * Handling of the -F command line option (force container format). + * + * Empty or "?" shows the supported container format names. + */ + int i; + + if ((!strcmp(optstr, "?")) || (*optstr == '\0')) + { + print_msg (STARTM, "\nSupported container format names are:\n\n"); + for (i = 0; container_a[i].name != NULL; i++) + print_msg (CONTM, "%s ", container_a[i].name); + print_msg (ENDM, "\n"); + exit (0); + } + + for (i = 0; container_a[i].name != NULL; i++) + if (!strcasecmp(container_a[i].name, optstr)) + return container_a[i].type; + + print_msg (ERRORM, "Unsupported container format name '%s'!\n", optstr); + exit (E_USAGE); +} + +static int +select_format (const char * optstr, int dir) +{ +/* + * Handling of the -f command line option (force input format). + * + * Empty or "?" shows the supported format names. + */ + int i; + + if ((!strcmp(optstr, "?")) || (*optstr == '\0')) + { + print_msg (STARTM, "\nSupported format names are:\n\n"); + for (i = 0; format_a[i].name != NULL; i++) + if (dir & format_a[i].dir) + print_msg (CONTM, "%s ", format_a[i].name); + print_msg (ENDM, "\n"); + exit (0); + } + + for (i = 0; format_a[i].name != NULL; i++) + if ((format_a[i].dir & dir) && (!strcasecmp(format_a[i].name, optstr))) + return format_a[i].fmt; + + print_msg (ERRORM, "Unsupported format name '%s'!\n", optstr); + exit (E_USAGE); +} + +void +select_playtgt (dspdev_t * dsp) +{ +/* + * Handling of the -o command line option (playback target selection). + * + * Empty or "?" shows the available playback sources. + */ + int i, src; + oss_mixer_enuminfo ei; + + if (ioctl (dsp->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &ei) == -1) + { + perror_msg ("SNDCTL_DSP_GET_PLAYTGT_NAMES"); + exit (E_SETUP_ERROR); + } + + if (ioctl (dsp->fd, SNDCTL_DSP_GET_PLAYTGT, &src) == -1) + { + perror_msg ("SNDCTL_DSP_GET_PLAYTGT"); + exit (E_SETUP_ERROR); + } + + if ((dsp->playtgt[0] == '\0') || (strcmp (dsp->playtgt, "?") == 0)) + { + print_msg (STARTM, + "\nPossible playback targets for the selected device:\n\n"); + + for (i = 0; i < ei.nvalues; i++) + { + print_msg (CONTM, "\t%s", ei.strings + ei.strindex[i]); + if (i == src) + print_msg (CONTM, " (currently selected)"); + print_msg (CONTM, "\n"); + } + print_msg (ENDM, "\n"); + exit (0); + } + + for (i = 0; i < ei.nvalues; i++) + { + char *s = ei.strings + ei.strindex[i]; + if (strcmp (s, dsp->playtgt) == 0) + { + src = i; + if (ioctl (dsp->fd, SNDCTL_DSP_SET_PLAYTGT, &src) == -1) + { + perror_msg ("SNDCTL_DSP_SET_PLAYTGT"); + exit (E_SETUP_ERROR); + } + + return; + } + } + + print_msg (ERRORM, + "Unknown playback target name '%s' - use -o? to get the list\n", + dsp->playtgt); + exit (E_USAGE); +} + +void +select_recsrc (dspdev_t * dsp) +{ +/* + * Handling of the -i command line option (recording source selection). + * + * Empty or "?" shows the available recording sources. + */ + int i, src; + oss_mixer_enuminfo ei; + + if (ioctl (dsp->fd, SNDCTL_DSP_GET_RECSRC_NAMES, &ei) == -1) + { + perror_msg ("SNDCTL_DSP_GET_RECSRC_NAMES"); + exit (E_SETUP_ERROR); + } + + if (ioctl (dsp->fd, SNDCTL_DSP_GET_RECSRC, &src) == -1) + { + perror_msg ("SNDCTL_DSP_GET_RECSRC"); + exit (E_SETUP_ERROR); + } + + if (dsp->recsrc[0] == '\0' || strcmp (dsp->recsrc, "?") == 0) + { + print_msg (STARTM, + "\nPossible recording sources for the selected device:\n\n"); + + for (i = 0; i < ei.nvalues; i++) + { + print_msg (CONTM, "\t%s", ei.strings + ei.strindex[i]); + if (i == src) + print_msg (CONTM, " (currently selected)"); + print_msg (CONTM, "\n"); + } + print_msg (ENDM, "\n"); + exit (0); + } + + for (i = 0; i < ei.nvalues; i++) + { + char *s = ei.strings + ei.strindex[i]; + if (strcmp (s, dsp->recsrc) == 0) + { + src = i; + if (ioctl (dsp->fd, SNDCTL_DSP_SET_RECSRC, &src) == -1) + { + perror_msg ("SNDCTL_DSP_SET_RECSRC"); + exit (E_SETUP_ERROR); + } + return; + } + } + + print_msg (ERRORM, + "Unknown recording source name '%s' - use -i? to get the list\n", + dsp->recsrc); + exit (E_USAGE); +} + +errors_t +setup_device (dspdev_t * dsp, int format, int channels, int speed) +{ + int tmp; + + if (dsp->speed != speed || dsp->format != format || + dsp->channels != channels || dsp->fd == -1) + { +#if 0 + ioctl (dsp->fd, SNDCTL_DSP_SYNC, NULL); + ioctl (dsp->fd, SNDCTL_DSP_HALT, NULL); +#else + close_device (dsp); + open_device (dsp); + if (dsp->playtgt != NULL) select_playtgt (dsp); + if (dsp->recsrc != NULL) select_recsrc (dsp); +#endif + } + else + { + ioctl (dsp->fd, SNDCTL_SETSONG, dsp->current_songname); + return E_OK; + } + + /* + * Report the current filename as the song name. + */ + ioctl (dsp->fd, SNDCTL_SETSONG, dsp->current_songname); + + tmp = APF_NORMAL; + ioctl (dsp->fd, SNDCTL_DSP_PROFILE, &tmp); + + tmp = format; + + if (ioctl (dsp->fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror_msg (dsp->dname); + print_msg (ERRORM, "Failed to select bits/sample\n"); + return E_SETUP_ERROR; + } + + if (tmp != format) + { + print_msg (ERRORM, "%s doesn't support this audio format (%x/%x).\n", + dsp->dname, format, tmp); + return E_FORMAT_UNSUPPORTED; + } + + tmp = channels; + + if (ioctl (dsp->fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror_msg (dsp->dname); + print_msg (ERRORM, "Failed to select number of channels.\n"); + return E_SETUP_ERROR; + } + + if (tmp != channels) + { +#ifdef SRC_SUPPORT + /* We'll convert mono to stereo, so it's no use warning */ + if ((channels != 1) || (tmp != 2)) +#endif + print_msg (ERRORM, "%s doesn't support %d channels (%d).\n", + dsp->dname, channels, tmp); + return E_CHANNELS_UNSUPPORTED; + } + + tmp = speed; + + if (ioctl (dsp->fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror_msg (dsp->dname); + print_msg (ERRORM, "Failed to select sampling rate.\n"); + return E_SETUP_ERROR; + } + +#ifndef SRC_SUPPORT + if (tmp != speed) + { + print_msg (WARNM, "Warning: Playback using %d Hz (file %d Hz)\n", + tmp, speed); + } +#endif + + dsp->speed = tmp; + dsp->channels = channels; + dsp->format = format; + + if (verbose > 1) + print_msg (VERBOSEM, "Setup device %s/%d/%d\n", + sample_format_name (dsp->format), dsp->channels, dsp->speed); + + if (dsp->reclevel != 0) + { + tmp = dsp->reclevel | (dsp->reclevel << 8); + + if (ioctl (dsp->fd, SNDCTL_DSP_SETRECVOL, &tmp) == -1) + perror ("SNDCTL_DSP_SETRECVOL"); + } + + return E_OK; +} + +static void +ossplay_getint (int signum) +{ +#if 0 + if (eflag == signum + 128) + { + signal (signum, SIG_DFL); + raise (signum); + } +#endif + eflag = signum + 128; +} + +int +ossplay_parse_opts (int argc, char ** argv, dspdev_t * dsp) +{ + extern char * optarg; + extern int optind; + char * p; + int c; + + while ((c = getopt (argc, argv, "FRS:Wc:d:f:g:hlo:qs:v")) != EOF) + { + switch (c) + { + case 'v': + verbose++; + quiet = 0; + int_conv = 2; + break; + + case 'R': + raw_mode = 1; + break; + + case 'q': + quiet++; + verbose = 0; + if (int_conv == 2) int_conv = 0; + break; + + case 'd': + if (*optarg >= '0' && *optarg <= '9') /* Only device number given */ + find_devname (dsp->dname, optarg); + else + snprintf (dsp->dname, OSS_DEVNODE_SIZE, "%s", optarg); + break; + + case 'o': + if (!strcmp(optarg, "?")) + { + dsp->playtgt = optarg; + dsp->flags = O_WRONLY; + open_device (dsp); + select_playtgt (dsp); + } + dsp->playtgt = optarg; + break; + + case 'f': + force_fmt = select_format (optarg, CP); + break; + + case 's': + sscanf (optarg, "%d", &force_speed); + break; + + case 'c': + sscanf (optarg, "%d", &force_channels); + break; + + case 'g': + sscanf (optarg, "%u", &lification); + int_conv = 1; + break; + + case 'l': + loop = 1; + break; + + case 'F': + case 'W': + raw_file = 1; + break; + + case 'S': + c = strlen (optarg); + if ((c > 0) && ((optarg[c - 1] == 'b') || (optarg[c - 1] == 'B'))) + { + errno = 0; + seek_byte = strtol (optarg, &p, 10); + if ((*p != '\0') || (seek_byte < 0)) ossplay_usage (argv[0]); + } + else + { + errno = 0; + seek_time = strtod (optarg, &p); + if ((*p != '\0') || (errno) || (seek_time < 0)) ossplay_usage (argv[0]); + } + break; + + default: + ossplay_usage (argv[0]); + } + + } + + if (argc < optind + 1) + ossplay_usage (argv[0]); + +#ifdef SIGQUIT + signal (SIGQUIT, ossplay_getint); +#endif + return optind; +} + +int +ossrecord_parse_opts (int argc, char ** argv, dspdev_t * dsp) +{ + char * p; + int c; + extern char * optarg; + extern int optind; + + if (argc < 2) + ossrecord_usage (argv[0]); + + while ((c = getopt (argc, argv, "F:L:MORSb:c:d:f:g:hi:lm:r:s:t:wv")) != EOF) + switch (c) + { + case 'F': + type = select_container (optarg); + break; + + case 'L': + dsp->reclevel = atoi (optarg); + if (dsp->reclevel < 1 || dsp->reclevel > 100) + { + print_msg (ERRORM, "%s: Bad recording level '%s'\n", + argv[0]?argv[0]:"", optarg); + exit (-1); + } + break; + + case 'M': + force_channels = 1; + break; + + case 'R': + raw_mode = 1; + break; + + case 'S': + force_channels = 2; + break; + + case 'b': + c = atoi (optarg); + c += c % 8; /* Simple WAV format always pads to a multiple of 8 */ + switch (c) + { + case 8: force_fmt = AFMT_U8; break; + case 16: force_fmt = AFMT_S16_LE; break; + case 24: force_fmt = AFMT_S24_PACKED; break; + case 32: force_fmt = AFMT_S32_LE; break; + default: + print_msg (ERRORM, "Error: Unsupported number of bits %d\n", c); + exit (E_FORMAT_UNSUPPORTED); + } + break; + + case 'c': + sscanf (optarg, "%d", &force_channels); + break; + + case 'd': + if (*optarg >= '0' && *optarg <= '9') /* Only device number given */ + find_devname (dsp->dname, optarg); + else + snprintf (dsp->dname, OSS_DEVNODE_SIZE, "%s", optarg); + break; + + case 'f': + force_fmt = select_format (optarg, CR); + break; + + case 'g': + sscanf (optarg, "%u", &lification); + if (amplification == 0) ossrecord_usage (argv[0]); + + case 'l': + level_meters = 1; + verbose = 1; + break; + + case 'i': + if (!strcmp(optarg, "?")) + { + dsp->recsrc = optarg; + dsp->flags = O_RDONLY; + open_device (dsp); + select_recsrc (dsp); + } + dsp->recsrc = optarg; + break; + + case 'm': + sscanf (optarg, "%u", &nfiles); + break; + + case 's': + sscanf (optarg, "%d", &force_speed); + if (force_speed == 0) + { + print_msg (ERRORM, "Bad sampling rate given\n"); + exit (E_USAGE); + } + if (force_speed < 1000) force_speed *= 1000; + break; + + case 'r': + c = snprintf (script, sizeof (script), "%s", optarg); + if (((size_t)c >= sizeof (script)) || (c < 0)) + { + print_msg (ERRORM, "-r argument is too long!\n"); + exit (E_USAGE); + } + break; + + case 't': + errno = 0; + datalimit = strtod (optarg, &p); + if ((*p != '\0') || (errno) || (datalimit <= 0)) ossrecord_usage (argv[0]); + break; + + case 'O': + overwrite = 0; + break; + + case 'w': + break; + + case 'v': + verbose = 1; + break; + + case 'h': + default: + ossrecord_usage (argv[0]); + } + + if (argc != optind + 1) + /* No file or multiple file names given */ + ossrecord_usage (argv[0]); + + if (force_fmt == 0) force_fmt = container_a[type].dformat; + if (force_channels == 0) force_channels = container_a[type].dchannels; + if (force_speed == 0) force_speed = container_a[type].dspeed; + switch (force_fmt) + { + case AFMT_S8: + case AFMT_U8: + case AFMT_S16_NE: + case AFMT_S24_NE: + case AFMT_S32_NE: break; + default: level_meters = 0; /* Not implemented */ + } + + if ((signal (SIGSEGV, ossplay_getint) == SIG_ERR) || +#ifdef SIGPIPE + (signal (SIGPIPE, ossplay_getint) == SIG_ERR) || +#endif + (signal (SIGTERM, ossplay_getint) == SIG_ERR) || +#ifdef SIGQUIT + (signal (SIGQUIT, ossplay_getint) == SIG_ERR) || +#endif + (signal (SIGINT, ossplay_getint) == SIG_ERR)) + print_msg (WARNM, "Signal handler not set up!\n"); + + if (verbose) + { + oss_audioinfo ai; + + ai.dev = -1; + + if (ioctl(dsp->fd, SNDCTL_ENGINEINFO, &ai) != -1) + print_msg (VERBOSEM, "Recording from %s\n", ai.name); + } + + return optind; +} + +ldouble_t +ossplay_ldexpl (ldouble_t num, int exp) +{ + /* + * Very simple emulation of ldexpl to avoid linking to libm or assuming + * anything about float representation. + */ + if (exp > 0) + { + while (exp > 31) + { + num *= 1UL << 31; + exp -= 31; + } + num *= 1UL << exp; + } + else if (exp < 0) + { + while (exp < -31) + { + num /= 1UL << 31; + exp += 31; + } + num /= 1UL << -exp; + } + + return num; +} + +static void +print_play_verbose_info (const unsigned char * buf, ssize_t l, void * metadata) +{ +/* + * Display a rough recording level meter, and the elapsed time. + */ + + verbose_values_t * val = (verbose_values_t *)metadata; + + val->secs += l/val->constant; + if (val->secs < val->next_sec) return; + val->next_sec += PLAY_UPDATE_INTERVAL/1000; + /* + * This check is done to ensure an update at the end of the playback. + * Note that some files lie about total time, so the second condition is + * necessary so that updates will still be constricted by PLAY_UPDATE_INTERVAL. + */ + if ((val->next_sec > val->tsecs) && (val->secs < val->tsecs)) val->next_sec = val->tsecs; + + print_update (get_db_level (buf, l, val->format), val->secs, val->tstring); + + return; +} + +static void +print_record_verbose_info (const unsigned char * buf, ssize_t l, + void * metadata) +{ +/* + * Display a rough recording level meter if enabled, and the elapsed time. + */ + + verbose_values_t * val = (verbose_values_t *)metadata; + int update_dots = 1; + + val->secs += l / val->constant; + + if (val->secs >= val->next_sec) + { + val->next_sec += REC_UPDATE_INTERVAL/1000; + if ((val->tsecs) && (val->next_sec > val->tsecs)) + val->next_sec = val->tsecs; + if (level_meters) + { + val->secs_timer2 = val->next_sec_timer2 = val->secs; + goto print_level; + } + print_record_update (-1, val->secs, val->tstring, 1); + } + else if ((level_meters) && (val->secs >= val->next_sec_timer2)) + { + update_dots = 0; +print_level: + val->next_sec_timer2 += LMETER_UPDATE_INTERVAL/1000; + if ((val->tsecs) && (val->next_sec_timer2 > val->tsecs)) + val->next_sec_timer2 = val->tsecs; + print_record_update (get_db_level (buf, l, val->format), val->secs_timer2, + val->tstring, update_dots); + } +} + +int +play (dspdev_t * dsp, int fd, big_t * datamark, big_t bsize, double total_time, + double constant, readfunc_t * readf, decoders_queue_t * dec, seekfunc_t * seekf) +{ +#define EXITPLAY(code) \ + do { \ + ossplay_free (buf); \ + ossplay_free (verbose_meta); \ + clear_update (); \ + ioctl (dsp->fd, SNDCTL_DSP_HALT_OUTPUT, NULL); \ + errno = 0; \ + return (code); \ + } while (0) + + big_t rsize = bsize; + big_t filesize = *datamark; + ssize_t outl; + unsigned char * buf, * obuf, contflag = 0; + decoders_queue_t * d; + verbose_values_t * verbose_meta = NULL; + + buf = (unsigned char *)ossplay_malloc (bsize); + + if (verbose) + { + verbose_meta = setup_verbose (dsp->format, + format2bits(dsp->format) * dsp->channels * + dsp->speed / 8.0, total_time); + if (seek_time == 0) print_play_verbose_info (NULL, 0, verbose_meta); + } + + *datamark = 0; + + while (*datamark < filesize) + { + if (eflag) EXITPLAY (eflag); + + rsize = bsize; + if (rsize > filesize - *datamark) rsize = filesize - *datamark; + + if ((seek_time != 0) && (seekf != NULL)) + { + errors_t ret; + + ret = seekf (fd, datamark, filesize, constant, rsize, dsp->channels, + dec->metadata); + if (ret == E_OK) + { + if (verbose) + { + verbose_meta->secs = (double)seek_time; + verbose_meta->next_sec = (double)seek_time; + print_play_verbose_info (NULL, 0, verbose_meta); + } + seek_time = 0; + continue; + } + else if (ret == SEEK_CONT_AFTER_DECODE) contflag = 1; + else EXITPLAY (ret); + } + + if ((outl = readf (fd, buf, rsize, dec->metadata)) <= 0) + { + if (errno) perror_msg ("read"); + if ((filesize != BIG_SPECIAL) && (*datamark < filesize) && (!eflag)) + { + print_msg (NOTIFYM, "Sound data ended prematurely!\n"); + } + EXITPLAY (eflag); + } + *datamark += outl; + + if (contflag) + { + contflag = 0; + continue; + } + + obuf = buf; d = dec; + do + { + outl = d->decoder (&(d->outbuf), obuf, outl, d->metadata); + obuf = d->outbuf; + d = d->next; + } + while (d != NULL); + + if (verbose) print_play_verbose_info (obuf, outl, verbose_meta); + if (write (dsp->fd, obuf, outl) == -1) + { + if ((errno == EINTR) && (eflag)) EXITPLAY (eflag); + ossplay_free (buf); + perror_msg ("audio write"); + exit (E_DECODE); + } + } + + ossplay_free (buf); + ossplay_free (verbose_meta); + clear_update (); + return 0; +} + +int +record (dspdev_t * dsp, FILE * wave_fp, const char * filename, double constant, + double datatime, big_t * data_size, decoders_queue_t * dec) +{ +#define EXITREC(code) \ + do { \ + ossplay_free (buf); \ + ossplay_free (verbose_meta); \ + clear_update (); \ + if ((eflag) && (verbose)) \ + print_msg (VERBOSEM, "\nStopped (%d).\n", eflag-128); \ + ioctl (dsp->fd, SNDCTL_DSP_HALT_INPUT, NULL); \ + return (code); \ + } while(0) + + unsigned char * buf, * obuf; + ssize_t l, outl; + big_t data_size_limit = *data_size; + decoders_queue_t * d; + verbose_values_t * verbose_meta = NULL; + + if (verbose) + { + verbose_meta = setup_verbose (dsp->format, constant, datatime); + strncpy (verbose_meta->tstring, filename, 20)[19] = 0; + } + + *data_size = 0; + buf = (unsigned char *)ossplay_malloc (RECBUF_SIZE); + /*LINTED*/ while (1) + { + if ((l = read (dsp->fd, buf, RECBUF_SIZE)) < 0) + { + if ((errno == EINTR) && (eflag)) EXITREC (eflag); + if (errno == ECONNRESET) EXITREC (E_ENCODE); /* Device disconnected */ + perror_msg (dsp->dname); + EXITREC (E_ENCODE); + } + if (l == 0) + { + print_msg (ERRORM, "Unexpected EOF on audio device\n"); + EXITREC (eflag); + } + + obuf = buf; d = dec; outl = l; + do + { + outl = d->decoder (&(d->outbuf), obuf, outl, d->metadata); + obuf = d->outbuf; + d = d->next; + } + while (d != NULL); + + if (eflag) EXITREC (eflag); + + if (fwrite (obuf, outl, 1, wave_fp) != 1) + { + if ((errno == EINTR) && (eflag)) EXITREC (eflag); + perror_msg (filename); + EXITREC (E_ENCODE); + } + + *data_size += outl; + if (verbose) print_record_verbose_info (obuf, outl, verbose_meta); + + if ((datalimit != 0) && (*data_size >= data_size_limit)) break; + } + + ossplay_free (buf); + ossplay_free (verbose_meta); + clear_update (); + print_msg (VERBOSEM, "\nDone.\n"); + return 0; +} + +errors_t +silence (dspdev_t * dsp, big_t len, int speed) +{ + errors_t ret; + ssize_t i; + unsigned char empty[1024]; + + ret = setup_device (dsp, AFMT_U8, 1, speed); + + if (ret == E_FORMAT_UNSUPPORTED) + { + len *= 4; + if ((ret = setup_device (dsp, AFMT_S16_NE, 2, speed))) return ret; + } + else if (ret) return ret; + + memset (empty, 0, 1024 * sizeof (unsigned char)); + + while (len > 0) + { + i = 1024; + if ((big_t)i > len) i = len; + if ((i = write (dsp->fd, empty, i)) < 0) return -1; + + len -= i; + } + + return E_OK; +} |