Merge branch 'master' of gitea.larke.org:kevin/libcw
This commit is contained in:
commit
83884b5424
@ -40,6 +40,8 @@ libcwSRC += src/libcw/cwUi.cpp src/libcw/cwUiTest.cpp
|
||||
|
||||
endif
|
||||
|
||||
libcwHDR += src/libcw/cwKeyboard.h
|
||||
libcwSRC += src/libcw/cwKeyboard.cpp
|
||||
|
||||
libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h
|
||||
libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp
|
||||
@ -56,8 +58,8 @@ libcwHDR += src/libcw/cwMidiPort.h src/libcw/cwAudioD
|
||||
libcwSRC += src/libcw/cwMidiPort.cpp src/libcw/cwMidiAlsa.cpp src/libcw/cwAudioDeviceAlsa.cpp src/libcw/cwAudioDeviceTest.cpp
|
||||
|
||||
if cwWEBSOCK
|
||||
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h
|
||||
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp
|
||||
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoMinTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h
|
||||
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoMinTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp
|
||||
|
||||
libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h
|
||||
libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp
|
||||
@ -65,6 +67,9 @@ libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp
|
||||
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h
|
||||
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp
|
||||
|
||||
# libcwHDR += src/libcw/cwCmInterface.h src/libcw/cwScoreFollower.h
|
||||
# libcwSRC += src/libcw/cwCmInterface.cpp src/libcw/cwScoreFollower.cpp
|
||||
|
||||
endif
|
||||
|
||||
|
||||
|
49
README.md
49
README.md
@ -2,6 +2,22 @@
|
||||
|
||||
# To Do
|
||||
|
||||
- Remove applications from the libcw folder and put them in their
|
||||
own folders.
|
||||
|
||||
- The UI needs to be better documented. Start by giving clear names
|
||||
to the various parts: Browser, UI Manager, UI Server, Application.
|
||||
Maybe describe in Model,View,Controller terms?
|
||||
|
||||
- Document the meaning and way that id's and names/labels are used,
|
||||
and intended to be used, and found by UI. As it is they are confusing.
|
||||
|
||||
- The UI app id map should be validated after the UI is created.
|
||||
In otherwords the parent/child pairs shoud actually exists.
|
||||
|
||||
- Arrange the project layout so that all the UI based apps use the same ui.js.
|
||||
Currently changes and improvements to one version of ui.js cannot be automatically
|
||||
shared.
|
||||
|
||||
- The ui manageer should buffer the current valid value of a given control
|
||||
so that the value can be accessed synchronously. This would prevent the application
|
||||
@ -11,14 +27,19 @@ read by the app (e.g. getUiValue( appId, valueRef)) just prior to being used.
|
||||
As it is the UI values that are on the interface cannot be accessed synchronously
|
||||
instead the app is forced to notice all 'value' changes and store the last legal value.
|
||||
|
||||
- Add an ui::appIdToUuId() that returns the first matching appId, and then
|
||||
optionally looks for duplicates as an error checking scheme.
|
||||
|
||||
- The ui eleA[] data structure should be changed to a tree
|
||||
because the current expandable array allows empty slots which need to be checked
|
||||
for whenever the list is iterated. It is also very inefficient to delete from the
|
||||
eleA[] because an exhaustive search is required to find all the children of the
|
||||
element to be deleted.
|
||||
|
||||
- Should a warning be issued by audioBuf functions which return a set of values:
|
||||
muteFlags(),toneFlags(), gain( ... gainA) but where the size of the dest array
|
||||
does not match the actual number of channesl?
|
||||
|
||||
- Document the meaning and way that id's and names/labels are used,
|
||||
and intended to be used, and found by UI. As it is they are confusing.
|
||||
|
||||
|
||||
|
||||
|
||||
- Any socket function which takes a IP/port address should have a version which also takes a sockaddr_in*.
|
||||
@ -47,17 +68,7 @@ This is easy to reproduce by simply decreasing the size of the buffers in the pr
|
||||
- numeric_convert() d_min is NOT zero, it's smallest positive number, this fails when src == 0.
|
||||
min value is now set to zero.
|
||||
|
||||
- The UI app id map should be validated after the UI is created.
|
||||
In otherwords the parent/child pairs shoud actually exists.
|
||||
|
||||
- Add an ui::appIdToUuId() that returns the first matching appId, and then
|
||||
optionally looks for duplicates as an error checking scheme.
|
||||
|
||||
- The ui eleA[] data structure should be changed to a tree
|
||||
because the current expandable array allows empty slots which need to be checked
|
||||
for whenever the list is iterated. It is also very inefficient to delete from the
|
||||
eleA[] because an exhaustive search is required to find all the children of the
|
||||
element to be deleted.
|
||||
|
||||
- thread needs setters and getters for internal variables
|
||||
|
||||
@ -65,7 +76,7 @@ element to be deleted.
|
||||
|
||||
- cwAudioBuf.cpp - the ch->fn in update() does not have the correct memory fence.
|
||||
|
||||
- change file names to match object names
|
||||
- Change file names to match object names
|
||||
|
||||
- Replace 24 bit read/write in cwAudioFile.cpp
|
||||
|
||||
@ -115,10 +126,14 @@ struct in_addr {
|
||||
|
||||
# Development Setup
|
||||
|
||||
0)
|
||||
```
|
||||
sudo dnf install g++ fftw-devel alsa-lib-devel libubsan
|
||||
```
|
||||
1) Install libwebsockets.
|
||||
|
||||
```
|
||||
sudo dnf install g++ openssl-devel cmake
|
||||
sudo dnf install openssl-devel cmake
|
||||
cd sdk
|
||||
git clone https://libwebsockets.org/repo/libwebsockets
|
||||
cd libwebsockets
|
||||
@ -145,7 +160,6 @@ struct in_addr {
|
||||
|
||||
# Flow Notes:
|
||||
|
||||
- Add a version of var_register() that both registers and returns the value of the variable.
|
||||
|
||||
- When a variable has a variant with a numberic channel should the 'all' channel variant be removed?
|
||||
|
||||
@ -167,6 +181,7 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o
|
||||
|
||||
- Create a master cross-fader.
|
||||
|
||||
DONE: Add a version of var_register() that both registers and returns the value of the variable.
|
||||
|
||||
Flow Instance Creation:
|
||||
-----------------------
|
||||
|
110
cwCmInterface.cpp
Normal file
110
cwCmInterface.cpp
Normal 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
19
cwCmInterface.h
Normal 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
|
@ -46,7 +46,8 @@ namespace cw
|
||||
kTestFailRC, // 27
|
||||
kInvalidStateRC, // 28
|
||||
kTypeMismatchRC, // 29
|
||||
kBaseAppRC // 30
|
||||
kNotImplementedRC, // 30
|
||||
kBaseAppRC // 31
|
||||
} cwRC_t;
|
||||
|
||||
typedef unsigned rc_t;
|
||||
|
10
cwFlow.cpp
10
cwFlow.cpp
@ -137,6 +137,7 @@ namespace cw
|
||||
const char* type_str = nullptr;
|
||||
unsigned type_flag = 0;
|
||||
bool srcVarFl = false;
|
||||
bool srcOptFl = false;
|
||||
var_desc_t* vd = mem::allocZ<var_desc_t>();
|
||||
|
||||
vd->label = var_obj->pair_label();
|
||||
@ -158,7 +159,9 @@ namespace cw
|
||||
}
|
||||
|
||||
// get the variable description
|
||||
if((rc = vd->cfg->getv_opt("srcFl", srcVarFl,"value",vd->val_cfg)) != kOkRC )
|
||||
if((rc = vd->cfg->getv_opt("srcFl", srcVarFl,
|
||||
"srcOptFl", srcOptFl,
|
||||
"value",vd->val_cfg)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing optional fields failed on class:%s variable: '%s'.", cd->label, vd->label );
|
||||
goto errLabel;
|
||||
@ -168,9 +171,10 @@ namespace cw
|
||||
vd->type |= type_flag;
|
||||
|
||||
if( srcVarFl )
|
||||
{
|
||||
vd->flags |= kSrcVarFl;
|
||||
}
|
||||
|
||||
if( srcOptFl )
|
||||
vd->flags |= kSrcOptVarFl;
|
||||
|
||||
vd->link = cd->varDescL;
|
||||
cd->varDescL = vd;
|
||||
|
@ -564,7 +564,7 @@ namespace cw
|
||||
// print a minutes counter
|
||||
inst->durSmpN += src_abuf->frameN;
|
||||
if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 )
|
||||
printf("%5.1f min\n", inst->durSmpN/(src_abuf->srate*60));
|
||||
printf("audio file out: %5.1f min\n", inst->durSmpN/(src_abuf->srate*60));
|
||||
|
||||
}
|
||||
|
||||
@ -982,35 +982,64 @@ namespace cw
|
||||
namespace audio_merge
|
||||
{
|
||||
enum {
|
||||
kIn0PId,
|
||||
kIn1PId,
|
||||
kGainPId,
|
||||
kOutPId,
|
||||
kInBasePId,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned srcN;
|
||||
} inst_t;
|
||||
|
||||
|
||||
rc_t create( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const abuf_t* abuf0 = nullptr; //
|
||||
const abuf_t* abuf1 = nullptr;
|
||||
unsigned outChN = 0;
|
||||
unsigned frameN = 0;
|
||||
srate_t srate = 0;
|
||||
|
||||
inst_t* inst = mem::allocZ<inst_t>();
|
||||
|
||||
ctx->userPtr = inst;
|
||||
|
||||
for(unsigned i=0; 1; ++i)
|
||||
{
|
||||
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,
|
||||
kIn0PId,"in0",abuf0,
|
||||
kIn1PId,"in1",abuf1 )) != kOkRC )
|
||||
if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
assert( abuf0->frameN == abuf1->frameN );
|
||||
if( i == 0 )
|
||||
{
|
||||
frameN = abuf->frameN;
|
||||
srate = abuf->srate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: check srate and frameN are same as first src
|
||||
}
|
||||
|
||||
outChN = abuf0->chN + abuf1->chN;
|
||||
inst->srcN += 1;
|
||||
outChN += abuf->chN;
|
||||
|
||||
}
|
||||
|
||||
//outChN = abuf0->chN + abuf1->chN;
|
||||
|
||||
// register the gain
|
||||
for(unsigned i=0; i<outChN; ++i)
|
||||
@ -1018,14 +1047,20 @@ namespace cw
|
||||
goto errLabel;
|
||||
|
||||
// create the output audio buffer
|
||||
rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN );
|
||||
rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srate, outChN, frameN );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t destroy( instance_t* ctx )
|
||||
{ return kOkRC; }
|
||||
{
|
||||
inst_t* inst = (inst_t*)ctx->userPtr;
|
||||
|
||||
mem::release(inst);
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t value( instance_t* ctx, variable_t* var )
|
||||
{ return kOkRC; }
|
||||
@ -1052,6 +1087,7 @@ namespace cw
|
||||
return outChIdx;
|
||||
}
|
||||
|
||||
/*
|
||||
rc_t exec( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -1074,6 +1110,31 @@ namespace cw
|
||||
|
||||
assert( oChIdx == obuf->chN );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
*/
|
||||
|
||||
rc_t exec( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
inst_t* inst = (inst_t*)ctx->userPtr;
|
||||
abuf_t* obuf = nullptr;
|
||||
unsigned oChIdx = 0;
|
||||
|
||||
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
for(unsigned i=0; i<inst->srcN; ++i)
|
||||
{
|
||||
const abuf_t* ibuf = nullptr;
|
||||
|
||||
if((rc = var_get(ctx,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
oChIdx = _exec( ctx, ibuf, obuf, oChIdx );
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
@ -1088,6 +1149,7 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// audio_mix
|
||||
|
@ -1014,8 +1014,9 @@ void cw::flow::class_dict_print( flow_t* p )
|
||||
for(; vd!=nullptr; vd=vd->link)
|
||||
{
|
||||
const char* srcFlStr = vd->flags & kSrcVarFl ? "src" : " ";
|
||||
const char* srcOptFlStr = vd->flags & kSrcOptVarFl ? "srcOpt" : " ";
|
||||
|
||||
printf(" %10s 0x%08x %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, cwStringNullGuard(vd->docText) );
|
||||
printf(" %10s 0x%08x %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, cwStringNullGuard(vd->docText) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1162,6 +1163,17 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns
|
||||
bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned chIdx )
|
||||
{ return _var_find_on_label_and_ch(inst,label,chIdx) != nullptr; }
|
||||
|
||||
bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned chIdx )
|
||||
{
|
||||
variable_t* varPtr = nullptr;
|
||||
rc_t rc;
|
||||
|
||||
if((rc = var_find( inst, label, chIdx, varPtr )) != kOkRC )
|
||||
return false;
|
||||
|
||||
return varPtr->value != nullptr;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef )
|
||||
{
|
||||
|
@ -115,7 +115,9 @@ namespace cw
|
||||
typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var );
|
||||
enum
|
||||
{
|
||||
kSrcVarFl = 0x01
|
||||
kSrcVarFl = 0x01,
|
||||
kSrcOptVarFl = 0x02
|
||||
|
||||
};
|
||||
|
||||
typedef struct class_members_str
|
||||
@ -397,6 +399,7 @@ namespace cw
|
||||
void _var_destroy( variable_t* var );
|
||||
|
||||
bool var_exists( instance_t* inst, const char* label, unsigned chIdx );
|
||||
bool var_has_value( instance_t* inst, const char* label, unsigned chIdx );
|
||||
|
||||
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, const variable_t*& varRef );
|
||||
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, variable_t*& varRef );
|
||||
|
85
cwIo.cpp
85
cwIo.cpp
@ -38,6 +38,14 @@ namespace cw
|
||||
|
||||
struct io_str;
|
||||
|
||||
typedef struct thread_str
|
||||
{
|
||||
unsigned id;
|
||||
void* arg;
|
||||
struct io_str* p;
|
||||
struct thread_str* link;
|
||||
} thread_t;
|
||||
|
||||
typedef struct timer_str
|
||||
{
|
||||
struct io_str* io;
|
||||
@ -86,7 +94,6 @@ namespace cw
|
||||
char* label;
|
||||
unsigned sockA_index;
|
||||
unsigned userId;
|
||||
|
||||
} socket_t;
|
||||
|
||||
typedef struct io_str
|
||||
@ -102,6 +109,8 @@ namespace cw
|
||||
|
||||
object_t* cfg;
|
||||
|
||||
thread_t* threadL;
|
||||
|
||||
timer_t* timerA;
|
||||
unsigned timerN;
|
||||
|
||||
@ -141,6 +150,36 @@ namespace cw
|
||||
io_t* _handleToPtr( handle_t h )
|
||||
{ return handleToPtr<handle_t,io_t>(h); }
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Thread
|
||||
//
|
||||
bool _threadFunc( void* arg )
|
||||
{
|
||||
thread_t* t = (thread_t*)arg;
|
||||
thread_msg_t tm = { .id=t->id, .arg=t->arg };
|
||||
msg_t m;
|
||||
|
||||
m.tid = kThreadTId;
|
||||
m.u.thread = &tm;
|
||||
|
||||
t->p->cbFunc( t->p->cbArg, &m );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _threadRelease( io_t* p )
|
||||
{
|
||||
thread_t* t0 = p->threadL;
|
||||
for(; t0!=nullptr; t0=t0->link)
|
||||
{
|
||||
thread_t* t1 = t0->link;
|
||||
mem::release(t0);
|
||||
t0 = t1;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Timer
|
||||
@ -335,7 +374,10 @@ namespace cw
|
||||
|
||||
// get the serial port list node
|
||||
if((cfg = c->find("serial")) == nullptr)
|
||||
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'serial' configuration.");
|
||||
{
|
||||
cwLogWarning("No 'serial' configuration.");
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
// the serial header values
|
||||
if((rc = cfg->getv("pollPeriodMs", pollPeriodMs,
|
||||
@ -413,6 +455,7 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( p->serialPortSrvH.isValid() )
|
||||
// the service is only started if at least one serial port is enabled
|
||||
if( serialPort::portCount( serialPortSrv::serialHandle(p->serialPortSrvH) ) > 0 )
|
||||
if((rc =serialPortSrv::start( p->serialPortSrvH )) != kOkRC )
|
||||
@ -461,8 +504,10 @@ namespace cw
|
||||
|
||||
// get the MIDI port cfg
|
||||
if((cfg = c->find("midi")) == nullptr)
|
||||
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'MIDI' configuration.");
|
||||
|
||||
{
|
||||
cwLogWarning("No 'MIDI' configuration.");
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
if((rc = cfg->getv(
|
||||
"parserBufByteN", parserBufByteN )) != kOkRC )
|
||||
@ -586,8 +631,8 @@ namespace cw
|
||||
// get the socket configuration node
|
||||
if((node = cfg->find("socket")) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Unable to locate the 'socket' configuration node.");
|
||||
goto errLabel;
|
||||
cwLogWarning("No 'socket' configuration node.");
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
// get the required socket arguments
|
||||
@ -1469,7 +1514,10 @@ namespace cw
|
||||
|
||||
// get the audio port node
|
||||
if((node = cfg->find("audio")) == nullptr )
|
||||
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'audio' configuration node.");
|
||||
{
|
||||
cwLogWarning("No 'audio' configuration node.");
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
// get the meterMs value
|
||||
if((rc = node->getv("meterMs", p->audioMeterCbPeriodMs, "threadTimeOutMs", p->audioThreadTimeOutMs )) != kOkRC )
|
||||
@ -1863,6 +1911,29 @@ void cw::io::report( handle_t h )
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Thread
|
||||
//
|
||||
|
||||
cw::rc_t cw::io::threadCreate( handle_t h, unsigned id, void* arg )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
io_t* p = _handleToPtr(h);
|
||||
thread_t* t = mem::allocZ<thread_t>(1);
|
||||
|
||||
t->id = id;
|
||||
t->arg = arg;
|
||||
t->p = p;
|
||||
t->link = p->threadL;
|
||||
p->threadL = t;
|
||||
|
||||
if((rc = thread_mach::add( p->threadMachH, _threadFunc, t )) != kOkRC )
|
||||
rc = cwLogError(rc,"Thread create failed.");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Timer
|
||||
|
13
cwIo.h
13
cwIo.h
@ -27,6 +27,7 @@ namespace cw
|
||||
|
||||
enum
|
||||
{
|
||||
kThreadTId,
|
||||
kTimerTId,
|
||||
kSerialTId,
|
||||
kMidiTId,
|
||||
@ -37,6 +38,12 @@ namespace cw
|
||||
kUiTId
|
||||
};
|
||||
|
||||
typedef struct thread_msg_str
|
||||
{
|
||||
unsigned id;
|
||||
void* arg;
|
||||
} thread_msg_t;
|
||||
|
||||
typedef struct timer_msg_str
|
||||
{
|
||||
unsigned id;
|
||||
@ -121,6 +128,7 @@ namespace cw
|
||||
unsigned tid;
|
||||
union
|
||||
{
|
||||
thread_msg_t* thread;
|
||||
timer_msg_t* timer;
|
||||
serial_msg_t* serial;
|
||||
midi_msg_t* midi;
|
||||
@ -151,6 +159,11 @@ namespace cw
|
||||
bool isShuttingDown( handle_t h );
|
||||
void report( handle_t h );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Thread
|
||||
//
|
||||
rc_t threadCreate( handle_t h, unsigned id, void* arg );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -35,9 +35,11 @@ namespace cw
|
||||
kMidiThruCheckId,
|
||||
kCurMidiEvtCntId,
|
||||
kTotalMidiEvtCntId,
|
||||
kMidiMuteCheckId,
|
||||
|
||||
kCurAudioSecsId,
|
||||
kTotalAudioSecsId,
|
||||
kAudioMuteCheckId,
|
||||
|
||||
kSaveBtnId,
|
||||
kOpenBtnId,
|
||||
@ -66,10 +68,11 @@ namespace cw
|
||||
{ kPanelDivId, kMidiThruCheckId, "midiThruCheckId" },
|
||||
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
|
||||
{ kPanelDivId, kTotalMidiEvtCntId, "totalMidiEvtCntId" },
|
||||
{ kPanelDivId, kMidiMuteCheckId, "midiMuteCheckId" },
|
||||
|
||||
{ kPanelDivId, kCurAudioSecsId, "curAudioSecsId" },
|
||||
{ kPanelDivId, kTotalAudioSecsId, "totalAudioSecsId" },
|
||||
|
||||
{ kPanelDivId, kAudioMuteCheckId, "audioMuteCheckId" },
|
||||
|
||||
{ kPanelDivId, kSaveBtnId, "saveBtnId" },
|
||||
{ kPanelDivId, kOpenBtnId, "openBtnId" },
|
||||
@ -90,6 +93,9 @@ namespace cw
|
||||
|
||||
midi_record_play::handle_t mrpH;
|
||||
audio_record_play::handle_t arpH;
|
||||
|
||||
const object_t* midi_play_record_cfg;
|
||||
|
||||
} app_t;
|
||||
|
||||
rc_t _parseCfg(app_t* app, const object_t* cfg )
|
||||
@ -99,7 +105,8 @@ namespace cw
|
||||
if((rc = cfg->getv(
|
||||
"record_dir", app->record_dir,
|
||||
"record_folder", app->record_folder,
|
||||
"record_fn_ext", app->record_fn_ext)) != kOkRC )
|
||||
"record_fn_ext", app->record_fn_ext,
|
||||
"midi_play_record", app->midi_play_record_cfg)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Audio MIDI app configuration parse failed.");
|
||||
}
|
||||
@ -173,6 +180,15 @@ namespace cw
|
||||
mem::release(fn);
|
||||
}
|
||||
|
||||
if((fn = filesys::makeFn(dir,"midi","csv",nullptr)) != nullptr )
|
||||
{
|
||||
if((rc0 = midi_record_play::save_csv( app->mrpH, fn )) != kOkRC )
|
||||
rc0 = cwLogError(rc0,"MIDI CSV file '%s' save failed.",fn);
|
||||
|
||||
mem::release(fn);
|
||||
}
|
||||
|
||||
|
||||
if((fn = filesys::makeFn(dir,"audio","wav",nullptr)) != nullptr )
|
||||
{
|
||||
if((rc1 = audio_record_play::save( app->arpH, fn )) != kOkRC )
|
||||
@ -355,6 +371,7 @@ namespace cw
|
||||
break;
|
||||
|
||||
case kReportBtnId:
|
||||
report( app->mrpH );
|
||||
break;
|
||||
|
||||
case kSaveBtnId:
|
||||
@ -375,6 +392,14 @@ namespace cw
|
||||
_set_midi_thru_state(app, m.value->u.b);
|
||||
break;
|
||||
|
||||
case kMidiMuteCheckId:
|
||||
midi_record_play::set_mute_state(app->mrpH,m.value->u.b);
|
||||
break;
|
||||
|
||||
case kAudioMuteCheckId:
|
||||
audio_record_play::set_mute_state(app->arpH,m.value->u.b);
|
||||
break;
|
||||
|
||||
case kStartBtnId:
|
||||
_on_ui_start(app);
|
||||
break;
|
||||
@ -518,7 +543,7 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg )
|
||||
return rc;
|
||||
|
||||
// create the MIDI record-play object
|
||||
if((rc = midi_record_play::create(app.mrpH,app.ioH,*cfg)) != kOkRC )
|
||||
if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI record-play object create failed.");
|
||||
goto errLabel;
|
||||
|
@ -40,6 +40,8 @@ namespace cw
|
||||
bool recordFl;
|
||||
bool startedFl;
|
||||
|
||||
bool mute_fl;
|
||||
|
||||
unsigned* audioInChMapA;
|
||||
unsigned audioInChMapN;
|
||||
unsigned* audioOutChMapA;
|
||||
@ -170,6 +172,9 @@ namespace cw
|
||||
{
|
||||
unsigned srcChIdx = p->audioInChMapA == nullptr ? chIdx : p->audioInChMapA[chIdx];
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -198,6 +203,8 @@ namespace cw
|
||||
{
|
||||
unsigned adst_idx = 0;
|
||||
|
||||
if( !p->mute_fl )
|
||||
{
|
||||
while(adst_idx < adst.dspFrameCnt)
|
||||
{
|
||||
am_audio_t* a;
|
||||
@ -221,6 +228,7 @@ namespace cw
|
||||
p->curFrameIdx += n;
|
||||
adst_idx += n;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: zero unused channels
|
||||
|
||||
@ -415,9 +423,16 @@ namespace cw
|
||||
else
|
||||
{
|
||||
if( m.oBufChCnt > 0 )
|
||||
{
|
||||
_audio_play(p,m);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned i=0; i<m.oBufChCnt; ++i)
|
||||
memset( m.oBufArray[i], 0, m.dspFrameCnt * sizeof(sample_t));
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -522,6 +537,22 @@ bool cw::audio_record_play::record_state( handle_t h )
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::audio_record_play::set_mute_state( handle_t h, bool mute_fl )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
audio_record_play_t* p = _handleToPtr(h);
|
||||
p->mute_fl = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool cw::audio_record_play::mute_state( handle_t h )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
audio_record_play_t* p = _handleToPtr(h);
|
||||
return p->mute_fl;
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::audio_record_play::save( handle_t h, const char* fn )
|
||||
{
|
||||
audio_record_play_t* p = _handleToPtr(h);
|
||||
|
@ -17,6 +17,8 @@ namespace cw
|
||||
rc_t clear( handle_t h );
|
||||
rc_t set_record_state( handle_t h, bool record_fl );
|
||||
bool record_state( handle_t h );
|
||||
rc_t set_mute_state( handle_t h, bool mute_fl );
|
||||
bool mute_state( handle_t h );
|
||||
rc_t save( handle_t h, const char* fn );
|
||||
rc_t open( handle_t h, const char* fn );
|
||||
double duration_seconds( handle_t h );
|
||||
|
@ -55,6 +55,16 @@ namespace cw
|
||||
unsigned pedalUpHalfVelId;
|
||||
unsigned pedalUpHalfVel;
|
||||
|
||||
unsigned velHistogram[ midi::kMidiVelCnt ];
|
||||
|
||||
bool force_damper_down_fl;
|
||||
unsigned force_damper_down_threshold;
|
||||
unsigned force_damper_down_velocity;
|
||||
|
||||
bool damper_dead_band_enable_fl;
|
||||
unsigned damper_dead_band_min_value;
|
||||
unsigned damper_dead_band_max_value;
|
||||
|
||||
} midi_device_t;
|
||||
|
||||
enum
|
||||
@ -89,10 +99,13 @@ namespace cw
|
||||
|
||||
bool startedFl;
|
||||
bool recordFl;
|
||||
bool muteFl;
|
||||
bool thruFl;
|
||||
bool logInFl; // log incoming message when not in 'record' mode.
|
||||
bool logOutFl; // log outgoing messages
|
||||
|
||||
bool velHistogramEnableFl;
|
||||
|
||||
bool halfPedalFl;
|
||||
unsigned halfPedalState;
|
||||
unsigned halfPedalNextUs;
|
||||
@ -190,13 +203,28 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
if((rc = ele->getv_opt( "vel_table", velTable,
|
||||
"pedal", pedalRecd)) != kOkRC )
|
||||
if((rc = ele->getv_opt(
|
||||
"vel_table", velTable,
|
||||
"pedal", pedalRecd,
|
||||
"force_damper_down_fl",p->midiDevA[i].force_damper_down_fl,
|
||||
"force_damper_down_threshold",p->midiDevA[i].force_damper_down_threshold,
|
||||
"force_damper_down_velocity", p->midiDevA[i].force_damper_down_velocity,
|
||||
"damper_dead_band_enable_fl", p->midiDevA[i].damper_dead_band_enable_fl,
|
||||
"damper_dead_band_min_value", p->midiDevA[i].damper_dead_band_min_value,
|
||||
"damper_dead_band_max_value", p->midiDevA[i].damper_dead_band_max_value)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"MIDI record play device optional argument parsing failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
cwLogInfo("Force Pedal: enabled:%i thresh:%i veloc:%i dead band: enable:%i min:%i max:%i",
|
||||
p->midiDevA[i].force_damper_down_fl,
|
||||
p->midiDevA[i].force_damper_down_threshold,
|
||||
p->midiDevA[i].force_damper_down_velocity,
|
||||
p->midiDevA[i].damper_dead_band_enable_fl,
|
||||
p->midiDevA[i].damper_dead_band_min_value,
|
||||
p->midiDevA[i].damper_dead_band_max_value );
|
||||
|
||||
p->midiDevA[i].midiOutDevLabel = mem::duplStr( midiOutDevLabel);
|
||||
p->midiDevA[i].midiOutPortLabel = mem::duplStr( midiOutPortLabel);
|
||||
p->midiDevA[i].enableFl = enableFl;
|
||||
@ -255,6 +283,9 @@ namespace cw
|
||||
{
|
||||
am_midi_msg_t* am = nullptr;
|
||||
|
||||
//if( !midi::isPedal(status,d0) )
|
||||
// printf("MIDI store: %i : ch:%i st:%i d0:%i d1:%i\n",p->iMsgArrayInIdx,ch,status,d0,d1);
|
||||
|
||||
// verify that space exists in the record buffer
|
||||
if( p->iMsgArrayInIdx < p->iMsgArrayN )
|
||||
{
|
||||
@ -281,13 +312,14 @@ namespace cw
|
||||
rc_t _event_callback( midi_record_play_t* p, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1, bool log_fl=true )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// if we have arrived at the stop time
|
||||
bool after_stop_time_fl = !time::isZero(p->end_play_event_timestamp) && time::isGT(timestamp,p->end_play_event_timestamp);
|
||||
bool after_all_off_fl = after_stop_time_fl && time::isGT(timestamp,p->all_off_timestamp);
|
||||
|
||||
bool is_note_on_fl = status==midi::kNoteOnMdId and d1 != 0;
|
||||
bool supress_fl = is_note_on_fl && after_stop_time_fl;
|
||||
bool is_damper_fl = status==midi::kCtlMdId and d0==midi::kSustainCtlMdId;
|
||||
bool supress_fl = (is_note_on_fl && after_stop_time_fl) || p->muteFl;
|
||||
bool is_pedal_fl = midi::isPedal( status, d0 );
|
||||
|
||||
if( after_all_off_fl )
|
||||
{
|
||||
@ -320,6 +352,19 @@ namespace cw
|
||||
out_d1 = p->midiDevA[i].velTableArray[ d1 ];
|
||||
}
|
||||
|
||||
// store the note-on velocity histogram data
|
||||
if( p->velHistogramEnableFl && is_note_on_fl && out_d1 < midi::kMidiVelCnt )
|
||||
p->midiDevA[i].velHistogram[ out_d1 ] += 1;
|
||||
|
||||
// if the damper pedal velocity is in the dead band then don't send it
|
||||
if( p->midiDevA[i].damper_dead_band_enable_fl && is_pedal_fl && p->midiDevA[i].damper_dead_band_min_value <= out_d1 && out_d1 <= p->midiDevA[i].damper_dead_band_max_value )
|
||||
out_d1 = 0;
|
||||
|
||||
// if the damper pedal velocity is over the 'forcing' threshold then force the damper down
|
||||
if( p->midiDevA[i].force_damper_down_fl && is_damper_fl && out_d1>p->midiDevA[i].force_damper_down_threshold )
|
||||
out_d1 = p->midiDevA[i].force_damper_down_velocity;
|
||||
|
||||
|
||||
// map the pedal down velocity
|
||||
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl )
|
||||
{
|
||||
@ -332,16 +377,20 @@ namespace cw
|
||||
if( d1 == p->midiDevA[i].pedalDownHalfVelId )
|
||||
out_d1 = p->midiDevA[i].pedalDownHalfVel;
|
||||
else
|
||||
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping.",d1);
|
||||
{
|
||||
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 )
|
||||
{
|
||||
io::midiDeviceSend( p->ioH, p->midiDevA[i].midiOutDevIdx, p->midiDevA[i].midiOutPortIdx, status + ch, d0, out_d1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( !after_stop_time_fl and p->cb )
|
||||
p->cb( p->cb_arg, id, timestamp, loc, ch, status, d0, d1 );
|
||||
p->cb( p->cb_arg, kMidiEventActionId, id, timestamp, loc, ch, status, d0, d1 );
|
||||
|
||||
if( log_fl && p->logOutFl )
|
||||
{
|
||||
@ -355,6 +404,7 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am, bool log_fl=true )
|
||||
{
|
||||
return _event_callback( p, am->id, am->timestamp, am->loc, am->ch, am->status, am->d0, am->d1, log_fl );
|
||||
@ -442,6 +492,53 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t _am_file_read_version_0( const char* fn, file::handle_t fH, am_midi_msg_t* amMsgArray, unsigned msgN )
|
||||
{
|
||||
// version 0 record type
|
||||
typedef struct msg_str
|
||||
{
|
||||
unsigned dev_idx;
|
||||
unsigned port_idx;
|
||||
time::spec_t timestamp;
|
||||
uint8_t ch;
|
||||
uint8_t st;
|
||||
uint8_t d0;
|
||||
uint8_t d1;
|
||||
unsigned microsecs;
|
||||
} zero_msg_t;
|
||||
|
||||
cw::rc_t rc = kOkRC;
|
||||
zero_msg_t* zMsgArray = mem::allocZ<zero_msg_t>(msgN);
|
||||
unsigned fileByteN = msgN * sizeof(zero_msg_t);
|
||||
|
||||
if((rc = file::read(fH,zMsgArray,fileByteN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<msgN; ++i)
|
||||
{
|
||||
am_midi_msg_t* am = amMsgArray + i;
|
||||
zero_msg_t* zm = zMsgArray + i;
|
||||
|
||||
am->devIdx = zm->dev_idx;
|
||||
am->portIdx = zm->port_idx;
|
||||
am->microsec = zm->microsecs;
|
||||
am->id = i;
|
||||
am->timestamp = zm->timestamp;
|
||||
am->loc = i;
|
||||
am->ch = zm->ch;
|
||||
am->status = zm->st;
|
||||
am->d0 = zm->d0;
|
||||
am->d1 = zm->d1;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
mem::release(zMsgArray);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Read the am_midi_msg_t records from a file written by _midi_write()
|
||||
// If msgArrayCntRef==0 and msgArrayRef==NULL then an array will be allocated and it is up
|
||||
// to the caller to release it, otherwise the msgArrayCntRef should be set to the count
|
||||
@ -450,8 +547,13 @@ namespace cw
|
||||
// msgArrayCntRef records will be returned.
|
||||
cw::rc_t _am_file_read( const char* fn, unsigned& msgArrayCntRef, am_midi_msg_t*& msgArrayRef )
|
||||
{
|
||||
|
||||
rc_t rc = kOkRC;
|
||||
unsigned n = 0;
|
||||
unsigned recordN = 0; // count of records in the ifle
|
||||
unsigned fileByteN = 0; // count of bytes in the file
|
||||
int version = 0; // version id (always a negative number)
|
||||
bool alloc_fl = false;
|
||||
bool print_fl = false;
|
||||
file::handle_t fH;
|
||||
|
||||
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
|
||||
@ -460,35 +562,81 @@ namespace cw
|
||||
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,"Version read failed on Audio-MIDI file: '%s'.", fn );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if the version is greater than 0 then this is a version 0 file (and 'version' holds the record count.
|
||||
if( version > 0 )
|
||||
{
|
||||
recordN = (unsigned)version;
|
||||
version = 0;
|
||||
}
|
||||
else // otherwise the second word in the file holds the size of the file
|
||||
{
|
||||
if((rc = file::read(fH,recordN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
// if the output msg array was not allocated - then allocate it here
|
||||
if( msgArrayCntRef == 0 || msgArrayRef == nullptr )
|
||||
{
|
||||
msgArrayRef = mem::allocZ<am_midi_msg_t>(n);
|
||||
alloc_fl = true;
|
||||
msgArrayRef = mem::allocZ<am_midi_msg_t>(recordN);
|
||||
}
|
||||
else // if the msg array was allocated but is too small - then decrease the count of records to be read from the file
|
||||
{
|
||||
if( recordN > msgArrayCntRef )
|
||||
{
|
||||
cwLogWarning("The count of message in Audio-MIDI file '%s' reduced from %i to %i.", fn, recordN, msgArrayCntRef );
|
||||
recordN = msgArrayCntRef;
|
||||
}
|
||||
}
|
||||
|
||||
if( version == 0 )
|
||||
{
|
||||
// read the version 0 file into a temporary buffer then translate to am_midi_msg_t records
|
||||
if((rc = _am_file_read_version_0( fn, fH, msgArrayRef, recordN )) != kOkRC )
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( n > msgArrayCntRef )
|
||||
{
|
||||
cwLogWarning("The count of message in Audio-MIDI file '%s' reduced from %i to %i.", fn, n, msgArrayCntRef );
|
||||
n = msgArrayCntRef;
|
||||
}
|
||||
}
|
||||
|
||||
if((rc = file::read(fH,msgArrayRef,n*sizeof(am_midi_msg_t))) != kOkRC )
|
||||
fileByteN = recordN * sizeof(am_midi_msg_t);
|
||||
if((rc = file::read(fH,msgArrayRef,fileByteN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kReadFailRC,"Data read failed on Audio-MIDI file: '%s'.", fn );
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
msgArrayCntRef = n;
|
||||
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) );
|
||||
}
|
||||
}
|
||||
|
||||
msgArrayCntRef = recordN;
|
||||
|
||||
errLabel:
|
||||
|
||||
if( rc != kOkRC and alloc_fl )
|
||||
{
|
||||
mem::release(msgArrayRef);
|
||||
msgArrayRef = nullptr;
|
||||
msgArrayCntRef = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -497,6 +645,7 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
unsigned n = 0;
|
||||
int version;
|
||||
file::handle_t fH;
|
||||
|
||||
if((rc = file::open(fH,fn,file::kReadFl)) != kOkRC )
|
||||
@ -505,6 +654,12 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = file::read(fH,version)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kReadFailRC,"Version read failed on Audio-MIDI file: '%s'.", fn );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = file::read(fH,n)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kReadFailRC,"Header read failed on Audio-MIDI file: '%s'.", fn );
|
||||
@ -539,6 +694,9 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
file::handle_t fH;
|
||||
// NOTE: version must be a small negative number to differentiate from file version that
|
||||
// whose first word is the count of records in the file, rather than the version number
|
||||
int version = -1;
|
||||
|
||||
if( p->iMsgArrayInIdx == 0 )
|
||||
{
|
||||
@ -553,6 +711,13 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// write the file version
|
||||
if((rc = write(fH,version)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kWriteFailRC,"Version write to '%s' failed.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// write the file header
|
||||
if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC )
|
||||
{
|
||||
@ -696,13 +861,67 @@ namespace cw
|
||||
|
||||
void _report_midi( midi_record_play_t* p )
|
||||
{
|
||||
printf("omsg cnt:%i\n",p->msgArrayInIdx);
|
||||
for(unsigned i=0; i<p->msgArrayInIdx; ++i)
|
||||
{
|
||||
am_midi_msg_t* mm = p->msgArray + i;
|
||||
_print_midi_msg(mm);
|
||||
}
|
||||
|
||||
|
||||
printf("imsg cnt:%i\n",p->iMsgArrayInIdx);
|
||||
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
|
||||
{
|
||||
am_midi_msg_t* mm = p->iMsgArray + i;
|
||||
_print_midi_msg(mm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rc_t _write_vel_histogram( midi_record_play_t* p )
|
||||
{
|
||||
const char* fname = "/home/kevin/temp/vel_histogram.txt";
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( !p->velHistogramEnableFl )
|
||||
return rc;
|
||||
|
||||
file::handle_t h;
|
||||
if((rc = file::open(h,fname,file::kWriteFl)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
for(unsigned i=0; i<p->midiDevN; ++i)
|
||||
if(p->midiDevA[i].enableFl )
|
||||
{
|
||||
for(unsigned j=0; j<midi::kMidiVelCnt; ++j)
|
||||
if((rc = file::printf(h,"%i,",p->midiDevA[i].velHistogram[j])) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Histogram output file (%s) write failed.",fname);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
file::printf(h,"\n");
|
||||
}
|
||||
|
||||
errLabel:
|
||||
file::close(h);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Fill the play buffer (msgArray) from the record buffer (iMsgArray)
|
||||
void _iMsgArray_to_msgArray(midi_record_play_t* p)
|
||||
{
|
||||
if( p->msgArrayN < p->iMsgArrayN)
|
||||
{
|
||||
mem::resize(p->msgArray,p->iMsgArrayN,mem::kZeroAllFl);
|
||||
p->msgArrayN = p->iMsgArrayN;
|
||||
}
|
||||
p->msgArrayOutIdx = 0;
|
||||
p->msgArrayInIdx = p->iMsgArrayInIdx;
|
||||
memcpy(p->msgArray,p->iMsgArray,p->iMsgArrayInIdx*sizeof(am_midi_msg_t));
|
||||
}
|
||||
|
||||
|
||||
rc_t _stop( midi_record_play_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -712,6 +931,8 @@ namespace cw
|
||||
time::spec_t t1;
|
||||
time::get(t1);
|
||||
|
||||
_write_vel_histogram( p );
|
||||
|
||||
// if we were recording
|
||||
if( p->recordFl )
|
||||
{
|
||||
@ -719,9 +940,12 @@ namespace cw
|
||||
// set the 'microsec' value for each MIDI msg as an offset from the first message[]
|
||||
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
|
||||
{
|
||||
p->msgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
|
||||
p->iMsgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
|
||||
}
|
||||
|
||||
// copy the recorded messages from the input buffer to the output buffer
|
||||
_iMsgArray_to_msgArray(p);
|
||||
|
||||
cwLogInfo("MIDI messages recorded: %i",p->msgArrayInIdx );
|
||||
|
||||
}
|
||||
@ -739,6 +963,9 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
if( p->cb != nullptr )
|
||||
p->cb( p->cb_arg, kPlayerStoppedActionId, kInvalidId, t1, kInvalidId, 0, 0, 0, 0 );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -753,6 +980,7 @@ namespace cw
|
||||
// if this is a sys-ex msg
|
||||
if( pkt->msgArray == NULL )
|
||||
{
|
||||
cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
|
||||
}
|
||||
else // this is a triple
|
||||
{
|
||||
@ -922,6 +1150,12 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
p->startedFl = true;
|
||||
|
||||
if( p->velHistogramEnableFl )
|
||||
for(unsigned i=0; i<p->midiDevN; ++i)
|
||||
if(p->midiDevA[i].enableFl )
|
||||
memset(p->midiDevA[i].velHistogram,0,sizeof(p->midiDevA[i].velHistogram));
|
||||
|
||||
|
||||
// set the end play time
|
||||
if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) )
|
||||
time::setZero(p->end_play_event_timestamp);
|
||||
@ -999,6 +1233,21 @@ bool cw::midi_record_play::record_state( handle_t h )
|
||||
return p->recordFl;
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi_record_play::set_mute_state( handle_t h, bool mute_fl )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
p->muteFl = mute_fl;
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool cw::midi_record_play::mute_state( handle_t h )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
return p->muteFl;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::midi_record_play::set_thru_state( handle_t h, bool thru_fl )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -1085,7 +1334,7 @@ cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp )
|
||||
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl, 0 );
|
||||
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl, 0 );
|
||||
|
||||
cwLogInfo("damper: %s.", damp_down_fl ? "down" : "up");
|
||||
//cwLogInfo("damper: %s.", damp_down_fl ? "down" : "up");
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1198,6 +1447,20 @@ void cw::midi_record_play::enable_device( handle_t h, unsigned devIdx, bool enab
|
||||
}
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi_record_play::send_midi_msg( handle_t h, unsigned devIdx, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
|
||||
if( devIdx >= p->midiDevN )
|
||||
return cwLogError(kInvalidArgRC,"The MIDI record-play device index '%i' is invalid.",devIdx );
|
||||
|
||||
if( p->midiDevA[devIdx].enableFl )
|
||||
io::midiDeviceSend( p->ioH, p->midiDevA[devIdx].midiOutDevIdx, p->midiDevA[devIdx].midiOutPortIdx, status + ch, d0, d1 );
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
|
||||
void cw::midi_record_play::half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
@ -1216,6 +1479,12 @@ cw::rc_t cw::midi_record_play::am_to_midi_file( const char* am_filename, const c
|
||||
unsigned msgArrayCnt = 0;
|
||||
am_midi_msg_t* msgArray = nullptr;
|
||||
|
||||
if(!filesys::isFile(am_filename))
|
||||
{
|
||||
cwLogError(kOpenFailRC,"The AM file '%s' does not exist.",am_filename);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = _am_file_read( am_filename, msgArrayCnt, msgArray )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Unable to read AM file '%s'.", cwStringNullGuard(am_filename));
|
||||
@ -1241,12 +1510,21 @@ cw::rc_t cw::midi_record_play::am_to_midi_dir( const char* inDir )
|
||||
filesys::dirEntry_t* dirEntryArray = nullptr;
|
||||
unsigned dirEntryCnt = 0;
|
||||
|
||||
if(( dirEntryArray = dirEntries( inDir, filesys::kDirFsFl, &dirEntryCnt )) == nullptr )
|
||||
if(( dirEntryArray = filesys::dirEntries( inDir, filesys::kDirFsFl | filesys::kFullPathFsFl, &dirEntryCnt )) == nullptr )
|
||||
goto errLabel;
|
||||
|
||||
for(unsigned i=0; i<dirEntryCnt; ++i)
|
||||
for(unsigned i=0; i<dirEntryCnt and rc==kOkRC; ++i)
|
||||
{
|
||||
printf("0x%x %s\n", dirEntryArray[i].flags, dirEntryArray[i].name);
|
||||
char* am_fn = filesys::makeFn( dirEntryArray[i].name, "midi", "am", NULL);
|
||||
char* midi_fn = filesys::makeFn( dirEntryArray[i].name, "midi", "mid", NULL);
|
||||
|
||||
cwLogInfo("0x%x AM:%s MIDI:%s", dirEntryArray[i].flags, dirEntryArray[i].name, midi_fn);
|
||||
|
||||
rc = am_to_midi_file( am_fn, midi_fn );
|
||||
|
||||
mem::release(am_fn);
|
||||
mem::release(midi_fn);
|
||||
|
||||
}
|
||||
|
||||
errLabel:
|
||||
@ -1284,3 +1562,9 @@ cw::rc_t cw::midi_record_play::am_to_midi_file( const object_t* cfg )
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
void cw::midi_record_play::report( handle_t h )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
_report_midi(p);
|
||||
}
|
||||
|
@ -19,8 +19,13 @@ namespace cw
|
||||
uint8_t d1;
|
||||
} midi_msg_t;
|
||||
|
||||
enum {
|
||||
kMidiEventActionId,
|
||||
kPlayerStoppedActionId
|
||||
};
|
||||
|
||||
typedef void (*event_callback_t)( void* arg, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
|
||||
typedef void (*event_callback_t)( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
|
||||
|
||||
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, event_callback_t cb=nullptr, void* cb_arg=nullptr );
|
||||
@ -36,6 +41,9 @@ namespace cw
|
||||
rc_t set_record_state( handle_t h, bool record_fl );
|
||||
bool record_state( handle_t h );
|
||||
|
||||
rc_t set_mute_state( handle_t h, bool record_fl );
|
||||
bool mute_state( handle_t h );
|
||||
|
||||
rc_t set_thru_state( handle_t h, bool record_thru );
|
||||
bool thru_state( handle_t h );
|
||||
|
||||
@ -58,6 +66,7 @@ namespace cw
|
||||
unsigned device_count( handle_t h );
|
||||
bool is_device_enabled( handle_t h, unsigned devIdx );
|
||||
void enable_device( handle_t h, unsigned devIdx, bool enableFl );
|
||||
rc_t send_midi_msg( handle_t h, unsigned devIdx, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
|
||||
void half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs );
|
||||
|
||||
@ -66,6 +75,8 @@ namespace cw
|
||||
rc_t am_to_midi_dir( const char* inDir );
|
||||
rc_t am_to_midi_file( const object_t* cfg );
|
||||
|
||||
void report( handle_t h );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
134
cwIoMinTest.cpp
Normal file
134
cwIoMinTest.cpp
Normal 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
11
cwIoMinTest.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef cwIoMinTest_H
|
||||
#define cwIoMinTest_H
|
||||
|
||||
|
||||
namespace cw
|
||||
{
|
||||
rc_t min_test( const object_t* cfg);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -40,6 +40,7 @@ namespace cw
|
||||
|
||||
kStartBtnId,
|
||||
kStopBtnId,
|
||||
kLiveCheckId,
|
||||
|
||||
kPrintMidiCheckId,
|
||||
kPianoMidiCheckId,
|
||||
@ -69,12 +70,14 @@ namespace cw
|
||||
|
||||
kStatusId,
|
||||
|
||||
/*
|
||||
kHalfPedalPedalVel,
|
||||
kHalfPedalDelayMs,
|
||||
kHalfPedalPitch,
|
||||
kHalfPedalVel,
|
||||
kHalfPedalDurMs,
|
||||
kHalfPedalDnDelayMs,
|
||||
*/
|
||||
|
||||
kLogId,
|
||||
|
||||
@ -85,6 +88,7 @@ namespace cw
|
||||
|
||||
kFragPresetRowId,
|
||||
kFragPresetSelId,
|
||||
kFragPresetSeqSelId,
|
||||
kFragPresetOrderId,
|
||||
|
||||
kFragInGainId,
|
||||
@ -94,14 +98,16 @@ namespace cw
|
||||
kFragBegPlayLocId,
|
||||
kFragEndPlayLocId,
|
||||
kFragPlayBtnId,
|
||||
kFragPlaySeqBtnId,
|
||||
kFragPlayAllBtnId,
|
||||
kFragNoteId
|
||||
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kPiano_MRP_DevIdx = 0,
|
||||
kSampler_MRP_DevIdx = 1
|
||||
kPiano_MRP_DevIdx = 1,
|
||||
kSampler_MRP_DevIdx = 0
|
||||
};
|
||||
|
||||
enum
|
||||
@ -121,6 +127,7 @@ namespace cw
|
||||
|
||||
{ kPanelDivId, kStartBtnId, "startBtnId" },
|
||||
{ kPanelDivId, kStopBtnId, "stopBtnId" },
|
||||
{ kPanelDivId, kLiveCheckId, "liveCheckId" },
|
||||
|
||||
{ kPanelDivId, kPrintMidiCheckId, "printMidiCheckId" },
|
||||
{ kPanelDivId, kPianoMidiCheckId, "pianoMidiCheckId" },
|
||||
@ -148,13 +155,14 @@ namespace cw
|
||||
{ kPanelDivId, kInsertBtnId, "insertBtnId" },
|
||||
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" },
|
||||
|
||||
/*
|
||||
{ kPanelDivId, kHalfPedalPedalVel, "halfPedalPedalVelId" },
|
||||
{ kPanelDivId, kHalfPedalDelayMs, "halfPedalDelayMsId" },
|
||||
{ kPanelDivId, kHalfPedalPitch, "halfPedalPitchId" },
|
||||
{ kPanelDivId, kHalfPedalVel, "halfPedalVelId" },
|
||||
{ kPanelDivId, kHalfPedalDurMs, "halfPedalDurMsId" },
|
||||
{ kPanelDivId, kHalfPedalDnDelayMs, "halfPedalDnDelayMsId" },
|
||||
|
||||
*/
|
||||
|
||||
{ kPanelDivId, kStatusId, "statusId" },
|
||||
{ kPanelDivId, kLogId, "logId" },
|
||||
@ -171,6 +179,8 @@ namespace cw
|
||||
{ kFragPanelId, kFragBegPlayLocId, "fragBegPlayLocId" },
|
||||
{ kFragPanelId, kFragEndPlayLocId, "fragEndPlayLocId" },
|
||||
{ kFragPanelId, kFragPlayBtnId, "fragPlayBtnId" },
|
||||
{ kFragPanelId, kFragPlaySeqBtnId, "fragPlaySeqBtnId" },
|
||||
{ kFragPanelId, kFragPlayAllBtnId, "fragPlayAllBtnId" },
|
||||
{ kFragPanelId, kFragNoteId, "fragNoteId" },
|
||||
|
||||
};
|
||||
@ -201,6 +211,7 @@ namespace cw
|
||||
const object_t* midi_play_record_cfg;
|
||||
const object_t* frag_panel_cfg;
|
||||
const object_t* presets_cfg;
|
||||
object_t* flow_proc_dict;
|
||||
const object_t* flow_cfg;
|
||||
|
||||
midi_record_play::handle_t mrpH;
|
||||
@ -214,12 +225,12 @@ namespace cw
|
||||
unsigned minLoc;
|
||||
unsigned maxLoc;
|
||||
|
||||
unsigned beg_play_loc;
|
||||
unsigned end_play_loc;
|
||||
|
||||
time::spec_t beg_play_timestamp;
|
||||
time::spec_t end_play_timestamp;
|
||||
|
||||
unsigned beg_play_loc;
|
||||
unsigned end_play_loc;
|
||||
|
||||
preset_sel::handle_t psH;
|
||||
io_flow::handle_t ioFlowH;
|
||||
|
||||
@ -236,11 +247,21 @@ namespace cw
|
||||
unsigned hpVel;
|
||||
unsigned hpDurMs;
|
||||
unsigned hpDnDelayMs;
|
||||
|
||||
bool seqActiveFl; // true if the sequence is currently active (set by 'Play Seq' btn)
|
||||
unsigned seqStartedFl; // set by the first seq idle callback
|
||||
unsigned seqFragId; //
|
||||
unsigned seqPresetIdx; //
|
||||
|
||||
bool useLiveMidiFl;
|
||||
|
||||
|
||||
} app_t;
|
||||
|
||||
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* flow_proc_dict_fn = nullptr;
|
||||
|
||||
if((rc = cfg->getv( "params", params_cfgRef,
|
||||
"flow", app->flow_cfg)) != kOkRC )
|
||||
@ -253,11 +274,14 @@ namespace cw
|
||||
"record_fn", app->record_fn,
|
||||
"record_fn_ext", app->record_fn_ext,
|
||||
"score_fn", app->scoreFn,
|
||||
"flow_proc_dict_fn",flow_proc_dict_fn,
|
||||
"midi_play_record", app->midi_play_record_cfg,
|
||||
"frag_panel", app->frag_panel_cfg,
|
||||
"presets", app->presets_cfg,
|
||||
"crossFadeSrate", app->crossFadeSrate,
|
||||
"crossFadeCount", app->crossFadeCnt)) != kOkRC )
|
||||
"crossFadeCount", app->crossFadeCnt,
|
||||
"beg_play_loc", app->beg_play_loc,
|
||||
"end_play_loc", app->end_play_loc)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
|
||||
}
|
||||
@ -274,6 +298,12 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = objectFromFile( flow_proc_dict_fn, app->flow_proc_dict )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The flow proc file '%s' parse failed.",app->flow_proc_dict);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// verify that the output directory exists
|
||||
if((rc = filesys::isDir(app->record_dir)) != kOkRC )
|
||||
if((rc = filesys::makeDir(app->record_dir)) != kOkRC )
|
||||
@ -321,6 +351,9 @@ namespace cw
|
||||
|
||||
rc_t _free( app_t& app )
|
||||
{
|
||||
if( app.flow_proc_dict != nullptr )
|
||||
app.flow_proc_dict->free();
|
||||
|
||||
mem::release((char*&)app.record_dir);
|
||||
mem::release((char*&)app.scoreFn);
|
||||
preset_sel::destroy(app.psH);
|
||||
@ -331,11 +364,16 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
|
||||
rc_t _apply_preset( app_t* app, const time::spec_t& ts, const preset_sel::frag_t* frag=nullptr )
|
||||
rc_t _apply_preset( app_t* app, const time::spec_t& ts, unsigned loc, const preset_sel::frag_t* frag=nullptr )
|
||||
{
|
||||
// if frag is NULL this is the beginning of a play session
|
||||
if( frag == nullptr )
|
||||
preset_sel::track_timestamp( app->psH, ts, frag);
|
||||
{
|
||||
preset_sel::track_loc_reset(app->psH);
|
||||
//preset_sel::track_timestamp( app->psH, ts, frag);
|
||||
|
||||
preset_sel::track_loc( app->psH, loc, frag);
|
||||
}
|
||||
|
||||
if( frag == nullptr )
|
||||
cwLogInfo("No preset fragment was found for the requested timestamp.");
|
||||
@ -343,13 +381,18 @@ namespace cw
|
||||
{
|
||||
unsigned preset_idx;
|
||||
|
||||
if((preset_idx = fragment_play_preset_index(frag)) == kInvalidIdx )
|
||||
// if the preset sequence player is active then apply the next selected seq. preset
|
||||
// otherwise select the next primary preset for ths fragment
|
||||
unsigned seq_idx_n = app->seqActiveFl ? app->seqPresetIdx : kInvalidIdx;
|
||||
|
||||
// get the preset index to play for this fragment
|
||||
if((preset_idx = fragment_play_preset_index(frag,seq_idx_n)) == kInvalidIdx )
|
||||
cwLogInfo("No preset has been assigned to the fragment at end loc. '%i'.",frag->endLoc );
|
||||
else
|
||||
{
|
||||
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx);
|
||||
|
||||
cwLogInfo("Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
|
||||
_set_status(app,"Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
|
||||
|
||||
if( preset_label != nullptr )
|
||||
{
|
||||
@ -395,10 +438,18 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _midi_play_callback( void* arg, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
void _midi_play_callback( void* arg, unsigned actionId, unsigned id, const time::spec_t timestamp, unsigned loc, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
{
|
||||
app_t* app = (app_t*)arg;
|
||||
switch( actionId )
|
||||
{
|
||||
case midi_record_play::kPlayerStoppedActionId:
|
||||
app->seqStartedFl=false;
|
||||
_set_status(app,"Done");
|
||||
break;
|
||||
|
||||
case midi_record_play::kMidiEventActionId:
|
||||
{
|
||||
if( app->printMidiFl )
|
||||
{
|
||||
const unsigned buf_byte_cnt = 256;
|
||||
@ -418,16 +469,67 @@ namespace cw
|
||||
if( midi_record_play::is_started(app->mrpH) )
|
||||
{
|
||||
const preset_sel::frag_t* f = nullptr;
|
||||
if( preset_sel::track_timestamp( app->psH, timestamp, f ) )
|
||||
//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, f );
|
||||
_apply_preset( app, timestamp, loc, f );
|
||||
|
||||
if( f != nullptr )
|
||||
_do_select_frag( app, f->guiUuId );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc_t _on_live_midi( app_t* app, const io::msg_t& msg )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( msg.u.midi != nullptr )
|
||||
{
|
||||
|
||||
const io::midi_msg_t& m = *msg.u.midi;
|
||||
const midi::packet_t* pkt = m.pkt;
|
||||
// for each midi msg
|
||||
for(unsigned j=0; j<pkt->msgCnt; ++j)
|
||||
{
|
||||
// if this is a sys-ex msg
|
||||
if( pkt->msgArray == NULL )
|
||||
{
|
||||
cwLogError(kNotImplementedRC,"Sys-ex recording not implemented.");
|
||||
}
|
||||
else // this is a triple
|
||||
{
|
||||
midi::msg_t* mm = pkt->msgArray + j;
|
||||
time::spec_t timestamp;
|
||||
unsigned id = kInvalidId;
|
||||
unsigned loc = app->beg_play_loc;
|
||||
|
||||
time::get(timestamp);
|
||||
|
||||
if( midi::isChStatus(mm->status) )
|
||||
{
|
||||
if(midi_record_play::send_midi_msg( app->mrpH, kSampler_MRP_DevIdx, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 ) == kOkRC )
|
||||
_midi_play_callback( app, midi_record_play::kMidiEventActionId, id, timestamp, loc, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// Find the closest locMap equal to or after 'loc'
|
||||
loc_map_t* _find_loc( app_t* app, unsigned loc )
|
||||
@ -468,6 +570,7 @@ namespace cw
|
||||
bool rewindFl = true;
|
||||
loc_map_t* begMap = nullptr;
|
||||
loc_map_t* endMap = nullptr;
|
||||
unsigned cur_loc = 0;
|
||||
|
||||
// if the player is already playing then stop it
|
||||
if( midi_record_play::is_started(app->mrpH) )
|
||||
@ -476,7 +579,7 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
midi_record_play::half_pedal_params( app->mrpH, app->hpDelayMs, app->hpPitch, app->hpVel, app->hpPedalVel, app->hpDurMs, app->hpDnDelayMs );
|
||||
//midi_record_play::half_pedal_params( app->mrpH, app->hpDelayMs, app->hpPitch, app->hpVel, app->hpPedalVel, app->hpDurMs, app->hpDnDelayMs );
|
||||
|
||||
|
||||
if((begMap = _find_loc(app,begLoc)) == nullptr )
|
||||
@ -495,7 +598,7 @@ namespace cw
|
||||
app->end_play_timestamp = endMap->timestamp;
|
||||
|
||||
|
||||
if( !time::isZero(app->beg_play_timestamp) )
|
||||
if( !time::isZero(app->beg_play_timestamp) && !app->useLiveMidiFl )
|
||||
{
|
||||
// seek the player to the requested loc
|
||||
if((rc = midi_record_play::seek( app->mrpH, app->beg_play_timestamp )) != kOkRC )
|
||||
@ -507,21 +610,26 @@ namespace cw
|
||||
}
|
||||
|
||||
// apply the preset which is active at the start time
|
||||
if((rc = _apply_preset( app, app->beg_play_timestamp )) != kOkRC )
|
||||
if((rc = _apply_preset( app, app->beg_play_timestamp, begMap->loc )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Preset application failed prior to MIDI start.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// start the MIDI playback
|
||||
if( !app->useLiveMidiFl )
|
||||
{
|
||||
if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI start failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
|
||||
if((cur_loc = midi_record_play::event_loc(app->mrpH)) > 0 )
|
||||
{
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
@ -552,6 +660,45 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
rc_t _do_seq_play_fragment( app_t* app, unsigned fragId )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( app->seqActiveFl )
|
||||
{
|
||||
app->seqActiveFl = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
app->seqFragId = fragId;
|
||||
app->seqPresetIdx = 0;
|
||||
app->seqStartedFl = true;
|
||||
app->seqActiveFl = true;
|
||||
}
|
||||
|
||||
// Note that if the MIDI player is already playing
|
||||
// calling '_do_play_fragment()' here will stop the player
|
||||
_do_play_fragment( app, app->seqFragId );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _do_seq_exec( app_t* app )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// if the seq player is active but currently stopped
|
||||
if( app->seqActiveFl && app->seqStartedFl==false)
|
||||
{
|
||||
app->seqPresetIdx += 1;
|
||||
app->seqStartedFl = app->seqPresetIdx < preset_sel::fragment_seq_count( app->psH, app->seqFragId );
|
||||
app->seqActiveFl = app->seqStartedFl;
|
||||
|
||||
if( app->seqStartedFl )
|
||||
_do_play_fragment( app, app->seqFragId );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _update_event_ui( app_t* app )
|
||||
{
|
||||
@ -639,6 +786,7 @@ namespace cw
|
||||
{
|
||||
_update_frag_ui( app, fragId, preset_sel::kPresetSelectVarId, preset_idx, fragPresetRowUuId, kFragPresetSelId, preset_idx, bValue );
|
||||
_update_frag_ui( app, fragId, preset_sel::kPresetOrderVarId, preset_idx, fragPresetRowUuId, kFragPresetOrderId, preset_idx, uValue );
|
||||
_update_frag_ui( app, fragId, preset_sel::kPresetSeqSelectVarId,preset_idx, fragPresetRowUuId, kFragPresetSeqSelId, preset_idx, bValue );
|
||||
}
|
||||
|
||||
}
|
||||
@ -667,7 +815,6 @@ namespace cw
|
||||
|
||||
get_value( app->psH, fragId, preset_sel::kGuiUuIdVarId, kInvalidId, fragPanelUuId );
|
||||
|
||||
|
||||
_update_frag_ui( app, fragId, preset_sel::kBegLocVarId, kInvalidId, fragPanelUuId, kFragBegLocId, uiChanId, uValue );
|
||||
_update_frag_ui( app, fragId, preset_sel::kEndLocVarId, kInvalidId, fragPanelUuId, kFragEndLocId, uiChanId, uValue );
|
||||
_update_frag_ui( app, fragId, preset_sel::kInGainVarId, kInvalidId, fragPanelUuId, kFragInGainId, uiChanId, dValue );
|
||||
@ -707,17 +854,25 @@ namespace cw
|
||||
{
|
||||
bool enableFl = begPlayLoc < endPlayLoc;
|
||||
unsigned fragUuId = kInvalidId;
|
||||
unsigned fragPlayBtnUuId = kInvalidId;
|
||||
|
||||
if((fragUuId = frag_to_gui_id( app->psH, blob->fragId )) != kInvalidId )
|
||||
if((fragPlayBtnUuId = uiFindElementUuId( app->ioH, fragUuId, kFragPlayBtnId, blob->presetId )) != kInvalidId )
|
||||
{
|
||||
uiSetEnable( app->ioH, fragPlayBtnUuId, enableFl );
|
||||
unsigned btnIdA[] = { kFragPlayBtnId, kFragPlaySeqBtnId, kFragPlayAllBtnId };
|
||||
unsigned btnIdN = sizeof(btnIdA)/sizeof(btnIdA[0]);
|
||||
|
||||
for(unsigned i=0; i<btnIdN; ++i)
|
||||
{
|
||||
unsigned btnUuId;
|
||||
if((btnUuId = uiFindElementUuId( app->ioH, fragUuId, btnIdA[i], blob->presetId )) != kInvalidId )
|
||||
uiSetEnable( app->ioH, btnUuId, enableFl );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if( enableFl )
|
||||
_clear_status(app);
|
||||
else
|
||||
_set_status(app,"Invalid fragment play range.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -756,10 +911,23 @@ namespace cw
|
||||
_update_frag_select_flags( app, blob->fragId);
|
||||
break;
|
||||
|
||||
case preset_sel::kPresetSeqSelectVarId:
|
||||
_update_frag_select_flags( app, blob->fragId);
|
||||
break;
|
||||
|
||||
|
||||
case preset_sel::kPlayBtnVarId:
|
||||
_do_play_fragment( app, blob->fragId );
|
||||
break;
|
||||
|
||||
case preset_sel::kPlaySeqBtnVarId:
|
||||
_do_seq_play_fragment( app, blob->fragId );
|
||||
break;
|
||||
|
||||
case preset_sel::kPlaySeqAllBtnVarId:
|
||||
_do_seq_play_fragment( app, blob->fragId );
|
||||
break;
|
||||
|
||||
case preset_sel::kBegPlayLocVarId:
|
||||
{
|
||||
unsigned endPlayLoc;
|
||||
@ -818,9 +986,16 @@ namespace cw
|
||||
if((rc = io::uiCreateNumb( app->ioH, uuId, colUuId, nullEleName, kFragPresetOrderId, chanId, nullClass, nullptr, 0, presetN, 1, 0 )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// store a connection for the select control back to the fragment record
|
||||
// store a connection for the order control back to the fragment record
|
||||
_frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetOrderVarId, preset_idx );
|
||||
|
||||
// preset sequence select check
|
||||
if((rc = io::uiCreateCheck( app->ioH, uuId, colUuId, nullEleName, kFragPresetSeqSelId, chanId, nullClass, nullptr )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// store a connection for the sequence select control back to the fragment record
|
||||
_frag_set_ui_blob(app, uuId, fragId, preset_sel::kPresetSeqSelectVarId, preset_idx );
|
||||
|
||||
errLabel:
|
||||
if(rc != kOkRC )
|
||||
rc = cwLogError(rc,"Preset control index '%i' create failed.");
|
||||
@ -877,6 +1052,8 @@ namespace cw
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragBegPlayLocId, fragChanId), fragId, preset_sel::kBegPlayLocVarId, kInvalidId );
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragEndPlayLocId, fragChanId), fragId, preset_sel::kEndPlayLocVarId, kInvalidId );
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayBtnId, fragChanId), fragId, preset_sel::kPlayBtnVarId, kInvalidId );
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlaySeqBtnId, fragChanId), fragId, preset_sel::kPlaySeqBtnVarId, kInvalidId );
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragPlayAllBtnId, fragChanId), fragId, preset_sel::kPlaySeqAllBtnVarId, kInvalidId );
|
||||
_frag_set_ui_blob(app, io::uiFindElementUuId(app->ioH, fragPanelUuId, kFragNoteId, fragChanId), fragId, preset_sel::kNoteVarId, kInvalidId );
|
||||
|
||||
|
||||
@ -1064,8 +1241,8 @@ namespace cw
|
||||
rc_t rc = kOkRC;
|
||||
unsigned midiEventN = 0;
|
||||
bool firstLoadFl = !app->scoreH.isValid();
|
||||
unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
|
||||
unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
|
||||
//unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
|
||||
//unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
|
||||
|
||||
// if the score is already loaded
|
||||
//if( app->scoreH.isValid() )
|
||||
@ -1075,24 +1252,39 @@ namespace cw
|
||||
_set_status(app,"Loading...");
|
||||
|
||||
|
||||
// Load the piano score
|
||||
// Load the piano score (this set's app->min/maxLoc)
|
||||
if((rc = _load_piano_score(app,midiEventN)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
/*
|
||||
if( !firstLoadFl)
|
||||
{
|
||||
minLoc = std::max(minLoc,app->minLoc);
|
||||
maxLoc = std::min(maxLoc,app->maxLoc);
|
||||
}
|
||||
*/
|
||||
|
||||
// reset the timestamp tracker
|
||||
track_timestamp_reset( app->psH );
|
||||
track_loc_reset( app->psH );
|
||||
|
||||
// set the range of the global play location controls
|
||||
if( firstLoadFl )
|
||||
{
|
||||
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, minLoc );
|
||||
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, maxLoc );
|
||||
unsigned begPlayLocUuId = io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId);
|
||||
unsigned endPlayLocUuId = io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId);
|
||||
|
||||
unsigned end_play_loc = app->end_play_loc==0 ? app->maxLoc : app->end_play_loc;
|
||||
unsigned beg_play_loc = app->minLoc <= app->beg_play_loc && app->beg_play_loc <= app->maxLoc ? app->beg_play_loc : app->minLoc;
|
||||
|
||||
io::uiSetNumbRange( app->ioH, begPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->minLoc );
|
||||
io::uiSetNumbRange( app->ioH, endPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->maxLoc );
|
||||
|
||||
//io::uiSendValue( app->ioH, begPlayLocUuId, app->minLoc);
|
||||
//io::uiSendValue( app->ioH, endPlayLocUuId, app->maxLoc);
|
||||
|
||||
|
||||
io::uiSendValue( app->ioH, begPlayLocUuId, beg_play_loc);
|
||||
io::uiSendValue( app->ioH, endPlayLocUuId, end_play_loc);
|
||||
|
||||
|
||||
// enable the 'End Loc' number box since the score is loaded
|
||||
@ -1105,6 +1297,7 @@ namespace cw
|
||||
// enable the start/stop buttons
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), true );
|
||||
|
||||
// restore the fragment records
|
||||
if( firstLoadFl )
|
||||
@ -1319,6 +1512,7 @@ namespace cw
|
||||
if( mrp_dev_idx <= midi_record_play::device_count(app->mrpH) )
|
||||
{
|
||||
bool enableFl = midi_record_play::is_device_enabled(app->mrpH, mrp_dev_idx );
|
||||
|
||||
io::uiSendValue( app->ioH, uuId, enableFl );
|
||||
}
|
||||
}
|
||||
@ -1357,6 +1551,7 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value )
|
||||
{
|
||||
switch( appId )
|
||||
@ -1426,7 +1621,7 @@ namespace cw
|
||||
}
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
|
||||
{
|
||||
@ -1437,13 +1632,14 @@ namespace cw
|
||||
// disable start and stop buttons until a score is loaded
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), false );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), false );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), false );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), false );
|
||||
|
||||
const preset_sel::frag_t* f = preset_sel::get_fragment_base( app->psH );
|
||||
for(; f!=nullptr; f=f->link)
|
||||
_update_frag_ui( app, f->fragId );
|
||||
|
||||
_do_load(app);
|
||||
//_do_load(app);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -1495,6 +1691,10 @@ namespace cw
|
||||
_do_stop_play(app);
|
||||
break;
|
||||
|
||||
case kLiveCheckId:
|
||||
app->useLiveMidiFl = m.value->u.b;
|
||||
break;
|
||||
|
||||
case kPrintMidiCheckId:
|
||||
app->printMidiFl = m.value->u.b;
|
||||
break;
|
||||
@ -1543,6 +1743,7 @@ namespace cw
|
||||
_on_ui_delete_btn(app);
|
||||
break;
|
||||
|
||||
/*
|
||||
case kHalfPedalPedalVel:
|
||||
case kHalfPedalDelayMs:
|
||||
case kHalfPedalPitch:
|
||||
@ -1551,6 +1752,7 @@ namespace cw
|
||||
case kHalfPedalDnDelayMs:
|
||||
_on_ui_half_pedal_value( app, m.appId, m.uuId, m.value->u.u );
|
||||
break;
|
||||
*/
|
||||
|
||||
case kFragInGainId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.d);
|
||||
@ -1581,6 +1783,14 @@ namespace cw
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kFragPlaySeqBtnId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kFragPlayAllBtnId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kFragNoteId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.s );
|
||||
break;
|
||||
@ -1592,6 +1802,11 @@ namespace cw
|
||||
case kFragPresetSelId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kFragPresetSeqSelId:
|
||||
_on_ui_frag_value( app, m.uuId, m.value->u.b );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -1677,6 +1892,7 @@ namespace cw
|
||||
_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
|
||||
break;
|
||||
|
||||
/*
|
||||
case kHalfPedalPedalVel:
|
||||
case kHalfPedalDelayMs:
|
||||
case kHalfPedalPitch:
|
||||
@ -1685,7 +1901,7 @@ namespace cw
|
||||
case kHalfPedalDnDelayMs:
|
||||
_on_echo_half_pedal( app, m.appId, m.uuId );
|
||||
break;
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
return rc;
|
||||
@ -1730,6 +1946,7 @@ namespace cw
|
||||
break;
|
||||
|
||||
case ui::kIdleOpId:
|
||||
_do_seq_exec( app );
|
||||
break;
|
||||
|
||||
case ui::kInvalidOpId:
|
||||
@ -1749,11 +1966,18 @@ namespace cw
|
||||
rc_t rc = kOkRC;
|
||||
app_t* app = reinterpret_cast<app_t*>(arg);
|
||||
|
||||
if( app->mrpH.isValid() )
|
||||
if( app->mrpH.isValid() && !app->useLiveMidiFl )
|
||||
{
|
||||
midi_record_play::exec( app->mrpH, *m );
|
||||
if( midi_record_play::is_started(app->mrpH) )
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
|
||||
{
|
||||
unsigned cur_loc = midi_record_play::event_loc(app->mrpH);
|
||||
if( cur_loc > 0 )
|
||||
{
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), cur_loc );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if( app->ioFlowH.isValid() )
|
||||
@ -1771,6 +1995,8 @@ namespace cw
|
||||
break;
|
||||
|
||||
case io::kMidiTId:
|
||||
if( app->useLiveMidiFl && app->mrpH.isValid() )
|
||||
_on_live_midi( app, *m );
|
||||
break;
|
||||
|
||||
case io::kAudioTId:
|
||||
@ -1802,7 +2028,7 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
|
||||
cw::rc_t cw::preset_sel_app::main( const object_t* cfg )
|
||||
{
|
||||
|
||||
rc_t rc;
|
||||
@ -1837,7 +2063,7 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
|
||||
}
|
||||
|
||||
// create the IO Flow controller
|
||||
if(app.flow_cfg==nullptr || flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,app.crossFadeSrate,app.crossFadeCnt,*flow_proc_dict,*app.flow_cfg)) != kOkRC )
|
||||
if(app.flow_cfg==nullptr || app.flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,app.crossFadeSrate,app.crossFadeCnt,*app.flow_proc_dict,*app.flow_cfg)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The IO Flow controller create failed.");
|
||||
goto errLabel;
|
||||
@ -1852,9 +2078,9 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
|
||||
|
||||
|
||||
// execute the io framework
|
||||
while( !isShuttingDown(app.ioH))
|
||||
while( !io::isShuttingDown(app.ioH))
|
||||
{
|
||||
exec(app.ioH);
|
||||
io::exec(app.ioH);
|
||||
sleepMs(50);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ namespace cw
|
||||
{
|
||||
namespace preset_sel_app
|
||||
{
|
||||
rc_t main( const object_t* cfg, const object_t* flow_proc_dict );
|
||||
rc_t main( const object_t* cfg );
|
||||
}
|
||||
}
|
||||
|
||||
|
304
cwKeyboard.cpp
Normal file
304
cwKeyboard.cpp
Normal 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
70
cwKeyboard.h
Normal 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
|
5
cwMidi.h
5
cwMidi.h
@ -11,6 +11,7 @@ namespace cw
|
||||
kInvalidMidiByte = 128,
|
||||
kMidiNoteCnt = kInvalidMidiByte,
|
||||
kMidiCtlCnt = kInvalidMidiByte,
|
||||
kMidiVelCnt = kInvalidMidiByte,
|
||||
kMidiPgmCnt = kInvalidMidiByte,
|
||||
kInvalidMidiPitch = kInvalidMidiByte,
|
||||
kInvalidMidiVelocity = kInvalidMidiByte,
|
||||
@ -98,6 +99,10 @@ namespace cw
|
||||
template< typename T> bool isSostenutoPedalDown( T s, T d0, T d1) { return ( isSostenutoPedal(s,d0) && (d1)>=64 ); }
|
||||
template< typename T> bool isSostenutoPedalUp( T s, T d0, T d1) { return ( isSostenutoPedal(s,d0) && (d1)<64 ); }
|
||||
|
||||
template< typename T> bool isSoftPedal( T s, T d0 ) { return ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)== kSoftPedalCtlMdId ); }
|
||||
template< typename T> bool isSoftPedalDown( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && (d1)>=64 ); }
|
||||
template< typename T> bool isSoftPedalUp( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && (d1)<64 ); }
|
||||
|
||||
template< typename T> bool isPedal( T s, T d0 ) { return ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)>=kSustainCtlMdId && (d0)<=kLegatoCtlMdId ); }
|
||||
template< typename T> bool isPedalDown( T s, T d0, T d1 ) { return ( isPedal(s,d0) && (d1)>=64 ); }
|
||||
template< typename T> bool isPedalUp( T s, T d0, T d1 ) { return ( isPedal(s,d0) && (d1)<64 ); }
|
||||
|
@ -134,11 +134,17 @@ namespace cw
|
||||
|
||||
// if no input
|
||||
if( rc == -EAGAIN )
|
||||
{
|
||||
// TODO: report or at least count error
|
||||
break;
|
||||
}
|
||||
|
||||
// if input buffer overrun
|
||||
if( rc == -ENOSPC )
|
||||
{
|
||||
// TODO: report or at least count error
|
||||
break;
|
||||
}
|
||||
|
||||
// get the device this event arrived from
|
||||
if( p->prvRcvDev==NULL || p->prvRcvDev->clientId != ev->source.client )
|
||||
|
@ -1116,6 +1116,48 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
rc_t _testBatchConvert( const object_t* cfg )
|
||||
{
|
||||
rc_t rc;
|
||||
const char* io_dir = nullptr;
|
||||
const char* session_dir = nullptr;
|
||||
unsigned take_begin = 0;
|
||||
unsigned take_end = 0;
|
||||
bool printWarningsFl = true;
|
||||
if((rc = cfg->getv("io_dir",io_dir,
|
||||
"session_dir",session_dir,
|
||||
"take_begin",take_begin,
|
||||
"take_end",take_end,
|
||||
"print_warnings_flag",printWarningsFl)) != kOkRC )
|
||||
{
|
||||
cwLogError(rc,"MIDI file batch convert to CSV failed.");
|
||||
}
|
||||
|
||||
for(unsigned i=take_begin; i<=take_end; ++i)
|
||||
{
|
||||
char take_dir[32];
|
||||
snprintf(take_dir,32,"record_%i",i);
|
||||
char* src_midi_fn = filesys::makeFn( io_dir, "midi", ".mid", session_dir, take_dir, nullptr );
|
||||
char* dst_csv_fn = filesys::makeFn( io_dir, "midi", ".csv", session_dir, take_dir, nullptr );
|
||||
|
||||
char* sm_fn = filesys::expandPath( src_midi_fn );
|
||||
char* dm_fn = filesys::expandPath( dst_csv_fn );
|
||||
|
||||
//rc = genCsvFile(mfn,cfn );
|
||||
cwLogInfo("Midi to CSV: src:%s dst:%s\n", sm_fn,dm_fn);
|
||||
|
||||
if((rc = genCsvFile(sm_fn, dm_fn, printWarningsFl )) != kOkRC )
|
||||
cwLogError(rc,"MIDI to CSV Conversion failed on %s to %s.",sm_fn,dm_fn);
|
||||
|
||||
mem::release(sm_fn);
|
||||
mem::release(dm_fn);
|
||||
|
||||
mem::release(src_midi_fn);
|
||||
mem::release(dst_csv_fn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1746,12 +1788,18 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
|
||||
trackMsg_t* m0 = noteM[k];
|
||||
|
||||
if( m0 == NULL )
|
||||
{
|
||||
if( warningFl )
|
||||
cwLogWarning("%i : Missing note-on instance for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// a key was released - so it should not already be up
|
||||
if( noteGateM[k]==0 )
|
||||
{
|
||||
if( warningFl )
|
||||
cwLogWarning("%i : Missing note-on for note-off:%s",m->uid,midi::midiToSciPitch(d0,NULL,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
noteGateM[k] -= 1; // update the note gate state
|
||||
@ -1783,7 +1831,7 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
|
||||
if( isSustainPedalUp(m) )
|
||||
{
|
||||
// if the sustain channel is already up
|
||||
if( sustGateV[ch]==0 )
|
||||
if( warningFl && sustGateV[ch]==0 )
|
||||
cwLogWarning("%i : The sustain pedal release message was received with no previous pedal down.",m->uid);
|
||||
|
||||
if( sustGateV[ch] >= 1 )
|
||||
@ -1830,7 +1878,7 @@ void cw::midi::file::calcNoteDurations( handle_t h, unsigned flags )
|
||||
if( isSostenutoPedalUp(m) )
|
||||
{
|
||||
// if the sustain channel is already up
|
||||
if( sostGateV[ch]==0 )
|
||||
if( warningFl && sostGateV[ch]==0 )
|
||||
cwLogWarning("%i : The sostenuto pedal release message was received with no previous pedal down.",m->uid);
|
||||
|
||||
if( sostGateV[ch] >= 1 )
|
||||
@ -2157,7 +2205,7 @@ cw::rc_t cw::midi::file::genPlotFile( const char* midiFn, const char* outFn )
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn )
|
||||
cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn, bool printWarningsFl)
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
handle_t mfH;
|
||||
@ -2166,7 +2214,7 @@ cw::rc_t cw::midi::file::genCsvFile( const char* midiFn, const char* csvFn )
|
||||
if((rc = open( mfH, midiFn )) != kOkRC )
|
||||
return cwLogError(rc,"The MIDI file object could not be opened from '%s'.",cwStringNullGuard(midiFn));
|
||||
|
||||
calcNoteDurations( mfH, 0 );
|
||||
calcNoteDurations( mfH, printWarningsFl ? kWarningsMfFl : 0 );
|
||||
|
||||
if((rc = cw::file::open(fH, csvFn,cw::file::kWriteFl)) != kOkRC )
|
||||
{
|
||||
@ -2351,13 +2399,23 @@ cw::rc_t cw::midi::file::test( const object_t* cfg )
|
||||
{
|
||||
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
const object_t* o;
|
||||
|
||||
if((o = cfg->find("rpt")) != nullptr )
|
||||
rc = _testReport(o);
|
||||
for(unsigned i=0; i<cfg->child_count(); ++i)
|
||||
{
|
||||
if((o = cfg->child_ele(i)) != nullptr )
|
||||
{
|
||||
if( strcmp(o->pair_label(),"rpt")==0 )
|
||||
rc = _testReport(o->pair_value());
|
||||
|
||||
if((o = cfg->find("csv")) != nullptr )
|
||||
rc = _testCsv(o);
|
||||
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;
|
||||
|
||||
|
@ -218,7 +218,7 @@ namespace cw
|
||||
|
||||
rc_t genSvgFile(const char* midiFn, const char* outSvgFn, const char* cssFn, bool standAloneFl, bool panZoomFl );
|
||||
|
||||
rc_t genCsvFile( const char* midiFn, const char* csvFn );
|
||||
rc_t genCsvFile( const char* midiFn, const char* csvFn, bool printWarningsFl=true );
|
||||
|
||||
// Generate a text file reportusing cmMIdiFilePrintMsgs()
|
||||
rc_t report( const char* midiFn, log::handle_t logH );
|
||||
|
168
cwPresetSel.cpp
168
cwPresetSel.cpp
@ -202,6 +202,56 @@ namespace cw
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
bool fl = (preset_id < frag->presetN) && (frag->presetA[ preset_id ].preset_idx == preset_id);
|
||||
@ -234,7 +284,11 @@ namespace cw
|
||||
case kPresetSelectVarId:
|
||||
for(unsigned i=0; i<f->presetN; ++i)
|
||||
f->presetA[i].playFl = f->presetA[i].preset_idx == presetId ? value : false;
|
||||
break;
|
||||
|
||||
case kPresetSeqSelectVarId:
|
||||
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
|
||||
f->presetA[ presetId ].seqFl = value;
|
||||
break;
|
||||
|
||||
case kPresetOrderVarId:
|
||||
@ -269,6 +323,14 @@ namespace cw
|
||||
case kPlayBtnVarId:
|
||||
break;
|
||||
|
||||
case kPlaySeqBtnVarId:
|
||||
f->seqAllFl = false;
|
||||
break;
|
||||
|
||||
case kPlaySeqAllBtnVarId:
|
||||
f->seqAllFl = true;
|
||||
break;
|
||||
|
||||
case kMasterWetInGainVarId:
|
||||
p->master_wet_in_gain = value;
|
||||
break;
|
||||
@ -329,6 +391,11 @@ namespace cw
|
||||
valueRef = f->presetA[ presetId ].playFl;
|
||||
break;
|
||||
|
||||
case kPresetSeqSelectVarId:
|
||||
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
|
||||
valueRef = f->presetA[ presetId ].seqFl;
|
||||
break;
|
||||
|
||||
case kPresetOrderVarId:
|
||||
if((rc = _validate_preset_id(f, presetId )) == kOkRC )
|
||||
valueRef = f->presetA[ presetId ].order;
|
||||
@ -361,6 +428,13 @@ namespace cw
|
||||
case kPlayBtnVarId:
|
||||
break;
|
||||
|
||||
case kPlaySeqBtnVarId:
|
||||
break;
|
||||
|
||||
case kPlaySeqAllBtnVarId:
|
||||
valueRef = f->seqAllFl;
|
||||
break;
|
||||
|
||||
case kMasterWetInGainVarId:
|
||||
valueRef = p->master_wet_in_gain;
|
||||
break;
|
||||
@ -769,8 +843,8 @@ bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const
|
||||
|
||||
time::spec_t t0;
|
||||
time::setZero(t0);
|
||||
unsigned elapsedMs = time::elapsedMs(t0,ts);
|
||||
double mins = elapsedMs / 60000.0;
|
||||
//unsigned elapsedMs = time::elapsedMs(t0,ts);
|
||||
//double mins = elapsedMs / 60000.0;
|
||||
|
||||
|
||||
// if this is the first call to 'track_timestamp()'.
|
||||
@ -802,15 +876,103 @@ bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const
|
||||
return frag_changed_fl;
|
||||
}
|
||||
|
||||
unsigned cw::preset_sel::fragment_play_preset_index( const frag_t* frag )
|
||||
|
||||
void cw::preset_sel::track_loc_reset( handle_t h )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
p->last_ts_frag = nullptr;
|
||||
}
|
||||
|
||||
bool cw::preset_sel::track_loc( handle_t h, unsigned loc, const cw::preset_sel::frag_t*& frag_Ref )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f = nullptr;
|
||||
bool frag_changed_fl = false;
|
||||
|
||||
|
||||
// if this is the first call to 'track_timestamp()'.
|
||||
if( p->last_ts_frag == nullptr )
|
||||
f = _fast_loc_to_frag(p,loc);
|
||||
else
|
||||
// if the 'ts' is in the same frag as previous call.
|
||||
if( _loc_is_in_frag(p->last_ts_frag,loc) )
|
||||
f = p->last_ts_frag;
|
||||
else
|
||||
// if 'ts' is in a later frag
|
||||
if( _loc_is_after_frag(p->last_ts_frag,loc) )
|
||||
f = _fast_loc_to_frag(p,loc,p->last_ts_frag);
|
||||
else // ts is prior to 'last_ts_frag'
|
||||
f = _fast_loc_to_frag(p,loc);
|
||||
|
||||
// 'f' will be null at this point if 'ts' is past the last preset.
|
||||
// In this case we should leave 'last_ts_frag' unchanged.
|
||||
|
||||
// if 'f' is valid but different from 'last_ts_frag'
|
||||
if( f != nullptr && f != p->last_ts_frag )
|
||||
{
|
||||
// don't allow the selected fragment to go backwards
|
||||
if( p->last_ts_frag == nullptr || (p->last_ts_frag != nullptr && p->last_ts_frag->endLoc < f->endLoc) )
|
||||
{
|
||||
p->last_ts_frag = f;
|
||||
frag_changed_fl = true;
|
||||
}
|
||||
}
|
||||
|
||||
frag_Ref = p->last_ts_frag;
|
||||
|
||||
return frag_changed_fl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned cw::preset_sel::fragment_play_preset_index( const frag_t* frag, unsigned preset_seq_idx )
|
||||
{
|
||||
unsigned n = 0;
|
||||
// for each preset
|
||||
for(unsigned i=0; i<frag->presetN; ++i)
|
||||
{
|
||||
// if 'preset_seq_idx' is not valid ...
|
||||
if( preset_seq_idx==kInvalidIdx )
|
||||
{
|
||||
// ...then select the first preset whose 'playFl' is set.
|
||||
if( frag->presetA[i].playFl )
|
||||
return frag->presetA[i].preset_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ... otherwise select the 'nth' preset whose 'seqFl' is set
|
||||
if( frag->presetA[i].seqFl || frag->seqAllFl )
|
||||
{
|
||||
if( n == preset_seq_idx )
|
||||
return frag->presetA[i].preset_idx;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalidIdx;
|
||||
}
|
||||
|
||||
unsigned cw::preset_sel::fragment_seq_count( handle_t h, unsigned fragId )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f = nullptr;
|
||||
unsigned n = 0;
|
||||
|
||||
if((rc = _find_frag(p,fragId,f)) != kOkRC )
|
||||
return 0;
|
||||
|
||||
if( f->seqAllFl )
|
||||
return f->presetN;
|
||||
|
||||
for(unsigned i=0; i<f->presetN; ++i)
|
||||
if( f->presetA[i].seqFl )
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::preset_sel::write( handle_t h, const char* fn )
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ namespace cw
|
||||
typedef struct preset_str
|
||||
{
|
||||
bool playFl; // play this preset
|
||||
bool seqFl; // play this preset during sequencing.
|
||||
unsigned preset_idx; // preset index into preset_labelA[].
|
||||
unsigned order; //
|
||||
} preset_t;
|
||||
@ -34,6 +35,7 @@ namespace cw
|
||||
unsigned presetN;
|
||||
|
||||
bool uiSelectFl;
|
||||
bool seqAllFl; // Set if all preset.seqFl's should be treated as though they are set to true.
|
||||
|
||||
struct frag_str* link;
|
||||
struct frag_str* prev;
|
||||
@ -51,10 +53,13 @@ namespace cw
|
||||
kBegPlayLocVarId,
|
||||
kEndPlayLocVarId,
|
||||
kPlayBtnVarId,
|
||||
kPlaySeqBtnVarId,
|
||||
kPlaySeqAllBtnVarId,
|
||||
kNoteVarId,
|
||||
|
||||
kPresetOrderVarId, // preset order value
|
||||
kPresetSelectVarId, // select a preset to play
|
||||
kPresetSeqSelectVarId, // sequence preset selections to play
|
||||
kPlayEnableVarId, // include in the segment to play
|
||||
kDryFlVarId, // play this fragment dry
|
||||
|
||||
@ -110,8 +115,15 @@ namespace cw
|
||||
void track_timestamp_reset( handle_t h );
|
||||
bool track_timestamp( handle_t h, const time::spec_t& ts, const cw::preset_sel::frag_t*& frag_Ref );
|
||||
|
||||
// Same as track_timestamp_???() but tracks the score 'loc' instead of timestamp.
|
||||
void track_loc_reset( handle_t h );
|
||||
bool track_loc( handle_t h, unsigned loc, const cw::preset_sel::frag_t*& frag_Ref );
|
||||
|
||||
// Return the preset index marked to play on this fragment.
|
||||
unsigned fragment_play_preset_index( const frag_t* frag );
|
||||
unsigned fragment_play_preset_index( const frag_t* frag, unsigned preset_seq_idx=kInvalidIdx );
|
||||
|
||||
// Return the count of presets whose 'seqFl' is set.
|
||||
unsigned fragment_seq_count( handle_t h, unsigned fragId );
|
||||
|
||||
rc_t write( handle_t h, const char* fn );
|
||||
rc_t read( handle_t h, const char* fn );
|
||||
|
130
cwScoreFollower.cpp
Normal file
130
cwScoreFollower.cpp
Normal 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
21
cwScoreFollower.h
Normal 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
|
@ -8,11 +8,23 @@ namespace cw
|
||||
typedef handle<struct thread_mach_str> handle_t;
|
||||
typedef thread::cbFunc_t threadFunc_t;
|
||||
|
||||
// Create a thread machine instance.
|
||||
// contextArray[ threadN ][ contextRecdByteN ] is an optional blob consisting of 'threadN' records each of size 'contextRecdByteN'.
|
||||
// Each of the records then becomes the entity which is used as the 'arg' value in the callback for the first 'threadN' threads.
|
||||
rc_t create( handle_t& hRef, threadFunc_t threadFunc=nullptr, void* contextArray=nullptr, unsigned contexRecdByteN=0, unsigned threadN=0 );
|
||||
rc_t destroy( handle_t& hRef );
|
||||
|
||||
// Create an additional thread. Note that the additional thread will be started by the next
|
||||
// call to 'start()'.
|
||||
rc_t add( handle_t h, threadFunc_t threadFunc, void* arg );
|
||||
|
||||
// Start all threads
|
||||
rc_t start( handle_t h );
|
||||
|
||||
// Stop all threads.
|
||||
rc_t stop( handle_t h );
|
||||
|
||||
// Check if all threads are shutdown.
|
||||
bool is_shutdown( handle_t h );
|
||||
}
|
||||
}
|
||||
|
65
cwUi.cpp
65
cwUi.cpp
@ -89,6 +89,7 @@ namespace cw
|
||||
char* recvBuf;
|
||||
unsigned recvBufN;
|
||||
unsigned recvBufIdx;
|
||||
unsigned recvShiftN;
|
||||
|
||||
unsigned* sessA; // sessA[ sessN ] array of wsSessId's
|
||||
unsigned sessN;
|
||||
@ -1048,6 +1049,53 @@ namespace cw
|
||||
return _setPropertyValue( h, propertyStr,uuId,enableFl ? 1 : 0 );
|
||||
}
|
||||
|
||||
rc_t _copy_msg_to_recv_buffer( ui_t* p, const void* msg, unsigned msgByteN )
|
||||
{
|
||||
if( msg == nullptr || msgByteN == 0)
|
||||
return kOkRC;
|
||||
|
||||
if( p->recvBufIdx + msgByteN > p->recvBufN )
|
||||
return cwLogError(kBufTooSmallRC,"The UI input buffer (%i) is too small.", p->recvBufN);
|
||||
|
||||
memcpy(p->recvBuf + p->recvBufIdx, msg, msgByteN );
|
||||
|
||||
p->recvBufIdx += msgByteN;
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
const char* _get_msg_from_recv_buffer( ui_t* p )
|
||||
{
|
||||
const char* msg = nullptr;
|
||||
unsigned i;
|
||||
|
||||
// shift off the previous msg
|
||||
if( p->recvShiftN > 0 )
|
||||
{
|
||||
assert( p->recvBufIdx >= p->recvShiftN );
|
||||
|
||||
memmove(p->recvBuf, p->recvBuf+p->recvShiftN, p->recvBufIdx - p->recvShiftN );
|
||||
p->recvBufIdx -= p->recvShiftN;
|
||||
p->recvShiftN = 0;
|
||||
|
||||
}
|
||||
|
||||
// locate the end of the next msg.
|
||||
if( p->recvBufIdx > 0 )
|
||||
{
|
||||
for(i=0; p->recvBuf[i]!=0 and i<p->recvBufIdx; ++i)
|
||||
{}
|
||||
|
||||
// if the end of the next msg was found
|
||||
if( i<p->recvBufIdx && p->recvBuf[i] == 0 )
|
||||
{
|
||||
p->recvShiftN = i+1;
|
||||
msg = p->recvBuf;
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1089,6 +1137,7 @@ cw::rc_t cw::ui::create(
|
||||
p->recvBuf = mem::allocZ<char>(fmtBufByteN);
|
||||
p->recvBufN = fmtBufByteN;
|
||||
p->recvBufIdx = 0;
|
||||
p->recvShiftN = 0;
|
||||
|
||||
// create the root element
|
||||
if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
|
||||
@ -1194,9 +1243,11 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg,
|
||||
rc_t rc = kOkRC;
|
||||
ui_t* p = _handleToPtr(h);
|
||||
opId_t opId = kInvalidOpId;
|
||||
ele_t* ele = nullptr;
|
||||
const char* msg = nullptr;
|
||||
value_t value;
|
||||
ele_t* ele;
|
||||
|
||||
/*
|
||||
const char* src_msg = (const char*)void_msg;
|
||||
const char* msg = src_msg;
|
||||
|
||||
@ -1233,6 +1284,15 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg,
|
||||
// the message is being processed so the buffer will end up empty
|
||||
// (if it was being used)
|
||||
p->recvBufIdx = 0;
|
||||
*/
|
||||
|
||||
// buffer the incoming msg
|
||||
if((rc = _copy_msg_to_recv_buffer( p, void_msg, msgByteN )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// remove and and act on each buffered msg
|
||||
while( (msg = _get_msg_from_recv_buffer(p)) != NULL )
|
||||
{
|
||||
|
||||
// parse the 'opId' from the message
|
||||
opId = _labelToOpId(msg);
|
||||
@ -1306,7 +1366,8 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg,
|
||||
break;
|
||||
|
||||
} // switch opId
|
||||
|
||||
}
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
34
cwUiDecls.h
34
cwUiDecls.h
@ -13,27 +13,27 @@ namespace cw
|
||||
|
||||
typedef enum
|
||||
{
|
||||
kInvalidOpId,
|
||||
kConnectOpId, // A new remote user interface was connected
|
||||
kInitOpId, // A remote user interface instance was created and is available. It needs to be updated with the current state of the UI from the server.
|
||||
kValueOpId, // The value of a remote user interface control changed. Send this value to the application engine.
|
||||
kCorruptOpId, // The value of the remote user interface is invalid or corrupt.
|
||||
kClickOpId, // A element on a remote user interface was clicked.
|
||||
kSelectOpId, // An element on a remote user interface was is 'selected' or 'deselected'.
|
||||
kEchoOpId, // A remote user interface is requesting an application engine value. The the current value of a ui element must be sent to the remote UI.
|
||||
kIdleOpId, // The application (UI server) is idle and waiting for the next event from a remote UI.
|
||||
kDisconnectOpId // A reemot user interface was disconnected.
|
||||
kInvalidOpId, // 0
|
||||
kConnectOpId, // 1 A new remote user interface was connected
|
||||
kInitOpId, // 2 A remote user interface instance was created and is available. It needs to be updated with the current state of the UI from the server.
|
||||
kValueOpId, // 3 The value of a remote user interface control changed. Send this value to the application engine.
|
||||
kCorruptOpId, // 4 The value of the remote user interface is invalid or corrupt.
|
||||
kClickOpId, // 5 A element on a remote user interface was clicked.
|
||||
kSelectOpId, // 6 An element on a remote user interface was is 'selected' or 'deselected'.
|
||||
kEchoOpId, // 7 A remote user interface is requesting an application engine value. The the current value of a ui element must be sent to the remote UI.
|
||||
kIdleOpId, // 8 The application (UI server) is idle and waiting for the next event from a remote UI.
|
||||
kDisconnectOpId // 9 A remote user interface was disconnected.
|
||||
} opId_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
kInvalidTId,
|
||||
kBoolTId,
|
||||
kIntTId,
|
||||
kUIntTId,
|
||||
kFloatTId,
|
||||
kDoubleTId,
|
||||
kStringTId
|
||||
kInvalidTId, // 0
|
||||
kBoolTId, // 1
|
||||
kIntTId, // 2
|
||||
kUIntTId, // 3
|
||||
kFloatTId, // 4
|
||||
kDoubleTId, // 5
|
||||
kStringTId // 6
|
||||
} dtypeId_t;
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
check:{ name: midiThruCheckId, title:"MIDI Thru" },
|
||||
numb_disp: { name: curMidiEvtCntId, title:"Current" },
|
||||
numb_disp: { name: totalMidiEvtCntId, title:"Total" },
|
||||
check:{ name: midiMuteCheckId, title:"Mute" },
|
||||
},
|
||||
|
||||
row: {
|
||||
@ -33,6 +34,7 @@
|
||||
check:{ name: audioThroughCheckId, title:"Audio Thru" },
|
||||
numb_disp: { name: curAudioSecsId, title:"Current:", decpl:1 },
|
||||
numb_disp: { name: totalAudioSecsId, title:"Total:", decpl:1 },
|
||||
check: { name: audioMuteCheckId, title:"Mute:" },
|
||||
},
|
||||
|
||||
row: {
|
||||
|
@ -21,10 +21,20 @@
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.fragPanelRow {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.fragPresetCtl {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.fragPresetCtl:nth-of-type(even){
|
||||
border: 1px solid Yellow;
|
||||
}
|
||||
|
||||
.fragPresetCtl .uiNumber {
|
||||
border: 1px solid LightSteelBlue;
|
||||
width: 80%;
|
||||
|
@ -467,7 +467,9 @@ function ui_create_check( parent_ele, d )
|
||||
ele.onclick = function() { ui_send_bool_value(this,dom_get_checkbox(this.id)); }
|
||||
|
||||
if( !d.hasOwnProperty('value') )
|
||||
{
|
||||
ui_send_echo(ele)
|
||||
}
|
||||
else
|
||||
{
|
||||
dom_set_checkbox(ele.id, d.value );
|
||||
@ -870,8 +872,6 @@ function ui_create_list( parent_ele, d )
|
||||
|
||||
function ui_set_value( d )
|
||||
{
|
||||
//console.log(d)
|
||||
|
||||
var eleId = d.uuId.toString()
|
||||
var ele = dom_id_to_ele(eleId)
|
||||
|
||||
@ -1089,7 +1089,9 @@ function ui_create( d )
|
||||
var parent_ele = ui_get_parent(d.parentUuId);
|
||||
var ele = null;
|
||||
|
||||
if( parent_ele != null )
|
||||
if( parent_ele == null )
|
||||
console.log("Parent ele not found.",d)
|
||||
else
|
||||
{
|
||||
switch( d.type )
|
||||
{
|
||||
|
@ -34,6 +34,7 @@
|
||||
button:{ name: stopBtnId, title:"Stop" },
|
||||
number:{ name: begLocNumbId, title:"Loc:", min:0, max:100000, step:1, decpl:0 },
|
||||
number:{ name: endLocNumbId, title:"End:", min:0, max:100000, step:1, decpl:0 },
|
||||
check: { name: liveCheckId, title:"Live" },
|
||||
},
|
||||
|
||||
row: {
|
||||
@ -49,6 +50,7 @@
|
||||
button:{ name: deleteBtnId, title:"Delete", enable: false },
|
||||
},
|
||||
|
||||
/*
|
||||
row: {
|
||||
number:{ name: halfPedalDelayMsId, title:"DelayMs:", min:0, max:5000, step:1, decpl:0 },
|
||||
number:{ name: halfPedalPedalVelId, title:"PVel:", min:0, max:127, step:1, decpl:0 },
|
||||
@ -57,6 +59,7 @@
|
||||
number:{ name: halfPedalDurMsId, title:"DurMs:", min:0, max:5000, step:1, decpl:0 },
|
||||
number:{ name: halfPedalDnDelayMsId, title:"DownMs:", min:0, max:5000, step:1, decpl:0 },
|
||||
},
|
||||
*/
|
||||
|
||||
row: {
|
||||
str_disp:{ name: statusId, title:"Status:", value: "" },
|
||||
|
Loading…
Reference in New Issue
Block a user