libcw/cwIoPresetSelApp.cpp

1169 lines
34 KiB
C++
Raw Normal View History

#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwObject.h"
#include "cwText.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 "cwIoFlow.h"
#include "cwPresetSel.h"
namespace cw
{
namespace preset_sel_app
{
// Application Id's for UI elements
enum
{
// Resource Based elements
kPanelDivId = 1000,
kQuitBtnId,
kIoReportBtnId,
kReportBtnId,
kStartBtnId,
kStopBtnId,
kMidiThruCheckId,
kCurMidiEvtCntId,
kTotalMidiEvtCntId,
kCurAudioSecsId,
kTotalAudioSecsId,
kSaveBtnId,
kLoadBtnId,
kFnStringId,
kBegPlayLocNumbId,
kEndPlayLocNumbId,
kInsertLocId,
kInsertBtnId,
kDeleteBtnId,
kFragListId,
kFragPanelId,
kFragEndLocId,
kFragPresetRowId,
kFragPresetSelId,
kFragPresetOrderId,
kFragGainId,
kFragWetDryGainId,
kFragFadeOutMsId
};
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, kReportBtnId, "reportBtnId" },
{ kPanelDivId, kStartBtnId, "startBtnId" },
{ kPanelDivId, kStopBtnId, "stopBtnId" },
{ kPanelDivId, kMidiThruCheckId, "midiThruCheckId" },
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
{ kPanelDivId, kTotalMidiEvtCntId, "totalMidiEvtCntId" },
{ kPanelDivId, kCurAudioSecsId, "curAudioSecsId" },
{ kPanelDivId, kTotalAudioSecsId, "totalAudioSecsId" },
{ kPanelDivId, kSaveBtnId, "saveBtnId" },
{ kPanelDivId, kLoadBtnId, "loadBtnId" },
{ kPanelDivId, kFnStringId, "filenameId" },
{ kPanelDivId, kBegPlayLocNumbId, "begLocNumbId" },
{ kPanelDivId, kEndPlayLocNumbId, "endLocNumbId" },
{ kPanelDivId, kInsertLocId, "insertLocId" },
{ kPanelDivId, kInsertBtnId, "insertBtnId" },
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" },
{ kPanelDivId, kFragListId, "fragListId" },
{ kFragListId, kFragPanelId, "fragPanelId"},
{ kFragPanelId, kFragEndLocId, "fragEndLocId" },
{ kFragPanelId, kFragPresetRowId, "fragPresetRowId" },
{ kFragPanelId, kFragGainId, "fragGainId" },
{ kFragPanelId, kFragWetDryGainId, "fragWetDryGainId" },
{ kFragPanelId, kFragFadeOutMsId, "fragFadeOutMsId" }
};
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_folder;
const char* record_fn_ext;
char* directory;
const char* scoreFn;
const object_t* frag_panel_cfg;
const object_t* presets_cfg;
const object_t* flow_cfg;
midi_record_play::handle_t mrpH;
score::handle_t scoreH;
loc_map_t* locMap;
unsigned locMapN;
unsigned insertLoc; // last valid insert location id received from the GUI
time::spec_t beg_play_timestamp;
time::spec_t end_play_timestamp;
preset_sel::handle_t psH;
io_flow::handle_t ioFlowH;
} app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
{
rc_t rc = kOkRC;
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_folder", app->record_folder,
"record_fn_ext", app->record_fn_ext,
"score_fn", app->scoreFn,
"frag_panel", app->frag_panel_cfg,
"presets", app->presets_cfg)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
}
// verify that the output directory exists
if((rc = filesys::isDir(app->record_dir)) != kOkRC )
if((rc = filesys::makeDir(app->record_dir)) != kOkRC )
rc = cwLogError(rc,"Unable to create the base output directory:%s.",cwStringNullGuard(app->record_dir));
app->insertLoc = kInvalidId; // initialize 'insertLoc' to be invalid
errLabel:
return rc;
}
rc_t _free( app_t& app )
{
preset_sel::destroy(app.psH);
io_flow::destroy(app.ioFlowH);
mem::release(app.locMap);
mem::release(app.directory);
return kOkRC;
}
char* _form_versioned_directory(app_t* app)
{
char* dir = nullptr;
for(unsigned version_numb=0; true; ++version_numb)
{
unsigned n = textLength(app->record_folder) + 32;
char folder[n+1];
snprintf(folder,n,"%s_%i",app->record_folder,version_numb);
if((dir = filesys::makeFn(app->record_dir,folder, NULL, NULL)) == nullptr )
{
cwLogError(kOpFailRC,"Unable to form a versioned directory from:'%s'",cwStringNullGuard(app->record_dir));
return nullptr;
}
if( !filesys::isDir(dir) )
break;
mem::release(dir);
}
return dir;
}
rc_t _apply_preset( app_t* app, const time::spec_t& ts, const preset_sel::frag_t* frag=nullptr )
{
if( frag == nullptr )
preset_sel::track_timestamp( app->psH, ts, frag);
if( frag == nullptr )
cwLogInfo("No preset fragment was found for the requested timestamp.");
else
{
unsigned preset_idx = fragment_play_preset_index(frag);
printf("Apply preset: '%s'.\n", preset_idx==kInvalidIdx ? "<invalid>" : preset_sel::preset_label(app->psH,preset_idx));
}
return kOkRC;
}
void _midi_play_callback( void* arg, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
app_t* app = (app_t*)arg;
if( id != kInvalidId )
{
const unsigned buf_byte_cnt = 256;
char buf[ buf_byte_cnt ];
event_to_string( app->scoreH, id, buf, buf_byte_cnt );
printf("%s\n",buf);
const preset_sel::frag_t* f = nullptr;
if( preset_sel::track_timestamp( app->psH, timestamp, f ) )
{
printf("NEW FRAG: id:%i loc:%i\n", f->fragId, f->endLoc );
_apply_preset( app, timestamp, f );
}
}
}
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) );
}
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:5i 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 )
{
rc_t rc = kOkRC;
// if
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;
// The uiChan is the fragment endLoc
unsigned uiChanId = fragEndLoc;
// The fragPanelUUid is the same as the fragId
unsigned fragPanelUuId = fragId;
// 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 );
}
}
return rc;
}
// Update the fragment UI withh the fragment record associated with 'fragId'
rc_t _update_frag_ui(app_t* app, unsigned fragId )
{
// Notes: fragId == fragPanelUuId
// uiChanId = endLoc 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;
unsigned uiChanId = endLoc;
unsigned fragPanelUuId = fragId;
_update_frag_ui( app, fragId, preset_sel::kEndLocVarId, kInvalidId, fragPanelUuId, kFragEndLocId, uiChanId, uValue );
_update_frag_ui( app, fragId, preset_sel::kGainVarId, kInvalidId, fragPanelUuId, kFragGainId, 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_select_flags( app, fragId, endLoc );
}
return rc;
}
template< typename T>
rc_t _on_ui_frag_value( app_t* app, unsigned uuId, const T& value )
{
rc_t rc = kOkRC;
unsigned blobByteN = 0;
ui_blob_t* blob = nullptr;
if(( blob = (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.");
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
}
//
if( blob->varId == preset_sel::kPresetSelectVarId )
_update_frag_select_flags( app, blob->fragId);
return rc;
}
rc_t _on_ui_save( app_t* app )
{
rc_t rc0 = kOkRC;
rc_t rc1 = kOkRC;
char* dir = nullptr;
char* fn = nullptr;
if((dir = _form_versioned_directory(app)) == nullptr )
return cwLogError(kOpFailRC,"Unable to form the versioned directory string.");
if( !filesys::isDir(dir) )
if((rc0 = filesys::makeDir(dir)) != kOkRC )
{
rc0 = cwLogError(rc0,"Attempt to create directory: '%s' failed.", cwStringNullGuard(dir));
goto errLabel;
}
if((fn = filesys::makeFn(dir,"midi","am",nullptr)) != nullptr )
{
if((rc0 = midi_record_play::save( app->mrpH, fn )) != kOkRC )
rc0 = cwLogError(rc0,"MIDI file '%s' save failed.",fn);
mem::release(fn);
}
if((fn = filesys::makeFn(dir,"audio","wav",nullptr)) != nullptr )
{
//if((rc1 = audio_record_play::save( app->arpH, fn )) != kOkRC )
// rc1 = cwLogError(rc1,"Audio file '%s' save failed.",fn);
mem::release(fn);
}
errLabel:
mem::release(dir);
return rcSelect(rc0,rc1);
}
rc_t _on_ui_load( app_t* app )
{
rc_t rc = kOkRC;
const score::event_t* e = nullptr;
unsigned midiEventN = 0;
midi_record_play::midi_msg_t* m = nullptr;
printf("Loading\n");
// 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;
// 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;
app->locMap[i].loc = e->loc;
app->locMap[i].timestamp = m[i].timestamp;
++i;
}
// 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;
}
mem::free(m);
}
// enable the '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 );
cwLogInfo("'%s' loaded.",app->scoreFn);
errLabel:
_update_event_ui( app );
return rc;
}
rc_t _on_ui_start( app_t* app )
{
rc_t rc=kOkRC;
bool rewindFl = true;
if( !time::isZero(app->beg_play_timestamp) )
{
// 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, app->beg_play_timestamp )) != kOkRC )
{
rc = cwLogError(rc,"Preset application failed prior to MIDI start.");
goto errLabel;
}
// start the MIDI playback
if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC )
{
rc = cwLogError(rc,"MIDI start failed.");
goto errLabel;
}
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) );
//io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kCurAudioSecsId), audio_record_play::current_loc_seconds(app->arpH) );
errLabel:
return rc;
}
rc_t _on_ui_stop( app_t* app )
{
rc_t rc = kOkRC;
if((rc = midi_record_play::stop(app->mrpH)) != kOkRC )
{
rc = cwLogError(rc,"MIDI start failed.");
goto errLabel;
}
//if((rc = audio_record_play::stop(app->arpH)) != kOkRC )
//{
// rc = cwLogError(rc,"Audio start failed.");
// goto errLabel;
//}
errLabel:
return rc;
}
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;
}
loc_map_t* _find_loc( app_t* app, unsigned loc )
{
unsigned i=0;
for(; i<app->locMapN; ++i)
if( app->locMap[i].loc == loc )
return app->locMap + i;
return nullptr;
}
bool _is_valid_insert_loc( app_t* app, unsigned loc )
{
bool fl0 = _find_loc(app,loc) != nullptr;
bool fl1 = preset_sel::is_fragment_loc( app->psH, loc)==false;
return fl0 && fl1;
}
rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc)
{
rc_t rc = kOkRC;
loc_map_t* map;
// locate the map record
if((map = _find_loc(app,loc)) == nullptr )
{
rc = cwLogError(kOpFailRC,"The location '%i' could not be found.",loc);
goto errLabel;
}
switch( appId )
{
case kBegPlayLocNumbId:
app->beg_play_timestamp = map->timestamp;
break;
case kEndPlayLocNumbId:
app->end_play_timestamp = map->timestamp;
break;
}
errLabel:
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;
// TODO: Clear GUI error indicator
}
else
{
app->insertLoc = kInvalidId;
cwLogWarning("Location '%i' is not valid.",insertLoc);
// TODO: Set GUI error indicator
}
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 select control back to the fragment record
_frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetOrderVarId, preset_idx );
errLabel:
if(rc != kOkRC )
rc = cwLogError(rc,"Preset control index '%i' create failed.");
return rc;
}
rc_t _on_ui_insert_btn( app_t* app )
{
rc_t rc = kOkRC;
unsigned fragListUuId = io::uiFindElementUuId( app->ioH, kFragListId );
unsigned fragChanId = app->insertLoc; // use the frag. endLoc as the channel id
unsigned fragPanelUuId = kInvalidId;
unsigned fragEndLocUuId = kInvalidId;
unsigned fragPresetRowUuId = kInvalidId;
unsigned presetN = preset_sel::preset_count( app->psH );
unsigned fragId = kInvalidId;
loc_map_t* loc_ts = nullptr;
// verify that frag panel resource object is initiailized
if( app->frag_panel_cfg == nullptr)
{
rc = cwLogError(kInvalidStateRC,"The fragment UI resource was not initialized.");
goto errLabel;
}
// 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_loc( app->psH, app->insertLoc ) )
{
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, app->insertLoc )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The time stamp associated with the 'End Loc' '%i' could not be found.",app->insertLoc);
goto errLabel;
}
// create the UI object
if((rc = io::uiCreateFromObject( app->ioH, app->frag_panel_cfg, 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 );
fragEndLocUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragEndLocId, fragChanId );
fragPresetRowUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragPresetRowId, fragChanId );
assert( fragPanelUuId != 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, app->insertLoc );
// The fragment id is the same as the frag panel UUId
fragId = fragPanelUuId;
// 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, kFragGainId, fragChanId), fragId, preset_sel::kGainVarId, 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 );
// 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;
// create the data record associated with the new fragment.
if((rc = preset_sel::create_fragment( app->psH, fragId, app->insertLoc, loc_ts->timestamp)) != kOkRC )
{
rc = cwLogError(rc,"Fragment data record create failed.");
goto errLabel;
}
// update the fragment UI
_update_frag_ui(app, fragPanelUuId );
errLabel:
return rc;
}
rc_t _on_ui_delete_btn( app_t* app )
{
rc_t rc = kOkRC;
unsigned fragId = kInvalidId;
// get the fragment id (uuid) of the selected fragment
if((fragId = preset_sel::ui_select_fragment_id(app->psH)) == kInvalidId )
{
rc = cwLogError(kInvalidStateRC,"There is no selected fragment to delete.");
goto errLabel;
}
// delete the fragment data record
if((rc = delete_fragment(app->psH,fragId)) != kOkRC )
goto errLabel;
// delete the fragment UI element
if((rc = io::uiDestroyElement( app->ioH, fragId )) != kOkRC )
goto errLabel;
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"Fragment delete failed.");
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 );
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 );
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 kReportBtnId:
preset_sel::report( app->psH );
break;
case kSaveBtnId:
_on_ui_save(app);
break;
case kLoadBtnId:
_on_ui_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:
_on_ui_stop(app);
break;
case kFnStringId:
mem::release(app->directory);
app->directory = mem::duplStr(m.value->u.s);
//rintf("filename:%s\n",app->directory);
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 kFragGainId:
_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.d );
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;
}
return rc;
}
rc_t _onUiClick( app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
// get the last selected fragment
unsigned uuId = preset_sel::ui_select_fragment_id(app->psH);
// is the last selected fragment the same as the clicked fragment
bool reclickFl = uuId == m.uuId;
// if a different fragment was clicked then deselect the last fragment in the UI
if( !reclickFl && uuId != kInvalidId )
uiSetSelect( app->ioH, uuId, false );
// select or deselect the clicked fragment
uiSetSelect( app->ioH, m.uuId, !reclickFl );
// Note: calls to uiSetSelect() produce callbacks to _onUiSelect().
return rc;
}
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
// track the currently selected fragment.
preset_sel::ui_select_fragment( app->psH, m.uuId, selectFl );
// enable/disable the delete fragment button
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kDeleteBtnId ), selectFl );
return rc;
}
rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
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::kClickOpId:
_onUiClick( app, m );
break;
case ui::kSelectOpId:
_onUiSelect( app, m );
break;
case ui::kEchoOpId:
_onUiEcho( app, m );
break;
case ui::kIdleOpId:
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) )
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) );
}
if( app->ioFlowH.isValid() )
{
io_flow::exec( app->ioFlowH, *m );
}
/*
if( app->arpH.isValid() )
{
audio_record_play::exec( app->arpH, *m );
if( audio_record_play::is_started(app->arpH) )
io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kCurAudioSecsId), audio_record_play::current_loc_seconds(app->arpH) );
}
*/
switch( m->tid )
{
case io::kTimerTId:
break;
case io::kSerialTId:
break;
case io::kMidiTId:
break;
case io::kAudioTId:
break;
case io::kAudioMeterTId:
break;
case io::kSockTId:
break;
case io::kWebSockTId:
break;
case io::kUiTId:
rc = _ui_callback(app,m->u.ui);
break;
default:
assert(0);
}
return rc;
}
}
}
cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
{
rc_t rc;
app_t app = { };
const object_t* params_cfg = nullptr;
// Parse the configuration
if((rc = _parseCfg(&app,cfg,params_cfg)) != kOkRC )
goto errLabel;
// create the io framework instance
if((rc = io::create(app.ioH,cfg,_io_callback,&app,mapA,mapN)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"IO Framework create failed.");
goto errLabel;
}
// 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 MIDI record-play object
if((rc = midi_record_play::create(app.mrpH,app.ioH,*params_cfg,_midi_play_callback,&app)) != kOkRC )
{
rc = cwLogError(rc,"MIDI record-play object create failed.");
goto errLabel;
}
// create the IO Flow controller
if(app.flow_cfg==nullptr || flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,*flow_proc_dict,*app.flow_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The IO Flow controller create failed.");
goto errLabel;
}
/*
// create the audio record-play object
if((rc = audio_record_play::create(app.arpH,app.ioH,*cfg)) != kOkRC )
{
rc = cwLogError(rc,"Audio record-play object 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( !isShuttingDown(app.ioH))
{
exec(app.ioH);
sleepMs(50);
}
errLabel:
_free(app);
io::destroy(app.ioH);
printf("Preset-select Done.\n");
return rc;
}