Adam Hoka avatar Adam Hoka committed cfbebae Merge

merge with 4front

Comments (0)

Files changed (48)

 f794ffc6c18524a62f4e3b81011e590b30bc5183 v4.2-build2001
 21b607a0014a5135cde83d02f3048a616295048e v4.2-build2002
 372b5c3940ab0478aadc4c45fbf79af9ce9195a3 v4.2-2003
+232d0de715505855a3b2b43fc3cfa34b8abb1231 v4.2-2004
 
 ______________________________________________________________________________
 
-4Front Technologies
-4035 Lafayette Place Unit F
-Culver City, CA 90232
+4Front Technologies, Inc.
+9826 Beverlywood Street
+Los Angeles, CA 90064
 U.S.A.
 
 Tel: (+01) 310-202-8530         WWW: http://www.opensound.com
-2003
+2004

cmd/ossmix/ossmix.man

 mixer API which supports some device specific features that may not available
 using other mixer applications. 
 
-OPTIOMS
+OPTIONS
 -D		Display device information.
 -a		Dump mixer settings for all mixers (normal format).
 -c		Dump mixer settings for all mixers (command format).

cmd/ossplay/ossplay.c

 #include <unistd.h>
 
 unsigned int amplification = 100;
-int eflag = 0, force_speed = 0, force_fmt = 0, force_channels = 0, verbose = 0,
-    quiet = 0;
+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;
   {"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 print_play_verbose_info (const unsigned char *, ssize_t, void *);
 static void print_record_verbose_info (const unsigned char *, ssize_t, void *);
 
-int
+big_t
 be_int (const unsigned char * p, int l)
 {
-  int i, val;
+  int i;
+  big_t val;
 
   val = 0;
 
   return val;
 }
 
-int
+big_t
 le_int (const unsigned char * p, int l)
 {
-  int i, val;
+  int i;
+  big_t val;
 
   val = 0;
 
     {
       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_MS_IMA_ADPCM_3BITS: return 3;
       case AFMT_MAC_IMA_ADPCM:
       case AFMT_MS_IMA_ADPCM:
       case AFMT_IMA_ADPCM:
   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, "            -F             Treat all input as raw PCM.\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");
              " 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);
 }
 
 
   tmp = format;
 
-  if (verbose > 1)
-    print_msg (VERBOSEM, "Setup device %s/%d/%d\n", sample_format_name (format),
-               channels, speed);
-
   if (ioctl (dsp->fd, SNDCTL_DSP_SETFMT, &tmp) == -1)
     {
       perror_msg (dsp->dname);
 
   if (tmp != channels)
     {
-      print_msg (ERRORM, "%s doesn't support %d channels (%d).\n",
-	         dsp->dname, channels, tmp);
+#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;
     }
 
       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);
   char * p;
   int c;
 
-  while ((c = getopt (argc, argv, "FRS:c:d:f:g:hlo:qs:v")) != EOF)
+  while ((c = getopt (argc, argv, "FRS:Wc:d:f:g:hlo:qs:v")) != EOF)
     {
       switch (c)
 	{
           break;
 
 	case 'F':
+	case 'W':
 	  raw_file = 1;
 	  break;
 
           sscanf (optarg, _PRIbig_t, &datalimit);
           break;
 
+        case 'O':
+          overwrite = 0;
+          break;
+
         case 'w':
-        case 'O':
           break;
 
         case 'v':
   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:
     ossplay_free (verbose_meta); \
     clear_update (); \
     ioctl (dsp->fd, SNDCTL_DSP_HALT_OUTPUT, NULL); \
+    errno = 0; \
     return (code); \
   } while (0)
 

cmd/ossplay/ossplay.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
 /* Sanity check - no allocation by ossplay should pass this. */
 #define OSSPLAY_MAX_MALLOC		32*1024*1024
 
-#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 ULONG_MAX
-#endif
-
-#ifndef OSS_NO_INTTYPES_H
+#if !defined(OSS_NO_INTTYPES_H) && !defined(OSS_NO_LONG_LONG)
 #define  __STDC_LIMIT_MACROS
 #include <inttypes.h>
 
 #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;
   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;
 }
 decoders_queue_t;
 
-int be_int (const unsigned char *, int);
+big_t be_int (const unsigned char *, int);
 const char * filepart (const char *);
 float format2bits (int);
-int le_int (const unsigned char *, 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 *);

cmd/ossplay/ossplay.man

 ossplay - Open Sound System playback program.
 
 SYNOPSIS
-ossplay [-FRhlvq] [-S secs ] [ -c channels ] [ -d devname ]
+ossplay [-RWhlvq] [-S secs ] [ -c channels ] [ -d devname ]
         [ -f fmtname | ? ] [ -g gain ] [ -o playtarget | ? ]
         [ -s rate ] filename | - ...
 
 -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.
--F		Treat all input as raw PCM data.
+-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.

cmd/ossplay/ossplay_console.c

 {
   void *ptr;
 
-  if (sz == 0) return NULL;
-  if (sz > OSSPLAY_MAX_MALLOC)
-    {
-      fprintf (stderr, "Unreasonable allocation size" _PRIbig_t ", aborting",
-               (big_t)sz);
-      exit (E_SETUP_ERROR);
-    }
+  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 %lu bytes\n", (unsigned long)sz);
-      exit (-1);
-    }
+  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;
 }
 
   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 -= bytes_read;
