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

This commit is contained in:
kevin 2022-05-15 10:37:51 -04:00
commit f75510f624
30 changed files with 1855 additions and 670 deletions

View File

@ -2,6 +2,15 @@
# To Do # 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: - Should a warning be issued by audioBuf functions which return a set of values:
muteFlags(),toneFlags(), gain( ... gainA) but where the size of the dest array muteFlags(),toneFlags(), gain( ... gainA) but where the size of the dest array
does not match the actual number of channesl? does not match the actual number of channesl?

View File

@ -605,7 +605,11 @@ namespace cw
int* dp = (int*)obuf; int* dp = (int*)obuf;
while( sp < ep ) 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; //*dp++ = (rand() - (RAND_MAX/2)) * 2;
} }

View File

@ -785,16 +785,16 @@ namespace cw
} }
template< typename T0, typename T1 > 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; unsigned i = 0;
T1 minDb = -100.0; double minDb = -100.0;
thresh = -fabs(thresh); thresh = -fabs(thresh);
for(i=0; i<binCnt; ++i) for(i=0; i<binCnt; ++i)
{ {
T1 y; double y;
if( x[i] < minDb ) if( x[i] < minDb )
x[i] = minDb; x[i] = minDb;
@ -813,7 +813,7 @@ namespace cw
} }
template< typename T0, typename T1 > 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; unsigned i=0;
@ -823,8 +823,8 @@ namespace cw
for(i=0; i<binCnt; ++i) for(i=0; i<binCnt; ++i)
{ {
T0 a = fabs(X1m[i]); double a = fabs(X1m[i]);
T0 d = a - thresh; double d = a - thresh;
X1m[i] = -thresh; X1m[i] = -thresh;
@ -841,11 +841,11 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
T0 X0m[binN]; double X0m[binN];
T0 X1m[binN]; double X1m[binN];
// take the mean of the the input magntitude spectrum // 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) // convert magnitude to db (range=-1000.0 to 0.0)
vop::ampl_to_db(X0m, magV, binN ); vop::ampl_to_db(X0m, magV, binN );
@ -867,10 +867,10 @@ namespace cw
vop::db_to_ampl(X1m, X1m, binN ); vop::db_to_ampl(X1m, X1m, binN );
// convert the mean input magnitude to db // convert the mean input magnitude to db
T0 idb = 20*log10(u0); double idb = 20*log10(u0);
// get the mean output magnitude spectra // get the mean output magnitude spectra
T0 u1 = vop::mean(X1m,binN); double u1 = vop::mean(X1m,binN);
if( idb > -150.0 ) if( idb > -150.0 )
{ {
@ -890,7 +890,6 @@ namespace cw
return rc; return rc;
} }
} }

View File

@ -63,6 +63,9 @@ bool cw::filesys::isDir( const char* dirStr )
errno = 0; errno = 0;
if( dirStr == nullptr )
return false;
if( stat(dirStr,&s) != 0 ) if( stat(dirStr,&s) != 0 )
{ {
// if the dir does not exist // if the dir does not exist
@ -81,6 +84,9 @@ bool cw::filesys::isFile( const char* fnStr )
struct stat s; struct stat s;
errno = 0; errno = 0;
if( fnStr == nullptr )
return false;
if( stat(fnStr,&s) != 0 ) if( stat(fnStr,&s) != 0 )
{ {
@ -101,6 +107,9 @@ bool cw::filesys::isLink( const char* fnStr )
struct stat s; struct stat s;
errno = 0; errno = 0;
if( fnStr == nullptr )
return false;
if( lstat(fnStr,&s) != 0 ) if( lstat(fnStr,&s) != 0 )
{ {
// if the file does not exist // if the file does not exist
@ -314,6 +323,9 @@ char* cw::filesys::expandPath( const char* dir )
memset(&res,0,sizeof(res)); memset(&res,0,sizeof(res));
if( dir == nullptr )
return nullptr;
if((sysRC = wordexp(dir,&res,flags)) != 0) if((sysRC = wordexp(dir,&res,flags)) != 0)
{ {
switch(sysRC) switch(sysRC)

View File

@ -368,16 +368,18 @@ namespace cw
// since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel // since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel
assert( base_src_var->chIdx == kAnyChIdx ); 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) 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; variable_t* svar = base_src_var;
for(; svar!=nullptr; svar=svar->ch_link) for(; svar!=nullptr; svar=svar->ch_link)
if( svar->chIdx == in_var->chIdx ) if( svar->chIdx == in_var->chIdx )
break; break;
// connect the src->input var
_connect_vars( svar==nullptr ? base_src_var : svar, in_var); _connect_vars( svar==nullptr ? base_src_var : svar, in_var);
} }
} }

View File

@ -40,6 +40,7 @@ namespace cw
unsigned fadeSmpN; // unsigned fadeSmpN; //
unsigned net_idx; unsigned net_idx;
} flow_network_t; } flow_network_t;
@ -54,6 +55,7 @@ namespace cw
flow::external_device_t* deviceA; flow::external_device_t* deviceA;
unsigned deviceN; unsigned deviceN;
bool fadeInputFl;
} flow_cross_t; } flow_cross_t;
flow_cross_t* _handleToPtr(handle_t h) 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* src = p->deviceA[devIdx].u.a.abuf;
flow::abuf_t* dst = net->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 ) void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
@ -253,11 +255,20 @@ namespace cw
template< typename T > 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 _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); flow_cross_t* p = _handleToPtr(h);
unsigned flow_idx = _get_flow_index(p, destId ); unsigned flow_idx = destId == kAllDestId ? 0 : _get_flow_index(p, destId );
return set_variable_value( p->netA[ flow_idx ].flowH, inst_label, var_label, chIdx, value ); 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; rc_t rc = kOkRC;
flow_cross_t* p = _handleToPtr(h); 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) for(unsigned i=0; i<p->netN; ++i)
{ {
flow_network_t* net = p->netA + 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) // zero the audio device output buffers because we are about to sum into them
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) for(unsigned j=0; j<p->deviceN; ++j)
_update_audio_input( p, p->netA + i, j ); if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )
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 ); _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) // sum the output from the network into the audio output device buffer
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) ) // (this is were newly active networks are faded in)
_update_audio_output( p, net, j ); 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; return rc;
@ -426,6 +442,9 @@ void cw::flow_cross::print( handle_t h )
{ {
flow_cross_t* p = _handleToPtr(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("flow_cross: sr:%7.1f\n", p->srate );
printf("master devices:\n"); printf("master devices:\n");
@ -434,7 +453,21 @@ void cw::flow_cross::print( handle_t h )
for(unsigned i=0; i<p->netN; ++i) 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 ); 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 );
}

View File

@ -12,6 +12,7 @@ namespace cw
{ {
kCurDestId, // Apply value to the current flow network 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) 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; } destId_t;
rc_t create( handle_t& hRef, rc_t create( handle_t& hRef,
@ -38,6 +39,7 @@ namespace cw
rc_t begin_cross_fade( handle_t h, unsigned crossFadeMs ); rc_t begin_cross_fade( handle_t h, unsigned crossFadeMs );
void print( handle_t h ); void print( handle_t h );
void print_network( handle_t h, flow_cross::destId_t destId );
} }
} }

View File

