summaryrefslogtreecommitdiff
path: root/cmd/ossplay
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ossplay')
-rw-r--r--cmd/ossplay/.config3
-rw-r--r--cmd/ossplay/ossplay.c1254
-rw-r--r--cmd/ossplay/ossplay.h291
-rw-r--r--cmd/ossplay/ossplay.man53
-rw-r--r--cmd/ossplay/ossplay_console.c397
-rw-r--r--cmd/ossplay/ossplay_console.h37
-rw-r--r--cmd/ossplay/ossplay_decode.c1783
-rw-r--r--cmd/ossplay/ossplay_decode.h30
-rw-r--r--cmd/ossplay/ossplay_parser.c1830
-rw-r--r--cmd/ossplay/ossplay_parser.h8
-rw-r--r--cmd/ossplay/ossplay_wparser.c358
-rw-r--r--cmd/ossplay/ossplay_wparser.h21
12 files changed, 6065 insertions, 0 deletions
diff --git a/cmd/ossplay/.config b/cmd/ossplay/.config
new file mode 100644
index 0000000..5c1fcde
--- /dev/null
+++ b/cmd/ossplay/.config
@@ -0,0 +1,3 @@
+cflags=$OGGDEFINE
+ldflags=$DLOPENLDFLAGS
+forgetos=VxWorks
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", &amplification);
+ 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", &amplification);
+ 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;
+}
diff --git a/cmd/ossplay/ossplay.h b/cmd/ossplay/ossplay.h
new file mode 100644
index 0000000..226ad5c
--- /dev/null
+++ b/cmd/ossplay/ossplay.h
@@ -0,0 +1,291 @@
+#ifndef OSSPLAY_H
+#define OSSPLAY_H
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <soundcard.h>
+#include "ossplay_console.h"
+
+#define PLAYBUF_SIZE 1024
+#define RECBUF_SIZE 512
+/* Parser's buf length */
+#define P_READBUF_SIZE 1024
+#define DEFAULT_CHANNELS 1
+#define DEFAULT_FORMAT AFMT_U8
+#define DEFAULT_SPEED 11025
+#define MAX_CHANNELS 128
+/*
+ * Every update of output in verbose mode while playing is separated by at
+ * least PLAY_UPDATE_INTERVAL milliseconds.
+ */
+#define PLAY_UPDATE_INTERVAL 200.0
+/* As above, but for recording */
+#define REC_UPDATE_INTERVAL 1000.0
+/* As above, but used for level meters while recording */
+#define LMETER_UPDATE_INTERVAL 20.0
+/* Should be smaller than the above. Used to ensure an update at end of file */
+#define UPDATE_EPSILON 1.0
+
+/* Sanity check - no allocation by ossplay should pass this. */
+#define OSSPLAY_MAX_MALLOC 32*1024*1024
+
+#if !defined(OSS_NO_INTTYPES_H) && !defined(OSS_NO_LONG_LONG)
+#define __STDC_LIMIT_MACROS
+#include <inttypes.h>
+
+typedef long double ldouble_t;
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef char flag;
+typedef intptr_t intptr;
+#define S32_MAX INT32_MAX
+#define S32_MIN INT32_MIN
+#define U32_MAX UINT32_MAX
+typedef uintmax_t big_t;
+typedef intmax_t sbig_t;
+#define _PRIbig_t "%ju"
+#define BIG_SPECIAL UINTMAX_MAX
+
+#else
+#ifdef OSS_NO_LONG_LONG
+typedef long sbig_t;
+typedef unsigned long big_t;
+#define _PRIbig_t "%lu"
+#define BIG_SPECIAL ULONG_MAX
+#else
+typedef long long sbig_t;
+typedef unsigned long long big_t;
+#define _PRIbig_t "%llu"
+#define BIG_SPECIAL ULLONG_MAX
+#endif
+
+typedef long double ldouble_t;
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef char flag;
+typedef long intptr;
+#define S32_MAX 2147483647
+#define S32_MIN (-S32_MAX - 1)
+#define U32_MAX 4294967295U
+#endif /* !OSS_NO_INTTYPES_H */
+
+/*
+ * We overload the format definitions to include some "fake" formats.
+ * Therefor, the values should be negative to avoid collusions.
+ */
+enum {
+ AFMT_MS_ADPCM = -256,
+ AFMT_MS_IMA_ADPCM,
+ AFMT_MS_IMA_ADPCM_3BITS,
+ AFMT_MAC_IMA_ADPCM,
+ AFMT_S24_PACKED_BE,
+ AFMT_CR_ADPCM_2,
+ AFMT_CR_ADPCM_3,
+ AFMT_CR_ADPCM_4,
+ AFMT_FIBO_DELTA,
+ AFMT_EXP_DELTA,
+ AFMT_FLOAT32_BE,
+ AFMT_FLOAT32_LE,
+ AFMT_DOUBLE64_BE,
+ AFMT_DOUBLE64_LE
+};
+#define AFMT_S24_PACKED_LE AFMT_S24_PACKED
+
+typedef struct {
+ int fd;
+ int format;
+ int channels;
+ int speed;
+ int flags;
+ int reclevel;
+#ifndef OSS_DEVNODE_SIZE
+#define OSS_DEVNODE_SIZE 32
+#endif
+ char dname[OSS_DEVNODE_SIZE];
+#ifndef OSS_LONGNAME_SIZE
+#define OSS_LONGNAME_SIZE 64
+#endif
+ char current_songname[OSS_LONGNAME_SIZE];
+ char * recsrc;
+ char * playtgt;
+}
+dspdev_t;
+
+typedef enum errors_t {
+ E_OK,
+ E_SETUP_ERROR,
+ E_FORMAT_UNSUPPORTED,
+ E_CHANNELS_UNSUPPORTED,
+ E_DECODE,
+ E_ENCODE,
+ E_USAGE,
+ /*
+ * Not an error, but since seek function can also return an error this needs
+ * to be different from the others
+ */
+ SEEK_CONT_AFTER_DECODE
+}
+errors_t;
+
+#ifdef OGG_SUPPORT
+#include <vorbis/vorbisfile.h>
+
+typedef struct {
+ void * vorbisfile_handle;
+ int (*ov_clear) (OggVorbis_File *);
+ vorbis_comment * (*ov_comment) (OggVorbis_File *, int);
+ vorbis_info * (*ov_info) (OggVorbis_File *, int);
+ int (*ov_open_callbacks) (void *, OggVorbis_File *, char *, long, ov_callbacks);
+ long (*ov_raw_tell) (OggVorbis_File *);
+ long (*ov_read) (OggVorbis_File *, char *, int, int, int, int, int *);
+ int (*ov_seekable) (OggVorbis_File *);
+ double (*ov_time_total) (OggVorbis_File *, int);
+ int (*ov_time_seek) (OggVorbis_File *, double);
+} dlopen_funcs_t;
+
+typedef struct {
+ OggVorbis_File vf;
+ dlopen_funcs_t * f;
+
+ int bitstream, setup;
+ dspdev_t * dsp;
+}
+ogg_data_t;
+#else
+typedef void * dlopen_funcs_t;
+#endif
+
+/*
+ * ossplay supports more containers than the list below. This type is used by
+ * the IFF parser, ossrecord and some other functions though.
+ */
+typedef enum fctypes_t {
+ RAW_FILE,
+ WAVE_FILE,
+ AU_FILE,
+ AIFF_FILE,
+ CAF_FILE,
+ AIFC_FILE,
+ WAVE_FILE_BE,
+ _8SVX_FILE,
+ _16SV_FILE,
+ MAUD_FILE,
+ W64_FILE,
+ OGG_FILE
+}
+fctypes_t;
+
+#define IS_IFF_FILE(t) (((t) == WAVE_FILE) || ((t) == WAVE_FILE_BE) || \
+ ((t) == AIFF_FILE) || ((t) == AIFC_FILE) || \
+ ((t) == _8SVX_FILE) || ((t) == _16SV_FILE) || \
+ ((t) == MAUD_FILE) \
+ )
+/*
+ * Used in the format_t table below.
+ * Shows what actions can be done with the format - Play, Record or both.
+ */
+typedef enum direction_t {
+ CP = 0x1,
+ CR = 0x2,
+ CRP = 0x3
+}
+direction_t;
+
+typedef struct fmt_struct {
+ const char * name;
+ const int fmt;
+ const direction_t dir;
+ const int may_conv;
+}
+format_t;
+
+typedef struct cnt_struct {
+ const char * name;
+ const fctypes_t type;
+ const int dformat;
+ const int dchannels;
+ const int dspeed;
+}
+container_t;
+
+typedef struct {
+ int coeff1, coeff2;
+}
+adpcm_coeff;
+
+typedef struct msadpcm_values {
+ uint16 nBlockAlign;
+ uint16 wSamplesPerBlock;
+ uint16 wNumCoeff;
+ uint16 bits;
+ adpcm_coeff coeff[32];
+ int channels;
+}
+msadpcm_values_t;
+
+typedef ssize_t (decfunc_t) (unsigned char **, unsigned char *, ssize_t,
+ void *);
+typedef errors_t (seekfunc_t) (int, big_t *, big_t, double, big_t, int, void *);
+typedef ssize_t (readfunc_t) (int, void *, size_t, void *);
+
+typedef enum decoder_flag_t {
+ FREE_NONE = 0,
+ FREE_OBUF = 1,
+ FREE_META = 2
+}
+decoder_flag_t;
+
+typedef struct decoders_queue {
+ struct decoders_queue * next;
+ decfunc_t * decoder;
+ unsigned char * outbuf;
+ void * metadata;
+ decoder_flag_t flag;
+}
+decoders_queue_t;
+
+big_t be_int (const unsigned char *, int);
+const char * filepart (const char *);
+float format2bits (int);
+big_t le_int (const unsigned char *, int);
+ldouble_t ossplay_ldexpl (ldouble_t, int);
+int ossplay_parse_opts (int, char **, dspdev_t *);
+int ossrecord_parse_opts (int, char **, dspdev_t *);
+int play (dspdev_t *, int, big_t *, big_t, double, double,
+ readfunc_t *, decoders_queue_t *, seekfunc_t *);
+int record (dspdev_t *, FILE *, const char *, double, double,
+ big_t *, decoders_queue_t * dec);
+const char * sample_format_name (int);
+errors_t setup_device (dspdev_t *, int, int, int);
+errors_t silence (dspdev_t *, big_t, int);
+
+void select_playtgt (dspdev_t *);
+void select_recsrc (dspdev_t *);
+void open_device (dspdev_t *);
+void close_device (dspdev_t *);
+
+#if !defined(OSS_BIG_ENDIAN) && !defined(OSS_LITTLE_ENDIAN)
+#if AFMT_S16_NE == AFMT_S16_BE
+#define OSS_BIG_ENDIAN
+#else
+#define OSS_LITTLE_ENDIAN
+#endif /* AFMT_S16_NE == AFMT_S16_BE */
+#endif /* !OSS_BIG_ENDIAN && !OSS_LITTLE_ENDIAN */
+
+#endif
diff --git a/cmd/ossplay/ossplay.man b/cmd/ossplay/ossplay.man
new file mode 100644
index 0000000..eb80da5
--- /dev/null
+++ b/cmd/ossplay/ossplay.man
@@ -0,0 +1,53 @@
+NAME
+ossplay - Open Sound System playback program.
+
+SYNOPSIS
+ossplay [-RWhlvq] [-S secs ] [ -c channels ] [ -d devname ]
+ [ -f fmtname | ? ] [ -g gain ] [ -o playtarget | ? ]
+ [ -s rate ] filename | - ...
+
+DESCRIPTION
+ossplay plays raw PCM, Microsoft RIFF (.wav), Sun ULaw (.au), Mac AIFF (.aif)
+and other types of audio files. By default the application will try to
+determine the audio file's format and play audio based on the stored
+inforation about sample format, number of channels and sampling rate.
+
+OPTIONS
+-v Verbose output. Multiple invocations increase the level
+ of verbosity.
+-q Quiet (no information printed).
+-l Loop playback indefinately.
+-d<devname> Select <devname> as the device (eg -d/dev/dsp2).
+-s<rate> Select the playback rate for raw PCM audio (eg -s48000).
+-c<channels Select the number of channels 1=mono 2=stereo, 4, 6, 8, etc.
+-f<fmtname> Select the input format (eg -fU8 or -fS16_BE).
+-f? Prints the list of supported format names.
+-o<playtarget> Selects the play target name if the device supports multiple
+ play targets (such as front, rear, side).
+-o? Prints the list of available play targets.
+-g<gain> Amplify all played samples by percentage given as argument.
+ 100 (default) means normal signal level, 200 means double level.
+-W Treat all input as raw PCM data.
+-R Disable redirection to virtual mixer engines and sample
+ rate/format conversions. Should not be used unless absolutely
+ necessary.
+-S<secs> Start playing at <secs> seconds from start of file.
+ The argument can contain a fractional part (e.g. -S1.2)
+-h Display usage information.
+
+INTERRUPT
+Sending a SIGQUIT (Ctrl-\e in most terminals) will make ossplay stop playing
+the currently played file and skip to the next file.
+
+NOTES
+The ossplay executable is the same as the ossrecord executable.
+Behaviour is decided by the name used to invoke the program.
+
+SEE ALSO
+ossrecord(1), ossmix(1), ossxmix(1)
+
+FILES
+/usr/bin/ossplay
+
+AUTHOR
+4Front Technologies
diff --git a/cmd/ossplay/ossplay_console.c b/cmd/ossplay/ossplay_console.c
new file mode 100644
index 0000000..f9b55bf
--- /dev/null
+++ b/cmd/ossplay/ossplay_console.c
@@ -0,0 +1,397 @@
+/*
+ * Purpose: Console output interface functions and related.
+ */
+/*
+ *
+ * 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_console.h"
+#include "ossplay_parser.h"
+#include "ossplay_decode.h"
+#include <sys/wait.h>
+#ifdef OGG_SUPPORT
+#include <dlfcn.h>
+#endif
+
+extern int eflag, quiet, verbose;
+extern flag from_stdin, loop;
+
+static FILE * normalout;
+static int dots = -11, direction = 0;
+
+void
+perror_msg (const char * s)
+{
+ if (quiet < 2) perror (s);
+}
+
+void
+clear_update (void)
+{
+ if (verbose) fprintf (normalout, "\r\n");
+ dots = -11;
+ direction = 0;
+}
+
+void
+print_update (int v, double secs, const char * total)
+{
+ char vu[12] = "-------++!!", * rtime;
+
+ if (v > 0) vu[v] = '\0';
+ else /* v == 0 */
+ {
+ vu[0] = '0';
+ vu[1] = '\0';
+ }
+
+ rtime = totime (secs);
+ fprintf (stdout, "\rTime: %s of %s VU %-11s", rtime, total, vu);
+ fflush (stdout);
+ ossplay_free (rtime);
+}
+
+void
+print_record_update (int v, double secs, const char * fname, int update)
+{
+ char vu[12] = "-------++!!";
+
+ int x1, x2, i;
+ extern int level_meters;
+
+ fprintf (stderr, "\r%s [", fname);
+ x1 = dots;
+ x2 = dots + 10;
+
+ if (update)
+ {
+ if (direction == 0)
+ {
+ dots++;
+ if (dots >= 10) direction = 1;
+ }
+ else
+ {
+ dots--;
+ if (dots <= -10) direction = 0;
+ }
+ }
+
+ if (dots < 0)
+ {
+ x1 = 0;
+ x2 = dots + 10;
+ if (x2 < 0) x2 = 0;
+ }
+ if (dots >= 0)
+ {
+ x2 = 10;
+ x1 = dots;
+ }
+
+ for (i = 0; i < x1; i++)
+ fprintf (stderr, " ");
+ for (i = x1; i < x2; i++)
+ fprintf (stderr, ".");
+ for (i = 0; i < 10 - x2; i++)
+ fprintf (stderr, " ");
+
+ if (secs < 60.0)
+ fprintf (stderr, "] %1.2f secs", secs);
+ else
+ {
+ int hours, mins;
+
+ mins = (int) (secs / 60.0);
+ secs -= (mins * 60);
+
+ hours = mins / 60;
+ mins = mins % 60;
+ fprintf (stderr, "] %02d:%02d:%02d", hours, mins, (int)secs);
+ }
+
+ if (!level_meters)
+ {
+ return;
+ }
+ else if (v > 0)
+ {
+ vu[v] = '\0';
+ fprintf (stderr, " VU %-11s", vu);
+ }
+ else if (v == 0)
+ {
+ fprintf (stderr, " VU %-11s", "0");
+ }
+
+ fflush (stderr);
+}
+
+void print_msg (prtype_t type, const char * fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ switch (type)
+ {
+ case NOTIFYM:
+ if (quiet) break;
+ case WARNM:
+ if (quiet == 2) break;
+ case ERRORM:
+ vfprintf (stderr, fmt, ap);
+ break;
+ case HELPM:
+ vfprintf (stdout, fmt, ap);
+ break;
+ case VERBOSEM:
+ if (verbose) vfprintf (normalout, fmt, ap);
+ break;
+ default: /* case NORMALM, STARTM, CONTM, ENDM: */
+ if (!quiet) vfprintf (normalout, fmt, ap);
+ break;
+ }
+ va_end (ap);
+}
+
+void *
+ossplay_malloc (size_t sz)
+{
+ void *ptr;
+
+ if ((sz == 0) || (sz > OSSPLAY_MAX_MALLOC)) {
+ fprintf (stderr, "Unreasonable allocation size " _PRIbig_t ", aborting",
+ (big_t)sz);
+ exit (E_SETUP_ERROR);
+ }
+ ptr = malloc (sz);
+ if (ptr == NULL) {
+ /* Not all libcs support using %z for size_t */
+ fprintf (stderr, "Can't allocate " _PRIbig_t " bytes\n", (big_t)sz);
+ exit (-1);
+ }
+ return ptr;
+}
+
+void
+ossplay_free (void * ptr)
+{
+ if (ptr == NULL) return;
+ free (ptr);
+}
+
+off_t
+ossplay_lseek_stdin (int fd, off_t off, int w)
+{
+ off_t i;
+ ssize_t bytes_read;
+ char buf[BUFSIZ];
+
+ if (w == SEEK_END) return -1;
+ if (off < 0) return -1;
+ if (off == 0) return 0;
+ i = off;
+ while (i > 0) {
+ bytes_read = read(fd, buf, (i > BUFSIZ)?BUFSIZ:i);
+ if (bytes_read == -1) return -1;
+ else if (bytes_read == 0) return off - i;
+ i -= bytes_read;
+ }
+ return off;
+}
+
+char *
+ossplay_strdup (const char * s)
+{
+ char * p;
+
+ if (s == NULL) return NULL;
+ p = strdup (s);
+ if (p == NULL)
+ {
+ fprintf (stderr, "Can't allocate memory for strdup\n");
+ exit (-1);
+ }
+ return p;
+}
+
+#ifdef OGG_SUPPORT
+int
+ossplay_dlclose (void * handle)
+{
+ return dlclose (handle);
+}
+
+void *
+ossplay_dlopen (const char * filename)
+{
+ return dlopen (filename, RTLD_LAZY | RTLD_LOCAL);
+}
+
+int
+ossplay_vdlsym (void * handle, ...)
+{
+ va_list ap;
+ const char * symbol;
+ void ** v = NULL;
+
+ va_start (ap, handle);
+
+ while (1)
+ {
+ v = va_arg (ap, void **);
+ if (v == (void **)NULL) break;
+ symbol = va_arg (ap, const char *);
+ *v = dlsym (handle, symbol);
+ if (*v == NULL)
+ {
+ const char * msg = dlerror();
+
+ print_msg (ERRORM, "Can't find symbol %s! (Error: %s)\n",
+ symbol, msg?msg:"");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+const char *
+ossplay_dlerror (void)
+{
+ return dlerror();
+}
+#endif
+
+static int
+ossplay_main (int argc, char ** argv)
+{
+ int i, loop_flag;
+ dspdev_t dsp = { -1 };
+ errors_t ret = E_OK;
+ dlopen_funcs_t * vft = NULL;
+
+ normalout = stdout;
+
+ i = ossplay_parse_opts (argc, argv, &dsp);
+
+ argc -= i - 1;
+ argv += i - 1;
+
+ dsp.flags = O_WRONLY;
+ open_device (&dsp);
+ if (dsp.playtgt != NULL) select_playtgt (&dsp);
+
+ do {
+ loop_flag = 0;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '\0') continue;
+ strncpy (dsp.current_songname, filepart (argv[i]),
+ sizeof (dsp.current_songname));
+ dsp.current_songname[sizeof (dsp.current_songname) - 1] = '\0';
+ from_stdin = !strcmp (argv[i], "-");
+ ret = play_file (&dsp, argv[i], &vft);
+ if (ret || from_stdin) argv[i] = "";
+ if ((ret == 0) && (!from_stdin)) loop_flag = 1;
+ eflag = 0;
+ }
+ } while (loop && loop_flag);
+
+#ifdef OGG_SUPPORT
+ if (vft != NULL)
+ {
+ ossplay_dlclose (vft->vorbisfile_handle);
+ ossplay_free (vft);
+ }
+#endif
+
+ close_device (&dsp);
+ return ret;
+}
+
+static int
+ossrecord_main (int argc, char ** argv)
+{
+ int i, oind;
+ char current_filename[512];
+ dspdev_t dsp = { -1 };
+ errors_t err;
+
+ extern int force_fmt, force_channels, force_speed, nfiles;
+ extern double datalimit;
+ extern fctypes_t type;
+ extern char script[512];
+
+ normalout = stderr;
+ /* Since recording can be redirected to stdout, we always output to stderr */
+
+ oind = ossrecord_parse_opts (argc, argv, &dsp);
+
+ dsp.flags = O_RDONLY;
+ open_device (&dsp);
+ if (dsp.recsrc != NULL) select_recsrc (&dsp);
+
+ strncpy (dsp.current_songname, filepart (argv[oind]),
+ sizeof (dsp.current_songname));
+ dsp.current_songname[sizeof (dsp.current_songname) - 1] = 0;
+
+ for (i = 0; i < nfiles; i++)
+ {
+ if (nfiles > 1)
+ /* XXX */
+ snprintf (current_filename, sizeof (current_filename),
+ argv[oind], i + 1);
+ else
+ snprintf (current_filename, sizeof (current_filename),
+ "%s", argv[oind]);
+ err = encode_sound (&dsp, type, current_filename, force_fmt,
+ force_channels, force_speed, datalimit);
+ if (*script)
+ {
+ if (fork () == 0)
+ {
+ if (execlp (script, script, current_filename, (char *)NULL) == -1)
+ {
+ perror (script);
+ exit (-1);
+ }
+ }
+
+ print_msg (NORMALM,
+ "Waiting for the '%s' script(s) to finish - please stand"
+ " by\n", script);
+ while (wait (NULL) != -1);
+ }
+
+ if (err) return err;
+ }
+
+ close_device (&dsp);
+ return 0;
+}
+
+char *
+totime (double secs)
+{
+ char time[20];
+ unsigned long min = secs / 60;
+
+ snprintf (time, 20, "%.2lu:%05.2f", min, secs - min * 60);
+
+ return ossplay_strdup (time);
+}
+
+int
+main (int argc, char **argv)
+{
+ if (strstr (filepart (argv[0]), "ossplay")) exit(ossplay_main (argc, argv));
+ exit(ossrecord_main (argc, argv));
+}
diff --git a/cmd/ossplay/ossplay_console.h b/cmd/ossplay/ossplay_console.h
new file mode 100644
index 0000000..c0f90fd
--- /dev/null
+++ b/cmd/ossplay/ossplay_console.h
@@ -0,0 +1,37 @@
+#ifndef _OSSPLAY_CONSOLE_H
+#define _OSSPLAY_CONSOLE_H
+
+#include <stddef.h>
+#include <sys/types.h>
+
+typedef enum {
+ ERRORM,
+ HELPM,
+ NORMALM,
+ NOTIFYM,
+ WARNM,
+ STARTM,
+ CONTM,
+ ENDM,
+ VERBOSEM
+}
+prtype_t;
+
+void clear_update (void);
+void ossplay_free (void *);
+void * ossplay_malloc (size_t);
+off_t ossplay_lseek_stdin (int, off_t, int);
+char * ossplay_strdup (const char *);
+#ifdef OGG_SUPPORT
+int ossplay_dlclose (void *);
+void * ossplay_dlopen (const char *);
+const char * ossplay_dlerror (void);
+int ossplay_vdlsym (void *, ...);
+#endif
+void perror_msg (const char * s);
+void print_msg (prtype_t, const char *, ...);
+void print_record_update (int, double, const char *, int);
+void print_update (int, double, const char *);
+char * totime (double);
+
+#endif
diff --git a/cmd/ossplay/ossplay_decode.c b/cmd/ossplay/ossplay_decode.c
new file mode 100644
index 0000000..cf769af
--- /dev/null
+++ b/cmd/ossplay/ossplay_decode.c
@@ -0,0 +1,1783 @@
+/*
+ * Purpose: Sample format decode routines for ossplay
+ *
+ */
+/*
+ *
+ * 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_wparser.h"
+
+typedef struct cradpcm_values {
+ const unsigned char * const * table;
+
+ signed char limit;
+ signed char shift;
+ signed char step;
+ unsigned char ratio;
+ unsigned char pred;
+}
+cradpcm_values_t;
+
+typedef struct fib_values {
+ unsigned char pred;
+ const signed char * table;
+}
+fib_values_t;
+
+typedef struct ima_values {
+ int channels;
+ int16 pred[MAX_CHANNELS];
+ int8 index[MAX_CHANNELS];
+}
+ima_values_t;
+
+#ifdef SRC_SUPPORT
+/*
+ * For actual use, we can rely on vmix.
+ * This is useful for testing though.
+ */
+#include "../../kernel/framework/audio/oss_grc3.c"
+
+typedef struct grc_values {
+ int bits;
+ int channels;
+ int speed;
+ int ospeed;
+ int obsize;
+ grc3state_t grc[];
+} grc_data_t;
+static decfunc_t decode_src;
+static decfunc_t decode_mono_to_stereo;
+static grc_data_t * setup_grc3 (int, int, int, int, int);
+#endif
+
+extern int amplification, eflag, force_speed, force_fmt, force_channels;
+extern flag int_conv, overwrite, verbose;
+extern char audio_devname[32];
+extern off_t (*ossplay_lseek) (int, off_t, int);
+extern double seek_time;
+extern const format_t format_a[];
+
+static void decode_ima (unsigned char *, unsigned char *, ssize_t, int16 *,
+ int8 *, int, int);
+static void decode_ima_3bits (unsigned char *, unsigned char *, ssize_t,
+ int16 *, int8 *, int, int);
+static decfunc_t decode_24;
+static decfunc_t decode_8_to_s16;
+static decfunc_t decode_amplify;
+static decfunc_t decode_cr;
+static decfunc_t decode_double64_be;
+static decfunc_t decode_double64_le;
+static decfunc_t decode_endian;
+static decfunc_t decode_fib;
+static decfunc_t decode_float32_be;
+static decfunc_t decode_float32_le;
+static decfunc_t decode_mac_ima;
+static decfunc_t decode_ms_ima;
+static decfunc_t decode_ms_adpcm;
+static decfunc_t decode_nul;
+static decfunc_t decode_raw_ima;
+
+static int32 float32_to_s32 (int, int, int);
+static int32 double64_to_s32 (int, int32, int32, int);
+
+static cradpcm_values_t * setup_cr (int, int);
+static fib_values_t * setup_fib (int, int);
+static decoders_queue_t * setup_normalize (int *, int *, decoders_queue_t *);
+
+static seekfunc_t seek_normal;
+static seekfunc_t seek_compressed;
+
+static readfunc_t read_normal;
+
+#ifdef OGG_SUPPORT
+static readfunc_t read_ogg;
+static seekfunc_t seek_ogg;
+#endif
+
+errors_t
+decode_sound (dspdev_t * dsp, int fd, big_t filesize, int format,
+ int channels, int speed, void * metadata)
+{
+ decoders_queue_t * dec, * decoders;
+ readfunc_t * readf;
+ seekfunc_t * seekf;
+ int bsize, obsize;
+ double constant, total_time;
+ errors_t ret = E_DECODE;
+
+ if (force_speed != 0) speed = force_speed;
+ if (force_channels != 0) channels = force_channels;
+ if (force_fmt != 0) format = force_fmt;
+ if ((channels > MAX_CHANNELS) || (channels == 0))
+ {
+ print_msg (ERRORM, "An unreasonable number of channels (%d), aborting\n",
+ channels);
+ return E_DECODE;
+ }
+
+ constant = format2bits (format) * speed * channels / 8.0;
+ if (constant == 0) return E_DECODE; /* Shouldn't ever happen */
+#if 0
+ /*
+ * There is no reason to use SNDCTL_DSP_GETBLKSIZE in applications like this.
+ * Using some fixed local buffer size will work equally well.
+ */
+ ioctl (dsp->fd, SNDCTL_DSP_GETBLKSIZE, &bsize);
+#else
+ bsize = PLAYBUF_SIZE;
+#endif
+
+ if (filesize < 2) return E_OK;
+ decoders = dec = (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ dec->next = NULL;
+ dec->flag = 0;
+ seekf = seek_normal;
+ readf = read_normal;
+ if (filesize != BIG_SPECIAL) total_time = filesize / constant;
+ else total_time = 0;
+
+ switch (format)
+ {
+ case AFMT_MS_ADPCM:
+ if (metadata == NULL)
+ {
+ msadpcm_values_t * val =
+ (msadpcm_values_t *)ossplay_malloc (sizeof (msadpcm_values_t));
+
+ val->channels = channels;
+ if (speed < 22000) val->nBlockAlign = 256;
+ else if (speed < 44000) val->nBlockAlign = 512;
+ else val->nBlockAlign = 1024;
+ val->wSamplesPerBlock = 8 * (val->nBlockAlign - 7 * channels) / (4 * channels) + 2;
+ val->wNumCoeff = 7;
+ val->coeff[0].coeff1 = 256; val->coeff[0].coeff2 = 0;
+ val->coeff[1].coeff1 = 512; val->coeff[1].coeff2 = -256;
+ val->coeff[2].coeff1 = 0; val->coeff[2].coeff2 = 0;
+ val->coeff[3].coeff1 = 192; val->coeff[3].coeff2 = 64;
+ val->coeff[4].coeff1 = 240; val->coeff[4].coeff2 = 0;
+ val->coeff[5].coeff1 = 460; val->coeff[5].coeff2 = -208;
+ val->coeff[6].coeff1 = 392; val->coeff[6].coeff2 = -232;
+
+ /* total_time = val->wSamplesPerBlock * filesize / val->nBlockAlign / constant; */
+ bsize = val->nBlockAlign;
+ total_time = 0;
+ dec->metadata = (void *)val;
+ dec->flag = FREE_META;
+ }
+ else
+ {
+ msadpcm_values_t * val = (msadpcm_values_t *)metadata;
+
+ /* Let's try anyway */
+ if (val->nBlockAlign == 0)
+ {
+ val->nBlockAlign = filesize - filesize % 4;
+ }
+ else
+ {
+ total_time = val->wSamplesPerBlock * filesize * channels /
+ val->nBlockAlign / constant / 2; /* 4/8 == 1/2 */
+ }
+ bsize = val->nBlockAlign;
+ dec->metadata = metadata;
+ }
+
+ dec->decoder = decode_ms_adpcm;
+ obsize = 4 * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag |= FREE_OBUF;
+ seekf = seek_compressed;
+
+ format = AFMT_S16_NE;
+ break;
+ case AFMT_MS_IMA_ADPCM:
+ case AFMT_MS_IMA_ADPCM_3BITS:
+ dec->metadata = metadata;
+ if (dec->metadata == NULL)
+ {
+ msadpcm_values_t * val =
+ (msadpcm_values_t *)ossplay_malloc (sizeof (msadpcm_values_t));
+
+ val->channels = channels;
+ val->bits = (format == AFMT_MS_IMA_ADPCM)?4:3;
+ val->nBlockAlign = 256 * channels * (speed > 11000)?speed/11000:1;
+ }
+ else
+ {
+ msadpcm_values_t * val = (msadpcm_values_t *)metadata;
+
+ /* Let's try anyway - some cameras make defective WAVs */
+ if (val->nBlockAlign == 0)
+ {
+ val->nBlockAlign = filesize - filesize % 4;
+ }
+ else
+ {
+ total_time = val->wSamplesPerBlock * filesize * val->bits * channels /
+ val->nBlockAlign / constant / 8.0;
+ }
+ bsize = val->nBlockAlign;
+ }
+
+ dec->decoder = decode_ms_ima;
+ if (format == AFMT_MS_IMA_ADPCM_3BITS)
+ obsize = (bsize * 16)/3 + 2;
+ /*
+ * 8 sample words per 3 bytes, each expanding to 2 bytes, plus 2 bytes
+ * to deal with fractions. Slight overestimation because bsize
+ * includes the headers too.
+ */
+ else
+ obsize = 4 * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF;
+ seekf = seek_compressed;
+
+ format = AFMT_S16_NE;
+ break;
+ case AFMT_MAC_IMA_ADPCM:
+ dec->metadata = (void *)(intptr)channels;
+ dec->decoder = decode_mac_ima;
+ bsize -= bsize % (MAC_IMA_BLKLEN * channels);
+ obsize = 4 * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF;
+ seekf = seek_compressed;
+
+ format = AFMT_S16_NE;
+ break;
+ case AFMT_IMA_ADPCM:
+ dec->metadata = (void *)ossplay_malloc (sizeof (ima_values_t));
+ memset (dec->metadata, 0, sizeof (ima_values_t));
+ ((ima_values_t *)(dec->metadata))->channels = channels;
+
+ dec->decoder = decode_raw_ima;
+ obsize = 4 * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF | FREE_META;
+ seekf = seek_compressed;
+
+ format = AFMT_S16_NE;
+ break;
+ case AFMT_CR_ADPCM_2:
+ case AFMT_CR_ADPCM_3:
+ case AFMT_CR_ADPCM_4:
+ dec->metadata = (void *)setup_cr (fd, format);;
+ if (dec->metadata == NULL) goto exit;
+ dec->decoder = decode_cr;
+ obsize = ((cradpcm_values_t *)dec->metadata)->ratio * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF | FREE_META;
+ seekf = seek_compressed;
+
+ if (filesize != BIG_SPECIAL) filesize--;
+ format = AFMT_U8;
+ break;
+ case AFMT_FIBO_DELTA:
+ case AFMT_EXP_DELTA:
+ dec->metadata = (void *)setup_fib (fd, format);;
+ if (dec->metadata == NULL) goto exit;
+ dec->decoder = decode_fib;
+ obsize = 2 * bsize;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF | FREE_META;
+ seekf = seek_compressed;
+
+ if (filesize != BIG_SPECIAL) filesize--;
+ format = AFMT_U8;
+ break;
+ case AFMT_S24_PACKED:
+ case AFMT_S24_PACKED_BE:
+ dec->metadata = (void *)(intptr)format;
+ dec->decoder = decode_24;
+ bsize -= bsize % 3;
+ obsize = bsize/3*4;
+ dec->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ dec->flag = FREE_OBUF;
+
+ format = AFMT_S32_NE;
+ break;
+ case AFMT_FLOAT32_BE:
+ case AFMT_FLOAT32_LE:
+ if (format == AFMT_FLOAT32_BE) dec->decoder = decode_float32_be;
+ else dec->decoder = decode_float32_le;
+ bsize -= bsize % 4;
+ obsize = bsize;
+ dec->outbuf = NULL;
+
+ format = AFMT_S32_NE;
+ break;
+ case AFMT_DOUBLE64_BE:
+ case AFMT_DOUBLE64_LE:
+ if (format == AFMT_DOUBLE64_BE) dec->decoder = decode_double64_be;
+ else dec->decoder = decode_double64_le;
+ bsize -= bsize % 8;
+ obsize = bsize/2;
+ dec->outbuf = NULL;
+
+ format = AFMT_S32_NE;
+ break;
+#ifdef OGG_SUPPORT
+ case AFMT_VORBIS:
+ readf = read_ogg;
+ dec->decoder = decode_nul;
+ dec->metadata = metadata;
+ obsize = bsize;
+ if (metadata == NULL) goto exit;
+ else
+ {
+ ogg_data_t * val = (ogg_data_t *)metadata;
+ if (val->f->ov_seekable (&val->vf))
+ {
+ seekf = seek_ogg;
+ total_time = val->f->ov_time_total (&val->vf, -1);
+ }
+ else
+ {
+ seekf = NULL;
+ total_time = 0;
+ }
+ }
+
+ format = AFMT_S16_NE;
+ break;
+#endif
+ default:
+ dec->decoder = decode_nul;
+
+ obsize = bsize;
+ break;
+ }
+
+ if (int_conv)
+ decoders = setup_normalize (&format, &obsize, decoders);
+
+ if ((amplification > 0) && (amplification != 100))
+ {
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->metadata = (void *)(intptr)format;
+ decoders->decoder = decode_amplify;
+ decoders->next = NULL;
+ decoders->outbuf = NULL;
+ decoders->flag = 0;
+ }
+
+ ret = setup_device (dsp, format, channels, speed);
+ if (ret == E_FORMAT_UNSUPPORTED)
+ {
+ int i, tmp;
+
+ for (i = 0; format_a[i].name != NULL; i++)
+ if (format_a[i].fmt == format)
+ {
+ tmp = format_a[i].may_conv;
+ if ((tmp == 0) || (tmp == format)) continue;
+ print_msg (WARNM, "Converting to format %s\n",
+ sample_format_name (tmp));
+ ret = setup_device (dsp, tmp, channels, speed);
+ if (ret == E_FORMAT_UNSUPPORTED) goto exit;
+ decoders = setup_normalize (&format, &obsize, decoders);
+ goto dcont;
+ }
+ goto exit;
+ }
+
+dcont:
+#ifdef SRC_SUPPORT
+ if ((ret == E_CHANNELS_UNSUPPORTED) && (channels == 1)) {
+ channels = 2;
+ if ((ret = setup_device (dsp, format, channels, speed))) goto exit;
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->metadata = (void *)(intptr)format;
+ decoders->decoder = decode_mono_to_stereo;
+ decoders->next = NULL;
+ obsize *= 2;
+ decoders->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ decoders->flag = FREE_OBUF;
+ }
+#endif
+
+ if (ret) goto exit;
+#ifdef SRC_SUPPORT
+ if (dsp->speed != speed) {
+ if ((format == AFMT_MU_LAW) || (format == AFMT_A_LAW))
+ decoders = setup_normalize (&format, &obsize, decoders);
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->decoder = decode_src;
+ decoders->next = NULL;
+ obsize *= (dsp->speed / speed + 1) * channels * sizeof (int);
+ decoders->metadata =
+ (void *)setup_grc3 (format, channels, dsp->speed, speed, obsize);
+ decoders->outbuf = (unsigned char *)ossplay_malloc (obsize);
+ decoders->flag = FREE_OBUF | FREE_META;
+ speed = dsp->speed;
+ }
+#endif
+
+ ret = play (dsp, fd, &filesize, bsize, total_time, constant, readf,
+ dec, seekf);
+
+exit:
+ decoders = dec;
+ while (decoders != NULL)
+ {
+ if (decoders->flag & FREE_META) ossplay_free (decoders->metadata);
+ if (decoders->flag & FREE_OBUF) ossplay_free (decoders->outbuf);
+ decoders = decoders->next;
+ ossplay_free (dec);
+ dec = decoders;
+ }
+
+ return ret;
+}
+
+errors_t
+encode_sound (dspdev_t * dsp, fctypes_t type, const char * fname, int format,
+ int channels, int speed, double data_time)
+{
+ big_t data_size = 0;
+ double constant;
+ int fd = -1;
+ decoders_queue_t * dec, * decoders = NULL;
+ errors_t ret;
+ FILE * wave_fp;
+
+ if ((ret = setup_device (dsp, format, channels, speed))) return ret;
+ constant = format2bits (format) * speed * channels / 8.0;
+
+ if (data_time != 0) data_size = data_time * constant;
+
+ if (strcmp (fname, "-") == 0) {
+ wave_fp = fdopen (1, "wb");
+ } else {
+ fd = open (fname, O_WRONLY | O_CREAT | (overwrite?O_TRUNC:O_EXCL), 0644);
+ if (fd == -1) {
+ perror (fname);
+ return E_ENCODE;
+ }
+ wave_fp = fdopen (fd, "wb");
+ }
+
+ if (wave_fp == NULL)
+ {
+ perror (fname);
+ if (fd != -1) close (fd);
+ return E_ENCODE;
+ }
+
+ if (channels == 1)
+ print_msg (VERBOSEM, "Recording wav: Speed %dHz %d bits Mono\n",
+ speed, (int)format2bits (format));
+ if (channels == 2)
+ print_msg (VERBOSEM, "Recording wav: Speed %dHz %d bits Stereo\n",
+ speed, (int)format2bits (format));
+ if (channels > 2)
+ print_msg (VERBOSEM, "Recording wav: Speed %dHz %d bits %d channels\n",
+ speed, (int)format2bits (format), channels);
+
+ /*
+ * Write the initial header
+ */
+ if (write_head (wave_fp, type, data_size, format, channels, speed))
+ return E_ENCODE;
+
+ decoders = dec =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ dec->next = NULL;
+ dec->flag = 0;
+ dec->decoder = decode_nul;
+
+ if ((amplification > 0) && (amplification != 100))
+ {
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->metadata = (void *)(intptr)format;
+ decoders->decoder = decode_amplify;
+ decoders->next = NULL;
+ decoders->outbuf = NULL;
+ decoders->flag = 0;
+ }
+
+ ret = record (dsp, wave_fp, fname, constant, data_time, &data_size, dec);
+
+ finalize_head (wave_fp, type, data_size, format, channels, speed);
+ fflush (wave_fp);
+ /*
+ * EINVAL and EROFS are returned for "special files which don't support
+ * syncronization". The user should already know he's writing to a special
+ * file (e.g. "ossrecord /dev/null"), so no need to warn.
+ */
+ if ((fsync (fileno (wave_fp)) == -1) && (errno != EINVAL) && (errno != EROFS))
+ {
+ perror (fname);
+ ret = E_ENCODE;
+ }
+ if (fclose (wave_fp) != 0)
+ {
+ perror (fname);
+ ret = E_ENCODE;
+ }
+
+ decoders = dec;
+ while (decoders != NULL)
+ {
+ if (decoders->flag & FREE_META) ossplay_free (decoders->metadata);
+ if (decoders->flag & FREE_OBUF) ossplay_free (decoders->outbuf);
+ decoders = decoders->next;
+ ossplay_free (dec);
+ dec = decoders;
+ }
+ return ret;
+}
+
+static ssize_t
+decode_24 (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ big_t outlen = 0;
+ ssize_t i;
+ int v1;
+ uint32 * u32;
+ int32 sample_s32, * outbuf = (int32 *) * obuf;
+ int format = (int)(intptr)metadata;
+
+ if (format == AFMT_S24_PACKED) v1 = 8;
+ else v1 = 24;
+
+ for (i = 0; i < l-2; i += 3)
+ {
+ u32 = (uint32 *) &sample_s32; /* Alias */
+
+ *u32 = (buf[i] << v1) | (buf[i + 1] << 16) | (buf[i + 2] << (32-v1));
+ outbuf[outlen++] = sample_s32;
+ }
+
+ return 4 * outlen;
+}
+
+static fib_values_t *
+setup_fib (int fd, int format)
+{
+ static const signed char CodeToDelta[16] = {
+ -34, -21, -13, -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8, 13, 21
+ };
+ static const signed char CodeToExpDelta[16] = {
+ -128, -64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64
+ };
+ unsigned char buf;
+ fib_values_t * val;
+
+ if (read (fd, &buf, 1) <= 0) return NULL;
+ val = (fib_values_t *)ossplay_malloc (sizeof (fib_values_t));
+ if (format == AFMT_EXP_DELTA) val->table = CodeToExpDelta;
+ else val->table = CodeToDelta;
+
+ val->pred = buf;
+
+ return val;
+}
+
+static cradpcm_values_t *
+setup_cr (int fd, int format)
+{
+ static const unsigned char T2[4][3] = {
+ { 128, 6, 1 },
+ { 32, 4, 1 },
+ { 8, 2, 1 },
+ { 2, 0, 1 }
+ };
+
+ static const unsigned char T3[3][3] = {
+ { 128, 5, 3 },
+ { 16, 2, 3 },
+ { 2, 0, 1 }
+ };
+
+ static const unsigned char T4[2][3] = {
+ { 128, 4, 7 },
+ { 8, 0, 7 }
+ };
+
+ static const unsigned char * t_row[4];
+
+ unsigned char buf;
+ cradpcm_values_t * val;
+ int i;
+
+ if (read (fd, &buf, 1) <= 0) return NULL;
+ val = (cradpcm_values_t *)ossplay_malloc (sizeof (cradpcm_values_t));
+ val->table = t_row;
+
+ if (format == AFMT_CR_ADPCM_2)
+ {
+ val->limit = 1;
+ val->step = val->shift = 2;
+ val->ratio = 4;
+ for (i=0; i < 4; i++) t_row[i] = T2[i];
+ }
+ else if (format == AFMT_CR_ADPCM_3)
+ {
+ val->limit = 3;
+ val->ratio = 3;
+ val->step = val->shift = 0;
+ for (i=0; i < 3; i++) t_row[i] = T3[i];
+ }
+ else /* if (format == AFMT_CR_ADPCM_4) */
+ {
+ val->limit = 5;
+ val->ratio = 2;
+ val->step = val->shift = 0;
+ for (i=0; i < 2; i++) t_row[i] = T4[i];
+ }
+
+ val->pred = buf;
+
+ return val;
+}
+
+static ssize_t
+decode_8_to_s16 (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ int format = (int)(intptr)metadata;
+ ssize_t i;
+ int16 * outbuf = (int16 *) * obuf;
+ static const int16 mu_law_table[256] = {
+ -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+ -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+ -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+ -11900,-11388,-10876,-10364,-9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+ };
+
+ static const int16 a_law_table[256] = {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+ -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+ -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
+ -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848,
+ 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
+ 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
+ 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
+ 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+ 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
+ 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+ 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424,
+ 88, 72, 120, 104, 24, 8, 56, 40,
+ 216, 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
+ 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
+ 688, 656, 752, 720, 560, 528, 624, 592,
+ 944, 912, 1008, 976, 816, 784, 880, 848
+ };
+
+ switch (format)
+ {
+ case AFMT_U8:
+ for (i = 0; i < l; i++) outbuf[i] = (buf[i] - 128) << 8;
+ break;
+ case AFMT_S8:
+ for (i = 0; i < l; i++) outbuf[i] = buf[i] << 8;
+ break;
+ case AFMT_MU_LAW:
+ for (i = 0; i < l; i++) outbuf[i] = mu_law_table[buf[i]];
+ break;
+ case AFMT_A_LAW:
+ for (i = 0; i < l; i++) outbuf[i] = a_law_table[buf[i]];
+ break;
+ }
+
+ return 2*l;
+}
+
+static ssize_t
+decode_cr (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ cradpcm_values_t * val = (cradpcm_values_t *) metadata;
+ int j, pred = val->pred, step = val->step;
+ unsigned char value;
+ signed char sign;
+ ssize_t i;
+
+ for (i=0; i < l; i++)
+ for (j=0; j < val->ratio; j++)
+ {
+ sign = (buf[i] & val->table[j][0])?-1:1;
+ value = (buf[i] >> val->table[j][1]) & val->table[j][2];
+ pred += sign*(value << step);
+ if (pred > 255) pred = 255;
+ else if (pred < 0) pred = 0;
+ (*obuf)[val->ratio*i+j] = pred;
+ if ((value >= val->limit) && (step < 3+val->shift)) step++;
+ if ((value == 0) && (step > val->shift)) step--;
+ }
+
+ val->pred = pred;
+ val->step = step;
+ return val->ratio*l;
+}
+
+static ssize_t
+decode_fib (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ fib_values_t * val = (fib_values_t *)metadata;
+ int x = val->pred;
+ unsigned char d;
+ ssize_t i;
+
+ for (i = 0; i < 2*l; i++)
+ {
+ d = buf[i/2];
+ if (i & 1) d &= 0xF;
+ else d >>= 4;
+ x += val->table[d];
+ if (x > 255) x = 255;
+ if (x < 0) x = 0;
+ (*obuf)[i] = x;
+ }
+
+ val->pred = x;
+ return 2*l;
+}
+
+static ssize_t
+decode_ms_adpcm (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ msadpcm_values_t * val = (msadpcm_values_t *)metadata;
+
+ int error_delta, i_delta, i = 0, nib = 0, channels = val->channels;
+ int AdaptionTable[16] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+ };
+ ssize_t outp = 0, x = 0;
+ int16 * wbuf = (int16 *)*obuf;
+ int32 delta[MAX_CHANNELS], samp1[MAX_CHANNELS], samp2[MAX_CHANNELS],
+ predictor[MAX_CHANNELS], new_samp, pred, n = 0;
+
+/*
+ * Playback procedure
+ */
+#define OUT_SAMPLE(s) \
+ do { \
+ if (s > 32767) s = 32767; else if (s < -32768) s = -32768; \
+ wbuf[outp++] = s; \
+ n += 2; \
+ } while(0)
+
+#define GETNIBBLE \
+ ((nib == 0) ? \
+ (buf[x + nib++] >> 4) & 0x0f : \
+ buf[x++ + --nib] & 0x0f \
+ )
+
+ for (i = 0; i < channels; i++)
+ {
+ predictor[i] = buf[x];
+ if (predictor[i] > val->wNumCoeff)
+ /* Shouldn't ever happen */
+ predictor[i] = val->wNumCoeff;
+ x++;
+ }
+
+ for (i = 0; i < channels; i++)
+ {
+ delta[i] = (int16) le_int (&buf[x], 2);
+ x += 2;
+ }
+
+ for (i = 0; i < channels; i++)
+ {
+ samp1[i] = (int16) le_int (&buf[x], 2);
+ x += 2;
+ OUT_SAMPLE (samp1[i]);
+ }
+
+ for (i = 0; i < channels; i++)
+ {
+ samp2[i] = (int16) le_int (&buf[x], 2);
+ x += 2;
+ OUT_SAMPLE (samp2[i]);
+ }
+
+ while (n < (val->wSamplesPerBlock * 2 * channels))
+ for (i = 0; i < channels; i++)
+ {
+ pred = ((samp1[i] * val->coeff[predictor[i]].coeff1)
+ + (samp2[i] * val->coeff[predictor[i]].coeff2)) / 256;
+
+ if (x > l) return 2*outp;
+ i_delta = error_delta = GETNIBBLE;
+
+ if (i_delta & 0x08)
+ i_delta -= 0x10; /* Convert to signed */
+
+ new_samp = pred + (delta[i] * i_delta);
+ OUT_SAMPLE (new_samp);
+
+ delta[i] = delta[i] * AdaptionTable[error_delta] / 256;
+ if (delta[i] < 16) delta[i] = 16;
+
+ samp2[i] = samp1[i];
+ samp1[i] = new_samp;
+ }
+
+ return 2*outp;
+}
+
+static ssize_t
+decode_nul (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ *obuf = buf;
+ return l;
+}
+
+static ssize_t
+decode_endian (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ int format = (int)(intptr)metadata;
+ ssize_t i;
+
+ switch (format)
+ {
+ case AFMT_S16_OE:
+ {
+ int16 * s = (int16 *)buf;
+
+ for (i = 0; i < l / 2; i++)
+ s[i] = ((s[i] >> 8) & 0x00FF) |
+ ((s[i] << 8) & 0xFF00);
+ }
+ break;
+ case AFMT_S32_OE:
+ case AFMT_S24_OE:
+ {
+ int32 * s = (int32 *)buf;
+
+ for (i = 0; i < l / 4; i++)
+ s[i] = ((s[i] >> 24) & 0x000000FF) |
+ ((s[i] << 8) & 0x00FF0000) | ((s[i] >> 8) & 0x0000FF00) |
+ ((s[i] << 24) & 0xFF000000);
+ }
+ break;
+#ifdef OSS_LITTLE_ENDIAN
+ case AFMT_U16_BE: /* U16_BE -> S16_LE */
+#else
+ case AFMT_U16_LE: /* U16_LE -> S16_BE */
+#endif
+ {
+ int16 * s = (int16 *)buf;
+
+ for (i = 0; i < l / 2; i++)
+ s[i] = (((s[i] >> 8) & 0x00FF) | ((s[i] << 8) & 0xFF00)) -
+ USHRT_MAX/2;
+ }
+ break;
+ /* Not an endian conversion, but included for completeness sake */
+#ifdef OSS_LITTLE_ENDIAN
+ case AFMT_U16_LE: /* U16_LE -> S16_LE */
+#else
+ case AFMT_U16_BE: /* U16_BE -> S16_BE */
+#endif
+ {
+ int16 * s = (int16 *)buf;
+
+ for (i = 0; i < l / 2; i++)
+ s[i] -= USHRT_MAX/2;
+ }
+ break;
+ }
+ *obuf = buf;
+ return l;
+}
+
+static ssize_t
+decode_amplify (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ int format = (int)(intptr)metadata;
+ ssize_t i, len;
+
+ switch (format)
+ {
+ case AFMT_S16_NE:
+ {
+ int16 *s = (int16 *)buf;
+ int32 tmp;
+
+ len = l / 2;
+ for (i = 0; i < len ; i++)
+ {
+ tmp = (int32)s[i] * amplification / 100;
+ if (tmp > SHRT_MAX) s[i] = SHRT_MAX;
+ else if (tmp < SHRT_MIN) s[i] = SHRT_MIN;
+ else s[i] = tmp;
+ }
+ }
+ break;
+ case AFMT_S32_NE:
+ case AFMT_S24_NE:
+ {
+ int32 *s = (int32 *)buf;
+ sbig_t tmp;
+
+ len = l / 4;
+ for (i = 0; i < len; i++)
+ {
+ tmp = (sbig_t)s[i] * amplification / 100;
+ if (tmp > S32_MAX) s[i] = S32_MAX;
+ else if (tmp < S32_MIN) s[i] = S32_MIN;
+ else s[i] = tmp;
+ }
+ }
+ break;
+ }
+
+ *obuf = buf;
+ return l;
+}
+
+static void
+decode_ima (unsigned char * obuf, unsigned char * buf, ssize_t l, int16 * pred0,
+ int8 * index0, int channels, int ch)
+{
+ int j;
+ int32 pred = *pred0;
+ int16 step;
+ int16 * outbuf = (int16 *) obuf;
+ int8 index = *index0, value;
+ signed char sign;
+ ssize_t i;
+ static const int step_tab[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66,
+ 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+ 32767
+ };
+
+ static const int8 iTab4[16] =
+ {-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
+
+ for (i=0; i < l; i++)
+ for (j=0; j < 2; j++)
+ {
+ value = (buf[i] >> 4*j) & 15;
+
+ step = step_tab[index];
+ index += iTab4[value];
+ if (index < 0) index = 0;
+ else if (index > 88) index = 88;
+
+ sign = 1 - 2 * ((value >> 3) & 1);
+ value &= 7;
+
+ pred += sign * (2 * value + 1) * step / 4;
+ if (pred > 32767) pred = 32767;
+ else if (pred < -32768) pred = -32768;
+
+ outbuf[channels*(2*i+j)+ch] = pred;
+ }
+
+ *index0 = index;
+ *pred0 = pred;
+
+ return;
+}
+
+static void
+decode_ima_3bits (unsigned char * obuf, unsigned char * buf, ssize_t l,
+ int16 * pred0, int8 * index0, int channels, int ch)
+{
+ int j;
+ signed char sign;
+ ssize_t i;
+
+ int32 pred = *pred0, raw;
+ int8 index = *index0, value;
+ int16 * outbuf = (int16 *) obuf, step;
+
+ static const int step_tab[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66,
+ 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+ 32767
+ };
+
+ static const int8 iTab3[8] =
+ {-1, -1, 1, 2, -1, -1, 1, 2};
+
+ for (i=0; i < l-2; i += 3)
+ {
+ raw = buf[i] + (buf[i+1] << 8) + (buf[i+2] << 16);
+ for (j = 0; j < 8; j++)
+ {
+ value = (raw >> (3*j)) & 7;
+
+ step = step_tab[index];
+ index += iTab3[value];
+ if (index < 0) index = 0;
+ else if (index > 88) index = 88;
+
+ sign = 1 - 2 * ((value >> 2) & 1);
+ value &= 3;
+
+ pred += sign * (2 * value + 1) * step / 4;
+ if (pred > 32767) pred = 32767;
+ else if (pred < -32768) pred = -32768;
+
+ outbuf[channels*(8*i/3+j)+ch] = pred;
+ }
+ }
+
+ *index0 = index;
+ *pred0 = pred;
+
+ return;
+}
+
+static ssize_t
+decode_mac_ima (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ ssize_t len = 0, olen = 0;
+ int i, channels = (int)(intptr)metadata;
+ int16 pred;
+ int8 index;
+
+ while (len < l)
+ {
+ for (i = 0; i < channels; i++)
+ {
+ if (len + MAC_IMA_BLKLEN > l) return olen;
+ pred = (int16)((buf[len] << 8) | (buf[len+1] & 128));
+ index = buf[len+1] & 127;
+ if (index > 88) index = 88;
+ len += 2;
+
+ decode_ima (*obuf + olen, buf + len, MAC_IMA_BLKLEN - 2, &pred,
+ &index, channels, i);
+ len += MAC_IMA_BLKLEN-2;
+ }
+ olen += 4*(MAC_IMA_BLKLEN - 2)*channels;
+ }
+
+ return olen;
+}
+
+static ssize_t
+decode_ms_ima (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ int i;
+ ssize_t len = 0, olen = 0;
+ msadpcm_values_t * val = (msadpcm_values_t *)metadata;
+ int8 index[MAX_CHANNELS];
+ int16 * outbuf = (int16 *) * obuf, pred[MAX_CHANNELS];
+
+ for (i = 0; i < val->channels; i++)
+ {
+ if (len >= l) return olen;
+ pred[i] = (int16) le_int (buf + len, 2);
+ /*
+ * The microsoft docs says the sample from the block header should be
+ * played.
+ */
+ outbuf[i] = pred[i];
+ olen += 2;
+ index[i] = buf[len + 2];
+ if (index[i] > 88) index[i] = 88;
+ if (index[i] < 0) index[i] = 0;
+ len += 4;
+ }
+
+ if (val->bits == 4)
+ while (len < l)
+ {
+ for (i = 0; i < val->channels; i++)
+ {
+ if (len + 4 > l) return olen;
+ decode_ima (*obuf + olen, buf + len, 4, &pred[i], &index[i],
+ val->channels, i);
+ len += 4;
+ }
+ olen += 2*8*val->channels;
+ }
+ else
+ {
+ unsigned char rbuf[12];
+ int j;
+
+ while (len < l)
+ {
+ if (len + 12*val->channels > l) return olen;
+ for (i = 0; i < val->channels; i++)
+ {
+ /*
+ * Each sample word for a channel in an IMA ADPCM RIFF file is 4
+ * bits. This doesn't resolve to an integral number of samples
+ * in a 3 bit ADPCM, so we use a simple method around this.
+ * This shouldn't skip samples since the spec guarantees the
+ * number of sample words in a block is divisible by 3.
+ */
+ for (j = 0; j < 12; j++)
+ rbuf[j] = buf[len + j%4 + (j/4)*(val->channels*4) + i*4];
+ decode_ima_3bits (*obuf + olen, rbuf, 12, &pred[i], &index[i],
+ val->channels, i);
+ }
+ /* 12 = 3 words per channel, each containing 4 bytes */
+ len += 12*val->channels;
+ /* 64 = 32 samples per channel, each expanding to 2 bytes */
+ olen += 64*val->channels;
+ }
+ }
+ return olen;
+}
+
+static ssize_t
+decode_raw_ima (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ ima_values_t * val = (ima_values_t *)metadata;
+ ssize_t olen = 0;
+
+ /* We can't tell if/how it's interleaved. */
+ decode_ima (*obuf, buf, l, &val->pred[0], &val->index[0], 1, 0);
+ olen = 4*l;
+
+ return olen;
+}
+
+static ssize_t
+decode_float32_be (unsigned char ** obuf, unsigned char * buf, ssize_t l,
+ void * metadata)
+{
+ ssize_t i;
+ int exp, man;
+ int32 * wbuf = (int32 *) buf;
+
+ for (i=0; i < l-3; i += 4)
+ {
+ exp = ((buf[i] & 0x7F) << 1) | ((buf[i+1] & 0x80) / 0x80);
+ man = ((buf[i+1] & 0x7F) << 16) | (buf[i+2] << 8) | buf[i+3];
+
+ *wbuf++ = float32_to_s32 (exp, man, (buf[i] & 0x80));
+ }
+
+ *obuf = buf;
+ return l;
+}
+
+static ssize_t
+decode_float32_le (unsigned char ** obuf, unsigned char * buf, ssize_t l,
+ void * metadata)
+{
+ ssize_t i;
+ int exp, man;
+ int32 * wbuf = (int32 *) buf;
+
+ for (i=0; i < l-3; i += 4)
+ {
+ exp = ((buf[i+3] & 0x7F) << 1) | ((buf[i+2] & 0x80) / 0x80);
+ man = ((buf[i+2] & 0x7F) << 16) | (buf[i+1] << 8) | buf[i];
+
+ *wbuf++ = float32_to_s32 (exp, man, (buf[i+3] & 0x80));
+ }
+
+ *obuf = buf;
+ return l;
+}
+
+static ssize_t
+decode_double64_be (unsigned char ** obuf, unsigned char * buf, ssize_t l,
+ void * metadata)
+{
+ ssize_t i;
+ int exp;
+ int32 * wbuf = (int32 *) buf, lower, upper;
+
+ for (i=0; i < l-7; i += 8)
+ {
+ exp = ((buf[i] & 0x7F) << 4) | ((buf[i+1] >> 4) & 0xF) ;
+
+ upper = ((buf[i+1] & 0xF) << 24) | (buf[i+2] << 16) | (buf[i+3] << 8) |
+ buf[i+4];
+ lower = (buf[i+5] << 16) | (buf[i+6] << 8) | buf[i+7];
+
+ *wbuf++ = double64_to_s32 (exp, upper, lower, buf[i] & 0x80);
+ }
+
+ *obuf = buf;
+ return l/2;
+}
+
+static ssize_t
+decode_double64_le (unsigned char ** obuf, unsigned char * buf, ssize_t l,
+ void * metadata)
+{
+ ssize_t i;
+ int exp;
+ int32 * wbuf = (int32 *) buf, lower, upper;
+
+ for (i=0; i < l-7; i += 8)
+ {
+ exp = ((buf[i+7] & 0x7F) << 4) | ((buf[i+6] >> 4) & 0xF);
+
+ upper = ((buf[i+6] & 0xF) << 24) | (buf[i+5] << 16) | (buf[i+4] << 8) |
+ buf[i+3];
+ lower = (buf[i+2] << 16) | (buf[i+1] << 8) | buf[i];
+
+ *wbuf++ = double64_to_s32 (exp, upper, lower, buf[i+7] & 0x80);
+ }
+
+ *obuf = buf;
+ return l/2;
+}
+
+static int32
+double64_to_s32 (int exp, int32 upper, int32 lower, int sign)
+{
+ ldouble_t out, value;
+
+ if ((exp != 0) && (exp != 2047))
+ {
+ value = (upper + lower / ((double)0x1000000))/((double)0x10000000) + 1;
+ value = ossplay_ldexpl (value, exp - 1023);
+ }
+ else if (exp == 0)
+ {
+#if 0
+ int j;
+
+ out = (upper + lower / ((double)0x1000000))/((double)0x10000000);
+ for (j=0; j < 73; j++) out /= 1 << 14;
+#endif
+ /* So low, that it's pretty much 0 for us */
+ return 0;
+ }
+ else /* exp == 2047 */
+ {
+ /*
+ * Either NaN, or +/- Inf. 0 is almost as close an approximation of
+ * Inf as the maximum sample value....
+ */
+ print_msg (WARNM, "exp == 2047 in file!\n");
+ return 0;
+ }
+
+ out = (sign ? 1 : -1) * value * S32_MIN;
+ if (out > S32_MAX) out = S32_MAX;
+ else if (out < S32_MIN) out = S32_MIN;
+
+ return out;
+}
+
+static int32
+float32_to_s32 (int exp, int man, int sign)
+{
+ ldouble_t out, value;
+
+ if ((exp != 0) && (exp != 255))
+ {
+ value = man ? (float)man/(float)0x800000 + 1 : 0.0;
+ value = ossplay_ldexpl (value, exp - 127);
+ }
+ else if (exp == 0)
+ {
+#if 0
+ value = (float)man / (float)0x800000;
+ value /= 1UL << 31; value /= 1UL << 31; value /= 1UL << 32;
+ value /= 1UL << 32;
+#endif
+ /* So low, that it's pretty much 0 for us */
+ return 0;
+ }
+ else /* exp == 255 */
+ {
+ /*
+ * Either NaN, or +/- Inf. 0 is almost as close an approximation of
+ * Inf as the maximum sample value....
+ */
+ print_msg (WARNM, "exp == 255 in file!\n");
+ return 0;
+ }
+
+ out = (sign ? 1 : -1) * value * S32_MIN;
+ if (out > S32_MAX) out = S32_MAX;
+ else if (out < S32_MIN) out = S32_MIN;
+
+ return out;
+}
+
+int
+get_db_level (const unsigned char * buf, ssize_t l, int format)
+{
+/*
+ * Display a rough recording level meter, and the elapsed time.
+ */
+ static const unsigned char db_table[256] = {
+ /* Lookup table for log10(ix)*2, ix=0..255 */
+ 0, 0, 1, 2, 2, 3, 3, 3, 4, 4,
+ 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
+ 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11
+ };
+
+ int32 level, v = 0;
+ ssize_t i;
+
+ level = 0;
+ if ((buf == NULL) || (l == 0)) return 0;
+
+ switch (format)
+ {
+ case AFMT_U8:
+ {
+ uint8 * p;
+
+ p = (uint8 *)buf;
+
+ for (i = 0; i < l; i++) {
+ v = (*p++);
+ if (v > level) level = v;
+ }
+ }
+ case AFMT_S8:
+ {
+ int8 * p;
+
+ p = (int8 *)buf;
+
+ for (i = 0; i < l; i++) {
+ v = *p++;
+ if (v < 0) {
+ /* This can be false on a two's-complement machine */
+ if (v != -v) v = -v;
+ else v = -(v+1);
+ }
+ if (v > level) level = v;
+ }
+ }
+ break;
+
+ case AFMT_S16_NE:
+ {
+ int16 * p;
+
+ p = (int16 *)buf;
+
+ for (i = 0; i < l / 2; i++) {
+ v = *p++;
+ if (v < 0) {
+ if (v != -v) v = -v;
+ else v = -(v+1);
+ }
+ if (v > level) level = v;
+ }
+ }
+ level >>= 8;
+ break;
+
+ case AFMT_S24_NE:
+ case AFMT_S32_NE:
+ {
+ int32 * p;
+
+ p = (int32 *)buf;
+
+ for (i = 0; i < l / 4; i++) {
+ v = *p++;
+ if (v < 0) {
+ if (v != -v) v = -v;
+ else v = -(v+1);
+ }
+ if (v > level) level = v;
+ }
+ }
+ level >>= 24;
+ break;
+ default: return -1;
+ }
+
+ if (level > 255) level = 255;
+ v = db_table[level];
+
+ return v;
+}
+
+static decoders_queue_t *
+setup_normalize (int * format, int * obsize, decoders_queue_t * decoders)
+{
+ if ((*format == AFMT_S16_OE) || (*format == AFMT_S32_OE) ||
+ (*format == AFMT_S24_OE) || (*format == AFMT_U16_LE) ||
+ (*format == AFMT_U16_BE))
+ {
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->decoder = decode_endian;
+ decoders->metadata = (void *)(intptr)*format;
+ switch (*format)
+ {
+ case AFMT_S32_OE: *format = AFMT_S32_NE; break;
+ case AFMT_S24_OE: *format = AFMT_S24_NE; break;
+ default: *format = AFMT_S16_NE; break;
+ }
+ decoders->next = NULL;
+ decoders->outbuf = NULL;
+ decoders->flag = 0;
+ }
+ else if ((*format == AFMT_U8) || (*format == AFMT_MU_LAW) ||
+ (*format == AFMT_S8) || (*format == AFMT_A_LAW))
+ {
+ decoders->next =
+ (decoders_queue_t *)ossplay_malloc (sizeof (decoders_queue_t));
+ decoders = decoders->next;
+ decoders->decoder = decode_8_to_s16;
+ decoders->metadata = (void *)(intptr)*format;
+ decoders->next = NULL;
+ *obsize *= 2;
+ decoders->outbuf = (unsigned char *)ossplay_malloc (*obsize);
+ decoders->flag = FREE_OBUF;
+ *format = AFMT_S16_NE;
+ }
+ return decoders;
+}
+
+verbose_values_t *
+setup_verbose (int format, double oconstant, double total_time)
+{
+ verbose_values_t * val;
+
+ val = (verbose_values_t *)ossplay_malloc (sizeof (verbose_values_t));
+
+ if (total_time == 0)
+ {
+ val->tsecs = 0;
+ strcpy (val->tstring, "unknown");
+ }
+ else
+ {
+ char * p;
+
+ val->tsecs = total_time;
+ p = totime (val->tsecs);
+ strncpy (val->tstring, p, sizeof (val->tstring));
+ ossplay_free (p);
+ val->tsecs -= UPDATE_EPSILON/1000;
+ }
+
+ val->secs = 0;
+ val->secs_timer2 = 0;
+ val->next_sec = 0;
+ val->next_sec_timer2 = 0;
+ val->format = format;
+ val->constant = oconstant;
+
+ return val;
+}
+
+static errors_t
+seek_normal (int fd, big_t * datamark, big_t filesize, double constant,
+ big_t rsize, int channels, void * metadata)
+{
+ big_t pos = seek_time * constant;
+ int ret;
+
+ pos -= pos % channels;
+ if ((pos > filesize) || (pos < *datamark)) return E_DECODE;
+
+ ret = ossplay_lseek (fd, pos - *datamark, SEEK_CUR);
+ if (ret == -1)
+ {
+ seek_time = 0;
+ return E_DECODE;
+ }
+ *datamark = ret;
+
+ return E_OK;
+}
+
+static errors_t
+seek_compressed (int fd, big_t * datamark, big_t filesize, double constant,
+ big_t rsize, int channels, void * metadata)
+/*
+ * We have to use this method because some compressed formats depend on the
+ * previous state of the decoder, and don't (yet?) have own seek function.
+ */
+{
+ big_t pos = seek_time * constant;
+
+ if (pos > filesize)
+ {
+ seek_time = 0;
+ return E_DECODE;
+ }
+
+ if (*datamark + rsize < pos)
+ {
+ return SEEK_CONT_AFTER_DECODE;
+ }
+ else
+ {
+ /* Still not entirely accurate. */
+ seek_time = *datamark / constant;
+ return E_OK;
+ }
+}
+
+static ssize_t
+read_normal (int fd, void * buf, size_t len, void * metadata)
+{
+ return read (fd, buf, len);
+}
+
+#ifdef OGG_SUPPORT
+static errors_t
+seek_ogg (int fd, big_t * datamark, big_t filesize, double constant,
+ big_t rsize, int channels, void * metadata)
+{
+ ogg_data_t * val = (ogg_data_t *)metadata;
+
+ if (val->f->ov_time_seek (&val->vf, seek_time) < 0)
+ {
+ seek_time = 0;
+ return E_DECODE;
+ }
+ *datamark = (big_t)val->f->ov_raw_tell (&val->vf);
+ return E_OK;
+}
+
+static ssize_t
+read_ogg (int fd, void * buf, size_t len, void * metadata)
+{
+ int c_bitstream;
+ ssize_t ret = 0;
+ ogg_data_t * val = (ogg_data_t *)metadata;
+
+ if (val->setup == 1)
+ {
+ vorbis_info * vi;
+
+ vi = val->f->ov_info (&val->vf, -1);
+
+ ret = setup_device (val->dsp, AFMT_S16_NE, vi->channels, vi->rate);
+ if (ret < 0) return -1;
+ val->setup = 0;
+ }
+
+ do
+ {
+#if 0
+ if (ret == OV_HOLE)
+ print_msg (NOTIFYM, "Hole in the OggVorbis stream!\n");
+#endif
+ c_bitstream = val->bitstream;
+ ret = (ssize_t)val->f->ov_read (&val->vf, (char *)buf, (int)len,
+#ifdef OSS_LITTLE_ENDIAN
+ 0,
+#else
+ 1,
+#endif
+ 2, 1, &val->bitstream);
+ }
+ while (ret == OV_HOLE);
+
+ if (ret == 0) return 0;
+ else if (ret < 0) return ret;
+
+ if ((c_bitstream != val->bitstream) && (c_bitstream != -1))
+ {
+ val->bitstream = c_bitstream;
+ val->setup = 1;
+ }
+
+ return ret;
+}
+#endif
+
+#ifdef SRC_SUPPORT
+#define GRC3_HIGH_QUALITY 4
+static ssize_t
+decode_mono_to_stereo (unsigned char ** obuf, unsigned char * buf,
+ ssize_t l, void * metadata)
+{
+ ssize_t i;
+ int format = (int)(intptr)metadata;
+
+ switch (format) {
+ case AFMT_U8:
+ case AFMT_S8: {
+ uint8 *r = (uint8 *)buf, *s = (uint8 *)*obuf;
+ for (i=0; i < l; i++) {
+ *s++ = *r;
+ *s++ = *r++;
+ }
+ }
+ break;
+ case AFMT_S16_LE:
+ case AFMT_S16_BE: {
+ int16 *r = (int16 *)buf, *s = (int16 *)*obuf;
+
+ for (i = 0; i < l/2 ; i++) {
+ *s++ = *r;
+ *s++ = *r++;
+ }
+ }
+ break;
+ case AFMT_S32_LE:
+ case AFMT_S32_BE:
+ case AFMT_S24_LE:
+ case AFMT_S24_BE: {
+ int32 *r = (int32 *)buf, *s = (int32 *)*obuf;
+
+ for (i = 0; i < l/4; i++) {
+ *s++ = *r;
+ *s++ = *r++;
+ }
+ }
+ break;
+ }
+ return 2*l;
+}
+
+static ssize_t
+decode_src (unsigned char ** obuf, unsigned char * buf, ssize_t l, void * metadata)
+{
+ grc_data_t * val = (grc_data_t *)metadata;
+ ssize_t outc = 0;
+ int i;
+
+ for (i=0; i<val->channels; i++) {
+ outc += grc3_convert (&val->grc[i], val->bits, GRC3_HIGH_QUALITY, buf,
+ *obuf, 8 * l / val->channels / val->bits,
+ val->obsize / val->channels / sizeof (int),
+ val->channels, i);
+ }
+
+ return outc * val->bits / 8;
+}
+
+static grc_data_t *
+setup_grc3 (int format, int channels, int speed, int ospeed, int obsize)
+{
+ int i;
+ grc_data_t * val = (grc_data_t *)ossplay_malloc (sizeof (grc_data_t) +
+ sizeof (grc3state_t) * channels);
+
+ val->bits = format2bits (format);
+ val->channels = channels;
+ val->speed = speed;
+ val->ospeed = ospeed;
+ val->obsize = obsize;
+
+ for (i=0; i<channels; i++) {
+ grc3_reset (&val->grc[i]);
+ grc3_setup (&val->grc[i], ospeed, speed);
+ }
+
+ return val;
+}
+#endif
+
diff --git a/cmd/ossplay/ossplay_decode.h b/cmd/ossplay/ossplay_decode.h
new file mode 100644
index 0000000..c05fe8b
--- /dev/null
+++ b/cmd/ossplay/ossplay_decode.h
@@ -0,0 +1,30 @@
+#ifndef OSSPLAY_DECODE_H
+#define OSSPLAY_DECODE_H
+
+#include "ossplay.h"
+
+#define MAC_IMA_BLKLEN 34
+/*
+ * ima4 block length in AIFC files. Qt has "stsd" chunk which can change this,
+ * but I know of no AIFC equivalent.
+ */
+
+typedef struct verbose_values {
+ char tstring[20];
+ double secs;
+ double next_sec;
+ double secs_timer2;
+ double next_sec_timer2;
+ double tsecs;
+ double constant;
+ int format;
+}
+verbose_values_t;
+
+errors_t decode_sound (dspdev_t *, int, big_t, int, int, int, void *);
+errors_t encode_sound (dspdev_t *, fctypes_t, const char *, int, int, int,
+ double);
+int get_db_level (const unsigned char *, ssize_t, int);
+verbose_values_t * setup_verbose (int, double, double);
+
+#endif
diff --git a/cmd/ossplay/ossplay_parser.c b/cmd/ossplay/ossplay_parser.c
new file mode 100644
index 0000000..fd7997f
--- /dev/null
+++ b/cmd/ossplay/ossplay_parser.c
@@ -0,0 +1,1830 @@
+/*
+ * Purpose: File format parse routines for ossplay
+ */
+/*
+ *
+ * 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_parser.h"
+#include "ossplay_decode.h"
+
+#include <ctype.h>
+#include <sys/stat.h>
+
+/* Magic numbers used in Sun and NeXT audio files (.au/.snd) */
+#define SUN_MAGIC 0x2e736e64 /* Really '.snd' */
+#define SUN_INV_MAGIC 0x646e732e /* '.snd' upside-down */
+#define DEC_MAGIC 0x2e736400 /* Really '\0ds.' (for DEC) */
+#define DEC_INV_MAGIC 0x0064732e /* '\0ds.' upside-down */
+
+/* Magic numbers for .w64 */
+#define riff_GUID 0x2E91CF11
+#define riff_GUID2 0xA5D628DB
+#define riff_GUID3 0x04C10000
+
+enum {
+ COMM_BIT,
+ SSND_BIT,
+ FVER_BIT
+};
+
+#define COMM_FOUND (1 << COMM_BIT)
+#define SSND_FOUND (1 << SSND_BIT)
+#define FVER_FOUND (1 << FVER_BIT)
+
+#define H(A, B, C, D) ((A << 24) | (B << 16) | (C << 8) | D)
+
+typedef struct {
+ msadpcm_values_t msadpcm_val;
+ int channels, fd, format, found, speed;
+ fctypes_t type;
+ uint32 chunk_id;
+ big_t cpos, chunk_size, cur_size, fut_size, sound_loc, sound_size, total_size;
+ const char * filename;
+ big_t (* ne_int) (const unsigned char *, int);
+}
+file_t;
+
+typedef int (chunk_parser_t) (uint32, unsigned char *, big_t, file_t *);
+
+enum {
+ CP_STOP_READING = -2,
+ CP_PLAY_NOW,
+ CP_OK
+};
+
+typedef ssize_t (file_read_t) (file_t *, unsigned char *, size_t);
+typedef int (file_init_t) (file_t *, unsigned char *);
+typedef int (file_iterator_t) (file_t *, unsigned char *, int);
+typedef ssize_t (file_seek_t) (file_t *, off_t, int);
+
+typedef enum {
+ R_ZERO_FLAG,
+ READ_NONE,
+ READ_ALL,
+ READ_PART
+}
+read_flag_t;
+
+typedef struct chunk_functions {
+ const uint32 id;
+ const uint32 d_chunk_size;
+ const read_flag_t read_chunk_f;
+ chunk_parser_t * f;
+}
+chunk_functions_t;
+
+typedef struct parser {
+ file_init_t * init;
+ file_read_t * read;
+ file_iterator_t * iterator;
+ const chunk_functions_t * perfile;
+ const chunk_functions_t * common;
+}
+parser_t;
+
+extern int quiet, verbose, force_fmt, force_speed;
+extern long seek_byte;
+extern flag from_stdin, raw_file;
+extern off_t (*ossplay_lseek) (int, off_t, int);
+
+static errors_t play_au (dspdev_t *, const char *, int, unsigned char *, int);
+static errors_t play_iff (dspdev_t *, const char *, int, unsigned char *, int, parser_t *);
+static errors_t play_voc (dspdev_t *, const char *, int, unsigned char *, int);
+static void print_verbose_fileinfo (const char *, int, int, int, int);
+
+static file_init_t caf_init;
+static file_iterator_t caf_iterator;
+static file_init_t iff_init;
+static file_read_t iff_read;
+static file_iterator_t iff_iterator;
+static chunk_parser_t iff_comment_parse;
+static file_init_t w64_init;
+static file_iterator_t w64_iterator;
+
+static chunk_parser_t _16sv_vhdr_parse;
+static chunk_parser_t _8svx_vhdr_parse;
+static chunk_parser_t aifc_comm_parse;
+static chunk_parser_t aifc_fver_parse;
+static chunk_parser_t aiff_comm_parse;
+static chunk_parser_t aiff_ssnd_parse;
+static chunk_parser_t caf_data_parse;
+static chunk_parser_t caf_desc_parse;
+static chunk_parser_t maud_chan_parse;
+static chunk_parser_t maud_mhdr_parse;
+static chunk_parser_t wave_data_parse;
+static chunk_parser_t wave_disp_parse;
+static chunk_parser_t wave_fmt_parse;
+static chunk_parser_t wave_list_parse;
+
+static const chunk_functions_t IFF_common[] = {
+ { H('A', 'N', 'N', 'O'), 0, READ_ALL, &iff_comment_parse },
+ { H('N', 'A', 'M', 'E'), 0, READ_ALL, &iff_comment_parse },
+ { H('(', 'c', ')', ' '), 0, READ_ALL, &iff_comment_parse },
+ { H('A', 'U', 'T', 'H'), 0, READ_ALL, &iff_comment_parse },
+ { 0, 0, READ_NONE, NULL }
+};
+
+static const chunk_functions_t AIFF_funcs[] = {
+ { H('C', 'O', 'M', 'M'), 18, READ_ALL, &aiff_comm_parse },
+ { H('S', 'S', 'N', 'D'), 8, READ_PART, &aiff_ssnd_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t AIFC_funcs[] = {
+ { H('C', 'O', 'M', 'M'), 22, READ_ALL, &aifc_comm_parse },
+ { H('S', 'S', 'N', 'D'), 8, READ_PART, &aiff_ssnd_parse },
+ { H('F', 'V', 'E', 'R'), 4, READ_ALL, &aifc_fver_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t WAVE_funcs[] = {
+ { H('f', 'm', 't', ' '), 14, READ_ALL, &wave_fmt_parse },
+ { H('d', 'a', 't', 'a'), 0, READ_NONE, &wave_data_parse },
+ { H('D', 'I', 'S', 'P'), 5, READ_ALL, &wave_disp_parse },
+ { H('L', 'I', 'S', 'T'), 12, READ_ALL, &wave_list_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t _8SVX_funcs[] = {
+ { H('B', 'O', 'D', 'Y'), 0, READ_NONE, &wave_data_parse },
+ { H('V', 'H', 'D', 'R'), 16, READ_ALL, &_8svx_vhdr_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t _16SV_funcs[] = {
+ { H('V', 'H', 'D', 'R'), 14, READ_ALL, &_16sv_vhdr_parse },
+ { H('B', 'O', 'D', 'Y'), 0, READ_NONE, &wave_data_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t MAUD_funcs[] = {
+ { H('M', 'D', 'A', 'T'), 0, READ_NONE, &wave_data_parse },
+ { H('C', 'H', 'A', 'N'), 4, READ_ALL, &maud_chan_parse },
+ { H('M', 'H', 'D', 'R'), 20, READ_ALL, &maud_mhdr_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+static const chunk_functions_t CAF_funcs[] = {
+ { H('d', 'e', 's', 'c'), 32, READ_ALL, &caf_desc_parse },
+ { H('d', 'a', 't', 'a'), 4, READ_NONE, &caf_data_parse },
+ { 0, 0, R_ZERO_FLAG, NULL }
+};
+
+#ifdef OGG_SUPPORT
+static errors_t play_ogg (dspdev_t *, const char *, int, unsigned char *, int,
+ dlopen_funcs_t **);
+
+/*
+ * OV_CALLBACKS_DEFAULT is not defined by older Vorbis versions so
+ * we have to define our own version.
+ */
+static int _ov_header_fseek_wrap_ (FILE *f, ogg_int64_t off, int whence)
+{
+ if (f == NULL) return -1;
+ return fseek(f, off, whence);
+}
+
+static ov_callbacks OV_CALLBACKS_DEFAULT_ = {
+ (size_t (*)(void *, size_t, size_t, void *)) fread,
+ (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap_,
+ (int (*)(void *)) fclose,
+ (long (*)(void *)) ftell
+};
+
+/* Only handles Ogg/Vorbis */
+static errors_t
+play_ogg (dspdev_t * dsp, const char * filename, int fd, unsigned char * hdr,
+ int l, dlopen_funcs_t ** vft)
+{
+
+ FILE * f;
+ errors_t ret = E_OK;
+ ogg_data_t ogg_data;
+ vorbis_info * vi;
+
+ f = fdopen (fd, "rb");
+
+ if (*vft == NULL)
+ {
+ *vft = (dlopen_funcs_t *)ossplay_malloc (sizeof (dlopen_funcs_t));
+
+ (*vft)->vorbisfile_handle = ossplay_dlopen ("libvorbisfile.so.3");
+
+ if ((*vft)->vorbisfile_handle == NULL)
+ {
+ const char * msg = ossplay_dlerror();
+
+ ossplay_free (*vft);
+ *vft = NULL;
+ print_msg (ERRORM, "Can't dlopen libvorbisfile! (Error was %s)\n",
+ msg?msg:"");
+ return E_DECODE;
+ }
+
+ /*
+ * Assigning to the address pointed by the casted lvalue is the
+ * POSIX.1-2003 workaround to the dlsym return type issue, and both the
+ * opengroup and glibc examples use that method, so this should be safe
+ * on POSIX systems.
+ */
+ if (ossplay_vdlsym ((*vft)->vorbisfile_handle,
+ (void **)&(*vft)->ov_clear, "ov_clear",
+ (void **)&(*vft)->ov_comment, "ov_comment",
+ (void **)&(*vft)->ov_info, "ov_info",
+ (void **)&(*vft)->ov_open_callbacks, "ov_open_callbacks",
+ (void **)&(*vft)->ov_raw_tell, "ov_raw_tell",
+ (void **)&(*vft)->ov_read, "ov_read",
+ (void **)&(*vft)->ov_seekable, "ov_seekable",
+ (void **)&(*vft)->ov_time_total, "ov_time_total",
+ (void **)&(*vft)->ov_time_seek, "ov_time_seek",
+ (void **)NULL))
+ {
+ /* The call already took care of making an error printout for us */
+ ossplay_dlclose ((*vft)->vorbisfile_handle);
+ ossplay_free (*vft);
+ *vft = NULL;
+ return E_DECODE;
+ }
+ }
+
+ ogg_data.f = *vft;
+
+ if (ogg_data.f->ov_open_callbacks (f, &ogg_data.vf, (char *)hdr, l,
+ OV_CALLBACKS_DEFAULT_) < 0)
+ {
+ print_msg (ERRORM, "File %s is not an OggVorbis file!\n", filename);
+ return E_DECODE;
+ }
+
+ ogg_data.dsp = dsp;
+ ogg_data.setup = 0;
+ ogg_data.bitstream = -1;
+
+ vi = ogg_data.f->ov_info (&ogg_data.vf, -1);
+ if (verbose)
+ {
+ vorbis_comment * oc = ogg_data.f->ov_comment(&ogg_data.vf,-1);
+ char **p = oc->user_comments;
+
+ while (*p)
+ print_msg (VERBOSEM, "%s: Comments: %s\n", filename, *p++);
+ if ((verbose > 1) && (oc->vendor))
+ print_msg (VERBOSEM, "%s: Vendor: %s\n", filename, oc->vendor);
+ print_msg (VERBOSEM, "%s: Nominal bitrate: %d\n", filename, vi->bitrate_nominal);
+
+ print_verbose_fileinfo (filename, OGG_FILE, AFMT_VORBIS, vi->channels, vi->rate);
+ }
+
+ ret = decode_sound (dsp, fd, BIG_SPECIAL, AFMT_VORBIS, vi->channels, vi->rate,
+ (void *)&ogg_data);
+
+ ogg_data.f->ov_clear (&ogg_data.vf);
+
+ return ret;
+}
+#endif
+
+errors_t
+play_file (dspdev_t * dsp, const char * filename, dlopen_funcs_t ** dlt)
+{
+ int fd, id;
+ ssize_t l, i;
+ unsigned char buf[P_READBUF_SIZE];
+ const char * bname, * suffix;
+ struct stat st;
+ errors_t ret = E_OK;
+
+ parser_t piff = {
+ &iff_init,
+ &iff_read,
+ &iff_iterator,
+ NULL,
+ NULL
+ };
+
+ if (from_stdin) {
+ FILE *fp;
+
+ fp = fdopen(0, "rb");
+ fd = fileno(fp);
+ /*
+ * Use emulation if stdin is not seekable (e.g. on Linux).
+ */
+ if (lseek (fd, 0, SEEK_CUR) == -1) ossplay_lseek = ossplay_lseek_stdin;
+ errno = 0;
+ bname = "-";
+ } else {
+ fd = open (filename, O_RDONLY, 0);
+ bname = filepart (filename);
+ }
+
+ if (fd == -1)
+ {
+ perror_msg (filename);
+ return E_DECODE;
+ }
+
+ if (seek_byte != 0)
+ {
+ ossplay_lseek (fd, (off_t)seek_byte, SEEK_CUR);
+ }
+
+ if (raw_file)
+ {
+ big_t len;
+
+ if (fstat (fd, &st) == -1) {
+ perror_msg (filename);
+ len = BIG_SPECIAL;
+ } else {
+ len = st.st_size;
+ }
+ print_msg (NORMALM, "%s: Playing RAW file.\n", bname);
+
+ ret = decode_sound (dsp, fd, len, DEFAULT_FORMAT,
+ DEFAULT_CHANNELS, DEFAULT_SPEED, NULL);
+ goto done;
+ }
+
+ if ((l = read (fd, buf, 12)) == -1)
+ {
+ perror_msg (filename);
+ goto seekerror;
+ }
+
+ if (l == 0)
+ {
+ print_msg (ERRORM, "%s is empty file.\n", bname);
+ goto seekerror;
+ }
+
+/*
+ * Try to detect the file type
+ */
+ id = be_int (buf, 4);
+ switch (id) {
+ case SUN_MAGIC:
+ case DEC_MAGIC:
+ case SUN_INV_MAGIC:
+ case DEC_INV_MAGIC:
+ if ((i = read (fd, buf + 12, 12)) == -1) {
+ perror_msg (filename);
+ goto seekerror;
+ }
+ l += i;
+ if (l < 24) break;
+ ret = play_au (dsp, bname, fd, buf, l);
+ goto done;
+ case H('C', 'r', 'e', 'a'):
+ if ((i = read (fd, buf + 12, 7)) == -1) {
+ perror_msg (filename);
+ goto seekerror;
+ }
+ l += i;
+ if ((l < 19) || (memcmp (buf, "Creative Voice File", 19))) break;
+ ret = play_voc (dsp, bname, fd, buf, l);
+ goto done;
+ case H('R', 'I', 'F', 'F'):
+ case H('R', 'I', 'F', 'X'):
+ if ((l < 12) || be_int (buf + 8, 4) != H('W', 'A', 'V', 'E')) break;
+ if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MS_IMA_ADPCM;
+ piff.perfile = WAVE_funcs;
+ ret = play_iff (dsp, bname, fd, buf, (id == H('R', 'I', 'F', 'X'))?
+ WAVE_FILE_BE:WAVE_FILE, &piff);
+ goto done;
+ case H('r', 'i', 'f', 'f'):
+ if ((l < 12) || (read (fd, buf + 12, 4) < 4)) break;
+ if (be_int (buf + 4, 4) != riff_GUID) break;
+ if (be_int (buf + 8, 4) != riff_GUID2) break;
+ if (be_int (buf + 12, 4) != riff_GUID3) break;
+ piff.perfile = WAVE_funcs;
+ piff.iterator = w64_iterator;
+ piff.init = w64_init;
+ ret = play_iff (dsp, bname, fd, buf, W64_FILE, &piff);
+ goto done;
+ case H('F', 'O', 'R', 'M'):
+ if (l < 12) break;
+ piff.common = IFF_common;
+ switch (be_int (buf + 8, 4)) {
+ case H('A', 'I', 'F', 'F'):
+ if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MAC_IMA_ADPCM;
+ piff.perfile = AIFF_funcs;
+ ret = play_iff (dsp, bname, fd, buf, AIFF_FILE, &piff);
+ goto done;
+ case H('A', 'I', 'F', 'C'):
+ if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MAC_IMA_ADPCM;
+ piff.perfile = AIFC_funcs;
+ ret = play_iff (dsp, bname, fd, buf, AIFC_FILE, &piff);
+ goto done;
+ case H('8', 'S', 'V', 'X'):
+ piff.perfile = _8SVX_funcs;
+ ret = play_iff (dsp, bname, fd, buf, _8SVX_FILE, &piff);
+ goto done;
+ case H('1', '6', 'S', 'V'):
+ piff.perfile = _16SV_funcs;
+ ret = play_iff (dsp, bname, fd, buf, _16SV_FILE, &piff);
+ goto done;
+ case H('M', 'A', 'U', 'D'):
+ piff.perfile = MAUD_funcs;
+ ret = play_iff (dsp, bname, fd, buf, MAUD_FILE, &piff);
+ goto done;
+ default: break;
+ }
+ piff.common = NULL;
+ break;
+ case H('c', 'a', 'f', 'f'):
+ piff.init = caf_init;
+ piff.iterator = caf_iterator;
+ piff.perfile = CAF_funcs;
+ ret = play_iff (dsp, bname, fd, buf, CAF_FILE, &piff);
+ goto done;
+ case H('O', 'g', 'g', 'S'):
+#ifdef OGG_SUPPORT
+ ret = play_ogg (dsp, bname, fd, buf, l, dlt);
+ fd = -1;
+ goto done;
+#endif
+ default: break;
+ }
+
+ ossplay_lseek (fd, 0, SEEK_SET); /* Start from the beginning */
+
+/*
+ * The file was not identified by its content. Try using the file name
+ * suffix.
+ */
+
+ suffix = strrchr (filename, '.');
+ if (suffix == NULL) suffix = filename;
+
+ if (fstat (fd, &st) == -1)
+ {
+ perror_msg (filename);
+ return E_DECODE;
+ }
+
+ if (strcmp (suffix, ".au") == 0 || strcmp (suffix, ".AU") == 0)
+ { /* Raw mu-Law data */
+ print_msg (VERBOSEM, "Playing raw mu-Law file %s\n", bname);
+
+ ret = decode_sound (dsp, fd, st.st_size, AFMT_MU_LAW, 1, 8000, NULL);
+ goto done;
+ }
+
+ if (strcmp (suffix, ".snd") == 0 || strcmp (suffix, ".SND") == 0)
+ {
+ print_msg (VERBOSEM,
+ "%s: Unknown format. Assuming RAW audio (%d/%d/%d).\n",
+ bname, DEFAULT_SPEED, DEFAULT_FORMAT, DEFAULT_CHANNELS);
+
+ ret = decode_sound (dsp, fd, st.st_size, DEFAULT_FORMAT, DEFAULT_CHANNELS,
+ DEFAULT_SPEED, NULL);
+ goto done;
+ }
+
+ if (strcmp (suffix, ".cdr") == 0 || strcmp (suffix, ".CDR") == 0)
+ {
+ print_msg (VERBOSEM, "%s: Playing CD-R (cdwrite) file.\n", bname);
+
+ ret = decode_sound (dsp, fd, st.st_size, AFMT_S16_BE, 2, 44100, NULL);
+ goto done;
+ }
+
+
+ if (strcmp (suffix, ".raw") == 0 || strcmp (suffix, ".RAW") == 0)
+ {
+ print_msg (VERBOSEM, "%s: Playing RAW file.\n", bname);
+
+ ret = decode_sound (dsp, fd, st.st_size, DEFAULT_FORMAT, DEFAULT_CHANNELS,
+ DEFAULT_SPEED, NULL);
+ goto done;
+ }
+
+ print_msg (ERRORM, "%s: Unrecognized audio file type.\n", filename);
+done:
+ if (fd != -1) close (fd);
+
+#if 0
+ ioctl (fd, SNDCTL_DSP_SYNC, NULL);
+#endif
+ return ret;
+seekerror:
+ close (fd);
+ return E_DECODE;
+}
+
+/*
+ * Generalized parser for chunked files, especially IFF based ones - handles
+ * WAV, AIFF, AIFC, CAF, W64, RF64, 8SVX, 16SV and MAUD.
+ */
+static errors_t
+play_iff (dspdev_t * dsp, const char * filename, int fd, unsigned char * buf,
+ int type, parser_t * p)
+{
+ int ret;
+
+ sbig_t rbytes;
+ const chunk_functions_t * i, * oi;
+ file_t f = { {
+ 256, 496, 7, 4, {
+ {256, 0},
+ {512, -256},
+ {0, 0},
+ {192, 64},
+ {240, 0},
+ {460, -208},
+ {392, -232} },
+ DEFAULT_CHANNELS
+ } };
+
+ if (force_fmt != 0) f.format = force_fmt;
+ else f.format = AFMT_S16_LE;
+ f.channels = DEFAULT_CHANNELS;
+ f.speed = DEFAULT_SPEED;
+ f.fd = fd;
+ f.filename = filename;
+ f.cur_size = 12;
+ f.fut_size = 12;
+ f.type = type;
+
+ rbytes = p->init (&f, buf);
+ if (rbytes < 0) return E_DECODE;
+ if (verbose > 1)
+ print_msg (VERBOSEM, "FORM len = %u\n", f.total_size);
+
+ if (p->perfile) oi = p->perfile;
+ else if (p->common) oi = p->common;
+ else return E_DECODE;
+ while (!p->iterator (&f, buf, rbytes)) {
+ for (i = oi; i->id != 0;) {
+ if (i->id != f.chunk_id) {
+ i++;
+ if ((i->id == 0) && (p->common) && (!i->read_chunk_f)) i = p->common;
+ continue;
+ }
+ if (f.chunk_size < i->d_chunk_size) {
+ print_msg (ERRORM, "%c%c%c%c chunk's size (" _PRIbig_t ") is smaller "
+ "than the expected size (%d)!\n", buf[0], buf[1], buf[2],
+ buf[3], f.chunk_size, i->d_chunk_size);
+ break;
+ }
+ if (i->read_chunk_f != READ_NONE) {
+ int rlen = (f.chunk_size > P_READBUF_SIZE)?P_READBUF_SIZE:f.chunk_size;
+
+ if ((i->read_chunk_f == READ_PART) && (f.chunk_size >= i->d_chunk_size))
+ rlen = i->d_chunk_size;
+ if ((rlen = p->read (&f, buf, rlen)) < 0) goto nexta;
+ rbytes = rlen;
+ } else {
+ rbytes = f.chunk_size;
+ }
+ if (i->f == NULL) break;
+ ret = i->f (f.chunk_id, buf, rbytes, &f);
+ if (ret == CP_PLAY_NOW) {
+ if ((i->read_chunk_f == READ_NONE) && (i->d_chunk_size))
+ ossplay_lseek (f.fd, i->d_chunk_size, SEEK_CUR);
+ goto stdinext;
+ }
+ if (ret == CP_STOP_READING) goto nexta;
+ if (ret) return ret;
+ break;
+ }
+
+ if ((f.chunk_size >= rbytes) && (f.fut_size < f.total_size))
+ if (ossplay_lseek (f.fd, f.fut_size - f.cur_size - f.cpos, SEEK_CUR) < 0)
+ break;
+ rbytes = 0;
+ }
+
+nexta:
+ if ((f.found & COMM_FOUND) == 0) {
+ print_msg (ERRORM, "%s: Couldn't find format chunk!\n", filename);
+ return E_DECODE;
+ }
+
+ if ((f.found & SSND_FOUND) == 0) {
+ print_msg (ERRORM, "%s: Couldn't find sound chunk!\n", filename);
+ return E_DECODE;
+ }
+
+ if ((type == AIFC_FILE) && ((f.found & FVER_FOUND) == 0))
+ print_msg (WARNM, "%s: Couldn't find AIFC FVER chunk.\n", filename);
+
+ if (ossplay_lseek (f.fd, f.sound_loc, SEEK_SET) == -1) {
+ perror_msg (filename);
+ return E_DECODE;
+ }
+
+stdinext:
+ if (verbose)
+ print_verbose_fileinfo (filename, type, f.format, f.channels, f.speed);
+
+ return decode_sound (dsp, fd, f.sound_size, f.format, f.channels, f.speed,
+ (void *)&(f.msadpcm_val));
+}
+
+/*ARGSUSED*/
+static errors_t
+play_au (dspdev_t * dsp, const char * filename, int fd, unsigned char * hdr,
+ int l)
+{
+ int channels = 1, format = AFMT_S8, speed = 11025;
+ big_t filelen;
+ uint32 fmt = 0, i, p = 24, an_len = 0;
+
+ p = be_int (hdr + 4, 4);
+ fmt = be_int (hdr + 12, 4);
+ speed = be_int (hdr + 16, 4);
+ channels = be_int (hdr + 20, 4);
+ if (memcmp (hdr + 8, "\xFF\xFF\xFF\xFF", 4)) {
+ filelen = be_int (hdr + 8, 4);
+ if (verbose > 1)
+ print_msg (VERBOSEM, "%s: Filelen: " _PRIbig_t "\n", filename, filelen);
+ } else {
+ struct stat st;
+
+ if (from_stdin || (fstat (fd, &st) == -1)) filelen = BIG_SPECIAL;
+ else filelen = st.st_size - 24 - p;
+ if (verbose > 1)
+ print_msg (VERBOSEM, "%s: Filelen: unspecified\n", filename);
+ }
+ if (verbose > 2) print_msg (VERBOSEM, "%s: Offset: %u\n", filename, p);
+
+ if (force_fmt == 0) switch (fmt)
+ {
+ case 1:
+ format = AFMT_MU_LAW;
+ break;
+
+ case 2:
+ format = AFMT_S8;
+ break;
+
+ case 3:
+ format = AFMT_S16_BE;
+ break;
+
+ case 4:
+ format = AFMT_S24_PACKED_BE;
+ break;
+
+ case 5:
+ format = AFMT_S32_BE;
+ break;
+
+ case 6:
+ format = AFMT_FLOAT32_BE;
+ break;
+
+ case 7:
+ format = AFMT_DOUBLE64_BE;
+ break;
+
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ print_msg (ERRORM, "%s: G.72x ADPCM encoded .au files are not supported\n",
+ filename);
+ return E_FORMAT_UNSUPPORTED;
+
+ case 27:
+ format = AFMT_A_LAW;
+ break;
+
+ default:
+ print_msg (ERRORM, "%s: Unknown encoding %d.\n", filename, fmt);
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ if (verbose)
+ {
+ print_verbose_fileinfo (filename, AU_FILE, format, channels, speed);
+
+ if (p > 24)
+ {
+ unsigned char * tag;
+
+ if (p > 1047) an_len = 1023;
+ else an_len = p - 24;
+ if (read (fd, hdr, an_len) < an_len)
+ {
+ print_msg (ERRORM, "%s: Can't read %u bytes from pos 24\n",
+ filename, an_len);
+ return E_DECODE;
+ }
+
+ tag = hdr + an_len;
+ for (i = 0; i < an_len; i++)
+ {
+ if (!isprint (hdr[i])) hdr[i] = ' ';
+ else tag = hdr + i + 1;
+ }
+ *tag = '\0';
+ print_msg (VERBOSEM, "%s: Annotations: %s\n", filename, hdr);
+ }
+ }
+
+ if (ossplay_lseek (fd, p - l - an_len, SEEK_CUR) == -1)
+ {
+ perror_msg (filename);
+ print_msg (ERRORM, "Can't seek to the data chunk\n");
+ return E_DECODE;
+ }
+
+ return decode_sound (dsp, fd, filelen, format, channels, speed, NULL);
+}
+
+/*ARGSUSED*/
+static errors_t
+play_voc (dspdev_t * dsp, const char * filename, int fd, unsigned char * hdr,
+ int l)
+{
+#define VREAD(fd, buf, len) \
+ do { \
+ if (read (fd, buf, len) < len) \
+ { \
+ print_msg (ERRORM, "%s: Can't read %d bytes at pos %d\n", \
+ filename, len, l); \
+ return E_DECODE; \
+ } \
+ pos += len; \
+ } while (0)
+
+ uint32 blklen, data_offs, fmt, id, len, loopcount = 0, loopoffs = 4,
+ pos = l + 7, tmp, vers;
+ unsigned char buf[256], block_type;
+ flag plock = 0;
+ int speed = 11025, channels = 1, bits = 8, format = AFMT_U8;
+ errors_t ret;
+
+ if (read (fd, hdr + 19, 7) < 7)
+ {
+ print_msg (ERRORM, "%s: Not a valid .VOC file\n", filename);
+ return E_DECODE;
+ }
+
+ data_offs = le_int (hdr + 0x14, 2);
+ vers = le_int (hdr + 0x16, 2);
+ id = le_int (hdr + 0x18, 2);
+
+ if ((((~vers) + 0x1234) & 0xffff) != id)
+ {
+ print_msg (ERRORM, "%s: Not a valid .VOC file\n", filename);
+ return E_DECODE;
+ }
+
+ print_msg (VERBOSEM, "Playing .VOC file %s\n", filename);
+
+ /*LINTED*/ while (1)
+ {
+ if (ossplay_lseek (fd, data_offs - pos, SEEK_CUR) == -1)
+ {
+ print_msg (ERRORM, "%s: Can't seek to pos %d\n", filename, data_offs);
+ return E_DECODE;
+ }
+ pos = data_offs + 4;
+
+ if ((tmp = read (fd, buf, 1)) < 1)
+ {
+ /* Don't warn when read returns 0 - it may be end of file. */
+ if (tmp != 0)
+ print_msg (ERRORM,
+ "%s: Can't read 1 byte at pos %d\n", filename, l);
+ return E_DECODE;
+ }
+
+ block_type = buf[0];
+
+ if (block_type == 0)
+ return E_OK; /* End */
+
+ if (read (fd, buf, 3) != 3)
+ {
+ print_msg (ERRORM, "%s: Truncated .VOC file (%d)\n",
+ filename, buf[0]);
+ return E_DECODE;
+ }
+
+ blklen = len = le_int (buf, 3);
+
+ if (verbose > 2)
+ print_msg (VERBOSEM, "%s: %0x: Block type %d, len %d\n",
+ filename, data_offs, block_type, len);
+ switch (block_type)
+ {
+
+ case 1: /* Sound data buf */
+ if (!plock)
+ {
+ VREAD (fd, buf, 2);
+
+ tmp = 256 - buf[0]; /* Time constant */
+ speed = (1000000 + tmp / 2) / tmp / channels;
+
+ fmt = buf[1];
+ len -= 2;
+
+ if (force_fmt != 0) break;
+ switch (fmt)
+ {
+ case 0: format = AFMT_U8; break;
+ case 1: format = AFMT_CR_ADPCM_4; break;
+ case 2: format = AFMT_CR_ADPCM_3; break;
+ case 3: format = AFMT_CR_ADPCM_2; break;
+ case 4: format = AFMT_S16_LE; break;
+ case 6: format = AFMT_A_LAW; break;
+ case 7: format = AFMT_MU_LAW; break;
+ default:
+ print_msg (ERRORM,
+ "%s: encoding %d is not supported\n",
+ filename, fmt);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ }
+
+ case 2: /* Continuation data */
+ if ((ret = decode_sound(dsp, fd, len, format, channels, speed, NULL)))
+ return ret;
+ pos += len;
+ break;
+
+ case 3: /* Silence */
+ VREAD (fd, buf, 3);
+ len = le_int (buf, 2);
+ tmp = 256 - buf[2]; /* Time constant */
+ speed = (1000000 + tmp / 2) / tmp;
+ if ((ret = silence (dsp, len, speed))) return ret;
+ break;
+
+ case 5: /* Text */
+ if (!quiet)
+ {
+ size_t i;
+
+ if (len > 256) len = 256;
+ VREAD (fd, buf, len);
+ for (i = 0; i < len; i++) if (!isprint (buf[i])) buf[i] = '.';
+ buf[len-1] = '\0';
+ print_msg (NORMALM, "Text: %s\n", buf);
+ }
+ break;
+
+ case 6: /* Loop start */
+ VREAD (fd, buf, 2);
+ loopoffs = data_offs + blklen + 4;
+ loopcount = le_int (buf, 2);
+ break;
+
+ case 7: /* End of repeat loop */
+ if (loopcount != 0xffff) loopcount--;
+
+ /* Set "return" point. Compensate for increment of data_offs. */
+ if (loopcount > 0) data_offs = loopoffs - blklen - 4;
+
+ break;
+
+ case 8: /* Sampling parameters */
+ VREAD (fd, buf, 4);
+
+ speed = 256000000/(channels * (65536 - le_int (buf, 2)));
+ channels = buf[3] + 1;
+ fmt = buf[2];
+ plock = 1;
+
+ if (force_fmt != 0) break;
+ switch (fmt)
+ {
+ case 0: format = AFMT_U8; break;
+ case 1: format = AFMT_CR_ADPCM_4; break;
+ case 2: format = AFMT_CR_ADPCM_3; break;
+ case 3: format = AFMT_CR_ADPCM_2; break;
+ case 4: format = AFMT_S16_LE; break;
+ case 6: format = AFMT_A_LAW; break;
+ case 7: format = AFMT_MU_LAW; break;
+ default:
+ print_msg (ERRORM,
+ "%s: encoding %d is not supported\n", filename, fmt);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ break;
+
+ case 9: /* New format sound data */
+ VREAD (fd, buf, 12);
+
+ len -= 12;
+
+ speed = le_int (buf, 3);
+ bits = buf[4];
+ channels = buf[5];
+ fmt = le_int (buf + 6, 2);
+
+ if (force_fmt == 0) switch (fmt)
+ {
+ case 0: format = AFMT_U8; break;
+ case 1: format = AFMT_CR_ADPCM_4; break;
+ case 2: format = AFMT_CR_ADPCM_3; break;
+ case 3: format = AFMT_CR_ADPCM_2; break;
+ case 4: format = AFMT_S16_LE; break;
+ case 6: format = AFMT_A_LAW; break;
+ case 7: format = AFMT_MU_LAW; break;
+ default:
+ print_msg (ERRORM,
+ "%s: encoding %d is not supported\n", filename, fmt);
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ if ((ret = decode_sound(dsp, fd, len, format, channels, speed, NULL)))
+ return ret;
+ pos += len;
+ break;
+ }
+
+ if (block_type != 8) plock = 0;
+ data_offs += blklen + 4;
+ }
+ /* return 0; */ /* Not reached */
+}
+
+static void
+print_verbose_fileinfo (const char * filename, int type, int format,
+ int channels, int speed)
+{
+ char chn[32];
+ const char * fmt = "";
+
+ if (force_speed) speed = force_speed;
+ switch (type)
+ {
+ case WAVE_FILE:
+ case WAVE_FILE_BE:
+ print_msg (VERBOSEM, "Playing WAVE file %s, ", filename); break;
+ case AIFC_FILE:
+ print_msg (VERBOSEM, "Playing AIFC file %s, ", filename); break;
+ case AIFF_FILE:
+ print_msg (VERBOSEM, "Playing AIFF file %s, ", filename); break;
+ case CAF_FILE:
+ print_msg (VERBOSEM, "Playing CAF file %s, ", filename); break;
+ case AU_FILE:
+ print_msg (VERBOSEM, "Playing AU file %s, ", filename); break;
+ case _8SVX_FILE:
+ print_msg (VERBOSEM, "Playing 8SVX file %s, ", filename); break;
+ case _16SV_FILE:
+ print_msg (VERBOSEM, "Playing 16SV file %s, ", filename); break;
+ case MAUD_FILE:
+ print_msg (VERBOSEM, "Playing MAUD file %s, ", filename); break;
+ case W64_FILE:
+ print_msg (VERBOSEM, "Playing W64 file %s, ", filename); break;
+ case OGG_FILE:
+ print_msg (VERBOSEM, "Playing OGG file %s, ", filename); break;
+ }
+
+ if (channels == 1)
+ strcpy (chn, "mono");
+ else if (channels == 2)
+ strcpy (chn, "stereo");
+ else
+ snprintf (chn, sizeof(chn), "%d channels", channels);
+
+ switch (format)
+ {
+ case AFMT_QUERY: fmt = "Invalid format"; break;
+ case AFMT_MAC_IMA_ADPCM:
+ case AFMT_MS_IMA_ADPCM:
+ case AFMT_IMA_ADPCM: fmt = "IMA ADPCM"; break;
+ case AFMT_MS_IMA_ADPCM_3BITS: fmt = "3BIT DVI ADPCM"; break;
+ case AFMT_MS_ADPCM: fmt = "MS-ADPCM"; break;
+ case AFMT_MU_LAW: fmt = "mu-law"; break;
+ case AFMT_A_LAW: fmt = "A-law"; break;
+ case AFMT_U8:
+ case AFMT_S8: fmt = "8 bits"; break;
+ case AFMT_S16_LE:
+ case AFMT_S16_BE:
+ case AFMT_U16_LE:
+ case AFMT_U16_BE: fmt = "16 bits"; break;
+ case AFMT_S24_LE:
+ case AFMT_S24_BE:
+ case AFMT_S24_PACKED_BE:
+ case AFMT_S24_PACKED: fmt = "24 bits"; break;
+ case AFMT_SPDIF_RAW:
+ case AFMT_S32_LE:
+ case AFMT_S32_BE: fmt = "32 bits"; break;
+ case AFMT_FLOAT32_LE:
+ case AFMT_FLOAT32_BE: fmt = "32 bit float"; break;
+ case AFMT_FLOAT: fmt = "float"; break;
+ case AFMT_DOUBLE64_LE:
+ case AFMT_DOUBLE64_BE: fmt = "64 bit float"; break;
+ case AFMT_VORBIS: fmt = "vorbis"; break;
+ case AFMT_MPEG: fmt = "mpeg"; break;
+ case AFMT_FIBO_DELTA: fmt = "fibonacci delta"; break;
+ case AFMT_EXP_DELTA: fmt = "exponential delta"; break;
+ }
+ print_msg (VERBOSEM, "%s/%s/%d Hz\n", fmt, chn, speed);
+}
+
+static int
+iff_init (file_t * f, unsigned char * buf)
+{
+ struct stat st;
+
+ if (f->type != WAVE_FILE) f->ne_int = be_int;
+ else f->ne_int = le_int;
+ if (f->type == _8SVX_FILE) f->format = AFMT_S8;
+ else if (f->type == WAVE_FILE) f->format = AFMT_S16_LE;
+ else f->format = AFMT_S16_BE;
+ f->total_size = f->ne_int (buf + 4, 4) + 8;
+
+ if (from_stdin) return 0;
+ if (fstat (f->fd, &st) == -1) return 0;
+ if (st.st_size < f->total_size) {
+ f->total_size = st.st_size;
+ print_msg (NOTIFYM, "%s: File size is smaller than the form size!\n",
+ f->filename);
+ }
+ return 0;
+}
+
+static ssize_t
+iff_read (file_t * f, unsigned char * buf, size_t l)
+{
+ ssize_t ret;
+
+ if (l > P_READBUF_SIZE) l = P_READBUF_SIZE;
+ if ((ret = read (f->fd, buf, l)) < l) return CP_STOP_READING;
+ f->cpos += ret;
+ return ret;
+}
+
+#if 0
+static ssize_t
+iff_seek (file_t * f, off_t off, int flags)
+{
+ off_t l;
+
+ /* We only do SEEK_CUR since we want to be able to stream from stdin */
+ if (flags != SEEK_CUR) return -1;
+ if (off <= 0) return 0;
+
+ l = ossplay_lseek (f->fd, off, flags);
+ return l;
+}
+#endif
+
+static int
+iff_iterator (file_t * f, unsigned char * buf, int l)
+{
+ f->cur_size = f->fut_size + 8;
+ if (f->cur_size >= f->total_size) return 1;
+
+ if (read (f->fd, buf, 8) < 8) {
+ print_msg (ERRORM, "%s: Cannot read chunk header at pos %u\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return E_DECODE;
+ }
+ f->chunk_id = be_int (buf, 4);
+ f->chunk_size = f->ne_int (buf + 4, 4);
+ f->cpos = 0;
+ f->fut_size += f->chunk_size + (f->chunk_size & 1) + 8;
+
+ if (verbose > 3)
+ print_msg (VERBOSEM, "%s: Reading chunk %c%c%c%c, size %d, pos %d, next %d\n",
+ f->filename, buf[0], buf[1], buf[2], buf[3], f->chunk_size,
+ f->cur_size - 8, f->fut_size);
+
+ if (f->chunk_size == 0) {
+ print_msg (NOTIFYM, "%s: Chunk size equals 0 (pos: %u)!\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return iff_iterator (f, buf, l);
+ }
+
+ return 0;
+}
+
+static int
+iff_comment_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ unsigned char * tag;
+ uint32 i;
+
+ if (!verbose) return 0;
+
+ print_msg (STARTM, "%s: ", f->filename);
+ switch (id) {
+ case H('N', 'A', 'M', 'E'):
+ print_msg (CONTM, "Name: ");
+ break;
+ case H('A', 'U', 'T', 'H'):
+ print_msg (CONTM, "Author: ");
+ break;
+ case H('(', 'c', ')', ' '):
+ print_msg (CONTM, "Copyright: ");
+ break;
+ case H('A', 'N', 'N', 'O'):
+ print_msg (CONTM, "Annonations: ");
+ break;
+ /* Should never be reached */
+ default: return 0;
+ }
+
+ tag = buf + len + 1;
+ for (i = 0; i < len; i++) {
+ if (!isprint (buf[i])) buf[i] = ' ';
+ else tag = buf + i + 1;
+ }
+ *tag = '\0';
+ print_msg (ENDM, "%s\n", buf);
+ return 0;
+}
+
+#define BITS2SFORMAT(endian) \
+ do { \
+ if (force_fmt == 0) switch (bits) \
+ { \
+ case 8: f->format = AFMT_S8; break; \
+ case 16: f->format = AFMT_S16_##endian; break; \
+ case 24: f->format = AFMT_S24_PACKED_##endian; break; \
+ case 32: f->format = AFMT_S32_##endian; break; \
+ default: f->format = AFMT_S16_##endian; break; \
+ } break; \
+ } while (0)
+
+static int
+aiff_comm_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ int bits;
+
+ f->channels = be_int (buf, 2);
+#if 0
+ num_frames = be_int (buf + 2, 4); /* ossplay doesn't use this */
+#endif
+ bits = be_int (buf + 6, 2);
+ bits += bits % 8;
+ f->msadpcm_val.bits = bits;
+ BITS2SFORMAT (BE);
+ {
+ /*
+ * Conversion from SANE's IEEE-754 extended 80-bit to long double.
+ * We take some shortcuts which don't affect this application.
+ * See http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
+ * for format into.
+ */
+ int exp;
+ ldouble_t COMM_rate = 0;
+
+ exp = ((buf[8] & 127) << 8) + buf[9] - 16383;
+#if 0
+ /*
+ * This part of the mantissa will typically be resolved to
+ * sub-Hz rates which we don't support anyway.
+ */
+ COMM_rate = (buf[14] << 24) + (buf[15] << 16) +
+ (buf[16] << 8) + buf[17];
+ COMM_rate /= 1L << 32;
+#endif
+ COMM_rate += ((buf[10] & 127) << 24) + (buf[11] << 16) +
+ (buf[12] << 8) + buf[13];
+ COMM_rate = ossplay_ldexpl (COMM_rate, exp-31);
+ if (buf[10] & 128)
+ COMM_rate += ossplay_ldexpl (1, exp); /* Normalize bit */
+ if (buf[8] & 128) COMM_rate = -COMM_rate; /* Sign bit */
+ if ((exp == 16384) || (COMM_rate <= 0)) {
+ print_msg (ERRORM, "Invalid sample rate!\n");
+ return E_DECODE;
+ }
+ f->speed = COMM_rate;
+ }
+ f->found |= COMM_FOUND;
+ return 0;
+}
+
+static int
+aifc_comm_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ int ret, bits;
+
+ if ((ret = aiff_comm_parse (id, buf, len, f))) return ret;
+
+ bits = f->msadpcm_val.bits;
+ f->msadpcm_val.bits = 0;
+
+ if ((force_fmt != 0) || (len < 22)) return 0;
+ switch (be_int (buf + 18, 4)) {
+ case H('N', 'O', 'N', 'E'): break;
+ case H('i', 'n', '1', '6'): f->format = AFMT_S16_BE; break;
+ case H('i', 'n', '2', '4'): f->format = AFMT_S24_BE; break;
+ case H('i', 'n', '3', '2'): f->format = AFMT_S32_BE; break;
+ case H('n', 'i', '2', '4'): f->format = AFMT_S24_LE; break;
+ case H('2', '3', 'n', 'i'):
+ case H('n', 'i', '3', '2'): f->format = AFMT_S32_LE; break;
+ /*
+ * sowt/tows were intended as 16 bits only, but some recording
+ * programs misinterpret this. We can try to handle that,
+ * since complaint programs set the bits field to 16 anyway.
+ * (See QT doc chap.4 section 3).
+ */
+ case H('s', 'o', 'w', 't'): BITS2SFORMAT (LE); break;
+ case H('t', 'w', 'o', 's'): BITS2SFORMAT (BE); break;
+ case H('r', 'a', 'w', ' '): f->format = AFMT_U8; break;
+ case H('a', 'l', 'a', 'w'):
+ case H('A', 'L', 'A', 'W'): f->format = AFMT_A_LAW; break;
+ case H('u', 'l', 'a', 'w'):
+ case H('U', 'L', 'A', 'W'): f->format = AFMT_MU_LAW; break;
+ case H('i', 'm', 'a', '4'): f->format = AFMT_MAC_IMA_ADPCM; break;
+ case H('f', 'l', '3', '2'):
+ case H('F', 'L', '3', '2'): f->format = AFMT_FLOAT32_BE; break;
+ case H('f', 'l', '6', '4'):
+ case H('F', 'L', '6', '4'): f->format = AFMT_DOUBLE64_BE; break;
+ default:
+ print_msg (ERRORM,
+ "%s: error: %c%c%c%c compression is not supported\n",
+ f->filename, *(buf + 18), *(buf + 19),
+ *(buf + 20), *(buf + 21));
+ return E_FORMAT_UNSUPPORTED;
+ }
+ return 0;
+}
+
+static int
+aiff_ssnd_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ uint32 offset;
+
+ if (f->found & SSND_FOUND) {
+ print_msg (ERRORM, "%s: error: SSND hunk not singular!\n", f->filename);
+ return E_DECODE;
+ }
+ f->found |= SSND_FOUND;
+
+ offset = be_int (buf, 4);
+#if 0
+ block_size = be_int (buf + 4, 4); /* ossplay doesn't use this */
+#endif
+ f->sound_loc = f->cur_size + 8 + offset;
+ f->sound_size = f->chunk_size - 8;
+ if (verbose > 2)
+ print_msg (VERBOSEM, "DATA chunk. Offs = " _PRIbig_t ", len = " _PRIbig_t
+ "\n", f->sound_loc, f->sound_size);
+
+ if (from_stdin) return CP_PLAY_NOW;
+ return 0;
+}
+
+static int
+aifc_fver_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ uint32 timestamp = be_int (buf, 4);
+ if (timestamp != 0xA2805140)
+ print_msg (WARNM, "%s: Timestamp doesn't match AIFC v1 timestamps!\n",
+ f->filename);
+ f->found |= FVER_FOUND;
+ return 0;
+}
+
+static int
+_8svx_vhdr_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ f->speed = be_int (buf + 12, 2);
+ f->found |= COMM_FOUND;
+ if (force_fmt != 0) return 0;
+ switch (buf[15]) {
+ case 0: f->format = AFMT_S8; break;
+ case 1: f->format = AFMT_FIBO_DELTA; break;
+ case 2: f->format = AFMT_EXP_DELTA; break;
+ default:
+ print_msg (ERRORM, "%s: Unsupported compression %d\n",
+ f->filename, buf[15]);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ return 0;
+}
+
+static int
+_16sv_vhdr_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ f->speed = be_int (buf + 12, 2);
+ f->found |= COMM_FOUND;
+ if (force_fmt != 0) f->format = AFMT_S16_BE;
+ return 0;
+}
+
+static int
+maud_chan_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ f->channels = be_int (buf, 4);
+ if (f->channels > 2) {
+ print_msg (ERRORM, "ossplay doesn't support MAUD format's "
+ "%d channel configuration\n", f->channels);
+ return E_CHANNELS_UNSUPPORTED;
+ }
+ return 0;
+}
+
+static int
+maud_mhdr_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ int bits = be_int (buf + 4, 2);
+
+ BITS2SFORMAT (BE);
+
+ if (be_int (buf + 12, 2) == 0) {
+ print_msg (ERRORM, "Invalid rate!\n");
+ return E_DECODE;
+ }
+ f->speed = be_int (buf + 8, 4) / be_int (buf + 12, 2);
+ f->channels = be_int (buf + 16, 2);
+ f->found |= COMM_FOUND;
+
+ if (force_fmt != 0) return 0;
+ switch (be_int (buf + 18, 2)) {
+ case 0: /* NONE */ break;
+ case 2: f->format = AFMT_A_LAW; break;
+ case 3: f->format = AFMT_MU_LAW; break;
+ case 6: f->format = AFMT_IMA_ADPCM; break;
+ default:
+ print_msg (ERRORM, "%s: format not supported", f->filename);
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static int
+wave_data_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ if (f->found & SSND_FOUND) {
+ print_msg (ERRORM, "%s: error: SSND hunk not singular!\n", f->filename);
+ return E_DECODE;
+ }
+
+ f->sound_loc = f->cur_size;
+ f->sound_size = f->chunk_size;
+ f->found |= SSND_FOUND;
+ if (verbose > 2)
+ print_msg (VERBOSEM, "DATA chunk. Offs = " _PRIbig_t ", len = " _PRIbig_t
+ "\n", f->sound_loc, f->sound_size);
+
+ if (from_stdin) return CP_PLAY_NOW;
+ return 0;
+}
+
+/* Cool Edit can create this chunk. Also some Windows files use it */
+static int
+wave_disp_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ unsigned char * tag;
+ int i;
+
+ if (verbose < 2) return 0;
+ if (f->ne_int (buf, 4) != 1) return 0;
+
+ buf += 4;
+ tag = buf + len;
+ for (i = 0; i < len-1; i++)
+ {
+ if (!isprint (buf[i])) buf[i] = ' ';
+ else tag = buf + i + 1;
+ }
+ *tag = '\0';
+ print_msg (VERBOSEM, "%s: %s\n", f->filename, buf);
+ return 0;
+}
+
+static int
+wave_list_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ unsigned char * sbuf = buf + 4, * tag;
+ int cssize = 4, i, slen = 4, subchunk_size;
+ uint32 chunk_id, subchunk_id;
+
+ if (!verbose) return 0;
+ chunk_id = be_int (buf, 4);
+ if (chunk_id != H('I', 'N', 'F', 'O')) return 0;
+ do {
+ subchunk_id = be_int (sbuf, 4);
+ subchunk_size = f->ne_int (sbuf + 4, 4);
+ if (verbose > 3)
+ print_msg (VERBOSEM, "%s: Reading subchunk %c%c%c%c, size %d\n",
+ f->filename, sbuf[0], sbuf[1], sbuf[2], sbuf[3],
+ subchunk_size);
+ if (subchunk_size == 0) return 0;
+ cssize += subchunk_size;
+ if (cssize > len) return 0;
+ sbuf += 8;
+ slen = subchunk_size + (subchunk_size & 1);
+ switch (subchunk_id) {
+ case H('I', 'A', 'R', 'L'):
+ print_msg (STARTM, "%s: Archival Location: ", f->filename);
+ break;
+ case H('I', 'A', 'R', 'T'):
+ print_msg (STARTM, "%s: Artist Name: ", f->filename);
+ break;
+ case H('I', 'C', 'M', 'S'):
+ print_msg (STARTM, "%s: Commissioned: ", f->filename);
+ break;
+ case H('I', 'C', 'M', 'T'):
+ print_msg (STARTM, "%s: Comment: ", f->filename);
+ break;
+ case H('I', 'C', 'O', 'P'):
+ print_msg (STARTM, "%s: Copyright: ", f->filename);
+ break;
+ case H('I', 'C', 'R', 'D'):
+ print_msg (STARTM, "%s: Creation date: ", f->filename);
+ break;
+ case H('I', 'E', 'N', 'G'):
+ print_msg (STARTM, "%s: Engineer: ", f->filename);
+ break;
+ case H('I', 'G', 'N', 'R'):
+ print_msg (STARTM, "%s: Genre: ", f->filename);
+ break;
+ case H('I', 'K', 'E', 'Y'):
+ print_msg (STARTM, "%s: Keywords: ", f->filename);
+ break;
+ case H('I', 'N', 'A', 'M'):
+ print_msg (STARTM, "%s: Name: ", f->filename);
+ break;
+ case H('I', 'P', 'R', 'D'):
+ print_msg (STARTM, "%s: Product: ", f->filename);
+ break;
+ case H('I', 'S', 'B', 'J'):
+ print_msg (STARTM, "%s: Subject: ", f->filename);
+ break;
+ case H('I', 'S', 'F', 'T'):
+ print_msg (STARTM, "%s: Software: ", f->filename);
+ break;
+ case H('I', 'S', 'R', 'C'):
+ print_msg (STARTM, "%s: Source: ", f->filename);
+ break;
+ case H('I', 'T', 'C', 'H'):
+ print_msg (STARTM, "%s: Technician: ", f->filename);
+ break;
+ default:
+ sbuf += slen;
+ continue;
+ }
+
+ tag = buf + slen;
+ /*
+ * According to the spec, all of the above hunks contain ZSTRs,
+ * so we can safely ignore the last char.
+ */
+ for (i = 0; i < slen-1; i++) {
+ if (!isprint (sbuf[i])) sbuf[i] = ' ';
+ else tag = sbuf + i + 1;
+ }
+ /* Remove trailing nonprintables */
+ *tag = '\0';
+ print_msg (ENDM, "%s\n", sbuf);
+ sbuf += slen;
+ cssize += 8;
+ } while (cssize < len);
+
+ return 0;
+}
+
+static int
+wave_fmt_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ unsigned int bits, i, x;
+ int wtype = 0x1;
+
+ if (f->found & COMM_FOUND) {
+ print_msg (ERRORM, "%s: error: fmt hunk not singular!\n",
+ f->filename);
+ return E_DECODE;
+ }
+
+ if (force_fmt == 0) wtype = f->format = f->ne_int (buf, 2);
+ if (verbose > 2)
+ print_msg (VERBOSEM, "FMT chunk: len = %u, fmt = %#x\n",
+ len, f->format);
+
+ f->msadpcm_val.channels = f->channels = f->ne_int (buf + 2, 2);
+ f->speed = f->ne_int (buf + 4, 4);
+#if 0
+ bytes_per_sec = be_int (buf + 8, 4); /* ossplay doesn't use this */
+#endif
+ f->msadpcm_val.nBlockAlign = f->ne_int (buf + 12, 2);
+ if (f->msadpcm_val.nBlockAlign == 0)
+ print_msg (WARNM, "%s: nBlockAlign is 0!\n", f->filename);
+ f->msadpcm_val.bits = bits = f->ne_int (buf + 14, 2);
+
+ if (wtype == 0xFFFE) { /* WAVE_FORMAT_EXTINSIBLE */
+ if (len < 40) {
+ print_msg (ERRORM, "%s: invalid fmt chunk\n", f->filename);
+ return E_DECODE;
+ }
+ /* TODO: parse the rest of WAVE_FORMAT_EXTENSIBLE */
+ f->format = f->ne_int (buf + 24, 2);
+ }
+ if (force_fmt == 0) switch (f->format) {
+ case 0x1: /* WAVE_FORMAT_PCM */
+ bits += bits % 8;
+ if (f->type == WAVE_FILE_BE) BITS2SFORMAT (BE);
+ else BITS2SFORMAT (LE);
+ if (bits == 8) f->format = AFMT_U8;
+ break;
+ case 0x2: /* WAVE_FORMAT_MS_ADPCM */
+ f->format = AFMT_MS_ADPCM;
+ break;
+ case 0x3: /* WAVE_FORMAT_IEEE_FLOAT */
+ if (bits == 32) f->format = AFMT_FLOAT32_LE;
+ else if (bits == 64) f->format = AFMT_DOUBLE64_LE;
+ else {
+ print_msg (ERRORM, "%s: Odd number of bits (%d) for "
+ "WAVE_FORMAT_IEEE_FLOAT!\n", f->filename, bits);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ break;
+ case 0x102: /* IBM_FORMAT_ALAW */
+ case 0x6: /* WAVE_FORMAT_ALAW */
+ f->format = AFMT_A_LAW;
+ break;
+ case 0x101: /* IBM_FORMAT_MULAW */
+ case 0x7: /* WAVE_FORMAT_MULAW */
+ f->format = AFMT_MU_LAW;
+ break;
+ case 0x11: /* WAVE_FORMAT_IMA_ADPCM */
+ if (bits == 4) f->format = AFMT_MS_IMA_ADPCM;
+ else if (bits == 3) f->format = AFMT_MS_IMA_ADPCM_3BITS;
+ else {
+ print_msg (ERRORM, "%s: Invalid number of bits (%d) for "
+ "WAVE_FORMAT_IMA_ADPCM!\n", f->filename, bits);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ break;
+#if 0
+ case 0x31: /* GSM 06.10 */
+ case 0x50: /* MPEG */
+ case 0x55: /* MPEG 3 */
+#endif
+ default:
+ print_msg (ERRORM, "%s: Unsupported wave format %#x\n",
+ f->filename, f->format);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ f->found |= COMM_FOUND;
+
+ if ((len < 20) ||
+ ((f->format != AFMT_MS_ADPCM) &&
+ (f->format != AFMT_MS_IMA_ADPCM) &&
+ (f->format != AFMT_MS_IMA_ADPCM_3BITS)
+ )
+ ) return 0;
+ f->msadpcm_val.wSamplesPerBlock = f->ne_int (buf + 18, 2);
+ if ((f->format != AFMT_MS_ADPCM) || (len < 22)) return 0;
+ f->msadpcm_val.wNumCoeff = f->ne_int (buf + 20, 2);
+ if (f->msadpcm_val.wNumCoeff > 32) f->msadpcm_val.wNumCoeff = 32;
+
+ x = 22;
+
+ for (i = 0; (i < f->msadpcm_val.wNumCoeff) && (x < len-3); i++) {
+ f->msadpcm_val.coeff[i].coeff1 = (int16) f->ne_int (buf + x, 2);
+ x += 2;
+ f->msadpcm_val.coeff[i].coeff2 = (int16) f->ne_int (buf + x, 2);
+ x += 2;
+ }
+ f->msadpcm_val.wNumCoeff = i;
+ return 0;
+}
+
+static int
+caf_init (file_t * f, unsigned char * buf)
+{
+ struct stat st;
+
+ memcpy (buf, buf+8, 4);
+ if (from_stdin || (fstat (f->fd, &st) == -1)) {
+ f->total_size = BIG_SPECIAL;
+ return 4;
+ }
+
+ f->total_size = st.st_size;
+ return 4;
+}
+
+static int
+caf_iterator (file_t * f, unsigned char * buf, int l)
+{
+ f->cur_size = f->fut_size + 12 - l;
+ if (f->cur_size >= f->total_size) return 1;
+
+ if (read (f->fd, buf + l, 12 - l) < 12 - l) {
+ print_msg (ERRORM, "%s: Cannot read chunk header at pos %u\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return E_DECODE;
+ }
+ f->chunk_id = be_int (buf, 4);
+ f->chunk_size = be_int (buf + 4, 8);
+ f->cpos = 0;
+ f->fut_size += f->chunk_size + 12 - l;
+
+ if (verbose > 3)
+ print_msg (VERBOSEM, "%s: Reading chunk %c%c%c%c, size %d, pos %d\n",
+ f->filename, buf[0], buf[1], buf[2], buf[3], f->chunk_size,
+ f->cur_size - 12);
+
+ if (f->chunk_size == 0) {
+ print_msg (NOTIFYM, "%s: Chunk size equals 0 (pos: %u)!\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return caf_iterator (f, buf, l);
+ }
+
+ return 0;
+}
+
+static int
+caf_data_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ if (f->found & SSND_FOUND) {
+ print_msg (ERRORM, "%s: error: SSND hunk not singular!\n", f->filename);
+ return E_DECODE;
+ }
+
+#if 0
+ uint32 editcount = be_int (buf, 4);
+#endif
+ f->sound_loc = f->cur_size + 4;
+ f->sound_size = f->chunk_size - 4;
+ f->found |= SSND_FOUND;
+
+ if (verbose > 2)
+ print_msg (VERBOSEM, "DATA chunk. Offs = " _PRIbig_t ", len = " _PRIbig_t
+ "\n", f->sound_loc, f->sound_size);
+ if (!memcmp (buf + 4, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8)) {
+ if (from_stdin) f->sound_size = BIG_SPECIAL;
+ /*
+ * CAF spec requires data chunks with -1 size to be last in file,
+ * so we're allowed to do the calculation below.
+ */
+ else f->sound_size = f->total_size - f->sound_loc;
+ return CP_PLAY_NOW;
+ }
+
+ if (from_stdin) return CP_PLAY_NOW;
+ return 0;
+}
+
+static int
+caf_desc_parse (uint32 id, unsigned char * buf, big_t len, file_t * f)
+{
+ int format;
+ uint32 bits, bytes_per_packet, flags, frames_per_packet;
+
+ {
+ /*
+ * Conversion from IEEE-754 extended 64-bit to double.
+ * We take some shortcuts which don't affect this application.
+ */
+ int exp;
+ double rate = 0;
+
+ exp = ((buf[0] & 127) << 4) + ((buf[1] & 240) >> 4) - 1023;
+#if 0
+ /*
+ * This part of the mantissa will typically be resolved to
+ * sub-Hz rates which we don't support anyway. We can also
+ * shave a few bits from buf[3] (buf[3] & 224 will do) and
+ * still get the same effect.
+ */
+ rate = (buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) +
+ buf[7];
+ rate /= 1L << 32;
+#endif
+ rate += ((buf[1] & 15) << 16) + (buf[2] << 8) + buf[3];
+ rate = ossplay_ldexpl (rate, exp-20);
+ if (exp != -1023)
+ rate += ossplay_ldexpl (1, exp); /* Normalize */
+ if (buf[0] & 128) rate = -rate; /* Sign bit */
+ if ((exp == 1024) || (rate <= 0)) {
+ print_msg (ERRORM, "%s: Invalid sample rate!\n", f->filename);
+ return E_DECODE;
+ }
+ f->speed = rate;
+ }
+
+ format = be_int (buf + 8, 4);
+ flags = be_int (buf + 12, 4);
+ bytes_per_packet = be_int (buf + 16, 4);
+ frames_per_packet = be_int (buf + 20, 4);
+ f->channels = be_int (buf + 24, 4);
+ bits = be_int (buf + 28, 4);
+ f->found |= COMM_FOUND;
+ if (force_fmt != 0) return 0;
+
+#define FLCHECK(fmt) do { \
+ f->format = (flags & 2)?AFMT_##fmt##_LE:AFMT_##fmt##_BE; \
+} while(0)
+
+ f->format = 0;
+ switch (format) {
+ case H('l', 'p', 'c', 'm'):
+ if ((flags & 1) && (bits != 32) && (bits != 64)) break;
+ switch (bits) {
+ case 8:
+ if (bytes_per_packet == f->channels) f->format = AFMT_S8;
+ break;
+ case 16:
+ if (bytes_per_packet == 2*f->channels) FLCHECK (S16);
+ break;
+ case 24:
+ if (bytes_per_packet == 3*f->channels) FLCHECK (S24_PACKED);
+ case 32:
+ if (bytes_per_packet == 4*f->channels) {
+ if (flags & 1) FLCHECK(FLOAT32);
+ else FLCHECK (S32);
+ }
+ break;
+ case 64:
+ if (flags & 1) FLCHECK (DOUBLE64);
+ default: break;
+ }
+ break;
+ case H('a', 'l', 'a', 'w'): f->format = AFMT_A_LAW; break;
+ case H('u', 'l', 'a', 'w'): f->format = AFMT_MU_LAW; break;
+ case H('i', 'm', 'a', '4'): f->format = AFMT_MAC_IMA_ADPCM; break;
+ default: break;
+ }
+ if (f->format == 0) {
+ print_msg (ERRORM, "%s: \"%c%c%c%c\" format (bits %d, bytes per "
+ "%d, flags 0x%X) is not supported\n", f->filename, buf[8],
+ buf[9], buf[10], buf[11], bits, bytes_per_packet, flags);
+ return E_FORMAT_UNSUPPORTED;
+ }
+ return 0;
+}
+
+static int
+w64_init (file_t * f, unsigned char * buf)
+{
+ struct stat st;
+
+ f->ne_int = le_int;
+ f->format = AFMT_S16_LE;
+ if (read (f->fd, buf, 8) < 8) return -1;
+ f->total_size = le_int (buf, 8);
+ f->fut_size = 40;
+ ossplay_lseek (f->fd, 16, SEEK_CUR);
+
+ if (from_stdin) return 0;
+ if (fstat (f->fd, &st) == -1) return 0;
+ if (st.st_size < f->total_size) {
+ f->total_size = st.st_size;
+ print_msg (NOTIFYM, "%s: File size is smaller than the form size!\n",
+ f->filename);
+ }
+ return 0;
+}
+
+static int
+w64_iterator (file_t * f, unsigned char * buf, int l)
+{
+ f->cur_size = f->fut_size + 24;
+ if (f->cur_size >= f->total_size) return 1;
+
+ if (read (f->fd, buf, 24) < 24) {
+ print_msg (ERRORM, "%s: Cannot read chunk header at pos %u\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return E_DECODE;
+ }
+
+ /* Only WAVE chunks are supported, so we can ignore the rest of the GUID */
+ f->chunk_id = be_int (buf, 4);
+ f->chunk_size = f->ne_int (buf + 16, 8);
+ f->cpos = 0;
+ f->fut_size += f->chunk_size;
+ f->chunk_size -= 24;
+
+ if (verbose > 3)
+ print_msg (VERBOSEM, "%s: Reading chunk %c%c%c%c, size " _PRIbig_t
+ ", pos %d\n", f->filename, buf[0], buf[1], buf[2], buf[3],
+ f->chunk_size - 24, f->cur_size);
+
+ if (f->chunk_size == 0) {
+ print_msg (NOTIFYM, "%s: Chunk size equals 0 (pos: %u)!\n",
+ f->filename, f->cur_size);
+ if ((f->found & SSND_FOUND) && (f->found & COMM_FOUND))
+ return CP_STOP_READING;
+ return w64_iterator (f, buf, l);
+ }
+
+ return 0;
+}
diff --git a/cmd/ossplay/ossplay_parser.h b/cmd/ossplay/ossplay_parser.h
new file mode 100644
index 0000000..f42d29d
--- /dev/null
+++ b/cmd/ossplay/ossplay_parser.h
@@ -0,0 +1,8 @@
+#ifndef OSSPLAY_PARSER_H
+#define OSSPLAY_PARSER_H
+
+#include "ossplay.h"
+
+errors_t play_file (dspdev_t *, const char *, dlopen_funcs_t **);
+
+#endif
diff --git a/cmd/ossplay/ossplay_wparser.c b/cmd/ossplay/ossplay_wparser.c
new file mode 100644
index 0000000..98cb4b5
--- /dev/null
+++ b/cmd/ossplay/ossplay_wparser.c
@@ -0,0 +1,358 @@
+/*
+ * Purpose: File header write routines for OSSRecord.
+ */
+/*
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <soundcard.h>
+#include <errno.h>
+#include "ossplay_wparser.h"
+
+#pragma pack(1)
+
+typedef struct {
+ char main_chunk[4];
+ uint32 length;
+ char chunk_type[4];
+
+ char sub_chunk[4];
+ uint32 sc_len;
+ uint16 format;
+ uint16 modus;
+ uint32 sample_fq;
+ uint32 byte_p_sec;
+ uint16 block_align;
+ uint16 bit_p_spl;
+
+ char data_chunk[4];
+ uint32 data_length;
+}
+WaveHeader;
+
+typedef struct {
+ char magic[4];
+ uint32 offset;
+ uint32 filelen;
+ uint32 fmt;
+ uint32 speed;
+ uint32 channels;
+ /*
+ * Some old specs say the field below is optional. Others say that this field
+ * is mandatory, and must be at least 4 bytes long (SoX prints out a warning).
+ * We'll put a nice "Made by OSSRecord" comment and shut SoX up.
+ */
+ char comment[18];
+}
+AuHeader;
+
+typedef struct {
+ char main_chunk[4];
+ uint32 length;
+ char chunk_type[4];
+
+ char sub_chunk[4];
+ uint32 comm_len;
+ uint16 channels;
+ uint32 num_frames;
+ uint16 bits;
+ unsigned char speed[10];
+ char data_chunk[4];
+ uint32 data_length;
+ uint32 offset;
+ uint32 blocksize;
+}
+AiffHeader;
+
+typedef struct {
+ char magic[4];
+ uint16 version;
+ uint16 flags;
+
+ char desc_chunk[4];
+ uint32 desc_length_hi, desc_length_lo;
+ unsigned char speed[8];
+ char format[4];
+ uint32 format_flags;
+ uint32 bytes_per_packet;
+ uint32 frames_per_packet;
+ uint32 channels;
+ uint32 bits;
+
+ char data_chunk[4];
+ uint32 length_hi, length_lo;
+ uint32 edit_count;
+}
+CafHeader;
+
+#pragma pack()
+
+extern FILE *wave_fp;
+
+static uint32
+bswap (uint32 x)
+{
+ uint32 y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 3;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+ *b = *a;
+
+ return y;
+}
+
+static uint16
+bswaps (uint16 x)
+{
+ uint16 y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 1;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b = *a;
+
+ return y;
+}
+
+int
+write_head (FILE * wave_fp, fctypes_t type, big_t datalimit,
+ int format, int channels, int speed)
+{
+ uint32 dl = datalimit;
+
+ if ((speed <= 0) || (channels <= 0) || (format <= 0)) return -1;
+
+ if (datalimit > U32_MAX) dl = U32_MAX;
+
+ switch (type)
+ {
+ case WAVE_FILE: {
+ WaveHeader wh;
+ int bits = format2bits (format);
+
+ if (datalimit > U32_MAX - sizeof (WaveHeader))
+ print_msg (WARNM, "Data size exceeds file format limit!\n");
+ if ((datalimit == 0) || (datalimit > U32_MAX - sizeof (WaveHeader)))
+ dl = U32_MAX - (U32_MAX % 2) - sizeof (WaveHeader);
+ switch (format) {
+ case AFMT_U8:
+ case AFMT_S16_LE:
+ case AFMT_S24_PACKED:
+ case AFMT_S32_LE:
+ wh.format = LE_SH (1);
+ break;
+ case AFMT_A_LAW:
+ wh.format = LE_SH (6);
+ break;
+ case AFMT_MU_LAW:
+ wh.format = LE_SH (7);
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by WAV writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ memcpy ((char *) &wh.main_chunk, "RIFF", 4);
+ wh.length = LE_INT (dl + sizeof (WaveHeader) - 8);
+ if (dl % 2) dl--;
+ memcpy ((char *) &wh.chunk_type, "WAVE", 4);
+ memcpy ((char *) &wh.sub_chunk, "fmt ", 4);
+ wh.sc_len = LE_INT (16);
+ wh.modus = LE_SH (channels);
+ wh.sample_fq = LE_INT (speed);
+ wh.block_align = LE_SH (channels * bits / 8);
+ wh.byte_p_sec = LE_INT (speed * channels * bits / 8);
+ wh.bit_p_spl = LE_SH (bits);
+ memcpy ((char *) &wh.data_chunk, "data", 4);
+ wh.data_length = LE_INT (dl);
+ if (fwrite (&wh, sizeof (WaveHeader), 1, wave_fp) == 0) return E_ENCODE;
+ } break;
+ case AU_FILE: {
+ AuHeader ah;
+
+ if ((datalimit == 0) || (datalimit > U32_MAX)) dl = 0xffffffff;
+ memcpy ((char *) &ah.magic, ".snd", 4);
+ ah.offset = BE_INT (sizeof (AuHeader));
+ ah.filelen = BE_INT (dl);
+ switch (format)
+ {
+ case AFMT_MU_LAW: ah.fmt = BE_INT (1); break;
+ case AFMT_S8: ah.fmt = BE_INT (2); break;
+ case AFMT_S16_BE: ah.fmt = BE_INT (3); break;
+ case AFMT_S24_PACKED_BE: ah.fmt = BE_INT (4); break;
+ case AFMT_S32_BE: ah.fmt = BE_INT (5); break;
+ case AFMT_A_LAW: ah.fmt = BE_INT (27); break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by AU writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+ ah.speed = BE_INT (speed);
+ ah.channels = BE_INT (channels);
+ memcpy ((char *) &ah.comment, "Made by OSSRecord", 18);
+ if (fwrite (&ah, sizeof (AuHeader), 1, wave_fp) == 0) return -1;
+ } break;
+ case CAF_FILE: {
+ CafHeader cfh;
+ int bits = format2bits (format);
+ uint32 i;
+
+ memcpy ((char *)&cfh.magic, "caff", 4);
+ cfh.version = BE_SH(1);
+ cfh.flags = 0;
+
+ memcpy ((char *)&cfh.desc_chunk, "desc", 4);
+ cfh.desc_length_hi = 0;
+ cfh.desc_length_lo = BE_INT (32);
+ cfh.format_flags = 0;
+ switch (format) {
+ case AFMT_S16_LE:
+ case AFMT_S24_PACKED_LE:
+ case AFMT_S32_LE: cfh.format_flags = BE_INT (2);
+ case AFMT_S8:
+ case AFMT_S16_BE:
+ case AFMT_S24_PACKED_BE:
+ case AFMT_S32_BE:
+ memcpy ((char *)&cfh.format, "lpcm", 4);
+ break;
+ case AFMT_MU_LAW:
+ memcpy ((char *)&cfh.format, "ulaw", 4);
+ break;
+ case AFMT_A_LAW:
+ memcpy ((char *)&cfh.format, "alaw", 4);
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by CAF writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+ cfh.frames_per_packet = BE_INT (1);
+ cfh.bytes_per_packet = BE_INT (bits / 8 * channels);
+ cfh.channels = BE_INT (channels);
+ cfh.bits = BE_INT (bits);
+
+ /* The method used here is good enough for sane values */
+ memset (cfh.speed, 0, sizeof (cfh.speed));
+ i = 0;
+ while ((1L << (i + 1)) <= speed) i++;
+ cfh.speed[0] = 64; cfh.speed[1] = (i-1) << 4;
+ i = (speed - (1 << i)) << (32-i);
+ cfh.speed[5] = i & 240;
+ cfh.speed[4] = (i >> 4) & 255;
+ cfh.speed[3] = (i >> 12) & 255;
+ cfh.speed[2] = (i >> 20) & 255;
+ cfh.speed[1] |= (i >> 28) & 15;
+
+ memcpy ((char *)&cfh.data_chunk, "data", 4);
+ if ((datalimit == 0) || (datalimit > U32_MAX)) {
+ cfh.length_lo = 0xffffffff;
+ cfh.length_hi = 0xffffffff;
+ } else {
+ cfh.length_lo = BE_INT (datalimit);
+ cfh.length_hi = 0;
+ }
+ cfh.edit_count = 0;
+ if (fwrite (&cfh, sizeof (CafHeader), 1, wave_fp) == 0)
+ return E_ENCODE;
+ } break;
+ case AIFF_FILE: {
+ AiffHeader afh;
+ int bits = format2bits (format);
+ uint32 i;
+
+ if (datalimit > U32_MAX - sizeof (AiffHeader))
+ print_msg (WARNM, "Data size exceeds file format limit!\n");
+ if ((datalimit == 0) || (datalimit > U32_MAX - sizeof (AiffHeader)))
+ dl = U32_MAX - (U32_MAX % 2) - sizeof (AiffHeader);
+ memcpy ((char *) &afh.main_chunk, "FORM", 4);
+ afh.length = BE_INT (dl + sizeof (AiffHeader) - 8);
+ if (dl % 2) dl--;
+ memcpy ((char *) &afh.chunk_type, "AIFF", 4);
+ memcpy ((char *) &afh.sub_chunk, "COMM", 4);
+ afh.comm_len = BE_INT (18);
+ afh.channels = BE_SH (channels);
+ afh.num_frames = BE_INT (dl / bits);
+ afh.bits = BE_SH (bits);
+ switch (format)
+ {
+ case AFMT_S8:
+ case AFMT_S16_BE:
+ case AFMT_S24_PACKED_BE:
+ case AFMT_S32_BE:
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by AIFF writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ /* The method used here is good enough for sane values */
+ memset (afh.speed, 0, sizeof (afh.speed));
+ i = 0;
+ while ((1L << (i + 1)) <= speed) i++;
+ afh.speed[0] = 64; afh.speed[1] = i-1;
+ i = (speed - (1 << i)) << (31-i);
+ afh.speed[5] = i & 255;
+ afh.speed[4] = (i >> 8) & 255;
+ afh.speed[3] = (i >> 16) & 255;
+ afh.speed[2] = ((i >> 24) & 127) + 128;
+
+ memcpy ((char *) &afh.data_chunk, "SSND", 4);
+ afh.data_length = BE_INT (dl);
+ afh.offset = BE_SH (0);
+ afh.blocksize = BE_SH (0);
+ if (fwrite (&afh, sizeof (AiffHeader), 1, wave_fp) == 0)
+ return E_ENCODE;
+ } break;
+ case RAW_FILE:
+ default: return 0;
+ }
+ return 0;
+}
+
+int
+finalize_head (FILE * wave_fp, fctypes_t type, big_t datalimit,
+ int format, int channels, int speed)
+{
+ if ((IS_IFF_FILE (type)) && (datalimit % 2))
+ {
+ /*
+ * All chunks must have an even length in an IFF file,
+ * so we have to add a pad byte in this case.
+ * Since we always write the data chunk last, we can
+ * just append it to end of file.
+ */
+ char flag = 0;
+
+ fseek (wave_fp, 0, SEEK_END);
+ if (fwrite (&flag , 1, 1, wave_fp) == 0)
+ print_msg (WARNM, "Couldn't add padding byte to SSND chunk!\n");
+ }
+ if ((type != RAW_FILE) && (fseek (wave_fp, 0, SEEK_SET) == -1)) return E_ENCODE;
+ write_head (wave_fp, type, datalimit, format, channels, speed);
+ return 0;
+}
diff --git a/cmd/ossplay/ossplay_wparser.h b/cmd/ossplay/ossplay_wparser.h
new file mode 100644
index 0000000..ae3e037
--- /dev/null
+++ b/cmd/ossplay/ossplay_wparser.h
@@ -0,0 +1,21 @@
+#ifndef _OSSRECORD_WPARSER_H
+#define _OSSRECORD_WPARSER_H
+
+#include "ossplay.h"
+
+int write_head (FILE *, fctypes_t, big_t, int, int, int);
+int finalize_head (FILE *, fctypes_t, big_t, int, int, int);
+
+#ifdef OSS_BIG_ENDIAN
+#define BE_INT(x) x
+#define BE_SH(x) x
+#define LE_INT(x) bswap(x)
+#define LE_SH(x) bswaps(x)
+#else
+#define BE_INT(x) bswap(x)
+#define BE_SH(x) bswaps(x)
+#define LE_INT(x) x
+#define LE_SH(x) x
+#endif /* OSS_BIG_ENDIAN */
+
+#endif