From 4d5e88e7663289fb9883502721813fbbaa769be1 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 21 Jul 2020 12:05:24 -0400
Subject: [PATCH 01/21] Makefile.am, clean.sh : Added '-lasound' to library
 build.  Made cleaning process more standard.

---
 Makefile.am    | 23 +++++++++++++----------
 build/clean.sh | 17 +++++++++--------
 2 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 099e3a8..c89c509 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -111,10 +111,10 @@ endif
 
 lib_LTLIBRARIES=
 include_HEADERS=
-
+AM_LDFLAGS=
 
 AM_CPPFLAGS = -D _GNU_SOURCE  -I.. -I$(srcdir)/src  -I$(srcdir)/src/dsp  -I$(srcdir)/src/vop  -I$(srcdir)/src/app
-AM_CFLAGS   = -Wno-multichar 
+AM_CFLAGS   = -Wall -Wno-multichar 
 AM_CXXFLAGS = 
 
 # debug/release switches
@@ -128,6 +128,7 @@ endif
 
 # Linux specific compiler flags
 if OS_LINUX
+  AM_LDFLAGS += -lasound  # why add this link flag? here's why: https://stackoverflow.com/questions/35480928/alsa-unexpected-results-when-called-from-shared-library
 if OS_64
   AM_CFLAGS  += -m64
 endif	
@@ -143,13 +144,15 @@ src_libcm_la_SOURCES   = $(cmSRC) $(cmHDR)
 include_HEADERS += $(cmHDR)
 lib_LTLIBRARIES += src/libcm.la
 
-
-# distclean-local sets the source tree back to it's minimal, pre-configure, state.
+# See: https://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Clean.html#Clean
+# 'distclean-local' is used by automake 'distclean' to perform customized local actions
+# ${exec_prefix} is the install prefix given to 'configure' by the user.
+# ${srcdir} is the directory of this Makefile and is set by autoconf.
 distclean-local:
 	rm -rf ${exec_prefix}/src        
-	rm -rf ${srcdir}/autom4te.cache
-	rm -rf ${srcdir}/build-aux
-	rm -f  ${srcdir}/m4/libtool.m4   ${srcdir}/m4/lt~obsolete.m4 ${srcdir}/m4/ltsugar.m4
-	rm -f  ${srcdir}/m4/ltversion.m4 ${srcdir}/m4/ltoptions.m4
-	rm -f  ${srcdir}/aclocal.m4      ${srcdir}/config.h.in ${srcdir}/config.h.in~
-	rm -f  ${srcdir}/Makefile.in     ${srcdir}/configure
+	rm -rf ${exec_prefix}/lib        
+	rm -rf ${exec_prefix}/include
+
+maintainer-clean-local:
+	${srcdir}/config.h.in~
+
diff --git a/build/clean.sh b/build/clean.sh
index 00fc879..f76078b 100755
--- a/build/clean.sh
+++ b/build/clean.sh
@@ -14,9 +14,9 @@ function clean_dir {
     
     rm -f  $1/bin/kc.app/Contents/MacOS/kc
     
-    rm -rf $1/include
-    rm -rf $1/lib
-    rm -rf $1/bin
+    #rm -rf $1/include
+    #rm -rf $1/lib
+    #rm -rf $1/bin
     rm -rf $1/.deps
     
 }
@@ -30,11 +30,12 @@ clean_dir osx/release
 
 rm -rf osx/debug/a.out.dSYM
 
-
-#rm -rf ../octave/results
-
-# remove all of emacs backup files (files ending width '~')
-# find ../ -name "*~" -exec rm {} \; 
+# delete everything created by 'autoreconf'.
+rm -rf ../build-aux
+rm -rf ../autom4te.cache
+rm -f  ../config.h.in ../config.h.in~ ../configure ../libtool.m4
+rm -f  ../Makefile.in ../aclocal.m4
+rm -f  ../m4/libtool.m4 ../m4/ltoptions.m4 ../m4/ltsugar.m4 ../m4/ltversion.m4 ../m4/lt~obsolete.m4
 
 
 

From 186f8156361f2fc1bacb48a1b21ab3757f8218a7 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 27 Jul 2020 17:36:43 -0400
Subject: [PATCH 02/21] cmAudDsp.h/c, cmAudioNrtDev.c,cmAudioSys.h : Audio
 devices are now set in aud_dsp.js by their name rather than their index.

---
 src/cmAudDsp.c      | 47 ++++++++++++++++++++++++++++++++++++++++++---
 src/cmAudDsp.h      |  3 ++-
 src/cmAudioNrtDev.c |  4 ++++
 src/cmAudioSys.h    | 22 +++++++++++----------
 4 files changed, 62 insertions(+), 14 deletions(-)