@ -98,7 +98,7 @@ namespace cw
typedef struct typedef struct
{ {
real_t value;
} inst_t; } inst_t;
@ -106,6 +106,7 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
real_t in_value = 0.5; 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 ) if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", in_value )) != kOkRC )
goto errLabel; goto errLabel;
@ -122,20 +123,33 @@ namespace cw
} }
rc_t destroy( instance_t* ctx ) rc_t destroy( instance_t* ctx )
{ return kOkRC; } {
mem::release( ctx->userPtr );
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var ) rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; } {
return kOkRC;
}
rc_t exec( instance_t* ctx ) rc_t exec( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
inst_t* inst = (inst_t*)(ctx->userPtr);
real_t value = 1; real_t value = 1;
var_get(ctx, kInPId, kAnyChIdx, value); var_get(ctx, kInPId, kAnyChIdx, value);
var_set(ctx, kOutPId, kAnyChIdx, value); var_set(ctx, kOutPId, kAnyChIdx, value);
var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) ); var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) );
if( inst->value != value )
{
inst->value = value;
}
return rc; return rc;
} }
@ -228,7 +242,9 @@ namespace cw
} }
else 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; 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); rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label);
else else
{ {
unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, src_abuf->chN);
unsigned n = src_abuf->frameN*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) for(unsigned i=0; i<n; ++i)
inst->ext_dev->u.a.abuf->buf[i] += src_abuf->buf[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 ); 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 ); rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle );
errLabel: errLabel:
@ -491,7 +508,7 @@ namespace cw
goto errLabel; 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 ) 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)); rc = cwLogError(kOpFailRC,"The audio file create failed on '%s'.",cwStringNullGuard(inst->filename));
@ -577,10 +594,18 @@ namespace cw
kOutPId kOutPId
}; };
typedef struct inst_str
{
unsigned n;
real_t vgain;
real_t gain;
} inst_t;
rc_t create( instance_t* ctx ) rc_t create( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; // const abuf_t* abuf = nullptr; //
ctx->userPtr = mem::allocZ<inst_t>();
// get the source audio buffer // get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC )
@ -600,16 +625,32 @@ namespace cw
} }
rc_t destroy( instance_t* ctx ) rc_t destroy( instance_t* ctx )
{ return kOkRC; } {
inst_t* inst = (inst_t*)(ctx->userPtr);
mem::release(inst);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var ) rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; } {
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 exec( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr; const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr; abuf_t* obuf = nullptr;
inst_t* inst = (inst_t*)(ctx->userPtr);
// get the src buffer // get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
@ -631,6 +672,13 @@ namespace cw
// apply the gain // apply the gain
for(unsigned j=0; j<ibuf->frameN; ++j) for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = gain * isig[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: errLabel:
@ -684,13 +732,19 @@ namespace cw
if( abuf->chN ) if( abuf->chN )
{ {
unsigned selChN = 0;
inst->chSelMap = mem::allocZ<bool>(abuf->chN); inst->chSelMap = mem::allocZ<bool>(abuf->chN);
if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC )
goto errLabel;
// register the gain // register the gain
for(unsigned i=0; i<abuf->chN; ++i) for(unsigned i=0; i<abuf->chN; ++i)
{ {
if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC ) if( i < selChN )
goto errLabel; if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC )
goto errLabel;
if( inst->chSelMap[i] ) if( inst->chSelMap[i] )
{ {
@ -1659,17 +1713,23 @@ namespace cw
if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN ) if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN )
{ {
double val = 0;
spec_dist_t* sd = inst->sdA[ var->chIdx ];
switch( var->vid ) switch( var->vid )
{ {
case kCeilingPId: var_get( var, inst->sdA[ var->chIdx ]->ceiling ); break; case kCeilingPId: rc = var_get( var, val ); sd->ceiling = val; break;
case kExpoPId: var_get( var, inst->sdA[ var->chIdx ]->expo ); break; case kExpoPId: rc = var_get( var, val ); sd->expo = val; break;
case kThreshPId: var_get( var, inst->sdA[ var->chIdx ]->thresh ); break; case kThreshPId: rc = var_get( var, val ); sd->thresh = val; break;
case kUprSlopePId: var_get( var, inst->sdA[ var->chIdx ]->uprSlope ); break; case kUprSlopePId: rc = var_get( var, val ); sd->uprSlope = val; break;
case kLwrSlopePId: var_get( var, inst->sdA[ var->chIdx ]->lwrSlope ); break; case kLwrSlopePId: rc = var_get( var, val ); sd->lwrSlope = val; break;
case kMixPId: var_get( var, inst->sdA[ var->chIdx ]->mix ); break; case kMixPId: rc = var_get( var, val ); sd->mix = val; break;
default: default:
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); 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; return rc;
@ -1833,20 +1893,24 @@ namespace cw
if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN ) if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN )
{ {
compressor_t* c = inst->cmpA[ var->chIdx ];
switch( var->vid ) switch( var->vid )
{ {
case kBypassPId: var_get( var, inst->cmpA[ var->chIdx ]->bypassFl ); break; case kBypassPId: rc = var_get( var, tmp ); c->bypassFl=tmp; break;
case kInGainPId: var_get( var, inst->cmpA[ var->chIdx ]->inGain ); break; case kInGainPId: rc = var_get( var, tmp ); c->inGain=tmp; break;
case kOutGainPId: var_get( var, inst->cmpA[ var->chIdx ]->outGain ); break; case kOutGainPId: rc = var_get( var, tmp ); c->outGain=tmp; break;
case kRatioPId: var_get( var, inst->cmpA[ var->chIdx ]->ratio_num ); break; case kRatioPId: rc = var_get( var, tmp ); c->ratio_num=tmp; break;
case kThreshPId: var_get( var, inst->cmpA[ var->chIdx ]->threshDb ); break; case kThreshPId: rc = var_get( var, tmp ); c->threshDb=tmp; break;
case kAtkMsPId: var_get( var, tmp ); set_attack_ms(inst->cmpA[ var->chIdx ], tmp ); break; case kAtkMsPId: rc = var_get( var, tmp ); dsp::compressor::set_attack_ms(c, tmp ); break;
case kRlsMsPId: var_get( var, tmp ); set_release_ms(inst->cmpA[ var->chIdx ], tmp ); break; case kRlsMsPId: rc = var_get( var, tmp ); dsp::compressor::set_release_ms(c, tmp ); break;
case kWndMsPId: var_get( var, tmp ); set_rms_wnd_ms(inst->cmpA[ var->chIdx ], tmp ); break; case kWndMsPId: rc = var_get( var, tmp ); dsp::compressor::set_rms_wnd_ms(c, tmp ); break;
case kMaxWndMsPId: break;
default: default:
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); 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; 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->maxDelayFrameN = std::max(inst->maxDelayFrameN, (unsigned)(fabs(maxDelayMs) * abuf->srate / 1000.0) );
inst->cntV[i] = (unsigned)(fabs(delayMs) * 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 exec( instance_t* ctx )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* ibuf = nullptr; const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr; abuf_t* obuf = nullptr;
inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* dbuf = inst->delayBuf;
// get the src buffer // get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) 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* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN;
sample_t* dsig = dbuf->buf + i*dbuf->frameN;
unsigned di = inst->idxV[i]; unsigned di = inst->idxV[i];
// if the delay is set to zero samples // if the delay is set to zero samples
@ -2018,8 +2090,8 @@ namespace cw
// otherwise the delay is non-zero positive sample count // otherwise the delay is non-zero positive sample count
for(unsigned j=0; j<ibuf->frameN; ++j) for(unsigned j=0; j<ibuf->frameN; ++j)
{ {
osig[j] = inst->delayBuf->buf[di]; // read delay output osig[j] = dsig[di]; // read delay output
inst->delayBuf->buf[di] = isig[j]; // set delay input dsig[di] = isig[j]; // set delay input
di = (di+1) % inst->cntV[i]; // update the delay index di = (di+1) % inst->cntV[i]; // update the delay index
} }
} }

View File

@ -339,7 +339,19 @@ namespace cw
valRef = non_const_val; valRef = non_const_val;
return rc; 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 ) 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 ) rc_t _validate_var_assignment( variable_t* var, unsigned typeFl )
{ {
if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) ) if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) )
@ -447,73 +426,73 @@ namespace cw
} }
template< typename T > 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."); cwLogError(kAssertFailRC,"Unimplemented variable setter.");
assert(0); assert(0);
} }
template<> 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[ local_value_idx ].u.b = val;
var->local_value.flags = kBoolTFl; var->local_value[ local_value_idx ].flags = kBoolTFl;
cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.u = val;
var->local_value.flags = kUIntTFl; var->local_value[ local_value_idx ].flags = kUIntTFl;
cwLogMod("%s.%s ch:%i %i (uint).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %i (uint).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.i = val;
var->local_value.flags = kIntTFl; var->local_value[ local_value_idx ].flags = kIntTFl;
cwLogMod("%s.%s ch:%i %i (int).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %i (int).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.f = val;
var->local_value.flags = kFloatTFl; var->local_value[ local_value_idx ].flags = kFloatTFl;
cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.d = val;
var->local_value.flags = kDoubleTFl; var->local_value[ local_value_idx ].flags = kDoubleTFl;
cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.s = mem::duplStr(val);
var->local_value.flags = kStringTFl; var->local_value[ local_value_idx ].flags = kStringTFl;
cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,val); cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,val);
} }
template<> 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[ local_value_idx ].u.abuf = val;
var->local_value.flags = kABufTFl; 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"); cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid");
} }
template<> 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[ local_value_idx ].u.fbuf = val;
var->local_value.flags = kFBufTFl; 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"); 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 _var_set_template( variable_t* var, unsigned typeFlag, T val )
{ {
rc_t rc; 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 ) 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 the instance is fully initialized ...
if( var->inst->varMapA != nullptr ) if( var->inst->varMapA != nullptr )
{ {
// ... then inform the proc. that the value changed // ... 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) // 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 ) 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 // send the value to connected downstream proc's
rc = _var_broadcast_new_value( var ); rc = _var_broadcast_new_value( var );
} }
else
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 )
{ {
// cancel the assignment and restore the original value
_value_release(&var->local_value); var->value = original_value;
var->local_value.u.b = val; var->local_value_idx = original_value_idx;
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 );
} }
errLabel:
return rc; 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 ) 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 > template< typename T >
@ -950,13 +815,12 @@ namespace cw
void _var_print( const variable_t* var ) 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" : " "; 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 ); printf(" %20s id:%4i ch:%3i : %s : ", var->label, var->vid, var->chIdx, conn_label );
if( var->value == nullptr ) if( var->value == nullptr )
_value_print( &var->local_value ); _value_print( &var->local_value[0] );
else else
_value_print( var->value ); _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; return a;
} }
void cw::flow::abuf_destroy( abuf_t* abuf ) void cw::flow::abuf_destroy( abuf_t*& abuf )
{ {
if( abuf == nullptr ) if( abuf == nullptr )
return; return;
@ -1077,7 +941,7 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned
return f; return f;
} }
void cw::flow::fbuf_destroy( fbuf_t* fbuf ) void cw::flow::fbuf_destroy( fbuf_t*& fbuf )
{ {
if( fbuf == nullptr ) if( fbuf == nullptr )
return; return;
@ -1210,7 +1074,8 @@ void cw::flow::_var_destroy( variable_t* var )
{ {
if( var != nullptr ) 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->label);
mem::release(var); 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 ) if( value_cfg == nullptr )
{ {
// Set the value of the new variable to the value of the 'any' channel // 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 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 ) if( base_var->local_value + base_var->local_value_idx == base_var->value )
var->value = &var->local_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; variable_t* var;
vRef = nullptr; 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; vRef = var;
return kOkRC; return kOkRC;
@ -1357,6 +1222,37 @@ cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx
return rc; 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val )
{ {

View File

@ -25,7 +25,8 @@ namespace cw
enum { enum {
kFbufVectN = 3, kFbufVectN = 3,
kAnyChIdx = kInvalidIdx kAnyChIdx = kInvalidIdx,
kLocalValueN = 2
}; };
typedef struct fbuf_str typedef struct fbuf_str
@ -162,11 +163,12 @@ namespace cw
char* label; // this variables label char* label; // this variables label
unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst' 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 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 value_t* value; // pointer to the value associated with this variable
unsigned chIdx; // channel index 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* 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* 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) struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number)
} variable_t; } variable_t;
@ -186,7 +188,7 @@ namespace cw
void* userPtr; // instance state 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 varMapChN; // max count of channels among all variables
unsigned varMapIdN; unsigned varMapIdN;
@ -224,13 +226,13 @@ namespace cw
// //
abuf_t* abuf_create( srate_t srate, unsigned chN, unsigned frameN ); 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 ); abuf_t* abuf_duplicate( const abuf_t* src );
rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN ); 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 ); 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 ); 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 ); fbuf_t* fbuf_duplicate( const fbuf_t* src );
inline bool value_is_abuf( const value_t* v ) { return v->flags & kABufTFl; } 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 ); void _var_destroy( variable_t* var );
bool var_exists( instance_t* inst, const char* label, unsigned chIdx ); bool var_exists( instance_t* inst, const char* label, unsigned chIdx );
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, const variable_t*& varRef ); rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, const variable_t*& varRef );
rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, variable_t*& varRef ); rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, variable_t*& varRef );
rc_t var_find( instance_t* inst, unsigned vid, 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, bool& valRef );
rc_t var_get( const variable_t* var, uint_t& valRef ); rc_t var_get( const variable_t* var, uint_t& valRef );