-    }
+  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;
 }
 
 
   do {
     loop_flag = 0;
-    for (i = 1; i < argc; i++)
-      {
-        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 == 0) loop_flag = 1;
-        eflag = 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

cmd/ossplay/ossplay_decode.c

 }
 ima_values_t;
 
-extern int amplification, force_speed, force_fmt, force_channels;
-extern flag int_conv, verbose;
+#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;
 static decfunc_t decode_float32_be;
 static decfunc_t decode_float32_le;
 static decfunc_t decode_mac_ima;
-static decfunc_t decode_mono_to_stereo;
 static decfunc_t decode_ms_ima;
 static decfunc_t decode_ms_adpcm;
 static decfunc_t decode_nul;
                 seekf = seek_ogg;
                 total_time = val->f->ov_time_total (&val->vf, -1);
               }
-           else
-             {
-               seekf = NULL;
-               total_time = 0;
-             }
-         }
+            else
+              {
+                seekf = NULL;
+                total_time = 0;
+              }
+          }
 
         format = AFMT_S16_NE;
         break;
     }
 
 dcont:
-  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;
-    }
+#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);
 
   if (datalimit != 0) datalimit *= constant;
 
-  if (strcmp (fname, "-") == 0)
+  if (strcmp (fname, "-") == 0) {
     wave_fp = fdopen (1, "wb");
-  else
-    {
-      fd = open (fname, O_WRONLY | O_CREAT, 0644);
-      if (fd == -1)
-        {
-          perror (fname);
-          return E_ENCODE;
-        }
-      wave_fp = fdopen (fd, "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)
     {
   /*
    * 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 -O /dev/null"), so no need to warn.
+   * file (e.g. "ossrecord /dev/null"), so no need to warn.
    */
   if ((fsync (fileno (wave_fp)) == -1) && (errno != EINVAL) && (errno != EROFS))
     {
 }
 
 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_float32_be (unsigned char ** obuf, unsigned char * buf, ssize_t l,
                    void * metadata)
 {
     {
       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++) - 128);
-              if (v < 0) v = -v;
-              if (v > level) level = v;
+          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;
+       break;
 
       case AFMT_S16_NE:
         {
 
           p = (int16 *)buf;
 
-          for (i = 0; i < l / 2; i++)
-            {
-              v = *p++;
-              if (v < 0) v = -v;
-              if (v > level) level = v;
+          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;
+          int32 * p;
 
-         p = (int32 *)buf;
+          p = (int32 *)buf;
 
-         for (i = 0; i < l / 4; i++)
-           {
-             v = *p++;
-             if (v < 0) v = -v;
-             if (v > level) level = v;
-           }
+          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;
   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
+

cmd/ossplay/ossplay_parser.c

 #define DEC_MAGIC	0x2e736400	/* Really '\0ds.' (for DEC) */
 #define DEC_INV_MAGIC	0x0064732e	/* '\0ds.' upside-down */
 
-/* Magic numbers for file formats based on IFF */
-#define FORM_MAGIC	0x464f524d	/* 'FORM' */
-#define AIFF_MAGIC	0x41494646	/* 'AIFF' */
-#define AIFC_MAGIC	0x41494643	/* 'AIFC' */
-#define _8SVX_MAGIC	0x38535658	/* '8SVX' */
-#define _16SV_MAGIC	0x31365356	/* '16SV' */
-#define MAUD_MAGIC	0x4D415544	/* 'MAUD' */
+/* Magic numbers for .w64 */
+#define riff_GUID	0x2E91CF11
+#define riff_GUID2	0xA5D628DB
+#define riff_GUID3	0x04C10000
 
-/* Magic numbers for .wav files */
-#define RIFF_MAGIC	0x52494646	/* 'RIFF' */
-#define RIFX_MAGIC	0x52494658	/* 'RIFX' */
-#define WAVE_MAGIC	0x57415645	/* 'WAVE' */
+enum {
+  COMM_BIT,
+  SSND_BIT,
+  FVER_BIT
+};
 
-/* Beginning of magic for Creative .voc files */
-#define Crea_MAGIC	0x43726561	/* 'Crea' */
+#define COMM_FOUND (1 << COMM_BIT)
+#define SSND_FOUND (1 << SSND_BIT)
+#define FVER_FOUND (1 << FVER_BIT)
 
-/* Ogg */
-#define OggS_MAGIC      0x4F676753      /* 'OggS' */
+#define H(A, B, C, D) ((A << 24) | (B << 16) | (C << 8) | D)
 
-extern int quiet, verbose, force_fmt;
+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);
+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 **);
 errors_t
 play_file (dspdev_t * dsp, const char * filename, dlopen_funcs_t ** dlt)
 {
-  int fd;
+  int fd, id;
   ssize_t l, i;
-  unsigned char buf[PLAYBUF_SIZE];
-  const char * suffix;
+  unsigned char buf[P_READBUF_SIZE];
+  const char * bname, * suffix;
   struct stat st;
   errors_t ret = E_OK;
 
-  if (from_stdin)
-    {
-      FILE *fp;
+  parser_t piff = {
+    &iff_init,
+    &iff_read,
+    &iff_iterator,
+    NULL,
+    NULL
+  };
 
-      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;
-    }
-  else fd = open (filename, O_RDONLY, 0);
+  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);
 
   if (raw_file)
     {
-      if (fstat (fd, &st) == -1)
-        {
-          perror_msg (filename);
-          return E_DECODE;
-        }
-      print_msg (NORMALM, "%s: Playing RAW file.\n", filepart (filename));
+      big_t len;
 
-      ret = decode_sound (dsp, fd, st.st_size, DEFAULT_FORMAT,
+      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 == 0)
     {
-      print_msg (ERRORM, "%s is empty file.\n", filepart (filename));
+      print_msg (ERRORM, "%s is empty file.\n", bname);
       goto seekerror;
     }
 
 /*
  * Try to detect the file type
  */
-  switch (be_int (buf, 4))
-    {
-      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, filepart (filename), fd, buf, l);
-        goto done;
-      case Crea_MAGIC:
-        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, filepart (filename), fd, buf, l);
-        goto done;
-      case RIFF_MAGIC:
-        if ((l < 12) || be_int (buf + 8, 4) != WAVE_MAGIC) break;
-        if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MS_IMA_ADPCM;
-        ret = play_iff (dsp, filepart (filename), fd, buf, WAVE_FILE);
-        goto done;
-      case RIFX_MAGIC:
-        if ((l < 12) || be_int (buf + 8, 4) != WAVE_MAGIC) break;
-        if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MS_IMA_ADPCM;
-        ret = play_iff (dsp, filepart (filename), fd, buf, WAVE_FILE_BE);
-        goto done;
-      case FORM_MAGIC:
-        if (l < 12) break;
-        switch (be_int (buf + 8, 4))
-          {
-            case AIFF_MAGIC:
-              if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MAC_IMA_ADPCM;
-              ret = play_iff (dsp, filepart (filename), fd, buf, AIFF_FILE);
-              goto done;
-            case AIFC_MAGIC:
-              if (force_fmt == AFMT_IMA_ADPCM) force_fmt = AFMT_MAC_IMA_ADPCM;
-              ret = play_iff (dsp, filepart (filename), fd, buf, AIFC_FILE);
-              goto done;
-            case _8SVX_MAGIC:
-              ret = play_iff (dsp, filepart (filename), fd, buf, _8SVX_FILE);
-              goto done;
-            case _16SV_MAGIC:
-              ret = play_iff (dsp, filepart (filename), fd, buf, _16SV_FILE);
-              goto done;
-            case MAUD_MAGIC:
-              ret = play_iff (dsp, filepart (filename), fd, buf, MAUD_FILE);
-              goto done;
-            default: break;
-          }
-      case OggS_MAGIC:
+  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, filepart (filename), fd, buf, l, dlt);
-        fd = -1;
-        goto done;
+      ret = play_ogg (dsp, bname, fd, buf, l, dlt);
+      fd = -1;
+      goto done;
 #endif
-      default: break;
-    }
+    default: break;
+  }
 
   ossplay_lseek (fd, 0, SEEK_SET);	/* Start from the beginning */
 
 
   if (strcmp (suffix, ".au") == 0 || strcmp (suffix, ".AU") == 0)
     {				/* Raw mu-Law data */
-      print_msg (VERBOSEM, "Playing raw mu-Law file %s\n",
-                 filepart (filename));
+      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;
     {
       print_msg (VERBOSEM,
                  "%s: Unknown format. Assuming RAW audio (%d/%d/%d).\n",
-                 filepart (filename), DEFAULT_SPEED, DEFAULT_FORMAT,
-                 DEFAULT_CHANNELS);
+                 bname, DEFAULT_SPEED, DEFAULT_FORMAT, DEFAULT_CHANNELS);
 
       ret = decode_sound (dsp, fd, st.st_size, DEFAULT_FORMAT, DEFAULT_CHANNELS,
                           DEFAULT_SPEED, NULL);
 
   if (strcmp (suffix, ".cdr") == 0 || strcmp (suffix, ".CDR") == 0)
     {
-      print_msg (VERBOSEM, "%s: Playing CD-R (cdwrite) file.\n",
-                 filepart (filename));
+      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", filename);
+      print_msg (VERBOSEM, "%s: Playing RAW file.\n", bname);
 
       ret = decode_sound (dsp, fd, st.st_size, DEFAULT_FORMAT, DEFAULT_CHANNELS,
                           DEFAULT_SPEED, NULL);
   if (fd != -1) close (fd);
 
 #if 0
-  ioctl (audiofd, SNDCTL_DSP_SYNC, NULL);
+  ioctl (fd, SNDCTL_DSP_SYNC, NULL);
 #endif
   return ret;
 seekerror:
   return E_DECODE;
 }
 
-/*ARGSUSED*/
+/*
+ * 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)
+          int type, parser_t * p)
 {
-/*
- * Generalized IFF parser - handles WAV, AIFF, AIFC, 8SVX, 16SV and MAUD.
- */
-  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)
+  int ret;
 
-#define LIST_HUNK 0x4C495354
-
-#define FVER_HUNK 0x46564552
-#define COMM_HUNK 0x434F4D4D
-#define SSND_HUNK 0x53534E44
-#define ANNO_HUNK 0x414E4E4F
-#define NAME_HUNK 0x4E414D45
-#define AUTH_HUNK 0x41555448
-#define COPY_HUNK 0x28632920 /* "(c) " */ 
-
-#define VHDR_HUNK 0x56484452
-#define BODY_HUNK 0x424f4459
-#define CHAN_HUNK 0x4348414e
-
-#define MDAT_HUNK 0x4D444154
-#define MHDR_HUNK 0x4D484452
-
-#define alaw_FMT 0x616C6177
-#define ALAW_FMT 0x414C4157
-#define ulaw_FMT 0x756C6177
-#define ULAW_FMT 0x554C4157
-#define sowt_FMT 0x736F7774
-#define twos_FMT 0x74776F73
-#define fl32_FMT 0x666C3332
-#define FL32_FMT 0x464C3332
-#define fl64_FMT 0x666C3634
-#define FL64_FMT 0x464C3634
-#define ima4_FMT 0x696D6134
-#define NONE_FMT 0x4E4F4E45
-#define raw_FMT  0x72617720
-#define in16_FMT 0x696E3136
-#define in24_FMT 0x696E3234
-#define ni24_FMT 0x6E693234
-#define in32_FMT 0x696E3332
-#define ni32_FMT 0x6E693332
-#define _23ni_FMT 0x32336E69
-
-#define fmt_HUNK 0x666D7420
-#define data_HUNK 0x64617461
-#define INFO_HUNK 0x494E464F
-#define DISP_HUNK 0x44495350
-
-#define IARL_HUNK 0x4941524C
-#define IART_HUNK 0x49415254
-#define ICMS_HUNK 0x49434D53
-#define ICMT_HUNK 0x49434D54
-#define ICOP_HUNK 0x49434F50
-#define ICRD_HUNK 0x49435244
-#define IENG_HUNK 0x49454E47
-#define IGNR_HUNK 0x49474E52
-#define IKEY_HUNK 0x494B4559
-#define INAM_HUNK 0x494E414D
-#define IPRD_HUNK 0x49505244
-#define ISBJ_HUNK 0x4953424A
-#define ISFT_HUNK 0x49534654
-#define ISRC_HUNK 0x49535243
-#define ITCH_HUNK 0x49544348
-
-#define READ_MACRO(fd, buf, len) \
-  do { \
-    if (read(fd, buf, len) < len) \
-      { \
-        print_msg (ERRORM, \
-                   "%s: error: cannot read data in chunk (chunk id: " \
-                   "0x%04X).\n", filename, chunk_id); \
-        if ((found & SSND_FOUND) && (found & COMM_FOUND)) goto nexta; \
-        else return E_DECODE; \
-      } \
-    chunk_pos += len; \
-   } while(0)
-
-#define ASEEK(fd, offset) \
-  do { \
-    if ((offset != 0) && (ossplay_lseek (fd, offset, SEEK_CUR) == -1)) \
-      { \
-        print_msg (ERRORM, \
-                   "%s: error: cannot seek to end of chunk (chunk id: " \
-                   "0x%04X).\n", filename, chunk_id); \
-        if ((found & SSND_FOUND) && (found & COMM_FOUND)) goto nexta; \
-        else return E_DECODE; \
-      } \
-    chunk_pos += offset; \
-  } while (0)
-
-#define AREAD(fd, buf, len) \
-  do { \
-    if (chunk_size < len + chunk_pos) \
-      { \
-        print_msg (ERRORM, \
-                   "%s: error: chunk size is too small (chunk id: 0x%04X).\n", \
-                   filename, chunk_id); \
-        if ((found & SSND_FOUND) && (found & COMM_FOUND)) goto nexta; \
-        else return E_DECODE; \
-      } \
-    READ_MACRO (fd, buf, len); \
-  } while (0)
-
-#define ONLYF(f) \
-  if (!(type & (f))) \
-    { \
-      break; \
-    } \
-
-#define BITS2SFORMAT(endian) \
-  do { \
-    if (force_fmt == 0) switch (bits) \
-      { \
-         case 8: format = AFMT_S8; break; \
-         case 16: format = AFMT_S16_##endian; break; \
-         case 24: format = AFMT_S24_PACKED_##endian; break; \
-         case 32: format = AFMT_S32_##endian; break; \
-         default: format = AFMT_S16_##endian; break; \
-     } break; \
-  } while (0)
-
-  int channels = 1, bits = 8, format, speed = 11025;
-  big_t csize = 12;
-  uint32 chunk_id, chunk_size = 18, chunk_pos, found = 0, offset = 0,
-         sound_loc = 0, sound_size = 0, timestamp, total_size;
-  int (*ne_int) (const unsigned char *p, int l) = be_int;
-
-  msadpcm_values_t msadpcm_val = {
+  sbig_t rbytes;
+  const chunk_functions_t * i, * oi;
+  file_t f = { {
     256, 496, 7, 4, {
       {256, 0},
       {512, -256},
       {460, -208},
       {392, -232} },
     DEFAULT_CHANNELS
-  };
+  } };
 
-  if (type == _8SVX_FILE) format = AFMT_S8;
-  else format = AFMT_S16_BE;
-  if (force_fmt != 0) format = force_fmt;
-  if (type == WAVE_FILE) ne_int = le_int;
+  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;
 
-  total_size = ne_int (buf + 4, 4);
+  rbytes = p->init (&f, buf);
+  if (rbytes < 0) return E_DECODE;
   if (verbose > 1)
-    print_msg (NORMALM, "Filelen = %u\n", total_size);
-  do
-    {
-      chunk_pos = 0;
-      if (read (fd, buf, 8) < 8)
-        {
-          print_msg (ERRORM, "%s: Cannot read chunk header at pos %u\n",
-                     filename, csize);
-          if ((found & SSND_FOUND) && (found & COMM_FOUND)) goto nexta;
-          return E_DECODE;
-        }
-      chunk_id = be_int (buf, 4);
-      chunk_size = ne_int (buf + 4, 4);
-      if (verbose > 3)
-        print_msg (VERBOSEM, "%s: Reading chunk %c%c%c%c, size %d\n",
-                   filename, buf[0], buf[1], buf[2], buf[3], chunk_size);
-      if (chunk_size == 0)
-        {
-          print_msg (ERRORM, "%s: Chunk size equals 0 (pos: %u)!\n",
-                     filename, csize);
-          if ((found & SSND_FOUND) && (found & COMM_FOUND)) goto nexta;
-          csize += 8;
-          continue;
-        }
+    print_msg (VERBOSEM, "FORM len = %u\n", f.total_size);
 
-      switch (chunk_id)
-        {
-          /* AIFF / AIFC chunks */
-          case COMM_HUNK:
-            ONLYF (AIFF_FILE | AIFC_FILE);
-            if (found & COMM_FOUND)
-              {
-                print_msg (ERRORM, "%s: error: COMM hunk not singular!\n",
-                           filename);
-                return E_DECODE;
-              }
-            if (type == AIFC_FILE) AREAD (fd, buf, 22);
-            else AREAD (fd, buf, 18);
-            found |= COMM_FOUND;
+  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;
 
-            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;
-            BITS2SFORMAT (BE);
-            {
-              /*
-               * Conversion from IEEE-754 extended 80-bit to long double.
-               * We take some shortcuts which don't affect this application.
-               */
-              int exp;
-              ldouble_t COMM_rate = 0;
+        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;
+    }
 
-              exp = ((buf[8] & 127) << 8) + buf[9] - 16383;
-#if 0
-              /*
-               * This part of the mantissa will always 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;
-                }
-              speed = COMM_rate;
-            }
-
-            if (type != AIFC_FILE) break;
-            if (force_fmt != 0) break;
-            switch (be_int (buf + 18, 4))
-              {
-                case NONE_FMT: break;
-                case in16_FMT: format = AFMT_S16_BE; break;
-                case in24_FMT: format = AFMT_S24_BE; break;
-                case in32_FMT: format = AFMT_S32_BE; break;
-                case ni24_FMT: format = AFMT_S24_LE; break;
-                case _23ni_FMT:
-                case ni32_FMT: 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 sowt_FMT: BITS2SFORMAT (LE); break;
-                case twos_FMT: BITS2SFORMAT (BE); break;
-                case raw_FMT: format = AFMT_U8; break;
-                case alaw_FMT:
-                case ALAW_FMT: format = AFMT_A_LAW; break;
-                case ulaw_FMT:
-                case ULAW_FMT: format = AFMT_MU_LAW; break;
-                case ima4_FMT: format = AFMT_MAC_IMA_ADPCM; break;
-                case fl32_FMT:
-                case FL32_FMT: format = AFMT_FLOAT32_BE; break;
-                case fl64_FMT:
-                case FL64_FMT: format = AFMT_DOUBLE64_BE; break;
-                default:
-                  print_msg (ERRORM,
-                           "%s: error: %c%c%c%c compression is not supported\n",
-                           filename, *(buf + 18), *(buf + 19),
-                           *(buf + 20), *(buf + 21));
-                  return E_FORMAT_UNSUPPORTED;
-              }
-            break;
-          case SSND_HUNK:
-            ONLYF (AIFF_FILE | AIFC_FILE);
-            if (found & SSND_FOUND)
-              {
-                print_msg (ERRORM,
-                           "%s: error: SSND hunk not singular!\n", filename);
-                return E_DECODE;
-              }
-            AREAD (fd, buf, 8);
-            found |= SSND_FOUND;
-
-            offset = be_int (buf, 4);
-#if 0
-            block_size = be_int (buf + 4, 4); /* ossplay doesn't use this */
-#endif
-            sound_loc = csize + 16 + offset;
-            sound_size = chunk_size - 8;
-
-            if ((from_stdin) && (ossplay_lseek == ossplay_lseek_stdin))
-              goto stdinext;
-            break;
-          case FVER_HUNK:
-            ONLYF (AIFC_FILE);
-            AREAD (fd, buf, 4);
-            timestamp = be_int (buf, 4);
-            /* timestamp = 0xA2805140 for typical AIFF files */
-            found |= FVER_FOUND;
-            break;
-
-          /* 8SVX / 16SV chunks */
-          case VHDR_HUNK:
-            ONLYF (_8SVX_FILE | _16SV_FILE);
-            AREAD (fd, buf, 16);
-            speed = be_int (buf + 12, 2);
-            found |= COMM_FOUND;
-            if ((force_fmt != 0) || (type != _8SVX_FILE)) break;
-            switch (buf[15])
-              {
-                case 0: format = AFMT_S8; break;
-                case 1: format = AFMT_FIBO_DELTA; break;
-                case 2: format = AFMT_EXP_DELTA; break;
-                default:
-                  print_msg (ERRORM, "%s: Unsupported compression %d\n",
-                             filename, buf[15]);
-                  return E_FORMAT_UNSUPPORTED;
-              }
-            break;
-          case data_HUNK: /* WAVE chunk */
-            ONLYF (WAVE_FILE | WAVE_FILE_BE);
-            if (verbose > 2)
-              print_msg (NORMALM,  "DATA chunk. Offs = %u, "
-                         "len = %u\n", csize+8, chunk_size);
-            sound_loc = csize + 8;
-          case MDAT_HUNK: /* MAUD chunk */
-          case BODY_HUNK: /* 8SVX/16SV */
-            sound_size = chunk_size;
-            if (chunk_id != data_HUNK) sound_loc = csize + 4;
-            found |= SSND_FOUND;
-            if ((from_stdin) && (ossplay_lseek == ossplay_lseek_stdin))
-              goto stdinext;
-            break;
-          case CHAN_HUNK:
-            /* Used in 8SVX too: http://amigan.1emu.net/reg/8SVX.CHAN.PAN.txt */
-            ONLYF (MAUD_FILE);
-            AREAD (fd, buf, 4);
-            channels = be_int (buf, 4);
-            if (channels > 2)
-              {
-                print_msg (ERRORM, "ossplay doesn't support MAUD format's "
-                           "%d channel configuration\n", channels);
-                return E_CHANNELS_UNSUPPORTED;
-              }
-            break;
-
-          /* MAUD chunks */
-          case MHDR_HUNK:
-            ONLYF (MAUD_FILE);
-            AREAD (fd, buf, 20);
-            bits = be_int (buf + 4, 2);
-            BITS2SFORMAT (BE);
-            if (be_int (buf + 12, 2) == 0)
-              {
-                print_msg (ERRORM, "Invalid rate!\n");
-                return E_DECODE;
-              }
-            speed = be_int (buf + 8, 4) / be_int (buf + 12, 2);
-            channels = be_int (buf + 16, 2);
-            found |= COMM_FOUND;
-            if (force_fmt != 0) break;
-            switch (be_int (buf + 18, 2))
-              {
-                case 0: /* NONE */ break;
-                case 2: format = AFMT_A_LAW; break;
-                case 3: format = AFMT_MU_LAW; break;
-                case 6: format = AFMT_IMA_ADPCM; break;
-                default:
-                  print_msg (ERRORM, "%s: format not supported", filename);
-                  return E_FORMAT_UNSUPPORTED;
-              }
-            break;
-
-          /* WAVE chunks */
-          case fmt_HUNK: { unsigned int len, i, x; int wtype = 0x1;
-            ONLYF (WAVE_FILE | WAVE_FILE_BE);
-            if (found & COMM_FOUND)
-              {
-                print_msg (ERRORM, "%s: error: fmt hunk not singular!\n",
-                           filename);
-                return E_DECODE;
-              }
-            if (chunk_size > 1024) len = 1024;
-            else len = chunk_size;
-            AREAD (fd, buf, len);
-
-            if (force_fmt == 0) wtype = format = ne_int (buf, 2);
-            if (verbose > 2)
-              print_msg (NORMALM,  "FMT chunk: len = %u, fmt = %#x\n",
-                         chunk_size, format);
-
-            msadpcm_val.channels = channels = ne_int (buf + 2, 2);
-            speed = ne_int (buf + 4, 4);
-#if 0
-            bytes_per_sec = be_int (buf + 8, 4); /* ossplay doesn't use this */
-#endif
-            msadpcm_val.nBlockAlign = ne_int (buf + 12, 2);
-            if (msadpcm_val.nBlockAlign == 0)
-              print_msg (WARNM, "%s: nBlockAlign is 0!\n", filename);
-            msadpcm_val.bits = bits = ne_int (buf + 14, 2);
-
-            if (wtype == 0xFFFE) /* WAVE_FORMAT_EXTINSIBLE */
-              {
-                if (chunk_size < 40)
-                  {
-                    print_msg (ERRORM, "%s: invalid fmt chunk\n", filename);
-                    return E_DECODE;
-                  }
-                /* TODO: parse the rest of WAVE_FORMAT_EXTENSIBLE */
-                format = ne_int (buf + 24, 2);
-              }
-            if (force_fmt == 0) switch (format)
-              {
-                case 0x1: /* WAVE_FORMAT_PCM */
-                  bits += bits % 8;
-                  if (type == WAVE_FILE) BITS2SFORMAT (LE);
-                  else BITS2SFORMAT (BE);
-                  if (bits == 8) format = AFMT_U8;
-                  break;
-                case 0x2: /* WAVE_FORMAT_MS_ADPCM */
-                  format = AFMT_MS_ADPCM;
-                  break;
-                case 0x3: /* WAVE_FORMAT_IEEE_FLOAT */
-                  if (bits == 32) format = AFMT_FLOAT32_LE;
-                  else if (bits == 64) format = AFMT_DOUBLE64_LE;
-                  else
-                    {
-                      print_msg (ERRORM, "%s: Odd number of bits (%d) for "
-                                 "WAVE_FORMAT_IEEE_FLOAT!\n", filename, bits);
-                      return E_FORMAT_UNSUPPORTED;
-                    }
-                   break;
-                case 0x102: /* IBM_FORMAT_ALAW */
-                case 0x6: /* WAVE_FORMAT_ALAW */
-                  format = AFMT_A_LAW;
-                  break;
-                case 0x101: /* IBM_FORMAT_MULAW */
-                case 0x7: /* WAVE_FORMAT_MULAW */
-                  format = AFMT_MU_LAW;
-                  break;
-                case 0x11: /* WAVE_FORMAT_IMA_ADPCM */
-                  if (bits == 4) format = AFMT_MS_IMA_ADPCM;
-                  else if (bits == 3) format = AFMT_MS_IMA_ADPCM_3BITS;
-                  else
-                    {
-                      print_msg (ERRORM, "%s: Invalid number of bits (%d) for "
-                                 "WAVE_FORMAT_IMA_ADPCM!\n", filename, bits);
-                      return E_FORMAT_UNSUPPORTED;
-                    }
-                  break;
-#if 0
-                case 0x50: /* MPEG */
-                case 0x55: /* MPEG 3 */
-#endif
-                default:
-                  print_msg (ERRORM, "%s: Unsupported wave format %#x\n",
-                             filename, format);
-                  return E_FORMAT_UNSUPPORTED;
-              }
-            found |= COMM_FOUND;
-
-            if ((chunk_size < 20) ||
-                ((format != AFMT_MS_ADPCM) &&
-                 (format != AFMT_MS_IMA_ADPCM) &&
-                 (format != AFMT_MS_IMA_ADPCM_3BITS)
-                )
-               ) break;
-            msadpcm_val.wSamplesPerBlock = ne_int (buf + 18, 2);
-            if ((format != AFMT_MS_ADPCM) || (chunk_size < 22)) break;
-            msadpcm_val.wNumCoeff = ne_int (buf + 20, 2);
-            if (msadpcm_val.wNumCoeff > 32) msadpcm_val.wNumCoeff = 32;
-
-            x = 22;
-
-            for (i = 0; (i < msadpcm_val.wNumCoeff) && (x < chunk_size-3); i++)
-              {
-                msadpcm_val.coeff[i].coeff1 = (int16) ne_int (buf + x, 2);
-                x += 2;
-                msadpcm_val.coeff[i].coeff2 = (int16) ne_int (buf + x, 2);
-                x += 2;
-              }
-            msadpcm_val.wNumCoeff = i;
-            } break;
-
-          case LIST_HUNK: {
-            unsigned char * tag;
-            uint32 i, cssize = 4, len, subchunk_id, subchunk_size;
-
-            ONLYF (WAVE_FILE | WAVE_FILE_BE);
-            if ((!verbose) || (chunk_size < 12)) break;
-            READ_MACRO (fd, buf, 4);
-            chunk_id = be_int (buf, 4);
-            if (chunk_id != INFO_HUNK) break;
-            do
-              {
-                cssize += 8;
-                if (cssize > chunk_size) break;
-                READ_MACRO (fd, buf, 8);
-                subchunk_id = be_int (buf, 4);
-                subchunk_size = ne_int (buf + 4, 4);
-                if (verbose > 3)
-                  print_msg (VERBOSEM, "%s: Reading subchunk %c%c%c%c, size %d\n",
-                             filename, buf[0], buf[1], buf[2], buf[3],
-                             subchunk_size);
-                if (subchunk_size == 0) break;
-                cssize += subchunk_size;
-                if (cssize > chunk_size) break;
-                switch (subchunk_id)
-                  {
-                    case IARL_HUNK:
-                      print_msg (STARTM, "%s: Archival Location: ", filename);
-                      break;
-                    case IART_HUNK:
-                      print_msg (STARTM, "%s: Artist Name: ", filename);
-                      break;
-                    case ICMS_HUNK:
-                      print_msg (STARTM, "%s: Commissioned: ", filename);
-                      break;
-                    case ICMT_HUNK:
-                      print_msg (STARTM, "%s: Comment: ", filename);
-                      break;
-                    case ICOP_HUNK:
-                      print_msg (STARTM, "%s: Copyright: ", filename);
-                      break;
-                    case ICRD_HUNK:
-                      print_msg (STARTM, "%s: Creation date: ", filename);
-                      break;
-                    case IENG_HUNK:
-                      print_msg (STARTM, "%s: Engineer: ", filename);
-                      break;
-                    case IGNR_HUNK:
-                      print_msg (STARTM, "%s: Genre: ", filename);
-                      break;
-                    case IKEY_HUNK:
-                      print_msg (STARTM, "%s: Keywords: ", filename);
-                      break;
-                    case INAM_HUNK:
-                      print_msg (STARTM, "%s: Name: ", filename);
-                      break;
-                    case IPRD_HUNK:
-                      print_msg (STARTM, "%s: Product: ", filename);
-                      break;
-                    case ISBJ_HUNK:
-                      print_msg (STARTM, "%s: Subject: ", filename);
-                      break;
-                    case ISFT_HUNK:
-                      print_msg (STARTM, "%s: Software: ", filename);
-                      break;
-                    case ISRC_HUNK:
-                      print_msg (STARTM, "%s: Source: ", filename);
-                      break;
-                    case ITCH_HUNK:
-                      print_msg (STARTM, "%s: Technician: ", filename);
-                      break;
-                    default:
-                      ASEEK (fd, subchunk_size + (subchunk_size & 1));
-                      continue;
-                  }
-
-                if (subchunk_size > 1024)
-                  {
-                    READ_MACRO (fd, buf, 1024);
-                    ASEEK (fd, subchunk_size + (subchunk_size & 1) - 1024);
-                    len = 1024;
-                  }
-                else
-                  {
-                    len = subchunk_size + (subchunk_size & 1);
-                    READ_MACRO (fd, buf, len);
-                  }
-
-                tag = buf + len;
-                /*
-                 * According to the spec, all of the above hunks contain ZSTRs,
-                 * so we can safely ignore the last char.
-                 */
-                for (i = 0; i < len-1; i++)
-                  {
-                    if (!isprint (buf[i])) buf[i] = ' ';
-                    else tag = buf + i + 1;
-                  }
-                /* Remove trailing nonprintables */
-                *tag = '\0';
-                print_msg (ENDM, "%s\n", buf);
-               } while (cssize < chunk_size);
-            } break;
-
-          /* Cool Edit can create this chunk. Also some Windows files use it */
-          case DISP_HUNK: {
-            unsigned char * tag;
-            uint32 i, len;
-
-            ONLYF (WAVE_FILE | WAVE_FILE_BE);
-            if ((verbose < 2) || (chunk_size < 4)) break;
-            READ_MACRO (fd, buf, 4);
-            if (ne_int (buf, 4) != 1) break;
-            if (chunk_size > 1028) len = 1024;
-            else len = chunk_size - 4;
-            AREAD (fd, buf, len);
-
-            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", filename, buf);
-            } break;
-
-          case NAME_HUNK:
-          case AUTH_HUNK:
-          case ANNO_HUNK:
-          case COPY_HUNK:
-            ONLYF (AIFF_FILE | AIFC_FILE | _8SVX_FILE | _16SV_FILE | MAUD_FILE);
-            if (verbose)
-              {
-                unsigned char * tag;
-                uint32 i, len;
-
-                print_msg (STARTM,  "%s: ", filename);
-                switch (chunk_id)
-                  {
-                    case NAME_HUNK:
-                      print_msg (CONTM, "Name: ");
-                      break;
-                    case AUTH_HUNK:
-                      print_msg (CONTM, "Author: ");
-                      break;
-                    case COPY_HUNK:
-                      print_msg (CONTM, "Copyright: ");
-                      break;
-                    case ANNO_HUNK:
-                      print_msg (CONTM, "Annonations: ");
-                      break;
-                  }
-                if (chunk_size > 1023) len = 1023;
-                else len = chunk_size;
-                AREAD (fd, buf, len);
-
-                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);
-                break;
-              }
-
-          default:
-            break;
-       }
-
-      csize += chunk_size + (chunk_size & 1) + 8;
-      ASEEK (fd, chunk_size + (chunk_size & 1) - chunk_pos);
-
-    } while (csize < total_size);
+    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 ((found & COMM_FOUND) == 0)
-      {
-        print_msg (ERRORM, "%s: Couldn't find format chunk!\n", filename);
-        return E_DECODE;
-      }
+  if ((f.found & COMM_FOUND) == 0) {
+    print_msg (ERRORM, "%s: Couldn't find format chunk!\n", filename);
+    return E_DECODE;
+  }
 
