//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org> 
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwNumericConvert.h"
#include "cwObject.h"
#include "cwFileSys.h"
#include "cwFile.h"
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwMidi.h"
#include "cwMidiFile.h"
#include "cwUiDecls.h"
#include "cwIo.h"
#include "cwScoreFollowerPerf.h"
#include "cwIoMidiRecordPlay.h"
#include "cwIoPresetSelApp.h"
#include "cwVectOps.h"
#include "cwMath.h"
#include "cwDspTypes.h"
#include "cwMtx.h"
#include "cwFlowDecl.h"
#include "cwFlow.h"
#include "cwFlowValue.h"
#include "cwFlowTypes.h"
#include "cwFlowCross.h"
#include "cwIoFlow.h"
#include "cwPresetSel.h"
#include "cwVelTableTuner.h"
#include "cwDynRefTbl.h"
#include "cwScoreParse.h"
#include "cwSfScore.h"
#include "cwSfTrack.h"
#include "cwPerfMeas.h"
#include "cwPianoScore.h"
#include "cwScoreFollower.h"


namespace cw
{
  namespace preset_sel_app
  {
    // Application Id's for UI elements
    enum
    {
      // Resource Based elements
      kPanelDivId = 1000,
      kQuitBtnId,
      kIoReportBtnId,
      kIoHwReportBtnId,
      kIoRtReportBtnId,
      kPresetReportBtnId,
      kMRP_ReportBtnId,
      kNetPrintBtnId,
      kReportBtnId,
      kLatencyBtnId,

      
      kStartBtnId,
      kStopBtnId,
      kGotoBtnId,
      kBegPlayLocNumbId,
      kEndPlayLocNumbId,
      kLockLoctnCheckId,      
      kLiveCheckId,
      kTrackMidiCheckId,

      kPrintMidiCheckId,
      kPianoMidiCheckId,
      kSamplerMidiCheckId,
      kSyncDelayMsId,

      kWetInGainId,
      kWetOutGainId,
      kDryGainId,
      
      kMidiThruCheckId,
      kCurMidiEvtCntId,
      kTotalMidiEvtCntId,
      
      kCurAudioSecsId,
      kTotalAudioSecsId,
                
      kSaveBtnId,
      kLoadBtnId,
      kPerfSelId,
      kAltSelId,
      
      kPriPresetProbCheckId,
      kSecPresetProbCheckId,
      kPresetInterpCheckId,
      kPresetAllowAllCheckId,
      kPresetDryPriorityCheckId,
      kPresetDrySelectedCheckId,

      
      kEnaRecordCheckId,
      kMidiSaveBtnId,
      kMidiLoadBtnId,
      kMidiLoadFnameId,
      kSfResetBtnId,
      kSfResetLocNumbId,


      kInsertLocId,
      kInsertBtnId,
      kDeleteBtnId,

      kStatusId,

      kPvWndSmpCntId,
      kSdBypassId,
      kSdInGainId,
      kSdCeilingId,
      kSdExpoId,
      kSdThreshId,
      kSdUprId,
      kSdLwrId,
      kSdMixId,
      kCmpBypassId,
      
      kAMtrIn0Id,
      kAMtrIn1Id,
      kAMtrOut0Id,
      kAMtrOut1Id,

      kLogId,

      kFragListId,
      kFragPanelId,
      kFragMeasId,
      kFragBegLocId,
      kFragEndLocId,
      
      kFragPresetRowId,
      kFragPresetSelId,
      kFragPresetSeqSelId,
      kFragPresetOrderId,
      kFragPresetAltId,
      
      kFragInGainId,
      kFragOutGainId,
      kFragWetDryGainId,
      kFragFadeOutMsId,
      kFragBegPlayLocId,
      kFragEndPlayLocId,
      kFragPlayBtnId,
      kFragPlaySeqBtnId,
      kFragPlayAllBtnId,
      kFragNoteId,

      kVelTblMinId = vtbl::kVtMinId,
      kVelTblMaxId = vtbl::kVtMaxId,

      kPerfOptionBaseId = kVelTblMaxId + 1,
    };

    
    // Application Id's for the resource based UI elements.
    ui::appIdMap_t mapA[] =
    {
      { ui::kRootAppId,  kPanelDivId,     "panelDivId" },
      { kPanelDivId,     kQuitBtnId,      "quitBtnId" },
      { kPanelDivId,     kIoReportBtnId,  "ioReportBtnId" },
      { kPanelDivId,     kIoHwReportBtnId, "ioHwReportBtnId" },
      { kPanelDivId,     kIoRtReportBtnId, "ioRtReportBtnId" },
      { kPanelDivId,     kMRP_ReportBtnId, "MRP_ReportBtnId" },      
      { kPanelDivId,     kPresetReportBtnId, "presetReportBtnId" },
      { kPanelDivId,     kNetPrintBtnId,  "netPrintBtnId" },
      { kPanelDivId,     kReportBtnId,    "reportBtnId" },
      { kPanelDivId,     kLatencyBtnId,   "latencyBtnId" },
        
      { kPanelDivId,     kStartBtnId,        "startBtnId" },
      { kPanelDivId,     kStopBtnId,         "stopBtnId" },
      { kPanelDivId,     kGotoBtnId,         "gotoBtnId" },
      
      { kPanelDivId,     kBegPlayLocNumbId,  "begLocNumbId" },
      { kPanelDivId,     kEndPlayLocNumbId,  "endLocNumbId" },      
      { kPanelDivId,     kLockLoctnCheckId,  "locLoctnCheckId" },
      { kPanelDivId,     kLiveCheckId,       "liveCheckId" },
      { kPanelDivId,     kTrackMidiCheckId,  "trackMidiCheckId" },

      { kPanelDivId,     kPrintMidiCheckId,  "printMidiCheckId" },
      { kPanelDivId,     kPianoMidiCheckId,  "pianoMidiCheckId" },
      { kPanelDivId,     kSamplerMidiCheckId,"samplerMidiCheckId" },
      { kPanelDivId,     kSyncDelayMsId,     "syncDelayMsId" },

      { kPanelDivId,     kWetInGainId,       "wetInGainId" },
      { kPanelDivId,     kWetOutGainId,      "wetOutGainId" },
      { kPanelDivId,     kDryGainId,         "dryGainId" },
      
      { kPanelDivId,     kMidiThruCheckId,   "midiThruCheckId" },
      { kPanelDivId,     kCurMidiEvtCntId,   "curMidiEvtCntId" },      
      { kPanelDivId,     kTotalMidiEvtCntId, "totalMidiEvtCntId" },
      
      { kPanelDivId,     kCurAudioSecsId,    "curAudioSecsId" },
      { kPanelDivId,     kTotalAudioSecsId,  "totalAudioSecsId" },
        
      { kPanelDivId,     kSaveBtnId,      "saveBtnId" },
      { kPanelDivId,     kLoadBtnId,      "loadBtnId" },
      { kPanelDivId,     kPerfSelId,      "perfSelId" },
      { kPanelDivId,     kAltSelId,       "altSelId" },
      { kPanelDivId,     kPriPresetProbCheckId,  "presetProbPriCheckId" },
      { kPanelDivId,     kSecPresetProbCheckId,  "presetProbSecCheckId" },
      { kPanelDivId,     kPresetInterpCheckId,   "presetInterpCheckId" },
      { kPanelDivId,     kPresetAllowAllCheckId, "presetAllowAllCheckId" },
      { kPanelDivId,     kPresetDryPriorityCheckId, "presetDryPriorityCheckId" },
      { kPanelDivId,     kPresetDrySelectedCheckId, "presetDrySelectedCheckId" },
      
      

      { kPanelDivId,     kEnaRecordCheckId,  "enaRecordCheckId" },
      { kPanelDivId,     kMidiSaveBtnId,     "midiSaveBtnId" },
      { kPanelDivId,     kMidiLoadBtnId,     "midiLoadBtnId" },
      { kPanelDivId,     kMidiLoadFnameId,   "midiLoadFnameId" },
      { kPanelDivId,     kSfResetBtnId,      "sfResetBtnId" },
      { kPanelDivId,     kSfResetLocNumbId,  "sfResetLocNumbId" },


      { kPanelDivId,     kInsertLocId,    "insertLocId" },
      { kPanelDivId,     kInsertBtnId,    "insertBtnId" },
      { kPanelDivId,     kDeleteBtnId,    "deleteBtnId" },

      { kPanelDivId,     kPvWndSmpCntId, "pvWndSmpCntId" },
      { kPanelDivId,     kSdBypassId,    "sdBypassId" },
      { kPanelDivId,     kSdInGainId,    "sdInGainId" },
      { kPanelDivId,     kSdCeilingId,   "sdCeilingId" },
      { kPanelDivId,     kSdExpoId,      "sdExpoId" },
      { kPanelDivId,     kSdThreshId,    "sdThreshId" },
      { kPanelDivId,     kSdUprId,       "sdUprId" },
      { kPanelDivId,     kSdLwrId,       "sdLwrId" },
      { kPanelDivId,     kSdMixId,       "sdMixId" },
      { kPanelDivId,     kCmpBypassId,   "cmpBypassId" },
      
      { kPanelDivId,     kStatusId,        "statusId" },
      { kPanelDivId,     kAMtrIn0Id,       "aMtrIn0"  },
      { kPanelDivId,     kAMtrIn1Id,       "aMtrIn1"  },
      { kPanelDivId,     kAMtrOut0Id,      "aMtrOut0" },
      { kPanelDivId,     kAMtrOut1Id,      "aMtrOut1" },
      
      { kPanelDivId,     kLogId,          "logId" },

      
      { kPanelDivId,     kFragListId,       "fragListId"   },
      { kFragListId,     kFragPanelId,      "fragPanelId"  },
      { kFragPanelId,    kFragMeasId,       "fragMeasId"   },
      { kFragPanelId,    kFragBegLocId,     "fragBegLocId" },
      { kFragPanelId,    kFragEndLocId,     "fragEndLocId" },
      { kFragPanelId,    kFragPresetRowId,  "fragPresetRowId" },
      { kFragPanelId,    kFragInGainId,     "fragInGainId"  },
      { kFragPanelId,    kFragOutGainId,    "fragOutGainId" },
      { kFragPanelId,    kFragWetDryGainId, "fragWetDryGainId" },
      { kFragPanelId,    kFragFadeOutMsId,  "fragFadeOutMsId"  },
      { kFragPanelId,    kFragBegPlayLocId, "fragBegPlayLocId" },
      { kFragPanelId,    kFragEndPlayLocId, "fragEndPlayLocId" },
      { kFragPanelId,    kFragPlayBtnId,    "fragPlayBtnId" },
      { kFragPanelId,    kFragPlaySeqBtnId, "fragPlaySeqBtnId" },
      { kFragPanelId,    kFragPlayAllBtnId, "fragPlayAllBtnId" },
      { kFragPanelId,    kFragNoteId,       "fragNoteId" },

      
      
    };

    unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);

    typedef struct loc_map_str
    {
      unsigned     loc;
      time::spec_t timestamp;
    } loc_map_t;

    typedef struct ui_blob_str
    {
      unsigned fragId;
      unsigned varId;
      unsigned presetId;
    } ui_blob_t;


    typedef struct vel_tbl_str
    {
      const char* name;
      const char* device;
    } vel_tbl_t;
    
    typedef struct perf_recording_str
    {
      char*                      fname;     // perf recording
      unsigned                   sess_numb;
      unsigned                   take_numb;
      unsigned                   beg_loc;
      unsigned                   end_loc;
      char*                      label;     // menu label
      unsigned                   id;        // menu appId
      unsigned                   uuId;      // menu uuid
      vel_tbl_t*                 vel_tblA;  // vel_tblA[ velTblN ] 
      unsigned                   vel_tblN;  //
      struct perf_recording_str* link;
    } perf_recording_t;
    
    typedef struct app_str
    {
      io::handle_t ioH;

      // path components for reading/writing the preset assignments
      const char* record_dir;
      const char* record_fn;
      const char* record_fn_ext;
      const char* record_backup_dir;
      
      //const char*     scoreFn;
      const object_t* perfDirL;
      const char*     velTableFname;
      const char*     velTableBackupDir;
      const object_t* midi_play_record_cfg;
      const object_t* presets_cfg;
      object_t*       flow_proc_dict;
      const object_t* flow_cfg;
      const object_t* score_follower_cfg;
      const char*     in_audio_dev_file;
      unsigned        in_audio_dev_idx;
      unsigned        in_audio_begin_loc;
      double          in_audio_offset_sec;

      score_follower::handle_t   sfH;
      midi_record_play::handle_t mrpH;

      perf_score::handle_t perfScoreH;
      loc_map_t*      locMap;    
      unsigned        locMapN;

      unsigned insertLoc;       // last valid insert location id received from the GUI
      
      unsigned minScoreLoc;          // min/max locations of the currently loaded score
      unsigned maxScoreLoc;          //
      unsigned minPerfLoc;           // min/max locations of the currently loaded performance
      unsigned maxPerfLoc;
      
      unsigned beg_play_loc;    // beg/end play loc's from the UI
      unsigned end_play_loc;
      bool     lockLoctnFl;

      preset_sel::handle_t      psH;      
      const preset_sel::frag_t* psNextFrag;
      time::spec_t              psLoadT0;
      
      vtbl::handle_t    vtH;
      io_flow::handle_t ioFlowH;
      
      double   crossFadeSrate;
      unsigned crossFadeCnt;

      bool printMidiFl;

      unsigned multiPresetFlags;

      bool     seqActiveFl;     // true if the sequence is currently active (set by 'Play Seq' btn)
      unsigned seqStartedFl;    // set by the first seq idle callback
      unsigned seqFragId;       // 
      unsigned seqPresetIdx;    //

      bool useLiveMidiFl;       // use incoming MIDI to drive program (otherwise use score file)
      bool trackMidiFl;         // apply presets based on MIDI location (otherwise respond only to direct manipulation of fragment control)

      bool        enableRecordFl; // enable recording of incoming MIDI 
      char*       midiRecordDir;
      const char* midiRecordFolder;
      char*       midiLoadFname;

      unsigned sfResetLoc;

      unsigned pvWndSmpCnt;
      bool     sdBypassFl;
      double   sdInGain;
      double   sdCeiling;
      double   sdExpo;
      double   sdThresh;
      double   sdUpr;
      double   sdLwr;
      double   sdMix;
      bool     cmpBypassFl;

      unsigned dfltSyncDelayMs;

      perf_recording_t* perfRecordingBeg;
      perf_recording_t* perfRecordingEnd;

      const char* dflt_perf_label;
      unsigned    dflt_perf_app_id;
      unsigned    run_dur_secs;
      
      
    } app_t;

    rc_t _apply_command_line_args( app_t* app, int argc, const char* argv[] )
    {
      rc_t rc = kOkRC;
      
      for(int i=0; i<argc ; i+=2)
      {
        if( textCompare(argv[i],"record_fn") == 0 )
        {
          app->record_fn = argv[i+1];
          goto found_fl;
        }
        /*
        if( textCompare(argv[i],"score_fn") == 0 )
        {
          app->scoreFn = argv[i+1];
          goto found_fl;
        }
        */
        if( textCompare(argv[i],"beg_play_loc") == 0 )
        {
          string_to_number( argv[i+1], app->beg_play_loc );
          goto found_fl;
        }

        if( textCompare(argv[i],"end_play_loc") == 0 )
        {
          string_to_number( argv[i+1], app->end_play_loc );
          goto found_fl;
        }

        rc = cwLogError(kSyntaxErrorRC,"The command line argument: '%s' was not recognized.",argv[i]);
        goto errLabel;

      found_fl:
        printf("Command line override '%s=%s' .\n",argv[i],argv[i+1]);
        
        
      }

    errLabel:
      return rc;
    }
    
    rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef, int argc, const char* argv[] )
    {
      rc_t        rc                = kOkRC;
      const char* flow_proc_dict_fn = nullptr;
      const char* midi_record_dir;
      
      if((rc = cfg->getv( "params", params_cfgRef,
                          "flow",   app->flow_cfg)) != kOkRC )
      {
        rc = cwLogError(kSyntaxErrorRC,"Preset Select App 'params' cfg record not found.");
        goto errLabel;
      }
        
      if((rc = params_cfgRef->getv( "record_dir",           app->record_dir,
                                    "record_fn",            app->record_fn,
                                    "record_fn_ext",        app->record_fn_ext,
            //"score_fn",             app->scoreFn,
                                    "perfDirL",             app->perfDirL,
                                    "flow_proc_dict_fn",    flow_proc_dict_fn,                                    
                                    "midi_play_record",     app->midi_play_record_cfg,
                                    "vel_table_fname",      app->velTableFname,
                                    "vel_table_backup_dir", app->velTableBackupDir,
                                    "presets",              app->presets_cfg,
                                    "crossFadeCount",       app->crossFadeCnt,
                                    "beg_play_loc",         app->beg_play_loc,
                                    "end_play_loc",         app->end_play_loc,
                                    "dflt_perf_label",      app->dflt_perf_label,
                                    "run_dur_secs",         app->run_dur_secs,
                                    "live_mode_fl",         app->useLiveMidiFl,
                                    "enable_recording_fl",  app->enableRecordFl,
                                    "midi_record_dir",      midi_record_dir,
                                    "midi_record_folder",   app->midiRecordFolder,
                                    "sf_reset_loc",         app->sfResetLoc,
                                    "score_follower",       app->score_follower_cfg)) != kOkRC )
      {
        rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
        goto errLabel;
      }

      if((rc = params_cfgRef->getv_opt( "in_audio_dev_file",  app->in_audio_dev_file,
                                        "in_audio_file_begin_loc", app->in_audio_begin_loc,
                                        "in_audio_file_offset_sec", app->in_audio_offset_sec)) != kOkRC )
      {
        rc = cwLogError(rc,"Parse of optional cfg. params failed..");
        goto errLabel;        
      }

      _apply_command_line_args(app,argc,argv);


      /*
      if((app->scoreFn    = filesys::expandPath( app->scoreFn )) == nullptr )
      {
        rc = cwLogError(kInvalidArgRC,"The score file name is invalid.");
        goto errLabel;
      }
      */
      
      if((app->record_dir = filesys::expandPath(app->record_dir)) == nullptr )
      {
        rc = cwLogError(kInvalidArgRC,"The record directory path is invalid.");
        goto errLabel;
      }

      if((app->midiRecordDir = filesys::expandPath( midi_record_dir )) == nullptr )
      {
        rc = cwLogError(kInvalidArgRC,"The midi record path is invalid.");
        goto errLabel;
      }
      
      if((app->record_backup_dir = filesys::makeFn(app->record_dir,"backup",nullptr,nullptr)) == nullptr )
      {
        rc = cwLogError(kInvalidArgRC,"The record backup directory path is invalid.");
        goto errLabel;
      }

      if((rc = objectFromFile( flow_proc_dict_fn, app->flow_proc_dict )) != kOkRC )
      {
        rc = cwLogError(kInvalidArgRC,"The flow proc file '%s' parse failed.",app->flow_proc_dict);
        goto errLabel;
      }

      // verify that the output directory exists
      if((rc = filesys::makeDir(app->record_dir)) != kOkRC )
      {
        rc = cwLogError(rc,"Unable to create the base output directory:%s.",cwStringNullGuard(app->record_dir));
        goto errLabel;
      }

      // verify that the midi record directory exists
      if((rc = filesys::makeDir(app->midiRecordDir)) != kOkRC )
      {
        rc = cwLogError(rc,"Unable to create the MIDI recording directory:%s.",cwStringNullGuard(app->midiRecordDir));
        goto errLabel;
      }
      
      // verify that the output backup directory exists
      if((rc = filesys::makeDir(app->record_backup_dir)) != kOkRC )
      {
        rc = cwLogError(rc,"Unable to create the output backup directory:%s.",cwStringNullGuard(app->record_backup_dir));
      }

      app->insertLoc = kInvalidId; // initialize 'insertLoc' to be invalid 
      
    errLabel:
      
      return rc;
    }
    

    void _set_statusv( app_t* app, const char* fmt, va_list vl )
    {
      const int sN = 128;
      char      s[sN];
      vsnprintf(s,sN,fmt,vl);      
      uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kStatusId), s );
      //printf("Status:%s\n",s);
    }

    
    void _set_status( app_t* app, const char* fmt, ... )
    {
      va_list vl;
      va_start(vl,fmt);
      _set_statusv(app, fmt, vl );
      va_end(vl);
    }

    void _clear_status( app_t* app )
    {
      _set_status(app,"Ok");
    }

    void _log_output_func( void* arg, unsigned level, const char* text )
    {
      //app_t*   app     = (app_t*)arg;
      //unsigned logUuId = uiFindElementUuId( app->ioH, kLogId);
      
      //uiSetLogLine( app->ioH, logUuId, text );
      log::defaultOutput(nullptr,level,text);
    }

    void _free_perf_recording_recd( perf_recording_t* prp )
    {
      if( prp != nullptr )
      {
        mem::release( prp->label );
        mem::release( prp->fname );
        mem::release(prp->vel_tblA);
        mem::release(prp );
      }
    }

    rc_t _free( app_t& app )      
    {
      if( app.flow_proc_dict != nullptr )
        app.flow_proc_dict->free();

      perf_recording_t* prp = app.perfRecordingBeg;
      while(prp != nullptr )
      {
        perf_recording_t* tmp = prp->link;
        _free_perf_recording_recd(prp);
        prp = tmp;
      }
      

      mem::release((char*&)app.record_backup_dir);
      mem::release((char*&)app.record_dir);
      //mem::release((char*&)app.scoreFn);
      mem::release(app.midiRecordDir);
      mem::release(app.midiLoadFname);
      vtbl::destroy(app.vtH);
      destroy(app.sfH);
      preset_sel::destroy(app.psH);
      io_flow::destroy(app.ioFlowH);
      midi_record_play::destroy(app.mrpH);
      perf_score::destroy( app.perfScoreH );
      mem::release(app.locMap);
      return kOkRC;
    }


    rc_t _load_perf_recording_menu( app_t* app )
    {
      rc_t              rc  = kOkRC;
      perf_recording_t* prp = nullptr;
      unsigned          id  = 0;
      unsigned          selectUuId = kInvalidId;
      
      // get the peformance menu UI uuid
      if((selectUuId = io::uiFindElementUuId( app->ioH, kPerfSelId )) == kInvalidId )
      {
        rc = cwLogError(rc,"The performance list base UI element does not exist.");
        goto errLabel;        
      }      

      // for each performance recording
      for(prp = app->perfRecordingBeg; prp!=nullptr; prp=prp->link)
      {
        // create an option entry in the selection ui
        if((rc = uiCreateOption( app->ioH, prp->uuId, selectUuId, nullptr, kPerfOptionBaseId+id, kInvalidId, "optClass", prp->label )) != kOkRC )
        {          
          rc = cwLogError(kSyntaxErrorRC,"The performance recording menu create failed on %s.",prp->label);
          goto errLabel;
        }

        if( app->dflt_perf_label )
          if( textIsEqual(prp->label,app->dflt_perf_label) )
          {
            app->dflt_perf_app_id = kPerfOptionBaseId+id;
            cwLogInfo("The default performance '%s' was found.",prp->label);
          }

        prp->id = id;
        id     += 1;
      }
      
    errLabel:
      
      return rc;
    }

    rc_t _load_alt_menu( app_t* app )
    {
      rc_t              rc  = kOkRC;
      unsigned          uuid;
      unsigned          selectUuId = kInvalidId;
      
      // get the peformance menu UI uuid
      if((selectUuId = io::uiFindElementUuId( app->ioH, kAltSelId )) == kInvalidId )
      {
        rc = cwLogError(rc,"The 'alt' list base UI element does not exist.");
        goto errLabel;        
      }      

      for(unsigned altId=0; altId<alt_count(app->psH); ++altId)
      {
        const char* label = alt_label(app->psH,altId);
        assert( label != nullptr );
        
        // create an option entry in the selection ui
        if((rc = uiCreateOption( app->ioH, uuid, selectUuId, nullptr, altId, kInvalidId, "optClass", label )) != kOkRC )
        {          
          rc = cwLogError(kSyntaxErrorRC,"The 'alt' menu create failed on %s.",cwStringNullGuard(label));
          goto errLabel;
        }        
      }
      
    errLabel:
      
      return rc;
    }

    
    rc_t _parse_perf_recording_vel_tbl( app_t* app, const object_t* velTblCfg, vel_tbl_t*& velTblA_Ref, unsigned& velTblN_Ref )
    {
      rc_t rc = kOkRC;
      
      velTblA_Ref = nullptr;
      velTblN_Ref = 0;
      
      unsigned   velTblN = velTblCfg->child_count();
      vel_tbl_t* velTblA = nullptr;
      
      if( velTblN > 0 )
      {
        velTblA = mem::allocZ<vel_tbl_t>(velTblN);

        for(unsigned i = 0; i<velTblN; ++i)
        {
          if((rc = velTblCfg->child_ele(i)->getv("name",velTblA[i].name,
                                                 "device",velTblA[i].device)) != kOkRC )
          {
            rc = cwLogError(rc,"The vel table at index '%i' parse failed.",i);
            goto errLabel;
          }
        }
      }

      velTblA_Ref = velTblA;
      velTblN_Ref = velTblN;
    errLabel:
      return rc;
    }

    rc_t _create_perf_recording_recd( app_t* app, const char* dir, const char* recording_folder, const char* fname, const object_t* velTblCfg )
    {
      rc_t              rc         = kOkRC;
      perf_recording_t* prp        = nullptr;
      object_t*         meta_cfg   = nullptr;
      const char*       take_label = nullptr;
      char*             perf_fname = nullptr;
      char*             meta_fname = nullptr;
      bool              skip_fl    = false;
      
      // create the performance recording file path
      if((perf_fname = filesys::makeFn(dir,fname,nullptr,recording_folder,nullptr)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The performance file name formation failed on directory '%s'.",cwStringNullGuard(recording_folder));
        goto errLabel;
      }

      // if path does not identify an existing file  - skip it
      if( !filesys::isFile(perf_fname) )
        goto errLabel;

      if((meta_fname = filesys::makeFn(dir,"meta","cfg",recording_folder,nullptr)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The performance meta file name formation failed on directory '%s'.",cwStringNullGuard(recording_folder));
        goto errLabel;          
      }
      
      // parse the perf. meta file
      if((rc = objectFromFile( meta_fname, meta_cfg )) != kOkRC )
      {
        rc = cwLogError(rc,"Performance meta file '%s' parse failed.",cwStringNullGuard(meta_fname));
        goto errLabel;
      }

      // allocate the perf_recording_t recd
      prp = mem::allocZ<perf_recording_t>();
        
      // read the meta file values
      if((rc = meta_cfg->getv("take_label",take_label,
                              "session_number",prp->sess_numb,
                              "take_number",prp->take_numb,
                              "beg_loc",prp->beg_loc,
                              "end_loc",prp->end_loc,
                              "skip_score_follow_fl",skip_fl)) != kOkRC )
      {
        rc = cwLogError(rc,"Performance meta file '%s' parse failed.",cwStringNullGuard(meta_fname));
        goto errLabel;          
      }

      if( skip_fl )
      {
        cwLogWarning("'Skip score follow flag' set.Skipping recorded performance '%s'.",cwStringNullGuard(take_label));
        goto errLabel;
      }
                           
      prp->label = mem::duplStr(take_label);
      prp->fname = mem::duplStr(perf_fname);
        
      if((rc = _parse_perf_recording_vel_tbl(app, velTblCfg, prp->vel_tblA, prp->vel_tblN )) != kOkRC )
      {
        rc = cwLogError(rc,"Parse failed on vel table entry for the recorded performance in '%s'.",cwStringNullGuard(dir));
        goto errLabel;
      }

      if( app->perfRecordingEnd == nullptr )
      {
        app->perfRecordingBeg = prp;
        app->perfRecordingEnd = prp;
      }
      else
      {          
        app->perfRecordingEnd->link = prp;            
        app->perfRecordingEnd = prp;
      }

    errLabel:
      if( rc != kOkRC || skip_fl )
        _free_perf_recording_recd( prp );

      mem::release(meta_fname);
      mem::release(perf_fname);

      if( meta_cfg != nullptr )
        meta_cfg->free();
      
      return rc;      
    }

    rc_t _parse_perf_recording_dir( app_t* app, const char* dir, const char* fname, const object_t* velTblCfg )
    {
      rc_t                 rc  = kOkRC;
      filesys::dirEntry_t* deA  = nullptr;
      unsigned             deN = 0;
      
      // get the directory entries based on 'dir'
      if((deA = filesys::dirEntries( dir, filesys::kDirFsFl, &deN )) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The attempt to get the performance directory at '%s' failed.",cwStringNullGuard(dir));
        goto errLabel;
      }

      if( deN == 0 )
        cwLogWarning("The performance recording directory '%s' was found to be empty.",cwStringNullGuard(dir));

      // for each directory entry
      for(unsigned i=0; i<deN; ++i)
        if((rc = _create_perf_recording_recd(app, dir, deA[i].name, fname, velTblCfg )) != kOkRC )
          goto errLabel;
      
    errLabel:
      mem::release(deA);
      return rc;

    }
    
    rc_t _load_perf_dir_selection_menu( app_t* app )
    {
      rc_t rc = kOkRC;
      // verify that a performance list was given
      if( app->perfDirL == nullptr || app->perfDirL->child_count()==0)
      {
        rc = cwLogError(rc,"The performance directorty list is missing or empty.");
        goto errLabel;
      }

      // for each performance directory
      for(unsigned i=0; i<app->perfDirL->child_count(); ++i)
      {
        const object_t* d         = nullptr;;
        const char*     dir       = nullptr;
        const char*     fname     = nullptr;
        const object_t* velTblCfg = nullptr;

        // get the directory dict. from the cfg file
        if((d = app->perfDirL->child_ele(i)) == nullptr || !d->is_dict() )
        {
          rc = cwLogError(kSyntaxErrorRC,"The performance directory entry at index '%i' is malformed.",i);
          goto errLabel;
        }

        // get the directory 
        if((rc = d->getv("dir",dir,
                         "fname",fname,
                         "vel_table", velTblCfg)) != kOkRC )
        {
          rc = cwLogError(rc ,"Error parsing the performance directory entry at index '%i'.",i);
          goto errLabel;
        }

        // create the performance records from this directory
        if((rc = _parse_perf_recording_dir(app,dir,fname,velTblCfg)) != kOkRC )
        {
          rc = cwLogError(rc ,"Error creating the performance directory entry at index '%i'.",i);
          goto errLabel;          
        }        
      }      

      if((rc = _load_perf_recording_menu( app )) != kOkRC )
      {
        rc = cwLogError(rc,"The performance menu creation failed.");
        goto errLabel;
      }

      
    errLabel:

      if(rc != kOkRC )
        rc = cwLogError(rc,"An error occured while creating the recorded performance list.");
        
      return rc;
    }

    
    double _get_system_sample_rate( app_t* app, const char* groupLabel )
    {
      unsigned groupIdx = kInvalidIdx;
      double   srate    = 0;
      
      if((groupIdx = audioGroupLabelToIndex( app->ioH, groupLabel )) == kInvalidIdx )
      {
        cwLogError(kOpFailRC,"The audio group '%s' could not be found.", cwStringNullGuard(groupLabel));
        goto errLabel;
      }

      if(( srate = audioGroupSampleRate( app->ioH, groupIdx )) == 0 )
      {
        cwLogError(kOpFailRC,"The sample rate could not be determined for the audio group: '%s'.", cwStringNullGuard(groupLabel));
        goto errLabel;
      }

    errLabel:
      
      return srate;
    }

    rc_t _apply_preset( app_t* app, unsigned loc, const perf_score::event_t* score_evt=nullptr, const preset_sel::frag_t* frag=nullptr  )
    {
      // if frag is NULL this is the beginning of a play session
      if( frag == nullptr )
      {
        preset_sel::track_loc_reset(app->psH);        
        preset_sel::track_loc( app->psH, loc, frag);
      }
      
      if( frag == nullptr )
        cwLogInfo("No preset fragment was found for the requested timestamp.");
      else
      {
        unsigned    preset_idx        = kInvalidIdx;
        const char* preset_label      = nullptr;
        const char* preset_type_label = "<None>";
        rc_t        apply_rc          = kOkRC;
        
        // if the preset sequence player is active then apply the next selected seq. preset
        // otherwise select the next primary preset for ths fragment
        unsigned seq_idx_n = app->seqActiveFl ? app->seqPresetIdx : kInvalidIdx;

        if( score_evt != nullptr )
        {
          //printf("Meas:e:%f d:%f t:%f c:%f\n",score_evt->even,score_evt->dyn,score_evt->tempo,score_evt->cost);
          printf("Meas:e:%f d:%f t:%f c:%f\n",score_evt->featV[perf_meas::kEvenValIdx],score_evt->featV[perf_meas::kDynValIdx],score_evt->featV[perf_meas::kTempoValIdx],score_evt->featV[perf_meas::kMatchCostValIdx]);
        }

        // if we are not automatically sequencing through the presets and a score event was given
        if( seq_idx_n == kInvalidIdx && score_evt != nullptr  )
        {
          unsigned multiPresetN = 0;

          // allow-any,dry-priority,dry-selected may only be set when pri-prob is set
          bool allowAnyFl    = cwIsFlag(app->multiPresetFlags,flow::kAllowAllPresetFl)    && cwIsFlag(app->multiPresetFlags,flow::kPriPresetProbFl);
          bool dryPriorityFl = cwIsFlag(app->multiPresetFlags,flow::kDryPriorityPresetFl) && cwIsFlag(app->multiPresetFlags,flow::kPriPresetProbFl);
          bool drySelectedFl = cwIsFlag(app->multiPresetFlags,flow::kDrySelectedPresetFl) && cwIsFlag(app->multiPresetFlags,flow::kPriPresetProbFl);

          unsigned activePresetFlags  = 0;

          activePresetFlags = cwEnaFlag(activePresetFlags, preset_sel::kAllActiveFl,   allowAnyFl);
          activePresetFlags = cwEnaFlag(activePresetFlags, preset_sel::kDryPriorityFl, dryPriorityFl);
          activePresetFlags = cwEnaFlag(activePresetFlags, preset_sel::kDrySelectedFl, drySelectedFl);
                    
          flow::multi_preset_selector_t mp_sel =
            { .flags     = app->multiPresetFlags,
              .coeffV    = score_evt->featV,
              .coeffMinV = score_evt->featMinV,
              .coeffMaxV = score_evt->featMaxV,
              .coeffN    = perf_meas::kValCnt,
              .presetA   = fragment_active_presets(app->psH,frag,activePresetFlags,multiPresetN),
              .presetN   = multiPresetN
            };

          if( mp_sel.presetA == nullptr || mp_sel.presetN == 0 )
            cwLogWarning("No active presets were found for loc:%i at end loc:%i.",loc,frag->endLoc);
          else
          {
            if( app->ioFlowH.isValid() )
              apply_rc = io_flow::apply_preset( app->ioFlowH, flow_cross::kNextDestId, mp_sel );
          }
          
          preset_label = "(multi)"; 

          preset_type_label = "multi";
            
        }
        else
        {          
          // get the preset index to play for this fragment
          if((preset_idx = fragment_play_preset_index(app->psH, frag, seq_idx_n)) == kInvalidIdx )
            cwLogInfo("No preset has been assigned to the fragment at end loc. '%i'.",frag->endLoc );
          else
            preset_label = preset_sel::preset_label(app->psH,preset_idx);
          
          if( preset_label != nullptr )
          {
            if( app->ioFlowH.isValid() )
              apply_rc = io_flow::apply_preset( app->ioFlowH, flow_cross::kNextDestId, preset_label );

            preset_type_label = "single";
          }

          // don't display preset updates unless the score is actually loaded          
          printf("Apply %s preset: '%s' : loc:%i status:%i\n", preset_type_label, preset_label==nullptr ? "<invalid>" : preset_label, loc, apply_rc );
          
        }          

        _set_status(app,"Apply %s preset: '%s'.", preset_type_label, preset_label==nullptr ? "<invalid>" : preset_label);
        
        // apply the fragment defined gain settings
        if( app->ioFlowH.isValid() )
        {
          io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::coeff_t)frag->igain );
          io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::coeff_t)frag->ogain );
          io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal",      "in",   flow::kAnyChIdx, (dsp::coeff_t)frag->wetDryGain );

          // activate the cross-fade
          io_flow::begin_cross_fade( app->ioFlowH, frag->fadeOutMs );
        }
      }

      return kOkRC;      
    }


    // Turn on the selection border for the clicked fragment
    rc_t _do_select_frag( app_t* app, unsigned clickedUuId )
    {
      rc_t rc = kOkRC;

      // get the last selected fragment
      unsigned prevFragId = preset_sel::ui_select_fragment_id(app->psH);
      unsigned prevUuId = preset_sel::frag_to_gui_id(app->psH,prevFragId,false);
      
      // is the last selected fragment the same as the clicked fragment
      bool reclickFl = prevUuId == clickedUuId;

      // if a different fragment was clicked then deselect the last fragment in the UI
      if( !reclickFl )
      {
        if(prevUuId != kInvalidId )
          io::uiSetSelect(   app->ioH, prevUuId, false );

        // select or deselect the clicked fragment
        io::uiSetSelect(   app->ioH, clickedUuId, !reclickFl );
      }
      
      // Note: calls to uiSetSelect() produce callbacks to _onUiSelect().

      return rc;
    }

    rc_t _do_stop_play( app_t* app )
    {
      rc_t rc = kOkRC;
      unsigned evt_cnt = 0;
      
      if( app->in_audio_dev_idx != kInvalidIdx )
      {
        if((rc = audioDeviceEnable( app->ioH, app->in_audio_dev_idx, true, false )) != kOkRC )
        {
          rc = cwLogError(rc,"Enable failed on audio device input file.");
          goto errLabel;
        }
      }
            
      if((rc = midi_record_play::stop(app->mrpH)) != kOkRC )
      {
        rc = cwLogError(rc,"MIDI stop failed.");
        goto errLabel;
      }

      if( app->enableRecordFl && (evt_cnt = midi_record_play::event_count(app->mrpH)) > 0 )
      {
        io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kTotalMidiEvtCntId ), evt_cnt );
        io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kMidiSaveBtnId ), true );
      }

    errLabel:
      return rc;
    }

    unsigned _get_loc_from_score_follower( app_t* app, double secs, unsigned muid, uint8_t status, uint8_t d0, uint8_t d1 )
    {
      unsigned loc = score_parse::kInvalidLocId;
            
      // if this is a MIDI note-on event - then udpate the score follower
      if( midi::isNoteOn(status,d1) && muid != kInvalidIdx )
      {
        unsigned smpIdx             = 0; // not functional - used to associate input with score follower output 
        bool     newMatchOccurredFl = false;
        if( exec( app->sfH, secs, smpIdx, muid, status, d0, d1, newMatchOccurredFl ) != kOkRC )
        {
          cwLogWarning("Score follower exec error.");
        }
        else
        {
          if( newMatchOccurredFl )
          {
            unsigned        matchLocN = 0;
            const unsigned* matchLocA = current_result_index_array( app->sfH, matchLocN );

            unsigned maxLocId = 0;

            printf("SF: ");
            for(unsigned i=0; i<matchLocN; ++i)
            {
              if( matchLocA[i] > maxLocId )
                maxLocId = matchLocA[i];
              printf("%i ",matchLocA[i]);
            }                  
            printf("\n");


            loc = maxLocId;
            
            clear_result_index_array(app->sfH);
          }
                
        }
      }
      
      return loc;      
    }
    
    void _midi_play_callback( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, const void* msg_arg, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
    {
      app_t* app = (app_t*)arg;
      switch( actionId )
      {
        case midi_record_play::kPlayerStoppedActionId:
          app->seqStartedFl = false;
          _do_stop_play(app);
          _set_status(app,"Done");
          break;
          
        case midi_record_play::kMidiEventActionId:
        {
          if( app->printMidiFl )
          {
            const unsigned buf_byte_cnt = 256;
            char           buf[ buf_byte_cnt ];
          
            // if this event is not in the score
            if( id == kInvalidId )
            {
              // TODO: print this out in the same format as event_to_string()
              snprintf(buf,buf_byte_cnt,"ch:%i status:0x%02x d0:%i d1:%i",ch,status,d0,d1);
            }
            else
              perf_score::event_to_string( app->perfScoreH, id, buf, buf_byte_cnt );
            printf("%s\n",buf);
          }

          if( midi_record_play::is_started(app->mrpH) )
          {
            const preset_sel::frag_t* f = nullptr;

            double sec = time::specToSeconds(timestamp);

            // call the score follower
            if( score_follower::is_enabled(app->sfH) )
              loc = _get_loc_from_score_follower( app, sec, id, status, d0, d1 );
          
            // TODO: ZERO SHOULD BE A VALID LOC VALUE - MAKE -1 THE INVALID LOC VALUE
            
            if( loc != score_parse::kInvalidLocId  && app->trackMidiFl )
            {
              if( preset_sel::track_loc( app->psH, loc, f ) )  
              {
                _apply_preset( app, loc, (const perf_score::event_t*)msg_arg, f );
                
                if( f != nullptr )
                  _do_select_frag( app, f->guiUuId );
              }
            }
            
          }
          break;
        }
      }
    }

    rc_t  _on_live_midi_event( app_t* app, const io::msg_t& msg )
    {
      rc_t rc = kOkRC;

      if( msg.u.midi != nullptr )
      {        
        const io::midi_msg_t& m   = *msg.u.midi;
        const midi::packet_t* pkt = m.pkt;
        
        // for each midi msg
        for(unsigned j = 0; j<pkt->msgCnt; ++j)
        {
          // if this is a sys-ex msg
          if( pkt->msgArray == NULL )
          {
            cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
          }
          else                  // this is a triple
          {
            time::spec_t timestamp;
            midi::msg_t* mm = pkt->msgArray + j;
            unsigned     id = app->enableRecordFl ? last_store_index(app->mrpH) : kInvalidId;
            unsigned     loc = app->beg_play_loc;
            
            time::get(timestamp);
            
            if( midi::isChStatus(mm->status) )
            {
              if(midi_record_play::send_midi_msg( app->mrpH, midi_record_play::kSampler_MRP_DevIdx, mm->ch, mm->status, mm->d0, mm->d1 ) == kOkRC )                
                _midi_play_callback( app, midi_record_play::kMidiEventActionId, id, timestamp, loc, nullptr, mm->ch, mm->status, mm->d0, mm->d1 );
            }

          }
        }          
      }

      return rc;
    }
      

    // Find the closest locMap equal to or after 'loc'
    loc_map_t* _find_loc( app_t* app, unsigned loc )
    {
      unsigned   i=0;
      loc_map_t* pre_loc_map = nullptr;
      
      for(; i<app->locMapN; ++i)
      {
        if( app->locMap[i].loc >= loc )
          return app->locMap +i;

        pre_loc_map = app->locMap + i;
           
      }
      
      return pre_loc_map;
    }

    rc_t  _loc_to_frame_index( app_t* app, unsigned loc, unsigned& frameIdxRef )
    {
      rc_t                  rc   = kOkRC;
      const perf_score::event_t* e0   = nullptr;
      const perf_score::event_t* e1   = nullptr;
      double                srate     = 0;
      double                secs = 0;

      frameIdxRef = kInvalidIdx;
      
      if( app->in_audio_begin_loc != score_parse::kInvalidLocId )
      {
        if((e0 = loc_to_event(app->perfScoreH,app->in_audio_begin_loc)) == nullptr )
        {
          rc = cwLogError(kInvalidArgRC,"The score event associated with the 'in_audio_beg_loc' loc:%i could not be found.",loc);
          goto errLabel;
        }
      }
      
      if((e1 = loc_to_event(app->perfScoreH,loc)) == nullptr )
      {
        rc = cwLogError(kInvalidArgRC,"The score event associated with the begin play loc:%i could not be found.",loc);
        goto errLabel;
      }

      if((srate = audioDeviceSampleRate(app->ioH, app->in_audio_dev_idx )) == 0 )
      {
        rc = cwLogError(kInvalidArgRC,"Audio device file sample rate could not be accessed.");
        goto errLabel;        
      }

      if( e1->sec < e0->sec )
        cwLogWarning("The audio file start time ('in_audio_beg_sec') (%f sec) is prior to the start play location %f sec.",e1->sec,e0->sec);
      else
      {
        secs = (e1->sec - e0->sec) + app->in_audio_offset_sec;      
        cwLogInfo("File offset %f seconds. %f %f",secs);
      }
      
      frameIdxRef = (unsigned)(secs * srate);
      
    errLabel:
      
      return rc;
    }

    bool _is_performance_loaded( app_t* app )
    {
      return app->perfScoreH.isValid() and event_count(app->perfScoreH) > 0;
    }

    rc_t _do_sf_reset( app_t* app, unsigned loc )
    {
      rc_t rc = kOkRC;

      track_loc_reset( app->psH );
      
      score_follower::reset(app->sfH,app->sfResetLoc);

      cwLogInfo("SF reset loc: %i",app->sfResetLoc);
      return rc;
    }
    
    

    rc_t _do_start( app_t* app, unsigned begLoc, unsigned endLoc )
    {
      rc_t       rc         = kOkRC;
      bool       rewindFl   = true;
      loc_map_t* begMap     = nullptr;
      loc_map_t* endMap     = nullptr;
      unsigned   score_loc  = app->sfResetLoc;
      unsigned   preset_loc = app->sfResetLoc;

      // if the player is already playing then stop it
      if( midi_record_play::is_started(app->mrpH) )
      {
        rc = _do_stop_play(app);
        goto errLabel;
      }

      // if we are using and audio file as the source
      if( app->in_audio_dev_idx != kInvalidIdx )
      {
        unsigned frameIdx = 0;
        if((rc = _loc_to_frame_index(app, begLoc, frameIdx )) != kOkRC )
        {
          rc = cwLogError(rc,"Frame index could not be calculated.");
          goto errLabel;
        }
        
        if((rc = audioDeviceSeek( app->ioH, app->in_audio_dev_idx, true, frameIdx )) != kOkRC )
        {
          rc = cwLogError(rc,"Seek failed on audio device input file.");
          goto errLabel;
        }

        if((rc = audioDeviceEnable( app->ioH, app->in_audio_dev_idx, true, true )) != kOkRC )
        {
          rc = cwLogError(rc,"Enable failed on audio device input file.");
          goto errLabel;
        }
      }

      // if a performance is loaded
      if( _is_performance_loaded(app) )
      {
        if((begMap = _find_loc(app,begLoc)) == nullptr )
        {
          rc = cwLogError(kInvalidArgRC,"The begin play location (%i) is not valid.",begLoc);
        goto errLabel;
        }

        if((endMap = _find_loc(app,endLoc)) == nullptr )
        {
          rc = cwLogError(kInvalidArgRC,"The end play location (%i) is not valid.",endLoc);
          goto errLabel;
        }

        if( !time::isZero(begMap->timestamp)  )
        {
          // seek the player to the requested loc
          if((rc = midi_record_play::seek( app->mrpH, begMap->timestamp )) != kOkRC )
          {
            rc = cwLogError(rc,"MIDI seek failed.");
            goto errLabel;
          }
          rewindFl = false;
        }

        score_loc  = begLoc;
        preset_loc = begMap->loc;
      }

      // if recording - empty the recording buffer
      if( app->enableRecordFl )
      {
        midi_record_play::set_record_state(app->mrpH,app->enableRecordFl);
        midi_record_play::clear(app->mrpH);
        io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kMidiSaveBtnId ), false );
      }
      
      // reset the score follower
      if((rc = _do_sf_reset(app,score_loc)) != kOkRC )
      {
        rc = cwLogError(rc,"Score follower reset failed.");
        goto errLabel;        
      }
      
      // apply the preset which is active at the start time
      if((rc = _apply_preset( app, preset_loc )) != kOkRC )
      {
        rc = cwLogError(rc,"Preset application failed prior to MIDI start.");
        goto errLabel;
      }

      // start the MIDI record/play unit
      if( _is_performance_loaded(app) || app->enableRecordFl )
      {
        unsigned evt_cnt = 0;
        
        if((rc = midi_record_play::start(app->mrpH,rewindFl,&endMap->timestamp)) != kOkRC )
        {
          rc = cwLogError(rc,"MIDI start failed.");
          goto errLabel;
        }


        // update the current event loc/count
        if( _is_performance_loaded(app) )
          evt_cnt = midi_record_play::event_loc(app->mrpH);
        else
          if( app->enableRecordFl )
            evt_cnt = midi_record_play::event_count(app->mrpH);
          
        io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), evt_cnt  );
      }
      
    errLabel:
      return rc;
    }

    rc_t _do_goto( app_t* app, unsigned loc )
    {
      rc_t rc = kOkRC;
      unsigned fragGuiId;

      // scroll to the top - This is a hack - move this logic in ui.js.
      if((fragGuiId = loc_to_gui_id(app->psH,1)) == kInvalidId )
      {
        rc = cwLogError(kInvalidArgRC,"The fragment loc '%i' could not be found.");
        goto errLabel;
      }
      
      if((rc = io::uiSetScrollTop(app->ioH,fragGuiId)) != kOkRC )
      {
        rc = cwLogError(rc,"Scroll to top failed on fragment GUI id:%i.",fragGuiId);
        goto errLabel;
      }

      // scroll to loc
      if((fragGuiId = loc_to_gui_id(app->psH,loc)) == kInvalidId )
      {
        rc = cwLogError(kInvalidArgRC,"The fragment loc '%i' could not be found.");
        goto errLabel;
      }
      
      if((rc = io::uiSetScrollTop(app->ioH,fragGuiId)) != kOkRC )
      {
        rc = cwLogError(rc,"Scroll to top failed on fragment GUI id:%i.",fragGuiId);
        goto errLabel;
      }

    errLabel:
      return rc;
    }
    

    // This function is used to apply the selected (checked) preset immediately.
    rc_t _apply_current_preset( app_t* app, unsigned fragId )
    {
      rc_t                      rc               = kOkRC;
      const preset_sel::frag_t* frag             = nullptr;
      bool                      orig_seqActiveFl = app->seqActiveFl;

      // temporarily turn of the preset sequencer (if it is active)
      app->seqActiveFl = false;

      if((frag = preset_sel::get_fragment( app->psH, fragId )) == nullptr )
      {
        rc = cwLogError(rc,"The fragment at id '%i' could not be accessed.",fragId);
        goto errLabel;
      }

      // apply the preset which is active at the start time
      if((rc = _apply_preset( app, frag->begPlayLoc, nullptr, frag )) != kOkRC )
      {
        rc = cwLogError(rc,"Preset application failed on fragment at id '%i'.",fragId);
        goto errLabel;        
      }

    errLabel:

      app->seqActiveFl = orig_seqActiveFl;
      return rc;
    }

    rc_t _do_play_fragment( app_t* app, unsigned fragId )
    {
      rc_t     rc;
      unsigned begLoc = 0;
      unsigned endLoc = 0;

      // get the fragment starting location as currently set in the UI
      // (this may be different from the fragment begin location)
      if((rc = get_value( app->psH, fragId, preset_sel::kBegPlayLocVarId, kInvalidId, begLoc )) != kOkRC )
      {
        rc = cwLogError(rc,"Could not retrieve the begin play location for fragment id:%i.",fragId);
        goto errLabel;
      }

      // get the fragment ending location as currently set in the UI
      // (this may be different from the fragment end location)
      if((rc = get_value( app->psH, fragId, preset_sel::kEndPlayLocVarId, kInvalidId, endLoc )) != kOkRC )
      {
        rc = cwLogError(rc,"Could not retrieve the begin play location for fragment id:%i.",fragId);
        goto errLabel;
      }

      rc = _do_start(app,begLoc,endLoc);
      
    errLabel:
      return rc;
      
    }

    rc_t _do_seq_play_fragment( app_t* app, unsigned fragId )
    {
      rc_t rc = kOkRC;
      
      if( app->seqActiveFl )
      {
        app->seqActiveFl = false;
      }
      else
      {
        app->seqFragId    = fragId;
        app->seqPresetIdx = 0;
        app->seqStartedFl = true;
        app->seqActiveFl  = true;        
      }

      // Note that if the MIDI player is already playing
      // calling '_do_play_fragment()' here will stop the player
      _do_play_fragment( app, app->seqFragId );

      return rc;
    }

    rc_t _do_seq_exec( app_t* app )
    {
      rc_t rc = kOkRC;
      
      // if the seq player is active but currently stopped
      if( app->seqActiveFl && app->seqStartedFl == false)
      {
        app->seqPresetIdx += 1;
        app->seqStartedFl  = app->seqPresetIdx < preset_sel::fragment_seq_count( app->psH, app->seqFragId );
        app->seqActiveFl   = app->seqStartedFl;
        
        if( app->seqStartedFl )
          _do_play_fragment( app, app->seqFragId );
      }
      return rc;
    }
    
    void _update_event_ui( app_t* app )
    {
      //io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId),   midi_record_play::event_index(app->mrpH) );
      //io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), midi_record_play::event_count(app->mrpH) );
      
      io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), app->maxPerfLoc-app->minPerfLoc );
    }

    // Update the UI with the value from the the fragment data record.
    template< typename T >
    rc_t _update_frag_ui( app_t* app, unsigned fragId, unsigned psVarId, unsigned psPresetId, unsigned uiParentUuId, unsigned uiVarAppId, unsigned uiChanId,  T& valRef )
    {
      rc_t     rc             = kOkRC;
      unsigned uuid           = kInvalidId;
      
      // Get the value from the data record
      if((rc = preset_sel::get_value( app->psH, fragId, psVarId, psPresetId, valRef )) != kOkRC )
      {
        rc = cwLogError(rc,"Unable to locate the preset value for var:%i preset:%i.",psVarId,psPresetId);
        goto errLabel;
      }

      // Get the UI uuId
      if(( uuid = io::uiFindElementUuId( app->ioH, uiParentUuId, uiVarAppId, uiChanId )) == kInvalidId )
      {
        rc = cwLogError(rc,"Unable to locate the UI uuid for appid:%i chanId:%i.", uiVarAppId, uiChanId );
        goto errLabel;
      }

      // Send the value to the UI
      if((rc = io::uiSendValue( app->ioH, uuid, valRef )) != kOkRC )
      {
        rc = cwLogError(rc,"Transmission of fragment value failed.");
        goto errLabel;
      }

    errLabel:
      return rc;
    }

    // Get the endLoc associated with this fragment id
    rc_t _frag_id_to_endloc( app_t* app, unsigned fragId, unsigned& endLocRef )
    {
      rc_t rc = kOkRC;

      endLocRef                                                                                 = kInvalidId;
      if((rc = get_value( app->psH, fragId, preset_sel::kEndLocVarId, kInvalidId, endLocRef )) != kOkRC )
        rc                                                                                      = cwLogError(rc,"Unable to get the 'end loc' value for fragment id:%i.",fragId);

      return rc;
    }

    // Update the preset select check boxes on a fragment panel
    rc_t _update_frag_select_flags( app_t* app, unsigned fragId, unsigned fragEndLoc = kInvalidId, bool apply_preset_fl = true )
    {
      rc_t rc = kOkRC;

      if( fragEndLoc == kInvalidId )
      {
        // get the endLoc associated with this fragment
        rc = _frag_id_to_endloc(app, fragId, fragEndLoc );
      }

      if( rc == kOkRC )
      {
        bool     bValue;
        unsigned uValue;
        const char* sValue;
        unsigned fragPanelUuId;
        
        // The uiChan is the fragment endLoc
        unsigned uiChanId = fragId; //fragEndLoc;

        // Get the fragPanelUUid
        get_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId );

        // uuid of the frag preset row
        unsigned fragPresetRowUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragPresetRowId, uiChanId );
        
        // Update each fragment preset control UI by getting it's current value from the fragment data record
        for(unsigned preset_idx=0; preset_idx<preset_sel::preset_count(app->psH); ++preset_idx)
        {
          _update_frag_ui( app, fragId, preset_sel::kPresetSelectVarId,   preset_idx, fragPresetRowUuId, kFragPresetSelId,    preset_idx,  bValue );          
          _update_frag_ui( app, fragId, preset_sel::kPresetOrderVarId,    preset_idx, fragPresetRowUuId, kFragPresetOrderId,  preset_idx,  uValue );          
          _update_frag_ui( app, fragId, preset_sel::kPresetAltVarId,      preset_idx, fragPresetRowUuId, kFragPresetAltId,    preset_idx,  sValue );          
          _update_frag_ui( app, fragId, preset_sel::kPresetSeqSelectVarId,preset_idx, fragPresetRowUuId, kFragPresetSeqSelId, preset_idx,  bValue );          
        }

        if( apply_preset_fl )
          _apply_current_preset(app, fragId );
        
      }
      
      return rc;
    }

    // Update the fragment UI withh the fragment record associated with 'fragId'
    rc_t _update_frag_ui(app_t* app, unsigned fragId, bool apply_preset_fl=true )
    {
      // Notes: 
      // uiChanId = fragId for panel values or uiChanId = preset_index for preset values
                                      
      rc_t     rc = kOkRC;
      unsigned endLoc;

      // Get the endLoc for this fragment
      if((rc = _frag_id_to_endloc(app, fragId, endLoc )) == kOkRC )
      {
        unsigned    uValue;
        double      dValue;
        const char* sValue;
        unsigned    uiChanId      = fragId; //endLoc;
        unsigned    fragPanelUuId = kInvalidId;
        
        get_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId );
        
        _update_frag_ui( app, fragId, preset_sel::kBegLocVarId,     kInvalidId, fragPanelUuId, kFragBegLocId,     uiChanId,  uValue );          
        _update_frag_ui( app, fragId, preset_sel::kEndLocVarId,     kInvalidId, fragPanelUuId, kFragEndLocId,     uiChanId,  uValue );          
        _update_frag_ui( app, fragId, preset_sel::kInGainVarId,     kInvalidId, fragPanelUuId, kFragInGainId,     uiChanId,  dValue );
        _update_frag_ui( app, fragId, preset_sel::kInGainVarId,     kInvalidId, fragPanelUuId, kFragOutGainId,    uiChanId,  dValue );
        _update_frag_ui( app, fragId, preset_sel::kFadeOutMsVarId,  kInvalidId, fragPanelUuId, kFragFadeOutMsId,  uiChanId,  dValue );
        _update_frag_ui( app, fragId, preset_sel::kWetGainVarId,    kInvalidId, fragPanelUuId, kFragWetDryGainId, uiChanId,  dValue );
        _update_frag_ui( app, fragId, preset_sel::kBegPlayLocVarId, kInvalidId, fragPanelUuId, kFragBegPlayLocId, uiChanId,  uValue );          
        _update_frag_ui( app, fragId, preset_sel::kEndPlayLocVarId, kInvalidId, fragPanelUuId, kFragEndPlayLocId, uiChanId,  uValue );          
        _update_frag_ui( app, fragId, preset_sel::kNoteVarId,       kInvalidId, fragPanelUuId, kFragNoteId,       uiChanId,  sValue );          
        
        _update_frag_select_flags( app, fragId, endLoc, apply_preset_fl );
        
      }
      
      return rc;
    }

    rc_t  _frag_uuid_to_blob( app_t* app, unsigned uuId, ui_blob_t*& blobRef )
    {
      unsigned blobByteN = 0;
      
      if(( blobRef = (ui_blob_t*)io::uiGetBlob( app->ioH, uuId, blobByteN )) == nullptr || blobByteN != sizeof(ui_blob_t) )
        return cwLogError(kInvalidStateRC,"A fragment UI blob was missing or corrupt for GUI uuid:%i.",uuId);

      return kOkRC;
    }

    void _enable_global_play_btn( app_t* app, bool enableFl )
    {
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), enableFl );
      //io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ),  enableFl );      
    }

    void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, unsigned, const char* ){}
    void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, const char*, unsigned  ){}
    void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, unsigned begPlayLoc, unsigned endPlayLoc )
    {
      bool     enableFl = begPlayLoc < endPlayLoc;
      unsigned fragUuId = kInvalidId;

      if((fragUuId = frag_to_gui_id( app->psH, blob->fragId )) != kInvalidId )
      {
        unsigned btnIdA[] = { kFragPlayBtnId, kFragPlaySeqBtnId, kFragPlayAllBtnId };
        unsigned btnIdN = sizeof(btnIdA)/sizeof(btnIdA[0]);

        for(unsigned i = 0; i<btnIdN; ++i)
        {
          unsigned btnUuId;
          if((btnUuId = uiFindElementUuId( app->ioH, fragUuId, btnIdA[i], blob->presetId )) != kInvalidId )
            uiSetEnable( app->ioH, btnUuId, enableFl );

        }
      }

      //if( enableFl )
      //  _clear_status(app);
      //else
      if( !enableFl )
      {
        _set_status(app,"Invalid fragment play range. beg:%i end:%i",begPlayLoc,endPlayLoc);
        cwLogError(kInvalidArgRC,"Invalid fragment play range. beg:%i end:%i",begPlayLoc,endPlayLoc);
      }

    }

    void _disable_frag_play_btn( app_t* app, unsigned fragBegEndUuId )
    {
      ui_blob_t* blob = nullptr;
      if(_frag_uuid_to_blob(app, fragBegEndUuId, blob) == kOkRC )
        _enable_frag_play_btn( app, blob, 1, (unsigned)0 );      
    }

    // Called when a UI value is changed in a fragment panel (e.g. gain, fadeMs, ...)
    template< typename T>
    rc_t _on_ui_frag_value( app_t* app, unsigned uuId, const T& value )
    {
      rc_t       rc   = kOkRC;
      ui_blob_t* blob = nullptr;

      if((rc = _frag_uuid_to_blob(app, uuId, blob)) != kOkRC )
        goto errLabel;

      rc = preset_sel::set_value( app->psH, blob->fragId, blob->varId, blob->presetId, value );

      if( rc != kOkRC )
      {
        // TODO: Set the error indicator on the GUI
      }
      else
      {
        // TODO: Clear the error indicator on the GUI
      }

      //
      switch( blob->varId )
      {
        case preset_sel::kPresetSelectVarId:
          _update_frag_select_flags( app, blob->fragId);
          break;

        case preset_sel::kPresetAltVarId:          
          _update_frag_select_flags( app, blob->fragId);
          break;

        case preset_sel::kPresetSeqSelectVarId:
          _update_frag_select_flags( app, blob->fragId);
          break;
          
          
        case preset_sel::kPlayBtnVarId:
          _do_play_fragment( app, blob->fragId );
          break;

        case preset_sel::kPlaySeqBtnVarId:
          _do_seq_play_fragment( app, blob->fragId );
          break;

        case preset_sel::kPlaySeqAllBtnVarId:
          _do_seq_play_fragment( app, blob->fragId );
          break;
          
        case preset_sel::kBegPlayLocVarId:
        {
          unsigned endPlayLoc;
          get_value( app->psH, blob->fragId, preset_sel::kEndPlayLocVarId, blob->presetId, endPlayLoc );
          _enable_frag_play_btn( app, blob, value, endPlayLoc );
        }
        break;
          
        case preset_sel::kEndPlayLocVarId:
        {
          unsigned begPlayLoc;
          get_value( app->psH, blob->fragId, preset_sel::kBegPlayLocVarId, blob->presetId, begPlayLoc );
          _enable_frag_play_btn( app, blob, begPlayLoc, value );
        }
        break;
      }

    errLabel:
      return rc;
    }


    rc_t _frag_set_ui_blob( app_t* app, unsigned uuId, unsigned fragId, unsigned varId, unsigned presetId )
    {
      ui_blob_t blob = { .fragId = fragId, .varId=varId, .presetId=presetId };
      return io::uiSetBlob( app->ioH, uuId, &blob, sizeof(blob) );      
    }
    
    rc_t _create_frag_preset_ctl( app_t* app, unsigned fragId, unsigned fragPresetRowUuId, unsigned presetN, unsigned preset_idx )
    {
      rc_t        rc           = kOkRC;
      unsigned    colUuId      = kInvalidId;
      unsigned    uuId         = kInvalidId;
      const char* nullEleName  = nullptr;
      const char* nullClass    = nullptr;
      unsigned    invalidAppId = kInvalidId;
      unsigned    chanId       = preset_idx; // chanId is the preset id
      const char* presetLabel  = preset_sel::preset_label( app->psH, preset_idx );
        
      assert( presetLabel != nullptr );

      // preset control column container
      if((rc = io::uiCreateDiv( app->ioH, colUuId, fragPresetRowUuId, nullEleName, invalidAppId, chanId, "col fragPresetCtl", nullptr )) != kOkRC )
        goto errLabel;

      // preset select check
      if((rc = io::uiCreateCheck( app->ioH, uuId, colUuId, nullEleName, kFragPresetSelId, chanId, nullClass, presetLabel )) != kOkRC )
        goto errLabel;

      // store a connection for the select control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetSelectVarId, preset_idx );

      /*
      // order/alt row container
      if((rc = io::uiCreateDiv( app->ioH, rowUuId, colUuId, nullEleName, invalidAppId, chanId, "uiRow", nullptr )) != kOkRC )
        goto errLabel;
      
      // preset order number
      if((rc = io::uiCreateNumb( app->ioH, uuId,  rowUuId, nullEleName, kFragPresetOrderId, chanId, "uiNumber fragLittleNumb", nullptr, 0, presetN, 1, 0 )) != kOkRC )
        goto errLabel;

      // store a connection for the order control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetOrderVarId, preset_idx );
      
      // preset alt letter
      if((rc = io::uiCreateStr( app->ioH, uuId,  rowUuId, nullEleName, kFragPresetAltId, chanId, "uiString fragLittleNumb", nullptr )) != kOkRC )
        goto errLabel;

      // store a connection for the order control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetAltVarId, preset_idx );
      */

      // preset order number
      if((rc = io::uiCreateNumb( app->ioH, uuId,  colUuId, nullEleName, kFragPresetOrderId, chanId, nullClass, nullptr, 0, presetN, 1, 0 )) != kOkRC )
        goto errLabel;

      // store a connection for the order control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetOrderVarId, preset_idx );
      
      // preset alt letter
      if((rc = io::uiCreateStr( app->ioH, uuId,  colUuId, nullEleName, kFragPresetAltId, chanId, nullClass, nullptr )) != kOkRC )
        goto errLabel;

      // store a connection for the order control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetAltVarId, preset_idx );
      
      // preset sequence select check
      if((rc = io::uiCreateCheck( app->ioH, uuId, colUuId, nullEleName, kFragPresetSeqSelId, chanId, nullClass, nullptr )) != kOkRC )
        goto errLabel;

      // store a connection for the sequence select control back to the fragment record
      _frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetSeqSelectVarId, preset_idx );

    errLabel:
      if(rc != kOkRC )
        rc = cwLogError(rc,"Preset control index '%i' create failed.", preset_idx);
      return rc;
    }

    rc_t _create_frag_ui( app_t* app, unsigned endLoc, unsigned fragId )
    {
      rc_t     rc                = kOkRC;
      unsigned fragListUuId      = io::uiFindElementUuId( app->ioH, kFragListId );
      unsigned fragChanId        = fragId; //endLoc; // use the frag. endLoc as the channel id
      unsigned fragPanelUuId     = kInvalidId;
      unsigned fragPresetRowUuId = kInvalidId;
      unsigned presetN           = preset_sel::preset_count( app->psH );
      unsigned fragBegLoc  = 0;

      // create the UI object
      if((rc = io::uiCreateFromRsrc( app->ioH, "frag_panel",  fragListUuId, fragChanId )) != kOkRC )
      {
        rc = cwLogError(rc,"The fragments UI object creation failed.");
        goto errLabel;
      }
      
      // get the uuid's of the new fragment panel and the endloc number display
      fragPanelUuId     = io::uiFindElementUuId( app->ioH, fragListUuId,  kFragPanelId,     fragChanId );
      fragPresetRowUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragPresetRowId, fragChanId );

      assert( fragPanelUuId     != kInvalidId );
      assert( fragPresetRowUuId != kInvalidId );

      // Make the fragment panel clickable
      io::uiSetClickable(   app->ioH, fragPanelUuId);

      // Set the fragment panel order.
      io::uiSetOrderKey( app->ioH, fragPanelUuId, endLoc );
      
      // Set the fragment beg/end play range
      get_value( app->psH, fragId, preset_sel::kBegPlayLocVarId, kInvalidId, fragBegLoc );

      uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragBegPlayLocId, fragChanId), app->minScoreLoc, app->maxScoreLoc, 1, 0, fragBegLoc );
      uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), app->minScoreLoc, app->maxScoreLoc, 1, 0, endLoc );

      // Attach blobs to the UI to allow convenient access back to the prese_sel data record
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragInGainId,     fragChanId), fragId, preset_sel::kInGainVarId,        kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragOutGainId,    fragChanId), fragId, preset_sel::kOutGainVarId,       kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragWetDryGainId, fragChanId), fragId, preset_sel::kWetGainVarId,       kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragFadeOutMsId,  fragChanId), fragId, preset_sel::kFadeOutMsVarId,     kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragBegPlayLocId, fragChanId), fragId, preset_sel::kBegPlayLocVarId,    kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), fragId, preset_sel::kEndPlayLocVarId,    kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayBtnId,    fragChanId), fragId, preset_sel::kPlayBtnVarId,       kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlaySeqBtnId, fragChanId), fragId, preset_sel::kPlaySeqBtnVarId,    kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayAllBtnId, fragChanId), fragId, preset_sel::kPlaySeqAllBtnVarId, kInvalidId );
      _frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragNoteId,       fragChanId), fragId, preset_sel::kNoteVarId,          kInvalidId );
      
      
      // create each of the preset controls
      for(unsigned preset_idx = 0; preset_idx<presetN; ++preset_idx)
        if((rc = _create_frag_preset_ctl(app, fragId, fragPresetRowUuId, presetN, preset_idx )) != kOkRC )
          goto errLabel;

      // set the uuid associated with this fragment
      preset_sel::set_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId );

    errLabel:
      return rc;
      
    }

    rc_t _fragment_load_data( app_t* app )
    {
      rc_t                      rc = kOkRC;
      char*                     fn = nullptr;
      
      // form the output file name
      if((fn = filesys::makeFn(app->record_dir, app->record_fn, app->record_fn_ext, NULL)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The preset select filename could not formed.");
        goto errLabel;
      }

      if( !filesys::isFile(fn) )
      {
        cwLogInfo("The preset selection data file: '%s' does not exist.",cwStringNullGuard(fn));
        goto errLabel;
      }

      // read the preset data file
      if((rc = preset_sel::read( app->psH, fn)) != kOkRC )
      {
        rc = cwLogError(rc,"File read failed on preset select.");
        goto errLabel;
      }

      get_loc_range(app->psH,app->minScoreLoc,app->maxScoreLoc);

      // Settting psNextFrag to a non-null value causes the
      app->psNextFrag = preset_sel::get_fragment_base(app->psH);

      _set_status(app,"Loaded fragment file.");

      time::get(app->psLoadT0);

    errLabel:
      mem::release(fn);

      return rc;
    }

rc_t _on_perf_select(app_t* app, unsigned optionAppId );
rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc);

    
    rc_t _fragment_restore_ui( app_t* app )
    {
      rc_t rc = kOkRC;
      
      // if the fragment UI has not already been created
      if( app->psNextFrag != nullptr )
      {
        unsigned fragId = app->psNextFrag->fragId;

        // create a fragment UI
        if((rc = _create_frag_ui(app, app->psNextFrag->endLoc, fragId )) != kOkRC )
        {
          cwLogError(rc,"Frag UI create failed.");
          goto errLabel;
        }

        // update the fragment UI
        _update_frag_ui(app, fragId, false );

        _set_status(app,"Loaded fragment loc:%i.",app->psNextFrag->endLoc);

        // prepare to create the next fragment UI
        app->psNextFrag = app->psNextFrag->link;

        // if all the fragment UI's have been created
        if( app->psNextFrag == nullptr )
        {

          io::uiSendMsg( app->ioH, "{ \"op\":\"attach\" }" );
          
          // the fragments are loaded enable the 'load' and 'alt' menu
          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPerfSelId ),   true );
          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kAltSelId ),   true );

          _set_status(app,"Load complete.");
          
          cwLogInfo("Fragment restore complete: elapsed secs:%f",time::elapsedSecs(app->psLoadT0));
          io::uiRealTimeReport(app->ioH);

          /*
          if( app->dflt_perf_app_id != kInvalidId )
          {
            uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kPerfSelId), app->dflt_perf_app_id );

            _on_perf_select(app, app->dflt_perf_app_id );

            

            uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), 2538 );
            uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), 3517 );

            _on_ui_play_loc(app, kBegPlayLocNumbId, 2538);
            _on_ui_play_loc(app, kBegPlayLocNumbId, 3517);

          }
          */
        }
      }

    errLabel:
      if( rc != kOkRC )
      {
        // TODO: HANDLE FAILURE
      }
      
      return rc;
    }

    /*
    rc_t _restore_fragment_data( app_t* app )
    {
      rc_t                      rc = kOkRC;
      char*                     fn = nullptr;
      const preset_sel::frag_t* f = nullptr;
      time::spec_t t0;

      time::get(t0);
      

      // form the output file name
      if((fn = filesys::makeFn(app->record_dir, app->record_fn, app->record_fn_ext, NULL)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The preset select filename could not formed.");
        goto errLabel;
      }

      if( !filesys::isFile(fn) )
      {
        cwLogInfo("The preset selection data file: '%s' does not exist.",cwStringNullGuard(fn));
        goto errLabel;
      }

      // read the preset data file
      if((rc = preset_sel::read( app->psH, fn)) != kOkRC )
      {
        rc = cwLogError(rc,"File write failed on preset select.");
        goto errLabel;
      }

      get_loc_range(app->psH,app->minScoreLoc,app->maxScoreLoc);
  
      //preset_sel::report( app->psH );

      f = preset_sel::get_fragment_base(app->psH);
      for(int i = 0; f!=nullptr; f=f->link,++i)
      {
        unsigned fragId = f->fragId;
        
        if((rc = _create_frag_ui(app, f->endLoc, fragId )) != kOkRC )
        {
          cwLogError(rc,"Frag UI create failed.");
          goto errLabel;
        }

        _update_frag_ui(app, fragId, false );
        
      }
      
    errLabel:
      mem::release(fn);

      printf("ELAPSED SECS:%f\n",time::elapsedSecs(t0));
      
      return rc;
    }
    */
    rc_t _on_ui_save( app_t* app )
    {
      rc_t  rc  = kOkRC;
      char* fn = nullptr;
      
      // form the output file name
      if((fn = filesys::makeFn(app->record_dir, app->record_fn, app->record_fn_ext, nullptr)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The preset select filename could not formed.");
        goto errLabel;
      }

      // backup the current output file to versioned copy
      if((rc = file::backup( app->record_dir, app->record_fn, app->record_fn_ext, app->record_backup_dir )) != kOkRC )
      {
        rc = cwLogError(kOpFailRC,"The preset select backup to '%s' failed.",cwStringNullGuard(app->record_backup_dir));
        goto errLabel;
      }
  
      // write the preset data file
      if((rc = preset_sel::write( app->psH, fn)) != kOkRC )
      {
        rc = cwLogError(rc,"File write failed on preset select.");
        goto errLabel;
      }

    errLabel:
      mem::release(fn);

      if( rc == kOkRC )
        _clear_status(app);
      else
        _set_status(app,"Save failed.");

      return rc;
    }

    int _compare_loc_map( const void* m0, const void* m1 )
    { return ((const loc_map_t*)m0)->loc - ((const loc_map_t*)m1)->loc; }

    rc_t _load_midi_player( app_t* app, unsigned& midiEventCntRef )
    {
      rc_t                          rc         = kOkRC;
      const perf_score::event_t*    e          = nullptr;
      unsigned                      midiEventN = 0;      
      midi_record_play::midi_msg_t* m          = nullptr;
      midiEventCntRef = 0;
      
      // get the count of MIDI events
      if((e = perf_score::base_event( app->perfScoreH )) != nullptr )
        for(; e != nullptr; e=e->link)
          if( e->status != 0 )
            midiEventN  += 1;

      // copy the MIDI events
      if((e = perf_score::base_event( app->perfScoreH )) != nullptr )
      {
        // allocate the locMap[]
        app->locMap  = mem::resizeZ<loc_map_t>( app->locMap, midiEventN ); 
        app->locMapN = midiEventN;
        app->minPerfLoc  = score_parse::kInvalidLocId;
        app->maxPerfLoc  = score_parse::kInvalidLocId;
                
        // allocate the the player msg array
        m = mem::allocZ<midi_record_play::midi_msg_t>( midiEventN );

        // load the player msg array
        for(unsigned i = 0; e!=nullptr && i<midiEventN; e= e->link)
          if( e->status != 0 )
          {
            time::millisecondsToSpec(m[i].timestamp,  (unsigned)(e->sec*1000) );
            m[i].ch     = e->status & 0x0f;
            m[i].status = e->status & 0xf0;
            m[i].d0     = e->d0;
            m[i].d1     = e->d1;
            m[i].id     = e->uid;
            m[i].loc    = e->loc;
            m[i].arg    = e;
            
            app->locMap[i].loc = e->loc;
            app->locMap[i].timestamp = m[i].timestamp;

            if( e->loc != score_parse::kInvalidLocId )
            {
              if( app->minPerfLoc == score_parse::kInvalidLocId )
                app->minPerfLoc = e->loc;
              else            
                app->minPerfLoc = std::min(app->minPerfLoc,e->loc);
              
              if( app->maxPerfLoc == score_parse::kInvalidLocId )
                app->maxPerfLoc = e->loc;
              else            
                app->maxPerfLoc = std::max(app->maxPerfLoc,e->loc);
            }
            
            ++i;
          }

        qsort( app->locMap, app->locMapN, sizeof(loc_map_t), _compare_loc_map );
        
        // load the player with the msg list
        if((rc = midi_record_play::load( app->mrpH, m, midiEventN )) != kOkRC )
        {
          cwLogError(rc,"MIDI player load failed.");
          goto errLabel;
        }

        midiEventCntRef = midiEventN;

        cwLogInfo("%i MIDI events loaded from score. Loc Min:%i Max:%i", midiEventN , app->minPerfLoc, app->maxPerfLoc);
      }
      
    errLabel:
      
      mem::release(m);        
      return rc;  
    }

    rc_t _set_vel_table( app_t* app, const vel_tbl_t* vtA, unsigned vtN )
    {
      rc_t rc;
      const uint8_t* tableA = nullptr;
      unsigned tableN = 0;
      unsigned midiDevIdx = kInvalidIdx;
      unsigned assignN = 0;
      
      if((rc = vel_table_disable(app->mrpH)) != kOkRC )
      {
        rc = cwLogError(rc,"Velocity table disable failed.");
        goto errLabel;
      }

     
      for(unsigned i=0; i<vtN; ++i)
      {
        if((midiDevIdx = label_to_device_index( app->mrpH, vtA[i].device)) == kInvalidIdx )
        {
          rc = cwLogError(kInvalidArgRC,"The MIDI device '%s' could not be found.",cwStringNullGuard(vtA[i].device));
          goto errLabel;
        }

        if((tableA = get_vel_table( app->vtH, vtA[i].name, tableN )) == nullptr )
        {
          rc = cwLogError(kInvalidArgRC,"The MIDI velocity table '%s' could not be found.",cwStringNullGuard(vtA[i].name));
          goto errLabel;
        }
        
        if((rc = vel_table_set(app->mrpH, midiDevIdx, tableA, tableN )) != kOkRC )
        {
          rc = cwLogError(rc,"Velocity table device:%s name:%s assignment failed.",cwStringNullGuard(vtA[i].device), cwStringNullGuard(vtA[i].name));
          goto errLabel;
        }

        cwLogInfo("Applied velocity table: %s to dev: %s.", cwStringNullGuard(vtA[i].name), cwStringNullGuard(vtA[i].device) );

        assignN += 1;
      }

      if( assignN == 0 )
        cwLogWarning("All velocity tables disabled.");
      
    errLabel:

      if( rc != kOkRC )
        rc = cwLogError(rc,"Velocity table assignment failed.");
      
      return rc;
    }

    rc_t _do_load_perf_score( app_t* app, const char* perf_fn, const vel_tbl_t* vtA=nullptr, unsigned vtN=0, unsigned beg_loc=score_parse::kInvalidLocId, unsigned end_loc=score_parse::kInvalidLocId )
    {
      rc_t     rc          = kOkRC;
      unsigned midiEventN  = 0;
      
      // only lock the current beg/end location settings if a valid perf. score is already loaded
      bool     lockLoctnFl = app->perfScoreH.isValid() && app->lockLoctnFl;
      
      cwLogInfo("Loading");
      _set_status(app,"Loading...");

      // load the performance
      if((rc= perf_score::create( app->perfScoreH, perf_fn )) != kOkRC )
      {
        cwLogError(rc,"Score create failed on '%s'.",perf_fn);
        goto errLabel;          
      }
      
      // load the midi player, create locMap[], set app->min/maxLoc
      if((rc = _load_midi_player(app, midiEventN )) != kOkRC )
      {
        rc = cwLogError(rc,"MIDI player load failed.");
        goto errLabel;          
      }

      // assign the vel. table 
      if((rc = _set_vel_table(app, vtA, vtN )) != kOkRC )
      {
        rc = cwLogError(rc,"Velocity table assignment failed.");
        goto errLabel;                  
      }
      
      // A performance is loaded so enable the UI
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kGotoBtnId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLockLoctnCheckId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEnaRecordCheckId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kTrackMidiCheckId ),  true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
      
      // set the UI begin/end play to the locations of the newly loaded performance
      if( !lockLoctnFl )
      {
        app->end_play_loc = end_loc==score_parse::kInvalidLocId ? app->maxPerfLoc : end_loc;
        app->beg_play_loc = beg_loc==score_parse::kInvalidLocId ? app->minPerfLoc : beg_loc;
      }
      
      // Update the master range of the play beg/end number widgets
      io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minPerfLoc, app->maxPerfLoc, 1, 0, app->beg_play_loc );
      io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minPerfLoc, app->maxPerfLoc, 1, 0, app->end_play_loc );

      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kBegPlayLocNumbId ), true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEndPlayLocNumbId ), true );

      io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->beg_play_loc);
      io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->end_play_loc);

      // set the range of the SF reset number
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetBtnId ),   true );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ),   true );

      io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minPerfLoc, app->maxPerfLoc, 1, 0, app->beg_play_loc );
      io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minPerfLoc);
      
      cwLogInfo("'%s' loaded.",perf_fn);

    errLabel:
 
      _update_event_ui( app );

      if( rc != kOkRC )
        _set_status(app,"Load failed.");
      else
        _set_status(app,"%i MIDI events loaded.",midiEventN);

      //io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLoadBtnId ), false );

      return rc;
    }
    
    rc_t _on_perf_select(app_t* app, unsigned optionAppId )
    {
      rc_t              rc       = kOkRC;
      unsigned          perf_idx = kInvalidIdx;
      perf_recording_t* prp      = nullptr;

      // validate the selected menu id
      if( optionAppId < kPerfOptionBaseId  )
      {
        rc = cwLogError(kInvalidArgRC,"The performance request menu id is not valid.");
        goto errLabel;
      }

      perf_idx = optionAppId - kPerfOptionBaseId;

      // locate the selected performance record
      for(prp=app->perfRecordingBeg; prp!=nullptr; prp=prp->link)
        if( prp->id == perf_idx )
          break;          

      // if the selected performance record was not found
      if( prp == nullptr )
      {
        rc = cwLogError(kSyntaxErrorRC,"The performance record with id:%i was not found.",perf_idx);
        goto errLabel;
      }

      printf("Loading:%s\n",prp->fname );
      
      // load the requested performance
      if((rc = _do_load_perf_score(app,prp->fname,prp->vel_tblA, prp->vel_tblN, prp->beg_loc, prp->end_loc)) != kOkRC )
      {
        rc = cwLogError(kSyntaxErrorRC,"The performance load failed.");
        goto errLabel;
      }

    errLabel:
      return rc;
    }

    rc_t _on_alt_select(app_t* app, unsigned optionAppId )
    {
      rc_t rc = kOkRC;
      if( optionAppId == kInvalidId || optionAppId >= alt_count(app->psH))
      {
        rc = cwLogError(kInvalidArgRC,"The selected 'alt' id (%i) is invalid.",optionAppId);
        goto errLabel;
      }

      if((rc = set_alternative( app->psH, optionAppId )) != kOkRC )
      {
        rc = cwLogError(rc,"Alt selection failed.");
        goto errLabel;
      }
      
      cwLogInfo("Alt:%s selected.",alt_label(app->psH, optionAppId));

    errLabel:
      return rc;
    }

    
    rc_t _on_ui_start( app_t* app )
    {
      return _do_start(app, app->beg_play_loc, app->end_play_loc );
    }


    rc_t _set_midi_thru_state( app_t* app, bool thru_fl )
    {
      rc_t rc = kOkRC;
      
      if((rc = midi_record_play::set_thru_state(app->mrpH,thru_fl)) != kOkRC )
        rc   = cwLogError(rc,"%s MIDI thru state failed.",thru_fl ? "Enable" : "Disable" );
      
      return rc;
    }

    bool _is_valid_insert_loc( app_t* app, unsigned loc )
    {
      // the minimum possible value for the insert location is 1 because the previous end loc must be
      // less than the insert location (BUT what if we are inserting location 0???? A: loc 0 is invalid
      //  ???? FIX the loc values1 ....
      if( loc < 1 )
        return false;
      
      bool fl0 = _find_loc(app,loc) != nullptr;
      bool fl1 = preset_sel::is_fragment_end_loc( app->psH, loc-1) == false;

      return fl0 && fl1;
    }


    // Called when the global play locations change
    rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc)
    {
      rc_t rc = kOkRC;

      // verify that the location exists
      if( _find_loc(app,loc) == nullptr )
        _set_status(app,"%i is an invalid location.",loc);
      else
      {
        switch( appId )
        {
          case kBegPlayLocNumbId:
            app->beg_play_loc = loc;
            break;
          
          case kEndPlayLocNumbId:
            app->end_play_loc = loc;
            break;
        }
        
        bool enableFl = app->beg_play_loc < app->end_play_loc;
      
        _enable_global_play_btn(app, enableFl );
        
        if(enableFl)
          _clear_status(app);
        else
          _set_status(app,"Invalid play location range.");
      }
      
      return rc;
    }

    rc_t _on_ui_insert_loc( app_t* app, unsigned insertLoc )
    {
      rc_t     rc            = kOkRC;
      bool     enableFl      = _is_valid_insert_loc(app,insertLoc);
      unsigned insertBtnUuId = io::uiFindElementUuId( app->ioH, kInsertBtnId );
      
      io::uiSetEnable( app->ioH, insertBtnUuId, enableFl );
      
      if( enableFl )
      {
        app->insertLoc = insertLoc;
        _clear_status(app);
      }
      else
      {
        app->insertLoc = kInvalidId;
        _set_status(app,"Location '%i' is not valid.",insertLoc);
      }
      return rc;
    }

    rc_t _on_ui_insert_btn( app_t* app )
    {
      rc_t                      rc      = kOkRC;
      unsigned                  fragId  = kInvalidId;
      loc_map_t*                loc_ts  = nullptr;
      const preset_sel::frag_t* f       = nullptr;
      unsigned                  end_loc = app->insertLoc - 1;
      

      // verify that the insertion location is valid
      if( app->insertLoc == kInvalidId )
      {
        rc = cwLogError(kInvalidIdRC,"The new fragment's 'End Loc' is not valid.");
        goto errLabel;
      }

      // verify that the end-loc is not already in use - this shouldn't be possible because the 'insert' btn should be disabled if the 'insertLoc' is not valid
      if( preset_sel::is_fragment_end_loc( app->psH, end_loc ) )
      {
        rc = cwLogError(kInvalidIdRC,"The new fragment's 'End Loc' is already in use.");
        goto errLabel;
      }

      // get the timestamp assoc'd with the the 'end-loc'
      if((loc_ts = _find_loc( app, end_loc )) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The time stamp associated with the 'End Loc' '%i' could not be found.",app->insertLoc-1);
        goto errLabel;
      }

      // create the data record associated with the new fragment.
      if((rc = preset_sel::create_fragment( app->psH, end_loc, loc_ts->timestamp, fragId)) != kOkRC )
      {
        rc = cwLogError(rc,"Fragment data record create failed.");
        goto errLabel;
      }
      
      // create the fragment UI panel
      if((rc = _create_frag_ui( app, end_loc, fragId )) != kOkRC )
      {
        rc = cwLogError(rc,"Fragment UI panel create failed.");
        goto errLabel;
      }
      
      
      // update the fragment UI
      _update_frag_ui(app, fragId );
      
      if((f = get_fragment(app->psH,fragId)) != nullptr && f->link != nullptr )
        _update_frag_ui(app, f->link->fragId );

      
      
    errLabel:
      return rc;
          
    }

    rc_t _on_ui_delete_btn( app_t* app )
    {
      rc_t                      rc   = kOkRC;
      unsigned                  fragId = kInvalidId;
      unsigned                  uuId = kInvalidId;
      const preset_sel::frag_t* f      = nullptr;;

      // get the fragment id (uuid) of the selected (high-lighted) fragment
      if((fragId = preset_sel::ui_select_fragment_id(app->psH)) == kInvalidId )
      {
        rc = cwLogError(kInvalidStateRC,"There is no selected fragment to delete.");
        goto errLabel;
      }

      // locate the uuid assocated with the specified fragid
      if((uuId = preset_sel::frag_to_gui_id(app->psH,fragId)) == kInvalidId )
      {
        rc = cwLogError(kInvalidIdRC,"The uuId associated with the fragment id %i could not be found.",fragId);
        goto errLabel;
      }

      // get a pointer to the fragment prior to the one to be deleted
      if((f = get_fragment(app->psH,fragId)) != nullptr )
        f                                     = f->prev;

      // delete the fragment data record
      if((rc = preset_sel::delete_fragment(app->psH,fragId)) != kOkRC )
        goto errLabel;

      // delete the fragment UI element
      if((rc = io::uiDestroyElement( app->ioH, uuId )) != kOkRC )
        goto errLabel;

      // update the fragment prior to deleted fragment
      if(f != nullptr )
        _update_frag_ui(app, f->fragId );
      
    errLabel:
      
      if( rc != kOkRC )
        rc = cwLogError(rc,"Fragment delete failed.");
      else
        cwLogInfo("Fragment %i deleted.",fragId);

      return rc;
    }

    void _update_enable_midi_load_btn( app_t* app )
    {
      bool enableBtnFl = !app->enableRecordFl && filesys::isFile(app->midiLoadFname);
      
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kMidiLoadBtnId ), enableBtnFl );
      
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kMidiLoadFnameId ), !app->enableRecordFl );
      
    }

    rc_t _on_ui_record_check( app_t* app, bool enableRecordFl )
    {
      app->enableRecordFl = enableRecordFl;
      midi_record_play::set_record_state(app->mrpH,app->enableRecordFl);

      _update_enable_midi_load_btn(app);
      return kOkRC;
    }

    rc_t _save_midi_meta_data(app_t* app, const char* dir )
    {
      rc_t  rc       = kOkRC;
      char* fname    = nullptr;
      int   bufCharN = 255;
      unsigned bN    = 0;
      char     buf[ bufCharN+1 ]; 
      
      // form the filename of the file to save
      if((fname = filesys::makeFn( dir, "meta", "cfg", nullptr, nullptr )) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The MIDI save filename formation failed.");
        goto errLabel;
      }

      if((bN = snprintf(buf,bufCharN,"{ begLoc:%i }",app->sfResetLoc)) == 0)
      {
        rc = cwLogError(kOpFailRC,"The meta data buffer formation failed.");
        goto errLabel;
      }
      
      if((rc = file::fnWrite(fname,buf,bN)) != kOkRC )
        goto errLabel;
      

    errLabel:

      if( rc != kOkRC )
        rc = cwLogError(rc,"MIDI meta data file save failed on %s.",cwStringNullGuard(fname));

      mem::release(fname);
      
      return rc;
      
    }
    
    rc_t _on_midi_save_btn(app_t* app)
    {
      rc_t  rc  = kOkRC;
      char* dir = nullptr;
      char* fname = nullptr;

      // create a versioned folder in the directory specified by app->midiRecordDir
      if((dir = filesys::makeVersionedDirectory(app->midiRecordDir,app->midiRecordFolder)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The versioned directory creation failed on: '%s/%s'.",cwStringNullGuard(app->midiRecordDir),cwStringNullGuard(app->midiRecordFolder));
        goto errLabel;
      }

      // form the filename of the MIDI csv file to save
      if((fname = filesys::makeFn( dir, "midi", "csv", nullptr, nullptr )) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The MIDI save filename formation failed.");
        goto errLabel;
      }

      // save the MIDI data as a CSV
      if((rc = midi_record_play::save_csv(app->mrpH,fname)) != kOkRC )
      {
        rc = cwLogError(kOpFailRC,"The MIDI save failed on:.", cwStringNullGuard(fname));
        goto errLabel;
      }
      
      mem::release(fname);

      // write the current score reset location as the meta data
      if((rc = _save_midi_meta_data(app,dir)) != kOkRC )
        goto errLabel;
        

      // form the filename of the MIDI SVG file to save
      if((fname = filesys::makeFn( dir, "midi_svg", "html", nullptr, nullptr )) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"The MIDI SVG filename formation failed.");
        goto errLabel;
      }

      // write the MIDI data as an SVG file
      if((rc = midi_record_play::write_svg(app->mrpH,fname)) != kOkRC )
      {
        rc = cwLogError(kOpFailRC,"The MIDI save failed on:'%s'.", cwStringNullGuard(fname));
        goto errLabel;
      }

      mem::release(fname);
      
      if( has_stored_performance(app->sfH) )
      {
        
        // form the filename of the MIDI csv file to save
        if((fname = filesys::makeFn( dir, "score_follow", "html", nullptr, nullptr )) == nullptr )
        {
          rc = cwLogError(kOpFailRC,"The score follower SVG filename formation failed.");
          goto errLabel;
        }

        // write the score follower SVG file
        if((rc = write_svg_file( app->sfH, fname )) != kOkRC )
        {
          rc = cwLogError(rc,"Score follower SVG create failed on:'%s'.", cwStringNullGuard(fname));
          goto errLabel;
        }

        mem::release(fname);

        // form the filename of the sync'd MIDI csv file to save
        if((fname = filesys::makeFn( dir, "play_score", "csv", nullptr, nullptr )) == nullptr )
        {
          rc = cwLogError(kOpFailRC,"The score follower SVG filename formation failed.");
          goto errLabel;
        }

        // sync the performance records to the score follow info
        if((rc = score_follower::sync_perf_to_score( app->sfH )) != kOkRC )
        {
          rc = cwLogError(rc,"The score follower sync failed.");
          goto errLabel;
        }

        // write the sync'd CSV file
        if((rc = save_synced_csv( app->mrpH, fname, score_follower::perf_base(app->sfH), score_follower::perf_count(app->sfH))) != kOkRC )
        {
          rc = cwLogError(rc,"Sync'd performance CSV write failed on '%s'.",cwStringNullGuard(fname));
          goto errLabel;
        }

        // set the load filename to the name of 'play_score.csv' 
        io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kMidiLoadFnameId ), fname );

      }
      
      _set_status(app,"%i events saved to %s. SVG files updated.",midi_record_play::event_count(app->mrpH),cwStringNullGuard(fname));

      
      
    errLabel:
      mem::release(fname);
      mem::release(dir);
      
      
      return rc;
    }

    /*
    rc_t _on_midi_load_btn_0(app_t* app)
    {      
      rc_t                 rc    = kOkRC;
      filesys::pathPart_t* pp    = nullptr;
      char*                fname = nullptr;
      object_t*            cfg   = nullptr;
      unsigned             sfResetLoc;
      
      if((rc = perf_score::create_from_midi_csv( app->perfScoreH, app->midiLoadFname )) != kOkRC )
      {
        rc = cwLogError(rc,"Piano score performance load failed on '%s'.",cwStringNullGuard(app->midiLoadFname));
        goto errLabel;
      }

      if((rc = _do_load_perf_score(app,nullptr)) != kOkRC )
      {
        rc = cwLogError(rc,"Performance load failed on '%s'.",cwStringNullGuard(app->midiLoadFname));
        goto errLabel;
      }
      
      if((pp = filesys::pathParts( app->midiLoadFname )) == nullptr )
      {
        rc = cwLogError(rc,"Splitting the path '%s' failed.", cwStringNullGuard(app->midiLoadFname));
        goto errLabel;
      }

      if((fname = filesys::makeFn(pp->dirStr,"meta","cfg",nullptr,nullptr)) == nullptr )
      {
        rc = cwLogError(kOpFailRC,"Meta file name formation failed.");
        goto errLabel;
      }

      if((rc = objectFromFile(fname,cfg)) != kOkRC )
      {
        rc = cwLogError(rc,"Meta file read failed on '%s'.",cwStringNullGuard(fname));
        goto errLabel;
      }

      if((rc = cfg->getv("begLoc",sfResetLoc)) != kOkRC )
      {
        rc = cwLogError(rc,"Meta parse failed on '%s'.",cwStringNullGuard(fname));
        goto errLabel;
      }

      io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ), sfResetLoc );

      app->sfResetLoc = sfResetLoc;
      
    errLabel:

      mem::release(pp);
      mem::release(fname);
      
      return rc;
    }
    */

    rc_t _on_midi_load_btn(app_t* app)
    {      
      rc_t                 rc    = kOkRC;

      if((rc = _do_load_perf_score(app,app->midiLoadFname)) != kOkRC )
      {
        rc = cwLogError(rc,"Piano score performance load failed on '%s'.",cwStringNullGuard(app->midiLoadFname));
        goto errLabel;        
      }
      
      
    errLabel:      
      return rc;
    }
    
    rc_t _on_midi_load_fname(app_t* app, const char* fname)
    {
      rc_t rc = kOkRC;

      app->midiLoadFname = mem::reallocStr( app->midiLoadFname, fname );

      _update_enable_midi_load_btn(app);
      
      return rc;
    }

    void _on_echo_midi_enable( app_t* app, unsigned uuId,  unsigned mrp_dev_idx )
    {
      if( mrp_dev_idx <= midi_record_play::device_count(app->mrpH)  )
      {
        bool enableFl = midi_record_play::is_device_enabled(app->mrpH, mrp_dev_idx );

        io::uiSendValue( app->ioH, uuId, enableFl );
      }
    }

    void _on_midi_enable( app_t* app, unsigned checkAppId, unsigned mrp_dev_idx, bool enableFl )
    {
      unsigned midi_dev_n = midi_record_play::device_count(app->mrpH);
      
      if(  mrp_dev_idx < midi_dev_n  )
        midi_record_play::enable_device(app->mrpH, mrp_dev_idx, enableFl );
      else
        cwLogError(kInvalidArgRC,"%i is not a valid MIDI device index for device count:%i.",mrp_dev_idx,midi_dev_n);
    }
    
    rc_t _on_echo_master_value( app_t* app, unsigned varId, unsigned uuId )
    {
      rc_t rc= kOkRC;
      double val = 0;
      if((rc  = get_value( app->psH, kInvalidId, varId, kInvalidId, val )) != kOkRC )
        rc = cwLogError(rc,"Unable to get the master value for var id:%i.",varId);
      else
        io::uiSendValue( app->ioH, uuId, val );
                        
      return rc;
    }
    
    rc_t _on_master_value( app_t* app, const char* inst_label, const char* var_label, unsigned varId, double value )
    {
      rc_t rc = kOkRC;
      if((rc = preset_sel::set_value( app->psH, kInvalidId, varId, kInvalidId, value )) != kOkRC )
        rc = cwLogError(rc,"Master value set failed on varId:%i %s.%s.",varId,cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
      else
      {
        if( app->ioFlowH.isValid() )
          if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, inst_label,  var_label,    flow::kAnyChIdx, (dsp::coeff_t)value )) != kOkRC )
            rc = cwLogError(rc,"Master value send failed on %s.%s.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
      }
      return rc;
    }

    rc_t  _on_master_audio_meter( app_t* app, const io::msg_t& msg )
    {
      io::audio_group_dev_t* agd         = msg.u.audioGroupDev;
      unsigned               n           = std::min(agd->chCnt,2U);
      unsigned               baseUiAppId = cwIsFlag(agd->flags,io::kInFl) ? kAMtrIn0Id : kAMtrOut0Id;
      
      for(unsigned i = 0; i<n; ++i)
      {
        unsigned uuid = io::uiFindElementUuId( app->ioH, baseUiAppId+i );

        double lin_val = agd->meterA[i];
        unsigned meter_value = (unsigned)(lin_val < 1e-5 ? 0  : (100.0 + 20*log10(lin_val)));

        io::uiSendValue( app->ioH, uuid, meter_value );
      }
      
      return kOkRC;
    }

    rc_t _on_sd_control( app_t* app, const io::ui_msg_t& m )
    {
      rc_t rc               = kOkRC;
      //unsigned    uuid      = io::uiFindElementUuId( app->ioH, m.appId );
      const char* var_label = nullptr;
      //assert(uuid != kInvalidId);
      
      switch( m.appId )
      {
        case kPvWndSmpCntId:
          var_label        = "wndSmpN";
          app->pvWndSmpCnt = m.value->u.u;
          if( app->ioFlowH.isValid() )
            rc  = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "pva",  var_label,    flow::kAnyChIdx, m.value->u.u );
          break;
	  
        case kSdBypassId:
          var_label       = "bypass";
          app->sdBypassFl = m.value->u.b;
          if( app->ioFlowH.isValid() )
            rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sd",  var_label,    flow::kAnyChIdx, m.value->u.b );
          break;
	  
        case kSdInGainId:
          var_label     = "igain";
          app->sdInGain = m.value->u.d;
          break;
	  
        case kSdCeilingId:
          var_label      = "ceiling";
          app->sdCeiling = m.value->u.d;
          break;
	  
        case kSdExpoId:
          var_label   = "expo";
          app->sdExpo = m.value->u.d;
          break;
	  
        case kSdThreshId:
          var_label     = "thresh";
          app->sdThresh = m.value->u.d;
          break;
	  
        case kSdUprId:
          var_label  = "upr";
          app->sdUpr = m.value->u.d;
          break;
	  
        case kSdLwrId:
          var_label  = "lwr";
          app->sdLwr = m.value->u.d;
          break;
	  
        case kSdMixId:
          var_label  = "mix";
          app->sdMix = m.value->u.d;
          break;

        case kCmpBypassId:
          var_label = "cmp-bypass";
          app->cmpBypassFl = m.value->u.b;
          if( app->ioFlowH.isValid() )
            rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "cmp",  "bypass",    flow::kAnyChIdx, m.value->u.b );
          break;
	  
        default:
          assert(0);
      }

      if( m.value->tid == ui::kDoubleTId && app->ioFlowH.isValid() )
        rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sd",  var_label,    flow::kAnyChIdx, (dsp::coeff_t)m.value->u.d );

      if(rc != kOkRC )
        rc = cwLogError(rc,"Attempt to set a spec-dist variable '%s'",var_label );

      return rc;
    }

    rc_t _on_live_midi_checkbox( app_t* app, bool useLiveMidiFl )
    {
      rc_t rc = kOkRC;
      dsp::ftime_t value;
      
      if( useLiveMidiFl )
      {
        if((rc = get_value( app->psH, kInvalidId, preset_sel::kMasterSyncDelayMsVarId, kInvalidId, app->dfltSyncDelayMs )) != kOkRC )
        {
          rc = cwLogError(rc,"Unable to access the sync delay value.");
          goto errLabel;
        }

        value = 0;
        io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kSyncDelayMsId ), 0 );        
      }
      else
      {
        value = app->dfltSyncDelayMs;
        io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kSyncDelayMsId ), app->dfltSyncDelayMs );        
      }

      if( app->ioFlowH.isValid() )
        if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sync_delay",  "delayMs",    flow::kAnyChIdx, (dsp::ftime_t)value )) != kOkRC )
          rc = cwLogError(rc,"Error setting sync delay 'flow' value.");
      
      
      app->useLiveMidiFl = useLiveMidiFl;
      
    errLabel:
      return rc;
    }

    rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
    {
      rc_t rc = kOkRC;

      _update_event_ui(app);

      /*
      // disable start and stop buttons until a score is loaded
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ),  false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ),   false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEnaRecordCheckId ), false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kTrackMidiCheckId ), false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ),   false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetBtnId ),   false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ),   false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPerfSelId ),   false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), false );
      */

      io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kMidiLoadFnameId), app->midiRecordDir);

      // load the fragment data - and begin the fragment UI creation processes
      // (This is not the correct place to do this - it should be in main()
      //  but putting it here guarantees that the basic UI is already built
      // and thus will be available to _fragment_restore_ui() when it is called
      // from the io::exec callback.  FIX-THIS)
      if((rc = _fragment_load_data(app)) != kOkRC )
        rc = cwLogError(rc,"Preset data restore failed.");

      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetInterpCheckId ),      false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetAllowAllCheckId ),    false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetDryPriorityCheckId ), false );
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetDrySelectedCheckId ), false );

      
      _on_live_midi_checkbox(app,app->useLiveMidiFl);

      return rc;
    }

    rc_t _onUiValue(app_t* app, const io::ui_msg_t& m )
    {
      rc_t rc = kOkRC;

      switch( m.appId )
      {
        case kQuitBtnId:
          io::stop( app->ioH );
          break;

        case kIoReportBtnId:
          io::report( app->ioH );
          break;

        case kIoHwReportBtnId:
          io::hardwareReport( app->ioH );
          break;
          
        case kIoRtReportBtnId:
          io::realTimeReport( app->ioH );
          break;

        case kNetPrintBtnId:
          if( app->ioFlowH.isValid() )
            io_flow::print_network(app->ioFlowH,flow_cross::kCurDestId);
          break;
            
        case kReportBtnId:
          //preset_sel::report( app->psH );
          //io_flow::apply_preset( app->ioFlowH, 2000.0, app->tmp==0 ? "a" : "b");
          //app->tmp = !app->tmp;
          //io_flow::print(app->ioFlowH);
          //io_flow::report(app->ioFlowH);
          //midi_record_play::save_csv(app->mrpH,"/home/kevin/temp/mrp_1.csv");
          //printf("%i %i\n",app->beg_play_loc,app->end_play_loc);
          //io::realTimeReport(app->ioH);
          //score_follower::write_svg_file(app->sfH,"/home/kevin/temp/temp_sf.html");
          //score_follower::midi_state_rt_report( app->sfH, "/home/kevin/temp/temp_midi_state_rt_report.txt" );
          //score_follower::score_report(app->sfH,"/home/kevin/temp/temp_cm_score_report.txt");
          break;
          
        case kPresetReportBtnId:
          preset_sel::report_presets(app->psH);
          break;

        case kMRP_ReportBtnId:
          midi_record_play::report(app->mrpH);
          break;
          
        case kLatencyBtnId:
          latency_measure_report(app->ioH);
          latency_measure_setup(app->ioH);
          break;

        case kSaveBtnId:
          _on_ui_save(app);
          break;

        case kPerfSelId:
          _on_perf_select(app,m.value->u.u);          
          break;

        case kAltSelId:
          _on_alt_select(app,m.value->u.u);
          break;

        case kPriPresetProbCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kPriPresetProbFl,m.value->u.b);

          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetAllowAllCheckId ),    m.value->u.b );
          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetDrySelectedCheckId ), m.value->u.b );

          break;
          
        case kSecPresetProbCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kSecPresetProbFl,m.value->u.b);

          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetInterpCheckId ),  m.value->u.b );
          
          break;

        case kPresetInterpCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kInterpPresetFl,m.value->u.b);
          break;
          
        case kPresetAllowAllCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kAllowAllPresetFl,m.value->u.b);
          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPresetDryPriorityCheckId ), m.value->u.b );
          break;

        case kPresetDryPriorityCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kDryPriorityPresetFl,m.value->u.b);
          break;

        case kPresetDrySelectedCheckId:
          app->multiPresetFlags = cwEnaFlag(app->multiPresetFlags,flow::kDrySelectedPresetFl,m.value->u.b);
          break;
          
        case kMidiThruCheckId:
          cwLogInfo("MIDI thru:%i",m.value->u.b);        
          _set_midi_thru_state(app, m.value->u.b);
          break;
            
        case kStartBtnId:
          _on_ui_start(app);
          break;
            
        case kStopBtnId:
          _do_stop_play(app);
          break;

        case kGotoBtnId:
          _do_goto(app, app->beg_play_loc);
          break;
          
        case kBegPlayLocNumbId:
          _on_ui_play_loc(app, m.appId, m.value->u.i);
          break;
          
        case kEndPlayLocNumbId:
          _on_ui_play_loc(app, m.appId, m.value->u.i);
          break;

        case kLockLoctnCheckId:
          app->lockLoctnFl = m.value->u.b;
          break;

        case kLiveCheckId:
          _on_live_midi_checkbox(app, m.value->u.b );
          break;
          
        case kTrackMidiCheckId:
          app->trackMidiFl = m.value->u.b;
          break;
	  
        case kPrintMidiCheckId:
          app->printMidiFl = m.value->u.b;
          break;
          
        case kPianoMidiCheckId:
          _on_midi_enable( app, m.appId, midi_record_play::kPiano_MRP_DevIdx, m.value->u.b );
          break;

        case kSamplerMidiCheckId:
          _on_midi_enable( app, m.appId, midi_record_play::kSampler_MRP_DevIdx, m.value->u.b );
          break;

        case kWetInGainId:
          _on_master_value( app, "mstr_wet_in_gain","gain", preset_sel::kMasterWetInGainVarId, m.value->u.d );
          break;
            
        case kWetOutGainId:
          _on_master_value( app, "mstr_wet_out_gain","gain",preset_sel::kMasterWetOutGainVarId, m.value->u.d );
          break;
          
        case kDryGainId:
          _on_master_value( app, "mstr_dry_out_gain", "gain", preset_sel::kMasterDryGainVarId, m.value->u.d );
          break;

        case kSyncDelayMsId:
          _on_master_value( app, "sync_delay","delayMs",preset_sel::kMasterSyncDelayMsVarId, (double)m.value->u.i );          
          break;
          
        case kInsertLocId:
          _on_ui_insert_loc(app, m.value->u.u );
          break;
          
        case kInsertBtnId:
          _on_ui_insert_btn(app);
          break;
          
        case kDeleteBtnId:
          _on_ui_delete_btn(app);
          break;

        case kEnaRecordCheckId:
          _on_ui_record_check(app, m.value->u.b);
          break;

        case kMidiSaveBtnId:
          _on_midi_save_btn(app);
          break;
          
        case kMidiLoadBtnId:
          _on_midi_load_btn(app);
          break;
          
        case kMidiLoadFnameId:
          _on_midi_load_fname(app,m.value->u.s);
          break;

        case kSfResetBtnId:
          _do_sf_reset(app,app->sfResetLoc);
          break;

        case kSfResetLocNumbId:
          app->sfResetLoc = m.value->u.u;
          _do_sf_reset(app,app->sfResetLoc);
          break;
          
          
        case kPvWndSmpCntId:
        case kSdBypassId:
        case kSdInGainId:
        case kSdCeilingId:
        case kSdExpoId:
        case kSdThreshId:
        case kSdUprId:
        case kSdLwrId:
        case kSdMixId:
        case kCmpBypassId:
          _on_sd_control(app,m);
          break;
          
        case kFragInGainId:
          _on_ui_frag_value( app, m.uuId, m.value->u.d);          
          break;

        case kFragOutGainId:
          _on_ui_frag_value( app, m.uuId, m.value->u.d);          
          break;
          
        case kFragWetDryGainId:
          _on_ui_frag_value( app, m.uuId, m.value->u.d );
          break;
          
        case kFragFadeOutMsId:
          _on_ui_frag_value( app, m.uuId, m.value->u.u );
          break;

        case kFragBegPlayLocId:
          _on_ui_frag_value( app, m.uuId, m.value->u.u );
          break;

        case kFragEndPlayLocId:
          _on_ui_frag_value( app, m.uuId, m.value->u.u );
          break;

        case kFragPlayBtnId:
          _on_ui_frag_value( app, m.uuId, m.value->u.b );
          break;
          
        case kFragPlaySeqBtnId:
          _on_ui_frag_value( app, m.uuId, m.value->u.b );
          break;

        case kFragPlayAllBtnId:
          _on_ui_frag_value( app, m.uuId, m.value->u.b );
          break;
          
        case kFragNoteId:
          _on_ui_frag_value( app, m.uuId, m.value->u.s );
          break;
          
        case kFragPresetOrderId:
          _on_ui_frag_value( app, m.uuId, m.value->u.u );
          break;

        case kFragPresetAltId:          
          _on_ui_frag_value( app, m.uuId, m.value->u.s );
          break;
          
        case kFragPresetSelId:
          _on_ui_frag_value( app, m.uuId, m.value->u.b );
          break;

        case kFragPresetSeqSelId:
          _on_ui_frag_value( app, m.uuId, m.value->u.b );
          break;

        default:
          if( kVelTblMinId <= m.appId  && m.appId < kVelTblMaxId )
            vtbl::on_ui_value( app->vtH, m);
      }

      return rc;
    }

    rc_t _onUiCorrupt( app_t* app, const io::ui_msg_t& m )
    {
      switch( m.appId )
      {
        case kBegPlayLocNumbId:
        case kEndPlayLocNumbId:
          _enable_global_play_btn(app,false);
          break;

        case kInsertLocId:
          io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertBtnId ),  false );
          break;

        case kFragBegPlayLocId:
        case kFragEndPlayLocId:
          _disable_frag_play_btn(app, m.uuId );
          break;
        
        default:
          break;
      }

      return kOkRC;
    }


    rc_t _onUiSelect( app_t* app, const io::ui_msg_t& m )
    {
      rc_t       rc       = kOkRC;
      bool       selectFl = m.value->u.b; // True/False if the fragment is being selected/deselected
      unsigned   fragId   = kInvalidId;
      
      if((fragId = preset_sel::gui_to_frag_id(app->psH,m.uuId)) == kInvalidId )
      {
        rc = cwLogError(kInvalidIdRC,"The fragment assoicated with the UuId %i could not be found.",m.uuId);
        goto errLabel;
      }
      
      // track the currently selected fragment.
      preset_sel::ui_select_fragment( app->psH, fragId, selectFl );

      // enable/disable the delete fragment button
      io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kDeleteBtnId ), selectFl );

    errLabel:
      return rc;
    }
    
    rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
    {
      rc_t rc = kOkRC;
      switch( m.appId )
      {
        case kPrintMidiCheckId:
          break;
          
        case kPianoMidiCheckId:
          _on_echo_midi_enable( app, m.uuId, midi_record_play::kPiano_MRP_DevIdx );
          break;
          
        case kSamplerMidiCheckId:
          _on_echo_midi_enable( app, m.uuId, midi_record_play::kSampler_MRP_DevIdx );
          break;

        case kPriPresetProbCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kPriPresetProbFl );
          break;

        case kSecPresetProbCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kSecPresetProbFl );
          break;
          
        case kPresetInterpCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kInterpPresetFl );
          break;

        case kPresetAllowAllCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kAllowAllPresetFl );
          break;

        case kPresetDryPriorityCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kDryPriorityPresetFl );
          break;

        case kPresetDrySelectedCheckId:
          io::uiSendValue( app->ioH, m.uuId, preset_cfg_flags(app->ioFlowH) & flow::kDrySelectedPresetFl );
          break;
          
        case kWetInGainId:
          _on_echo_master_value( app, preset_sel::kMasterWetInGainVarId, m.uuId );
          break;
          
        case kWetOutGainId:
          _on_echo_master_value( app, preset_sel::kMasterWetOutGainVarId, m.uuId );
          break;
          
        case kDryGainId:
          _on_echo_master_value( app, preset_sel::kMasterDryGainVarId, m.uuId );
          break;
          
        case kSyncDelayMsId:
          //_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
          break;

        case kBegPlayLocNumbId:
          io::uiSendValue( app->ioH, m.uuId, app->beg_play_loc );
          break;
          
        case kEndPlayLocNumbId:
          io::uiSendValue( app->ioH, m.uuId, app->end_play_loc );
          break;

        case kLiveCheckId:
          io::uiSendValue( app->ioH, m.uuId, app->useLiveMidiFl );
          break;

        case kTrackMidiCheckId:
          io::uiSendValue( app->ioH, m.uuId, app->trackMidiFl );
          break;

        case kEnaRecordCheckId:
          io::uiSendValue( app->ioH, m.uuId, app->enableRecordFl );
          break;

        case kMidiLoadBtnId:
          _update_enable_midi_load_btn(app);
          break;

        case kSfResetLocNumbId:
          io::uiSendValue( app->ioH, m.uuId, app->sfResetLoc );
          break;
          
        case kPvWndSmpCntId:
          io::uiSendValue( app->ioH, m.uuId, app->pvWndSmpCnt );
          break;

        case kSdBypassId:
          io::uiSendValue( app->ioH, m.uuId, app->sdBypassFl );
          break;
	  
        case kSdInGainId:
          io::uiSendValue( app->ioH, m.uuId, app->sdInGain );
          break;
	  
        case kSdCeilingId:
          io::uiSendValue( app->ioH, m.uuId, app->sdCeiling );
          break;
	  
        case kSdExpoId:
          io::uiSendValue( app->ioH, m.uuId, app->sdExpo );
          break;
	  
        case kSdThreshId:
          io::uiSendValue( app->ioH, m.uuId, app->sdThresh );
          break;
	  
        case kSdUprId:
          io::uiSendValue( app->ioH, m.uuId, app->sdUpr );
          break;
	  
        case kSdLwrId:
          io::uiSendValue( app->ioH, m.uuId, app->sdLwr );
          break;
	  
        case kSdMixId:
          io::uiSendValue( app->ioH, m.uuId, app->sdMix );
          break;
	  
        case kCmpBypassId:
          io::uiSendValue( app->ioH, m.uuId, app->cmpBypassFl );
          break;

        default:
          if( kVelTblMinId <= m.appId && m.appId <= kVelTblMaxId )
            vtbl::on_ui_echo( app->vtH, m );
	  
      }
      return rc;
    }
    
    rc_t _ui_callback( app_t* app, const io::ui_msg_t& m )
    {
      rc_t rc = kOkRC;
      
      switch( m.opId )
      {
        case ui::kConnectOpId:
          cwLogInfo("UI Connected: wsSessId:%i.",m.wsSessId);
          break;
          
        case ui::kDisconnectOpId:
          cwLogInfo("UI Disconnected: wsSessId:%i.",m.wsSessId);          
          break;
          
        case ui::kInitOpId:
          _onUiInit(app,m);
          break;

        case ui::kValueOpId:
          _onUiValue( app, m );
          break;

        case ui::kCorruptOpId:
          _onUiCorrupt( app, m );
          break;

        case ui::kClickOpId:
          _do_select_frag( app, m.uuId );
          break;

        case ui::kSelectOpId:
          _onUiSelect( app, m );
          break;
        
        case ui::kEchoOpId:
          _onUiEcho( app, m );
          break;

        case ui::kIdleOpId:
          _do_seq_exec( app );
          break;
          
        case ui::kInvalidOpId:
          // fall through
        default:
          assert(0);
          break;
        
      }

      return rc;
    }

    // The main application callback
    rc_t _io_callback( void* arg, const io::msg_t* m )
    {
      rc_t rc = kOkRC;
      app_t* app = reinterpret_cast<app_t*>(arg);

      if( app->mrpH.isValid() )
      {
        midi_record_play::exec( app->mrpH, *m );
        
        if( midi_record_play::is_started(app->mrpH)  )
        {
          unsigned evt_cnt = 0;
          
          if( app->useLiveMidiFl )
          {
            evt_cnt = app->enableRecordFl ? midi_record_play::event_index(app->mrpH) : 0;
          }
          else
          {
            evt_cnt  = midi_record_play::event_loc(app->mrpH);
          }
          
          if( evt_cnt > 0 )
            io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), evt_cnt );
        }
      }

      if( app->ioFlowH.isValid() )
      {
        io_flow::exec( app->ioFlowH, *m );
      }
      
      
      switch( m->tid )
      {
        case io::kTimerTId:
          break;
            
        case io::kSerialTId:
          break;
          
        case io::kMidiTId:
          if( app->useLiveMidiFl && app->mrpH.isValid() )
            _on_live_midi_event( app, *m );
          break;
          
        case io::kAudioTId:        
          break;

        case io::kAudioMeterTId:
          _on_master_audio_meter(app, *m );
          break;
          
        case io::kSockTId:
          break;
          
        case io::kWebSockTId:
          break;
          
        case io::kUiTId:
          rc = _ui_callback(app,m->u.ui);
          break;

        case io::kExecTId:
          _fragment_restore_ui( app );
          vtbl::exec(app->vtH);
          break;

        default:
          assert(0);
        
      }

      return rc;
    }          
    
    
  }
}