View File

@ -1553,7 +1553,7 @@ namespace cw
// UI // 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 ) 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; io_t* p = (io_t*)cbArg;
@ -1569,7 +1569,7 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const char* uiCfgLabel = "ui"; const char* uiCfgLabel = "ui";
ui::ws::args_t args; ui::ws::args_t args = {};
// Duplicate the application id map // 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; return rc;
} }
void cw::io::uiReport( handle_t h )
{
ui::handle_t uiH;
if(_handleToUiHandle(h,uiH) == kOkRC )
ui::report(uiH);
}

2
cwIo.h
View File

@ -364,7 +364,7 @@ namespace cw
rc_t uiSendValue( handle_t h, unsigned uuId, double value ); rc_t uiSendValue( handle_t h, unsigned uuId, double value );
rc_t uiSendValue( handle_t h, unsigned uuId, const char* value ); rc_t uiSendValue( handle_t h, unsigned uuId, const char* value );
void uiReport( handle_t h );
} }
} }

View File

@ -251,7 +251,7 @@ namespace cw
rc_t _audio_callback( io_flow_t* p, io::audio_msg_t& m ) rc_t _audio_callback( io_flow_t* p, io::audio_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
flow::abuf_t* abuf; flow::abuf_t* abuf = nullptr;
// if there is incoming (recorded) audio // if there is incoming (recorded) audio
if( m.iBufChCnt > 0 ) 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 ) cw::rc_t cw::io_flow::begin_cross_fade( handle_t h, unsigned crossFadeMs )
{ return flow_cross::begin_cross_fade( _handleToPtr(h)->crossFlowH, 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 ); }

View File

@ -30,6 +30,8 @@ namespace cw
rc_t begin_cross_fade( handle_t h, unsigned crossFadeMs ); 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 );
} }
} }

View File

