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 endif
libcwHDR += src/libcw/cwKeyboard.h
libcwSRC += src/libcw/cwKeyboard.cpp
libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h
libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp 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 libcwSRC += src/libcw/cwMidiPort.cpp src/libcw/cwMidiAlsa.cpp src/libcw/cwAudioDeviceAlsa.cpp src/libcw/cwAudioDeviceTest.cpp
if cwWEBSOCK if cwWEBSOCK
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h 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/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp 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 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 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 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 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 endif

View File

@ -2,6 +2,22 @@
# To Do # 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 - 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 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 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. 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: - 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 muteFlags(),toneFlags(), gain( ... gainA) but where the size of the dest array
does not match the actual number of channesl? 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*. - 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. - numeric_convert() d_min is NOT zero, it's smallest positive number, this fails when src == 0.
min value is now set to zero. 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 - 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. - 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 - 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 '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. The 'appId' is assigned by the UI server when the JS client regsiters the control.
Client sends 'init' message. Client sends 'init' message.
Server sends 'create' messages. Server sends 'create' messages.
Client sends 'register' messages. Client sends 'register' messages.
Server send' 'id_assign' messages. Server send' 'id_assign' messages.
# sockaddr_in reference # sockaddr_in reference
@ -115,10 +126,14 @@ struct in_addr {
# Development Setup # Development Setup
0)
```
sudo dnf install g++ fftw-devel alsa-lib-devel libubsan
```
1) Install libwebsockets. 1) Install libwebsockets.
``` ```
sudo dnf install g++ openssl-devel cmake sudo dnf install openssl-devel cmake
cd sdk cd sdk
git clone https://libwebsockets.org/repo/libwebsockets git clone https://libwebsockets.org/repo/libwebsockets
cd libwebsockets cd libwebsockets
@ -145,7 +160,6 @@ struct in_addr {
# Flow Notes: # 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? - 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. - 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: 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 kTestFailRC, // 27
kInvalidStateRC, // 28 kInvalidStateRC, // 28
kTypeMismatchRC, // 29 kTypeMismatchRC, // 29
kBaseAppRC // 30 kNotImplementedRC, // 30
kBaseAppRC // 31
} cwRC_t; } cwRC_t;
typedef unsigned rc_t; typedef unsigned rc_t;

View File

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

View File

