diff --git a/Makefile.am b/Makefile.am index 8cffeff..1943093 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,6 +62,9 @@ libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoSocketChat libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp +libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h +libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp + endif diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp new file mode 100644 index 0000000..8abbe32 --- /dev/null +++ b/cwIoPresetSelApp.cpp @@ -0,0 +1,602 @@ +#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" + +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, + + kLocNumbId + + }; + + 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, kLocNumbId, "locNumbId" } + + }; + + unsigned mapN = sizeof(mapA)/sizeof(mapA[0]); + + typedef struct loc_map_str + { + unsigned loc; + time::spec_t timestamp; + } loc_map_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; + + midi_record_play::handle_t mrpH; + //audio_record_play::handle_t arpH; + + score::handle_t scoreH; + loc_map_t* locMap; + unsigned locMapN; + + } app_t; + + rc_t _parseCfg(app_t* app, const object_t* cfg ) + { + rc_t rc = kOkRC; + + const object_t* params = nullptr; + + if((rc = cfg->getv( "params", params)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Preset Select App 'params' cfg record not found."); + goto errLabel; + } + + if((rc = params->getv( "record_dir", app->record_dir, + "record_folder", app->record_folder, + "record_fn_ext", app->record_fn_ext, + "score_fn", app->scoreFn )) != 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)); + + + errLabel: + + return rc; + } + + rc_t _free( app_t& app ) + { + 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; + } + + 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; + const unsigned buf_byte_cnt = 256; + char buf[ buf_byte_cnt ]; + if( id != kInvalidId ) + { + event_to_string( app->scoreH, id, buf, buf_byte_cnt ); + printf("%s\n",buf); + } + } + + 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( midiEventN ); + app->locMapN = midiEventN; + + // allocate the the player msg array + m = mem::allocZ( midiEventN ); + + // load the player msg array + for(unsigned i=0; e!=nullptr && ilink) + 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); + + } + + cwLogInfo("'%s' loaded.",app->scoreFn); + + errLabel: + + io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) ); + io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), midi_record_play::event_count(app->mrpH) ); + + return rc; + } + + rc_t _on_ui_start( app_t* app ) + { + rc_t rc=kOkRC; + + if((rc = midi_record_play::start(app->mrpH)) != kOkRC ) + { + rc = cwLogError(rc,"MIDI start failed."); + goto errLabel; + } + + //if((rc = audio_record_play::start(app->arpH)) != kOkRC ) + //{ + // rc = cwLogError(rc,"Audio start failed."); + // goto errLabel; + //} + + io::uiSendValue( app->ioH, kInvalidId, 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; + } + + + rc_t _on_ui_loc(app_t* app, unsigned loc) + { + rc_t rc = kOkRC; + unsigned i=0; + for(; ilocMapN; ++i) + { + if( app->locMap[i].loc == loc ) + { + // + if((rc = midi_record_play::seek( app->mrpH, app->locMap[i].timestamp )) != kOkRC ) + { + rc = cwLogError(rc,"MIDI seek failed."); + } + else + { + start( app->mrpH, false ); + io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) ); + } + break; + + } + } + + if( i == app->locMapN ) + rc = cwLogError(kOpFailRC,"The location '%i' could not be found.",loc); + + return rc; + } + + + rc_t _onUiInit(app_t* app, const io::ui_msg_t& m ) + { + rc_t rc = kOkRC; + + 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: + 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); + //printf("filename:%s\n",app->directory); + break; + + case kLocNumbId: + _on_ui_loc(app, m.value->u.i); + break; + } + + 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::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(arg); + + if( app->mrpH.isValid() ) + { + midi_record_play::exec( app->mrpH, *m ); + if( midi_record_play::is_started(app->mrpH) ) + io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) ); + } + + /* + 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 ) +{ + rc_t rc; + app_t app = {}; + + // Parse the configuration + if((rc = _parseCfg(&app,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 MIDI record-play object + if((rc = midi_record_play::create(app.mrpH,app.ioH,*cfg,_midi_play_callback,&app)) != kOkRC ) + { + rc = cwLogError(rc,"MIDI record-play object 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; + +} diff --git a/cwIoPresetSelApp.h b/cwIoPresetSelApp.h new file mode 100644 index 0000000..c0a9f6d --- /dev/null +++ b/cwIoPresetSelApp.h @@ -0,0 +1,13 @@ +#ifndef cwIoPresetSelApp_h +#define cwIoPresetSelApp_h + +namespace cw +{ + namespace preset_sel_app + { + rc_t main( const object_t* cfg ); + } +} + + +#endif diff --git a/cwPianoScore.cpp b/cwPianoScore.cpp new file mode 100644 index 0000000..a058f60 --- /dev/null +++ b/cwPianoScore.cpp @@ -0,0 +1,234 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwObject.h" +#include "cwPianoScore.h" +#include "cwMidi.h" + +namespace cw +{ + namespace score + { + typedef struct score_str + { + event_t* base; + event_t* end; + } score_t; + + score_t* _handleToPtr(handle_t h) + { + return handleToPtr(h); + } + + rc_t _destroy( score_t* p ) + { + rc_t rc = kOkRC; + event_t* e = p->base; + while( e != nullptr ) + { + event_t* e0 = e->link; + + mem::free(e); + + e = e0; + } + return rc; + } + + rc_t _parse_event_list( score_t* p, const object_t* cfg ) + { + rc_t rc; + const object_t* eventL; + + if((rc = cfg->getv( "evtL", eventL )) != kOkRC || eventL->is_list()==false ) + rc = cwLogError( rc, "Unable to locate the 'evtL' configuration tag."); + else + { + unsigned eventN = eventL->child_count(); + + for(unsigned i=0; ichild_ele(i); + + event_t* e = mem::allocZ(); + + if((rc = evt_cfg->getv( "meas", e->meas, + "voice", e->voice, + "loc", e->loc, + "tick", e->tick, + "sec", e->sec )) != kOkRC ) + { + rc = cwLogError(rc,"Score parse failed on required event fields at event index:%i.",i); + goto errLabel; + } + + if((rc = evt_cfg->getv_opt( "rval", e->rval, + "sci_pitch", sci_pitch, + "dmark", dmark, + "dlevel", e->dlevel, + "status", e->status, + "d0", e->d0, + "d1", e->d1, + "grace", grace_mark, + "section", e->section, + "bpm", e->bpm, + "bar", e->bar)) != kOkRC ) + { + rc = cwLogError(rc,"Score parse failed on optional event fields at event index:%i.",i); + goto errLabel; + } + + + if( sci_pitch != nullptr ) + memcpy(e->sci_pitch,sci_pitch,sizeof(e->sci_pitch)); + + if( dmark != nullptr ) + memcpy(e->dmark,dmark,sizeof(e->dmark)); + + if( grace_mark != nullptr ) + memcpy(e->grace_mark,grace_mark,sizeof(e->grace_mark)); + + // assign the UID + e->uid = i; + + // link the event into the event list + if( p->end != nullptr ) + p->end->link = e; + else + p->base = e; + + p->end = e; + + } + + } + errLabel: + return rc; + } + + const event_t* _uid_to_event( score_t* p, unsigned uid ) + { + const event_t* e = p->base; + for(; e!=nullptr; e=e->link) + if( e->uid == uid ) + return e; + + return nullptr; + } + + } +} + + +cw::rc_t cw::score::create( handle_t& hRef, const char* fn ) +{ + rc_t rc; + object_t* cfg = nullptr; + + if((rc = destroy(hRef)) != kOkRC ) + return rc; + + // parse the cfg file + if((rc = objectFromFile( fn, cfg )) != kOkRC ) + { + rc = cwLogError(rc,"Score parse failed on file: '%s'.", fn); + goto errLabel; + } + + rc = create(hRef,cfg); + + errLabel: + return rc; +} + +cw::rc_t cw::score::create( handle_t& hRef, const object_t* cfg ) +{ + rc_t rc; + if((rc = destroy(hRef)) != kOkRC ) + return rc; + + score_t* p = mem::allocZ< score_t >(); + + // parse the event list + if((rc = _parse_event_list(p, cfg)) != kOkRC ) + { + rc = cwLogError(rc,"Score event list parse failed."); + goto errLabel; + } + + hRef.set(p); + + errLabel: + if( rc != kOkRC ) + destroy(hRef); + + return rc; +} + +cw::rc_t cw::score::destroy( handle_t& hRef ) +{ + rc_t rc = kOkRC; + + if( !hRef.isValid() ) + return rc; + + score_t* p = _handleToPtr(hRef); + + if((rc = _destroy(p)) != kOkRC ) + return rc; + + mem::release(p); + hRef.clear(); + + return rc; +} + + +unsigned cw::score::event_count( handle_t h ) +{ + score_t* p = _handleToPtr(h); + unsigned n = 0; + for(event_t* e=p->base; e!=nullptr; e=e->link) + ++n; + + return n; + +} + +const cw::score::event_t* cw::score::base_event( handle_t h ) +{ + score_t* p = _handleToPtr(h); + return p->base; +} + + +cw::rc_t cw::score::event_to_string( handle_t h, unsigned uid, char* buf, unsigned buf_byte_cnt ) +{ + score_t* p = _handleToPtr(h); + const event_t* e = nullptr; + rc_t rc = kOkRC; + + if((e = _uid_to_event( p, uid )) == nullptr ) + rc = cwLogError(kInvalidIdRC,"A score event with uid=%i does not exist.",uid); + else + { + const char* sci_pitch = strlen(e->sci_pitch) ? e->sci_pitch : ""; + const char* dyn_mark = strlen(e->dmark) ? e->dmark : ""; + const char* grace_mark= strlen(e->grace_mark) ? e->grace_mark : ""; + + if( midi::isSustainPedal( e->status, e->d0 ) ) + sci_pitch = midi::isPedalDown( e->status, e->d0, e->d1 ) ? "Dv" : "D^"; + else + if( midi::isSostenutoPedal( e->status, e->d0 ) ) + sci_pitch = midi::isPedalDown( e->status, e->d0, e->d1 ) ? "Sv" : "S^"; + + snprintf(buf,buf_byte_cnt,"uid:%5i meas:%4i loc:%4i tick:%8i sec:%8.3f %4s %5s %3s (st:0x%02x d1:0x%02x d0:0x%02x)", e->uid, e->meas, e->loc, e->tick, e->sec, sci_pitch, dyn_mark, grace_mark, e->status, e->d0, e->d1 ); + } + + return rc; +} diff --git a/cwPianoScore.h b/cwPianoScore.h new file mode 100644 index 0000000..cc9d80e --- /dev/null +++ b/cwPianoScore.h @@ -0,0 +1,47 @@ +#ifndef cwPianoScore_h +#define cwPianoScore_h + +namespace cw +{ + namespace score + { + typedef handle handle_t; + + typedef struct event_str + { + unsigned uid; // unique id for this event + unsigned meas; // measure number + unsigned voice; // score number + unsigned loc; // score location + unsigned tick; // event tick location + double sec; // event absolute time in seconds + double rval; // event rythmic value 2=1/2 1/4 .5=2 or 0 + char sci_pitch[4]; // scientific pitch + char dmark[6]; // dynamic mark (e.g. "pp","mf","fff") + unsigned dlevel; // dynamic level as an integer associated with dyn. mark + unsigned status; // MIDI status < type | channel > or 0 + unsigned d0; // MIDI d0 or 0 + unsigned d1; // MIDI d1 or 0 + unsigned bpm; // tempo BPM or 0 + char grace_mark[4]; // grace mark or 0 + unsigned bar; // bar number or 0 + unsigned section; // section number or 0 + struct event_str* link; // list link + } event_t; + + rc_t create( handle_t& hRef, const char* fn ); + rc_t create( handle_t& hRef, const object_t* cfg ); + rc_t destroy( handle_t& hRef ); + + unsigned event_count( handle_t h ); + const event_t* base_event( handle_t h ); + + rc_t event_to_string( handle_t h, unsigned uid, char* buf, unsigned buf_byte_cnt ); + + + + } +} + + +#endif diff --git a/html/preset_sel/css/ui.css b/html/preset_sel/css/ui.css new file mode 100644 index 0000000..96b18c7 --- /dev/null +++ b/html/preset_sel/css/ui.css @@ -0,0 +1,117 @@ + +body { + background-color: LightCyan; +} + + +.title_disconnected { + color: red; +} + +.title_connected { + color: green; +} + +input, label, button, select { + font-family: sans-serif; + font-size: 10px; + height: 20px; +} + +button { + margin-right: 4px; + margin-left: 4px; +} + +div { + /*border: 1px solid black; */ + background-color: LightSteelBlue; +} + +div p { + font-family: sans-serif; + font-size: 12px; + margin-top: 2px; + margin-bottom: 2px; + background-color: LightSteelBlue; +} + +label { + /*border: 1px solid red;*/ + width: 50px; + padding-left: 10px; + margin-top: 3px; +} + + + +.uiCtlDiv { + display: flex; + flex-direction: row; + align-items: center; + background-color: LightBlue; +} + +.uiCtlDiv input { + background-color: PowderBlue; +} + +.uiNumberDiv input { + width: 50px; +} + +.uiPanel { + background-color: LightBlue; +} + +.uiRow { + display: flex; + flex-direction: row; + align-items: center; + background-color: LightBlue; + margin-bottom: 4px; + margin-top: 4px; + +} + +.uiCol { + display: flex; + flex-direction: column; + align-items: center; + background-color: LightBlue; +} + + +/* outer log div - contains the log label and the log scroller */ +.uiLogDiv { + display: flex; + flex-direction: column; + + align-items: flex-start; /* left justify */ + align-content: flex-stretch; /* fill horizontal space */ + +} + +.uiLogDiv label { + width: 100%; +} + + +/* log scroller */ +.uiLog { + display:flex; + flex-direction: column; + + height: 150px; + overflow-x: hidden; /* 'hidden' to remove scroll bar */ + overflow-y: auto; + width: 100%; + + background-color: PowderBlue; + +} + +/* The log text */ +.uiLog pre { + +} diff --git a/html/preset_sel/index.html b/html/preset_sel/index.html new file mode 100644 index 0000000..b97f180 --- /dev/null +++ b/html/preset_sel/index.html @@ -0,0 +1,25 @@ + + + + + Preset Selection App + + + + + + + + +
+

