Compare commits
58 Commits
f577b09415
...
700a670f40
Author | SHA1 | Date | |
---|---|---|---|
|
700a670f40 | ||
|
626f5b49c5 | ||
|
d85ab3cb7e | ||
|
8c9ff2cd52 | ||
|
4b1bc534a5 | ||
|
c69a9819ae | ||
|
b6685340c8 | ||
|
0c274f3389 | ||
|
086b4d116f | ||
|
90a910704e | ||
|
016702e4e4 | ||
|
d57789ea74 | ||
|
8926765c28 | ||
|
a4bc01a000 | ||
|
8ea77f1c2f | ||
|
15b83c1a5f | ||
|
ca6f444b0d | ||
|
a29b6ec06e | ||
|
964384ffbb | ||
|
249400d2ce | ||
|
e6524ea945 | ||
|
b19671dbdc | ||
|
63fde6f13d | ||
|
1fa37a5438 | ||
|
673e93cf9e | ||
|
8ea759d11e | ||
|
6d59d652fd | ||
|
06caedf493 | ||
|
fc94d3859d | ||
|
ccdac53013 | ||
|
cb401cdb9b | ||
|
e095b0ccd8 | ||
|
e3b9b82289 | ||
|
88ae8bd0e4 | ||
|
f7d23b40ce | ||
|
1bde870d3a | ||
|
db3e96cdf6 | ||
|
fac5b2b31a | ||
|
58e545d58c | ||
|
851f282178 | ||
|
d09d4d0311 | ||
|
171d32f262 | ||
|
9c57b3434f | ||
|
0f2a42940e | ||
|
9be8f5adbb | ||
|
47c69d2161 | ||
|
8af87f8d6d | ||
|
2bf875ec94 | ||
|
b4ad4449c3 | ||
|
e545c06818 | ||
|
fefbe68309 | ||
|
1b0cec7535 | ||
|
f69d3e2c55 | ||
|
d2fab180ec | ||
|
dded0e63be | ||
|
a1735740c7 | ||
|
b6296bcebe | ||
|
ef5b099dd4 |
@ -34,8 +34,8 @@ libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp src/libcw/cwWa
|
||||
libcwHDR += src/libcw/cwAudioFileOps.h src/libcw/cwAudioTransforms.h src/libcw/cwDspTransforms.h src/libcw/cwAudioFileProc.h src/libcw/cwPvAudioFileProc.h
|
||||
libcwSRC += src/libcw/cwAudioFileOps.cpp src/libcw/cwAudioTransforms.cpp src/libcw/cwDspTransforms.cpp src/libcw/cwAudioFileProc.cpp src/libcw/cwPvAudioFileProc.cpp
|
||||
|
||||
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h
|
||||
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp
|
||||
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowValue.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowPerf.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h
|
||||
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowValue.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowPerf.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp
|
||||
|
||||
if cwWEBSOCK
|
||||
libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h
|
||||
|
123
archive.md
Normal file
123
archive.md
Normal file
@ -0,0 +1,123 @@
|
||||
Done
|
||||
----
|
||||
- DONE: Remove `preset_label` and `type_src_label` from `_var_channelize()` and report error
|
||||
locations from the point of call.
|
||||
|
||||
- DONE: Move proc_dict.cfg to libcw directory.
|
||||
|
||||
- DONE: The proc inst 'args' should be able to create mult variables. The only way to instantiate
|
||||
new mult variables now is via the 'in' stmt.
|
||||
|
||||
- DONE: The `audio_merge` implementaiton is wrong. It should mimic `audio_mix` where all igain
|
||||
coeff's are instantiated even if they are not referenced.
|
||||
|
||||
- DONE: Add the `caw` examples to the test suite.
|
||||
|
||||
|
||||
- DONE: Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's.
|
||||
|
||||
- DONE: Compile presets: at load time the presets should be resolved
|
||||
to the proc and vars to which they will be assigned.
|
||||
|
||||
|
||||
- DONE: (We are not removing the kAnyChIdx)
|
||||
Should the var's with multiple channels remove the 'kAnyChIdx'?
|
||||
This may be a good idea because 'kAnyChIdx' will in general not be used
|
||||
if a var has been channelized - and yet it is possible for another
|
||||
var to connect to it as a source ... which doesn't provoke an error
|
||||
but would almost certainly not do what the user expects.
|
||||
Note that the kAnyChIdx provides an easy way to set all of the channels
|
||||
of a variable to the same value.
|
||||
|
||||
- DONE: verifiy that all proc variables values have a valid type - (i.e. (type & typeMask) != 0)
|
||||
when the proc instance create is complete. This checks that both the type is assigned and
|
||||
a valid value has been assigned - since the type is assigned the first time a value is set.
|
||||
|
||||
- DONE: 'poly' should be implemented as a proc-inst with an internal network - but the
|
||||
elements of the network should be visible outside of it.
|
||||
|
||||
- DONE: 'sub' should be implemented as proc-inst with an internal network, but the
|
||||
elements of the network should not be visible outside of it. Instead it should
|
||||
include the idea of input and output ports which act as proxies to the physical
|
||||
ports of the internal elements.
|
||||
|
||||
- DONE: 'poly' and 'sub' should be arbitrarily nestable.
|
||||
|
||||
- DONE: Allow multiple types on an input.
|
||||
For example 'adder' should have a single input
|
||||
which can by any numeric type.
|
||||
|
||||
|
||||
- DONE: Make a standard way to turn on output printing from any port on any instance
|
||||
This might be a better approach to logging than having a 'printer' object.
|
||||
Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } `
|
||||
|
||||
- Complete user-def-procs:
|
||||
+ User-Def-Procs should have presets written in terms of the user-def-proc vars rather than the network vars
|
||||
or the value application needs to follow the internal variable src_var back to the proxy var.
|
||||
|
||||
+ DONE: write a paragraph in the flow_doc.md about overall approach taken to user-def-proc implementation.
|
||||
|
||||
+ DONE: user-def-proc var desc's should be the same as non+user-def-proc vars but also include the 'proxy' field.
|
||||
In particular they should get default values.
|
||||
If a var desc is part of a user-def-proc then it must have a proxy.
|
||||
The output variables of var desc's must have the 'out' attribute
|
||||
|
||||
|
||||
+ DONE: improve the user-def-proc creating code by using consistent naming + use proxy or wrap but not both
|
||||
|
||||
+ DONE: improve code comments on user-def-proc creation
|
||||
|
||||
|
||||
- DONE: Implement feedback
|
||||
|
||||
- DONE: Implement the ability to set backward connections - from late to early proc's.
|
||||
This can be done by implementing the same process as 'in_stmt' but in a separate
|
||||
'out_stmt'. The difficulty is that it prevents doing some checks until the network
|
||||
is completely specified. For example if audio inputs can accept connections from
|
||||
later proc's then they will not have all of their inputs when they are instantiated.
|
||||
One way around this is to instantiate them with an initial set of inputs but then
|
||||
allow those inputs to be replaced by a later connection.
|
||||
|
||||
BUGS:
|
||||
- DONE: The counter modulo mode is not working as expected.
|
||||
|
||||
|
||||
|
||||
- DONE: Implement 'preset' proc. This will involve implementing the 'cfg' datatype.
|
||||
|
||||
- DONE: Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them?
|
||||
|
||||
- DONE: Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates?
|
||||
|
||||
- DONE: There is no way for a proc in a poly context to use it's poly channel number to
|
||||
select a mult variable. For example in an osc in a poly has no way to select
|
||||
the frequency of the osc by conneting to a non-poly proc - like a list.
|
||||
Consider:
|
||||
1. Use a difference 'in' statememt (e.g. 'poly-in' but the
|
||||
same syntax used for connecting 'mult' variables.)
|
||||
2. Include the proc name in the 'in' var to indicate a poly index is being iterated
|
||||
e.g. `lfo: { class:sine_tone, in:{ osc_.dc:list.value_ } }`
|
||||
|
||||
- DONE: Fix up the coding language - change the use of `instance_t` to `proc_t` and `inst` to `proc`, change use of `ctx` in cwFlowProc
|
||||
|
||||
|
||||
|
||||
DONE: After the network is fully instantiated the network and class presets
|
||||
are compiled. At this point all preset values must be resolvable to
|
||||
an actual proc variable. A warning is issued for presets with values
|
||||
that cannot be resolved and they are disabled. The primary reason
|
||||
that a preset might not be resolvable is by targetting a variable
|
||||
channel that does not exist.
|
||||
|
||||
- DONE: All cfg to value conversion should go through `cfg_to_value()`.
|
||||
|
||||
|
||||
Names
|
||||
------
|
||||
ixon -
|
||||
hoot
|
||||
caw, screech, warble, coo, peep, hoot, gobble, quack, honk, whistle, tweet, cheep, chirrup, trill, squawk, seet,
|
||||
cluck,cackle,clack
|
||||
cock-a-dooodle-doo
|
||||
song,tune,aria
|
@ -1196,7 +1196,7 @@ namespace cw
|
||||
|
||||
for(unsigned i=0; i<bufSmpCnt; dbp+=3, ++i)
|
||||
{
|
||||
int ismp = sbuf[i]*kMax24Bits;
|
||||
int ismp = (int)(sbuf[i]*(int)kMax24Bits);
|
||||
int_to_24(ismp,dbp);
|
||||
}
|
||||
|
||||
@ -1210,7 +1210,7 @@ namespace cw
|
||||
|
||||
for(unsigned i=0; i<bufSmpCnt; dbp+=3, ++i)
|
||||
{
|
||||
int ismp = sbuf[i]*kMax24Bits;
|
||||
int ismp = (int)(sbuf[i]*(int)kMax24Bits);
|
||||
int_to_24(ismp,dbp);
|
||||
}
|
||||
return _write_samples_to_file(p,sizeof(dbuf[0]),bufSmpCnt,dbuf);
|
||||
@ -1857,6 +1857,7 @@ cw::rc_t cw::audiofile::writeDouble( handle_t h, unsigned frmCnt, unsigned ch
|
||||
break;
|
||||
|
||||
case 24:
|
||||
assert(0);
|
||||
break;
|
||||
|
||||
case 32:
|
||||
|
@ -617,9 +617,9 @@ namespace cw
|
||||
{
|
||||
wnd_func::exec(p->wf, p->sb->outV, p->sb->wndSmpCnt );
|
||||
|
||||
// convert float to double
|
||||
T1 cvtV[ p->wf->wndN ];
|
||||
vop::copy(cvtV, p->wf->outV, p->wf->wndN );
|
||||
// convert float to double
|
||||
T1 cvtV[ p->wf->wndN ];
|
||||
vop::copy(cvtV, p->wf->outV, p->wf->wndN );
|
||||
|
||||
fft::exec(p->ft, cvtV, p->wf->wndN);
|
||||
|
||||
|
@ -98,7 +98,7 @@ cw::rc_t cw::dsp::compressor::destroy( obj_t*& p )
|
||||
See compressor.m
|
||||
*/
|
||||
|
||||
cw::rc_t cw::dsp::compressor::exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n )
|
||||
cw::rc_t cw::dsp::compressor::exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n, bool enable_fl )
|
||||
{
|
||||
|
||||
sample_t xx[n];
|
||||
@ -113,7 +113,7 @@ cw::rc_t cw::dsp::compressor::exec( obj_t* p, const sample_t* x, sample_t* y, un
|
||||
rmsDb += 100.0;
|
||||
|
||||
// if the compressor is bypassed
|
||||
if( p->bypassFl )
|
||||
if( p->bypassFl || !enable_fl )
|
||||
{
|
||||
vop::copy(y,x,n); // copy through - with no input gain
|
||||
return kOkRC;
|
||||
|
@ -40,7 +40,7 @@ namespace cw
|
||||
|
||||
rc_t create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t inGain, ftime_t rmsWndMaxMs, ftime_t rmsWndMs, coeff_t threshDb, coeff_t ratio, ftime_t atkMs, ftime_t rlsMs, coeff_t outGain, bool bypassFl );
|
||||
rc_t destroy( obj_t*& pp );
|
||||
rc_t exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n );
|
||||
rc_t exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n, bool enable_fl );
|
||||
|
||||
void set_attack_ms( obj_t* p, ftime_t ms );
|
||||
void set_release_ms( obj_t* p, ftime_t ms );
|
||||
|
@ -121,6 +121,27 @@ cw::rc_t cw::dyn_ref_tbl::create( handle_t& hRef, const object_t* cfg )
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::dyn_ref_tbl::create( handle_t& hRef, const char* cfg_fname )
|
||||
{
|
||||
rc_t rc;
|
||||
object_t* cfg = nullptr;
|
||||
|
||||
if((rc = objectFromFile( cfg_fname, cfg )) != kOkRC )
|
||||
{
|
||||
cwLogError(rc,"Dynamics reference table parse cfg. parse failed on '%s'.",cwStringNullGuard(cfg_fname));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
rc = create(hRef,cfg);
|
||||
|
||||
errLabel:
|
||||
cfg->free();
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::dyn_ref_tbl::destroy( handle_t& hRef )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
@ -19,6 +19,8 @@ namespace cw
|
||||
|
||||
// Parse object like: [ { mark:<> level:<>, vel:<> } ]
|
||||
rc_t create( handle_t& hRef, const object_t* cfg );
|
||||
rc_t create( handle_t& hRef, const char* cfg_fname );
|
||||
|
||||
rc_t destroy( handle_t& hRef );
|
||||
|
||||
const char* level_to_marker( handle_t h, unsigned level );
|
||||
|
145
cwFlow.cpp
145
cwFlow.cpp
@ -17,10 +17,11 @@
|
||||
#include "cwMidiDecls.h"
|
||||
#include "cwFlowDecl.h"
|
||||
#include "cwFlow.h"
|
||||
#include "cwFlowValue.h"
|
||||
#include "cwFlowTypes.h"
|
||||
#include "cwFlowNet.h"
|
||||
#include "cwFlowProc.h"
|
||||
|
||||
#include "cwFlowPerf.h"
|
||||
namespace cw
|
||||
{
|
||||
namespace flow
|
||||
@ -75,6 +76,12 @@ namespace cw
|
||||
{ "midi_split", &midi_split::members },
|
||||
{ "midi_file", &midi_file::members },
|
||||
{ "midi_merge", &midi_merge::members },
|
||||
{ "poly_xform_ctl", &poly_xform_ctl::members },
|
||||
{ "score_player", &score_player::members },
|
||||
{ "vel_table", &vel_table::members },
|
||||
{ "preset_select", &preset_select::members },
|
||||
{ "gutim_ps", &gutim_ps::members },
|
||||
{ "score_follower", &score_follower::members },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
@ -91,6 +98,7 @@ namespace cw
|
||||
{ return handleToPtr<handle_t,flow_t>(h); }
|
||||
|
||||
|
||||
/*
|
||||
rc_t _is_var_flag_set( const object_t* var_flags_obj, const char* flag_label, const char* classLabel, const char* varLabel, bool&is_set_flag_ref )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -116,7 +124,8 @@ namespace cw
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
rc_t _parse_udp_var_proxy_string( const char* proxyStr, var_desc_t* var_desc )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
@ -219,19 +228,14 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// get the variable description
|
||||
if((rc = vd->cfg->getv("doc", vd->docText)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing failed on class:%s variable: '%s'.", class_desc->label, vd->label );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// get the variable description
|
||||
if((rc = vd->cfg->getv_opt("flags", var_flags_obj,
|
||||
"type", var_value_type_str,
|
||||
"value", vd->val_cfg,
|
||||
"proxy", proxy_string )) != kOkRC )
|
||||
if((rc = vd->cfg->readv("doc", kReqFl, vd->docText,
|
||||
"flags", kOptFl, var_flags_obj,
|
||||
"type", kOptFl, var_value_type_str,
|
||||
"value", kOptFl, vd->val_cfg,
|
||||
"fmt", kOptFl, vd->fmt_cfg,
|
||||
"ui", kOptFl, vd->ui_cfg,
|
||||
"proxy", kOptFl, proxy_string )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing optional fields failed.");
|
||||
goto errLabel;
|
||||
@ -239,11 +243,23 @@ namespace cw
|
||||
|
||||
// convert the type string to a numeric type flag
|
||||
if( var_value_type_str != nullptr )
|
||||
{
|
||||
if( (vd->type = value_type_label_to_flag( var_value_type_str )) == kInvalidTId )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Invalid variable description type flag: '%s' was encountered.", var_value_type_str );
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
// if this is a 'record' type with a 'fmt' specifier
|
||||
if( vd->type & kRBufTFl && vd->fmt_cfg != nullptr )
|
||||
{
|
||||
if((rc = recd_format_create( vd->fmt.recd_fmt, vd->fmt_cfg )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The record type associated with the 'fmt' field could not be created.");
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
// parse the proxy string into it's two parts: <proc>.<var>
|
||||
if( proxy_string != nullptr )
|
||||
@ -290,6 +306,43 @@ namespace cw
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t _create_preset_list( class_preset_t*& presetL, const object_t* presetD )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// parse the preset dictionary
|
||||
if( presetD != nullptr )
|
||||
{
|
||||
if( !presetD->is_dict() )
|
||||
{
|
||||
rc = cwLogError(rc,"The preset dictionary is not a dictionary." );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// for each preset in the class desc.
|
||||
for(unsigned j=0; j<presetD->child_count(); ++j)
|
||||
{
|
||||
const object_t* pair = presetD->child_ele(j);
|
||||
|
||||
if( !pair->pair_value()->is_dict() )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The preset '%s' is not a dictionary.", cwStringNullGuard(pair->pair_label()));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
class_preset_t* preset = mem::allocZ< class_preset_t >();
|
||||
|
||||
preset->label = pair->pair_label();
|
||||
preset->cfg = pair->pair_value();
|
||||
preset->link = presetL;
|
||||
presetL = preset;
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _parse_class_cfg(flow_t* p, const object_t* classCfg)
|
||||
{
|
||||
@ -321,6 +374,7 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
/*
|
||||
// parse the preset dictionary
|
||||
if( presetD != nullptr )
|
||||
{
|
||||
@ -350,6 +404,13 @@ namespace cw
|
||||
cd->presetL = preset;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if((rc = _create_preset_list( cd->presetL, presetD )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The presets for the class desc: '%s' could not be parsed.",cwStringNullGuard(cd->label));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// create the class descripiton
|
||||
if((rc = _create_class_ui_desc(cd)) != kOkRC )
|
||||
@ -743,6 +804,18 @@ namespace cw
|
||||
return rc;
|
||||
|
||||
network_destroy(p->net);
|
||||
|
||||
global_var_t* gv=p->globalVarL;
|
||||
while( gv != nullptr )
|
||||
{
|
||||
global_var_t* gv0 = gv->link;
|
||||
mem::release(gv->var_label);
|
||||
mem::release(gv->blob);
|
||||
mem::release(gv);
|
||||
gv = gv0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_release_class_desc_array(p->classDescA,p->classDescN);
|
||||
_release_class_desc_array(p->udpDescA,p->udpDescN);
|
||||
@ -845,24 +918,26 @@ cw::rc_t cw::flow::create( handle_t& hRef,
|
||||
p->maxCycleCount = kInvalidCnt;
|
||||
p->proj_dir = proj_dir;
|
||||
p->printLogHdrFl = true;
|
||||
p->ui_create_fl = false;
|
||||
p->ui_callback = ui_callback;
|
||||
p->ui_callback_arg= ui_callback_arg;
|
||||
p->ui_var_head.store(&p->ui_var_stub);
|
||||
p->ui_var_tail = &p->ui_var_stub;
|
||||
|
||||
// parse the optional args
|
||||
if((rc = pgmCfg->readv("network", 0, p->networkCfg,
|
||||
"non_real_time_fl", kOptFl, p->non_real_time_fl,
|
||||
"frames_per_cycle", kOptFl, p->framesPerCycle,
|
||||
"sample_rate", kOptFl, p->sample_rate,
|
||||
"max_cycle_count", kOptFl, maxCycleCount,
|
||||
"dur_limit_secs", kOptFl, durLimitSecs,
|
||||
"preset", kOptFl, p->init_net_preset_label,
|
||||
"print_class_dict_fl", kOptFl, printClassDictFl,
|
||||
"print_network_fl", kOptFl, p->printNetworkFl,
|
||||
"multiPriPresetProbFl", kOptFl, p->multiPriPresetProbFl,
|
||||
"multiSecPresetProbFl", kOptFl, p->multiSecPresetProbFl,
|
||||
"multiPresetInterpFl", kOptFl, p->multiPresetInterpFl)) != kOkRC )
|
||||
if((rc = pgmCfg->readv("network", 0, p->networkCfg,
|
||||
"non_real_time_fl", kOptFl, p->non_real_time_fl,
|
||||
"frames_per_cycle", kOptFl, p->framesPerCycle,
|
||||
"sample_rate", kOptFl, p->sample_rate,
|
||||
"max_cycle_count", kOptFl, maxCycleCount,
|
||||
"dur_limit_secs", kOptFl, durLimitSecs,
|
||||
"ui_create_fl", kOptFl, p->ui_create_fl,
|
||||
"preset", kOptFl, p->init_net_preset_label,
|
||||
"print_class_dict_fl", kOptFl, printClassDictFl,
|
||||
"print_network_fl", kOptFl, p->printNetworkFl,
|
||||
"multiPriPresetProbFl", kOptFl, p->multiPriPresetProbFl,
|
||||
"multiSecPresetProbFl", kOptFl, p->multiSecPresetProbFl,
|
||||
"multiPresetInterpFl", kOptFl, p->multiPresetInterpFl)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Error parsing the network system parameters.");
|
||||
goto errLabel;
|
||||
@ -961,6 +1036,7 @@ cw::rc_t cw::flow::initialize( handle_t h,
|
||||
rc_t rc = kOkRC;
|
||||
variable_t* proxyVarL = nullptr;
|
||||
flow_t* p = _handleToPtr(h);
|
||||
const char* root_label = "root";
|
||||
|
||||
p->deviceA = deviceA;
|
||||
p->deviceN = deviceN;
|
||||
@ -993,12 +1069,17 @@ cw::rc_t cw::flow::initialize( handle_t h,
|
||||
}
|
||||
|
||||
// instantiate the network
|
||||
if((rc = network_create(p,&p->networkCfg,1,proxyVarL,1,p->init_net_preset_label,p->net)) != kOkRC )
|
||||
if((rc = network_create(p,&root_label,&p->networkCfg,1,proxyVarL,1,p->net)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Network creation failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if a network preset was specified apply it here
|
||||
if( p->init_net_preset_label != nullptr && p->net != nullptr )
|
||||
network_apply_preset(*p->net,p->init_net_preset_label);
|
||||
|
||||
|
||||
if( p->printNetworkFl && p->net != nullptr )
|
||||
network_print(*p->net);
|
||||
|
||||
@ -1100,6 +1181,16 @@ cw::rc_t cw::flow::exec( handle_t h )
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::flow::send_ui_updates( handle_t h )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
flow_t* p = _handleToPtr(h);
|
||||
|
||||
_make_flow_to_ui_callback(p);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::flow::apply_preset( handle_t h, const char* presetLabel )
|
||||
{
|
||||
|
8
cwFlow.h
8
cwFlow.h
@ -51,7 +51,13 @@ namespace cw
|
||||
rc_t exec_cycle( handle_t h );
|
||||
|
||||
// Run a non-real-time program to completion.
|
||||
rc_t exec( handle_t h );
|
||||
rc_t exec( handle_t h );
|
||||
|
||||
// Send any pending updates from the flow network to the UI.
|
||||
// This happens automatically if exec() exec_cycle() is called.
|
||||
// Calling this function is only necessary when the state of the
|
||||
// network is changed outside of runtime.
|
||||
rc_t send_ui_updates( handle_t h );
|
||||
|
||||
rc_t apply_preset( handle_t h, const char* presetLabel );
|
||||
rc_t apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff );
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "cwMidiDecls.h"
|
||||
#include "cwFlowDecl.h"
|
||||
#include "cwFlow.h"
|
||||
#include "cwFlowValue.h"
|
||||
#include "cwFlowTypes.h"
|
||||
#include "cwFlowProc.h"
|
||||
#include "cwFlowCross.h"
|
||||
|
13
cwFlowDecl.h
13
cwFlowDecl.h
@ -118,7 +118,7 @@ namespace cw
|
||||
const char* label; // flow::variable_t::label
|
||||
unsigned label_sfx_id; // flow::variable_t::label_sfx_id
|
||||
|
||||
const object_t* desc_cfg; // var desc cfg from flow::var_desc_t
|
||||
const object_t* ui_cfg; // ui cfg from flow::var_desc_t
|
||||
unsigned desc_flags; // flow::var_desc_t::flags
|
||||
|
||||
bool has_source_fl; // true if this var is connected to a source var
|
||||
@ -128,6 +128,13 @@ namespace cw
|
||||
unsigned ch_cnt; // 0=kAnyChIdx only, kInvalidCnt=no channels, 1=mono, 2=stereo, ...
|
||||
|
||||
unsigned user_id; // uuId of the UI element that represents this var
|
||||
|
||||
bool disable_fl; // true if this ui var is disabled
|
||||
bool hide_fl; // true if this ui var is hidden
|
||||
|
||||
bool new_disable_fl;
|
||||
bool new_hide_fl;
|
||||
|
||||
} ui_var_t;
|
||||
|
||||
struct proc_str;
|
||||
@ -156,6 +163,8 @@ namespace cw
|
||||
typedef struct ui_net_str
|
||||
{
|
||||
struct network_str* net;
|
||||
|
||||
bool ui_create_fl; // Set via network flag 'ui_create_fl'.
|
||||
|
||||
ui_proc_t* procA; // procA[procN]
|
||||
unsigned procN;
|
||||
@ -168,7 +177,7 @@ namespace cw
|
||||
|
||||
} ui_net_t;
|
||||
|
||||
typedef rc_t (*ui_callback_t)( void* arg, const ui_var_t* ui_var );
|
||||
typedef rc_t (*ui_callback_t)( void* arg, ui_var_t* ui_var );
|
||||
|
||||
|
||||
}
|
||||
|
815
cwFlowNet.cpp
815
cwFlowNet.cpp
File diff suppressed because it is too large
Load Diff
12
cwFlowNet.h
12
cwFlowNet.h
@ -12,12 +12,12 @@ namespace cw
|
||||
// The only time netCfgN will be greater than 1 is when a heterogenous poly network is being
|
||||
// instantiated.
|
||||
rc_t network_create( flow_t* p,
|
||||
const object_t* const * netCfgA, // netCfgA[netCfgN]
|
||||
unsigned netCfgN, // count of cfg. records in netCfgN
|
||||
variable_t* proxyVarL, //
|
||||
unsigned polyCnt, // Count of poly subnets to create or 1 if the network is not poly
|
||||
const char* preset_label, // Optional top-level preset label
|
||||
network_t*& net_ref // Returned network handle.
|
||||
const char* const * netLabelA, // netLabel[netCfgN]
|
||||
const object_t* const * netCfgA, // netCfgA[netCfgN]
|
||||
unsigned netCfgN, // count of cfg. records in netCfgN
|
||||
variable_t* proxyVarL, //
|
||||
unsigned polyCnt, // Count of poly subnets to create or 1 if the network is not poly
|
||||
network_t*& net_ref // Returned network handle.
|
||||
);
|
||||
|
||||
rc_t network_destroy( network_t*& net );
|
||||
|
2522
cwFlowPerf.cpp
Normal file
2522
cwFlowPerf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
12
cwFlowPerf.h
Normal file
12
cwFlowPerf.h
Normal file
@ -0,0 +1,12 @@
|
||||
namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
namespace score_player { extern class_members_t members; }
|
||||
namespace vel_table { extern class_members_t members; }
|
||||
namespace preset_select { extern class_members_t members; }
|
||||
namespace gutim_ps { extern class_members_t members; }
|
||||
namespace score_follower { extern class_members_t members; }
|
||||
|
||||
}
|
||||
}
|
1339
cwFlowProc.cpp
1339
cwFlowProc.cpp
File diff suppressed because it is too large
Load Diff
34
cwFlowProc.h
34
cwFlowProc.h
@ -4,6 +4,39 @@ namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
|
||||
template< typename inst_t >
|
||||
rc_t std_destroy( proc_t* proc )
|
||||
{
|
||||
inst_t* p = (inst_t*)proc->userPtr;
|
||||
rc_t rc = _destroy(proc,p);
|
||||
mem::release(proc->userPtr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template< typename inst_t >
|
||||
rc_t std_create( proc_t* proc )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
proc->userPtr = mem::allocZ<inst_t>();
|
||||
if((rc = _create(proc,(inst_t*)proc->userPtr)) != kOkRC )
|
||||
std_destroy<inst_t>(proc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template< typename inst_t >
|
||||
rc_t std_value( proc_t* proc, variable_t* var )
|
||||
{ return _value(proc,(inst_t*)proc->userPtr, var); }
|
||||
|
||||
template< typename inst_t >
|
||||
rc_t std_exec( proc_t* proc )
|
||||
{ return _exec(proc,(inst_t*)proc->userPtr); }
|
||||
|
||||
template< typename inst_t >
|
||||
rc_t std_report( proc_t* proc )
|
||||
{ return _report(proc,(inst_t*)proc->userPtr); }
|
||||
|
||||
|
||||
namespace user_def_proc { extern class_members_t members; }
|
||||
namespace poly { extern class_members_t members; }
|
||||
namespace midi_in { extern class_members_t members; }
|
||||
@ -48,6 +81,7 @@ namespace cw
|
||||
namespace midi_split { extern class_members_t members; }
|
||||
namespace midi_file { extern class_members_t members; }
|
||||
namespace midi_merge { extern class_members_t members; }
|
||||
namespace poly_xform_ctl { extern class_members_t members; }
|
||||
|
||||
}
|
||||
}
|
||||
|
1555
cwFlowTypes.cpp
1555
cwFlowTypes.cpp
File diff suppressed because it is too large
Load Diff
401
cwFlowTypes.h
401
cwFlowTypes.h
@ -5,128 +5,6 @@ namespace cw
|
||||
namespace flow
|
||||
{
|
||||
|
||||
typedef dsp::coeff_t coeff_t;
|
||||
typedef dsp::sample_t sample_t;
|
||||
typedef dsp::fd_sample_t fd_sample_t;
|
||||
typedef dsp::srate_t srate_t;
|
||||
typedef dsp::ftime_t ftime_t;
|
||||
typedef unsigned uint_t;
|
||||
typedef int int_t;
|
||||
|
||||
|
||||
typedef unsigned vid_t;
|
||||
|
||||
enum {
|
||||
kBaseSfxId = 0,
|
||||
kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz)
|
||||
kAnyChIdx = kInvalidIdx,
|
||||
kLocalValueN = 2,
|
||||
kDefaultFramesPerCycle=64,
|
||||
kDefaultSampleRate=48000
|
||||
};
|
||||
|
||||
typedef struct abuf_str
|
||||
{
|
||||
srate_t srate; // Signal sample rate
|
||||
unsigned chN; // Count of channels
|
||||
unsigned frameN; // Count of sample frames per channel
|
||||
unsigned bufAllocSmpN; // Size of allocated buf[] in samples.
|
||||
sample_t* buf; // buf[ chN ][ frameN ]
|
||||
} abuf_t;
|
||||
|
||||
|
||||
typedef struct fbuf_str
|
||||
{
|
||||
unsigned memByteN; // Count of bytes in mem[].
|
||||
void* mem; // mem[ memByteN ] All dynamically allocated memory used by this fbuf.
|
||||
|
||||
srate_t srate; // signal sample rate
|
||||
unsigned flags; // See kXXXFbufFl
|
||||
unsigned chN; // count of channels
|
||||
unsigned* maxBinN_V; // maxBinN_V[chN] max value that binN_V[i] is allowed to take
|
||||
unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel
|
||||
unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count
|
||||
fd_sample_t** magV; // magV[ chN ][ binN ]
|
||||
fd_sample_t** phsV; // phsV[ chN ][ binN ]
|
||||
fd_sample_t** hzV; // hzV[ chN ][ binN ]
|
||||
bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate)
|
||||
} fbuf_t;
|
||||
|
||||
typedef struct mbuf_str
|
||||
{
|
||||
const midi::ch_msg_t* msgA;
|
||||
unsigned msgN;
|
||||
} mbuf_t;
|
||||
|
||||
enum
|
||||
{
|
||||
kInvalidTFl = 0x00000000,
|
||||
kBoolTFl = 0x00000001,
|
||||
kUIntTFl = 0x00000002,
|
||||
kIntTFl = 0x00000004,
|
||||
kFloatTFl = 0x00000008,
|
||||
kDoubleTFl = 0x00000010,
|
||||
|
||||
kBoolMtxTFl = 0x00000020,
|
||||
kUIntMtxTFl = 0x00000040,
|
||||
kIntMtxTFl = 0x00000080,
|
||||
kFloatMtxTFl = 0x00000100,
|
||||
kDoubleMtxTFl= 0x00000200,
|
||||
|
||||
kABufTFl = 0x00000400,
|
||||
kFBufTFl = 0x00000800,
|
||||
kMBufTFl = 0x00001000,
|
||||
kStringTFl = 0x00002000,
|
||||
kTimeTFl = 0x00004000,
|
||||
kCfgTFl = 0x00008000,
|
||||
|
||||
kTypeMask = 0x0000ffff,
|
||||
|
||||
kRuntimeTFl = 0x80000000,
|
||||
|
||||
kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl,
|
||||
kMtxTFl = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl,
|
||||
kAllTFl = kTypeMask
|
||||
};
|
||||
|
||||
typedef struct mtx_str
|
||||
{
|
||||
union {
|
||||
struct mtx::mtx_str< unsigned >* u;
|
||||
struct mtx::mtx_str< int >* i;
|
||||
struct mtx::mtx_str< float >* f;
|
||||
struct mtx::mtx_str< double >* d;
|
||||
} u;
|
||||
} mtx_t;
|
||||
|
||||
typedef struct value_str
|
||||
{
|
||||
unsigned tflag;
|
||||
|
||||
union {
|
||||
bool b;
|
||||
uint_t u;
|
||||
int_t i;
|
||||
float f;
|
||||
double d;
|
||||
|
||||
mtx_t* mtx;
|
||||
abuf_t* abuf;
|
||||
fbuf_t* fbuf;
|
||||
mbuf_t* mbuf;
|
||||
|
||||
char* s;
|
||||
|
||||
const object_t* cfg;
|
||||
void* p;
|
||||
|
||||
} u;
|
||||
|
||||
struct value_str* link;
|
||||
|
||||
} value_t;
|
||||
|
||||
|
||||
struct proc_str;
|
||||
struct variable_str;
|
||||
|
||||
@ -136,13 +14,16 @@ namespace cw
|
||||
// var_desc_t attribute flags
|
||||
enum
|
||||
{
|
||||
kInvalidVarDescFl = 0x00,
|
||||
kSrcVarDescFl = 0x01,
|
||||
kSrcOptVarDescFl = 0x02,
|
||||
kNoSrcVarDescFl = 0x04,
|
||||
kInitVarDescFl = 0x08,
|
||||
kMultVarDescFl = 0x10,
|
||||
kUdpOutVarDescFl = 0x20
|
||||
kInvalidVarDescFl = 0x000,
|
||||
kSrcVarDescFl = 0x001,
|
||||
kSrcOptVarDescFl = 0x002,
|
||||
kNoSrcVarDescFl = 0x004,
|
||||
kInitVarDescFl = 0x008,
|
||||
kMultVarDescFl = 0x010,
|
||||
kUdpOutVarDescFl = 0x020,
|
||||
kUiCreateVarDescFl = 0x040,
|
||||
kUiDisableVarDescFl = 0x080,
|
||||
kUiHideVarDescFl = 0x100
|
||||
};
|
||||
|
||||
typedef struct class_members_str
|
||||
@ -153,15 +34,22 @@ namespace cw
|
||||
member_func_t exec;
|
||||
member_func_t report;
|
||||
} class_members_t;
|
||||
|
||||
|
||||
typedef struct var_desc_str
|
||||
{
|
||||
const object_t* cfg; // The cfg object that describes this variable from 'flow_class'.
|
||||
const object_t* val_cfg; // An object containing the default value for this variable.
|
||||
const object_t* fmt_cfg; // An object containg the format (e.g. record fields) information
|
||||
const object_t* ui_cfg; // The UI cfg for this var.
|
||||
const char* label; // Name of this var.
|
||||
unsigned type; // Value type id (e.g. kBoolTFl, kIntTFl, ...)
|
||||
unsigned flags; // Attributes for this var. (e.g. kSrcVarFl )
|
||||
const char* docText; // User help string for this var.
|
||||
|
||||
union
|
||||
{
|
||||
recd_fmt_t* recd_fmt; // the 'recd_type.base' is never set in 'recd_type' because it is only valid once the var is instantiated
|
||||
} fmt;
|
||||
|
||||
char* proxyProcLabel;
|
||||
char* proxyVarLabel;
|
||||
@ -191,7 +79,7 @@ namespace cw
|
||||
kInvalidVarFl = 0x00,
|
||||
kLogVarFl = 0x01,
|
||||
kProxiedVarFl = 0x02,
|
||||
kProxiedOutVarFl = 0x04
|
||||
kProxiedOutVarFl = 0x04,
|
||||
};
|
||||
|
||||
// Note: The concatenation of 'vid' and 'chIdx' should form a unique identifier among all variables
|
||||
@ -216,7 +104,7 @@ namespace cw
|
||||
unsigned local_value_idx; // local_value[] is double buffered to allow the cur value of the buf[] to be held while the next value is validated (see _var_set_template())
|
||||
struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value)
|
||||
value_t* value; // pointer to the value associated with this variable
|
||||
|
||||
|
||||
struct variable_str* var_link; // instance.varL list link
|
||||
struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number)
|
||||
|
||||
@ -230,18 +118,25 @@ namespace cw
|
||||
|
||||
|
||||
struct network_str;
|
||||
|
||||
enum {
|
||||
kUiCreateProcFl = 0x01
|
||||
};
|
||||
|
||||
typedef struct proc_str
|
||||
{
|
||||
struct flow_str* ctx; // global system context
|
||||
struct network_str* net; // network which owns this proc
|
||||
|
||||
unsigned flags; // See k???ProcFl
|
||||
|
||||
class_desc_t* class_desc; //
|
||||
|
||||
char* label; // instance label
|
||||
unsigned label_sfx_id; // label suffix id (set to kBaseSfxId (0) unless poly is non-null)
|
||||
|
||||
const object_t* proc_cfg; // instance configuration
|
||||
class_preset_t* presetL; // instance presets
|
||||
|
||||
void* userPtr; // instance state
|
||||
|
||||
@ -252,18 +147,43 @@ namespace cw
|
||||
unsigned varMapN; // varMapN = varMapIdN * varMapChN
|
||||
variable_t** varMapA; // varMapA[ varMapN ] = allows fast lookup from ('vid','chIdx) to variable
|
||||
|
||||
// For 'poly' proc's 'internal_net' is a list linked by network_t.poly_link.
|
||||
struct network_str* internal_net;
|
||||
unsigned internal_net_cnt; // count of hetergenous networks contained in the internal_net linked list.
|
||||
|
||||
} proc_t;
|
||||
|
||||
|
||||
// preset_value_t holds a preset value and the proc/var to which it will be applied.
|
||||
typedef struct preset_value_str
|
||||
struct network_preset_str;
|
||||
typedef struct proc_var_value_str
|
||||
{
|
||||
proc_t* proc; // proc target for this preset value
|
||||
variable_t* var; // var target for this preset value
|
||||
value_t value; // Preset value.
|
||||
unsigned pairTblIdx; // Index into the preset pair table for this preset value
|
||||
} proc_var_value_t;
|
||||
|
||||
typedef struct net_preset_ref_str
|
||||
{
|
||||
struct network_str* net_preset_net; // poly net this preset will be applied to
|
||||
const network_preset_str* net_preset; // network_preset_t of presets
|
||||
} net_preset_ref_t;
|
||||
|
||||
typedef enum {
|
||||
kDirectPresetValueTId,
|
||||
kNetRefPresetValueTId,
|
||||
} preset_val_tid_t;
|
||||
|
||||
// preset_value_t holds a preset value and the proc/var to which it will be applied.
|
||||
typedef struct preset_value_str
|
||||
{
|
||||
preset_val_tid_t tid;
|
||||
|
||||
union
|
||||
{
|
||||
proc_var_value_t pvv; // Direct proc/var/value tuples.
|
||||
net_preset_ref_t npv; // Refers to a network_preset_t and a list of preset_values.
|
||||
} u;
|
||||
|
||||
struct preset_value_str* link;
|
||||
} preset_value_t;
|
||||
|
||||
@ -273,8 +193,6 @@ namespace cw
|
||||
preset_value_t* value_tail; // Last preset value in the list.
|
||||
} preset_value_list_t;
|
||||
|
||||
struct network_preset_str;
|
||||
|
||||
typedef struct dual_preset_str
|
||||
{
|
||||
const struct network_preset_str* pri;
|
||||
@ -308,19 +226,20 @@ namespace cw
|
||||
const value_t* value; //
|
||||
} network_preset_pair_t;
|
||||
|
||||
typedef struct net_global_var_str
|
||||
typedef struct global_var_str
|
||||
{
|
||||
const char* class_label;
|
||||
char* var_label;
|
||||
void* blob;
|
||||
unsigned blobByteN;
|
||||
|
||||
struct net_global_var_str* link;
|
||||
|
||||
} net_global_var_t;
|
||||
struct global_var_str* link;
|
||||
} global_var_t;
|
||||
|
||||
typedef struct network_str
|
||||
{
|
||||
const char* label;
|
||||
|
||||
const object_t* procsCfg; // network proc list
|
||||
const object_t* presetsCfg; // presets designed for this network
|
||||
|
||||
@ -334,10 +253,10 @@ namespace cw
|
||||
network_preset_pair_t* preset_pairA;
|
||||
unsigned preset_pairN;
|
||||
|
||||
net_global_var_t* globalVarL;
|
||||
|
||||
struct network_str* poly_link;
|
||||
unsigned poly_idx;
|
||||
unsigned polyN; // Count of networks in poly net or 1 if not part of a poly net
|
||||
// (for het. poly net's this counts the number of net's for each het. array)
|
||||
unsigned poly_idx; // Index in poly net.
|
||||
struct network_str* poly_link; // Link to next net in poly.
|
||||
|
||||
ui_net_t* ui_net;
|
||||
|
||||
@ -369,8 +288,8 @@ namespace cw
|
||||
class_desc_t* classDescA; //
|
||||
unsigned classDescN; //
|
||||
|
||||
class_desc_t* udpDescA; //
|
||||
unsigned udpDescN; //
|
||||
class_desc_t* udpDescA; //
|
||||
unsigned udpDescN; //
|
||||
|
||||
external_device_t* deviceA; // deviceA[ deviceN ] external device description array
|
||||
unsigned deviceN; //
|
||||
@ -383,58 +302,21 @@ namespace cw
|
||||
|
||||
network_t* net; // The root of the network instance
|
||||
|
||||
ui_callback_t ui_callback;
|
||||
void* ui_callback_arg;
|
||||
bool ui_create_fl; // dflt: false Set by network flag: ui_create_fl, override with proc inst ui:{ create_fl }
|
||||
ui_callback_t ui_callback;
|
||||
void* ui_callback_arg;
|
||||
|
||||
std::atomic<variable_t*> ui_var_head; // Linked lists of var's to send to the UI
|
||||
variable_t ui_var_stub;
|
||||
variable_t* ui_var_tail;
|
||||
|
||||
global_var_t* globalVarL;
|
||||
|
||||
} flow_t;
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Value Only
|
||||
//
|
||||
|
||||
inline void set_null( value_t& v, unsigned tflag ) { v.tflag=tflag; v.u.p=nullptr; }
|
||||
inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->tflag,kNumericTFl); }
|
||||
inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->tflag,kMtxTFl); }
|
||||
|
||||
// if all of the src flags are set in the dst flags then the two types are convertable.
|
||||
inline bool can_convert( unsigned src_tflag, unsigned dst_tflag ) { return (src_tflag&dst_tflag)==src_tflag; }
|
||||
|
||||
|
||||
|
||||
abuf_t* abuf_create( srate_t srate, unsigned chN, unsigned frameN );
|
||||
void abuf_destroy( abuf_t*& buf );
|
||||
|
||||
// If 'dst' is null then a new abuf is allocated, filled with the contents of 'src'.
|
||||
// If 'dst' is non-null and there is enough space for the contents of 'src' then only a copy is executed.
|
||||
// If there is not enough space then dst is reallocated.
|
||||
abuf_t* abuf_duplicate( abuf_t* dst, const abuf_t* src );
|
||||
rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN );
|
||||
const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx );
|
||||
|
||||
fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
void fbuf_destroy( fbuf_t*& buf );
|
||||
|
||||
// Memory allocation will only occur if dst is null, or the size of dst's internal buffer are too small.
|
||||
fbuf_t* fbuf_duplicate( fbuf_t* dst, const fbuf_t* src );
|
||||
|
||||
mbuf_t* mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 );
|
||||
void mbuf_destroy( mbuf_t*& buf );
|
||||
mbuf_t* mbuf_duplicate( const mbuf_t* src );
|
||||
|
||||
inline bool value_is_abuf( const value_t* v ) { return v->tflag & kABufTFl; }
|
||||
inline bool value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; }
|
||||
|
||||
unsigned value_type_label_to_flag( const char* type_desc );
|
||||
const char* value_type_flag_to_label( unsigned flag );
|
||||
|
||||
void value_duplicate( value_t& dst, const value_t& src );
|
||||
|
||||
void value_print( const value_t* value, bool info_fl=false);
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
@ -448,16 +330,108 @@ namespace cw
|
||||
const char* var_desc_flag_to_attribute( unsigned flag );
|
||||
const idLabelPair_t* var_desc_flag_array( unsigned& array_cnt_ref );
|
||||
|
||||
void class_desc_destroy( class_desc_t* class_desc);
|
||||
class_desc_t* class_desc_find( flow_t* p, const char* class_desc_label );
|
||||
void class_desc_destroy( class_desc_t* class_desc);
|
||||
class_desc_t* class_desc_find( flow_t* p, const char* class_desc_label );
|
||||
const class_desc_t* class_desc_find( const flow_t* p, const char* class_desc_label );
|
||||
|
||||
var_desc_t* var_desc_find( class_desc_t* cd, const char* var_label );
|
||||
const var_desc_t* var_desc_find( const class_desc_t* cd, const char* var_label );
|
||||
rc_t var_desc_find( class_desc_t* cd, const char* var_label, var_desc_t*& vdRef );
|
||||
|
||||
const class_preset_t* class_preset_find( const class_desc_t* cd, const char* preset_label );
|
||||
bool var_desc_has_recd_format( var_desc_t* vd );
|
||||
|
||||
void class_dict_print( flow_t* p );
|
||||
const class_preset_t* class_preset_find( const class_desc_t* cd, const char* preset_label );
|
||||
|
||||
template<class T>
|
||||
rc_t class_preset_value( const class_preset_t* class_preset, const char* var_label, unsigned ch_idx, T& val )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const object_t* preset_val = nullptr;
|
||||
|
||||
if((rc = class_preset->cfg->get(var_label, preset_val )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The preset variable '%s' on the preset '%s' could not be parsed.",cwStringNullGuard(var_label),cwStringNullGuard(class_preset->label));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if( preset_val->is_list() )
|
||||
{
|
||||
if( ch_idx >= preset_val->child_count() || (preset_val = preset_val->child_ele( ch_idx )) == nullptr )
|
||||
{
|
||||
rc = cwLogError(rc,"The channel index '%i' is invalid for the preset '%s:%s'.",ch_idx,cwStringNullGuard(class_preset->label),cwStringNullGuard(var_label));
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if((rc = preset_val->value( val )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The preset value '%s:%s ch:%i' could not be parsed.",cwStringNullGuard(class_preset->label),cwStringNullGuard(var_label),ch_idx);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
rc_t class_preset_value( const class_desc_t* class_desc, const char* class_preset_label, const char* var_label, unsigned ch_idx, T& val )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const class_preset_t* class_preset = nullptr;
|
||||
|
||||
if((class_preset = class_preset_find(class_desc, class_preset_label)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kEleNotFoundRC,"The preset '%s' could not be found on the class description: '%s'.",cwStringNullGuard(class_preset_label),cwStringNullGuard(class_desc->label));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
rc = class_preset_value(class_preset, var_label, ch_idx, val );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
rc_t class_preset_value( const flow_t* p, const char* class_desc_label, const char* class_preset_label, const char* var_label, unsigned ch_idx, T& val )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const class_desc_t* class_desc;
|
||||
if((class_desc = class_desc_find(p,class_desc_label)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kEleNotFoundRC,"The class description '%s' could not be found.",cwStringNullGuard(class_desc_label));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
rc = class_preset_value( class_desc, class_preset_label, var_label, ch_idx, val );
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc_t class_preset_value_channel_count( const class_preset_t* class_preset, const char* var_label, unsigned& ch_cnt_ref );
|
||||
rc_t class_preset_value_channel_count( const class_desc_t* class_desc, const char* class_preset_label, const char* var_label, unsigned& ch_cnt_ref );
|
||||
rc_t class_preset_value_channel_count( const flow_t* p, const char* class_desc_label, const char* class_preset_label, const char* var_label, unsigned& ch_cnt_ref );
|
||||
|
||||
|
||||
rc_t class_preset_has_var( const class_preset_t* class_preset, const char* var_label, bool& fl_ref );
|
||||
rc_t class_preset_has_var( const class_desc_t* class_desc, const char* class_preset_label, const char* var_label, bool& fl_ref );
|
||||
rc_t class_preset_has_var( const flow_t* p, const char* class_desc_label, const char* class_preset_label, const char* var_label, bool& fl_ref );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Flow
|
||||
//
|
||||
|
||||
void class_dict_print( flow_t* p );
|
||||
|
||||
external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr );
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
@ -465,12 +439,6 @@ namespace cw
|
||||
// Network
|
||||
//
|
||||
|
||||
// Access a blob stored via network_global_var()
|
||||
void* network_global_var( proc_t* proc, const char* var_label );
|
||||
|
||||
// Copy a named blob into the network global variable space.
|
||||
rc_t network_global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN );
|
||||
|
||||
|
||||
void network_print(const network_t& net );
|
||||
|
||||
@ -479,9 +447,6 @@ namespace cw
|
||||
unsigned proc_mult_count( const network_t& net, const char* proc_label );
|
||||
|
||||
rc_t proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref );
|
||||
|
||||
unsigned network_poly_count( const network_t& net );
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
@ -494,8 +459,14 @@ namespace cw
|
||||
proc_t* proc_find( network_t& net, const char* proc_label, unsigned sfx_id );
|
||||
rc_t proc_find( network_t& net, const char* proc_label, unsigned sfx_id, proc_t*& procPtrRef );
|
||||
|
||||
external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr );
|
||||
const class_preset_t* proc_preset_find( const proc_t* cd, const char* preset_label );
|
||||
|
||||
// Access a blob stored via global_var()
|
||||
void* global_var( proc_t* proc, const char* var_label );
|
||||
|
||||
// Copy a named blob into the network global variable space.
|
||||
rc_t global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN );
|
||||
|
||||
void proc_print( proc_t* proc );
|
||||
|
||||
// Count of all var instances on this proc. This is a count of the length of proc->varL.
|
||||
@ -546,6 +517,9 @@ namespace cw
|
||||
// Return true if this var is acting as a source for another var.
|
||||
bool is_a_source_var( const variable_t* var );
|
||||
|
||||
// Returns true if var->varDesc->fmt.recd_fmt is valid.
|
||||
bool var_has_recd_format( const variable_t* var );
|
||||
|
||||
// Connect in_var to src_var.
|
||||
void var_connect( variable_t* src_var, variable_t* in_var );
|
||||
|
||||
@ -562,6 +536,8 @@ namespace cw
|
||||
// Send a variable value to the UI
|
||||
rc_t var_send_to_ui( variable_t* var );
|
||||
rc_t var_send_to_ui( proc_t* proc, unsigned vid, unsigned chIdx );
|
||||
rc_t var_send_to_ui_enable( proc_t* proc, unsigned vid, unsigned chIdx, bool enable_fl );
|
||||
rc_t var_send_to_ui_show( proc_t* proc, unsigned vid, unsigned chIdx, bool show_fl );
|
||||
|
||||
//-----------------
|
||||
//
|
||||
@ -641,7 +617,19 @@ namespace cw
|
||||
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN );
|
||||
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const recd_type_t* recd_type, recd_t* recdA, unsigned recdN );
|
||||
|
||||
// Alloc a recd_array, using an internal call to var_alloc_recd_array(), and assign all records to the specified variable.
|
||||
// The caller is responsible for destroying (recd_array_destroy()) the returned recd_array.
|
||||
rc_t var_alloc_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const recd_type_t* baseh, recd_array_t*& recd_arrray_ref, unsigned recdN=0 );
|
||||
|
||||
|
||||
// Alloc the recd_array_t based on the recd_type defined by the variable. Set recdN to a non-zero value to override the 'alloc_cnt'
|
||||
// which may have been set in the variables user provided cfg.
|
||||
// The caller is responsible for destroying (recd_array_destroy()) the returned recd_array.
|
||||
rc_t var_alloc_record_array( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const recd_type_t* base, recd_array_t*& recd_array_ref, unsigned recdN=0 );
|
||||
|
||||
|
||||
inline rc_t _var_register_and_set(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
|
||||
|
||||
template< typename T0, typename T1, typename T2, typename... ARGS >
|
||||
@ -685,7 +673,6 @@ namespace cw
|
||||
rc_t var_channel_count( proc_t* proc, const char* label, unsigned sfx_idx, unsigned& chCntRef );
|
||||
rc_t var_channel_count( const variable_t* var, unsigned& chCntRef );
|
||||
|
||||
rc_t cfg_to_value( const object_t* cfg, value_t& value_ref );
|
||||
|
||||
|
||||
//
|
||||
@ -704,6 +691,8 @@ namespace cw
|
||||
rc_t var_get( variable_t* var, fbuf_t*& valRef );
|
||||
rc_t var_get( const variable_t* var, const mbuf_t*& valRef );
|
||||
rc_t var_get( variable_t* var, mbuf_t*& valRef );
|
||||
rc_t var_get( const variable_t* var, const rbuf_t*& valRef );
|
||||
rc_t var_get( variable_t* var, rbuf_t*& valRef );
|
||||
rc_t var_get( const variable_t* var, const object_t*& valRef );
|
||||
|
||||
template< typename T>
|
||||
@ -742,6 +731,7 @@ namespace cw
|
||||
rc_t var_set( variable_t* var, abuf_t* val );
|
||||
rc_t var_set( variable_t* var, fbuf_t* val );
|
||||
rc_t var_set( variable_t* var, mbuf_t* val );
|
||||
rc_t var_set( variable_t* var, rbuf_t* val );
|
||||
rc_t var_set( variable_t* var, const object_t* val );
|
||||
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const value_t* val );
|
||||
@ -754,6 +744,7 @@ namespace cw
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, abuf_t* val );
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, fbuf_t* val );
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, mbuf_t* val );
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, rbuf_t* val );
|
||||
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val );
|
||||
|
||||
|
||||
|
1938
cwFlowValue.cpp
Normal file
1938
cwFlowValue.cpp
Normal file
File diff suppressed because it is too large
Load Diff
434
cwFlowValue.h
Normal file
434
cwFlowValue.h
Normal file
@ -0,0 +1,434 @@
|
||||
#ifndef cwFlowValue_h
|
||||
#define cwFLowValue_h
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
typedef dsp::coeff_t coeff_t;
|
||||
typedef dsp::sample_t sample_t;
|
||||
typedef dsp::fd_sample_t fd_sample_t;
|
||||
typedef dsp::srate_t srate_t;
|
||||
typedef dsp::ftime_t ftime_t;
|
||||
typedef unsigned uint_t;
|
||||
typedef int int_t;
|
||||
|
||||
typedef unsigned vid_t;
|
||||
|
||||
enum {
|
||||
kBaseSfxId = 0,
|
||||
kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz)
|
||||
kAnyChIdx = kInvalidIdx,
|
||||
kLocalValueN = 2,
|
||||
kDefaultFramesPerCycle=64,
|
||||
kDefaultSampleRate=48000
|
||||
};
|
||||
|
||||
typedef struct abuf_str
|
||||
{
|
||||
srate_t srate; // Signal sample rate
|
||||
unsigned chN; // Count of channels
|
||||
unsigned frameN; // Count of sample frames per channel
|
||||
unsigned bufAllocSmpN; // Size of allocated buf[] in samples.
|
||||
sample_t* buf; // buf[ chN * frameN ] ch0: 0:frameN, ch1: frameN:2*frame, ...
|
||||
} abuf_t;
|
||||
|
||||
|
||||
typedef struct fbuf_str
|
||||
{
|
||||
unsigned memByteN; // Count of bytes in mem[].
|
||||
void* mem; // mem[ memByteN ] All dynamically allocated memory used by this fbuf.
|
||||
|
||||
srate_t srate; // signal sample rate
|
||||
unsigned flags; // See kXXXFbufFl
|
||||
unsigned chN; // count of channels
|
||||
unsigned* maxBinN_V; // maxBinN_V[chN] max value that binN_V[i] is allowed to take
|
||||
unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel
|
||||
unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count
|
||||
fd_sample_t** magV; // magV[ chN ][ binN ]
|
||||
fd_sample_t** phsV; // phsV[ chN ][ binN ]
|
||||
fd_sample_t** hzV; // hzV[ chN ][ binN ]
|
||||
bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate)
|
||||
} fbuf_t;
|
||||
|
||||
typedef struct mbuf_str
|
||||
{
|
||||
const midi::ch_msg_t* msgA;
|
||||
unsigned msgN;
|
||||
} mbuf_t;
|
||||
|
||||
enum
|
||||
{
|
||||
kInvalidTFl = 0x00000000,
|
||||
kBoolTFl = 0x00000001,
|
||||
kUIntTFl = 0x00000002,
|
||||
kIntTFl = 0x00000004,
|
||||
kFloatTFl = 0x00000008,
|
||||
kDoubleTFl = 0x00000010,
|
||||
|
||||
kBoolMtxTFl = 0x00000020,
|
||||
kUIntMtxTFl = 0x00000040,
|
||||
kIntMtxTFl = 0x00000080,
|
||||
kFloatMtxTFl = 0x00000100,
|
||||
kDoubleMtxTFl= 0x00000200,
|
||||
|
||||
kABufTFl = 0x00000400,
|
||||
kFBufTFl = 0x00000800,
|
||||
kMBufTFl = 0x00001000,
|
||||
kRBufTFl = 0x00002000,
|
||||
kStringTFl = 0x00004000,
|
||||
kTimeTFl = 0x00008000,
|
||||
kCfgTFl = 0x00010000,
|
||||
kMidiTFl = 0x00020000,
|
||||
|
||||
kTypeMask = 0x0003ffff,
|
||||
|
||||
kRuntimeTFl = 0x80000000, // The type of the value associated with this variable will be set by the proc instances during instantiation
|
||||
|
||||
kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl,
|
||||
kMtxTFl = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl,
|
||||
kAllTFl = kTypeMask
|
||||
};
|
||||
|
||||
typedef struct mtx_str
|
||||
{
|
||||
union {
|
||||
struct mtx::mtx_str< unsigned >* u;
|
||||
struct mtx::mtx_str< int >* i;
|
||||
struct mtx::mtx_str< float >* f;
|
||||
struct mtx::mtx_str< double >* d;
|
||||
} u;
|
||||
} mtx_t;
|
||||
|
||||
struct recd_type_str;
|
||||
struct recd_str;
|
||||
typedef struct rbuf_str
|
||||
{
|
||||
const struct recd_type_str* type; // all msgs are formed from this type
|
||||
const struct recd_str* recdA; // recdA[ recdN ]
|
||||
unsigned recdN; //
|
||||
} rbuf_t;
|
||||
|
||||
typedef struct value_str
|
||||
{
|
||||
unsigned tflag;
|
||||
|
||||
union {
|
||||
bool b;
|
||||
uint_t u;
|
||||
int_t i;
|
||||
float f;
|
||||
double d;
|
||||
|
||||
mtx_t* mtx;
|
||||
abuf_t* abuf;
|
||||
fbuf_t* fbuf;
|
||||
mbuf_t* mbuf;
|
||||
rbuf_t* rbuf;
|
||||
|
||||
char* s;
|
||||
|
||||
const object_t* cfg;
|
||||
midi::ch_msg_t* midi;
|
||||
void* p;
|
||||
|
||||
|
||||
} u;
|
||||
|
||||
struct value_str* link;
|
||||
|
||||
} value_t;
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Value Only
|
||||
//
|
||||
|
||||
inline void set_null( value_t& v, unsigned tflag ) { v.tflag=tflag; v.u.p=nullptr; }
|
||||
inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->tflag,kNumericTFl); }
|
||||
inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->tflag,kMtxTFl); }
|
||||
|
||||
// if all of the src flags are set in the dst flags then the two types are convertable.
|
||||
inline bool can_convert( unsigned src_tflag, unsigned dst_tflag ) { return (src_tflag&dst_tflag)==src_tflag; }
|
||||
|
||||
|
||||
abuf_t* abuf_create( srate_t srate, unsigned chN, unsigned frameN );
|
||||
void abuf_destroy( abuf_t*& buf );
|
||||
|
||||
// If 'dst' is null then a new abuf is allocated, filled with the contents of 'src'.
|
||||
// If 'dst' is non-null and there is enough space for the contents of 'src' then only a copy is executed.
|
||||
// If there is not enough space then dst is reallocated.
|
||||
abuf_t* abuf_duplicate( abuf_t* dst, const abuf_t* src );
|
||||
void abuf_zero( abuf_t* buf );
|
||||
rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN );
|
||||
const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx );
|
||||
|
||||
fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
|
||||
void fbuf_zero( fbuf_t* fbuf );
|
||||
void fbuf_destroy( fbuf_t*& buf );
|
||||
|
||||
// Memory allocation will only occur if dst is null, or the size of dst's internal buffer are too small.
|
||||
fbuf_t* fbuf_duplicate( fbuf_t* dst, const fbuf_t* src );
|
||||
|
||||
mbuf_t* mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 );
|
||||
void mbuf_destroy( mbuf_t*& buf );
|
||||
mbuf_t* mbuf_duplicate( const mbuf_t* src );
|
||||
|
||||
rbuf_t* rbuf_create( const struct recd_type_str* type=nullptr, const struct recd_str* recdA=nullptr, unsigned recdN=0 );
|
||||
void rbuf_destroy( rbuf_t*& buf );
|
||||
rbuf_t* rbuf_duplicate( const rbuf_t* src );
|
||||
void rbuf_setup( rbuf_t* rbuf, struct recd_type_str* type, struct recd_str* recdA, unsigned recdN );
|
||||
|
||||
|
||||
inline bool value_is_abuf( const value_t* v ) { return v->tflag & kABufTFl; }
|
||||
inline bool value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; }
|
||||
|
||||
unsigned value_type_label_to_flag( const char* type_desc );
|
||||
const char* value_type_flag_to_label( unsigned flag );
|
||||
|
||||
void value_release( value_t* v );
|
||||
void value_duplicate( value_t& dst, const value_t& src );
|
||||
|
||||
// For all numeric types this function set's the type of 'value_ref' to the type of cfg.
|
||||
// For all other types sets the type of 'value_ref' to kCfgTFl and stores 'cfg' to value_ref.u.cfg;
|
||||
rc_t value_from_cfg( const object_t* cfg, value_t& value_ref );
|
||||
|
||||
// Assigns src to dst. If dst has a value type then src is converted to this type.
|
||||
// If the conversion is not possible then the function fail.s
|
||||
rc_t value_from_value( const value_t& src, value_t& dst );
|
||||
|
||||
void value_print( const value_t* value, bool info_fl=false);
|
||||
|
||||
rc_t value_get( const value_t* val, bool& valRef );
|
||||
rc_t value_set( value_t* val, bool v );
|
||||
|
||||
rc_t value_get( const value_t* val, uint_t& valRef );
|
||||
rc_t value_set( value_t* val, uint_t v );
|
||||
|
||||
rc_t value_get( const value_t* val, int_t& valRef );
|
||||
rc_t value_set( value_t* val, int_t v );
|
||||
|
||||
rc_t value_get( const value_t* val, float& valRef );
|
||||
rc_t value_set( value_t* val, float v );
|
||||
|
||||
rc_t value_get( const value_t* val, double& valRef );
|
||||
rc_t value_set( value_t* val, double v );
|
||||
|
||||
rc_t value_get( const value_t* val, const char*& valRef );
|
||||
rc_t value_set( value_t* val, const char* v );
|
||||
|
||||
rc_t value_get( value_t* val, abuf_t*& valRef );
|
||||
rc_t value_get( value_t* val, const abuf_t*& valRef );
|
||||
rc_t value_set( value_t* val, abuf_t* v );
|
||||
|
||||
rc_t value_get( value_t* val, fbuf_t*& valRef );
|
||||
rc_t value_get( value_t* val, const fbuf_t*& valRef );
|
||||
rc_t value_set( value_t* val, fbuf_t* v );
|
||||
|
||||
rc_t value_get( value_t* val, mbuf_t*& valRef );
|
||||
rc_t value_get( value_t* val, const mbuf_t*& valRef );
|
||||
rc_t value_set( value_t* val, mbuf_t* v );
|
||||
|
||||
rc_t value_get( value_t* val, rbuf_t*& valRef );
|
||||
rc_t value_get( value_t* val, const rbuf_t*& valRef );
|
||||
rc_t value_set( value_t* val, rbuf_t* v );
|
||||
|
||||
rc_t value_get( value_t* val, const object_t*& valRef );
|
||||
rc_t value_set( value_t* val, const object_t* v );
|
||||
|
||||
rc_t value_get( const value_t* val, midi::ch_msg_t*& valRef );
|
||||
rc_t value_get( const value_t* val, const midi::ch_msg_t*& valRef );
|
||||
rc_t value_set( value_t* val, midi::ch_msg_t* v );
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Record
|
||||
//
|
||||
|
||||
typedef struct recd_field_str
|
||||
{
|
||||
bool group_fl; // set if this field record is a group
|
||||
char* label; // field or group label
|
||||
value_t value; // default value for this field
|
||||
char* doc; // documentation field for this field
|
||||
union
|
||||
{
|
||||
unsigned index; // index into recd_t.valA of the value associated with this field
|
||||
struct recd_field_str* group_fieldL;
|
||||
} u;
|
||||
|
||||
struct recd_field_str* link;
|
||||
} recd_field_t;
|
||||
|
||||
typedef struct recd_type_str
|
||||
{
|
||||
recd_field_t* fieldL; // linked list of field spec's
|
||||
unsigned fieldN; // length of fieldL list (fieldN + base->fieldN) is total field count
|
||||
const struct recd_type_str* base; // base recd type that this field inherits from
|
||||
} recd_type_t;
|
||||
|
||||
typedef struct recd_fmt_str
|
||||
{
|
||||
unsigned alloc_cnt; // count of records to pre-allocate
|
||||
const object_t* req_fieldL; // label of required fields
|
||||
recd_type_t* recd_type; // record type for this variable
|
||||
} recd_fmt_t;
|
||||
|
||||
typedef struct recd_array_str
|
||||
{
|
||||
recd_type_t* type; // recd_type_t of this record array
|
||||
value_t* valA; // valA[ allocRecdN * type->fieldN ]
|
||||
struct recd_str* recdA; // recdA[ allocRecdN ]
|
||||
unsigned allocRecdN; //
|
||||
} recd_array_t;
|
||||
|
||||
typedef struct recd_str
|
||||
{
|
||||
struct value_str* valA; // varA[ recd_type_t.fieldN ] array of field values
|
||||
const struct recd_str* base; // Pointer to the records inherited fields.
|
||||
} recd_t;
|
||||
|
||||
|
||||
// Create/destroy a recd_format_t object.
|
||||
rc_t recd_format_create( recd_fmt_t*& recd_fmt_ref, const object_t* cfg, unsigned dflt_alloc_cnt=32 );
|
||||
void recd_format_destroy( recd_fmt_t*& recd_fmt_ref );
|
||||
|
||||
// Create a recd_type_t instance from a cfg. description.
|
||||
rc_t recd_type_create( recd_type_t*& recd_type_ref, const recd_type_t* base_type, const object_t* cfg );
|
||||
void recd_type_destroy( recd_type_t*& recd_type );
|
||||
|
||||
// Count of fields combined local and base record types.
|
||||
rc_t recd_type_max_field_count( const recd_type_t* recd_type );
|
||||
|
||||
// Get the field index associated with a named field.
|
||||
// Use '.' notation to separate groups from fields.
|
||||
// Note if this is a 'local' field then the high bit in the returned index will be set.
|
||||
unsigned recd_type_field_index( const recd_type_t* recd_type, const char* field_label);
|
||||
|
||||
// Given a field index return the field label.
|
||||
const char* recd_type_field_index_to_label( const recd_type_t* recd_type, unsigned field_idx );
|
||||
|
||||
// Set the record base pointer and the value of all fields with default values.
|
||||
rc_t recd_init( const recd_type_t* recd_type, const recd_t* base, recd_t* r );
|
||||
|
||||
// Print the recd_type info. to the console.
|
||||
void recd_type_print( const recd_type_t* recd_type );
|
||||
|
||||
|
||||
// Read the value from a single record field
|
||||
template< typename T >
|
||||
rc_t recd_get( const recd_type_t* type, const recd_t* recd, unsigned field_idx, T& val_ref )
|
||||
{
|
||||
if( field_idx < type->fieldN )
|
||||
return value_get( recd->valA + field_idx, val_ref );
|
||||
|
||||
return recd_get( type->base, recd->base, field_idx - type->fieldN, val_ref );
|
||||
}
|
||||
|
||||
inline rc_t _recd_get(const recd_type_t* recd_type, recd_t* r ) { return kOkRC; }
|
||||
|
||||
template< typename T1, typename... ARGS >
|
||||
rc_t _recd_get( const recd_type_t* recd_type, const recd_t* recd, unsigned field_idx, T1& val, ARGS&&... args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if((rc = recd_get(recd_type,recd,field_idx,val)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
return _recd_get(recd_type,recd,std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
// Read the value of multiple record fields.
|
||||
template< typename T1, typename... ARGS >
|
||||
rc_t recd_get( const recd_type_t* recd_type, const recd_t* recd, unsigned field_idx, T1& val, ARGS&&... args )
|
||||
{
|
||||
return _recd_get(recd_type,recd,field_idx,val,args...);
|
||||
}
|
||||
|
||||
// Set the base record pointer for a record with an inherited base
|
||||
inline rc_t recd_set_base( const recd_type_t* type, recd_t* recd, const recd_t* base )
|
||||
{
|
||||
// if we are setting base then the type must have a base type
|
||||
assert( (type->base == nullptr && base==nullptr) || (type->base!=nullptr && base!=nullptr) );
|
||||
|
||||
recd->base = base;
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
rc_t recd_set( const recd_type_t* type, const recd_t* base, recd_t* recd, unsigned field_idx, const T& val )
|
||||
{
|
||||
if( field_idx >= type->fieldN )
|
||||
return cwLogError(kInvalidArgRC,"Only 'local' record value may be set.");
|
||||
|
||||
// set the base of this record
|
||||
recd_set_base(type,recd,base);
|
||||
|
||||
return value_set( recd->valA + field_idx, val );
|
||||
}
|
||||
|
||||
inline rc_t _recd_set( const recd_type_t* recd_type, recd_t* recd ) { return kOkRC; }
|
||||
|
||||
template< typename T1, typename... ARGS >
|
||||
rc_t _recd_set( const recd_type_t* recd_type, recd_t* recd, unsigned field_idx, const T1& val, ARGS&&... args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( field_idx >= recd_type->fieldN )
|
||||
return cwLogError(kInvalidArgRC,"Fields in the inherited record may not be set.");
|
||||
|
||||
if((rc = value_set( recd->valA + field_idx, val)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Field set failed on '%s'.", cwStringNullGuard(recd_type_field_index_to_label( recd_type, field_idx )));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
return _recd_set(recd_type,recd,std::forward<ARGS>(args)...);
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Set multiple fields of a record.
|
||||
template< typename T1, typename... ARGS >
|
||||
rc_t recd_set( const recd_type_t* recd_type, const recd_t* base, recd_t* recd, unsigned field_idx, const T1& val, ARGS&&... args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
if((rc = recd_init( recd_type, base, recd )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
|
||||
if((rc = _recd_set(recd_type,recd,field_idx,val,args...)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
// Print a record to the console.
|
||||
rc_t recd_print( const recd_type_t* recd_type, const recd_t* r );
|
||||
|
||||
// Create/destroy a buffer of records.
|
||||
rc_t recd_array_create( recd_array_t*& recd_array_ref, recd_type_t* recd_type, const recd_type_t* base, unsigned allocRecdN );
|
||||
rc_t recd_array_destroy( recd_array_t*& recd_array_ref );
|
||||
|
||||
// Copy records into a recd_array. This function fails if there are less than
|
||||
// 'src_recdN' records already allocated in 'dest_recd_array'.
|
||||
// The source and destination record types should be the same, but this
|
||||
// function does very little to verify that the actually are.
|
||||
rc_t recd_copy( const recd_type_t* src_recd_type, const recd_t* src_recdA, unsigned src_recdN, recd_array_t* dest_recd_array );
|
||||
|
||||
rc_t value_test( const test::test_args_t& args );
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
24
cwIo.cpp
24
cwIo.cpp
@ -2883,6 +2883,30 @@ cw::rc_t cw::io::midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx,
|
||||
return midi::device::send( p->midiH, devIdx, portIdx, status, d0, d1 );
|
||||
}
|
||||
|
||||
cw::rc_t cw::io::midiDeviceAllNotesOff( handle_t h, unsigned devIdx, unsigned portIdx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
unsigned devN = midiDeviceCount(h);
|
||||
for(unsigned i=0; i<devN; ++i)
|
||||
if( devIdx==kInvalidIdx || devIdx==i )
|
||||
{
|
||||
unsigned portN = midiDevicePortCount(h,i,false);
|
||||
for(unsigned j=0; j<portN; ++j)
|
||||
if( portIdx==kInvalidIdx || portIdx==j )
|
||||
{
|
||||
rc_t rc0;
|
||||
if((rc0 = midiDeviceSend(h,i,j,midi::kCtlMdId,midi::kResetAllCtlsMdId,0)) != kOkRC) // reset all controllers
|
||||
rc = cwLogError(rc0,"Send reset all controllers failed on %i:%i.",i,j);
|
||||
|
||||
if((rc0 = midiDeviceSend(h,i,j,midi::kCtlMdId,midi::kAllNotesOffMdId,0)) != kOkRC) // all notes off
|
||||
rc = cwLogError(rc0,"Send all-notes-off failed on %i:%i.",i,j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned cw::io::midiDeviceMaxBufferMsgCount( handle_t h )
|
||||
{
|
||||
io_t* p = _handleToPtr(h);
|
||||
|
1
cwIo.h
1
cwIo.h
@ -235,6 +235,7 @@ namespace cw
|
||||
const char* midiDevicePortName( handle_t h, unsigned devIdx, bool inputFl, unsigned portIdx );
|
||||
unsigned midiDevicePortIndex( handle_t h, unsigned devIdx, bool inputFl, const char* portName );
|
||||
rc_t midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
rc_t midiDeviceAllNotesOff( handle_t h, unsigned devIdx=kInvalidIdx, unsigned portIdx=kInvalidIdx );
|
||||
|
||||
unsigned midiDeviceMaxBufferMsgCount( handle_t h );
|
||||
const midi::ch_msg_t* midiDeviceBuffer( handle_t h, unsigned& msgCntRef );
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "cwDspTypes.h"
|
||||
#include "cwFlowDecl.h"
|
||||
#include "cwFlow.h"
|
||||
#include "cwFlowValue.h"
|
||||
#include "cwFlowTypes.h"
|
||||
#include "cwFlowCross.h"
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "cwDspTypes.h"
|
||||
#include "cwFlowDecl.h"
|
||||
#include "cwFlow.h"
|
||||
#include "cwFlowValue.h"
|
||||
#include "cwFlowTypes.h"
|
||||
|
||||
#include "cwIo.h"
|
||||
@ -526,7 +527,7 @@ namespace cw
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
rc_t _ui_callback_tpl( io_flow_ctl_t* p, const flow::ui_var_t* ui_var )
|
||||
rc_t _ui_callback_tpl( io_flow_ctl_t* p, flow::ui_var_t* ui_var )
|
||||
{
|
||||
rc_t rc;
|
||||
|
||||
@ -548,7 +549,7 @@ namespace cw
|
||||
}
|
||||
|
||||
// This function is called with messages for the UI from the flow proc instances
|
||||
rc_t _ui_callback( void* arg, const flow::ui_var_t* ui_var )
|
||||
rc_t _ui_callback( void* arg, flow::ui_var_t* ui_var )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
@ -559,6 +560,28 @@ namespace cw
|
||||
rc = cwLogError(kInvalidArgRC,"The user_id (uuid) of the variable was not set.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if( ui_var->new_disable_fl != ui_var->disable_fl )
|
||||
{
|
||||
if((rc = uiSetEnable( p->ioH, ui_var->user_id, !ui_var->new_disable_fl )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"UI enable/disable update failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
ui_var->disable_fl = ui_var->new_disable_fl;
|
||||
}
|
||||
|
||||
if( ui_var->new_hide_fl != ui_var->hide_fl )
|
||||
{
|
||||
if((rc = uiSetVisible( p->ioH, ui_var->user_id, !ui_var->new_hide_fl )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"UI hide/show update failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
ui_var->hide_fl = ui_var->new_hide_fl;
|
||||
}
|
||||
|
||||
switch( ui_var->value_tid & flow::kTypeMask )
|
||||
{
|
||||
@ -804,6 +827,35 @@ bool cw::io_flow_ctl::program_is_initialized( handle_t h )
|
||||
return p->init_fl;
|
||||
}
|
||||
|
||||
cw::rc_t cw::io_flow_ctl::program_apply_preset( handle_t h, unsigned preset_idx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* preset_label = nullptr;
|
||||
io_flow_ctl_t* p = _handleToPtr(h);
|
||||
if( !p->init_fl )
|
||||
{
|
||||
rc = cwLogError(kInvalidStateRC,"The preset cannot be applied because the program is not initialized.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((preset_label = flow::preset_label(p->flowH,preset_idx)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The preset index '%i' is invalid.",preset_idx);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = apply_preset(p->flowH,preset_label)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Application of the preset '%s' failed.",preset_label);
|
||||
goto errLabel;
|
||||
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
const cw::flow::ui_net_t* cw::io_flow_ctl::program_ui_net( handle_t h )
|
||||
{
|
||||
io_flow_ctl_t* p = _handleToPtr(h);
|
||||
@ -881,6 +933,17 @@ bool cw::io_flow_ctl::is_exec_complete( handle_t h )
|
||||
return p->done_fl;
|
||||
}
|
||||
|
||||
cw::rc_t cw::io_flow_ctl::send_ui_updates( handle_t h )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
io_flow_ctl_t* p = _handleToPtr(h);
|
||||
if( program_is_initialized(h) )
|
||||
rc = send_ui_updates(p->flowH);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref )
|
||||
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
|
||||
|
@ -36,6 +36,8 @@ namespace cw
|
||||
rc_t program_initialize( handle_t h, unsigned preset_idx=kInvalidIdx );
|
||||
bool program_is_initialized( handle_t h );
|
||||
|
||||
rc_t program_apply_preset( handle_t h, unsigned preset_idx );
|
||||
|
||||
// Get the UI description data structures for the current program.
|
||||
const flow::ui_net_t* program_ui_net( handle_t h );
|
||||
|
||||
@ -52,6 +54,8 @@ namespace cw
|
||||
// The current program has completed.
|
||||
bool is_exec_complete( handle_t h );
|
||||
|
||||
rc_t send_ui_updates( handle_t h );
|
||||
|
||||
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref );
|
||||
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, int& value_ref );
|
||||
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned& value_ref );
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "cwMtx.h"
|
||||
#include "cwFlowDecl.h"
|
||||
#include "cwFlow.h"
|
||||
#include "cwFlowValue.h"
|
||||
#include "cwFlowTypes.h"
|
||||
#include "cwFlowCross.h"
|
||||
#include "cwIoFlow.h"
|
||||
|
130
cwMidi.h
130
cwMidi.h
@ -7,79 +7,75 @@ namespace cw
|
||||
{
|
||||
namespace midi
|
||||
{
|
||||
enum
|
||||
{
|
||||
kMidiChCnt = 16,
|
||||
kInvalidMidiByte = 128,
|
||||
kMidiNoteCnt = kInvalidMidiByte,
|
||||
kMidiCtlCnt = kInvalidMidiByte,
|
||||
kMidiVelCnt = kInvalidMidiByte,
|
||||
kMidiPgmCnt = kInvalidMidiByte,
|
||||
kInvalidMidiPitch = kInvalidMidiByte,
|
||||
kInvalidMidiVelocity = kInvalidMidiByte,
|
||||
kInvalidMidiCtl = kInvalidMidiByte,
|
||||
kInvalidMidiPgm = kInvalidMidiByte,
|
||||
kMidiSciPitchCharCnt = 5 // A#-1
|
||||
};
|
||||
const uint8_t kMidiChCnt = 16;
|
||||
const uint8_t kInvalidMidiByte = 128;
|
||||
const uint8_t kMidiNoteCnt = kInvalidMidiByte;
|
||||
const uint8_t kMidiCtlCnt = kInvalidMidiByte;
|
||||
const uint8_t kMidiVelCnt = kInvalidMidiByte;
|
||||
const uint8_t kMidiPgmCnt = kInvalidMidiByte;
|
||||
const uint8_t kInvalidMidiPitch = kInvalidMidiByte;
|
||||
const uint8_t kInvalidMidiVelocity = kInvalidMidiByte;
|
||||
const uint8_t kInvalidMidiCtl = kInvalidMidiByte;
|
||||
const uint8_t kInvalidMidiPgm = kInvalidMidiByte;
|
||||
const uint8_t kMidiSciPitchCharCnt = 5; // A#-1
|
||||
|
||||
|
||||
// MIDI status bytes
|
||||
enum
|
||||
{
|
||||
kInvalidStatusMdId = 0x00,
|
||||
kNoteOffMdId = 0x80,
|
||||
kNoteOnMdId = 0x90,
|
||||
kPolyPresMdId = 0xa0,
|
||||
kCtlMdId = 0xb0,
|
||||
kPgmMdId = 0xc0,
|
||||
kChPresMdId = 0xd0,
|
||||
kPbendMdId = 0xe0,
|
||||
kSysExMdId = 0xf0,
|
||||
const unsigned kInvalidStatusMdId = 0x00;
|
||||
|
||||
const unsigned kNoteOffMdId = 0x80;
|
||||
const unsigned kNoteOnMdId = 0x90;
|
||||
const unsigned kPolyPresMdId = 0xa0;
|
||||
const unsigned kCtlMdId = 0xb0;
|
||||
const unsigned kPgmMdId = 0xc0;
|
||||
const unsigned kChPresMdId = 0xd0;
|
||||
const unsigned kPbendMdId = 0xe0;
|
||||
const unsigned kSysExMdId = 0xf0;
|
||||
|
||||
kSysComMtcMdId = 0xf1,
|
||||
kSysComSppMdId = 0xf2,
|
||||
kSysComSelMdId = 0xf3,
|
||||
kSysComUndef0MdId = 0xf4,
|
||||
kSysComUndef1MdId = 0xf5,
|
||||
kSysComTuneMdId = 0xf6,
|
||||
kSysComEoxMdId = 0xf7,
|
||||
const unsigned kSysComMtcMdId = 0xf1;
|
||||
const unsigned kSysComSppMdId = 0xf2;
|
||||
const unsigned kSysComSelMdId = 0xf3;
|
||||
const unsigned kSysComUndef0MdId = 0xf4;
|
||||
const unsigned kSysComUndef1MdId = 0xf5;
|
||||
const unsigned kSysComTuneMdId = 0xf6;
|
||||
const unsigned kSysComEoxMdId = 0xf7;
|
||||
|
||||
kSysRtClockMdId = 0xf8,
|
||||
kSysRtUndef0MdId = 0xf9,
|
||||
kSysRtStartMdId = 0xfa,
|
||||
kSysRtContMdId = 0xfb,
|
||||
kSysRtStopMdId = 0xfc,
|
||||
kSysRtUndef1MdId = 0xfd,
|
||||
kSysRtSenseMdId = 0xfe,
|
||||
kSysRtResetMdId = 0xff,
|
||||
kMetaStId = 0xff,
|
||||
const unsigned kSysRtClockMdId = 0xf8;
|
||||
const unsigned kSysRtUndef0MdId = 0xf9;
|
||||
const unsigned kSysRtStartMdId = 0xfa;
|
||||
const unsigned kSysRtContMdId = 0xfb;
|
||||
const unsigned kSysRtStopMdId = 0xfc;
|
||||
const unsigned kSysRtUndef1MdId = 0xfd;
|
||||
const unsigned kSysRtSenseMdId = 0xfe;
|
||||
const unsigned kSysRtResetMdId = 0xff;
|
||||
const unsigned kMetaStId = 0xff;
|
||||
|
||||
kSeqNumbMdId = 0x00,
|
||||
kTextMdId = 0x01,
|
||||
kCopyMdId = 0x02,
|
||||
kTrkNameMdId = 0x03,
|
||||
kInstrNameMdId = 0x04,
|
||||
kLyricsMdId = 0x05,
|
||||
kMarkerMdId = 0x06,
|
||||
kCuePointMdId = 0x07,
|
||||
kMidiChMdId = 0x20,
|
||||
kMidiPortMdId = 0x21,
|
||||
kEndOfTrkMdId = 0x2f,
|
||||
kTempoMdId = 0x51,
|
||||
kSmpteMdId = 0x54,
|
||||
kTimeSigMdId = 0x58,
|
||||
kKeySigMdId = 0x59,
|
||||
kSeqSpecMdId = 0x7f,
|
||||
kInvalidMetaMdId = 0x80,
|
||||
const unsigned kSeqNumbMdId = 0x00;
|
||||
const unsigned kTextMdId = 0x01;
|
||||
const unsigned kCopyMdId = 0x02;
|
||||
const unsigned kTrkNameMdId = 0x03;
|
||||
const unsigned kInstrNameMdId = 0x04;
|
||||
const unsigned kLyricsMdId = 0x05;
|
||||
const unsigned kMarkerMdId = 0x06;
|
||||
const unsigned kCuePointMdId = 0x07;
|
||||
const unsigned kMidiChMdId = 0x20;
|
||||
const unsigned kMidiPortMdId = 0x21;
|
||||
const unsigned kEndOfTrkMdId = 0x2f;
|
||||
const unsigned kTempoMdId = 0x51;
|
||||
const unsigned kSmpteMdId = 0x54;
|
||||
const unsigned kTimeSigMdId = 0x58;
|
||||
const unsigned kKeySigMdId = 0x59;
|
||||
const unsigned kSeqSpecMdId = 0x7f;
|
||||
const unsigned kInvalidMetaMdId = 0x80;
|
||||
|
||||
const unsigned kSustainCtlMdId = 0x40;
|
||||
const unsigned kPortamentoCtlMdId = 0x41;
|
||||
const unsigned kSostenutoCtlMdId = 0x42;
|
||||
const unsigned kSoftPedalCtlMdId = 0x43;
|
||||
const unsigned kLegatoCtlMdId = 0x44;
|
||||
|
||||
kSustainCtlMdId = 0x40,
|
||||
kPortamentoCtlMdId = 0x41,
|
||||
kSostenutoCtlMdId = 0x42,
|
||||
kSoftPedalCtlMdId = 0x43,
|
||||
kLegatoCtlMdId = 0x44
|
||||
|
||||
};
|
||||
|
||||
const uint8_t kResetAllCtlsMdId = 121;
|
||||
const uint8_t kAllNotesOffMdId = 123;
|
||||
|
||||
|
||||
//===============================================================================================
|
||||
@ -115,6 +111,8 @@ namespace cw
|
||||
template< typename T> bool isSoftPedalDown( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && isPedalDown(d1)); }
|
||||
template< typename T> bool isSoftPedalUp( T s, T d0, T d1) { return ( isSoftPedal(s,d0) && isPedalUp(d1)); }
|
||||
|
||||
template< typename T> bool isAllNotesOff( T s, T d0 ) { return isCtlStatus(s) && (d0)==kAllNotesOffMdId; }
|
||||
template< typename T> bool isResetAllCtls( T s, T d0 ) { return isCtlStatus(s) && (d0)==kResetAllCtlsMdId; }
|
||||
|
||||
|
||||
typedef uint8_t byte_t;
|
||||
|
@ -323,12 +323,12 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN )
|
||||
{
|
||||
// TODO: continue to iterate through the blocks waiting for the consumer
|
||||
// to make more space available.
|
||||
//_block_report(p);
|
||||
// _block_report(p);
|
||||
|
||||
// BEWARE: BUG BUG BUG: Since the cwLog makes calls to cwWebSocket
|
||||
// this error message, and subsequent error messages,
|
||||
// will result in a recursive loop which will crash the program.
|
||||
rc = cwLogError(kBufTooSmallRC,"NbMpScQueue overflow.");
|
||||
rc = cwLogError(kBufTooSmallRC,"NbMpScQueue overflow. Increase 'queueBlkCnt' and/or 'queueBlkByteCnt'");
|
||||
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,7 @@ namespace cw
|
||||
|
||||
if( score_fl )
|
||||
{
|
||||
e->loc = kInvalidId;
|
||||
if((rc = getv(csvH,"oloc",e->loc )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Error parsing CSV.");
|
||||
|
@ -66,13 +66,15 @@ namespace cw
|
||||
bool has_loc_info_flag( handle_t h );
|
||||
|
||||
unsigned event_count( handle_t h );
|
||||
|
||||
// Get first event in linked list.
|
||||
const event_t* base_event( handle_t h );
|
||||
|
||||
const event_t* loc_to_event( handle_t h, unsigned loc );
|
||||
|
||||
// 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 test( const object_t* cfg );
|
||||
|
||||
}
|
||||
|
321
cwPresetSel.cpp
321
cwPresetSel.cpp
@ -69,6 +69,7 @@ namespace cw
|
||||
|
||||
unsigned cur_alt_idx;
|
||||
|
||||
unsigned dryPresetIdx;
|
||||
|
||||
} preset_sel_t;
|
||||
|
||||
@ -142,7 +143,7 @@ namespace cw
|
||||
mem::release(f->note);
|
||||
mem::release(f->presetA);
|
||||
mem::release(f->altPresetIdxA);
|
||||
//mem::release(f->multiPresetA);
|
||||
mem::release(f->probDomA);
|
||||
mem::release(f);
|
||||
goto errLabel;
|
||||
}
|
||||
@ -180,6 +181,250 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Probabilistic selection
|
||||
//
|
||||
unsigned _get_preset_order( const preset_t* p, unsigned max_valid_order )
|
||||
{
|
||||
if( (p->order == 0 && p->playFl == false) || p->order > max_valid_order )
|
||||
return 0;
|
||||
|
||||
if( p->order == 0 && p->playFl )
|
||||
return 1;
|
||||
|
||||
assert( 1 <= p->order && p->order <= max_valid_order );
|
||||
return p->order;
|
||||
}
|
||||
|
||||
rc_t _pre_calc_for_prob_select( preset_sel_t* p, frag_t * f)
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
unsigned activeOrderA[ f->presetN ] = {};
|
||||
unsigned activeIdxA[ f->presetN ] = {};
|
||||
unsigned common_mult = 1;
|
||||
f->probDomN = 0;
|
||||
f->probDomainMax = 0;
|
||||
|
||||
for(unsigned i=0; i<f->presetN; ++i)
|
||||
{
|
||||
unsigned order = _get_preset_order(f->presetA + i, f->presetN-1);
|
||||
|
||||
if( order > 0 )
|
||||
{
|
||||
if( order> 0 && activeOrderA[order]==0 )
|
||||
common_mult *= order;
|
||||
|
||||
activeIdxA[f->probDomN] = i;
|
||||
f->probDomN += 1;
|
||||
|
||||
activeOrderA[ order ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// if no preset was selected in this fragment then
|
||||
// select the 'dry' preset.
|
||||
if( f->probDomN == 0 )
|
||||
{
|
||||
assert( p->dryPresetIdx != kInvalidIdx && p->dryPresetIdx < f->presetN );
|
||||
|
||||
f->probDomN = 1;
|
||||
activeIdxA[0] = p->dryPresetIdx;
|
||||
|
||||
if(activeIdxA[0] == kInvalidIdx )
|
||||
{
|
||||
cwLogError(kEleNotFoundRC,"No preset was selected at end loc:%i and 'dry' preset exists as a default.",f->endLoc);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
f->probDomA = mem::allocZ<prob_domain_t>(f->probDomN);
|
||||
|
||||
for(unsigned i=0; i<f->probDomN; ++i)
|
||||
{
|
||||
preset_t* preset = f->presetA + activeIdxA[i];
|
||||
unsigned order = _get_preset_order( preset, f->presetN-1);
|
||||
|
||||
preset->prob_dom_idx = i;
|
||||
|
||||
f->probDomA[i].index = activeIdxA[i];
|
||||
f->probDomA[i].order = preset->playFl ? 0 : order;
|
||||
f->probDomA[i].domain = order==0 ? 1 : common_mult / order;
|
||||
|
||||
f->probDomainMax += f->probDomA[i].domain;
|
||||
}
|
||||
|
||||
std::sort(f->probDomA,
|
||||
f->probDomA + f->probDomN,
|
||||
[](const prob_domain_t& a,const prob_domain_t& b){ return a.order<b.order; } );
|
||||
|
||||
assert( f->probDomN>=1 && f->probDomA != nullptr );
|
||||
|
||||
errLabel:
|
||||
|
||||
if( rc != kOkRC )
|
||||
rc = cwLogError(rc,"Pre-calculations for probabilistic preset selection failed.");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned _prob_select_uniform_preset( const preset_sel_t* p,
|
||||
const frag_t* f,
|
||||
bool dry_on_play_fl, // select dry if dry has the 'playFl' selected
|
||||
bool dry_on_selected_fl, // select dry if it has order > 0
|
||||
bool allow_all_fl, // select from among all presets - not just the ones in probDomA[]
|
||||
unsigned skip_preset_idx=kInvalidIdx )
|
||||
{
|
||||
assert( p->dryPresetIdx != kInvalidIdx && p->dryPresetIdx < f->presetN );
|
||||
unsigned preset_idx = kInvalidIdx;
|
||||
unsigned domN = allow_all_fl ? f->presetN : f->probDomN;
|
||||
unsigned skipProbDomIdx = kInvalidIdx;
|
||||
unsigned pdi = kInvalidIdx;
|
||||
|
||||
|
||||
// if dry_on_play_fl is set and the dry preset playFl is set
|
||||
if( dry_on_play_fl && f->drySelectedFl )
|
||||
{
|
||||
//printf("PS:DOP!\n");
|
||||
preset_idx = p->dryPresetIdx;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if dry_on_selected_fl is set and the dry preset has a non-zero order
|
||||
if( dry_on_selected_fl && (f->presetA[ p->dryPresetIdx ].playFl || f->presetA[ p->dryPresetIdx ].order > 0) )
|
||||
{
|
||||
//printf("PS:DOS!\n");
|
||||
preset_idx = p->dryPresetIdx;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if no options exist then return the dry preset
|
||||
if( f->probDomN == 0 )
|
||||
{
|
||||
//printf("PS:NO OPTS!\n");
|
||||
preset_idx = p->dryPresetIdx;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if only one option exists
|
||||
if( f->probDomN == 1 && allow_all_fl==false )
|
||||
{
|
||||
//printf("PS:ONE OPT!\n");
|
||||
assert( allow_all_fl == false );
|
||||
preset_idx = f->probDomA[0].index;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// if skip-preset was given and it is included in the candidate set
|
||||
if( skip_preset_idx != kInvalidIdx && (f->presetA[ skip_preset_idx ].playFl || f->presetA[ skip_preset_idx ].order>0) )
|
||||
{
|
||||
domN -= 1;
|
||||
if( !allow_all_fl )
|
||||
skipProbDomIdx = f->presetA[ skip_preset_idx ].prob_dom_idx;
|
||||
}
|
||||
|
||||
// if only one option exists after removing the skip-preset
|
||||
if( domN == 1 )
|
||||
{
|
||||
|
||||
//printf("PS:ONE non-SKIP!\n");
|
||||
assert( allow_all_fl == false );
|
||||
preset_idx = skipProbDomIdx==0 ? 1 : 0;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
pdi = std::min(domN-1,(unsigned)floor(((double)std::rand() * domN / RAND_MAX)));
|
||||
|
||||
if( allow_all_fl )
|
||||
{
|
||||
if( skip_preset_idx != kInvalidIdx && pdi >= skip_preset_idx && pdi < f->presetN-1 )
|
||||
pdi += 1;
|
||||
|
||||
preset_idx = pdi;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( skipProbDomIdx!=kInvalidIdx && pdi >= skipProbDomIdx && pdi < f->probDomN-1 )
|
||||
pdi += 1;
|
||||
|
||||
preset_idx = f->probDomA[ pdi ].index;
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
|
||||
//if( preset_idx == kInvalidIdx || preset_idx >= f->presetN || preset_idx == skip_preset_idx )
|
||||
// printf("PS: INVALIDATED! %i pdi:%i N:%i domN:%i procDomN:%i skip:%i\n",preset_idx,pdi,f->presetN,domN,f->probDomN, skip_preset_idx);
|
||||
|
||||
return preset_idx == kInvalidIdx || preset_idx >= f->presetN || preset_idx == skip_preset_idx ? kInvalidIdx : preset_idx;
|
||||
|
||||
}
|
||||
|
||||
unsigned _prob_select_weighted_preset( const preset_sel_t* p,
|
||||
const frag_t* f,
|
||||
bool dry_on_play_fl, // select dry if dry has the 'playFl' selected
|
||||
unsigned skip_preset_idx = kInvalidIdx )
|
||||
{
|
||||
unsigned domMax = f->probDomainMax;
|
||||
unsigned preset_idx = kInvalidIdx;
|
||||
unsigned x = 0;
|
||||
unsigned x_acc = 0;
|
||||
|
||||
assert( f->probDomN>=1 && f->probDomA != nullptr );
|
||||
|
||||
// if dry_on_play_fl is set and the dry preset playFl is set
|
||||
if( dry_on_play_fl && f->drySelectedFl )
|
||||
{
|
||||
preset_idx = p->dryPresetIdx;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if there is only one possible preset to select
|
||||
if( f->probDomN == 1 )
|
||||
{
|
||||
preset_idx = f->probDomA[0].index;
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if a preset should be left out of consideration
|
||||
if( skip_preset_idx != kInvalidIdx )
|
||||
{
|
||||
assert( skip_preset_idx <= f->presetN );
|
||||
assert( f->presetA[ skip_preset_idx ].prob_dom_idx < f->probDomN );
|
||||
assert( domMax >= f->probDomA[ f->presetA[ skip_preset_idx].prob_dom_idx ].domain );
|
||||
|
||||
domMax -= f->probDomA[ f->presetA[ skip_preset_idx].prob_dom_idx ].domain;
|
||||
}
|
||||
|
||||
|
||||
// generate random integer between 0 and domMax
|
||||
x = (unsigned)floor(((double)std::rand() * domMax / RAND_MAX));
|
||||
|
||||
for(unsigned i=0; i<f->probDomN; ++i)
|
||||
if( f->probDomA[i].index != skip_preset_idx )
|
||||
{
|
||||
x_acc += f->probDomA[i].domain;
|
||||
|
||||
if( x < x_acc || (f->probDomN==2 && skip_preset_idx != kInvalidIdx) )
|
||||
{
|
||||
assert( f->presetA[ f->probDomA[i].index ].prob_dom_idx == i );
|
||||
preset_idx = f->probDomA[i].index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return preset_idx == kInvalidIdx || preset_idx >= f->presetN ? kInvalidIdx : preset_idx;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// 'Alt' related
|
||||
//
|
||||
void _print_preset_alts( preset_sel_t* p, const frag_t* f, const char* label )
|
||||
{
|
||||
printf("%s : ",label);
|
||||
@ -287,6 +532,10 @@ namespace cw
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
|
||||
frag_t* _find_frag( preset_sel_t* p, unsigned fragId )
|
||||
{
|
||||
frag_t* f;
|
||||
@ -909,6 +1158,7 @@ cw::rc_t cw::preset_sel::create( handle_t& hRef, const object_t* cfg )
|
||||
return rc;
|
||||
|
||||
p = mem::allocZ<preset_sel_t>();
|
||||
p->dryPresetIdx = kInvalidIdx;
|
||||
|
||||
// parse the cfg
|
||||
if((rc = cfg->getv( "preset_labelL", preset_labelL,
|
||||
@ -982,6 +1232,7 @@ cw::rc_t cw::preset_sel::create( handle_t& hRef, const object_t* cfg )
|
||||
|
||||
|
||||
p->defaultPresetIdx = kInvalidIdx;
|
||||
|
||||
if( default_preset_label != nullptr )
|
||||
if((p->defaultPresetIdx = _preset_label_to_index(p,default_preset_label)) ==kInvalidIdx )
|
||||
cwLogError(kInvalidIdRC,"The default preset label '%s' could not be found.",cwStringNullGuard(default_preset_label));
|
||||
@ -990,8 +1241,17 @@ cw::rc_t cw::preset_sel::create( handle_t& hRef, const object_t* cfg )
|
||||
cwLogError(kInvalidStateRC,"No default preset was set.");
|
||||
|
||||
if( p->dryPresetOrder == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidStateRC,"The 'dry' preset was not found.");
|
||||
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((p->dryPresetIdx = _preset_label_to_index(p,"dry")) == kInvalidIdx )
|
||||
{
|
||||
rc = cwLogError(kEleNotFoundRC,"The 'dry' preset does not exist.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
hRef.set(p);
|
||||
|
||||
@ -1564,6 +1824,52 @@ const cw::flow::preset_order_t* cw::preset_sel::fragment_active_presets( handle
|
||||
return preset_order;
|
||||
}
|
||||
|
||||
unsigned cw::preset_sel::prob_select_preset_index( handle_t h,
|
||||
const frag_t* f,
|
||||
unsigned flags,
|
||||
unsigned skip_preset_idx )
|
||||
{
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
unsigned preset_idx = kInvalidIdx;
|
||||
bool dry_on_play_fl = cwIsFlag(flags,kDryOnPlayFl);
|
||||
|
||||
// if selecting deterministically ...
|
||||
if( cwIsNotFlag(flags,kUseProbFl) )
|
||||
{
|
||||
//printf("ps: deterministic skip:%i\n",skip_preset_idx);
|
||||
|
||||
for(unsigned i=0; i<f->probDomN; ++i)
|
||||
if( f->probDomA[i].index != skip_preset_idx )
|
||||
{
|
||||
// ... pick the first available preset
|
||||
preset_idx = f->probDomA[i].index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if using a uniform distribution
|
||||
if( cwIsFlag(flags,kUniformFl) )
|
||||
{
|
||||
bool dry_on_selected_fl = cwIsFlag(flags,kDryOnSelFl);
|
||||
bool allow_all_fl = cwIsFlag(flags,kAllowAllFl);
|
||||
|
||||
//printf("ps: uniform dop:%i all:%i sel-dry:%i skip:%i\n",dry_on_play_fl,allow_all_fl,dry_on_selected_fl,skip_preset_idx);
|
||||
|
||||
preset_idx = _prob_select_uniform_preset(p,f,dry_on_play_fl, dry_on_selected_fl, allow_all_fl, skip_preset_idx );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//printf("ps: weighted dop:%i skip:%i\n",dry_on_play_fl,skip_preset_idx);
|
||||
|
||||
preset_idx = _prob_select_weighted_preset( p, f, dry_on_play_fl, skip_preset_idx );
|
||||
}
|
||||
}
|
||||
|
||||
return preset_idx;
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::preset_sel::write( handle_t h, const char* fn )
|
||||
{
|
||||
@ -1649,7 +1955,7 @@ cw::rc_t cw::preset_sel::read( handle_t h, const char* fn )
|
||||
preset_sel_t* p = _handleToPtr(h);
|
||||
object_t* root = nullptr;
|
||||
const object_t* fragL_obj = nullptr;
|
||||
unsigned dryPresetIdx = _preset_label_to_index(p,"dry");
|
||||
|
||||
|
||||
// parse the preset file
|
||||
if((rc = objectFromFile(fn,root)) != kOkRC )
|
||||
@ -1712,7 +2018,6 @@ cw::rc_t cw::preset_sel::read( handle_t h, const char* fn )
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// create a new fragment
|
||||
if((rc = create_fragment( h, endLoc, end_ts, fragId)) != kOkRC )
|
||||
{
|
||||
@ -1782,15 +2087,19 @@ cw::rc_t cw::preset_sel::read( handle_t h, const char* fn )
|
||||
f->altPresetIdxA[0] = preset_idx;
|
||||
|
||||
// if the dry preset is selected
|
||||
if( preset_idx == dryPresetIdx )
|
||||
if( preset_idx == p->dryPresetIdx )
|
||||
f->drySelectedFl = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if only one preset is active and the dry preset is active
|
||||
f->dryOnlyFl = activePresetN==1 && (f->presetA[dryPresetIdx].order>0 || f->presetA[dryPresetIdx].playFl);
|
||||
f->dryOnlyFl = activePresetN==1 && (f->presetA[p->dryPresetIdx].order>0 || f->presetA[p->dryPresetIdx].playFl);
|
||||
|
||||
// setup for prob. preset selection
|
||||
if((rc = _pre_calc_for_prob_select(p, f)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,9 +16,17 @@ namespace cw
|
||||
bool seqFl; // play this preset during sequencing.
|
||||
unsigned preset_idx; // preset index into preset_labelA[].
|
||||
unsigned order; // selection label
|
||||
char* alt_str; // 'alt' label
|
||||
char* alt_str; // 'alt' label
|
||||
unsigned prob_dom_idx; // index of this preset in probDomA[]
|
||||
} preset_t;
|
||||
|
||||
typedef struct prob_domain_str
|
||||
{
|
||||
unsigned index; // index of preset into frag.presetA[]
|
||||
unsigned order; // preset order value or 0 if the playFl is set on presetA[index] and presetA[index].order==0
|
||||
unsigned domain; // probability domain area (greater for more likely preset values)
|
||||
} prob_domain_t;
|
||||
|
||||
typedef struct frag_str
|
||||
{
|
||||
unsigned fragId; // Unique fragment id
|
||||
@ -46,6 +54,10 @@ namespace cw
|
||||
bool uiSelectFl;
|
||||
bool seqAllFl; // Set if all preset.seqFl's should be treated as though they are set to true.
|
||||
|
||||
prob_domain_t* probDomA; // probDomA[ probDomN ] ascending order on 'order' - preset with playFl set is always first
|
||||
unsigned probDomN;
|
||||
unsigned probDomainMax; // sum(probDomA.domain)
|
||||
|
||||
struct frag_str* link;
|
||||
struct frag_str* prev;
|
||||
} frag_t;
|
||||
@ -150,6 +162,20 @@ namespace cw
|
||||
};
|
||||
|
||||
const flow::preset_order_t* fragment_active_presets( handle_t h, const frag_t* f, unsigned flags, unsigned& count_ref );
|
||||
|
||||
enum {
|
||||
kUseProbFl = 0x01, // True=Select the preset probalistically. False=Select the preset with the lowest non-zero order.
|
||||
kUniformFl = 0x02, // Ignored if kUseProbFl is not set. True=Use uniform PDF to select preset. False=Use 'order' weightings to select preset.
|
||||
kDryOnPlayFl = 0x04, // Ignored if kUseProbFl is not set. True=Select 'dry' if marked with 'play-fl'. False=Choose probabilistically.
|
||||
kAllowAllFl = 0x08, // Ignored if kUseProbFl is not set. True=Select from all presets. False=Select from presets with order>0 or play_fl set.
|
||||
kDryOnSelFl = 0x10, // Ignored if kUseProbFl and kUniformFl is not set. True=Select 'dry' if dry order>0 or play_fl set. Otherwise choose with uniform prob.
|
||||
};
|
||||
|
||||
unsigned prob_select_preset_index( handle_t h,
|
||||
const frag_t* f,
|
||||
unsigned flags,
|
||||
unsigned skip_preset_idx = kInvalidIdx );
|
||||
|
||||
|
||||
rc_t write( handle_t h, const char* fn );
|
||||
rc_t read( handle_t h, const char* fn );
|
||||
|
@ -30,7 +30,10 @@
|
||||
#include "cwWaveTableBank.h"
|
||||
#include "cwWaveTableNotes.h"
|
||||
|
||||
#include "cwMidiDecls.h"
|
||||
|
||||
#include "cwFlowTest.h"
|
||||
#include "cwFlowValue.h"
|
||||
|
||||
#include "cwThread.h"
|
||||
#include "cwThreadMach.h"
|
||||
@ -60,6 +63,7 @@ namespace cw
|
||||
{ "/audio_transform", dsp::test },
|
||||
{ "/wt_note", wt_note::test },
|
||||
{ "/thread_tasks", thread_tasks::test },
|
||||
{ "/flow_value", flow::value_test },
|
||||
{ "/numeric_convert", numericConvertTest },
|
||||
{ nullptr, nullptr },
|
||||
};
|
||||
|
15
cwText.cpp
15
cwText.cpp
@ -160,6 +160,21 @@ char* cw::firstMatchChar( char* s, char c )
|
||||
}
|
||||
|
||||
const char* cw::firstMatchChar( const char* s, char c )
|
||||
{ return firstMatchChar((char*)s,c); }
|
||||
|
||||
char* cw::firstMatchChar( char* s, unsigned n, char c )
|
||||
{
|
||||
if( s == nullptr )
|
||||
return nullptr;
|
||||
|
||||
for(unsigned i=0; *s && i<n; ++s,++i)
|
||||
if(*s == c)
|
||||
return s;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const char* cw::firstMatchChar( const char* s, unsigned n, char c )
|
||||
{
|
||||
return firstMatchChar((char*)s,c);
|
||||
}
|
||||
|
2
cwText.h
2
cwText.h
@ -69,6 +69,8 @@ namespace cw
|
||||
// if 'c' does not occur in s[]
|
||||
char* firstMatchChar( char* s, char c );
|
||||
const char* firstMatchChar( const char* s, char c );
|
||||
char* firstMatchChar( char* s, unsigned sn, char c );
|
||||
const char* firstMatchChar( const char* s, unsigned sn, char c );
|
||||
|
||||
// Find the last occurrent of 'c' in s[].
|
||||
char* lastMatchChar( char* s, char c );
|
||||
|
@ -408,7 +408,7 @@ cw::rc_t cw::thread_tasks::run( handle_t h, task_t* taskA, unsigned taskN, unsig
|
||||
case kTimeOutRC:
|
||||
// mutex is unlocked
|
||||
p->mutexLockFl = false;
|
||||
cwLogWarning("Thread tasks timed out.");
|
||||
cwLogWarning("Thread tasks timed out after %i ms.",timeOutMs);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -34,7 +34,6 @@ namespace cw
|
||||
{
|
||||
|
||||
typedef handle<struct thread_tasks_str> handle_t;
|
||||
typedef thread::cbFunc_t threadFunc_t;
|
||||
|
||||
// Create a thread tasks machine with threadN records
|
||||
rc_t create( handle_t& hRef, unsigned threadN );
|
||||
|
18
cwTime.cpp
18
cwTime.cpp
@ -279,7 +279,7 @@ void cw::time::advanceMicros( spec_t& ts, unsigned us )
|
||||
|
||||
void cw::time::advanceMs( spec_t& ts, unsigned ms )
|
||||
{
|
||||
|
||||
/*
|
||||
const unsigned ns_per_sec = 1000000000;
|
||||
|
||||
ts.tv_nsec += ms * 1000000;
|
||||
@ -287,6 +287,22 @@ void cw::time::advanceMs( spec_t& ts, unsigned ms )
|
||||
|
||||
ts.tv_sec += sec;
|
||||
ts.tv_nsec -= sec * ns_per_sec;
|
||||
*/
|
||||
|
||||
const unsigned ns_per_sec = 1000000000;
|
||||
const unsigned ns_per_ms = ns_per_sec/1000;
|
||||
|
||||
unsigned secs = ms / 1000;
|
||||
unsigned ms_rem = ms - (secs*1000);
|
||||
unsigned nano_rem = ms_rem * ns_per_ms;
|
||||
|
||||
ts.tv_sec += secs;
|
||||
ts.tv_nsec += nano_rem;
|
||||
while(ts.tv_nsec >= ns_per_sec )
|
||||
{
|
||||
ts.tv_nsec -= ns_per_sec;
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
33
cwVectOps.h
33
cwVectOps.h
@ -72,7 +72,7 @@ namespace cw
|
||||
|
||||
template< typename T >
|
||||
void zero( T* v, unsigned n )
|
||||
{ fill(v,n,0); }
|
||||
{ fill(v,n,(T)0); }
|
||||
|
||||
template< typename T >
|
||||
void ones( T* v, unsigned n )
|
||||
@ -379,6 +379,17 @@ namespace cw
|
||||
return beg + (incr*i);
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
T* urand( T* y, unsigned yN, const T& min_val, const T& max_val )
|
||||
{
|
||||
unsigned i = 0;
|
||||
for(; i<yN; ++i)
|
||||
y[i] = min_val + (T)((double)(std::rand() * (max_val-min_val)) / RAND_MAX);
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
T sum( const T* v, unsigned n )
|
||||
{
|
||||
@ -389,6 +400,16 @@ namespace cw
|
||||
return y;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
T abs_sum( const T* v, unsigned n )
|
||||
{
|
||||
T y = 0;
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
y += std::abs(v[i]);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
T prod( const T* v, unsigned n )
|
||||
{
|
||||
@ -409,6 +430,16 @@ namespace cw
|
||||
return sum;
|
||||
}
|
||||
|
||||
template< typename T0 >
|
||||
T0 sum_sq( const T0* v, unsigned n )
|
||||
{
|
||||
T0 sum = 0;
|
||||
for(unsigned i=0; i<n; ++i)
|
||||
sum += v[i] * v[i];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
//==================================================================================================================
|
||||
// Statistics
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "cwWaveTableBank.h"
|
||||
#include "cwMidi.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwThread.h"
|
||||
#include "cwThreadMach.h"
|
||||
|
||||
namespace cw
|
||||
{
|
||||
@ -165,20 +167,21 @@ namespace cw
|
||||
|
||||
for(unsigned i=0; i<p->instrN; ++i )
|
||||
_destroy_instr(p->instrA[i]);
|
||||
|
||||
|
||||
mem::release(p->instrA);
|
||||
mem::release(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _alloc_wt( wt_bank_t* p,
|
||||
wt_t* wt,
|
||||
wt_tid_t tid,
|
||||
srate_t srate,
|
||||
const sample_t* aV,
|
||||
unsigned posn_smp_idx,
|
||||
unsigned aN,
|
||||
double hz,
|
||||
double rms)
|
||||
unsigned _alloc_wt( const wt_bank_t* p,
|
||||
wt_t* wt,
|
||||
wt_tid_t tid,
|
||||
srate_t srate,
|
||||
const sample_t* aV,
|
||||
unsigned posn_smp_idx,
|
||||
unsigned aN,
|
||||
double hz,
|
||||
double rms)
|
||||
{
|
||||
wt->tid = tid;
|
||||
wt->aN = aN;
|
||||
@ -190,7 +193,8 @@ namespace cw
|
||||
wt->posn_smp_idx = posn_smp_idx;
|
||||
|
||||
unsigned allocSmpCnt = p->padSmpN + wt->aN + p->padSmpN;
|
||||
p->allocAudioBytesN += allocSmpCnt * sizeof(sample_t);
|
||||
//p->allocAudioBytesN += allocSmpCnt * sizeof(sample_t);
|
||||
unsigned allocByteCnt = allocSmpCnt * sizeof(sample_t);
|
||||
|
||||
// allocate the wavetable audio buffer
|
||||
wt->aV = mem::allocZ<sample_t>( allocSmpCnt );
|
||||
@ -204,6 +208,7 @@ namespace cw
|
||||
// fill the wavetable suffix
|
||||
vop::copy(wt->aV + p->padSmpN + wt->aN, wt->aV + p->padSmpN, p->padSmpN );
|
||||
|
||||
return allocByteCnt;
|
||||
}
|
||||
|
||||
rc_t _create_instr_pv_map( instr_t* instr )
|
||||
@ -259,11 +264,140 @@ namespace cw
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct load_pitch_str
|
||||
{
|
||||
const wt_bank_t* p;
|
||||
const instr_t* instr;
|
||||
pitch_t* pitch;
|
||||
const object_t* pitchR;
|
||||
unsigned index;
|
||||
unsigned allocByteN;
|
||||
rc_t rc;
|
||||
} load_pitch_t;
|
||||
|
||||
rc_t _load_pitch( load_pitch_t* args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
double hz = 0;
|
||||
srate_t srate = 0;
|
||||
const char* audio_fname = nullptr;
|
||||
const object_t* velL = nullptr;
|
||||
audio_buf_t abuf{};
|
||||
|
||||
const wt_bank_t* p = args->p;
|
||||
const instr_t* instr = args->instr;
|
||||
pitch_t* pitch = args->pitch;
|
||||
const object_t* pitchR = args->pitchR;
|
||||
unsigned index = args->index;
|
||||
|
||||
if(pitchR == nullptr || !pitchR->is_dict() )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The pitch record at index %i is not valid.",index);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = pitchR->getv("midi_pitch",pitch->midi_pitch,
|
||||
"srate",srate,
|
||||
"est_hz_mean",hz,
|
||||
"audio_fname",audio_fname,
|
||||
"velL",velL)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error while reading pitch record at index %i.",index);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
cwLogInfo("pitch:%i %i %s",pitch->midi_pitch,p->allocAudioBytesN/(1024*1024),audio_fname);
|
||||
|
||||
// read the audio file into abuf
|
||||
if((rc = _audio_buf_alloc(abuf, audio_fname )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
pitch->velN = velL->child_count();
|
||||
pitch->velA = mem::allocZ<vel_t>( pitch->velN );
|
||||
|
||||
for(unsigned j=0; j<pitch->velN; ++j)
|
||||
{
|
||||
const object_t* chL = nullptr;
|
||||
const object_t* velR = velL->child_ele(j);
|
||||
vel_t* vel = pitch->velA + j;
|
||||
|
||||
if( velR==nullptr || !velR->is_dict() )
|
||||
{
|
||||
rc = cwLogError(rc,"The velocity record at index %i on MIDI pitch %i is invalid.",j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = velR->getv("vel",vel->vel,
|
||||
"bsi",vel->bsi,
|
||||
"chL",chL)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error while reading the velocity record at index %i from the pitch record for MIDI pitch:%i.",j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
vel->mc_seq.chN = chL->child_count();
|
||||
vel->mc_seq.chA = mem::allocZ<wt_seq_t>( vel->mc_seq.chN );
|
||||
|
||||
for(unsigned ch_idx=0; ch_idx<pitch->velA[j].mc_seq.chN; ++ch_idx)
|
||||
{
|
||||
|
||||
const object_t* wtL = chL->child_ele(ch_idx);
|
||||
wt_seq_t* wts = vel->mc_seq.chA + ch_idx;
|
||||
|
||||
wts->wtN = wtL->child_count() + 1;
|
||||
wts->wtA = mem::allocZ<wt_t>( wts->wtN );
|
||||
|
||||
for(unsigned wti=0; wti<wts->wtN-1; ++wti)
|
||||
{
|
||||
const object_t* wtR = wtL->child_ele(wti);
|
||||
wt_t* wt = wts->wtA + wti + 1;
|
||||
unsigned wtbi = kInvalidIdx;
|
||||
unsigned wtei = kInvalidIdx;
|
||||
double rms = 0;
|
||||
|
||||
if((rc = wtR->getv("wtbi",wtbi,
|
||||
"wtei",wtei,
|
||||
"rms",rms,
|
||||
"est_hz",wt->hz)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error in wavetable record at index %i, channel index:%i, velocity index:%i midi pitch:%i.",wti,ch_idx,j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if this is the first looping wave table then insert the attack wave table before it
|
||||
if( wti==0 )
|
||||
{
|
||||
args->allocByteN += _alloc_wt(p,wts->wtA,dsp::wt_osc::kOneShotWtTId,srate,abuf.ch_buf[ch_idx], vel->bsi, wtbi-vel->bsi,hz,0);
|
||||
}
|
||||
|
||||
args->allocByteN += _alloc_wt(p,wt,dsp::wt_osc::kLoopWtTId,srate,abuf.ch_buf[ch_idx],wtbi,wtei-wtbi,hz,rms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
||||
_audio_buf_free(abuf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _load_pitch_thread_func( void* arg )
|
||||
{
|
||||
load_pitch_t* a = (load_pitch_t*)arg;
|
||||
a->rc = _load_pitch( a );
|
||||
return a->rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname )
|
||||
cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname, unsigned loadThreadCnt )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
@ -276,7 +410,7 @@ cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN, const char* inst
|
||||
hRef.set(p);
|
||||
|
||||
if( instr_json_fname != nullptr )
|
||||
if((rc = load(hRef,instr_json_fname)) != kOkRC )
|
||||
if((rc = load(hRef,instr_json_fname,loadThreadCnt)) != kOkRC )
|
||||
{
|
||||
hRef.clear();
|
||||
}
|
||||
@ -308,7 +442,7 @@ cw::rc_t cw::wt_bank::destroy( handle_t& hRef )
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname )
|
||||
cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname, unsigned threadN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
wt_bank_t* p = _handleToPtr(h);
|
||||
@ -316,7 +450,10 @@ cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname )
|
||||
const object_t* pitchL = nullptr;
|
||||
const char* instr_label = nullptr;
|
||||
instr_t* instr = nullptr;
|
||||
audio_buf_t abuf{};
|
||||
//audio_buf_t abuf{};
|
||||
load_pitch_t* argsA = nullptr;
|
||||
thread_tasks::handle_t threadTasksH; //
|
||||
thread_tasks::task_t* taskA = nullptr;
|
||||
|
||||
if((rc = objectFromFile(instr_json_fname,f)) != kOkRC )
|
||||
goto errLabel;
|
||||
@ -339,102 +476,51 @@ cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname )
|
||||
instr->pitchN = pitchL->child_count();
|
||||
instr->pitchA = mem::allocZ<pitch_t>(instr->pitchN);
|
||||
|
||||
argsA = mem::allocZ<load_pitch_t>(instr->pitchN);
|
||||
taskA = mem::allocZ<thread_tasks::task_t>(instr->pitchN);
|
||||
|
||||
|
||||
for(unsigned i=0; i<instr->pitchN; ++i)
|
||||
{
|
||||
double hz = 0;
|
||||
srate_t srate = 0;
|
||||
const char* audio_fname = nullptr;
|
||||
const object_t* velL = nullptr;
|
||||
const object_t* pitchR = pitchL->child_ele(i);
|
||||
pitch_t* pitch = instr->pitchA + i;
|
||||
|
||||
if(pitchR == nullptr || !pitchR->is_dict() )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The pitch record at index %i is not valid.",i);
|
||||
goto errLabel;
|
||||
}
|
||||
load_pitch_t* r = argsA + i;
|
||||
r->p = p;
|
||||
r->instr = instr;
|
||||
r->pitch = instr->pitchA + i;
|
||||
r->pitchR = pitchL->child_ele(i);
|
||||
r->index = i;
|
||||
|
||||
if((rc = pitchR->getv("midi_pitch",pitch->midi_pitch,
|
||||
"srate",srate,
|
||||
"est_hz_mean",hz,
|
||||
"audio_fname",audio_fname,
|
||||
"velL",velL)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error while reading pitch record at index %i.",i);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
cwLogInfo("pitch:%i %i %s",pitch->midi_pitch,p->allocAudioBytesN/(1024*1024),audio_fname);
|
||||
|
||||
// read the audio file into abuf
|
||||
if((rc = _audio_buf_alloc(abuf, audio_fname )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
pitch->velN = velL->child_count();
|
||||
pitch->velA = mem::allocZ<vel_t>( pitch->velN );
|
||||
|
||||
for(unsigned j=0; j<instr->pitchA[i].velN; ++j)
|
||||
{
|
||||
const object_t* chL = nullptr;
|
||||
const object_t* velR = velL->child_ele(j);
|
||||
vel_t* vel = pitch->velA + j;
|
||||
|
||||
if( velR==nullptr || !velR->is_dict() )
|
||||
{
|
||||
rc = cwLogError(rc,"The velocity record at index %i on MIDI pitch %i is invalid.",j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = velR->getv("vel",vel->vel,
|
||||
"bsi",vel->bsi,
|
||||
"chL",chL)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error while reading the velocity record at index %i from the pitch record for MIDI pitch:%i.",j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
vel->mc_seq.chN = chL->child_count();
|
||||
vel->mc_seq.chA = mem::allocZ<wt_seq_t>( vel->mc_seq.chN );
|
||||
|
||||
for(unsigned ch_idx=0; ch_idx<instr->pitchA[i].velA[j].mc_seq.chN; ++ch_idx)
|
||||
{
|
||||
|
||||
const object_t* wtL = chL->child_ele(ch_idx);
|
||||
wt_seq_t* wts = vel->mc_seq.chA + ch_idx;
|
||||
|
||||
wts->wtN = wtL->child_count() + 1;
|
||||
wts->wtA = mem::allocZ<wt_t>( wts->wtN );
|
||||
|
||||
for(unsigned wti=0; wti<wts->wtN-1; ++wti)
|
||||
{
|
||||
const object_t* wtR = wtL->child_ele(wti);
|
||||
wt_t* wt = wts->wtA + wti + 1;
|
||||
unsigned wtbi = kInvalidIdx;
|
||||
unsigned wtei = kInvalidIdx;
|
||||
double rms = 0;
|
||||
|
||||
if((rc = wtR->getv("wtbi",wtbi,
|
||||
"wtei",wtei,
|
||||
"rms",rms,
|
||||
"est_hz",wt->hz)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Instrument file syntax error in wavetable record at index %i, channel index:%i, velocity index:%i midi pitch:%i.",wti,ch_idx,j,pitch->midi_pitch);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if this is the first looping wave table then insert the attack wave table before it
|
||||
if( wti==0 )
|
||||
{
|
||||
_alloc_wt(p,wts->wtA,dsp::wt_osc::kOneShotWtTId,srate,abuf.ch_buf[ch_idx], vel->bsi, wtbi-vel->bsi,hz,0);
|
||||
}
|
||||
|
||||
_alloc_wt(p,wt,dsp::wt_osc::kLoopWtTId,srate,abuf.ch_buf[ch_idx],wtbi,wtei-wtbi,hz,rms);
|
||||
}
|
||||
}
|
||||
}
|
||||
taskA[i].func = _load_pitch_thread_func;
|
||||
taskA[i].arg = r;
|
||||
taskA[i].rc = kOkRC;
|
||||
}
|
||||
|
||||
if( threadN>1 )
|
||||
{
|
||||
// create a thread_tasks object
|
||||
if((rc = thread_tasks::create( threadTasksH, threadN )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Thread machine create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = run(threadTasksH, taskA, instr->pitchN, 120*1000 )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Thread machine run failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
for(unsigned i=0; i<instr->pitchN; ++i)
|
||||
{
|
||||
p->allocAudioBytesN += argsA[i].allocByteN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned i=0; i<instr->pitchN; ++i)
|
||||
if((rc = _load_pitch( argsA + i )) != kOkRC )
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = _create_instr_pv_map( instr )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
@ -449,7 +535,11 @@ errLabel:
|
||||
rc = cwLogError(rc,"Wave table bank load failed on '%s'.",cwStringNullGuard(instr_json_fname));
|
||||
}
|
||||
|
||||
_audio_buf_free(abuf);
|
||||
//_audio_buf_free(abuf);
|
||||
|
||||
mem::release(argsA);
|
||||
mem::release(taskA);
|
||||
destroy(threadTasksH);
|
||||
|
||||
if( f != nullptr )
|
||||
f->free();
|
||||
|
@ -19,12 +19,12 @@ namespace cw
|
||||
typedef struct dsp::multi_ch_wt_seq_osc::multi_ch_wt_seq_str<sample_t,srate_t> multi_ch_wt_seq_t;
|
||||
|
||||
|
||||
rc_t create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname=nullptr );
|
||||
rc_t create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname=nullptr, unsigned loadThreadCnt=16 );
|
||||
rc_t destroy( handle_t& hRef );
|
||||
|
||||
void report( handle_t h );
|
||||
|
||||
rc_t load( handle_t h, const char* instr_json_fname );
|
||||
rc_t load( handle_t h, const char* instr_json_fname, unsigned threadN=16 );
|
||||
|
||||
unsigned instr_count( handle_t h );
|
||||
|
||||
|
@ -13,17 +13,26 @@
|
||||
vars: {
|
||||
dev_label: { type:string, value:"<all>", doc:"MIDI input device label. Set to '<all>' to accept input from any device."},
|
||||
port_label:{ type:string, value:"<all>", doc:"MIDI input device port label. Set to '<all>' to accept input from any device."},
|
||||
out: { type:midi, doc:"MIDI input port." },
|
||||
out: { type:midi, doc:"MIDI input port." },
|
||||
r_out: { type:record, doc:"MIDI record.",
|
||||
fmt: {
|
||||
fields: {
|
||||
midi: { type:m3, doc:"MIDI channel event message" },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
midi_out: {
|
||||
doc: [ "MIDI output device."],
|
||||
vars: {
|
||||
dev_label: { type:string, doc:"MIDI output device label."},
|
||||
port_label:{ type:string, doc:"MIDI output device port label."},
|
||||
dev_label: { type:string, flags:["init"], doc:"MIDI output device label."},
|
||||
port_label:{ type:string, flags:["init"], doc:"MIDI output device port label."},
|
||||
buf_cnt: { type:uint, flags:["init"], value:64, doc:"MIDI message buffer count." },
|
||||
in: { type:midi, doc:"MIDI output port."}
|
||||
in: { type:midi, doc:"MIDI messages to send."},
|
||||
rin: { type:record, fmt:{ required:["midi"]}, doc:"Record input. (must have 'midi' field)"},
|
||||
print_fl: { type:bool, value:false, doc:"Print the output to the console."}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +76,11 @@
|
||||
gain: { type:coeff, value:1.0, doc:"Gain coefficient." }
|
||||
out: { type:audio, doc:"Audio output." },
|
||||
}
|
||||
presets:
|
||||
{
|
||||
mute_off: { gain:1 },
|
||||
mute_on: { gain:0 }
|
||||
}
|
||||
}
|
||||
|
||||
audio_split: {
|
||||
@ -154,18 +168,22 @@
|
||||
a220 : { hz:220 },
|
||||
a440 : { hz:440 },
|
||||
a880 : { hz:880 },
|
||||
mono: { ch_cnt:1, gain:0.75 }
|
||||
mono: { ch_cnt:1, gain:0.75 },
|
||||
lfo1: { hz:1, phase:0.11 },
|
||||
lfo2: { hz:2, phase:0.22 },
|
||||
lfo3: { hz:3, phase:0.33 },
|
||||
}
|
||||
}
|
||||
|
||||
pv_analysis: {
|
||||
vars: {
|
||||
in: { type:audio, flags:["src"], doc:"Audio input." },
|
||||
maxWndSmpN: { type:uint, value: 512, doc:"Maximum window sample count." },
|
||||
wndSmpN: { type:uint, value: 512, doc:"Window sample count." },
|
||||
hopSmpN: { type:uint, value: 128, doc:"Hop sample count." },
|
||||
hzFl: { type:bool, value: false, doc:"Calculate frequency via the method of phase changeof each bin." },
|
||||
out: { type:spectrum, doc:"Spectrum output." }
|
||||
in: { type:audio, flags:["src"], doc:"Audio input." },
|
||||
enable: { type:bool, value: true, doc:"Enable/disable the processor."}
|
||||
maxWndSmpN: { type:uint, value: 1024, doc:"Maximum window sample count." },
|
||||
wndSmpN: { type:uint, value: 512, doc:"Window sample count." },
|
||||
hopSmpN: { type:uint, value: 128, doc:"Hop sample count." },
|
||||
hzFl: { type:bool, value: false, doc:"Calculate frequency via the method of phase change of each bin." },
|
||||
out: { type:spectrum, doc:"Spectrum output." }
|
||||
}
|
||||
|
||||
presets: {
|
||||
@ -181,62 +199,62 @@
|
||||
}
|
||||
|
||||
a: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
b: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
c: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
d: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
f_1: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
f_2: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
f_3: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
f_4: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
g: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
g_a: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
g_1_a: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [512,1024],
|
||||
hopSmpN: 128
|
||||
}
|
||||
|
||||
g_1_d: {
|
||||
wndSmpN: 512,
|
||||
wndSmpN: [1024,512],
|
||||
hopSmpN: 128
|
||||
}
|
||||
}
|
||||
@ -244,15 +262,16 @@
|
||||
|
||||
pv_synthesis: {
|
||||
vars: {
|
||||
in: { type:spectrum, flags:["src"], doc:"Spectrum input." },
|
||||
out: { type:audio, doc:"Audio output." }
|
||||
in: { type:spectrum, flags:["src"], doc:"Spectrum input." },
|
||||
enable: { type:bool, value: true, doc:"Enable/disable the processor."}
|
||||
out: { type:audio, doc:"Audio output." }
|
||||
}
|
||||
}
|
||||
|
||||
spec_dist: {
|
||||
vars: {
|
||||
in: { type:spectrum, flags:["src"], doc:"Spectrum input." },
|
||||
|
||||
enable: { type:bool, value: true, doc:"Enable/disable this processor."},
|
||||
bypass: { type:bool, value: false, doc:"Copy input to output without transform."},
|
||||
ceiling: { type:coeff, value: 30.0, doc:"Ceiling parameter."},
|
||||
expo: { type:coeff, value: 2.0, doc:"Exponent parameter."},
|
||||
@ -393,8 +412,9 @@
|
||||
|
||||
compressor: {
|
||||
vars: {
|
||||
in: { type:audio, flags:["src"] true, doc:"Audio input." },
|
||||
bypass: { type:bool, value: false, doc:"Bypass the compressor."},
|
||||
in: { type:audio, flags:["src"], doc:"Audio input." },
|
||||
enable: { type:bool, value: true, doc:"Same as bypass with opposite polarity." },
|
||||
bypass: { type:bool, value: false, doc:"Bypass the compressor."},
|
||||
igain: { type:coeff, value: 1.0, doc:"Input gain."},
|
||||
thresh: { type:coeff, value: 90.0, doc:"Attack threshold in dB."},
|
||||
ratio: { type:coeff, value: 2.0, doc:"Compression ratio."},
|
||||
@ -509,12 +529,12 @@
|
||||
|
||||
limiter: {
|
||||
vars: {
|
||||
in: { type:audio, flags:["src"] true, doc:"Audio input." },
|
||||
bypass: { type:bool, value: false, doc:"Bypass the limiter."},
|
||||
in: { type:audio, flags:["src"], doc:"Audio input." },
|
||||
bypass: { type:bool, value: false, doc:"Bypass the limiter."},
|
||||
igain: { type:coeff, value: 1.0, doc:"Input gain."},
|
||||
thresh: { type:coeff, value: 0.0, doc:"Linear (0.0-1.0) threshold."},
|
||||
ogain: { type:coeff, value: 1.0, doc:"Output gain."},
|
||||
out: { type:audio, doc:"Audio output." },
|
||||
out: { type:audio, doc:"Audio output." },
|
||||
}
|
||||
|
||||
presets: {
|
||||
@ -563,6 +583,8 @@
|
||||
vars: {
|
||||
count: { type:uint, flags:["init"], value:1, doc:"Count of network duplicates." },
|
||||
parallel_fl: { type:bool, flags:["init"], value:false, doc:"True to run voices concurrently." }
|
||||
preset_sfx_id:{ type:uint, value:-1, doc:"The voice index to assign the prefix to or -1 if there is no preset to assign." },
|
||||
preset_label: { type:string, value:"", doc:"The network preset to activate on voice channel 'preset_idx'." },
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,11 +669,11 @@
|
||||
// string,cfg,numeric (uint,int,float,double)
|
||||
list: {
|
||||
vars: {
|
||||
cfg_fname: { type:string, value:"", flags:["init"], doc:"List cfg file." },
|
||||
in: { type:uint, flags:["src"], doc:"List selection index." },
|
||||
list: { type:cfg, value:{} doc:"List as a 'cfg' object." },
|
||||
out: { type:runtime, doc:"List output value." },
|
||||
value: { type:runtime, flags["mult"], doc:"List 'mult' output per list value." },
|
||||
cfg_fname: { type:string, flags:["init"], value:"", doc:"List cfg file." },
|
||||
in: { type:uint, flags:["src"], doc:"List selection index." },
|
||||
list: { type:cfg, flags:["init"], value:{} doc:"List as a 'cfg' object." },
|
||||
out: { type:runtime, doc:"List output value." },
|
||||
value: { type:runtime, flags["mult"], doc:"List 'mult' output per list value." },
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,8 +715,11 @@
|
||||
|
||||
midi_voice: {
|
||||
vars: {
|
||||
in: { type:midi, doc:"MIDI in" },
|
||||
out: { type:audio, doc:"Audio out" }
|
||||
in: { type:midi, doc:"MIDI in" },
|
||||
gain: { type:coeff, value:1.0, doc:"Audio gain." },
|
||||
chCnt: { type:uint, flags["init"], doc:"Output audio channel count."},
|
||||
out: { type:audio, doc:"Audio out" },
|
||||
print_fl: { type:bool, value:false, doc:"Print MIDI message to console." },
|
||||
done_fl: { type:bool, value:false, doc:"Triggers when voice is available."}
|
||||
}
|
||||
},
|
||||
@ -705,7 +730,7 @@
|
||||
|
||||
vars: {
|
||||
|
||||
in: { type:midi, doc:"MIDI input."},
|
||||
in: { type:record, fmt:{ required:["midi"]} doc:"MIDI input."},
|
||||
voice_cnt: { type:uint, value:3, flags:["init"], doc:"Count of voices." },
|
||||
out: { type:midi, flags:["mult"], doc:"MIDI output to voices. One per voice." },
|
||||
done_fl: { type:bool, value:false, flags:["mult"], doc:"Voice available feedback triggers from voices. One per voice."},
|
||||
@ -720,8 +745,14 @@
|
||||
in: { type:midi, doc:"MIDI in" },
|
||||
out: { type:audio, doc:"Audio out" },
|
||||
done_fl: { type:bool, value:false, doc:"Triggers when voice is available."},
|
||||
test_pitch: { type:uint, value:0, doc:"Pitch to test." },
|
||||
gate_fl: { type:bool, value:false, doc:"True when voice is active, false when inactive." },
|
||||
rls_coeff: { type:coeff, value:0.9, doc:"Release decay factor. Increase for longer decays."},
|
||||
rls_thresh:{ type:coeff, value:0.01,doc:"Note off threshold. Decrease for longer decays."},
|
||||
|
||||
test_pitch: { type:uint, value:0, doc:"Pitch to test." },
|
||||
test_key_pitch: { type:uint, value:48, doc:"Base pitch to use for lowest velocity when in 'test' mode." },
|
||||
|
||||
load_thread_cnt: { type:uint, flags["init"], value:16, doc:"Count of threads to use for wave table loading." }
|
||||
}
|
||||
},
|
||||
|
||||
@ -780,5 +811,189 @@
|
||||
}
|
||||
}
|
||||
|
||||
poly_xform_ctl: {
|
||||
doc: ["On 'trigger' mix and route all incoming audio to the next available output channel "],
|
||||
vars: {
|
||||
out_ch_cnt:{ type:uint, flags:["init"], doc:"Count of audio output channels."},
|
||||
in: { type:audio, flags:["src","mult"], doc:"Audio input." },
|
||||
midi: { type:midi, flags:["mult"], doc:"Per audio channel MIDI message from poly_voice_ctl." },
|
||||
donefl: { type:bool, flags["mult"], doc:"Per audio channel voice 'available' flags." },
|
||||
trig: { type:all, doc:"Trigger to advance to next channel."},
|
||||
out_ch_idx:{ type:uint, value:0, doc:"Transform preset id. Change indicates that successive notes should go to a new output." },
|
||||
out: { type:audio, flags:["mult"] doc:"Audio output."},
|
||||
}
|
||||
}
|
||||
|
||||
score_player: {
|
||||
doc: [ "Generate MIDI and score location information from a score file." ]
|
||||
vars: {
|
||||
fname: { type:string, flags:["init"], value:"", doc:"Score file name." },
|
||||
stopping_ms: { type:uint, flags:["init"], value:5000, doc:"Maximum stopping time in milliseconds." },
|
||||
start: { type:all, value:false, doc:"Start playback" },
|
||||
stop: { type:all, value:false, doc:"Stop playback" },
|
||||
b_loc: { type:uint, value:0, doc:"Score begin location." },
|
||||
e_loc: { type:uint, value:0, doc:"Score end location." },
|
||||
b_meas: { type:uint, value:0, doc:"Score begin measure." },
|
||||
e_meas: { type:uint, value:0, doc:"Score end measure." },
|
||||
done_fl: { type:bool, value:false, doc:"Emits true on done." },
|
||||
out: { type:record, doc:"Score event record.",
|
||||
fmt: {
|
||||
fields: {
|
||||
midi: { type:m3, doc:"MIDI channel event message" },
|
||||
loc: { type:uint, value:-1, doc:"Score location id." },
|
||||
meas: { type:uint, value:-1, doc:"Score measure." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
vel_table: {
|
||||
doc:[ "Remap MIDI velocity values."]
|
||||
vars: {
|
||||
vel_tbl_fname:{ type:string, flags:["init"], value:"", doc:"Velocity table filename as create by vwVelTableTuner." },
|
||||
vel_tbl_label:{ type:string, flags:["init"], value:"", doc:"Name of the active velocity table referenced by 'vel_fname'."},
|
||||
in: { type:record, fmt:{ required:["midi"]}, doc:"Record with 'midi' field."},
|
||||
out:{ type:record, doc:"Same as input record with midi velocity remapped according the velocity table."
|
||||
fmt: { fields: { midi: { type:m3, doc:"MIDI channel event message" } }}
|
||||
|
||||
}
|
||||
recdbufN: { type:uint, flags:["init"], value:128, doc:"Internal record buffer size."}
|
||||
}
|
||||
}
|
||||
|
||||
score_follower: {
|
||||
doc:[ "MIDI score follower: Sets the 'loc' field of the output record according to the score location." ]
|
||||
vars: {
|
||||
in: { type:record, doc:"Input record with 'midi' and 'loc' fields." },
|
||||
dyntbl_fname: { type:string, flags:["init"], value:"", doc:"Dynamics reference table file name." },
|
||||
score_fname: { type:string, flags:["init"], value:"", doc:"Score file with location information." },
|
||||
score_wnd: { type:uint, flags:["init"], value:7, doc:"Count of locations in the score search window." },
|
||||
midi_wnd: { type:uint, flags:["init"], value:15, doc:"Count of MIDI events to align in the score search window." },
|
||||
print_fl: { type:bool, flags:["init"], value:false, doc:"Set to print log of score follower state." },
|
||||
back_track_fl: { type:bool, flags["init"], value:false, doc:"Set if the score follower is allowed to report locations out of order." },
|
||||
loc: { type:uint, doc:"Score location." },
|
||||
out: { type:record, doc:"Input record with the 'loc' field set ." }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
preset_select: {
|
||||
doc:[ "Given a score location emit a preset label."],
|
||||
vars: {
|
||||
cfg: { type:cfg, flags:["init"], doc:"Initial preset configuration." },
|
||||
in: { type:record, fmt:{ required:["loc"]} doc:"Input record with 'loc' field." },
|
||||
fname: { type:string, flags:["init"], value:"", doc:"Preset file name."},
|
||||
loc: { type:uint, value:0, doc:"Seek to this location." },
|
||||
|
||||
//out_idx: { type:uint, value:0, doc:"Transform output index." },
|
||||
preset_label: { type:string, value:"", doc:"Preset label to apply." }
|
||||
}
|
||||
|
||||
presets: {
|
||||
init: {
|
||||
cfg: {
|
||||
preset_labelL: [ "dry", "a", "b", "c", "d", "f1", "f2", "f3", "f4", "g", "ga", "g1a", "g1d" ],
|
||||
|
||||
alt_labelL: [ "A","B","C","D","E","F","G" ],
|
||||
|
||||
default_gain: 1.0,
|
||||
default_wet_dry_gain: 0.5,
|
||||
default_fade_ms: 50.0,
|
||||
default_preset: "dry",
|
||||
|
||||
default_master_wet_in_gain: 1.0,
|
||||
default_master_wet_out_gain: 1.0,
|
||||
default_master_dry_gain: 1.0,
|
||||
default_master_sync_delay_ms: 400, // spirio 400
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gutim_ps: {
|
||||
doc:[ "Given score location and MIDI note messages emit transform parameters."],
|
||||
vars: {
|
||||
cfg: { type:cfg, flags:["init"], doc:"Initial preset configuration." },
|
||||
fname: { type:string, flags:["init"], value:"", doc:"Preset file name."},
|
||||
in: { type:record, fmt:{ required:["loc"] }, doc:"Input record with 'loc' field." },
|
||||
loc: { type:uint, value:0, doc:"Seek to this location." },
|
||||
reset: { type:bool, value:false, doc:"Reset to initial state."},
|
||||
per_note_fl: { type:bool, value:false, doc:"Update the selected preset on every note, otherwise update on new location values." },
|
||||
|
||||
pri_prob_fl: { type:bool, value: false, doc:"Select primary preset probabilstically." }
|
||||
pri_uniform_fl: { type:bool, flags:["ui_disable"], value: false, doc:"Use a uniform probability distribution rather than an 'order' weighted distribution." },
|
||||
pri_dry_on_play_fl: { type: bool, flags:["ui_disable"], value: true, doc:"Select 'dry' if it is the manually selected preset." },
|
||||
pri_allow_all_fl: { type: bool, flags:["ui_disable"], value: false, doc:"Select from all possible presets rather than the presets with 'order' > 0." },
|
||||
pri_dry_on_sel_fl: { type: bool, flags:["ui_disable"], value: true, doc:"Select 'dry' if 'allow_all' is selected and 'dry' has 'order' > 0." },
|
||||
|
||||
interp_fl: { type: bool, value: false, doc:"Interpolate between the primary and secondary presets." },
|
||||
interp_dist: { type: float, flags:["ui_disable"], value: 0.5, doc:"Unit fractionaly distance between primary and secondary preset." },
|
||||
interp_rand_fl: { type: bool, flags:["ui_disable"], value: true, doc:"Randomly select the distance between the primary and secondary preset." },
|
||||
|
||||
sec_prob_fl: { type:bool, flags:["ui_disable"], value: false, doc:"Select secondary preset probabilstically." }
|
||||
sec_uniform_fl: { type:bool, flags:["ui_disable"], value: false, doc:"Use a uniform probability distribution rather than an 'order' weighted distribution." },
|
||||
sec_dry_on_play_fl: { type: bool, flags:["ui_disable"], value: true, doc:"Select 'dry' if it is the manually selected preset." },
|
||||
sec_allow_all_fl: { type: bool, flags:["ui_disable"], value: false, doc:"Select from all possible presets rather than the presets with 'order' > 0." },
|
||||
sec_dry_on_sel_fl: { type: bool, flags:["ui_disable"], value: true, doc:"Select 'dry' if 'allow_all' is selected and 'dry' has 'order' > 0." },
|
||||
|
||||
|
||||
midi_in: { type:midi, flags:["mult","no_ui"], doc:"Per voice MIDI in" },
|
||||
wnd_smp_cnt: { type:uint, value:512, flags:["mult","no_ui"], doc:"PVA window sample count" },
|
||||
ceiling: { type:coeff, value:20, flags:["mult","no_ui"], doc:"SD ceiling parameter" },
|
||||
expo: { type:coeff, value:2.0, flags:["mult","no_ui"], doc:"SD expo parameter" },
|
||||
thresh: { type:coeff, value:70.0, flags:["mult","no_ui"], doc:"SD thresh parameter" },
|
||||
upr: { type:coeff, value:0.0, flags:["mult","no_ui"], doc:"SD upr parameter" },
|
||||
lwr: { type:coeff, value:2.0, flags:["mult","no_ui"], doc:"SD lwr parameter" },
|
||||
mix: { type:coeff, value:0.0, flags:["mult","no_ui"], doc:"SD mix parameter" },
|
||||
c_igain: { type:coeff, value:1.0, flags:["mult","no_ui"], doc:"Compressor igain parameter" },
|
||||
c_ogain: { type:coeff, value:1.0, flags:["mult","no_ui"], doc:"Compressor ogain paramter" },
|
||||
dry_gain: { type:coeff, value:1.0, flags:["mult","no_ui"], doc:"Output gain." },
|
||||
}
|
||||
|
||||
|
||||
presets: {
|
||||
dry: { dry_gain:0 },
|
||||
a: { dry_gain:1.0 },
|
||||
b: { dry_gain:1.0 },
|
||||
c: { dry_gain:1.0 },
|
||||
d: { dry_gain:1.0 },
|
||||
f_1: { dry_gain:1.0 },
|
||||
f_2: { dry_gain:1.0 },
|
||||
f_3: { dry_gain:1.0 },
|
||||
f_4: { dry_gain:1.0 },
|
||||
g: { dry_gain:1.0 },
|
||||
g_a: { dry_gain:1.0 },
|
||||
g_1_a: { dry_gain:1.0 },
|
||||
g_1_d: { dry_gain:1.0 },
|
||||
|
||||
init: {
|
||||
cfg: {
|
||||
preset_labelL: [ "dry", "a", "b", "c", "d", "f1", "f2", "f3", "f4", "g", "ga", "g1a", "g1d" ],
|
||||
|
||||
alt_labelL: [ "A","B","C","D","E","F","G" ],
|
||||
|
||||
default_gain: 1.0,
|
||||
default_wet_dry_gain: 0.5,
|
||||
default_fade_ms: 50.0,
|
||||
default_preset: "dry",
|
||||
|
||||
default_master_wet_in_gain: 1.0,
|
||||
default_master_wet_out_gain: 1.0,
|
||||
default_master_dry_gain: 1.0,
|
||||
default_master_sync_delay_ms: 400, // spirio 400
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
694
notes.md
694
notes.md
@ -941,19 +941,44 @@ resolvable without more information.
|
||||
|
||||
### TODO:
|
||||
|
||||
- Eliminate the value() custom proc_t function and replace it by setting a 'delta flag' on the
|
||||
- Why doesn't the C7 on the downbeat of meas. 11 sound?
|
||||
- DONE: Allow setting the location of the score player. This should also reset the sampler and voice control.
|
||||
- DONE: The voice ctl should respond to all-notes-off message and reset each sampler channel
|
||||
|
||||
- The following two tasks need more consideration. As it is variables assume that aggregate
|
||||
types are destroyed on exit. This is very convenient. Consider using 'symbols' to represent
|
||||
strings, consider adding a 'const-string' type to eliminate memory allocation of string assignment
|
||||
- Memory allocation of all non-integral types should be the responsibility of the the processor instances
|
||||
this includes strings. Change the built-in string type to be a const string and
|
||||
make it the responsibility of the proc instances with 'string' var's to handle
|
||||
alloc and dealloc of strings.
|
||||
|
||||
- String assignment is allocating memory:
|
||||
See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.`
|
||||
|
||||
- DONE Presets do not work for hetergenous networks.
|
||||
|
||||
- Add a proc class flag: 'top-level-only-fl' to indicate that a processor
|
||||
cannot run as part of a poly. Any processor that calls a global function,
|
||||
like 'network_apply_preset()' must run a the top level only.
|
||||
|
||||
- Consider eliminating the value() custom proc_t function and replace it by setting a 'delta flag' on the
|
||||
variables that change. Optionally a linked list of changed variables could be implemented to
|
||||
avoid having to search for changed variable values - although this list might have to be implemented as a thread safe linked list.
|
||||
|
||||
- Allow proc's to send messages to the UI. Implementation: During exec() the proc builds a global list of variables whose values
|
||||
- value() should return a special return-code value to indicate that the
|
||||
value should not be updated and distinguish it from an error code - which should stop the system.
|
||||
|
||||
|
||||
- DONE: Allow proc's to send messages to the UI. Implementation: During exec() the proc builds a global list of variables whose values
|
||||
should be passed to the UI. Adding to the list must be done atomically, but removing can be non-atomic because it will happen
|
||||
at the end of the network 'exec' cycle when no proc's are being executed. See cwMpScNbQueue push() for an example of how to do this.
|
||||
|
||||
- Allow min/max limits on numeric variables.
|
||||
|
||||
- Add a 'doc' string-list to the class desc.
|
||||
- DONE: Add a 'doc' string-list to the class desc.
|
||||
|
||||
- Try using adding 'time' type and 'cfg' types to a 'log:{...}' stmt.
|
||||
- Add 'time' type and 'cfg' types to a 'log:{...}' stmt.
|
||||
|
||||
- print_network_fl and print_proc_dict_fl should be given from the command line.
|
||||
(but it's ok to leave them as cfg flags also)
|
||||
@ -962,16 +987,19 @@ at the end of the network 'exec' cycle when no proc's are being executed. See c
|
||||
|
||||
- Add 'doc' strings to user-defined proc data structure.
|
||||
|
||||
- It is an error to specify a suffix_id on a poly network proc because the suffix_id's are generated automatically.
|
||||
- DONE: It is an error to specify a suffix_id on a poly network proc because the suffix_id's are generated automatically.
|
||||
This error should be caught by the compiler.
|
||||
|
||||
- How do user defined procedures handle suffix id's?
|
||||
|
||||
- Add a 'preset' arg to 'poly' so that a preset can be selected via the owning network.
|
||||
- DONE: Add a 'preset' arg to 'poly' so that a preset can be selected via the owning network.
|
||||
Currently it is not possible to select a preset for a poly.
|
||||
|
||||
- Automatic assignment of sfx_id's should only occur when the network is a 'poly'.
|
||||
This should be easy to detect.
|
||||
- DONE: Automatic assignment of sfx_id's should only occur when the network is a 'poly'.
|
||||
This should be easy to detect.
|
||||
|
||||
- DONE: If a proc, inside a poly, is given a numeric suffix then that suffix will
|
||||
overwrite the label_sfx_id assigned by the system. This case should be detected.
|
||||
|
||||
- When a var value is given to var_create() it does not appear to channelize the
|
||||
var if value is a list. Is a value ever given directly to `var_create()`?
|
||||
@ -979,15 +1007,12 @@ Look at all the places `var_create()` is called can the value arg. be removed?
|
||||
|
||||
- var_channelize() should never be called at runtime.
|
||||
|
||||
- Re-write the currawong circuit with caw.
|
||||
- DONE: Re-write the currawong circuit with caw.
|
||||
|
||||
- Finish audio feedback example - this will probably involve writing an `audio_silence` class.
|
||||
|
||||
- Issue a warning if memory is allocated during runtime.
|
||||
|
||||
- String assignment is allocating memory:
|
||||
See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.`
|
||||
|
||||
- cwMpScNbQueue is allocating memory. This makes it blocking.
|
||||
|
||||
- Check for illegal variable names in class descriptions. (no periods, trailing digits, or trailing underscores)
|
||||
@ -1013,16 +1038,19 @@ Look at all the places `var_create()` is called can the value arg. be removed?
|
||||
|
||||
+ Disabled "disp_str" should turn grey.
|
||||
|
||||
+ mult var's with more than 3 values should be put into a list or use a 'disclose' button
|
||||
|
||||
+ mult proc's with more than 3 instances should be put into a list or use a 'disclose' button
|
||||
|
||||
|
||||
|
||||
- Class presets cannot address 'mult' variables. Maybe this is ok since 'mult' variables are generally connected to a source?
|
||||
... although 'gain' mult variables are not necessarily connected to a source see: `audio_split` or `audio_mix`.
|
||||
Has this problem been addressed by allowing mult variables to be instantiated in the 'args' statement?
|
||||
|
||||
- Documentation w/ examples.
|
||||
- Write processor development documentation w/ examples.
|
||||
+ Write the rules for each implementing member functions.
|
||||
|
||||
- value() should return a special return-code value to indicate that the
|
||||
value should not be updated and distinguish it from an error code - which should stop the system.
|
||||
|
||||
- flow classes and variable should have a consistent naming style: camelCase or snake_case.
|
||||
|
||||
@ -1035,7 +1063,6 @@ value should not be updated and distinguish it from an error code - which should
|
||||
|
||||
- Reduce runtime overhead for var get/set operations.
|
||||
|
||||
- Implement matrix types.
|
||||
|
||||
- Should the `object_t` be used in place of `value_t`?
|
||||
|
||||
@ -1048,6 +1075,10 @@ value should not be updated and distinguish it from an error code - which should
|
||||
- Audio inputs should be able to be initialized with a channel count and srate without actually connecting an input.
|
||||
This will allow feedback connections to be attached to them at a later stage of the network
|
||||
instantiation.
|
||||
|
||||
- The signal srate should determine the sample rate used by a given processor.
|
||||
The system sample rate should only be used a default/fallback value.
|
||||
Processors that have mandatory signal inputs should never need to also have an srate parameter.
|
||||
|
||||
- Implement user-defined-proc preset application.
|
||||
|
||||
@ -1061,15 +1092,23 @@ value should not be updated and distinguish it from an error code - which should
|
||||
|
||||
- Implement a debug mode to aid in building networks and user-defined-procs (or is logging good enough)
|
||||
|
||||
- Implement multi-field messages.
|
||||
- DONE: Implement multi-field messages.
|
||||
|
||||
- Implement user defined data types.
|
||||
|
||||
- Look more closely at the way of identify an in-stmt src-net or a out-stmt in-net.
|
||||
- Implement matrix types.
|
||||
|
||||
- Add a 'trigger' data type. The 'kAllTId' isn't really doing anything.
|
||||
|
||||
- There should be special logging macros inside procs that automatically log the instance name.
|
||||
|
||||
- Look more closely at the way to identify an in-stmt src-net or a out-stmt in-net.
|
||||
It's not clear there is a difference between specifying `_` and the default behaviour.
|
||||
Is there a way to tell it to search the entire network from the root? Isn't that
|
||||
what '_' is supposed to do?
|
||||
|
||||
- cwAudioFile cannot convert float or double input samples to 24 bit output samples
|
||||
See audiofile::writeFloat() and audiofile::writeDouble().
|
||||
|
||||
|
||||
Host Environments:
|
||||
@ -1078,129 +1117,6 @@ Host Environments:
|
||||
- CLI, no GUI, w/ I/O and real-time
|
||||
- GUI, with configurable control panels
|
||||
|
||||
Done
|
||||
----
|
||||
- DONE: Remove `preset_label` and `type_src_label` from `_var_channelize()` and report error
|
||||
locations from the point of call.
|
||||
|
||||
- DONE: Move proc_dict.cfg to libcw directory.
|
||||
|
||||
- DONE: The proc inst 'args' should be able to create mult variables. The only way to instantiate
|
||||
new mult variables now is via the 'in' stmt.
|
||||
|
||||
- DONE: The `audio_merge` implementaiton is wrong. It should mimic `audio_mix` where all igain
|
||||
coeff's are instantiated even if they are not referenced.
|
||||
|
||||
- DONE: Add the `caw` examples to the test suite.
|
||||
|
||||
|
||||
- DONE: Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's.
|
||||
|
||||
- DONE: Compile presets: at load time the presets should be resolved
|
||||
to the proc and vars to which they will be assigned.
|
||||
|
||||
|
||||
- DONE: (We are not removing the kAnyChIdx)
|
||||
Should the var's with multiple channels remove the 'kAnyChIdx'?
|
||||
This may be a good idea because 'kAnyChIdx' will in general not be used
|
||||
if a var has been channelized - and yet it is possible for another
|
||||
var to connect to it as a source ... which doesn't provoke an error
|
||||
but would almost certainly not do what the user expects.
|
||||
Note that the kAnyChIdx provides an easy way to set all of the channels
|
||||
of a variable to the same value.
|
||||
|
||||
- DONE: verifiy that all proc variables values have a valid type - (i.e. (type & typeMask) != 0)
|
||||
when the proc instance create is complete. This checks that both the type is assigned and
|
||||
a valid value has been assigned - since the type is assigned the first time a value is set.
|
||||
|
||||
- DONE: 'poly' should be implemented as a proc-inst with an internal network - but the
|
||||
elements of the network should be visible outside of it.
|
||||
|
||||
- DONE: 'sub' should be implemented as proc-inst with an internal network, but the
|
||||
elements of the network should not be visible outside of it. Instead it should
|
||||
include the idea of input and output ports which act as proxies to the physical
|
||||
ports of the internal elements.
|
||||
|
||||
- DONE: 'poly' and 'sub' should be arbitrarily nestable.
|
||||
|
||||
- DONE: Allow multiple types on an input.
|
||||
For example 'adder' should have a single input
|
||||
which can by any numeric type.
|
||||
|
||||
|
||||
- DONE: Make a standard way to turn on output printing from any port on any instance
|
||||
This might be a better approach to logging than having a 'printer' object.
|
||||
Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } `
|
||||
|
||||
- Complete user-def-procs:
|
||||
+ User-Def-Procs should have presets written in terms of the user-def-proc vars rather than the network vars
|
||||
or the value application needs to follow the internal variable src_var back to the proxy var.
|
||||
|
||||
+ DONE: write a paragraph in the flow_doc.md about overall approach taken to user-def-proc implementation.
|
||||
|
||||
+ DONE: user-def-proc var desc's should be the same as non+user-def-proc vars but also include the 'proxy' field.
|
||||
In particular they should get default values.
|
||||
If a var desc is part of a user-def-proc then it must have a proxy.
|
||||
The output variables of var desc's must have the 'out' attribute
|
||||
|
||||
|
||||
+ DONE: improve the user-def-proc creating code by using consistent naming + use proxy or wrap but not both
|
||||
|
||||
+ DONE: improve code comments on user-def-proc creation
|
||||
|
||||
|
||||
- DONE: Implement feedback
|
||||
|
||||
- DONE: Implement the ability to set backward connections - from late to early proc's.
|
||||
This can be done by implementing the same process as 'in_stmt' but in a separate
|
||||
'out_stmt'. The difficulty is that it prevents doing some checks until the network
|
||||
is completely specified. For example if audio inputs can accept connections from
|
||||
later proc's then they will not have all of their inputs when they are instantiated.
|
||||
One way around this is to instantiate them with an initial set of inputs but then
|
||||
allow those inputs to be replaced by a later connection.
|
||||
|
||||
BUGS:
|
||||
- DONE: The counter modulo mode is not working as expected.
|
||||
|
||||
|
||||
|
||||
- DONE: Implement 'preset' proc. This will involve implementing the 'cfg' datatype.
|
||||
|
||||
- DONE: Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them?
|
||||
|
||||
- DONE: Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates?
|
||||
|
||||
- DONE: There is no way for a proc in a poly context to use it's poly channel number to
|
||||
select a mult variable. For example in an osc in a poly has no way to select
|
||||
the frequency of the osc by conneting to a non-poly proc - like a list.
|
||||
Consider:
|
||||
1. Use a difference 'in' statememt (e.g. 'poly-in' but the
|
||||
same syntax used for connecting 'mult' variables.)
|
||||
2. Include the proc name in the 'in' var to indicate a poly index is being iterated
|
||||
e.g. `lfo: { class:sine_tone, in:{ osc_.dc:list.value_ } }`
|
||||
|
||||
- DONE: Fix up the coding language - change the use of `instance_t` to `proc_t` and `inst` to `proc`, change use of `ctx` in cwFlowProc
|
||||
|
||||
|
||||
|
||||
DONE: After the network is fully instantiated the network and class presets
|
||||
are compiled. At this point all preset values must be resolvable to
|
||||
an actual proc variable. A warning is issued for presets with values
|
||||
that cannot be resolved and they are disabled. The primary reason
|
||||
that a preset might not be resolvable is by targetting a variable
|
||||
channel that does not exist.
|
||||
|
||||
- DONE: All cfg to value conversion should go through `cfg_to_value()`.
|
||||
|
||||
|
||||
Names
|
||||
------
|
||||
ixon -
|
||||
hoot
|
||||
caw, screech, warble, coo, peep, hoot, gobble, quack, honk, whistle, tweet, cheep, chirrup, trill, squawk, seet,
|
||||
cluck,cackle,clack
|
||||
cock-a-dooodle-doo
|
||||
song,tune,aria
|
||||
|
||||
Proc instantiation
|
||||
------------------
|
||||
@ -1282,17 +1198,507 @@ caw w/ UI
|
||||
- get basic information from proc dict
|
||||
- get override information from the network
|
||||
|
||||
3.
|
||||
|
||||
Network Execution:
|
||||
------------------
|
||||
|
||||
1. During real-time execution the network is executed on callbacks from the audio subsytem.
|
||||
These callbacks occur asynchronously on the IO system audio processing thread.
|
||||
|
||||
2. During real-time processing the MIDI callbacks are also asynchronous.
|
||||
|
||||
|
||||
|
||||
Network Architecture and Theory of Operation
|
||||
---------------------------------------------
|
||||
|
||||
A _caw_ graph is an ordered set of processors where a given
|
||||
processor may contain a set of internal networks.
|
||||
This leads to a heirarchy of networks and processors
|
||||
that can be depicted like this.
|
||||
|
||||
[ diagram goes here.]
|
||||
|
||||
This diagram shows a two level network, where the internal
|
||||
network contains an array of networks.
|
||||
|
||||
Networks are executed sequentially, one processor at a time, from top
|
||||
to bottom. Networks that are members of the same network array,
|
||||
referred to as silbing networks, may however execute concurrently to
|
||||
one another. To avoid concurrency hazards sibling networks may
|
||||
therefore not contain any inter-connnections, and the language
|
||||
precludes them.
|
||||
|
||||
There are two primary thread hazards to this arrangment:
|
||||
1. Processors executing outside of the top level
|
||||
should not write to global data or interact with
|
||||
the global system API.
|
||||
|
||||
There is no way for the top level of a network to be a sibling
|
||||
network, therefore processors that are part of this network are
|
||||
guaranteed to run in sequence and be the only processor running while
|
||||
they are executing.
|
||||
|
||||
For example, network presets can only be applied between execution
|
||||
cycles (by the control application) or by a top-level processor. This
|
||||
is the case because it guarantees that no processors are running when
|
||||
the preset values are set.
|
||||
|
||||
|
||||
2. If a processor receives data from a sibling network it is possible
|
||||
that the processors value() function is called from multiple
|
||||
concurrent threads. Processors which receive data from sibling
|
||||
networks (e.g. audio_mixer, poly_xform_ctl) should either not
|
||||
implement value() functions or be thread aware in their handling of
|
||||
calls to value().
|
||||
|
||||
Depending on the nature of the processing in the value() function
|
||||
this may not be particularly problematic since a given variable may
|
||||
only be connected to a single source. While the value() function
|
||||
may be called from multiple overlapping threads the arguments
|
||||
to each thread will refer to a unique variable. The built-in variable update
|
||||
process is carefully designed to exploit this invariant and not modify
|
||||
any process state with the exception of the targetted variable itself.
|
||||
|
||||
The danger in value() function processing is in writing to any process
|
||||
state, or any other variable than the one being reported as changing.
|
||||
Likewise it should be recognized that even reading the value of other
|
||||
variables should be done with caution. Reading other variables is
|
||||
thread safe in the sense that the internal state of the processor will
|
||||
be safe to traverse. The actual value of other variables however may be
|
||||
inconsistent relative to one another, and not the same as when the
|
||||
processors exec() function eventually runs - since they too may be in
|
||||
the process of being updated.
|
||||
|
||||
The purpose of the value() function is to provide a single
|
||||
easy way of picking up changed incoming values without
|
||||
having to test for changed values in the exec() function.
|
||||
It shouldn't be used as an alternate exec() function.
|
||||
|
||||
Note that the create() and destroy() calls for all processors
|
||||
in the entire graph occur in a single thread and therefore do
|
||||
not need to take multi-thread precautions - at least relative
|
||||
to other _caw_ based execution.
|
||||
|
||||
Records
|
||||
-------
|
||||
|
||||
The primary reason to use a 'record' data type is to allow multiple
|
||||
data values to be transmitted during a single cycle and be received as
|
||||
a single incoming value. In effect a record allows a structured table
|
||||
of values to be transmitted during a single execution cycle. For
|
||||
example let's say that the output of a processor (G) is an irregular pulse
|
||||
whose rate might be faster than the audio frequency. This would
|
||||
require multiple pairs of value (delta-time,amplitude) to be generated
|
||||
during a given cycle.
|
||||
|
||||
Without the use of the record data type this would require that the generating
|
||||
processor have two output variables 'dtime' and 'amplitude' which
|
||||
would be updated multiple times during a single execution cycle. The
|
||||
receiving processor (R) would then need to respond to each of those
|
||||
changes by implementing a value() function and storing the incoming
|
||||
values in an internal array. The stored values could then be acted
|
||||
upon during the receiving processors exec() function.
|
||||
|
||||
If R didn't take this approach, and simply read
|
||||
the incoming variables at the beginning of it's own execution cycle,
|
||||
it would only see the value of the two output variables as they were left at the
|
||||
end of the G execution cycle. The previous values
|
||||
transmitted during the execution cycle would be lost.
|
||||
|
||||
By explicitely transmitting a record G makes clear that multiple
|
||||
values may be transmitted during a single execution cycle, while
|
||||
providing the convenience R of automatically storing those value.
|
||||
Furthermore the tranmission overhead is minimized by only transmitting
|
||||
a single aggregate value rather than multiple individual values.
|
||||
|
||||
Another example, would be MIDI values that contain some additional
|
||||
side information. The MIDI data type already has the feature that it
|
||||
can generate multiple messages per execution cycle. It's format
|
||||
however is fixed. There is no way to add addtional information, like
|
||||
score location, to each message. The fields of the record data type
|
||||
however can hold any of the other data types.
|
||||
|
||||
Another reason to use the record data type is to simplify
|
||||
the output and input interfaces to a processor and thereby
|
||||
decrease the number of connections between processors.
|
||||
They also make clear that a set of values is synchronized in time.
|
||||
For example a set of (x,y) coordinates placed in a record
|
||||
make it clear that the two values belong together in a way
|
||||
that two input variables may not.
|
||||
|
||||
Finally, records are also very efficient. Given that the field index is
|
||||
computed in advance setting and getting the field variable
|
||||
is very fast. As mentioned above transmitting a record avoids the overhead
|
||||
of notifying receiving processors of every new value. The
|
||||
receiving processor is only notified that the record
|
||||
as a whole has changed.
|
||||
|
||||
Records are also implented in such a way that appending
|
||||
a additition fields to an existing record is very fast.
|
||||
The new record effectively inherits the contents of the
|
||||
existing record by reference. No data is copied.
|
||||
For an example of this see the `vel_table` implementation.
|
||||
|
||||
Optional Variables
|
||||
-------------------
|
||||
|
||||
The current design does not allow for optional variables.
|
||||
At processor instantiation all defined variables must exist and have a
|
||||
valid value. In generate this is a good thing because it
|
||||
means that call to get_var() will always return a value.
|
||||
|
||||
midi_out has implemented 'in' and 'rin' as optional
|
||||
variables. Look there for an example of how to accomplish this.
|
||||
|
||||
|
||||
Presets:
|
||||
----------------
|
||||
|
||||
Preset description and application without the presence of 'poly' proc's is very straight forward:
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
example_1:
|
||||
{
|
||||
|
||||
network: {
|
||||
|
||||
procs: {
|
||||
lfo: { class: sine_tone, args:{ hz:3, dc:440, gain:110 }
|
||||
presets:
|
||||
{
|
||||
ps_a:{ hz:2, dc:220, gain:55 },
|
||||
ps_b:{ hz:4, dc:110, gain:220 },
|
||||
}
|
||||
}
|
||||
|
||||
sh: { class: sample_hold, in:{ in:lfo.out } }
|
||||
osc: { class: sine_tone, in:{ hz:sh.out }, args:{ ch_cnt:2 } },
|
||||
gain: { class: audio_gain, in:{ in:osc.out }, args:{ gain:0.3 } },
|
||||
aout: { class: audio_out, in:{ in:gain.out }, args:{ dev_label:"main"} }
|
||||
}
|
||||
|
||||
presets:
|
||||
{
|
||||
a: { gain:{ gain:0.2 } }, // One value sets both channels.
|
||||
b: { gain:{ gain:[0.1,0.3] } }, // Multi-channel preset.
|
||||
c: { osc:a880 } }, // Apply a class preset
|
||||
d: { osc:mono } }, // Apply a class preset with an ignored 'init' variable.
|
||||
f: { osc:a220, lfo:ps_a } }, // Apply a local preset and class preset
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Calling `network_apply_preset(preset_label)` with one of the network preset labels 'a'-'f' will
|
||||
work as expected in all of these cases.
|
||||
|
||||
Notes:
|
||||
|
||||
1. All preset values and proc/var's can be resolved at compile time.
|
||||
|
||||
2. Applying the network can be accomplished by resolving the network preset_label to a network_preset_t
|
||||
in network_t.presetA and calling flow::var_set() on each attached preset_value_t.
|
||||
|
||||
3. Proc preset labels, as used in presets 'c','d','e' in the example, are resolved by first looking
|
||||
for the label in the processor instance configation and then in the processor class description.
|
||||
|
||||
|
||||
---
|
||||
|
||||
The following example shows how the preset processor labels can use suffix notation to address a range of processors.
|
||||
|
||||
```
|
||||
example_2:
|
||||
{
|
||||
network: {
|
||||
procs: {
|
||||
osc: { class: sine_tone, args: { ch_cnt:6, hz:[110,220,440,880,1760, 3520] }},
|
||||
split: { class: audio_split, in:{ in:osc.out }, args: { select:[ 0,0, 1,1, 2,2 ] } },
|
||||
|
||||
// Create three gain controls: g:0,g:1,g:2 using the processor label numeric suffix syntax.
|
||||
g0: { class:audio_gain, in:{ in:split0.out0 }, args:{ gain:0.9} },
|
||||
g1: { class:audio_gain, in:{ in:split0.out1 }, args:{ gain:0.5} },
|
||||
g2: { class:audio_gain, in:{ in:split0.out2 }, args:{ gain:0.2} },
|
||||
|
||||
merge: { class: audio_merge, in:{ in_:g_.out } },
|
||||
out: { class: audio_out, in:{ in:merge.out }, args:{ dev_label:"main" }}
|
||||
}
|
||||
|
||||
presets: {
|
||||
a: { g_: { gain:0.1 } }, // Use suffix notation to apply a preset value to g0,g1,g2.
|
||||
b: { g0_2: { gain:0.2 } }, // Use suffix notation to apply a preset value to g0 and g1.
|
||||
c: { g2: { gain:0.3 } }, // Apply a preset value to g2 only.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
example_3: {
|
||||
|
||||
|
||||
|
||||
network: {
|
||||
|
||||
procs: {
|
||||
|
||||
// LFO gain parameters - one per poly voice
|
||||
g_list: { class: list, args: { in:0, list:[ 110f,220f,440f ]}},
|
||||
|
||||
// LFO DC offset parameters - one per poly voice
|
||||
dc_list: { class: list, args: { in:0, list:[ 220f,440f,880f ]}},
|
||||
|
||||
osc_poly: {
|
||||
class: poly,
|
||||
args: { count:3 }, // Create 3 instances of 'network'.
|
||||
|
||||
network: {
|
||||
procs: {
|
||||
lfo: { class: sine_tone, in:{ _.dc:_.dc_list.value_, _.gain:_.g_list.value_ } args: { ch_cnt:1, hz:3 }},
|
||||
sh: { class: sample_hold, in:{ in:lfo.out }},
|
||||
osc: { class: sine_tone, in:{ hz: sh.out }},
|
||||
},
|
||||
|
||||
presets:
|
||||
{
|
||||
a:{ lfo:{ gain:0.1 } },
|
||||
b:{ lfo:{ gain:0.2 } },
|
||||
c:{ lfo0_1: { gain:0.3 }, lfo2:{ gain:0.4 } },
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the instances of `osc_poly.osc_.out` to create one `audio_merge`
|
||||
// input for every output from the polyphonic network.
|
||||
merge: { class: audio_merge, in:{ in_:osc_poly.osc_.out}, args:{ gain:1, out_gain:0.5 }},
|
||||
aout: { class: audio_out, in:{ in:merge.out } args:{ dev_label:"main"} }
|
||||
}
|
||||
|
||||
presets: {
|
||||
a:{ osc_poly:a, merge:{ out_gain:0.3 } },
|
||||
b:{ osc_poly:b, merge:{ out_gain:0.2 } },
|
||||
c:{ osc_poly:c, merge:{ out_gain:0.1 } },
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1. If a poly preset processor label does not have a numeric suffix then it is applied to all instances.
|
||||
The alternative to this rule is to use an '_' suffix to imply 'all' processors of the given name.
|
||||
|
||||
2. A preset in an outer network may not directly address a processor in an inner network, however
|
||||
it may select a named preset in an inner network.
|
||||
|
||||
3. Rule 2 can be generalized to: Network presets may only address processors which it contains directly - not nested processors.
|
||||
In the example the outer most presets may therefore address the 'osc_poly' presets by label, but not
|
||||
the processors contained by 'osc_poly'.
|
||||
|
||||
---
|
||||
Presets with hetergenous poly networks
|
||||
```
|
||||
example_03:
|
||||
{
|
||||
network: {
|
||||
|
||||
procs: {
|
||||
|
||||
osc_poly: {
|
||||
class: poly,
|
||||
|
||||
// For het-poly networks the 'count' value given
|
||||
// in the top level proc is the default value for
|
||||
// the poly-count for following networks.
|
||||
// This value may be overriden in the network
|
||||
// definition itself - as it is in this example.
|
||||
args: { count:2, parallel_fl:true },
|
||||
|
||||
network: {
|
||||
net_a: {
|
||||
count: 4, // override higher level 'count'
|
||||
|
||||
procs: {
|
||||
osc: { class: sine_tone, args:{ hz: 100 }},
|
||||
},
|
||||
|
||||
presets:
|
||||
{
|
||||
a: { osc:{ hz:110 } },
|
||||
b: { osc:{ hz:120 } },
|
||||
}
|
||||
},
|
||||
|
||||
net_b: {
|
||||
count 3, // override higher level 'count'
|
||||
procs: {
|
||||
osc: { class: sine_tone, args:{ hz: 200 }},
|
||||
},
|
||||
|
||||
presets:
|
||||
{
|
||||
a: { osc:{hz:220} },
|
||||
b: { osc:{hz:230} }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
presets: {
|
||||
aa: { net_a:a, net_b:a },
|
||||
bb: { net_a:b, net_b:b },
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Iterate over the instances of `osc_poly.osc_.out` to create one `audio_merge`
|
||||
// input for every output from the polyphonic network.
|
||||
merge: { class: audio_merge, in:{ in_:osc_poly.osc_.out}, args:{ gain:1, out_gain:0.5 }
|
||||
presets: {
|
||||
a:{ gain:0.3 }
|
||||
b:{ gain:0.2 }
|
||||
}
|
||||
},
|
||||
aout: { class: audio_out, in:{ in:merge.out } args:{ dev_label:"main"} }
|
||||
|
||||
}
|
||||
|
||||
presets: {
|
||||
a:{ osc_poly1:aa, merge:a },
|
||||
b:{ osc_poly0_2:bb, merge:b },
|
||||
c:{ osc_poly:bb, merge:{ out_gain:0.1 } },
|
||||
d:{ osc_poly0:bb, merge:{ out_gain:0.05} }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
Dual Presets
|
||||
|
||||
---
|
||||
Dual Presets with poly networks.
|
||||
|
||||
---
|
||||
Dual Presets with heterogenous poly networks
|
||||
|
||||
---
|
||||
Presets with user defined processors
|
||||
|
||||
---
|
||||
Presets with user defined processors in poly networks.
|
||||
|
||||
---
|
||||
Presets with user defined processors containing poly networks.
|
||||
|
||||
|
||||
---
|
||||
Use 'interface' objects to intercept preset values so that they
|
||||
can be processed before being passed on to a the object that
|
||||
they represent.
|
||||
|
||||
'interface' object have the same interface as the object to which their 'class' argument
|
||||
refers but do nothing other than pass the values to their output ports.
|
||||
|
||||
```
|
||||
example_4:
|
||||
{
|
||||
|
||||
network: {
|
||||
|
||||
procs: {
|
||||
|
||||
lfoIF: { class: interface, args:{ class:sine_tone } },
|
||||
|
||||
// put a modifier here
|
||||
|
||||
lfo: { class: sine_tone, in:{ hz:lfoIF.hz, dc:lfoIF.dc, gain:lfoIF.gain } }
|
||||
sh: { class: sample_hold, in:{ in:lfo.out } }
|
||||
osc: { class: sine_tone, in:{ hz:sh.out }, args:{ ch_cnt:2 } },
|
||||
gain: { class: audio_gain, in:{ in:osc.out }, args:{ gain:0.3 } },
|
||||
aout: { class: audio_out, in:{ in:gain.out }, args:{ dev_label:"main"} }
|
||||
}
|
||||
|
||||
presets:
|
||||
{
|
||||
a: { lfoIF: { hz:1, dc:110, gain:55 } },
|
||||
b: { lfoIF: { hz:2, dc:220, gain:110 } },
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Preset Implementation:
|
||||
|
||||
All presets are resolved to (proc,var,value) tuples when the networks are created.
|
||||
A given named network preset is therefore a list these tuples.
|
||||
Applying the preset is then just a matter of calling
|
||||
var_set(proc,var,value) for each tuple in the list.
|
||||
This pre-processing approach mostly avoids having to do value parsing
|
||||
or variable resolution at runtime.
|
||||
|
||||
|
||||
Preset dictionaries have the following grammar:
|
||||
```
|
||||
<preset-dict> -> <preset-label> : { <proc-label>: (<proc-preset-label> | <value-dict>) }
|
||||
|
||||
<value-dict> -> { <var_label>:(<literal> | [ <literal>* ])
|
||||
``
|
||||
|
||||
A preset is a named ('<preset-label>') dictionary.
|
||||
The pairs contained by the dictionary reference processors (<proc-label>).
|
||||
The value of each pair is either a dictionary of variable
|
||||
values (<value-dict>) or a label (<proc-preset-label>).
|
||||
|
||||
The <value-dict> is a collection of literal values
|
||||
which can be directly converted to (proc,var,value) tuples.
|
||||
In the case where a value is a list of literals the
|
||||
individual values are used to address successive channels.
|
||||
As part of variable resolution new variable channels will
|
||||
be created if a preset references a channel that does
|
||||
not yet exist on the given variable. This guarantees
|
||||
that the variable channel will be valid should the preset
|
||||
be applied.
|
||||
|
||||
When the value of a preset pair is a <proc-preset-label>
|
||||
the label may refer to one of three possible
|
||||
source of preset variable values.
|
||||
1. Processor class preset. This is a named <value-dict> defined with the processor class description.
|
||||
2. Processor instance preset. This is a named <value-dict> defined with the processor instance.
|
||||
3. Poly processor network preset. If the <proc-label> associated with
|
||||
this <proc-preset-label> is a 'poly' processor then this label refers
|
||||
to a network preset defined within the 'poly' instances.
|
||||
|
||||
Note that both <proc-label> and <var-label> strings may use 'suffix'
|
||||
notation. In the case of variables this allows the preset to target
|
||||
specific 'mult' variable instances or ranges of instances.
|
||||
When the network is a poly network using suffix notation
|
||||
with a <proc-label> allows the target to particular
|
||||
instances or ranges of instances.
|
||||
|
||||
|
||||
Final Notes:
|
||||
|
||||
1. External network preset application requests that come from the control application
|
||||
(e.g. caw::main()), or requests that occur from any processor that is not in the top level,
|
||||
must be deferred until the end of the execution cycle when no processors are running.
|
||||
|
||||
Network preset application requests that occur from top level processors (processors running
|
||||
in the outmost network can be applied directly because by definition the top level processors
|
||||
run synchronously.
|
||||
|
||||
One way to handle this is to have a 'apply_preset' at the top level that takes
|
||||
a preset label as input and applies it directly.
|
||||
|
||||
2. Maybe network presets should only be 'label' based and and processor instance
|
||||
presets should only be 'value' based? Does this actually help anything?
|
||||
Given that the system isn't currently limited in this way maybe it doesn't matter.
|
||||
|
||||
3. To Do:
|
||||
|
||||
- Processor instance presets have not been implemented.
|
||||
- Preset application request deferrment has not been implemented.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user