@ -27,43 +27,94 @@ namespace cw
unsigned id; unsigned id;
time::spec_t timestamp; time::spec_t timestamp;
unsigned loc;
uint8_t ch; uint8_t ch;
uint8_t status; uint8_t status;
uint8_t d0; uint8_t d0;
uint8_t d1; uint8_t d1;
} am_midi_msg_t; } 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* midiOutDevLabel;
char* midiOutPortLabel; char* midiOutPortLabel;
unsigned midiOutDevIdx; unsigned midiOutDevIdx;
unsigned midiOutPortIdx; 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 startedFl;
bool recordFl; bool recordFl;
bool thruFl; 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 play_time;
time::spec_t start_time; time::spec_t start_time;
time::spec_t end_play_event_timestamp; time::spec_t end_play_event_timestamp;
time::spec_t all_off_timestamp;
bool pedalFl; time::spec_t store_time;
event_callback_t cb; event_callback_t cb;
void* cb_arg; void* cb_arg;
} midi_record_play_t; } midi_record_play_t;
enum enum
{ {
kMidiRecordPlayTimerId kMidiRecordPlayTimerId
@ -79,10 +130,17 @@ namespace cw
if((timerIdx = io::timerLabelToIndex( p->ioH, TIMER_LABEL )) != kInvalidIdx ) if((timerIdx = io::timerLabelToIndex( p->ioH, TIMER_LABEL )) != kInvalidIdx )
io::timerDestroy( p->ioH, timerIdx); 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->msgArray);
mem::release(p->midiOutDevLabel); mem::release(p->iMsgArray);
mem::release(p->midiOutPortLabel);
mem::release(p); mem::release(p);
return rc; return rc;
@ -91,74 +149,293 @@ namespace cw
rc_t _parseCfg(midi_record_play_t* p, const object_t& cfg ) rc_t _parseCfg(midi_record_play_t* p, const object_t& cfg )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const object_t* midiDevL = nullptr;
if((rc = cfg.getv( if((rc = cfg.getv(
"max_midi_msg_count", p->msgArrayN, "max_midi_msg_count", p->msgArrayN,
"midi_timer_period_micro_sec", p->midi_timer_period_micro_sec, "midi_timer_period_micro_sec", p->midi_timer_period_micro_sec,
"midi_out_device", p->midiOutDevLabel, "all_off_delay_ms", p->all_off_delay_ms,
"midi_out_port", p->midiOutPortLabel)) != kOkRC ) "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."); rc = cwLogError(kSyntaxErrorRC,"MIDI record play configuration parse failed.");
goto errLabel; 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 // allocate the MIDI msg buffer
p->msgArray = mem::allocZ<am_midi_msg_t>( p->msgArrayN ); p->msgArray = mem::allocZ<am_midi_msg_t>( p->msgArrayN );
p->midiOutDevLabel = mem::duplStr( p->midiOutDevLabel); p->iMsgArray = mem::allocZ<am_midi_msg_t>( p->iMsgArrayN );
p->midiOutPortLabel = mem::duplStr( p->midiOutPortLabel);
errLabel: errLabel:
return rc; return rc;
} }
rc_t _stop( midi_record_play_t* p ); 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; 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); rc = _stop(p);
} }
else else
{ {
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, status + ch, d0, d1 );
if( p->cb ) if( p->halfPedalFl )
p->cb( p->cb_arg, id, timestamp, ch, status, d0, d1 ); {
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; 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}; 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 ) void _set_midi_msg_next_index( midi_record_play_t* p, unsigned next_idx )
{ {
p->msgArrayInIdx = next_idx; p->iMsgArrayInIdx = next_idx;
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->midiMsgArrayInIdx );
} }
// 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) void _set_midi_msg_next_play_index(midi_record_play_t* p, unsigned next_idx)
{ {
p->msgArrayOutIdx = next_idx; p->msgArrayOutIdx = next_idx;
@ -214,7 +491,8 @@ namespace cw
return rc; 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 _midi_read( midi_record_play_t* p, const char* fn )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -245,21 +523,24 @@ namespace cw
goto errLabel; goto errLabel;
} }
_set_midi_msg_next_index(p, n ); p->msgArrayInIdx = n;
cwLogInfo("Read %i from '%s'.",n,fn); cwLogInfo("Read %i from '%s'.",n,fn);
errLabel: errLabel:
file::close(fH);
return rc; return rc;
} }
// Write the record buffer to an AM file
rc_t _midi_write( midi_record_play_t* p, const char* fn ) rc_t _midi_write( midi_record_play_t* p, const char* fn )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
file::handle_t fH; file::handle_t fH;
if( p->msgArrayInIdx == 0 ) if( p->iMsgArrayInIdx == 0 )
{ {
cwLogWarning("Nothing to write."); cwLogWarning("Nothing to write.");
return rc; return rc;
@ -273,30 +554,77 @@ namespace cw
} }
// write the file header // 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)); rc = cwLogError(kWriteFailRC,"Header write to '%s' failed.",cwStringNullGuard(fn));
goto errLabel; goto errLabel;
} }
// write the file data // 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)); rc = cwLogError(kWriteFailRC,"Data write to '%s' failed.",cwStringNullGuard(fn));
goto errLabel; goto errLabel;
} }
// update UI msg count errLabel:
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
file::close(fH); file::close(fH);
cwLogInfo("Saved %i events to '%s'.", p->msgArrayInIdx, fn ); cwLogInfo("Saved %i events to '%s'.", p->iMsgArrayInIdx, fn );
errLabel:
return rc; 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 _midi_file_write( const char* fn, const am_midi_msg_t* msgArray, unsigned msgArrayCnt )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -384,13 +712,14 @@ namespace cw
time::spec_t t1; time::spec_t t1;
time::get(t1); time::get(t1);
// if we were recording
if( p->recordFl ) if( p->recordFl )
{ {
// set the 'microsec' value for each MIDI msg // set the 'microsec' value for each MIDI msg as an offset from the first message[]
for(unsigned i=0; i<p->msgArrayInIdx; ++i) 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 ); cwLogInfo("MIDI messages recorded: %i",p->msgArrayInIdx );
@ -400,26 +729,19 @@ namespace cw
{ {
io::timerStop( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) ); 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 // all notes off
_transmit_ctl( p, 0, 121, 0 ); // reset all controllers _transmit_ctl( p, 0, 121, 0, 0 ); // reset all controllers
_transmit_ctl( p, 0, 123, 0 ); // all notes off _transmit_ctl( p, 0, 123, 0, 0 ); // all notes off
_transmit_ctl( p, 0, 0, 0 ); // switch to bank 0 _transmit_ctl( p, 0, 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 );
p->pedalFl = false;
} }
cwLogInfo("Runtime: %5.2f seconds.", time::elapsedMs(p->start_time,t1)/1000.0 );
return rc; return rc;
} }
rc_t _midi_receive( midi_record_play_t* p, const io::midi_msg_t& m ) rc_t _midi_receive( midi_record_play_t* p, const io::midi_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -436,68 +758,42 @@ namespace cw
{ {
//if( !midi::isPedal(pkt->msgArray[j].status,pkt->msgArray[j].d0) ) //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 // verify that space exists in the record buffer
if( p->msgArrayInIdx >= p->msgArrayN ) if( p->iMsgArrayInIdx >= p->iMsgArrayN )
{ {
_stop(p); _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; goto errLabel;
} }
else else
{ {
// copy the msg into the record buffer // copy the msg into the record buffer
am_midi_msg_t* am = p->msgArray + p->msgArrayInIdx;
midi::msg_t* mm = pkt->msgArray + j; midi::msg_t* mm = pkt->msgArray + j;
if( midi::isChStatus(mm->status) ) 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; if( p->thruFl && am != nullptr )
am->devIdx = pkt->devIdx; _transmit_msg( p, am, false );
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;
//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: errLabel:
return rc; return rc;
} }
rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m) rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m)
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -510,46 +806,27 @@ namespace cw
unsigned cur_time_us = time::elapsedMicros(p->play_time,t); unsigned cur_time_us = time::elapsedMicros(p->play_time,t);
while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us ) if( p->halfPedalFl )
{ _half_pedal_update( p, cur_time_us );
else
am_midi_msg_t* mm = p->msgArray + p->msgArrayOutIdx; while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us )
//_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 )
{ {
am_midi_msg_t* mm = p->msgArray + p->msgArrayOutIdx;
//_print_midi_msg(mm);
_transmit_msg( p, 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 all MIDI messages have been played
if( p->msgArrayOutIdx >= p->msgArrayInIdx ) if( p->msgArrayOutIdx >= p->msgArrayInIdx )
{ {
_stop(p); _stop(p);
break; break;
}
} }
}
} }
return rc; 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->ioH = ioH;
p->cb = cb; p->cb = cb;
p->cb_arg = cb_arg; 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) ); midi_device_t* dev = p->midiDevA + i;
goto errLabel;
} if( !p->midiDevA[i].enableFl )
continue;
if((p->midiOutPortIdx = io::midiDevicePortIndex(p->ioH,p->midiOutDevIdx,false,p->midiOutPortLabel)) == kInvalidIdx )
{ if((dev->midiOutDevIdx = io::midiDeviceIndex(p->ioH,dev->midiOutDevLabel)) == kInvalidIdx )
rc = cwLogError(kInvalidArgRC,"The MIDI output port: '%s' was not found.", cwStringNullGuard(p->midiOutPortLabel) ); {
goto errLabel; 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 // 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); midi_record_play_t* p = _handleToPtr(h);
p->startedFl = true; p->startedFl = true;
p->pedalFl = false;
// set the end play time // set the end play time
if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) ) if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) )
time::setZero(p->end_play_event_timestamp); time::setZero(p->end_play_event_timestamp);
else else
{
p->end_play_event_timestamp = *end_play_event_timestamp; 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); time::get(p->start_time);
if( p->recordFl ) if( p->recordFl || p->logInFl or p->logOutFl )
{ {
_set_midi_msg_next_index(p, 0 ); _set_midi_msg_next_index(p, 0 );
} }
else
if( !p->recordFl )
{ {
time::get(p->play_time); 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. // This will cause that event to be played back immediately.
time::subtractMicros(p->play_time, p->msgArray[ p->msgArrayOutIdx ].microsec ); 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) ); 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); 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 ) cw::rc_t cw::midi_record_play::open( handle_t h, const char* fn )
{ {
midi_record_play_t* p = _handleToPtr(h); 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].id = msg[i].id;
p->msgArray[i].timestamp = msg[i].timestamp; p->msgArray[i].timestamp = msg[i].timestamp;
p->msgArray[i].loc = msg[i].loc;
p->msgArray[i].ch = msg[i].ch; p->msgArray[i].ch = msg[i].ch;
p->msgArray[i].status = msg[i].status; p->msgArray[i].status = msg[i].status;
p->msgArray[i].d0 = msg[i].d0; p->msgArray[i].d0 = msg[i].d0;
p->msgArray[i].d1 = msg[i].d1; p->msgArray[i].d1 = msg[i].d1;
p->msgArray[i].devIdx = p->midiOutDevIdx; p->msgArray[i].devIdx = kInvalidIdx;
p->msgArray[i].portIdx = p->midiOutPortIdx; p->msgArray[i].portIdx = kInvalidIdx;
p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp); p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp);
} }
p->msgArrayInIdx = msg_count; 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; p->msgArrayOutIdx = i;
_transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl ); _transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl ); _transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl ); _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; 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 ) unsigned cw::midi_record_play::event_index( handle_t h )
{ {
midi_record_play_t* p = _handleToPtr(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; 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 ) cw::rc_t cw::midi_record_play::am_to_midi_file( const char* am_filename, const char* midi_filename )
{ {

View File

@ -12,14 +12,15 @@ namespace cw
{ {
unsigned id; unsigned id;
time::spec_t timestamp; time::spec_t timestamp;
unsigned loc;
uint8_t ch; uint8_t ch;
uint8_t status; uint8_t status;
uint8_t d0; uint8_t d0;
uint8_t d1; uint8_t d1;
} midi_msg_t; } 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 ); 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 ); bool thru_state( handle_t h );
rc_t save( handle_t h, const char* fn ); 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 ); 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 load( handle_t h, const midi_msg_t* msg, unsigned msg_count );
rc_t seek( handle_t h, time::spec_t timestamp ); 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 ); 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 // 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_file( const char* am_filename, const char* midi_filename );
rc_t am_to_midi_dir( const char* inDir ); rc_t am_to_midi_dir( const char* inDir );