diff --git a/src/cmAudDsp.c b/src/cmAudDsp.c
index 4b97570..213a7dd 100644
--- a/src/cmAudDsp.c
+++ b/src/cmAudDsp.c
@@ -354,10 +354,15 @@ cmAdRC_t _cmAdParseSysJsonTree( cmAd_t* p )
     {
       cmAudioSysArgs_t*   asap        = &p->asCfgArray[i].cfg.ssArray[j].args;
       const cmJsonNode_t* argsNodePtr = cmJsonArrayElementC(ssArrayNodePtr,j);
+      
+      asap->inDevIdx    = cmInvalidIdx;
+      asap->outDevIdx   = cmInvalidIdx;
+      asap->inDevLabel  = NULL;
+      asap->outDevLabel = NULL;
 
       if((jsRC = cmJsonMemberValues( argsNodePtr, &errLabelPtr,
-          "inDevIdx",           kIntTId,  &asap->inDevIdx,
-          "outDevIdx",          kIntTId,  &asap->outDevIdx,
+          "inDevLabel",         kStringTId,  &asap->inDevLabel,
+          "outDevLabel",        kStringTId,  &asap->outDevLabel,
           "syncToInputFl",      kTrueTId, &asap->syncInputFl,
           "msgQueueByteCnt",    kIntTId,  &asap->msgQueueByteCnt,
           "devFramesPerCycle",  kIntTId,  &asap->devFramesPerCycle,
@@ -443,6 +448,35 @@ cmAdRC_t _cmAdCreateAggDevices( cmAd_t* p )
   return rc;
 }
 
+cmAdRC_t _cmAdResolveDeviceLabels( cmAd_t* p )
+{
+  cmAdRC_t rc = kOkAdRC;
+  unsigned i,j;
+  
+  // for each cmAsAudioSysCfg record in audioSysCfgArray[]
+  for(i=0; i<p->asCfgCnt; ++i)
+  {
+    // for each audio system sub-subsystem 
+    for(j=0; j<p->asCfgArray[i].cfg.ssCnt; ++j)
+    {
+      cmAudioSysArgs_t*   asap        = &p->asCfgArray[i].cfg.ssArray[j].args;
+      if((asap->inDevIdx = cmApDeviceLabelToIndex( asap->inDevLabel )) == cmInvalidId )
+      {
+        rc = cmErrMsg(&p->err,kInvalidAudioDevIdxAdRC,"The audio input device '%s' could not be found.", cmStringNullGuard(asap->inDevLabel));
+        goto errLabel;
+      }
+      
+      if((asap->outDevIdx = cmApDeviceLabelToIndex( asap->outDevLabel )) == cmInvalidId )
+      {
+        rc = cmErrMsg(&p->err,kInvalidAudioDevIdxAdRC,"The audio input device '%s' could not be found.", cmStringNullGuard(asap->inDevLabel));
+        goto errLabel;
+      }
+    }
+  }
+ errLabel:
+  return rc;
+}
+
 cmAdRC_t _cmAdCreateNrtDevices( cmAd_t* p )
 {
   cmAdRC_t rc = kOkAdRC;
@@ -721,10 +755,11 @@ cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbFunc, vo
   if((rc = _cmAdParseSysJsonTree(p)) != kOkAdRC )
     goto errLabel;
 
+ 
   // create the aggregate device
   if( _cmAdCreateAggDevices(p) != kOkAdRC )
     goto errLabel;
-
+  
   // create the non-real-time devices
   if( _cmAdCreateNrtDevices(p) != kOkAdRC )
     goto errLabel;
@@ -740,6 +775,12 @@ cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbFunc, vo
     goto errLabel;
   }
 
+  if( _cmAdResolveDeviceLabels(p) != kOkApRC )
+  {
+    rc = cmErrMsg(&p->err,kAudioPortFailAdRC,"Audio device labels could not be resolved..");
+    goto errLabel;
+  }
+
   // initialize the audio buffer
   if( cmApBufInitialize( cmApDeviceCount(), p->meterMs ) != kOkApRC )
   {
diff --git a/src/cmAudDsp.h b/src/cmAudDsp.h
index 152c94e..6e0c88b 100644
--- a/src/cmAudDsp.h
+++ b/src/cmAudDsp.h
@@ -27,7 +27,8 @@ extern "C" {
     kAggDevCreateFailAdRC,
     kNrtDevSysFailAdRC,
     kAfpDevSysFailAdRC,
-    kNetSysFailAdRC
+    kNetSysFailAdRC,
+    kInvalidAudioDevIdxAdRC
   };
 
 
diff --git a/src/cmAudioNrtDev.c b/src/cmAudioNrtDev.c
index c22f770..1408df3 100644
--- a/src/cmAudioNrtDev.c
+++ b/src/cmAudioNrtDev.c
@@ -189,6 +189,10 @@ cmApRC_t cmApNrtAllocate( cmRpt_t* rpt )
 cmApRC_t cmApNrtFree()
 {
   cmApRC_t rc = kOkApRC;
+  
+  if( _cmNrt == NULL )
+    return rc;
+  
   cmApNrtDev_t* dp = _cmNrt->devs;
   while( dp != NULL )
   {
diff --git a/src/cmAudioSys.h b/src/cmAudioSys.h
index 9a5b28e..7fc5c8d 100644
--- a/src/cmAudioSys.h
+++ b/src/cmAudioSys.h
@@ -130,16 +130,18 @@ extern "C" {
   // Audio device sub-sytem configuration record 
   typedef struct cmAudioSysArgs_str
   {
-    cmRpt_t*       rpt;               // system console object
-    unsigned       inDevIdx;          // input audio device
-    unsigned       outDevIdx;         // output audio device
-    bool           syncInputFl;       // true/false sync the DSP update callbacks with audio input/output
-    unsigned       msgQueueByteCnt;   // Size of the internal msg queue used to buffer msgs arriving via cmAudioSysDeliverMsg().
-    unsigned       devFramesPerCycle; // (512) Audio device samples per channel per device update buffer.
-    unsigned       dspFramesPerCycle; // (64)  Audio samples per channel per DSP cycle.
-    unsigned       audioBufCnt;       // (3)   Audio device buffers.
-    double         srate;             // Audio sample rate.
-    int            srateMult;         // Sample rate multiplication factor (negative for divide)
+    cmRpt_t*        rpt;               // system console object
+    const cmChar_t* inDevLabel;        // input audio device text label
+    const cmChar_t* outDevLabel;       // output audio device text label
+    unsigned        inDevIdx;          // input audio device index
+    unsigned        outDevIdx;         // output audio device index 
+    bool            syncInputFl;       // true/false sync the DSP update callbacks with audio input/output
+    unsigned        msgQueueByteCnt;   // Size of the internal msg queue used to buffer msgs arriving via cmAudioSysDeliverMsg().
+    unsigned        devFramesPerCycle; // (512) Audio device samples per channel per device update buffer.
+    unsigned        dspFramesPerCycle; // (64)  Audio samples per channel per DSP cycle.
+    unsigned        audioBufCnt;       // (3)   Audio device buffers.
+    double          srate;             // Audio sample rate.
+    int             srateMult;         // Sample rate multiplication factor (negative for divide)
   } cmAudioSysArgs_t;
 
   // Audio sub-system configuration record.

From 50f5d151455be056d095b709b83a26926f67bb5d Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 27 Jul 2020 17:39:16 -0400
Subject: [PATCH 03/21] cmDspBuiltIn.c : Added 'enable' to AudioOut, Added
 chIdx to WaveTable

---
 src/dsp/cmDspBuiltIn.c | 59 ++++++++++++++++++++++++++++++------------
 1 file changed, 43 insertions(+), 16 deletions(-)

diff --git a/src/dsp/cmDspBuiltIn.c b/src/dsp/cmDspBuiltIn.c
index 7864709..4f3dc02 100644
--- a/src/dsp/cmDspBuiltIn.c
+++ b/src/dsp/cmDspBuiltIn.c
@@ -1067,6 +1067,7 @@ enum
 {
   kChAoId,
   kGainAoId,
+  kEnableAoId,
   kInAoId
 };
 
@@ -1075,23 +1076,30 @@ cmDspClass_t _cmAudioOutDC;
 typedef struct
 {
   cmDspInst_t inst;
+  unsigned    onSymId;
+  unsigned    offSymId;  
 } cmDspAudioOut_t;
 
 cmDspInst_t*  _cmDspAudioOutAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
 {
   cmDspVarArg_t args[] =
   {
-    { "ch",  kChAoId,  0,      0, kInDsvFl | kUIntDsvFl   | kReqArgDsvFl, "Audio output channel index"},
-    { "gain",kGainAoId,0,      0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain multiplier"},  
-    { "in",  kInAoId,  0,      1, kInDsvFl | kAudioBufDsvFl, "Audio input" },
+    { "ch",    kChAoId,    0,      0, kInDsvFl | kUIntDsvFl   | kReqArgDsvFl, "Audio output channel index"},
+    { "gain",  kGainAoId,  0,      0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain multiplier"},
+    { "enable",kEnableAoId,0,      0, kInDsvFl | kSymDsvFl    | kOptArgDsvFl, "Enable: on off"},
+    { "in",    kInAoId,    0,      1, kInDsvFl | kAudioBufDsvFl, "Audio input" },
     { NULL, 0, 0, 0, 0 }
   };
 
   cmDspAudioOut_t* p = cmDspInstAlloc(cmDspAudioOut_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
 
+  p->offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
+  p->onSymId  = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
+
 
   cmDspSetDefaultUInt(   ctx, &p->inst, kChAoId,   0,   0);
   cmDspSetDefaultDouble( ctx, &p->inst, kGainAoId, 0, 1.0);
+  cmDspSetDefaultSymbol( ctx, &p->inst, kEnableAoId, p->onSymId );
 
   return &p->inst;
 }
@@ -1106,10 +1114,12 @@ cmDspRC_t _cmDspAudioOutReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
 
 cmDspRC_t _cmDspAudioOutExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
 {
-  cmDspRC_t         rc     = kOkDspRC;
-  unsigned          chIdx  = cmDspUInt(inst,kChAoId);
-  unsigned          oChCnt = ctx->ctx->oChCnt;
-  double            gain   = cmDspDouble(inst,kGainAoId);
+  cmDspRC_t        rc       = kOkDspRC;
+  cmDspAudioOut_t* p        = (cmDspAudioOut_t*)inst;
+  unsigned         chIdx    = cmDspUInt(inst,kChAoId);
+  bool             enableFl = cmDspSymbol(inst,kEnableAoId) == p->onSymId;
+  unsigned         oChCnt   = ctx->ctx->oChCnt;
+  double           gain     = cmDspDouble(inst,kGainAoId);
 
   if( chIdx >= oChCnt )
   {
@@ -1118,7 +1128,7 @@ cmDspRC_t _cmDspAudioOutExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_
     return rc;
   }
 
-  const cmSample_t* sp     = cmDspAudioBuf(ctx,inst,kInAoId,0);
+  const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInAoId,0);
 
   if( sp == NULL )
   {
@@ -1132,7 +1142,7 @@ cmDspRC_t _cmDspAudioOutExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_
   
   // if this channel is disabled or set to pass-through then chArray[chIdx] will be NULL
   if( ctx->ctx->oChArray[chIdx] != NULL )
-    cmVOS_MultVVS(ctx->ctx->oChArray[chIdx],n,sp,(cmSample_t)gain);
+    cmVOS_MultVVS(ctx->ctx->oChArray[chIdx],n,sp, (cmSample_t)(enableFl ? gain : 0));
 
   return kOkDspRC;
 }
@@ -1154,6 +1164,11 @@ cmDspRC_t _cmDspAudioOutRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_
     case kGainAoId:
       cmDspSetEvent(ctx,inst,evt);
       break;
+      
+    case kEnableAoId:
+      cmDspSetEvent(ctx,inst,evt);
+      break;
+
   }
   return rc;
 }
@@ -2907,6 +2922,7 @@ enum
   kLoopWtId,
   kBegWtId,
   kEndWtId,
+  kChWtId,
   kCmdWtId,
   kOtWtId,
   kGainWtId,
@@ -2951,6 +2967,7 @@ typedef struct
   cmAudioFileH_t afH;           // current audio file handle
   int            nxtBegSmpIdx;  // the beg/end sample index to use with the next filename to arrive at port 'fn'
   int            nxtEndSmpIdx;  //
+  unsigned       nxtChIdx;
   cmThreadH_t    thH;
   bool           loadFileFl;
   cmDspCtx_t*    ctx;
@@ -2976,6 +2993,7 @@ cmDspInst_t*  _cmDspWaveTableAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsi
     { "loop",   kLoopWtId,   0, 0, kInDsvFl  | kIntDsvFl  | kOptArgDsvFl, "-1=loop forever  >0=loop count (dflt:-1)"},
     { "beg",    kBegWtId,    0, 0, kInDsvFl  | kIntDsvFl  | kOptArgDsvFl, "File begin sample index" },
     { "end",    kEndWtId,    0, 0, kInDsvFl  | kIntDsvFl  | kOptArgDsvFl, "File end sample index (-1=play all)" },
+    { "ch",     kChWtId,     0, 0, kInDsvFl  | kUIntDsvFl | kOptArgDsvFl, "File channel index 0=left, 1=right" },
     { "cmd",    kCmdWtId,    0, 0, kInDsvFl  | kSymDsvFl  | kOptArgDsvFl, "Command: on off"},
     { "ot",     kOtWtId,     0, 0, kInDsvFl  | kUIntDsvFl | kOptArgDsvFl, "Overtone count"},
     { "gain",   kGainWtId,   0, 0, kInDsvFl  | kDoubleDsvFl|kOptArgDsvFl, "Gain"},
@@ -3005,6 +3023,7 @@ cmDspInst_t*  _cmDspWaveTableAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsi
   cmDspSetDefaultInt(   ctx, &p->inst, kLoopWtId,  0,    -1 );
   cmDspSetDefaultInt(   ctx, &p->inst, kBegWtId,   0,     0 );
   cmDspSetDefaultInt(   ctx, &p->inst, kEndWtId,   0,    -1 );
+  cmDspSetDefaultUInt(  ctx, &p->inst, kChWtId,    0,     0 );  
   cmDspSetDefaultSymbol(ctx, &p->inst, kCmdWtId,   p->onSymId );
   cmDspSetDefaultUInt(  ctx, &p->inst, kOtWtId,    0,     5 );
   cmDspSetDefaultDouble(ctx, &p->inst, kGainWtId,  0,     1.0 );
@@ -3038,10 +3057,9 @@ cmDspRC_t _cmDspWaveTableFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
 // mode then the the function will automatically begin reading from the begining of the
 // file segment.  If the end of the file segment is encountered and the wave table is not
 // in loop mode then the empty portion of wt[] will be set to zero.
-cmDspRC_t _cmDspWaveTableReadBlock( cmDspCtx_t* ctx, cmDspWaveTable_t* p, cmSample_t* wt, unsigned rdSmpCnt, int begSmpIdx, int endSmpIdx, int maxLoopCnt  )
+cmDspRC_t _cmDspWaveTableReadBlock( cmDspCtx_t* ctx, cmDspWaveTable_t* p, cmSample_t* wt, unsigned rdSmpCnt, unsigned chIdx, int begSmpIdx, int endSmpIdx, int maxLoopCnt  )
 {
   unsigned    actFrmCnt = 0;
-  unsigned    chIdx     = 0;
   unsigned    chCnt     = 1;
   unsigned    fn        = endSmpIdx - p->fi + 1; // count of samples between p->fi and endSmpIdx
   unsigned    n0        = rdSmpCnt;
@@ -3117,9 +3135,10 @@ cmDspRC_t _cmDspWaveTableReadAudioFile( cmDspCtx_t* ctx, cmDspWaveTable_t* p, un
 {
   unsigned    n0        = rdSmpCnt;
   unsigned    n1        = 0;
-  int         begSmpIdx = cmDspInt(&p->inst,kBegWtId);
-  int         endSmpIdx = cmDspInt(&p->inst,kEndWtId);
-  int         maxLoopCnt= cmDspInt(&p->inst,kLoopWtId);
+  int         begSmpIdx = cmDspInt( &p->inst,kBegWtId);
+  int         endSmpIdx = cmDspInt( &p->inst,kEndWtId);
+  unsigned    chIdx     = cmDspUInt(&p->inst,kChWtId);
+  int         maxLoopCnt= cmDspInt( &p->inst,kLoopWtId);
 
   if( endSmpIdx < begSmpIdx )
     endSmpIdx = p->fn-1;
@@ -3137,7 +3156,7 @@ cmDspRC_t _cmDspWaveTableReadAudioFile( cmDspCtx_t* ctx, cmDspWaveTable_t* p, un
   if( p->doneFl )
     cmVOS_Zero(p->wt + p->wti,n0);
   else
-    if( _cmDspWaveTableReadBlock(ctx, p, p->wt+p->wti, n0,begSmpIdx,endSmpIdx,maxLoopCnt  ) != kOkDspRC )
+    if( _cmDspWaveTableReadBlock(ctx, p, p->wt+p->wti, n0, chIdx, begSmpIdx,endSmpIdx,maxLoopCnt  ) != kOkDspRC )
       return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
 
   p->wtn -= n0;   // decrease the count of available samples
@@ -3149,7 +3168,7 @@ cmDspRC_t _cmDspWaveTableReadAudioFile( cmDspCtx_t* ctx, cmDspWaveTable_t* p, un
     if( p->doneFl )
       cmVOS_Zero(p->wt,n1);
     else
-      if( _cmDspWaveTableReadBlock(ctx, p, p->wt, n1,begSmpIdx,endSmpIdx,maxLoopCnt  ) != kOkDspRC )
+      if( _cmDspWaveTableReadBlock(ctx, p, p->wt, n1, chIdx, begSmpIdx,endSmpIdx,maxLoopCnt  ) != kOkDspRC )
         return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
 
     p->wtn -= n1;  // decrease the count of available samples
@@ -3382,6 +3401,7 @@ cmDspRC_t _cmDspWaveTableReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEv
 
   p->nxtBegSmpIdx = cmDspInt(&p->inst,kBegWtId);
   p->nxtEndSmpIdx = cmDspInt(&p->inst,kEndWtId);
+  p->nxtChIdx     = cmDspUInt(&p->inst,kChWtId);
 
   return _cmDspWaveTableCreateTable(ctx,p);
 
@@ -3490,6 +3510,7 @@ cmDspRC_t _cmDspWaveTableRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
           cmDspSetEvent(ctx,inst,evt);                       // set the file name variable
           cmDspSetInt(ctx,inst,kBegWtId,p->nxtBegSmpIdx);    // set the beg/end smp idx var's from the stored nxtBeg/EndSmpIdx values
           cmDspSetInt(ctx,inst,kEndWtId,p->nxtEndSmpIdx);    // 
+          cmDspSetUInt(ctx,inst,kChWtId, p->nxtChIdx);       // 
           cmDspSetUInt(ctx,inst,kShapeWtId,kFileWtId);       // switch to file mode 
           rc = _cmDspWaveTableCreateTable(ctx,p);            // reload the wavetable
         }
@@ -3506,6 +3527,11 @@ cmDspRC_t _cmDspWaveTableRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
       p->nxtEndSmpIdx = cmDsvGetInt(evt->valuePtr);
       break;
 
+    case kChWtId:
+      // store for next incoming file name msg
+      p->nxtChIdx = cmDsvGetUInt(evt->valuePtr);
+      break;
+      
     case kShapeWtId:
       if( cmDsvGetUInt(evt->valuePtr) < kShapeWtCnt )
       {
@@ -5546,6 +5572,7 @@ cmDspClassConsFunc_t _cmDspClassBuiltInArray[] =
   cm1UpClassCons,
   cmGateToSymClassCons,
   cmPortToSymClassCons,
+  cmIntToSymClassCons,
   cmRouterClassCons,
   cmAvailChClassCons,
 

From ebecf50417e6c7aa71a01acb52fe1b1287a07577 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 27 Jul 2020 17:40:01 -0400
Subject: [PATCH 04/21] cmDspFx.h/c : Added IntToSym

---
 src/dsp/cmDspFx.c | 177 +++++++++++++++++++++++++++++++++++++++++++++-
 src/dsp/cmDspFx.h |   1 +
 2 files changed, 177 insertions(+), 1 deletion(-)

diff --git a/src/dsp/cmDspFx.c b/src/dsp/cmDspFx.c
index 5b7a860..1353907 100644
--- a/src/dsp/cmDspFx.c
+++ b/src/dsp/cmDspFx.c
@@ -5587,6 +5587,7 @@ cmDspInst_t*  _cmDspPortToSym_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, uns
     // register the symbol
     symIdArray[i] = cmSymTblRegisterSymbol(ctx->stH,symLabel);
 
+    // input port - any msg in this port will generate an output from 'out' as well as the associated output port
     cmDspArgSetup(ctx, args+kBaseInPtsId+i, symLabel, cmInvalidId, kBaseInPtsId+i, 0, 0, kInDsvFl  | kTypeDsvMask, cmTsPrintfH(ctx->lhH,"%s Input.",symLabel) );
 
     cmDspArgSetup(ctx, args+baseOutPtsId+i, symLabel, cmInvalidId, baseOutPtsId+i, 0, 0, kOutDsvFl | kSymDsvFl,    cmTsPrintfH(ctx->lhH,"%s Output.",symLabel) );
@@ -5631,7 +5632,7 @@ cmDspRC_t _cmDspPortToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEv
     unsigned idx = evt->dstVarId - kBaseInPtsId;
     assert( idx < p->symIdCnt );
     cmDspSetSymbol(ctx,inst,p->baseOutPtsId + idx, p->symIdArray[idx]);
-    return cmDspSetSymbol(ctx,inst,kOutPtsId,p->symIdArray[ evt->dstVarId - kBaseInPtsId ]);
+    return cmDspSetSymbol(ctx,inst,kOutPtsId,      p->symIdArray[idx]);
   }
 
   return rc;
@@ -5652,6 +5653,180 @@ cmDspClass_t* cmPortToSymClassCons( cmDspCtx_t* ctx )
   return &_cmPortToSym_DC;
 }
 
+//------------------------------------------------------------------------------------------------------------
+//)
+//( { label:cmDspIntToSym file_desc:"Send a pre-defined symbol every time a message arrives a given input port." kw:[sunit] }
+enum
+{
+ kInItsId,
+ kOutItsId,
+ kBaseInItsId
+};
+
+cmDspClass_t _cmIntToSym_DC;
+
+typedef struct
+{
+  cmDspInst_t inst;
+  int*        intArray;
+  unsigned*   symIdArray;
+  unsigned    symIdCnt;
+  unsigned    baseIntItsId;
+  unsigned    baseOutItsId;
+  
+} cmDspIntToSym_t;
+
+cmDspInst_t*  _cmDspIntToSym_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
+{
+  va_list       vl1;
+  va_copy(vl1,vl);
+
+  if( va_cnt < 2 || va_cnt % 2 !=0  )
+  {
+    va_end(vl1);
+    cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'IntToSym' constructor argument list must contain at least one int/symbol pair and all pairs must be complete.");
+    return NULL;
+  }
+
+  unsigned      symCnt    = va_cnt/2;
+  unsigned      argCnt    = 2 + 3*symCnt;
+  cmDspVarArg_t args[argCnt+1];
+
+  unsigned* symIdArray   = cmMemAllocZ(unsigned,symCnt);
+  int*      intArray     = cmMemAllocZ(int,symCnt);
+  unsigned  baseIntItsId = kBaseInItsId + symCnt;  
+  unsigned  baseOutItsId = baseIntItsId + symCnt;
+
+  // setup the integer input and symbol output port arg recd
+  cmDspArgSetup(ctx,args,  "in",  cmInvalidId, kInItsId,  0, 0, kInDsvFl  | kIntDsvFl, "Integer input" );
+  cmDspArgSetup(ctx,args+1,"out", cmInvalidId, kOutItsId, 0, 0, kOutDsvFl | kSymDsvFl, "Output" );
+
+  unsigned i;
+
+  for(i=0; i<symCnt; ++i)
+  {
+    // get the integer value
+    intArray[i] = va_arg(vl,int);
+    
+    // get the symbol label
+    const cmChar_t* symLabel = va_arg(vl,const char*);
+    assert( symLabel != NULL );
+
+    unsigned intLabelN = (symLabel==NULL ? 0 : strlen(symLabel)) + 5;
+    cmChar_t intLabel[ intLabelN ];
+    snprintf(intLabel,intLabelN,"%s%s", symLabel==NULL ? "" : symLabel, "-int" );
+
+    // register the symbol
+    symIdArray[i] = cmSymTblRegisterSymbol(ctx->stH,symLabel);
+
+    // trigger port associated with this symbol (any msg on this port will trigger an output)
+    cmDspArgSetup(ctx, args+kBaseInItsId+i, symLabel, cmInvalidId, kBaseInItsId+i, 0, 0, kInDsvFl  | kTypeDsvMask, cmTsPrintfH(ctx->lhH,"%s Input.",symLabel) );
+
+    // this port is used to set the integer value associated with this symbol
+    cmDspArgSetup(ctx, args+baseIntItsId+i, intLabel, cmInvalidId, baseIntItsId+i, 0, 0, kInDsvFl  | kIntDsvFl,  cmTsPrintfH(ctx->lhH,"Set the integer value associated with %s.",symLabel) );
+    
+    // symbol output port - when ever this symbol is sent out it will go out this port as well as the 'out' port
+    cmDspArgSetup(ctx, args+baseOutItsId+i, symLabel, cmInvalidId, baseOutItsId+i, 0, 0, kOutDsvFl | kSymDsvFl,    cmTsPrintfH(ctx->lhH,"%s Output.",symLabel) );
+
+    
+  }
+
+  cmDspArgSetupNull(args + argCnt);
+
+  cmDspIntToSym_t* p = cmDspInstAlloc(cmDspIntToSym_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl1);
+
+  p->symIdCnt     = symCnt;
+  p->intArray     = intArray;
+  p->symIdArray   = symIdArray;
+  p->baseOutItsId = baseOutItsId;
+  p->baseIntItsId = baseIntItsId;
+
+  cmDspSetDefaultSymbol(ctx,&p->inst,kOutItsId,cmInvalidId);
+
+  va_end(vl1);
+
+  return &p->inst;
+}
+cmDspRC_t _cmDspIntToSym_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
+{
+  cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst;
+  cmMemFree(p->symIdArray);
+  return kOkDspRC;
+}
+
+cmDspRC_t _cmDspIntToSym_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
+{
+  return cmDspApplyAllDefaults(ctx,inst);
+}
+
+cmDspRC_t _cmDspIntToSymSendOut( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned idx )
+{
+  cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst;
+  assert( idx < p->symIdCnt );
+  cmDspSetSymbol(ctx,inst,p->baseOutItsId + idx, p->symIdArray[idx]);
+  return cmDspSetSymbol(ctx, inst, kOutItsId, p->symIdArray[ idx ]);
+}
+
+
+cmDspRC_t _cmDspIntToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
+{ 
+  cmDspRC_t    rc = kOkDspRC;
+  cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst;
+  unsigned idx = cmInvalidIdx;
+
+  // if an integer arrived at 'in'
+  if( evt->dstVarId == kInItsId )
+  {
+    unsigned i;
+    int      intVal = cmDspInt(inst,kInItsId);
+    
+    for(i=0; i<p->symIdCnt; ++i)
+      if( intVal == p->intArray[i] )
+      {
+        rc = _cmDspIntToSymSendOut( ctx, inst, idx );
+        idx = i;
+        break;
+      } 
+  }
+  else
+  {  
+    // if a msg of any type is recieved on an input port - send out the associated symbol
+    if( kBaseInItsId <= evt->dstVarId && evt->dstVarId < kBaseInItsId + p->symIdCnt )
+    {
+      _cmDspIntToSymSendOut( ctx, inst, evt->dstVarId - kBaseInItsId );
+    }
+    else
+      
+      // if this is a new interger value for this symbol
+      if( p->baseIntItsId <= evt->dstVarId && evt->dstVarId < p->baseIntItsId + p->symIdCnt )
+      {
+        cmDspSetEvent(ctx,inst,evt);
+
+        int x = cmDspInt( inst, evt->dstVarId );
+        printf("%i %i\n", x, p->symIdArray[evt->dstVarId - p->baseIntItsId] );
+        p->intArray[ evt->dstVarId - p->baseIntItsId ] = x;
+      }
+  }
+
+  
+  return rc;
+}
+
+cmDspClass_t* cmIntToSymClassCons( cmDspCtx_t* ctx )
+{
+  cmDspClassSetup(&_cmIntToSym_DC,ctx,"IntToSym",
+    NULL,
+    _cmDspIntToSym_Alloc,
+    _cmDspIntToSym_Free,
+    _cmDspIntToSym_Reset,
+    NULL,
+    _cmDspIntToSym_Recv,
+    NULL,NULL,
+    "If a message of any kind is received on a port then send the symbol associated with the port.");
+
+  return &_cmIntToSym_DC;
+}
+
 //------------------------------------------------------------------------------------------------------------
 //)
 //( { label:cmDspRouter file_desc:"Route the input value to one of multiple output ports." kw:[sunit] }
diff --git a/src/dsp/cmDspFx.h b/src/dsp/cmDspFx.h
index fe84570..c5af6bb 100644
--- a/src/dsp/cmDspFx.h
+++ b/src/dsp/cmDspFx.h
@@ -36,6 +36,7 @@ extern "C" {
   struct cmDspClass_str* cm1UpClassCons(        cmDspCtx_t* ctx );
   struct cmDspClass_str* cmGateToSymClassCons(  cmDspCtx_t* ctx );
   struct cmDspClass_str* cmPortToSymClassCons(  cmDspCtx_t* ctx );
+  struct cmDspClass_str* cmIntToSymClassCons(   cmDspCtx_t* ctx );  
   struct cmDspClass_str* cmRouterClassCons(     cmDspCtx_t* ctx );
   struct cmDspClass_str* cmAvailChClassCons(    cmDspCtx_t* ctx );
   struct cmDspClass_str* cmPresetClassCons(     cmDspCtx_t* ctx );

From 003a7deddf2de9cc4799f81fbef4a3ca512cf9b3 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 27 Jul 2020 17:41:34 -0400
Subject: [PATCH 05/21] cmDspPgm.h/c : Added IntToSym example.  Added initial
 set of shortened macros for programming.

---
 src/cmPP_NARG.h    | 45 ++++++++++++++++++++++++++++
 src/dsp/cmDspPgm.c | 75 ++++++++++++++++++++++++++++++++++++----------
 src/dsp/cmDspSys.h | 15 ++++++++--
 3 files changed, 117 insertions(+), 18 deletions(-)
 create mode 100644 src/cmPP_NARG.h

diff --git a/src/cmPP_NARG.h b/src/cmPP_NARG.h
new file mode 100644
index 0000000..edf54ec
--- /dev/null
+++ b/src/cmPP_NARG.h
@@ -0,0 +1,45 @@
+#ifndef cmPP_NARG_H
+#define cmPP_NARG_H
+
+
+// Taken from here:
+// https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s
+// and here:
+// https://stackoverflow.com/questions/4421681/how-to-count-the-number-of-arguments-passed-to-a-function-that-accepts-a-variabl
+
+#define PP_NARG(...) \
+         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
+
+#define PP_NARG_(...) \
+         PP_128TH_ARG(__VA_ARGS__)
+
+#define PP_128TH_ARG( \
+          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
+         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
+         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
+         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
+         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
+         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
+         _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
+         _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
+         _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
+         _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
+         _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
+         _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
+         _121,_122,_123,_124,_125,_126,_127,N,...) N
+#define PP_RSEQ_N() \
+         127,126,125,124,123,122,121,120, \
+         119,118,117,116,115,114,113,112,111,110, \
+         109,108,107,106,105,104,103,102,101,100, \
+         99,98,97,96,95,94,93,92,91,90, \
+         89,88,87,86,85,84,83,82,81,80, \
+         79,78,77,76,75,74,73,72,71,70, \
+         69,68,67,66,65,64,63,62,61,60, \
+         59,58,57,56,55,54,53,52,51,50, \
+         49,48,47,46,45,44,43,42,41,40, \
+         39,38,37,36,35,34,33,32,31,30, \
+         29,28,27,26,25,24,23,22,21,20, \
+         19,18,17,16,15,14,13,12,11,10, \
+         9,8,7,6,5,4,3,2,1,0
+
+#endif
diff --git a/src/dsp/cmDspPgm.c b/src/dsp/cmDspPgm.c
index 897ffe6..61c1606 100644
--- a/src/dsp/cmDspPgm.c
+++ b/src/dsp/cmDspPgm.c
@@ -21,6 +21,7 @@
 #include "cmTime.h"
 #include "cmAudioSys.h"
 #include "cmProcObj.h"
+#include "cmPP_NARG.h"
 #include "cmDspCtx.h"
 #include "cmDspClass.h"
 #include "cmDspSys.h"
@@ -2870,36 +2871,77 @@ cmDspRC_t _cmDspSysPgm_PortToSym( cmDspSysH_t h, void** userPtrPtr )
 {
   cmDspRC_t rc = kOkDspRC;
 
-  cmDspInst_t* btn0  = cmDspSysAllocButton( h, "Btn0", 0.0 );
-  cmDspInst_t* btn1  = cmDspSysAllocButton( h, "Btn1", 0.0 );
-  cmDspInst_t* btn2  = cmDspSysAllocButton( h, "Btn2", 0.0 );  
+  inst_t* btn0  = button( "Btn0", 0.0 );
+  inst_t* btn1  = button( "Btn1", 0.0 );
+  inst_t* btn2  = button( "Btn2", 0.0 );  
 
-  cmDspInst_t* pts   = cmDspSysAllocInst( h, "PortToSym", NULL, 3, "one", "two", "three");
+  inst_t* pts   = inst( "PortToSym", NULL, "one", "two", "three");
 
-  cmDspInst_t* pr0  = cmDspSysAllocInst( h, "Printer", NULL, 1, "0:" );
-  cmDspInst_t* pr1  = cmDspSysAllocInst( h, "Printer", NULL, 1, "1:" );
+  inst_t* pr0  = inst( "Printer", NULL,  "sym:" );
+  inst_t* pr1  = inst( "Printer", NULL,  "btn:" );
+  inst_t* pr2  = inst( "Printer", NULL,  "out:" );
 
   // check for allocation errors
   if((rc = cmDspSysLastRC(h)) != kOkDspRC )
     goto errLabel;
   
-  cmDspSysInstallCb(   h, btn0, "out", pts, "one",NULL);
-  cmDspSysInstallCb(   h, btn1, "out", pts, "two",NULL);
-  cmDspSysInstallCb(   h, btn2, "out", pts, "three",NULL);
+  event( btn0, out, pts, one );
+  event( btn1, out, pts, two );
+  event( btn2, out, pts, three );
 
-  cmDspSysInstallCb(   h, btn0, "out", pr1, "in",NULL);
-  cmDspSysInstallCb(   h, btn1, "out", pr1, "in",NULL);
-  cmDspSysInstallCb(   h, btn2, "out", pr1, "in",NULL);
-
-  cmDspSysInstallCb(   h, pts,  "one",   pr0, "in", NULL );
-  cmDspSysInstallCb(   h, pts,  "two",   pr0, "in", NULL );
-  cmDspSysInstallCb(   h, pts,  "three", pr0, "in", NULL );
+  event( btn0, out, pr1, in );
+  event( btn1, out, pr1, in );
+  event( btn2, out, pr1, in );
 
+  event( pts,  one,   pr0, in );
+  event( pts,  two,   pr0, in );
+  event( pts,  three, pr0, in );
+  event( pts,  out,   pr2, in );
 
  errLabel:
   return rc;
 }
 
+//------------------------------------------------------------------------------
+//)
+//( { label:cmDspPgm_IntToSym file_desc:"IntToSym example program." kw:[spgm] }
+cmDspRC_t _cmDspSysPgm_IntToSym( cmDspSysH_t h, void** userPtrPtr )
+{
+  cmDspRC_t rc = kOkDspRC;
+
+  inst_t* btn0  = button( "Btn0", 0.0 );
+  inst_t* btn1  = button( "Btn1", 1.0 );
+  inst_t* btn2  = button( "Btn2", 2.0 );  
+
+  inst_t* pts   = inst( "IntToSym", NULL, 1, "one", 2, "two", 3, "three");
+
+  inst_t* pr0  = inst( "Printer", NULL,  "btn:" );
+  inst_t* pr1  = inst( "Printer", NULL,  "sym:" );
+  inst_t* pr2  = inst( "Printer", NULL,  "out:" );
+
+  // check for allocation errors
+  if((rc = cmDspSysLastRC(h)) != kOkDspRC )
+    goto errLabel;
+  
+  event( btn0, out, pts, one );
+  event( btn1, out, pts, two );
+  event( btn2, out, pts, three );
+
+  event( btn0, out, pr0, in );
+  event( btn1, out, pr0, in );
+  event( btn2, out, pr0, in );
+
+  event( pts,  one,   pr1, in );
+  event( pts,  two,   pr1, in );
+  event( pts,  three, pr1, in );
+
+  event( pts, out, pr2, in );
+  
+ errLabel:
+  return rc;
+}
+
+
 //------------------------------------------------------------------------------
 //)
 //( { label:cmDspPgm_Line file_desc:"Line generator example program." kw:[spgm] }
@@ -3288,6 +3330,7 @@ _cmDspSysPgm_t _cmDspSysPgmArray[] =
   { "line",              _cmDspSysPgm_Line,           NULL, NULL },
   { "1Up",               _cmDspSysPgm_1Up,            NULL, NULL },
   { "PortToSym",         _cmDspSysPgm_PortToSym,      NULL, NULL },
+  { "IntToSym",          _cmDspSysPgm_IntToSym,       NULL, NULL },
   { "preset",            _cmDspSysPgm_Preset,         NULL, NULL },
   { "rsrcWr",            _cmDspSysPgm_RsrcWr,         NULL, NULL },
   { "router",            _cmDspSysPgm_Router,         NULL, NULL },
diff --git a/src/dsp/cmDspSys.h b/src/dsp/cmDspSys.h
index 28fef38..5d78501 100644
--- a/src/dsp/cmDspSys.h
+++ b/src/dsp/cmDspSys.h
@@ -109,8 +109,7 @@ extern "C" {
   cmDspRC_t    cmDspSysInsertHorzBorder( cmDspSysH_t h );
 
   cmDspRC_t    cmDspSysNewPage( cmDspSysH_t h, const cmChar_t* title );
-
-
+  
   //----------------------------------------------------------------------------------------------------
   // Connection Functions.
   //
@@ -278,6 +277,18 @@ extern "C" {
 
   //)
 
+  typedef cmDspInst_t inst_t;
+  
+#define inst( classLabel, instLabel, ... )       cmDspSysAllocInst( h, classLabel, instLabel, PP_NARG(__VA_ARGS__), __VA_ARGS__ )
+#define button( label, real_val )                cmDspSysAllocButton(  h, label, real_val )
+#define scalar( label, rmin, rmax, rstep, rval ) cmDspSysAllocScalar(  h, label, rmin, rmax, rstep, rval )
+
+  
+#define audio( src, sarg, dst, darg )      cmDspSysConnectAudio( h, src, #sarg, dst, #darg )
+#define event( src, sarg, dst, darg )      cmDspSysInstallCb( h, src, #sarg, dst, #darg, NULL )
+
+  
+  
 #ifdef __cplusplus
   }
 #endif

From 77367919b7c8aa2cb02ba639aca3dc73bf260fd0 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 27 Jul 2020 19:38:57 -0400
Subject: [PATCH 06/21] .gitignore : updated to ignore files based on new make
 style.

---
 .gitignore | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/.gitignore b/.gitignore
index d07bba3..badd313 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,20 @@
 Makefile.in
 .DS_Store
+aclocal.m4
+config.h.in
+config.h.in~
+configure
+Makefile.in
+
+autom4te.cache
+build-aux
+
+m4/libtool.m4
+m4/lt~obsolete.m4
+m4/libtool.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
+m4/.DS_Store
 

From b16673bbc09d892d3646ff6937db541feb86e2e7 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 28 Jul 2020 14:09:04 -0400
Subject: [PATCH 07/21] cmDspFx.c,cmDspPgm.c: Finalized IntToSym.

---
 src/dsp/cmDspFx.c  | 10 ++++------
 src/dsp/cmDspPgm.c | 26 +++++++++++++-------------
 2 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/src/dsp/cmDspFx.c b/src/dsp/cmDspFx.c
index 1353907..67252a5 100644
--- a/src/dsp/cmDspFx.c
+++ b/src/dsp/cmDspFx.c
@@ -5772,19 +5772,19 @@ cmDspRC_t _cmDspIntToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
 { 
   cmDspRC_t    rc = kOkDspRC;
   cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst;
-  unsigned idx = cmInvalidIdx;
 
   // if an integer arrived at 'in'
   if( evt->dstVarId == kInItsId )
   {
+    cmDspSetEvent(ctx,inst,evt);
+
     unsigned i;
     int      intVal = cmDspInt(inst,kInItsId);
     
     for(i=0; i<p->symIdCnt; ++i)
       if( intVal == p->intArray[i] )
       {
-        rc = _cmDspIntToSymSendOut( ctx, inst, idx );
-        idx = i;
+        rc = _cmDspIntToSymSendOut( ctx, inst, i );
         break;
       } 
   }
@@ -5802,9 +5802,7 @@ cmDspRC_t _cmDspIntToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt
       {
         cmDspSetEvent(ctx,inst,evt);
 
-        int x = cmDspInt( inst, evt->dstVarId );
-        printf("%i %i\n", x, p->symIdArray[evt->dstVarId - p->baseIntItsId] );
-        p->intArray[ evt->dstVarId - p->baseIntItsId ] = x;
+        p->intArray[ evt->dstVarId - p->baseIntItsId ] = cmDspInt( inst, evt->dstVarId );
       }
   }
 
diff --git a/src/dsp/cmDspPgm.c b/src/dsp/cmDspPgm.c
index 61c1606..6725238 100644
--- a/src/dsp/cmDspPgm.c
+++ b/src/dsp/cmDspPgm.c
@@ -2909,32 +2909,32 @@ cmDspRC_t _cmDspSysPgm_IntToSym( cmDspSysH_t h, void** userPtrPtr )
 {
   cmDspRC_t rc = kOkDspRC;
 
-  inst_t* btn0  = button( "Btn0", 0.0 );
-  inst_t* btn1  = button( "Btn1", 1.0 );
-  inst_t* btn2  = button( "Btn2", 2.0 );  
+  inst_t* sel0   = scalar( "Sel0", 0.0, 10.0, 1.0, 1.0 );
+  inst_t* sel1   = scalar( "Sel1", 0.0, 10.0, 1.0, 1.0 );
+  inst_t* sel2   = scalar( "Sel2", 0.0, 10.0, 1.0, 1.0 );
+  inst_t* val    = scalar( "Val",  0.0, 10.0, 1.0, 1.0 );
 
-  inst_t* pts   = inst( "IntToSym", NULL, 1, "one", 2, "two", 3, "three");
+  inst_t* pts   = inst( "IntToSym", NULL, 0, "one", 0, "two", 0, "three");
 
-  inst_t* pr0  = inst( "Printer", NULL,  "btn:" );
+  inst_t* pr0  = inst( "Printer", NULL,  "val:" );
   inst_t* pr1  = inst( "Printer", NULL,  "sym:" );
   inst_t* pr2  = inst( "Printer", NULL,  "out:" );
 
   // check for allocation errors
   if((rc = cmDspSysLastRC(h)) != kOkDspRC )
     goto errLabel;
-  
-  event( btn0, out, pts, one );
-  event( btn1, out, pts, two );
-  event( btn2, out, pts, three );
 
-  event( btn0, out, pr0, in );
-  event( btn1, out, pr0, in );
-  event( btn2, out, pr0, in );
+  event( sel0, val, pts, one-int );
+  event( sel1, val, pts, two-int );
+  event( sel2, val, pts, three-int );
+  
+  event( val, val, pts, in );
+  event( val, val, pr0, in );
 
   event( pts,  one,   pr1, in );
   event( pts,  two,   pr1, in );
   event( pts,  three, pr1, in );
-
+  
   event( pts, out, pr2, in );
   
  errLabel:

From 6c8ab0a131f4b570d305986d40830d04085bd37f Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 28 Jul 2020 14:10:40 -0400
Subject: [PATCH 08/21] cmDspPgmKrTimeLineLiteAf.c : Added stop location and
 wet,dry master gain.

---
 src/dsp/cmDspPgmKrTimeLineLiteAf.c | 141 +++++++++++++++--------------
 1 file changed, 75 insertions(+), 66 deletions(-)

diff --git a/src/dsp/cmDspPgmKrTimeLineLiteAf.c b/src/dsp/cmDspPgmKrTimeLineLiteAf.c
index 100fda6..91e5137 100644
--- a/src/dsp/cmDspPgmKrTimeLineLiteAf.c
+++ b/src/dsp/cmDspPgmKrTimeLineLiteAf.c
@@ -62,27 +62,22 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   //int baseAudioInCh =  0; // 2;
   int baseAudioOutCh = 0;//  2;
 
-  //cmDspInst_t* ai0 = cmDspSysAllocInst(h,"AudioIn",     NULL,  1, baseAudioInCh + 0);
-  //cmDspInst_t* ai1 = cmDspSysAllocInst(h,"AudioIn",     NULL,  1, baseAudioInCh + 1);
-  //cmDspInst_t* mip = cmDspSysAllocInst(h,"MidiIn",      NULL,  2, "MOTU - Traveler mk3", "MIDI Port");
-  //cmDspInst_t* mip = cmDspSysAllocInst(h,"MidiIn",      NULL,  2, "Apple Inc. - IAC Driver", "Bus 1");
   
   cmDspInst_t* tlp  = cmDspSysAllocInst(h,"TimeLine",    "tl",  2, r.tlFn, r.tlPrefixPath );
   cmDspInst_t* scp  = cmDspSysAllocInst(h,"Score",       "sc",  1, r.scFn );
   cmDspInst_t* pts  = cmDspSysAllocInst(h,"PortToSym",   NULL,  2, "on", "off" );
 
-  cmDspInst_t* php =  cmDspSysAllocInst(h,"Phasor",   NULL,  0 );
-  cmDspInst_t* wtp =  cmDspSysAllocInst(h,"WaveTable",NULL,  2, ((int)cmDspSysSampleRate(h)), 1 );
+  cmDspInst_t* php =  cmDspSysAllocInst(h,"Phasor",    NULL,  0 );
+  cmDspInst_t* wt0 =  cmDspSysAllocInst(h,"WaveTable", NULL,  7, ((int)cmDspSysSampleRate(h)), 1, NULL, -1, 0, -1, 0 );
+  cmDspInst_t* wt1 =  cmDspSysAllocInst(h,"WaveTable", NULL,  7, ((int)cmDspSysSampleRate(h)), 1, NULL, -1, 0, -1, 1 );
 
   
   cmDspInst_t* mfp  = cmDspSysAllocInst(h,"MidiFilePlay",NULL,  0 );
   cmDspInst_t* nmp  = cmDspSysAllocInst(h,"NanoMap",     NULL,  0 );
-  //cmDspInst_t* pic  = cmDspSysAllocInst(h,"Picadae",     NULL,  0 );
-  cmDspInst_t* mop  = cmDspSysAllocInst(h,"MidiOut",     NULL,  2, "Fastlane","Fastlane MIDI A" );
-  cmDspInst_t* mo2p = cmDspSysAllocInst(h,"MidiOut",     NULL,  2, "Fastlane","Fastlane MIDI B");
   cmDspInst_t* sfp  = cmDspSysAllocInst(h,"ScFol",       NULL,  5, r.scFn, sfBufCnt, sfMaxWndCnt, sfMinVel, sfEnaMeasFl );
   cmDspInst_t* amp  = cmDspSysAllocInst(h,"ActiveMeas",  NULL,  1, 100 );
   cmDspInst_t* modp = cmDspSysAllocInst(h,"ScMod",       NULL,  2, r.modFn, "m1" );
+  cmDspInst_t* its  = cmDspSysAllocInst(h,"IntToSym",    NULL,  2, 0, "off");
  
   unsigned   preGrpSymId     = cmDspSysPresetRegisterGroup(h,"tl");
   unsigned   cmpPreGrpSymId  = cmDspSysPresetRegisterGroup(h,"tl_cmp"); 
@@ -95,14 +90,14 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspSysNewPage(h,"Controls-1");
   _cmDspSys_TlXformChain(h, &c1, preGrpSymId, cmpPreGrpSymId, amp, modp, 1, 1 );
 
+  cmDspInst_t* lmix = cmDspSysAllocInst(h, "AMix",      NULL, 1, 2 );
+  cmDspInst_t* rmix = cmDspSysAllocInst(h, "AMix",      NULL, 1, 2 );
 
-  cmDspInst_t* ao0 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+2 ); // 4 Piano     1 Output
-  cmDspInst_t* ao1 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+3 ); // 5          2
-  cmDspInst_t* ao2 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+0 ); // 2 Transform 1 OUtput
-  cmDspInst_t* ao3 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+1 ); // 3          2
+  cmDspInst_t* ao0 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+0 ); 
+  cmDspInst_t* ao1 = cmDspSysAllocInst(h,"AudioOut",    NULL,   1, baseAudioOutCh+1 ); 
 
   cmDspSysNewPage(h,"Main");
-  cmDspInst_t* notesOffb= cmDspSysAllocInst(h,"Button", "notesOff",   2, kButtonDuiId, 1.0 );
+  //cmDspInst_t* notesOffb= cmDspSysAllocInst(h,"Button", "notesOff",   2, kButtonDuiId, 1.0 );
   cmDspInst_t* onb     = cmDspSysAllocInst(h,"Button", "start",   2, kButtonDuiId, 1.0 );
   cmDspInst_t* offb    = cmDspSysAllocInst(h,"Button", "stop",    2, kButtonDuiId, 1.0 );
   cmDspInst_t* mod_sel = cmDspSysAllocMsgList(h, NULL, "mod_sel", 1 );
@@ -115,11 +110,7 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   // Record <-> Live switches
   cmDspInst_t* tlRt  = cmDspSysAllocInst(h,"Router", NULL, 2, 2, 0);  // time line swich
   cmDspInst_t* mfpRt = cmDspSysAllocInst(h,"Router", NULL, 2, 2, 0);
-  //cmDspInst_t* amRt  = cmDspSysAllocInst(h,"Router", NULL, 2, 2, 0);
 
-  //cmDspSysNewColumn(h,0);
-  //cmDspInst_t* igain0 = cmDspSysAllocInst(h,"Scalar", "In Gain-0",    5, kNumberDuiId, 0.0,   100.0,0.01,   1.0 );  
-  //cmDspInst_t* igain1 = cmDspSysAllocInst(h,"Scalar", "In Gain-1",    5, kNumberDuiId, 0.0,   100.0,0.01,   1.0 );  
 
   //cmDspSysNewColumn(h,0);
   cmDspInst_t* ogain0 = cmDspSysAllocInst(h,"Scalar", "Dry Out Gain-0",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
@@ -127,6 +118,16 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspInst_t* ogain2 = cmDspSysAllocInst(h,"Scalar", "Wet Out Gain-2",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
   cmDspInst_t* ogain3 = cmDspSysAllocInst(h,"Scalar", "Wet Out Gain-3",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
 
+  cmDspInst_t* ogainW = cmDspSysAllocInst(h,"Scalar", "Wet Master",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
+  cmDspInst_t* ogainD = cmDspSysAllocInst(h,"Scalar", "Dry Master",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
+
+  cmDspInst_t* gmult0  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult1  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult2  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult3  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
+  
+ 
+  
   // Audio file recording
   cmDspInst_t* recdGain= cmDspSysAllocInst(h,"Scalar", "Recd Gain",  5, kNumberDuiId, 0.0,   100.0,0.01, 1.5 );  
   cmDspInst_t* recdChk = cmDspSysAllocInst(h,"Button", "Record",     2, kCheckDuiId, 0.0 );
@@ -135,10 +136,11 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspInst_t* mi0p    = cmDspSysAllocInst(h,"AMeter","In 0",  0);
   cmDspInst_t* mi1p    = cmDspSysAllocInst(h,"AMeter","In 1",  0);
 
-  cmDspInst_t* meas    = cmDspSysAllocInst(h,"Scalar", "Meas",    5, kNumberDuiId, 1.0,   59.0,1.0,   1.0 );  
+  cmDspInst_t* meas     = cmDspSysAllocInst(h,"Scalar", "Begin Meas", 5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );  
+  cmDspInst_t* emeas    = cmDspSysAllocInst(h,"Scalar", "End   Meas", 5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );  
   cmDspSysInstallCb( h, meas, "val", scp, "meas", NULL);
   cmDspSysInstallCb( h, meas, "val", tlp, "meas", NULL);
-
+  
 
 
 
@@ -173,37 +175,34 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspSysInstallCb(h, recdPtS, "out", afop,    "sel",   NULL );
 
   // Audio connections
-
-  cmDspSysConnectAudio(h, php, "out", wtp, "phs" );   // phasor -> wave table
-  cmDspSysConnectAudio(h, wtp, "out", ao0, "in" );    // wave table -> audio out (dry output)
-  cmDspSysConnectAudio(h, wtp, "out", ao1, "in" );    // 
+  cmDspSysConnectAudio(h, php, "out", wt0, "phs" );      // phasor -> wave table
+  cmDspSysConnectAudio(h, php, "out", wt1, "phs" );      // 
   
-  //cmDspSysConnectAudio( h, ai0,   "out", ao0,   "in" );  //  dry signal through 
-  //cmDspSysConnectAudio( h, ai1,   "out", ao1,   "in" );  //
-  
-  //cmDspSysConnectAudio( h, ai0,   "out", mi0p,   "in" );  //
-  //cmDspSysConnectAudio( h, ai0,   "out", c0.kr0, "in" );  // ain -> ch0.kr0
-  //cmDspSysConnectAudio( h, ai0,   "out", c0.kr1, "in" );  // ain -> ch0.kr1
+  cmDspSysConnectAudio(h, wt0, "out", lmix, "in-0" );    // wave table -> audio out (dry output)
+  cmDspSysConnectAudio(h, wt1, "out", rmix, "in-0" );    // 
 
-  cmDspSysConnectAudio( h, wtp,   "out", mi0p,   "in" );  //
-  cmDspSysConnectAudio( h, wtp,   "out", c0.kr0, "in" );  // ain -> ch0.kr0
-  cmDspSysConnectAudio( h, wtp,   "out", c0.kr1, "in" );  // ain -> ch0.kr1
+  //cmDspSysConnectAudio(h, wt0, "out", lmix, "in-1" );    // wave table -> audio out (dry output)
+  //cmDspSysConnectAudio(h, wt1, "out", rmix, "in-1" );    // 
+
+
+  cmDspSysConnectAudio( h, wt0,   "out", mi0p,   "in" );  //
+  cmDspSysConnectAudio( h, wt0,   "out", c0.kr0, "in" );  // ain -> ch0.kr0
+  cmDspSysConnectAudio( h, wt0,   "out", c0.kr1, "in" );  // ain -> ch0.kr1
 
   
-  cmDspSysConnectAudio( h, c0.cmp,"out", ao2,    "in" );  // ch0.cmp -> aout
+  cmDspSysConnectAudio( h, c0.cmp,"out", lmix,    "in-1" );  // ch0.cmp -> aout
   cmDspSysConnectAudio( h, c0.cmp,"out", afop,   "in0");  // ch0.cmp -> audio_file_out
 
-  //cmDspSysConnectAudio( h, ai1,   "out", mi1p,   "in" );  //
-  //cmDspSysConnectAudio( h, ai1,   "out", c1.kr0, "in" );  // ain -> ch1.kr0
-  //cmDspSysConnectAudio( h, ai1,   "out", c1.kr1, "in" );  // ain -> ch1.kr1
 
-  cmDspSysConnectAudio( h, wtp,   "out", mi1p,   "in" );  //
-  cmDspSysConnectAudio( h, wtp,   "out", c1.kr0, "in" );  // ain -> ch1.kr0
-  cmDspSysConnectAudio( h, wtp,   "out", c1.kr1, "in" );  // ain -> ch1.kr1
+  cmDspSysConnectAudio( h, wt1,   "out", mi1p,   "in" );  //
+  cmDspSysConnectAudio( h, wt1,   "out", c1.kr0, "in" );  // ain -> ch1.kr0
+  cmDspSysConnectAudio( h, wt1,   "out", c1.kr1, "in" );  // ain -> ch1.kr1
 
-  cmDspSysConnectAudio( h, c1.cmp,"out", ao3,    "in" );  // ch1.cmp -> aout
+  cmDspSysConnectAudio( h, c1.cmp,"out", rmix,    "in-1" );  // ch1.cmp -> aout
   cmDspSysConnectAudio( h, c1.cmp,"out", afop,   "in1");  // ch1.cmp ->audio_file_out
   
+  cmDspSysConnectAudio( h, lmix, "out", ao0, "in" );
+  cmDspSysConnectAudio( h, rmix, "out", ao1, "in" );
 
 
   cmDspSysInstallCb( h, clrBtn, "sym",    amp, "cmd",   NULL ); // clear active meas.
@@ -252,8 +251,9 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspSysInstallCb( h, sfp, "vcost",prt,   "in",  NULL ); //
   cmDspSysInstallCb( h, sfp, "vtyp", prc,   "in", NULL ); //
   */
+  
   // wave-table to time-line cursor
-  //cmDspSysInstallCb(   h, wtp, "fidx",tlp,  "curs", NULL); 
+  cmDspSysInstallCb(   h, wt0, "fidx",tlp,  "curs", NULL); 
 
   cmDspSysInstallCb(h, prePath, "out", tlp, "path", NULL );
 
@@ -267,7 +267,8 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspSysInstallCb(h, mfpRt,"s-out-0",mfp,  "sel",   NULL );
 
   cmDspSysInstallCb(h, onb, "sym",     pts,     "on",     NULL );
-  cmDspSysInstallCb(h, pts, "on",      wtp,     "cmd",    NULL );
+  cmDspSysInstallCb(h, pts, "on",      wt0,     "cmd",    NULL );
+  cmDspSysInstallCb(h, pts, "on",      wt1,     "cmd",    NULL );
   cmDspSysInstallCb(h, pts, "on",      modp,    "cmd",    NULL );
   cmDspSysInstallCb(h, onb, "sym",     amCmd,   "rewind", NULL );
   cmDspSysInstallCb(h, onb, "out",     c0.achan,"reset",  NULL );
@@ -278,11 +279,10 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   // stop connections
   cmDspSysInstallCb(h, tlp,  "mfn", pts, "off",   NULL ); // Prevents WT start on new audio file from TL.
   cmDspSysInstallCb(h, offb, "sym", mfp, "sel",   NULL ); 
-  cmDspSysInstallCb(h, offb, "sym", pts, "off",   NULL );
-  cmDspSysInstallCb(h, pts,  "off", wtp, "cmd",   NULL );
+  cmDspSysInstallCb(h, offb, "sym", pts, "off",   NULL );  
+  cmDspSysInstallCb(h, pts,  "off", wt0, "cmd",   NULL );
+  cmDspSysInstallCb(h, pts,  "off", wt1, "cmd",   NULL );
   cmDspSysInstallCb(h, pts,  "off", modp,"cmd",   NULL );
-  cmDspSysInstallCb(h, offb, "sym", mop, "reset", NULL );
-  cmDspSysInstallCb(h, offb, "sym", mo2p,"reset", NULL );
 
 
   // time-line to MIDI file player selection
@@ -292,9 +292,13 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
 
 
   // time-line to Audio file player selection
-  cmDspSysInstallCb(h, tlp, "absi", wtp, "beg",   NULL );
-  cmDspSysInstallCb(h, tlp, "aesi", wtp, "end",   NULL );
-  cmDspSysInstallCb(h, tlp, "afn",  wtp, "fn",    NULL );
+  cmDspSysInstallCb(h, tlp, "absi", wt0, "beg",   NULL );
+  cmDspSysInstallCb(h, tlp, "aesi", wt0, "end",   NULL );
+  cmDspSysInstallCb(h, tlp, "afn",  wt0, "fn",    NULL );
+
+  cmDspSysInstallCb(h, tlp, "absi", wt1, "beg",   NULL );
+  cmDspSysInstallCb(h, tlp, "aesi", wt1, "end",   NULL );
+  cmDspSysInstallCb(h, tlp, "afn",  wt1, "fn",    NULL );
   
   // score to score follower - to set initial search location
   cmDspSysInstallCb(h, scp, "sel",    sfp, "index", NULL );
@@ -309,40 +313,45 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
 
   cmDspSysInstallCb(h, msrc,   "d1",     sfp,  "d1",    NULL );
   cmDspSysInstallCb(h, msrc,   "d1",     nmp,  "d1",    NULL );
-  cmDspSysInstallCb(h, nmp,   "d1",     mop,  "d1",    NULL );
-  //cmDspSysInstallCb(h, nmp,   "d1",     pic, "d1",    NULL );
-  //cmDspSysInstallCb(h, pic,   "d1",     mo2p, "d1",    NULL );
 
   cmDspSysInstallCb(h, msrc,  "d0",      sfp,  "d0",   NULL );
   cmDspSysInstallCb(h, msrc,  "d0",      nmp,  "d0",   NULL );
-  cmDspSysInstallCb(h, nmp,  "d0",      mop,  "d0",   NULL );
-  //cmDspSysInstallCb(h, nmp,  "d0",      pic, "d0",   NULL );
-  //cmDspSysInstallCb(h, pic,   "d0",     mo2p, "d0",    NULL );
 
   cmDspSysInstallCb(h, msrc,  "status",  sfp,  "status",NULL );
   cmDspSysInstallCb(h, msrc,  "status",  nmp,  "status",NULL );
-  cmDspSysInstallCb(h, nmp,  "status",  mop,  "status",NULL );
-  //cmDspSysInstallCb(h, nmp,  "status",  pic, "status",NULL );
-  //cmDspSysInstallCb(h, pic,   "status",  mo2p, "status",    NULL );
 
 
   // score follower to recd_play,modulator and printers
   cmDspSysInstallCb(h, sfp, "out",     modp,    "index", NULL );
   cmDspSysInstallCb(h, sfp, "recent",  prp,     "in",  NULL );  // report 'recent' but only act on 'max' loc index
 
-  //cmDspSysInstallCb(h, igain0, "val", ai0, "gain", NULL );   // input gain control
-  //cmDspSysInstallCb(h, igain1, "val", ai1, "gain", NULL );
-
+  cmDspSysInstallCb( h, emeas, "val", its,  "off-int", NULL);
+  cmDspSysInstallCb( h, sfp,   "out", its,  "in",  NULL);
+  cmDspSysInstallCb( h, its,   "out", offb, "in",  NULL);
+  cmDspSysInstallCb( h, its,   "out", prp, "in",  NULL);
+  
 
   cmDspSysInstallCb(h, modp, "dgain0",  ogain0, "val",  NULL );
   cmDspSysInstallCb(h, modp, "dgain1",  ogain1, "val",  NULL );
   cmDspSysInstallCb(h, modp, "wgain0",  ogain2, "val",  NULL );
   cmDspSysInstallCb(h, modp, "wgain1",  ogain3, "val",  NULL );
 
-  cmDspSysInstallCb(h, ogain0, "val", ao0, "gain", NULL );   // output gain control - dry 0
-  cmDspSysInstallCb(h, ogain1, "val", ao1, "gain", NULL );   //                       dry 1
-  cmDspSysInstallCb(h, ogain2, "val", ao2, "gain", NULL );   //                       wet 0
-  cmDspSysInstallCb(h, ogain3, "val", ao3, "gain", NULL );   //                       wet 1
+
+  cmDspSysInstallCb(h, ogain0,  "val", gmult0, "in-0", NULL );
+  cmDspSysInstallCb(h, ogainD,  "val", gmult0, "in-1", NULL );
+  
+  cmDspSysInstallCb(h, ogain1,  "val", gmult1, "in-0", NULL );
+  cmDspSysInstallCb(h, ogainD,  "val", gmult1, "in-1", NULL );
+  
+  cmDspSysInstallCb(h, ogain2,  "val", gmult2, "in-0", NULL );
+  cmDspSysInstallCb(h, ogainW,  "val", gmult2, "in-1", NULL );
+  cmDspSysInstallCb(h, ogain3,  "val", gmult3, "in-0", NULL );
+  cmDspSysInstallCb(h, ogainW,  "val", gmult3, "in-1", NULL );
+  
+  cmDspSysInstallCb(h, gmult0, "out", lmix, "gain-0", NULL );   // output gain control - dry 0
+  cmDspSysInstallCb(h, gmult1, "out", rmix, "gain-0", NULL );   //                       dry 1
+  cmDspSysInstallCb(h, gmult2, "out", lmix, "gain-1", NULL );   //                       wet 0
+  cmDspSysInstallCb(h, gmult3, "out", rmix, "gain-1", NULL );   //                       wet 1
 
 
   return rc;

From ad0b1ad901b273cb4ee6185e785a6f994ff019e1 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Fri, 31 Jul 2020 07:35:27 -0400
Subject: [PATCH 09/21] cmXScore.c : Added missing underscore to 't_frac' in
 score CSV column header.

---
 src/app/cmXScore.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c
index 3e3c75c..32c3e95 100644
--- a/src/app/cmXScore.c
+++ b/src/app/cmXScore.c
@@ -2836,7 +2836,7 @@ cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p )
   {
     "id","trk","evt","opcode","dticks","micros","status",
     "meta","ch","d0","d1","arg0","arg1","bar","skip",
-    "even","grace","tempo","t frac","dyn","section","play_recd","remark",NULL
+    "even","grace","tempo","t_frac","dyn","section","play_recd","remark",NULL
   };
 
   cmCsvCell_t* lcp = NULL;

From f3af534ad5594488f1cdf5090269df22a342c2b2 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Sat, 1 Aug 2020 08:07:50 -0400
Subject: [PATCH 10/21] cmMidiFile.h/c : Added flags arg. to
 cmMidiFileCalcNoteDurations(), Added cmMidiFileIsPedalUp/Down() macro.
 cmMidi.h : Fixed bug in cmMidiIsNoteOn() macro.

---
 src/app/cmMidiScoreFollow.c |   3 +-
 src/app/cmTakeSeqBldr.c     |   2 +-
 src/app/cmTimeLine.c        |   2 +-
 src/app/cmXScore.c          |  14 ++--
 src/cmDList.c               |   2 +-
 src/cmMidi.h                |   6 +-
 src/cmMidiFile.c            | 156 ++++++++++++++++++++++++++++++------
 src/cmMidiFile.h            |  27 +++++--
 src/cmXml.c                 |   3 +
 9 files changed, 167 insertions(+), 48 deletions(-)

diff --git a/src/app/cmMidiScoreFollow.c b/src/app/cmMidiScoreFollow.c
index a837159..1b32b67 100644
--- a/src/app/cmMidiScoreFollow.c
+++ b/src/app/cmMidiScoreFollow.c
@@ -149,6 +149,7 @@ unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcher
   return scUid;
 }
 
+// This is the score follower callback function 
 void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp )
 {
   _cmMsf_ScoreFollow_t* r = (_cmMsf_ScoreFollow_t*)arg;
@@ -193,7 +194,7 @@ cmMsfRC_t cmMidiScoreFollowMain(
     goto errLabel;
   }
 
-  // setup the callback record
+  // setup the callback record with an array that has twice as many records as there are score events
   if((sfr.rAllocN  = cmScoreEvtCount( scH )*2) == 0)
   {
     rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreCsvFn));
diff --git a/src/app/cmTakeSeqBldr.c b/src/app/cmTakeSeqBldr.c
index 4da3939..61477cb 100644
--- a/src/app/cmTakeSeqBldr.c
+++ b/src/app/cmTakeSeqBldr.c
@@ -960,7 +960,7 @@ cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool ov
   //cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), false );
   
   // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks)
-  cmMidiFileCalcNoteDurations( mfH );
+  cmMidiFileCalcNoteDurations( mfH, 0 );
   
   unsigned                 i     = 0;
   unsigned                 n     = cmMidiFileMsgCount(mfH);
diff --git a/src/app/cmTimeLine.c b/src/app/cmTimeLine.c
index 708f7a0..2897353 100644
--- a/src/app/cmTimeLine.c
+++ b/src/app/cmTimeLine.c
@@ -733,7 +733,7 @@ cmTlRC_t _cmTlAllocMidiFileRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmCh
   //cmMidiFileTickToSamples(mfH,p->srate,false);
 
   // assign note durations to all note-on msg's
-  cmMidiFileCalcNoteDurations(mfH);
+  cmMidiFileCalcNoteDurations(mfH,0);
 
   unsigned recdByteCnt = sizeof(cmTlMidiFile_t) + strlen(fn) + 1;
 
diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c
index 3e3c75c..51e0e1f 100644
--- a/src/app/cmXScore.c
+++ b/src/app/cmXScore.c
@@ -4084,7 +4084,7 @@ cmXsRC_t cmXScoreTest(
 
   if( editFn!=NULL && !cmFsIsFile(editFn) )
   {
-    cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.",editFn);
+    cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.\n",editFn);
     cmXScoreGenEditFile(ctx,xmlFn,editFn);
     editFn = NULL;
   }
@@ -4101,14 +4101,14 @@ cmXsRC_t cmXScoreTest(
 
     _cmXsIsCsvValid(ctx,h,csvOutFn);
   }
+
+  // measure the score complexity
+  double wndSecs = 1.0;
+    
+  _cmXsMeasComplexity(h,wndSecs);
   
   if( midiOutFn != NULL )
-  {
-    // measure the score complexity
-    double wndSecs = 1.0;
-    
-    _cmXsMeasComplexity(h,wndSecs);
-    
+  {  
     cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
 
     _cmXsWriteMidiFile(ctx, h, beginMeasNumb, beginBPM, pp->dirStr, pp->fnStr );
diff --git a/src/cmDList.c b/src/cmDList.c
index 831b106..29b6a63 100644
--- a/src/cmDList.c
+++ b/src/cmDList.c
@@ -150,7 +150,7 @@ void _cmDListIndexFree( cmDList_t* p, cmDListIndex_t* x )
       // x is the first index
       if( x0 == NULL )
       {
-        assert( x1 = p->indexes );
+        assert( x1 == p->indexes );
         p->indexes = x->link;
       }
       else
diff --git a/src/cmMidi.h b/src/cmMidi.h
index 653941e..b9ae113 100644
--- a/src/cmMidi.h
+++ b/src/cmMidi.h
@@ -88,9 +88,9 @@ extern "C" {
 #define cmMidiIsStatus( s )   (kNoteOffMdId <= (s) /*&& ((unsigned)(s)) <= kSysRtResetMdId*/ )
 #define cmMidiIsChStatus( s ) (kNoteOffMdId <= (s) && (s) <  kSysExMdId)
 
-#define cmMidiIsNoteOn( s )      ( kNoteOnMdId <= (s) && (s) <= (kNoteOnMdId + kMidiChCnt) )
-#define cmMidiIsNoteOff( s, d1 ) ( (cmMidiIsNoteOn(s) && (d1)==0) || (kNoteOffMdId <= (s) && (s) <= (kNoteOffMdId + kMidiChCnt)) )
-#define cmMidiIsCtl( s )         ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) )
+#define cmMidiIsNoteOn( s )      ( kNoteOnMdId <= (s) && (s) < (kNoteOnMdId + kMidiChCnt) )
+#define cmMidiIsNoteOff( s, d1 ) ( (cmMidiIsNoteOn(s) && (d1)==0) || (kNoteOffMdId <= (s) && (s) < (kNoteOffMdId + kMidiChCnt)) )
+#define cmMidiIsCtl( s )         ( kCtlMdId <= (s) && (s) < (kCtlMdId + kMidiChCnt) )
 
 #define cmMidiIsSustainPedal(     s, d0 )    ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)== kSustainCtlMdId )
 #define cmMidiIsSustainPedalDown( s, d0, d1) ( cmMidiIsSustainPedal(s,d0) && (d1)>=64 )
diff --git a/src/cmMidiFile.c b/src/cmMidiFile.c
index bf52be2..a47f413 100644
--- a/src/cmMidiFile.c
+++ b/src/cmMidiFile.c
@@ -423,6 +423,34 @@ cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
   return rc;
 }
 
+void _cmMidiFileDrop( _cmMidiFile_t* p )
+{
+  unsigned i;
+  unsigned n = 0;
+  for(i=0; i<p->trkN; ++i)
+  {
+    _cmMidiTrack_t*   trk = p->trkV + i;
+    cmMidiTrackMsg_t* m0  = NULL;
+    cmMidiTrackMsg_t* m   = trk->base;
+    
+    for(; m!=NULL; m=m->link)
+    {
+      if( cmIsFlag(m->flags,kDropTrkMsgFl) )
+      {
+        ++n;
+        if( m0 == NULL )
+          trk->base = m->link;
+        else
+          m0->link = m->link;
+      }
+      else
+      {
+        m0 = m;
+      }      
+    }
+  }
+}
+
 int _cmMidiFileSortFunc( const void *p0, const void* p1 )
 {  
   if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
@@ -1369,7 +1397,6 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy
   mfp->msgVDirtyFl = true;
 
   return kOkMfRC;
-
 }
 
 cmMfRC_t  cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg )
@@ -1532,6 +1559,38 @@ unsigned  cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un
   return mi;
 }
 
+/*
+1.Move closest previous tempo msg to begin.
+2.The first msg in each track must be the first msg >= begin.time
+3.Remove all msgs > end.time - except the 'endMsg' for each note/pedal that is active at end time.
+
+
+ */
+
+unsigned _cmMidiFileIsEndMsg( cmMidiTrackMsg_t* m, cmMidiTrackMsg_t** endMsgArray, unsigned n )
+{
+  unsigned i = 0;
+  for(; i<n; ++i)
+    if( endMsgArray[i] == m )
+      return i;
+
+  return cmInvalidIdx;
+}
+
+bool _cmMidiFileAllEndMsgFound( cmMidiTrackMsg_t** noteMsgArray, unsigned n0, cmMidiTrackMsg_t** pedalMsgArray, unsigned n1 )
+{
+  unsigned i=0;
+  for(; i<n0; ++i)
+    if( noteMsgArray[i] != NULL )
+      return false;
+
+  for(i=0; i<n1; ++i)
+    if( pedalMsgArray[i] != NULL )
+      return false;
+
+  return true;
+}
+
 double  cmMidiFileDurSecs( cmMidiFileH_t h )
 {
   _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
@@ -1544,14 +1603,6 @@ double  cmMidiFileDurSecs( cmMidiFileH_t h )
   return msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
 }
 
-typedef struct _cmMidiVoice_str
-{
-  const  cmMidiTrackMsg_t*  mp;
-  unsigned                  durMicros;
-  bool                      sustainFl;
-  struct _cmMidiVoice_str*  link;
-} _cmMidiVoice_t;
-
 
 void _cmMidiFileSetDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1 )
 {
@@ -1574,10 +1625,11 @@ bool _cmMidiFileCalcNoteDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1, int not
   return true;
 }
 
-void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
+void cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags )
 {
   _cmMidiFile_t* p;
-
+  bool warningFl = cmIsFlag(flags,kWarningsMfFl);
+  
   if((p = _cmMidiFileHandleToPtr(h)) == NULL )
     return;
 
@@ -1586,13 +1638,14 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
 
   unsigned          mi = cmInvalidId;
   cmMidiTrackMsg_t* noteM[     kMidiNoteCnt * kMidiChCnt ];  // ptr to note-on or NULL if the note is not sounding
-  cmMidiTrackMsg_t* sustV[                    kMidiChCnt ];
-  cmMidiTrackMsg_t* sostV[                    kMidiChCnt ];
+  cmMidiTrackMsg_t* sustV[                    kMidiChCnt ];  // ptr to last sustain pedal down msg or NULL if susteain pedal is not down
+  cmMidiTrackMsg_t* sostV[                    kMidiChCnt ];  // ptr to last sost. pedal down msg or NULL if sost. pedal is not down
   int               noteGateM[ kMidiNoteCnt * kMidiChCnt ];  // true if the associated note key is depressed
   bool              sostGateM[ kMidiNoteCnt * kMidiChCnt ];  // true if the associated note was active when the sost. pedal went down
   int               sustGateV[ kMidiChCnt];                  // true if the associated sustain pedal is down
   int               sostGateV[ kMidiChCnt];                  // true if the associated sostenuto pedal is down
   unsigned          i,j;
+  unsigned          n = 0;
   
   const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
   
@@ -1634,12 +1687,22 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
       unsigned  k = ch*kMidiNoteCnt + d0;
 
       // there should be no existing sounding note instance for this pitch
-      //if( noteGateM[k] == 0 && noteM[k] != NULL )
-      //  cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
+      if( noteGateM[k] == 0 && noteM[k] != NULL )
+      {
+        if( warningFl )
+          cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
 
+        if( cmIsFlag(flags,kDropReattacksMfFl) )
+        {
+          m->flags |= kDropTrkMsgFl;
+          n += 1;
+        }
+          
+      }
+      // if this is a re-attack 
       if( noteM[k] != NULL )
         noteGateM[k] += 1;
-      else
+      else // this is a new attack
       {
         noteM[k]     = m;
         noteGateM[k] = 1;
@@ -1676,8 +1739,8 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
         if( cmMidiFileIsSustainPedalDown(m) )
         {
           // if the sustain channel is already down
-          //if( sustGateV[ch] )
-          //  cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid);
+          if( warningFl && sustGateV[ch] )
+            cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid);
 
           sustGateV[ch] += 1;
 
@@ -1722,8 +1785,8 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
             if( cmMidiFileIsSostenutoPedalDown(m) )
             {
               // if the sustain channel is already down
-              //if( sostGateV[ch] )
-              //  cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid);
+              if( warningFl && sostGateV[ch] )
+                cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid);
 
               // record the notes that are active when the sostenuto pedal went down
               unsigned k = ch * kMidiNoteCnt;
@@ -1770,6 +1833,46 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
               }
     
   } // for each midi file event
+
+
+  if( warningFl )
+  {
+    unsigned sustChN   = 0; // count of channels where the sustain pedal was left on at the end of the file
+    unsigned sostChN   = 0; //                             sostenuto
+    unsigned sustInstN = 0; // count of sustain   on with no previous sustain off
+    unsigned sostInstN = 0; //          sostenuto on   
+    unsigned noteN     = 0; // count of notes left on at the end of the file
+    unsigned noteInstN = 0; // count of reattacks
+    
+    // initialize the state tracking variables
+    for(i=0; i<kMidiChCnt; ++i)
+    {
+      if( sustV[i]!=NULL )
+        sustChN += 1;
+      
+      sustInstN += sustGateV[i]; 
+      
+        if( sostV[i] != NULL )
+          sostChN += 1;
+      
+      sostInstN += sostGateV[i] = 0;
+      
+      for(j=0; j<kMidiNoteCnt; ++j)
+      {
+        noteN     += noteM[ i*kMidiNoteCnt + j ] != NULL;
+        noteInstN += noteGateM[ i*kMidiNoteCnt + j ];
+      }
+    }
+
+    cmErrWarnMsg(&p->err,kEventTerminationMfRC,"note:%i inst:%i sustain: %i inst: %i sost: %i inst: %i",noteN,noteInstN,sustChN,sustInstN,sostChN,sostInstN);
+  }
+
+  // drop 
+  if( cmIsFlag(flags,kDropReattacksMfFl) )
+    _cmMidiFileDrop(p);
+  
+
+
 }
 
 void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
@@ -1833,15 +1936,16 @@ void _cmMidiFilePrintHdr( const _cmMidiFile_t* mfp, cmRpt_t* rpt )
 
   cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN);
 
-  cmRptPrintf(rpt," UID     dtick     atick      amicro     type  ch  D0  D1\n");
-  cmRptPrintf(rpt,"----- ---------- ---------- ---------- : ---- --- --- ---\n");
+  cmRptPrintf(rpt," UID  trk    dtick     atick      amicro     type  ch  D0  D1\n");
+  cmRptPrintf(rpt,"----- --- ---------- ---------- ---------- : ---- --- --- ---\n");
   
 }
 
 void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
 {
-  cmRptPrintf(rpt,"%5i %10u %10llu %10llu : ",
+  cmRptPrintf(rpt,"%5i %3i %10u %10llu %10llu : ",
     tmp->uid,
+    tmp->trkIdx,
     tmp->dtick,
     tmp->atick,
     tmp->amicro );
@@ -1980,7 +2084,7 @@ cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCh
     goto errLabel;
   }
   
-  cmMidiFileCalcNoteDurations( mfH );
+  cmMidiFileCalcNoteDurations( mfH, 0 );
   
   if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
     return cmErrMsg(&p->err,kFileFailMfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
@@ -2013,7 +2117,7 @@ cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCha
     goto errLabel;
   }
 
- cmMidiFileCalcNoteDurations( mfH );
+  cmMidiFileCalcNoteDurations( mfH, 0 );
 
   msgN = cmMidiFileMsgCount(mfH);
   msgs = cmMidiFileMsgArray(mfH);
@@ -2159,7 +2263,7 @@ void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
     return;
   }
 
-  cmMidiFileCalcNoteDurations(  h );
+  cmMidiFileCalcNoteDurations(  h, 0 );
 
   if( 1 )
   {
diff --git a/src/cmMidiFile.h b/src/cmMidiFile.h
index 6775f31..b5428e2 100644
--- a/src/cmMidiFile.h
+++ b/src/cmMidiFile.h
@@ -63,11 +63,16 @@ extern "C" {
     struct cmMidiTrackMsg_str* end; // note-off or pedal-up message
   } cmMidiChMsg_t;
 
+  enum
+  {
+   kDropTrkMsgFl = 0x01
+  };
 
   typedef struct cmMidiTrackMsg_str
   {
+    unsigned                   flags;   // see k???TrkMsgFl
     unsigned                   uid;     // uid's are unique among all msg's in the file
-    unsigned                   dtick;   // delta ticks between events on this track
+    unsigned                   dtick;   // delta ticks between events on this track (ticks between this event and the previous event on this track)
     unsigned long long         atick;   // global (all tracks interleaved) accumulated ticks
     unsigned long long         amicro;  // global (all tracks interleaved) accumulated microseconds adjusted for tempo changes
     cmMidiByte_t               status;  // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
@@ -91,9 +96,12 @@ extern "C" {
     } u;
   } cmMidiTrackMsg_t;
 
-#define cmMidiFileIsNoteOn(m)         (cmMidiIsNoteOn((m)->status) && (m)->u.chMsgPtr->d1>0)
+#define cmMidiFileIsNoteOn(m)         (cmMidiIsNoteOn((m)->status) && ((m)->u.chMsgPtr->d1>0))
 #define cmMidiFileIsNoteOff(m)        (cmMidiIsNoteOff((m)->status,(m)->u.chMsgPtr->d1))
-  
+
+#define cmMidiFileIsPedalUp(m)        (cmMidiIsPedalUp(    (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) )
+#define cmMidiFileIsPedalDown(m)      (cmMidiIsPedalDown(  (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) )
+
 #define cmMidiFileIsSustainPedalUp(m)     (cmMidiIsSustainPedalUp(    (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
 #define cmMidiFileIsSustainPedalDown(m)   (cmMidiIsSustainPedalDown(  (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1))
   
@@ -118,7 +126,9 @@ extern "C" {
     kUidNotFoundMfRC,    // 13
     kUidNotANoteMsgMfRC, // 14
     kInvalidTrkIndexMfRC,// 15
-    kSvgFailMfRC         // 16
+    kSvgFailMfRC,        // 16
+    kMsgNotFoundMfRC,     // 17
+    kEventTerminationMfRC // 18
   };
 
   extern cmMidiFileH_t cmMidiFileNullHandle;
@@ -172,6 +182,7 @@ extern "C" {
   // Set the velocity of a note-on/off msg identified by 'uid'.
   cmMfRC_t             cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel );
 
+  
   // Insert a MIDI message relative to the reference msg identified by 'uid'.
   // If dtick is positive/negative then the new msg is inserted after/before the reference msg.  
   cmMfRC_t             cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
@@ -199,8 +210,9 @@ extern "C" {
 
   double                cmMidiFileDurSecs( cmMidiFileH_t h );
 
-  // Calculate Note Duration 
-  void                  cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
+  // Calculate Note Duration
+  enum { kWarningsMfFl=0x01, kDropReattacksMfFl=0x02 };
+  void                  cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags );
 
   // Set the delay prior to the first non-zero msg.
   void                  cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
@@ -218,8 +230,7 @@ extern "C" {
   {
     unsigned           uid;
     unsigned long long amicro;
-    unsigned           density;
-    
+    unsigned           density; 
   } cmMidiFileDensity_t;
 
   // Generate the note onset density measure for each note in the MIDI file.
diff --git a/src/cmXml.c b/src/cmXml.c
index cfbd27d..05399ee 100644
--- a/src/cmXml.c
+++ b/src/cmXml.c
@@ -650,6 +650,9 @@ cmXmlRC_t _cmXmlReadNode( cmXml_t* p, cmXmlNode_t* parent )
       return rc;
     }
 
+    if( np==NULL && p->stack==NULL)
+      break;
+
     // if an  end-tag was just read or node was created but closed then pop the stack
     if( np==NULL || (np==p->stack && cmIsFlag(np->flags,kClosedXmlFl)) )
       p->stack = p->stack->parent;

From 75f8c880f76c65341d76f0aa55a4c3ecf33ade87 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:14:23 -0400
Subject: [PATCH 11/21] cmXScore.h/c : Added 'damperRptFl' to print out pedal
 state during pedal processing.

---
 src/app/cmXScore.c | 43 ++++++++++++++++++++++++++++++-------------
 src/app/cmXScore.h |  6 +++---
 2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c
index 51e0e1f..13fe5af 100644
--- a/src/app/cmXScore.c
+++ b/src/app/cmXScore.c
@@ -681,12 +681,13 @@ cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t*
   return _cmXScorePushNote(p, meas, voiceId, note );
 }
 
-cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* noteXmlNode, unsigned tick, unsigned duration, double rvalue, const cmChar_t* tvalue, unsigned flags )
+cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* noteXmlNode, unsigned tick, unsigned duration, unsigned staff, double rvalue, const cmChar_t* tvalue, unsigned flags )
 {
   cmXsNote_t* note    = cmLhAllocZ(p->lhH,cmXsNote_t,1);
   unsigned    voiceId = 0;    // non-note's are always assigned to voiceId=0;
 
   note->tick     = tick;
+  note->staff    = staff;
   note->flags    = flags;
   note->rvalue   = rvalue;
   note->tvalue   = tvalue;
@@ -822,7 +823,7 @@ cmXsRC_t  _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNo
   }
 
   if( pushFl )
-   rc = _cmXScorePushNonNote(p,meas,dnp,tick+offset,duration,rvalue,tvalue,flags);
+    rc = _cmXScorePushNonNote(p,meas,dnp,tick+offset,duration,staff,rvalue,tvalue,flags);
 
   return rc;
 }
@@ -868,7 +869,7 @@ cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t*
   }
 
   // store the bar line
-  if((rc = _cmXScorePushNonNote(p,meas,mnp,tick,0,0,NULL,kBarXsFl)) != kOkXsRC )
+  if((rc = _cmXScorePushNonNote(p,meas,mnp,tick,0,0,0,NULL,kBarXsFl)) != kOkXsRC )
     return rc;
 
   np = mnp->children;
@@ -1562,7 +1563,7 @@ void _cmXScoreFixBarLines( cmXScore_t* p )
 }
 
 // Assign pedal down durations to pedal down events.
-cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
+cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
 {
   cmXsRC_t    rc = kOkXsRC;
   cmXsPart_t* pp = p->partL;
@@ -1584,11 +1585,14 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
           case 0:
             break;
             
-          case kDampDnXsFl:
+          case kDampDnXsFl:            
             if( dnp != NULL )
               cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper down not preceded by damper up in measure:%i.",mp->number);
             else
               dnp = np;
+
+            if( reportFl )
+              cmRptPrintf(p->err.rpt,"Damp Down : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
             break;
             
           case kDampUpXsFl:
@@ -1599,6 +1603,10 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
               dnp->duration = np->tick - dnp->tick;
               dnp = NULL;
             }
+            
+            if( reportFl )
+              cmRptPrintf(p->err.rpt,"Damp Up   : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
+            
             break;
             
           case kDampUpDnXsFl:
@@ -1609,13 +1617,19 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
               dnp->duration = np->tick - dnp->tick;
               dnp = np;
             }
+            
+            if( reportFl )
+              cmRptPrintf(p->err.rpt,"Damp UpDn : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
             break;
             
           case kSostDnXsFl:
             if( snp != NULL )
               cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto down not preceded by sostenuto up in measure:%i.",mp->number);
             else
-              snp = np;            
+              snp = np;
+            
+            if( reportFl )
+              cmRptPrintf(p->err.rpt,"Sost Down : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
             break;
             
           case kSostUpXsFl:
@@ -1626,6 +1640,8 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
               snp->duration = np->tick - snp->tick;
               snp = NULL;
             }
+            if( reportFl )
+              cmRptPrintf(p->err.rpt,"Sost Up   : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
             break;
             
           default:
@@ -2706,7 +2722,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
 
 
 
-cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn )
+cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl )
 {
   cmXsRC_t rc = kOkXsRC;
 
@@ -2771,7 +2787,7 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, c
   }
 
   // assign durations to pedal down events
-  _cmXScoreProcessPedals(p);
+  _cmXScoreProcessPedals(p,damperRptFl);
 
   // remove some notes which share a pitch and are overlapped or embedded within another note.
   _cmXScoreProcessOverlappingNotes(p);
@@ -3408,7 +3424,7 @@ void _cmXScoreGenEditFileWrite( void* arg, const cmChar_t* text )
   }
 }
 
-cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn )
+cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl )
 {
   cmXsH_t   xsH = cmXsNullHandle;
   cmFileH_t fH  = cmFileNullHandle;
@@ -3419,7 +3435,7 @@ cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_
   cmErrSetup(&err,&ctx->rpt,"cmXScoreGenEditFile");
   cmRptSetup(&rpt,_cmXScoreGenEditFileWrite,_cmXScoreGenEditFileWrite,&fH);
 
-  if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL)) != kOkXsRC )
+  if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != kOkXsRC )
     return rc;
 
   if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
@@ -4077,7 +4093,8 @@ cmXsRC_t cmXScoreTest(
   int             beginMeasNumb,
   int             beginBPM,
   bool            standAloneFl,
-  bool            panZoomFl )
+  bool            panZoomFl,
+  bool            damperRptFl)
 {
   cmXsRC_t rc;
   cmXsH_t h = cmXsNullHandle;
@@ -4085,13 +4102,13 @@ cmXsRC_t cmXScoreTest(
   if( editFn!=NULL && !cmFsIsFile(editFn) )
   {
     cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.\n",editFn);
-    cmXScoreGenEditFile(ctx,xmlFn,editFn);
+    cmXScoreGenEditFile(ctx,xmlFn,editFn,damperRptFl);
     editFn = NULL;
   }
   
 
   // Parse the XML file and apply the changes in editFn.
-  if((rc = cmXScoreInitialize( ctx, &h, xmlFn,editFn)) != kOkXsRC )
+  if((rc = cmXScoreInitialize( ctx, &h, xmlFn,editFn, damperRptFl )) != kOkXsRC )
     return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
 
   if( csvOutFn != NULL )
diff --git a/src/app/cmXScore.h b/src/app/cmXScore.h
index 19b6205..c412368 100644
--- a/src/app/cmXScore.h
+++ b/src/app/cmXScore.h
@@ -47,7 +47,7 @@ extern "C" {
   // Initialize an cmXScore object from a Sibelius generated MusicXML file.
   // 'editFn' is used to add additional information to the score.
   // See cmXScoreGenEditFile()
-  cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn );
+  cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl );
   cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
 
   
@@ -60,7 +60,7 @@ extern "C" {
   // Generate a template 'edit file'. This file can be edited by hand to included additional
   // information in the score. See the 'editFn' argument to cmXScoreInitialize() for where
   // this file is used.
-  cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn );
+  cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl );
 
   // Generate the CSV file suitable for use by cmScore.
   //