-    if ((found & SSND_FOUND) == 0)
-      {
-        print_msg (ERRORM, "%s: Couldn't find sound 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) && ((found & FVER_FOUND) == 0))
-      print_msg (WARNM, "%s: Couldn't find AIFC FVER chunk.\n", filename);
+  if ((type == AIFC_FILE) && ((f.found & FVER_FOUND) == 0))
+    print_msg (WARNM, "%s: Couldn't find AIFC FVER chunk.\n", filename);
 
-    if (ossplay_lseek (fd, sound_loc, SEEK_SET) == -1)
-      {
-        perror_msg (filename);
-        print_msg (ERRORM, "Can't seek in file\n");
-        return E_DECODE;
-      }
+  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, format, channels, speed);
+    print_verbose_fileinfo (filename, type, f.format, f.channels, f.speed);
 
-  return decode_sound (dsp, fd, sound_size, format, channels, speed,
-                       (void *)&msadpcm_val);
+  return decode_sound (dsp, fd, f.sound_size, f.format, f.channels, f.speed,
+                       (void *)&(f.msadpcm_val));
 }
 
 /*ARGSUSED*/
   uint32 fmt = 0, i, p = 24, an_len = 0;
 
   p = be_int (hdr + 4, 4);
-  if (memcmp (hdr + 8, "\xFF\xFF\xFF\xFF", 4))
-    filelen = be_int (hdr + 8, 4);
-  else
-    filelen = BIG_SPECIAL;
   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 (verbose > 1)
-    {
-      if (filelen == BIG_SPECIAL)
-        print_msg (NORMALM, "%s: Filelen: unspecified\n", filename);
-      else
-        print_msg (NORMALM, "%s: Filelen: %u\n", filename, filelen);
-    }
-  if (verbose > 2) print_msg (NORMALM, "%s: Offset: %u\n", filename, p);
+    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)
     {
               else tag = hdr + i + 1;
             }
           *tag = '\0';
-          print_msg (NORMALM, "%s: Annotations: %s\n", filename, hdr);
+          print_msg (VERBOSEM, "%s: Annotations: %s\n", filename, hdr);
         }
     }
 
       blklen = len = le_int (buf, 3);
 
       if (verbose > 2)
-	print_msg (NORMALM, "%s: %0x: Block type %d, len %d\n",
+	print_msg (VERBOSEM, "%s: %0x: Block type %d, len %d\n",
 		   filename, data_offs, block_type, len);
       switch (block_type)
 	{
   char chn[32];
   const char * fmt = "";
 
+  if (force_speed) speed = force_speed;
   switch (type)
     {
       case WAVE_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 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;
     }
     }
   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)