audio_midi : Initial implementation of audio_midi io-based app.
This commit is contained in:
parent
c66b1f4470
commit
a511b00493
@ -50,8 +50,8 @@ libcwHDR += src/libcw/cwMidiPort.h src/libcw/cwAudioD
|
|||||||
libcwSRC += src/libcw/cwMidiPort.cpp src/libcw/cwMidiAlsa.cpp src/libcw/cwAudioDeviceAlsa.cpp src/libcw/cwAudioDeviceTest.cpp
|
libcwSRC += src/libcw/cwMidiPort.cpp src/libcw/cwMidiAlsa.cpp src/libcw/cwAudioDeviceAlsa.cpp src/libcw/cwAudioDeviceTest.cpp
|
||||||
|
|
||||||
if cwWEBSOCK
|
if cwWEBSOCK
|
||||||
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h
|
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h
|
||||||
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp
|
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp
|
||||||
endif
|
endif
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
721
cwIoAudioMidi.cpp
Normal file
721
cwIoAudioMidi.cpp
Normal file
@ -0,0 +1,721 @@
|
|||||||
|
#include "cwCommon.h"
|
||||||
|
#include "cwLog.h"
|
||||||
|
#include "cwCommonImpl.h"
|
||||||
|
#include "cwMem.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 "cwIoAudioMidi.h"
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
namespace audio_midi
|
||||||
|
{
|
||||||
|
|
||||||
|
// Application Id's for UI elements
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
// Resource Based elements
|
||||||
|
kPanelDivId = 1000,
|
||||||
|
kQuitBtnId,
|
||||||
|
kIoReportBtnId,
|
||||||
|
kReportBtnId,
|
||||||
|
kRecordCheckId,
|
||||||
|
kStartBtnId,
|
||||||
|
kStopBtnId,
|
||||||
|
kClearBtnId,
|
||||||
|
kMsgCntId,
|
||||||
|
kSaveBtnId,
|
||||||
|
kOpenBtnId,
|
||||||
|
kFnStringId
|
||||||
|
};
|
||||||
|
|
||||||
|
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, kRecordCheckId, "recordCheckId" },
|
||||||
|
{ kPanelDivId, kStartBtnId, "startBtnId" },
|
||||||
|
{ kPanelDivId, kStopBtnId, "stopBtnId" },
|
||||||
|
{ kPanelDivId, kClearBtnId, "clearBtnId" },
|
||||||
|
{ kPanelDivId, kMsgCntId, "msgCntId" },
|
||||||
|
|
||||||
|
{ kPanelDivId, kSaveBtnId, "saveBtnId" },
|
||||||
|
{ kPanelDivId, kOpenBtnId, "openBtnId" },
|
||||||
|
{ kPanelDivId, kFnStringId, "filenameId" },
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
|
||||||
|
|
||||||
|
typedef struct am_audio_str
|
||||||
|
{
|
||||||
|
time::spec_t timestamp;
|
||||||
|
unsigned chCnt;
|
||||||
|
|
||||||
|
|
||||||
|
} am_audio_t;
|
||||||
|
|
||||||
|
typedef struct am_midi_msg_str
|
||||||
|
{
|
||||||
|
unsigned devIdx;
|
||||||
|
unsigned portIdx;
|
||||||
|
time::spec_t timestamp;
|
||||||
|
uint8_t ch;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t d0;
|
||||||
|
uint8_t d1;
|
||||||
|
|
||||||
|
unsigned microsec;
|
||||||
|
|
||||||
|
} am_midi_msg_t;
|
||||||
|
|
||||||
|
typedef struct app_str
|
||||||
|
{
|
||||||
|
const char* record_dir;
|
||||||
|
const char* record_fn;
|
||||||
|
const char* record_fn_ext;
|
||||||
|
am_midi_msg_t* midiMsgArray;
|
||||||
|
unsigned midiMsgArrayN;
|
||||||
|
unsigned midiMsgArrayInIdx;
|
||||||
|
unsigned midiMsgArrayOutIdx;
|
||||||
|
unsigned midi_timer_period_micro_sec;
|
||||||
|
|
||||||
|
const char* midiOutDevLabel;
|
||||||
|
const char* midiOutPortLabel;
|
||||||
|
unsigned midiOutDevIdx;
|
||||||
|
unsigned midiOutPortIdx;
|
||||||
|
|
||||||
|
time::spec_t play_time;
|
||||||
|
char* filename;
|
||||||
|
|
||||||
|
time::spec_t start_time;
|
||||||
|
unsigned midiFilterCnt;
|
||||||
|
|
||||||
|
bool recordFl;
|
||||||
|
bool startedFl;
|
||||||
|
|
||||||
|
handle_t ioH;
|
||||||
|
} app_t;
|
||||||
|
|
||||||
|
rc_t _parseCfg(app_t* app, const object_t* cfg )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
if((rc = cfg->getv(
|
||||||
|
"record_dir", app->record_dir,
|
||||||
|
"record_fn", app->record_fn,
|
||||||
|
"record_fn_ext", app->record_fn_ext,
|
||||||
|
"max_midi_msg_count", app->midiMsgArrayN,
|
||||||
|
"midi_timer_period_micro_sec", app->midi_timer_period_micro_sec,
|
||||||
|
"midi_out_device", app->midiOutDevLabel,
|
||||||
|
"midi_out_port", app->midiOutPortLabel)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kSyntaxErrorRC,"Audio MIDI app configuration parse failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate the MIDI msg buffer
|
||||||
|
app->midiMsgArray = mem::allocZ<am_midi_msg_t>( app->midiMsgArrayN );
|
||||||
|
|
||||||
|
// verify that the output directory exists
|
||||||
|
filesys::makeDir(app->record_dir);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _free( app_t& app )
|
||||||
|
{
|
||||||
|
mem::release(app.midiMsgArray);
|
||||||
|
mem::release(app.filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _resolve_midi_device_port_index( app_t* app )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
if((app->midiOutDevIdx = io::midiDeviceIndex(app->ioH,app->midiOutDevLabel)) == kInvalidIdx )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kInvalidArgRC,"The MIDI output device: '%s' was not found.", cwStringNullGuard(app->midiOutDevLabel) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if((app->midiOutPortIdx = io::midiDevicePortIndex(app->ioH,app->midiOutDevIdx,false,app->midiOutPortLabel)) == kInvalidIdx )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kInvalidArgRC,"The MIDI output port: '%s' was not found.", cwStringNullGuard(app->midiOutPortLabel) );
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("MIDI DEV: %i PORT:%i\n",app->midiOutDevIdx,app->midiOutPortIdx);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _set_midi_msg_next_index( app_t* app, unsigned next_idx )
|
||||||
|
{
|
||||||
|
app->midiMsgArrayInIdx = next_idx;
|
||||||
|
|
||||||
|
io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kMsgCntId), app->midiMsgArrayInIdx );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _set_midi_msg_next_play_index(app_t* app, unsigned next_idx)
|
||||||
|
{
|
||||||
|
app->midiMsgArrayOutIdx = next_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _on_start_btn( app_t* app )
|
||||||
|
{
|
||||||
|
app->startedFl = true;
|
||||||
|
|
||||||
|
time::get(app->start_time);
|
||||||
|
|
||||||
|
if( app->recordFl )
|
||||||
|
{
|
||||||
|
app->midiFilterCnt = 0;
|
||||||
|
|
||||||
|
_set_midi_msg_next_index(app, 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_set_midi_msg_next_play_index(app,0);
|
||||||
|
io::timerStart( app->ioH, io::timerIdToIndex(app->ioH, kAmMidiTimerId) );
|
||||||
|
time::get(app->play_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _on_stop_btn( app_t* app )
|
||||||
|
{
|
||||||
|
app->startedFl = false;
|
||||||
|
|
||||||
|
time::spec_t t1;
|
||||||
|
time::get(t1);
|
||||||
|
|
||||||
|
if( app->recordFl )
|
||||||
|
{
|
||||||
|
|
||||||
|
// set the 'microsec' value for each MIDI msg
|
||||||
|
for(unsigned i=0; i<app->midiMsgArrayInIdx; ++i)
|
||||||
|
{
|
||||||
|
app->midiMsgArray[i].microsec = time::elapsedMicros(app->midiMsgArray[0].timestamp,app->midiMsgArray[i].timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
cwLogInfo("MIDI messages recorded: %i filtered: %i\n",app->midiMsgArrayInIdx, app->midiFilterCnt );
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
io::timerStop( app->ioH, io::timerIdToIndex(app->ioH, kAmMidiTimerId) );
|
||||||
|
}
|
||||||
|
|
||||||
|
cwLogInfo("Runtime: %5.2f seconds.", time::elapsedMs(app->start_time,t1)/1000.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _read_midi( app_t* app )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
char* fn = filesys::makeFn( app->record_dir, app->filename, NULL, NULL );
|
||||||
|
file::handle_t fH;
|
||||||
|
unsigned n = 0;
|
||||||
|
|
||||||
|
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kOpenFailRC,"Unable to locate the file: '%s'.", fn );
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = file::read(fH,n)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( n > app->midiMsgArrayN )
|
||||||
|
{
|
||||||
|
cwLogWarning("The count of message in Audio-MIDI file '%s' reduced from %i to %i.", fn, n, app->midiMsgArrayN );
|
||||||
|
n = app->midiMsgArrayN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = file::read(fH,app->midiMsgArray,n*sizeof(am_midi_msg_t))) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_midi_msg_next_index(app, n );
|
||||||
|
|
||||||
|
cwLogInfo("Read %i from '%s'.",n,fn);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
mem::release(fn);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _write_midi( app_t* app )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
char* fn = nullptr;
|
||||||
|
file::handle_t fH;
|
||||||
|
|
||||||
|
if( app->midiMsgArrayInIdx == 0 )
|
||||||
|
{
|
||||||
|
cwLogWarning("Nothing to write.");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// form the filename
|
||||||
|
if((fn = filesys::makeVersionedFn( app->record_dir, app->record_fn, app->record_fn_ext, NULL )) == nullptr )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kOpFailRC,"Unable to form versioned filename in '%s' with prefix: '%s' and extension: '%s'.",
|
||||||
|
cwStringNullGuard(app->record_dir),
|
||||||
|
cwStringNullGuard(app->record_fn),
|
||||||
|
cwStringNullGuard(app->record_fn_ext));
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file
|
||||||
|
if((rc = file::open(fH,fn,file::kWriteFl)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kOpenFailRC,"Unable to create the file '%s'.",cwStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the file header
|
||||||
|
if((rc = write(fH,app->midiMsgArrayInIdx)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kWriteFailRC,"Header write to '%s' failed.",cwStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the file data
|
||||||
|
if((rc = write(fH,app->midiMsgArray,sizeof(am_midi_msg_t)*app->midiMsgArrayInIdx)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(kWriteFailRC,"Data write to '%s' failed.",cwStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update UI msg count
|
||||||
|
io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kMsgCntId), app->midiMsgArrayInIdx );
|
||||||
|
|
||||||
|
file::close(fH);
|
||||||
|
|
||||||
|
cwLogInfo("Saved %i events to '%s'.", app->midiMsgArrayInIdx, fn );
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
mem::release(fn);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _print_midi_msg( const am_midi_msg_t* mm )
|
||||||
|
{
|
||||||
|
printf("%i %i : %10i : %2i 0x%02x 0x%02x 0x%02x\n", mm->devIdx, mm->portIdx, mm->microsec, mm->ch, mm->status, mm->d0, mm->d1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void _report_midi( app_t* app )
|
||||||
|
{
|
||||||
|
for(unsigned i=0; i<app->midiMsgArrayInIdx; ++i)
|
||||||
|
{
|
||||||
|
am_midi_msg_t* mm = app->midiMsgArray + i;
|
||||||
|
_print_midi_msg(mm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _onUiInit(app_t* app, const ui_msg_t& m )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _onUiValue(app_t* app, const 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:
|
||||||
|
_report_midi(app);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSaveBtnId:
|
||||||
|
_write_midi(app);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kOpenBtnId:
|
||||||
|
printf("open btn\n");
|
||||||
|
_read_midi(app);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kRecordCheckId:
|
||||||
|
cwLogInfo("Record:%i",m.value->u.b);
|
||||||
|
app->recordFl = m.value->u.b;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStartBtnId:
|
||||||
|
cwLogInfo("Start");
|
||||||
|
_on_start_btn(app);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStopBtnId:
|
||||||
|
cwLogInfo("Stop");
|
||||||
|
_on_stop_btn(app);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kClearBtnId:
|
||||||
|
cwLogInfo("Clear");
|
||||||
|
_set_midi_msg_next_index(app, 0 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kFnStringId:
|
||||||
|
mem::release(app->filename);
|
||||||
|
app->filename = mem::duplStr(m.value->u.s);
|
||||||
|
printf("filename:%s\n",app->filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _onUiEcho(app_t* app, const ui_msg_t& m )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t uiCb( app_t* app, const 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rc_t timerCb(app_t* app, timer_msg_t& m)
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
// if the MIDI player is started and in 'play' mode and msg remain to be played
|
||||||
|
if( app->startedFl && (app->recordFl==false) && (app->midiMsgArrayOutIdx < app->midiMsgArrayInIdx))
|
||||||
|
{
|
||||||
|
time::spec_t t;
|
||||||
|
time::get(t);
|
||||||
|
|
||||||
|
unsigned cur_time_us = time::elapsedMicros(app->play_time,t);
|
||||||
|
|
||||||
|
while( app->midiMsgArray[ app->midiMsgArrayOutIdx ].microsec <= cur_time_us )
|
||||||
|
{
|
||||||
|
|
||||||
|
am_midi_msg_t* mm = app->midiMsgArray + app->midiMsgArrayOutIdx;
|
||||||
|
|
||||||
|
//_print_midi_msg(mm);
|
||||||
|
|
||||||
|
io::midiDeviceSend( app->ioH, app->midiOutDevIdx, app->midiOutPortIdx, mm->status + mm->ch, mm->d0, mm->d1 );
|
||||||
|
|
||||||
|
_set_midi_msg_next_play_index(app, app->midiMsgArrayOutIdx+1 );
|
||||||
|
|
||||||
|
// if all MIDI messages have been played
|
||||||
|
if( app->midiMsgArrayOutIdx >= app->midiMsgArrayInIdx )
|
||||||
|
{
|
||||||
|
_on_stop_btn(app);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _midi_filter( const midi::msg_t* mm )
|
||||||
|
{
|
||||||
|
//bool drop_fl = (mm->status & 0xf0) == midi::kCtlMdId && (64 <= mm->d0) && (mm->d0 <= 67) && (mm->d1 < 25);
|
||||||
|
//return drop_fl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t midiCb( app_t* app, const midi_msg_t& m )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
// this is a sys ex msg use: pkt->sysExMsg[j]
|
||||||
|
}
|
||||||
|
else // this is a triple
|
||||||
|
{
|
||||||
|
if( app->recordFl && app->startedFl )
|
||||||
|
{
|
||||||
|
|
||||||
|
// verify that space exists in the record buffer
|
||||||
|
if( app->midiMsgArrayInIdx >= app->midiMsgArrayN )
|
||||||
|
{
|
||||||
|
_on_stop_btn(app);
|
||||||
|
rc = cwLogError(kBufTooSmallRC,"MIDI message record buffer is full. % messages.",app->midiMsgArrayN);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// copy the msg into the record buffer
|
||||||
|
am_midi_msg_t* am = app->midiMsgArray + app->midiMsgArrayInIdx;
|
||||||
|
midi::msg_t* mm = pkt->msgArray + j;
|
||||||
|
|
||||||
|
if( _midi_filter(mm) )
|
||||||
|
{
|
||||||
|
app->midiFilterCnt++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
am->devIdx = pkt->devIdx;
|
||||||
|
am->portIdx = pkt->portIdx;
|
||||||
|
am->timestamp = mm->timeStamp;
|
||||||
|
am->ch = mm->status & 0x0f;
|
||||||
|
am->status = mm->status & 0xf0;
|
||||||
|
am->d0 = mm->d0;
|
||||||
|
am->d1 = mm->d1;
|
||||||
|
|
||||||
|
app->midiMsgArrayInIdx += 1;
|
||||||
|
|
||||||
|
// send msg count
|
||||||
|
io::uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,kMsgCntId), app->midiMsgArrayInIdx );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if( pkt->msgArray == NULL )
|
||||||
|
printf("io midi cb: 0x%x ",pkt->sysExMsg[j]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( !_midi_filter(pkt->msgArray + j) )
|
||||||
|
printf("io midi cb: %ld %ld 0x%x %i %i\n", pkt->msgArray[j].timeStamp.tv_sec, pkt->msgArray[j].timeStamp.tv_nsec, pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t audioCb( app_t* app, const audio_msg_t& m )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
unsigned chN = std::min(m.iBufChCnt,m.oBufChCnt);
|
||||||
|
unsigned byteCnt = m.dspFrameCnt * sizeof(sample_t);
|
||||||
|
|
||||||
|
// Copy the input to the output
|
||||||
|
for(unsigned i=0; i<chN; ++i)
|
||||||
|
if( m.oBufArray[i] != NULL )
|
||||||
|
{
|
||||||
|
// the input channel is not disabled
|
||||||
|
if( m.iBufArray[i] != NULL )
|
||||||
|
{
|
||||||
|
for(unsigned j=0; j<m.dspFrameCnt; ++j )
|
||||||
|
m.oBufArray[i][j] = m.iBufArray[i][j];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the input channel is disabled but the output is not - so fill the output with zeros
|
||||||
|
memset(m.oBufArray[i], 0, byteCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The main application callback
|
||||||
|
rc_t ioCb( void* arg, const msg_t* m )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
app_t* app = reinterpret_cast<app_t*>(arg);
|
||||||
|
|
||||||
|
switch( m->tid )
|
||||||
|
{
|
||||||
|
case kTimerTId:
|
||||||
|
if( m->u.timer != nullptr )
|
||||||
|
rc = timerCb(app,*m->u.timer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSerialTId:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kMidiTId:
|
||||||
|
if( m->u.midi != nullptr )
|
||||||
|
rc = midiCb(app,*m->u.midi);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kAudioTId:
|
||||||
|
if( m->u.audio != nullptr )
|
||||||
|
rc = audioCb(app,*m->u.audio);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kAudioMeterTId:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSockTId:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kWebSockTId:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kUiTId:
|
||||||
|
rc = uiCb(app,m->u.ui);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _report( handle_t h )
|
||||||
|
{
|
||||||
|
for(unsigned i=0; i<serialDeviceCount(h); ++i)
|
||||||
|
printf("serial: %s\n", serialDeviceLabel(h,i));
|
||||||
|
|
||||||
|
for(unsigned i=0; i<midiDeviceCount(h); ++i)
|
||||||
|
for(unsigned j=0; j<2; ++j)
|
||||||
|
{
|
||||||
|
bool inputFl = j==0;
|
||||||
|
unsigned m = midiDevicePortCount(h,i,inputFl);
|
||||||
|
for(unsigned k=0; k<m; ++k)
|
||||||
|
printf("midi: %s: %s : %s\n", inputFl ? "in ":"out", midiDeviceName(h,i), midiDevicePortName(h,i,inputFl,k));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned i=0; i<audioDeviceCount(h); ++i)
|
||||||
|
printf("audio: %s\n", audioDeviceName(h,i));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::io::audio_midi::main( const object_t* cfg )
|
||||||
|
{
|
||||||
|
|
||||||
|
rc_t rc;
|
||||||
|
app_t app = {};
|
||||||
|
|
||||||
|
if((rc = _parseCfg(&app,cfg)) != kOkRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
// create the io framework instance
|
||||||
|
if((rc = create(app.ioH,cfg,ioCb,&app,mapA,mapN)) != kOkRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// create the MIDI playback timer
|
||||||
|
if((rc = timerCreate( app.ioH, "am_timer", kAmMidiTimerId, app.midi_timer_period_micro_sec)) != kOkRC )
|
||||||
|
{
|
||||||
|
cwLogError(rc,"Audio-MIDI timer create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//report(app.ioH);
|
||||||
|
|
||||||
|
// start the io framework instance
|
||||||
|
if((rc = start(app.ioH)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Audio-MIDI app start failed.");
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
// resolve the MIDI out dev/port indexes from the MIDI out dev/port labels
|
||||||
|
rc_t rc0 = _resolve_midi_device_port_index(&app);
|
||||||
|
|
||||||
|
|
||||||
|
// resolve the audio group index from the lavel
|
||||||
|
rc_t rc1 = kOkRC;
|
||||||
|
unsigned devIdx;
|
||||||
|
|
||||||
|
if( (devIdx = audioDeviceLabelToIndex(app.ioH, "main")) == kInvalidIdx )
|
||||||
|
rc1 = cwLogError(kOpFailRC, "Unable to locate the requested audio device.");
|
||||||
|
|
||||||
|
if(rcSelect(rc0,rc1) != kOkRC )
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute the io framework
|
||||||
|
while( !isShuttingDown(app.ioH))
|
||||||
|
{
|
||||||
|
exec(app.ioH);
|
||||||
|
sleepMs(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
_free(app);
|
||||||
|
destroy(app.ioH);
|
||||||
|
printf("Audio-MIDI Done.\n");
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
16
cwIoAudioMidi.h
Normal file
16
cwIoAudioMidi.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef cwIoAudioMidi_H
|
||||||
|
#define cwIoAudioMidi_H
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
namespace audio_midi
|
||||||
|
{
|
||||||
|
rc_t main( const object_t* cfg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
75
html/audio_midi/css/ui.css
Normal file
75
html/audio_midi/css/ui.css
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uiCol {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background-color: LightBlue;
|
||||||
|
}
|
||||||
|
|
25
html/audio_midi/index.html
Normal file
25
html/audio_midi/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>UI Test App</title>
|
||||||
|
<script type="text/javascript" src="js/ui.js"></script>
|
||||||
|
<link href="css/ui.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script text="text/javascript">
|
||||||
|
window.addEventListener("load",main, false )
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="appTitleDiv" class="uiRow">
|
||||||
|
<p id="appTitleId">UI Test:</p>
|
||||||
|
<p id="connectTitleId">Disconnected</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="0" class="uiAppDiv">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
906
html/audio_midi/js/ui.js
Normal file
906
html/audio_midi/js/ui.js
Normal file
@ -0,0 +1,906 @@
|
|||||||
|
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<children.length; i++)
|
||||||
|
{
|
||||||
|
if( children[i].id == r.ele_id )
|
||||||
|
{
|
||||||
|
select_ele.selectedIndex = i
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uiOnSelectClick( ele )
|
||||||
|
{
|
||||||
|
cmdstr = "mode ui ele_type select op choose parent_id "+ele.parentElement.id+" option_id " + ele.id
|
||||||
|
websocket.send(cmdstr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function uiNumberOnKeyUp( e )
|
||||||
|
{
|
||||||
|
if( e.keyCode == 13 )
|
||||||
|
{
|
||||||
|
//console.log(e)
|
||||||
|
cmdstr = "mode ui ele_type number op change parent_id "+e.srcElement.parentElement.id+" ele_id " + e.srcElement.id + " value " + e.srcElement.value
|
||||||
|
websocket.send(cmdstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function uiNumberCreate( r )
|
||||||
|
{
|
||||||
|
var parent_ele;
|
||||||
|
|
||||||
|
if((parent_ele = uiGetParent(r)) != null )
|
||||||
|
{
|
||||||
|
ele = document.createElement("input")
|
||||||
|
ele.id = r.ele_id
|
||||||
|
ele.setAttribute('type','number')
|
||||||
|
ele.addEventListener('keyup',uiNumberOnKeyUp)
|
||||||
|
parent_ele.appendChild(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uiNumberSet( r )
|
||||||
|
{
|
||||||
|
var ele;
|
||||||
|
|
||||||
|
//console.log("ele_id:" + r.ele_id + " parent_id:" + r.parent_id + " value:" + r.value)
|
||||||
|
|
||||||
|
if((ele = document.getElementById(r.parent_id)) != null)
|
||||||
|
{
|
||||||
|
switch( r.ele_id )
|
||||||
|
{
|
||||||
|
case "0": ele.min = r.value; break;
|
||||||
|
case "1": ele.max = r.value; break;
|
||||||
|
case "2": ele.step = r.value; break;
|
||||||
|
case "3": ele.value = r.value; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function dom_child_by_id( parentEle, child_id )
|
||||||
|
{
|
||||||
|
var childrenL = parentEle.children
|
||||||
|
for(var i=0; i<childrenL.length; i++)
|
||||||
|
{
|
||||||
|
if( childrenL[i].id == child_id )
|
||||||
|
{
|
||||||
|
return childrenL[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dom_set_option_by_text( ele_id, text )
|
||||||
|
{
|
||||||
|
var ele = dom_id_to_ele(ele_id);
|
||||||
|
|
||||||
|
for (var i = 0; i < ele.options.length; i++)
|
||||||
|
{
|
||||||
|
if (ele.options[i].text === text)
|
||||||
|
{
|
||||||
|
ele.selectedIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function dom_set_number( ele_id, val )
|
||||||
|
{
|
||||||
|
dom_id_to_ele(ele_id).value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
function dom_id_to_ele( id )
|
||||||
|
{ return document.getElementById(id); }
|
||||||
|
|
||||||
|
function dom_set_checkbox( ele_id, fl )
|
||||||
|
{ dom_id_to_ele(ele_id).checked = fl }
|
||||||
|
|
||||||
|
function dom_get_checkbox( ele_id )
|
||||||
|
{ return dom_id_to_ele(ele_id).checked }
|
||||||
|
|
||||||
|
function dom_create_ele( ele_type )
|
||||||
|
{
|
||||||
|
ele = document.createElement(ele_type);
|
||||||
|
return ele
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
function ui_error( msg )
|
||||||
|
{
|
||||||
|
console.log("Error: " + msg )
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_send_value( ele, typeId, value )
|
||||||
|
{
|
||||||
|
ws_send("value " + ele.id + " " + typeId + " : " + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_send_bool_value( ele, value ) { ui_send_value(ele,'b',value); }
|
||||||
|
function ui_send_int_value( ele, value ) { ui_send_value(ele,'i',value); }
|
||||||
|
function ui_send_float_value( ele, value ) { ui_send_value(ele,'f',value); }
|
||||||
|
function ui_send_string_value( ele, value ) { ui_send_value(ele,'s',value); }
|
||||||
|
|
||||||
|
function ui_send_echo( ele )
|
||||||
|
{
|
||||||
|
ws_send("echo " + ele.id )
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_print_children( eleId )
|
||||||
|
{
|
||||||
|
var childrenL = dom_id_to_ele(eleId).children
|
||||||
|
|
||||||
|
for(var i=0; i<childrenL.length; i++)
|
||||||
|
{
|
||||||
|
console.log( childrenL[i] )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_get_parent( parentId )
|
||||||
|
{
|
||||||
|
if( parentId==null || parentId.trim().length == 0 )
|
||||||
|
parentId = _rootId
|
||||||
|
|
||||||
|
parent_ele = dom_id_to_ele(parentId);
|
||||||
|
|
||||||
|
if( parent_ele == null )
|
||||||
|
{
|
||||||
|
ui_error("Parent element id: " + parentId + " not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent_ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ui_create_ele( parent_ele, ele_type, d, dfltClassName )
|
||||||
|
{
|
||||||
|
// create the ctl object
|
||||||
|
var ele = dom_create_ele(ele_type);
|
||||||
|
|
||||||
|
if( ele == null )
|
||||||
|
ui_error(ele_type +" element create failed.");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ele.id = d.uuId;
|
||||||
|
|
||||||
|
if(d.hasOwnProperty('className') )
|
||||||
|
ele.className = d.className;
|
||||||
|
else
|
||||||
|
ele.className = dfltClassName;
|
||||||
|
|
||||||
|
if(d.hasOwnProperty('appId'))
|
||||||
|
ele.appId = d.appId;
|
||||||
|
else
|
||||||
|
ele.appId = null;
|
||||||
|
|
||||||
|
//console.log("Created: " + ele_type + " parent:" + d.parentUuId + " id:" + ele.id + " appId:" + ele.appId)
|
||||||
|
|
||||||
|
parent_ele.appendChild(ele);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return ele
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_ctl( parent_ele, ele_type, label, d, dfltEleClassName )
|
||||||
|
{
|
||||||
|
// create an enclosing div
|
||||||
|
var div_ele = dom_create_ele("div");
|
||||||
|
|
||||||
|
div_ele.className = "uiCtlDiv " + dfltEleClassName + "Div"
|
||||||
|
|
||||||
|
parent_ele.appendChild( div_ele );
|
||||||
|
|
||||||
|
var label_ele = div_ele
|
||||||
|
|
||||||
|
// if label is not null then create an enclosing 'label' element
|
||||||
|
if( label != null )
|
||||||
|
{
|
||||||
|
label = label.trim();
|
||||||
|
|
||||||
|
if( label.length > 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_title( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ele( parent_ele, "label", d, "uiTitle" );
|
||||||
|
|
||||||
|
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<sel_ele.options.length; ++i)
|
||||||
|
if( sel_ele.options[i].appId == option_appId )
|
||||||
|
{
|
||||||
|
sel_ele.selectedIndex = i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_error("Select option index not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_select( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ctl( parent_ele, "select", d.title, d, "uiSelect" );
|
||||||
|
ele.onchange = function() { ui_on_select(this) }
|
||||||
|
|
||||||
|
if( !d.hasOwnProperty('value') )
|
||||||
|
{
|
||||||
|
ui_send_echo(ele)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Note that d.value is the appId of the default selected option
|
||||||
|
ele.defaultOptionAppId = d.value;
|
||||||
|
ui_send_int_value(ele,ele.defaultOptionAppId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_option( parent_ele, d )
|
||||||
|
{
|
||||||
|
var opt_ele = ui_create_ele( parent_ele, "option", d, "uiOption" );
|
||||||
|
|
||||||
|
if( opt_ele != null )
|
||||||
|
{
|
||||||
|
if(d.hasOwnProperty('className'))
|
||||||
|
opt_ele.className = d.className;
|
||||||
|
|
||||||
|
opt_ele.innerHTML = d.title;
|
||||||
|
|
||||||
|
// d.value, if it exists, is a boolean indicating that this is the default option
|
||||||
|
var fl0 = d.hasOwnProperty('value') && d.value != 0;
|
||||||
|
|
||||||
|
// The parent 'select' element may also have been given the app id of the default option
|
||||||
|
// (and this option may be it)
|
||||||
|
var fl1 = parent_ele.hasOwnProperty('defaultOptionAppId') && parent_ele.defaultOptionAppId == ele.appId;
|
||||||
|
|
||||||
|
if(fl0 || fl1 )
|
||||||
|
{
|
||||||
|
parent_ele.selectedIndex = parent_ele.options.length-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opt_ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _ui_on_focus( ele )
|
||||||
|
{
|
||||||
|
_focusId=ele.id;
|
||||||
|
_focusVal=ele.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _ui_on_string_blur( ele )
|
||||||
|
{
|
||||||
|
if( ele.id == _focusId )
|
||||||
|
{
|
||||||
|
if( ele.value != _focusVal )
|
||||||
|
ui_send_string_value(ele,ele.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_string( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiString" );
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
ele.addEventListener('keyup', function(e) { if(e.keyCode===13){ ui_send_string_value(this, this.value); }} );
|
||||||
|
ele.addEventListener('focus', function(e) { _ui_on_focus(this); } );
|
||||||
|
ele.addEventListener('blur', function(e) { _ui_on_string_blur(this); } );
|
||||||
|
|
||||||
|
if( !d.hasOwnProperty('value') )
|
||||||
|
ui_send_echo(ele);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ele.value = d.value;
|
||||||
|
ui_send_string_value(ele,ele.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _ui_send_number( ele )
|
||||||
|
{
|
||||||
|
var val = 0;
|
||||||
|
if( ele.decpl == 0 )
|
||||||
|
val = Number.parseInt(ele.value)
|
||||||
|
else
|
||||||
|
val = Number.parseFloat(ele.value)
|
||||||
|
|
||||||
|
if( !(ele.minValue<=val && val<=ele.maxValue))
|
||||||
|
ele.style.borderColor = "red"
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ele.style.borderColor = ""
|
||||||
|
|
||||||
|
if( ele.decpl == 0 )
|
||||||
|
ui_send_int_value(ele,ele.value);
|
||||||
|
else
|
||||||
|
ui_send_float_value(ele,ele.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function _ui_on_number_blur( ele )
|
||||||
|
{
|
||||||
|
if( ele.id == _focusId )
|
||||||
|
{
|
||||||
|
if( ele.value != _focusVal )
|
||||||
|
{
|
||||||
|
_ui_send_number(ele)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_number_keyup( e )
|
||||||
|
{
|
||||||
|
if( e.keyCode===13 )
|
||||||
|
{
|
||||||
|
var ele = dom_id_to_ele(e.target.id)
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
//console.log("min:"+ele.minValue+" max:"+ele.maxValue)
|
||||||
|
_ui_send_number(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_number( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiNumber" );
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
ele.maxValue = d.max;
|
||||||
|
ele.minValue = d.min;
|
||||||
|
ele.stepValue = d.step;
|
||||||
|
ele.decpl = d.decpl;
|
||||||
|
ele.addEventListener('keyup', ui_number_keyup );
|
||||||
|
ele.addEventListener('focus', function(e) { _ui_on_focus(this); } );
|
||||||
|
ele.addEventListener('blur', function(e) { _ui_on_number_blur(this); } );
|
||||||
|
|
||||||
|
if( d.hasOwnProperty('value') && d.min <= d.value && d.value <= d.max )
|
||||||
|
{
|
||||||
|
|
||||||
|
ele.value = d.value;
|
||||||
|
if( d.decpl == 0 )
|
||||||
|
ui_send_int_value( ele, ele.value )
|
||||||
|
else
|
||||||
|
ui_send_float_value( ele, ele.value )
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui_send_echo(ele);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_set_number_display( ele_id, value )
|
||||||
|
{
|
||||||
|
var ele = dom_id_to_ele(ele_id);
|
||||||
|
|
||||||
|
if( typeof(value)=="number")
|
||||||
|
{
|
||||||
|
var val = value.toString();
|
||||||
|
|
||||||
|
if( ele.decpl == 0 )
|
||||||
|
ele.innerHTML = parseInt(val,10);
|
||||||
|
else
|
||||||
|
ele.innerHTML = parseFloat(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_number_display( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ctl( parent_ele, "label", d.title, d, "uiNumbDisp" );
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
ele.decpl = d.decpl;
|
||||||
|
|
||||||
|
if( d.hasOwnProperty('value') )
|
||||||
|
{
|
||||||
|
ui_set_number_display(ele.id, d.value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui_send_echo(ele);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ele;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_set_progress( ele_id, value )
|
||||||
|
{
|
||||||
|
var ele = dom_id_to_ele(ele_id);
|
||||||
|
|
||||||
|
ele.value = Math.round( ele.max * (value - ele.minValue) / (ele.maxValue - ele.minValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create_progress( parent_ele, d )
|
||||||
|
{
|
||||||
|
var ele = ui_create_ctl( parent_ele, "progress", d.title, d, "uiProgress" );
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
ele.max = 100;
|
||||||
|
ele.maxValue = d.max;
|
||||||
|
ele.minValue = d.min;
|
||||||
|
if( !d.hasOwnProperty('value') )
|
||||||
|
ui_send_echo(ele);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui_set_progress( ele.id, d.value );
|
||||||
|
ui_send_int_value( ele, ele.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return ele
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_set_value( d )
|
||||||
|
{
|
||||||
|
//console.log(d)
|
||||||
|
var eleId = d.uuId.toString()
|
||||||
|
var ele = dom_id_to_ele(eleId)
|
||||||
|
|
||||||
|
if( ele == null )
|
||||||
|
console.log("ele '"+eleId+"' not found");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( !ele.hasOwnProperty("uiEleType") )
|
||||||
|
{
|
||||||
|
console.log("No type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ele != null && ele.hasOwnProperty("uiEleType"))
|
||||||
|
{
|
||||||
|
//console.log("found: "+ele.uiEleType)
|
||||||
|
|
||||||
|
switch( ele.uiEleType )
|
||||||
|
{
|
||||||
|
case "div":
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "title":
|
||||||
|
ele.innerHTML = d.value
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "button":
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "check":
|
||||||
|
dom_set_checkbox(ele.id,d.value)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "select":
|
||||||
|
ui_select_set_from_option_app_id(ele,d.value)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "option":
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
ele.value = d.value
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "number":
|
||||||
|
ele.value = d.value
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "numb_disp":
|
||||||
|
ui_set_number_display(ele.id,d.value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "progress":
|
||||||
|
ele.value = d.value
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ui_error("Unknown UI element type: " + d.type )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_create( d )
|
||||||
|
{
|
||||||
|
if( typeof(d.parentUuId) == "number")
|
||||||
|
d.parentUuId = d.parentUuId.toString()
|
||||||
|
|
||||||
|
if( typeof(d.uuId) == "number" )
|
||||||
|
d.uuId = d.uuId.toString()
|
||||||
|
|
||||||
|
|
||||||
|
var parent_ele = ui_get_parent(d.parentUuId);
|
||||||
|
var ele = null;
|
||||||
|
|
||||||
|
if( parent_ele != null )
|
||||||
|
{
|
||||||
|
switch( d.type )
|
||||||
|
{
|
||||||
|
case "div":
|
||||||
|
ele = ui_create_div( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "panel":
|
||||||
|
ele = ui_create_panel_div( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "row":
|
||||||
|
ele = ui_create_row_div( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "col":
|
||||||
|
ele = ui_create_col_div( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "title":
|
||||||
|
ele = ui_create_title( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "button":
|
||||||
|
ele = ui_create_button( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "check":
|
||||||
|
ele = ui_create_check( parent_ele, d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "select":
|
||||||
|
ele = ui_create_select( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "option":
|
||||||
|
ele = ui_create_option( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
ele = ui_create_string( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "number":
|
||||||
|
ele = ui_create_number( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "numb_disp":
|
||||||
|
ele = ui_create_number_display( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "progress":
|
||||||
|
ele = ui_create_progress( parent_ele, d );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ui_error("Unknown UI element type: " + d.type )
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ele != null )
|
||||||
|
{
|
||||||
|
ele.uiEleType = d.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function ws_send( s )
|
||||||
|
{
|
||||||
|
//console.log(s)
|
||||||
|
|
||||||
|
_ws.send(s+"\0")
|
||||||
|
}
|
||||||
|
|
||||||
|
function ws_on_msg( jsonMsg )
|
||||||
|
{
|
||||||
|
//console.log(jsonMsg)
|
||||||
|
|
||||||
|
d = JSON.parse(jsonMsg.data);
|
||||||
|
|
||||||
|
switch( d.op )
|
||||||
|
{
|
||||||
|
case 'create':
|
||||||
|
ui_create( d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
ui_set_value( d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ui_error("Unknown UI operation. " + d.op )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function ws_on_open()
|
||||||
|
{
|
||||||
|
set_app_title( "Connected", "title_connected" );
|
||||||
|
_ws.send("init")
|
||||||
|
}
|
||||||
|
|
||||||
|
function ws_on_close()
|
||||||
|
{
|
||||||
|
set_app_title( "Disconnected", "title_disconnected" );
|
||||||
|
}
|
||||||
|
|
||||||
|
function ws_form_url(urlSuffix)
|
||||||
|
{
|
||||||
|
var pcol;
|
||||||
|
var u = document.URL;
|
||||||
|
|
||||||
|
pcol = "ws://";
|
||||||
|
if (u.substring(0, 4) === "http")
|
||||||
|
u = u.substr(7);
|
||||||
|
|
||||||
|
u = u.split("/");
|
||||||
|
|
||||||
|
return pcol + u[0] + "/" + urlSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main()
|
||||||
|
{
|
||||||
|
rootEle = dom_id_to_ele(_rootId);
|
||||||
|
rootEle.uuId = 0;
|
||||||
|
rootEle.id = _nextEleId;
|
||||||
|
_nextEleId += 1;
|
||||||
|
|
||||||
|
//console.log(ws_form_url(""))
|
||||||
|
|
||||||
|
_ws = new WebSocket(ws_form_url(""),"ui_protocol")
|
||||||
|
|
||||||
|
_ws.onmessage = ws_on_msg
|
||||||
|
_ws.onopen = ws_on_open
|
||||||
|
_ws.onclose = ws_on_close;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
33
html/audio_midi/ui.cfg
Normal file
33
html/audio_midi/ui.cfg
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
parent: "uiDivId"
|
||||||
|
|
||||||
|
panel: {
|
||||||
|
|
||||||
|
name: "panelDivId",
|
||||||
|
title: "Audio MIDI",
|
||||||
|
|
||||||
|
row: {
|
||||||
|
button:{ name: quitBtnId, title:"Quit" },
|
||||||
|
button:{ name: ioReportBtnId, title:"IO Report" },
|
||||||
|
button:{ name: reportBtnId, title:"App Report" },
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
row: {
|
||||||
|
check:{ name: recordCheckId, title:"Record" },
|
||||||
|
button:{ name: startBtnId, title:"Start" },
|
||||||
|
button:{ name: stopBtnId, title:"Stop" },
|
||||||
|
button:{ name: clearBtnId, title:"Clear" },
|
||||||
|
numb_disp: { name: msgCntId, title:"Count" },
|
||||||
|
},
|
||||||
|
|
||||||
|
row: {
|
||||||
|
string:{ name: filenameId, title:"File Name:", value:"record.am" },
|
||||||
|
button:{ name: openBtnId, title:"Open" },
|
||||||
|
button:{ name: saveBtnId, title:"Save" },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user