@@ -72,7 +72,7 @@ extern "C" {
   // Set reportFl to true to print a report of the score following processing.
   // Set begMeasNumb to the first measure the to be written to the output csv, MIDI and SVG files.
   // Set begBPM to 0 to use the tempo from the score otherwise set it to the tempo at begMeasNumb.
-  cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, bool reportFl, int begMeasNumb, int begBPM, bool svgStandAloneFl, bool svgPanZoomFl );
+  cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, bool reportFl, int begMeasNumb, int begBPM, bool svgStandAloneFl, bool svgPanZoomFl, bool damperRptFl );
   
 #ifdef __cplusplus
 }

From af0e716b1f45ee19a4cf501e300a1d6ab0b5af0b Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:17:56 -0400
Subject: [PATCH 12/21] cmProc2.h/c : Added igain,igainV to cmSpecDist2.

---
 src/cmProc2.c | 18 ++++++++++++------
 src/cmProc2.h | 19 ++++++++++---------
 2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/src/cmProc2.c b/src/cmProc2.c
index ff2c3b0..1fd2545 100644
--- a/src/cmProc2.c
+++ b/src/cmProc2.c
@@ -6501,7 +6501,8 @@ cmRC_t cmSpecDist2Init( cmSpecDist2_t* p, unsigned procSmpCnt, double srate, uns
   p->wndSmpCnt    = wndSmpCnt;
   p->hopSmpCnt    = (unsigned)floor(wndSmpCnt/hopFcmt);
   p->procSmpCnt   = procSmpCnt;
-
+  p->igain        = 1.0;
+  
   p->ceiling      = 30;
   p->expo         = 2.0;
     
@@ -6511,8 +6512,9 @@ cmRC_t cmSpecDist2Init( cmSpecDist2_t* p, unsigned procSmpCnt, double srate, uns
 
   p->mix          = 0.0;
 
-  p->pva = cmPvAnlAlloc(  p->obj.ctx, NULL, procSmpCnt, srate, wndSmpCnt, p->hopSmpCnt, flags );
-  p->pvs = cmPvSynAlloc(  p->obj.ctx, NULL, procSmpCnt, srate, wndSmpCnt, p->hopSmpCnt, olaWndTypeId );
+  p->igainV = cmMemResizeZ( cmSample_t, p->igainV, procSmpCnt );
+  p->pva    = cmPvAnlAlloc(  p->obj.ctx, NULL, procSmpCnt, srate, wndSmpCnt, p->hopSmpCnt, flags );
+  p->pvs    = cmPvSynAlloc(  p->obj.ctx, NULL, procSmpCnt, srate, wndSmpCnt, p->hopSmpCnt, olaWndTypeId );
 
 
   return rc;
@@ -6522,7 +6524,7 @@ cmRC_t cmSpecDist2Final(cmSpecDist2_t* p )
 {
   cmRC_t rc = cmOkRC;
 
-  
+  cmMemFree(p->igainV);
   cmPvAnlFree(&p->pva);
   cmPvSynFree(&p->pvs);
   return rc;
@@ -6585,8 +6587,12 @@ cmRC_t  cmSpecDist2Exec( cmSpecDist2_t* p, const cmSample_t* sp, unsigned sn )
 
   unsigned binN = p->pva->binCnt;
 
+  cmVOS_MultVVS( p->igainV, sn, sp, p->igain );
+
+  //printf("%f\n",p->igainV[0]);
+
   // cmPvAnlExec() returns true when it calc's a new spectral output frame
-  if( cmPvAnlExec( p->pva, sp, sn ) )
+  if( cmPvAnlExec( p->pva, p->igainV, sn ) )
   {
     cmReal_t X0m[binN];
     cmReal_t X1m[binN]; 
@@ -6652,7 +6658,7 @@ const cmSample_t* cmSpecDist2Out(  cmSpecDist2_t* p )
 
 void  cmSpecDist2Report( cmSpecDist2_t* p )
 {
-  printf("ceil:%f expo:%f mix:%f thresh:%f upr:%f lwr:%f\n", p->ceiling,p->expo,p->mix,p->thresh,p->lwrSlope,p->uprSlope);
+  printf("igain:%f ceil:%f expo:%f mix:%f thresh:%f upr:%f lwr:%f\n", p->igain, p->ceiling,p->expo,p->mix,p->thresh,p->lwrSlope,p->uprSlope);
 }
 
 
diff --git a/src/cmProc2.h b/src/cmProc2.h
index 21b0b6c..2b9ec49 100644
--- a/src/cmProc2.h
+++ b/src/cmProc2.h
@@ -1318,15 +1318,16 @@ extern "C" {
 
   typedef struct
   {
-    cmObj    obj;
-    double   srate;
-    unsigned wndSmpCnt;
-    unsigned hopFcmt;
-    unsigned hopSmpCnt;
-    unsigned procSmpCnt;
-    
-    cmPvAnl* pva;
-    cmPvSyn* pvs;
+    cmObj       obj;
+    double      srate;
+    unsigned    wndSmpCnt;
+    unsigned    hopFcmt;
+    unsigned    hopSmpCnt;
+    unsigned    procSmpCnt;
+    double      igain;
+    cmSample_t* igainV;
+    cmPvAnl*    pva;
+    cmPvSyn*    pvs;
 
     double   ceiling;
     double   expo;    

From 349979f8b58a5307bb752a9e249b6d65b9e62eb7 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:18:31 -0400
Subject: [PATCH 13/21] cmProc4.c : Print out preset label in
 _cmScModActivateGroup().

---
 src/cmProc4.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/cmProc4.c b/src/cmProc4.c
index cdc49e2..89d312b 100644
--- a/src/cmProc4.c
+++ b/src/cmProc4.c
@@ -3342,6 +3342,8 @@ cmRC_t  _cmScModActivateGroup( cmScModulator* p, cmScModEntry_t* ep )
     {
       unsigned idx = 0;
 
+      printf("Activating:%s\n",cmSymTblLabel(p->stH,ep->beg.symId));
+        
       return  _cmScModActivateEntries( p, g->earray, &idx, g->en, ep->beg.symId );
     }
 

From 220ca1577c6ce07d9979bc007a7a6648c1ab4c09 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:20:13 -0400
Subject: [PATCH 14/21] cmDspKr.c : Update 'Kr2' to use input igain variable.

---
 src/dsp/cmDspKr.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/dsp/cmDspKr.c b/src/dsp/cmDspKr.c
index 5af96bb..187a233 100644
--- a/src/dsp/cmDspKr.c
+++ b/src/dsp/cmDspKr.c
@@ -302,6 +302,8 @@ enum
   kMixKr2Id,
 
   kWetKr2Id,
+  kIgainKr2Id,
+  
   kAudioInKr2Id,
   kAudioOutKr2Id
 };
@@ -320,7 +322,7 @@ cmDspInst_t*  _cmDspKr2Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned s
 {
   cmDspVarArg_t args[] =
   {
-    { "wndn",    kWndSmpCntKr2Id,   0, 0,   kInDsvFl  | kUIntDsvFl   | kReqArgDsvFl,   "Window sample count"   },
+    { "wndn",    kWndSmpCntKr2Id,   0, 0,   kInDsvFl  | kUIntDsvFl   | kReqArgDsvFl,   "Window sample count" },
     { "hopf",    kHopFactKr2Id,     0, 0,   kInDsvFl  | kUIntDsvFl   | kOptArgDsvFl,   "Hop factor" },
     
     { "ceil",    kCeilKr2Id,     0, 0,   kInDsvFl  | kDoubleDsvFl | kOptArgDsvFl,   "Ceiling" },
@@ -333,6 +335,8 @@ cmDspInst_t*  _cmDspKr2Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned s
     { "mix",     kMixKr2Id,         0, 0,   kInDsvFl  | kDoubleDsvFl | kOptArgDsvFl,   "Mix"},
     
     { "wet",     kWetKr2Id,         0, 0,   kInDsvFl  | kSampleDsvFl,                  "Wet mix level."},
+    { "igain",   kIgainKr2Id,       0, 0,   kInDsvFl  | kDoubleDsvFl | kOptArgDsvFl,   "Input gain."},
+    
     { "in",      kAudioInKr2Id,     0, 0,   kInDsvFl  | kAudioBufDsvFl, "Audio Input" },
     { "out",     kAudioOutKr2Id,    0, 1,   kOutDsvFl | kAudioBufDsvFl, "Audio Output" },
     { NULL, 0, 0, 0, 0 }
@@ -355,7 +359,10 @@ cmDspInst_t*  _cmDspKr2Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned s
   cmDspSetDefaultDouble( ctx,&p->inst, kMixKr2Id,      0, 0.0 );
   
   cmDspSetDefaultSample( ctx,&p->inst, kWetKr2Id,      0, 1.0);
+  
+  cmDspSetDefaultDouble( ctx,&p->inst, kIgainKr2Id,    0, 0.0 );
 
+  
   //_cmDspKr2CmInit(ctx,p); // initialize the cm library
 
   p->ctx = cmCtxAlloc(NULL,ctx->rpt,ctx->lhH,ctx->stH);
@@ -504,11 +511,15 @@ cmDspRC_t _cmDspKr2Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* ev
     case kWetKr2Id:
       break;
 
+    case kIgainKr2Id:
+      p->sdp->igain = cmDspDouble(inst,kIgainKr2Id);
+      break;
+
     default:
       { assert(0); }
   }
 
-  cmSpecDist2Report(p->sdp);
+  //cmSpecDist2Report(p->sdp);
   
   return rc;
 }

From 90bfc6c05ba3268a115f0b6c3adf799dc414a9f6 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:22:26 -0400
Subject: [PATCH 15/21] cmDspBuiltIn.c, cmDspValue.h/c : text in 'label' object
 can now be updated with any variable type.

---
 src/dsp/cmDspBuiltIn.c | 77 ++++++++++++++++++++++++++++++++++++------
 src/dsp/cmDspValue.c   | 73 +++++++++++++++++++++++++++++++++++++++
 src/dsp/cmDspValue.h   |  1 +
 3 files changed, 141 insertions(+), 10 deletions(-)

diff --git a/src/dsp/cmDspBuiltIn.c b/src/dsp/cmDspBuiltIn.c
index 4f3dc02..eea6070 100644
--- a/src/dsp/cmDspBuiltIn.c
+++ b/src/dsp/cmDspBuiltIn.c
@@ -1702,7 +1702,8 @@ cmDspClass_t*  cmMeterClassCons( cmDspCtx_t* ctx )
 enum
 {
   kInLbId,
-  kAlignLbId
+  kAlignLbId,
+  kTextLbId
 };
 
 cmDspClass_t _cmLabelDC;
@@ -1710,27 +1711,68 @@ cmDspClass_t _cmLabelDC;
 typedef struct
 {
   cmDspInst_t inst;
+  cmChar_t* label;
 } cmDspLabel_t;
 
 cmDspInst_t*  _cmDspLabelAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
 {
-  cmDspVarArg_t args[] =
+  const unsigned  argCnt = 3;
+  cmDspVarArg_t   args[argCnt+1];
+  cmChar_t*       label  = NULL;
+  unsigned        align  = kLeftAlignDuiId;
+  va_list         vl1;
+  
+  va_copy(vl1,vl);
+
+  if( va_cnt < 1 )
   {
-    { "in",   kInLbId,    0, 0,  kInDsvFl | kStrzDsvFl  | kReqArgDsvFl,  "LabelText" },
-    { "align",kAlignLbId, 0, 0,  kInDsvFl | kUIntDsvFl  | kOptArgDsvFl,  "Alignment 0=right 1=left 2=center" },
-    { NULL, 0, 0, 0, 0 }
-  };
+    va_end(vl1);
+    cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'label' constructor argument list must contain at least one argument.");
+    return NULL;
+  }
 
-  cmDspLabel_t* p = cmDspInstAlloc(cmDspLabel_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
+  // get the default label
+  const char* clabel = va_arg(vl,const char*);
+  if( clabel != NULL && strlen(clabel) > 0 )
+  {
+    label = cmLhAllocStr(ctx->lhH,clabel);
+    printf("%s\n",label);
+  }
 
-  cmDspSetDefaultDouble(ctx, &p->inst, kAlignLbId,  0.0, kLeftAlignDuiId);
+  // if an alignment id was provided
+  if( va_cnt > 1 )
+    align = va_arg(vl,double);
 
+  // setup the arg. config. array.
+  cmDspArgSetup(ctx,args+0,"in",   cmInvalidId, kInLbId,    0,0, kInDsvFl | kTypeDsvMask, "Input to set label" );
+  cmDspArgSetup(ctx,args+1,"align",cmInvalidId, kAlignLbId, 0,0, kInDsvFl | kUIntDsvFl,   "Justification: 0=right 1=center 2=left" );
+  cmDspArgSetup(ctx,args+2,"text", cmInvalidId, kTextLbId,  0,0, kInDsvFl | kStrzDsvFl,   "Label text");
+  cmDspArgSetupNull(args + argCnt);
+
+  // create the instance
+  cmDspLabel_t* p = cmDspInstAlloc(cmDspLabel_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl1);
+
+  p->label = label;
+
+  // set the default variable values
+  cmDspSetDefaultDouble(ctx, &p->inst, kAlignLbId,  0.0, align);
+  cmDspSetDefaultStrcz( ctx, &p->inst, kTextLbId,  NULL, label==NULL ? "" : label );
+  
   // create the UI control
-  cmDspUiLabelCreate(ctx,&p->inst,kInLbId,kAlignLbId);
+  cmDspUiLabelCreate(ctx,&p->inst,kTextLbId,kAlignLbId);
 
+  va_end(vl1);
+  
   return &p->inst;
 }
 
+cmDspRC_t _cmDspLabelFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
+{
+  cmDspLabel_t* p = (cmDspLabel_t*)inst;
+  cmLhFree(ctx->lhH,p->label);
+  return kOkDspRC;
+}
+
 cmDspRC_t _cmDspLabelReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
 {
   cmDspApplyAllDefaults(ctx,inst);
@@ -1739,6 +1781,21 @@ cmDspRC_t _cmDspLabelReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t*
 
 cmDspRC_t _cmDspLabelRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
 {
+  const unsigned bN = 128;
+  cmChar_t b[ bN+1 ];
+
+  // if this event is arriving on the 'in' port ...
+  if( evt->dstVarId == kInLbId )
+  {
+    cmDspLabel_t* p = (cmDspLabel_t*)inst;
+    // ... then convert it to a string
+    cmDsvToString( evt->valuePtr, p->label, b, bN );
+
+    // and set the 'label' variable 
+    return cmDspSetStrcz(ctx, inst, kTextLbId, b );
+  }
+
+  
   return cmDspSetEvent(ctx,inst,evt);
 }
 
@@ -1747,7 +1804,7 @@ cmDspClass_t*  cmLabelClassCons( cmDspCtx_t* ctx )
   cmDspClassSetup(&_cmLabelDC,ctx,"Label",
     NULL,
     _cmDspLabelAlloc,
-    NULL,
+    _cmDspLabelFree,
     _cmDspLabelReset,
     NULL,
     _cmDspLabelRecv,
diff --git a/src/dsp/cmDspValue.c b/src/dsp/cmDspValue.c
index 1f5fc30..8a97b32 100644
--- a/src/dsp/cmDspValue.c
+++ b/src/dsp/cmDspValue.c
@@ -1527,3 +1527,76 @@ void cmDsvPrint( const cmDspValue_t* vp, const cmChar_t* label, cmRpt_t* rpt )
     }
   }
 }
+
+void cmDsvToString( const cmDspValue_t* vp, const cmChar_t* label, cmChar_t* s, unsigned sN )
+{
+  vp = _vcptr(vp); 
+
+  const char* noLbl="";
+  const char* lbl  = label==NULL? noLbl : label;
+
+  if( cmDsvIsMtx(vp) )
+  {
+    unsigned i,j;
+    unsigned rn = cmDsvCols(vp);
+    unsigned cn = cmDsvRows(vp);
+    for(i=0; i<rn; ++i)
+    {
+      for(j=0; j<cn && sN>2; ++j)
+      {
+        switch( cmDsvBasicType(vp) )
+        {
+          case kCharDsvFl:   snprintf(s,sN,"%s%c ",lbl,vp->u.m.u.cp[  (j*rn) + i ]); break;
+          case kUCharDsvFl:  snprintf(s,sN,"%s%c ",lbl,vp->u.m.u.ucp[ (j*rn) + i ]); break;
+          case kShortDsvFl:  snprintf(s,sN,"%s%i ",lbl,vp->u.m.u.sp[  (j*rn) + i ]); break;
+          case kUShortDsvFl: snprintf(s,sN,"%s%i ",lbl,vp->u.m.u.usp[ (j*rn) + i ]); break;
+          case kLongDsvFl:   snprintf(s,sN,"%s%li ",lbl,vp->u.m.u.lp[  (j*rn) + i ]); break;
+          case kULongDsvFl:  snprintf(s,sN,"%s%li ",lbl,vp->u.m.u.ulp[ (j*rn) + i ]); break;
+          case kIntDsvFl:    snprintf(s,sN,"%s%i ",lbl,vp->u.m.u.ip[  (j*rn) + i ]); break;
+          case kUIntDsvFl:   snprintf(s,sN,"%s%i ",lbl,vp->u.m.u.up[  (j*rn) + i ]); break;
+          case kFloatDsvFl:  snprintf(s,sN,"%s%f ",lbl,vp->u.m.u.fp[  (j*rn) + i ]); break;
+          case kDoubleDsvFl: snprintf(s,sN,"%s%f ",lbl,vp->u.m.u.dp[  (j*rn) + i ]); break;
+          case kSampleDsvFl: snprintf(s,sN,"%s%f ",lbl,vp->u.m.u.ap[  (j*rn) + i ]); break;
+          case kRealDsvFl:   snprintf(s,sN,"%s%f ",lbl,vp->u.m.u.rp[  (j*rn) + i ]); break;
+          case kStrzDsvFl:   snprintf(s,sN,"%s%s ",lbl,vp->u.m.u.zp[  (j*rn) + i ]); break;
+          case kJsonDsvFl:   cmJsonLeafToString(vp->u.m.u.jp[ (j*rn) + i ],s,sN);   break;    
+          default:
+            { assert(0); }
+        }
+
+        unsigned n = strlen(s);
+        sN -= n;
+        s  += n;
+        
+        
+      }
+      if( sN > 2 )
+        snprintf(s,sN,"\n");
+    }
+  }
+  else
+  {
+    switch( cmDsvBasicType(vp) )
+    {
+      case kBoolDsvFl:   snprintf(s,sN,"%s%s ",lbl,vp->u.b ? "true" : "false"); break;
+      case kCharDsvFl:   snprintf(s,sN,"%s%c ",lbl,vp->u.c);  break;
+      case kUCharDsvFl:  snprintf(s,sN,"%s%c ",lbl,vp->u.uc); break;
+      case kShortDsvFl:  snprintf(s,sN,"%s%i ",lbl,vp->u.s);  break;
+      case kUShortDsvFl: snprintf(s,sN,"%s%i ",lbl,vp->u.us); break;
+      case kLongDsvFl:   snprintf(s,sN,"%s%li ",lbl,vp->u.l);  break;
+      case kULongDsvFl:  snprintf(s,sN,"%s%li ",lbl,vp->u.ul); break;
+      case kIntDsvFl:    snprintf(s,sN,"%s%i ",lbl,vp->u.i);  break;
+      case kUIntDsvFl:   snprintf(s,sN,"%s%i ",lbl,vp->u.u);  break;
+      case kFloatDsvFl:  snprintf(s,sN,"%s%f ",lbl,vp->u.f);  break;
+      case kDoubleDsvFl: snprintf(s,sN,"%s%f ",lbl,vp->u.d);  break;
+      case kSampleDsvFl: snprintf(s,sN,"%s%f ",lbl,vp->u.a);  break;
+      case kRealDsvFl:   snprintf(s,sN,"%s%f ",lbl,vp->u.r);  break;
+      case kPtrDsvFl:    snprintf(s,sN,"%s%p ",lbl,vp->u.vp); break;
+      case kStrzDsvFl:   snprintf(s,sN,"%s%s ",lbl,vp->u.z);  break;
+      case kSymDsvFl:    snprintf(s,sN,"%s%i ",lbl,vp->u.u); break;
+      case kJsonDsvFl:   cmJsonLeafToString(vp->u.j,s,sN);   break;    
+      default:
+        { assert(0); }
+    }
+  }
+}
diff --git a/src/dsp/cmDspValue.h b/src/dsp/cmDspValue.h
index dee73fd..4181339 100644
--- a/src/dsp/cmDspValue.h
+++ b/src/dsp/cmDspValue.h
@@ -331,6 +331,7 @@ extern "C" {
   cmDsvRC_t cmDsvDeserializeJson(     cmDspValue_t* vp, cmJsonH_t jsH );
 
   void     cmDsvPrint( const cmDspValue_t* vp, const cmChar_t* label,  cmRpt_t* rpt );
+  void     cmDsvToString( const cmDspValue_t* vp, const cmChar_t* label,  cmChar_t* s, unsigned sN );
 
   //)
   

From c9dc4941789a1cdacdec008f2791808f8aa196a3 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:23:16 -0400
Subject: [PATCH 16/21] cmDspFx.c, cmDspFx.c : If the second character in the
 'scalarOp' object is '$' then any input triggers an output.

---
 src/dsp/cmDspFx.c  | 32 ++++++++++++++++++++++++--------
 src/dsp/cmDspPgm.c | 20 +++++++++++++++++---
 2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/src/dsp/cmDspFx.c b/src/dsp/cmDspFx.c
index 67252a5..bbb1c68 100644
--- a/src/dsp/cmDspFx.c
+++ b/src/dsp/cmDspFx.c
@@ -2909,6 +2909,7 @@ typedef struct cmDspScalar_str
   cmDspInst_t          inst;
   _cmDspScalarOpFunc_t func;
   unsigned             inPortCnt;
+  bool                 allActiveFl;                  
 } cmDspScalarOp_t;
 
 cmDspRC_t _cmDspScalarOpFuncMult(cmDspCtx_t* ctx, cmDspInst_t* inst )
@@ -2966,6 +2967,7 @@ cmDspInst_t*  _cmDspScalarOpAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsig
   double             dfltVal[ inPortCnt ];
   unsigned           i;
   _cmDspScalarOpFunc_t fp = NULL;
+  bool allActiveFl = false;
 
   // validate the count of input ports
   if( inPortCnt == 0 )
@@ -2974,12 +2976,26 @@ cmDspInst_t*  _cmDspScalarOpAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsig
     goto errLabel;
   }
 
-  // locate the operation function
-  if( strcmp(opIdStr,"*") == 0 )
-    fp = _cmDspScalarOpFuncMult;
-  else
-    if( strcmp(opIdStr,"+") == 0 )
-      fp = _cmDspScalarOpFuncAdd;
+  if( opIdStr != NULL )
+  {
+    switch( opIdStr[0] )
+    {
+      case '*':
+        fp = _cmDspScalarOpFuncMult;
+        break;
+      case '+':
+        fp = _cmDspScalarOpFuncAdd;
+        break;      
+    }
+
+    // if the second character of the operator string is '$' then all input ports trigger an output
+    if( strlen( opIdStr ) > 0 && opIdStr[1]=='$' )
+      allActiveFl = true;
+    
+    
+  }
+  
+  
 
   // validate the operation function
   if( fp == NULL )
@@ -3012,7 +3028,7 @@ cmDspInst_t*  _cmDspScalarOpAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsig
 
   p->inPortCnt = inPortCnt;
   p->func      = fp;
-  
+  p->allActiveFl = allActiveFl;
   va_end(vl1);
 
   return &p->inst;
@@ -3039,7 +3055,7 @@ cmDspRC_t _cmDspScalarOpRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_
 
   if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
   {
-    if( evt->dstVarId == kBaseOpdSoId )
+    if( evt->dstVarId == kBaseOpdSoId || p->allActiveFl )
       p->func(ctx,inst);
   }
 
diff --git a/src/dsp/cmDspPgm.c b/src/dsp/cmDspPgm.c
index 6725238..fb347bb 100644
--- a/src/dsp/cmDspPgm.c
+++ b/src/dsp/cmDspPgm.c
@@ -898,7 +898,7 @@ cmDspRC_t _cmDspSysPgm_UiTest(cmDspSysH_t h, void** userPtrPtr )
   cmDspInst_t* prp = cmDspSysAllocInst(h,"Printer", NULL,   1, ">" );
   cmDspInst_t* mtp = cmDspSysAllocInst(h,"Meter", "meter",  3, 0.0,  0.0, 4.0);
   cmDspInst_t* ctp = cmDspSysAllocInst(h,"Counter", NULL,   3, 0.0, 10.0, 1.0 );
-                     cmDspSysAllocInst(h,"Label",  "label1", 1, "label2");
+  cmDspInst_t* lbl = cmDspSysAllocInst(h,"Label",  "label1", 1, "label2");
   if((rc = cmDspSysLastRC(h)) != kOkDspRC )
     return rc;
 
@@ -924,6 +924,8 @@ cmDspRC_t _cmDspSysPgm_UiTest(cmDspSysH_t h, void** userPtrPtr )
   cmDspSysInstallCb(h, chb, "out", prp, "in", NULL );
   cmDspSysInstallCb(h, chb, "sym", prp, "in", NULL );
 
+  cmDspSysInstallCb(h, mdp, "val", lbl, "in", NULL );
+
   return rc;
 
 }
@@ -2055,8 +2057,8 @@ cmDspRC_t _cmDspSysPgm_ScalarOp( cmDspSysH_t h, void** userPtrPtr )
 {
   cmDspRC_t rc;
 
-  cmDspInst_t* add   = cmDspSysAllocInst(   h, "ScalarOp", NULL,  6, 2, "+", "in-0", 0.0, "in-1", 0.0 );
-  cmDspInst_t* mul0  = cmDspSysAllocInst(   h, "ScalarOp", NULL,  6, 2, "*", "in-0", 0.0, "in-1", 0.0 );
+  cmDspInst_t* add   = cmDspSysAllocInst(   h, "ScalarOp", NULL,  6, 2, "+",  "in-0", 0.0, "in-1", 0.0 );
+  cmDspInst_t* mul0  = cmDspSysAllocInst(   h, "ScalarOp", NULL,  6, 2, "*$", "in-0", 0.0, "in-1", 0.0 );
   cmDspInst_t* mul1  = cmDspSysAllocInst(   h, "ScalarOp", NULL,  6, 2, "*", "in-0", 0.0, "in-1", 0.0 );
   cmDspInst_t* in    = cmDspSysAllocScalar( h, "Input",      0.0, 10.0, 0.001, 0.0);
   cmDspInst_t* in_m  = cmDspSysAllocScalar( h, "Input_M",    0.0, 10.0, 0.001, 0.0);
@@ -2068,6 +2070,18 @@ cmDspRC_t _cmDspSysPgm_ScalarOp( cmDspSysH_t h, void** userPtrPtr )
   if((rc = cmDspSysLastRC(h)) != kOkDspRC )
     goto errLabel;
 
+  // Notice that changing 'in' or 'in_m' causes 'out' to be recomputed, but other
+  // changes are cached prior to 'add'.  This prevents the program from going into
+  // an infinite loop.
+  //
+  //     in   -> mult0
+  //     in_m -> mult0--+
+  // +-->fb   -> mult1  +----> add
+  // |   fb_m -> mult1-------> add --------+------> out
+  // |                                     |
+  // +-------------------------------------+
+  //
+  
   cmDspSysInstallCb( h, in,    "val", mul0, "in-0", NULL );
   cmDspSysInstallCb( h, in_m,  "val", mul0, "in-1", NULL );
   cmDspSysInstallCb( h, fb,    "val", mul1, "in-0", NULL );

From 4edc0c968d51695d4d37c0750d74650da026797b Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 3 Aug 2020 12:25:41 -0400
Subject: [PATCH 17/21] cmDspPgmKrChain2.c,cmDspPgmKrTimeLineLiteAf.c : many
 changes to refine output.

---
 src/dsp/cmDspPgmKrChain2.c         | 17 +++++++++---
 src/dsp/cmDspPgmKrTimeLineLiteAf.c | 43 +++++++++++++++++-------------
 2 files changed, 37 insertions(+), 23 deletions(-)

diff --git a/src/dsp/cmDspPgmKrChain2.c b/src/dsp/cmDspPgmKrChain2.c
index 21aaf78..051b509 100644
--- a/src/dsp/cmDspPgmKrChain2.c
+++ b/src/dsp/cmDspPgmKrChain2.c
@@ -105,6 +105,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   unsigned paramRtChCnt = 2;
   cmDspInst_t* wnd_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
   cmDspInst_t* hop_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
+  cmDspInst_t* ign_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
   cmDspInst_t* cel_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
   cmDspInst_t* exp_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
   cmDspInst_t* mix_rt   = cmDspSysAllocInst(h, "Router",      NULL,  2,  paramRtChCnt, paramRtChCnt-1 );
@@ -118,7 +119,9 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   cmDspInst_t* kr1  = cmDspSysAllocInst(h, "Kr2",        NULL,   2, krWndSmpCnt, krHopFact );
   cmDspInst_t* xfad = cmDspSysAllocInst(h, "Xfader",     NULL,   3, xfadeChCnt,  xfadeMs, xfadeInitFl ); 
   cmDspInst_t* mix  = cmDspSysAllocInst(h, "AMix",       NULL,   3, xfadeChCnt,  mixGain, mixGain );
-  cmDspInst_t* cmp  = cmDspSysAllocInst(h, "Compressor", NULL,   8, cmpBypassFl, cmpThreshDb, cmpRatio_num, cmpAtkMs, cmpRlsMs, cmpMakeup, cmpWndMs, cmpWndMaxMs ); 
+  cmDspInst_t* cmp  = cmDspSysAllocInst(h, "Compressor", NULL,   8, cmpBypassFl, cmpThreshDb, cmpRatio_num, cmpAtkMs, cmpRlsMs, cmpMakeup, cmpWndMs, cmpWndMaxMs );
+
+  
 
   // Internal audio connections
   cmDspSysConnectAudio(h, kr0,  "out",   xfad, "in-0");
@@ -158,6 +161,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   cmDspSysNewColumn(h,0);
   cmDspInst_t* wnd_ctl = cmDspSysAllocMsgListP(h,preGrpSymId,NULL, lbl("WndSmpCnt"), NULL, "wndSmpCnt", 2);
   cmDspInst_t* hop_ctl = cmDspSysAllocMsgListP(h,preGrpSymId,NULL, lbl("HopFact"),   NULL, "hopFact",   2);
+  cmDspInst_t* ign_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("In Gain"),   0.0,  10.0, 0.001, 1.0 );
   cmDspInst_t* cel_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("Ceiling"),   0.0, 100.0, 0.1,  30.0 );
   cmDspInst_t* exp_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("Expo"),    -10.0,  10.0, 0.01,  2.0 );
   cmDspInst_t* mix_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("Mix"),       0.0,   1.0, 0.01,  0.0 );    
