Merge branch 'master' of gitea.larke.org:kevin/libcw

This commit is contained in:
kevin 2022-11-14 18:34:07 -05:00
commit 83884b5424
37 changed files with 2229 additions and 332 deletions

View File

@ -40,6 +40,8 @@ libcwSRC += src/libcw/cwUi.cpp src/libcw/cwUiTest.cpp
endif
libcwHDR += src/libcw/cwKeyboard.h
libcwSRC += src/libcw/cwKeyboard.cpp
libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h
libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp
@ -56,14 +58,17 @@ 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
if cwWEBSOCK
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 src/libcw/cwIoAudioMidi.cpp
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoMinTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoMinTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp
libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h
libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp
# libcwHDR += src/libcw/cwCmInterface.h src/libcw/cwScoreFollower.h
# libcwSRC += src/libcw/cwCmInterface.cpp src/libcw/cwScoreFollower.cpp
endif

View File

@ -2,6 +2,22 @@
# To Do
- Remove applications from the libcw folder and put them in their
own folders.
- The UI needs to be better documented. Start by giving clear names
to the various parts: Browser, UI Manager, UI Server, Application.
Maybe describe in Model,View,Controller terms?
- Document the meaning and way that id's and names/labels are used,
and intended to be used, and found by UI. As it is they are confusing.
- The UI app id map should be validated after the UI is created.
In otherwords the parent/child pairs shoud actually exists.
- Arrange the project layout so that all the UI based apps use the same ui.js.
Currently changes and improvements to one version of ui.js cannot be automatically
shared.
- The ui manageer should buffer the current valid value of a given control
so that the value can be accessed synchronously. This would prevent the application
@ -11,14 +27,19 @@ read by the app (e.g. getUiValue( appId, valueRef)) just prior to being used.
As it is the UI values that are on the interface cannot be accessed synchronously
instead the app is forced to notice all 'value' changes and store the last legal value.
- Add an ui::appIdToUuId() that returns the first matching appId, and then
optionally looks for duplicates as an error checking scheme.
- The ui eleA[] data structure should be changed to a tree
because the current expandable array allows empty slots which need to be checked
for whenever the list is iterated. It is also very inefficient to delete from the
eleA[] because an exhaustive search is required to find all the children of the
element to be deleted.
- Should a warning be issued by audioBuf functions which return a set of values:
muteFlags(),toneFlags(), gain( ... gainA) but where the size of the dest array
does not match the actual number of channesl?
- Document the meaning and way that id's and names/labels are used,
and intended to be used, and found by UI. As it is they are confusing.
- Any socket function which takes a IP/port address should have a version which also takes a sockaddr_in*.
@ -47,17 +68,7 @@ This is easy to reproduce by simply decreasing the size of the buffers in the pr
- numeric_convert() d_min is NOT zero, it's smallest positive number, this fails when src == 0.
min value is now set to zero.
- The UI app id map should be validated after the UI is created.
In otherwords the parent/child pairs shoud actually exists.
- Add an ui::appIdToUuId() that returns the first matching appId, and then
optionally looks for duplicates as an error checking scheme.
- The ui eleA[] data structure should be changed to a tree
because the current expandable array allows empty slots which need to be checked
for whenever the list is iterated. It is also very inefficient to delete from the
eleA[] because an exhaustive search is required to find all the children of the
element to be deleted.
- thread needs setters and getters for internal variables
@ -65,7 +76,7 @@ element to be deleted.
- cwAudioBuf.cpp - the ch->fn in update() does not have the correct memory fence.
- change file names to match object names
- Change file names to match object names
- Replace 24 bit read/write in cwAudioFile.cpp
@ -90,9 +101,9 @@ The 'jsUuId' is generated by the JS client when the UI element is created.
The 'uuId' is generated by the UI server when the JS client registers the control.
The 'appId' is assigned by the UI server when the JS client regsiters the control.
Client sends 'init' message.
Server sends 'create' messages.
Client sends 'register' messages.
Client sends 'init' message.
Server sends 'create' messages.
Client sends 'register' messages.
Server send' 'id_assign' messages.
# sockaddr_in reference
@ -115,10 +126,14 @@ struct in_addr {
# Development Setup
0)
```
sudo dnf install g++ fftw-devel alsa-lib-devel libubsan
```
1) Install libwebsockets.
```
sudo dnf install g++ openssl-devel cmake
sudo dnf install openssl-devel cmake
cd sdk
git clone https://libwebsockets.org/repo/libwebsockets
cd libwebsockets
@ -145,7 +160,6 @@ struct in_addr {
# Flow Notes:
- Add a version of var_register() that both registers and returns the value of the variable.
- When a variable has a variant with a numberic channel should the 'all' channel variant be removed?
@ -167,6 +181,7 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o
- Create a master cross-fader.
DONE: Add a version of var_register() that both registers and returns the value of the variable.
Flow Instance Creation:
-----------------------

110
cwCmInterface.cpp Normal file
View File

@ -0,0 +1,110 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwCmInterface.h"
#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmLinkedHeap.h"
#include "cmTime.h"
#include "cmMidi.h"
#include "cmSymTbl.h"
#include "cmScore.h"
#include "cmText.h"
#include "cmFileSys.h"
extern "C" {
void _cm_print_info( void* arg, const char* text )
{
cwLogInfo(text);
}
void _cm_print_error( void* arg, const char* text )
{
cwLogError(cw::kOpFailRC,text);
}
}
namespace cw
{
namespace cm
{
typedef struct cm_str
{
::cmCtx_t ctx;
} cm_t;
cm_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,cm_t>(h); }
rc_t _destroy( cm_t* p )
{
if( p != nullptr )
{
cmTsFinalize();
cmFsFinalize();
cmMdReport( kIgnoreNormalMmFl );
cmMdFinalize();
mem::release(p);
}
return kOkRC;
}
}
}
cw::rc_t cw::cm::create( handle_t& hRef )
{
rc_t rc = kOkRC;
cm_t* p = nullptr;
bool memDebugFl = 0; //cmDEBUG_FL;
unsigned memGuardByteCnt = memDebugFl ? 8 : 0;
unsigned memAlignByteCnt = 16;
unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
const cmChar_t* appTitle = "cwtest";
if((rc = destroy(hRef)) != kOkRC )
return rc;
p = mem::allocZ<cm_t>();
cmCtxSetup(&p->ctx,appTitle,_cm_print_info,_cm_print_error,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &p->ctx.rpt );
cmFsInitialize( &p->ctx, appTitle);
cmTsInitialize( &p->ctx );
return rc;
}
cw::rc_t cw::cm::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
cm_t* p = nullptr;
if( !hRef.isValid() )
return rc;
p = _handleToPtr(hRef);
if((rc = _destroy(p)) != kOkRC )
return rc;
hRef.clear();
return rc;
}
::cmCtx_t* cw::cm::context( handle_t h )
{
cm_t* p = _handleToPtr(h);
return &p->ctx;
}

19
cwCmInterface.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef cwCmInterface_h
#define cwCmInterface_h
namespace cw
{
namespace cm {
extern "C" { struct cmCtx_str; }
typedef handle< struct cm_str > handle_t;
rc_t create( handle_t& hRef );
rc_t destroy( handle_t& hRef );
::cmCtx_t* context( handle_t h );
}
}
#endif

View File

@ -46,7 +46,8 @@ namespace cw
kTestFailRC, // 27
kInvalidStateRC, // 28
kTypeMismatchRC, // 29
kBaseAppRC // 30
kNotImplementedRC, // 30
kBaseAppRC // 31
} cwRC_t;
typedef unsigned rc_t;

View File

@ -137,6 +137,7 @@ namespace cw
const char* type_str = nullptr;
unsigned type_flag = 0;
bool srcVarFl = false;
bool srcOptFl = false;
var_desc_t* vd = mem::allocZ<var_desc_t>();
vd->label = var_obj->pair_label();
@ -158,7 +159,9 @@ namespace cw
}
// get the variable description
if((rc = vd->cfg->getv_opt("srcFl", srcVarFl,"value",vd->val_cfg)) != kOkRC )
if((rc = vd->cfg->getv_opt("srcFl", srcVarFl,
"srcOptFl", srcOptFl,
"value",vd->val_cfg)) != kOkRC )
{
rc = cwLogError(rc,"Parsing optional fields failed on class:%s variable: '%s'.", cd->label, vd->label );
goto errLabel;
@ -168,9 +171,10 @@ namespace cw
vd->type |= type_flag;
if( srcVarFl )
{
vd->flags |= kSrcVarFl;
}
if( srcOptFl )
vd->flags |= kSrcOptVarFl;
vd->link = cd->varDescL;
cd->varDescL = vd;

View File

@ -564,7 +564,7 @@ namespace cw
// print a minutes counter
inst->durSmpN += src_abuf->frameN;
if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 )
printf("%5.1f min\n", inst->durSmpN/(src_abuf->srate*60));
printf("audio file out: %5.1f min\n", inst->durSmpN/(src_abuf->srate*60));
}
@ -982,35 +982,64 @@ namespace cw
namespace audio_merge
{
enum {
kIn0PId,
kIn1PId,
kGainPId,
kOutPId,
kInBasePId,
};
typedef struct
{
unsigned srcN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf0 = nullptr; //
const abuf_t* abuf1 = nullptr;
unsigned outChN = 0;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,
kIn0PId,"in0",abuf0,
kIn1PId,"in1",abuf1 )) != kOkRC )
unsigned frameN = 0;
srate_t srate = 0;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
for(unsigned i=0; 1; ++i)
{
goto errLabel;
const abuf_t* abuf = nullptr; //
char label[32];
snprintf(label,31,"in%i",i);
label[31] = 0;
// TODO: allow non-contiguous source labels
// the source labels must be contiguous
if( !var_has_value( ctx, label, kAnyChIdx ) )
break;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC )
{
goto errLabel;
}
if( i == 0 )
{
frameN = abuf->frameN;
srate = abuf->srate;
}
else
{
// TODO: check srate and frameN are same as first src
}
inst->srcN += 1;
outChN += abuf->chN;
}
assert( abuf0->frameN == abuf1->frameN );
outChN = abuf0->chN + abuf1->chN;
//outChN = abuf0->chN + abuf1->chN;
// register the gain
for(unsigned i=0; i<outChN; ++i)
@ -1018,14 +1047,20 @@ namespace cw
goto errLabel;
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN );
rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srate, outChN, frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{ return kOkRC; }
{
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
@ -1052,6 +1087,7 @@ namespace cw
return outChIdx;
}
/*
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
@ -1074,6 +1110,31 @@ namespace cw
assert( oChIdx == obuf->chN );
errLabel:
return rc;
}
*/
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* obuf = nullptr;
unsigned oChIdx = 0;
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
for(unsigned i=0; i<inst->srcN; ++i)
{
const abuf_t* ibuf = nullptr;
if((rc = var_get(ctx,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
oChIdx = _exec( ctx, ibuf, obuf, oChIdx );
}
errLabel:
return rc;
}
@ -1087,6 +1148,7 @@ namespace cw
};
}
//------------------------------------------------------------------------------------------------------------------
//

View File

@ -1013,9 +1013,10 @@ void cw::flow::class_dict_print( flow_t* p )
for(; vd!=nullptr; vd=vd->link)
{
const char* srcFlStr = vd->flags&kSrcVarFl ? "src" : " ";
const char* srcFlStr = vd->flags & kSrcVarFl ? "src" : " ";
const char* srcOptFlStr = vd->flags & kSrcOptVarFl ? "srcOpt" : " ";
printf(" %10s 0x%08x %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, cwStringNullGuard(vd->docText) );
printf(" %10s 0x%08x %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, cwStringNullGuard(vd->docText) );
}
}
}
@ -1162,6 +1163,17 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns
bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned chIdx )
{ return _var_find_on_label_and_ch(inst,label,chIdx) != nullptr; }
bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned chIdx )
{
variable_t* varPtr = nullptr;
rc_t rc;
if((rc = var_find( inst, label, chIdx, varPtr )) != kOkRC )
return false;
return varPtr->value != nullptr;
}
cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef )
{

View File

@ -115,7 +115,9 @@ namespace cw
typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var );
enum
{
kSrcVarFl = 0x01
kSrcVarFl = 0x01,
kSrcOptVarFl = 0x02
};
typedef struct class_members_str
@ -397,6 +399,7 @@ namespace cw
void _var_destroy( variable_t* var );
bool var_exists( instance_t* inst, const char* label, unsigned chIdx );
bool var_has_value( instance_t* inst, const char* label, unsigned chIdx );
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, const variable_t*& varRef );
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, variable_t*& varRef );

View File

@ -38,6 +38,14 @@ namespace cw
struct io_str;
typedef struct thread_str
{
unsigned id;
void* arg;
struct io_str* p;
struct thread_str* link;
} thread_t;
typedef struct timer_str
{
struct io_str* io;
@ -85,8 +93,7 @@ namespace cw
bool enableFl;
char* label;
unsigned sockA_index;
unsigned userId;
unsigned userId;
} socket_t;
typedef struct io_str
@ -102,6 +109,8 @@ namespace cw
object_t* cfg;
thread_t* threadL;
timer_t* timerA;
unsigned timerN;
@ -141,6 +150,36 @@ namespace cw
io_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,io_t>(h); }
//----------------------------------------------------------------------------------------------------------
//
// Thread
//
bool _threadFunc( void* arg )
{
thread_t* t = (thread_t*)arg;
thread_msg_t tm = { .id=t->id, .arg=t->arg };
msg_t m;
m.tid = kThreadTId;
m.u.thread = &tm;
t->p->cbFunc( t->p->cbArg, &m );
return true;
}
void _threadRelease( io_t* p )
{
thread_t* t0 = p->threadL;
for(; t0!=nullptr; t0=t0->link)
{
thread_t* t1 = t0->link;
mem::release(t0);
t0 = t1;
}
}
//----------------------------------------------------------------------------------------------------------
//
// Timer
@ -335,7 +374,10 @@ namespace cw
// get the serial port list node
if((cfg = c->find("serial")) == nullptr)
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'serial' configuration.");
{
cwLogWarning("No 'serial' configuration.");
return kOkRC;
}
// the serial header values
if((rc = cfg->getv("pollPeriodMs", pollPeriodMs,
@ -413,10 +455,11 @@ namespace cw
{
rc_t rc = kOkRC;
// the service is only started if at least one serial port is enabled
if( serialPort::portCount( serialPortSrv::serialHandle(p->serialPortSrvH) ) > 0 )
if((rc =serialPortSrv::start( p->serialPortSrvH )) != kOkRC )
rc = cwLogError(rc,"The serial port server start failed.");
if( p->serialPortSrvH.isValid() )
// the service is only started if at least one serial port is enabled
if( serialPort::portCount( serialPortSrv::serialHandle(p->serialPortSrvH) ) > 0 )
if((rc =serialPortSrv::start( p->serialPortSrvH )) != kOkRC )
rc = cwLogError(rc,"The serial port server start failed.");
return rc;
}
@ -461,9 +504,11 @@ namespace cw
// get the MIDI port cfg
if((cfg = c->find("midi")) == nullptr)
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'MIDI' configuration.");
{
cwLogWarning("No 'MIDI' configuration.");
return kOkRC;
}
if((rc = cfg->getv(
"parserBufByteN", parserBufByteN )) != kOkRC )
{
@ -586,8 +631,8 @@ namespace cw
// get the socket configuration node
if((node = cfg->find("socket")) == nullptr )
{
rc = cwLogError(kSyntaxErrorRC,"Unable to locate the 'socket' configuration node.");
goto errLabel;
cwLogWarning("No 'socket' configuration node.");
return kOkRC;
}
// get the required socket arguments
@ -1469,8 +1514,11 @@ namespace cw
// get the audio port node
if((node = cfg->find("audio")) == nullptr )
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'audio' configuration node.");
{
cwLogWarning("No 'audio' configuration node.");
return kOkRC;
}
// get the meterMs value
if((rc = node->getv("meterMs", p->audioMeterCbPeriodMs, "threadTimeOutMs", p->audioThreadTimeOutMs )) != kOkRC )
{
@ -1863,6 +1911,29 @@ void cw::io::report( handle_t h )
}
//----------------------------------------------------------------------------------------------------------
//
// Thread
//
cw::rc_t cw::io::threadCreate( handle_t h, unsigned id, void* arg )
{
rc_t rc = kOkRC;
io_t* p = _handleToPtr(h);
thread_t* t = mem::allocZ<thread_t>(1);
t->id = id;
t->arg = arg;
t->p = p;
t->link = p->threadL;
p->threadL = t;
if((rc = thread_mach::add( p->threadMachH, _threadFunc, t )) != kOkRC )
rc = cwLogError(rc,"Thread create failed.");
return rc;
}
//----------------------------------------------------------------------------------------------------------
//
// Timer

13
cwIo.h
View File

@ -27,6 +27,7 @@ namespace cw
enum
{
kThreadTId,
kTimerTId,
kSerialTId,
kMidiTId,
@ -37,6 +38,12 @@ namespace cw
kUiTId
};
typedef struct thread_msg_str
{
unsigned id;
void* arg;
} thread_msg_t;
typedef struct timer_msg_str
{
unsigned id;
@ -121,6 +128,7 @@ namespace cw
unsigned tid;
union
{
thread_msg_t* thread;
timer_msg_t* timer;
serial_msg_t* serial;
midi_msg_t* midi;
@ -151,6 +159,11 @@ namespace cw
bool isShuttingDown( handle_t h );
void report( handle_t h );
//----------------------------------------------------------------------------------------------------------
//
// Thread
//
rc_t threadCreate( handle_t h, unsigned id, void* arg );
//----------------------------------------------------------------------------------------------------------
//

View File

@ -35,9 +35,11 @@ namespace cw
kMidiThruCheckId,
kCurMidiEvtCntId,
kTotalMidiEvtCntId,
kMidiMuteCheckId,
kCurAudioSecsId,
kTotalAudioSecsId,
kAudioMuteCheckId,
kSaveBtnId,
kOpenBtnId,
@ -66,10 +68,11 @@ namespace cw
{ kPanelDivId, kMidiThruCheckId, "midiThruCheckId" },
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
{ kPanelDivId, kTotalMidiEvtCntId, "totalMidiEvtCntId" },
{ kPanelDivId, kMidiMuteCheckId, "midiMuteCheckId" },
{ kPanelDivId, kCurAudioSecsId, "curAudioSecsId" },
{ kPanelDivId, kTotalAudioSecsId, "totalAudioSecsId" },
{ kPanelDivId, kAudioMuteCheckId, "audioMuteCheckId" },
{ kPanelDivId, kSaveBtnId, "saveBtnId" },
{ kPanelDivId, kOpenBtnId, "openBtnId" },
@ -89,7 +92,10 @@ namespace cw
char* directory;
midi_record_play::handle_t mrpH;
audio_record_play::handle_t arpH;
audio_record_play::handle_t arpH;
const object_t* midi_play_record_cfg;
} app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg )
@ -99,7 +105,8 @@ namespace cw
if((rc = cfg->getv(
"record_dir", app->record_dir,
"record_folder", app->record_folder,
"record_fn_ext", app->record_fn_ext)) != kOkRC )
"record_fn_ext", app->record_fn_ext,
"midi_play_record", app->midi_play_record_cfg)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"Audio MIDI app configuration parse failed.");
}
@ -172,6 +179,15 @@ namespace cw
mem::release(fn);
}
if((fn = filesys::makeFn(dir,"midi","csv",nullptr)) != nullptr )
{
if((rc0 = midi_record_play::save_csv( app->mrpH, fn )) != kOkRC )
rc0 = cwLogError(rc0,"MIDI CSV file '%s' save failed.",fn);
mem::release(fn);
}
if((fn = filesys::makeFn(dir,"audio","wav",nullptr)) != nullptr )
{
@ -355,6 +371,7 @@ namespace cw
break;
case kReportBtnId:
report( app->mrpH );
break;
case kSaveBtnId:
@ -374,6 +391,14 @@ namespace cw
cwLogInfo("MIDI thru:%i",m.value->u.b);
_set_midi_thru_state(app, m.value->u.b);
break;
case kMidiMuteCheckId:
midi_record_play::set_mute_state(app->mrpH,m.value->u.b);
break;
case kAudioMuteCheckId:
audio_record_play::set_mute_state(app->arpH,m.value->u.b);
break;
case kStartBtnId:
_on_ui_start(app);
@ -518,7 +543,7 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg )
return rc;
// create the MIDI record-play object
if((rc = midi_record_play::create(app.mrpH,app.ioH,*cfg)) != kOkRC )
if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg)) != kOkRC )
{
rc = cwLogError(rc,"MIDI record-play object create failed.");
goto errLabel;

View File

@ -39,6 +39,8 @@ namespace cw
unsigned curFrameIdx;
bool recordFl;
bool startedFl;
bool mute_fl;
unsigned* audioInChMapA;
unsigned audioInChMapN;
@ -169,8 +171,11 @@ namespace cw
for(unsigned chIdx=0; chIdx<chCnt; ++chIdx)
{
unsigned srcChIdx = p->audioInChMapA == nullptr ? chIdx : p->audioInChMapA[chIdx];
memcpy(a->audioBuf + chIdx*asrc.dspFrameCnt, asrc.iBufArray[ srcChIdx ], asrc.dspFrameCnt * sizeof(sample_t));
if( srcChIdx >= asrc.iBufChCnt )
cwLogError(kInvalidArgRC,"Invalid input channel map index:%i >= %i.",srcChIdx,asrc.iBufChCnt);
else
memcpy(a->audioBuf + chIdx*asrc.dspFrameCnt, asrc.iBufArray[ srcChIdx ], asrc.dspFrameCnt * sizeof(sample_t));
}
a->chCnt = chCnt;
@ -197,31 +202,34 @@ namespace cw
void _audio_play( audio_record_play_t* p, io::audio_msg_t& adst )
{
unsigned adst_idx = 0;
while(adst_idx < adst.dspFrameCnt)
if( !p->mute_fl )
{
am_audio_t* a;
unsigned sample_offs = 0;
if((a = _am_audio_from_sample_index(p, p->curFrameIdx, sample_offs )) == nullptr )
break;
unsigned n = std::min(a->dspFrameCnt - sample_offs, adst.dspFrameCnt );
// TODO: Verify that this is correct - it looks like sample_offs should have to be incremented
for(unsigned i=0; i<a->chCnt; ++i)
while(adst_idx < adst.dspFrameCnt)
{
unsigned dstChIdx = p->audioOutChMapA != nullptr && i < p->audioOutChMapN ? p->audioOutChMapA[i] : i;
am_audio_t* a;
unsigned sample_offs = 0;
if((a = _am_audio_from_sample_index(p, p->curFrameIdx, sample_offs )) == nullptr )
break;
if( dstChIdx < adst.oBufChCnt )
memcpy( adst.oBufArray[ dstChIdx ] + adst_idx, a->audioBuf + sample_offs, n * sizeof(sample_t));
unsigned n = std::min(a->dspFrameCnt - sample_offs, adst.dspFrameCnt );
// TODO: Verify that this is correct - it looks like sample_offs should have to be incremented
for(unsigned i=0; i<a->chCnt; ++i)
{
unsigned dstChIdx = p->audioOutChMapA != nullptr && i < p->audioOutChMapN ? p->audioOutChMapA[i] : i;
if( dstChIdx < adst.oBufChCnt )
memcpy( adst.oBufArray[ dstChIdx ] + adst_idx, a->audioBuf + sample_offs, n * sizeof(sample_t));
}
p->curFrameIdx += n;
adst_idx += n;
}
p->curFrameIdx += n;
adst_idx += n;
}
// TODO: zero unused channels
if( adst_idx < adst.dspFrameCnt )
@ -415,9 +423,16 @@ namespace cw
else
{
if( m.oBufChCnt > 0 )
{
_audio_play(p,m);
}
}
}
else
{
for(unsigned i=0; i<m.oBufChCnt; ++i)
memset( m.oBufArray[i], 0, m.dspFrameCnt * sizeof(sample_t));
}
return rc;
}
@ -522,6 +537,22 @@ bool cw::audio_record_play::record_state( handle_t h )
return rc;
}
cw::rc_t cw::audio_record_play::set_mute_state( handle_t h, bool mute_fl )
{
rc_t rc = kOkRC;
audio_record_play_t* p = _handleToPtr(h);
p->mute_fl = true;
return rc;
}
bool cw::audio_record_play::mute_state( handle_t h )
{
rc_t rc = kOkRC;
audio_record_play_t* p = _handleToPtr(h);
return p->mute_fl;
return rc;
}
cw::rc_t cw::audio_record_play::save( handle_t h, const char* fn )
{
audio_record_play_t* p = _handleToPtr(h);

View File

@ -17,6 +17,8 @@ namespace cw
rc_t clear( handle_t h );
rc_t set_record_state( handle_t h, bool record_fl );
bool record_state( handle_t h );
rc_t set_mute_state( handle_t h, bool mute_fl );
bool mute_state( handle_t h );
rc_t save( handle_t h, const char* fn );
rc_t open( handle_t h, const char* fn );
double duration_seconds( handle_t h );

View File

@ -54,6 +54,16 @@ namespace cw
unsigned pedalUpHalfVelId;
unsigned pedalUpHalfVel;
unsigned velHistogram[ midi::kMidiVelCnt ];
bool force_damper_down_fl;
unsigned force_damper_down_threshold;
unsigned force_damper_down_velocity;
bool damper_dead_band_enable_fl;
unsigned damper_dead_band_min_value;
unsigned damper_dead_band_max_value;
} midi_device_t;
@ -89,9 +99,12 @@ namespace cw
bool startedFl;
bool recordFl;
bool muteFl;
bool thruFl;
bool logInFl; // log incoming message when not in 'record' mode.
bool logOutFl; // log outgoing messages
bool velHistogramEnableFl;
bool halfPedalFl;
unsigned halfPedalState;
@ -190,12 +203,27 @@ namespace cw
}
if((rc = ele->getv_opt( "vel_table", velTable,
"pedal", pedalRecd)) != kOkRC )
if((rc = ele->getv_opt(
"vel_table", velTable,
"pedal", pedalRecd,
"force_damper_down_fl",p->midiDevA[i].force_damper_down_fl,
"force_damper_down_threshold",p->midiDevA[i].force_damper_down_threshold,
"force_damper_down_velocity", p->midiDevA[i].force_damper_down_velocity,
"damper_dead_band_enable_fl", p->midiDevA[i].damper_dead_band_enable_fl,
"damper_dead_band_min_value", p->midiDevA[i].damper_dead_band_min_value,
"damper_dead_band_max_value", p->midiDevA[i].damper_dead_band_max_value)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"MIDI record play device optional argument parsing failed.");
goto errLabel;
}
cwLogInfo("Force Pedal: enabled:%i thresh:%i veloc:%i dead band: enable:%i min:%i max:%i",
p->midiDevA[i].force_damper_down_fl,
p->midiDevA[i].force_damper_down_threshold,
p->midiDevA[i].force_damper_down_velocity,
p->midiDevA[i].damper_dead_band_enable_fl,
p->midiDevA[i].damper_dead_band_min_value,
p->midiDevA[i].damper_dead_band_max_value );
p->midiDevA[i].midiOutDevLabel = mem::duplStr( midiOutDevLabel);
p->midiDevA[i].midiOutPortLabel = mem::duplStr( midiOutPortLabel);
@ -254,6 +282,9 @@ namespace cw
const am_midi_msg_t* _midi_store( midi_record_play_t* p, unsigned devIdx, unsigned portIdx, const time::spec_t& ts, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
am_midi_msg_t* am = nullptr;
//if( !midi::isPedal(status,d0) )
// printf("MIDI store: %i : ch:%i st:%i d0:%i d1:%i\n",p->iMsgArrayInIdx,ch,status,d0,d1);
// verify that space exists in the record buffer
if( p->iMsgArrayInIdx < p->iMsgArrayN )
@ -281,13 +312,14 @@ namespace cw
rc_t _event_callback( midi_record_play_t* p, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1, bool log_fl=true )
{
rc_t rc = kOkRC;
// if we have arrived at the stop time
bool after_stop_time_fl = !time::isZero(p->end_play_event_timestamp) && time::isGT(timestamp,p->end_play_event_timestamp);
bool after_all_off_fl = after_stop_time_fl && time::isGT(timestamp,p->all_off_timestamp);
bool is_note_on_fl = status==midi::kNoteOnMdId and d1 != 0;
bool supress_fl = is_note_on_fl && after_stop_time_fl;
bool is_damper_fl = status==midi::kCtlMdId and d0==midi::kSustainCtlMdId;
bool supress_fl = (is_note_on_fl && after_stop_time_fl) || p->muteFl;
bool is_pedal_fl = midi::isPedal( status, d0 );
if( after_all_off_fl )
{
@ -320,6 +352,19 @@ namespace cw
out_d1 = p->midiDevA[i].velTableArray[ d1 ];
}
// store the note-on velocity histogram data
if( p->velHistogramEnableFl && is_note_on_fl && out_d1 < midi::kMidiVelCnt )
p->midiDevA[i].velHistogram[ out_d1 ] += 1;
// if the damper pedal velocity is in the dead band then don't send it
if( p->midiDevA[i].damper_dead_band_enable_fl && is_pedal_fl && p->midiDevA[i].damper_dead_band_min_value <= out_d1 && out_d1 <= p->midiDevA[i].damper_dead_band_max_value )
out_d1 = 0;
// if the damper pedal velocity is over the 'forcing' threshold then force the damper down
if( p->midiDevA[i].force_damper_down_fl && is_damper_fl && out_d1>p->midiDevA[i].force_damper_down_threshold )
out_d1 = p->midiDevA[i].force_damper_down_velocity;
// map the pedal down velocity
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl )
{
@ -331,17 +376,21 @@ namespace cw
else
if( d1 == p->midiDevA[i].pedalDownHalfVelId )
out_d1 = p->midiDevA[i].pedalDownHalfVel;
else
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping.",d1);
else
{
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping. Remove the 'pedal' stanza from the MIDI device cfg to prevent pedal mapping.",d1);
}
}
}
if( !supress_fl )
if( !supress_fl )
{
io::midiDeviceSend( p->ioH, p->midiDevA[i].midiOutDevIdx, p->midiDevA[i].midiOutPortIdx, status + ch, d0, out_d1 );
}
}
if( !after_stop_time_fl and p->cb )
p->cb( p->cb_arg, id, timestamp, loc, ch, status, d0, d1 );
p->cb( p->cb_arg, kMidiEventActionId, id, timestamp, loc, ch, status, d0, d1 );
if( log_fl && p->logOutFl )
{
@ -355,6 +404,7 @@ namespace cw
return rc;
}
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am, bool log_fl=true )
{
return _event_callback( p, am->id, am->timestamp, am->loc, am->ch, am->status, am->d0, am->d1, log_fl );
@ -442,6 +492,53 @@ namespace cw
}
cw::rc_t _am_file_read_version_0( const char* fn, file::handle_t fH, am_midi_msg_t* amMsgArray, unsigned msgN )
{
// version 0 record type
typedef struct msg_str
{
unsigned dev_idx;
unsigned port_idx;
time::spec_t timestamp;
uint8_t ch;
uint8_t st;
uint8_t d0;
uint8_t d1;
unsigned microsecs;
} zero_msg_t;
cw::rc_t rc = kOkRC;
zero_msg_t* zMsgArray = mem::allocZ<zero_msg_t>(msgN);
unsigned fileByteN = msgN * sizeof(zero_msg_t);
if((rc = file::read(fH,zMsgArray,fileByteN)) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
for(unsigned i=0; i<msgN; ++i)
{
am_midi_msg_t* am = amMsgArray + i;
zero_msg_t* zm = zMsgArray + i;
am->devIdx = zm->dev_idx;
am->portIdx = zm->port_idx;
am->microsec = zm->microsecs;
am->id = i;
am->timestamp = zm->timestamp;
am->loc = i;
am->ch = zm->ch;
am->status = zm->st;
am->d0 = zm->d0;
am->d1 = zm->d1;
}
errLabel:
mem::release(zMsgArray);
return rc;
}
// Read the am_midi_msg_t records from a file written by _midi_write()
// If msgArrayCntRef==0 and msgArrayRef==NULL then an array will be allocated and it is up
// to the caller to release it, otherwise the msgArrayCntRef should be set to the count
@ -450,44 +547,95 @@ namespace cw
// msgArrayCntRef records will be returned.
cw::rc_t _am_file_read( const char* fn, unsigned& msgArrayCntRef, am_midi_msg_t*& msgArrayRef )
{
rc_t rc = kOkRC;
unsigned n = 0;
rc_t rc = kOkRC;
unsigned recordN = 0; // count of records in the ifle
unsigned fileByteN = 0; // count of bytes in the file
int version = 0; // version id (always a negative number)
bool alloc_fl = false;
bool print_fl = false;
file::handle_t fH;
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
{
rc = cwLogError(kOpenFailRC,"Unable to locate the AM file: '%s'.", fn );
goto errLabel;
}
if((rc = file::read(fH,n)) != kOkRC )
// read the first word - which is either a version id or the count of records in the file
// if this is a version 0 file
if((rc = file::read(fH,version)) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
rc = cwLogError(kReadFailRC,"Version read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
// if the version is greater than 0 then this is a version 0 file (and 'version' holds the record count.
if( version > 0 )
{
recordN = (unsigned)version;
version = 0;
}
else // otherwise the second word in the file holds the size of the file
{
if((rc = file::read(fH,recordN)) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
}
// if the output msg array was not allocated - then allocate it here
if( msgArrayCntRef == 0 || msgArrayRef == nullptr )
{
msgArrayRef = mem::allocZ<am_midi_msg_t>(n);
alloc_fl = true;
msgArrayRef = mem::allocZ<am_midi_msg_t>(recordN);
}
else // if the msg array was allocated but is too small - then decrease the count of records to be read from the file
{
if( recordN > msgArrayCntRef )
{
cwLogWarning("The count of message in Audio-MIDI file '%s' reduced from %i to %i.", fn, recordN, msgArrayCntRef );
recordN = msgArrayCntRef;
}
}
if( version == 0 )
{
// read the version 0 file into a temporary buffer then translate to am_midi_msg_t records
if((rc = _am_file_read_version_0( fn, fH, msgArrayRef, recordN )) != kOkRC )
goto errLabel;
}
else
{
if( n > msgArrayCntRef )
fileByteN = recordN * sizeof(am_midi_msg_t);
if((rc = file::read(fH,msgArrayRef,fileByteN)) != kOkRC )
{
cwLogWarning("The count of message in Audio-MIDI file '%s' reduced from %i to %i.", fn, n, msgArrayCntRef );
n = msgArrayCntRef;
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
}
if( print_fl )
{
for(unsigned i=0; i<recordN; ++i)
{
am_midi_msg_t* m = msgArrayRef;
double dt = time::elapsedSecs( m[0].timestamp, m[i].timestamp );
printf("%4i %4i : %6.2f %2x %2x %2x %2x : %6.2f\n", m[i].devIdx, m[i].portIdx, dt, m[i].ch, m[i].status, m[i].d0, m[i].d1, m[i].microsec/(1000.0*1000.0) );
}
}
if((rc = file::read(fH,msgArrayRef,n*sizeof(am_midi_msg_t))) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
msgArrayCntRef = n;
msgArrayCntRef = recordN;
errLabel:
if( rc != kOkRC and alloc_fl )
{
mem::release(msgArrayRef);
msgArrayRef = nullptr;
msgArrayCntRef = 0;
}
return rc;
}
@ -497,14 +645,21 @@ namespace cw
{
rc_t rc = kOkRC;
unsigned n = 0;
int version;
file::handle_t fH;
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,version)) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Version read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
if((rc = file::read(fH,n)) != kOkRC )
{
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
@ -539,7 +694,10 @@ namespace cw
{
rc_t rc = kOkRC;
file::handle_t fH;
// NOTE: version must be a small negative number to differentiate from file version that
// whose first word is the count of records in the file, rather than the version number
int version = -1;
if( p->iMsgArrayInIdx == 0 )
{
cwLogWarning("Nothing to write.");
@ -553,6 +711,13 @@ namespace cw
goto errLabel;
}
// write the file version
if((rc = write(fH,version)) != kOkRC )
{
rc = cwLogError(kWriteFailRC,"Version write to '%s' failed.",cwStringNullGuard(fn));
goto errLabel;
}
// write the file header
if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC )
{
@ -696,13 +861,67 @@ namespace cw
void _report_midi( midi_record_play_t* p )
{
printf("omsg cnt:%i\n",p->msgArrayInIdx);
for(unsigned i=0; i<p->msgArrayInIdx; ++i)
{
am_midi_msg_t* mm = p->msgArray + i;
_print_midi_msg(mm);
}
printf("imsg cnt:%i\n",p->iMsgArrayInIdx);
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
{
am_midi_msg_t* mm = p->iMsgArray + i;
_print_midi_msg(mm);
}
}
rc_t _write_vel_histogram( midi_record_play_t* p )
{
const char* fname = "/home/kevin/temp/vel_histogram.txt";
rc_t rc = kOkRC;
if( !p->velHistogramEnableFl )
return rc;
file::handle_t h;
if((rc = file::open(h,fname,file::kWriteFl)) != kOkRC )
return rc;
for(unsigned i=0; i<p->midiDevN; ++i)
if(p->midiDevA[i].enableFl )
{
for(unsigned j=0; j<midi::kMidiVelCnt; ++j)
if((rc = file::printf(h,"%i,",p->midiDevA[i].velHistogram[j])) != kOkRC )
{
rc = cwLogError(rc,"Histogram output file (%s) write failed.",fname);
goto errLabel;
}
file::printf(h,"\n");
}
errLabel:
file::close(h);
return rc;
}
// Fill the play buffer (msgArray) from the record buffer (iMsgArray)
void _iMsgArray_to_msgArray(midi_record_play_t* p)
{
if( p->msgArrayN < p->iMsgArrayN)
{
mem::resize(p->msgArray,p->iMsgArrayN,mem::kZeroAllFl);
p->msgArrayN = p->iMsgArrayN;
}
p->msgArrayOutIdx = 0;
p->msgArrayInIdx = p->iMsgArrayInIdx;
memcpy(p->msgArray,p->iMsgArray,p->iMsgArrayInIdx*sizeof(am_midi_msg_t));
}
rc_t _stop( midi_record_play_t* p )
{
rc_t rc = kOkRC;
@ -712,6 +931,8 @@ namespace cw
time::spec_t t1;
time::get(t1);
_write_vel_histogram( p );
// if we were recording
if( p->recordFl )
{
@ -719,9 +940,12 @@ namespace cw
// set the 'microsec' value for each MIDI msg as an offset from the first message[]
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
{
p->msgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
p->iMsgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
}
// copy the recorded messages from the input buffer to the output buffer
_iMsgArray_to_msgArray(p);
cwLogInfo("MIDI messages recorded: %i",p->msgArrayInIdx );
}
@ -739,6 +963,9 @@ namespace cw
}
if( p->cb != nullptr )
p->cb( p->cb_arg, kPlayerStoppedActionId, kInvalidId, t1, kInvalidId, 0, 0, 0, 0 );
return rc;
}
@ -753,12 +980,13 @@ namespace cw
// if this is a sys-ex msg
if( pkt->msgArray == NULL )
{
cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
}
else // this is a triple
{
//if( !midi::isPedal(pkt->msgArray[j].status,pkt->msgArray[j].d0) )
//printf("IN: 0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
// printf("IN: 0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
if( (p->recordFl || p->logInFl) && p->startedFl )
{
@ -922,6 +1150,12 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
midi_record_play_t* p = _handleToPtr(h);
p->startedFl = true;
if( p->velHistogramEnableFl )
for(unsigned i=0; i<p->midiDevN; ++i)
if(p->midiDevA[i].enableFl )
memset(p->midiDevA[i].velHistogram,0,sizeof(p->midiDevA[i].velHistogram));
// set the end play time
if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) )
time::setZero(p->end_play_event_timestamp);
@ -999,6 +1233,21 @@ bool cw::midi_record_play::record_state( handle_t h )
return p->recordFl;
}
cw::rc_t cw::midi_record_play::set_mute_state( handle_t h, bool mute_fl )
{
rc_t rc = kOkRC;
midi_record_play_t* p = _handleToPtr(h);
p->muteFl = mute_fl;
return rc;
}
bool cw::midi_record_play::mute_state( handle_t h )
{
midi_record_play_t* p = _handleToPtr(h);
return p->muteFl;
}
cw::rc_t cw::midi_record_play::set_thru_state( handle_t h, bool thru_fl )
{
rc_t rc = kOkRC;
@ -1085,7 +1334,7 @@ cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp )
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl, 0 );
cwLogInfo("damper: %s.", damp_down_fl ? "down" : "up");
//cwLogInfo("damper: %s.", damp_down_fl ? "down" : "up");
break;
}
@ -1198,6 +1447,20 @@ void cw::midi_record_play::enable_device( handle_t h, unsigned devIdx, bool enab
}
}
cw::rc_t cw::midi_record_play::send_midi_msg( handle_t h, unsigned devIdx, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
midi_record_play_t* p = _handleToPtr(h);
if( devIdx >= p->midiDevN )
return cwLogError(kInvalidArgRC,"The MIDI record-play device index '%i' is invalid.",devIdx );
if( p->midiDevA[devIdx].enableFl )
io::midiDeviceSend( p->ioH, p->midiDevA[devIdx].midiOutDevIdx, p->midiDevA[devIdx].midiOutPortIdx, status + ch, d0, d1 );
return kOkRC;
}
void cw::midi_record_play::half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs )
{
midi_record_play_t* p = _handleToPtr(h);
@ -1216,6 +1479,12 @@ cw::rc_t cw::midi_record_play::am_to_midi_file( const char* am_filename, const c
unsigned msgArrayCnt = 0;
am_midi_msg_t* msgArray = nullptr;
if(!filesys::isFile(am_filename))
{
cwLogError(kOpenFailRC,"The AM file '%s' does not exist.",am_filename);
goto errLabel;
}
if((rc = _am_file_read( am_filename, msgArrayCnt, msgArray )) != kOkRC )
{
rc = cwLogError(rc,"Unable to read AM file '%s'.", cwStringNullGuard(am_filename));
@ -1241,12 +1510,21 @@ cw::rc_t cw::midi_record_play::am_to_midi_dir( const char* inDir )
filesys::dirEntry_t* dirEntryArray = nullptr;
unsigned dirEntryCnt = 0;
if(( dirEntryArray = dirEntries( inDir, filesys::kDirFsFl, &dirEntryCnt )) == nullptr )
if(( dirEntryArray = filesys::dirEntries( inDir, filesys::kDirFsFl | filesys::kFullPathFsFl, &dirEntryCnt )) == nullptr )
goto errLabel;
for(unsigned i=0; i<dirEntryCnt; ++i)
for(unsigned i=0; i<dirEntryCnt and rc==kOkRC; ++i)
{
printf("0x%x %s\n", dirEntryArray[i].flags, dirEntryArray[i].name);
char* am_fn = filesys::makeFn( dirEntryArray[i].name, "midi", "am", NULL);
char* midi_fn = filesys::makeFn( dirEntryArray[i].name, "midi", "mid", NULL);
cwLogInfo("0x%x AM:%s MIDI:%s", dirEntryArray[i].flags, dirEntryArray[i].name, midi_fn);
rc = am_to_midi_file( am_fn, midi_fn );
mem::release(am_fn);
mem::release(midi_fn);
}
errLabel:
@ -1284,3 +1562,9 @@ cw::rc_t cw::midi_record_play::am_to_midi_file( const object_t* cfg )
return rc;
}
void cw::midi_record_play::report( handle_t h )
{
midi_record_play_t* p = _handleToPtr(h);
_report_midi(p);
}

View File

@ -19,8 +19,13 @@ namespace cw
uint8_t d1;
} midi_msg_t;
enum {
kMidiEventActionId,
kPlayerStoppedActionId
};
typedef void (*event_callback_t)( void* arg, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
typedef void (*event_callback_t)( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, event_callback_t cb=nullptr, void* cb_arg=nullptr );
@ -36,6 +41,9 @@ namespace cw
rc_t set_record_state( handle_t h, bool record_fl );
bool record_state( handle_t h );
rc_t set_mute_state( handle_t h, bool record_fl );
bool mute_state( handle_t h );
rc_t set_thru_state( handle_t h, bool record_thru );
bool thru_state( handle_t h );
@ -58,6 +66,7 @@ namespace cw
unsigned device_count( handle_t h );
bool is_device_enabled( handle_t h, unsigned devIdx );
void enable_device( handle_t h, unsigned devIdx, bool enableFl );
rc_t send_midi_msg( handle_t h, unsigned devIdx, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
void half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs );
@ -65,6 +74,8 @@ namespace cw
rc_t am_to_midi_file( const char* am_filename, const char* midi_filename );
rc_t am_to_midi_dir( const char* inDir );
rc_t am_to_midi_file( const object_t* cfg );
void report( handle_t h );
}
}

134
cwIoMinTest.cpp Normal file
View File

@ -0,0 +1,134 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwKeyboard.h"
#include "cwMem.h"
#include "cwObject.h"
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwMidi.h"
#include "cwUiDecls.h"
#include "cwIo.h"
#include "cwIoMinTest.h"
namespace cw
{
enum
{
kThread0Id,
kThread1Id
};
typedef struct app_str
{
unsigned n0;
unsigned n1;
io::handle_t ioH;
} app_t;
void minTestThreadCb( app_t* app, const io::thread_msg_t* tm )
{
switch( tm->id )
{
case kThread0Id:
app->n0 += 1;
break;
case kThread1Id:
app->n1 += 1;
break;
}
}
// The main application callback
rc_t minTestCb( void* arg, const io::msg_t* m )
{
rc_t rc = kOkRC;
app_t* app = reinterpret_cast<app_t*>(arg);
switch( m->tid )
{
case io::kThreadTId:
minTestThreadCb( app, m->u.thread );
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:
break;
default:
assert(0);
}
return rc;
}
}
cw::rc_t cw::min_test( const object_t* cfg )
{
rc_t rc;
app_t app = {};
// create the io framework instance
if((rc = create(app.ioH,cfg,minTestCb,&app)) != kOkRC )
return rc;
if((rc = threadCreate( app.ioH, kThread0Id, &app )) != kOkRC )
{
rc = cwLogError(rc,"Thread 0 create failed.");
goto errLabel;
}
if((rc = threadCreate( app.ioH, kThread1Id, &app )) != kOkRC )
{
rc = cwLogError(rc,"Thread 1 create failed.");
goto errLabel;
}
// start the io framework instance
if((rc = start(app.ioH)) != kOkRC )
{
rc = cwLogError(rc,"Test app start failed.");
goto errLabel;
}
printf("<enter> to quit.\n");
// execuite the io framework
while( !isShuttingDown(app.ioH))
{
exec(app.ioH);
sleepMs(500);
if( isKeyWaiting() )
break;
printf("%i %i\n",app.n0,app.n1);
}
errLabel:
destroy(app.ioH);
printf("ioMinTest Done.\n");
return rc;
}

11
cwIoMinTest.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef cwIoMinTest_H
#define cwIoMinTest_H
namespace cw
{
rc_t min_test( const object_t* cfg);
}
#endif

View File

@ -40,6 +40,7 @@ namespace cw
kStartBtnId,
kStopBtnId,
kLiveCheckId,
kPrintMidiCheckId,
kPianoMidiCheckId,
@ -69,13 +70,15 @@ namespace cw
kStatusId,
/*
kHalfPedalPedalVel,
kHalfPedalDelayMs,
kHalfPedalPitch,
kHalfPedalVel,
kHalfPedalDurMs,
kHalfPedalDnDelayMs,
*/
kLogId,
kFragListId,
@ -85,6 +88,7 @@ namespace cw
kFragPresetRowId,
kFragPresetSelId,
kFragPresetSeqSelId,
kFragPresetOrderId,
kFragInGainId,
@ -94,14 +98,16 @@ namespace cw
kFragBegPlayLocId,
kFragEndPlayLocId,
kFragPlayBtnId,
kFragPlaySeqBtnId,
kFragPlayAllBtnId,
kFragNoteId
};
enum
{
kPiano_MRP_DevIdx = 0,
kSampler_MRP_DevIdx = 1
kPiano_MRP_DevIdx = 1,
kSampler_MRP_DevIdx = 0
};
enum
@ -121,6 +127,7 @@ namespace cw
{ kPanelDivId, kStartBtnId, "startBtnId" },
{ kPanelDivId, kStopBtnId, "stopBtnId" },
{ kPanelDivId, kLiveCheckId, "liveCheckId" },
{ kPanelDivId, kPrintMidiCheckId, "printMidiCheckId" },
{ kPanelDivId, kPianoMidiCheckId, "pianoMidiCheckId" },
@ -148,13 +155,14 @@ namespace cw
{ kPanelDivId, kInsertBtnId, "insertBtnId" },
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" },
/*
{ kPanelDivId, kHalfPedalPedalVel, "halfPedalPedalVelId" },
{ kPanelDivId, kHalfPedalDelayMs, "halfPedalDelayMsId" },
{ kPanelDivId, kHalfPedalPitch, "halfPedalPitchId" },
{ kPanelDivId, kHalfPedalVel, "halfPedalVelId" },
{ kPanelDivId, kHalfPedalDurMs, "halfPedalDurMsId" },
{ kPanelDivId, kHalfPedalDnDelayMs, "halfPedalDnDelayMsId" },
*/
{ kPanelDivId, kStatusId, "statusId" },
{ kPanelDivId, kLogId, "logId" },
@ -171,7 +179,9 @@ namespace cw
{ kFragPanelId, kFragBegPlayLocId, "fragBegPlayLocId" },
{ kFragPanelId, kFragEndPlayLocId, "fragEndPlayLocId" },
{ kFragPanelId, kFragPlayBtnId, "fragPlayBtnId" },
{ kFragPanelId, kFragNoteId, "fragNoteId" },
{ kFragPanelId, kFragPlaySeqBtnId, "fragPlaySeqBtnId" },
{ kFragPanelId, kFragPlayAllBtnId, "fragPlayAllBtnId" },
{ kFragPanelId, kFragNoteId, "fragNoteId" },
};
@ -201,6 +211,7 @@ namespace cw
const object_t* midi_play_record_cfg;
const object_t* frag_panel_cfg;
const object_t* presets_cfg;
object_t* flow_proc_dict;
const object_t* flow_cfg;
midi_record_play::handle_t mrpH;
@ -210,15 +221,15 @@ namespace cw
unsigned locMapN;
unsigned insertLoc; // last valid insert location id received from the GUI
unsigned minLoc;
unsigned maxLoc;
unsigned beg_play_loc;
unsigned end_play_loc;
time::spec_t beg_play_timestamp;
time::spec_t end_play_timestamp;
unsigned beg_play_loc;
unsigned end_play_loc;
preset_sel::handle_t psH;
io_flow::handle_t ioFlowH;
@ -236,11 +247,21 @@ namespace cw
unsigned hpVel;
unsigned hpDurMs;
unsigned hpDnDelayMs;
bool seqActiveFl; // true if the sequence is currently active (set by 'Play Seq' btn)
unsigned seqStartedFl; // set by the first seq idle callback
unsigned seqFragId; //
unsigned seqPresetIdx; //
bool useLiveMidiFl;
} app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
{
rc_t rc = kOkRC;
const char* flow_proc_dict_fn = nullptr;
if((rc = cfg->getv( "params", params_cfgRef,
"flow", app->flow_cfg)) != kOkRC )
@ -253,11 +274,14 @@ namespace cw
"record_fn", app->record_fn,
"record_fn_ext", app->record_fn_ext,
"score_fn", app->scoreFn,
"flow_proc_dict_fn",flow_proc_dict_fn,
"midi_play_record", app->midi_play_record_cfg,
"frag_panel", app->frag_panel_cfg,
"presets", app->presets_cfg,
"crossFadeSrate", app->crossFadeSrate,
"crossFadeCount", app->crossFadeCnt)) != kOkRC )
"crossFadeCount", app->crossFadeCnt,
"beg_play_loc", app->beg_play_loc,
"end_play_loc", app->end_play_loc)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
}
@ -273,6 +297,12 @@ namespace cw
rc = cwLogError(kInvalidArgRC,"The record directory path is invalid.");
goto errLabel;
}
if((rc = objectFromFile( flow_proc_dict_fn, app->flow_proc_dict )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"The flow proc file '%s' parse failed.",app->flow_proc_dict);
goto errLabel;
}
// verify that the output directory exists
if((rc = filesys::isDir(app->record_dir)) != kOkRC )
@ -321,6 +351,9 @@ namespace cw
rc_t _free( app_t& app )
{
if( app.flow_proc_dict != nullptr )
app.flow_proc_dict->free();
mem::release((char*&)app.record_dir);
mem::release((char*&)app.scoreFn);
preset_sel::destroy(app.psH);
@ -331,25 +364,35 @@ namespace cw
return kOkRC;
}
rc_t _apply_preset( app_t* app, const time::spec_t& ts, const preset_sel::frag_t* frag=nullptr )
rc_t _apply_preset( app_t* app, const time::spec_t& ts, unsigned loc, const preset_sel::frag_t* frag=nullptr )
{
// if frag is NULL this is the beginning of a play session
if( frag == nullptr )
preset_sel::track_timestamp( app->psH, ts, frag);
{
preset_sel::track_loc_reset(app->psH);
//preset_sel::track_timestamp( app->psH, ts, frag);
preset_sel::track_loc( app->psH, loc, frag);
}
if( frag == nullptr )
cwLogInfo("No preset fragment was found for the requested timestamp.");
else
{
unsigned preset_idx;
if((preset_idx = fragment_play_preset_index(frag)) == kInvalidIdx )
// if the preset sequence player is active then apply the next selected seq. preset
// otherwise select the next primary preset for ths fragment
unsigned seq_idx_n = app->seqActiveFl ? app->seqPresetIdx : kInvalidIdx;
// get the preset index to play for this fragment
if((preset_idx = fragment_play_preset_index(frag,seq_idx_n)) == kInvalidIdx )
cwLogInfo("No preset has been assigned to the fragment at end loc. '%i'.",frag->endLoc );
else
{
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx);
cwLogInfo("Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
_set_status(app,"Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
if( preset_label != nullptr )
{
@ -395,40 +438,99 @@ namespace cw
return rc;
}
void _midi_play_callback( void* arg, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
void _midi_play_callback( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
app_t* app = (app_t*)arg;
if( app->printMidiFl )
{
const unsigned buf_byte_cnt = 256;
char buf[ buf_byte_cnt ];
switch( actionId )
{
case midi_record_play::kPlayerStoppedActionId:
app->seqStartedFl=false;
_set_status(app,"Done");
break;
// if this event is not in the score
if( id == kInvalidId )
{
// TODO: print this out in the same format as event_to_string()
snprintf(buf,buf_byte_cnt,"ch:%i status:0x%02x d0:%i d1:%i",ch,status,d0,d1);
}
else
score::event_to_string( app->scoreH, id, buf, buf_byte_cnt );
printf("%s\n",buf);
}
if( midi_record_play::is_started(app->mrpH) )
case midi_record_play::kMidiEventActionId:
{
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 );
if( f != nullptr )
_do_select_frag( app, f->guiUuId );
if( app->printMidiFl )
{
const unsigned buf_byte_cnt = 256;
char buf[ buf_byte_cnt ];
// if this event is not in the score
if( id == kInvalidId )
{
// TODO: print this out in the same format as event_to_string()
snprintf(buf,buf_byte_cnt,"ch:%i status:0x%02x d0:%i d1:%i",ch,status,d0,d1);
}
else
score::event_to_string( app->scoreH, id, buf, buf_byte_cnt );
printf("%s\n",buf);
}
if( midi_record_play::is_started(app->mrpH) )
{
const preset_sel::frag_t* f = nullptr;
//if( preset_sel::track_timestamp( app->psH, timestamp, f ) )
// ZERO SHOULD BE A VALID LOC VALUE - MAKE -1 THE INVALID LOC VALUE
if( loc != 0 )
{
if( preset_sel::track_loc( app->psH, loc, f ) )
{
//printf("NEW FRAG: id:%i loc:%i\n", f->fragId, f->endLoc );
_apply_preset( app, timestamp, loc, f );
if( f != nullptr )
_do_select_frag( app, f->guiUuId );
}
}
}
break;
}
}
}
rc_t _on_live_midi( app_t* app, const io::msg_t& msg )
{
rc_t rc = kOkRC;
if( msg.u.midi != nullptr )
{
const io::midi_msg_t& m = *msg.u.midi;
const midi::packet_t* pkt = m.pkt;
// for each midi msg
for(unsigned j=0; j<pkt->msgCnt; ++j)
{
// if this is a sys-ex msg
if( pkt->msgArray == NULL )
{
cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
}
else // this is a triple
{
midi::msg_t* mm = pkt->msgArray + j;
time::spec_t timestamp;
unsigned id = kInvalidId;
unsigned loc = app->beg_play_loc;
time::get(timestamp);
if( midi::isChStatus(mm->status) )
{
if(midi_record_play::send_midi_msg( app->mrpH, kSampler_MRP_DevIdx, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 ) == kOkRC )
_midi_play_callback( app, midi_record_play::kMidiEventActionId, id, timestamp, loc, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 );
}
}
}
}
return rc;
}
// Find the closest locMap equal to or after 'loc'
loc_map_t* _find_loc( app_t* app, unsigned loc )
{
@ -468,6 +570,7 @@ namespace cw
bool rewindFl = true;
loc_map_t* begMap = nullptr;
loc_map_t* endMap = nullptr;
unsigned cur_loc = 0;
// if the player is already playing then stop it
if( midi_record_play::is_started(app->mrpH) )
@ -476,7 +579,7 @@ namespace cw
goto errLabel;
}
midi_record_play::half_pedal_params( app->mrpH, app->hpDelayMs, app->hpPitch, app->hpVel, app->hpPedalVel, app->hpDurMs, app->hpDnDelayMs );
//midi_record_play::half_pedal_params( app->mrpH, app->hpDelayMs, app->hpPitch, app->hpVel, app->hpPedalVel, app->hpDurMs, app->hpDnDelayMs );
if((begMap = _find_loc(app,begLoc)) == nullptr )
@ -495,7 +598,7 @@ namespace cw
app->end_play_timestamp = endMap->timestamp;
if( !time::isZero(app->beg_play_timestamp) )
if( !time::isZero(app->beg_play_timestamp) && !app->useLiveMidiFl )
{
// seek the player to the requested loc
if((rc = midi_record_play::seek( app->mrpH, app->beg_play_timestamp )) != kOkRC )
@ -507,22 +610,27 @@ namespace cw
}
// apply the preset which is active at the start time
if((rc = _apply_preset( app, app->beg_play_timestamp )) != kOkRC )
if((rc = _apply_preset( app, app->beg_play_timestamp, begMap->loc )) != 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 )
if( !app->useLiveMidiFl )
{
rc = cwLogError(rc,"MIDI start failed.");
goto errLabel;
if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC )
{
rc = cwLogError(rc,"MIDI start failed.");
goto errLabel;
}
if((cur_loc = midi_record_play::event_loc(app->mrpH)) > 0 )
{
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
}
}
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
errLabel:
return rc;
}
@ -552,6 +660,45 @@ namespace cw
}
rc_t _do_seq_play_fragment( app_t* app, unsigned fragId )
{
rc_t rc = kOkRC;
if( app->seqActiveFl )
{
app->seqActiveFl = false;
}
else
{
app->seqFragId = fragId;
app->seqPresetIdx = 0;
app->seqStartedFl = true;
app->seqActiveFl = true;
}
// Note that if the MIDI player is already playing
// calling '_do_play_fragment()' here will stop the player
_do_play_fragment( app, app->seqFragId );
return rc;
}
rc_t _do_seq_exec( app_t* app )
{
rc_t rc = kOkRC;
// if the seq player is active but currently stopped
if( app->seqActiveFl && app->seqStartedFl==false)
{
app->seqPresetIdx += 1;
app->seqStartedFl = app->seqPresetIdx < preset_sel::fragment_seq_count( app->psH, app->seqFragId );
app->seqActiveFl = app->seqStartedFl;
if( app->seqStartedFl )
_do_play_fragment( app, app->seqFragId );
}
return rc;
}
void _update_event_ui( app_t* app )
{
@ -637,8 +784,9 @@ namespace cw
// Update each fragment preset control UI by getting it's current value from the fragment data record
for(unsigned preset_idx=0; preset_idx<preset_sel::preset_count(app->psH); ++preset_idx)
{
_update_frag_ui( app, fragId, preset_sel::kPresetSelectVarId, preset_idx, fragPresetRowUuId, kFragPresetSelId, preset_idx, bValue );
_update_frag_ui( app, fragId, preset_sel::kPresetOrderVarId, preset_idx, fragPresetRowUuId, kFragPresetOrderId, preset_idx, uValue );
_update_frag_ui( app, fragId, preset_sel::kPresetSelectVarId, preset_idx, fragPresetRowUuId, kFragPresetSelId, preset_idx, bValue );
_update_frag_ui( app, fragId, preset_sel::kPresetOrderVarId, preset_idx, fragPresetRowUuId, kFragPresetOrderId, preset_idx, uValue );
_update_frag_ui( app, fragId, preset_sel::kPresetSeqSelectVarId,preset_idx, fragPresetRowUuId, kFragPresetSeqSelId, preset_idx, bValue );
}
}
@ -666,7 +814,6 @@ namespace cw
unsigned fragPanelUuId = kInvalidId;
get_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId );
_update_frag_ui( app, fragId, preset_sel::kBegLocVarId, kInvalidId, fragPanelUuId, kFragBegLocId, uiChanId, uValue );
_update_frag_ui( app, fragId, preset_sel::kEndLocVarId, kInvalidId, fragPanelUuId, kFragEndLocId, uiChanId, uValue );
@ -705,20 +852,28 @@ namespace cw
void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, const char*, unsigned ){}
void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, unsigned begPlayLoc, unsigned endPlayLoc )
{
bool enableFl = begPlayLoc < endPlayLoc;
unsigned fragUuId = kInvalidId;
unsigned fragPlayBtnUuId = kInvalidId;
bool enableFl = begPlayLoc < endPlayLoc;
unsigned fragUuId = kInvalidId;
if((fragUuId = frag_to_gui_id( app->psH, blob->fragId )) != kInvalidId )
if((fragPlayBtnUuId = uiFindElementUuId( app->ioH, fragUuId, kFragPlayBtnId, blob->presetId )) != kInvalidId )
{
unsigned btnIdA[] = { kFragPlayBtnId, kFragPlaySeqBtnId, kFragPlayAllBtnId };
unsigned btnIdN = sizeof(btnIdA)/sizeof(btnIdA[0]);
for(unsigned i=0; i<btnIdN; ++i)
{
uiSetEnable( app->ioH, fragPlayBtnUuId, enableFl );
if( enableFl )
_clear_status(app);
else
_set_status(app,"Invalid fragment play range.");
unsigned btnUuId;
if((btnUuId = uiFindElementUuId( app->ioH, fragUuId, btnIdA[i], blob->presetId )) != kInvalidId )
uiSetEnable( app->ioH, btnUuId, enableFl );
}
}
if( enableFl )
_clear_status(app);
else
_set_status(app,"Invalid fragment play range.");
}
void _disable_frag_play_btn( app_t* app, unsigned fragBegEndUuId )
@ -755,11 +910,24 @@ namespace cw
case preset_sel::kPresetSelectVarId:
_update_frag_select_flags( app, blob->fragId);
break;
case preset_sel::kPresetSeqSelectVarId:
_update_frag_select_flags( app, blob->fragId);
break;
case preset_sel::kPlayBtnVarId:
_do_play_fragment( app, blob->fragId );
break;
case preset_sel::kPlaySeqBtnVarId:
_do_seq_play_fragment( app, blob->fragId );
break;
case preset_sel::kPlaySeqAllBtnVarId:
_do_seq_play_fragment( app, blob->fragId );
break;
case preset_sel::kBegPlayLocVarId:
{
unsigned endPlayLoc;
@ -818,9 +986,16 @@ namespace cw
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
// store a connection for the order control back to the fragment record
_frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetOrderVarId, preset_idx );
// preset sequence select check
if((rc = io::uiCreateCheck( app->ioH, uuId, colUuId, nullEleName, kFragPresetSeqSelId, chanId, nullClass, nullptr )) != kOkRC )
goto errLabel;
// store a connection for the sequence select control back to the fragment record
_frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetSeqSelectVarId, preset_idx );
errLabel:
if(rc != kOkRC )
rc = cwLogError(rc,"Preset control index '%i' create failed.");
@ -870,14 +1045,16 @@ namespace cw
// Attach blobs to the UI to allow convenient access back to the prese_sel data record
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragInGainId, fragChanId), fragId, preset_sel::kInGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragOutGainId, fragChanId), fragId, preset_sel::kOutGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragWetDryGainId, fragChanId), fragId, preset_sel::kWetGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragFadeOutMsId, fragChanId), fragId, preset_sel::kFadeOutMsVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragBegPlayLocId, fragChanId), fragId, preset_sel::kBegPlayLocVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), fragId, preset_sel::kEndPlayLocVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayBtnId, fragChanId), fragId, preset_sel::kPlayBtnVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragNoteId, fragChanId), fragId, preset_sel::kNoteVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragInGainId, fragChanId), fragId, preset_sel::kInGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragOutGainId, fragChanId), fragId, preset_sel::kOutGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragWetDryGainId, fragChanId), fragId, preset_sel::kWetGainVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragFadeOutMsId, fragChanId), fragId, preset_sel::kFadeOutMsVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragBegPlayLocId, fragChanId), fragId, preset_sel::kBegPlayLocVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), fragId, preset_sel::kEndPlayLocVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayBtnId, fragChanId), fragId, preset_sel::kPlayBtnVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlaySeqBtnId, fragChanId), fragId, preset_sel::kPlaySeqBtnVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayAllBtnId, fragChanId), fragId, preset_sel::kPlaySeqAllBtnVarId, kInvalidId );
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragNoteId, fragChanId), fragId, preset_sel::kNoteVarId, kInvalidId );
// create each of the preset controls
@ -1064,8 +1241,8 @@ namespace cw
rc_t rc = kOkRC;
unsigned midiEventN = 0;
bool firstLoadFl = !app->scoreH.isValid();
unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
//unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
//unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
// if the score is already loaded
//if( app->scoreH.isValid() )
@ -1075,26 +1252,41 @@ namespace cw
_set_status(app,"Loading...");
// Load the piano score
// Load the piano score (this set's app->min/maxLoc)
if((rc = _load_piano_score(app,midiEventN)) != kOkRC )
goto errLabel;
/*
if( !firstLoadFl)
{
minLoc = std::max(minLoc,app->minLoc);
maxLoc = std::min(maxLoc,app->maxLoc);
}
*/
// reset the timestamp tracker
track_timestamp_reset( app->psH );
track_loc_reset( app->psH );
// set the range of the global play location controls
if( firstLoadFl )
{
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, minLoc );
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, maxLoc );
unsigned begPlayLocUuId = io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId);
unsigned endPlayLocUuId = io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId);
unsigned end_play_loc = app->end_play_loc==0 ? app->maxLoc : app->end_play_loc;
unsigned beg_play_loc = app->minLoc <= app->beg_play_loc && app->beg_play_loc <= app->maxLoc ? app->beg_play_loc : app->minLoc;
io::uiSetNumbRange( app->ioH, begPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->minLoc );
io::uiSetNumbRange( app->ioH, endPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->maxLoc );
//io::uiSendValue( app->ioH, begPlayLocUuId, app->minLoc);
//io::uiSendValue( app->ioH, endPlayLocUuId, app->maxLoc);
io::uiSendValue( app->ioH, begPlayLocUuId, beg_play_loc);
io::uiSendValue( app->ioH, endPlayLocUuId, end_play_loc);
// enable the 'End Loc' number box since the score is loaded
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
}
@ -1105,6 +1297,7 @@ namespace cw
// enable the start/stop buttons
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), true );
// restore the fragment records
if( firstLoadFl )
@ -1319,6 +1512,7 @@ namespace cw
if( mrp_dev_idx <= midi_record_play::device_count(app->mrpH) )
{
bool enableFl = midi_record_play::is_device_enabled(app->mrpH, mrp_dev_idx );
io::uiSendValue( app->ioH, uuId, enableFl );
}
}
@ -1357,6 +1551,7 @@ namespace cw
return rc;
}
/*
rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value )
{
switch( appId )
@ -1391,7 +1586,7 @@ namespace cw
}
return kOkRC;
}
rc_t _on_echo_half_pedal( app_t* app, unsigned appId, unsigned uuId )
{
switch( appId )
@ -1426,7 +1621,7 @@ namespace cw
}
return kOkRC;
}
*/
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
{
@ -1437,13 +1632,14 @@ namespace cw
// disable start and stop buttons until a score is loaded
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), false );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), false );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), false );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), false );
const preset_sel::frag_t* f = preset_sel::get_fragment_base( app->psH );
for(; f!=nullptr; f=f->link)
_update_frag_ui( app, f->fragId );
_do_load(app);
//_do_load(app);
return rc;
}
@ -1495,6 +1691,10 @@ namespace cw
_do_stop_play(app);
break;
case kLiveCheckId:
app->useLiveMidiFl = m.value->u.b;
break;
case kPrintMidiCheckId:
app->printMidiFl = m.value->u.b;
break;
@ -1543,6 +1743,7 @@ namespace cw
_on_ui_delete_btn(app);
break;
/*
case kHalfPedalPedalVel:
case kHalfPedalDelayMs:
case kHalfPedalPitch:
@ -1551,6 +1752,7 @@ namespace cw
case kHalfPedalDnDelayMs:
_on_ui_half_pedal_value( app, m.appId, m.uuId, m.value->u.u );
break;
*/
case kFragInGainId:
_on_ui_frag_value( app, m.uuId, m.value->u.d);
@ -1580,7 +1782,15 @@ namespace cw
case kFragPlayBtnId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
case kFragPlaySeqBtnId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
case kFragPlayAllBtnId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
case kFragNoteId:
_on_ui_frag_value( app, m.uuId, m.value->u.s );
break;
@ -1592,6 +1802,11 @@ namespace cw
case kFragPresetSelId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
case kFragPresetSeqSelId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
}
return rc;
@ -1644,7 +1859,7 @@ namespace cw
errLabel:
return rc;
}
rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
@ -1677,6 +1892,7 @@ namespace cw
_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
break;
/*
case kHalfPedalPedalVel:
case kHalfPedalDelayMs:
case kHalfPedalPitch:
@ -1685,7 +1901,7 @@ namespace cw
case kHalfPedalDnDelayMs:
_on_echo_half_pedal( app, m.appId, m.uuId );
break;
*/
}
return rc;
@ -1730,6 +1946,7 @@ namespace cw
break;
case ui::kIdleOpId:
_do_seq_exec( app );
break;
case ui::kInvalidOpId:
@ -1749,11 +1966,18 @@ namespace cw
rc_t rc = kOkRC;
app_t* app = reinterpret_cast<app_t*>(arg);
if( app->mrpH.isValid() )
if( app->mrpH.isValid() && !app->useLiveMidiFl )
{
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_loc(app->mrpH) );
{
unsigned cur_loc = midi_record_play::event_loc(app->mrpH);
if( cur_loc > 0 )
{
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
}
}
}
if( app->ioFlowH.isValid() )
@ -1771,6 +1995,8 @@ namespace cw
break;
case io::kMidiTId:
if( app->useLiveMidiFl && app->mrpH.isValid() )
_on_live_midi( app, *m );
break;
case io::kAudioTId:
@ -1802,7 +2028,7 @@ namespace cw
}
cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
cw::rc_t cw::preset_sel_app::main( const object_t* cfg )
{
rc_t rc;
@ -1837,7 +2063,7 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
}
// create the IO Flow controller
if(app.flow_cfg==nullptr || flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,app.crossFadeSrate,app.crossFadeCnt,*flow_proc_dict,*app.flow_cfg)) != kOkRC )
if(app.flow_cfg==nullptr || app.flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,app.crossFadeSrate,app.crossFadeCnt,*app.flow_proc_dict,*app.flow_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The IO Flow controller create failed.");
goto errLabel;
@ -1852,9 +2078,9 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
// execute the io framework
while( !isShuttingDown(app.ioH))
while( !io::isShuttingDown(app.ioH))
{
exec(app.ioH);
io::exec(app.ioH);
sleepMs(50);
}

View File

@ -5,7 +5,7 @@ namespace cw
{
namespace preset_sel_app
{
rc_t main( const object_t* cfg, const object_t* flow_proc_dict );
rc_t main( const object_t* cfg );
}
}

304
cwKeyboard.cpp Normal file
View File

@ -0,0 +1,304 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include "cwKeyboard.h"
namespace cw
{
struct termios new_settings;
struct termios stored_settings;
void set_keypress(void)
{
struct termios new_settings;
tcgetattr(0,&stored_settings);
new_settings = stored_settings;
new_settings.c_lflag &= (~ICANON);
new_settings.c_lflag &= (~ECHO);
new_settings.c_cc[VTIME] = 0;
//int i;
//for(i=0; i<NCCS; ++i)
// printf("%i ",new_settings.c_cc[i]);
//printf("\n");
tcgetattr(0,&stored_settings);
new_settings.c_cc[VMIN] = 1;
tcsetattr(0,TCSANOW,&new_settings);
}
void reset_keypress(void)
{
tcsetattr(0,TCSANOW,&stored_settings);
}
#define CM_KB_TBL_CNT (10)
unsigned _cmKbTbl[][CM_KB_TBL_CNT] =
{
// alt ctl code
{ 3, 27, 91, 68, 0, 0, 0, 0, 0, kLeftArrowKId },
{ 3, 27, 91, 67, 0, 0, 0, 0, 0, kRightArrowKId },
{ 3, 27, 91, 65, 0, 0, 0, 0, 0, kUpArrowKId },
{ 3, 27, 91, 66, 0, 0, 0, 0, 0, kDownArrowKId },
{ 3, 27, 79, 72, 0, 0, 0, 0, 0, kHomeKId },
{ 3, 27, 79, 70, 0, 0, 0, 0, 0, kEndKId },
{ 4, 27, 91, 53, 126, 0, 0, 0, 0, kPgUpKId },
{ 4, 27, 91, 54, 126, 0, 0, 0, 0, kPgDownKId },
{ 4, 27, 91, 50, 126, 0, 0, 0, 0, kInsertKId },
{ 4, 27, 91, 51, 126, 0, 0, 0, 0, kDeleteKId },
{ 6, 27, 91, 49, 59, 53, 68, 0, 1, kLeftArrowKId },
{ 6, 27, 91, 49, 59, 53, 67, 0, 1, kRightArrowKId },
{ 6, 27, 91, 49, 59, 53, 65, 0, 1, kUpArrowKId },
{ 6, 27, 91, 49, 59, 53, 66, 0, 1, kDownArrowKId },
{ 6, 27, 91, 53, 59, 53, 126, 0, 1, kPgUpKId },
{ 6, 27, 91, 54, 59, 53, 126, 0, 1, kPgDownKId },
{ 4, 27, 91, 51, 59, 53, 126, 0, 1, kDeleteKId },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, kInvalidKId }
};
}
void cw::keyPress( cmKbRecd* p )
{
const int bufN = 16;
char buf[bufN];
int n,j, k;
int rc;
char c;
if( p != NULL )
{
p->code = kInvalidKId;
p->ch = 0;
p->ctlFl = false;
p->altFl = false;
}
set_keypress();
// block for the first character
if((rc = read(0, &c, 1 )) == 1)
buf[0]=c;
// loop in non-blocking for successive characters
new_settings.c_cc[VMIN] = 0;
tcsetattr(0,TCSANOW,&new_settings);
for(n=1; n<bufN; ++n)
if(read(0,&c,1) == 1 )
buf[n] = c;
else
break;
new_settings.c_cc[VMIN] = 1;
tcsetattr(0,TCSANOW,&new_settings);
/*
for(j=0; j<n; ++j)
printf("{%c (%i)} ",buf[j],buf[j]);
printf(" :%i\f\n",n);
fflush(stdout);
*/
if( p != NULL )
{
// translate the keypress
if( n == 1)
{
p->code = kAsciiKId;
p->ch = buf[0];
p->ctlFl = buf[0] <= 31;
}
else
{
for(j=0; _cmKbTbl[j][0] != 0; ++j)
if( _cmKbTbl[j][0] == (unsigned)n )
{
for(k=1; k<=n; ++k)
if( _cmKbTbl[j][k] != (unsigned)buf[k-1] )
break;
// if the key was found
if( k==n+1 )
{
p->code = _cmKbTbl[j][ CM_KB_TBL_CNT - 1 ];
p->ctlFl = _cmKbTbl[j][ CM_KB_TBL_CNT - 2 ];
break;
}
}
}
}
reset_keypress();
}
// Based on: // From: http://www.flipcode.com/archives/_kbhit_for_Linux.shtml
int cw::isKeyWaiting()
{
static const int STDIN = 0;
static bool initialized = false;
struct timeval timeout;
fd_set rdset;
if( !initialized )
{
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
if(0)
{
FD_ZERO(&rdset);
FD_SET(STDIN, &rdset);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
// time out immediately if STDIN is not ready.
return select(STDIN + 1, &rdset, NULL, NULL, &timeout);
}
else
{
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
}
void cw::kbTest1()
{
set_keypress();
int c = 0;
int r;
printf("'q' to quit\n");
while( c != 'q' )
{
printf("0>"); fflush(stdout);
r = read(0, &c, 1 );
printf("0: %c (%i)\r\n",(char)c,c);
new_settings.c_cc[VMIN] = 0;
tcsetattr(0,TCSANOW,&new_settings);
if( r == 1 && c == 27 )
{
r = read(0, &c, 1 );
printf("1: %c (%i)\n",(char)c,c);
if( r == 1 && c == '[' )
{
r = read(0, &c, 1 );
printf("2: %c (%i)\n",(char)c,c);
}
}
new_settings.c_cc[VMIN] = 1;
tcsetattr(0,TCSANOW,&new_settings);
}
reset_keypress();
}
void cw::kbTest2()
{
set_keypress();
fd_set rfds;
struct timeval tv;
int retval;
int c=0;
printf("'q' to quit\n");
while( c != 'q' )
{
int i = 0;
printf(">");
do
{
// Watch stdin (fd 0) to see when it has input.
FD_ZERO(&rfds);
FD_SET(0, &rfds);
// don't wait
tv.tv_sec = 0;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, i==0 ? NULL : &tv);
// Don't rely on the value of tv now - it may have been overwritten by select
// if an error occurred
if (retval == -1)
perror("select()");
else
{
// if data is waiting
if (retval)
{
c = getchar();
printf("%i %c (%i) ",i,(char)c,c);
++i;
}
else
{
printf("\n");
break; // no data available
}
}
} while( 1 );
}
reset_keypress();
}
void cw::kbTest3()
{
set_keypress();
int i =0;
printf("<enter> to quit");
while(1)
{
cw::sleepMs(500); // sleep milliseconds
printf("%i\n",i);
i += 1;
if( isKeyWaiting() )
break;
}
reset_keypress();
}

70
cwKeyboard.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef cwKeyboard_H
#define cwKeyboard_H
namespace cw
{
enum
{
kInvalidKId,
kAsciiKId,
kLeftArrowKId,
kRightArrowKId,
kUpArrowKId,
kDownArrowKId,
kHomeKId,
kEndKId,
kPgUpKId,
kPgDownKId,
kInsertKId,
kDeleteKId,
};
typedef struct
{
unsigned code;
char ch;
bool ctlFl;
bool altFl;
} cmKbRecd;
// Set 'p' to NULL if the value of the key is not required.
void keyPress( cmKbRecd* p );
// Return non-zero if a key is waiting to be read otherwise return 0.
// Use getchar() to pick up the key.
//
// Example:
// while( 1 )
// {
// if( cmIsKeyWaiting() == 0 )
// usleep(20000);
// else
// {
// char c = getchar();
// switch(c)
// {
// ....
// }
// }
//
// }
//
// TODO: Note that this function turns off line-buffering on stdin.
// It should be changed to a three function sequence.
// bool org_state = cmSetStdinLineBuffering(false);
// ....
// isKeyWaiting()
// ....
// setStdinLineBuffering(org_state)
int isKeyWaiting();
void kbTest1();
void kbTest2();
void kbTest3();
}
#endif

View File

@ -11,6 +11,7 @@ namespace cw
kInvalidMidiByte = 128,
kMidiNoteCnt = kInvalidMidiByte,
kMidiCtlCnt = kInvalidMidiByte,
kMidiVelCnt = kInvalidMidiByte,
kMidiPgmCnt = kInvalidMidiByte,
kInvalidMidiPitch = kInvalidMidiByte,
kInvalidMidiVelocity = kInvalidMidiByte,
@ -98,6 +99,10 @@ namespace cw
template< typename T> bool isSostenutoPedalDown( T s, T d0, T d1) { return ( isSostenutoPedal(s,d0) && (d1)>=64 ); }
template< typename T> bool isSostenutoPedalUp( T s, T d0, T d1) { return ( isSostenutoPedal(s,d0) && (d1)<64 ); }
template< typename T> bool isSoftPedal( T s, T d0 ) { return ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)== kSoftPedalCtlMdId ); }
template< typename T> bool isSoftPedalDown( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && (d1)>=64 ); }
template< typename T> bool isSoftPedalUp( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && (d1)<64 ); }
template< typename T> bool isPedal( T s, T d0 ) { return ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)>=kSustainCtlMdId && (d0)<=kLegatoCtlMdId ); }
template< typename T> bool isPedalDown( T s, T d0, T d1 ) { return ( isPedal(s,d0) && (d1)>=64 ); }
template< typename T> bool isPedalUp( T s, T d0, T d1 ) { return ( isPedal(s,d0) && (d1)<64 ); }

View File

@ -134,12 +134,18 @@ namespace cw
// if no input
if( rc == -EAGAIN )
{
// TODO: report or at least count error
break;
}
// if input buffer overrun
if( rc == -ENOSPC )
{
// TODO: report or at least count error
break;
}
// get the device this event arrived from
if( p->prvRcvDev==NULL || p->prvRcvDev->clientId != ev->source.client )
p->prvRcvDev = _cmMpClientIdToDev(p,ev->source.client);
@ -150,7 +156,7 @@ namespace cw
if( p->prvRcvDev == NULL || p->prvRcvPort == NULL )
continue;
//printf("%i %x\n",ev->type,ev->type);
//printf("dev:%i port:%i ch:%i %i\n",ev->source.client,ev->source.port,ev->data.note.channel,ev->data.note.note);

View File

@ -1114,8 +1114,50 @@ namespace cw
return rc;
}
rc_t _testBatchConvert( const object_t* cfg )
{
rc_t rc;
const char* io_dir = nullptr;
const char* session_dir = nullptr;
unsigned take_begin = 0;
unsigned take_end = 0;
bool printWarningsFl = true;
if((rc = cfg->getv("io_dir",io_dir,
"session_dir",session_dir,
"take_begin",take_begin,
"take_end",take_end,
"print_warnings_flag",printWarningsFl)) != kOkRC )
{
cwLogError(rc,"MIDI file batch convert to CSV failed.");
}
for(unsigned i=take_begin; i<=take_end; ++i)
{
char take_dir[32];
snprintf(take_dir,32,"record_%i",i);
char* src_midi_fn = filesys::makeFn( io_dir, "midi", ".mid", session_dir, take_dir, nullptr );
char* dst_csv_fn = filesys::makeFn( io_dir, "midi", ".csv", session_dir, take_dir, nullptr );
char* sm_fn = filesys::expandPath( src_midi_fn );
char* dm_fn = filesys::expandPath( dst_csv_fn );
//rc = genCsvFile(mfn,cfn );
cwLogInfo("Midi to CSV: src:%s dst:%s\n", sm_fn,dm_fn);
if((rc = genCsvFile(sm_fn, dm_fn, printWarningsFl )) != kOkRC )
cwLogError(rc,"MIDI to CSV Conversion failed on %s to %s.",sm_fn,dm_fn);
mem::release(sm_fn);
mem::release(dm_fn);
mem::release(src_midi_fn);
mem::release(dst_csv_fn);
}
return rc;
}
}
}
@ -1746,12 +1788,18 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
trackMsg_t* m0 = noteM[k];
if( m0 == NULL )
cwLogWarning("%i : Missing note-on instance for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
{
if( warningFl )
cwLogWarning("%i : Missing note-on instance for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
}
else
{
// a key was released - so it should not already be up
if( noteGateM[k]==0 )
cwLogWarning("%i : Missing note-on for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
{
if( warningFl )
cwLogWarning("%i : Missing note-on for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
}
else
{
noteGateM[k] -= 1; // update the note gate state
@ -1783,7 +1831,7 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
if( isSustainPedalUp(m) )
{
// if the sustain channel is already up
if( sustGateV[ch]==0 )
if( warningFl && sustGateV[ch]==0 )
cwLogWarning("%i : The sustain pedal release message was received with no previous pedal down.",m->uid);
if( sustGateV[ch] >= 1 )
@ -1830,7 +1878,7 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
if( isSostenutoPedalUp(m) )
{
// if the sustain channel is already up
if( sostGateV[ch]==0 )
if( warningFl && sostGateV[ch]==0 )
cwLogWarning("%i : The sostenuto pedal release message was received with no previous pedal down.",m->uid);
if( sostGateV[ch] >= 1 )
@ -2157,7 +2205,7 @@ cw::rc_t cw::midi::file::genPlotFile( const char* midiFn, const char* outFn )
return rc;
}
cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn )
cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn, bool printWarningsFl)
{
rc_t rc = kOkRC;
handle_t mfH;
@ -2166,7 +2214,7 @@ cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn )
if((rc = open( mfH, midiFn )) != kOkRC )
return cwLogError(rc,"The MIDI file object could not be opened from '%s'.",cwStringNullGuard(midiFn));
calcNoteDurations( mfH, 0 );
calcNoteDurations( mfH, printWarningsFl ? kWarningsMfFl : 0 );
if((rc = cw::file::open(fH, csvFn,cw::file::kWriteFl)) != kOkRC )
{
@ -2351,13 +2399,23 @@ cw::rc_t cw::midi::file::test( const object_t* cfg )
{
rc_t rc = kOkRC;
const object_t* o;
if((o = cfg->find("rpt")) != nullptr )
rc = _testReport(o);
const object_t* o;
if((o = cfg->find("csv")) != nullptr )
rc = _testCsv(o);
for(unsigned i=0; i<cfg->child_count(); ++i)
{
if((o = cfg->child_ele(i)) != nullptr )
{
if( strcmp(o->pair_label(),"rpt")==0 )
rc = _testReport(o->pair_value());
if( strcmp(o->pair_label(),"csv")==0 )
rc = _testCsv(o->pair_value());
if( strcmp(o->pair_label(),"batch_convert")==0 )
rc = _testBatchConvert(o->pair_value());
}
}
return rc;

View File

@ -218,7 +218,7 @@ namespace cw
rc_t genSvgFile(const char* midiFn, const char* outSvgFn, const char* cssFn, bool standAloneFl, bool panZoomFl );
rc_t genCsvFile( const char* midiFn, const char* csvFn );
rc_t genCsvFile( const char* midiFn, const char* csvFn, bool printWarningsFl=true );
// Generate a text file reportusing cmMIdiFilePrintMsgs()
rc_t report( const char* midiFn, log::handle_t logH );

View File

@ -200,6 +200,56 @@ namespace cw
return nullptr;
}
bool _loc_is_in_frag( const frag_t* f, unsigned loc )
{
// if f is the earliest fragment
if( f->prev == nullptr )
return loc <= f->endLoc;
// else f->prev->end_loc < loc && loc <= f->end_loc
return f->prev->endLoc < loc && loc <= f->endLoc;
}
bool _loc_is_before_frag( const frag_t* f, unsigned loc )
{
// if loc is past f
if( loc > f->endLoc )
return false;
// loc may now only be inside or before f
// if f is the first frag then loc must be inside it
if( f->prev == nullptr )
return false;
// is loc before f
return loc <= f->prev->endLoc;
}
bool _loc_is_after_frag( const frag_t* f, unsigned loc )
{
return loc > f->endLoc;
}
// Scan from through the fragment list to find the fragment containing loc.
frag_t* _fast_loc_to_frag( preset_sel_t* p, unsigned loc, frag_t* init_frag=nullptr )
{
frag_t* f = init_frag==nullptr ? p->fragL : init_frag;
for(; f!=nullptr; f=f->link)
if( _loc_is_in_frag(f,loc) )
return f;
return nullptr;
}
rc_t _validate_preset_id( const frag_t* frag, unsigned preset_id )
@ -234,9 +284,13 @@ namespace cw
case kPresetSelectVarId:
for(unsigned i=0; i<f->presetN; ++i)
f->presetA[i].playFl = f->presetA[i].preset_idx == presetId ? value : false;
break;
case kPresetSeqSelectVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
f->presetA[ presetId ].seqFl = value;
break;
case kPresetOrderVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
f->presetA[ presetId ].order = value;
@ -269,6 +323,14 @@ namespace cw
case kPlayBtnVarId:
break;
case kPlaySeqBtnVarId:
f->seqAllFl = false;
break;
case kPlaySeqAllBtnVarId:
f->seqAllFl = true;
break;
case kMasterWetInGainVarId:
p->master_wet_in_gain = value;
break;
@ -329,6 +391,11 @@ namespace cw
valueRef = f->presetA[ presetId ].playFl;
break;
case kPresetSeqSelectVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
valueRef = f->presetA[ presetId ].seqFl;
break;
case kPresetOrderVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
valueRef = f->presetA[ presetId ].order;
@ -360,6 +427,13 @@ namespace cw
case kPlayBtnVarId:
break;
case kPlaySeqBtnVarId:
break;
case kPlaySeqAllBtnVarId:
valueRef = f->seqAllFl;
break;
case kMasterWetInGainVarId:
valueRef = p->master_wet_in_gain;
@ -769,8 +843,8 @@ bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const
time::spec_t t0;
time::setZero(t0);
unsigned elapsedMs = time::elapsedMs(t0,ts);
double mins = elapsedMs / 60000.0;
//unsigned elapsedMs = time::elapsedMs(t0,ts);
//double mins = elapsedMs / 60000.0;
// if this is the first call to 'track_timestamp()'.
@ -802,15 +876,103 @@ bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const
return frag_changed_fl;
}
unsigned cw::preset_sel::fragment_play_preset_index( const frag_t* frag )
void cw::preset_sel::track_loc_reset( handle_t h )
{
preset_sel_t* p = _handleToPtr(h);
p->last_ts_frag = nullptr;
}
bool cw::preset_sel::track_loc( handle_t h, unsigned loc, const cw::preset_sel::frag_t*& frag_Ref )
{
preset_sel_t* p = _handleToPtr(h);
frag_t* f = nullptr;
bool frag_changed_fl = false;
// if this is the first call to 'track_timestamp()'.
if( p->last_ts_frag == nullptr )
f = _fast_loc_to_frag(p,loc);
else
// if the 'ts' is in the same frag as previous call.
if( _loc_is_in_frag(p->last_ts_frag,loc) )
f = p->last_ts_frag;
else
// if 'ts' is in a later frag
if( _loc_is_after_frag(p->last_ts_frag,loc) )
f = _fast_loc_to_frag(p,loc,p->last_ts_frag);
else // ts is prior to 'last_ts_frag'
f = _fast_loc_to_frag(p,loc);
// 'f' will be null at this point if 'ts' is past the last preset.
// In this case we should leave 'last_ts_frag' unchanged.
// if 'f' is valid but different from 'last_ts_frag'
if( f != nullptr && f != p->last_ts_frag )
{
// don't allow the selected fragment to go backwards
if( p->last_ts_frag == nullptr || (p->last_ts_frag != nullptr && p->last_ts_frag->endLoc < f->endLoc) )
{
p->last_ts_frag = f;
frag_changed_fl = true;
}
}
frag_Ref = p->last_ts_frag;
return frag_changed_fl;
}
unsigned cw::preset_sel::fragment_play_preset_index( const frag_t* frag, unsigned preset_seq_idx )
{
unsigned n = 0;
// for each preset
for(unsigned i=0; i<frag->presetN; ++i)
if( frag->presetA[i].playFl )
return frag->presetA[i].preset_idx;
{
// if 'preset_seq_idx' is not valid ...
if( preset_seq_idx==kInvalidIdx )
{
// ...then select the first preset whose 'playFl' is set.
if( frag->presetA[i].playFl )
return frag->presetA[i].preset_idx;
}
else
{
// ... otherwise select the 'nth' preset whose 'seqFl' is set
if( frag->presetA[i].seqFl || frag->seqAllFl )
{
if( n == preset_seq_idx )
return frag->presetA[i].preset_idx;
++n;
}
}
}
return kInvalidIdx;
}
unsigned cw::preset_sel::fragment_seq_count( handle_t h, unsigned fragId )
{
rc_t rc = kOkRC;
preset_sel_t* p = _handleToPtr(h);
frag_t* f = nullptr;
unsigned n = 0;
if((rc = _find_frag(p,fragId,f)) != kOkRC )
return 0;
if( f->seqAllFl )
return f->presetN;
for(unsigned i=0; i<f->presetN; ++i)
if( f->presetA[i].seqFl )
++n;
return n;
}
cw::rc_t cw::preset_sel::write( handle_t h, const char* fn )
{

View File

@ -11,6 +11,7 @@ namespace cw
typedef struct preset_str
{
bool playFl; // play this preset
bool seqFl; // play this preset during sequencing.
unsigned preset_idx; // preset index into preset_labelA[].
unsigned order; //
} preset_t;
@ -34,6 +35,7 @@ namespace cw
unsigned presetN;
bool uiSelectFl;
bool seqAllFl; // Set if all preset.seqFl's should be treated as though they are set to true.
struct frag_str* link;
struct frag_str* prev;
@ -51,12 +53,15 @@ namespace cw
kBegPlayLocVarId,
kEndPlayLocVarId,
kPlayBtnVarId,
kPlaySeqBtnVarId,
kPlaySeqAllBtnVarId,
kNoteVarId,
kPresetOrderVarId, // preset order value
kPresetSelectVarId, // select a preset to play
kPlayEnableVarId, // include in the segment to play
kDryFlVarId, // play this fragment dry
kPresetOrderVarId, // preset order value
kPresetSelectVarId, // select a preset to play
kPresetSeqSelectVarId, // sequence preset selections to play
kPlayEnableVarId, // include in the segment to play
kDryFlVarId, // play this fragment dry
kBaseMasterVarId, // All 'master' variables have id's greater than kBaseMasterVarId
@ -110,8 +115,15 @@ namespace cw
void track_timestamp_reset( handle_t h );
bool track_timestamp( handle_t h, const time::spec_t& ts, const cw::preset_sel::frag_t*& frag_Ref );
// Same as track_timestamp_???() but tracks the score 'loc' instead of timestamp.
void track_loc_reset( handle_t h );
bool track_loc( handle_t h, unsigned loc, const cw::preset_sel::frag_t*& frag_Ref );
// Return the preset index marked to play on this fragment.
unsigned fragment_play_preset_index( const frag_t* frag );
unsigned fragment_play_preset_index( const frag_t* frag, unsigned preset_seq_idx=kInvalidIdx );
// Return the count of presets whose 'seqFl' is set.
unsigned fragment_seq_count( handle_t h, unsigned fragId );
rc_t write( handle_t h, const char* fn );
rc_t read( handle_t h, const char* fn );

130
cwScoreFollower.cpp Normal file
View File

@ -0,0 +1,130 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwObject.h"
#include "cwMidi.h"
#include "cwScoreFollower.h"
#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmTime.h"
#include "cmMidi.h"
#include "cmSymTbl.h"
#include "cmScore.h"
namespace cw
{
namespace score_follower
{
typedef struct score_follower_str
{
unsigned search_area_locN;
unsigned key_wnd_locN;
char* score_csv_fname;
} score_follower_t;
score_follower_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,score_follower_t>(h); }
rc_t _parse_cfg( score_follower_t* p, const object_t* cfg )
{
rc_t rc = kOkRC;
const char* score_csv_fname;
if((rc = cfg->getv("score_csv_fname", score_csv_fname,
"search_area_locN", p->search_area_locN,
"key_wnd_locN", p->key_wnd_locN )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC, "Score follower argument parsing failed.");
goto errLabel;
}
if((app->score_csv_fname = filesys::expandPath( score_csv_fname )) == nullptr )
{
rc = cwLogError(kOpFailRC,"Score follower score file expansion failed.");
goto errLabel;
}
errLabel:
return rc;
}
rc_t _destroy( score_follower_t* p)
{
mem::release(p->score_csv_fname);
mem::release(p);
return kOkRC;
}
}
}
cw::rc_t cw::score_follower::create( handle_t& hRef, const object_t* cfg, double srate )
{
rc_t rc = kOkRC;
score_follower_t* p = nullptr;
if((rc = destroy(hRef)) != kOkRC )
return rc;
p = mem::allocZ<score_follower_t>();
if((rc = _parse_cfg(p,cfg)) != kOkRC )
goto errLabel;
cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, double srate, const unsigned* dynRefArray, unsigned dynRefCnt, cmScCb_t cbFunc, void* cbArg, cmSymTblH_t stH );
//cmRC_t cmScMatcherInit( cmScMatcher* p, double srate, cmScH_t scH, unsigned scWndN, unsigned midiWndN, cmScMatcherCb_t cbFunc, void* cbArg );
hRef.set(p);
errLabel:
if( rc != kOkRC )
{
_destroy(p);
cwLogError(rc,"Score follower create failed.");
}
return rc;
}
cw::rc_t cw::score_follower::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
score_follower_t* p = nullptr;
if( !hRef.isValid() )
return rc;
p = _handleToPtr(hRef);
if((rc = _destroy(p)) != kOkRC )
return rc;
hRef.clear();
return rc;
}
cw::rc_t cw::score_follower::reset( handle_t h, unsigned loc )
{
rc_t rc = kOkRC;
return rc;
}
cw::rc_t cw::score_follower::exec( handle_t h, unsigned smpIdx, unsigned muid, unsigned status, uint8_t d0, uint8_t d1, unsigned* scLocIdxPtr )
{
rc_t rc = kOkRC;
return rc;
}

21
cwScoreFollower.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef cwScoreFollower_h
#define cwScoreFollower_h
namespace cw
{
namespace score_follower
{
typedef handle< struct score_follower_str > handle_t;
rc_t create( handle_t& hRef, const object_t* cfg, double srate );
rc_t destroy( handle_t& hRef );
rc_t reset( handle_t h, unsigned loc );
rc_t exec( handle_t h, unsigned smpIdx, unsigned muid, unsigned status, uint8_t d0, uint8_t d1, unsigned* scLocIdxPtr );
}
}
#endif

View File

@ -8,11 +8,23 @@ namespace cw
typedef handle<struct thread_mach_str> handle_t;
typedef thread::cbFunc_t threadFunc_t;
// Create a thread machine instance.
// contextArray[ threadN ][ contextRecdByteN ] is an optional blob consisting of 'threadN' records each of size 'contextRecdByteN'.
// Each of the records then becomes the entity which is used as the 'arg' value in the callback for the first 'threadN' threads.
rc_t create( handle_t& hRef, threadFunc_t threadFunc=nullptr, void* contextArray=nullptr, unsigned contexRecdByteN=0, unsigned threadN=0 );
rc_t destroy( handle_t& hRef );
// Create an additional thread. Note that the additional thread will be started by the next
// call to 'start()'.
rc_t add( handle_t h, threadFunc_t threadFunc, void* arg );
// Start all threads
rc_t start( handle_t h );
// Stop all threads.
rc_t stop( handle_t h );
// Check if all threads are shutdown.
bool is_shutdown( handle_t h );
}
}

195
cwUi.cpp
View File

@ -89,6 +89,7 @@ namespace cw
char* recvBuf;
unsigned recvBufN;
unsigned recvBufIdx;
unsigned recvShiftN;
unsigned* sessA; // sessA[ sessN ] array of wsSessId's
unsigned sessN;
@ -1047,7 +1048,54 @@ namespace cw
{
return _setPropertyValue( h, propertyStr,uuId,enableFl ? 1 : 0 );
}
rc_t _copy_msg_to_recv_buffer( ui_t* p, const void* msg, unsigned msgByteN )
{
if( msg == nullptr || msgByteN == 0)
return kOkRC;
if( p->recvBufIdx + msgByteN > p->recvBufN )
return cwLogError(kBufTooSmallRC,"The UI input buffer (%i) is too small.", p->recvBufN);
memcpy(p->recvBuf + p->recvBufIdx, msg, msgByteN );
p->recvBufIdx += msgByteN;
return kOkRC;
}
const char* _get_msg_from_recv_buffer( ui_t* p )
{
const char* msg = nullptr;
unsigned i;
// shift off the previous msg
if( p->recvShiftN > 0 )
{
assert( p->recvBufIdx >= p->recvShiftN );
memmove(p->recvBuf, p->recvBuf+p->recvShiftN, p->recvBufIdx - p->recvShiftN );
p->recvBufIdx -= p->recvShiftN;
p->recvShiftN = 0;
}
// locate the end of the next msg.
if( p->recvBufIdx > 0 )
{
for(i=0; p->recvBuf[i]!=0 and i<p->recvBufIdx; ++i)
{}
// if the end of the next msg was found
if( i<p->recvBufIdx && p->recvBuf[i] == 0 )
{
p->recvShiftN = i+1;
msg = p->recvBuf;
}
}
return msg;
}
}
}
@ -1089,6 +1137,7 @@ cw::rc_t cw::ui::create(
p->recvBuf = mem::allocZ<char>(fmtBufByteN);
p->recvBufN = fmtBufByteN;
p->recvBufIdx = 0;
p->recvShiftN = 0;
// create the root element
if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
@ -1191,12 +1240,14 @@ cw::rc_t cw::ui::onDisconnect( handle_t h, unsigned wsSessId )
cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg, unsigned msgByteN )
{
rc_t rc = kOkRC;
ui_t* p = _handleToPtr(h);
opId_t opId = kInvalidOpId;
value_t value;
ele_t* ele;
rc_t rc = kOkRC;
ui_t* p = _handleToPtr(h);
opId_t opId = kInvalidOpId;
ele_t* ele = nullptr;
const char* msg = nullptr;
value_t value;
/*
const char* src_msg = (const char*)void_msg;
const char* msg = src_msg;
@ -1233,80 +1284,90 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg,
// the message is being processed so the buffer will end up empty
// (if it was being used)
p->recvBufIdx = 0;
// parse the 'opId' from the message
opId = _labelToOpId(msg);
*/
switch( opId )
// buffer the incoming msg
if((rc = _copy_msg_to_recv_buffer( p, void_msg, msgByteN )) != kOkRC )
goto errLabel;
// remove and and act on each buffered msg
while( (msg = _get_msg_from_recv_buffer(p)) != NULL )
{
case kInitOpId:
// if the app cfg included a reference to a UI resource file then instantiate it here
_onNewRemoteUi( p, wsSessId );
// parse the 'opId' from the message
opId = _labelToOpId(msg);
switch( opId )
{
case kInitOpId:
// if the app cfg included a reference to a UI resource file then instantiate it here
_onNewRemoteUi( p, wsSessId );
// Pass on the 'init' msg to the app.
p->uiCbFunc( p->uiCbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break;
// Pass on the 'init' msg to the app.
p->uiCbFunc( p->uiCbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break;
case kValueOpId:
if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'value' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
case kValueOpId:
if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'value' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
}
break;
}
break;
case kCorruptOpId:
if((ele = _parse_corrupt_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'corrupt' message parse failed.");
else
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
break;
case kCorruptOpId:
if((ele = _parse_corrupt_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'corrupt' message parse failed.");
else
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
break;
case kClickOpId:
if((ele = _parse_click_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'click' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
}
break;
case kClickOpId:
if((ele = _parse_click_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'click' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
}
break;
case kSelectOpId:
if((ele = _parse_select_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'select' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
}
break;
case kSelectOpId:
if((ele = _parse_select_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'select' message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
}
break;
case kEchoOpId:
if((ele = _parse_echo_msg(p,(const char*)msg)) == nullptr )
cwLogError(kOpFailRC,"UI Echo message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId,nullptr );
}
break;
case kEchoOpId:
if((ele = _parse_echo_msg(p,(const char*)msg)) == nullptr )
cwLogError(kOpFailRC,"UI Echo message parse failed.");
else
{
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId,nullptr );
}
break;
case kIdleOpId:
p->uiCbFunc( p->uiCbArg, kInvalidId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break;
case kIdleOpId:
p->uiCbFunc( p->uiCbArg, kInvalidId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break;
case kInvalidOpId:
cwLogError(kInvalidIdRC,"The UI received a NULL op. id.");
break;
case kInvalidOpId:
cwLogError(kInvalidIdRC,"The UI received a NULL op. id.");
break;
default:
cwLogError(kInvalidIdRC,"The UI received an unknown op. id.");
break;
default:
cwLogError(kInvalidIdRC,"The UI received an unknown op. id.");
break;
} // switch opId
} // switch opId
}
errLabel:
return rc;
}

View File

@ -13,27 +13,27 @@ namespace cw
typedef enum
{
kInvalidOpId,
kConnectOpId, // A new remote user interface was connected
kInitOpId, // A remote user interface instance was created and is available. It needs to be updated with the current state of the UI from the server.
kValueOpId, // The value of a remote user interface control changed. Send this value to the application engine.
kCorruptOpId, // The value of the remote user interface is invalid or corrupt.
kClickOpId, // A element on a remote user interface was clicked.
kSelectOpId, // An element on a remote user interface was is 'selected' or 'deselected'.
kEchoOpId, // A remote user interface is requesting an application engine value. The the current value of a ui element must be sent to the remote UI.
kIdleOpId, // The application (UI server) is idle and waiting for the next event from a remote UI.
kDisconnectOpId // A reemot user interface was disconnected.
kInvalidOpId, // 0
kConnectOpId, // 1 A new remote user interface was connected
kInitOpId, // 2 A remote user interface instance was created and is available. It needs to be updated with the current state of the UI from the server.
kValueOpId, // 3 The value of a remote user interface control changed. Send this value to the application engine.
kCorruptOpId, // 4 The value of the remote user interface is invalid or corrupt.
kClickOpId, // 5 A element on a remote user interface was clicked.
kSelectOpId, // 6 An element on a remote user interface was is 'selected' or 'deselected'.
kEchoOpId, // 7 A remote user interface is requesting an application engine value. The the current value of a ui element must be sent to the remote UI.
kIdleOpId, // 8 The application (UI server) is idle and waiting for the next event from a remote UI.
kDisconnectOpId // 9 A remote user interface was disconnected.
} opId_t;
typedef enum
{
kInvalidTId,
kBoolTId,
kIntTId,
kUIntTId,
kFloatTId,
kDoubleTId,
kStringTId
kInvalidTId, // 0
kBoolTId, // 1
kIntTId, // 2
kUIntTId, // 3
kFloatTId, // 4
kDoubleTId, // 5
kStringTId // 6
} dtypeId_t;

View File

@ -26,6 +26,7 @@
check:{ name: midiThruCheckId, title:"MIDI Thru" },
numb_disp: { name: curMidiEvtCntId, title:"Current" },
numb_disp: { name: totalMidiEvtCntId, title:"Total" },
check:{ name: midiMuteCheckId, title:"Mute" },
},
row: {
@ -33,6 +34,7 @@
check:{ name: audioThroughCheckId, title:"Audio Thru" },
numb_disp: { name: curAudioSecsId, title:"Current:", decpl:1 },
numb_disp: { name: totalAudioSecsId, title:"Total:", decpl:1 },
check: { name: audioMuteCheckId, title:"Mute:" },
},
row: {

View File

@ -21,10 +21,20 @@
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%;

View File

@ -467,7 +467,9 @@ function ui_create_check( parent_ele, d )
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 );
@ -870,8 +872,6 @@ function ui_create_list( parent_ele, d )
function ui_set_value( d )
{
//console.log(d)
var eleId = d.uuId.toString()
var ele = dom_id_to_ele(eleId)
@ -1089,7 +1089,9 @@ function ui_create( d )
var parent_ele = ui_get_parent(d.parentUuId);
var ele = null;
if( parent_ele != null )
if( parent_ele == null )
console.log("Parent ele not found.",d)
else
{
switch( d.type )
{

View File

@ -34,6 +34,7 @@
button:{ name: stopBtnId, title:"Stop" },
number:{ name: begLocNumbId, title:"Loc:", min:0, max:100000, step:1, decpl:0 },
number:{ name: endLocNumbId, title:"End:", min:0, max:100000, step:1, decpl:0 },
check: { name: liveCheckId, title:"Live" },
},
row: {
@ -49,6 +50,7 @@
button:{ name: deleteBtnId, title:"Delete", enable: false },
},
/*
row: {
number:{ name: halfPedalDelayMsId, title:"DelayMs:", min:0, max:5000, step:1, decpl:0 },
number:{ name: halfPedalPedalVelId, title:"PVel:", min:0, max:127, step:1, decpl:0 },
@ -57,6 +59,7 @@
number:{ name: halfPedalDurMsId, title:"DurMs:", min:0, max:5000, step:1, decpl:0 },
number:{ name: halfPedalDnDelayMsId, title:"DownMs:", min:0, max:5000, step:1, decpl:0 },
},
*/
row: {
str_disp:{ name: statusId, title:"Status:", value: "" },