#ifndef cmProc2_h
#define cmProc2_h

#ifdef __cplusplus
extern "C" {
#endif

  //------------------------------------------------------------------------------------------------------------
  // cmArray is an expandable array designed to work easily with the alloc/init/final/free model
  // used by this library.  The arrays can be safely used by using the cmArrayAllocXXX macros 
  // with static cmArray member fields during object allocation. cmArrayResizeXXX macros are then
  // used during the object initialization phase to allocate the actual array data space.  Notice that
  // when used this way there is no need to call cmArrayFinal() prior to cmArrayResizeXXX(). 

  // The data memory used by cmArray's is allocated through the cmAllocData() and cmAllocDataZ()
  // macros.  The resulting base memory address is therefore guaranteed to be aligned to a
  // 16 byte address boundary.
  typedef struct
  {
    cmObj    obj;
    char*    ptr;
    unsigned allocByteCnt;
    unsigned eleCnt;
    unsigned eleByteCnt;
  } cmArray;

  enum
  {
    kZeroArrayFl = 0x01
  };

  cmArray* cmArrayAllocate(       cmCtx* c, cmArray* p, unsigned eleCnt, unsigned eleByteCnt, unsigned flags );
  cmRC_t   cmArrayFree(           cmArray** pp );
  cmRC_t   cmArrayInit(           cmArray* p, unsigned eleCnt, unsigned eleByteCnt, unsigned flags );
  cmRC_t   cmArrayFinal(          cmArray* p );
  char*    cmArrayReallocDestroy( cmArray* p, unsigned newEleCnt, unsigned newEleByteCnt, unsigned flags );
  void     cmArrayReallocDestroyV(cmArray* p, int      eleByteCnt,unsigned flags,  ... );
  char*    cmArrayReallocPreserve(cmArray* p, unsigned newEleCnt, unsigned newEleByteCnt, unsigned flags );


#define cmArrayAlloc(      c, p )               cmArrayAllocate(c,p,0,0,0);
#define cmArrayAllocInit(  c, p, eleCnt, type ) cmArrayAllocate(c,p,eleCnt,sizeof(type),0)
#define cmArrayAllocInitZ( c, p, eleCnt, type ) cmArrayAllocate(c,p,eleCnt,sizeof(type),kZeroArrayFl)

#define cmArrayResize(         c, p, newEleCnt, type ) (type*)cmArrayReallocDestroy(c,p,newEleCnt,sizeof(type), 0 )
#define cmArrayResizeZ(        c, p, newEleCnt, type ) (type*)cmArrayReallocDestroy(c,p,newEleCnt,sizeof(type), kZeroArrayFl )
#define cmArrayResizePreserve( c, p, newEleCnt, type ) (type*)cmArrayReallocPreserve(c,p,newEleCnt,sizeof(type), 0 )
#define cmArrayResizePreserveZ(c, p, newEleCnt, type ) (type*)cmArrayReallocPreserve(c,p,newEleCnt,sizeof(type), kZeroArrayFl )
#define cmArrayResizeV(        c, p, type, ... )              cmArrayReallocDestroyV(c,p,sizeof(type),0,##__VA_ARGS__)
#define cmArrayResizeVZ(       c, p, type, ... )              cmArrayReallocDestroyV(c,p,sizeof(type),kZeroArrayFl,##__VA_ARGS__)
  

#define cmArrayPtr(     type, p )            (type*)(p)->ptr
#define cmArrayCount(   p )                  (p)->eleCnt


  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj         obj;
    cmAudioFileH_t  h;
    unsigned      chCnt;
    unsigned      curChCnt;
    unsigned      procSmpCnt;
    char*         fn;
    cmSample_t*   bufV;
  } cmAudioFileWr;

  cmAudioFileWr*     cmAudioFileWrAlloc( cmCtx* c, cmAudioFileWr* p, unsigned procSmpCnt, const char* fn, double srate, unsigned chCnt, unsigned bitsPerSample );
  cmRC_t             cmAudioFileWrFree(  cmAudioFileWr** pp );
  cmRC_t             cmAudioFileWrInit(  cmAudioFileWr* p, unsigned procSmpCnt, const char* fn, double srate, unsigned chCnt, unsigned bitsPerSample );
  cmRC_t             cmAudioFileWrFinal( cmAudioFileWr* p );
  cmRC_t             cmAudioFileWrExec(  cmAudioFileWr* p, unsigned chIdx, const cmSample_t* sp, unsigned sn );
  void               cmAudioFileWrTest();

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj      obj;
    unsigned    rn;
    unsigned    cn;
    cmSample_t *bufPtr;

  } cmMatrixBuf;

  /// Set p to NULL to dynamically allocate the object
  cmMatrixBuf*       cmMatrixBufAllocFile(cmCtx* c, cmMatrixBuf* p, const char* fn );
  cmMatrixBuf*       cmMatrixBufAllocCopy(cmCtx* c, cmMatrixBuf* p, unsigned rn, unsigned cn, const cmSample_t* sp );
  cmMatrixBuf*       cmMatrixBufAlloc(    cmCtx* c, cmMatrixBuf* p, unsigned rn, unsigned cn );
  cmRC_t             cmMatrixBufFree(     cmMatrixBuf**p );
  cmRC_t             cmMatrixBufInitFile( cmMatrixBuf* p, const char* fn );
  cmRC_t             cmMatrixBufInitCopy( cmMatrixBuf* p, unsigned rn, unsigned cn, const cmSample_t* sp );
  cmRC_t             cmMatrixBufInit(     cmMatrixBuf* p, unsigned rn, unsigned cn );
  cmRC_t             cmMatrixBufFinal(    cmMatrixBuf* p );
  cmSample_t*        cmMatrixBufColPtr(   cmMatrixBuf* p, unsigned ci );
  cmSample_t*        cmMatrixBufRowPtr(   cmMatrixBuf* p, unsigned ri );
  void               cmMatrixBufTest();


  //------------------------------------------------------------------------------------------------------------

  enum
  {
    kInvalidWfId,
    kSineWfId,
    kCosWfId,
    kSquareWfId,
    kTriangleWfId,
    kSawtoothWfId,
    kWhiteWfId,
    kPinkWfId,
    kPulseWfId,
    kImpulseWfId,
    kSilenceWfId,
    kPhasorWfId,
    kSeqWfId,            // always incrementing integer sequence (srate,frq,otCnt is ignored)
  };

  typedef struct
  {
    cmObj              obj;
    unsigned           wfId;
    unsigned           overToneCnt;
    double             fundFrqHz;
    cmSample_t*        outV;
    unsigned           outN;   // outN == procSmpCnt
    unsigned           phase;
    cmSample_t         delaySmp;
    double             srate;
  } cmSigGen;


  /// Set p to NULL to dynamically allocate the object
  /// The last three arguments are optional. Set wfId to kInvalidWfId to allocate the signal generator without initializint it.
  cmSigGen* cmSigGenAlloc( cmCtx* c, cmSigGen*  p, unsigned procSmpCnt, double srate, unsigned wfId, double fundFrqHz, unsigned overToneCnt );
  cmRC_t    cmSigGenFree(  cmSigGen** p );
  cmRC_t    cmSigGenInit(  cmSigGen*  p, unsigned procSmpCnt, double srate, unsigned wfId, double fundFrqHz, unsigned overToneCnt );
  cmRC_t    cmSigGenFinal( cmSigGen*  p );
  cmRC_t    cmSigGenExec(  cmSigGen*  p );

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj*      obj;
    cmSample_t* bufPtr;
    unsigned    bufSmpCnt;    // count of samples in the delay line (bufSmpCnt = procSmpCnt+delaySmpCnt)
    unsigned    procSmpCnt;   // maximum legal samples to receive in a single call to cmDelayExec()
    unsigned    delaySmpCnt;  // delay time in samples
    int         delayInIdx;   // index into bufPtr[] of next element to receive an incoming sample
    unsigned    outCnt;       // count of valid buffers in outV[] 
    cmSample_t* outV[2];      // pointers to output buffers
    unsigned    outN[2];      // length of output buffers (the sum of the length of both output buffers is always procSmpCnt)
  } cmDelay;

  cmDelay*          cmDelayAlloc(   cmCtx* c, cmDelay* p, unsigned procSmpCnt, unsigned delaySmpCnt );
  cmRC_t            cmDelayFree(    cmDelay** p );
  cmRC_t            cmDelayInit(    cmDelay* p, unsigned procSmpCnt, unsigned delaySmpCnt );
  cmRC_t            cmDelayFinal(   cmDelay* p );
  cmRC_t            cmDelayCopyIn(  cmDelay* p, const cmSample_t* sp, unsigned sn );
  cmRC_t            cmDelayAdvance( cmDelay* p, unsigned sn );
  cmRC_t            cmDelayExec(    cmDelay* p, const cmSample_t* sp, unsigned sn, bool bypassFl );
  void              cmDelayTest();

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj       obj;
    double*     coeffV;      // FIR coefficient vector (impulse response)
    unsigned    coeffCnt;    // count of elements in coeffV
    double*     delayV;      // delay vector contains one less elements than the coeff array
    cmSample_t* outV;        // output signal
    unsigned    outN;        // length of the output signal (outN == ctx.procSmpCnt)
    unsigned    delayIdx;    // current next sample to receive input in the the delay line

  } cmFIR;

  enum { kHighPassFIRFl = 0x01 };

  // Note that the relative values of passHz and stopHz do not matter
  // for low-pass vs high-pass filters.  In practice passHz and
  // stopHz can be swapped with no effect on the filter in either
  // case.  Set p to NULL to dynamically allocate the object.
  cmFIR* cmFIRAllocKaiser(cmCtx* c, cmFIR* p, unsigned procSmpCnt, double srate, double passHz, double stopHz, double passDb, double stopDb, unsigned flags );

  // Set wndV[sincSmpCnt] to NULL to use a unity window otherwise set it to a window
  // function of length sincSmpCnt.
  cmFIR* cmFIRAllocSinc(  cmCtx* c, cmFIR* p, unsigned procSmpCnt, double srate, unsigned sincSmpCnt, double fcHz, unsigned flags, const double* wndV );
  cmRC_t cmFIRFree(       cmFIR** pp );
  cmRC_t cmFIRInitKaiser( cmFIR* p, unsigned procSmpCnt, double srate, double passHz,       double stopHz, double passDb, double stopDb, unsigned flags );
  cmRC_t cmFIRInitSinc(   cmFIR* p, unsigned procSmpCnt, double srate, unsigned sincSmpCnt, double fcHz,   unsigned flags, const double* wndV );
  cmRC_t cmFIRFinal(      cmFIR* p );
  cmRC_t cmFIRExec(       cmFIR* p, const cmSample_t* sp, unsigned sn );
  void   cmFIRTest();

  //------------------------------------------------------------------------------------------------------------
  // Apply a generic function to a windowed signal with a one sample hop size.

  typedef cmSample_t (*cmFuncFiltPtr_t)( const cmSample_t* sp, unsigned sn, void* userPtr );

  typedef struct
  {
    cmObj           obj;
    cmFuncFiltPtr_t funcPtr;
    cmShiftBuf      shiftBuf;
    cmSample_t*     outV;
    unsigned        outN;          // outN == procSmpCnt
    unsigned        curWndSmpCnt;
    unsigned        wndSmpCnt;
    void*           userPtr;
  } cmFuncFilter;

  /// Set p to NULL to dynamically allocate the object.
  cmFuncFilter* cmFuncFilterAlloc( cmCtx* c, cmFuncFilter* p, unsigned procSmpCnt, cmFuncFiltPtr_t funcPtr, void* userPtr, unsigned wndSmpCnt );
  cmRC_t        cmFuncFilterFree(  cmFuncFilter** pp );
  cmRC_t        cmFuncFilterInit(  cmFuncFilter* p, unsigned procSmpCnt, cmFuncFiltPtr_t funcPtr, void* userPtr, unsigned wndSmpCnt );
  cmRC_t        cmFuncFilterFinal( cmFuncFilter* p );
  cmRC_t        cmFuncFilterExec(  cmFuncFilter* p, const cmSample_t* sp, unsigned sn );
  void          cmFuncFilterTest();

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj           obj;
    unsigned        stateN;    // count of states
    unsigned        symN;      // count of discrete observation symbols

    cmReal_t* initV;     // initial state probability vector init[ stateN ]
    cmReal_t* transM;    // transition probability matrix   trans[ stateN (current), stateN (next) ]
    cmReal_t* stsM;      // state to symbol prob. matrix    stsM[  stateN, symN ]

  } cmDhmm;


  cmDhmm* cmDhmmAlloc( cmCtx* c, cmDhmm* p, unsigned stateN, unsigned symN, cmReal_t* initV,  cmReal_t* transM, cmReal_t* stsM );
  cmRC_t  cmDhmmFree(  cmDhmm** pp );
  cmRC_t  cmDhmmInit(  cmDhmm* p, unsigned stateN, unsigned symN, cmReal_t* initV, cmReal_t* transM, cmReal_t* stsM );
  cmRC_t  cmDhmmFinal( cmDhmm* p );
  cmRC_t  cmDhmmExec(  cmDhmm* p );

  cmRC_t  cmDhmmGenObsSequence( cmDhmm* p, unsigned* dbp, unsigned dn );
  cmRC_t  cmDhmmForwardEval(    cmDhmm* p, const cmReal_t* statePrV, const unsigned* obsV, unsigned obsN, cmReal_t* alphaM, unsigned flags, cmReal_t* logProbPtr );
  cmRC_t  cmDhmmReport(         cmDhmm* p );

  void    cmDhmmTest();

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj     obj;
    cmFftSR*  fft;
    cmIFftRS* ifft;

    cmComplexR_t* H; 

    unsigned    hn;
    cmSample_t* olaV;   // olaV[hn-1];

    cmSample_t* outV;  // outV[procSmpCnt]
    unsigned    outN;  // outN == procSmpCnt
  } cmConvolve;

  // After cmConvolveExec() outV[outN] contains the first outN samples
  // which are complete and can be used by the application.
  // The tail of the convolution is held in olaV[hn-1] and will
  // be automatically summed with the beginning of the next convolution
  // frame. 

  // BUG BUG BUG
  // This code seems to have a problem when hn != procSmpCnt (or maybe hn > procSmpCnt ???).  
  // See mas/main.c convolve() where procSmpCnt must be set to wndSmpCnt size or 
  // only the first half of the window is emitted.


  // h[hn] is the impulse response to convolve with
  cmConvolve* cmConvolveAlloc( cmCtx* c, cmConvolve* p, const cmSample_t* h, unsigned hn, unsigned procSmpCnt );
  cmRC_t      cmConvolveFree(  cmConvolve** pp );
  cmRC_t      cmConvolveInit(  cmConvolve* p, const cmSample_t* h, unsigned hn, unsigned procSmpCnt );
  cmRC_t      cmConvolveFinal( cmConvolve* p );
  // xn must be <= procSmpCnt
  cmRC_t      cmConvolveExec(  cmConvolve* p, const cmSample_t* x, unsigned xn );

  cmRC_t      cmConvolveSignal( cmCtx* c, const cmSample_t* h, unsigned hn, const cmSample_t* x, unsigned xn, cmSample_t* y, unsigned yn );

  cmRC_t      cmConvolveTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH );

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj     obj; 
    cmReal_t* dctMtx;    // dctMtx[ binCnt, bandCnt ]
    cmReal_t* filtMask;  // filtMask[ bandCnt, bandCnt ]
    unsigned  binCnt;    // bin cnt of input magnitude spectrum
    unsigned  bandCnt;   // must be <= kDefaultBarkBandCnt
    cmReal_t* outV;      // outV[binCnt]

  } cmBfcc;

  cmBfcc* cmBfccAlloc( cmCtx* ctx, cmBfcc* p, unsigned bandCnt, unsigned binCnt, double binHz );
  cmRC_t  cmBfccFree(  cmBfcc** pp );
  cmRC_t  cmBfccInit(  cmBfcc* p, unsigned bandCnt, unsigned binCnt, double binHz );
  cmRC_t  cmBfccFinal( cmBfcc* p );
  cmRC_t  cmBfccExec(  cmBfcc* p, const cmReal_t* magV, unsigned binCnt );
  void    cmBfccTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH );

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj     obj; 
    //cmIFftRR  ft;
    unsigned  dct_cn;    // (binCnt-1)*2
    cmReal_t* dctM;      // dctM[ outN, dct_cn ]
    unsigned  binCnt;    // bin cnt of input magnitude spectrum
    unsigned  outN;      // count of cepstral coeff's
    cmReal_t* outV;      // outV[outN]
    

  } cmCeps;

  // outN is the number of cepstral coeff's in the output vector
  cmCeps* cmCepsAlloc( cmCtx* ctx, cmCeps* p, unsigned binCnt, unsigned outN );
  cmRC_t  cmCepsFree(  cmCeps** pp );
  cmRC_t  cmCepsInit(  cmCeps* p, unsigned binCnt, unsigned outN );
  cmRC_t  cmCepsFinal( cmCeps* p );
  cmRC_t  cmCepsExec(  cmCeps* p, const cmReal_t* magV, const cmReal_t* phsV, unsigned binCnt );


  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj       obj;
    cmWndFunc   wf;
    unsigned    wndSmpCnt;
    unsigned    hopSmpCnt;
    unsigned    procSmpCnt;

    cmSample_t* bufV;       // bufV[wndSmpCnt] overlap add buffer 
    cmSample_t* outV;       // outV[hopSmpCnt] output vector
    cmSample_t* outPtr;     // outPtr[procSmpCnt] output vector
    unsigned    idx;        // idx of next val in bufV[] to be moved to outV[]


  } cmOla;

  // hopSmpCnt must be <= wndSmpCnt.
  // hopSmpCnt must be an even multiple of procSmpCnt.
  // Call cmOlaExecR() or cmOlaExecS() at the spectral frame rate.
  // Call cmOlaExecOut() at the time domain audio frame rate.

  // Set wndTypeId to one of the cmWndFuncXXX enumerated widnow type id's.
  cmOla* cmOlaAlloc( cmCtx* ctx, cmOla* p, unsigned wndSmpCnt, unsigned hopSmpCnt, unsigned procSmpCnt, unsigned wndTypeId );
  cmRC_t cmOlaFree(  cmOla** pp );
  cmRC_t cmOlaInit(  cmOla* p, unsigned wndSmpCnt, unsigned hopSmpCnt, unsigned procSmpCnt, unsigned wndTypeId );
  cmRC_t cmOlaFinal( cmOla* p );
  cmRC_t cmOlaExecS( cmOla* p, const cmSample_t* xV, unsigned xN );
  cmRC_t cmOlaExecR( cmOla* p, const cmReal_t*   xV, unsigned xN );
  const cmSample_t* cmOlaExecOut(cmOla* p );



  //------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    cmObj     obj;
    cmReal_t* hzV;  // hzV[binCnt] output vector - frequency in Hertz 

    cmReal_t* phsV; // phsV[binCnt] 

    cmReal_t* wV;   // bin freq in rads/hop
    double   srate;
    unsigned hopSmpCnt;
    unsigned binCnt;

  } cmPhsToFrq;

  cmPhsToFrq* cmPhsToFrqAlloc( cmCtx* c, cmPhsToFrq* p, double srate, unsigned binCnt, unsigned hopSmpCnt );
  cmRC_t  cmPhsToFrqFree( cmPhsToFrq** p );
  cmRC_t  cmPhsToFrqInit( cmPhsToFrq*  p, double srate, unsigned binCnt, unsigned hopSmpCnt );
  cmRC_t  cmPhsToFrqFinal(cmPhsToFrq*  p );
  cmRC_t  cmPhsToFrqExec( cmPhsToFrq*  p, const cmReal_t* phsV );
 

  //------------------------------------------------------------------------------------------------------------

  enum 
  {
    kNoCalcHzPvaFl = 0x00,
    kCalcHzPvaFl   = 0x01
  };

  typedef struct
  {
    cmObj           obj;
    cmShiftBuf      sb;
    cmFftSR         ft;
    cmWndFunc       wf;
    cmPhsToFrq      pf;

    unsigned        flags;
    unsigned        procSmpCnt;
    double          srate;
    unsigned        wndSmpCnt;
    unsigned        hopSmpCnt;
    unsigned        binCnt;

    const cmReal_t* magV; // amplitude NOT power
    const cmReal_t* phsV;
    const cmReal_t* hzV;

  
  } cmPvAnl;

  cmPvAnl*   cmPvAnlAlloc( cmCtx* ctx, cmPvAnl* p, unsigned procSmpCnt, double srate, unsigned wndSmpCnt, unsigned hopSmpCnt, unsigned flags );
  cmRC_t     cmPvAnlFree( cmPvAnl** pp );
  cmRC_t     cmPvAnlInit( cmPvAnl* p, unsigned procSmpCnt, double srate, unsigned wndSmpCnt, unsigned hopSmpCnt, unsigned flags );
  cmRC_t     cmPvAnlFinal(cmPvAnl* p );

  // Returns true when a new spectrum has been computed
  bool     cmPvAnlExec( cmPvAnl* p, const cmSample_t* x, unsigned xN );

  //------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    cmObj           obj;
    cmIFftRS        ft;
    cmWndFunc       wf;
    cmOla           ola;

    cmReal_t*       minRphV;
    cmReal_t*       maxRphV;
    cmReal_t*       itrV;
    cmReal_t*       phs0V;
    cmReal_t*       mag0V;
    cmReal_t*       phsV;
    cmReal_t*       magV; 

    double          outSrate;
    unsigned        procSmpCnt;
    unsigned        wndSmpCnt;
    unsigned        hopSmpCnt;
    unsigned        binCnt;
  
  } cmPvSyn;

  cmPvSyn*   cmPvSynAlloc( cmCtx* ctx, cmPvSyn* p, unsigned procSmpCnt, double outSrate, unsigned wndSmpCnt, unsigned hopSmpCnt,unsigned wndTypeId );
  cmRC_t     cmPvSynFree( cmPvSyn** pp );
  cmRC_t     cmPvSynInit( cmPvSyn* p, unsigned procSmpCnt, double outSrate, unsigned wndSmpCnt, unsigned hopSmpCnt,unsigned wndTypeId );
  cmRC_t     cmPvSynFinal(cmPvSyn* p );
  cmRC_t     cmPvSynExec( cmPvSyn* p, const cmReal_t* magV, const cmReal_t* phsV );
  const cmSample_t* cmPvSynExecOut(cmPvSyn* p );



  //------------------------------------------------------------------------------------------------------------


  // callback selector values
  enum
  {
    kAttackMsId,
    kReleaseMsId,
    kDspMsId            // return 0 if the voice is no longer active
  };

  // voice flags
  enum
  {
    kActiveMsFl  = 0x01, // set if the voice is active
    kKeyGateMsFl = 0x02, // set if the key  is down for this note
  };

  struct cmMidiSynth_str;
  struct cmMidiSynthCh_str;
  struct cmMidiVoice_str;


  // voice update callback - use voicePtr->pgm.cbDataPtr to get voice specific data
  typedef int (*cmMidiSynthCb_t)( struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt );


  typedef struct
  {
    cmMidiByte_t    pgm;          // MIDI pgm number   
    cmMidiSynthCb_t cbPtr;        // voice update callback
    void*           cbDataPtr;    // user data pointer
  } cmMidiSynthPgm;


  typedef struct cmMidiVoice_str
  {
    unsigned                  index;    // voice index
    unsigned                  flags;    // see kXXXMsFl above
    cmMidiByte_t              pitch;    // note-on pitch
    cmMidiByte_t              velocity; // note-on/off veloctiy
    cmMidiSynthPgm            pgm;      // pgm associated with this voice
    struct cmMidiSynthCh_str* chPtr;    // pointer to owning ch
    struct cmMidiVoice_str*   link;     // link to next active/avail voice in chain
  } cmMidiVoice;

  typedef struct cmMidiSynthCh_str
  {
    cmMidiByte_t            midiCtl[  kMidiCtlCnt ]; // current ctl values
    short                   pitchBend;               // current pitch bend value
    cmMidiByte_t            pgm;                     // last pgm received
    cmMidiVoice*            active;                  // first active voice on this channel
    struct cmMidiSynth_str* synthPtr;                // owning synth
  } cmMidiSynthCh;

  typedef struct cmMidiSynth_str
  {
    cmObj          obj;
    cmMidiSynthCh  chArray[ kMidiChCnt ];   // midi channel array  
    unsigned       voiceCnt;                // count of voice records
    cmMidiVoice*   avail;                   // avail voice chain
    unsigned       activeVoiceCnt;          // current count of active voices
    unsigned       voiceStealCnt;           // count of times voice stealing was required 
    cmMidiVoice*   voiceArray;              // array of voice records
    cmMidiSynthPgm pgmArray[ kMidiPgmCnt ]; // array of pgm records
    unsigned       procSmpCnt;              // samples per DSP cycle 
    unsigned       outChCnt;                // count of output channels
    cmSample_t*    outM;                    // outM[ procSmpCnt, outChCnt ] output buffer
    cmSample_t**   outChArray;              // buffer of pointers to each output channel
    cmReal_t       srate;                   // output signal sample rate
  } cmMidiSynth;

  cmMidiSynth* cmMidiSynthAlloc( cmCtx* ctx, cmMidiSynth* p, const cmMidiSynthPgm* pgmArray, unsigned pgmCnt, unsigned voiceCnt, unsigned procSmpCnt, unsigned outChCnt, cmReal_t srate  );
  cmRC_t       cmMidiSynthFree(  cmMidiSynth** pp );
  cmRC_t       cmMidiSynthInit(  cmMidiSynth* p, const cmMidiSynthPgm* pgmArray, unsigned pgmCnt, unsigned voiceCnt, unsigned procSmpCnt, unsigned outChCnt, cmReal_t srate  );
  cmRC_t       cmMidiSynthFinal( cmMidiSynth* p );
  cmRC_t       cmMidiSynthOnMidi(cmMidiSynth* p, const cmMidiPacket_t* pktArray, unsigned pktCnt );
  cmRC_t       cmMidiSynthExec(  cmMidiSynth* p, cmSample_t** outChArray, unsigned outChCnt );


  //------------------------------------------------------------------------------------------------------------

  // state id's
  enum
  {
    kOffWtId,
    kAtkWtId,
    kDcyWtId,
    kSusWtId,
    kRlsWtId
  };

  typedef struct
  {
    cmObj       obj;              
    cmReal_t    hz;        // current frq in Hz  
    cmReal_t    level;     // current gain (0.0 to 1.0)
    cmReal_t    phase;     // osc phase (radians)
    unsigned    durSmpCnt; // count of samples generated so far
    unsigned    state;     // osc state - see kXXXWtId above
    cmSample_t* outV;      // signal output vector
    unsigned    outN;      // samples in outV[]
  } cmWtVoice;

  cmWtVoice* cmWtVoiceAlloc( cmCtx* ctx, cmWtVoice* p, unsigned procSmpCnt, cmReal_t hz );
  cmRC_t     cmWtVoiceFree( cmWtVoice** pp );
  cmRC_t     cmWtVoiceInit( cmWtVoice*  p, unsigned procSmpCnt, cmReal_t hz );
  cmRC_t     cmWtVoiceFinal( cmWtVoice* p );

  // 'sel' values are cmMidiSynthExec (kXXXMsId) values
  // Set outChArray[] to NULL to use internal audio buffer.
  int        cmWtVoiceExec( cmWtVoice* p, struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt );


  //------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    cmObj obj;

    cmWtVoice**    voiceArray;   // osc state array
    unsigned       voiceCnt;

    cmSample_t*    buf;
    cmSample_t**   chArray;
    unsigned       chCnt;
    unsigned       procSmpCnt; // count of samples in each chArray[i] sample vector

    double         srate;     // synth sample rate

  } cmWtVoiceBank;

  cmWtVoiceBank* cmWtVoiceBankAlloc( cmCtx* ctx, cmWtVoiceBank* p, double srate, unsigned procSmpCnt, unsigned voiceCnt, unsigned chCnt );
  cmRC_t     cmWtVoiceBankFree( cmWtVoiceBank** pp );
  cmRC_t     cmWtVoiceBankInit( cmWtVoiceBank*  p, double srate, unsigned procSmpCnt, unsigned voiceCnt, unsigned chCnt );
  cmRC_t     cmWtVoiceBankFinal( cmWtVoiceBank* p );

  // 'sel' values are cmMidiSynthExec (kXXXMsId) values
  // Set outChArray[] to NULL to use internal audio buffer.
  // Return 0 if the voice has gone inactive otherwise return 1.
  int        cmWtVoiceBankExec( cmWtVoiceBank* p, struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* chArray[], unsigned chCnt );



  //------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    cmObj           obj;
    cmSample_t*     bufV;   // bufV[ bufN ]
    unsigned        bufN;
    cmAudioFileInfo_t info;
    unsigned        begSmpIdx;
    unsigned        chIdx;
    char*           fn;
  } cmAudioFileBuf;

  // set 'durSmpCnt' to cmInvalidCnt to include all samples to the end of the file
  cmAudioFileBuf* cmAudioFileBufAlloc( cmCtx* ctx, cmAudioFileBuf* p, unsigned procSmpCnt, const char* fn, unsigned chIdx, unsigned begSmpIdx, unsigned durSmpCnt );
  cmRC_t          cmAudioFileBufFree( cmAudioFileBuf** pp );
  cmRC_t          cmAudioFileBufInit( cmAudioFileBuf* p, unsigned procSmpCnt, const char* fn, unsigned chIdx, unsigned begSmpIdx, unsigned durSmpCnt );
  cmRC_t          cmAudioFileBufFinal(cmAudioFileBuf* p );

  // Returns the count of samples copied into outV or 0 if smpIdx >= p->bufN. 
  // If less than outN samples are available then the remaining samples are set to 0.  
  unsigned        cmAudioFileBufExec( cmAudioFileBuf* p, unsigned smpIdx, cmSample_t* outV, unsigned outN, bool sumIntoOutFl );


  //------------------------------------------------------------------------------------------------------------
  // Multi-delay.  Each of the taps of this delay operates as a independent delay with feedback.
  
  // Delay line specification.
  typedef struct
  {
    cmReal_t    delayGain;       // delay gain
    cmReal_t    delayMs;         // delay time in milliseconds  
    cmReal_t    delaySmpFrac;    // delay time in samples (next fractional delay index = inIdx - delaySmpFrac) 
    cmSample_t* delayBuf;        // delayBuf[delayBufSmpCnt] delay line memory 
    int         delayBufSmpCnt;  // delay buffer length in samples
    int         inIdx;           // next delay input index
  } cmMDelayHead;

  typedef struct
  {
    cmObj         obj;
    unsigned      delayCnt;      // count of taps
    cmMDelayHead* delayArray;    // tap specs  
    cmSample_t*   outV;          // outV[outN] output buffer
    unsigned      outN;          // procSmpCnt
    cmReal_t      fbCoeff;       // feedback coeff.
    cmReal_t      srate;         // system sample rate
  } cmMDelay;


  cmMDelay* cmMDelayAlloc( cmCtx* ctx, cmMDelay* p, unsigned procSmpCnt, cmReal_t srate, cmReal_t fbCoeff, unsigned delayCnt, const cmReal_t* delayMsArray, const cmReal_t* delayGainArray );
  cmRC_t    cmMDelayFree(  cmMDelay** pp );
  cmRC_t    cmMDelayInit( cmMDelay* p, unsigned procSmpCnt, cmReal_t srate, cmReal_t fbCoeff, unsigned delayCnt, const cmReal_t* delayMsArray, const cmReal_t* delayGainArray );
  cmRC_t    cmMDelayFinal( cmMDelay* p );
  cmRC_t    cmMDelayExec(  cmMDelay* p, const cmSample_t* sigV, cmSample_t* outV, unsigned sigN, bool bypassFl ); 
  void      cmMDelaySetTapMs( cmMDelay* p, unsigned tapIdx, cmReal_t ms );
  void      cmMDelaySetTapGain(cmMDelay* p, unsigned tapIdx, cmReal_t gain );
  void      cmMDelayReport( cmMDelay* p, cmRpt_t* rpt );

  //------------------------------------------------------------------------------------------------------------
  enum
  {
    kEnableAspFl  = 0x01,
    kDelAspFl     = 0x02
  };


  typedef struct cmAudioSeg_str
  {
    cmAudioFileBuf*    bufPtr;     // pointer to the audio file buffer this segment is contained in
    unsigned           id;         // id (unique amoung segments) 
    unsigned           smpIdx;     // offset into audioBuf[] of first sample
    unsigned           smpCnt;     // total count of samples to play 
    unsigned           outChIdx;   // output buffer channel 
    unsigned           outSmpIdx;  // outSmpIdx + smpIdx == next sample to play
    unsigned           flags;      // see kXXXAspFl
  } cmAudioSeg;

  typedef struct
  {
    cmObj             obj;
    unsigned          segCnt;
    cmAudioSeg*       segArray;
    unsigned          procSmpCnt;
    cmSample_t**      outChArray;
    unsigned          outChCnt;
    cmSample_t*       outM;
  } cmAudioSegPlayer;

  cmAudioSegPlayer* cmAudioSegPlayerAlloc( cmCtx* ctx, cmAudioSegPlayer* p, unsigned procSmpCnt, unsigned outChCnt );
  cmRC_t            cmAudioSegPlayerFree(   cmAudioSegPlayer** pp );
  cmRC_t            cmAudioSegPlayerInit(   cmAudioSegPlayer* p, unsigned procSmpCnt, unsigned outChCnt );
  cmRC_t            cmAudioSegPlayerFinal(  cmAudioSegPlayer* p );
  cmRC_t            cmAudioSegPlayerInsert( cmAudioSegPlayer* p, unsigned id, cmAudioFileBuf* bufPtr, unsigned smpIdx, unsigned smpCnt, unsigned outChIdx );
  cmRC_t            cmAudioSegPlayerEdit(   cmAudioSegPlayer* p, unsigned id, cmAudioFileBuf* bufPtr, unsigned smpIdx, unsigned smpCnt, unsigned outChIdx );
  cmRC_t            cmAudioSegPlayerRemove( cmAudioSegPlayer* p, unsigned id, bool delFl );
  cmRC_t            cmAudioSegPlayerEnable( cmAudioSegPlayer* p, unsigned id, bool enableFl, unsigned outSmpIdx );
  cmRC_t            cmAudioSegPlayerReset(  cmAudioSegPlayer* p );
  cmRC_t            cmAudioSegPlayerExec(   cmAudioSegPlayer* p, cmSample_t** outChPtr, unsigned chCnt, unsigned outSmpCnt );


  //------------------------------------------------------------------------------------------------------------
  /*
  cmReal_t (*cmCluster0DistFunc_t)( void* userPtr, const cmReal_t* v0, const cmReal_t* v1, unsigned binCnt );

  typedef struct
  {
    cmObj                     obj;
    unsigned                  flags;
    unsigned                  stateCnt;
    unsigned                  binCnt;
    cmReal_t*                 oM;          // oM[ binCnt,   stateCnt ]
    unsigned*                 tM;          // tM[ stateCnt, stateCnt ]
    cmReal_t*                  dV;          // dV[ state
    cmCluster0DistFunc_t      distFunc;
    void*                     distUserPtr;
    unsigned                  cnt;
  } cmCluster0;
  
  enum
  {
    kCalcTransFl = 0x01,
    kCalcDurFl   = 0x02
  };

  cmCluster0* cmCluster0Alloc( cmCtx* ctx, cmCluster0* ap, unsigned stateCnt, unsigned binCnt, unsigned flags, cmCluster0DistFunc_t distFunc, void* dstUserPtr ); 
  cmRC_t           cmCluster0Free(  cmCluster0** pp );
  cmRC_t           cmCluster0Init(  cmCluster0* p,  unsigned stateCnt, unsigned binCnt, unsigned flags, cmCluster0DistFunc_t distFunc, void* dstUserPtr ); 
  cmRC_t           cmCluster0Final( cmCluster0* p );
  cmRC_t           cmCluster0Exec(  cmCluster0* p, const cmReal_t* v, unsigned vn );
  */

  //------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj     obj;

    unsigned  n;
    unsigned  m;
    unsigned  r;
    unsigned  maxIterCnt;
    unsigned  convergeCnt;
    
    cmReal_t* V;  // V[n,m]
    cmReal_t* W;  // W[n,r]
    cmReal_t* H;  // H[r,m]

    cmReal_t* tr;
    cmReal_t* x;
    cmReal_t* t0nm;
    cmReal_t* t1nm;
    cmReal_t* Wt;
    cmReal_t* Ht;
    cmReal_t* trm;
    unsigned* crm;
    cmReal_t* tnr;
    unsigned* c0;
    unsigned* c1;
    unsigned* c0m;
    unsigned* c1m;
    unsigned* idxV;
    
  } cmNmf_t;


  cmNmf_t* cmNmfAlloc( cmCtx* ctx, cmNmf_t* ap, unsigned n, unsigned m, unsigned r, unsigned maxIterCnt, unsigned convergeCnt );
  cmRC_t   cmNmfFree( cmNmf_t** pp );
  cmRC_t   cmNmfInit( cmNmf_t* p,  unsigned n, unsigned m, unsigned r, unsigned maxIterCnt, unsigned convergeCnt );
  cmRC_t   cmNmfFinal(cmNmf_t* p );

  // 
  cmRC_t   cmNmfExec( cmNmf_t* p, const cmReal_t* v, unsigned cn );


  //------------------------------------------------------------------------------------------------------------
  // cmVectArray buffers row vectors of arbitrary lenght in  memory.
  // The buffers may then be access using the cmVectArrayGetXXX() functions.
  // The entire contents of the file may be written to a file using atVectArrayWrite().
  // The file may then be read in back into memory using cmVectArrayAllocFromFile()
  // or in octave via readVectArray.m.
  // A rectantular matrix in memory may be written to a VectArray file in one operation
  // via the function cmVectArrayWriteMatrixXXX(). 

  typedef struct cmVectArrayVect_str
  {
    unsigned n;  // length of this vector in values (not bytes)

    union           
    {
      char*        v;  // raw memory vector pointer
      double*     dV;  // dV[n] vector of doubles  
      float*      fV;  // fV[n] vecotr of floats
      cmSample_t* sV;  // sV[n] vector of cmSample_t
      int*        iV;
      unsigned*   uV;
    } u;

    struct cmVectArrayVect_str* link; // link to next element record

  } cmVectArrayVect_t;

  enum
  {
    kDoubleVaFl = 0x01,
    kRealVaFl   = 0x01,
    kFloatVaFl  = 0x02,
    kSampleVaFl = 0x02,
    kIntVaFl    = 0x04,
    kUIntVaFl   = 0x08,
    kVaMask     = 0x0f
  };

  typedef struct
  {
    cmObj              obj;
    cmVectArrayVect_t* bp;          // first list element
    cmVectArrayVect_t* ep;          // last list element
    unsigned           vectCnt;     // count of elements in linked list
    unsigned           flags;       // data vector type (See: kFloatVaFl, kDoubleVaFl, ... )
    unsigned           typeByteCnt; // size of a single data vector value (e.g. 4=float 8=double)
    unsigned           maxEleCnt;   // length of the longest data vector
    double*            tempV;
    cmVectArrayVect_t* cur;
  } cmVectArray_t;

  // Flags must be set to one of the kXXXVAFl flag values.
  cmVectArray_t* cmVectArrayAlloc( cmCtx* ctx, unsigned flags );
  cmVectArray_t* cmVectArrayAllocFromFile(cmCtx* ctx, const char* fn );

  cmRC_t cmVectArrayFree(    cmVectArray_t** pp );

  // Release all the stored vectors but do not release the object.
  cmRC_t cmVectArrayClear(   cmVectArray_t* p );

  // Return the count of vectors contained in the vector array.
  cmRC_t cmVectArrayCount(   const cmVectArray_t* p );

  // Return the maximum element count among all rows.
  unsigned cmVectArrayMaxRowCount( const cmVectArray_t* p );

  // Store a new vector by appending it to the end of the internal vector list.
  // Note that the true type of v[] in the call to cmVectArrayAppendV() must match
  // the data type set in p->flags.
  cmRC_t cmVectArrayAppendV( cmVectArray_t* p, const void* v,       unsigned vn );
  cmRC_t cmVectArrayAppendS( cmVectArray_t* p, const cmSample_t* v, unsigned vn );
  cmRC_t cmVectArrayAppendR( cmVectArray_t* p, const cmReal_t* v,   unsigned vn );
  cmRC_t cmVectArrayAppendF( cmVectArray_t* p, const float* v,      unsigned vn );
  cmRC_t cmVectArrayAppendD( cmVectArray_t* p, const double* v,     unsigned vn );
  cmRC_t cmVectArrayAppendI( cmVectArray_t* p, const int* v,        unsigned vn );
  cmRC_t cmVectArrayAppendU( cmVectArray_t* p, const unsigned* v,   unsigned vn );

  // Write a vector array in a format that can be read by readVectArray.m.
  cmRC_t cmVectArrayWrite(   cmVectArray_t* p, const char* fn );

  // Print the vector array to rpt.
  cmRC_t cmVectArrayPrint( cmVectArray_t* p, cmRpt_t* rpt );
  
  typedef cmRC_t (*cmVectArrayForEachFuncS_t)( void* arg, unsigned idx, const cmSample_t* xV, unsigned xN );
  unsigned cmVectArrayForEachS( cmVectArray_t* p, unsigned idx, unsigned cnt, cmVectArrayForEachFuncS_t func, void* arg ); 

  // Write the vector v[vn] in the VectArray file format.
  // Note that the true type of v[] in cmVectArrayWriteVectoV() must match the
  // data type set in the 'flags' parameter.
  cmRC_t cmVectArrayWriteVectorV( cmCtx* ctx, const char* fn, const void*       v, unsigned  vn, unsigned flags );
  cmRC_t cmVectArrayWriteVectorS( cmCtx* ctx, const char* fn, const cmSample_t* v, unsigned  vn );
  cmRC_t cmVectArrayWriteVectorR( cmCtx* ctx, const char* fn, const cmReal_t*   v, unsigned  vn );  
  cmRC_t cmVectArrayWriteVectorD( cmCtx* ctx, const char* fn, const double*     v, unsigned  vn );
  cmRC_t cmVectArrayWriteVectorF( cmCtx* ctx, const char* fn, const float*      v, unsigned  vn );
  cmRC_t cmVectArrayWriteVectorI( cmCtx* ctx, const char* fn, const int*        v, unsigned  vn );
  cmRC_t cmVectArrayWriteVectorU( cmCtx* ctx, const char* fn, const unsigned*   v, unsigned  vn );

  // Write the column-major matrix m[rn,cn] to the file 'fn'.
  // Note that the true type of m[] in cmVectArrayWriteMatrixV() must match the
  // data type set in the 'flags' parameter.
  cmRC_t cmVectArrayWriteMatrixV( cmCtx* ctx, const char* fn, const void*       m, unsigned  rn, unsigned cn, unsigned flags );
  cmRC_t cmVectArrayWriteMatrixS( cmCtx* ctx, const char* fn, const cmSample_t* m, unsigned  rn, unsigned cn );
  cmRC_t cmVectArrayWriteMatrixR( cmCtx* ctx, const char* fn, const cmReal_t*   m, unsigned  rn, unsigned cn );  
  cmRC_t cmVectArrayWriteMatrixD( cmCtx* ctx, const char* fn, const double*     m, unsigned  rn, unsigned cn );
  cmRC_t cmVectArrayWriteMatrixF( cmCtx* ctx, const char* fn, const float*      m, unsigned  rn, unsigned cn );
  cmRC_t cmVectArrayWriteMatrixI( cmCtx* ctx, const char* fn, const int*        m, unsigned  rn, unsigned cn );
  cmRC_t cmVectArrayWriteMatrixU( cmCtx* ctx, const char* fn, const unsigned*   m, unsigned  rn, unsigned cn );

  // Read a VectArray file and return it as a matrix.
  // The returned memory must be released with a subsequent call to cmMemFree().
  // Note that the true type of the pointer address 'mRef' in the call to 
  // cmVectArrayReadMatrixV() must match the data type of the cmVectArray_t
  // specified by 'fn'.
  cmRC_t cmVectArrayReadMatrixV( cmCtx* ctx, const char* fn, void**       mRef, unsigned* rnRef, unsigned* cnRef );
  cmRC_t cmVectArrayReadMatrixS( cmCtx* ctx, const char* fn, cmSample_t** mRef, unsigned* rnRef, unsigned* cnRef );
  cmRC_t cmVectArrayReadMatrixR( cmCtx* ctx, const char* fn, cmReal_t**   mRef, unsigned* rnRef, unsigned* cnRef );  
  cmRC_t cmVectArrayReadMatrixD( cmCtx* ctx, const char* fn, double**     mRef, unsigned* rnRef, unsigned* cnRef );
  cmRC_t cmVectArrayReadMatrixF( cmCtx* ctx, const char* fn, float**      mRef, unsigned* rnRef, unsigned* cnRef );
  cmRC_t cmVectArrayReadMatrixI( cmCtx* ctx, const char* fn, int**        mRef, unsigned* rnRef, unsigned* cnRef );
  cmRC_t cmVectArrayReadMatrixU( cmCtx* ctx, const char* fn, unsigned**   mRef, unsigned* rnRef, unsigned* cnRef );

  // Row iteration control functions.
  cmRC_t   cmVectArrayRewind(   cmVectArray_t* p );
  cmRC_t   cmVectArrayAdvance(  cmVectArray_t* p, unsigned n );
  bool     cmVectArrayIsEOL(    const cmVectArray_t* p );
  unsigned cmVectArrayEleCount( const cmVectArray_t* p );

  // Copy the current row vector to v[].
  // Note that the true type of v[] in cmVectArrayGetV() must match the data type of 'p'.
  cmRC_t  cmVectArrayGetV(     cmVectArray_t* p, void*       v, unsigned* vnRef );
  cmRC_t  cmVectArrayGetS(     cmVectArray_t* p, cmSample_t* v, unsigned* vnRef );  
  cmRC_t  cmVectArrayGetR(     cmVectArray_t* p, cmReal_t*   v, unsigned* vnRef );
  cmRC_t  cmVectArrayGetD(     cmVectArray_t* p, double*     v, unsigned* vnRef );  
  cmRC_t  cmVectArrayGetF(     cmVectArray_t* p, float*      v, unsigned* vnRef );
  cmRC_t  cmVectArrayGetI(     cmVectArray_t* p, int*        v, unsigned* vnRef );
  cmRC_t  cmVectArrayGetU(     cmVectArray_t* p, unsigned*   v, unsigned* vnRef );

  // Set *resultFlRef to true if m[rn,cn] is equal to the cmVectArray_t specified by 'fn'.
  // Note that the true type of 'm[]' in the call to cmVectArrayMatrixIsEqualV()
  // must match the data type set in 'flags'.
  cmRC_t  cmVectArrayMatrixIsEqualV( cmCtx* ctx, const char* fn, const void*       m, unsigned rn, unsigned cn, bool* resultFlRef, unsigned flags );
  cmRC_t  cmVectArrayMatrixIsEqualS( cmCtx* ctx, const char* fn, const cmSample_t* m, unsigned rn, unsigned cn, bool* resultFlRef );  
  cmRC_t  cmVectArrayMatrixIsEqualR( cmCtx* ctx, const char* fn, const cmReal_t*   m, unsigned rn, unsigned cn, bool* resultFlRef );  
  cmRC_t  cmVectArrayMatrixIsEqualD( cmCtx* ctx, const char* fn, const double*     m, unsigned rn, unsigned cn, bool* resultFlRef );  
  cmRC_t  cmVectArrayMatrixIsEqualF( cmCtx* ctx, const char* fn, const float*      m, unsigned rn, unsigned cn, bool* resultFlRef );  
  cmRC_t  cmVectArrayMatrixIsEqualI( cmCtx* ctx, const char* fn, const int*        m, unsigned rn, unsigned cn, bool* resultFlRef );  
  cmRC_t  cmVectArrayMatrixIsEqualU( cmCtx* ctx, const char* fn, const unsigned*   m, unsigned rn, unsigned cn, bool* resultFlRef );  

  // If a vector array is composed of repeating blocks of 'groupCnt' sub-vectors 
  // where the concatenated ith sub-vectors in each group form a single super-vector then
  // this function will return the super-vector.  Use cmMemFree(*vRef) to release
  // the returned super-vector.
  cmRC_t   cmVectArrayFormVectR( cmVectArray_t* p, unsigned groupIdx, unsigned groupCnt, cmReal_t**   vRef, unsigned* vnRef );
  cmRC_t   cmVectArrayFormVectF( cmVectArray_t* p, unsigned groupIdx, unsigned groupCnt, float**      vRef, unsigned* vnRef );

  cmRC_t   cmVectArrayFormVectColF( cmVectArray_t* p, unsigned groupIdx, unsigned groupCnt, unsigned colIdx, float**    vRef, unsigned* vnRef );
  cmRC_t   cmVectArrayFormVectColU( cmVectArray_t* p, unsigned groupIdx, unsigned groupCnt, unsigned colIdx, unsigned** vRef, unsigned* vnRef );
  cmRC_t   cmVectArrayTest( cmCtx* ctx, const char* fn, bool genFl );  

  //-----------------------------------------------------------------------------------------------------------------------
  // Spectral whitening filter.
  // Based on: Klapuri, A., 2006: Multiple fundamental frequency estimation by summing
  //  harmonic amplitudes.

  typedef struct
  {
    cmObj     obj;
    unsigned  binCnt;  // 
    cmReal_t  binHz;   //
    unsigned  bandCnt; //
    cmReal_t  coeff;   //
    cmReal_t* whiV;    // whiV[bandCnt+2] - fractional bin index of each center frequency 
    cmReal_t* whM;     // whM[binCnt,bandCnt]
    cmReal_t* iV;      // iV[ binCnt ] - working memory
  } cmWhFilt;

  cmWhFilt* cmWhFiltAlloc( cmCtx* c, cmWhFilt* p, unsigned binCnt, cmReal_t binHz, cmReal_t coeff, cmReal_t maxHz );
  cmRC_t    cmWhFiltFree( cmWhFilt** pp );
  cmRC_t    cmWhFiltInit( cmWhFilt* p, unsigned binCnt, cmReal_t binHz, cmReal_t coeff, cmReal_t maxHz );
  cmRC_t    cmWhFiltFinal( cmWhFilt* p );
  cmRC_t    cmWhFiltExec( cmWhFilt* p, const cmReal_t* xV, cmReal_t* yV, unsigned xyN );

  //-----------------------------------------------------------------------------------------------------------------------
  typedef enum
  {
    kNoStateFrqTrkId,
    kDlyFrqTrkId,
    kAtkFrqTrkId,
    kSusFrqTrkId,
    kDcyFrqTrkId
  } cmFrqTrkAttenStateId_t;

  typedef struct
  {
    double      srate;          // system sample rate
    unsigned    chCnt;          // tracking channel count
    unsigned    binCnt;         // count of spectrum elements passed in each call to cmFrqTrkExec()
    unsigned    hopSmpCnt;      // phase vocoder hop count in samples
    cmReal_t    stRange;        // maximum allowable semi-tones between a tracker and a peak
    cmReal_t    wndSecs;        // duration of the  
    cmReal_t    minTrkSec;      // minimum track length before track is considered stable
    cmReal_t    maxTrkDeadSec;  // maximum length of time a tracker may fail to connect to a peak before being declared disconnected.
    cmReal_t    pkThreshDb;     // minimum amplitide in Decibels of a selected spectral peak.
    cmReal_t    pkAtkThreshDb;  // minimum amplitude in Decibels for the first frame of a new track.
    cmReal_t    pkMaxHz;        // maximum frequency to track
    cmReal_t    whFiltCoeff;

    cmReal_t    attenThresh;
    cmReal_t    attenGain; 
    cmReal_t    attenDlySec;
    cmReal_t    attenAtkSec;   

    const char* logFn;          // log file name or NULL if no file is to be written
    const char* levelFn;        // level file name or NULL if no file is to be written
    const char* specFn;         // spectrum file name or NULL if no file is to be written
    const char* attenFn;

  } cmFrqTrkArgs_t;

  typedef struct
  {
    bool     activeFl; 
    unsigned id;
    unsigned tN;   // age of this track in frames
    unsigned dN;   // count of consecutive times this ch has not connected 
    cmReal_t hz;   // current center frequency
    cmReal_t db;   // current magnitude

    cmReal_t* dbV; // dbV[]
    cmReal_t* hzV; // hzV[]  
    unsigned  si;
    unsigned  sn;
    
    cmReal_t db_mean;
    cmReal_t db_std;
    cmReal_t hz_mean;
    cmReal_t hz_std;

    cmReal_t score;

    cmFrqTrkAttenStateId_t state;
    int      attenPhsIdx;
    cmReal_t attenGain;
  } cmFrqTrkCh_t;

  struct cmBinMtxFile_str;

  typedef struct cmFrqTrk_str
  {
    cmObj         obj;
    cmFrqTrkArgs_t  a;
    cmFrqTrkCh_t*  ch;  // ch[ a.chCnt ]
    unsigned       hN;  // count of magnitude buffer frames 
    unsigned       sN;  // count of frames in channel statistics buffers
    unsigned       bN;  // count of bins in peak matrices
    cmReal_t*     dbM;  // dbM[ hN, bN ]
    unsigned       hi;  // next row of dbM to fill
    unsigned       fN;  // total count of frames processed.
    cmReal_t      binHz;

    cmReal_t*     dbV;
    unsigned*     pkiV;
    unsigned      deadN_max; // max. count of hops a tracker may fail to connect before being set to inactive
    unsigned      minTrkN;   // minimum track length in hops
    unsigned      nextTrkId;

    unsigned      newTrkCnt;
    unsigned      curTrkCnt;
    unsigned      deadTrkCnt;

    cmReal_t*     aV;
    int           attenDlyPhsMax;
    int           attenPhsMax;

    cmWhFilt*      wf;

    cmVectArray_t* logVa;
    cmVectArray_t* levelVa;
    cmVectArray_t* specVa;
    cmVectArray_t* attenVa;

    cmChar_t*      logFn;
    cmChar_t*      levelFn;
    cmChar_t*      specFn;
    cmChar_t*      attenFn;

  } cmFrqTrk;

  //
  // 1. Calculate the mean spectral magnitude profile over the last hN frames.
  // 2. Locate the peaks in the profile.
  // 3. Allow each active tracker to select the closest peak to extend its life.
  //     a) The distance between the trackers current location and a given
  //        peak is measured based on magnitude and frequency over time.
  //     b) There is a frequency range limit outside of which a given track-peak
  //        connection may not go.
  //     c) There is an amplitude threshold below which a track may not fall.

  cmFrqTrk* cmFrqTrkAlloc( cmCtx* c, cmFrqTrk* p, const cmFrqTrkArgs_t* a );
  cmRC_t    cmFrqTrkFree( cmFrqTrk** pp );
  cmRC_t    cmFrqTrkInit( cmFrqTrk* p, const cmFrqTrkArgs_t* a );
  cmRC_t    cmFrqTrkFinal( cmFrqTrk* p );
  cmRC_t    cmFrqTrkExec( cmFrqTrk* p, const cmReal_t* magV, const cmReal_t* phsV, const cmReal_t* hzV );
  void      cmFrqTrkPrint( cmFrqTrk* p );

  //------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    double   srate;
    unsigned binCnt;
    unsigned hopSmpCnt;
    unsigned bufMs;
    cmReal_t maxHz;
  } cmFbCtlArgs_t;

  typedef struct
  {
    cmObj          obj;
    cmFbCtlArgs_t  a;
    unsigned       binCnt;
    unsigned       frmCnt;
    cmReal_t*      bM;           // bM[ frmCnt, binCnt ];
    unsigned       bfi;          // current buffer frame (column) index
    unsigned       bfN;          // currrent count of frames in the buffer
    cmReal_t*      rmsV;         // rmsV[ frmCnt ];   
    cmReal_t*      sV;           // sV[ binCnt ]
    cmReal_t*      uV;
    cmVectArray_t* sva;
    cmVectArray_t* uva;
  } cmFbCtl_t;

  cmFbCtl_t* cmFbCtlAlloc( cmCtx* c, cmFbCtl_t* p, const cmFbCtlArgs_t* a );
  cmRC_t     cmFbCtlFree( cmFbCtl_t** pp );
  cmRC_t     cmFbCtlInit( cmFbCtl_t* p, const cmFbCtlArgs_t* a );
  cmRC_t     cmFbCtlFinal(cmFbCtl_t* p );
  cmRC_t     cmFbCtlExec( cmFbCtl_t* p, const cmReal_t* xV );

  //-----------------------------------------------------------------------------------------------------------------------

  typedef struct
  {
    cmObj obj;
    cmReal_t* rmsV;  // rmsV[rmsN]
    unsigned    rmsN;  // 
    unsigned    rmsIdx;//
    cmReal_t    rmsValue; // last RMS value
    cmSample_t* envV;  // envV[envN]
    unsigned    envN;  // atkSmp + rlsSmp;
    unsigned    threshN;
    unsigned    threshIdx;
    float       threshLvl;
    float       rlsLvl;
    unsigned    envIdx;
    double      gain;
    unsigned    atkCnt;
  } cmExpander;
  
  cmExpander* cmExpanderAlloc( cmCtx* c, cmExpander* p, double srate, unsigned procSmpCnt, double threshDb, double rlsDb, double threshMs, double rmsMs, double atkMs, double rlsMs );
  cmRC_t      cmExpanderFree(  cmExpander** pp );
  cmRC_t      cmExpanderInit( cmExpander* p, double srate, unsigned procSmpCnt, double threshDb, double rlsDb, double threshMs, double rmsMs, double atkMs, double rlsMs );
  cmRC_t      cmExpanderFinal( cmExpander* p );
  cmRC_t      cmExpanderExec( cmExpander* p, cmSample_t* x, cmSample_t* y, unsigned xyN );
  cmRC_t      cmExpanderExecD( cmExpander* p, double* x, double* y, unsigned xyN );
  //-----------------------------------------------------------------------------------------------------------------------
  typedef struct
  {
    cmObj obj;
    cmExpander** b;   // b[bandN]
    unsigned    bandN; 
    double      rmsValue;
    unsigned    atkCnt;
  } cmExpanderBank;


  cmExpanderBank* cmExpanderBankAlloc( cmCtx* c, cmExpanderBank* p, unsigned bandN, double srate, unsigned procSmpCnt, double threshDb, double rlsDb, double threshMs, double rmsMs, double atkMs, double rlsMs );
  cmRC_t      cmExpanderBankFree(  cmExpanderBank** pp );
  cmRC_t      cmExpanderBankInit(  cmExpanderBank* p, unsigned bandN, double srate, unsigned procSmpCnt, double threshDb, double rlsDb, double threshMs, double rmsMs, double atkMs, double rlsMs );
  cmRC_t      cmExpanderBankFinal( cmExpanderBank* p );
  cmRC_t      cmExpanderBankExec(  cmExpanderBank* p, cmSample_t* x, unsigned bandN );
  cmRC_t      cmExpanderBankExecD(  cmExpanderBank* p, double* x, unsigned bandN );
  

  //------------------------------------------------------------------------------------------------------------

  enum
  {
    kBypassModeSdId,  // 0 - no effect
    kBasicModeSdId,   // 1 - fixed thresh
    kSpecCentSdId,    // 2 - thresh = max magn - (offset * spec_cent)
    kAmpEnvSdId,      // 3 - thresh = max magn - offset
    kBumpSdId,
    kModeSdCnt
  };

  typedef struct
  {
    cmObj    obj;
    double   srate;
    unsigned wndSmpCnt;
    unsigned hopFcmt;
    unsigned hopSmpCnt;
    unsigned procSmpCnt;

    cmPvAnl*  pva;
    cmPvSyn*  pvs;

    cmFrqTrk* ft;
    cmFbCtl_t*  fbc;
    cmExpanderBank* exb;

    unsigned mode;
    double   thresh;

    double   uprSlope;
    double   lwrSlope;
    double   offset;
    bool     invertFl;

    double   spcBwHz;   // spectral centroid bandwidth in Hz
    double   spcSmArg;  // spectral centroid smoothing 
    double   spcMin;
    double   spcMax;
    unsigned spcBinCnt; // count of bins used in the spectral centroid 
    cmReal_t* hzV;    // hzV[spcBinCnt];
    cmReal_t  spc;

    unsigned  spcCnt;
    cmReal_t  spcSum;
    cmReal_t  spcSqSum;
   
    cmReal_t  aeSmMax;   // smoothed max bin magn - used by spectral centroid
    cmReal_t  aeSmOffs;  // smoothed offset 
    
    cmReal_t  ae;
    cmReal_t  aeMin;
    cmReal_t  aeMax;
    cmReal_t  aeUnit;

    cmReal_t ogain;

    unsigned phaseModIndex;

    unsigned       fi;          // total count of frames processed by cmSpecDistExec()
    unsigned       hN;
    unsigned       hi;
    cmReal_t*      iSpecM;      // iSpecMtx[hN binN]
    cmReal_t*      iSpecV;      // mean of rows of iSpecM 
    cmVectArray_t* iSpecVa;     
    cmReal_t*      oSpecM;      // oSpecMtx[hN binN]
    cmReal_t*      oSpecV;      // mean of rows of oSpecM
    cmVectArray_t* oSpecVa;

  } cmSpecDist_t;

  cmSpecDist_t*     cmSpecDistAlloc( cmCtx* ctx,cmSpecDist_t* ap, unsigned procSmpCnt, double srate, unsigned wndSmpCnt, unsigned hopFcmt, unsigned olaWndTypeId  ); 
  cmRC_t            cmSpecDistFree( cmSpecDist_t** pp );
  cmRC_t            cmSpecDistInit( cmSpecDist_t* p, unsigned procSmpCnt, double srate, unsigned wndSmpCnt, unsigned hopFcmt, unsigned olaWndTypeId  );
  cmRC_t            cmSpecDistFinal(cmSpecDist_t* p );
  cmRC_t            cmSpecDistExec( cmSpecDist_t* p, const cmSample_t* sp, unsigned sn );
  const cmSample_t* cmSpecDistOut(  cmSpecDist_t* p );


  //------------------------------------------------------------------------------------------------------------
  // Write a binary matrix file in the format acceppted by the octave function readBinFile.m

  typedef struct cmBinMtxFile_str
  {
    cmObj      obj;
    cmFileH_t  fh;
    unsigned   rowCnt;
    unsigned   maxRowEleCnt;
    unsigned   eleByteCnt;  
  } cmBinMtxFile_t;

  cmBinMtxFile_t* cmBinMtxFileAlloc( cmCtx* ctx, cmBinMtxFile_t* ap, const cmChar_t* fn );
  cmRC_t       cmBinMtxFileFree(  cmBinMtxFile_t** pp );
  cmRC_t       cmBinMtxFileInit(  cmBinMtxFile_t* p, const cmChar_t* fn );
  cmRC_t       cmBinMtxFileFinal( cmBinMtxFile_t* p );

  // Write one row of 'xn' columns to the matrix file.
  cmRC_t       cmBinMtxFileExecS( cmBinMtxFile_t* p, const cmSample_t* x, unsigned xn );
  cmRC_t       cmBinMtxFileExecR( cmBinMtxFile_t* p, const cmReal_t*   x, unsigned xn );

  bool         cmBinMtxFileIsValid( cmBinMtxFile_t* p );

  // Write a binary matrix file. 
  // The matrix data is provided as sp[rowCnt,colCnt] or rp[rowCnt,colCnt].
  // The matrix is assumed to be in column major order (like all matrices in the cm library)
  // Either 'sp' or 'rp' must be given but not both.
  // 'ctx' is optional and defaults to NULL.
  // If 'ctx' is not provided then 'rpt' must be provided.
  // If 'ctx' is provided then 'rpt' is not used.
  // See cmAudioFileReadWriteTest() in cmProcTest.c for an example usage.
  cmRC_t cmBinMtxFileWrite( const cmChar_t* fn, unsigned rowCnt, unsigned colCnt, const cmSample_t* sp, const cmReal_t* rp, cmCtx* ctx, cmRpt_t* rpt );
  
  // Return the matrix file geometry.
  // rowCntPtr,colCntPtr and eleByteCntPtr are optional
  cmRC_t cmBinMtxFileSize( cmCtx_t* ctx, const cmChar_t* fn, unsigned* rowCntPtr, unsigned* colCntPtr, unsigned* eleByteCntPtr );

  // Fill buf[rowCnt*colCnt*byteEleCnt] buffer from the binary matrix file 'fn'.
  // rowCnt,colCnt,eleByteCnt must be exactly the same as the actual file.
  // Use cmBinMtxFileSize() to determine the buffer size prior to calling this function.
  // colCntV[colCnt] is optional.
  cmRC_t cmBinMtxFileRead( cmCtx_t* ctx, const cmChar_t* fn, unsigned rowCnt, unsigned colCnt, unsigned eleByteCnt, void* buf, unsigned* colCntV );




#ifdef __cplusplus
}
#endif


#endif