Preset Selection:

+

Disconnected

+
+ +
+
+ + + diff --git a/html/preset_sel/js/ui.js b/html/preset_sel/js/ui.js new file mode 100644 index 0000000..2043de7 --- /dev/null +++ b/html/preset_sel/js/ui.js @@ -0,0 +1,1046 @@ +var _ws = null; +var _rootId = "0"; +var _nextEleId = 0; +var _focusId = null; +var _focusVal = null; + +function set_app_title( suffix, className ) +{ + var ele = document.getElementById('connectTitleId'); + ele.innerHTML = suffix + ele.className = className +} + + +function uiOnError( msg, r) +{ + console.log("Error:" + msg); +} + +function uiGetParent( r ) +{ + parent_ele = document.getElementById(r.parent_id); + + if( parent_ele == null ) + { + uiOnError("Parent not found. parent_id:" + r.parent_id,r); + } + + return parent_ele; +} + +function uiCreateEle( r ) +{ + var parent_ele; + + if((parent_ele = uiGetParent(r)) != null ) + { + ele = document.createElement(r.ele_type) + ele.id = r.ele_id; + ele.className = r.value; + + parent_ele.appendChild(ele) + } +} + +function uiRemoveChildren( r ) +{ + ele = document.getElementById(r.ele_id) + + while (ele.firstChild) + { + ele.removeChild(ele.firstChild); + } +} + +function uiDivCreate( r ) +{ uiCreateEle(r) } + +function uiLabelCreate( r ) +{ + var parent_ele; + + if((parent_ele = uiGetParent(r)) != null ) + { + ele = document.createElement("label") + ele.htmlFor = r.ele_id + ele.innerHTML = r.value; + parent_ele.appendChild(ele) + } + +} + +function uiSelectCreate( r ) +{ + uiCreateEle(r) +} + +function uiSelectClear( r ) +{ uiRemoveChildren(r) } + +function uiSelectInsert( r ) +{ + var select_ele; + + if((select_ele = uiGetParent(r)) != null ) + { + var option = document.createElement('option'); + + option.id = r.ele_id; + option.innerHTML = r.value; + option.value = r.ele_id; + option.onclick = function() { uiOnSelectClick(this) } + + select_ele.appendChild(option) + } +} + +function uiSelectChoose( r ) +{ + var select_ele; + + if((select_ele = uiGetParent(r)) != null ) + { + if( select_ele.hasChildNodes()) + { + var children = select_ele.childNodes + for(var i=0; i 0) + { + label_ele = dom_create_ele("label"); + + label_ele.innerHTML = label; + + div_ele.appendChild(label_ele) + } + } + + return ui_create_ele( div_ele, ele_type, d, dfltEleClassName ); +} + +function ui_create_div( parent_ele, d ) +{ + var div_ele = ui_create_ele( parent_ele, "div", d, "uiDiv" ); + + if( div_ele != null ) + { + + if( d.title != null ) + { + var title = d.title.trim() + + if( title.length > 0 ) + { + var p_ele = dom_create_ele("p") + + p_ele.innerHTML = title + + div_ele.appendChild( p_ele ) + } + } + } + + return div_ele; +} + +function ui_create_panel_div( parent_ele, d ) +{ + d.type = "div" + var div_ele = ui_create_div( parent_ele, d ); + + if( !d.hasOwnProperty('className') ) + div_ele.className = "uiPanel" + + return div_ele +} + +function ui_create_row_div( parent_ele, d ) +{ + d.type = "div" + var div_ele = ui_create_div( parent_ele, d ); + + if( !d.hasOwnProperty('className') ) + div_ele.className = "uiRow" + + return div_ele +} + +function ui_create_col_div( parent_ele, d ) +{ + d.type = "div" + var div_ele = ui_create_div( parent_ele, d ); + + if( !d.hasOwnProperty('className') ) + div_ele.className = "uiCol" + + return div_ele +} + + +function ui_create_label( parent_ele, d ) +{ + var ele = ui_create_ele( parent_ele, "label", d, "uiLabel" ); + + if( ele != null ) + { + ele.innerHTML = d.title; + } + + return ele; +} + +function ui_create_button( parent_ele, d ) +{ + var ele = ui_create_ctl( parent_ele, "button", null, d, "uiButton" ); + + if( ele != null ) + { + ele.innerHTML = d.title; + ele.onclick = function() { ui_send_int_value(this,1); } + } + + return ele; +} + +function ui_create_check( parent_ele, d ) +{ + var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiCheck" ) + + if( ele != null ) + { + ele.type = "checkbox"; + + ele.onclick = function() { ui_send_bool_value(this,dom_get_checkbox(this.id)); } + + if( !d.hasOwnProperty('value') ) + ui_send_echo(ele) + else + { + dom_set_checkbox(ele.id, d.value ); + ui_send_bool_value(ele,dom_get_checkbox(ele.id)) + } + + } + return ele; +} + +// +// Note: The value of a 'select' widget is always set by the 'appId' +// of the selected 'option'. Likewise the 'appId' of the selected +// option is returned as the value of the select widget. +// +function ui_on_select( ele ) +{ + ui_send_int_value(ele,ele.options[ ele.selectedIndex ].appId); +} + +function ui_select_set_from_option_app_id( sel_ele, option_appId ) +{ + var i; + for(i=0; i to the containing div + var ele = dom_create_ele("pre") + + ele.id = log_ele.id + "_pre" + ele.onclick = _on_log_click; + ele.auto_scroll_flag = true; + + log_ele.appendChild(ele) + + return log_ele +} + +function ui_set_log_text( ele, value ) +{ + var child_id = ele.id + "_pre" + + for(var i=0; i