Merge branch 'master' of gitea.larke.org:kevin/libcw
This commit is contained in:
commit
f75510f624
@ -2,6 +2,15 @@
|
||||
|
||||
# To Do
|
||||
|
||||
|
||||
- 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
|
||||
from having to explicitely store all UI values and handle all the 'value' and 'echo'
|
||||
request. It would support a model where the UI values get changed and then
|
||||
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.
|
||||
|
||||
- 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?
|
||||
|
@ -605,7 +605,11 @@ namespace cw
|
||||
int* dp = (int*)obuf;
|
||||
|
||||
while( sp < ep )
|
||||
*dp++ = (int)(*sp++ * 0x7fffffff);
|
||||
{
|
||||
device::sample_t v = *sp++;
|
||||
v = ((v > 1 ? 1 : v) < -1 ? -1 : v);
|
||||
*dp++ = (int)(v * 0x7fffffff);
|
||||
}
|
||||
//*dp++ = (rand() - (RAND_MAX/2)) * 2;
|
||||
|
||||
}
|
||||
|
@ -785,16 +785,16 @@ namespace cw
|
||||
}
|
||||
|
||||
template< typename T0, typename T1 >
|
||||
void _cmSpecDist2Bump( struct obj_str<T0,T1>* p, T0* x, unsigned binCnt, T1 thresh, T1 expo)
|
||||
void _cmSpecDist2Bump( struct obj_str<T0,T1>* p, double* x, unsigned binCnt, double thresh, double expo)
|
||||
{
|
||||
unsigned i = 0;
|
||||
T1 minDb = -100.0;
|
||||
double minDb = -100.0;
|
||||
|
||||
thresh = -fabs(thresh);
|
||||
|
||||
for(i=0; i<binCnt; ++i)
|
||||
{
|
||||
T1 y;
|
||||
double y;
|
||||
|
||||
if( x[i] < minDb )
|
||||
x[i] = minDb;
|
||||
@ -813,7 +813,7 @@ namespace cw
|
||||
}
|
||||
|
||||
template< typename T0, typename T1 >
|
||||
void _cmSpecDist2BasicMode( struct obj_str<T0,T1>* p, T0* X1m, unsigned binCnt, T1 thresh, T1 upr, T1 lwr )
|
||||
void _cmSpecDist2BasicMode( struct obj_str<T0,T1>* p, double* X1m, unsigned binCnt, double thresh, double upr, double lwr )
|
||||
{
|
||||
|
||||
unsigned i=0;
|
||||
@ -823,8 +823,8 @@ namespace cw
|
||||
|
||||
for(i=0; i<binCnt; ++i)
|
||||
{
|
||||
T0 a = fabs(X1m[i]);
|
||||
T0 d = a - thresh;
|
||||
double a = fabs(X1m[i]);
|
||||
double d = a - thresh;
|
||||
|
||||
X1m[i] = -thresh;
|
||||
|
||||
@ -841,11 +841,11 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
T0 X0m[binN];
|
||||
T0 X1m[binN];
|
||||
double X0m[binN];
|
||||
double X1m[binN];
|
||||
|
||||
// take the mean of the the input magntitude spectrum
|
||||
T0 u0 = vop::mean(magV,binN);
|
||||
double u0 = vop::mean(magV,binN);
|
||||
|
||||
// convert magnitude to db (range=-1000.0 to 0.0)
|
||||
vop::ampl_to_db(X0m, magV, binN );
|
||||
@ -867,10 +867,10 @@ namespace cw
|
||||
vop::db_to_ampl(X1m, X1m, binN );
|
||||
|
||||
// convert the mean input magnitude to db
|
||||
T0 idb = 20*log10(u0);
|
||||
double idb = 20*log10(u0);
|
||||
|
||||
// get the mean output magnitude spectra
|
||||
T0 u1 = vop::mean(X1m,binN);
|
||||
double u1 = vop::mean(X1m,binN);
|
||||
|
||||
if( idb > -150.0 )
|
||||
{
|
||||
@ -890,7 +890,6 @@ namespace cw
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,9 @@ bool cw::filesys::isDir( const char* dirStr )
|
||||
|
||||
errno = 0;
|
||||
|
||||
if( dirStr == nullptr )
|
||||
return false;
|
||||
|
||||
if( stat(dirStr,&s) != 0 )
|
||||
{
|
||||
// if the dir does not exist
|
||||
@ -81,6 +84,9 @@ bool cw::filesys::isFile( const char* fnStr )
|
||||
struct stat s;
|
||||
errno = 0;
|
||||
|
||||
if( fnStr == nullptr )
|
||||
return false;
|
||||
|
||||
if( stat(fnStr,&s) != 0 )
|
||||
{
|
||||
|
||||
@ -101,6 +107,9 @@ bool cw::filesys::isLink( const char* fnStr )
|
||||
struct stat s;
|
||||
errno = 0;
|
||||
|
||||
if( fnStr == nullptr )
|
||||
return false;
|
||||
|
||||
if( lstat(fnStr,&s) != 0 )
|
||||
{
|
||||
// if the file does not exist
|
||||
@ -314,6 +323,9 @@ char* cw::filesys::expandPath( const char* dir )
|
||||
|
||||
memset(&res,0,sizeof(res));
|
||||
|
||||
if( dir == nullptr )
|
||||
return nullptr;
|
||||
|
||||
if((sysRC = wordexp(dir,&res,flags)) != 0)
|
||||
{
|
||||
switch(sysRC)
|
||||
|
@ -368,16 +368,18 @@ namespace cw
|
||||
// since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel
|
||||
assert( base_src_var->chIdx == kAnyChIdx );
|
||||
|
||||
printf("%s %s\n",inst->label,var->label);
|
||||
//printf("%s %s\n",inst->label,var->label);
|
||||
|
||||
// for each var channel in the input var
|
||||
for(variable_t* in_var = var->ch_link; in_var != nullptr; in_var=in_var->ch_link)
|
||||
{
|
||||
|
||||
|
||||
// locate the matching channel on the 'src' var
|
||||
variable_t* svar = base_src_var;
|
||||
for(; svar!=nullptr; svar=svar->ch_link)
|
||||
if( svar->chIdx == in_var->chIdx )
|
||||
break;
|
||||
|
||||
// connect the src->input var
|
||||
_connect_vars( svar==nullptr ? base_src_var : svar, in_var);
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ namespace cw
|
||||
unsigned fadeSmpN; //
|
||||
|
||||
unsigned net_idx;
|
||||
|
||||
|
||||
} flow_network_t;
|
||||
|
||||
@ -54,6 +55,7 @@ namespace cw
|
||||
flow::external_device_t* deviceA;
|
||||
unsigned deviceN;
|
||||
|
||||
bool fadeInputFl;
|
||||
} flow_cross_t;
|
||||
|
||||
flow_cross_t* _handleToPtr(handle_t h)
|
||||
@ -208,9 +210,9 @@ namespace cw
|
||||
flow::abuf_t* src = p->deviceA[devIdx].u.a.abuf;
|
||||
flow::abuf_t* dst = net->deviceA[devIdx].u.a.abuf;
|
||||
|
||||
memset( dst->buf, 0, dst->chN * dst->frameN * sizeof(flow::sample_t));
|
||||
memcpy( dst->buf, src->buf, dst->chN * dst->frameN * sizeof(flow::sample_t));
|
||||
|
||||
_fade_audio( src, dst, net );
|
||||
//_fade_audio( src, dst, net );
|
||||
}
|
||||
|
||||
void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
|
||||
@ -253,11 +255,20 @@ namespace cw
|
||||
template< typename T >
|
||||
rc_t _set_variable_value( handle_t h, destId_t destId, const char* inst_label, const char* var_label, unsigned chIdx, const T& value )
|
||||
{
|
||||
//rc_t rc = kOkRC;
|
||||
rc_t rc = kOkRC;
|
||||
flow_cross_t* p = _handleToPtr(h);
|
||||
unsigned flow_idx = _get_flow_index(p, destId );
|
||||
return set_variable_value( p->netA[ flow_idx ].flowH, inst_label, var_label, chIdx, value );
|
||||
|
||||
unsigned flow_idx = destId == kAllDestId ? 0 : _get_flow_index(p, destId );
|
||||
unsigned flow_cnt = destId == kAllDestId ? p->netN : flow_idx + 1;
|
||||
|
||||
for(; flow_idx < flow_cnt; ++flow_idx )
|
||||
if((rc = set_variable_value( p->netA[ flow_idx ].flowH, inst_label, var_label, chIdx, value )) != kOkRC )
|
||||
{
|
||||
cwLogError(rc,"Set variable value failed on cross-network index: %i.",flow_idx);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -331,28 +342,33 @@ cw::rc_t cw::flow_cross::exec_cycle( handle_t h )
|
||||
rc_t rc = kOkRC;
|
||||
flow_cross_t* p = _handleToPtr(h);
|
||||
|
||||
// Note that all networks must be updated so that they maintain
|
||||
// their state (e.g. delay line memory) event if their output
|
||||
// is faded out
|
||||
for(unsigned i=0; i<p->netN; ++i)
|
||||
{
|
||||
flow_network_t* net = p->netA + i;
|
||||
|
||||
if( net->stateId != kInactiveStateId )
|
||||
{
|
||||
// We generally don't want to fade the input because the state
|
||||
// of the network delay lines would then be invalid when the
|
||||
// network is eventually made active again
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) )
|
||||
_update_audio_input( p, p->netA + i, j );
|
||||
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) )
|
||||
_update_audio_input( p, p->netA + i, j );
|
||||
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )
|
||||
// zero the audio device output buffers because we are about to sum into them
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )
|
||||
_zero_audio_output( p, net, j );
|
||||
|
||||
|
||||
flow::exec_cycle( net->flowH );
|
||||
// update the network
|
||||
flow::exec_cycle( net->flowH );
|
||||
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )
|
||||
_update_audio_output( p, net, j );
|
||||
}
|
||||
// sum the output from the network into the audio output device buffer
|
||||
// (this is were newly active networks are faded in)
|
||||
for(unsigned j=0; j<p->deviceN; ++j)
|
||||
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )
|
||||
_update_audio_output( p, net, j );
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -426,6 +442,9 @@ void cw::flow_cross::print( handle_t h )
|
||||
{
|
||||
flow_cross_t* p = _handleToPtr(h);
|
||||
|
||||
unsigned cur_flow_idx = _get_flow_index( p, kCurDestId );
|
||||
unsigned next_flow_idx = _get_flow_index( p, kNextDestId );
|
||||
|
||||
printf("flow_cross: sr:%7.1f\n", p->srate );
|
||||
|
||||
printf("master devices:\n");
|
||||
@ -434,7 +453,21 @@ void cw::flow_cross::print( handle_t h )
|
||||
|
||||
for(unsigned i=0; i<p->netN; ++i)
|
||||
{
|
||||
printf("cross network:%i \n",i);
|
||||
const char* label = i==cur_flow_idx ? "Current" : (i==next_flow_idx ? "Next" : "");
|
||||
printf("cross network:%i (%s)\n",i,label);
|
||||
flow::print_network( p->netA[i].flowH );
|
||||
}
|
||||
}
|
||||
|
||||
void cw::flow_cross::print_network( handle_t h, destId_t destId )
|
||||
{
|
||||
flow_cross_t* p = _handleToPtr(h);
|
||||
unsigned flow_idx = _get_flow_index( p, destId );
|
||||
|
||||
if( flow_idx == kInvalidIdx )
|
||||
cwLogError(kInvalidArgRC,"The network id '%i' is not valid. Cannot print the network state.",destId);
|
||||
else
|
||||
flow::print_network( p->netA[flow_idx].flowH );
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ namespace cw
|
||||
{
|
||||
kCurDestId, // Apply value to the current flow network
|
||||
kNextDestId, // Apply value to the next flow network (i.e. network which will be current following the next cross-fade)
|
||||
kAllDestId, // Apply value to all the flow networks
|
||||
} destId_t;
|
||||
|
||||
rc_t create( handle_t& hRef,
|
||||
@ -38,6 +39,7 @@ namespace cw
|
||||
rc_t begin_cross_fade( handle_t h, unsigned crossFadeMs );
|
||||
|
||||
void print( handle_t h );
|
||||
void print_network( handle_t h, flow_cross::destId_t destId );
|
||||
|
||||
}
|
||||
}
|
||||
|
134
cwFlowProc.cpp
134
cwFlowProc.cpp
@ -98,7 +98,7 @@ namespace cw
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
real_t value;
|
||||
} inst_t;
|
||||
|
||||
|
||||
@ -106,6 +106,7 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
real_t in_value = 0.5;
|
||||
ctx->userPtr = mem::allocZ<inst_t>();
|
||||
|
||||
if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", in_value )) != kOkRC )
|
||||
goto errLabel;
|
||||
@ -122,20 +123,33 @@ namespace cw
|
||||
}
|
||||
|
||||
rc_t destroy( instance_t* ctx )
|
||||
{ return kOkRC; }
|
||||
{
|
||||
mem::release( ctx->userPtr );
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t value( instance_t* ctx, variable_t* var )
|
||||
{ return kOkRC; }
|
||||
{
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t exec( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
inst_t* inst = (inst_t*)(ctx->userPtr);
|
||||
|
||||
real_t value = 1;
|
||||
|
||||
var_get(ctx, kInPId, kAnyChIdx, value);
|
||||
var_set(ctx, kOutPId, kAnyChIdx, value);
|
||||
var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) );
|
||||
|
||||
if( inst->value != value )
|
||||
{
|
||||
inst->value = value;
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -228,7 +242,9 @@ namespace cw
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(abuf->buf,inst->ext_dev->u.a.abuf->buf, abuf->frameN*abuf->chN*sizeof(sample_t));
|
||||
unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, abuf->chN );
|
||||
unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, abuf->frameN );
|
||||
memcpy(abuf->buf,inst->ext_dev->u.a.abuf->buf, frameN*chN*sizeof(sample_t));
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -316,8 +332,9 @@ namespace cw
|
||||
rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label);
|
||||
else
|
||||
{
|
||||
|
||||
unsigned n = src_abuf->frameN*src_abuf->chN;
|
||||
unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, src_abuf->chN);
|
||||
unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, src_abuf->frameN);
|
||||
unsigned n = chN * frameN;
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
inst->ext_dev->u.a.abuf->buf[i] += src_abuf->buf[i];
|
||||
|
||||
@ -384,7 +401,7 @@ namespace cw
|
||||
|
||||
cwLogInfo("Audio '%s' srate:%f chs:%i frames:%i %f seconds.",inst->filename,info.srate,info.chCnt,info.frameCnt, info.frameCnt/info.srate );
|
||||
|
||||
// create one output audio buffer
|
||||
// create one output audio buffer - with the same configuration as the source audio file
|
||||
rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle );
|
||||
|
||||
errLabel:
|
||||
@ -491,7 +508,7 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// create the audio file
|
||||
// create the audio file with the same channel count as the incoming signal
|
||||
if((rc = audiofile::create( inst->afH, inst->filename, src_abuf->srate, audioFileBits, src_abuf->chN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kOpFailRC,"The audio file create failed on '%s'.",cwStringNullGuard(inst->filename));
|
||||
@ -577,10 +594,18 @@ namespace cw
|
||||
kOutPId
|
||||
};
|
||||
|
||||
typedef struct inst_str
|
||||
{
|
||||
unsigned n;
|
||||
real_t vgain;
|
||||
real_t gain;
|
||||
} inst_t;
|
||||
|
||||
rc_t create( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const abuf_t* abuf = nullptr; //
|
||||
const abuf_t* abuf = nullptr; //
|
||||
ctx->userPtr = mem::allocZ<inst_t>();
|
||||
|
||||
// get the source audio buffer
|
||||
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC )
|
||||
@ -600,16 +625,32 @@ namespace cw
|
||||
}
|
||||
|
||||
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; }
|
||||
{
|
||||
real_t value = 0;
|
||||
inst_t* inst = (inst_t*)ctx->userPtr;
|
||||
var_get(ctx,kGainPId,0,value);
|
||||
|
||||
if( inst->vgain != value )
|
||||
{
|
||||
inst->vgain = value;
|
||||
//printf("VALUE GAIN: %s %s : %f\n", ctx->label, var->label, value );
|
||||
}
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t exec( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const abuf_t* ibuf = nullptr;
|
||||
abuf_t* obuf = nullptr;
|
||||
inst_t* inst = (inst_t*)(ctx->userPtr);
|
||||
|
||||
// get the src buffer
|
||||
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
|
||||
@ -631,6 +672,13 @@ namespace cw
|
||||
// apply the gain
|
||||
for(unsigned j=0; j<ibuf->frameN; ++j)
|
||||
osig[j] = gain * isig[j];
|
||||
|
||||
if( i==0 && gain != inst->gain )
|
||||
{
|
||||
inst->gain = gain;
|
||||
//printf("EXEC GAIN: %s %f\n",ctx->label,gain);
|
||||
//instance_print(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
@ -684,13 +732,19 @@ namespace cw
|
||||
|
||||
if( abuf->chN )
|
||||
{
|
||||
unsigned selChN = 0;
|
||||
|
||||
inst->chSelMap = mem::allocZ<bool>(abuf->chN);
|
||||
|
||||
|
||||
if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// register the gain
|
||||
for(unsigned i=0; i<abuf->chN; ++i)
|
||||
{
|
||||
if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC )
|
||||
goto errLabel;
|
||||
if( i < selChN )
|
||||
if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
if( inst->chSelMap[i] )
|
||||
{
|
||||
@ -1659,17 +1713,23 @@ namespace cw
|
||||
|
||||
if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN )
|
||||
{
|
||||
double val = 0;
|
||||
spec_dist_t* sd = inst->sdA[ var->chIdx ];
|
||||
|
||||
switch( var->vid )
|
||||
{
|
||||
case kCeilingPId: var_get( var, inst->sdA[ var->chIdx ]->ceiling ); break;
|
||||
case kExpoPId: var_get( var, inst->sdA[ var->chIdx ]->expo ); break;
|
||||
case kThreshPId: var_get( var, inst->sdA[ var->chIdx ]->thresh ); break;
|
||||
case kUprSlopePId: var_get( var, inst->sdA[ var->chIdx ]->uprSlope ); break;
|
||||
case kLwrSlopePId: var_get( var, inst->sdA[ var->chIdx ]->lwrSlope ); break;
|
||||
case kMixPId: var_get( var, inst->sdA[ var->chIdx ]->mix ); break;
|
||||
case kCeilingPId: rc = var_get( var, val ); sd->ceiling = val; break;
|
||||
case kExpoPId: rc = var_get( var, val ); sd->expo = val; break;
|
||||
case kThreshPId: rc = var_get( var, val ); sd->thresh = val; break;
|
||||
case kUprSlopePId: rc = var_get( var, val ); sd->uprSlope = val; break;
|
||||
case kLwrSlopePId: rc = var_get( var, val ); sd->lwrSlope = val; break;
|
||||
case kMixPId: rc = var_get( var, val ); sd->mix = val; break;
|
||||
default:
|
||||
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
|
||||
}
|
||||
|
||||
//printf("sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f\n",
|
||||
// sd->ceiling, sd->expo, sd->thresh, sd->uprSlope, sd->lwrSlope, sd->mix, rc, val );
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -1833,20 +1893,24 @@ namespace cw
|
||||
|
||||
if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN )
|
||||
{
|
||||
compressor_t* c = inst->cmpA[ var->chIdx ];
|
||||
|
||||
switch( var->vid )
|
||||
{
|
||||
case kBypassPId: var_get( var, inst->cmpA[ var->chIdx ]->bypassFl ); break;
|
||||
case kInGainPId: var_get( var, inst->cmpA[ var->chIdx ]->inGain ); break;
|
||||
case kOutGainPId: var_get( var, inst->cmpA[ var->chIdx ]->outGain ); break;
|
||||
case kRatioPId: var_get( var, inst->cmpA[ var->chIdx ]->ratio_num ); break;
|
||||
case kThreshPId: var_get( var, inst->cmpA[ var->chIdx ]->threshDb ); break;
|
||||
case kAtkMsPId: var_get( var, tmp ); set_attack_ms(inst->cmpA[ var->chIdx ], tmp ); break;
|
||||
case kRlsMsPId: var_get( var, tmp ); set_release_ms(inst->cmpA[ var->chIdx ], tmp ); break;
|
||||
case kWndMsPId: var_get( var, tmp ); set_rms_wnd_ms(inst->cmpA[ var->chIdx ], tmp ); break;
|
||||
case kBypassPId: rc = var_get( var, tmp ); c->bypassFl=tmp; break;
|
||||
case kInGainPId: rc = var_get( var, tmp ); c->inGain=tmp; break;
|
||||
case kOutGainPId: rc = var_get( var, tmp ); c->outGain=tmp; break;
|
||||
case kRatioPId: rc = var_get( var, tmp ); c->ratio_num=tmp; break;
|
||||
case kThreshPId: rc = var_get( var, tmp ); c->threshDb=tmp; break;
|
||||
case kAtkMsPId: rc = var_get( var, tmp ); dsp::compressor::set_attack_ms(c, tmp ); break;
|
||||
case kRlsMsPId: rc = var_get( var, tmp ); dsp::compressor::set_release_ms(c, tmp ); break;
|
||||
case kWndMsPId: rc = var_get( var, tmp ); dsp::compressor::set_rms_wnd_ms(c, tmp ); break;
|
||||
case kMaxWndMsPId: break;
|
||||
default:
|
||||
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
|
||||
}
|
||||
//printf("cmp byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n",
|
||||
// c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp);
|
||||
}
|
||||
|
||||
|
||||
@ -1957,6 +2021,12 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if( delayMs > maxDelayMs )
|
||||
{
|
||||
cwLogWarning("'delayMs' (%i) is being reduced to 'maxDelayMs' (%i) on the delay instance:%s.",delayMs,maxDelayMs,ctx->label);
|
||||
delayMs = maxDelayMs;
|
||||
}
|
||||
|
||||
inst->maxDelayFrameN = std::max(inst->maxDelayFrameN, (unsigned)(fabs(maxDelayMs) * abuf->srate / 1000.0) );
|
||||
|
||||
inst->cntV[i] = (unsigned)(fabs(delayMs) * abuf->srate / 1000.0);
|
||||
@ -1991,9 +2061,10 @@ namespace cw
|
||||
rc_t exec( instance_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
inst_t* inst = (inst_t*)ctx->userPtr;
|
||||
const abuf_t* ibuf = nullptr;
|
||||
abuf_t* obuf = nullptr;
|
||||
inst_t* inst = (inst_t*)ctx->userPtr;
|
||||
abuf_t* dbuf = inst->delayBuf;
|
||||
|
||||
// get the src buffer
|
||||
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
|
||||
@ -2008,6 +2079,7 @@ namespace cw
|
||||
{
|
||||
sample_t* isig = ibuf->buf + i*ibuf->frameN;
|
||||
sample_t* osig = obuf->buf + i*obuf->frameN;
|
||||
sample_t* dsig = dbuf->buf + i*dbuf->frameN;
|
||||
unsigned di = inst->idxV[i];
|
||||
|
||||
// if the delay is set to zero samples
|
||||
@ -2018,8 +2090,8 @@ namespace cw
|
||||
// otherwise the delay is non-zero positive sample count
|
||||
for(unsigned j=0; j<ibuf->frameN; ++j)
|
||||
{
|
||||
osig[j] = inst->delayBuf->buf[di]; // read delay output
|
||||
inst->delayBuf->buf[di] = isig[j]; // set delay input
|
||||
osig[j] = dsig[di]; // read delay output
|
||||
dsig[di] = isig[j]; // set delay input
|
||||
di = (di+1) % inst->cntV[i]; // update the delay index
|
||||
}
|
||||
}
|
||||
|
358
cwFlowTypes.cpp
358
cwFlowTypes.cpp
@ -339,7 +339,19 @@ namespace cw
|
||||
valRef = non_const_val;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template< typename T >
|
||||
rc_t _val_get_driver( const variable_t* var, T& valRef )
|
||||
{
|
||||
if( var == nullptr )
|
||||
return cwLogError(kInvalidArgRC,"Cannnot get the value of a non-existent variable.");
|
||||
|
||||
if( var->value == nullptr )
|
||||
return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s.%s ch:%i.",cwStringNullGuard(var->inst->label),cwStringNullGuard(var->label),var->chIdx);
|
||||
|
||||
return _val_get(var->value,valRef);
|
||||
}
|
||||
|
||||
rc_t _var_find_to_set( instance_t* inst, unsigned vid, unsigned chIdx, unsigned typeFl, variable_t*& varRef )
|
||||
{
|
||||
@ -390,39 +402,6 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
// Find a variable: If an exact match on label,chIdx is found this is returned.
|
||||
// Otherwise if the arg 'chIdx' is a numeric channel idx the 'any' var is returned.
|
||||
// if the arg 'chIdx' is 'any' then the 'any' or the lowest numeric channel is returned
|
||||
variable_t* _var_find_best( instance_t* inst, const char* label, unsigned chIdx )
|
||||
{
|
||||
/*
|
||||
variable_t* v0 = nullptr;
|
||||
variable_t* var = inst->varL;
|
||||
|
||||
for(; var!=nullptr; var=var->var_link)
|
||||
if( textCompare(var->label,label) == 0 )
|
||||
{
|
||||
// if the channel matches exactly - return this var
|
||||
if( var->chIdx == chIdx )
|
||||
return var;
|
||||
|
||||
// if we encounter a var with 'any' channel - store this we will keep looking because we may still find an exact match
|
||||
if( var->chIdx == kAnyChIdx )
|
||||
v0 = var;
|
||||
else
|
||||
// if 'any' was requested then try to get ch==0 but a non-zero channel will suffice otherwise
|
||||
if( chIdx == kAnyChIdx )
|
||||
if( v0 == nullptr || var->chIdx < v0->chIdx )
|
||||
v0 = var;
|
||||
}
|
||||
|
||||
return v0;
|
||||
*/
|
||||
|
||||
return _var_find_on_label_and_ch(inst,label,chIdx);
|
||||
|
||||
}
|
||||
|
||||
rc_t _validate_var_assignment( variable_t* var, unsigned typeFl )
|
||||
{
|
||||
if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) )
|
||||
@ -447,73 +426,73 @@ namespace cw
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void _var_setter( variable_t* var, T val )
|
||||
void _var_setter( variable_t* var, unsigned local_value_idx, T val )
|
||||
{
|
||||
cwLogError(kAssertFailRC,"Unimplemented variable setter.");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<bool>( variable_t* var, bool val )
|
||||
void _var_setter<bool>( variable_t* var, unsigned local_value_idx, bool val )
|
||||
{
|
||||
var->local_value.u.b = val;
|
||||
var->local_value.flags = kBoolTFl;
|
||||
var->local_value[ local_value_idx ].u.b = val;
|
||||
var->local_value[ local_value_idx ].flags = kBoolTFl;
|
||||
cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<unsigned>( variable_t* var, unsigned val )
|
||||
void _var_setter<unsigned>( variable_t* var, unsigned local_value_idx, unsigned val )
|
||||
{
|
||||
var->local_value.u.u = val;
|
||||
var->local_value.flags = kUIntTFl;
|
||||
var->local_value[ local_value_idx ].u.u = val;
|
||||
var->local_value[ local_value_idx ].flags = kUIntTFl;
|
||||
cwLogMod("%s.%s ch:%i %i (uint).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<int>( variable_t* var, int val )
|
||||
void _var_setter<int>( variable_t* var, unsigned local_value_idx, int val )
|
||||
{
|
||||
var->local_value.u.i = val;
|
||||
var->local_value.flags = kIntTFl;
|
||||
var->local_value[ local_value_idx ].u.i = val;
|
||||
var->local_value[ local_value_idx ].flags = kIntTFl;
|
||||
cwLogMod("%s.%s ch:%i %i (int).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<float>( variable_t* var, float val )
|
||||
void _var_setter<float>( variable_t* var, unsigned local_value_idx, float val )
|
||||
{
|
||||
var->local_value.u.f = val;
|
||||
var->local_value.flags = kFloatTFl;
|
||||
var->local_value[ local_value_idx ].u.f = val;
|
||||
var->local_value[ local_value_idx ].flags = kFloatTFl;
|
||||
cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<double>( variable_t* var, double val )
|
||||
void _var_setter<double>( variable_t* var, unsigned local_value_idx, double val )
|
||||
{
|
||||
var->local_value.u.d = val;
|
||||
var->local_value.flags = kDoubleTFl;
|
||||
var->local_value[ local_value_idx ].u.d = val;
|
||||
var->local_value[ local_value_idx ].flags = kDoubleTFl;
|
||||
cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<const char*>( variable_t* var, const char* val )
|
||||
void _var_setter<const char*>( variable_t* var, unsigned local_value_idx, const char* val )
|
||||
{
|
||||
var->local_value.u.s = mem::duplStr(val);
|
||||
var->local_value.flags = kStringTFl;
|
||||
var->local_value[ local_value_idx ].u.s = mem::duplStr(val);
|
||||
var->local_value[ local_value_idx ].flags = kStringTFl;
|
||||
cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,val);
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<abuf_t*>( variable_t* var, abuf_t* val )
|
||||
void _var_setter<abuf_t*>( variable_t* var, unsigned local_value_idx, abuf_t* val )
|
||||
{
|
||||
var->local_value.u.abuf = val;
|
||||
var->local_value.flags = kABufTFl;
|
||||
var->local_value[ local_value_idx ].u.abuf = val;
|
||||
var->local_value[ local_value_idx ].flags = kABufTFl;
|
||||
cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid");
|
||||
}
|
||||
|
||||
template<>
|
||||
void _var_setter<fbuf_t*>( variable_t* var, fbuf_t* val )
|
||||
void _var_setter<fbuf_t*>( variable_t* var, unsigned local_value_idx, fbuf_t* val )
|
||||
{
|
||||
var->local_value.u.fbuf = val;
|
||||
var->local_value.flags = kFBufTFl;
|
||||
var->local_value[ local_value_idx ].u.fbuf = val;
|
||||
var->local_value[ local_value_idx ].flags = kFBufTFl;
|
||||
cwLogMod("%s.%s ch:%i %s (fbuf).",var->inst->label,var->label,var->chIdx,fbuf==nullptr ? "null" : "valid");
|
||||
}
|
||||
|
||||
@ -521,183 +500,69 @@ namespace cw
|
||||
rc_t _var_set_template( variable_t* var, unsigned typeFlag, T val )
|
||||
{
|
||||
rc_t rc;
|
||||
|
||||
unsigned next_local_value_idx = (var->local_value_idx + 1) % kLocalValueN;
|
||||
|
||||
// store the pointer to the current value of this variable
|
||||
value_t* original_value = var->value;
|
||||
unsigned original_value_idx = var->local_value_idx;
|
||||
|
||||
// verify that this is a legal assignment
|
||||
if((rc = _validate_var_assignment( var, typeFlag )) != kOkRC )
|
||||
return rc;
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// release the previous value in the next slot
|
||||
_value_release(&var->local_value[next_local_value_idx]);
|
||||
|
||||
// set the new local value
|
||||
_var_setter(var,next_local_value_idx,val);
|
||||
|
||||
// make the new local value current
|
||||
var->value = var->local_value + next_local_value_idx;
|
||||
var->local_value_idx = next_local_value_idx;
|
||||
|
||||
// If the instance is fully initialized ...
|
||||
if( var->inst->varMapA != nullptr )
|
||||
{
|
||||
// ... then inform the proc. that the value changed
|
||||
// (we don't want to do call to occur if we are inside or prior to 'proc.create()'
|
||||
// Note 1: We don't want to this call to occur if we are inside or prior to 'proc.create()'
|
||||
// call because calls' to 'proc.value()' will see the instance in a incomplete state)
|
||||
rc = var->inst->class_desc->members->value( var->inst, var );
|
||||
// Note 2: If this call returns an error then the value assignment is cancelled
|
||||
// and the value does not change.
|
||||
rc = var->inst->class_desc->members->value( var->inst, var );
|
||||
}
|
||||
|
||||
if( rc == kOkRC )
|
||||
{
|
||||
// release the current value
|
||||
_value_release(&var->local_value);
|
||||
|
||||
// set the new local value
|
||||
_var_setter(var,val);
|
||||
|
||||
// make the var point to the local value
|
||||
var->value = &var->local_value;
|
||||
|
||||
|
||||
// send the value to connected downstream proc's
|
||||
rc = _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _var_set( variable_t* var, bool val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kBoolTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
if((rc = var->inst->class_desc->members->value( var->inst, var )) == kOkRC )
|
||||
else
|
||||
{
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.b = val;
|
||||
var->local_value.flags = kBoolTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val);
|
||||
|
||||
rc = _var_broadcast_new_value( var );
|
||||
// cancel the assignment and restore the original value
|
||||
var->value = original_value;
|
||||
var->local_value_idx = original_value_idx;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, unsigned val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kUIntTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
if((rc = var->inst->class_desc->members->value( var->inst, var )) == kOkRC )
|
||||
{
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.u = val;
|
||||
var->local_value.flags = kUIntTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %i (uint_t).",var->inst->label,var->label,var->chIdx,val);
|
||||
|
||||
_var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, int val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kIntTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.i = val;
|
||||
var->local_value.flags = kIntTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %i (int_t).",var->inst->label,var->label,var->chIdx,val);
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, float val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kFloatTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.f = val;
|
||||
var->local_value.flags = kFloatTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val);
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, double val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kDoubleTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.d = val;
|
||||
var->local_value.flags = kDoubleTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val);
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, const char* val )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kStringTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.s = mem::duplStr(val);
|
||||
var->local_value.flags = kStringTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,cwStringNullGuard(val));
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, abuf_t* abuf )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kABufTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.abuf = abuf;
|
||||
var->local_value.flags = kABufTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid");
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
|
||||
}
|
||||
|
||||
rc_t _var_set( variable_t* var, fbuf_t* fbuf )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = _validate_var_assignment( var, kFBufTFl )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
_value_release(&var->local_value);
|
||||
var->local_value.u.fbuf = fbuf;
|
||||
var->local_value.flags = kFBufTFl;
|
||||
var->value = &var->local_value;
|
||||
|
||||
cwLogMod("%s.%s ch:%i %s (fbuf).",var->inst->label,var->label,var->chIdx,fbuf==nullptr ? "null" : "valid");
|
||||
|
||||
return _var_broadcast_new_value( var );
|
||||
}
|
||||
|
||||
|
||||
bool is_connected_to_external_proc( const variable_t* var )
|
||||
{
|
||||
return var->src_var != nullptr && var->value != nullptr && var->value != &var->local_value;
|
||||
// if this var does not have a 'src_ptr' then it can't be connected to an external proc
|
||||
if( var->src_var == nullptr || var->value == nullptr )
|
||||
return false;
|
||||
|
||||
// if this var is using a local value then it can't be connected to an external proc
|
||||
for(unsigned i=0; i<kLocalValueN; ++i)
|
||||
if( var->value == var->local_value + i )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
@ -950,13 +815,12 @@ namespace cw
|
||||
|
||||
void _var_print( const variable_t* var )
|
||||
{
|
||||
//const char* local_label = var->value==nullptr || var->value == &var->local_value ? "local" : " ";
|
||||
const char* conn_label = is_connected_to_external_proc(var) ? "extern" : " ";
|
||||
|
||||
printf(" %20s id:%4i ch:%3i : %s : ", var->label, var->vid, var->chIdx, conn_label );
|
||||
|
||||
if( var->value == nullptr )
|
||||
_value_print( &var->local_value );
|
||||
_value_print( &var->local_value[0] );
|
||||
else
|
||||
_value_print( var->value );
|
||||
|
||||
@ -999,7 +863,7 @@ cw::flow::abuf_t* cw::flow::abuf_create( srate_t srate, unsigned chN, unsigned f
|
||||
return a;
|
||||
}
|
||||
|
||||
void cw::flow::abuf_destroy( abuf_t* abuf )
|
||||
void cw::flow::abuf_destroy( abuf_t*& abuf )
|
||||
{
|
||||
if( abuf == nullptr )
|
||||
return;
|
||||
@ -1077,7 +941,7 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned
|
||||
return f;
|
||||
}
|
||||
|
||||
void cw::flow::fbuf_destroy( fbuf_t* fbuf )
|
||||
void cw::flow::fbuf_destroy( fbuf_t*& fbuf )
|
||||
{
|
||||
if( fbuf == nullptr )
|
||||
return;
|
||||
@ -1210,7 +1074,8 @@ void cw::flow::_var_destroy( variable_t* var )
|
||||
{
|
||||
if( var != nullptr )
|
||||
{
|
||||
_value_release(&var->local_value);
|
||||
for(unsigned i=0; i<kLocalValueN; ++i)
|
||||
_value_release(var->local_value+i);
|
||||
mem::release(var->label);
|
||||
mem::release(var);
|
||||
}
|
||||
@ -1262,11 +1127,11 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns
|
||||
if( value_cfg == nullptr )
|
||||
{
|
||||
// Set the value of the new variable to the value of the 'any' channel
|
||||
_value_duplicate( var->local_value, base_var->local_value );
|
||||
_value_duplicate( var->local_value[ var->local_value_idx], base_var->local_value[ base_var->local_value_idx ] );
|
||||
|
||||
// If the 'any' channel value was set to point to it's local value then do same with this value
|
||||
if( &base_var->local_value == base_var->value )
|
||||
var->value = &var->local_value;
|
||||
if( base_var->local_value + base_var->local_value_idx == base_var->value )
|
||||
var->value = var->local_value + var->local_value_idx;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1340,7 +1205,7 @@ cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx
|
||||
variable_t* var;
|
||||
vRef = nullptr;
|
||||
|
||||
if((var = _var_find_best(inst,label,chIdx)) != nullptr )
|
||||
if((var = _var_find_on_label_and_ch(inst,label,chIdx)) != nullptr )
|
||||
{
|
||||
vRef = var;
|
||||
return kOkRC;
|
||||
@ -1357,6 +1222,37 @@ cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::flow::var_channel_count( instance_t* inst, const char* label, unsigned& chCntRef )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const variable_t* var= nullptr;
|
||||
if((rc = var_find(inst,label,kAnyChIdx,var)) != kOkRC )
|
||||
return cwLogError(rc,"Channel count was not available because the variable '%s.%s' does not exist.",cwStringNullGuard(inst->label),cwStringNullGuard(label));
|
||||
|
||||
return var_channel_count(var,chCntRef);
|
||||
}
|
||||
|
||||
cw::rc_t cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const variable_t* v;
|
||||
|
||||
chCntRef = 0;
|
||||
|
||||
if((rc = var_find( var->inst, var->label, kAnyChIdx, v )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kInvalidStateRC,"The base channel variable instance could not be found for the variable '%s.%s'.",var->inst->label,var->label);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
for(v = v->ch_link; v!=nullptr; v=v->ch_link)
|
||||
chCntRef += 1;
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef )
|
||||
{
|
||||
@ -1432,34 +1328,34 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label
|
||||
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, bool& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, uint_t& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, int_t& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, float& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, double& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, const char*& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, const abuf_t*& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( variable_t* var, abuf_t*& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( const variable_t* var, const fbuf_t*& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_get( variable_t* var, fbuf_t*& valRef )
|
||||
{ return _val_get(var->value,valRef); }
|
||||
{ return _val_get_driver(var,valRef); }
|
||||
|
||||
cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val )
|
||||
{
|
||||
|
@ -25,7 +25,8 @@ namespace cw
|
||||
|
||||
enum {
|
||||
kFbufVectN = 3,
|
||||
kAnyChIdx = kInvalidIdx
|
||||
kAnyChIdx = kInvalidIdx,
|
||||
kLocalValueN = 2
|
||||
};
|
||||
|
||||
typedef struct fbuf_str
|
||||
@ -162,11 +163,12 @@ namespace cw
|
||||
char* label; // this variables label
|
||||
unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst'
|
||||
var_desc_t* varDesc; // the variable description for this variable
|
||||
value_t local_value; // the local value instance (actual value if this is not a 'src' variable)
|
||||
value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable)
|
||||
unsigned local_value_idx;
|
||||
value_t* value; // pointer to the value associated with this variable
|
||||
unsigned chIdx; // channel index
|
||||
struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value)
|
||||
struct variable_str* var_link; // link to other var's on 'inst'
|
||||
struct variable_str* var_link; // instance.varL link list
|
||||
struct variable_str* connect_link; // list of outgoing connections
|
||||
struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number)
|
||||
} variable_t;
|
||||
@ -186,7 +188,7 @@ namespace cw
|
||||
|
||||
void* userPtr; // instance state
|
||||
|
||||
variable_t* varL; // list of instance value
|
||||
variable_t* varL; // list of all variables on this instance
|
||||
|
||||
unsigned varMapChN; // max count of channels among all variables
|
||||
unsigned varMapIdN;
|
||||
@ -224,13 +226,13 @@ namespace cw
|
||||
//
|
||||
|
||||
abuf_t* abuf_create( srate_t srate, unsigned chN, unsigned frameN );
|
||||
void abuf_destroy( abuf_t* buf );
|
||||
void abuf_destroy( abuf_t*& buf );
|
||||
abuf_t* abuf_duplicate( const abuf_t* src );
|
||||
rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN );
|
||||
const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx );
|
||||
|
||||
fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned binN, unsigned hopSmpN, const sample_t** magV=nullptr, const sample_t** phsV=nullptr, const sample_t** hzV=nullptr );
|
||||
void fbuf_destroy( fbuf_t* buf );
|
||||
void fbuf_destroy( fbuf_t*& buf );
|
||||
fbuf_t* fbuf_duplicate( const fbuf_t* src );
|
||||
|
||||
inline bool value_is_abuf( const value_t* v ) { return v->flags & kABufTFl; }
|
||||
@ -395,10 +397,15 @@ namespace cw
|
||||
void _var_destroy( variable_t* var );
|
||||
|
||||
bool var_exists( 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 );
|
||||
rc_t var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef );
|
||||
|
||||
// Count of numbered channels - does not count the kAnyChIdx variable instance.
|
||||
rc_t var_channel_count( instance_t* inst, const char* label, unsigned& chCntRef );
|
||||
rc_t var_channel_count( const variable_t* var, unsigned& chCntRef );
|
||||
|
||||
|
||||
rc_t var_get( const variable_t* var, bool& valRef );
|
||||
rc_t var_get( const variable_t* var, uint_t& valRef );
|
||||
|
10
cwIo.cpp
10
cwIo.cpp
@ -1553,7 +1553,7 @@ namespace cw
|
||||
// UI
|
||||
//
|
||||
|
||||
// This function is called by the websocket with messages comring from a remote UI.
|
||||
// This function is called by the websocket with messages coming from a remote UI.
|
||||
rc_t _uiCallback( void* cbArg, unsigned wsSessId, ui::opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, unsigned chanId, const ui::value_t* v )
|
||||
{
|
||||
io_t* p = (io_t*)cbArg;
|
||||
@ -1569,7 +1569,7 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* uiCfgLabel = "ui";
|
||||
ui::ws::args_t args;
|
||||
ui::ws::args_t args = {};
|
||||
|
||||
|
||||
// Duplicate the application id map
|
||||
@ -3134,3 +3134,9 @@ cw::rc_t cw::io::uiSendValue( handle_t h, unsigned uuId, const char* value )
|
||||
return rc;
|
||||
}
|
||||
|
||||
void cw::io::uiReport( handle_t h )
|
||||
{
|
||||
ui::handle_t uiH;
|
||||
if(_handleToUiHandle(h,uiH) == kOkRC )
|
||||
ui::report(uiH);
|
||||
}
|
||||
|
2
cwIo.h
2
cwIo.h
@ -364,7 +364,7 @@ namespace cw
|
||||
rc_t uiSendValue( handle_t h, unsigned uuId, double value );
|
||||
rc_t uiSendValue( handle_t h, unsigned uuId, const char* value );
|
||||
|
||||
|
||||
void uiReport( handle_t h );
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ namespace cw
|
||||
rc_t _audio_callback( io_flow_t* p, io::audio_msg_t& m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
flow::abuf_t* abuf;
|
||||
flow::abuf_t* abuf = nullptr;
|
||||
|
||||
// if there is incoming (recorded) audio
|
||||
if( m.iBufChCnt > 0 )
|
||||
@ -436,3 +436,9 @@ cw::rc_t cw::io_flow::set_variable_value( handle_t h, flow_cross::destId_t destI
|
||||
|
||||
cw::rc_t cw::io_flow::begin_cross_fade( handle_t h, unsigned crossFadeMs )
|
||||
{ return flow_cross::begin_cross_fade( _handleToPtr(h)->crossFlowH, crossFadeMs ); }
|
||||
|
||||
void cw::io_flow::print( handle_t h )
|
||||
{ return flow_cross::print( _handleToPtr(h)->crossFlowH ); }
|
||||
|
||||
void cw::io_flow::print_network( handle_t h, flow_cross::destId_t destId )
|
||||
{ return flow_cross::print_network( _handleToPtr(h)->crossFlowH, destId ); }
|
||||
|
@ -30,6 +30,8 @@ namespace cw
|
||||
rc_t begin_cross_fade( handle_t h, unsigned crossFadeMs );
|
||||
|
||||
|
||||
void print( handle_t h );
|
||||
void print_network( handle_t h, flow_cross::destId_t destId );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,43 +27,94 @@ namespace cw
|
||||
|
||||
unsigned id;
|
||||
time::spec_t timestamp;
|
||||
unsigned loc;
|
||||
uint8_t ch;
|
||||
uint8_t status;
|
||||
uint8_t d0;
|
||||
uint8_t d1;
|
||||
|
||||
} am_midi_msg_t;
|
||||
|
||||
typedef struct midi_record_play_str
|
||||
{
|
||||
io::handle_t ioH;
|
||||
|
||||
am_midi_msg_t* msgArray;
|
||||
unsigned msgArrayN;
|
||||
unsigned msgArrayInIdx;
|
||||
unsigned msgArrayOutIdx;
|
||||
unsigned midi_timer_period_micro_sec;
|
||||
|
||||
typedef struct midi_device_str
|
||||
{
|
||||
char* midiOutDevLabel;
|
||||
char* midiOutPortLabel;
|
||||
unsigned midiOutDevIdx;
|
||||
unsigned midiOutPortIdx;
|
||||
bool enableFl;
|
||||
unsigned velTableN;
|
||||
uint8_t* velTableArray;
|
||||
bool pedalMapEnableFl;
|
||||
|
||||
unsigned pedalDownVelId;
|
||||
unsigned pedalDownVel;
|
||||
|
||||
unsigned pedalDownHalfVelId;
|
||||
unsigned pedalDownHalfVel;
|
||||
|
||||
unsigned pedalUpHalfVelId;
|
||||
unsigned pedalUpHalfVel;
|
||||
|
||||
} midi_device_t;
|
||||
|
||||
enum
|
||||
{
|
||||
kHalfPedalDone,
|
||||
kWaitForBegin,
|
||||
kWaitForNoteOn,
|
||||
kWaitForNoteOff,
|
||||
kWaitForPedalUp,
|
||||
kWaitForPedalDown,
|
||||
};
|
||||
|
||||
|
||||
typedef struct midi_record_play_str
|
||||
{
|
||||
io::handle_t ioH;
|
||||
|
||||
am_midi_msg_t* msgArray; // msgArray[ msgArrayN ]
|
||||
unsigned msgArrayN; // Count of messages allocated in msgArray.
|
||||
unsigned msgArrayInIdx; // Next available space for loaded MIDI messages (also the current count of msgs in msgArray[])
|
||||
unsigned msgArrayOutIdx; // Next message to transmit in msgArray[]
|
||||
unsigned midi_timer_period_micro_sec; // Timer period in microseconds
|
||||
unsigned all_off_delay_ms; // Wait this long before turning all notes off after the last note-on has played
|
||||
|
||||
am_midi_msg_t* iMsgArray; // msgArray[ msgArrayN ]
|
||||
unsigned iMsgArrayN; // Count of messages allocated in msgArray.
|
||||
unsigned iMsgArrayInIdx; // Next available space for incoming MIDI messages (also the current count of msgs in msgArray[])
|
||||
|
||||
|
||||
midi_device_t* midiDevA;
|
||||
unsigned midiDevN;
|
||||
|
||||
bool startedFl;
|
||||
bool recordFl;
|
||||
bool thruFl;
|
||||
bool logInFl; // log incoming message when not in 'record' mode.
|
||||
bool logOutFl; // log outgoing messages
|
||||
|
||||
bool halfPedalFl;
|
||||
unsigned halfPedalState;
|
||||
unsigned halfPedalNextUs;
|
||||
unsigned halfPedalNoteDelayUs;
|
||||
unsigned halfPedalNoteDurUs;
|
||||
unsigned halfPedalUpDelayUs;
|
||||
unsigned halfPedalDownDelayUs;
|
||||
uint8_t halfPedalMidiPitch;
|
||||
uint8_t halfPedalMidiNoteVel;
|
||||
uint8_t halfPedalMidiPedalVel;
|
||||
|
||||
time::spec_t play_time;
|
||||
time::spec_t start_time;
|
||||
time::spec_t end_play_event_timestamp;
|
||||
|
||||
bool pedalFl;
|
||||
time::spec_t all_off_timestamp;
|
||||
time::spec_t store_time;
|
||||
|
||||
event_callback_t cb;
|
||||
void* cb_arg;
|
||||
|
||||
} midi_record_play_t;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
kMidiRecordPlayTimerId
|
||||
@ -79,10 +130,17 @@ namespace cw
|
||||
|
||||
if((timerIdx = io::timerLabelToIndex( p->ioH, TIMER_LABEL )) != kInvalidIdx )
|
||||
io::timerDestroy( p->ioH, timerIdx);
|
||||
|
||||
|
||||
for(unsigned i=0; i<p->midiDevN; ++i)
|
||||
{
|
||||
mem::release(p->midiDevA[i].midiOutDevLabel);
|
||||
mem::release(p->midiDevA[i].midiOutPortLabel);
|
||||
mem::release(p->midiDevA[i].velTableArray);
|
||||
}
|
||||
|
||||
mem::release(p->midiDevA);
|
||||
mem::release(p->msgArray);
|
||||
mem::release(p->midiOutDevLabel);
|
||||
mem::release(p->midiOutPortLabel);
|
||||
mem::release(p->iMsgArray);
|
||||
mem::release(p);
|
||||
|
||||
return rc;
|
||||
@ -91,74 +149,293 @@ namespace cw
|
||||
rc_t _parseCfg(midi_record_play_t* p, const object_t& cfg )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
const object_t* midiDevL = nullptr;
|
||||
if((rc = cfg.getv(
|
||||
"max_midi_msg_count", p->msgArrayN,
|
||||
"midi_timer_period_micro_sec", p->midi_timer_period_micro_sec,
|
||||
"midi_out_device", p->midiOutDevLabel,
|
||||
"midi_out_port", p->midiOutPortLabel)) != kOkRC )
|
||||
"all_off_delay_ms", p->all_off_delay_ms,
|
||||
"midi_device_list", midiDevL,
|
||||
"log_in_flag", p->logInFl,
|
||||
"log_out_flag", p->logOutFl,
|
||||
"half_pedal_flag", p->halfPedalFl)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"MIDI record play configuration parse failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->iMsgArrayN = p->msgArrayN;
|
||||
|
||||
if( midiDevL->child_count() > 0 )
|
||||
{
|
||||
p->midiDevN = midiDevL->child_count();
|
||||
p->midiDevA = mem::allocZ<midi_device_t>(p->midiDevN);
|
||||
|
||||
printf("Midi record play devices:%i\n",p->midiDevN);
|
||||
|
||||
for(unsigned i=0; i<p->midiDevN; ++i)
|
||||
{
|
||||
const object_t* ele = midiDevL->child_ele(i);
|
||||
const char* midiOutDevLabel = nullptr;
|
||||
const char* midiOutPortLabel = nullptr;
|
||||
const object_t* velTable = nullptr;
|
||||
const object_t* pedalRecd = nullptr;
|
||||
bool enableFl = false;
|
||||
|
||||
if((rc = ele->getv( "midi_out_device", midiOutDevLabel,
|
||||
"midi_out_port", midiOutPortLabel,
|
||||
"enableFl", enableFl)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"MIDI record play device list configuration parse failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
if((rc = ele->getv_opt( "vel_table", velTable,
|
||||
"pedal", pedalRecd)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"MIDI record play device optional argument parsing failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->midiDevA[i].midiOutDevLabel = mem::duplStr( midiOutDevLabel);
|
||||
p->midiDevA[i].midiOutPortLabel = mem::duplStr( midiOutPortLabel);
|
||||
p->midiDevA[i].enableFl = enableFl;
|
||||
|
||||
if( velTable != nullptr )
|
||||
{
|
||||
p->midiDevA[i].velTableN = velTable->child_count();
|
||||
p->midiDevA[i].velTableArray = mem::allocZ<uint8_t>(p->midiDevA[i].velTableN);
|
||||
|
||||
|
||||
for(unsigned j=0; j<p->midiDevA[i].velTableN; ++j)
|
||||
{
|
||||
if((rc = velTable->child_ele(j)->value( p->midiDevA[i].velTableArray[j] )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"An error occured while parsing the velocity table for MIDI device:'%s' port:'%s'.",midiOutDevLabel,midiOutPortLabel);
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pedalRecd != nullptr )
|
||||
{
|
||||
if((rc = pedalRecd->getv( "down_id", p->midiDevA[i].pedalDownVelId,
|
||||
"down_vel", p->midiDevA[i].pedalDownVel,
|
||||
"half_down_id", p->midiDevA[i].pedalDownHalfVelId,
|
||||
"half_down_vel", p->midiDevA[i].pedalDownHalfVel,
|
||||
"half_up_id", p->midiDevA[i].pedalUpHalfVelId,
|
||||
"half_up_vel", p->midiDevA[i].pedalUpHalfVel
|
||||
)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"An error occured while parsing the pedal record for MIDI device:'%s' port:'%s'.",midiOutDevLabel,midiOutPortLabel);
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->midiDevA[i].pedalMapEnableFl = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// allocate the MIDI msg buffer
|
||||
p->msgArray = mem::allocZ<am_midi_msg_t>( p->msgArrayN );
|
||||
p->midiOutDevLabel = mem::duplStr( p->midiOutDevLabel);
|
||||
p->midiOutPortLabel = mem::duplStr( p->midiOutPortLabel);
|
||||
|
||||
p->iMsgArray = mem::allocZ<am_midi_msg_t>( p->iMsgArrayN );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _stop( midi_record_play_t* p );
|
||||
|
||||
|
||||
rc_t _event_callback( midi_record_play_t* p, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
const am_midi_msg_t* _midi_store( midi_record_play_t* p, unsigned devIdx, unsigned portIdx, const time::spec_t& ts, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
{
|
||||
am_midi_msg_t* am = nullptr;
|
||||
|
||||
// verify that space exists in the record buffer
|
||||
if( p->iMsgArrayInIdx < p->iMsgArrayN )
|
||||
{
|
||||
// MAKE THIS ATOMIC
|
||||
unsigned id = p->iMsgArrayInIdx;
|
||||
++p->iMsgArrayInIdx;
|
||||
|
||||
am = p->iMsgArray + id;
|
||||
|
||||
am->id = id;
|
||||
am->devIdx = devIdx;
|
||||
am->portIdx = portIdx;
|
||||
am->timestamp = ts;
|
||||
am->ch = ch;
|
||||
am->status = status;
|
||||
am->d0 = d0;
|
||||
am->d1 = d1;
|
||||
|
||||
}
|
||||
|
||||
return am;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if( !time::isZero(p->end_play_event_timestamp) && time::isGTE(timestamp,p->end_play_event_timestamp))
|
||||
if( after_all_off_fl )
|
||||
{
|
||||
rc = _stop(p);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, status + ch, d0, d1 );
|
||||
|
||||
if( p->cb )
|
||||
p->cb( p->cb_arg, id, timestamp, ch, status, d0, d1 );
|
||||
if( p->halfPedalFl )
|
||||
{
|
||||
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && d1 != 0 )
|
||||
d1 = p->halfPedalMidiPedalVel;
|
||||
}
|
||||
|
||||
|
||||
// for each midi device
|
||||
for(unsigned i=0; i<p->midiDevN; ++i)
|
||||
if(p->midiDevA[i].enableFl )
|
||||
{
|
||||
uint8_t out_d1 = d1;
|
||||
|
||||
if( !p->halfPedalFl )
|
||||
{
|
||||
// map the note on velocity
|
||||
if( is_note_on_fl and p->midiDevA[i].velTableArray != nullptr )
|
||||
{
|
||||
if( d1 >= p->midiDevA[i].velTableN )
|
||||
cwLogError(kInvalidIdRC,"A MIDI note-on velocity (%i) outside the velocity table range was encountered.",d1);
|
||||
else
|
||||
out_d1 = p->midiDevA[i].velTableArray[ d1 ];
|
||||
}
|
||||
|
||||
// map the pedal down velocity
|
||||
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl )
|
||||
{
|
||||
if( d1 == 0 )
|
||||
out_d1 = 0;
|
||||
else
|
||||
if( d1 == p->midiDevA[i].pedalDownVelId )
|
||||
out_d1 = p->midiDevA[i].pedalDownVel;
|
||||
else
|
||||
if( d1 == p->midiDevA[i].pedalDownHalfVelId )
|
||||
out_d1 = p->midiDevA[i].pedalDownHalfVel;
|
||||
else
|
||||
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping.",d1);
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
if( log_fl && p->logOutFl )
|
||||
{
|
||||
// Note: The device of outgoing messages is set to p->midiDevN + 1 to distinguish it from
|
||||
// incoming messages.
|
||||
_midi_store( p, p->midiDevN, 0, timestamp, ch, status, d0, d1 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am )
|
||||
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->ch, am->status, am->d0, am->d1 );
|
||||
return _event_callback( p, am->id, am->timestamp, am->loc, am->ch, am->status, am->d0, am->d1, log_fl );
|
||||
}
|
||||
|
||||
rc_t _transmit_ctl( midi_record_play_t* p, unsigned ch, unsigned ctlId, unsigned ctlVal )
|
||||
rc_t _transmit_note( midi_record_play_t* p, unsigned ch, unsigned pitch, unsigned vel, unsigned microsecs )
|
||||
{
|
||||
time::spec_t ts = {0};
|
||||
return _event_callback( p, kInvalidId, ts, ch, midi::kCtlMdId, ctlId, ctlVal );
|
||||
time::microsecondsToSpec( ts, microsecs );
|
||||
return _event_callback( p, kInvalidId, ts, kInvalidId, ch, midi::kNoteOnMdId, pitch, vel );
|
||||
}
|
||||
|
||||
rc_t _transmit_ctl( midi_record_play_t* p, unsigned ch, unsigned ctlId, unsigned ctlVal, unsigned microsecs )
|
||||
{
|
||||
time::spec_t ts = {0};
|
||||
time::microsecondsToSpec( ts, microsecs );
|
||||
return _event_callback( p, kInvalidId, ts, kInvalidId, ch, midi::kCtlMdId, ctlId, ctlVal );
|
||||
}
|
||||
|
||||
rc_t _transmit_pedal( midi_record_play_t* p, unsigned ch, unsigned pedalCtlId, bool pedalDownFl )
|
||||
rc_t _transmit_pedal( midi_record_play_t* p, unsigned ch, unsigned pedalCtlId, bool pedalDownFl, unsigned microsecs )
|
||||
{
|
||||
return _transmit_ctl( p, ch, pedalCtlId, pedalDownFl ? 127 : 0);
|
||||
return _transmit_ctl( p, ch, pedalCtlId, pedalDownFl ? 127 : 0, microsecs);
|
||||
}
|
||||
|
||||
void _half_pedal_update( midi_record_play_t* p, unsigned cur_time_us )
|
||||
{
|
||||
if( cur_time_us >= p->halfPedalNextUs )
|
||||
{
|
||||
unsigned midi_ch = 0;
|
||||
switch( p->halfPedalState )
|
||||
{
|
||||
|
||||
case kWaitForBegin:
|
||||
printf("down: %i %i\n",cur_time_us/1000,p->halfPedalMidiPedalVel);
|
||||
_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, p->halfPedalMidiPedalVel, cur_time_us);
|
||||
p->halfPedalState = kWaitForNoteOn;
|
||||
p->halfPedalNextUs += p->halfPedalNoteDelayUs;
|
||||
break;
|
||||
|
||||
case kWaitForNoteOn:
|
||||
printf("note: %i\n",cur_time_us/1000);
|
||||
_transmit_note( p, midi_ch, p->halfPedalMidiPitch, p->halfPedalMidiNoteVel, cur_time_us );
|
||||
p->halfPedalNextUs += p->halfPedalNoteDurUs;
|
||||
p->halfPedalState = kWaitForNoteOff;
|
||||
break;
|
||||
|
||||
case kWaitForNoteOff:
|
||||
printf("off: %i\n",cur_time_us/1000);
|
||||
_transmit_note( p, midi_ch, p->halfPedalMidiPitch, 0, cur_time_us );
|
||||
p->halfPedalNextUs += p->halfPedalUpDelayUs;
|
||||
p->halfPedalState = kWaitForPedalUp;
|
||||
break;
|
||||
|
||||
case kWaitForPedalUp:
|
||||
printf("up: %i\n",cur_time_us/1000);
|
||||
_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, 0, cur_time_us);
|
||||
p->halfPedalNextUs += p->halfPedalDownDelayUs;
|
||||
p->halfPedalState = kWaitForPedalDown;
|
||||
break;
|
||||
|
||||
case kWaitForPedalDown:
|
||||
//printf("down: %i\n",cur_time_us/1000);
|
||||
//_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, p->halfPedalMidiPedalVel, cur_time_us);
|
||||
//_stop(p);
|
||||
p->halfPedalState = kHalfPedalDone;
|
||||
break;
|
||||
|
||||
|
||||
case kHalfPedalDone:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the next location to store an incoming MIDI message
|
||||
void _set_midi_msg_next_index( midi_record_play_t* p, unsigned next_idx )
|
||||
{
|
||||
p->msgArrayInIdx = next_idx;
|
||||
|
||||
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->midiMsgArrayInIdx );
|
||||
|
||||
p->iMsgArrayInIdx = next_idx;
|
||||
}
|
||||
|
||||
// Set the next index of the next MIDI message to transmit
|
||||
void _set_midi_msg_next_play_index(midi_record_play_t* p, unsigned next_idx)
|
||||
{
|
||||
p->msgArrayOutIdx = next_idx;
|
||||
@ -214,7 +491,8 @@ namespace cw
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// Fill the play buffer from a previously store AM file.
|
||||
rc_t _midi_read( midi_record_play_t* p, const char* fn )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -245,21 +523,24 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
_set_midi_msg_next_index(p, n );
|
||||
p->msgArrayInIdx = n;
|
||||
|
||||
cwLogInfo("Read %i from '%s'.",n,fn);
|
||||
|
||||
errLabel:
|
||||
|
||||
file::close(fH);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// Write the record buffer to an AM file
|
||||
rc_t _midi_write( midi_record_play_t* p, const char* fn )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
file::handle_t fH;
|
||||
|
||||
if( p->msgArrayInIdx == 0 )
|
||||
if( p->iMsgArrayInIdx == 0 )
|
||||
{
|
||||
cwLogWarning("Nothing to write.");
|
||||
return rc;
|
||||
@ -273,30 +554,77 @@ namespace cw
|
||||
}
|
||||
|
||||
// write the file header
|
||||
if((rc = write(fH,p->msgArrayInIdx)) != kOkRC )
|
||||
if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kWriteFailRC,"Header write to '%s' failed.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// write the file data
|
||||
if((rc = write(fH,p->msgArray,sizeof(am_midi_msg_t)*p->msgArrayInIdx)) != kOkRC )
|
||||
if((rc = write(fH,p->iMsgArray,sizeof(am_midi_msg_t)*p->iMsgArrayInIdx)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kWriteFailRC,"Data write to '%s' failed.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// update UI msg count
|
||||
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
|
||||
|
||||
errLabel:
|
||||
file::close(fH);
|
||||
|
||||
cwLogInfo("Saved %i events to '%s'.", p->msgArrayInIdx, fn );
|
||||
cwLogInfo("Saved %i events to '%s'.", p->iMsgArrayInIdx, fn );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _write_csv( midi_record_play_t* p, const char* fn )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
file::handle_t fH;
|
||||
|
||||
if( p->iMsgArrayInIdx == 0 )
|
||||
{
|
||||
cwLogWarning("Nothing to write.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
// open the file
|
||||
if((rc = file::open(fH,fn,file::kWriteFl)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kOpenFailRC,"Unable to create the file '%s'.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
file::printf(fH,"dev,port,microsec,id,sec,ch,status,d0,d1\n");
|
||||
|
||||
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
|
||||
{
|
||||
const am_midi_msg_t* m = p->iMsgArray + i;
|
||||
|
||||
double secs = time::elapsedSecs( p->iMsgArray[0].timestamp, p->iMsgArray[i].timestamp );
|
||||
|
||||
char sciPitch[ midi::kMidiSciPitchCharCnt + 1 ];
|
||||
if( m->status == midi::kNoteOnMdId )
|
||||
midi::midiToSciPitch( m->d0, sciPitch, midi::kMidiSciPitchCharCnt );
|
||||
else
|
||||
strcpy(sciPitch,"");
|
||||
|
||||
if((rc = file::printf(fH, "%3i,%3i,%8i,%3i,%8.4f,%2i,0x%2x,%5s,%3i,%3i\n",
|
||||
m->devIdx, m->portIdx, m->microsec, m->id, secs,
|
||||
m->ch, m->status, sciPitch, m->d0, m->d1 )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Write failed on line:%i", i+1 );
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
file::close(fH);
|
||||
|
||||
cwLogInfo("Saved %i events to '%s'.", p->iMsgArrayInIdx, fn );
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
rc_t _midi_file_write( const char* fn, const am_midi_msg_t* msgArray, unsigned msgArrayCnt )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -384,13 +712,14 @@ namespace cw
|
||||
time::spec_t t1;
|
||||
time::get(t1);
|
||||
|
||||
// if we were recording
|
||||
if( p->recordFl )
|
||||
{
|
||||
|
||||
// set the 'microsec' value for each MIDI msg
|
||||
for(unsigned i=0; i<p->msgArrayInIdx; ++i)
|
||||
// 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->msgArray[0].timestamp,p->msgArray[i].timestamp);
|
||||
p->msgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
|
||||
}
|
||||
|
||||
cwLogInfo("MIDI messages recorded: %i",p->msgArrayInIdx );
|
||||
@ -400,26 +729,19 @@ namespace cw
|
||||
{
|
||||
io::timerStop( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
|
||||
|
||||
// TODO: should work for all channels
|
||||
// TODO:
|
||||
// BUG BUG BUG: should work for all channels
|
||||
|
||||
// all notes off
|
||||
_transmit_ctl( p, 0, 121, 0 ); // reset all controllers
|
||||
_transmit_ctl( p, 0, 123, 0 ); // all notes off
|
||||
_transmit_ctl( p, 0, 0, 0 ); // switch to bank 0
|
||||
|
||||
// send pgm change 0
|
||||
time::spec_t ts = {0};
|
||||
_event_callback( p, kInvalidId, ts, 0, midi::kPgmMdId, 0, 0 );
|
||||
_transmit_ctl( p, 0, 121, 0, 0 ); // reset all controllers
|
||||
_transmit_ctl( p, 0, 123, 0, 0 ); // all notes off
|
||||
_transmit_ctl( p, 0, 0, 0, 0 ); // switch to bank 0
|
||||
|
||||
p->pedalFl = false;
|
||||
|
||||
}
|
||||
|
||||
cwLogInfo("Runtime: %5.2f seconds.", time::elapsedMs(p->start_time,t1)/1000.0 );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _midi_receive( midi_record_play_t* p, const io::midi_msg_t& m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -436,68 +758,42 @@ namespace cw
|
||||
{
|
||||
|
||||
//if( !midi::isPedal(pkt->msgArray[j].status,pkt->msgArray[j].d0) )
|
||||
// printf("0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
|
||||
//printf("IN: 0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
|
||||
|
||||
if( p->recordFl && p->startedFl )
|
||||
if( (p->recordFl || p->logInFl) && p->startedFl )
|
||||
{
|
||||
|
||||
// verify that space exists in the record buffer
|
||||
if( p->msgArrayInIdx >= p->msgArrayN )
|
||||
if( p->iMsgArrayInIdx >= p->iMsgArrayN )
|
||||
{
|
||||
_stop(p);
|
||||
rc = cwLogError(kBufTooSmallRC,"MIDI message record buffer is full. % messages.",p->msgArrayN);
|
||||
rc = cwLogError(kBufTooSmallRC,"MIDI message record buffer is full. % messages.",p->iMsgArrayN);
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy the msg into the record buffer
|
||||
am_midi_msg_t* am = p->msgArray + p->msgArrayInIdx;
|
||||
midi::msg_t* mm = pkt->msgArray + j;
|
||||
|
||||
if( midi::isChStatus(mm->status) )
|
||||
{
|
||||
const am_midi_msg_t* am = _midi_store( p, pkt->devIdx, pkt->portIdx, mm->timeStamp, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 );
|
||||
|
||||
am->id = p->msgArrayInIdx;
|
||||
am->devIdx = pkt->devIdx;
|
||||
am->portIdx = pkt->portIdx;
|
||||
am->timestamp = mm->timeStamp;
|
||||
am->ch = mm->status & 0x0f;
|
||||
am->status = mm->status & 0xf0;
|
||||
am->d0 = mm->d0;
|
||||
am->d1 = mm->d1;
|
||||
if( p->thruFl && am != nullptr )
|
||||
_transmit_msg( p, am, false );
|
||||
|
||||
//printf("st:0x%x ch:%i d0:0x%x d1:0x%x\n",am->status,am->ch,am->d0,am->d1);
|
||||
|
||||
p->msgArrayInIdx += 1;
|
||||
|
||||
if( p->thruFl )
|
||||
{
|
||||
_transmit_msg( p, am );
|
||||
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, am->status + am->ch, am->d0, am->d1 );
|
||||
}
|
||||
|
||||
// send msg count
|
||||
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if( pkt->msgArray == NULL )
|
||||
printf("io midi cb: 0x%x ",pkt->sysExMsg[j]);
|
||||
else
|
||||
{
|
||||
if( !_midi_filter(pkt->msgArray + j) )
|
||||
printf("io midi cb: %ld %ld 0x%x %i %i\n", pkt->msgArray[j].timeStamp.tv_sec, pkt->msgArray[j].timeStamp.tv_nsec, pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m)
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -510,46 +806,27 @@ namespace cw
|
||||
|
||||
unsigned cur_time_us = time::elapsedMicros(p->play_time,t);
|
||||
|
||||
while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us )
|
||||
{
|
||||
|
||||
am_midi_msg_t* mm = p->msgArray + p->msgArrayOutIdx;
|
||||
|
||||
//_print_midi_msg(mm);
|
||||
|
||||
bool skipFl = false;
|
||||
|
||||
/*
|
||||
// if this is a pedal message
|
||||
if( mm->status == midi::kCtlMdId && (mm->d0 == midi::kSustainCtlMdId || mm->d0 == midi::kSostenutoCtlMdId || mm->d0 == midi::kSoftPedalCtlMdId ) )
|
||||
{
|
||||
// if the pedal is down
|
||||
if( p->pedalFl )
|
||||
{
|
||||
skipFl = mm->d1 > 64;
|
||||
p->pedalFl = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
skipFl = mm->d1 <= 64;
|
||||
p->pedalFl = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if( !skipFl )
|
||||
if( p->halfPedalFl )
|
||||
_half_pedal_update( p, cur_time_us );
|
||||
else
|
||||
while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us )
|
||||
{
|
||||
|
||||
am_midi_msg_t* mm = p->msgArray + p->msgArrayOutIdx;
|
||||
|
||||
//_print_midi_msg(mm);
|
||||
|
||||
_transmit_msg( p, mm );
|
||||
}
|
||||
_set_midi_msg_next_play_index(p, p->msgArrayOutIdx+1 );
|
||||
|
||||
_set_midi_msg_next_play_index(p, p->msgArrayOutIdx+1 );
|
||||
|
||||
// if all MIDI messages have been played
|
||||
if( p->msgArrayOutIdx >= p->msgArrayInIdx )
|
||||
{
|
||||
_stop(p);
|
||||
break;
|
||||
// if all MIDI messages have been played
|
||||
if( p->msgArrayOutIdx >= p->msgArrayInIdx )
|
||||
{
|
||||
_stop(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -574,17 +851,37 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const o
|
||||
p->ioH = ioH;
|
||||
p->cb = cb;
|
||||
p->cb_arg = cb_arg;
|
||||
p->halfPedalState = kHalfPedalDone;
|
||||
p->halfPedalNextUs = 0;
|
||||
p->halfPedalNoteDelayUs = 100 * 1000;
|
||||
p->halfPedalNoteDurUs = 1000 * 1000;
|
||||
p->halfPedalUpDelayUs = 1000 * 1000;
|
||||
p->halfPedalDownDelayUs = 1000 * 1000;
|
||||
p->halfPedalMidiPitch = 64;
|
||||
p->halfPedalMidiNoteVel = 64;
|
||||
p->halfPedalMidiPedalVel = 127;
|
||||
|
||||
if((p->midiOutDevIdx = io::midiDeviceIndex(p->ioH,p->midiOutDevLabel)) == kInvalidIdx )
|
||||
|
||||
for( unsigned i=0; i<p->midiDevN; ++i)
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The MIDI output device: '%s' was not found.", cwStringNullGuard(p->midiOutDevLabel) );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((p->midiOutPortIdx = io::midiDevicePortIndex(p->ioH,p->midiOutDevIdx,false,p->midiOutPortLabel)) == kInvalidIdx )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The MIDI output port: '%s' was not found.", cwStringNullGuard(p->midiOutPortLabel) );
|
||||
goto errLabel;
|
||||
midi_device_t* dev = p->midiDevA + i;
|
||||
|
||||
if( !p->midiDevA[i].enableFl )
|
||||
continue;
|
||||
|
||||
if((dev->midiOutDevIdx = io::midiDeviceIndex(p->ioH,dev->midiOutDevLabel)) == kInvalidIdx )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The MIDI output device: '%s' was not found.", cwStringNullGuard(dev->midiOutDevLabel) );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((dev->midiOutPortIdx = io::midiDevicePortIndex(p->ioH,dev->midiOutDevIdx,false,dev->midiOutPortLabel)) == kInvalidIdx )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The MIDI output port: '%s' was not found.", cwStringNullGuard(dev->midiOutPortLabel) );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
printf("%s %s : %i %i\n",dev->midiOutDevLabel, dev->midiOutPortLabel, dev->midiOutDevIdx, dev->midiOutPortIdx );
|
||||
}
|
||||
|
||||
// create the MIDI playback timer
|
||||
@ -624,21 +921,25 @@ 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;
|
||||
p->pedalFl = false;
|
||||
|
||||
// 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);
|
||||
else
|
||||
{
|
||||
p->end_play_event_timestamp = *end_play_event_timestamp;
|
||||
p->all_off_timestamp = *end_play_event_timestamp;
|
||||
time::advanceMs( p->all_off_timestamp, p->all_off_delay_ms);
|
||||
}
|
||||
|
||||
time::get(p->start_time);
|
||||
|
||||
if( p->recordFl )
|
||||
if( p->recordFl || p->logInFl or p->logOutFl )
|
||||
{
|
||||
_set_midi_msg_next_index(p, 0 );
|
||||
}
|
||||
else
|
||||
|
||||
if( !p->recordFl )
|
||||
{
|
||||
time::get(p->play_time);
|
||||
|
||||
@ -650,6 +951,12 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
|
||||
// This will cause that event to be played back immediately.
|
||||
time::subtractMicros(p->play_time, p->msgArray[ p->msgArrayOutIdx ].microsec );
|
||||
}
|
||||
|
||||
if( p->halfPedalFl )
|
||||
{
|
||||
p->halfPedalNextUs = 0;
|
||||
p->halfPedalState = kWaitForBegin;
|
||||
}
|
||||
|
||||
io::timerStart( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
|
||||
}
|
||||
@ -712,6 +1019,12 @@ cw::rc_t cw::midi_record_play::save( handle_t h, const char* fn )
|
||||
return _midi_write(p,fn);
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi_record_play::save_csv( handle_t h, const char* fn )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
return _write_csv(p,fn);
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi_record_play::open( handle_t h, const char* fn )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
@ -734,13 +1047,15 @@ cw::rc_t cw::midi_record_play::load( handle_t h, const midi_msg_t* msg, unsigned
|
||||
{
|
||||
p->msgArray[i].id = msg[i].id;
|
||||
p->msgArray[i].timestamp = msg[i].timestamp;
|
||||
p->msgArray[i].loc = msg[i].loc;
|
||||
p->msgArray[i].ch = msg[i].ch;
|
||||
p->msgArray[i].status = msg[i].status;
|
||||
p->msgArray[i].d0 = msg[i].d0;
|
||||
p->msgArray[i].d1 = msg[i].d1;
|
||||
p->msgArray[i].devIdx = p->midiOutDevIdx;
|
||||
p->msgArray[i].portIdx = p->midiOutPortIdx;
|
||||
p->msgArray[i].devIdx = kInvalidIdx;
|
||||
p->msgArray[i].portIdx = kInvalidIdx;
|
||||
p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp);
|
||||
|
||||
}
|
||||
|
||||
p->msgArrayInIdx = msg_count;
|
||||
@ -766,13 +1081,12 @@ cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp )
|
||||
{
|
||||
p->msgArrayOutIdx = i;
|
||||
|
||||
_transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl );
|
||||
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl );
|
||||
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl );
|
||||
_transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl, 0 );
|
||||
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl, 0 );
|
||||
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl, 0 );
|
||||
|
||||
cwLogInfo("damper: %s.", damp_down_fl ? "down" : "up");
|
||||
|
||||
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSustainCtlMdId, damp_down_fl ? 127 : 0 );
|
||||
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSostenutoCtlMdId, sost_down_fl ? 127 : 0 );
|
||||
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl ? 127 : 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -813,7 +1127,17 @@ unsigned cw::midi_record_play::event_count( handle_t h )
|
||||
unsigned cw::midi_record_play::event_index( handle_t h )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
return p->recordFl ? p->msgArrayInIdx : p->msgArrayOutIdx;
|
||||
return p->recordFl ? p->iMsgArrayInIdx : p->msgArrayOutIdx;
|
||||
}
|
||||
|
||||
unsigned cw::midi_record_play::event_loc( handle_t h )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
|
||||
if( !p->recordFl && 0 <= p->msgArrayOutIdx && p->msgArrayOutIdx < p->msgArrayN )
|
||||
return p->msgArray[ p->msgArrayOutIdx ].loc;
|
||||
|
||||
return kInvalidId;
|
||||
}
|
||||
|
||||
|
||||
@ -842,7 +1166,49 @@ cw::rc_t cw::midi_record_play::exec( handle_t h, const io::msg_t& m )
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned cw::midi_record_play::device_count( handle_t h )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
return p->midiDevN;
|
||||
}
|
||||
|
||||
bool cw::midi_record_play::is_device_enabled( handle_t h, unsigned devIdx )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
bool fl = false;
|
||||
|
||||
if( devIdx >= p->midiDevN )
|
||||
cwLogError(kInvalidArgRC,"The MIDI record-play device index '%i' is invalid.",devIdx );
|
||||
else
|
||||
fl = p->midiDevA[devIdx].enableFl;
|
||||
|
||||
return fl;
|
||||
}
|
||||
|
||||
void cw::midi_record_play::enable_device( handle_t h, unsigned devIdx, bool enableFl )
|
||||
{
|
||||
midi_record_play_t* p = _handleToPtr(h);
|
||||
|
||||
if( devIdx >= p->midiDevN )
|
||||
cwLogError(kInvalidArgRC,"The MIDI record-play device index '%i' is invalid.",devIdx );
|
||||
else
|
||||
{
|
||||
p->midiDevA[devIdx].enableFl = enableFl;
|
||||
printf("Enable: %i = %i\n",devIdx,enableFl);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
p->halfPedalNoteDelayUs = noteDelayMs * 1000;
|
||||
p->halfPedalNoteDurUs = noteDurMs * 1000;
|
||||
p->halfPedalDownDelayUs = downDelayMs * 1000;
|
||||
p->halfPedalMidiPitch = pitch;
|
||||
p->halfPedalMidiNoteVel = vel;
|
||||
p->halfPedalMidiPedalVel= pedal_vel;
|
||||
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi_record_play::am_to_midi_file( const char* am_filename, const char* midi_filename )
|
||||
{
|
||||
|
@ -12,14 +12,15 @@ namespace cw
|
||||
{
|
||||
unsigned id;
|
||||
time::spec_t timestamp;
|
||||
unsigned loc;
|
||||
uint8_t ch;
|
||||
uint8_t status;
|
||||
uint8_t d0;
|
||||
uint8_t d1;
|
||||
uint8_t d1;
|
||||
} midi_msg_t;
|
||||
|
||||
|
||||
typedef void (*event_callback_t)( void* arg, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
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 );
|
||||
|
||||
|
||||
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, event_callback_t cb=nullptr, void* cb_arg=nullptr );
|
||||
@ -39,15 +40,27 @@ namespace cw
|
||||
bool thru_state( handle_t h );
|
||||
|
||||
rc_t save( handle_t h, const char* fn );
|
||||
rc_t save_csv( handle_t h, const char* fn );
|
||||
|
||||
rc_t open( handle_t h, const char* fn );
|
||||
|
||||
// Load the playback buffer with messages to output.
|
||||
rc_t load( handle_t h, const midi_msg_t* msg, unsigned msg_count );
|
||||
|
||||
rc_t seek( handle_t h, time::spec_t timestamp );
|
||||
|
||||
unsigned event_count( handle_t h ); // Current count of stored messages.
|
||||
unsigned event_index( handle_t h ); // record mode: index of next event to store play mode:index of next event to play
|
||||
unsigned event_loc( handle_t h ); // play mode: loc of next event to play record mode:kInvalidId
|
||||
|
||||
unsigned event_count( handle_t h );
|
||||
unsigned event_index( handle_t h );
|
||||
rc_t exec( handle_t h, const io::msg_t& msg );
|
||||
|
||||
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 );
|
||||
|
||||
void half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs );
|
||||
|
||||
// Convert an audio-midi file to a MIDI file
|
||||
rc_t am_to_midi_file( const char* am_filename, const char* midi_filename );
|
||||
rc_t am_to_midi_dir( const char* inDir );
|
||||
|
@ -35,10 +35,20 @@ namespace cw
|
||||
kPanelDivId = 1000,
|
||||
kQuitBtnId,
|
||||
kIoReportBtnId,
|
||||
kNetPrintBtnId,
|
||||
kReportBtnId,
|
||||
|
||||
kStartBtnId,
|
||||
kStopBtnId,
|
||||
|
||||
kPrintMidiCheckId,
|
||||
kPianoMidiCheckId,
|
||||
kSamplerMidiCheckId,
|
||||
kSyncDelayMsId,
|
||||
|
||||
kWetInGainId,
|
||||
kWetOutGainId,
|
||||
kDryGainId,
|
||||
|
||||
kMidiThruCheckId,
|
||||
kCurMidiEvtCntId,
|
||||
@ -59,6 +69,13 @@ namespace cw
|
||||
|
||||
kStatusId,
|
||||
|
||||
kHalfPedalPedalVel,
|
||||
kHalfPedalDelayMs,
|
||||
kHalfPedalPitch,
|
||||
kHalfPedalVel,
|
||||
kHalfPedalDurMs,
|
||||
kHalfPedalDnDelayMs,
|
||||
|
||||
kLogId,
|
||||
|
||||
kFragListId,
|
||||
@ -81,6 +98,12 @@ namespace cw
|
||||
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kPiano_MRP_DevIdx = 0,
|
||||
kSampler_MRP_DevIdx = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kAmMidiTimerId
|
||||
@ -93,10 +116,20 @@ namespace cw
|
||||
{ ui::kRootAppId, kPanelDivId, "panelDivId" },
|
||||
{ kPanelDivId, kQuitBtnId, "quitBtnId" },
|
||||
{ kPanelDivId, kIoReportBtnId, "ioReportBtnId" },
|
||||
{ kPanelDivId, kNetPrintBtnId, "netPrintBtnId" },
|
||||
{ kPanelDivId, kReportBtnId, "reportBtnId" },
|
||||
|
||||
{ kPanelDivId, kStartBtnId, "startBtnId" },
|
||||
{ kPanelDivId, kStopBtnId, "stopBtnId" },
|
||||
|
||||
{ kPanelDivId, kPrintMidiCheckId, "printMidiCheckId" },
|
||||
{ kPanelDivId, kPianoMidiCheckId, "pianoMidiCheckId" },
|
||||
{ kPanelDivId, kSamplerMidiCheckId,"samplerMidiCheckId" },
|
||||
{ kPanelDivId, kSyncDelayMsId, "syncDelayMsId" },
|
||||
|
||||
{ kPanelDivId, kWetInGainId, "wetInGainId" },
|
||||
{ kPanelDivId, kWetOutGainId, "wetOutGainId" },
|
||||
{ kPanelDivId, kDryGainId, "dryGainId" },
|
||||
|
||||
{ kPanelDivId, kMidiThruCheckId, "midiThruCheckId" },
|
||||
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
|
||||
@ -114,6 +147,15 @@ namespace cw
|
||||
{ kPanelDivId, kInsertLocId, "insertLocId" },
|
||||
{ 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" },
|
||||
|
||||
@ -156,6 +198,7 @@ namespace cw
|
||||
const char* record_fn;
|
||||
const char* record_fn_ext;
|
||||
const char* scoreFn;
|
||||
const object_t* midi_play_record_cfg;
|
||||
const object_t* frag_panel_cfg;
|
||||
const object_t* presets_cfg;
|
||||
const object_t* flow_cfg;
|
||||
@ -184,7 +227,15 @@ namespace cw
|
||||
|
||||
double crossFadeSrate;
|
||||
unsigned crossFadeCnt;
|
||||
|
||||
|
||||
bool printMidiFl;
|
||||
|
||||
unsigned hpDelayMs;
|
||||
unsigned hpPedalVel;
|
||||
unsigned hpPitch;
|
||||
unsigned hpVel;
|
||||
unsigned hpDurMs;
|
||||
unsigned hpDnDelayMs;
|
||||
} app_t;
|
||||
|
||||
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
|
||||
@ -198,18 +249,31 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = params_cfgRef->getv( "record_dir", app->record_dir,
|
||||
"record_fn", app->record_fn,
|
||||
"record_fn_ext", app->record_fn_ext,
|
||||
"score_fn", app->scoreFn,
|
||||
"frag_panel", app->frag_panel_cfg,
|
||||
"presets", app->presets_cfg,
|
||||
"crossFadeSrate",app->crossFadeSrate,
|
||||
"crossFadeCount",app->crossFadeCnt)) != kOkRC )
|
||||
if((rc = params_cfgRef->getv( "record_dir", app->record_dir,
|
||||
"record_fn", app->record_fn,
|
||||
"record_fn_ext", app->record_fn_ext,
|
||||
"score_fn", app->scoreFn,
|
||||
"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 )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed.");
|
||||
}
|
||||
|
||||
if((app->scoreFn = filesys::expandPath( app->scoreFn )) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The score file name is invalid.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((app->record_dir = filesys::expandPath(app->record_dir)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The record directory path is invalid.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// verify that the output directory exists
|
||||
if((rc = filesys::isDir(app->record_dir)) != kOkRC )
|
||||
if((rc = filesys::makeDir(app->record_dir)) != kOkRC )
|
||||
@ -257,6 +321,8 @@ namespace cw
|
||||
|
||||
rc_t _free( app_t& app )
|
||||
{
|
||||
mem::release((char*&)app.record_dir);
|
||||
mem::release((char*&)app.scoreFn);
|
||||
preset_sel::destroy(app.psH);
|
||||
io_flow::destroy(app.ioFlowH);
|
||||
midi_record_play::destroy(app.mrpH);
|
||||
@ -283,15 +349,15 @@ namespace cw
|
||||
{
|
||||
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx);
|
||||
|
||||
cwLogInfo("Apply preset: '%s'.\n", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
|
||||
cwLogInfo("Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
|
||||
|
||||
if( preset_label != nullptr )
|
||||
{
|
||||
io_flow::apply_preset( app->ioFlowH, flow_cross::kNextDestId, preset_label );
|
||||
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal", "in", flow::kAnyChIdx, (dsp::real_t)frag->wetDryGain );
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "split_wet", "gain", flow::kAnyChIdx, (dsp::real_t)frag->igain );
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "cmp", "ogain", flow::kAnyChIdx, (dsp::real_t)frag->ogain );
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::real_t)frag->igain );
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::real_t)frag->ogain );
|
||||
io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal", "in", flow::kAnyChIdx, (dsp::real_t)frag->wetDryGain );
|
||||
|
||||
io_flow::begin_cross_fade( app->ioFlowH, frag->fadeOutMs );
|
||||
}
|
||||
@ -301,34 +367,98 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
void _midi_play_callback( void* arg, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
|
||||
|
||||
// Turn on the selection border for the clicked fragment
|
||||
rc_t _do_select_frag( app_t* app, unsigned clickedUuId )
|
||||
{
|
||||
app_t* app = (app_t*)arg;
|
||||
if( id != kInvalidId )
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// get the last selected fragment
|
||||
unsigned prevFragId = preset_sel::ui_select_fragment_id(app->psH);
|
||||
unsigned prevUuId = preset_sel::frag_to_gui_id(app->psH,prevFragId,false);
|
||||
|
||||
// is the last selected fragment the same as the clicked fragment
|
||||
bool reclickFl = prevUuId == clickedUuId;
|
||||
|
||||
// if a different fragment was clicked then deselect the last fragment in the UI
|
||||
if( !reclickFl )
|
||||
{
|
||||
const unsigned buf_byte_cnt = 256;
|
||||
char buf[ buf_byte_cnt ];
|
||||
event_to_string( app->scoreH, id, buf, buf_byte_cnt );
|
||||
printf("%s\n",buf);
|
||||
|
||||
const preset_sel::frag_t* f = nullptr;
|
||||
if( preset_sel::track_timestamp( app->psH, timestamp, f ) )
|
||||
{
|
||||
//printf("NEW FRAG: id:%i loc:%i\n", f->fragId, f->endLoc );
|
||||
_apply_preset( app, timestamp, f );
|
||||
}
|
||||
|
||||
if(prevUuId != kInvalidId )
|
||||
io::uiSetSelect( app->ioH, prevUuId, false );
|
||||
|
||||
// select or deselect the clicked fragment
|
||||
io::uiSetSelect( app->ioH, clickedUuId, !reclickFl );
|
||||
}
|
||||
|
||||
// Note: calls to uiSetSelect() produce callbacks to _onUiSelect().
|
||||
|
||||
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 )
|
||||
{
|
||||
app_t* app = (app_t*)arg;
|
||||
|
||||
if( app->printMidiFl )
|
||||
{
|
||||
const unsigned buf_byte_cnt = 256;
|
||||
char buf[ buf_byte_cnt ];
|
||||
|
||||
// if this event is not in the score
|
||||
if( id == kInvalidId )
|
||||
{
|
||||
// TODO: print this out in the same format as event_to_string()
|
||||
snprintf(buf,buf_byte_cnt,"ch:%i status:0x%02x d0:%i d1:%i",ch,status,d0,d1);
|
||||
}
|
||||
else
|
||||
score::event_to_string( app->scoreH, id, buf, buf_byte_cnt );
|
||||
printf("%s\n",buf);
|
||||
}
|
||||
|
||||
if( midi_record_play::is_started(app->mrpH) )
|
||||
{
|
||||
const preset_sel::frag_t* f = nullptr;
|
||||
if( preset_sel::track_timestamp( app->psH, timestamp, f ) )
|
||||
{
|
||||
//printf("NEW FRAG: id:%i loc:%i\n", f->fragId, f->endLoc );
|
||||
_apply_preset( app, timestamp, f );
|
||||
|
||||
if( f != nullptr )
|
||||
_do_select_frag( app, f->guiUuId );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the closest locMap equal to or after 'loc'
|
||||
loc_map_t* _find_loc( app_t* app, unsigned loc )
|
||||
{
|
||||
unsigned i=0;
|
||||
loc_map_t* pre_loc_map = nullptr;
|
||||
|
||||
for(; i<app->locMapN; ++i)
|
||||
if( app->locMap[i].loc == loc )
|
||||
return app->locMap + i;
|
||||
return nullptr;
|
||||
{
|
||||
if( app->locMap[i].loc >= loc )
|
||||
return app->locMap +i;
|
||||
|
||||
pre_loc_map = app->locMap + i;
|
||||
|
||||
}
|
||||
|
||||
return pre_loc_map;
|
||||
}
|
||||
|
||||
rc_t _do_stop_play( app_t* app )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if((rc = midi_record_play::stop(app->mrpH)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI stop failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -339,6 +469,16 @@ namespace cw
|
||||
loc_map_t* begMap = nullptr;
|
||||
loc_map_t* endMap = nullptr;
|
||||
|
||||
// if the player is already playing then stop it
|
||||
if( midi_record_play::is_started(app->mrpH) )
|
||||
{
|
||||
rc = _do_stop_play(app);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The begin play location is not valid.");
|
||||
@ -381,7 +521,7 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) );
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
@ -415,8 +555,10 @@ namespace cw
|
||||
|
||||
void _update_event_ui( app_t* app )
|
||||
{
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) );
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), midi_record_play::event_count(app->mrpH) );
|
||||
//io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) );
|
||||
//io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), midi_record_play::event_count(app->mrpH) );
|
||||
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), app->maxLoc );
|
||||
}
|
||||
|
||||
// Update the UI with the value from the the fragment data record.
|
||||
@ -752,7 +894,7 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
rc_t _restore( app_t* app )
|
||||
rc_t _restore_fragment_data( app_t* app )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
char* fn = nullptr;
|
||||
@ -778,10 +920,10 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
preset_sel::report( app->psH );
|
||||
//preset_sel::report( app->psH );
|
||||
|
||||
f = preset_sel::get_fragment_base(app->psH);
|
||||
for(; f!=nullptr; f=f->link)
|
||||
for(int i=0; f!=nullptr; f=f->link,++i)
|
||||
{
|
||||
unsigned fragId = f->fragId;
|
||||
|
||||
@ -792,7 +934,7 @@ namespace cw
|
||||
}
|
||||
|
||||
_update_frag_ui(app, fragId );
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -834,21 +976,18 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
int _compare_loc_map( const void* m0, const void* m1 )
|
||||
{ return ((const loc_map_t*)m0)->loc - ((const loc_map_t*)m1)->loc; }
|
||||
|
||||
rc_t _do_load( app_t* app )
|
||||
rc_t _load_piano_score( app_t* app, unsigned& midiEventCntRef )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const score::event_t* e = nullptr;
|
||||
unsigned midiEventN = 0;
|
||||
midi_record_play::midi_msg_t* m = nullptr;
|
||||
|
||||
// if the score is already loaded
|
||||
if( app->scoreH.isValid() )
|
||||
return rc;
|
||||
|
||||
cwLogInfo("Loading");
|
||||
_set_status(app,"Loading...");
|
||||
|
||||
midiEventCntRef = 0;
|
||||
|
||||
// create the score
|
||||
if((rc = score::create( app->scoreH, app->scoreFn )) != kOkRC )
|
||||
{
|
||||
@ -885,6 +1024,7 @@ namespace cw
|
||||
m[i].d0 = e->d0;
|
||||
m[i].d1 = e->d1;
|
||||
m[i].id = e->uid;
|
||||
m[i].loc = e->loc;
|
||||
|
||||
app->locMap[i].loc = e->loc;
|
||||
app->locMap[i].timestamp = m[i].timestamp;
|
||||
@ -895,6 +1035,8 @@ namespace cw
|
||||
++i;
|
||||
}
|
||||
|
||||
qsort( app->locMap, app->locMapN, sizeof(loc_map_t), _compare_loc_map );
|
||||
|
||||
// load the player with the msg list
|
||||
if((rc = midi_record_play::load( app->mrpH, m, midiEventN )) != kOkRC )
|
||||
{
|
||||
@ -902,36 +1044,80 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
mem::free(m);
|
||||
|
||||
cwLogInfo("%i MIDI events loaded from score.", midiEventN );
|
||||
|
||||
mem::free(m);
|
||||
}
|
||||
|
||||
// set the range of the global play location controls
|
||||
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->minLoc );
|
||||
io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->maxLoc );
|
||||
errLabel:
|
||||
|
||||
midiEventCntRef = midiEventN;
|
||||
|
||||
// enable the 'End Loc' number box since the score is loaded
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
rc_t _do_load( app_t* app )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
unsigned midiEventN = 0;
|
||||
bool firstLoadFl = !app->scoreH.isValid();
|
||||
unsigned minLoc = firstLoadFl ? 0 : app->minLoc;
|
||||
unsigned maxLoc = firstLoadFl ? 0 : app->maxLoc;
|
||||
|
||||
// if the score is already loaded
|
||||
//if( app->scoreH.isValid() )
|
||||
// return rc;
|
||||
|
||||
cwLogInfo("Loading");
|
||||
_set_status(app,"Loading...");
|
||||
|
||||
|
||||
// Load the piano score
|
||||
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 );
|
||||
|
||||
// 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 );
|
||||
|
||||
|
||||
// enable the 'End Loc' number box since the score is loaded
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true );
|
||||
}
|
||||
|
||||
// update the current event and event count
|
||||
_update_event_ui(app);
|
||||
|
||||
|
||||
// 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 );
|
||||
|
||||
|
||||
// restore the fragment records
|
||||
if((rc = _restore( app )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Restore failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
if( firstLoadFl )
|
||||
if((rc = _restore_fragment_data( app )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Restore failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLoadBtnId ), false );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLoadBtnId ), true );
|
||||
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true );
|
||||
|
||||
|
||||
cwLogInfo("'%s' loaded.",app->scoreFn);
|
||||
|
||||
errLabel:
|
||||
@ -946,25 +1132,11 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _on_ui_start( app_t* app )
|
||||
{
|
||||
return _do_play(app, app->beg_play_loc, app->end_play_loc );
|
||||
}
|
||||
|
||||
rc_t _on_ui_stop( app_t* app )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if((rc = midi_record_play::stop(app->mrpH)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI start failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _set_midi_thru_state( app_t* app, bool thru_fl )
|
||||
{
|
||||
@ -990,26 +1162,31 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( appId )
|
||||
{
|
||||
case kBegPlayLocNumbId:
|
||||
app->beg_play_loc = loc;
|
||||
break;
|
||||
|
||||
case kEndPlayLocNumbId:
|
||||
app->end_play_loc = loc;
|
||||
break;
|
||||
}
|
||||
|
||||
bool enableFl = app->beg_play_loc < app->end_play_loc;
|
||||
|
||||
_enable_global_play_btn(app, enableFl );
|
||||
|
||||
if(enableFl)
|
||||
_clear_status(app);
|
||||
// verify that the location exists
|
||||
if( _find_loc(app,loc) == nullptr )
|
||||
_set_status(app,"%i is an invalid location.",loc);
|
||||
else
|
||||
_set_status(app,"Invalid play location range.");
|
||||
|
||||
{
|
||||
switch( appId )
|
||||
{
|
||||
case kBegPlayLocNumbId:
|
||||
app->beg_play_loc = loc;
|
||||
break;
|
||||
|
||||
case kEndPlayLocNumbId:
|
||||
app->end_play_loc = loc;
|
||||
break;
|
||||
}
|
||||
|
||||
bool enableFl = app->beg_play_loc < app->end_play_loc;
|
||||
|
||||
_enable_global_play_btn(app, enableFl );
|
||||
|
||||
if(enableFl)
|
||||
_clear_status(app);
|
||||
else
|
||||
_set_status(app,"Invalid play location range.");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -1131,10 +1308,126 @@ namespace cw
|
||||
|
||||
if( rc != kOkRC )
|
||||
rc = cwLogError(rc,"Fragment delete failed.");
|
||||
else
|
||||
cwLogInfo("Fragment %i deleted.",fragId);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _on_echo_midi_enable( app_t* app, unsigned uuId, unsigned mrp_dev_idx )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
void _on_midi_enable( app_t* app, unsigned checkAppId, unsigned mrp_dev_idx, bool enableFl )
|
||||
{
|
||||
unsigned midi_dev_n = midi_record_play::device_count(app->mrpH);
|
||||
|
||||
if( mrp_dev_idx < midi_dev_n )
|
||||
midi_record_play::enable_device(app->mrpH, mrp_dev_idx, enableFl );
|
||||
else
|
||||
cwLogError(kInvalidArgRC,"%i is not a valid MIDI device index for device count:%i.",mrp_dev_idx,midi_dev_n);
|
||||
}
|
||||
|
||||
rc_t _on_echo_master_value( app_t* app, unsigned varId, unsigned uuId )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
double val = 0;
|
||||
if((rc = get_value( app->psH, kInvalidId, varId, kInvalidId, val )) != kOkRC )
|
||||
rc = cwLogError(rc,"Unable to get the master value for var id:%i.",varId);
|
||||
else
|
||||
io::uiSendValue( app->ioH, uuId, val );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _on_master_value( app_t* app, const char* inst_label, const char* var_label, unsigned varId, double value )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
if((rc = preset_sel::set_value( app->psH, kInvalidId, varId, kInvalidId, value )) != kOkRC )
|
||||
rc = cwLogError(rc,"Master value set failed on varId:%i %s.%s.",varId,cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
|
||||
else
|
||||
if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, inst_label, var_label, flow::kAnyChIdx, (dsp::real_t)value )) != kOkRC )
|
||||
rc = cwLogError(rc,"Master value send failed on %s.%s.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value )
|
||||
{
|
||||
switch( appId )
|
||||
{
|
||||
case kHalfPedalDelayMs:
|
||||
app->hpDelayMs = value;
|
||||
break;
|
||||
|
||||
case kHalfPedalPedalVel:
|
||||
app->hpPedalVel = value;
|
||||
break;
|
||||
|
||||
case kHalfPedalPitch:
|
||||
app->hpPitch = value;
|
||||
break;
|
||||
|
||||
case kHalfPedalVel:
|
||||
app->hpVel = value;
|
||||
break;
|
||||
|
||||
case kHalfPedalDurMs:
|
||||
app->hpDurMs = value;
|
||||
break;
|
||||
|
||||
case kHalfPedalDnDelayMs:
|
||||
app->hpDnDelayMs = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
{ assert(0); }
|
||||
|
||||
}
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t _on_echo_half_pedal( app_t* app, unsigned appId, unsigned uuId )
|
||||
{
|
||||
switch( appId )
|
||||
{
|
||||
case kHalfPedalDelayMs:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpDelayMs );
|
||||
break;
|
||||
|
||||
case kHalfPedalPedalVel:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpPedalVel );
|
||||
break;
|
||||
|
||||
case kHalfPedalPitch:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpPitch );
|
||||
break;
|
||||
|
||||
case kHalfPedalVel:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpVel );
|
||||
break;
|
||||
|
||||
case kHalfPedalDurMs:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpDurMs );
|
||||
break;
|
||||
|
||||
case kHalfPedalDnDelayMs:
|
||||
io::uiSendValue( app->ioH, uuId, app->hpDnDelayMs );
|
||||
break;
|
||||
|
||||
default:
|
||||
{ assert(0); }
|
||||
|
||||
}
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
|
||||
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -1150,7 +1443,7 @@ namespace cw
|
||||
for(; f!=nullptr; f=f->link)
|
||||
_update_frag_ui( app, f->fragId );
|
||||
|
||||
//_do_load(app);
|
||||
_do_load(app);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -1168,11 +1461,17 @@ namespace cw
|
||||
case kIoReportBtnId:
|
||||
io::report( app->ioH );
|
||||
break;
|
||||
|
||||
case kNetPrintBtnId:
|
||||
io_flow::print_network(app->ioFlowH,flow_cross::kCurDestId);
|
||||
break;
|
||||
|
||||
case kReportBtnId:
|
||||
//preset_sel::report( app->psH );
|
||||
preset_sel::report( app->psH );
|
||||
//io_flow::apply_preset( app->ioFlowH, 2000.0, app->tmp==0 ? "a" : "b");
|
||||
//app->tmp = !app->tmp;
|
||||
//io_flow::print(app->ioFlowH);
|
||||
//midi_record_play::save_csv(app->mrpH,"/home/kevin/temp/mrp_1.csv");
|
||||
break;
|
||||
|
||||
case kSaveBtnId:
|
||||
@ -1193,9 +1492,37 @@ namespace cw
|
||||
break;
|
||||
|
||||
case kStopBtnId:
|
||||
_on_ui_stop(app);
|
||||
_do_stop_play(app);
|
||||
break;
|
||||
|
||||
case kPrintMidiCheckId:
|
||||
app->printMidiFl = m.value->u.b;
|
||||
break;
|
||||
|
||||
case kPianoMidiCheckId:
|
||||
_on_midi_enable( app, m.appId, kPiano_MRP_DevIdx, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kSamplerMidiCheckId:
|
||||
_on_midi_enable( app, m.appId, kSampler_MRP_DevIdx, m.value->u.b );
|
||||
break;
|
||||
|
||||
case kWetInGainId:
|
||||
_on_master_value( app, "mstr_wet_in_gain","gain", preset_sel::kMasterWetInGainVarId, m.value->u.d );
|
||||
break;
|
||||
|
||||
case kWetOutGainId:
|
||||
_on_master_value( app, "mstr_wet_out_gain","gain",preset_sel::kMasterWetOutGainVarId, m.value->u.d );
|
||||
break;
|
||||
|
||||
case kDryGainId:
|
||||
_on_master_value( app, "mstr_dry_out_gain", "gain", preset_sel::kMasterDryGainVarId, m.value->u.d );
|
||||
break;
|
||||
|
||||
case kSyncDelayMsId:
|
||||
_on_master_value( app, "sync_delay","delayMs",preset_sel::kMasterSyncDelayMsVarId, (double)m.value->u.i );
|
||||
break;
|
||||
|
||||
case kBegPlayLocNumbId:
|
||||
_on_ui_play_loc(app, m.appId, m.value->u.i);
|
||||
break;
|
||||
@ -1216,6 +1543,15 @@ namespace cw
|
||||
_on_ui_delete_btn(app);
|
||||
break;
|
||||
|
||||
case kHalfPedalPedalVel:
|
||||
case kHalfPedalDelayMs:
|
||||
case kHalfPedalPitch:
|
||||
case kHalfPedalVel:
|
||||
case kHalfPedalDurMs:
|
||||
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);
|
||||
break;
|
||||
@ -1286,31 +1622,6 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t _onUiClick( app_t* app, const io::ui_msg_t& m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// get the last selected fragment
|
||||
unsigned prevFragId = preset_sel::ui_select_fragment_id(app->psH);
|
||||
unsigned prevUuId = preset_sel::frag_to_gui_id(app->psH,prevFragId,false);
|
||||
|
||||
// is the last selected fragment the same as the clicked fragment
|
||||
bool reclickFl = prevUuId == m.uuId;
|
||||
|
||||
// if a different fragment was clicked then deselect the last fragment in the UI
|
||||
if( !reclickFl )
|
||||
{
|
||||
if(prevUuId != kInvalidId )
|
||||
uiSetSelect( app->ioH, prevUuId, false );
|
||||
|
||||
// select or deselect the clicked fragment
|
||||
uiSetSelect( app->ioH, m.uuId, !reclickFl );
|
||||
}
|
||||
|
||||
// Note: calls to uiSetSelect() produce callbacks to _onUiSelect().
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _onUiSelect( app_t* app, const io::ui_msg_t& m )
|
||||
{
|
||||
@ -1333,10 +1644,50 @@ namespace cw
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
switch( m.appId )
|
||||
{
|
||||
case kPrintMidiCheckId:
|
||||
break;
|
||||
|
||||
case kPianoMidiCheckId:
|
||||
_on_echo_midi_enable( app, m.uuId, kPiano_MRP_DevIdx );
|
||||
break;
|
||||
|
||||
case kSamplerMidiCheckId:
|
||||
_on_echo_midi_enable( app, m.uuId, kSampler_MRP_DevIdx );
|
||||
break;
|
||||
|
||||
case kWetInGainId:
|
||||
_on_echo_master_value( app, preset_sel::kMasterWetInGainVarId, m.uuId );
|
||||
break;
|
||||
|
||||
case kWetOutGainId:
|
||||
_on_echo_master_value( app, preset_sel::kMasterWetOutGainVarId, m.uuId );
|
||||
break;
|
||||
|
||||
case kDryGainId:
|
||||
_on_echo_master_value( app, preset_sel::kMasterDryGainVarId, m.uuId );
|
||||
break;
|
||||
|
||||
case kSyncDelayMsId:
|
||||
_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
|
||||
break;
|
||||
|
||||
case kHalfPedalPedalVel:
|
||||
case kHalfPedalDelayMs:
|
||||
case kHalfPedalPitch:
|
||||
case kHalfPedalVel:
|
||||
case kHalfPedalDurMs:
|
||||
case kHalfPedalDnDelayMs:
|
||||
_on_echo_half_pedal( app, m.appId, m.uuId );
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1367,7 +1718,7 @@ namespace cw
|
||||
break;
|
||||
|
||||
case ui::kClickOpId:
|
||||
_onUiClick( app, m );
|
||||
_do_select_frag( app, m.uuId );
|
||||
break;
|
||||
|
||||
case ui::kSelectOpId:
|
||||
@ -1402,7 +1753,7 @@ namespace cw
|
||||
{
|
||||
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_index(app->mrpH) );
|
||||
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
|
||||
}
|
||||
|
||||
if( app->ioFlowH.isValid() )
|
||||
@ -1453,8 +1804,9 @@ namespace cw
|
||||
|
||||
cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
|
||||
{
|
||||
|
||||
rc_t rc;
|
||||
app_t app = { };
|
||||
app_t app = { .hpDelayMs=250, .hpPedalVel=127, .hpPitch=64, .hpVel=64, .hpDurMs=500, .hpDnDelayMs=1000 };
|
||||
const object_t* params_cfg = nullptr;
|
||||
|
||||
// Parse the configuration
|
||||
@ -1477,14 +1829,13 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// create the MIDI record-play object
|
||||
if((rc = midi_record_play::create(app.mrpH,app.ioH,*params_cfg,_midi_play_callback,&app)) != kOkRC )
|
||||
if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg,_midi_play_callback,&app)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI record-play object create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// 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 )
|
||||
{
|
||||
@ -1492,13 +1843,14 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// start the io framework instance
|
||||
// start the IO framework instance
|
||||
if((rc = io::start(app.ioH)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Preset-select app start failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// execute the io framework
|
||||
while( !isShuttingDown(app.ioH))
|
||||
{
|
||||
|
2
cwMidi.h
2
cwMidi.h
@ -129,7 +129,7 @@ namespace cw
|
||||
|
||||
// If label is NULL or labelCharCnt==0 then a pointer to an internal static
|
||||
// buffer is returned. If label[] is given the it
|
||||
// should have at least 5 (kMidiPitchCharCnt) char's (including the terminating zero).
|
||||
// should have at least 5 (kMidiSciPitchCharCnt) char's (including the terminating zero).
|
||||
// If 'pitch' is outside of the range 0-127 then a blank string is returned.
|
||||
const char* midiToSciPitch( uint8_t pitch, char* label, unsigned labelCharCnt );
|
||||
|
||||
|
@ -464,7 +464,7 @@ namespace cw
|
||||
|
||||
void _cmMpReportPort( textBuf::handle_t tbH, const port_t* port )
|
||||
{
|
||||
textBuf::print( tbH," client:%i port:%i %s caps:(",port->alsa_addr.client,port->alsa_addr.port,port->nameStr);
|
||||
textBuf::print( tbH," client:%i port:%i '%s' caps:(",port->alsa_addr.client,port->alsa_addr.port,port->nameStr);
|
||||
if( port->alsa_cap & SND_SEQ_PORT_CAP_READ ) textBuf::print( tbH,"Read " );
|
||||
if( port->alsa_cap & SND_SEQ_PORT_CAP_WRITE ) textBuf::print( tbH,"Writ " );
|
||||
if( port->alsa_cap & SND_SEQ_PORT_CAP_SYNC_READ ) textBuf::print( tbH,"Syrd " );
|
||||
@ -896,7 +896,7 @@ void cw::midi::device::report( handle_t h, textBuf::handle_t tbH )
|
||||
{
|
||||
const dev_t* d = p->devArray + i;
|
||||
|
||||
textBuf::print( tbH,"%i : Device: %s \n",i,cwStringNullGuard(d->nameStr));
|
||||
textBuf::print( tbH,"%i : Device: '%s' \n",i,cwStringNullGuard(d->nameStr));
|
||||
|
||||
if(d->iPortCnt > 0 )
|
||||
textBuf::print( tbH," Input:\n");
|
||||
|
221
cwPianoScore.cpp
221
cwPianoScore.cpp
@ -6,6 +6,7 @@
|
||||
#include "cwPianoScore.h"
|
||||
#include "cwMidi.h"
|
||||
#include "cwTime.h"
|
||||
#include "cwFile.h"
|
||||
|
||||
namespace cw
|
||||
{
|
||||
@ -16,6 +17,11 @@ namespace cw
|
||||
event_t* base;
|
||||
event_t* end;
|
||||
unsigned maxLocId;
|
||||
|
||||
event_t** uid_mapA;
|
||||
unsigned uid_mapN;
|
||||
unsigned min_uid;
|
||||
|
||||
} score_t;
|
||||
|
||||
score_t* _handleToPtr(handle_t h)
|
||||
@ -38,6 +44,198 @@ namespace cw
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned _scan_to_end_of_field( const char* lineBuf, unsigned buf_idx, unsigned bufCharCnt )
|
||||
{
|
||||
for(; buf_idx < bufCharCnt; ++buf_idx )
|
||||
{
|
||||
if( lineBuf[buf_idx] == '"' )
|
||||
{
|
||||
for(++buf_idx; buf_idx < bufCharCnt; ++buf_idx)
|
||||
if( lineBuf[buf_idx] == '"' )
|
||||
break;
|
||||
}
|
||||
|
||||
if( lineBuf[buf_idx] == ',')
|
||||
break;
|
||||
}
|
||||
|
||||
return buf_idx;
|
||||
}
|
||||
|
||||
|
||||
rc_t _parse_csv_double( const char* lineBuf, unsigned bfi, unsigned efi, double &valueRef )
|
||||
{
|
||||
errno = 0;
|
||||
valueRef = strtod(lineBuf+bfi,nullptr);
|
||||
if( errno != 0 )
|
||||
return cwLogError(kOpFailRC,"CSV String to number conversion failed.");
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t _parse_csv_unsigned( const char* lineBuf, unsigned bfi, unsigned efi, unsigned &valueRef )
|
||||
{
|
||||
rc_t rc;
|
||||
double v;
|
||||
if((rc = _parse_csv_double(lineBuf,bfi,efi,v)) == kOkRC )
|
||||
valueRef = (unsigned)v;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _parse_csv_line( score_t* p, event_t* e, char* line_buf, unsigned lineBufCharCnt )
|
||||
{
|
||||
enum
|
||||
{
|
||||
kMeas_FIdx,
|
||||
kIndex_FIdx,
|
||||
kVoice_FIdx,
|
||||
kLoc_FIdx,
|
||||
kTick_FIdx,
|
||||
kSec_FIdx,
|
||||
kDur_FIdx,
|
||||
kRval_FIdx,
|
||||
kSPitch_FIdx,
|
||||
kDMark_FIdx,
|
||||
kDLevel_FIdx,
|
||||
kStatus_FIdx,
|
||||
kD0_FIdx,
|
||||
kD1_FIdx,
|
||||
kBar_FIdx,
|
||||
kSection_FIdx,
|
||||
kBpm_FIdx,
|
||||
kGrace_FIdx,
|
||||
kPedal_FIdx,
|
||||
kMax_FIdx
|
||||
};
|
||||
|
||||
rc_t rc = kOkRC;
|
||||
unsigned bfi = 0;
|
||||
unsigned efi = 0;
|
||||
unsigned field_idx = 0;
|
||||
|
||||
|
||||
for(field_idx=0; field_idx != kMax_FIdx; ++field_idx)
|
||||
{
|
||||
if((efi = _scan_to_end_of_field(line_buf,efi,lineBufCharCnt)) == kInvalidIdx )
|
||||
{
|
||||
rc = cwLogError( rc, "End of field scan failed");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
if( bfi != efi )
|
||||
{
|
||||
switch( field_idx )
|
||||
{
|
||||
case kLoc_FIdx:
|
||||
rc = _parse_csv_unsigned( line_buf, bfi, efi, e->loc );
|
||||
break;
|
||||
|
||||
case kSec_FIdx:
|
||||
rc = _parse_csv_double( line_buf, bfi, efi, e->sec );
|
||||
break;
|
||||
|
||||
case kStatus_FIdx:
|
||||
rc = _parse_csv_unsigned( line_buf, bfi, efi, e->status );
|
||||
break;
|
||||
|
||||
case kD0_FIdx:
|
||||
rc = _parse_csv_unsigned( line_buf, bfi, efi, e->d0 );
|
||||
break;
|
||||
|
||||
case kD1_FIdx:
|
||||
rc = _parse_csv_unsigned( line_buf, bfi, efi, e->d1 );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bfi = efi + 1;
|
||||
efi = efi + 1;
|
||||
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t _parse_csv( score_t* p, const char* fn )
|
||||
{
|
||||
rc_t rc;
|
||||
file::handle_t fH;
|
||||
unsigned line_count = 0;
|
||||
char* lineBufPtr = nullptr;
|
||||
unsigned lineBufCharCnt = 0;
|
||||
event_t* e = nullptr;
|
||||
|
||||
if((rc = file::open( fH, fn, file::kReadFl )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError( rc, "Piano score file open failed on '%s'.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = file::lineCount(fH,&line_count)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError( rc, "Line count query failed on '%s'.",cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->min_uid = kInvalidId;
|
||||
p->uid_mapN = 0;
|
||||
|
||||
for(unsigned line=0; line<line_count; ++line)
|
||||
if( line > 0 ) // skip column title line
|
||||
{
|
||||
|
||||
if((rc = getLineAuto( fH, &lineBufPtr, &lineBufCharCnt )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError( rc, "Line read failed on '%s' line number '%i'.",cwStringNullGuard(fn),line+1);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
e = mem::allocZ<event_t>();
|
||||
|
||||
if((rc = _parse_csv_line( p, e, lineBufPtr, lineBufCharCnt )) != kOkRC )
|
||||
{
|
||||
mem::release(e);
|
||||
rc = cwLogError( rc, "Line parse failed on '%s' line number '%i'.",cwStringNullGuard(fn),line+1);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// assign the UID
|
||||
e->uid = line;
|
||||
|
||||
// link the event into the event list
|
||||
if( p->end != nullptr )
|
||||
p->end->link = e;
|
||||
else
|
||||
p->base = e;
|
||||
|
||||
p->end = e;
|
||||
|
||||
// track the max 'loc' id
|
||||
if( e->loc > p->maxLocId )
|
||||
p->maxLocId = e->loc;
|
||||
|
||||
if( p->min_uid == kInvalidId || e->uid < p->min_uid )
|
||||
p->min_uid = e->uid;
|
||||
|
||||
p->uid_mapN += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
errLabel:
|
||||
mem::release(lineBufPtr);
|
||||
file::close(fH);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _parse_event_list( score_t* p, const object_t* cfg )
|
||||
{
|
||||
rc_t rc;
|
||||
@ -136,11 +334,13 @@ cw::rc_t cw::score::create( handle_t& hRef, const char* fn )
|
||||
{
|
||||
rc_t rc;
|
||||
object_t* cfg = nullptr;
|
||||
score_t* p = nullptr;
|
||||
|
||||
if((rc = destroy(hRef)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
// parse the cfg file
|
||||
/*
|
||||
if((rc = objectFromFile( fn, cfg )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Score parse failed on file: '%s'.", fn);
|
||||
@ -148,11 +348,23 @@ cw::rc_t cw::score::create( handle_t& hRef, const char* fn )
|
||||
}
|
||||
|
||||
rc = create(hRef,cfg);
|
||||
*/
|
||||
|
||||
p = mem::allocZ< score_t >();
|
||||
|
||||
rc = _parse_csv(p,fn);
|
||||
|
||||
errLabel:
|
||||
|
||||
|
||||
hRef.set(p);
|
||||
|
||||
//errLabel:
|
||||
|
||||
if( cfg != nullptr )
|
||||
cfg->free();
|
||||
|
||||
if( rc != kOkRC )
|
||||
destroy(hRef);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -255,6 +467,13 @@ bool cw::score::is_loc_valid( handle_t h, unsigned locId )
|
||||
return locId < p->maxLocId;
|
||||
}
|
||||
|
||||
const cw::score::event_t* cw::score::uid_to_event( handle_t h, unsigned uid )
|
||||
{
|
||||
|
||||
score_t* p = _handleToPtr(h);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::score::test( const object_t* cfg )
|
||||
{
|
||||
|
@ -40,6 +40,8 @@ namespace cw
|
||||
unsigned loc_count( handle_t h );
|
||||
bool is_loc_valid( handle_t h, unsigned locId );
|
||||
|
||||
const event_t* uid_to_event( handle_t h, unsigned uid );
|
||||
|
||||
// Format the event as a string for printing.
|
||||
rc_t event_to_string( handle_t h, unsigned uid, char* buf, unsigned buf_byte_cnt );
|
||||
|
||||
|
124
cwPresetSel.cpp
124
cwPresetSel.cpp
@ -31,6 +31,11 @@ namespace cw
|
||||
unsigned next_frag_id;
|
||||
|
||||
frag_t* last_ts_frag;
|
||||
|
||||
double master_wet_in_gain;
|
||||
double master_wet_out_gain;
|
||||
double master_dry_gain;
|
||||
double master_sync_delay_ms;
|
||||
|
||||
} preset_sel_t;
|
||||
|
||||
@ -205,6 +210,9 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
bool _is_master_var_id( unsigned varId )
|
||||
{ return varId > kBaseMasterVarId; }
|
||||
|
||||
template< typename T >
|
||||
rc_t _set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, const T& value )
|
||||
{
|
||||
@ -212,9 +220,10 @@ namespace cw
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f = nullptr;
|
||||
|
||||
// locate the requested fragment
|
||||
if((rc = _find_frag(p,fragId,f)) != kOkRC )
|
||||
goto errLabel;
|
||||
// if this is not a 'master' variable then locate the requested fragment
|
||||
if( !_is_master_var_id(varId) )
|
||||
if((rc = _find_frag(p,fragId,f)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
switch( varId )
|
||||
{
|
||||
@ -259,6 +268,22 @@ namespace cw
|
||||
|
||||
case kPlayBtnVarId:
|
||||
break;
|
||||
|
||||
case kMasterWetInGainVarId:
|
||||
p->master_wet_in_gain = value;
|
||||
break;
|
||||
|
||||
case kMasterWetOutGainVarId:
|
||||
p->master_wet_out_gain = value;
|
||||
break;
|
||||
|
||||
case kMasterDryGainVarId:
|
||||
p->master_dry_gain = value;
|
||||
break;
|
||||
|
||||
case kMasterSyncDelayMsVarId:
|
||||
p->master_sync_delay_ms = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId);
|
||||
@ -280,9 +305,10 @@ namespace cw
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f = nullptr;
|
||||
|
||||
// locate the requested fragment
|
||||
if((rc = _find_frag(p,fragId,f)) != kOkRC )
|
||||
goto errLabel;
|
||||
// if this is not a 'master' variable then locate the requested fragment
|
||||
if( !_is_master_var_id( varId ) )
|
||||
if((rc = _find_frag(p,fragId,f)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
switch( varId )
|
||||
{
|
||||
@ -335,6 +361,22 @@ namespace cw
|
||||
case kPlayBtnVarId:
|
||||
break;
|
||||
|
||||
case kMasterWetInGainVarId:
|
||||
valueRef = p->master_wet_in_gain;
|
||||
break;
|
||||
|
||||
case kMasterWetOutGainVarId:
|
||||
valueRef = p->master_wet_out_gain;
|
||||
break;
|
||||
|
||||
case kMasterDryGainVarId:
|
||||
valueRef = p->master_dry_gain;
|
||||
break;
|
||||
|
||||
case kMasterSyncDelayMsVarId:
|
||||
valueRef = p->master_sync_delay_ms;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId);
|
||||
goto errLabel;
|
||||
@ -365,11 +407,15 @@ cw::rc_t cw::preset_sel::create( handle_t& hRef, const object_t* cfg )
|
||||
p = mem::allocZ<preset_sel_t>();
|
||||
|
||||
// parse the cfg
|
||||
if((rc = cfg->getv( "preset_labelL", labelL,
|
||||
"default_gain", p->defaultGain,
|
||||
"default_wet_dry_gain", p->defaultWetDryGain,
|
||||
"default_fade_ms", p->defaultFadeOutMs,
|
||||
"default_preset", default_preset_label)) != kOkRC )
|
||||
if((rc = cfg->getv( "preset_labelL", labelL,
|
||||
"default_gain", p->defaultGain,
|
||||
"default_wet_dry_gain", p->defaultWetDryGain,
|
||||
"default_fade_ms", p->defaultFadeOutMs,
|
||||
"default_preset", default_preset_label,
|
||||
"default_master_wet_in_gain", p->master_wet_in_gain,
|
||||
"default_master_wet_out_gain", p->master_wet_out_gain,
|
||||
"default_master_dry_gain", p->master_dry_gain,
|
||||
"default_master_sync_delay_ms", p->master_sync_delay_ms)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The preset configuration parse failed.");
|
||||
goto errLabel;
|
||||
@ -591,29 +637,27 @@ cw::rc_t cw::preset_sel::create_fragment( handle_t h, unsigned end_loc, time::sp
|
||||
cw::rc_t cw::preset_sel::delete_fragment( handle_t h, unsigned fragId )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f0 = nullptr;
|
||||
frag_t* f1 = p->fragL;
|
||||
frag_t* f = p->fragL;
|
||||
|
||||
for(; f1!=nullptr; f1=f1->link)
|
||||
{
|
||||
if( f1->fragId == fragId )
|
||||
for(; f!=nullptr; f=f->link)
|
||||
if( f->fragId == fragId )
|
||||
{
|
||||
if( f0 == nullptr )
|
||||
p->fragL = f1->link;
|
||||
if( f->prev == nullptr )
|
||||
p->fragL = f->link;
|
||||
else
|
||||
f0->link = f1->link;
|
||||
f->prev->link = f->link;
|
||||
|
||||
if( f->link != nullptr )
|
||||
f->link->prev = f->prev;
|
||||
|
||||
// release the fragment
|
||||
mem::release(f1->presetA);
|
||||
mem::release(f1);
|
||||
mem::release(f->presetA);
|
||||
mem::release(f);
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
f0 = f1;
|
||||
}
|
||||
|
||||
return kOkRC;
|
||||
return cwLogError(kInvalidArgRC,"The fragment '%i' could not be found to delete.",fragId);
|
||||
}
|
||||
|
||||
bool cw::preset_sel::is_fragment_loc( handle_t h, unsigned loc )
|
||||
@ -711,12 +755,24 @@ cw::rc_t cw::preset_sel::get_value( handle_t h, unsigned fragId, unsigned varId,
|
||||
return rc;
|
||||
}
|
||||
|
||||
void cw::preset_sel::track_timestamp_reset( handle_t h )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
p->last_ts_frag = nullptr;
|
||||
}
|
||||
|
||||
bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const cw::preset_sel::frag_t*& frag_Ref )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
frag_t* f = nullptr;
|
||||
bool frag_changed_fl = false;
|
||||
|
||||
time::spec_t t0;
|
||||
time::setZero(t0);
|
||||
unsigned elapsedMs = time::elapsedMs(t0,ts);
|
||||
double mins = elapsedMs / 60000.0;
|
||||
|
||||
|
||||
// if this is the first call to 'track_timestamp()'.
|
||||
if( p->last_ts_frag == nullptr )
|
||||
f = _timestamp_to_frag(p,ts);
|
||||
@ -799,8 +855,12 @@ cw::rc_t cw::preset_sel::write( handle_t h, const char* fn )
|
||||
}
|
||||
}
|
||||
|
||||
newPairObject("fragL", fragL_obj, root);
|
||||
newPairObject("fragN", fragN, root);
|
||||
newPairObject("fragL", fragL_obj, root);
|
||||
newPairObject("fragN", fragN, root);
|
||||
newPairObject("masterWetInGain", p->master_wet_in_gain, root );
|
||||
newPairObject("masterWetOutGain", p->master_wet_out_gain, root );
|
||||
newPairObject("masterDryGain", p->master_dry_gain, root );
|
||||
newPairObject("masterSyncDelayMs", p->master_sync_delay_ms,root );
|
||||
|
||||
unsigned bytes_per_frag = 1024;
|
||||
|
||||
@ -848,8 +908,12 @@ cw::rc_t cw::preset_sel::read( handle_t h, const char* fn )
|
||||
_destroy_all_frags(p);
|
||||
|
||||
// parse the root level
|
||||
if((rc = root->getv( "fragN", fragN,
|
||||
"fragL", fragL_obj )) != kOkRC )
|
||||
if((rc = root->getv( "fragN", fragN,
|
||||
"fragL", fragL_obj,
|
||||
"masterWetInGain", p->master_wet_in_gain,
|
||||
"masterWetOutGain", p->master_wet_out_gain,
|
||||
"masterDryGain", p->master_dry_gain,
|
||||
"masterSyncDelayMs",p->master_sync_delay_ms)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Root preset select parse failed on '%s'.", cwStringNullGuard(fn));
|
||||
goto errLabel;
|
||||
@ -965,7 +1029,7 @@ cw::rc_t cw::preset_sel::report( handle_t h )
|
||||
unsigned elapsedMs = time::elapsedMs(t0,f->endTimestamp);
|
||||
double mins = elapsedMs / 60000.0;
|
||||
|
||||
cwLogInfo("%3i id:%3i end loc:%3i end min:%7.2f",i,f->fragId,f->endLoc, mins);
|
||||
cwLogInfo("%3i id:%3i end loc:%3i end min:%f",i,f->fragId,f->endLoc, mins);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -57,6 +57,13 @@ namespace cw
|
||||
kPresetSelectVarId, // select a preset to play
|
||||
kPlayEnableVarId, // include in the segment to play
|
||||
kDryFlVarId, // play this fragment dry
|
||||
|
||||
|
||||
kBaseMasterVarId, // All 'master' variables have id's greater than kBaseMasterVarId
|
||||
kMasterWetInGainVarId,
|
||||
kMasterWetOutGainVarId,
|
||||
kMasterDryGainVarId,
|
||||
kMasterSyncDelayMsVarId
|
||||
};
|
||||
|
||||
rc_t create( handle_t& hRef, const object_t* cfg );
|
||||
@ -77,8 +84,11 @@ namespace cw
|
||||
rc_t delete_fragment( handle_t h, unsigned fragId );
|
||||
|
||||
bool is_fragment_loc( handle_t h, unsigned loc );
|
||||
|
||||
|
||||
// Return the fragment id of the 'selected' fragment.
|
||||
unsigned ui_select_fragment_id( handle_t h );
|
||||
|
||||
// Set the 'select_flag' on this fragment and remove it from all others.
|
||||
void ui_select_fragment( handle_t h, unsigned fragId, bool selectFl );
|
||||
|
||||
|
||||
@ -97,6 +107,7 @@ namespace cw
|
||||
// If 'ts' is past the last defined fragment then the last fragment is returned.
|
||||
// If no fragments are defined 'frag_Ref' is set to nullptr.
|
||||
// The return value is true when the value of frag_Ref changes from the previous call.
|
||||
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 );
|
||||
|
||||
// Return the preset index marked to play on this fragment.
|
||||
|
13
cwTime.cpp
13
cwTime.cpp
@ -78,6 +78,19 @@ unsigned cw::time::elapsedMs( const spec_t& t0 )
|
||||
return elapsedMs(t0,t1);
|
||||
}
|
||||
|
||||
double cw::time::elapsedSecs( const spec_t& t0, const spec_t& t1 )
|
||||
{
|
||||
return elapsedMicros(t0,t1) / 1000000.0;
|
||||
}
|
||||
|
||||
double cw::time::elapsedSecs( const spec_t& t0 )
|
||||
{
|
||||
spec_t t1;
|
||||
get(t1);
|
||||
return elapsedSecs(t0,t1);
|
||||
}
|
||||
|
||||
|
||||
unsigned cw::time::absElapsedMicros( const spec_t& t0, const spec_t& t1 )
|
||||
{
|
||||
if( isLTE(t0,t1) )
|
||||
|
6
cwTime.h
6
cwTime.h
@ -29,7 +29,11 @@ namespace cw
|
||||
// Wrapper on elapsedMicros()
|
||||
unsigned elapsedMs( const spec_t& t0, const spec_t& t1 );
|
||||
unsigned elapsedMs( const spec_t& t0 );
|
||||
|
||||
|
||||
// Wrapper on elapsedMicros()
|
||||
double elapsedSecs( const spec_t& t0, const spec_t& t1 );
|
||||
double elapsedSecs( const spec_t& t0 );
|
||||
|
||||
// Same as elapsedMicros() but the times are not assumed to be ordered.
|
||||
// The function therefore begins by swapping t1 and t0 if t0 is after t1.
|
||||
unsigned absElapsedMicros( const spec_t& t0, const spec_t& t1 );
|
||||
|
102
cwUi.cpp
102
cwUi.cpp
@ -86,6 +86,9 @@ namespace cw
|
||||
appIdMapRecd_t* appIdMap; // map of application parent/child/js id's
|
||||
char* buf; // buf[bufN] output message formatting buffer
|
||||
unsigned bufN; //
|
||||
char* recvBuf;
|
||||
unsigned recvBufN;
|
||||
unsigned recvBufIdx;
|
||||
|
||||
unsigned* sessA; // sessA[ sessN ] array of wsSessId's
|
||||
unsigned sessN;
|
||||
@ -153,6 +156,7 @@ namespace cw
|
||||
mem::release(p->sessA);
|
||||
mem::release(p->eleA);
|
||||
mem::release(p->buf);
|
||||
mem::release(p->recvBuf);
|
||||
|
||||
mem::release(p);
|
||||
|
||||
@ -963,7 +967,7 @@ namespace cw
|
||||
if( p->sendCbFunc != nullptr )
|
||||
{
|
||||
const char* mFmt = "{ \"op\":\"%s\", \"uuId\":%i, \"value\":%s }";
|
||||
const int mbufN = 512;
|
||||
const int mbufN = 1024;
|
||||
char vbuf[vbufN];
|
||||
char mbuf[mbufN];
|
||||
|
||||
@ -1082,6 +1086,9 @@ cw::rc_t cw::ui::create(
|
||||
p->sendCbArg = sendCbArg;
|
||||
p->buf = mem::allocZ<char>(fmtBufByteN);
|
||||
p->bufN = fmtBufByteN;
|
||||
p->recvBuf = mem::allocZ<char>(fmtBufByteN);
|
||||
p->recvBufN = fmtBufByteN;
|
||||
p->recvBufIdx = 0;
|
||||
|
||||
// create the root element
|
||||
if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
|
||||
@ -1182,14 +1189,55 @@ cw::rc_t cw::ui::onDisconnect( handle_t h, unsigned wsSessId )
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* msg, unsigned msgByteN )
|
||||
cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* void_msg, unsigned msgByteN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
ui_t* p = _handleToPtr(h);
|
||||
opId_t opId = _labelToOpId((const char*)msg);
|
||||
opId_t opId = kInvalidOpId;
|
||||
value_t value;
|
||||
ele_t* ele;
|
||||
|
||||
const char* src_msg = (const char*)void_msg;
|
||||
const char* msg = src_msg;
|
||||
|
||||
// if the incoming message is valid
|
||||
if( msgByteN > 0 and src_msg != nullptr )
|
||||
{
|
||||
// if there is a partial msg in the recv buffer (recvBufIdx!=0)
|
||||
// or the incoming message is a partial mesg - then buffer the message
|
||||
// (Note: incoming messages that are not zero terminated are partial.")
|
||||
if( p->recvBufIdx != 0 || src_msg[msgByteN-1] != 0 )
|
||||
{
|
||||
// verify the buffer is large enough to hold the msg
|
||||
if(p->recvBufIdx + msgByteN > p->recvBufN )
|
||||
{
|
||||
rc = cwLogError(kOpFailRC,"The UI input buffer (%i) is too small.", p->recvBufN);
|
||||
p->recvBufIdx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// update it with the incoming text
|
||||
strncpy( p->recvBuf + p->recvBufIdx, src_msg, msgByteN );
|
||||
p->recvBufIdx += msgByteN;
|
||||
msg = p->recvBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// if the incoming message is not zero terminated then it was a partial
|
||||
// message it was buffered and there is nothing else to do.
|
||||
if( src_msg[msgByteN-1] != 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// the message is being processed so the buffer will end up empty
|
||||
// (if it was being used)
|
||||
p->recvBufIdx = 0;
|
||||
|
||||
// parse the 'opId' from the message
|
||||
opId = _labelToOpId(msg);
|
||||
|
||||
|
||||
switch( opId )
|
||||
{
|
||||
case kInitOpId:
|
||||
@ -1517,12 +1565,13 @@ cw::rc_t cw::ui::setLogLine( handle_t h, unsigned uuId, const char* text )
|
||||
rc = sendValueString(h,uuId,text);
|
||||
else
|
||||
{
|
||||
int sn = textLength(text);
|
||||
unsigned sn = textLength(text);
|
||||
sn += n + 1;
|
||||
|
||||
char s[ sn ];
|
||||
|
||||
// alloc. a lot of extra space to cover the space need for the '\' escape character
|
||||
char s[ sn*2 ];
|
||||
unsigned i,j;
|
||||
for( i=0,j=0; text[i]; ++i,++j)
|
||||
for( i=0,j=0; text[i] && j<sn; ++i,++j)
|
||||
{
|
||||
char ch = text[i];
|
||||
bool escape_fl = true;
|
||||
@ -1542,12 +1591,15 @@ cw::rc_t cw::ui::setLogLine( handle_t h, unsigned uuId, const char* text )
|
||||
|
||||
if( escape_fl )
|
||||
s[j++] = '\\';
|
||||
|
||||
s[j] = ch;
|
||||
|
||||
if( j < sn )
|
||||
s[j] = ch;
|
||||
}
|
||||
|
||||
s[sn-1] = 0;
|
||||
|
||||
|
||||
|
||||
//printf("%s %s\n",text,s);
|
||||
|
||||
rc = sendValueString(h,uuId,s);
|
||||
@ -1781,11 +1833,12 @@ void cw::ui::report( handle_t h )
|
||||
if(p->eleA[i] != nullptr )
|
||||
{
|
||||
const ele_t* e = p->eleA[i];
|
||||
|
||||
|
||||
unsigned parUuId = e->phys_parent==NULL ? kInvalidId : e->phys_parent->uuId;
|
||||
const char* parEleName = e->phys_parent==NULL || e->phys_parent->eleName == NULL ? "" : e->phys_parent->eleName;
|
||||
|
||||
printf("uu:%5i app:%5i %20s : parent uu:%5i app:%5i %20s ", e->uuId, e->appId, e->eleName == NULL ? "" : e->eleName, parUuId, e->logical_parent->appId, parEleName );
|
||||
unsigned parUuId = e->phys_parent==NULL ? kInvalidId : e->phys_parent->uuId;
|
||||
const char* parEleName = e->phys_parent==NULL || e->phys_parent->eleName == NULL ? "" : e->phys_parent->eleName;
|
||||
unsigned logParentAppId = e->logical_parent==NULL ? kInvalidId : e->logical_parent->appId;
|
||||
printf("uu:%5i app:%5i chan:%5i %20s : parent uu:%5i app:%5i %20s ", e->uuId, e->appId, e->chanId, e->eleName == NULL ? "" : e->eleName, parUuId, logParentAppId, parEleName );
|
||||
|
||||
for(unsigned i=0; i<e->attr->child_count(); ++i)
|
||||
{
|
||||
@ -1869,9 +1922,10 @@ namespace cw
|
||||
|
||||
cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* object_label )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const object_t* op = &o;
|
||||
char* uiCfgFn = nullptr;
|
||||
rc_t rc = kOkRC;
|
||||
const object_t* op = &o;
|
||||
char* uiCfgFn = nullptr;
|
||||
char* physRootDir = nullptr;
|
||||
|
||||
memset(&args,0,sizeof(args));
|
||||
|
||||
@ -1889,17 +1943,27 @@ cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* ob
|
||||
rc = cwLogError(rc,"'ui' cfg. parse failed.");
|
||||
}
|
||||
|
||||
// expand the physical root directory
|
||||
if((physRootDir = filesys::expandPath( args.physRootDir)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The physical root directory of the UI cfg. is invalid.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if a default UI resource script was given then convert it into an object
|
||||
if( uiCfgFn != nullptr )
|
||||
{
|
||||
char* fn = filesys::makeFn( args.physRootDir, uiCfgFn, nullptr, nullptr );
|
||||
char* fn = filesys::makeFn( physRootDir, uiCfgFn, nullptr, nullptr );
|
||||
|
||||
if((rc = objectFromFile(fn,args.uiRsrc)) != kOkRC )
|
||||
rc = cwLogError(rc,"An error occurred while parsing the UI resource script in '%s'.", cwStringNullGuard(uiCfgFn));
|
||||
|
||||
mem::release(fn);
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
mem::release(physRootDir);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2024,7 +2088,7 @@ cw::rc_t cw::ui::ws::exec( handle_t h )
|
||||
cwLogError(rc,"The UI websock execution failed.");
|
||||
|
||||
// make the idle callback
|
||||
ui::onReceive( p->uiH, kInvalidId, "idle", strlen("idle") );
|
||||
ui::onReceive( p->uiH, kInvalidId, "idle", strlen("idle")+1 );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
7
cwUi.h
7
cwUi.h
@ -41,7 +41,12 @@ namespace cw
|
||||
// A UI was disconnected
|
||||
rc_t onDisconnect( handle_t h, unsigned wsSessId );
|
||||
|
||||
// Receive a msg from a remote UI
|
||||
// Receive a msg from a remote UI.
|
||||
//
|
||||
// Note that individual messages are delinated with zero termination.
|
||||
// Therefore a message which is not zero terminated is considered
|
||||
// a partial message and will be buffered until the suffix of the message
|
||||
// arrives.
|
||||
rc_t onReceive( handle_t h, unsigned wsSessId, const void* msg, unsigned byteN );
|
||||
|
||||
// Locate an element whose parent uuid is 'parentUuId' with a child named 'eleName'.
|
||||
|
20
cwVectOps.h
20
cwVectOps.h
@ -54,7 +54,7 @@ namespace cw
|
||||
void copy( T0* v0, const T1* v1, unsigned n )
|
||||
{
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
v0[i] = v1[i];
|
||||
v0[i] = (T0)v1[i];
|
||||
}
|
||||
|
||||
template< typename T0, typename T1 >
|
||||
@ -151,7 +151,7 @@ namespace cw
|
||||
void mul( T0* v0, const T1* v1, unsigned n )
|
||||
{
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
v0[i] *= v1[i];
|
||||
v0[i] = v0[i] * (T1)v1[i];
|
||||
}
|
||||
|
||||
template< typename T0, typename T1 >
|
||||
@ -169,8 +169,8 @@ namespace cw
|
||||
v0[i] *= scalar;
|
||||
}
|
||||
|
||||
template< typename T0, typename T1 >
|
||||
void mul( T0* y0, const T0* v0, const T1& scalar, unsigned n )
|
||||
template< typename T0, typename T1, typename T2 >
|
||||
void mul( T0* y0, const T1* v0, const T2& scalar, unsigned n )
|
||||
{
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
y0[i] = v0[i] * scalar;
|
||||
@ -363,15 +363,15 @@ namespace cw
|
||||
return init_idx;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
T* ampl_to_db( T* dbp, const T* sbp, unsigned dn, T minDb=-1000 )
|
||||
template< typename T0, typename T1 >
|
||||
T0* ampl_to_db( T0* dbp, const T1* sbp, unsigned dn, T0 minDb=-1000 )
|
||||
{
|
||||
T minVal = pow(10.0,minDb/20.0);
|
||||
T* dp = dbp;
|
||||
T* ep = dp + dn;
|
||||
T0 minVal = pow(10.0,minDb/20.0);
|
||||
T0* dp = dbp;
|
||||
T0* ep = dp + dn;
|
||||
|
||||
for(; dp<ep; ++dp,++sbp)
|
||||
*dp = *sbp<minVal ? minDb : 20.0 * log10(*sbp);
|
||||
*dp = (T0)(*sbp<minVal ? minDb : 20.0 * log10(*sbp));
|
||||
return dbp;
|
||||
|
||||
}
|
||||
|
@ -1226,7 +1226,7 @@ function ws_on_msg( jsonMsg )
|
||||
function ws_on_open()
|
||||
{
|
||||
set_app_title( "Connected", "title_connected" );
|
||||
_ws.send("init")
|
||||
ws_send("init")
|
||||
}
|
||||
|
||||
function ws_on_close()
|
||||
|
@ -10,11 +10,24 @@
|
||||
row: {
|
||||
button:{ name: quitBtnId, title:"Quit" },
|
||||
button:{ name: ioReportBtnId, title:"IO Report" },
|
||||
button:{ name: netPrintBtnId, title:"Print Network" }
|
||||
button:{ name: reportBtnId, title:"App Report" },
|
||||
button:{ name: loadBtnId, title:"Load" },
|
||||
button:{ name: saveBtnId, title:"Save" },
|
||||
},
|
||||
|
||||
row: {
|
||||
check: { name: printMidiCheckId, title: "Print MIDI" },
|
||||
check: { name: pianoMidiCheckId, title: "Piano MIDI" },
|
||||
check: { name: samplerMidiCheckId, title: "Sampler MIDI" },
|
||||
number: { name: syncDelayMsId, title: "Delay (ms)", min:0, max:1000, step:1, decpl:0 },
|
||||
},
|
||||
|
||||
row: {
|
||||
number: { name: wetInGainId, title:"Wet In Gain", min:0, max:100.0, step:0.01, decpl:3 },
|
||||
number: { name: wetOutGainId, title:"Wet Out Gain", min:0, max:100.0, step:0.01, decpl:3 },
|
||||
number: { name: dryGainId, title:"Dry Gain", min:0, max:100.0, step:0.01, decpl:3 },
|
||||
},
|
||||
|
||||
row: {
|
||||
button:{ name: startBtnId, title:"Start" },
|
||||
@ -25,8 +38,8 @@
|
||||
|
||||
row: {
|
||||
check:{ name: midiThruCheckId, title:"MIDI Thru" },
|
||||
numb_disp: { name: curMidiEvtCntId, title:"Current:" },
|
||||
numb_disp: { name: totalMidiEvtCntId, title:"Total:" },
|
||||
numb_disp: { name: curMidiEvtCntId, title:"Current Loc:" },
|
||||
numb_disp: { name: totalMidiEvtCntId, title:"Max Loc:" },
|
||||
},
|
||||
|
||||
|
||||
@ -36,6 +49,15 @@
|
||||
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 },
|
||||
number:{ name: halfPedalPitchId, title:"Pitch:", min:0, max:127, step:1, decpl:0 },
|
||||
number:{ name: halfPedalVelId, title:"Vel:", min:0, max:127, step:1, decpl:0 },
|
||||
number:{ name: halfPedalDurMsId, title:"DurMs:", min:0, max:5000, step:1, decpl:0 },
|
||||
number:{ name: halfPedalDnDelayMsId, title:"DownMs:", min:0, max:5000, step:1, decpl:0 },
|
||||
},
|
||||
|
||||
row: {
|
||||
str_disp:{ name: statusId, title:"Status:", value: "" },
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user