@@ -166,7 +170,6 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   cmDspInst_t* lwr_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("Lwr slope"), 0.3,  10.0, 0.01,  2.0 );
   cmDspInst_t* wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId,NULL, lbl("Wet Dry"),   0.0,   1.0, 0.001, 1.0 );
 
-  
   cmDspSysInstallCb(h, wnd_ctl, "out",         wnd_rt, "f-in",    NULL );
   cmDspSysInstallCb(h, achan,   "ch",          wnd_rt, "sel",     NULL );   // ach->rt sel
   cmDspSysInstallCb(h, wnd_rt,  "f-out-0",     kr0,    "wndn",    NULL );   // wndn->kr
@@ -176,7 +179,12 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   cmDspSysInstallCb(h, achan,   "ch",          hop_rt, "sel",     NULL );   // ach->rt sel
   cmDspSysInstallCb(h, hop_rt,  "f-out-0",     kr0,    "hopf",    NULL );   // hopf->kr
   cmDspSysInstallCb(h, hop_rt,  "f-out-1",     kr1,    "hopf",    NULL );   // hopf->kr
-  
+
+  cmDspSysInstallCb(h, ign_ctl,     "val",     ign_rt, "f-in",    NULL );
+  cmDspSysInstallCb(h, achan,       "ch",      ign_rt, "sel",     NULL );   // ach->rt sel
+  cmDspSysInstallCb(h, ign_rt,      "f-out-0", kr0,    "igain",   NULL );   // ign->kr
+  cmDspSysInstallCb(h, ign_rt,      "f-out-1", kr1,    "igain",   NULL );   // ign->kr
+    
   cmDspSysInstallCb(h, thr_ctl,     "val",     thr_rt, "f-in",    NULL );
   cmDspSysInstallCb(h, achan,       "ch",      thr_rt, "sel",     NULL );   // ach->rt sel
   cmDspSysInstallCb(h, thr_rt,      "f-out-0", kr0,    "thrh",    NULL );   // thr->kr
