Initial working version.

This commit is contained in:
kevin 2024-02-17 08:27:18 -05:00
parent ff643dd314
commit f566198285
9 changed files with 1799 additions and 23 deletions

View File

@ -1,5 +1,4 @@
ACLOCAL_AMFLAGS = -I m4 # use custom macro's in ./m4
lib_LTLIBRARIES=
bin_PROGRAMS=
include_HEADERS=
@ -16,9 +15,6 @@ include_HEADERS=
# -Wno-multichar - turns off multi-character constant warnings from cmAudioFile.c
WS_DIR = $(HOME)/sdk/libwebsockets/build/out
AM_CPPFLAGS = -I.. -I$(srcdir)/src/libcw
AM_CFLAGS = -Wno-multichar
AM_CXXFLAGS = -Wno-multichar
@ -38,7 +34,7 @@ endif
if OS_LINUX
if OS_64
AM_LDFLAGS += -L/usr/lib64
AM_LDFLAGS += -L/usr/lib64/atlas -L/usr/lib64
AM_CFLAGS += -m64
endif
@ -86,8 +82,6 @@ if cwWEB
endif
if cwWEBSOCK
# AM_CPPFLAGS += -I$(WS_DIR)/include
# AM_LDFLAGS += -L$(WS_DIR)/lib
src_proj_proj_LDADD += -lwebsockets
endif

View File

@ -16,8 +16,6 @@ cd ${curdir}
--enable-alsa \
CFLAGS="-g -Wall" \
CXXFLAGS="-g -Wall" \
CPPFLAGS="-I${HOME}/sdk/libwebsockets/build/out/include" \
LDFLAGS="-L${HOME}/sdk/libwebsockets/build/out/lib" \
LIBS=

View File

@ -0,0 +1,59 @@
.fragList {
border: 1px solid LightSteelBlue;
width: 1000px;
height: 450px;
}
.fragList label {
width: 50px;
background-color: LightBlue;
}
.fragList .uiNumbDisp {
width: 25px;
}
.fragPanel {
border: 1px solid LightSteelBlue;
padding-bottom: 5px;
padding-top: 5px;
}
.fragPanelRow {
padding-right: 15px;
padding-left: 15px;
justify-content: space-around;
}
.fragPresetCtl {
width: 50px;
}
.fragPresetCtl:nth-of-type(even){
border: 1px solid Yellow;
}
.fragPresetCtl .uiNumber {
border: 1px solid LightSteelBlue;
width: 80%;
}
.fragPresetCtl .uiString {
border: 1px solid LightSteelBlue;
width: 80%;
}
.fragNote {
width: 575px;
}
.uiRow {
padding-bottom: 5px;
padding-top: 5px;
}
.velTunerPanel {
border: 1px solid black;
}

148
src/proj/html/css/ui.css Normal file
View File

@ -0,0 +1,148 @@
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;
}
/* 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%;
background-color: LightSteelBlue;
}
/* log scroller */
.uiLog {
display:flex;
flex-direction: column;
height: 150px;
overflow-x: hidden; /* 'hidden' to remove x scroll bar */
overflow-y: auto;
width: 100%;
background-color: PowderBlue;
}
/* The log text */
.uiLog pre {
}
/* outer list div - contains the list label and the list scroller */
.uiListDiv {
display: flex;
flex-direction: column;
align-items: flex-start; /* left justify */
align-content: flex-stretch; /* fill horizontal space */
}
.uiListDiv label {
width: 100%;
background-color: LightSteelBlue;
}
.uiList {
display:flex;
flex-direction: column;
height: 100px;
overflow-x: hidden; /* 'hidden' to remove scroll bar */
overflow-y: auto;
width: 100%;
background-color: PowderBlue;
}
.uiStringDisp {
width: 100%;
}
.uiSelected {
border: 1px solid blue;
}

24
src/proj/html/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Preset Selection App</title>
<script type="text/javascript" src="js/ui.js"></script>
<link href="css/ui.css" rel="stylesheet">
<link href="css/preset_sel.css" rel="stylesheet">
<script text="text/javascript">
window.addEventListener("load",main, false )
</script>
</head>
<body>
<div id="appTitleDiv" class="uiRow">
<p id="appTitleId">Preset Selection:</p>
<p id="connectTitleId">Disconnected</p>
</div>
</body>
</html>

1201
src/proj/html/js/ui.js Normal file

