Logo Search packages:      
Sourcecode: labplot version File versions

modules.c

/*
      Audio File Library
      Copyright (C) 2000, Silicon Graphics, Inc.

      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Library General Public
      License as published by the Free Software Foundation; either
      version 2 of the License, or (at your option) any later version.

      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Library General Public License for more details.

      You should have received a copy of the GNU Library General Public
      License along with this library; if not, write to the
      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      Boston, MA  02111-1307  USA.
*/

/*
      modules.c
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <assert.h>

#include <audiofile.h>
#include "afinternal.h"
#include "modules.h"
#include "pcm.h"
#include "util.h"
#include "units.h"
#include "compression.h"
#include "byteorder.h"
#include "print.h"

#include "rebuffer.h"

#ifdef DEBUG
#define CHNK(X) X
#define DEBG(X) X
#else
#define CHNK(X)
#define DEBG(X)
#endif

#define NULLMODULEPARAM

extern _PCMInfo _af_default_signed_integer_pcm_mappings[];
extern _PCMInfo _af_default_unsigned_integer_pcm_mappings[];
extern _PCMInfo _af_default_float_pcm_mapping;
extern _PCMInfo _af_default_double_pcm_mapping;

extern _CompressionUnit _af_compression[];

/* Define rebuffering modules. */
extern _AFmodule int2rebufferv2f, int2rebufferf2v;

/*
      module utility routines
*/

/*
      _AFnewmodinst creates a module instance from a module.
      It returns a structure, not a pointer to a structure.
*/
_AFmoduleinst _AFnewmodinst (_AFmodule *mod)
{
      _AFmoduleinst ret;

      ret.inc = ret.outc = NULL;
      ret.modspec = NULL;
      ret.u.pull.source = NULL;
      ret.mod = mod;
      ret.free_on_close = AF_FALSE;
      ret.valid = AF_FALSE;

      return(ret);
}

/*
      _AFfreemodspec:   useful routine for mod.free function pointer
*/
void _AFfreemodspec (_AFmoduleinst *i)
{
      if (i->modspec)
            free(i->modspec);
      i->modspec = NULL;
}

/*
      _AFpull: used a lot -- see comments in README.modules
*/
AFframecount _AFpull (_AFmoduleinst *i, AFframecount nframes2pull)
{
      _AFmoduleinst *src = i->u.pull.source;

      i->inc->nframes = nframes2pull;
      CHNK(printf("%s pulling %" AF_FRAMECOUNT_PRINT_FMT " frames from %s\n",
            i->mod->name, i->inc->nframes, src->mod->name));
      (*src->mod->run_pull)(src);
      CHNK(_af_print_chunk(i->inc));

      CHNK(printf("%s received %" AF_FRAMECOUNT_PRINT_FMT " frames from %s\n",
            i->mod->name, i->inc->nframes, src->mod->name));
      return i->inc->nframes;
}

/*
      _AFsimplemodrun
*/
void _AFsimplemodrun_pull (_AFmoduleinst *i)
{
      _AFpull(i, i->outc->nframes);
      (*i->mod->run)(i->inc, i->outc, i->modspec);
}

/*
      _AFpush
*/
void _AFpush (_AFmoduleinst *i, AFframecount nframes2push)
{
      _AFmoduleinst *snk = i->u.push.sink;
      i->outc->nframes = nframes2push;
      CHNK(printf("%s pushing %" AF_FRAMECOUNT_PRINT_FMT " frames into %s\n",
            i->mod->name, i->outc->nframes, snk->mod->name));
      CHNK(_af_print_chunk(i->outc));
      (*(snk->mod->run_push))(snk);
}

/*
      _AFpushat
*/
void _AFpushat (_AFmoduleinst *i, AFframecount startframe, bool stretchint,
      AFframecount nframes2push)
{
      _AFmoduleinst *snk = i->u.push.sink;

      void *saved_buf = i->outc->buf;
      i->outc->buf = ((char *)i->outc->buf) +
            (_af_format_frame_size_uncompressed(&i->outc->f,stretchint) * startframe);

      i->outc->nframes = nframes2push;
      CHNK(printf("%s pushing %" AF_FRAMECOUNT_PRINT_FMT " frames into %s "
            "with OFFSET %" AF_FILEOFFSET_PRINT_FMT " frames\n",
            i->mod->name, i->outc->nframes, snk->mod->name, startframe));
      CHNK(_af_print_chunk(i->outc));
      (*(snk->mod->run_push))(snk);

      i->outc->buf = saved_buf;
}

/*
      _AFsimplemodrun
*/
void _AFsimplemodrun_push (_AFmoduleinst *i)
{
      i->outc->nframes = i->inc->nframes;
      (*(i->mod->run))(i->inc, i->outc, i->modspec);
      _AFpush(i, i->outc->nframes);
}

/*
      These macros each declare a module.

      The module uses _AFsimplemodrun_pull and _AFsimplemodrun_push
      (see comments in README.modules).  Thus we only have to define
      one routine that does the actual processing.

      The arguments to the macros are as follows:

      name - name of module
      desc - code for module's "describe" function (see README.modules)
      intype - type of elements of input buffer
      outtype - type of elements of output buffer
      action - action to take in inner loop -- indexes "ip" and "op" with "i"

      modspectype - (MODULEM) this will initialize a pointer "m"
      to this instance's modspec data, which is of type modspectype

      Don't use _MODULE directly.

      The code in "desc" is executed once, after the module is
      initialized.  It can reference "i->modspec" and should modify
      "i->outc->f".  A pointer "f" initialized to "&i->outc->f" is
      emitted prior to "desc"; use this to to keep the code cleaner.

      Note that the generated "run" routine shouldn't set outc->nframes since

      * outc->nframes is set to inc->nframes by _AFsimplemodrun_push
      * inc->nframes is set to outc->nframes by _AFsimplemodrun_pull

      The whole point of the simplified "run" routine is that you don't
      have to worry about push or pull.

      See README.modules for more info on how modules work.
*/

#define _MODULE( name, desc, \
      intype, outtype, chans, preamble, action, postamble )\
static void name##run(_AFchunk *inc, _AFchunk *outc, void *modspec)\
{\
      intype *ip = inc->buf;\
      outtype *op = outc->buf;\
      int count = inc->nframes * (chans);\
      int i;\
      \
      preamble;\
      for(i=0; i < count; ++i) \
      action;\
      postamble;\
}\
\
static void name##describe(struct _AFmoduleinst *i)\
{\
      _AudioFormat *f = &i->outc->f; \
      desc;\
}\
\
static _AFmodule name =\
{ \
      #name,\
      name##describe, \
      AF_NULL, AF_NULL, \
      _AFsimplemodrun_pull, AF_NULL, AF_NULL, \
      _AFsimplemodrun_push, AF_NULL, AF_NULL, \
      name##run, \
      _AFfreemodspec \
};

#define MODULE(name, desc, intype, outtype, action)\
      _MODULE(name, desc, intype, outtype, inc->f.channelCount, \
      NULLMODULEPARAM, action, NULLMODULEPARAM)

#define MODULEM(name, desc, intype, outtype, modspectype, action)\
      _MODULE(name, desc, intype, outtype, inc->f.channelCount, \
      modspectype *m = (modspectype *) modspec, action, NULLMODULEPARAM)

/*
      Byte-order-swapping modules.
*/

#define MODULESWAP(name, type, action) \
MODULE(name, \
      f->byteOrder = (f->byteOrder==AF_BYTEORDER_LITTLEENDIAN) ?\
      AF_BYTEORDER_BIGENDIAN : AF_BYTEORDER_LITTLEENDIAN,\
      type, type,\
      action)

MODULESWAP(swap2, uchar2,
      { char3u u; uchar1 c; u.uchar2.s0 = ip[i];
      c = u.uchar1.c1; u.uchar1.c1 = u.uchar1.c0; u.uchar1.c0 = c;
      op[i] = u.uchar2.s0; })

MODULESWAP(swap3, real_char3,
      { char3u u; uchar1 c; u.real_char3_low.c3 = ip[i];
      c = u.uchar1.c3; u.uchar1.c3 = u.uchar1.c1; u.uchar1.c1 = c;
      op[i] = u.real_char3_low.c3; })

MODULESWAP(swap4, uchar4,
      { char3u u; uchar1 c; u.uchar4.i = ip[i];
      c = u.uchar1.c3; u.uchar1.c3 = u.uchar1.c0; u.uchar1.c0 = c;
      c = u.uchar1.c1; u.uchar1.c1 = u.uchar1.c2; u.uchar1.c2 = c;
      op[i] = u.uchar4.i; })

MODULESWAP(swap8, real_char8,
      { real_char8 *i8 = &ip[i]; real_char8 *o8 = &op[i];
      o8->c0 = i8->c7;
      o8->c1 = i8->c6;
      o8->c2 = i8->c5;
      o8->c3 = i8->c4;
      o8->c4 = i8->c3;
      o8->c5 = i8->c2;
      o8->c6 = i8->c1;
      o8->c7 = i8->c0; })

/*
      modules for dealing with 3-byte integers
*/

/* convert 0xaabbcc to 0xssaabbcc */
#ifdef WORDS_BIGENDIAN
MODULE(real_char3_to_schar3, f /* NOTUSED */, real_char3, schar3,
      {
            char3u u;
            u.real_char3_high.c3 = ip[i];
            u.real_char3_high.pad = 0;
            op[i] = u.schar3.i >> 8;
      })
#else
MODULE(real_char3_to_schar3, f /* NOTUSED */, real_char3, schar3,
      {
            char3u u;
            u.real_char3_low.c3 = ip[i];
            u.real_char3_low.pad = 0;
            op[i] = u.schar3.i >> 8;
      })
#endif

/* convert 0xaabbcc to 0x00aabbcc */
#ifdef WORDS_BIGENDIAN
MODULE(real_char3_to_uchar3, f /* NOTUSED */, real_char3, uchar3,
      {
            char3u u;
            u.real_char3_high.c3 = ip[i];
            u.real_char3_high.pad = 0;
            op[i] = u.uchar3.i >> 8;
      })
#else
MODULE(real_char3_to_uchar3, f /* NOTUSED */, real_char3, uchar3,
      {
            char3u u;
            u.real_char3_low.c3 = ip[i];
            u.real_char3_low.pad = 0;
            op[i] = u.uchar3.i >> 8;
      })
#endif