View File

@ -35,10 +35,20 @@ namespace cw
kPanelDivId = 1000, kPanelDivId = 1000,
kQuitBtnId, kQuitBtnId,
kIoReportBtnId, kIoReportBtnId,
kNetPrintBtnId,
kReportBtnId, kReportBtnId,
kStartBtnId, kStartBtnId,
kStopBtnId, kStopBtnId,
kPrintMidiCheckId,
kPianoMidiCheckId,
kSamplerMidiCheckId,
kSyncDelayMsId,
kWetInGainId,
kWetOutGainId,
kDryGainId,
kMidiThruCheckId, kMidiThruCheckId,
kCurMidiEvtCntId, kCurMidiEvtCntId,
@ -59,6 +69,13 @@ namespace cw
kStatusId, kStatusId,
kHalfPedalPedalVel,
kHalfPedalDelayMs,
kHalfPedalPitch,
kHalfPedalVel,
kHalfPedalDurMs,
kHalfPedalDnDelayMs,
kLogId, kLogId,
kFragListId, kFragListId,
@ -81,6 +98,12 @@ namespace cw
}; };
enum
{
kPiano_MRP_DevIdx = 0,
kSampler_MRP_DevIdx = 1
};
enum enum
{ {
kAmMidiTimerId kAmMidiTimerId
@ -93,10 +116,20 @@ namespace cw
{ ui::kRootAppId, kPanelDivId, "panelDivId" }, { ui::kRootAppId, kPanelDivId, "panelDivId" },
{ kPanelDivId, kQuitBtnId, "quitBtnId" }, { kPanelDivId, kQuitBtnId, "quitBtnId" },
{ kPanelDivId, kIoReportBtnId, "ioReportBtnId" }, { kPanelDivId, kIoReportBtnId, "ioReportBtnId" },
{ kPanelDivId, kNetPrintBtnId, "netPrintBtnId" },
{ kPanelDivId, kReportBtnId, "reportBtnId" }, { kPanelDivId, kReportBtnId, "reportBtnId" },
{ kPanelDivId, kStartBtnId, "startBtnId" }, { kPanelDivId, kStartBtnId, "startBtnId" },
{ kPanelDivId, kStopBtnId, "stopBtnId" }, { 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, kMidiThruCheckId, "midiThruCheckId" },
{ kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" }, { kPanelDivId, kCurMidiEvtCntId, "curMidiEvtCntId" },
@ -114,6 +147,15 @@ namespace cw
{ kPanelDivId, kInsertLocId, "insertLocId" }, { kPanelDivId, kInsertLocId, "insertLocId" },
{ kPanelDivId, kInsertBtnId, "insertBtnId" }, { kPanelDivId, kInsertBtnId, "insertBtnId" },
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" }, { 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, kStatusId, "statusId" },
{ kPanelDivId, kLogId, "logId" }, { kPanelDivId, kLogId, "logId" },
@ -156,6 +198,7 @@ namespace cw
const char* record_fn; const char* record_fn;
const char* record_fn_ext; const char* record_fn_ext;
const char* scoreFn; const char* scoreFn;
const object_t* midi_play_record_cfg;
const object_t* frag_panel_cfg; const object_t* frag_panel_cfg;
const object_t* presets_cfg; const object_t* presets_cfg;
const object_t* flow_cfg; const object_t* flow_cfg;
@ -184,7 +227,15 @@ namespace cw
double crossFadeSrate; double crossFadeSrate;
unsigned crossFadeCnt; unsigned crossFadeCnt;
bool printMidiFl;
unsigned hpDelayMs;
unsigned hpPedalVel;
unsigned hpPitch;
unsigned hpVel;
unsigned hpDurMs;
unsigned hpDnDelayMs;
} app_t; } app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef ) rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
@ -198,18 +249,31 @@ namespace cw
goto errLabel; goto errLabel;
} }
if((rc = params_cfgRef->getv( "record_dir", app->record_dir, if((rc = params_cfgRef->getv( "record_dir", app->record_dir,
"record_fn", app->record_fn, "record_fn", app->record_fn,
"record_fn_ext", app->record_fn_ext, "record_fn_ext", app->record_fn_ext,
"score_fn", app->scoreFn, "score_fn", app->scoreFn,
"frag_panel", app->frag_panel_cfg, "midi_play_record", app->midi_play_record_cfg,
"presets", app->presets_cfg, "frag_panel", app->frag_panel_cfg,
"crossFadeSrate",app->crossFadeSrate, "presets", app->presets_cfg,
"crossFadeCount",app->crossFadeCnt)) != kOkRC ) "crossFadeSrate", app->crossFadeSrate,
"crossFadeCount", app->crossFadeCnt)) != kOkRC )
{ {
rc = cwLogError(kSyntaxErrorRC,"Preset Select App configuration parse failed."); 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 // verify that the output directory exists
if((rc = filesys::isDir(app->record_dir)) != kOkRC ) if((rc = filesys::isDir(app->record_dir)) != kOkRC )
if((rc = filesys::makeDir(app->record_dir)) != kOkRC ) if((rc = filesys::makeDir(app->record_dir)) != kOkRC )
@ -257,6 +321,8 @@ namespace cw
rc_t _free( app_t& app ) rc_t _free( app_t& app )
{ {
mem::release((char*&)app.record_dir);
mem::release((char*&)app.scoreFn);
preset_sel::destroy(app.psH); preset_sel::destroy(app.psH);
io_flow::destroy(app.ioFlowH); io_flow::destroy(app.ioFlowH);
midi_record_play::destroy(app.mrpH); midi_record_play::destroy(app.mrpH);
@ -283,15 +349,15 @@ namespace cw
{ {
const char* preset_label = preset_sel::preset_label(app->psH,preset_idx); const char* preset_label = preset_sel::preset_label(app->psH,preset_idx);
cwLogInfo("Apply preset: '%s'.\n", preset_idx==kInvalidIdx ? "<invalid>" : preset_label); cwLogInfo("Apply preset: '%s'.", preset_idx==kInvalidIdx ? "<invalid>" : preset_label);
if( preset_label != nullptr ) if( preset_label != nullptr )
{ {
io_flow::apply_preset( app->ioFlowH, flow_cross::kNextDestId, preset_label ); 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, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::real_t)frag->igain );
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, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::real_t)frag->ogain );
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, "wd_bal", "in", flow::kAnyChIdx, (dsp::real_t)frag->wetDryGain );
io_flow::begin_cross_fade( app->ioFlowH, frag->fadeOutMs ); io_flow::begin_cross_fade( app->ioFlowH, frag->fadeOutMs );
} }
@ -301,34 +367,98 @@ namespace cw
return kOkRC; 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; rc_t rc = kOkRC;
if( id != kInvalidId )
// 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; if(prevUuId != kInvalidId )
char buf[ buf_byte_cnt ]; io::uiSetSelect( app->ioH, prevUuId, false );
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 );
}
// 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 ) loc_map_t* _find_loc( app_t* app, unsigned loc )
{ {
unsigned i=0; unsigned i=0;
loc_map_t* pre_loc_map = nullptr;
for(; i<app->locMapN; ++i) for(; i<app->locMapN; ++i)
if( app->locMap[i].loc == loc ) {
return app->locMap + i; if( app->locMap[i].loc >= loc )
return nullptr; 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* begMap = nullptr;
loc_map_t* endMap = 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 ) if((begMap = _find_loc(app,begLoc)) == nullptr )
{ {
rc = cwLogError(kInvalidArgRC,"The begin play location is not valid."); 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: errLabel:
return rc; return rc;
@ -415,8 +555,10 @@ namespace cw
void _update_event_ui( app_t* app ) 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,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), 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. // 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; rc_t rc = kOkRC;
char* fn = nullptr; char* fn = nullptr;
@ -778,10 +920,10 @@ namespace cw
goto errLabel; goto errLabel;
} }
preset_sel::report( app->psH ); //preset_sel::report( app->psH );
f = preset_sel::get_fragment_base(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; unsigned fragId = f->fragId;
@ -792,7 +934,7 @@ namespace cw
} }
_update_frag_ui(app, fragId ); _update_frag_ui(app, fragId );
} }
@ -834,21 +976,18 @@ namespace cw
return rc; 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; rc_t rc = kOkRC;
const score::event_t* e = nullptr; const score::event_t* e = nullptr;
unsigned midiEventN = 0; unsigned midiEventN = 0;
midi_record_play::midi_msg_t* m = nullptr; midi_record_play::midi_msg_t* m = nullptr;
// if the score is already loaded midiEventCntRef = 0;
if( app->scoreH.isValid() )
return rc;
cwLogInfo("Loading");
_set_status(app,"Loading...");
// create the score // create the score
if((rc = score::create( app->scoreH, app->scoreFn )) != kOkRC ) if((rc = score::create( app->scoreH, app->scoreFn )) != kOkRC )
{ {
@ -885,6 +1024,7 @@ namespace cw
m[i].d0 = e->d0; m[i].d0 = e->d0;
m[i].d1 = e->d1; m[i].d1 = e->d1;
m[i].id = e->uid; m[i].id = e->uid;
m[i].loc = e->loc;
app->locMap[i].loc = e->loc; app->locMap[i].loc = e->loc;
app->locMap[i].timestamp = m[i].timestamp; app->locMap[i].timestamp = m[i].timestamp;
@ -895,6 +1035,8 @@ namespace cw
++i; ++i;
} }
qsort( app->locMap, app->locMapN, sizeof(loc_map_t), _compare_loc_map );
// load the player with the msg list // load the player with the msg list
if((rc = midi_record_play::load( app->mrpH, m, midiEventN )) != kOkRC ) if((rc = midi_record_play::load( app->mrpH, m, midiEventN )) != kOkRC )
{ {
@ -902,36 +1044,80 @@ namespace cw
goto errLabel; 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 errLabel:
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 );
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 the current event and event count
_update_event_ui(app); _update_event_ui(app);
// enable the start/stop buttons // enable the start/stop buttons
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true );
io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true );
// restore the fragment records // restore the fragment records
if((rc = _restore( app )) != kOkRC ) if( firstLoadFl )
{ if((rc = _restore_fragment_data( app )) != kOkRC )
rc = cwLogError(rc,"Restore failed."); {
goto errLabel; 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 ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true );
cwLogInfo("'%s' loaded.",app->scoreFn); cwLogInfo("'%s' loaded.",app->scoreFn);
errLabel: errLabel:
@ -946,25 +1132,11 @@ namespace cw
return rc; return rc;
} }
rc_t _on_ui_start( app_t* app ) rc_t _on_ui_start( app_t* app )
{ {
return _do_play(app, app->beg_play_loc, app->end_play_loc ); 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 ) rc_t _set_midi_thru_state( app_t* app, bool thru_fl )
{ {
@ -990,26 +1162,31 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
switch( appId ) // verify that the location exists
{ if( _find_loc(app,loc) == nullptr )
case kBegPlayLocNumbId: _set_status(app,"%i is an invalid location.",loc);
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 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; return rc;
} }
@ -1131,10 +1308,126 @@ namespace cw
if( rc != kOkRC ) if( rc != kOkRC )
rc = cwLogError(rc,"Fragment delete failed."); 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; 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 _onUiInit(app_t* app, const io::ui_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -1150,7 +1443,7 @@ namespace cw
for(; f!=nullptr; f=f->link) for(; f!=nullptr; f=f->link)
_update_frag_ui( app, f->fragId ); _update_frag_ui( app, f->fragId );
//_do_load(app); _do_load(app);
return rc; return rc;
} }
@ -1168,11 +1461,17 @@ namespace cw
case kIoReportBtnId: case kIoReportBtnId:
io::report( app->ioH ); io::report( app->ioH );
break; break;
case kNetPrintBtnId:
io_flow::print_network(app->ioFlowH,flow_cross::kCurDestId);
break;
case kReportBtnId: 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"); //io_flow::apply_preset( app->ioFlowH, 2000.0, app->tmp==0 ? "a" : "b");
//app->tmp = !app->tmp; //app->tmp = !app->tmp;
//io_flow::print(app->ioFlowH);
//midi_record_play::save_csv(app->mrpH,"/home/kevin/temp/mrp_1.csv");
break; break;
case kSaveBtnId: case kSaveBtnId:
@ -1193,9 +1492,37 @@ namespace cw
break; break;
case kStopBtnId: case kStopBtnId:
_on_ui_stop(app); _do_stop_play(app);
break; 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: case kBegPlayLocNumbId:
_on_ui_play_loc(app, m.appId, m.value->u.i); _on_ui_play_loc(app, m.appId, m.value->u.i);
break; break;
@ -1216,6 +1543,15 @@ namespace cw
_on_ui_delete_btn(app); _on_ui_delete_btn(app);
break; 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: case kFragInGainId:
_on_ui_frag_value( app, m.uuId, m.value->u.d); _on_ui_frag_value( app, m.uuId, m.value->u.d);
break; break;
@ -1286,31 +1622,6 @@ namespace cw
return kOkRC; 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 ) rc_t _onUiSelect( app_t* app, const io::ui_msg_t& m )
{ {
@ -1333,10 +1644,50 @@ namespace cw
errLabel: errLabel:
return rc; return rc;
} }
rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m ) rc_t _onUiEcho(app_t* app, const io::ui_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
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; return rc;
} }
@ -1367,7 +1718,7 @@ namespace cw
break; break;
case ui::kClickOpId: case ui::kClickOpId:
_onUiClick( app, m ); _do_select_frag( app, m.uuId );
break; break;
case ui::kSelectOpId: case ui::kSelectOpId:
@ -1402,7 +1753,7 @@ namespace cw
{ {
midi_record_play::exec( app->mrpH, *m ); midi_record_play::exec( app->mrpH, *m );
if( midi_record_play::is_started(app->mrpH) ) if( midi_record_play::is_started(app->mrpH) )
io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) ); io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_loc(app->mrpH) );
} }
if( app->ioFlowH.isValid() ) 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 ) cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
{ {
rc_t rc; 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; const object_t* params_cfg = nullptr;
// Parse the configuration // 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; goto errLabel;
} }
// create the MIDI record-play object // 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."); rc = cwLogError(rc,"MIDI record-play object create failed.");
goto errLabel; goto errLabel;
} }
// create the IO Flow controller // create the IO Flow controller
if(app.flow_cfg==nullptr || flow_proc_dict==nullptr || (rc = io_flow::create(app.ioFlowH,app.ioH,app.crossFadeSrate,app.crossFadeCnt,*flow_proc_dict,*app.flow_cfg)) != kOkRC ) if(app.flow_cfg==nullptr || 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; goto errLabel;
} }
// start the io framework instance // start the IO framework instance
if((rc = io::start(app.ioH)) != kOkRC ) if((rc = io::start(app.ioH)) != kOkRC )
{ {
rc = cwLogError(rc,"Preset-select app start failed."); rc = cwLogError(rc,"Preset-select app start failed.");
goto errLabel; goto errLabel;
} }
// execute the io framework // execute the io framework
while( !isShuttingDown(app.ioH)) while( !isShuttingDown(app.ioH))
{ {

View File

@ -129,7 +129,7 @@ namespace cw
// If label is NULL or labelCharCnt==0 then a pointer to an internal static // If label is NULL or labelCharCnt==0 then a pointer to an internal static
// buffer is returned. If label[] is given the it // 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. // 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 ); const char* midiToSciPitch( uint8_t pitch, char* label, unsigned labelCharCnt );

View File

@ -464,7 +464,7 @@ namespace cw
void _cmMpReportPort( textBuf::handle_t tbH, const port_t* port ) 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_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_WRITE ) textBuf::print( tbH,"Writ " );
if( port->alsa_cap & SND_SEQ_PORT_CAP_SYNC_READ ) textBuf::print( tbH,"Syrd " ); 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; 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 ) if(d->iPortCnt > 0 )
textBuf::print( tbH," Input:\n"); textBuf::print( tbH," Input:\n");