@ -564,7 +564,7 @@ namespace cw
// print a minutes counter // print a minutes counter
inst->durSmpN += src_abuf->frameN; inst->durSmpN += src_abuf->frameN;
if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 ) 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 namespace audio_merge
{ {
enum { enum {
kIn0PId,
kIn1PId,
kGainPId, kGainPId,
kOutPId, kOutPId,
kInBasePId,
}; };
typedef struct typedef struct
{ {
unsigned srcN;
} inst_t; } inst_t;
rc_t create( instance_t* ctx ) rc_t create( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const abuf_t* abuf0 = nullptr; //
const abuf_t* abuf1 = nullptr;
unsigned outChN = 0; unsigned outChN = 0;
unsigned frameN = 0;
// get the source audio buffer srate_t srate = 0;
if((rc = var_register_and_get(ctx, kAnyChIdx,
kIn0PId,"in0",abuf0, inst_t* inst = mem::allocZ<inst_t>();
kIn1PId,"in1",abuf1 )) != kOkRC )
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 // register the gain
for(unsigned i=0; i<outChN; ++i) for(unsigned i=0; i<outChN; ++i)
@ -1018,14 +1047,20 @@ namespace cw
goto errLabel; goto errLabel;
// create the output audio buffer // 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: errLabel:
return rc; return rc;
} }
rc_t destroy( instance_t* ctx ) 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 ) rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; } { return kOkRC; }
@ -1052,6 +1087,7 @@ namespace cw
return outChIdx; return outChIdx;
} }
/*
rc_t exec( instance_t* ctx ) rc_t exec( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -1074,6 +1110,31 @@ namespace cw
assert( oChIdx == obuf->chN ); 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: errLabel:
return rc; 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) 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 ) bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned chIdx )
{ return _var_find_on_label_and_ch(inst,label,chIdx) != nullptr; } { 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 ) 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 ); typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var );
enum enum
{ {
kSrcVarFl = 0x01 kSrcVarFl = 0x01,
kSrcOptVarFl = 0x02
}; };
typedef struct class_members_str typedef struct class_members_str
@ -397,6 +399,7 @@ namespace cw
void _var_destroy( variable_t* var ); void _var_destroy( variable_t* var );
bool var_exists( instance_t* inst, const char* label, unsigned chIdx ); 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, const variable_t*& varRef );
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, 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; 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 typedef struct timer_str
{ {
struct io_str* io; struct io_str* io;
@ -85,8 +93,7 @@ namespace cw
bool enableFl; bool enableFl;
char* label; char* label;
unsigned sockA_index; unsigned sockA_index;
unsigned userId; unsigned userId;
} socket_t; } socket_t;
typedef struct io_str typedef struct io_str
@ -102,6 +109,8 @@ namespace cw
object_t* cfg; object_t* cfg;
thread_t* threadL;
timer_t* timerA; timer_t* timerA;
unsigned timerN; unsigned timerN;
@ -141,6 +150,36 @@ namespace cw
io_t* _handleToPtr( handle_t h ) io_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,io_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 // Timer
@ -335,7 +374,10 @@ namespace cw
// get the serial port list node // get the serial port list node
if((cfg = c->find("serial")) == nullptr) 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 // the serial header values
if((rc = cfg->getv("pollPeriodMs", pollPeriodMs, if((rc = cfg->getv("pollPeriodMs", pollPeriodMs,
@ -413,10 +455,11 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
// the service is only started if at least one serial port is enabled if( p->serialPortSrvH.isValid() )
if( serialPort::portCount( serialPortSrv::serialHandle(p->serialPortSrvH) ) > 0 ) // the service is only started if at least one serial port is enabled
if((rc =serialPortSrv::start( p->serialPortSrvH )) != kOkRC ) if( serialPort::portCount( serialPortSrv::serialHandle(p->serialPortSrvH) ) > 0 )
rc = cwLogError(rc,"The serial port server start failed."); if((rc =serialPortSrv::start( p->serialPortSrvH )) != kOkRC )
rc = cwLogError(rc,"The serial port server start failed.");
return rc; return rc;
} }
@ -461,9 +504,11 @@ namespace cw
// get the MIDI port cfg // get the MIDI port cfg
if((cfg = c->find("midi")) == nullptr) 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( if((rc = cfg->getv(
"parserBufByteN", parserBufByteN )) != kOkRC ) "parserBufByteN", parserBufByteN )) != kOkRC )
{ {
@ -586,8 +631,8 @@ namespace cw
// get the socket configuration node // get the socket configuration node
if((node = cfg->find("socket")) == nullptr ) if((node = cfg->find("socket")) == nullptr )
{ {
rc = cwLogError(kSyntaxErrorRC,"Unable to locate the 'socket' configuration node."); cwLogWarning("No 'socket' configuration node.");
goto errLabel; return kOkRC;
} }
// get the required socket arguments // get the required socket arguments
@ -1469,8 +1514,11 @@ namespace cw
// get the audio port node // get the audio port node
if((node = cfg->find("audio")) == nullptr ) 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 // get the meterMs value
if((rc = node->getv("meterMs", p->audioMeterCbPeriodMs, "threadTimeOutMs", p->audioThreadTimeOutMs )) != kOkRC ) 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 // Timer

13
cwIo.h
View File

@ -27,6 +27,7 @@ namespace cw
enum enum
{ {
kThreadTId,
kTimerTId, kTimerTId,
kSerialTId, kSerialTId,
kMidiTId, kMidiTId,
@ -37,6 +38,12 @@ namespace cw
kUiTId kUiTId
}; };
typedef struct thread_msg_str
{
unsigned id;
void* arg;
} thread_msg_t;
typedef struct timer_msg_str typedef struct timer_msg_str
{ {
unsigned id; unsigned id;
@ -121,6 +128,7 @@ namespace cw
unsigned tid; unsigned tid;
union union
{ {
thread_msg_t* thread;
timer_msg_t* timer; timer_msg_t* timer;
serial_msg_t* serial; serial_msg_t* serial;
midi_msg_t* midi; midi_msg_t* midi;
@ -151,6 +159,11 @@ namespace cw
bool isShuttingDown( handle_t h ); bool isShuttingDown( handle_t h );
void report( 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, kMidiThruCheckId,
kCurMidiEvtCntId, kCurMidiEvtCntId,
kTotalMidiEvtCntId, kTotalMidiEvtCntId,
kMidiMuteCheckId,
kCurAudioSecsId, kCurAudioSecsId,
kTotalAudioSecsId, kTotalAudioSecsId,
kAudioMuteCheckId,
kSaveBtnId, kSaveBtnId,
kOpenBtnId, kOpenBtnId,
@ -66,10 +68,11 @@ namespace cw
{ kPanelDivId, kMidiThruCheckId, "midiThruCheckId" }, { kPanelDivId, kMidiThruCheckId, "midiThruCheckId" },
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" }, { kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
{ kPanelDivId, kTotalMidiEvtCntId, "totalMidiEvtCntId" }, { kPanelDivId, kTotalMidiEvtCntId, "totalMidiEvtCntId" },
{ kPanelDivId, kMidiMuteCheckId, "midiMuteCheckId" },
{ kPanelDivId, kCurAudioSecsId, "curAudioSecsId" }, { kPanelDivId, kCurAudioSecsId, "curAudioSecsId" },
{ kPanelDivId, kTotalAudioSecsId, "totalAudioSecsId" }, { kPanelDivId, kTotalAudioSecsId, "totalAudioSecsId" },
{ kPanelDivId, kAudioMuteCheckId, "audioMuteCheckId" },
{ kPanelDivId, kSaveBtnId, "saveBtnId" }, { kPanelDivId, kSaveBtnId, "saveBtnId" },
{ kPanelDivId, kOpenBtnId, "openBtnId" }, { kPanelDivId, kOpenBtnId, "openBtnId" },
@ -89,7 +92,10 @@ namespace cw
char* directory; char* directory;
midi_record_play::handle_t mrpH; 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; } app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg ) rc_t _parseCfg(app_t* app, const object_t* cfg )
@ -99,7 +105,8 @@ namespace cw
if((rc = cfg->getv( if((rc = cfg->getv(
"record_dir", app->record_dir, "record_dir", app->record_dir,
"record_folder", app->record_folder, "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."); rc = cwLogError(kSyntaxErrorRC,"Audio MIDI app configuration parse failed.");
} }
@ -172,6 +179,15 @@ namespace cw
mem::release(fn); 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 ) if((fn = filesys::makeFn(dir,"audio","wav",nullptr)) != nullptr )
{ {
@ -355,6 +371,7 @@ namespace cw
break; break;
case kReportBtnId: case kReportBtnId:
report( app->mrpH );
break; break;
case kSaveBtnId: case kSaveBtnId:
@ -374,6 +391,14 @@ namespace cw
cwLogInfo("MIDI thru:%i",m.value->u.b); cwLogInfo("MIDI thru:%i",m.value->u.b);
_set_midi_thru_state(app, m.value->u.b); _set_midi_thru_state(app, m.value->u.b);
break; 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: case kStartBtnId:
_on_ui_start(app); _on_ui_start(app);
@ -518,7 +543,7 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg )
return rc; return rc;
// create the MIDI record-play object // 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."); rc = cwLogError(rc,"MIDI record-play object create failed.");
goto errLabel; goto errLabel;

View File

@ -39,6 +39,8 @@ namespace cw
unsigned curFrameIdx; unsigned curFrameIdx;
bool recordFl; bool recordFl;
bool startedFl; bool startedFl;
bool mute_fl;
unsigned* audioInChMapA; unsigned* audioInChMapA;
unsigned audioInChMapN; unsigned audioInChMapN;
@ -169,8 +171,11 @@ namespace cw
for(unsigned chIdx=0; chIdx<chCnt; ++chIdx) for(unsigned chIdx=0; chIdx<chCnt; ++chIdx)
{ {
unsigned srcChIdx = p->audioInChMapA == nullptr ? chIdx : p->audioInChMapA[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; a->chCnt = chCnt;
@ -197,31 +202,34 @@ namespace cw
void _audio_play( audio_record_play_t* p, io::audio_msg_t& adst ) void _audio_play( audio_record_play_t* p, io::audio_msg_t& adst )
{ {
unsigned adst_idx = 0; unsigned adst_idx = 0;
while(adst_idx < adst.dspFrameCnt) if( !p->mute_fl )
{ {
am_audio_t* a; while(adst_idx < adst.dspFrameCnt)
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)
{ {
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 ) unsigned n = std::min(a->dspFrameCnt - sample_offs, adst.dspFrameCnt );
memcpy( adst.oBufArray[ dstChIdx ] + adst_idx, a->audioBuf + sample_offs, n * sizeof(sample_t));
// 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 // TODO: zero unused channels
if( adst_idx < adst.dspFrameCnt ) if( adst_idx < adst.dspFrameCnt )
@ -415,9 +423,16 @@ namespace cw
else else
{ {
if( m.oBufChCnt > 0 ) if( m.oBufChCnt > 0 )
{
_audio_play(p,m); _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; return rc;
} }
@ -522,6 +537,22 @@ bool cw::audio_record_play::record_state( handle_t h )
return rc; 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 ) cw::rc_t cw::audio_record_play::save( handle_t h, const char* fn )
{ {
audio_record_play_t* p = _handleToPtr(h); audio_record_play_t* p = _handleToPtr(h);

View File

@ -17,6 +17,8 @@ namespace cw
rc_t clear( handle_t h ); rc_t clear( handle_t h );
rc_t set_record_state( handle_t h, bool record_fl ); rc_t set_record_state( handle_t h, bool record_fl );
bool record_state( handle_t h ); 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 save( handle_t h, const char* fn );
rc_t open( handle_t h, const char* fn ); rc_t open( handle_t h, const char* fn );
double duration_seconds( handle_t h ); double duration_seconds( handle_t h );

View File

@ -54,6 +54,16 @@ namespace cw
unsigned pedalUpHalfVelId; unsigned pedalUpHalfVelId;
unsigned pedalUpHalfVel; 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; } midi_device_t;
@ -89,9 +99,12 @@ namespace cw
bool startedFl; bool startedFl;
bool recordFl; bool recordFl;
bool muteFl;
bool thruFl; bool thruFl;
bool logInFl; // log incoming message when not in 'record' mode. bool logInFl; // log incoming message when not in 'record' mode.
bool logOutFl; // log outgoing messages bool logOutFl; // log outgoing messages
bool velHistogramEnableFl;
bool halfPedalFl; bool halfPedalFl;
unsigned halfPedalState; unsigned halfPedalState;
@ -190,12 +203,27 @@ namespace cw
} }
if((rc = ele->getv_opt( "vel_table", velTable, if((rc = ele->getv_opt(
"pedal", pedalRecd)) != kOkRC ) "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."); rc = cwLogError(kSyntaxErrorRC,"MIDI record play device optional argument parsing failed.");
goto errLabel; 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].midiOutDevLabel = mem::duplStr( midiOutDevLabel);
p->midiDevA[i].midiOutPortLabel = mem::duplStr( midiOutPortLabel); 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 ) 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; 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 // verify that space exists in the record buffer
if( p->iMsgArrayInIdx < p->iMsgArrayN ) 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 _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; rc_t rc = kOkRC;
// if we have arrived at the stop time // 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_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 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 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 ) if( after_all_off_fl )
{ {
@ -320,6 +352,19 @@ namespace cw
out_d1 = p->midiDevA[i].velTableArray[ d1 ]; 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 // map the pedal down velocity
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl ) if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl )
{ {
@ -331,17 +376,21 @@ namespace cw
else else
if( d1 == p->midiDevA[i].pedalDownHalfVelId ) if( d1 == p->midiDevA[i].pedalDownHalfVelId )
out_d1 = p->midiDevA[i].pedalDownHalfVel; out_d1 = p->midiDevA[i].pedalDownHalfVel;
else else
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping.",d1); {
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 ); 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 ) 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 ) if( log_fl && p->logOutFl )
{ {
@ -355,6 +404,7 @@ namespace cw
return rc; return rc;
} }
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am, bool log_fl=true ) 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 ); 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() // 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 // 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 // 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. // msgArrayCntRef records will be returned.
cw::rc_t _am_file_read( const char* fn, unsigned& msgArrayCntRef, am_midi_msg_t*& msgArrayRef ) 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; file::handle_t fH;
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC ) if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
{ {
rc = cwLogError(kOpenFailRC,"Unable to locate the AM file: '%s'.", fn ); rc = cwLogError(kOpenFailRC,"Unable to locate the AM file: '%s'.", fn );
goto errLabel; 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; 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 ) 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 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 ); rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
n = msgArrayCntRef; 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 ) msgArrayCntRef = recordN;
{
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
goto errLabel;
}
msgArrayCntRef = n;
errLabel: errLabel:
if( rc != kOkRC and alloc_fl )
{
mem::release(msgArrayRef);
msgArrayRef = nullptr;
msgArrayCntRef = 0;
}
return rc; return rc;
} }
@ -497,14 +645,21 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
unsigned n = 0; unsigned n = 0;
int version;
file::handle_t fH; file::handle_t fH;
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC ) if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
{ {
rc = cwLogError(kOpenFailRC,"Unable to locate the file: '%s'.", fn ); rc = cwLogError(kOpenFailRC,"Unable to locate the file: '%s'.", fn );
goto errLabel; 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 ) if((rc = file::read(fH,n)) != kOkRC )
{ {
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn ); rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
@ -539,7 +694,10 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
file::handle_t fH; 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 ) if( p->iMsgArrayInIdx == 0 )
{ {
cwLogWarning("Nothing to write."); cwLogWarning("Nothing to write.");
@ -553,6 +711,13 @@ namespace cw
goto errLabel; 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 // write the file header
if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC ) if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC )
{ {
@ -696,13 +861,67 @@ namespace cw
void _report_midi( midi_record_play_t* p ) void _report_midi( midi_record_play_t* p )
{ {
printf("omsg cnt:%i\n",p->msgArrayInIdx);
for(unsigned i=0; i<p->msgArrayInIdx; ++i) for(unsigned i=0; i<p->msgArrayInIdx; ++i)
{ {
am_midi_msg_t* mm = p->msgArray + i; am_midi_msg_t* mm = p->msgArray + i;
_print_midi_msg(mm); _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 _stop( midi_record_play_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -712,6 +931,8 @@ namespace cw
time::spec_t t1; time::spec_t t1;
time::get(t1); time::get(t1);
_write_vel_histogram( p );
// if we were recording // if we were recording
if( p->recordFl ) if( p->recordFl )
{ {
@ -719,9 +940,12 @@ namespace cw
// set the 'microsec' value for each MIDI msg as an offset from the first message[] // set the 'microsec' value for each MIDI msg as an offset from the first message[]
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i) 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 ); 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; return rc;
} }
@ -753,12 +980,13 @@ namespace cw
// if this is a sys-ex msg // if this is a sys-ex msg
if( pkt->msgArray == NULL ) if( pkt->msgArray == NULL )
{ {
cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
} }
else // this is a triple else // this is a triple
{ {
//if( !midi::isPedal(pkt->msgArray[j].status,pkt->msgArray[j].d0) ) //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 ) 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); midi_record_play_t* p = _handleToPtr(h);
p->startedFl = true; 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 // set the end play time
if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) ) if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) )
time::setZero(p->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; 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 ) cw::rc_t cw::midi_record_play::set_thru_state( handle_t h, bool thru_fl )
{ {
rc_t rc = kOkRC; 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::kSostenutoCtlMdId, sost_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_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; 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 ) 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); 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; unsigned msgArrayCnt = 0;
am_midi_msg_t* msgArray = nullptr; 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 ) if((rc = _am_file_read( am_filename, msgArrayCnt, msgArray )) != kOkRC )
{ {
rc = cwLogError(rc,"Unable to read AM file '%s'.", cwStringNullGuard(am_filename)); 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; filesys::dirEntry_t* dirEntryArray = nullptr;
unsigned dirEntryCnt = 0; unsigned dirEntryCnt = 0;
if(( dirEntryArray = dirEntries( inDir, filesys::kDirFsFl, &dirEntryCnt )) == nullptr ) if(( dirEntryArray = filesys::dirEntries( inDir, filesys::kDirFsFl | filesys::kFullPathFsFl, &dirEntryCnt )) == nullptr )
goto errLabel; 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: errLabel:
@ -1284,3 +1562,9 @@ cw::rc_t cw::midi_record_play::am_to_midi_file( const object_t* cfg )
return rc; 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; uint8_t d1;
} midi_msg_t; } 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 ); 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 ); rc_t set_record_state( handle_t h, bool record_fl );
bool record_state( handle_t h ); 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 ); rc_t set_thru_state( handle_t h, bool record_thru );
bool thru_state( handle_t h ); bool thru_state( handle_t h );
@ -58,6 +66,7 @@ namespace cw
unsigned device_count( handle_t h ); unsigned device_count( handle_t h );
bool is_device_enabled( handle_t h, unsigned devIdx ); bool is_device_enabled( handle_t h, unsigned devIdx );
void enable_device( handle_t h, unsigned devIdx, bool enableFl ); 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 ); 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_file( const char* am_filename, const char* midi_filename );
rc_t am_to_midi_dir( const char* inDir ); rc_t am_to_midi_dir( const char* inDir );
rc_t am_to_midi_file( const object_t* cfg ); 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, kStartBtnId,
kStopBtnId, kStopBtnId,
kLiveCheckId,
kPrintMidiCheckId, kPrintMidiCheckId,
kPianoMidiCheckId, kPianoMidiCheckId,
@ -69,13 +70,15 @@ namespace cw
kStatusId, kStatusId,
/*
kHalfPedalPedalVel, kHalfPedalPedalVel,
kHalfPedalDelayMs, kHalfPedalDelayMs,
kHalfPedalPitch, kHalfPedalPitch,
kHalfPedalVel, kHalfPedalVel,
kHalfPedalDurMs, kHalfPedalDurMs,
kHalfPedalDnDelayMs, kHalfPedalDnDelayMs,
*/
kLogId, kLogId,
kFragListId, kFragListId,
@ -85,6 +88,7 @@ namespace cw
kFragPresetRowId, kFragPresetRowId,
kFragPresetSelId, kFragPresetSelId,
kFragPresetSeqSelId,
kFragPresetOrderId, kFragPresetOrderId,
kFragInGainId, kFragInGainId,
@ -94,14 +98,16 @@ namespace cw
kFragBegPlayLocId, kFragBegPlayLocId,
kFragEndPlayLocId, kFragEndPlayLocId,
kFragPlayBtnId, kFragPlayBtnId,
kFragPlaySeqBtnId,
kFragPlayAllBtnId,
kFragNoteId kFragNoteId
}; };
enum enum
{ {
kPiano_MRP_DevIdx = 0, kPiano_MRP_DevIdx = 1,
kSampler_MRP_DevIdx = 1 kSampler_MRP_DevIdx = 0
}; };
enum enum
@ -121,6 +127,7 @@ namespace cw
{ kPanelDivId, kStartBtnId, "startBtnId" }, { kPanelDivId, kStartBtnId, "startBtnId" },
{ kPanelDivId, kStopBtnId, "stopBtnId" }, { kPanelDivId, kStopBtnId, "stopBtnId" },
{ kPanelDivId, kLiveCheckId, "liveCheckId" },
{ kPanelDivId, kPrintMidiCheckId, "printMidiCheckId" }, { kPanelDivId, kPrintMidiCheckId, "printMidiCheckId" },
{ kPanelDivId, kPianoMidiCheckId, "pianoMidiCheckId" }, { kPanelDivId, kPianoMidiCheckId, "pianoMidiCheckId" },
@ -148,13 +155,14 @@ namespace cw
{ kPanelDivId, kInsertBtnId, "insertBtnId" }, { kPanelDivId, kInsertBtnId, "insertBtnId" },
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" }, { kPanelDivId, kDeleteBtnId, "deleteBtnId" },
/*
{ kPanelDivId, kHalfPedalPedalVel, "halfPedalPedalVelId" }, { kPanelDivId, kHalfPedalPedalVel, "halfPedalPedalVelId" },
{ kPanelDivId, kHalfPedalDelayMs, "halfPedalDelayMsId" }, { kPanelDivId, kHalfPedalDelayMs, "halfPedalDelayMsId" },
{ kPanelDivId, kHalfPedalPitch, "halfPedalPitchId" }, { kPanelDivId, kHalfPedalPitch, "halfPedalPitchId" },
{ kPanelDivId, kHalfPedalVel, "halfPedalVelId" }, { kPanelDivId, kHalfPedalVel, "halfPedalVelId" },
{ kPanelDivId, kHalfPedalDurMs, "halfPedalDurMsId" }, { kPanelDivId, kHalfPedalDurMs, "halfPedalDurMsId" },
{ kPanelDivId, kHalfPedalDnDelayMs, "halfPedalDnDelayMsId" }, { kPanelDivId, kHalfPedalDnDelayMs, "halfPedalDnDelayMsId" },
*/
{ kPanelDivId, kStatusId, "statusId" }, { kPanelDivId, kStatusId, "statusId" },
{ kPanelDivId, kLogId, "logId" }, { kPanelDivId, kLogId, "logId" },
@ -171,7 +179,9 @@ namespace cw
{ kFragPanelId, kFragBegPlayLocId, "fragBegPlayLocId" }, { kFragPanelId, kFragBegPlayLocId, "fragBegPlayLocId" },
{ kFragPanelId, kFragEndPlayLocId, "fragEndPlayLocId" }, { kFragPanelId, kFragEndPlayLocId, "fragEndPlayLocId" },
{ kFragPanelId, kFragPlayBtnId, "fragPlayBtnId" }, { 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* midi_play_record_cfg;
const object_t* frag_panel_cfg; const object_t* frag_panel_cfg;
const object_t* presets_cfg; const object_t* presets_cfg;
object_t* flow_proc_dict;
const object_t* flow_cfg; const object_t* flow_cfg;
midi_record_play::handle_t mrpH; midi_record_play::handle_t mrpH;
@ -210,15 +221,15 @@ namespace cw
unsigned locMapN; unsigned locMapN;
unsigned insertLoc; // last valid insert location id received from the GUI unsigned insertLoc; // last valid insert location id received from the GUI
unsigned minLoc; unsigned minLoc;
unsigned maxLoc; unsigned maxLoc;
unsigned beg_play_loc;
unsigned end_play_loc;
time::spec_t beg_play_timestamp; time::spec_t beg_play_timestamp;
time::spec_t end_play_timestamp; time::spec_t end_play_timestamp;
unsigned beg_play_loc;
unsigned end_play_loc;
preset_sel::handle_t psH; preset_sel::handle_t psH;
io_flow::handle_t ioFlowH; io_flow::handle_t ioFlowH;
@ -236,11 +247,21 @@ namespace cw
unsigned hpVel; unsigned hpVel;
unsigned hpDurMs; unsigned hpDurMs;
unsigned hpDnDelayMs; 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; } app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef ) rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const char* flow_proc_dict_fn = nullptr;
if((rc = cfg->getv( "params", params_cfgRef, if((rc = cfg->getv( "params", params_cfgRef,
"flow", app->flow_cfg)) != kOkRC ) "flow", app->flow_cfg)) != kOkRC )
@ -253,11 +274,14 @@ namespace cw
"record_fn", app->record_fn, "record_fn", app->record_fn,
"record_fn_ext", app->record_fn_ext, "record_fn_ext", app->record_fn_ext,
"score_fn", app->scoreFn, "score_fn", app->scoreFn,
"flow_proc_dict_fn",flow_proc_dict_fn,
"midi_play_record", app->midi_play_record_cfg, "midi_play_record", app->midi_play_record_cfg,
"frag_panel", app->frag_panel_cfg, "frag_panel", app->frag_panel_cfg,
"presets", app->presets_cfg, "presets", app->presets_cfg,
"crossFadeSrate", app->crossFadeSrate, "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."); rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
} }
@ -273,6 +297,12 @@ namespace cw
rc = cwLogError(kInvalidArgRC,"The record directory path is invalid."); rc = cwLogError(kInvalidArgRC,"The record directory path is invalid.");
goto errLabel; 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 // verify that the output directory exists
if((rc = filesys::isDir(app->record_dir)) != kOkRC ) if((rc = filesys::isDir(app->record_dir)) != kOkRC )
@ -321,6 +351,9 @@ namespace cw
rc_t _free( app_t& app ) 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.record_dir);
mem::release((char*&)app.scoreFn); mem::release((char*&)app.scoreFn);
preset_sel::destroy(app.psH); preset_sel::destroy(app.psH);
@ -331,25 +364,35 @@ namespace cw
return kOkRC; return kOkRC;
} }
rc_t _apply_preset( app_t* app, const time::spec_t& ts, unsigned loc, const preset_sel::frag_t* frag=nullptr )
rc_t _apply_preset( app_t* app, const time::spec_t& ts, const preset_sel::frag_t* frag=nullptr )
{ {
// if frag is NULL this is the beginning of a play session
if( frag == nullptr ) 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 ) if( frag == nullptr )
cwLogInfo("No preset fragment was found for the requested timestamp."); cwLogInfo("No preset fragment was found for the requested timestamp.");
else else
{ {
unsigned preset_idx; 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 ); cwLogInfo("No preset has been assigned to the fragment at end loc. '%i'.",frag->endLoc );
else else
{ {
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx); 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 ) if( preset_label != nullptr )
{ {
@ -395,40 +438,99 @@ namespace cw
return rc; 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; app_t* app = (app_t*)arg;
switch( actionId )
if( app->printMidiFl ) {
{ case midi_record_play::kPlayerStoppedActionId:
const unsigned buf_byte_cnt = 256; app->seqStartedFl=false;
char buf[ buf_byte_cnt ]; _set_status(app,"Done");
break;
// if this event is not in the score case midi_record_play::kMidiEventActionId:
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( app->printMidiFl )
if( preset_sel::track_timestamp( app->psH, timestamp, f ) ) {
{ const unsigned buf_byte_cnt = 256;
//printf("NEW FRAG: id:%i loc:%i\n", f->fragId, f->endLoc ); char buf[ buf_byte_cnt ];
_apply_preset( app, timestamp, f );
// if this event is not in the score
if( f != nullptr ) if( id == kInvalidId )
_do_select_frag( app, f->guiUuId ); {
// 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' // Find the closest locMap equal to or after 'loc'
loc_map_t* _find_loc( app_t* app, unsigned loc ) loc_map_t* _find_loc( app_t* app, unsigned loc )
{ {
@ -468,6 +570,7 @@ namespace cw
bool rewindFl = true; bool rewindFl = true;
loc_map_t* begMap = nullptr; loc_map_t* begMap = nullptr;
loc_map_t* endMap = nullptr; loc_map_t* endMap = nullptr;
unsigned cur_loc = 0;
// if the player is already playing then stop it // if the player is already playing then stop it
if( midi_record_play::is_started(app->mrpH) ) if( midi_record_play::is_started(app->mrpH) )
@ -476,7 +579,7 @@ namespace cw
goto errLabel; 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 ) if((begMap = _find_loc(app,begLoc)) == nullptr )
@ -495,7 +598,7 @@ namespace cw
app->end_play_timestamp = endMap->timestamp; 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 // seek the player to the requested loc
if((rc = midi_record_play::seek( app->mrpH, app->beg_play_timestamp )) != kOkRC ) 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 // 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."); rc = cwLogError(rc,"Preset application failed prior to MIDI start.");
goto errLabel; goto errLabel;
} }
// start the MIDI playback // 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."); if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC )
goto errLabel; {
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: errLabel:
return rc; 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 ) 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 // 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) 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::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::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; unsigned fragPanelUuId = kInvalidId;
get_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId ); 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::kBegLocVarId, kInvalidId, fragPanelUuId, kFragBegLocId, uiChanId, uValue );
_update_frag_ui( app, fragId, preset_sel::kEndLocVarId, kInvalidId, fragPanelUuId, kFragEndLocId, 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, const char*, unsigned ){}
void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, unsigned begPlayLoc, unsigned endPlayLoc ) void _enable_frag_play_btn( app_t* app, ui_blob_t* blob, unsigned begPlayLoc, unsigned endPlayLoc )
{ {
bool enableFl = begPlayLoc < endPlayLoc; bool enableFl = begPlayLoc < endPlayLoc;
unsigned fragUuId = kInvalidId; unsigned fragUuId = kInvalidId;
unsigned fragPlayBtnUuId = kInvalidId;
if((fragUuId = frag_to_gui_id( app->psH, blob->fragId )) != 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 ); unsigned btnUuId;
if( enableFl ) if((btnUuId = uiFindElementUuId( app->ioH, fragUuId, btnIdA[i], blob->presetId )) != kInvalidId )
_clear_status(app); uiSetEnable( app->ioH, btnUuId, enableFl );
else
_set_status(app,"Invalid fragment play range.");
} }
}
if( enableFl )
_clear_status(app);
else
_set_status(app,"Invalid fragment play range.");
} }
void _disable_frag_play_btn( app_t* app, unsigned fragBegEndUuId ) void _disable_frag_play_btn( app_t* app, unsigned fragBegEndUuId )
@ -755,11 +910,24 @@ namespace cw
case preset_sel::kPresetSelectVarId: case preset_sel::kPresetSelectVarId:
_update_frag_select_flags( app, blob->fragId); _update_frag_select_flags( app, blob->fragId);
break; break;
case preset_sel::kPresetSeqSelectVarId:
_update_frag_select_flags( app, blob->fragId);
break;
case preset_sel::kPlayBtnVarId: case preset_sel::kPlayBtnVarId:
_do_play_fragment( app, blob->fragId ); _do_play_fragment( app, blob->fragId );
break; 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: case preset_sel::kBegPlayLocVarId:
{ {
unsigned endPlayLoc; 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 ) if((rc = io::uiCreateNumb( app->ioH, uuId, colUuId, nullEleName, kFragPresetOrderId, chanId, nullClass, nullptr, 0, presetN, 1, 0 )) != kOkRC )
goto errLabel; 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 ); _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: errLabel:
if(rc != kOkRC ) if(rc != kOkRC )
rc = cwLogError(rc,"Preset control index '%i' create failed."); 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 // 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, 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, 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, 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, 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, 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, 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, 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, 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 // create each of the preset controls
@ -1064,8 +1241,8 @@ namespace cw
rc_t rc = kOkRC; rc_t rc = kOkRC;
unsigned midiEventN = 0; unsigned midiEventN = 0;
bool firstLoadFl = !app->scoreH.isValid(); bool firstLoadFl = !app->scoreH.isValid();
unsigned minLoc = firstLoadFl ? 0 : app->minLoc; //unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc; //unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
// if the score is already loaded // if the score is already loaded
//if( app->scoreH.isValid() ) //if( app->scoreH.isValid() )
@ -1075,26 +1252,41 @@ namespace cw
_set_status(app,"Loading..."); _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 ) if((rc = _load_piano_score(app,midiEventN)) != kOkRC )
goto errLabel; goto errLabel;
/*
if( !firstLoadFl) if( !firstLoadFl)
{ {
minLoc = std::max(minLoc,app->minLoc); minLoc = std::max(minLoc,app->minLoc);
maxLoc = std::min(maxLoc,app->maxLoc); maxLoc = std::min(maxLoc,app->maxLoc);
} }
*/
// reset the timestamp tracker // reset the timestamp tracker
track_timestamp_reset( app->psH ); track_loc_reset( app->psH );
// set the range of the global play location controls // set the range of the global play location controls
if( firstLoadFl ) if( firstLoadFl )
{ {
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, minLoc ); unsigned begPlayLocUuId = io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId);
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, maxLoc ); 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 // enable the 'End Loc' number box since the score is loaded
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
} }
@ -1105,6 +1297,7 @@ namespace cw
// enable the start/stop buttons // enable the start/stop buttons
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true ); 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, kStopBtnId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), true );
// restore the fragment records // restore the fragment records
if( firstLoadFl ) if( firstLoadFl )
@ -1319,6 +1512,7 @@ namespace cw
if( mrp_dev_idx <= midi_record_play::device_count(app->mrpH) ) if( mrp_dev_idx <= midi_record_play::device_count(app->mrpH) )
{ {
bool enableFl = midi_record_play::is_device_enabled(app->mrpH, mrp_dev_idx ); bool enableFl = midi_record_play::is_device_enabled(app->mrpH, mrp_dev_idx );
io::uiSendValue( app->ioH, uuId, enableFl ); io::uiSendValue( app->ioH, uuId, enableFl );
} }
} }
@ -1357,6 +1551,7 @@ namespace cw
return rc; return rc;
} }
/*
rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value ) rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value )
{ {
switch( appId ) switch( appId )
@ -1391,7 +1586,7 @@ namespace cw
} }
return kOkRC; return kOkRC;
} }
rc_t _on_echo_half_pedal( app_t* app, unsigned appId, unsigned uuId ) rc_t _on_echo_half_pedal( app_t* app, unsigned appId, unsigned uuId )
{ {
switch( appId ) switch( appId )
@ -1426,7 +1621,7 @@ namespace cw
} }
return kOkRC; return kOkRC;
} }
*/
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m ) 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 // 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, kStartBtnId ), false );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), 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 ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), false );
const preset_sel::frag_t* f = preset_sel::get_fragment_base( app->psH ); const preset_sel::frag_t* f = preset_sel::get_fragment_base( app->psH );
for(; f!=nullptr; f=f->link) for(; f!=nullptr; f=f->link)
_update_frag_ui( app, f->fragId ); _update_frag_ui( app, f->fragId );
_do_load(app); //_do_load(app);
return rc; return rc;
} }
@ -1495,6 +1691,10 @@ namespace cw
_do_stop_play(app); _do_stop_play(app);
break; break;
case kLiveCheckId:
app->useLiveMidiFl = m.value->u.b;
break;
case kPrintMidiCheckId: case kPrintMidiCheckId:
app->printMidiFl = m.value->u.b; app->printMidiFl = m.value->u.b;
break; break;
@ -1543,6 +1743,7 @@ namespace cw
_on_ui_delete_btn(app); _on_ui_delete_btn(app);
break; break;
/*
case kHalfPedalPedalVel: case kHalfPedalPedalVel:
case kHalfPedalDelayMs: case kHalfPedalDelayMs:
case kHalfPedalPitch: case kHalfPedalPitch:
@ -1551,6 +1752,7 @@ namespace cw
case kHalfPedalDnDelayMs: case kHalfPedalDnDelayMs:
_on_ui_half_pedal_value( app, m.appId, m.uuId, m.value->u.u ); _on_ui_half_pedal_value( app, m.appId, m.uuId, m.value->u.u );
break; break;
*/
case kFragInGainId: case kFragInGainId:
_on_ui_frag_value( app, m.uuId, m.value->u.d); _on_ui_frag_value( app, m.uuId, m.value->u.d);
@ -1580,7 +1782,15 @@ namespace cw
case kFragPlayBtnId: case kFragPlayBtnId:
_on_ui_frag_value( app, m.uuId, m.value->u.b ); _on_ui_frag_value( app, m.uuId, m.value->u.b );
break; 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: case kFragNoteId:
_on_ui_frag_value( app, m.uuId, m.value->u.s ); _on_ui_frag_value( app, m.uuId, m.value->u.s );
break; break;
@ -1592,6 +1802,11 @@ namespace cw
case kFragPresetSelId: case kFragPresetSelId:
_on_ui_frag_value( app, m.uuId, m.value->u.b ); _on_ui_frag_value( app, m.uuId, m.value->u.b );
break; break;
case kFragPresetSeqSelId:
_on_ui_frag_value( app, m.uuId, m.value->u.b );
break;
} }
return rc; return rc;
@ -1644,7 +1859,7 @@ namespace cw
errLabel: errLabel:
return rc; return rc;
} }
rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m ) rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -1677,6 +1892,7 @@ namespace cw
_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId ); _on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
break; break;
/*
case kHalfPedalPedalVel: case kHalfPedalPedalVel:
case kHalfPedalDelayMs: case kHalfPedalDelayMs:
case kHalfPedalPitch: case kHalfPedalPitch:
@ -1685,7 +1901,7 @@ namespace cw
case kHalfPedalDnDelayMs: case kHalfPedalDnDelayMs:
_on_echo_half_pedal( app, m.appId, m.uuId ); _on_echo_half_pedal( app, m.appId, m.uuId );
break; break;
*/
} }
return rc; return rc;
@ -1730,6 +1946,7 @@ namespace cw
break; break;
case ui::kIdleOpId: case ui::kIdleOpId:
_do_seq_exec( app );
break; break;
case ui::kInvalidOpId: case ui::kInvalidOpId:
@ -1749,11 +1966,18 @@ namespace cw
rc_t rc = kOkRC; rc_t rc = kOkRC;
app_t* app = reinterpret_cast<app_t*>(arg); 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 ); midi_record_play::exec( app->mrpH, *m );
if( midi_record_play::is_started(app->mrpH) ) 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() ) if( app->ioFlowH.isValid() )
@ -1771,6 +1995,8 @@ namespace cw
break; break;
case io::kMidiTId: case io::kMidiTId:
if( app->useLiveMidiFl && app->mrpH.isValid() )
_on_live_midi( app, *m );
break; break;
case io::kAudioTId: 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; 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 // 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."); rc = cwLogError(rc,"The IO Flow controller create failed.");
goto errLabel; 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 // execute the io framework
while( !isShuttingDown(app.ioH)) while( !io::isShuttingDown(app.ioH))
{ {
exec(app.ioH); io::exec(app.ioH);
sleepMs(50); sleepMs(50);
} }