/* convert 0x??aabbcc to 0xaabbcc */
#ifdef WORDS_BIGENDIAN
MODULE(char3_to_real_char3, f /* NOTUSED */, uchar3, real_char3,
      {
            char3u u;
            u.uchar3.i = ip[i];
            op[i] = u.real_char3_low.c3;
      })
#else
MODULE(char3_to_real_char3, f /* NOTUSED */, uchar3, real_char3,
      {
            char3u u;
            u.uchar3.i = ip[i];
            op[i] = u.real_char3_high.c3;
      })
#endif

/*
      float <--> double ; CASTS
*/

MODULE(float2double, f->sampleFormat = AF_SAMPFMT_DOUBLE,
      float, double, op[i] = ip[i] )
MODULE(double2float, f->sampleFormat = AF_SAMPFMT_FLOAT,
      double, float, op[i] = ip[i] )

/*
      int2floatN - expects 8N-bit 2's comp ints, outputs floats ; CASTS
*/

MODULE(int2float1, f->sampleFormat = AF_SAMPFMT_FLOAT,
      schar1, float, op[i] = ip[i])
MODULE(int2float2, f->sampleFormat = AF_SAMPFMT_FLOAT,
      schar2, float, op[i] = ip[i])
MODULE(int2float3, f->sampleFormat = AF_SAMPFMT_FLOAT,
      schar3, float, op[i] = ip[i])
MODULE(int2float4, f->sampleFormat = AF_SAMPFMT_FLOAT,
      schar4, float, op[i] = ip[i])

/*
      int2doubleN - expects 8N-bit 2's comp ints, outputs doubles ; CASTS
*/

MODULE(int2double1, f->sampleFormat = AF_SAMPFMT_DOUBLE,
      schar1, double, op[i] = ip[i])
MODULE(int2double2, f->sampleFormat = AF_SAMPFMT_DOUBLE,
      schar2, double, op[i] = ip[i])
MODULE(int2double3, f->sampleFormat = AF_SAMPFMT_DOUBLE,
      schar3, double, op[i] = ip[i])
MODULE(int2double4, f->sampleFormat = AF_SAMPFMT_DOUBLE,
      schar4, double, op[i] = ip[i])

/*
      The following modules perform the transformation between one
      pcm mapping and another.

      The modules all use MODULETRANS; some of them also perform
      clipping.

      Use initpcmmod() to create an instance of any of these modules.
      initpcmmod() takes an _PCMInfo describing the desired output
      pcm mapping.
*/

typedef struct pcmmodspec
{
      /* These are the computed parameters of the transformation. */
      double m, b;
      double maxv, minv;

      /* This is what goes in i->outc->f. */
      _PCMInfo output_mapping;
} pcmmodspec;

/*
      initpcmmod
*/
static _AFmoduleinst initpcmmod (_AFmodule *mod,
      _PCMInfo *input_mapping, _PCMInfo *output_mapping)
{
      _AFmoduleinst ret = _AFnewmodinst(mod);
      pcmmodspec *m = _af_malloc(sizeof (pcmmodspec));
      ret.modspec = m;

      /* Remember output mapping for use in the describe function. */
      m->output_mapping = *output_mapping;

      /*
            Compute values needed to perform transformation if the module
            being initialized does a transformation..
      */
      if (input_mapping)
      {
            m->m = output_mapping->slope / input_mapping->slope;
            m->b = output_mapping->intercept - m->m * input_mapping->intercept;
      }

      /* Remember clip values. */
      m->minv = output_mapping->minClip;
      m->maxv = output_mapping->maxClip;
      return ret;
}

#define MODULETRANS( name, xtradesc, intype, outtype, action ) \
MODULEM(name, \
      { \
            f->pcm = ((pcmmodspec *) i->modspec)->output_mapping; \
            xtradesc; \
      }, \
      intype, outtype, pcmmodspec, \
      action)


MODULETRANS(floattransform, NULLMODULEPARAM, float, float, \
      op[i]=(m->b + m->m * ip[i]))
MODULETRANS(doubletransform, NULLMODULEPARAM, double, double, \
      op[i]=(m->b + m->m * ip[i]))

/*
      float2intN_clip - expects floats,
            outputs CLIPped, 8N-bit, transformed 2's comp ints
      double2intN_clip - same deal with doubles
*/

#define TRANS_CLIP(type) \
{\
      double d=(m->b + m->m * ip[i]); \
      op[i] = \
            (((type)((d>(m->maxv)) ? (m->maxv) : ((d<(m->minv))?(m->minv):d)))); \
}

MODULETRANS(float2int1_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 8; },
      float, schar1, TRANS_CLIP(schar1))
MODULETRANS(float2int2_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 16; },
      float, schar2, TRANS_CLIP(schar2))
MODULETRANS(float2int3_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 24; },
      float, schar3, TRANS_CLIP(schar3))
MODULETRANS(float2int4_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 32; },
      float, schar4, TRANS_CLIP(schar4))

MODULETRANS(double2int1_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 8; },
      double, schar1, TRANS_CLIP(schar1))
MODULETRANS(double2int2_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 16; },
      double, schar2, TRANS_CLIP(schar2))
MODULETRANS(double2int3_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 24; },
      double, schar3, TRANS_CLIP(schar3))
MODULETRANS(double2int4_clip,
      { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 32; },
      double, schar4, TRANS_CLIP(schar4))

/*
      clipping modules - use initpcmmod() to make one of these

      clips to range given as argument to init function.
*/

#define MODULECLIP(name, type)\
MODULEM(name, \
      { f->pcm = ((pcmmodspec *)i->modspec)->output_mapping; }, \
      type, type, pcmmodspec, \
      { \
            type d=ip[i]; \
            type min=(type)(m->minv); \
            type max=(type)(m->maxv); \
            op[i] = ((d>max) ? max : ((d<min) ? min : d)); \
      } )

MODULECLIP(clipfloat, float)
MODULECLIP(clipdouble, double)
MODULECLIP(clip1, schar1)
MODULECLIP(clip2, schar2)
MODULECLIP(clip3, schar3)
MODULECLIP(clip4, schar4)

/*
      unsigned2signedN - expects 8N-bit unsigned ints, outputs 2's comp
*/