View File

@ -6,6 +6,7 @@
#include "cwPianoScore.h" #include "cwPianoScore.h"
#include "cwMidi.h" #include "cwMidi.h"
#include "cwTime.h" #include "cwTime.h"
#include "cwFile.h"
namespace cw namespace cw
{ {
@ -16,6 +17,11 @@ namespace cw
event_t* base; event_t* base;
event_t* end; event_t* end;
unsigned maxLocId; unsigned maxLocId;
event_t** uid_mapA;
unsigned uid_mapN;
unsigned min_uid;
} score_t; } score_t;
score_t* _handleToPtr(handle_t h) score_t* _handleToPtr(handle_t h)
@ -38,6 +44,198 @@ namespace cw
return rc; 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 _parse_event_list( score_t* p, const object_t* cfg )
{ {
rc_t rc; rc_t rc;
@ -136,11 +334,13 @@ cw::rc_t cw::score::create( handle_t& hRef, const char* fn )
{ {
rc_t rc; rc_t rc;
object_t* cfg = nullptr; object_t* cfg = nullptr;
score_t* p = nullptr;
if((rc = destroy(hRef)) != kOkRC ) if((rc = destroy(hRef)) != kOkRC )
return rc; return rc;
// parse the cfg file // parse the cfg file
/*
if((rc = objectFromFile( fn, cfg )) != kOkRC ) if((rc = objectFromFile( fn, cfg )) != kOkRC )
{ {
rc = cwLogError(rc,"Score parse failed on file: '%s'.", fn); 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); rc = create(hRef,cfg);
*/
p = mem::allocZ< score_t >();
rc = _parse_csv(p,fn);
errLabel:
hRef.set(p);
//errLabel:
if( cfg != nullptr ) if( cfg != nullptr )
cfg->free(); cfg->free();
if( rc != kOkRC )
destroy(hRef);
return rc; return rc;
} }
@ -255,6 +467,13 @@ bool cw::score::is_loc_valid( handle_t h, unsigned locId )
return locId < p->maxLocId; 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 ) cw::rc_t cw::score::test( const object_t* cfg )
{ {

View File

@ -40,6 +40,8 @@ namespace cw
unsigned loc_count( handle_t h ); unsigned loc_count( handle_t h );
bool is_loc_valid( handle_t h, unsigned locId ); 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. // Format the event as a string for printing.
rc_t event_to_string( handle_t h, unsigned uid, char* buf, unsigned buf_byte_cnt ); rc_t event_to_string( handle_t h, unsigned uid, char* buf, unsigned buf_byte_cnt );

View File

@ -31,6 +31,11 @@ namespace cw
unsigned next_frag_id; unsigned next_frag_id;
frag_t* last_ts_frag; 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; } preset_sel_t;
@ -205,6 +210,9 @@ namespace cw
} }
bool _is_master_var_id( unsigned varId )
{ return varId > kBaseMasterVarId; }
template< typename T > template< typename T >
rc_t _set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, const T& value ) 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); preset_sel_t* p = _handleToPtr(h);
frag_t* f = nullptr; frag_t* f = nullptr;
// locate the requested fragment // if this is not a 'master' variable then locate the requested fragment
if((rc = _find_frag(p,fragId,f)) != kOkRC ) if( !_is_master_var_id(varId) )
goto errLabel; if((rc = _find_frag(p,fragId,f)) != kOkRC )
goto errLabel;
switch( varId ) switch( varId )
{ {
@ -259,6 +268,22 @@ namespace cw
case kPlayBtnVarId: case kPlayBtnVarId:
break; 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: default:
rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId); 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); preset_sel_t* p = _handleToPtr(h);
frag_t* f = nullptr; frag_t* f = nullptr;
// locate the requested fragment // if this is not a 'master' variable then locate the requested fragment
if((rc = _find_frag(p,fragId,f)) != kOkRC ) if( !_is_master_var_id( varId ) )
goto errLabel; if((rc = _find_frag(p,fragId,f)) != kOkRC )
goto errLabel;
switch( varId ) switch( varId )
{ {
@ -335,6 +361,22 @@ namespace cw
case kPlayBtnVarId: case kPlayBtnVarId:
break; 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: default:
rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId); rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId);
goto errLabel; 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>(); p = mem::allocZ<preset_sel_t>();
// parse the cfg // parse the cfg
if((rc = cfg->getv( "preset_labelL", labelL, if((rc = cfg->getv( "preset_labelL", labelL,
"default_gain", p->defaultGain, "default_gain", p->defaultGain,
"default_wet_dry_gain", p->defaultWetDryGain, "default_wet_dry_gain", p->defaultWetDryGain,
"default_fade_ms", p->defaultFadeOutMs, "default_fade_ms", p->defaultFadeOutMs,
"default_preset", default_preset_label)) != kOkRC ) "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."); rc = cwLogError(rc,"The preset configuration parse failed.");
goto errLabel; 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 ) cw::rc_t cw::preset_sel::delete_fragment( handle_t h, unsigned fragId )
{ {
preset_sel_t* p = _handleToPtr(h); preset_sel_t* p = _handleToPtr(h);
frag_t* f0 = nullptr; frag_t* f = p->fragL;
frag_t* f1 = p->fragL;
for(; f1!=nullptr; f1=f1->link) for(; f!=nullptr; f=f->link)
{ if( f->fragId == fragId )
if( f1->fragId == fragId )
{ {
if( f0 == nullptr ) if( f->prev == nullptr )
p->fragL = f1->link; p->fragL = f->link;
else else
f0->link = f1->link; f->prev->link = f->link;
if( f->link != nullptr )
f->link->prev = f->prev;
// release the fragment // release the fragment
mem::release(f1->presetA); mem::release(f->presetA);
mem::release(f1); mem::release(f);
return kOkRC; 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 ) 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; 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 ) 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); preset_sel_t* p = _handleToPtr(h);
frag_t* f = nullptr; frag_t* f = nullptr;
bool frag_changed_fl = false; 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 this is the first call to 'track_timestamp()'.
if( p->last_ts_frag == nullptr ) if( p->last_ts_frag == nullptr )
f = _timestamp_to_frag(p,ts); 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("fragL", fragL_obj, root);
newPairObject("fragN", fragN, 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; 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); _destroy_all_frags(p);
// parse the root level // parse the root level
if((rc = root->getv( "fragN", fragN, if((rc = root->getv( "fragN", fragN,
"fragL", fragL_obj )) != kOkRC ) "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)); rc = cwLogError(rc,"Root preset select parse failed on '%s'.", cwStringNullGuard(fn));
goto errLabel; goto errLabel;
@ -965,7 +1029,7 @@ cw::rc_t cw::preset_sel::report( handle_t h )
unsigned elapsedMs = time::elapsedMs(t0,f->endTimestamp); unsigned elapsedMs = time::elapsedMs(t0,f->endTimestamp);
double mins = elapsedMs / 60000.0; 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; return rc;

View File

@ -57,6 +57,13 @@ namespace cw
kPresetSelectVarId, // select a preset to play kPresetSelectVarId, // select a preset to play
kPlayEnableVarId, // include in the segment to play kPlayEnableVarId, // include in the segment to play
kDryFlVarId, // play this fragment dry 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 ); 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 ); rc_t delete_fragment( handle_t h, unsigned fragId );
bool is_fragment_loc( handle_t h, unsigned loc ); 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 ); 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 ); 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 'ts' is past the last defined fragment then the last fragment is returned.
// If no fragments are defined 'frag_Ref' is set to nullptr. // 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. // 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 ); 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. // Return the preset index marked to play on this fragment.

View File

@ -78,6 +78,19 @@ unsigned cw::time::elapsedMs( const spec_t& t0 )
return elapsedMs(t0,t1); 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 ) unsigned cw::time::absElapsedMicros( const spec_t& t0, const spec_t& t1 )
{ {
if( isLTE(t0,t1) ) if( isLTE(t0,t1) )

View File

@ -29,7 +29,11 @@ namespace cw
// Wrapper on elapsedMicros() // Wrapper on elapsedMicros()
unsigned elapsedMs( const spec_t& t0, const spec_t& t1 ); unsigned elapsedMs( const spec_t& t0, const spec_t& t1 );
unsigned elapsedMs( const spec_t& t0 ); 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. // 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. // The function therefore begins by swapping t1 and t0 if t0 is after t1.
unsigned absElapsedMicros( const spec_t& t0, const spec_t& t1 ); unsigned absElapsedMicros( const spec_t& t0, const spec_t& t1 );

102
cwUi.cpp
View File

@ -86,6 +86,9 @@ namespace cw
appIdMapRecd_t* appIdMap; // map of application parent/child/js id's appIdMapRecd_t* appIdMap; // map of application parent/child/js id's
char* buf; // buf[bufN] output message formatting buffer char* buf; // buf[bufN] output message formatting buffer
unsigned bufN; // unsigned bufN; //
char* recvBuf;
unsigned recvBufN;
unsigned recvBufIdx;
unsigned* sessA; // sessA[ sessN ] array of wsSessId's unsigned* sessA; // sessA[ sessN ] array of wsSessId's
unsigned sessN; unsigned sessN;
@ -153,6 +156,7 @@ namespace cw
mem::release(p->sessA); mem::release(p->sessA);
mem::release(p->eleA); mem::release(p->eleA);
mem::release(p->buf); mem::release(p->buf);
mem::release(p->recvBuf);
mem::release(p); mem::release(p);
@ -963,7 +967,7 @@ namespace cw
if( p->sendCbFunc != nullptr ) if( p->sendCbFunc != nullptr )
{ {
const char* mFmt = "{ \"op\":\"%s\", \"uuId\":%i, \"value\":%s }"; const char* mFmt = "{ \"op\":\"%s\", \"uuId\":%i, \"value\":%s }";
const int mbufN = 512; const int mbufN = 1024;
char vbuf[vbufN]; char vbuf[vbufN];
char mbuf[mbufN]; char mbuf[mbufN];
@ -1082,6 +1086,9 @@ cw::rc_t cw::ui::create(
p->sendCbArg = sendCbArg; p->sendCbArg = sendCbArg;
p->buf = mem::allocZ<char>(fmtBufByteN); p->buf = mem::allocZ<char>(fmtBufByteN);
p->bufN = fmtBufByteN; p->bufN = fmtBufByteN;
p->recvBuf = mem::allocZ<char>(fmtBufByteN);
p->recvBufN = fmtBufByteN;
p->recvBufIdx = 0;
// create the root element // create the root element
if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId ) if((ele = _createBaseEle(p, nullptr, kRootAppId, kInvalidId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
@ -1182,14 +1189,55 @@ cw::rc_t cw::ui::onDisconnect( handle_t h, unsigned wsSessId )
return kOkRC; 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; rc_t rc = kOkRC;
ui_t* p = _handleToPtr(h); ui_t* p = _handleToPtr(h);
opId_t opId = _labelToOpId((const char*)msg); opId_t opId = kInvalidOpId;
value_t value; value_t value;
ele_t* ele; 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 ) switch( opId )
{ {
case kInitOpId: 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); rc = sendValueString(h,uuId,text);
else else
{ {
int sn = textLength(text); unsigned sn = textLength(text);
sn += n + 1; 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; 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]; char ch = text[i];
bool escape_fl = true; 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 ) if( escape_fl )
s[j++] = '\\'; s[j++] = '\\';
s[j] = ch; if( j < sn )
s[j] = ch;
} }
s[sn-1] = 0; s[sn-1] = 0;
//printf("%s %s\n",text,s); //printf("%s %s\n",text,s);
rc = sendValueString(h,uuId,s); rc = sendValueString(h,uuId,s);
@ -1781,11 +1833,12 @@ void cw::ui::report( handle_t h )
if(p->eleA[i] != nullptr ) if(p->eleA[i] != nullptr )
{ {
const ele_t* e = p->eleA[i]; const ele_t* e = p->eleA[i];
unsigned parUuId = e->phys_parent==NULL ? kInvalidId : e->phys_parent->uuId; 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; 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 %20s : parent uu:%5i app:%5i %20s ", e->uuId, e->appId, e->eleName == NULL ? "" : e->eleName, parUuId, e->logical_parent->appId, parEleName ); 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) 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 ) cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* object_label )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const object_t* op = &o; const object_t* op = &o;
char* uiCfgFn = nullptr; char* uiCfgFn = nullptr;
char* physRootDir = nullptr;
memset(&args,0,sizeof(args)); 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."); 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 a default UI resource script was given then convert it into an object
if( uiCfgFn != nullptr ) 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 ) if((rc = objectFromFile(fn,args.uiRsrc)) != kOkRC )
rc = cwLogError(rc,"An error occurred while parsing the UI resource script in '%s'.", cwStringNullGuard(uiCfgFn)); rc = cwLogError(rc,"An error occurred while parsing the UI resource script in '%s'.", cwStringNullGuard(uiCfgFn));
mem::release(fn); mem::release(fn);
} }
errLabel:
mem::release(physRootDir);
return rc; return rc;
} }
@ -2024,7 +2088,7 @@ cw::rc_t cw::ui::ws::exec( handle_t h )
cwLogError(rc,"The UI websock execution failed."); cwLogError(rc,"The UI websock execution failed.");
// make the idle callback // make the idle callback
ui::onReceive( p->uiH, kInvalidId, "idle", strlen("idle") ); ui::onReceive( p->uiH, kInvalidId, "idle", strlen("idle")+1 );
return rc; return rc;
} }

7
cwUi.h
View File

@ -41,7 +41,12 @@ namespace cw
// A UI was disconnected // A UI was disconnected
rc_t onDisconnect( handle_t h, unsigned wsSessId ); 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 ); 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'. // Locate an element whose parent uuid is 'parentUuId' with a child named 'eleName'.

View File

@ -54,7 +54,7 @@ namespace cw
void copy( T0* v0, const T1* v1, unsigned n ) void copy( T0* v0, const T1* v1, unsigned n )
{ {
for(unsigned i=0; i<n; ++i) for(unsigned i=0; i<n; ++i)
v0[i] = v1[i]; v0[i] = (T0)v1[i];
} }
template< typename T0, typename T1 > template< typename T0, typename T1 >
@ -151,7 +151,7 @@ namespace cw
void mul( T0* v0, const T1* v1, unsigned n ) void mul( T0* v0, const T1* v1, unsigned n )
{ {
for(unsigned i=0; i<n; ++i) for(unsigned i=0; i<n; ++i)
v0[i] *= v1[i]; v0[i] = v0[i] * (T1)v1[i];
} }
template< typename T0, typename T1 > template< typename T0, typename T1 >
@ -169,8 +169,8 @@ namespace cw
v0[i] *= scalar; v0[i] *= scalar;
} }
template< typename T0, typename T1 > template< typename T0, typename T1, typename T2 >
void mul( T0* y0, const T0* v0, const T1& scalar, unsigned n ) void mul( T0* y0, const T1* v0, const T2& scalar, unsigned n )
{ {
for(unsigned i=0; i<n; ++i) for(unsigned i=0; i<n; ++i)
y0[i] = v0[i] * scalar; y0[i] = v0[i] * scalar;
@ -363,15 +363,15 @@ namespace cw
return init_idx; return init_idx;
} }
template< typename T > template< typename T0, typename T1 >
T* ampl_to_db( T* dbp, const T* sbp, unsigned dn, T minDb=-1000 ) T0* ampl_to_db( T0* dbp, const T1* sbp, unsigned dn, T0 minDb=-1000 )
{ {
T minVal = pow(10.0,minDb/20.0); T0 minVal = pow(10.0,minDb/20.0);
T* dp = dbp; T0* dp = dbp;
T* ep = dp + dn; T0* ep = dp + dn;
for(; dp<ep; ++dp,++sbp) for(; dp<ep; ++dp,++sbp)
*dp = *sbp<minVal ? minDb : 20.0 * log10(*sbp); *dp = (T0)(*sbp<minVal ? minDb : 20.0 * log10(*sbp));
return dbp; return dbp;
} }