View File

@ -5,7 +5,7 @@ namespace cw
{ {
namespace preset_sel_app 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, kInvalidMidiByte = 128,
kMidiNoteCnt = kInvalidMidiByte, kMidiNoteCnt = kInvalidMidiByte,
kMidiCtlCnt = kInvalidMidiByte, kMidiCtlCnt = kInvalidMidiByte,
kMidiVelCnt = kInvalidMidiByte,
kMidiPgmCnt = kInvalidMidiByte, kMidiPgmCnt = kInvalidMidiByte,
kInvalidMidiPitch = kInvalidMidiByte, kInvalidMidiPitch = kInvalidMidiByte,
kInvalidMidiVelocity = 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 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 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 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 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 ); } 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 no input
if( rc == -EAGAIN ) if( rc == -EAGAIN )
{
// TODO: report or at least count error
break; break;
}
// if input buffer overrun // if input buffer overrun
if( rc == -ENOSPC ) if( rc == -ENOSPC )
{
// TODO: report or at least count error
break; break;
}
// get the device this event arrived from // get the device this event arrived from
if( p->prvRcvDev==NULL || p->prvRcvDev->clientId != ev->source.client ) if( p->prvRcvDev==NULL || p->prvRcvDev->clientId != ev->source.client )
p->prvRcvDev = _cmMpClientIdToDev(p,ev->source.client); p->prvRcvDev = _cmMpClientIdToDev(p,ev->source.client);
@ -150,7 +156,7 @@ namespace cw
if( p->prvRcvDev == NULL || p->prvRcvPort == NULL ) if( p->prvRcvDev == NULL || p->prvRcvPort == NULL )
continue; continue;
//printf("%i %x\n",ev->type,ev->type); //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); //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; 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]; trackMsg_t* m0 = noteM[k];
if( m0 == NULL ) 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 else
{ {
// a key was released - so it should not already be up // a key was released - so it should not already be up
if( noteGateM[k]==0 ) 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 else
{ {
noteGateM[k] -= 1; // update the note gate state 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( isSustainPedalUp(m) )
{ {
// if the sustain channel is already up // 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); cwLogWarning("%i : The sustain pedal release message was received with no previous pedal down.",m->uid);
if( sustGateV[ch] >= 1 ) if( sustGateV[ch] >= 1 )
@ -1830,7 +1878,7 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
if( isSostenutoPedalUp(m) ) if( isSostenutoPedalUp(m) )
{ {
// if the sustain channel is already up // 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); cwLogWarning("%i : The sostenuto pedal release message was received with no previous pedal down.",m->uid);
if( sostGateV[ch] >= 1 ) if( sostGateV[ch] >= 1 )
@ -2157,7 +2205,7 @@ cw::rc_t cw::midi::file::genPlotFile( const char* midiFn, const char* outFn )
return rc; 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; rc_t rc = kOkRC;
handle_t mfH; 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 ) if((rc = open( mfH, midiFn )) != kOkRC )
return cwLogError(rc,"The MIDI file object could not be opened from '%s'.",cwStringNullGuard(midiFn)); 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 ) 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; rc_t rc = kOkRC;
const object_t* o;
if((o = cfg->find("rpt")) != nullptr ) const object_t* o;
rc = _testReport(o);
if((o = cfg->find("csv")) != nullptr ) for(unsigned i=0; i<cfg->child_count(); ++i)
rc = _testCsv(o); {
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; 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 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() // Generate a text file reportusing cmMIdiFilePrintMsgs()
rc_t report( const char* midiFn, log::handle_t logH ); rc_t report( const char* midiFn, log::handle_t logH );

View File

@ -200,6 +200,56 @@ namespace cw
return nullptr; 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 ) rc_t _validate_preset_id( const frag_t* frag, unsigned preset_id )
@ -234,9 +284,13 @@ namespace cw
case kPresetSelectVarId: case kPresetSelectVarId:
for(unsigned i=0; i<f->presetN; ++i) for(unsigned i=0; i<f->presetN; ++i)
f->presetA[i].playFl = f->presetA[i].preset_idx == presetId ? value : false; f->presetA[i].playFl = f->presetA[i].preset_idx == presetId ? value : false;
break; break;
case kPresetSeqSelectVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
f->presetA[ presetId ].seqFl = value;
break;
case kPresetOrderVarId: case kPresetOrderVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC ) if((rc = _validate_preset_id(f, presetId )) == kOkRC )
f->presetA[ presetId ].order = value; f->presetA[ presetId ].order = value;
@ -269,6 +323,14 @@ namespace cw
case kPlayBtnVarId: case kPlayBtnVarId:
break; break;
case kPlaySeqBtnVarId:
f->seqAllFl = false;
break;
case kPlaySeqAllBtnVarId:
f->seqAllFl = true;
break;
case kMasterWetInGainVarId: case kMasterWetInGainVarId:
p->master_wet_in_gain = value; p->master_wet_in_gain = value;
break; break;
@ -329,6 +391,11 @@ namespace cw
valueRef = f->presetA[ presetId ].playFl; valueRef = f->presetA[ presetId ].playFl;
break; break;
case kPresetSeqSelectVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
valueRef = f->presetA[ presetId ].seqFl;
break;
case kPresetOrderVarId: case kPresetOrderVarId:
if((rc = _validate_preset_id(f, presetId )) == kOkRC ) if((rc = _validate_preset_id(f, presetId )) == kOkRC )
valueRef = f->presetA[ presetId ].order; valueRef = f->presetA[ presetId ].order;
@ -360,6 +427,13 @@ namespace cw
case kPlayBtnVarId: case kPlayBtnVarId:
break; break;
case kPlaySeqBtnVarId:
break;
case kPlaySeqAllBtnVarId:
valueRef = f->seqAllFl;
break;
case kMasterWetInGainVarId: case kMasterWetInGainVarId:
valueRef = p->master_wet_in_gain; 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::spec_t t0;
time::setZero(t0); time::setZero(t0);
unsigned elapsedMs = time::elapsedMs(t0,ts); //unsigned elapsedMs = time::elapsedMs(t0,ts);
double mins = elapsedMs / 60000.0; //double mins = elapsedMs / 60000.0;
// if this is the first call to 'track_timestamp()'. // 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; 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) 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; 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 ) 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 typedef struct preset_str
{ {
bool playFl; // play this preset bool playFl; // play this preset
bool seqFl; // play this preset during sequencing.
unsigned preset_idx; // preset index into preset_labelA[]. unsigned preset_idx; // preset index into preset_labelA[].
unsigned order; // unsigned order; //
} preset_t; } preset_t;
@ -34,6 +35,7 @@ namespace cw
unsigned presetN; unsigned presetN;
bool uiSelectFl; 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* link;
struct frag_str* prev; struct frag_str* prev;
@ -51,12 +53,15 @@ namespace cw
kBegPlayLocVarId, kBegPlayLocVarId,
kEndPlayLocVarId, kEndPlayLocVarId,
kPlayBtnVarId, kPlayBtnVarId,
kPlaySeqBtnVarId,
kPlaySeqAllBtnVarId,
kNoteVarId, kNoteVarId,
kPresetOrderVarId, // preset order value kPresetOrderVarId, // preset order value
kPresetSelectVarId, // select a preset to play kPresetSelectVarId, // select a preset to play
kPlayEnableVarId, // include in the segment to play kPresetSeqSelectVarId, // sequence preset selections to play
kDryFlVarId, // play this fragment dry kPlayEnableVarId, // include in the segment to play
kDryFlVarId, // play this fragment dry
kBaseMasterVarId, // All 'master' variables have id's greater than kBaseMasterVarId kBaseMasterVarId, // All 'master' variables have id's greater than kBaseMasterVarId
@ -110,8 +115,15 @@ namespace cw
void track_timestamp_reset( handle_t h ); 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 ); 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. // 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 write( handle_t h, const char* fn );
rc_t read( 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 handle<struct thread_mach_str> handle_t;
typedef thread::cbFunc_t threadFunc_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 create( handle_t& hRef, threadFunc_t threadFunc=nullptr, void* contextArray=nullptr, unsigned contexRecdByteN=0, unsigned threadN=0 );
rc_t destroy( handle_t& hRef ); 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 ); rc_t add( handle_t h, threadFunc_t threadFunc, void* arg );
// Start all threads
rc_t start( handle_t h ); rc_t start( handle_t h );
// Stop all threads.
rc_t stop( handle_t h ); rc_t stop( handle_t h );
// Check if all threads are shutdown.
bool is_shutdown( handle_t h ); bool is_shutdown( handle_t h );
} }
} }

