libcw/cwIoPresetSelApp.cpp

2572 lines
82 KiB
C++

#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.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 "cwUiDecls.h"
#include "cwIo.h"
#include "cwIoMidiRecordPlay.h"
#include "cwIoPresetSelApp.h"
#include "cwPianoScore.h"
#include "cwVectOps.h"
#include "cwMath.h"
#include "cwDspTypes.h"
#include "cwMtx.h"
#include "cwFlow.h"
#include "cwFlowTypes.h"
#include "cwFlowCross.h"
#include "cwIoFlow.h"
#include "cwPresetSel.h"
#include "cwVelTableTuner.h"
#include "cwCmInterface.h"
#include "cwScoreFollower.h"
#define INVALID_LOC (0)
namespace cw
{
namespace preset_sel_app
{
// Application Id's for UI elements
enum
{
// Resource Based elements
kPanelDivId = 1000,
kQuitBtnId,
kIoReportBtnId,
kNetPrintBtnId,
kReportBtnId,
kStartBtnId,
kStopBtnId,
kLiveCheckId,
kTrackMidiCheckId,
kPrintMidiCheckId,
kPianoMidiCheckId,
kSamplerMidiCheckId,
kSyncDelayMsId,
kWetInGainId,
kWetOutGainId,
kDryGainId,
kMidiThruCheckId,
kCurMidiEvtCntId,
kTotalMidiEvtCntId,
kCurAudioSecsId,
kTotalAudioSecsId,
kSaveBtnId,
kLoadBtnId,
kBegPlayLocNumbId,
kEndPlayLocNumbId,
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,
kFragInGainId,
kFragOutGainId,
kFragWetDryGainId,
kFragFadeOutMsId,
kFragBegPlayLocId,
kFragEndPlayLocId,
kFragPlayBtnId,
kFragPlaySeqBtnId,
kFragPlayAllBtnId,
kFragNoteId,
kVelTblMinId = vtbl::kVtMinId,
kVelTblMaxId = vtbl::kVtMaxId
};
enum
{
kAmMidiTimerId
};
// Application Id's for the resource based UI elements.
ui::appIdMap_t mapA[] =
{
{ ui::kRootAppId, kPanelDivId, "panelDivId" },
{ kPanelDivId, kQuitBtnId, "quitBtnId" },
{ kPanelDivId, kIoReportBtnId, "ioReportBtnId" },
{ kPanelDivId, kNetPrintBtnId, "netPrintBtnId" },
{ kPanelDivId, kReportBtnId, "reportBtnId" },
{ kPanelDivId, kStartBtnId, "startBtnId" },
{ kPanelDivId, kStopBtnId, "stopBtnId" },
{ 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, kBegPlayLocNumbId, "begLocNumbId" },
{ kPanelDivId, kEndPlayLocNumbId, "endLocNumbId" },
{ 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 app_str
{
io::handle_t ioH;
const char* record_dir;
const char* record_fn;
const char* record_fn_ext;
const char* record_backup_dir;
const char* scoreFn;
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;
cm::handle_t cmCtxH;
score_follower::handle_t sfH;
midi_record_play::handle_t mrpH;
unsigned scoreFollowMaxLocId;
score::handle_t scoreH;
loc_map_t* locMap;
unsigned locMapN;
unsigned insertLoc; // last valid insert location id received from the GUI
unsigned minLoc;
unsigned maxLoc;
time::spec_t beg_play_timestamp;
time::spec_t end_play_timestamp;
unsigned beg_play_loc;
unsigned end_play_loc;
preset_sel::handle_t psH;
vtbl::handle_t vtH;
io_flow::handle_t ioFlowH;
double crossFadeSrate;
unsigned crossFadeCnt;
bool printMidiFl;
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 supressPresetLogFl;
unsigned pvWndSmpCnt;
bool sdBypassFl;
double sdInGain;
double sdCeiling;
double sdExpo;
double sdThresh;
double sdUpr;
double sdLwr;
double sdMix;
bool cmpBypassFl;
} 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;
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,
"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,
"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->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 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);
}
rc_t _free( app_t& app )
{
if( app.flow_proc_dict != nullptr )
app.flow_proc_dict->free();
mem::release((char*&)app.record_backup_dir);
mem::release((char*&)app.record_dir);
mem::release((char*&)app.scoreFn);
vtbl::destroy(app.vtH);
preset_sel::destroy(app.psH);
io_flow::destroy(app.ioFlowH);
midi_record_play::destroy(app.mrpH);
score::destroy( app.scoreH );
mem::release(app.locMap);
return kOkRC;
}
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 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;
// 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;
// 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
{
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx);
// don't display preset updates unless the score is actually loaded
printf("Apply preset: '%s' : loc:%i\n", preset_idx==kInvalidIdx ? "<invalid>" : preset_label, loc );
_set_status(app,"Apply preset: '%s'.", preset_idx == kInvalidIdx ? "<invalid>" : preset_label);
if( preset_label != nullptr )
{
io_flow::apply_preset( app->ioFlowH, flow_cross::kNextDestId, preset_label );
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::real_t)frag->igain );
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::real_t)frag->ogain );
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal", "in", flow::kAnyChIdx, (dsp::real_t)frag->wetDryGain );
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;
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;
}
errLabel:
return rc;
}
unsigned _get_loc_from_score_follower( app_t* app, double secs, uint8_t status, uint8_t d0, uint8_t d1 )
{
unsigned loc = INVALID_LOC;
// if this is a MIDI note-on event - then udpate the score follower
if( midi::isNoteOn(status,d1) )
{
unsigned smpIdx = 0; // not functional - used to associate input with score follower output
unsigned muid = 0; //
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_match_id_array( app->sfH, matchLocN );
unsigned maxLocId = 0;
if( true )
{
printf("SF: ");
for(unsigned i=0; i<matchLocN; ++i)
{
if( matchLocA[i] > maxLocId )
maxLocId = matchLocA[i];
printf("%i ",matchLocA[i]);
}
printf("\n");
}
// notice if the end of the score was reached
if( maxLocId > app->scoreFollowMaxLocId )
{
loc = maxLocId;
app->scoreFollowMaxLocId = maxLocId;
}
clear_match_id_array(app->sfH);
}
}
}
return loc;
}
void _midi_play_callback( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, 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
score::event_to_string( app->scoreH, 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);
loc = _get_loc_from_score_follower( app, sec, status, d0, d1 );
// TODO: ZERO SHOULD BE A VALID LOC VALUE - MAKE -1 THE INVALID LOC VALUE
if( loc != INVALID_LOC && app->trackMidiFl )
{
if( preset_sel::track_loc( app->psH, loc, f ) )
{
_apply_preset( app, loc, f );
if( f != nullptr )
_do_select_frag( app, f->guiUuId );
}
}
}
break;
}
}
}
rc_t _on_live_midi( 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
{
midi::msg_t* mm = pkt->msgArray + j;
time::spec_t timestamp;
unsigned id = 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->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 ) == kOkRC )
_midi_play_callback( app, midi_record_play::kMidiEventActionId, id, timestamp, loc, mm->status & 0x0f, mm->status & 0xf0, 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 score::event_t* e0 = nullptr;
const score::event_t* e1 = nullptr;
double srate = 0;
double secs = 0;
frameIdxRef = kInvalidIdx;
if( app->in_audio_begin_loc != INVALID_LOC )
{
if((e0 = loc_to_event(app->scoreH,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->scoreH,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;
}
rc_t _do_play( 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 cur_loc = 0;
// 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( 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((begMap = _find_loc(app,begLoc)) == nullptr )
{
rc = cwLogError(kInvalidArgRC,"The begin play location is not valid.");
goto errLabel;
}
if((endMap = _find_loc(app,endLoc)) == nullptr )
{
rc = cwLogError(kInvalidArgRC,"The end play location is not valid.");
goto errLabel;
}
if((rc = score_follower::reset(app->sfH,begLoc)) != kOkRC )
{
rc = cwLogError(rc,"Score follower reset failed.");
goto errLabel;
}
app->beg_play_timestamp = begMap->timestamp;
app->end_play_timestamp = endMap->timestamp;
if( !time::isZero(app->beg_play_timestamp) && !app->useLiveMidiFl )
{
// seek the player to the requested loc
if((rc = midi_record_play::seek( app->mrpH, app->beg_play_timestamp )) != kOkRC )
{
rc = cwLogError(rc,"MIDI seek failed.");
goto errLabel;
}
rewindFl = false;
}
// apply the preset which is active at the start time
if((rc = _apply_preset( app, begMap->loc )) != kOkRC )
{
rc = cwLogError(rc,"Preset application failed prior to MIDI start.");
goto errLabel;
}
// start the MIDI playback
if( !app->useLiveMidiFl )
{
if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC )
{
rc = cwLogError(rc,"MIDI start failed.");
goto errLabel;
}
if((cur_loc = midi_record_play::event_loc(app->mrpH)) > 0 )
{
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
}
}
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, 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_play(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->maxLoc );
}
// 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;
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::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
_set_status(app,"Invalid fragment play range.");
}
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::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 );
// 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 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.");
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 fragBegLocUuId = kInvalidId;
unsigned fragEndLocUuId = 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 );
fragBegLocUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragBegLocId, fragChanId );
fragEndLocUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragEndLocId, fragChanId );
fragPresetRowUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragPresetRowId, fragChanId );
assert( fragPanelUuId != kInvalidId );
assert( fragBegLocUuId != kInvalidId );
assert( fragEndLocUuId != 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->minLoc, app->maxLoc, 1, 0, fragBegLoc );
uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), app->minLoc, app->maxLoc, 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 _restore_fragment_data( app_t* app )
{
rc_t rc = kOkRC;
char* fn = nullptr;
const preset_sel::frag_t* f = 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 write failed on preset select.");
goto errLabel;
}
//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);
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_piano_score( app_t* app, unsigned& midiEventCntRef )
{
rc_t rc = kOkRC;
const score::event_t* e = nullptr;
unsigned midiEventN = 0;
midi_record_play::midi_msg_t* m = nullptr;
midiEventCntRef = 0;
// create the score
if((rc= score::create( app->scoreH, app->scoreFn )) != kOkRC )
{
cwLogError(rc,"Score create failed on '%s'.",app->scoreFn);
goto errLabel;
}
// get the count of MIDI events
if((e = score::base_event( app->scoreH )) != nullptr )
for(; e != nullptr; e=e->link)
if( e->status != 0 )
midiEventN += 1;
// copy the MIDI events
if((e = score::base_event( app->scoreH )) != nullptr )
{
// allocate the locMap[]
mem::free(app->locMap);
app->locMap = mem::allocZ<loc_map_t>( midiEventN );
app->locMapN = midiEventN;
app->minLoc = midiEventN;
app->maxLoc = 0;
// 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 )
{
//if( (e->status & 0x0f) != 0 )
// printf("%i %i \n", i, e->status & 0x0f);
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;
app->locMap[i].loc = e->loc;
app->locMap[i].timestamp = m[i].timestamp;
app->minLoc = std::min(app->minLoc,e->loc);
app->maxLoc = std::max(app->maxLoc,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;
}
cwLogInfo("%i MIDI events loaded from score.", midiEventN );
mem::free(m);
}
errLabel:
midiEventCntRef = midiEventN;
return rc;
}
rc_t _do_load( app_t* app )
{
rc_t rc = kOkRC;
unsigned midiEventN = 0;
bool firstLoadFl = !app->scoreH.isValid();
cwLogInfo("Loading");
_set_status(app,"Loading...");
// Load the piano score (this set's app->min/maxLoc)
if((rc = _load_piano_score(app,midiEventN)) != kOkRC )
goto errLabel;
// reset the timestamp tracker
track_loc_reset( app->psH );
// set the range of the global play location controls
if( firstLoadFl )
{
unsigned begPlayLocUuId = io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId);
unsigned endPlayLocUuId = io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId);
if( app->end_play_loc == 0 )
app->end_play_loc = app->maxLoc;
if( !(app->minLoc <= app->beg_play_loc && app->beg_play_loc <= app->maxLoc) )
app->beg_play_loc = app->minLoc;
io::uiSetNumbRange( app->ioH, begPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->beg_play_loc );
io::uiSetNumbRange( app->ioH, endPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->end_play_loc );
io::uiSendValue( app->ioH, begPlayLocUuId, app->beg_play_loc);
io::uiSendValue( app->ioH, endPlayLocUuId, app->end_play_loc);
// enable the insert 'End Loc' number box since the score is loaded
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
}
// update the current event and event count
_update_event_ui(app);
// enable the start/stop buttons
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, kLiveCheckId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kTrackMidiCheckId ), true );
// restore the fragment records
if( firstLoadFl )
if((rc = _restore_fragment_data( app )) != kOkRC )
{
rc = cwLogError(rc,"Restore failed.");
goto errLabel;
}
//io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLoadBtnId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true );
cwLogInfo("'%s' loaded.",app->scoreFn);
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_ui_start( app_t* app )
{
return _do_play(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 _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((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, inst_label, var_label, flow::kAnyChIdx, (dsp::real_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;
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;
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;
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 )
rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sd", var_label, flow::kAnyChIdx, (dsp::real_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 _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, kTrackMidiCheckId ), false );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), false );
const preset_sel::frag_t* f = preset_sel::get_fragment_base( app->psH );
for(; f!=nullptr; f=f->link)
_update_frag_ui( app, f->fragId );
//_do_load(app);
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 kNetPrintBtnId:
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);
//midi_record_play::report(app->mrpH);
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 kSaveBtnId:
_on_ui_save(app);
break;
case kLoadBtnId:
_do_load(app);
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 kLiveCheckId:
app->useLiveMidiFl = 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 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 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 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 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 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 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() && !app->useLiveMidiFl )
{
midi_record_play::exec( app->mrpH, *m );
if( midi_record_play::is_started(app->mrpH) )
{
unsigned cur_loc = midi_record_play::event_loc(app->mrpH);
if( cur_loc > 0 )
{
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
}
}
}
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( 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:
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 = { .trackMidiFl = true,
.pvWndSmpCnt = 512,
.sdBypassFl = false,
.sdInGain = 1.0,
.sdCeiling = 20.0,
.sdExpo = 2.0,
.sdThresh = 60.0,
.sdUpr = -1.1,
.sdLwr = 2.0,
.sdMix = 0.0,
.cmpBypassFl = false
};
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;
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);
//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;
}
// create the 'cm' context object - which is needed by the score follower
if((rc = create(app.cmCtxH)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"cm context create failed.");
goto errLabel;
}
// create the score follower
if((rc = create(app.sfH, app.score_follower_cfg, app.cmCtxH, 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,app.velTableFname,_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 IO Flow controller
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;
}
// start the IO framework instance
if((rc = io::start(app.ioH)) != kOkRC )
{
rc = cwLogError(rc,"Preset-select app start failed.");
goto errLabel;
}
// execute the io framework
while( !io::isShuttingDown(app.ioH))
{
io::exec(app.ioH);
sleepMs(50);
}
// 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;
}