cw::rc_t cw::preset_sel_app::main( const object_t* cfg, int argc, const char* argv[] )
{

  rc_t rc;
  app_t app = {};
  app.trackMidiFl      = true;
  app.pvWndSmpCnt      = 512;
  app.sdBypassFl       = false;
  app.sdInGain         = 1.0;
  app.sdCeiling        = 20.0;
  app.sdExpo           = 2.0;
  app.sdThresh         = 60.0;
  app.sdUpr            = -1.1;
  app.sdLwr            = 2.0;
  app.sdMix            = 0.0;
  app.cmpBypassFl      = false;
  app.dflt_perf_app_id = kInvalidId;
	
  
  const object_t* params_cfg = nullptr;

  unsigned              vtMapN  = vtbl::get_ui_id_map_count();
  const ui::appIdMap_t* vtMap   = vtbl::get_ui_id_map( kPanelDivId );  
  unsigned              bigMapN = mapN + vtMapN;
  ui::appIdMap_t        bigMap[ bigMapN ];
  double                sysSampleRate = 0;
  time::spec_t          start_time = time::current_time();

  for(unsigned i=0; i<mapN; ++i)
    bigMap[i] = mapA[i];
  
  for(unsigned i=0; i<vtMapN; ++i)
    bigMap[mapN+i] = vtMap[i];
  
  
  // Parse the configuration
  if((rc = _parseCfg(&app,cfg,params_cfg,argc,argv)) != kOkRC )
    goto errLabel;
        
  // create the io framework instance
  if((rc = io::create(app.ioH,cfg,_io_callback,&app,bigMap,bigMapN)) != kOkRC )
  {
    rc = cwLogError(kOpFailRC,"IO Framework create failed.");
    goto errLabel;
  }

  log::setOutputCb( log::globalHandle(),_log_output_func,&app);
  setLevel( log::globalHandle(), log::kPrint_LogLevel );
    
  //io::report(app.ioH);

  // if an input audio file is being used
  app.in_audio_dev_idx = kInvalidIdx;
  if( app.in_audio_dev_file != nullptr )
  {
    if((app.in_audio_dev_idx = audioDeviceLabelToIndex(app.ioH, app.in_audio_dev_file )) == kInvalidIdx )
    {
      rc = cwLogError(kInvalidArgRC,"The input audio device file '%s' could not be found.",cwStringNullGuard(app.in_audio_dev_file));
      goto errLabel;          
    }

    if((rc = audioDeviceEnable( app.ioH, app.in_audio_dev_idx, true, false )) != kOkRC )
    {
      rc = cwLogError(rc,"The input audio device file disable failed.");
      goto errLabel;                
    }
  }

  // get the system sample rate
  if((sysSampleRate = _get_system_sample_rate(&app, "main" )) == 0)
  {
    rc = cwLogError(rc,"The system sample rate could not be determined.");
    goto errLabel;
  }

  cwLogInfo("System sample rate:%f",sysSampleRate);

  // create the preset selection state object
  if((rc = create(app.psH, app.presets_cfg )) != kOkRC )
  {
    rc = cwLogError(kOpFailRC,"Preset state control object create failed.");
    goto errLabel;
  }

  get_value( app.psH, kInvalidId, preset_sel::kMasterSyncDelayMsVarId, kInvalidId, app.dfltSyncDelayMs );  

  // create the score follower
  if((rc = create(app.sfH, app.score_follower_cfg, sysSampleRate )) != kOkRC )
  {
    rc = cwLogError(kOpFailRC,"score follower create failed.");
    goto errLabel;
  }

  // create the MIDI record-play object
  if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg,nullptr,_midi_play_callback,&app)) != kOkRC )
  {
    rc = cwLogError(rc,"MIDI record-play object create failed.");
    goto errLabel;
  }

  // create the vel table tuner
  if((rc = create( app.vtH, app.ioH, app.mrpH, app.velTableFname, app.velTableBackupDir)) != kOkRC )
  {
    rc = cwLogError(rc,"velocity table tuner create failed.");
    goto errLabel;
  }

  // create the performance selection menu
  if((rc= _load_perf_dir_selection_menu(&app)) != kOkRC )
  {
    rc = cwLogError(rc,"The performance list UI create failed.");
    goto errLabel;
  }

  // create the alt. selection menu
  if((rc = _load_alt_menu(&app)) != kOkRC )
  {
    rc = cwLogError(rc,"The 'alt' list UI create failed.");
    goto errLabel;
  }
  
  // create the IO Flow controller
  if( !audioIsEnabled(app.ioH) )
  {
    cwLogInfo("Audio disabled.");
  }
  else
  {
    if(app.flow_cfg==nullptr || app.flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,sysSampleRate,app.crossFadeCnt,*app.flow_proc_dict,*app.flow_cfg)) != kOkRC )
    {
      rc = cwLogError(rc,"The IO Flow controller create failed.");
      goto errLabel;
    }
    app.multiPresetFlags = preset_cfg_flags(app.ioFlowH);
  }

  printf("ioFlow is %s valid.\n",app.ioFlowH.isValid() ? "" : "not");
  
  // start the IO framework instance
  if((rc = io::start(app.ioH)) != kOkRC )
  {
    rc = cwLogError(rc,"Preset-select app start failed.");
    goto errLabel;    
  }

  //io::uiReport(app.ioH);

  
  // execute the io framework
  while( !io::isShuttingDown(app.ioH))
  {
    const unsigned wsTimeOutMs = 50;
    time::spec_t t0 = time::current_time();

    unsigned timeOutMs = app.psNextFrag != nullptr ? 0 : wsTimeOutMs;

    // This call may block on the websocket handle.
    io::exec(app.ioH,timeOutMs);

    time::spec_t t1  = time::current_time();
    unsigned     dMs = time::elapsedMs(t0,t1);
    
    if( dMs < wsTimeOutMs && app.psNextFrag == nullptr )
    {      
      sleepMs( wsTimeOutMs-dMs );      
    }

    if( app.run_dur_secs != 0 && time::elapsedMs( start_time )/1000 > app.run_dur_secs )
    {
      printf("Run duration expired (%i secs).  Shutting down.\n",app.run_dur_secs);
      io::stop(app.ioH);
    }
  }

  // stop the io framework
  if((rc = io::stop(app.ioH)) != kOkRC )
  {
    rc = cwLogError(rc,"IO API stop failed.");
    goto errLabel;    
  }
  
 errLabel:

  
  _free(app);
  io::destroy(app.ioH);
  printf("Preset-select Done.\n");
  return rc;
  
}