195
cwUi.cpp
View File

@ -89,6 +89,7 @@ namespace cw
char* recvBuf; char* recvBuf;
unsigned recvBufN; unsigned recvBufN;
unsigned recvBufIdx; unsigned recvBufIdx;
unsigned recvShiftN;
unsigned* sessA; // sessA[ sessN ] array of wsSessId's unsigned* sessA; // sessA[ sessN ] array of wsSessId's
unsigned sessN; unsigned sessN;
@ -1047,7 +1048,54 @@ namespace cw
{ {
return _setPropertyValue( h, propertyStr,uuId,enableFl ? 1 : 0 ); 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->recvBuf = mem::allocZ<char>(fmtBufByteN);
p->recvBufN = fmtBufByteN; p->recvBufN = fmtBufByteN;
p->recvBufIdx = 0; p->recvBufIdx = 0;
p->recvShiftN = 0;
// create the root element // create the root element
if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId ) 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 ) cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg, unsigned msgByteN )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
ui_t* p = _handleToPtr(h); ui_t* p = _handleToPtr(h);
opId_t opId = kInvalidOpId; opId_t opId = kInvalidOpId;
value_t value; ele_t* ele = nullptr;
ele_t* ele; const char* msg = nullptr;
value_t value;
/*
const char* src_msg = (const char*)void_msg; const char* src_msg = (const char*)void_msg;
const char* msg = src_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 // the message is being processed so the buffer will end up empty
// (if it was being used) // (if it was being used)
p->recvBufIdx = 0; p->recvBufIdx = 0;
*/
// parse the 'opId' from the message
opId = _labelToOpId(msg);
// buffer the incoming msg
switch( opId ) 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 // parse the 'opId' from the message
_onNewRemoteUi( p, wsSessId ); 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. // Pass on the 'init' msg to the app.
p->uiCbFunc( p->uiCbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break; break;
case kValueOpId: case kValueOpId:
if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr ) if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'value' message parse failed."); cwLogError(kOpFailRC,"UI 'value' message parse failed.");
else else
{ {
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
} }
break; break;
case kCorruptOpId: case kCorruptOpId:
if((ele = _parse_corrupt_msg(p, (const char*)msg )) == nullptr ) if((ele = _parse_corrupt_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'corrupt' message parse failed."); cwLogError(kOpFailRC,"UI 'corrupt' message parse failed.");
else else
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
break; break;
case kClickOpId: case kClickOpId:
if((ele = _parse_click_msg(p, (const char*)msg )) == nullptr ) if((ele = _parse_click_msg(p, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'click' message parse failed."); cwLogError(kOpFailRC,"UI 'click' message parse failed.");
else else
{ {
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
} }
break; break;
case kSelectOpId: case kSelectOpId:
if((ele = _parse_select_msg(p, value, (const char*)msg )) == nullptr ) if((ele = _parse_select_msg(p, value, (const char*)msg )) == nullptr )
cwLogError(kOpFailRC,"UI 'select' message parse failed."); cwLogError(kOpFailRC,"UI 'select' message parse failed.");
else else
{ {
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId, &value );
} }
break; break;
case kEchoOpId: case kEchoOpId:
if((ele = _parse_echo_msg(p,(const char*)msg)) == nullptr ) if((ele = _parse_echo_msg(p,(const char*)msg)) == nullptr )
cwLogError(kOpFailRC,"UI Echo message parse failed."); cwLogError(kOpFailRC,"UI Echo message parse failed.");
else else
{ {
p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId,nullptr ); p->uiCbFunc( p->uiCbArg, wsSessId, opId, ele->logical_parent->appId, ele->uuId, ele->appId, ele->chanId,nullptr );
} }
break; break;
case kIdleOpId: case kIdleOpId:
p->uiCbFunc( p->uiCbArg, kInvalidId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr ); p->uiCbFunc( p->uiCbArg, kInvalidId, opId, kInvalidId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break; break;
case kInvalidOpId: case kInvalidOpId:
cwLogError(kInvalidIdRC,"The UI received a NULL op. id."); cwLogError(kInvalidIdRC,"The UI received a NULL op. id.");
break; break;
default: default:
cwLogError(kInvalidIdRC,"The UI received an unknown op. id."); cwLogError(kInvalidIdRC,"The UI received an unknown op. id.");
break; break;
} // switch opId } // switch opId
}
errLabel:
return rc; return rc;
} }