File diff suppressed because it is too large Load Diff

25
src/proj/html/ui.cfg Normal file
View File

@ -0,0 +1,25 @@
{
main: {
parent: "uiDivId"
panel: {
name: "panelDivId",
title: "",
row: {
button:{ name: quitBtnId, title:"Quit" },
button:{ name: ioReportBtnId, title:"IO Report" },
button:{ name: netPrintBtnId, title:"Print Network" }
button:{ name: reportBtnId, title:"App Report" },
button:{ name: latencyBtnId, title:"Latency Reset"},
},
row: {
number: { name:valueNumbId, title:"Value", min:0, max:10, step:1, decpl:0 },
}
}
}
}

View File

@ -1,3 +1,104 @@
{
param: "hello"
param: 5,
libcw: {
io: {
callbackMutexTimeOutMs: 100,
}
ui: {
physRootDir: "~/src/cw_io_template/src/proj/html",
dfltPageFn: "index.html",
port: 5687,
rcvBufByteN: 2048,
xmtBufByteN: 2048,
fmtBufByteN: 4096,
websockTimeOutMs: 50, // max time out while blocking for a websock event
idleMsgPeriodMs: 50, // period without messages before an idle message is generated
uiCfgFn: "ui.cfg", // default UI resource description
asyncFl: false
},
serial: {
pollPeriodMs: 50,
recvBufByteN: 512,
array: [
{
enableFl: false,
asyncFl: false,
label: "port1", // User label
device: "/dev/ttyUSB0", // Serial device name
baud: 115200,
bits: 8,
stop: 1,
parity: "no",
}
]
},
midi: {
parseBufByteCnt: 1024,
appNameStr: "cwtest",
fileDevName: "file_dev",
fileDevReadAheadMicros: 3000,
testFileLabel: "file_0",
testFileEnableFl: false
file_ports: [
{ "label":"file_0",
//"file": "/home/kevin/src/cwtest/src/cwtest/cfg/gutim_full/data1/beck1/record_4/midi.mid",
"enable_fl": false },
]
asyncFl: true,
},
audio: {
meterMs: 50, // audio meter filter length and meter callback period
threadTimeOutMs: 50, // audio thread cond var time out
groupL: [
{
enableFl: false, // (req)
asyncFl: true,
label: "main", // (req) User label
id: 0, // (req) User id (can also be set at runtime)
srate: 48000, // (req) Sample rate used by all devices in this group
dspFrameCnt: 64 // (req) Size of DSP processing buffers
}
],
deviceL: [
{
// System device name
//device: "Scarlett 18i20 USB USB Audio",
device: "USB Audio CODEC USB Audio",
//device: "HDA Intel PCH CS4208 Analog",
activeFl: false, // (req)
meterFl: true, // (opt)
label: "main", // (req) User label
userId: 0, // (opt) User id (can also be set at runtime)
framesPerCycle: 512, // (req) Samples per audio device cycle
cycleCnt: 3, // (req) Count of device cycle buffers.
inGroup: "main", // (opt) All devices in a group must be 'ready' to source
outGroup: "main", // (opt) or sink data before an audio callback is made for that group
}
]
},
socket: {
asyncFl: false,
maxSocketCnt: 10,
recvBufByteCnt: 4096,
threadTimeOutMs: 50,
socketL: [],
}
}
}

View File