View File

@ -1226,7 +1226,7 @@ function ws_on_msg( jsonMsg )
function ws_on_open() function ws_on_open()
{ {
set_app_title( "Connected", "title_connected" ); set_app_title( "Connected", "title_connected" );
_ws.send("init") ws_send("init")
} }
function ws_on_close() function ws_on_close()

View File

@ -10,11 +10,24 @@
row: { row: {
button:{ name: quitBtnId, title:"Quit" }, button:{ name: quitBtnId, title:"Quit" },
button:{ name: ioReportBtnId, title:"IO Report" }, button:{ name: ioReportBtnId, title:"IO Report" },
button:{ name: netPrintBtnId, title:"Print Network" }
button:{ name: reportBtnId, title:"App Report" }, button:{ name: reportBtnId, title:"App Report" },
button:{ name: loadBtnId, title:"Load" }, button:{ name: loadBtnId, title:"Load" },
button:{ name: saveBtnId, title:"Save" }, 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: { row: {
button:{ name: startBtnId, title:"Start" }, button:{ name: startBtnId, title:"Start" },
@ -25,8 +38,8 @@
row: { row: {
check:{ name: midiThruCheckId, title:"MIDI Thru" }, check:{ name: midiThruCheckId, title:"MIDI Thru" },
numb_disp: { name: curMidiEvtCntId, title:"Current:" }, numb_disp: { name: curMidiEvtCntId, title:"Current Loc:" },
numb_disp: { name: totalMidiEvtCntId, title:"Total:" }, numb_disp: { name: totalMidiEvtCntId, title:"Max Loc:" },
}, },
@ -36,6 +49,15 @@
button:{ name: deleteBtnId, title:"Delete", enable: false }, 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: { row: {
str_disp:{ name: statusId, title:"Status:", value: "" }, str_disp:{ name: statusId, title:"Status:", value: "" },
} }