MODULE(unsigned2signed1,
      {
            double shift = (double) MIN_INT8;
            f->sampleFormat = AF_SAMPFMT_TWOSCOMP;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      uchar1, schar1,
      op[i] = ip[i] + MIN_INT8)
MODULE(unsigned2signed2,
      {
            double shift = (double) MIN_INT16;
            f->sampleFormat = AF_SAMPFMT_TWOSCOMP;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      uchar2, schar2,
      op[i] = ip[i] + MIN_INT16)
MODULE(unsigned2signed3,
      {
            double shift = (double) MIN_INT24;
            f->sampleFormat = AF_SAMPFMT_TWOSCOMP;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      uchar3, schar3,
      op[i] = ip[i] + MIN_INT24)
MODULE(unsigned2signed4,
      {
            double shift = (double) MIN_INT32;
            f->sampleFormat = AF_SAMPFMT_TWOSCOMP;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      uchar4, schar4,
      op[i] = ip[i] + MIN_INT32)

/* !! unsigned2signed4 shouldn't work, but it does !! */


/*
      signed2unsignedN - expects 8N-bit 2's comp ints, outputs unsigned
*/

MODULE(signed2unsigned1,
      {
            double shift = -(double) MIN_INT8;
            f->sampleFormat = AF_SAMPFMT_UNSIGNED;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      schar1, uchar1,
      op[i] = ip[i] - MIN_INT8)
MODULE(signed2unsigned2,
      {
            double shift = -(double) MIN_INT16;
            f->sampleFormat = AF_SAMPFMT_UNSIGNED;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      schar2, uchar2,
      op[i] = ip[i] - MIN_INT16)
MODULE(signed2unsigned3,
      {
            double shift = -(double) MIN_INT24;
            f->sampleFormat = AF_SAMPFMT_UNSIGNED;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      schar3, uchar3,
      op[i] = ip[i] - MIN_INT24)
MODULE(signed2unsigned4,
      {
            double shift = -(double) MIN_INT32;
            f->sampleFormat = AF_SAMPFMT_UNSIGNED;
            f->pcm.intercept += shift;
            f->pcm.minClip += shift;
            f->pcm.maxClip += shift;
      },
      schar4, uchar4,
      op[i] = ip[i] - MIN_INT32)

/* !! signed2unsigned4 shouldn't work, but it does !! */


/*
      These convert between different 2's complement integer formats
      with no roundoff/asymmetric errors.  They should also work faster
      than converting integers to floats and back to other integers.

      They are only meant to be used when the input and output integers
      have the default PCM mapping; otherwise, arrangemodules will
      make the conversion go through floating point and these modules
      will not be used.
*/

#define intmap _af_default_signed_integer_pcm_mappings /* shorthand */

MODULE(int1_2, { f->sampleWidth = 16; f->pcm=intmap[2]; },
      schar1, schar2, op[i] = ip[i] << 8)
MODULE(int1_3, { f->sampleWidth = 24; f->pcm=intmap[3]; },
      schar1, schar3, op[i] = ip[i] << 16)
MODULE(int1_4, { f->sampleWidth = 32; f->pcm=intmap[4]; },
      schar1, schar4, op[i] = ip[i] << 24)

MODULE(int2_1, { f->sampleWidth = 8; f->pcm=intmap[1]; },
      schar2, schar1, op[i] = ip[i] >> 8)
MODULE(int2_3, { f->sampleWidth = 24; f->pcm=intmap[3]; },
      schar2, schar3, op[i] = ip[i] << 8)
MODULE(int2_4, { f->sampleWidth = 32; f->pcm=intmap[4]; },
      schar2, schar4, op[i] = ip[i] << 16)

MODULE(int3_1, { f->sampleWidth = 8; f->pcm=intmap[1]; },
      schar3, schar1, op[i] = ip[i] >> 16)
MODULE(int3_2, { f->sampleWidth = 16; f->pcm=intmap[2]; },
      schar3, schar2, op[i] = ip[i] >> 8)
MODULE(int3_4, { f->sampleWidth = 32; f->pcm=intmap[4]; },
      schar3, schar4, op[i] = ip[i] << 8)

MODULE(int4_1, { f->sampleWidth = 8; f->pcm=intmap[1]; },
      schar4, schar1, op[i] = ip[i] >> 24)
MODULE(int4_2, { f->sampleWidth = 16; f->pcm=intmap[2]; },
      schar4, schar2, op[i] = ip[i] >> 16)
MODULE(int4_3, { f->sampleWidth = 24; f->pcm=intmap[3]; },
      schar4, schar3, op[i] = ip[i] >> 8)

#undef intmap

/*
      channel changer modules - convert channels using channel matrix

      The channel matrix is a two-dimensional array of doubles, the rows
      of which correspond to the virtual format, and the columns
      of which correspond to the file format.

      If the channel matrix is null (unspecified), then the default
      behavior occurs (see initchannelchange).

      Internally, the module holds a copy of the matrix in which the
      rows correspond to the output format, and the columns correspond
      to the input format (therefore, if reading==AF_FALSE, the matrix
      is transposed as it is copied).
*/

typedef struct channelchangedata
{
      int   outchannels;
      double      minClip;
      double      maxClip;
      double      *matrix;
} channelchangedata;

/*
      channelchangefree
*/
static void channelchangefree (struct _AFmoduleinst *i)
{
      channelchangedata *d = i->modspec;

      assert(d);
      assert(d->matrix);

      free(d->matrix);
      free(d);

      i->modspec = AF_NULL;
}

/*
      channelchangedescribe
*/
static void channelchangedescribe (struct _AFmoduleinst *i)
{
      channelchangedata *m = (channelchangedata *) i->modspec;
      i->outc->f.channelCount = m->outchannels;
      i->outc->f.pcm.minClip = m->minClip;
      i->outc->f.pcm.maxClip = m->maxClip;
}

#define CHANNELMOD( name, type, zero_op, action, afteraction ) \
static void name##run(_AFchunk *inc, _AFchunk *outc, void *modspec) \
{ \
      type *ip = inc->buf; \
      type *op = outc->buf; \
      double *matrix = ((channelchangedata *)modspec)->matrix; \
      double *m; \
      int frame, inch, outch; \
      \
      for (frame=0; frame < outc->nframes; frame++) \
      { \
            type *ipsave; \
            \
            m = matrix; \
            ipsave = ip; \
            \
            for (outch = 0; outch < outc->f.channelCount; outch++) \
            { \
                  zero_op; \
                  ip = ipsave; \
                  \
                  for (inch = 0; inch < inc->f.channelCount; inch++) \
                        action;\
                  \
                  afteraction; \
                  op++;\
            }\
      }\
}\
\
static _AFmodule name =\
{ \
      #name, \
      channelchangedescribe, \
      AF_NULL, AF_NULL, \
      _AFsimplemodrun_pull, AF_NULL, AF_NULL, \
      _AFsimplemodrun_push, AF_NULL, AF_NULL, \
      name##run, \
      channelchangefree \
};

CHANNELMOD(channelchangefloat,  float,  *op = 0.0, *op += *ip++ * *m++, \
      NULLMODULEPARAM)
CHANNELMOD(channelchangedouble, double, *op = 0.0, *op += *ip++ * *m++, \
      NULLMODULEPARAM)

#define CHANNELINTMOD(name, type) \
      CHANNELMOD(name, type, \
      double d=0.0, \
      d += *ip++ * *m++, \
      { \
            double minv=outc->f.pcm.minClip; \
            double maxv=outc->f.pcm.maxClip; \
            *op = (type) ((d>maxv) ? maxv : ((d<minv) ? minv : d)); \
      } )

CHANNELINTMOD(channelchange1, schar1)
CHANNELINTMOD(channelchange2, schar2)
CHANNELINTMOD(channelchange3, schar3)
CHANNELINTMOD(channelchange4, schar4)

/*
      initchannelchange
*/
static _AFmoduleinst initchannelchange (_AFmodule *mod,
      double *matrix, _PCMInfo *outpcm,
      int inchannels, int outchannels,
      bool reading)
{
      _AFmoduleinst ret;
      channelchangedata *d;
      int i, j;

      ret = _AFnewmodinst(mod);

      d = _af_malloc(sizeof (channelchangedata));
      ret.modspec = d;
      d->outchannels = outchannels;
      d->minClip = outpcm->minClip;
      d->maxClip = outpcm->maxClip;
      d->matrix = _af_malloc(sizeof (double) * inchannels * outchannels);

      /*
            Set d->matrix to a default matrix if a matrix was not specified.
      */
      if (!matrix)
      {
            bool special=AF_FALSE;

            /* Handle many common special cases. */

            if (inchannels==1 && outchannels==2)
            {
                  static double m[]={1,1};
                  matrix=m;
                  special=AF_TRUE;
            }
            else if (inchannels==1 && outchannels==4)
            {
                  static double m[]={1,1,0,0};
                  matrix=m;
                  special=AF_TRUE;
            }
            else if (inchannels==2 && outchannels==1)
            {
                  static double m[]={.5,.5};
                  matrix=m;
                  special=AF_TRUE;
            }
            else if (inchannels==2 && outchannels==4)
            {
                  static double m[]={1,0,0,1,0,0,0,0};
                  matrix=m;
                  special=AF_TRUE;
            }
            else if (inchannels==4 && outchannels==1)
            {
                  static double m[]={.5,.5,.5,.5};
                  matrix=m;
                  special=AF_TRUE;
            }
            else if (inchannels==4 && outchannels==2)
            {
                  static double m[]={1,0,1,0,0,1,0,1};
                  matrix=m;
                  special=AF_TRUE;
            }
            else
            {
                  /*
                        Each input channel from 1 to N
                        maps to output channel 1 to N where
                        N=min(inchannels, outchannels).
                  */

                  for(i=0; i < inchannels; i++)
                        for(j=0; j < outchannels; j++)
                              d->matrix[j*inchannels + i] =
                                    (i==j) ? 1.0 : 0.0;
            }

            if (special)
                  memcpy(d->matrix, matrix,
                        sizeof (double) * inchannels * outchannels);
      }
      /* Otherwise transfer matrix into d->matrix. */
      else
      {
            /* reading: copy matrix */
            if (reading)
            {
                  memcpy(d->matrix, matrix, sizeof (double) * inchannels * outchannels);
            }
            /* writing: transpose matrix */
            else
            {
                  for (i=0; i < inchannels; i++)
                        for (j=0; j < outchannels; j++)
                              d->matrix[j*inchannels + i] =
                                    matrix[i*outchannels + j];
            }
      }

      DEBG(printf("channelchange d->matrix="));
      DEBG(_af_print_channel_matrix(d->matrix, inchannels, outchannels));
      DEBG(printf("\n"));

      return(ret);
}

/* just used here */
typedef struct current_state
{
      _AFmoduleinst *modinst; /* current mod instance we're creating */
      _AFchunk *inchunk;      /* current input chunk */
      _AFchunk *outchunk;     /* current output chunk */
} current_state;

/*
      addmod is called once per added module instance.  It does the
      work of putting the module instance in the list and assigning
      it an input and output chunk.
*/
static void addmod (current_state *current, _AFmoduleinst modinst)
{
      *(current->modinst) = modinst;
      current->modinst->valid = AF_TRUE; /* at this point mod must be valid */

      /* Assign the new module instance an input and an output chunk. */

      current->modinst->inc = current->inchunk;
      current->modinst->outc = current->outchunk;

      /*
            The output chunk has the same format and number of frames
            as input chunk, except in whatever way the 'describe'
            method tells us (see README.modules).
      */

      *(current->outchunk) = *(current->inchunk);

      if (current->modinst->mod->describe)
            (*current->modinst->mod->describe)(current->modinst);

      /*
            Advance to next module and next chunks.  Note that next
            moduleinst will have this module's out chunk as input.
      */

      current->modinst++;
      current->inchunk = current->outchunk;
      current->outchunk++;
}

/*
      initfilemods:

      Functions that deal with extended-lifetime file read / file write
      modules and their extended-lifetime rebuffer modules.
      called once in the lifetime of an AFfilehandle.

      If h->access == _AF_READ_ACCESS:

      Create the module which will be the first module in the chain,
      the one which reads the file.  This module does the decompression
      if necessary, or it could just be a PCM file reader.

      If h->access == _AF_WRITE_ACCESS:

      Create the module which will be the last module in the chain,
      the one which writes the file.      This module does the compression
      if necessary, or it could just be a PCM file writer.

      Also creates a rebuffer module for these modules if necessary.
*/
static status initfilemods (_Track *track, AFfilehandle h)
{
      int compressionIndex;
      _CompressionUnit *compunit;
      AFframecount chunkframes;

      compressionIndex = _af_compression_index_from_id(track->f.compressionType);
      compunit = &_af_compression[compressionIndex];

      /* Invalidate everything. */

      track->ms.filemodinst.valid = AF_FALSE;
      track->ms.filemod_rebufferinst.valid = AF_FALSE;

      /*
            Seek to beginning of sound data in the track.

            This is needed ONLY for those modules which have to
            read/write some kind of pre-data header or table in the
            sound data chunk of the file (such as aware).  This is NOT
            the seek that sets the file at the beginning of the data.
      */
      /* XXXmpruett -- we currently don't set seekok.
      if (h->seekok && af_fseek(h->fh, track->fpos_first_frame, SEEK_SET) < 0)
      */
      if (af_fseek(h->fh, track->fpos_first_frame, SEEK_SET) < 0)
      {
            _af_error(AF_BAD_LSEEK, "unable to position file handle at beginning of sound data");
            return AF_FAIL;
      }

      /* Create file read/write module. */

      track->filemodhappy = AF_TRUE;

      if (h->access == _AF_READ_ACCESS)
            track->ms.filemodinst =
                  (*compunit->initdecompress)(track, h->fh, h->seekok,
                        (h->fileFormat==AF_FILE_RAWDATA), &chunkframes);
      else
            track->ms.filemodinst =
                  (*compunit->initcompress)(track, h->fh, h->seekok,
                        (h->fileFormat==AF_FILE_RAWDATA), &chunkframes);

      if (!track->filemodhappy)
            return AF_FAIL;

      track->ms.filemodinst.valid = AF_TRUE;

      /*
            WHEN DOES THE FILE GET LSEEKED ?

            Somebody sometime has got to lseek the file to the
            beginning of the audio data.  Similarly, somebody
            has to lseek the file at the time of reset or sync.
            Furthermore, we have to make sure that we operate
            correctly if more than one track is being read or written.
            This is handled differently based on whether we are
            reading or writing, and whether the ONE_TRACK_ONLY lseek
            optimization is engaged.

            READING:

            If we are reading, the file needs to be positioned once
            before we start reading and then once per seek.

            If ONE_TRACK_ONLY is not defined, then there can
            be multiple tracks in the file.  Thus any call to
            afReadFrames could cause the file pointer to be
            put anywhere: we can not rely on the file pointer
            tracking only one track in the file, thus we must seek
            to the current position in track N whenever we begin
            an AFreadframes on track N.  Thus the lseek is done in
            afReadFrames.  When a reset occurs (including the initial
            one), we merely set trk->fpos_next_frame, and the next
            afReadFrames will seek the file there before proceeding.

            If ONE_TRACK_ONLY is defined, meaning there can only
            be 1 track in the file, we do not need to ever seek
            during normal sequential operation, because the file
            read module is the only module which ever accesses the
            file after _afOpenFile returns.  In this case, we do
            not need to do the expensive lseek at the beginning of
            every AFreadframes call.  We need only seek once when the
            file is first opened and once when the file is seeked.
            At both of these times, we reset the modules.  So we
            can do the lseek in resetmodules() right after it has
            called all of the modules' reset2 methods.

            WRITING:

            If we are writing, the file needs to be positioned once
            before we start writing and it needs to be positioned
            after every complete sync operation on the file.

            If ONE_TRACK_ONLY is not defined, then there can be
            multiple tracks in the file.  This assumes space for
            the tracks has been preallocated.  Thus any call to
            AFwriteframes could cause the file pointer to be
            put anywhere: we can not rely on the file pointer
            tracking only one track in the file, thus we must seek
            to the current position in track n whenever we begin
            an AFwriteframes on track n.  Thus the lseek is done
            in AFwriteframes.  When we first start, and when a sync
            occurs, we merely set trk->fpos_next_frame, and the next
            AFwriteframes will seek the file there before proceeding.

            If ONE_TRACK_ONLY is defined, meaning there can only
            be 1 track in the file, we do not need to ever seek
            during normal sequential operation, because the file
            write module is the only module which ever accesses
            the file after _AFopenfile returns.  In this case, we
            do not need to do the expensive lseek at the beginning
            of every AFwriteframes call.  We can do the lseek for
            the initial case right here (that's what you see below),
            and we can do the lseek for syncs in _AFsyncmodules right
            after it has called all of the modules' sync2 methods.

            One annoying exceptional case is _AFeditrate, which
            can get called at any time.  But it saves and restores
            the file position at its beginning and end, so it's
            no problem.

            WHY THE F(*#&@ DON'T YOU JUST HAVE MULTIPLE FILE DESCRIPTORS?

            The obviously and blatantly better way to do this would be
            to simply open one fd per track and then all the problems
            go away!  Too bad we offer afOpenFD() in the API, which
            makes it impossible for the AF to get more than 1 fd
            for the file.  afOpenFD() is unfortunately used far
            more often than afOpenFile(), so the benefit of doing
            the optimization the "right" way in the cases where
            afOpenFile() are used are not currently too great.
            But one day we will have to phase out afOpenFD().
            Too bad, it seemed like such a great idea when we put
            it in.
      */

#ifdef ONE_TRACK_ONLY
      if (h->access == _AF_WRITE_ACCESS)
      {
            if (h->seekok && af_fseek(h->fh, track->fpos_next_frame, SEEK_SET) < 0)
            {
                  _af_error(AF_BAD_LSEEK,
                        "unable to position write ptr at first data frame");
                  return AF_FAIL;
            }
      }
#endif

      /* Create its rebuffer module. */

      if (compunit->needsRebuffer)
      {
            /* We assume the following for now. */
            assert(compunit->nativeSampleFormat == AF_SAMPFMT_TWOSCOMP);
            assert(compunit->nativeSampleWidth == 16);

            if (h->access == _AF_WRITE_ACCESS)
                  track->ms.filemod_rebufferinst =
                        _af_initint2rebufferv2f(chunkframes*track->f.channelCount,
                              compunit->multiple_of);
            else
                  track->ms.filemod_rebufferinst =
                        _af_initint2rebufferf2v(chunkframes*track->f.channelCount,
                              compunit->multiple_of);

            track->ms.filemod_rebufferinst.valid = AF_TRUE;
      }
      else
            track->ms.filemod_rebufferinst.valid = AF_FALSE;

      /*
            These modules should not get freed until the file handle
            is destroyed (i.e. the file is closed).
      */

      track->ms.filemodinst.free_on_close = AF_TRUE;
      track->ms.filemod_rebufferinst.free_on_close = AF_TRUE;

      return AF_SUCCEED;
}

/*
      addfilereadmods: called once per setup of the modules
      for a given AFfilehandle
*/
static status addfilereadmods (current_state *current, _Track *track,
      AFfilehandle h)
{
      assert(track->ms.filemodinst.valid);

      /* Fail in case code is broken and NDEBUG is defined. */
      if (!track->ms.filemodinst.valid)
            return AF_FAIL;

      addmod(current, track->ms.filemodinst);
      if (track->ms.filemod_rebufferinst.valid)
            addmod(current, track->ms.filemod_rebufferinst);

      return AF_SUCCEED;
}

/*
      addfilewritemods is called once per setup of the modules
      for a given AFfilehandle.
*/
static status addfilewritemods (current_state *current, _Track *track,
      AFfilehandle h)
{
      assert(track->ms.filemodinst.valid);

      /* Fail in case code is broken and NDEBUG is defined. */
      if (!track->ms.filemodinst.valid)
            return(AF_FAIL);

      if (track->ms.filemod_rebufferinst.valid)
            addmod(current, track->ms.filemod_rebufferinst);

      addmod(current, track->ms.filemodinst);

      return(AF_SUCCEED);
}

/*
      disposefilemods: called once in the lifetime of an AFfilehandle
*/
static status disposefilemods (_Track *track)
{
      if (track->ms.filemodinst.valid &&
            track->ms.filemodinst.mod->free)
            (*track->ms.filemodinst.mod->free)(&track->ms.filemodinst);

      track->ms.filemodinst.valid = AF_FALSE;

      if (track->ms.filemod_rebufferinst.valid &&
            track->ms.filemod_rebufferinst.mod->free)
            (*track->ms.filemod_rebufferinst.mod->free)(&track->ms.filemod_rebufferinst);

      track->ms.filemod_rebufferinst.valid = AF_FALSE;

      return AF_SUCCEED;
}

/*
      useAP: rate conversion AP decision maker and warner and kludger
*/
static bool useAP (double inrate, double outrate,
      double *inratep, double *outratep)
{
      bool instandard =
            (inrate==8000 || inrate==11025 || inrate==16000 ||
            inrate==22050 || inrate==32000 || inrate==44100 ||
            inrate==48000);
      bool outstandard =
            (outrate==8000 || outrate==11025 || outrate==16000 ||
            outrate==22050 || outrate==32000 || outrate==44100 ||
            outrate==48000);
      bool incodec;
      bool outcodec;

      incodec = (inrate==_AF_SRATE_CODEC || inrate==(long)_AF_SRATE_CODEC);
      outcodec = (outrate==_AF_SRATE_CODEC || outrate==(long)_AF_SRATE_CODEC);

      *inratep = inrate;
      *outratep = outrate;

      if (instandard && outstandard) return AF_TRUE;
      if (incodec && outstandard && outrate != 8000.00)
      {
            _af_error(AF_WARNING_CODEC_RATE,
                  "WARNING using input rate 8 kHz instead of %.30g Hz "
                  "to allow high-quality rate conversion",
                  inrate);
            *inratep = 8000.00;
            return AF_TRUE;
      }
      if (instandard && inrate != 8000.00 && outcodec)
      {
            _af_error(AF_WARNING_CODEC_RATE,
                  "WARNING using output rate 8 kHz instead of %.30g Hz "
                  "to allow high-quality rate conversion",
                  outrate);
            *outratep = 8000.00;
            return AF_TRUE;
      }

      if (!instandard && !outstandard)
            _af_error(AF_WARNING_RATECVT,
                  "WARNING using lower quality rate conversion due to "
                  "rates %.30g and %.30g -- "
                  "output file may contain audible artifacts",
                  inrate, outrate);
      else if (!instandard)
            _af_error(AF_WARNING_RATECVT,
                  "WARNING using lower quality rate conversion due to "
                  "input rate %.30g -- "
                  "output file may contain audible artifacts",
                  inrate);
      else  /* !outstandard */
            _af_error(AF_WARNING_RATECVT,
                  "WARNING using lower quality rate conversion due to "
                  "output rate %.30g -- "
                  "output file may contain audible artifacts",
                  outrate);

      return AF_FALSE;
}

/*
      initrateconvertmods handles the extended-life rate conversion
      module and its extended-life rebuffer module called once in the
      lifetime of an AFfilehandle.
*/
static void initrateconvertmods (bool reading, _Track *track)
{
      /* no rate conversion initially */
      track->ms.rateconvertinst.valid = AF_FALSE;
      track->ms.rateconvert_rebufferinst.valid = AF_FALSE;
}

static void disposerateconvertmods (_Track *);

/* XXXmpruett rate conversion is disabled for now */
#if 0
/*
      addrateconvertmods: called once per setup of the modules
      for a given AFfilehandle
*/
static void addrateconvertmods (current_state *current, int nchannels,
      double inrate, double outrate,
      bool reading, _Track *track)
{
      AFframecount inframes, outframes;

      /* Check if we are no longer rate converting. */
      if (inrate == outrate)
      {
            disposerateconvertmods(track);
            track->ratecvt_filter_params_set = AF_FALSE; /* XXX HACK */
      }
      else
      {
            /*
                  We need new rateconverter if we didn't have one
                  or if rate has changed or rate conversion params
                  have changed.
            */
            if (!track->ms.rateconvertinst.valid ||
                  inrate != track->ms.rateconvert_inrate ||
                  outrate != track->ms.rateconvert_outrate ||
                  track->ratecvt_filter_params_set /* HACK */)
            {
                  bool usingAP = useAP(inrate, outrate, &inrate, &outrate);

                  disposerateconvertmods(track);
                  track->ratecvt_filter_params_set = AF_FALSE; /* HACK */

                  if (usingAP)
                  {
                        track->ms.rateconvertinst = InitAFRateConvert(inrate, outrate,
                        nchannels,
                        track->taper, track->dynamic_range,
                        &inframes, &outframes,
                        track, reading);

                        if (!reading)
                              track->ms.rateconvert_rebufferinst =
                                    initfloatrebufferv2f(inframes*nchannels, AF_FALSE);
                        else
                              track->ms.rateconvert_rebufferinst =
                                    initfloatrebufferf2v(outframes*nchannels, AF_FALSE);

                        track->ms.rateconvertinst.valid = AF_TRUE;
                        track->ms.rateconvert_rebufferinst.valid = AF_TRUE;
                  }
                  else
                  {
                        track->ms.rateconvertinst = initpolyratecvt(track,
                              inrate, outrate,
                              nchannels, reading);

                        track->ms.rateconvertinst.valid = AF_TRUE;
                        track->ms.rateconvert_rebufferinst.valid = AF_FALSE;
                  }

                  track->ms.rateconvert_inrate = inrate;
                  track->ms.rateconvert_outrate = outrate;

                  track->ms.rateconvertinst.free_on_close = AF_TRUE;
                  track->ms.rateconvert_rebufferinst.free_on_close = AF_TRUE;
            }

            /* Add the rate conversion modules. */

            if (!reading && track->ms.rateconvert_rebufferinst.valid)
                  addmod(current, track->ms.rateconvert_rebufferinst);

            addmod(current, track->ms.rateconvertinst);

            if (reading && track->ms.rateconvert_rebufferinst.valid)
                  addmod(current, track->ms.rateconvert_rebufferinst);
      }
}

/*
      disposerateconvertmods is called once in the lifetime of an
      AFfilehandle.
*/
static void disposerateconvertmods (_Track *track)
{
      /*
            Neither module is necessarily valid--there could have been
            an error, or the modules could possibly never have been set up.
      */
      if (track->ms.rateconvertinst.valid &&
            track->ms.rateconvertinst.mod->free)
      {
            (*track->ms.rateconvertinst.mod->free)
                  (&track->ms.rateconvertinst);
      }

      track->ms.rateconvertinst.valid = AF_FALSE;

      if (track->ms.rateconvert_rebufferinst.valid &&
            track->ms.rateconvert_rebufferinst.mod->free)
      {
            (*track->ms.rateconvert_rebufferinst.mod->free)
                  (&track->ms.rateconvert_rebufferinst);
      }

      track->ms.rateconvert_rebufferinst.valid = AF_FALSE;
}
#endif /* XXXmpruett rate conversion is disabled for now */

/* -------------------------------------------------------------- */

/* The stuff in this section is used by arrangemodules(). */

static _AFmodule *unsigned2signed[5] =
{
      NULL,
      &unsigned2signed1, &unsigned2signed2,
      &unsigned2signed3, &unsigned2signed4
};

static _AFmodule *signed2unsigned[5] =
{
      NULL,
      &signed2unsigned1, &signed2unsigned2,
      &signed2unsigned3, &signed2unsigned4
};

static _AFmodule *swapbytes[9] =
{
      NULL, NULL, &swap2, &swap3, &swap4,
      NULL, NULL, NULL, &swap8
};

/* don't forget int24_fmt is really 24 bits right-justified in 32 bits */

typedef enum format_code
{
      int8_fmt,
      int16_fmt,
      int24_fmt,
      int32_fmt,
      float_fmt,
      double_fmt
} format_code;

#define isinteger(fc) ((fc) <= int32_fmt)
#define isfloating(fc) ((fc) >= float_fmt)

/*
      get_format_code
*/
static format_code get_format_code (_AudioFormat *fmt)
{
      if (fmt->sampleFormat == AF_SAMPFMT_FLOAT)
            return float_fmt;
      if (fmt->sampleFormat == AF_SAMPFMT_DOUBLE)
            return double_fmt;

      if (fmt->sampleFormat == AF_SAMPFMT_TWOSCOMP ||
            fmt->sampleFormat == AF_SAMPFMT_UNSIGNED)
      {
            switch (_af_format_sample_size_uncompressed(fmt, AF_FALSE))
            {
                  case 1: return int8_fmt;
                  case 2: return int16_fmt;
                  case 3: return int24_fmt;
                  case 4: return int32_fmt;
            }
      }

      /* NOTREACHED */
      assert(0);
      return -1;
}

static _AFmodule *to_flt[6] =
{
      &int2float1, &int2float2, &int2float3, &int2float4,
      NULL, &double2float
};

static _AFmodule *to_dbl[6] =
{
      &int2double1, &int2double2, &int2double3, &int2double4,
      &float2double, NULL
};

static _AFmodule *clip[6] =
{
      &clip1, &clip2, &clip3, &clip4,
      &clipfloat, &clipdouble
};

static _AFmodule *channelchanges[6] =
{
      &channelchange1, &channelchange2, &channelchange3, &channelchange4,
      &channelchangefloat, &channelchangedouble
};

/* indices are of type format_code: matrix[infmtcode][outfmtcode] */
static _AFmodule *convertmatrix[6][6] =
{
      /* TO:
      {
            int8_fmt, int16_fmt,
            int24_fmt, int32_fmt,
            float_fmt, double_fmt
      }
      */

      /* FROM int8_fmt */
      {
            NULL, &int1_2,
            &int1_3, &int1_4,
            &int2float1, &int2double1
      },

      /* FROM int16_fmt */
      {
            &int2_1, NULL,
            &int2_3, &int2_4,
            &int2float2, &int2double2
      },

      /* FROM int24_fmt */
      {
            &int3_1, &int3_2,
            NULL, &int3_4,
            &int2float3, &int2double3
      },

      /* FROM int32_fmt */
      {
            &int4_1, &int4_2,
            &int4_3, NULL,
            &int2float4, &int2double4
      },

      /* FROM float_fmt */
      {
            &float2int1_clip, &float2int2_clip,
            &float2int3_clip, &float2int4_clip,
            NULL, &float2double
      },

      /* FROM double_fmt */
      {
            &double2int1_clip, &double2int2_clip,
            &double2int3_clip, &double2int4_clip,
            &double2float, NULL
      }
};

static _PCMInfo *intmappings[6] =
{
      &_af_default_signed_integer_pcm_mappings[1],
      &_af_default_signed_integer_pcm_mappings[2],
      &_af_default_signed_integer_pcm_mappings[3],
      &_af_default_signed_integer_pcm_mappings[4],
      AF_NULL, AF_NULL
};

/*
      trivial_int_clip
*/
static bool trivial_int_clip (_AudioFormat *f, format_code code)
{
      return (intmappings[code] != NULL &&
            f->pcm.minClip == intmappings[code]->minClip   &&
            f->pcm.maxClip == intmappings[code]->maxClip);
}

/*
      trivial_int_mapping
*/
static bool trivial_int_mapping (_AudioFormat *f, format_code code)
{
      return (intmappings[code] != NULL &&
            f->pcm.slope == intmappings[code]->slope &&
            f->pcm.intercept == intmappings[code]->intercept);
}

/*
      arrangemodules decides which modules to use and creates instances
      of them.
*/
static status arrangemodules (_AFfilehandle *h, _Track *track)
{
      bool reading = (h->access == _AF_READ_ACCESS);

      current_state current;

      bool rateconverting, transforming;
      bool already_clipped_output, already_transformed_output;

      int insampbytes, outsampbytes;
      int chans;

      format_code infc, outfc;

      /*
            in and out are the formats at the start and end of the
            chain of modules, respectively.
      */

      _AudioFormat in, out;

      /* in==FILE, out==virtual (user) */
      if (reading)
      {
            in = track->f;
            out = track->v;
      }
      /* in==virtual (user), out==FILE */
      else
      {
            in = track->v;
            out = track->f;
      }

      infc = get_format_code(&in);
      outfc = get_format_code(&out);

      /* flags */

      rateconverting = (in.sampleRate != out.sampleRate);

      /*
            throughout routine:

            current.modinst points to current module
            current.inchunk points to current in chunk, always outchunk-1
            current.outchunk points to current out chunk

            The addmod() function does most of the work.  It calls the
            "describe" module function, during which a module looks
            at inc->f and writes the format it will output in outc->f.
      */

      current.modinst = track->ms.module;

      current.inchunk = track->ms.chunk;
      current.outchunk = track->ms.chunk + 1;

      current.inchunk->f = in;

      /*
            max # of modules that could be needed together
            may need to change this if you change this function
      */
      #define MAX_MODULES 17

      /* Actually arrange the modules.  Call addmod() to add one. */

      /* Add file reader and possibly a decompressor. */

      if (reading)
            if (AF_FAIL == addfilereadmods(&current, track, h))
                  return AF_FAIL;

      /* Make data native-endian. */

      if (in.byteOrder != _AF_BYTEORDER_NATIVE)
      {
            int bytes_per_samp = _af_format_sample_size_uncompressed(&in, !reading);

            if (bytes_per_samp > 1 &&
                  in.compressionType == AF_COMPRESSION_NONE)
            {
                  assert(swapbytes[bytes_per_samp]);
                  addmod(&current, _AFnewmodinst(swapbytes[bytes_per_samp]));
            }
            else
                  in.byteOrder = _AF_BYTEORDER_NATIVE;
      }

      /* Handle nasty 3-byte input cases. */

      insampbytes = _af_format_sample_size_uncompressed(&in, AF_FALSE);

      if (isinteger(infc) && insampbytes == 3)
      {
            if (reading || in.compressionType != AF_COMPRESSION_NONE)
            {
                  /*
                        We're reading 3-byte ints from a file.
                        At this point stretch them to 4-byte ints
                        by sign-extending or adding a zero-valued
                        most significant byte.  We could also
                        be reading/writing 3-byte samples output
                        from a decompressor.
                  */
                  if (in.sampleFormat == AF_SAMPFMT_UNSIGNED)
                        addmod(&current, _AFnewmodinst(&real_char3_to_uchar3));
                  else
                        addmod(&current, _AFnewmodinst(&real_char3_to_schar3));
            }
            else /* writing, non-compressed */
            {
                  /*
                        We're processing 3-byte ints from the
                        user, which come in as sign-extended
                        4-byte quantities.  How convenient:
                        this is what we want.
                  */
            }
      }

      /* Make data signed. */

      if (in.sampleFormat == AF_SAMPFMT_UNSIGNED)
      {
            addmod(&current, _AFnewmodinst(unsigned2signed[insampbytes]));
      }

      /* Standardize pcm mapping of "in" and "out". */

      /*
            Since they are used to compute transformations in the
            inner section of this routine (inside of sign conversion),
            we need in.pcm and out.pcm in terms of AF_SAMPFMT_TWOSCOMP
            numbers.
      */
      in.pcm = current.inchunk->f.pcm; /* "in" is easy */

      if (out.sampleFormat == AF_SAMPFMT_UNSIGNED) /* "out": undo the unsigned shift */
      {
            double shift = intmappings[outfc]->minClip;
            out.pcm.intercept += shift;
            out.pcm.minClip += shift;
            out.pcm.maxClip += shift;
      }

      /* ------ CLIP user's input samples if necessary */

      if (in.pcm.minClip < in.pcm.maxClip && !trivial_int_clip(&in, infc))
            addmod(&current, initpcmmod(clip[infc], AF_NULL, &in.pcm));

      /*
            At this point, we assume we can have doubles, floats,
            and 1-, 2-, and 4-byte signed integers on the input and
            on the output (or 4-byte integers with 24 significant
            (low) bits, int24_fmt).

            Now we handle rate conversion and pcm transformation.
      */

      /* If rate conversion will happen, we must have floats. */
      /*
            This may result in loss of precision.  This bug must be
            fixed eventually.
      */
      if (rateconverting && infc != float_fmt)
      {
            addmod(&current, _AFnewmodinst(to_flt[infc]));
            infc = float_fmt;
      }

      /*
            We must make sure the output samples will get clipped
            to SOMETHING reasonable if we are rateconverting.
            The user cannot possibly expect to need to clip values
            just because rate conversion is on.
      */

      if (out.pcm.minClip >= out.pcm.maxClip && rateconverting)
      {
            out.pcm.minClip = out.pcm.intercept - out.pcm.slope;
            out.pcm.maxClip = out.pcm.intercept + out.pcm.slope;
      }

      already_clipped_output = AF_FALSE;
      already_transformed_output = AF_FALSE;

      /*
            We need to perform a transformation (in floating point)
            if the input and output PCM mappings are different.

            The only exceptions are the trivial integer conversions
            (i.e., full-range integers of one # of bytes to full-range
            integers to another # of bytes).
      */

      transforming = (in.pcm.slope != out.pcm.slope ||
            in.pcm.intercept != out.pcm.intercept) &&
            !(trivial_int_mapping(&in, infc) &&
            trivial_int_mapping(&out,outfc));

      /*
            If we have ints on input and the user is performing a
            change of mapping other than a trivial one, we must go
            to floats or doubles.
      */

      if (isinteger(infc) && transforming)
      {
            /*
                  Use doubles if either the in or out format has
                  that kind of precision.
            */
            if (infc == int32_fmt ||
                  outfc == double_fmt || outfc == int32_fmt)
            {
                  addmod(&current, _AFnewmodinst(to_dbl[infc]));
                  infc = double_fmt;
            }
            else
            {
                  addmod(&current, _AFnewmodinst(to_flt[infc]));
                  infc = float_fmt;
            }
      }

      DEBG(printf("arrangemodules  in="); _af_print_audioformat(&in););
      DEBG(printf("arrangemodules out="); _af_print_audioformat(&out););
      DEBG(printf("arrangemodules transforming=%d\n", transforming));
      DEBG(printf("arrangemodules infc=%d outfc=%d\n", infc, outfc));

      /*
            invariant:

            At this point, if infc is an integer format, then we are
            not rate converting, nor are we perfoming any change of
            mapping other than possibly a trivial int->int conversion.
      */

      /* ----- convert format infc to format outfc */

      /* change channels if appropriate now */

      if (in.channelCount != out.channelCount &&
            (infc > outfc || (infc==outfc && out.channelCount < in.channelCount)))
      {
            addmod(&current,
                  initchannelchange(channelchanges[infc],
                  track->channelMatrix, &in.pcm,
                  in.channelCount, out.channelCount,
                  reading));
            chans = out.channelCount;
      }
      else
            chans = in.channelCount;

      /* Transform floats if appropriate now. */

      if (transforming &&
            infc==double_fmt && isfloating(outfc))
      {
            addmod(&current, initpcmmod(&doubletransform, &in.pcm, &out.pcm));
      }

#if 0 /* XXXmpruett */
      /*
            Handle rate conversion (will do the right thing if
            not rate converting).
      */

      addrateconvertmods(&current, chans, in.sampleRate, out.sampleRate, reading, track);
#endif

      /* Add format conversion, if needed */

      if (convertmatrix[infc][outfc])
      {
            /*
                  for float/double -> int conversions, the module
                  we use here also does the transformation and
                  clipping.

                  We use initpcmmod() in any case because it is harmless
                  for the other modules in convertmatrix[][].
            */
            if (isfloating(infc) && isinteger(outfc)) /* "float"->"int" */
            {
                  already_clipped_output = AF_TRUE;
                  already_transformed_output = AF_TRUE;
            }
            addmod(&current, initpcmmod(convertmatrix[infc][outfc],
                  &in.pcm, &out.pcm));
      }

      /* Transform floats if appropriate now. */

      if (transforming && !already_transformed_output && infc != double_fmt)
      {
            if (outfc==double_fmt)
                  addmod(&current, initpcmmod(&doubletransform,
                        &in.pcm, &out.pcm));
            else if (outfc==float_fmt)
                  addmod(&current, initpcmmod(&floattransform,
                        &in.pcm, &out.pcm));
      }

      /* Change channels if appropriate now. */

      if (in.channelCount != out.channelCount &&
            (outfc > infc || (infc==outfc && in.channelCount < out.channelCount)))
      {
            addmod(&current,
                  initchannelchange(channelchanges[outfc],
                        track->channelMatrix, &out.pcm,
                        in.channelCount, out.channelCount,
                        reading));
      }

      /* ------ CLIP user's output samples if needed */

      if (!already_clipped_output)
      {
            if (out.pcm.minClip < out.pcm.maxClip &&
                  !trivial_int_clip(&out, outfc))
            {
                  addmod(&current, initpcmmod(clip[outfc], NULL, &out.pcm));
            }
      }

      /* Make data unsigned if neccessary. */

      outsampbytes = _af_format_sample_size_uncompressed(&out, AF_FALSE);

      if (out.sampleFormat == AF_SAMPFMT_UNSIGNED)
            addmod(&current, _AFnewmodinst(signed2unsigned[outsampbytes]));

      /* Handle nasty 3-byte output cases. */

      if (isinteger(outfc) && outsampbytes == 3)
      {
            if (!reading || out.compressionType != AF_COMPRESSION_NONE)
            {
                  /*
                        We're writing 3-byte ints into a file.
                        We have 4-byte ints.  Squish them to
                        3 by truncating the high byte off.
                        we could also be reading/writing ints
                        into a compressor.  note this works for
                        signed and unsigned, and has to.
                  */
                  addmod(&current, _AFnewmodinst(&char3_to_real_char3));
            }
            else /* reading, not compressed */
            {
                  /*
                        We're reading 3-byte ints into the
                        user's buffer.

                        The user expects
                        1. 4-byte sign-extended ints (3 bytes
                        sign extended in 4 bytes) or
                        2. 4-byte unsigned ints (3 bytes in 4 bytes).

                        How convenient: this is just what we have.
                  */
            }
      }

      if (out.byteOrder != _AF_BYTEORDER_NATIVE)
      {
            int bytes_per_samp = _af_format_sample_size_uncompressed(&out, reading);

            if (bytes_per_samp > 1 && out.compressionType == AF_COMPRESSION_NONE)
            {
                  assert(swapbytes[bytes_per_samp]);
                  addmod(&current, _AFnewmodinst(swapbytes[bytes_per_samp]));
            }
      }

      /* Add file writer, possibly a compressor. */

      if (!reading)
            if (AF_FAIL == addfilewritemods(&current, track, h))
                  return(AF_FAIL);

      /* Now all modules are arranged! */

      track->ms.nmodules = current.modinst - track->ms.module;

#ifdef UNLIMITED_CHUNK_NVFRAMES
      /*
            OPTIMIZATION: normally, when we set up the modules, AFreadframes
            and AFwriteframes must pull and push chunks of size at most
            _AF_ATOMIC_NVFRAMES.

            For the simplest configurations of modules (1 module, no
            compression), no buffering at all needs to be done by the
            module system.    In these cases, afReadFrames/afWriteFrames
            can pull/push as many virtual frames as they want
            in one call.  This flag tells tells afReadFrames and
            afWriteFrames whether they can do so.

            Note that if this flag is set, file modules cannot rely
            on the intermediate working buffer which _AFsetupmodules
            usually allocates for them in their input or output chunk
            (for reading or writing, respectively).  This is why if
            we are reading/writing compressed data, this optimization
            is turned off.

            There are warnings to this effect in the pcm
            (uncompressed) file read/write module.    If you want to
            apply this optimization to other types, be sure to put
            similar warnings in the code.
      */
      if (track->ms.nmodules == 1 &&
            track->v.compressionType == AF_COMPRESSION_NONE &&
            track->f.compressionType == AF_COMPRESSION_NONE)
            track->ms.mustuseatomicnvframes = AF_FALSE;
      else
            track->ms.mustuseatomicnvframes = AF_TRUE;
#else
      track->ms.mustuseatomicnvframes = AF_TRUE;
#endif

      return AF_SUCCEED;
}

/*
      disposemodules will free old buffers and free old modules, except
      those marked with free_on_close.

      The modules existing before we dispose them could be:

      1. none (we may have only called _AFinitmodules and not _AFsetupmodules)
      2. some invalid PARTIALLY ALLOCATED ones (e.g. the last _AFsetupmodules
      had an error) or
      3. a perfectly valid set of modules.

      disposemodules will deal with all three cases.
*/
static void disposemodules (_Track *track)
{
      if (track->ms.module)
      {
            int i;

            for (i=0; i < MAX_MODULES; i++)
            {
                  _AFmoduleinst *mod = &track->ms.module[i];

#ifdef AF_DEBUG
                  if (!mod->valid && i < track->ms.nmodules)
                        printf("disposemodules: WARNING in-range invalid module found '%s'\n", mod->mod->name);
#endif

                  if (mod->valid && !mod->free_on_close && mod->mod->free)
                  {
                        (*mod->mod->free)(mod);
                        mod->valid = AF_FALSE;
                  }
            }

            free(track->ms.module);
            track->ms.module = AF_NULL;
      }
      track->ms.nmodules = 0;

      if (track->ms.chunk)
      {
            free(track->ms.chunk);
            track->ms.chunk = AF_NULL;
      }

      if (track->ms.buffer)
      {
            int i;
            for (i=0; i < (MAX_MODULES+1); i++)
            {
                  if (track->ms.buffer[i] != AF_NULL)
                  {
                        free(track->ms.buffer[i]);
                        track->ms.buffer[i] = AF_NULL;
                  }
            }
            free(track->ms.buffer);
            track->ms.buffer = AF_NULL;
      }
}

/*
      resetmodules: see advanced section in README.modules for more info
*/
static status resetmodules (_AFfilehandle *h, _Track *track)
{
      int i;

      /*
            We should already have called _AFsetupmodules.
            (Actually this is called from the end of _AFsetupmodules
            but whatever).
      */

      assert(!track->ms.modulesdirty);

      /* Assume all is well with track. */
      track->filemodhappy = AF_TRUE;

      CHNK(printf("resetmodules running reset1 routines\n"));

      /* Reset all modules. */
      for (i=track->ms.nmodules-1; i >= 0; i--)
      {
            /* reset1 */
            if (track->ms.module[i].mod->reset1 != AF_NULL)
                  (*track->ms.module[i].mod->reset1)(&track->ms.module[i]);
      }

      /* Clear out frames2ignore here; the modules will increment it. */
      track->frames2ignore = 0;

      if (!track->filemodhappy)
            return AF_FAIL;

      CHNK(printf("resetmodules running reset2 routines\n"));

      for (i=0; i < track->ms.nmodules; i++)
      {
            /* reset2 */
            if (track->ms.module[i].mod->reset2 != AF_NULL)
                  (*track->ms.module[i].mod->reset2)(&track->ms.module[i]);
      }

      CHNK(printf("resetmodules completed\n"));

      if (!track->filemodhappy)
            return AF_FAIL;

#ifdef ONE_TRACK_ONLY
      /*
            For an explanation of this, see the comment in
            initfilemods which explains how and when the file is
            lseek'ed.
      */
      if (h->seekok)
            if (lseek(h->fd, track->fpos_next_frame, SEEK_SET) < 0)
            {
                  _af_error(AF_BAD_LSEEK,
                        "unable to position read pointer at next data frame");
                  return AF_FAIL;
            }
#endif

      return AF_SUCCEED;
}

/*
      _AFsyncmodules
*/
status _AFsyncmodules (AFfilehandle h, _Track *track)
{
      int i;

      /* We should already have called _AFsetupmodules. */
      assert(!track->ms.modulesdirty);

      /* Assume all is well with track. */
      track->filemodhappy = AF_TRUE;

      CHNK(printf("_AFsyncmodules running sync1 routines\n"));

      /* Sync all modules. */
      for(i=track->ms.nmodules-1; i >= 0; i-- )
      {
            /* sync1 */
            if (AF_NULL != track->ms.module[i].mod->sync1)
                  (*track->ms.module[i].mod->sync1)(&track->ms.module[i]);
      }

      if (!track->filemodhappy)
            return AF_FAIL;

      CHNK(printf("_AFsyncmodules running sync2 routines\n"));

      for (i=0; i < track->ms.nmodules; i++)
      {
            /* sync2 */
            if (AF_NULL != track->ms.module[i].mod->sync2)
                  (*track->ms.module[i].mod->sync2)(&track->ms.module[i]);
      }

      CHNK(printf("_AFsyncmodules completed\n"));

      if (!track->filemodhappy)
            return AF_FAIL;

#ifdef ONE_TRACK_ONLY
      /*
            For an explanation of this, see the comment in
            initfilemods which explains how and when the file is
            lseek'ed.
      */
      if (h->seekok)
            if (lseek( h->fd, track->fpos_next_frame, SEEK_SET) < 0 )
            {
                  _af_error(AF_BAD_LSEEK,
                  "unable to position write ptr at next data frame");
                  return(AF_FAIL);
            }
#endif

      return AF_SUCCEED;
}

/*
      _AFsetupmodules:
      - frees any old modules, chunks, and buffers
      - looks at the input and output format and sets up a whole new
      set of input and output modules (using arrangemodules())
      - assigns those modules chunks
      - allocates buffers and assigns the buffers to the chunks
      - initializes various track fields pertaining to the module system

      It returns AF_FAIL on any kind of error.

      It sets modulesdirty to AF_FALSE if it was able to clean the
      modules (although an error still could have occurred after
      cleaning them).
*/
status _AFsetupmodules (AFfilehandle h, _Track *track)
{
      _AFmoduleinst *modules;
      _AFchunk *chunks;
      void **buffers;
      int maxbufsize, bufsize, i;
      double rateratiof2v, fframepos;

      /*
            The purpose of this function is to "clean" the modules:

            * All of the fields in trk->ms are completely set
            and valid.

            * track->totalvframes and track->next[fv]frame are set
            and valid and trk->modulesdirty will be set to AF_FALSE
            if this function succeeds.

            This function also resets the modules on files open for read.
            it will return AF_FAIL if either cleaning the modules fails,
            or this reset fails.

            The comments will tell you which part does what.
      */

      /*
            NOTE: we cannot trust any value in track->ms until we
            have called disposemodules(), at which time things are
            cleared to reasonable "zero" values.

            It is possible for track->ms to be in an illegal state
            at this point, if the last _AFsetupmodules failed with
            an error.

            We can trust track->totalvframes and track->next[fv]frame
            because they are only modified after successfully building
            the modules.
      */

      /*
            Disallow compression in virtual format for now.
      */
      if (track->v.compressionType != AF_COMPRESSION_NONE)
      {
            _af_error(AF_BAD_NOT_IMPLEMENTED,
                  "library does not support compression in virtual format yet");
            return AF_FAIL;
      }

      /*
            Check that virtual compression parameters are ok.
      */
      {
            int idx = _af_compression_index_from_id(track->v.compressionType);
            if ((*_af_compression[idx].fmtok)(&track->v) == AF_FALSE)
            {
                  return AF_FAIL;
            }
      }

      /*
            track->nextvframe and track->nextfframe:

            At this point, only track->nextvframe contains useful
            information, since track->nextfframe may be swayed by
            currently buffered frames.

            Also track->nextvframe is currently in the scale of the
            old sampling rate, not the new one we are setting up.

            So at this point we remember where we are in the file
            (in floating point) in terms of the file sampling rate.

            We will use this later in this function to set both
            track->nextfframe (for reading) and track->nextvframe
            (for reading and writing).

            We must be careful to use the old rates, not the ones
            in track->{f,v}.
      */

      /* If modules have been set up at all */
      if (track->ms.old_v_rate > 0)
      {
            assert(track->ms.old_f_rate > 0);
            rateratiof2v = track->ms.old_f_rate / track->ms.old_v_rate;
            fframepos = track->nextvframe * rateratiof2v;
      }
      else
            /* We start at frame zero. */
            fframepos = 0;

      /*
            Dispose the existing modules (except extended-life ones).

            See the function for info on what the module state could
            be at this time.
      */

      disposemodules(track);

      /*
            Here we allocate the highest number of module instances
            (and chunks) chained together we could possibly need.

            This is how the chunks are used:

            module[n]'s input chunk is chunk[n]
            module[n]'s output chunk is chunk[n+1]

            chunk[n]'s buffer, if it is not the user's buffer, is buffer[n].

            For reading chunk[0] is not usually used.
            For writing chunk[nmodules] is not usually used.

            We allocate a buffer for chunk[0] on reading and
            chunk[nmodules] when writing because the file reading
            or file writing module, if it does compression or
            decompression, may need extra space in which to place
            the result of its processing before reading or writing it.

            Also note that chunk[0].f and chunk[nmodules].f are used in
            arrangemodules().
      */
      modules = _af_malloc(sizeof (_AFmoduleinst) * MAX_MODULES);
      if (modules == AF_NULL)
            return AF_FAIL;
      for (i=0; i < MAX_MODULES; i++)
            modules[i].valid = AF_FALSE;

      chunks = _af_malloc(sizeof (_AFchunk) * (MAX_MODULES+1));
      if (chunks == AF_NULL)
            return AF_FAIL;

      buffers = _af_malloc(sizeof (void *) * (MAX_MODULES+1));
      if (buffers == AF_NULL)
            return AF_FAIL;
      /*
            It is very important to initialize each buffers[i] to NULL;
            dispose frees them all if !NULL.
      */

      for (i=0; i < (MAX_MODULES+1); i++)
            buffers[i] = AF_NULL;

      track->ms.module = modules;
      /*
            nmodules is a bogus value here, set just for sanity
            (in case of broken code).
      */

      track->ms.nmodules = 0;
      track->ms.chunk = chunks;
      track->ms.buffer = buffers;

      /*
            Figure out the best modules to use to convert the
            data and initialize instances of those modules.
            Fills "track->ms.module" and most of "track->ms.chunk"
            arrays (all but the buffers) as it goes.  Sets
            "track->ms.nmodules" As a side benefit, this function
            also leaves information about the data format at each
            stage in the "f" field of each chunk.
      */
      if (arrangemodules(h, track) == AF_FAIL)
      {
            /*
                  At this point the modules are in an incompletely
                  initialized and probably illegal state.  nmodules
                  could be meaningful or not.  Things are nasty.

                  But as long as any API call that uses the
                  modules calls _AFsetupmodules() first (which
                  then calls disposemodules(), which can handle
                  this nastiness), we can restore the modules to
                  a sane initial state and things will be ok.
            */

            return AF_FAIL;
      }

      /*
            At this point modules and nmodules are almost completely
            filled in (modules aren't actually connected to one
            another), but buffer[n] and chunk[n].buf are still in
            a null state.

            track->totalvframes and track->next[fv]frame have not yet been
            set to a valid state.
      */

      /*
            Now go through the modules:

            1. Connect up the source/sink fields properly.
            2. Use the information left in the _AudioFormat field
            of each chunk by setupmodules() along with the
            "max_pull"/"max_push" module function to figure
            out the biggest buffer size that could be needed.
      */

      /* filemod reports error here */
      track->filemodhappy = AF_TRUE;
      maxbufsize = 0;

      if (h->access == _AF_READ_ACCESS)
      {
            track->ms.chunk[track->ms.nmodules].nframes = _AF_ATOMIC_NVFRAMES;

            for (i=track->ms.nmodules-1; i >= 0; i--)
            {
                  _AFchunk *inc = &track->ms.chunk[i];
                  _AFchunk *outc = &track->ms.chunk[i+1];

                  /* check bufsize needed for current output chunk */

                  bufsize = outc->nframes * _af_format_frame_size(&outc->f, AF_TRUE);
                  if (bufsize > maxbufsize)
                  maxbufsize = bufsize;

                  if (i != 0)
                  {
                        /* Connect source pointer for this module. */

                        track->ms.module[i].u.pull.source = &track->ms.module[i-1];
                  }

                  /*
                        Determine inc->nframes from outc->nframes.
                        If the max_pull function is present, we use it,
                        otherwise we assume module does no weird
                        buffering or rate conversion.
                  */
                  if (track->ms.module[i].mod->max_pull)
                        (*track->ms.module[i].mod->max_pull)(&track->ms.module[i]);
                  else
                        inc->nframes = outc->nframes;
            }

            if (!track->filemodhappy)
                  return AF_FAIL;

            /*
                  Check bufsize needed for filemod's input chunk
                  (intermediate buffer) based on an uncompressed
                  (output chunk) framesize.
            */

            {
                  _AFmoduleinst *filemod = &track->ms.module[0];
                  bufsize = filemod->inc->nframes *
                        _af_format_frame_size(&filemod->outc->f, AF_TRUE);
                  if (bufsize > maxbufsize)
                        maxbufsize = bufsize;
            }
      }
      else
      {
            track->ms.chunk[0].nframes = _AF_ATOMIC_NVFRAMES;

            for (i=0; i < track->ms.nmodules; i++)
            {
                  _AFchunk *inc = &track->ms.chunk[i];
                  _AFchunk *outc = &track->ms.chunk[i+1];

                  /* Check bufsize needed for current input chunk. */

                  bufsize = inc->nframes * _af_format_frame_size(&inc->f, AF_TRUE);
                  if (bufsize > maxbufsize)
                        maxbufsize = bufsize;

                  if (i != track->ms.nmodules-1)
                  {
                        /* Connect sink pointer. */

                        track->ms.module[i].u.push.sink = &track->ms.module[i+1];
                  }

                  /*
                        Determine outc->nframes from inc->nframes.
                        If the max_push function is present, we use it,
                        otherwise we assume module does no weird
                        buffering or rate conversion.
                  */
                  if (track->ms.module[i].mod->max_push)
                        (*track->ms.module[i].mod->max_push)(&track->ms.module[i]);
                  else
                        outc->nframes = inc->nframes;
            }

            if (!track->filemodhappy)
                  return AF_FAIL;

            /*
                  Check bufsize needed for filemod's output chunk
                  (intermediate buffer) based on an uncompressed (input
                  chunk) framesize.
            */

            {
                  _AFmoduleinst *filemod = &track->ms.module[track->ms.nmodules-1];
                  bufsize = filemod->outc->nframes *
                  _af_format_frame_size(&filemod->inc->f, AF_TRUE);
                  if (bufsize > maxbufsize)
                        maxbufsize = bufsize;
            }
      }

      /*
            At this point everything is totally set up with the
            modules except that the chunk buffers have not been
            allocated, and thus buffer[n] and chunk[n].buf have
            not been set.  But now we know how big they should be
            (maxbufsize).

            track->totalvframes and track->next[fv]frame have not
            yet been set to a valid state.
      */
      DEBG(printf("_AFsetupmodules: maxbufsize=%d\n", maxbufsize));

      /*
            One of these will get overwritten to point to user's
            buffer.  The other one will be allocated below (for file
            read/write module).
      */

      track->ms.chunk[track->ms.nmodules].buf = AF_NULL;
      track->ms.chunk[0].buf = AF_NULL;

      /*
            One of these will be allocated for the file read/write
            module The other will be completely unused.
      */
      track->ms.buffer[track->ms.nmodules] = AF_NULL;
      track->ms.buffer[0] = AF_NULL;

      /*
            Now that we know how big buffers have to be, allocate
            buffers and assign them to the module instances.

            Note that track->ms.chunk[nmodules].buf (reading) or
            track->ms.chunk[0].buf (writing) will get overwritten
            in _AFreadframes or _AFwriteframes to point to the
            user's buffer.

            We allocate a buffer for track->ms.chunk[0].buf (reading)
            or track->ms.chunk[nmodules].buf (writing) not because
            it is needed for the modules to work, but as a working
            buffer for the file reading / file writing modules.

            Also note that some modules may change their inc->buf or
            outc->buf to point to something internal to the module
            before calling their source or sink.

            So module code must be careful not to assume that a buffer
            address will not change.  Only for chunk[nmodules]
            (reading) or chunk[0] (writing) is such trickery
            disallowed.
      */

      if (h->access == _AF_READ_ACCESS)
            for (i=track->ms.nmodules-1; i >= 0; i--)
            {
                  if ((track->ms.buffer[i] = _af_malloc(maxbufsize)) == AF_NULL)
                        return AF_FAIL;
                  track->ms.chunk[i].buf = track->ms.buffer[i];
            }
      else
            for (i=1; i <= track->ms.nmodules; i++)
            {
                  if ((track->ms.buffer[i] = _af_malloc(maxbufsize)) == AF_NULL)
                        return AF_FAIL;
                  track->ms.chunk[i].buf = track->ms.buffer[i];
            }

      /*
            Hooray!  The modules are now in a completely valid state.
            But we can't set track->ms.modulesdirty to AF_FALSE yet...

            track->totalvframes and track->next[fv]frame have not yet been
            set to a valid state.
      */
      if (h->access == _AF_READ_ACCESS)
      {
            /*
                  Set total number of virtual frames based on new rate.
            */
            if (track->totalfframes == -1)
                  track->totalvframes = -1;
            else
                  track->totalvframes = track->totalfframes *
                        (track->v.sampleRate / track->f.sampleRate);

            /*
                  track->nextvframe and track->nextfframe:

                  Currently our only indication of where we were
                  in the file is the variable fframepos, which
                  contains (in floating point) our offset in file
                  frames based on the old track->nextvframe.

                  Now we get as close as we can to that original
                  position, given the new sampling rate.
            */

            track->nextfframe = (AFframecount) fframepos;
            track->nextvframe = (AFframecount) (fframepos * (track->v.sampleRate / track->f.sampleRate));

            /*
                  Now we can say the module system is in a
                  clean state.  Any errors we get from here on
                  are reported but not critical.
            */

            track->ms.modulesdirty = AF_FALSE;

            /* Set up for next time. */
            track->ms.old_f_rate = track->f.sampleRate;
            track->ms.old_v_rate = track->v.sampleRate;

            /*
                  Now we reset all the modules.

                  If we are here because the user did afSeekFrame,
                  the actual seek will be performed here.
                  Otherwise this reset will set things up so that
                  we are at the same file offset we were at before
                  (or as close as possible given a change in
                  rate conversion).
            */

            /* Report error, but we're still clean. */
            if (AF_SUCCEED != resetmodules(h, track))
                  return AF_FAIL;
      }
      /* Handle the case of _AF_WRITE_ACCESS. */
      else
      {
            /*
                  Don't mess with track->nextfframe or
                  track->totalfframes.  Scale virtual frame position
                  relative to old virtual position.
            */

            track->nextvframe = track->totalvframes =
                  (AFframecount) (fframepos * (track->v.sampleRate / track->f.sampleRate));

            /*
                  Now we can say the module system is in a
                  clean state.  Any errors we get from here on
                  are reported but not critical.
            */

            track->ms.modulesdirty = AF_FALSE;

            /* Set up for next time. */
            track->ms.old_f_rate = track->f.sampleRate;
            track->ms.old_v_rate = track->v.sampleRate;
      }

      DEBG(_af_print_filehandle(h));

#ifdef DEBUG
      for (i=track->ms.nmodules-1; i >= 0; i--)
      {
            _AFmoduleinst *inst = &track->ms.module[i];
      }

      {
            /* Print format summary. */

            printf("%s ->\n", (h->access == _AF_READ_ACCESS) ? "file" : "user");
            for (i=0; i < track->ms.nmodules; i++)
            {
                  _AFmoduleinst *inst = &track->ms.module[i];
                  _af_print_audioformat(&inst->inc->f);
                  printf(" -> %s(%d) ->\n", inst->mod->name, i);
            }
            _af_print_audioformat(&track->ms.chunk[track->ms.nmodules].f);
            printf(" -> %s\n", (h->access != _AF_READ_ACCESS) ? "file" : "user");
      }
#endif

      /*
            If we get here, then not only are the modules clean, but
            whatever we did after the modules became clean succeeded.
            So we gloat about our success.
      */
      return AF_SUCCEED;
}

/*
      _AFinitmodules: this routine sets the initial value of the module-
      related fields of the track when the track is first created.

      It also initializes the file read or file write modules.
      See README.modules for info on this.

      Set "modulesdirty" flag on each track, so that the first
      read/write/seek will set up the modules.
*/
status _AFinitmodules (AFfilehandle h, _Track *track)
{
      track->channelMatrix = NULL;

      /* HACK: see private.h for a description of this hack */
      track->taper = 10;
      track->dynamic_range = 100;
      track->ratecvt_filter_params_set = AF_TRUE;

      track->ms.nmodules = 0;
      track->ms.module = NULL;
      track->ms.chunk = NULL;
      track->ms.buffer = NULL;

      track->ms.modulesdirty = AF_TRUE;

      track->ms.filemodinst.valid = AF_FALSE;
      track->ms.filemod_rebufferinst.valid = AF_FALSE;

      track->ms.rateconvertinst.valid = AF_FALSE;
      track->ms.rateconvert_rebufferinst.valid = AF_FALSE;

      /* bogus value in case of bad code */
      track->ms.mustuseatomicnvframes = AF_TRUE;

      /* old_f_rate and old_v_rate MUST be set to <= 0 here. */
      track->ms.old_f_rate = -1;
      track->ms.old_v_rate = -1;

      /*
            Initialize extended-life file read or file write modules.
      */
      if (AF_FAIL == initfilemods(track, h))
            return AF_FAIL;

      /*
            Initialize extended-life rate convert modules (to NULL).
      */
      initrateconvertmods(h->access == _AF_READ_ACCESS, track);

      /*
            NOTE: Only now that we have initialized filemods is
            track->totalfframes guaranteed to be ready.  (The unit
            cannot always tell how many frames are in the file.)
      */

      /* totalfframes could be -1. */
      track->totalvframes = track->totalfframes;
      track->nextvframe = 0;
      track->frames2ignore = 0;

      return AF_SUCCEED;
}

/*
      _AFfreemodules:
      called once when filehandle is being freed
      opposite of initmodules
      free all modules, even the active ones
*/
void _AFfreemodules (_Track *track)
{
      disposemodules(track);
      disposefilemods(track);
#if 0 /* XXXmpruett rate conversion is deactivated for now */
      disposerateconvertmods(track);
#endif
}

Generated by  Doxygen 1.6.0   Back to index