@ -4,46 +4,272 @@
#include "cwText.h"
#include "cwObject.h"
#include "cwIo.h"
using namespace cw;
typedef struct app_str
{
object_t* cfg;
unsigned value;
const object_t* io_cfg;
io::handle_t ioH;
} app_t;
enum
{
kPanelDivId,
kQuitBtnId,
kIoReportBtnId,
kNetPrintBtnId,
kReportBtnId,
kLatencyBtnId,
kValueNumbId
};
ui::appIdMap_t appIdMapA[] = {
{ ui::kRootAppId, kPanelDivId, "panelDivId" },
{ kPanelDivId, kQuitBtnId, "quitBtnId" },
{ kPanelDivId, kIoReportBtnId, "ioReportBtnId" },
{ kPanelDivId, kNetPrintBtnId, "netPrintBtnId" },
{ kPanelDivId, kReportBtnId, "reportBtnId" },
{ kPanelDivId, kLatencyBtnId, "latencyBtnId" },
{ kPanelDivId, kValueNumbId, "valueNumbId" }
};
const unsigned appIdMapN = sizeof(appIdMapA)/sizeof(appIdMapA[0]);
void print( void* arg, const char* text )
{
printf("%s\n",text);
}
int main( int argc, char* argv[] )
rc_t _ui_value_callback(app_t* app, const io::ui_msg_t& m )
{
switch( m.appId )
{
case kQuitBtnId:
io::stop( app->ioH );
break;
case kIoReportBtnId:
io::report(app->ioH);
break;
case kNetPrintBtnId:
break;
case kReportBtnId:
break;
case kLatencyBtnId:
latency_measure_report(app->ioH);
latency_measure_setup(app->ioH);
break;
case kValueNumbId:
app->value = m.value->u.u;
cwLogInfo("Setting value:%i",app->value);
break;
}
return kOkRC;
}
rc_t _ui_echo_callback(app_t* app, const io::ui_msg_t& m )
{
switch( m.appId )
{
case kValueNumbId:
{
uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kValueNumbId ), app->value );
}
break;
}
return kOkRC;
}
rc_t _ui_callback( app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
object_t* cfg = nullptr;
cw::log::createGlobal();
cwLogInfo("Project template: args:%i", argc);
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:
cwLogInfo("UI Init.");
break;
case ui::kValueOpId:
_ui_value_callback( app, m );
break;
case ui::kCorruptOpId:
cwLogInfo("UI Corrupt.");
break;
case ui::kClickOpId:
cwLogInfo("UI Click.");
break;
case ui::kSelectOpId:
cwLogInfo("UI Select.");
break;
case ui::kEchoOpId:
_ui_echo_callback( app, m );
break;
case ui::kIdleOpId:
break;
case ui::kInvalidOpId:
// fall through
default:
assert(0);
break;
}
return rc;
}
rc_t _io_callback( void* arg, const io::msg_t* m )
{
app_t* app = (app_t*)arg;
switch( m->tid )
{
case io::kThreadTId:
break;
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:
_ui_callback(app,m->u.ui);
break;
case io::kExecTId:
break;
default:
assert(0);
}
return kOkRC;
}
rc_t _parse_cfg( app_t& app, int argc, char* argv[] )
{
rc_t rc = kOkRC;
if( argc < 2 || textLength(argv[1])==0 )
{
cwLogError(kInvalidArgRC,"No cfg. file was given.");
rc = cwLogError(kInvalidArgRC,"No cfg. file was given.");
goto errLabel;
}
else
{
const char* val = nullptr;
if((rc = objectFromFile(argv[1],cfg)) != kOkRC )
if((rc = objectFromFile(argv[1],app.cfg)) != kOkRC )
{
cwLogError(rc,"The file '%s'.",argv[1]);
rc = cwLogError(rc,"The file '%s'.",argv[1]);
goto errLabel;
}
if((rc = cfg->getv("param",val)) != kOkRC )
if((rc = app.cfg->getv("param", app.value,
"libcw", app.io_cfg)) != kOkRC )
{
cwLogError(kSyntaxErrorRC,"The 'param' cfg. field was not found.");
rc = cwLogError(kSyntaxErrorRC,"The 'param' cfg. field was not found.");
goto errLabel;
}
cwLogInfo("param=%s",cwStringNullGuard(val));
}
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"App. cfg. parse failed.");
return rc;
}
int main( int argc, char* argv[] )
{
rc_t rc = kOkRC;
app_t app = {};
cw::log::createGlobal();
cwLogInfo("Project template: args:%i", argc);
if((rc = _parse_cfg(app,argc,argv)) != kOkRC )
goto errLabel;
if((rc = create( app.ioH, app.io_cfg, _io_callback, &app, appIdMapA, appIdMapN ) ) != kOkRC )
{
rc = cwLogError(rc,"IO 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;
}
//io::uiReport(app.ioH);
// execute the io framework
while( !io::isShuttingDown(app.ioH))
{
// This call will block on the websocket handle
// for up to io_cfg->ui.websockTimeOutMs milliseconds
io::exec(app.ioH);
}
// stop the io framework
if((rc = io::stop(app.ioH)) != kOkRC )
{
rc = cwLogError(rc,"IO API stop failed.");
goto errLabel;
}
errLabel:
destroy(app.ioH);
if( app.cfg != nullptr )
app.cfg->free();
cw::log::destroyGlobal();
return 0;