@@ -260,6 +268,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
 
   
   cmDspSysInstallCb(h, modp, mlbl("hop"),  hop_ctl, "sel", NULL );
+  cmDspSysInstallCb(h, modp, mlbl("ign"),  ign_ctl, "val", NULL );
   cmDspSysInstallCb(h, modp, mlbl("ceil"), cel_ctl, "val", NULL );
   cmDspSysInstallCb(h, modp, mlbl("expo"), exp_ctl, "val", NULL );
   cmDspSysInstallCb(h, modp, mlbl("mix"),  mix_ctl, "val", NULL );
@@ -267,7 +276,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c,  unsigned preGrpS
   cmDspSysInstallCb(h, modp, mlbl("upr"),  upr_ctl, "val", NULL );
   cmDspSysInstallCb(h, modp, mlbl("lwr"),  lwr_ctl, "val", NULL );
   cmDspSysInstallCb(h, modp, mlbl("wet"),  wet_ctl, "val", NULL );
-  cmDspSysInstallCb(h, modp, mlbl("sw"),   achan,       "trig", NULL ); // See also: amp.sfloc->achan.trig
+  cmDspSysInstallCb(h, modp, mlbl("sw"),   achan,  "trig", NULL ); // See also: amp.sfloc->achan.trig
 
   
   c->achan = achan; 
diff --git a/src/dsp/cmDspPgmKrTimeLineLiteAf.c b/src/dsp/cmDspPgmKrTimeLineLiteAf.c
index 91e5137..dc07094 100644
--- a/src/dsp/cmDspPgmKrTimeLineLiteAf.c
+++ b/src/dsp/cmDspPgmKrTimeLineLiteAf.c
@@ -83,6 +83,9 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   unsigned   cmpPreGrpSymId  = cmDspSysPresetRegisterGroup(h,"tl_cmp"); 
 
   cmDspTlXform_t c0,c1;
+  memset(&c0,0,sizeof(c0));
+  memset(&c1,0,sizeof(c1));
+    
 
   cmDspSysNewPage(h,"Controls-0");
   _cmDspSys_TlXformChain(h, &c0, preGrpSymId, cmpPreGrpSymId, amp, modp, 0, 0 );
