Merge branch 'master' of https://gitea.currawongproject.org/cml/libcw
This commit is contained in:
commit
7b3a79bffc
@ -62,6 +62,7 @@ namespace cw
|
||||
{ "poly_voice_ctl", &poly_voice_ctl::members },
|
||||
{ "midi_voice", &midi_voice::members },
|
||||
{ "piano_voice", &piano_voice::members },
|
||||
{ "voice_detector", &voice_detector::members },
|
||||
{ "sample_hold", &sample_hold::members },
|
||||
{ "number", &number::members },
|
||||
{ "reg", ®::members },
|
||||
@ -82,6 +83,7 @@ namespace cw
|
||||
{ "preset_select", &preset_select::members },
|
||||
{ "gutim_ps", &gutim_ps::members },
|
||||
{ "score_follower", &score_follower::members },
|
||||
{ "score_follower_2",&score_follower_2::members },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
705
cwFlowPerf.cpp
705
cwFlowPerf.cpp
File diff suppressed because it is too large
Load Diff
11
cwFlowPerf.h
11
cwFlowPerf.h
@ -2,11 +2,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; }
|
||||
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; }
|
||||
namespace score_follower_2 { extern class_members_t members; }
|
||||
|
||||
}
|
||||
}
|
||||
|
362
cwFlowProc.cpp
362
cwFlowProc.cpp
@ -809,7 +809,8 @@ namespace cw
|
||||
kBufMsgCntPId,
|
||||
kInPId,
|
||||
kRInPId,
|
||||
kPrintFlPId
|
||||
kPrintFlPId,
|
||||
kEnableFlPId,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@ -832,13 +833,14 @@ namespace cw
|
||||
const char* port_label = nullptr;
|
||||
rbuf_t* rbuf = nullptr;
|
||||
bool printFl = false;
|
||||
|
||||
bool enableFl = false;
|
||||
|
||||
// Register variables and get their current value
|
||||
if((rc = var_register_and_get( proc, kAnyChIdx,
|
||||
kDevLabelPId, "dev_label", kBaseSfxId, dev_label,
|
||||
kPortLabelPId,"port_label", kBaseSfxId, port_label,
|
||||
kPrintFlPId, "print_fl", kBaseSfxId, printFl,
|
||||
kEnableFlPId, "enable_fl", kBaseSfxId, enableFl,
|
||||
kBufMsgCntPId,"buf_cnt", kBaseSfxId, p->msgN )) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
@ -907,9 +909,15 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
void _send_msg( inst_t* p, bool print_fl, const midi::ch_msg_t* m )
|
||||
void _send_msg( inst_t* p, bool print_fl, bool enable_fl, const midi::ch_msg_t* m )
|
||||
{
|
||||
p->ext_dev->u.m.sendTripleFunc( p->ext_dev, m->ch, m->status, m->d0, m->d1 );
|
||||
//if( midi::isNoteOn(m->status,m->d1) )
|
||||
// printf("mo:%i %s\n",m->d0,midi::midiToSciPitch(m->d0));
|
||||
|
||||
|
||||
if( enable_fl )
|
||||
p->ext_dev->u.m.sendTripleFunc( p->ext_dev, m->ch, m->status, m->d0, m->d1 );
|
||||
|
||||
if( print_fl )
|
||||
{
|
||||
cwLogPrint("%2i 0x%2x %3i %3i : %s %s\n",m->ch, m->status, m->d0, m->d1, cwStringNullGuard(p->ext_dev->devLabel),cwStringNullGuard(p->ext_dev->portLabel));
|
||||
@ -921,9 +929,12 @@ namespace cw
|
||||
rc_t rc = kOkRC;
|
||||
const rbuf_t* rbuf = nullptr;
|
||||
bool print_fl = false;
|
||||
bool enable_fl = true;
|
||||
const mbuf_t* src_mbuf = nullptr;
|
||||
|
||||
var_get(proc,kPrintFlPId,kAnyChIdx,print_fl);
|
||||
var_get(proc,kEnableFlPId,kAnyChIdx,enable_fl);
|
||||
|
||||
|
||||
if( p->rin_exists_fl )
|
||||
{
|
||||
@ -937,7 +948,7 @@ namespace cw
|
||||
const midi::ch_msg_t* m = nullptr;
|
||||
|
||||
if((rc = recd_get(rbuf->type,r,p->midi_fld_idx,m)) == kOkRC )
|
||||
_send_msg(p,print_fl,m);
|
||||
_send_msg(p,print_fl,enable_fl,m);
|
||||
else
|
||||
{
|
||||
rc = cwLogError(rc,"Record 'midi' field read failed.");
|
||||
@ -957,7 +968,7 @@ namespace cw
|
||||
{
|
||||
for(unsigned i=0; i<src_mbuf->msgN; ++i)
|
||||
{
|
||||
_send_msg(p,print_fl,src_mbuf->msgA + i);
|
||||
_send_msg(p,print_fl,enable_fl,src_mbuf->msgA + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4464,6 +4475,7 @@ namespace cw
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned baseGateFlPId;
|
||||
unsigned baseDoneFlPId;
|
||||
|
||||
midi_t midiA[ midi::kMidiNoteCnt ];
|
||||
@ -4496,7 +4508,12 @@ namespace cw
|
||||
p->voiceA[voice_idx].pitch = midi::kInvalidMidiPitch;
|
||||
|
||||
if( p->ns_fl )
|
||||
_store_note_state( proc, p, 0, 0, 0, 0, voice_idx );
|
||||
_store_note_state( proc, p, 0, 0, 0, 0, voice_idx );
|
||||
|
||||
// set the gate signal low
|
||||
//printf("pvc:%i off\n",voice_idx);
|
||||
var_set(proc,p->baseGateFlPId + voice_idx,kAnyChIdx,false);
|
||||
|
||||
}
|
||||
|
||||
void _reset_all_voices( proc_t* proc, inst_t* p )
|
||||
@ -4523,7 +4540,8 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->baseDoneFlPId = kBaseOutPId + p->voiceN;
|
||||
p->baseGateFlPId = kBaseOutPId + p->voiceN;
|
||||
p->baseDoneFlPId = p->baseGateFlPId + p->voiceN;
|
||||
p->voiceMsgN = kVoiceMsgN;
|
||||
p->voiceA = mem::allocZ<voice_t>(p->voiceN);
|
||||
|
||||
@ -4539,8 +4557,10 @@ namespace cw
|
||||
if((rc = var_register_and_set( proc, "out", i, kBaseOutPId + i, kAnyChIdx, nullptr, 0 )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// create one 'done_fl' variable per voice
|
||||
if((rc = var_register_and_set( proc, kAnyChIdx, p->baseDoneFlPId + i, "done_fl", i, false )) != kOkRC )
|
||||
// create one 'done_fl' and 'gate_fl' variable per voice
|
||||
if((rc = var_register_and_set( proc, kAnyChIdx,
|
||||
p->baseDoneFlPId + i, "done_fl", i, false,
|
||||
p->baseGateFlPId + i, "gate_fl", i, false )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
p->voiceA[i].msgA = mem::allocZ<midi::ch_msg_t>(p->voiceMsgN);
|
||||
@ -4733,6 +4753,10 @@ namespace cw
|
||||
|
||||
if( p->ns_fl )
|
||||
_store_note_state( proc, p, m->uid, midi::kNoteOnMdId, m->d0, m->d1, voice_idx );
|
||||
|
||||
// set the gate signal high
|
||||
//printf("pvc:%i on\n",voice_idx);
|
||||
var_set(proc,p->baseGateFlPId + voice_idx,kAnyChIdx,true);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -5392,10 +5416,12 @@ namespace cw
|
||||
p->gain_coeff = p->kReleaseGain;
|
||||
}
|
||||
|
||||
void _on_note_off( inst_t* p )
|
||||
void _on_note_off( proc_t* proc, inst_t* p )
|
||||
{
|
||||
p->noff_fl = true;
|
||||
|
||||
//printf("nof:%i %i\n",proc->label_sfx_id,p->pitch);
|
||||
|
||||
if( !p->sustain_fl )
|
||||
_begin_note_release(p);
|
||||
}
|
||||
@ -5473,9 +5499,9 @@ namespace cw
|
||||
|
||||
rc_t _exec( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
abuf_t* abuf = nullptr;
|
||||
mbuf_t* mbuf = nullptr;
|
||||
rc_t rc = kOkRC;
|
||||
abuf_t* abuf = nullptr;
|
||||
mbuf_t* mbuf = nullptr;
|
||||
unsigned actualFrmN = 0;
|
||||
//sample_t rms = 0;
|
||||
|
||||
@ -5508,13 +5534,13 @@ namespace cw
|
||||
}
|
||||
else
|
||||
{
|
||||
_on_note_off(p);
|
||||
_on_note_off(proc,p);
|
||||
_store_note_state(proc, p, m->uid, midi::kNoteOffMdId, m->d0, 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case midi::kNoteOffMdId:
|
||||
_on_note_off(p);
|
||||
_on_note_off(proc,p);
|
||||
_store_note_state(proc, p, m->uid, midi::kNoteOnMdId, m->d0, 0 );
|
||||
break;
|
||||
|
||||
@ -5569,6 +5595,155 @@ namespace cw
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// voice_detector
|
||||
//
|
||||
namespace voice_detector
|
||||
{
|
||||
enum {
|
||||
kInPId,
|
||||
kEnableFlPId,
|
||||
kRlsThreshPId,
|
||||
kDoneFlPId
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
kRmsBufN = 30
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool enable_fl;
|
||||
bool above_fl;
|
||||
bool done_fl;
|
||||
bool rls_thresh;
|
||||
unsigned delta_cnt;
|
||||
sample_t rms_buf[ kRmsBufN ];
|
||||
unsigned rms_buf_idx;
|
||||
unsigned rms_buf_cnt;
|
||||
|
||||
} inst_t;
|
||||
|
||||
sample_t _calc_rms( inst_t* p, abuf_t* abuf )
|
||||
{
|
||||
p->rms_buf[ p->rms_buf_idx ] = 0;
|
||||
|
||||
// store the max rms among all channels
|
||||
for( unsigned i=0; i<abuf->chN; ++i)
|
||||
{
|
||||
sample_t rms;
|
||||
if((rms = vop::sum_sq(abuf->buf + (i*abuf->frameN), abuf->frameN )) > p->rms_buf[ p->rms_buf_idx ] )
|
||||
p->rms_buf[ p->rms_buf_idx ] = rms;
|
||||
}
|
||||
|
||||
if( ++p->rms_buf_idx >= kRmsBufN )
|
||||
p->rms_buf_idx = 0;
|
||||
|
||||
if( p->rms_buf_cnt++ >= kRmsBufN )
|
||||
p->rms_buf_cnt = kRmsBufN;
|
||||
|
||||
return std::sqrt( vop::mean(p->rms_buf,p->rms_buf_cnt) );
|
||||
|
||||
}
|
||||
|
||||
rc_t _create( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
abuf_t* abuf = nullptr;
|
||||
|
||||
// register the input audio variable
|
||||
if((rc = var_register_and_get(proc,kAnyChIdx,
|
||||
kInPId, "in", kBaseSfxId, abuf,
|
||||
kEnableFlPId, "enable_fl", kBaseSfxId, p->enable_fl,
|
||||
kRlsThreshPId,"rls_thresh",kBaseSfxId, p->rls_thresh,
|
||||
kDoneFlPId, "done_fl", kBaseSfxId, p->done_fl)) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _destroy( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _notify( proc_t* proc, inst_t* p, variable_t* var )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( var->vid )
|
||||
{
|
||||
case kEnableFlPId:
|
||||
var_get(var,p->enable_fl);
|
||||
if( p->enable_fl )
|
||||
{
|
||||
var_set(proc,kDoneFlPId,kAnyChIdx,false);
|
||||
p->above_fl = false;
|
||||
}
|
||||
|
||||
printf("vd-ena:%i %i\n",proc->label_sfx_id,p->enable_fl);
|
||||
break;
|
||||
|
||||
case kRlsThreshPId:
|
||||
var_get(var,p->rls_thresh);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _exec( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
abuf_t* abuf = nullptr;
|
||||
sample_t rms;
|
||||
|
||||
if((rc = var_get(proc,kInPId,kAnyChIdx,abuf)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
rms = _calc_rms(p,abuf);
|
||||
|
||||
if( rms > p->rls_thresh )
|
||||
p->above_fl = true;
|
||||
|
||||
if( p->above_fl && rms < p->rls_thresh)
|
||||
p->delta_cnt += 1;
|
||||
else
|
||||
p->delta_cnt = 0;
|
||||
|
||||
if( p->enable_fl && p->delta_cnt >= 3 )
|
||||
{
|
||||
printf("vd:%i off\n",proc->label_sfx_id);
|
||||
p->done_fl = true;
|
||||
var_set(proc,kDoneFlPId,kAnyChIdx,true);
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _report( proc_t* proc, inst_t* p )
|
||||
{ return kOkRC; }
|
||||
|
||||
class_members_t members = {
|
||||
.create = std_create<inst_t>,
|
||||
.destroy = std_destroy<inst_t>,
|
||||
.notify = std_notify<inst_t>,
|
||||
.exec = std_exec<inst_t>,
|
||||
.report = std_report<inst_t>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
@ -8056,8 +8231,12 @@ namespace cw
|
||||
enum {
|
||||
kMidiFileNamePId,
|
||||
kCsvFileNamePId,
|
||||
kCsvFileName2PId,
|
||||
kStartPId,
|
||||
kStopPId,
|
||||
kDoneFlPId,
|
||||
kOutPId
|
||||
kOutPId,
|
||||
kROutPId
|
||||
};
|
||||
|
||||
typedef struct msg_str
|
||||
@ -8077,14 +8256,60 @@ namespace cw
|
||||
|
||||
char* midi_fname;
|
||||
char* csv_fname;
|
||||
char* csv_fname_2;
|
||||
|
||||
bool start_trig_fl; // the start btn was clicked
|
||||
bool stop_trig_fl; // the stop btn was clicked
|
||||
|
||||
|
||||
recd_array_t* recd_array; // output record array for 'out'.
|
||||
unsigned midi_fld_idx; // pre-computed record field indexes
|
||||
|
||||
} inst_t;
|
||||
|
||||
rc_t _alloc_recd_array( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const recd_type_t* base, recd_array_t*& recd_array_ref )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
variable_t* var = nullptr;
|
||||
|
||||
// find the record variable
|
||||
if((rc = var_find( proc, var_label, sfx_id, chIdx, var )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The record variable '%s:%i' could was not found.",cwStringNullGuard(var_label),sfx_id);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// verify that the variable has a record format
|
||||
if( !var_has_recd_format(var) )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The variable does not have a valid record format.");
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
recd_fmt_t* recd_fmt = var->varDesc->fmt.recd_fmt;
|
||||
|
||||
// create the recd_array
|
||||
if((rc = recd_array_create( recd_array_ref, recd_fmt->recd_type, base, recd_fmt->alloc_cnt )) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
if( rc != kOkRC )
|
||||
rc = cwLogError(rc,"Record array create failed on the variable '%s:%i ch:%i.",cwStringNullGuard(var_label),sfx_id,chIdx);
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
rc_t _create( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* midi_fname = nullptr;
|
||||
const char* csv_fname = nullptr;
|
||||
const char* csv_fname_2= nullptr;
|
||||
const midi::file::trackMsg_t** tmA = nullptr;
|
||||
unsigned msgAllocN = 0;
|
||||
bool done_fl = false;
|
||||
@ -8093,13 +8318,30 @@ namespace cw
|
||||
time::setZero(asecs);
|
||||
|
||||
if((rc = var_register_and_get(proc,kAnyChIdx,
|
||||
kMidiFileNamePId, "fname", kBaseSfxId, midi_fname,
|
||||
kCsvFileNamePId, "csv_fname", kBaseSfxId, csv_fname,
|
||||
kDoneFlPId, "done_fl", kBaseSfxId, done_fl)) != kOkRC )
|
||||
kMidiFileNamePId, "fname", kBaseSfxId, midi_fname,
|
||||
kCsvFileNamePId, "csv_fname", kBaseSfxId, csv_fname,
|
||||
kCsvFileName2PId, "alt_csv_fname", kBaseSfxId, csv_fname_2,
|
||||
kDoneFlPId, "done_fl", kBaseSfxId, done_fl)) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = var_register(proc,kAnyChIdx,
|
||||
kStartPId, "start", kBaseSfxId,
|
||||
kStopPId, "stop", kBaseSfxId )) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
if( csv_fname_2 != nullptr && textLength(csv_fname_2)>0 )
|
||||
if((p->csv_fname_2 = proc_expand_filename(proc,csv_fname_2)) == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The MIDI CSV 2 filename could not be formed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
if( csv_fname != nullptr && textLength(csv_fname)>0 )
|
||||
if((p->csv_fname = proc_expand_filename(proc,csv_fname)) == nullptr )
|
||||
{
|
||||
@ -8128,9 +8370,15 @@ namespace cw
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = cwLogError(kOpenFailRC,"No MIDI or CSV filename was given.");
|
||||
}
|
||||
if( p->csv_fname_2 != nullptr && textLength(p->csv_fname_2)>0 )
|
||||
{
|
||||
if((rc = midi::file::open_csv_2(p->mfH,p->csv_fname_2)) != kOkRC )
|
||||
goto errLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = cwLogError(kOpenFailRC,"No MIDI or CSV filename was given.");
|
||||
}
|
||||
}
|
||||
|
||||
// create one output MIDI buffer
|
||||
@ -8148,12 +8396,14 @@ namespace cw
|
||||
{
|
||||
const midi::file::trackMsg_t* tm = tmA[i];
|
||||
msg_t* m = p->msgA + p->msg_idx;
|
||||
double secs;
|
||||
|
||||
m->m = p->chMsgA + p->msg_idx;
|
||||
|
||||
time::microsecondsToSpec( m->m->timeStamp, tmA[i]->amicro );
|
||||
|
||||
m->sample_idx = (unsigned)(proc->ctx->sample_rate * time::specToSeconds(m->m->timeStamp));
|
||||
|
||||
m->sample_idx = (unsigned)(proc->ctx->sample_rate * (secs = time::specToSeconds(m->m->timeStamp)));
|
||||
|
||||
|
||||
m->m->devIdx = 0;
|
||||
m->m->portIdx = 0;
|
||||
@ -8174,6 +8424,18 @@ namespace cw
|
||||
|
||||
p->msgN = p->msg_idx;
|
||||
p->msg_idx = 0;
|
||||
|
||||
// allocate the output recd array
|
||||
if((rc = _alloc_recd_array( proc, "r_out", kBaseSfxId, kAnyChIdx, nullptr, p->recd_array )) != kOkRC )
|
||||
{
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// create one output record buffer
|
||||
rc = var_register_and_set( proc, "r_out", kBaseSfxId, kROutPId, kAnyChIdx, p->recd_array->type, nullptr, 0 );
|
||||
|
||||
p->midi_fld_idx = recd_type_field_index( p->recd_array->type, "midi");
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
@ -8182,9 +8444,10 @@ namespace cw
|
||||
rc_t _destroy( proc_t* proc, inst_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
recd_array_destroy(p->recd_array);
|
||||
mem::release(p->midi_fname);
|
||||
mem::release(p->csv_fname);
|
||||
mem::release(p->csv_fname_2);
|
||||
close(p->mfH);
|
||||
|
||||
return rc;
|
||||
@ -8193,6 +8456,36 @@ namespace cw
|
||||
rc_t _notify( proc_t* proc, inst_t* p, variable_t* var )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( var->vid )
|
||||
{
|
||||
case kStartPId:
|
||||
p->start_trig_fl = true;
|
||||
break;
|
||||
|
||||
case kStopPId:
|
||||
p->stop_trig_fl = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t _set_output_record( inst_t* p, rbuf_t* rbuf, const midi::ch_msg_t* m )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// if the output record array is full
|
||||
if( rbuf->recdN >= p->recd_array->allocRecdN )
|
||||
{
|
||||
rc = cwLogError(kBufTooSmallRC,"The internal record buffer overflowed. (buf recd count:%i).",p->recd_array->allocRecdN);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
recd_set( rbuf->type, nullptr, p->recd_array->recdA + rbuf->recdN, p->midi_fld_idx, (midi::ch_msg_t*)m );
|
||||
rbuf->recdN += 1;
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -8201,6 +8494,7 @@ namespace cw
|
||||
rc_t rc = kOkRC;
|
||||
mbuf_t* mbuf = nullptr;
|
||||
bool done_fl = false;
|
||||
rbuf_t* rbuf = nullptr;
|
||||
|
||||
p->sample_idx += proc->ctx->framesPerCycle;
|
||||
|
||||
@ -8227,6 +8521,22 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
// get the output variable
|
||||
if((rc = var_get(proc,kROutPId,kAnyChIdx,rbuf)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kInvalidStateRC,"The midi-in '%s' does not have a valid output record buffer.",proc->label);
|
||||
}
|
||||
else
|
||||
{
|
||||
rbuf->recdA = p->recd_array->recdA;
|
||||
rbuf->recdN = 0;
|
||||
|
||||
for(unsigned i=0; i<mbuf->msgN; ++i)
|
||||
_set_output_record(p,rbuf, mbuf->msgA + i);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if( done_fl )
|
||||
var_set(proc, kDoneFlPId, kAnyChIdx, true );
|
||||
|
||||
|
@ -65,6 +65,7 @@ namespace cw
|
||||
namespace xfade_ctl { extern class_members_t members; }
|
||||
namespace midi_voice { extern class_members_t members; }
|
||||
namespace piano_voice { extern class_members_t members; }
|
||||
namespace voice_detector { extern class_members_t members; }
|
||||
namespace poly_voice_ctl { extern class_members_t members; }
|
||||
namespace sample_hold { extern class_members_t members; }
|
||||
namespace number { extern class_members_t members; }
|
||||
|
105
cwMidiFile.cpp
105
cwMidiFile.cpp
@ -1497,6 +1497,110 @@ errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi::file::open_csv_2( handle_t& hRef, const char* midi_csv_fname )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
file_t* p = nullptr;
|
||||
csv::handle_t csvH;
|
||||
const char* titleA[] = { "UID","trk","dtick","atick","amicro","type","ch","D0","D1" };
|
||||
unsigned titleN = sizeof(titleA)/sizeof(titleA[0]);
|
||||
unsigned line_idx = 0;
|
||||
unsigned lineN = 0;
|
||||
unsigned trkN = 1;
|
||||
unsigned trkIdx = 0;
|
||||
unsigned TpQN = 1260;
|
||||
unsigned BpM = 120;
|
||||
|
||||
if((rc = _create(hRef)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
if((p = _handleToPtr(hRef)) == nullptr )
|
||||
goto errLabel;
|
||||
|
||||
_init( p, trkN, TpQN );
|
||||
|
||||
|
||||
if((rc = csv::create(csvH,midi_csv_fname,titleA,titleN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI CSV file open failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = line_count(csvH,lineN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"MIDI CSV line count access failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
for(; (rc = next_line(csvH)) == kOkRC; ++line_idx )
|
||||
{
|
||||
unsigned uid;
|
||||
unsigned amicro;
|
||||
unsigned dtick;
|
||||
unsigned atick;
|
||||
const char* type = nullptr;
|
||||
unsigned ch;
|
||||
unsigned d0;
|
||||
unsigned d1;
|
||||
uint8_t status = 0;
|
||||
|
||||
if((rc = getv(csvH,"UID",uid,"dtick",dtick,"atick",atick,"amicro",amicro,"type",type,"ch",ch,"D0",d0,"D1",d1)) != kOkRC )
|
||||
{
|
||||
cwLogError(rc,"Error reading CSV line %i.",line_idx+1);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if(textIsEqual(type,"non"))
|
||||
status = midi::kNoteOnMdId;
|
||||
else
|
||||
if(textIsEqual(type,"nof"))
|
||||
status = midi::kNoteOffMdId;
|
||||
else
|
||||
if(textIsEqual(type,"ctl"))
|
||||
status = midi::kCtlMdId;
|
||||
else
|
||||
if(textIsEqual(type,"tempo"))
|
||||
{
|
||||
// note the tempo is taken from the 'ch' column
|
||||
if((rc = insertTrackTempoMsg(hRef, trkIdx, atick, ch )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"BPM insert failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Unknown message type:'%s'.",cwStringNullGuard(type));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
if( status != 0 )
|
||||
{
|
||||
assert( ch<=15 && d0 <= 127 && d1 <= 127 );
|
||||
|
||||
if((rc = insertTrackChMsg(hRef, trkIdx, atick, ch+status, d0, d1 )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Channel msg insert failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
destroy(csvH);
|
||||
|
||||
if( rc == kEofRC )
|
||||
rc = kOkRC;
|
||||
|
||||
if( rc != kOkRC )
|
||||
rc = cwLogError(rc,"MIDI csv file parse failed on '%s'.",cwStringNullGuard(midi_csv_fname));
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::midi::file::create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN )
|
||||
{
|
||||
@ -1791,6 +1895,7 @@ cw::rc_t cw::midi::file::insertTrackMsg( handle_t h, unsigned trkIdx, const tra
|
||||
// fill the track record
|
||||
m->uid = p->nextUid++;
|
||||
m->atick = msg->atick;
|
||||
m->amicro = msg->amicro;
|
||||
m->status = msg->status;
|
||||
m->metaId = msg->metaId;
|
||||
m->trkIdx = trkIdx;
|
||||
|
@ -121,6 +121,9 @@ namespace cw
|
||||
// tpQN = ticks per quarter note should be given on the first line. (Defaults to 1260).
|
||||
// bpm = beats per minute should be given on the first line. (Defaults to 60).
|
||||
rc_t open_csv( handle_t& hRef, const char* csv_fname );
|
||||
|
||||
// Open MIDI CSV as generated by 'Workshop' recordings.
|
||||
rc_t open_csv_2( handle_t& hRef, const char* csv_fname );
|
||||
|
||||
// Create an empty MIDI file object.
|
||||
rc_t create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN );
|
||||
|
@ -239,7 +239,7 @@ namespace cw
|
||||
|
||||
}
|
||||
|
||||
rc_t _trkr_on_new_note( trkr_t* trk, double sec, unsigned pitch, unsigned vel, bool rpt_fl, unsigned& matched_loc_id_ref )
|
||||
rc_t _trkr_on_new_note( trkr_t* trk, double sec, unsigned pitch, unsigned vel, bool rpt_fl, unsigned& matched_loc_id_ref, unsigned& score_vel_ref )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
double d_corr_sec = 0.0;
|
||||
@ -257,6 +257,7 @@ namespace cw
|
||||
unsigned match_ni = kInvalidIdx;
|
||||
double match_val = 0;
|
||||
|
||||
score_vel_ref = -1;
|
||||
matched_loc_id_ref = kInvalidId;
|
||||
|
||||
assert( trk->exp_loc_idx != kInvalidIdx && trk->exp_loc_idx < trk->sf->locN );
|
||||
@ -362,6 +363,7 @@ namespace cw
|
||||
trk->loc_match_cntA[ match_loc_idx ] += 1;
|
||||
trk->note_match_cntA[ match_ni ] += 1;
|
||||
|
||||
score_vel_ref = trk->sf->noteA[ match_ni ].vel;
|
||||
matched_loc_id_ref = match_loc_id;
|
||||
|
||||
// notice if we arrived at the end of the score tracking range
|
||||
@ -805,7 +807,7 @@ errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::score_follow_2::create( handle_t& hRef, const args_t& args )
|
||||
cw::rc_t cw::score_follow_2::create( handle_t& hRef, const args_t& args, perf_score::handle_t scoreH )
|
||||
{
|
||||
rc_t rc;
|
||||
if((rc = destroy(hRef)) != kOkRC )
|
||||
@ -813,10 +815,10 @@ cw::rc_t cw::score_follow_2::create( handle_t& hRef, const args_t& args )
|
||||
|
||||
sf_t* p = mem::allocZ<sf_t>();
|
||||
|
||||
if((rc = _get_loc_and_note_count( p, args.scoreH )) != kOkRC )
|
||||
if((rc = _get_loc_and_note_count( p, scoreH )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
if((rc = _alloc_fill_loc_and_note_arrays( p, args.scoreH )) != kOkRC )
|
||||
if((rc = _alloc_fill_loc_and_note_arrays( p, scoreH )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
if((rc = _alloc_and_fill_loc_note_arrays( p )) != kOkRC )
|
||||
@ -894,18 +896,19 @@ cw::rc_t cw::score_follow_2::reset( handle_t h, unsigned beg_loc_id, unsigned en
|
||||
|
||||
_trkr_reset(p->trk,beg_loc_id);
|
||||
|
||||
cwLogInfo("SF2 reset: %i %i",beg_loc_id,end_loc_id);
|
||||
|
||||
errLabel:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::score_follow_2::on_new_note( handle_t h, unsigned uid, double sec, uint8_t pitch, uint8_t vel, unsigned& loc_id )
|
||||
cw::rc_t cw::score_follow_2::on_new_note( handle_t h, unsigned uid, double sec, uint8_t pitch, uint8_t vel, unsigned& matched_loc_id_ref, unsigned& score_vel_ref )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
sf_t* p = _handleToPtr(h);
|
||||
unsigned matched_loc_id = kInvalidId;
|
||||
|
||||
_trkr_on_new_note(p->trk,sec,pitch,vel, p->args.rpt_fl, matched_loc_id);
|
||||
_trkr_on_new_note(p->trk,sec,pitch,vel, p->args.rpt_fl, matched_loc_id_ref, score_vel_ref);
|
||||
|
||||
if( p->resultN < p->resultAllocN )
|
||||
{
|
||||
@ -913,7 +916,7 @@ cw::rc_t cw::score_follow_2::on_new_note( handle_t h, unsigned uid, double sec,
|
||||
r->perf_uid = uid;
|
||||
r->perf_pitch = pitch;
|
||||
r->perf_vel = vel;
|
||||
r->match_loc_id = matched_loc_id;
|
||||
r->match_loc_id = matched_loc_id_ref;
|
||||
p->resultN += 1;
|
||||
}
|
||||
|
||||
|
@ -6,22 +6,20 @@ namespace cw
|
||||
typedef handle< struct sf_str > handle_t;
|
||||
|
||||
typedef struct args_str
|
||||
{
|
||||
perf_score::handle_t scoreH;
|
||||
|
||||
{
|
||||
double pre_affinity_sec; // 1.0 look back affinity duration
|
||||
double post_affinity_sec; // 3.0 look forward affinity duration
|
||||
double pre_wnd_sec; // 2.0 look back search window
|
||||
double post_wnd_sec; // 5.0 look forward search window
|
||||
|
||||
double decay_coeff; // 0.995affinity decay coeff
|
||||
double decay_coeff; // 0.995 affinity decay coeff
|
||||
|
||||
double d_sec_err_thresh_lo; // 0.4 reject if d_loc > d_loc_thresh_lod and d_time > d_time_thresh_lo
|
||||
int d_loc_thresh_lo; // 3
|
||||
int d_loc_thresh_lo; // 3
|
||||
|
||||
double d_sec_err_thresh_hi; // 1.5 reject if d_loc != 0 and d_time > d_time_thresh_hi
|
||||
int d_loc_thresh_hi; // 4 reject if d_loc > d_loc_thresh_hi
|
||||
int d_loc_stats_thresh; // 3 reject for time stats updates if d_loc > d_loc_stats_thresh
|
||||
int d_loc_thresh_hi; // 4 reject if d_loc > d_loc_thresh_hi
|
||||
int d_loc_stats_thresh; // 3 reject for time stats updates if d_loc > d_loc_stats_thresh
|
||||
|
||||
bool rpt_fl; // set to turn on debug reporting
|
||||
|
||||
@ -29,13 +27,13 @@ namespace cw
|
||||
|
||||
rc_t parse_args( const object_t* cfg, args_t& args );
|
||||
|
||||
rc_t create( handle_t& hRef, const args_t& args );
|
||||
rc_t create( handle_t& hRef, const args_t& args, perf_score::handle_t scoreH );
|
||||
|
||||
rc_t destroy( handle_t& hRef );
|
||||
|
||||
rc_t reset( handle_t h, unsigned beg_loc_id, unsigned end_loc_id );
|
||||
|
||||
rc_t on_new_note( handle_t h, unsigned uid, double sec, uint8_t pitch, uint8_t vel, unsigned& loc_id );
|
||||
rc_t on_new_note( handle_t h, unsigned uid, double sec, uint8_t pitch, uint8_t vel, unsigned& loc_id_ref, unsigned& score_vel_ref );
|
||||
|
||||
// Decay the affinity window and if necessary trigger a cycle of async background processing
|
||||
rc_t do_exec( handle_t h );
|
||||
|
@ -138,10 +138,8 @@ namespace cw
|
||||
|
||||
//cwLogInfo("Following: (%i notes found) sample count:%i %s.",mf.evtN,mf.sampleN,midi_csv_fname);
|
||||
|
||||
sf_args.scoreH = scoreH;
|
||||
|
||||
// create the score-follower
|
||||
if((rc = score_follow_2::create(sfH,sf_args)) != kOkRC )
|
||||
if((rc = score_follow_2::create(sfH,sf_args,scoreH)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Score follower create failed.");
|
||||
goto errLabel;
|
||||
@ -159,11 +157,12 @@ namespace cw
|
||||
while( smp_idx <= mf.evtA[midi_evt_idx].sample_idx && mf.evtA[midi_evt_idx].sample_idx < smp_idx + smp_per_cycle )
|
||||
{
|
||||
const midi_evt_t* e = mf.evtA + midi_evt_idx++;
|
||||
unsigned loc_id;
|
||||
|
||||
unsigned loc_id = kInvalidId;
|
||||
unsigned score_vel = -1;
|
||||
|
||||
//printf("%f pitch:%i vel:%i\n",e->sec,e->pitch,e->vel);
|
||||
|
||||
if((rc = on_new_note( sfH, e->uid, e->sec, e->pitch, e->vel, loc_id )) != kOkRC )
|
||||
if((rc = on_new_note( sfH, e->uid, e->sec, e->pitch, e->vel, loc_id, score_vel )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"SF2 note processing failed on note UID:%i.",e->uid);
|
||||
goto errLabel;
|
||||
|
@ -44,6 +44,7 @@
|
||||
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."}
|
||||
enable_fl: { type:bool, value:true, doc:"Enable the output port."}
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,7 +676,7 @@
|
||||
in: { type:uint, flags:["notify","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." },
|
||||
value: { type:runtime, flags:["mult"], doc:"List 'mult' output per list value." },
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,7 +736,8 @@
|
||||
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."},
|
||||
gate_fl: { type:bool, flags:["mult"], doc:"Output: Per voice. Goes true with note-on, false when done_fl is set." },
|
||||
done_fl: { type:bool, value:false, flags:["mult"], doc:"Input: Voice available feedback triggers from voices. One per voice."},
|
||||
|
||||
}
|
||||
},
|
||||
@ -743,11 +745,11 @@
|
||||
piano_voice: {
|
||||
vars: {
|
||||
wtb_fname: { type:string, flags:["init"], doc:"Wave table bank cfg. file." },
|
||||
wtb_instr: { type:string, value:"piano", flags:["init"], doc:"Instrument label of the selected wave-table bank."},
|
||||
in: { type:midi, doc:"MIDI in" },
|
||||
out: { type:audio, doc:"Audio out" },
|
||||
done_fl: { type:bool, value:false, doc:"Triggers when voice is available."},
|
||||
gate_fl: { type:bool, value:false, doc:"True when voice is active, false when inactive." },
|
||||
wtb_instr: { type:string, value:"piano", flags:["init"], doc:"Instrument label of the selected wave-table bank."},
|
||||
in: { type:midi, doc:"MIDI in" },
|
||||
out: { type:audio, doc:"Audio out" },
|
||||
done_fl: { type:bool, value:false, doc:"Output: True when voice is available, false when active."},
|
||||
gate_fl: { type:bool, value:false, doc:"Output: 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."},
|
||||
|
||||
@ -758,6 +760,15 @@
|
||||
}
|
||||
},
|
||||
|
||||
voice_detector: {
|
||||
vars: {
|
||||
in: { type:audio, doc:"Audio in" },
|
||||
enable_fl: { type:bool, flags:[notify], value:false, doc:"Set when this voice detector is enabled." },
|
||||
rls_thresh: { type:coeff, flags:[notify], value:0.01, doc:"Voice off threshold." },
|
||||
done_fl: { type:bool, value:true, doc:"Output: Set when the voice is inactive, cleared when it is active."}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
print: {
|
||||
vars: {
|
||||
@ -797,10 +808,23 @@
|
||||
"bpm = beats per minute should be given on the first line. (Defaults to 60)." ]
|
||||
|
||||
vars: {
|
||||
fname: { type:string, flags:["init"], value:"", doc:"MIDI file name." },
|
||||
csv_fname: { type:string, flags:["init"], value:"", doc:"MIDI CSV fname. See: midi::file open_csv()." },
|
||||
done_fl: { type:bool, value:false, doc:"Emits true on done." },
|
||||
out: { type:midi, doc:"MIDI output."}
|
||||
fname: { type:string, flags:["init"], value:"", doc:"MIDI file name." },
|
||||
csv_fname: { type:string, flags:["init"], value:"", doc:"MIDI CSV fname. See: midi::file open_csv()." },
|
||||
alt_csv_fname: { type:string, flags:["init"], value:"", doc:"MIDI CSV fname. See: midi::file open_csv_2()." },
|
||||
start: { type:all, flags:["notify"], value:false, doc:"Start playback" },
|
||||
stop: { type:all, flags:["notify"], value:false, doc:"Stop playback" },
|
||||
done_fl: { type:bool, value:false, doc:"Emits true on done." },
|
||||
out: { type:midi, doc:"MIDI output."},
|
||||
r_out: { type:record, doc:"MIDI output as records.",
|
||||
fmt: {
|
||||
alloc_cnt:1024,
|
||||
fields: {
|
||||
midi: { type:m3, doc:"MIDI channel event message" },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -838,6 +862,7 @@
|
||||
b_meas: { type:uint, flags:["notify"], value:0, doc:"Score begin measure." },
|
||||
e_meas: { type:uint, flags:["notify"], value:0, doc:"Score end measure." },
|
||||
done_fl: { type:bool, value:false, doc:"Emits true on done." },
|
||||
loc_cnt: { type:uint, value:0, doc:"Output: Value of max 'loc'."},
|
||||
out: { type:record, doc:"Score event record.",
|
||||
fmt: {
|
||||
fields: {
|
||||
@ -851,7 +876,9 @@
|
||||
},
|
||||
|
||||
vel_table: {
|
||||
doc:[ "Remap MIDI velocity values."]
|
||||
doc:[ "Remap MIDI velocity values."
|
||||
"If the incoming record has a 'score_vel' field then this is taken as the source velocity to be mapped and the MIDI message velocity is ignored."
|
||||
]
|
||||
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'."},
|
||||
@ -879,6 +906,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
score_follower_2: {
|
||||
doc:[ "MIDI score follower: Sets the 'loc' field of the output record according to the score location." ]
|
||||
vars: {
|
||||
in: { type:record, fmt:{ required:["midi"]} doc:"Input record with 'midi' and 'loc' fields." },
|
||||
score_fname: { type:string, flags:["init"], value:"", doc:"Score file with location information." },
|
||||
b_loc: { type:uint, value:0, doc:"Score begin location." },
|
||||
e_loc: { type:uint, value:0, doc:"Score end location." },
|
||||
reset_trigger { type:all, flags:["notify"], value:false, doc:"Reset the score follower." },
|
||||
print_fl: { type:bool, flags:["init"], value:false, doc:"Set to print log of score follower state." },
|
||||
out:{ type:record, doc:"Pass-through of incoming MIDI with score location and score velocity."
|
||||
fmt: {
|
||||
alloc_cnt:1024, // internal recd_array size
|
||||
fields: {
|
||||
loc: { type:uint, value:-1, doc:"Score location id or if score track failed." } ,
|
||||
score_vel: { type:uint, value:-1, doc:"Score mapped velocity or -1 if 'loc' is -1." }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
preset_select: {
|
||||
doc:[ "Given a score location emit a preset label."],
|
||||
@ -920,12 +967,18 @@
|
||||
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, flags:["notify"], 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." },
|
||||
cfg: { type:cfg, flags:["init"], doc:"Initial preset configuration." },
|
||||
fname: { type:string, flags:["init"], value:"", doc:"Preset file name."},
|
||||
loc_cnt: { type:uint, flags:["init"], value:0, doc:"Count of uniq 'loc' id's in the score."},
|
||||
//in: { type:record, fmt:{ required:["loc"] }, doc:"Input record with 'loc' field." },
|
||||
in: { type:record, doc:"Input record with 'loc' field." },
|
||||
loc: { type:uint, value:0, doc:"Seek to this location." },
|
||||
reset: { type:bool, flags:["notify"], value:false, doc:"Reset to initial state."},
|
||||
pri_manual_sel: { type:uint, flags:["notify"], value:-1, doc:"Manually select the presets.", ui:{ type:list} },
|
||||
sec_manual_sel: { type:uint, flags:["notify"], value:-1, doc:"Manually select the presets.", ui:{ type:list} },
|
||||
per_note_fl: { type:bool, value:false, doc:"Change the preset on every note, otherwise change on new preset 'fragments'." },
|
||||
per_loc_fl: { type:bool, value:false, doc:"Change the preset on every score location, change according to 'per_note_fl'." },
|
||||
dry_chord_fl: { type:bool, value:false, doc:"Set to make 50% of all chord notes dry."},
|
||||
|
||||
pri_prob_fl: { type:bool, flags:["notify"], value: false, doc:"Select primary preset probabilstically." }
|
||||
pri_uniform_fl: { type:bool, flags:["notify","ui_disable"], value: false, doc:"Use a uniform probability distribution rather than an 'order' weighted distribution." },
|
||||
|
9
notes.md
9
notes.md
@ -941,6 +941,10 @@ resolvable without more information.
|
||||
|
||||
### TODO:
|
||||
|
||||
- Check for duplicate field labels. Particularly when using a base record.
|
||||
It's easy to declare a field named 'midi' and then inherit a record with a field named 'midi' - this is a crash bug.
|
||||
|
||||
|
||||
- DONE: Why doesn't the C7 on the downbeat of meas. 11 sound? (... it does but is quiet due to velocity table)
|
||||
- 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.
|
||||
@ -1415,6 +1419,11 @@ 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.
|
||||
|
||||
Note that record field values may not be changed in place because
|
||||
this would change the value for all other processors that
|
||||
receive the record. Incoming records must therefore be
|
||||
copied if they are changed.
|
||||
|
||||
Variable Change Notification
|
||||
----------------------------
|
||||
Processors are not directly notified when one of their connected variables changes.
|
||||
|
Loading…
Reference in New Issue
Block a user