View File

@ -13,27 +13,27 @@ namespace cw
typedef enum typedef enum
{ {
kInvalidOpId, kInvalidOpId, // 0
kConnectOpId, // A new remote user interface was connected kConnectOpId, // 1 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. 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, // The value of a remote user interface control changed. Send this value to the application engine. kValueOpId, // 3 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. kCorruptOpId, // 4 The value of the remote user interface is invalid or corrupt.
kClickOpId, // A element on a remote user interface was clicked. kClickOpId, // 5 A element on a remote user interface was clicked.
kSelectOpId, // An element on a remote user interface was is 'selected' or 'deselected'. kSelectOpId, // 6 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. 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, // The application (UI server) is idle and waiting for the next event from a remote UI. kIdleOpId, // 8 The application (UI server) is idle and waiting for the next event from a remote UI.
kDisconnectOpId // A reemot user interface was disconnected. kDisconnectOpId // 9 A remote user interface was disconnected.
} opId_t; } opId_t;
typedef enum typedef enum
{ {
kInvalidTId, kInvalidTId, // 0
kBoolTId, kBoolTId, // 1
kIntTId, kIntTId, // 2
kUIntTId, kUIntTId, // 3
kFloatTId, kFloatTId, // 4
kDoubleTId, kDoubleTId, // 5
kStringTId kStringTId // 6
} dtypeId_t; } dtypeId_t;