@@ -121,10 +124,10 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspInst_t* ogainW = cmDspSysAllocInst(h,"Scalar", "Wet Master",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
   cmDspInst_t* ogainD = cmDspSysAllocInst(h,"Scalar", "Dry Master",   5, kNumberDuiId, 0.0,   10.0,0.01,   1.0 );  
 
-  cmDspInst_t* gmult0  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
-  cmDspInst_t* gmult1  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
-  cmDspInst_t* gmult2  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
-  cmDspInst_t* gmult3  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult0  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*$", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult1  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*$", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult2  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*$", "in-0", 1.0, "in-1", 1.0 );
+  cmDspInst_t* gmult3  = cmDspSysAllocInst(h,"ScalarOp", NULL, 6, 2, "*$", "in-0", 1.0, "in-1", 1.0 );
   
  
   
@@ -136,8 +139,10 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
   cmDspInst_t* mi0p    = cmDspSysAllocInst(h,"AMeter","In 0",  0);
   cmDspInst_t* mi1p    = cmDspSysAllocInst(h,"AMeter","In 1",  0);
 
-  cmDspInst_t* meas     = cmDspSysAllocInst(h,"Scalar", "Begin Meas", 5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );  
-  cmDspInst_t* emeas    = cmDspSysAllocInst(h,"Scalar", "End   Meas", 5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );  
+  cmDspInst_t* meas    = cmDspSysAllocInst(h,"Scalar", "Begin Meas", 5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );  
+  cmDspInst_t* eloc    = cmDspSysAllocInst(h,"Scalar", "End   Loc",  5, kNumberDuiId, 1.0, 1000.0, 1.0, 1.0 );
+  cmDspInst_t* sfp_loc = cmDspSysAllocInst(h,"Label",  NULL, 1, "sf loc:");
+
   cmDspSysInstallCb( h, meas, "val", scp, "meas", NULL);
   cmDspSysInstallCb( h, meas, "val", tlp, "meas", NULL);
   
@@ -323,36 +328,36 @@ cmDspRC_t _cmDspSysPgm_TimeLineLiteAf(cmDspSysH_t h, void** userPtrPtr )
 
   // score follower to recd_play,modulator and printers
   cmDspSysInstallCb(h, sfp, "out",     modp,    "index", NULL );
-  cmDspSysInstallCb(h, sfp, "recent",  prp,     "in",  NULL );  // report 'recent' but only act on 'max' loc index
+  cmDspSysInstallCb(h, sfp, "recent",  sfp_loc,   "in",  NULL );  // report 'recent' but only act on 'max' loc index
 
-  cmDspSysInstallCb( h, emeas, "val", its,  "off-int", NULL);
+  cmDspSysInstallCb( h, eloc , "val", its,  "off-int", NULL);
   cmDspSysInstallCb( h, sfp,   "out", its,  "in",  NULL);
   cmDspSysInstallCb( h, its,   "out", offb, "in",  NULL);
   cmDspSysInstallCb( h, its,   "out", prp, "in",  NULL);
   
 
-  cmDspSysInstallCb(h, modp, "dgain0",  ogain0, "val",  NULL );
+  cmDspSysInstallCb(h, modp, "dgain0",  ogain0, "val",  NULL ); // mod -> ogain
   cmDspSysInstallCb(h, modp, "dgain1",  ogain1, "val",  NULL );
   cmDspSysInstallCb(h, modp, "wgain0",  ogain2, "val",  NULL );
   cmDspSysInstallCb(h, modp, "wgain1",  ogain3, "val",  NULL );
 
 
-  cmDspSysInstallCb(h, ogain0,  "val", gmult0, "in-0", NULL );
-  cmDspSysInstallCb(h, ogainD,  "val", gmult0, "in-1", NULL );
-  
+  cmDspSysInstallCb(h, ogain0,  "val", gmult0, "in-0", NULL ); // ogain scalars -> gmult 0
   cmDspSysInstallCb(h, ogain1,  "val", gmult1, "in-0", NULL );
-  cmDspSysInstallCb(h, ogainD,  "val", gmult1, "in-1", NULL );
-  
   cmDspSysInstallCb(h, ogain2,  "val", gmult2, "in-0", NULL );
-  cmDspSysInstallCb(h, ogainW,  "val", gmult2, "in-1", NULL );
   cmDspSysInstallCb(h, ogain3,  "val", gmult3, "in-0", NULL );
+  
+  cmDspSysInstallCb(h, ogainD,  "val", gmult0, "in-1", NULL ); // master scalars -> gmult 1
+  cmDspSysInstallCb(h, ogainD,  "val", gmult1, "in-1", NULL );
+  cmDspSysInstallCb(h, ogainW,  "val", gmult2, "in-1", NULL );
   cmDspSysInstallCb(h, ogainW,  "val", gmult3, "in-1", NULL );
   
-  cmDspSysInstallCb(h, gmult0, "out", lmix, "gain-0", NULL );   // output gain control - dry 0
-  cmDspSysInstallCb(h, gmult1, "out", rmix, "gain-0", NULL );   //                       dry 1
-  cmDspSysInstallCb(h, gmult2, "out", lmix, "gain-1", NULL );   //                       wet 0
-  cmDspSysInstallCb(h, gmult3, "out", rmix, "gain-1", NULL );   //                       wet 1
+  cmDspSysInstallCb(h, gmult0, "out", lmix, "gain-0", NULL );   // gmult -> wdmix - l dry 
+  cmDspSysInstallCb(h, gmult1, "out", rmix, "gain-0", NULL );   //                  r dry
+  cmDspSysInstallCb(h, gmult2, "out", lmix, "gain-1", NULL );   //                  l wet
+  cmDspSysInstallCb(h, gmult3, "out", rmix, "gain-1", NULL );   //                  r wet
 
+  //cmDspSysInstallCb(h, gmult2, "out", prp, "in", NULL );
 
   return rc;
 }

From 4942b48206bf1013420e53c80ac79e029d5ed003 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 4 Aug 2020 11:21:13 -0400
Subject: [PATCH 18/21] cmXScore.c : A warning is now reported when a
 overlapped note triggers a 'shorten/shift' resolution. Tick number added to
 pedal state warning messages.

---
 src/app/cmXScore.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c
index cf8479a..5892a42 100644
--- a/src/app/cmXScore.c
+++ b/src/app/cmXScore.c
@@ -1368,7 +1368,10 @@ void  _cmXScoreProcessOverlappingNotes( cmXScore_t* p )
               fnp->tied_dur -= d;
 
             // move the second note just past it
-            np->tick       = fnp->tick + fnp->tied_dur + 1;
+            np->tick       = fnp->tick + fnp->tied_dur;
+
+            cmErrWarnMsg(&p->err,kOverlapWarnXsRC,"A shorten/shift operation was done to reconcile two overlapped %s (ticks:%i %i) notes in measure %i.",cmMidiToSciPitch(np->pitch,NULL,0),fnp->tick,np->tick,np->meas->number);
+
           }
         }
     }
@@ -1587,7 +1590,7 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
             
           case kDampDnXsFl:            
             if( dnp != NULL )
-              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper down not preceded by damper up in measure:%i.",mp->number);
+              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper down not preceded by damper up in measure:%i tick:%i.",mp->number,np->tick);
             else
               dnp = np;
 
@@ -1597,7 +1600,7 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
             
           case kDampUpXsFl:
             if( dnp == NULL )
-              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up not preceded by damper down in measure:%i.",mp->number);
+              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up not preceded by damper down in measure:%i tick:%i.",mp->number,np->tick);
             else
             {
               dnp->duration = np->tick - dnp->tick;
@@ -1611,7 +1614,7 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
             
           case kDampUpDnXsFl:
             if( dnp == NULL )
-              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up/down not preceded by damper down in measure:%i.",mp->number);
+              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up/down not preceded by damper down in measure:%i tick:%i.",mp->number,np->tick);
             else
             {            
               dnp->duration = np->tick - dnp->tick;
@@ -1624,7 +1627,7 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
             
           case kSostDnXsFl:
             if( snp != NULL )
-              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto down not preceded by sostenuto up in measure:%i.",mp->number);
+              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto down not preceded by sostenuto up in measure:%i tick:%i.",mp->number, np->tick);
             else
               snp = np;
             
@@ -1634,7 +1637,7 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
             
           case kSostUpXsFl:
             if( snp == NULL )
-              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto up not preceded by sostenuto down in measure:%i.",mp->number);
+              cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto up not preceded by sostenuto down in measure:%i tick:%i.",mp->number,np->tick);
             else
             {
               snp->duration = np->tick - snp->tick;
@@ -2102,7 +2105,7 @@ cmXsNote_t*  _cmXsReorderFindNote( cmXScore_t* p, unsigned measNumb, const cmXsR
         {
 
 
-          if( 0 /*mp->number==19*/ )
+          if( 0 /*mp->number==27*/ )
             printf("voice: %i %i loc:%i %i  tick:%i %i pitch:%i %i rval:%f %f idx:%i %i \n",
               np->voice->id, r->voice, 
               np->locIdx ,  r->locIdx ,

From b81204a8d22f1b91ea2764d258b43121d835a0b3 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Mon, 24 Aug 2020 09:48:30 -0400
Subject: [PATCH 19/21] cmMsgProtocol.h/cmDspClass.h/cmDspUi.c : Added
 cmDspUi.c:cmDspProgram() is done to send a message to the host from the DSP
 process when the DSP program is done.

---
 src/cmMsgProtocol.h  | 3 ++-
 src/dsp/cmDspClass.h | 5 ++++-
 src/dsp/cmDspUi.c    | 4 ++++
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/cmMsgProtocol.h b/src/cmMsgProtocol.h
index d4f29db..e96e96a 100644
--- a/src/cmMsgProtocol.h
+++ b/src/cmMsgProtocol.h
@@ -41,6 +41,7 @@ extern "C" {
     kDeviceDuiId,          // ui<--eng device label
     kProgramDuiId,         // ui<--eng program label
     kProgramDfltDuiId,     // ui<--eng dflt program label
+    kPgmDoneDuiId,         // ui<--end the program is done 
     
     // The following selId's are used by cmAudDsp to indicate various commands.
     kSetAudioCfgDuiId,     // 1) select an audio system setup
@@ -54,7 +55,7 @@ extern "C" {
     kSendMsgDuiId,         // forward msg to the audio system
     kDevReportDuiId,       // print a device report
     kPrintPgmDuiId,        // write the currently loaded pgm as a JSON file
-
+    
     kRightAlignDuiId = 0,  // label alignment id used by kLabelDuiId 
     kLeftAlignDuiId,  
     kCenterAlignDuiId
diff --git a/src/dsp/cmDspClass.h b/src/dsp/cmDspClass.h
index c1c4ed0..8d2da2e 100644
--- a/src/dsp/cmDspClass.h
+++ b/src/dsp/cmDspClass.h
@@ -390,6 +390,9 @@ extern "C" {
   // Used to transmit messages to the audio system.
   cmDspRC_t  cmDspSendValueToAudioSys( cmDspCtx_t* ctx, unsigned msgTypeId, unsigned selId, unsigned valId, const cmDspValue_t* valPtr ); 
 
+  // Notify the system that the program is done and can be shutdown
+  cmDspRC_t cmDspProgramIsDone( cmDspCtx_t* ctx );
+  
   // The following functions are used to send message to the UI and are 
   // implemented in cmDspUi.c.  They are declared here because they are 
   // visible to the cmDspInst functions which use them but are not available
@@ -417,7 +420,7 @@ extern "C" {
   cmDspRC_t   cmDspUiFnameCreate(  cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned valVarId, unsigned patVarId, unsigned dirVarId );
   cmDspRC_t   cmDspUiMsgListCreate(cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned height, unsigned listVarId, unsigned selVarId );
 
- 
+  
   //)
   
 #ifdef __cplusplus
diff --git a/src/dsp/cmDspUi.c b/src/dsp/cmDspUi.c
index 2aff36c..4ae728f 100644
--- a/src/dsp/cmDspUi.c
+++ b/src/dsp/cmDspUi.c
@@ -188,6 +188,9 @@ cmDspRC_t _cmDspUiUseInstSymbolAsLabel( cmDspCtx_t* ctx, cmDspInst_t* inst, unsi
 cmDspRC_t  cmDspSendValueToAudioSys( cmDspCtx_t* ctx, unsigned msgTypeId, unsigned selId, unsigned valId, const cmDspValue_t* valPtr )
 { return _cmDspUiMsg(ctx, msgTypeId, selId, 0, NULL, valId, valPtr ); }
 
+cmDspRC_t cmDspProgramIsDone( cmDspCtx_t* ctx )
+{ return _cmDspUiMsg(ctx, kUiSelAsId, kPgmDoneDuiId, 0, NULL, cmInvalidId, NULL ); }
+
 cmDspRC_t   cmDspUiConsolePrint( cmDspCtx_t* ctx, cmChar_t* text )
 {
   cmDspValue_t v;
@@ -514,3 +517,4 @@ cmDspRC_t  cmDspUiNewPage(          cmDspCtx_t* ctx, const cmChar_t* title )
 
   return rc;
 }
+

From 033204dd04c68f43426e093e662e3f3e0b965669 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 13 Oct 2020 15:12:19 -0400
Subject: [PATCH 20/21] cmXScore.h : Added cmXScoreMergedEditFiles().

---
 src/app/cmXScore.c | 638 ++++++++++++++++++++++++++++++++++++++++-----
 src/app/cmXScore.h |   5 +-
 2 files changed, 579 insertions(+), 64 deletions(-)

diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c
index 5892a42..c98abb6 100644
--- a/src/app/cmXScore.c
+++ b/src/app/cmXScore.c
@@ -110,12 +110,14 @@ typedef struct cmXsNote_str
   unsigned                    locIdx;   // location index (chords share the same location index)
   double                      rvalue;   // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...)
   const cmChar_t*             tvalue;   // text value
+  const cmChar_t*             editStr;  // merged manual edit string
 
   unsigned                    evenGroupId;   // eveness group id
   unsigned                    dynGroupId;    // dynamics group id
   unsigned                    tempoGroupId;  // tempo group id
   unsigned                    graceGroupId;  // grace note group id
 
+
   struct cmXsVoice_str*       voice;    // voice this note belongs to
   struct cmXsMeas_str*        meas;     // measure this note belongs to
 
@@ -230,6 +232,33 @@ cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
   return NULL;
 }
 
+cmXsMeas_t* _cmXsFindMeas( cmXsPart_t* part, unsigned measNumb )
+{
+  cmXsMeas_t* m = part->measL;
+  for(; m!=NULL; m=m->link)
+    if( m->number == measNumb )
+      return m;
+
+  return NULL;
+}
+
+cmXsNote_t* _cmXsFindNote( cmXsMeas_t* m, unsigned idx, unsigned midi, double rval, unsigned durtn, unsigned* idxRef )
+{
+  unsigned i;
+  cmXsNote_t* np = m->noteL;
+  for(i=0; np!=NULL; np=np->slink,++i)
+  {
+    //printf("idx:%i %i midi:%i %i rval:%f %f durtn:%i %i\n", i,idx, np->pitch,midi, np->rvalue,rval, np->tied_dur,durtn);
+    if( i>=idx && np->pitch==midi && np->rvalue==rval && np->tied_dur==durtn )
+    {
+      *idxRef = i;
+      return np;
+    }
+  }
+  
+  return NULL;  
+}
+
 cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
 {
   cmXsVoice_t* v;
@@ -612,6 +641,7 @@ cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t*
   cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
   unsigned    voiceId;
 
+  note->pitch   = kInvalidMidiPitch;
   note->meas    = meas;
   note->xmlNode = nnp;
 
@@ -686,6 +716,7 @@ cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_
   cmXsNote_t* note    = cmLhAllocZ(p->lhH,cmXsNote_t,1);
   unsigned    voiceId = 0;    // non-note's are always assigned to voiceId=0;
 
+  note->pitch    = kInvalidMidiPitch;
   note->tick     = tick;
   note->staff    = staff;
   note->flags    = flags;
@@ -798,6 +829,11 @@ cmXsRC_t  _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNo
 
       flags = kSectionXsFl;
     }
+    else
+    {
+      // we only care about 'words' in 'enclosures'
+      pushFl = false;
+    }
   }
   else
 
@@ -2028,7 +2064,7 @@ cmXsRC_t _cmXScoreProcessDynamicForks( cmXScore_t* p )
 //-------------------------------------------------------------------------------------------
 
 
-typedef struct
+typedef struct _cmXsReorder_str
 {
   unsigned    idx;      // Fields from the reordering input file which are
   unsigned    voice;    // used to match the reorder record to 
@@ -2046,8 +2082,26 @@ typedef struct
   unsigned    graceFlags;   // 0=ignore See kXXXGraceXsFl
   unsigned    graceGroupId; // 0=ignore >0=grace note group id
   unsigned    pitch;        // 0=ignore >0 new pitch
+
+  const char* editStr;
+
+  struct _cmXsReorder_str* link;
 } cmXsReorder_t;
 
+typedef struct _cmXsReorderMeas_str
+{
+  unsigned                     measNumb;
+  cmXsReorder_t*               beg;
+  cmXsReorder_t*               end;
+  struct _cmXsReorderMeas_str* link;
+} cmXsReorderMeas_t;
+
+typedef struct
+{
+  cmXsReorderMeas_t* beg;
+  cmXsReorderMeas_t* end;
+} cmXsReorderFile_t;
+
 typedef struct _cmXScoreDynMark_str
 {
   const cmChar_t* mark;   //
@@ -2088,6 +2142,90 @@ _cmXScoreDynMark_t _cmXScoreDynMarkArray[] =
   
 };
 
+cmXsReorderMeas_t* _cmXsReorderFileAllocMeas( cmXScore_t* p, cmXsReorderFile_t* rfp, unsigned measNumb )
+{
+  cmXsReorderMeas_t* m = cmLhAllocZ(p->lhH,cmXsReorderMeas_t,1);
+  m->measNumb          = measNumb;
+            
+  if( rfp->end == NULL )
+  {
+    rfp->beg = m;
+    rfp->end = m;
+  }
+  else
+  {
+    rfp->end->link = m;
+    rfp->end = m;
+  }
+  return m;
+}
+
+cmXsReorderMeas_t* _cmXsReorderFileFindMeas( cmXsReorderFile_t* rfp, unsigned measNumb )
+{
+  cmXsReorderMeas_t* m = rfp->beg;
+  for(; m!=NULL; m=m->link)
+    if( m->measNumb == measNumb )
+      return m;
+
+  return NULL;
+}
+
+
+cmXsReorder_t* _cmXsReorderMeasAllocEvent( cmXScore_t* p, cmXsReorderMeas_t* m )
+{
+  cmXsReorder_t* r = cmLhAllocZ(p->lhH, cmXsReorder_t,1);
+
+  r->midi = kInvalidMidiPitch;
+  
+  if( m->end == NULL )
+  {
+    m->beg = r;
+    m->end = r;
+  }
+  else
+  {
+    m->end->link = r;
+    m->end = r;
+  }
+
+  return r;
+}
+
+// find key in meas (m) by searching after event idx0.
+cmXsReorder_t* _cmXsReorderFindEvent( cmXsReorderMeas_t* m, unsigned idx0, cmXsReorder_t* key )
+{
+  cmXsReorder_t* r;
+  for(r=m->beg; r!=NULL; r=r->link)
+    if( r->idx >= idx0 && r->midi==key->midi && r->rval==key->rval && r->durtn==key->durtn )
+      return r;
+
+  return NULL;
+}
+
+cmXsReorder_t* _cmXsReorderMeasPop( cmXsReorderMeas_t* m )
+{
+  cmXsReorder_t* r0 = NULL;
+  cmXsReorder_t* r  = NULL;
+  for(r=m->beg; r!=NULL; r=r->link)
+  {
+    if( r == m->end )
+      break;
+    r0 = r;
+  }
+
+  if( r0 == NULL )
+  {
+    m->beg = NULL;
+    m->end = NULL;
+  }
+  else
+  {
+    m->end       = r0;
+    m->end->link = NULL;
+  }
+
+  return r;
+}
 
 cmXsNote_t*  _cmXsReorderFindNote( cmXScore_t* p, unsigned measNumb, const cmXsReorder_t* r, unsigned iii )
 {
@@ -2138,7 +2276,8 @@ void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned
 {
   // Create a new score event record
   cmXsNote_t* nn = cmLhAllocZ(p->lhH,cmXsNote_t,1);
-  
+
+  nn->pitch = kInvalidMidiPitch;
   nn->uid   = p->nextUid++;
   nn->voice = r->note->voice;
   nn->meas  = r->note->meas;
@@ -2165,7 +2304,108 @@ void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned
   
 }
 
-cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN )
+cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, cmXsReorderMeas_t* m )
+{
+  unsigned i;
+  cmXsReorder_t* r;
+  
+  if( m->beg == NULL )
+    return kOkXsRC;
+
+  // set the 'note' field on each cmXsReorder_t record
+  for(r=m->beg,i=0; r!=NULL; r=r->link,++i)
+    if((r->note = _cmXsReorderFindNote(p,m->measNumb,r,i)) == NULL )
+      return kSyntaxErrorXsRC;
+
+  // remove deleted notes
+  for(r=m->beg; r!=NULL; r=r->link)
+    if( cmIsFlag(r->newFlags,kDeleteXsFl) )
+      if( _cmXScoreRemoveNote( r->note ) != kOkXsRC )
+        return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Event marked to skip was not found in measure: %i",m->measNumb);
+      
+  cmXsMeas_t* mp  = m->beg->note->meas;
+  cmXsNote_t* n0p = NULL;
+
+  assert( mp->number == m->measNumb );
+
+  // Reassign the slink of the cmXsNote_t records in this measure
+  // according to their order in rV[].
+  for(r=m->beg; r!=NULL; r=r->link)
+  {
+    
+    if( cmIsFlag(r->newFlags,kDeleteXsFl) )
+      continue;
+    
+    if( n0p == NULL )
+      mp->noteL = r->note;
+    else
+      n0p->slink = r->note;
+
+    // if a new tick was specified 
+    if( r->newTick != 0 )
+      r->note->tick = r->newTick;
+
+    // if a dynamic or velocity mark was included
+    if( r->dynIdx != cmInvalidIdx )
+    {
+      r->note->dynamics = _cmXScoreDynMarkArray[ r->dynIdx ].dyn;
+      r->note->vel      = _cmXScoreDynMarkArray[ r->dynIdx ].vel;
+    }
+
+    // Set the dynamic fork begin/end flags for later _cmXScoreProcessDynamicForks()
+    if( cmIsFlag(r->newFlags,kDynBegForkXsFl) )
+      r->note->flags = cmSetFlag(r->note->flags,kDynBegForkXsFl); 
+
+    if( cmIsFlag(r->newFlags,kDynEndForkXsFl) )
+      r->note->flags = cmSetFlag(r->note->flags,kDynEndForkXsFl); 
+
+    
+    // if the tie end flag was set
+    if( cmIsFlag(r->newFlags,kTieEndXsFl) )
+    {
+      r->note->flags |= kTieEndXsFl;
+      r->note->flags  = cmClrFlag( r->note->flags, kOnsetXsFl );
+      r->newFlags     = cmClrFlag( r->newFlags,    kTieEndXsFl);
+    }
+
+    // if a new note value was specified
+    if( r->pitch != 0 )
+      r->note->pitch = r->pitch;
+    
+    r->note->flags        |= r->graceFlags;
+    r->note->graceGroupId  = r->graceGroupId;
+
+
+    
+    n0p        = r->note;
+    n0p->slink = NULL;
+  }
+
+  
+  // Insert new note records for pedal up/dn events.
+  for(r=m->beg; r!=NULL; r=r->link)    
+  {
+    if( r->newFlags != 0 )
+    {
+      if( cmIsFlag(r->newFlags,kDampDnXsFl ) )
+        _cmXScoreInsertPedalEvent(p,r,kDampDnXsFl);
+
+      if( cmIsFlag(r->newFlags,kSostDnXsFl ) )
+        _cmXScoreInsertPedalEvent(p,r,kSostDnXsFl);
+      
+      if( cmIsFlag(r->newFlags,kDampUpXsFl ) )
+        _cmXScoreInsertPedalEvent(p,r,kDampUpXsFl);
+
+      if( cmIsFlag(r->newFlags,kSostUpXsFl ) )
+        _cmXScoreInsertPedalEvent(p,r,kSostUpXsFl);      
+    }
+  }
+
+  return kOkXsRC;
+
+}
+
+cmXsRC_t _cmXScoreReorderMeas0( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN )
 {
   unsigned i;
 
@@ -2266,19 +2506,22 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t*
 
 }
 
-cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, unsigned* dynIdxRef, unsigned* flagsRef, int measNumb )
-{
-  cmXsRC_t        rc        = kOkXsRC;
-  const cmChar_t* s         = NULL;
-  bool            begForkFl = false;
-  bool            endForkFl = false;
-    
 
+cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, char** s0, unsigned* dynIdxRef, unsigned* flagsRef, int measNumb )
+{
+  cmXsRC_t  rc        = kOkXsRC;
+  cmChar_t* s         = NULL;
+  bool      begForkFl = false;
+  bool      endForkFl = false;
+    
   *dynIdxRef = cmInvalidIdx;
 
   // locate the '!' which indicates the start of a dynamic marking
   if( (s = strchr(b,'!')) == NULL )
     return rc;
+
+  if( *s0==NULL || s<*s0 )    
+    *s0 = s;
   
   ++s; // increment past the '!'
   
@@ -2373,19 +2616,22 @@ cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lin
 }
 
 
-cmXsRC_t  _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* newFlagsRef )
+cmXsRC_t  _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* newFlagsRef )
 {
-  cmXsRC_t rc = kOkXsRC;
-  const cmChar_t* s;
-  bool doneFl = false;
-  unsigned i = 0;
-  
+  cmXsRC_t  rc     = kOkXsRC;
+  cmChar_t* s;
+  bool      doneFl = false;
+  unsigned  i      = 0;
+
   *newFlagsRef = 0;
   
   // tilde indicates a pedal event
   if((s = strchr(b,'~')) == NULL )
     return rc;
 
+  if( *s0==NULL || s<*s0)
+    *s0 = s;
+  
   do
   {
     ++s;
@@ -2434,14 +2680,17 @@ cmXsRC_t  _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned
   return rc;
 }
 
-cmXsRC_t  _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* tickRef )
+cmXsRC_t  _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* tickRef )
 {
   cmXsRC_t rc = kOkXsRC;
-  const cmChar_t* s;
+  cmChar_t* s;
 
   if((s = strchr(b,'@')) == NULL )
     return rc;
 
+  if( *s0 == NULL || s<*s0 )
+    *s0 = s;
+  
   ++s;
 
   if(!isdigit(*s))
@@ -2453,14 +2702,17 @@ cmXsRC_t  _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned l
   return rc;
 }
 
-cmXsRC_t  _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, cmXsReorder_t* r, unsigned* graceGroupIdRef )
+cmXsRC_t  _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, cmXsReorder_t* r, unsigned* graceGroupIdRef )
 {
-  cmXsRC_t        rc = kOkXsRC;
-  const cmChar_t* s;
+  cmXsRC_t  rc = kOkXsRC;
+  cmChar_t* s;
 
   if((s = strchr(b,'%')) == NULL )
     return rc;
 
+  if( *s0==NULL || s<*s0 )
+    *s0 = s;
+  
   ++s;
 
   r->graceGroupId = *graceGroupIdRef;
@@ -2502,12 +2754,12 @@ cmXsRC_t  _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned
   
 }
 
-cmXsRC_t  _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* pitchRef )
+cmXsRC_t  _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* pitchRef )
 {
-  cmXsRC_t rc = kOkXsRC;
+  cmXsRC_t  rc = kOkXsRC;
   cmChar_t* s;
-  cmChar_t buf[4];
-  unsigned i,j;
+  cmChar_t  buf[4];
+  unsigned  i,j;
   memset(buf,0,sizeof(buf));
 
   *pitchRef = 0;
@@ -2515,6 +2767,9 @@ cmXsRC_t  _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned
   if((s = strchr(b,'$')) == NULL )
     return rc;
 
+  if( *s0==NULL || s<*s0)
+    *s0 = s;
+  
   ++s;
 
   j=2;
@@ -2546,7 +2801,232 @@ cmXsRC_t  _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned
   return rc;  
 }
 