View File

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

View File

@ -21,10 +21,20 @@
padding-top: 5px; padding-top: 5px;
} }
.fragPanelRow {
padding-right: 15px;
padding-left: 15px;
justify-content: space-around;
}
.fragPresetCtl { .fragPresetCtl {
width: 50px; width: 50px;
} }
.fragPresetCtl:nth-of-type(even){
border: 1px solid Yellow;
}
.fragPresetCtl .uiNumber { .fragPresetCtl .uiNumber {
border: 1px solid LightSteelBlue; border: 1px solid LightSteelBlue;
width: 80%; 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)); } ele.onclick = function() { ui_send_bool_value(this,dom_get_checkbox(this.id)); }
if( !d.hasOwnProperty('value') ) if( !d.hasOwnProperty('value') )
{
ui_send_echo(ele) ui_send_echo(ele)
}
else else
{ {
dom_set_checkbox(ele.id, d.value ); dom_set_checkbox(ele.id, d.value );
@ -870,8 +872,6 @@ function ui_create_list( parent_ele, d )
function ui_set_value( d ) function ui_set_value( d )
{ {
//console.log(d)
var eleId = d.uuId.toString() var eleId = d.uuId.toString()
var ele = dom_id_to_ele(eleId) var ele = dom_id_to_ele(eleId)
@ -1089,7 +1089,9 @@ function ui_create( d )
var parent_ele = ui_get_parent(d.parentUuId); var parent_ele = ui_get_parent(d.parentUuId);
var ele = null; var ele = null;
if( parent_ele != null ) if( parent_ele == null )
console.log("Parent ele not found.",d)
else
{ {
switch( d.type ) switch( d.type )
{ {

View File

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