+cmXsRC_t _cmXsReadEditFile( cmXScore_t* p, const cmChar_t* fn, unsigned* graceGroupIdPtr, cmXsReorderFile_t* rfp )
+{
+  typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
+
+  cmXsRC_t           rc           = kOkXsRC;
+  cmFileH_t          fH           = cmFileNullHandle;
+  cmChar_t*          b            = NULL;
+  unsigned           bN           = 0;
+  unsigned           ln           = 0;
+  stateId_t          stateId      = kFindMeasStId;
+  cmXsReorderMeas_t* curMeas      = NULL;
+  
+  *graceGroupIdPtr = 1;
+  
+  if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
+  {
+    rc = cmErrMsg(&p->err,kFileFailXsRC,"The reordering file '%s' could not be opened.",cmStringNullGuard(fn));
+    return rc;
+  }
+
+  for(; cmFileGetLineAuto(fH,&b,&bN) == kOkFileRC; ++ln)
+  {
+    switch( stateId )
+    {
+      case kFindEventStId:      // scanning past labels to an event line
+        {
+          unsigned voice,loc;
+          if( sscanf(b,"%i %i",&voice,&loc) != 2 )
+            continue;
+
+          stateId = kReadEventStId;
+        }
+        // fall through
+
+      case kReadEventStId:
+        {
+          cmXsReorder_t* r = _cmXsReorderMeasAllocEvent(p, curMeas );
+          char           pitchStr[4];
+          char*          s0 = NULL;
+
+          // parse an event line
+          if( sscanf(b,"%i %i %i %i %i %f",&r->idx,&r->voice,&r->locIdx,&r->tick,&r->durtn,&r->rval) == 6 )
+          {
+            assert( strlen(b)>=52);
+            int PC = 39; // text file column where first pitch char occurs
+            
+            if( b[PC] == ' ')
+              r->midi = kInvalidMidiPitch;
+            else
+            {
+              pitchStr[0] = b[PC+0];
+              pitchStr[1] = b[PC+1];
+              pitchStr[2] = b[PC+2];              
+              pitchStr[3] = 0;
+              
+              if( !isdigit(pitchStr[2]) )
+                r->midi = kInvalidMidiPitch;
+              else
+              {
+                if( pitchStr[1] == ' ')
+                {
+                  pitchStr[1] = pitchStr[2];
+                  pitchStr[2] = 0;
+                }
+
+                r->midi = cmSciPitchToMidi(pitchStr);
+
+                //printf("%i %i %s %s\n",curMeas->measNumb,r->midi,pitchStr,fn);
+              }
+            }
+            
+            // parse the flag edits following a '~'
+            if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r->newFlags)) != kOkXsRC )
+              goto errLabel;
+
+            // parse the dynamic marking following a '!'
+            if((rc = _cmXScoreReorderParseDyn(p,b,ln+1, &s0, &r->dynIdx, &r->newFlags, curMeas->measNumb)) != kOkXsRC )
+              goto errLabel;
+                        
+            // parse the @newtick marker
+            if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r->newTick)) != kOkXsRC )
+              goto errLabel;
+
+            // parse the %grace note marker
+            if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, r, graceGroupIdPtr)) != kOkXsRC )
+              goto errLabel;
+
+            // parse the $pitch marker
+            if((rc =  _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r->pitch )) != kOkXsRC )
+              goto errLabel;
+
+            if( s0 != NULL )
+              r->editStr = cmTextTrimEnd(cmLhAllocStrN( p->lhH, s0, strlen(s0)+1 ));
+            
+            continue;
+          }
+
+          // remove the last reorder record because it was not filled
+          _cmXsReorderMeasPop(curMeas);
+          
+          stateId = kFindMeasStId;
+          // fall through
+        }
+
+      case kFindMeasStId:  // scanning for a bar-line
+        {
+          char     colon;
+          unsigned measNumb = 0;
+
+          if( sscanf(b,"%i %c",&measNumb,&colon) == 2 && colon == ':' )
+          {
+            curMeas = _cmXsReorderFileAllocMeas( p, rfp, measNumb );
+            stateId = kFindEventStId;
+          }
+        }
+        break;
+    }
+
+  }
+ errLabel:
+  cmMemFree(b);
+  cmFileClose(&fH);
+  return rc;  
+}
+
 cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
+{
+  cmXsRC_t           rc           = kOkXsRC;
+  unsigned           graceGroupId = 1;
+  cmXsReorderFile_t  rf;
+  cmXsReorderMeas_t* m;
+  memset(&rf,0,sizeof(rf));
+  
+  if((rc = _cmXsReadEditFile( p, fn, &graceGroupId, &rf )) != kOkXsRC )
+    return rc;
+
+  // reorder each measure
+  for(m=rf.beg; m!=NULL; m=m->link)
+    if((rc =  _cmXScoreReorderMeas(p, m)) != kOkXsRC )
+      goto errLabel;
+  
+  // the ticks may have changed so the 'secs' and 'dsecs' must be updated
+  _cmXScoreSetAbsoluteTime( p );
+
+  // the bar lines should be the first event in the measure
+  _cmXScoreFixBarLines(p);
+
+  // resort to force the links to be correct 
+  _cmXScoreSort(p);
+
+  // process the grace notes.
+  _cmXScoreProcessGraceNotes( p, graceGroupId );
+
+  // inserting grace notes may have left the score unsorted
+  _cmXScoreSort(p);
+  
+  // process the dynamic forks
+  _cmXScoreProcessDynamicForks(p);
+
+  //_cmXScoreReport(p, NULL, true );
+
+ errLabel:
+  return rc;
+}
+
+
+
+cmXsRC_t _cmXsMergeEditFiles( cmXScore_t* p, unsigned measNumb0, const cmChar_t* keyEditFn, unsigned keyMeasNumb, const cmChar_t* outFn )
+{
+  cmXsRC_t           rc            = kOkXsRC;
+  unsigned           graceGroup1Id = 1;
+  unsigned           measNumb1     = keyMeasNumb;
+  cmXsReorderFile_t  rf1;
+  memset(&rf1,0,sizeof(rf1));
+
+  if((rc = _cmXsReadEditFile( p, keyEditFn, &graceGroup1Id, &rf1 )) != kOkXsRC )
+    return rc;
+
+  while(1)
+  {
+    cmXsMeas_t*        m0   = _cmXsFindMeas( p->partL, measNumb0 );
+    cmXsReorderMeas_t* m1   = _cmXsReorderFileFindMeas( &rf1, measNumb1 );
+    cmXsReorder_t*     key  = NULL;
+    unsigned           idx0 = 0;
+    
+    if( m1==NULL )
+    {
+      rc = cmErrMsg(&p->err,kEventNotFoundXsRC,"The measure %i was not found in the key edit file '%s'.",keyMeasNumb,cmStringNullGuard(keyEditFn));
+      break;
+    }
+    
+    key  = m1->beg;
+    
+    for(; key!=NULL; key=key->link)
+    {
+      unsigned    idx1 = cmInvalidIdx;
+      cmXsNote_t* np   = _cmXsFindNote( m0, idx0, key->midi, key->rval, key->durtn, &idx1);
+      
+      if( np==NULL )
+      {
+        if( key->editStr != NULL )
+        {
+          const char* sciPitch = key->midi!=kInvalidMidiPitch ? cmMidiToSciPitch(key->midi,NULL,0) : "<non-pitch>";
+          cmErrWarnMsg(&p->err,kEventNotFoundXsRC,"Sync error: meas: ref:%i key:%i index:%i %s (midi:%i) edit:%s did not match to the reference edit file.", measNumb0,m1->measNumb,key->idx, sciPitch, key->midi, key->editStr);
+        }
+      }
+      else
+      {
+        np->editStr = key->editStr;
+        
+        if( key->editStr != NULL )
+          idx0        = idx1;
+      }
+    }
+    
+    ++measNumb0;
+    ++measNumb1;
+
+  }
+  
+  return rc;
+}
+
+
+
+cmXsRC_t _cmXsApplyEditFile0( cmXScore_t* p, const cmChar_t* fn )
 {
   typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
 
@@ -2586,6 +3066,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
         {
           cmXsReorder_t r;
           char     pitchStr[4];
+          char* s0 = NULL;
 
           memset(&r,0,sizeof(r));
           
@@ -2596,7 +3077,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
             int PC = 39; // text file column where first pitch char occurs
             
             if( b[PC] == ' ')
-              r.midi = 0;
+              r.midi = kInvalidMidiPitch;
             else
             {
               pitchStr[0] = b[PC+0];
@@ -2605,7 +3086,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
               pitchStr[3] = 0;
               
               if( !isdigit(pitchStr[2]) )
-                r.midi = 0;
+                r.midi = kInvalidMidiPitch;
               else
               {
                 if( pitchStr[1] == ' ')
@@ -2621,40 +3102,24 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
 
             
             // parse the flag edits following a '~'
-            if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &r.newFlags)) != kOkXsRC )
+            if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r.newFlags)) != kOkXsRC )
               goto errLabel;
 
             // parse the dynamic marking following a '!'
-            if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&r.dynIdx, &r.newFlags, measNumb)) != kOkXsRC )
+            if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&s0, &r.dynIdx, &r.newFlags, measNumb)) != kOkXsRC )
               goto errLabel;
                         
             // parse the @newtick marker
-            if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &r.newTick)) != kOkXsRC )
+            if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r.newTick)) != kOkXsRC )
               goto errLabel;
 
             // parse the %grace note marker
-            if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &r, &graceGroupId)) != kOkXsRC )
+            if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, &r, &graceGroupId)) != kOkXsRC )
               goto errLabel;
 
             // parse the $pitch marker
-            if((rc =  _cmXScoreReorderParsePitch(p, b, ln+1, &r.pitch )) != kOkXsRC )
+            if((rc =  _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r.pitch )) != kOkXsRC )
               goto errLabel;
-
-            // process grace notes - these need to be processed separate from
-            // the _cmXScoreReorderMeas() because grace notes may cross measure boundaries.
-            /*
-            if( r.graceType != 0 )
-            {
-              r.graceGroupId = graceGroupId;
-
-              // if this is an end of a grace note group
-              if( r.graceType != 'g' && r.graceType != 'b' )
-              {
-                graceGroupId += 1;
-              }
-              
-            }
-            */
             
             // store the record
             assert( ri < rN );
@@ -2666,7 +3131,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
 
           // the end of the measure was encountered -
           // reorder the measure based on the cmXsReorder_t in rV[ri]
-          if((rc =  _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC )
+          if((rc =  _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC )
             goto errLabel;
 
           ri = 0;
@@ -2692,7 +3157,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
 
   // If reorder records remain to be processed
   if( ri > 0 )
-    if((rc =  _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC )
+    if((rc =  _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC )
       goto errLabel;
     
   
@@ -2724,8 +3189,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
 }
 
 
-
-cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl )
+cmXsRC_t cmXScoreAlloc( cmCtx_t* ctx, cmXsH_t* hp )
 {
   cmXsRC_t rc = kOkXsRC;
 
@@ -2740,6 +3204,21 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, c
   if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
     return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
 
+  hp->h = p;
+  
+  return rc;
+}
+
+cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl )
+{
+  cmXsRC_t rc;
+  cmXScore_t* p = NULL;
+  
+  if((rc = cmXScoreAlloc(ctx,hp)) != kOkXsRC )
+    goto errLabel;
+
+  p  = _cmXScoreHandleToPtr(*hp);
+    
   // open the music xml file
   if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
   {
@@ -2798,9 +3277,8 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, c
   
  errLabel:
   if( rc != kOkXsRC )
-    _cmXScoreFinalize(p);
-  else
-    hp->h = p;
+    cmXScoreFinalize(hp);
+  
 
   return rc;
 }
@@ -3321,6 +3799,9 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
 
   if( note->dynamics != 0)
     cmRptPrintf(rpt," dyn=%i %i",note->dynamics,note->vel);
+
+  if( note->editStr != NULL )
+    cmRptPrintf(rpt, " %s", note->editStr );
   
   /*
   if( cmIsFlag(note->flags,kBegGraceXsFl) )
@@ -3371,6 +3852,7 @@ void  _cmXScoreReport( cmXScore_t* p, cmRpt_t* rpt, bool sortFl )
 
           t1 = note->slink==NULL ? note->tick : note->slink->tick;
 
+          // check that this note is in tick order
           if( !(t0 <= note->tick && note->tick <= t1) )
           {
             cmRptPrintf(rpt," +");
@@ -3427,32 +3909,41 @@ void _cmXScoreGenEditFileWrite( void* arg, const cmChar_t* text )
   }
 }
 
-cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl )
+cmXsRC_t _cmXScoreEditFileRpt( cmCtx_t* ctx, cmXScore_t* p, const cmChar_t* outFn, bool damperRptFl )
 {
-  cmXsH_t   xsH = cmXsNullHandle;
-  cmFileH_t fH  = cmFileNullHandle;
   cmXsRC_t  rc  = kOkXsRC;
   cmErr_t   err;
   cmRpt_t   rpt;
+  cmFileH_t fH  = cmFileNullHandle;
 
   cmErrSetup(&err,&ctx->rpt,"cmXScoreGenEditFile");
   cmRptSetup(&rpt,_cmXScoreGenEditFileWrite,_cmXScoreGenEditFileWrite,&fH);
-
-  if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != kOkXsRC )
-    return rc;
-
+  
   if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
   {
     cmErrMsg(&err,kFileFailXsRC,"Unable to open the output file '%s'.",cmStringNullGuard(outFn));
     goto errLabel;
   }
   
-  cmXScoreReport(xsH,&rpt,true);
+  _cmXScoreReport(p,&rpt,true);
   
  errLabel:
   
   if( cmFileClose(&fH) != kOkFileRC )
     rc = cmErrMsg(&err,kFileFailXsRC,"File close failed on '%s'.",cmStringNullGuard(outFn));
+
+  return rc;
+}
+
+cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl )
+{
+  cmXsH_t   xsH = cmXsNullHandle;
+  cmXsRC_t  rc  = kOkXsRC;
+
+  if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != kOkXsRC )
+    return rc;
+
+  rc = _cmXScoreEditFileRpt( ctx, _cmXScoreHandleToPtr(xsH), outFn, damperRptFl );
   
   cmXScoreFinalize(&xsH);
 
@@ -4148,3 +4639,24 @@ cmXsRC_t cmXScoreTest(
   return cmXScoreFinalize(&h);
 
 }
+
+
+cmXsRC_t cmXScoreMergeEditFiles( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* refEditFn,  unsigned refBegMeasNumb, const cmChar_t* editFn, unsigned keyMeasNumb, const cmChar_t* outFn )
+{
+  cmXsH_t  h           = cmXsNullHandle;
+  cmXsRC_t rc;
+  bool     damperRptFl = false;
+  
+  if((rc = cmXScoreInitialize(ctx, &h, xmlFn, refEditFn, damperRptFl )) == kOkXsRC )
+  {
+    cmXScore_t* p  = _cmXScoreHandleToPtr(h);
+    
+    if((rc = _cmXsMergeEditFiles(p, refBegMeasNumb, editFn, keyMeasNumb, outFn )) == kOkXsRC )
+      rc = _cmXScoreEditFileRpt( ctx, p, outFn, damperRptFl );
+    
+
+    cmXScoreFinalize(&h);
+  }
+
+  return rc;
+}
diff --git a/src/app/cmXScore.h b/src/app/cmXScore.h
index c412368..df7a352 100644
--- a/src/app/cmXScore.h
+++ b/src/app/cmXScore.h
@@ -20,7 +20,8 @@ extern "C" {
     kFileFailXsRC,
     kSvgFailXsRC,
     kOverlapWarnXsRC,
-    kZeroLengthEventXsRC
+    kZeroLengthEventXsRC,
+    kEventNotFoundXsRC
   };
 
   typedef cmRC_t     cmXsRC_t;
@@ -73,6 +74,8 @@ extern "C" {
   // Set begMeasNumb to the first measure the to be written to the output csv, MIDI and SVG files.
   // Set begBPM to 0 to use the tempo from the score otherwise set it to the tempo at begMeasNumb.
   cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, bool reportFl, int begMeasNumb, int begBPM, bool svgStandAloneFl, bool svgPanZoomFl, bool damperRptFl );
+
+  cmXsRC_t cmXScoreMergeEditFiles( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* refEditFn,  unsigned refBegMeasNumb, const cmChar_t* editFn, unsigned keyMeasNumb, const cmChar_t* outFn );
   
 #ifdef __cplusplus
 }

From b93e3729eb67e5c24f37a3dd0f33d8a77492e3d9 Mon Sep 17 00:00:00 2001
From: kevin <kevin@zonk.verizon.net>
Date: Tue, 13 Oct 2020 15:13:24 -0400
Subject: [PATCH 21/21] README.md : Initial commit.

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a987ef
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+
+libcm is a C application development framework with an emphasis on audio signal processing.
+