805 lines
23 KiB
C++
805 lines
23 KiB
C++
#include "cwCommon.h"
|
|
#include "cwLog.h"
|
|
#include "cwCommonImpl.h"
|
|
#include "cwTest.h"
|
|
#include "cwMem.h"
|
|
#include "cwText.h"
|
|
#include "cwObject.h"
|
|
#include "cwVectOps.h"
|
|
|
|
#include "cwMtx.h"
|
|
|
|
#include "cwDspTypes.h" // srate_t, sample_t, coeff_t, ...
|
|
|
|
#include "cwTime.h"
|
|
#include "cwMidiDecls.h"
|
|
#include "cwMidi.h"
|
|
#include "cwMidiFile.h"
|
|
|
|
#include "cwFlowDecl.h"
|
|
#include "cwFlow.h"
|
|
#include "cwFlowValue.h"
|
|
#include "cwFlowTypes.h"
|
|
#include "cwFlowNet.h"
|
|
#include "cwFlowProc.h"
|
|
|
|
|
|
#include "cwDynRefTbl.h"
|
|
#include "cwScoreParse.h"
|
|
#include "cwSfScore.h"
|
|
#include "cwSfTrack.h"
|
|
#include "cwPerfMeas.h"
|
|
#include "cwScoreFollowerPerf.h"
|
|
#include "cwScoreFollower.h"
|
|
|
|
#include "cwPianoScore.h"
|
|
|
|
#include "cwFlowPerf.h"
|
|
|
|
namespace cw
|
|
{
|
|
|
|
namespace flow
|
|
{
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
//
|
|
// Score Player
|
|
//
|
|
|
|
|
|
namespace score_player
|
|
{
|
|
enum {
|
|
kScoreFNamePId,
|
|
kVelTblFnamePId,
|
|
kVelTblLabelPId,
|
|
kDoneFlPId,
|
|
kOutPId,
|
|
kLocPId
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
unsigned sample_idx;
|
|
unsigned loc;
|
|
unsigned meas;
|
|
unsigned d1; // inital d1 value before velocity mapping was applied
|
|
midi::ch_msg_t* midi; // index of associated msg in chMsgA
|
|
} msg_t;
|
|
|
|
typedef struct vel_tbl_str
|
|
{
|
|
unsigned* tblA;
|
|
unsigned tblN;
|
|
char* label;
|
|
struct vel_tbl_str* link;
|
|
} vel_tbl_t;
|
|
|
|
typedef struct
|
|
{
|
|
|
|
unsigned msgAllocN;
|
|
unsigned msgN;
|
|
msg_t* msgA; // msgA[ msgN ]
|
|
midi::ch_msg_t* chMsgA; // chMsgA[ msgN ]
|
|
|
|
vel_tbl_t* velTblL; // List of vel. tables.
|
|
vel_tbl_t* activeVelTbl; // Currently active vel. table or null if no vel. tbl is active.
|
|
|
|
|
|
unsigned sample_idx;
|
|
unsigned msg_idx;
|
|
} inst_t;
|
|
|
|
rc_t _load_score( proc_t* proc, inst_t* p, const char* score_fname )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
perf_score::handle_t perfScoreH;
|
|
const perf_score::event_t* score_evt = nullptr;
|
|
char* fname = nullptr;
|
|
|
|
if( score_fname == nullptr || textLength(score_fname)==0 )
|
|
{
|
|
rc = cwLogError(kInvalidArgRC,"The score filename is blank.");
|
|
goto errLabel;
|
|
}
|
|
|
|
if((fname = proc_expand_filename( proc, score_fname )) == nullptr )
|
|
{
|
|
rc = cwLogError(kOpFailRC,"The score filename (%s) is invalid.",score_fname);
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc= perf_score::create( perfScoreH, fname )) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Score create failed on '%s'.",fname);
|
|
goto errLabel;
|
|
}
|
|
|
|
if((p->msgAllocN = perf_score::event_count(perfScoreH)) == 0 )
|
|
{
|
|
rc = cwLogWarning("The score '%s' is empty.",fname);
|
|
goto errLabel;
|
|
}
|
|
|
|
if((score_evt = perf_score::base_event(perfScoreH)) == nullptr )
|
|
{
|
|
rc = cwLogError(kOpFailRC,"The score '%s' could not be accessed.",fname);
|
|
goto errLabel;
|
|
}
|
|
|
|
p->msgA = mem::allocZ<msg_t>(p->msgAllocN);
|
|
p->chMsgA = mem::allocZ<midi::ch_msg_t>(p->msgAllocN);
|
|
|
|
for(; p->msgN<p->msgAllocN && score_evt !=nullptr; score_evt=score_evt->link)
|
|
{
|
|
if( score_evt->status != 0 )
|
|
{
|
|
msg_t* m = p->msgA + p->msgN;
|
|
midi::ch_msg_t* mm = p->chMsgA + p->msgN;
|
|
|
|
m->sample_idx = (unsigned)(proc->ctx->sample_rate * score_evt->sec);
|
|
m->loc = score_evt->loc;
|
|
m->meas = score_evt->meas;
|
|
m->midi = mm;
|
|
|
|
time::fracSecondsToSpec( mm->timeStamp, score_evt->sec );
|
|
|
|
mm->devIdx = kInvalidIdx;
|
|
mm->portIdx= kInvalidIdx;
|
|
mm->uid = score_evt->uid;
|
|
mm->ch = score_evt->status & 0x0f;
|
|
mm->status = score_evt->status & 0xf0;
|
|
mm->d0 = score_evt->d0;
|
|
mm->d1 = score_evt->d1;
|
|
m->d1 = score_evt->d1;
|
|
|
|
p->msgN += 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
errLabel:
|
|
if( rc != kOkRC )
|
|
rc = cwLogError(rc,"Score load failed on '%s'.",cwStringNullGuard(fname));
|
|
|
|
perf_score::destroy(perfScoreH);
|
|
|
|
mem::release(fname);
|
|
|
|
return rc;
|
|
}
|
|
|
|
rc_t _load_vel_table_file( proc_t* proc, inst_t* p, const char* vel_tbl_fname )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
object_t* cfg = nullptr;
|
|
const object_t* tblL = nullptr;
|
|
unsigned tblN = 0;
|
|
char* fname = nullptr;
|
|
|
|
if( vel_tbl_fname == nullptr || textLength(vel_tbl_fname)==0 )
|
|
{
|
|
rc = cwLogError(kInvalidArgRC,"The velocity table filename is blank.");
|
|
goto errLabel;
|
|
}
|
|
|
|
if((fname = proc_expand_filename( proc, vel_tbl_fname )) == nullptr )
|
|
{
|
|
rc = cwLogError(kOpFailRC,"The velocity table filename (%s) is invalid.",vel_tbl_fname);
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = objectFromFile(fname,cfg)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Velocity table file parse failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = cfg->getv("tables",tblL)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Velocity table file has no 'tables' field.");
|
|
goto errLabel;
|
|
}
|
|
|
|
tblN = tblL->child_count();
|
|
|
|
for(unsigned i=0; i<tblN; ++i)
|
|
{
|
|
const object_t* tbl = tblL->child_ele(i);
|
|
const object_t* velListCfg = nullptr;
|
|
vel_tbl_t* vt = nullptr;
|
|
|
|
const char* label = nullptr;
|
|
|
|
if((rc = tbl->getv("table",velListCfg,
|
|
"name",label)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Velocity table at index %i failed.",i);
|
|
goto errLabel;
|
|
}
|
|
|
|
vt = mem::allocZ<vel_tbl_t>();
|
|
vt->link = p->velTblL;
|
|
p->velTblL = vt;
|
|
vt->tblN = velListCfg->child_count();
|
|
vt->label = mem::duplStr(label);
|
|
|
|
// if the table is empty
|
|
if( vt->tblN == 0 )
|
|
{
|
|
rc = cwLogError(rc,"The velocity table named '%s' appears to be blank.",cwStringNullGuard(label));
|
|
continue;
|
|
}
|
|
|
|
vt->tblA = mem::allocZ<unsigned>(vt->tblN);
|
|
|
|
for(unsigned j=0; j<vt->tblN; ++j)
|
|
{
|
|
const object_t* intCfg;
|
|
if((intCfg = velListCfg->child_ele(j)) == nullptr )
|
|
{
|
|
rc = cwLogError(rc,"Access to the integer value at index %i failed on vel. table '%s'.",j,cwStringNullGuard(label));
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = intCfg->value(vt->tblA[j])) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Parse failed on integer value at index %i in vel. table '%s'.",j,cwStringNullGuard(label));
|
|
goto errLabel;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
errLabel:
|
|
if( rc != kOkRC )
|
|
rc = cwLogError(rc,"Score velocity table file load failed on '%s'.",cwStringNullGuard(vel_tbl_fname));
|
|
|
|
if( cfg != nullptr )
|
|
cfg->free();
|
|
|
|
mem::release(fname);
|
|
|
|
return rc;
|
|
}
|
|
|
|
rc_t _activate_vel_table( proc_t* proc, inst_t* p, const char* vel_tbl_label )
|
|
{
|
|
for(vel_tbl_t* vt = p->velTblL; vt!=nullptr; vt=vt->link)
|
|
if( textIsEqual(vt->label,vel_tbl_label))
|
|
{
|
|
p->activeVelTbl = vt;
|
|
return kOkRC;
|
|
}
|
|
|
|
cwLogWarning("The requested velocity table '%s' was not found on the score instance '%s:%i'.",vel_tbl_label,proc->label, proc->label_sfx_id);
|
|
|
|
return kOkRC;
|
|
}
|
|
|
|
rc_t _apply_vel_table( inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
if( p->activeVelTbl == nullptr )
|
|
{
|
|
cwLogWarning("A velocity table has not been selected.");
|
|
goto errLabel;
|
|
}
|
|
|
|
for(unsigned i=0; i<p->msgN; ++i)
|
|
{
|
|
midi::ch_msg_t* m = p->msgA[i].midi;
|
|
|
|
if( midi::isNoteOn(m->status,m->d1) )
|
|
{
|
|
if( p->msgA[i].d1 >= p->activeVelTbl->tblN )
|
|
{
|
|
rc = cwLogError(kInvalidArgRC,"The pre-mapped velocity value %i is outside of the range (%i) of the velocity table '%s'.",p->msgA[i].d1,p->activeVelTbl->tblN,cwStringNullGuard(p->activeVelTbl->label));
|
|
goto errLabel;
|
|
}
|
|
|
|
m->d1 = p->activeVelTbl->tblA[ p->msgA[i].d1 ];
|
|
}
|
|
}
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
rc_t _create( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
const char* score_fname = nullptr;
|
|
const char* vel_tbl_fname = nullptr;
|
|
const char* vel_tbl_label = nullptr;
|
|
|
|
if((rc = var_register_and_get(proc,kAnyChIdx,
|
|
kScoreFNamePId, "fname", kBaseSfxId, score_fname,
|
|
kVelTblFnamePId, "vel_tbl_fname", kBaseSfxId, vel_tbl_fname,
|
|
kVelTblLabelPId, "vel_tbl_label", kBaseSfxId, vel_tbl_label)) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = var_register(proc,kAnyChIdx,
|
|
kLocPId,"loc", kBaseSfxId,
|
|
kDoneFlPId,"done_fl", kBaseSfxId )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// load the score
|
|
if((rc = _load_score( proc, p, score_fname )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// load p->velTblL from the vel table file
|
|
if((rc = _load_vel_table_file( proc, p, vel_tbl_fname )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// activate the selected velocity table
|
|
if((rc = _activate_vel_table( proc, p, vel_tbl_label )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// apply the selected velocity table
|
|
if( p->activeVelTbl != nullptr )
|
|
{
|
|
if((rc = _apply_vel_table( p )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
}
|
|
|
|
// create one output MIDI buffer
|
|
rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 );
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
rc_t _destroy( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
for(vel_tbl_t* vt=p->velTblL; vt!=nullptr; vt=vt->link)
|
|
{
|
|
vel_tbl_t* vt0 = vt->link;
|
|
mem::release(vt->tblA);
|
|
mem::release(vt);
|
|
vt = vt0;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
rc_t _value( proc_t* proc, inst_t* p, variable_t* var )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
return rc;
|
|
}
|
|
|
|
rc_t _exec( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
mbuf_t* mbuf = nullptr;
|
|
bool done_fl = false;
|
|
|
|
p->sample_idx += proc->ctx->framesPerCycle;
|
|
|
|
// get the output variable
|
|
if((rc = var_get(proc,kOutPId,kAnyChIdx,mbuf)) != kOkRC )
|
|
rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",proc->label);
|
|
else
|
|
{
|
|
mbuf->msgA = nullptr;
|
|
mbuf->msgN = 0;
|
|
|
|
while( p->msg_idx < p->msgN && p->sample_idx >= p->msgA[p->msg_idx].sample_idx )
|
|
{
|
|
if( mbuf->msgA == nullptr )
|
|
{
|
|
// Note we only set a pointer to the first msg in p->chMsgA[]
|
|
// successive messages will be consecutively after it.
|
|
mbuf->msgA = p->msgA[p->msg_idx].midi;
|
|
}
|
|
|
|
mbuf->msgN += 1;
|
|
|
|
p->msg_idx += 1;
|
|
|
|
done_fl = p->msg_idx == p->msgN;
|
|
|
|
}
|
|
|
|
|
|
if( done_fl )
|
|
var_set(proc, kDoneFlPId, kAnyChIdx, true );
|
|
|
|
}
|
|
|
|
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>,
|
|
.value = std_value<inst_t>,
|
|
.exec = std_exec<inst_t>,
|
|
.report = std_report<inst_t>
|
|
};
|
|
|
|
} // score_player
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
//
|
|
// Midi To Msg
|
|
//
|
|
namespace midi_to_msg
|
|
{
|
|
enum {
|
|
kInPId,
|
|
kFieldsPId,
|
|
kBufCntPId,
|
|
kOutPId
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
recd_array_t* recd_array;
|
|
|
|
unsigned allocRecdN;
|
|
unsigned midiFieldIdx;
|
|
} inst_t;
|
|
|
|
rc_t _create_recd_array( inst_t* p, const char* fields_string )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
const char* prefix_string = "midi ";
|
|
unsigned sn = textLength(prefix_string) + textLength(fields_string);
|
|
char s[ sn+1 ];
|
|
strcpy(s,prefix_string);
|
|
strcat(s,fields_string);
|
|
s[sn] = 0;
|
|
|
|
recd_type_t* recd_type = nullptr;
|
|
|
|
if((rc = recd_type_create( recd_type, nullptr, s )) != kOkRC )
|
|
{
|
|
rc = cwLogError(kOpFailRC,"recd type failed on field strings:'%s'.",cwStringNullGuard(s));
|
|
goto errLabel;
|
|
}
|
|
|
|
|
|
// Allocate a recd_buf large enough to hold the max. number of messages arriving on a single call to exec().
|
|
if((rc = recd_array_create( p->recd_array, recd_type, p->allocRecdN )) != kOkRC )
|
|
{
|
|
rc = cwLogError(kOpFailRC,"recd_array create of size %i failed.",p->allocRecdN);
|
|
goto errLabel;
|
|
}
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
|
|
rc_t _create( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
const char* fields_string = nullptr;
|
|
|
|
if((rc = var_register( proc, kAnyChIdx, kInPId, "in", kBaseSfxId )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = var_register_and_get( proc, kAnyChIdx,
|
|
kBufCntPId, "buf_cnt", kBaseSfxId, p->allocRecdN,
|
|
kFieldsPId, "fields", kBaseSfxId, fields_string )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
|
|
if((rc = _create_recd_array(p,fields_string )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// create one output msg buffer
|
|
if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, p->recd_array->type, nullptr, 0 )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
if((p->midiFieldIdx = recd_type_field_index( p->recd_array->type, "midi")) != kOkRC )
|
|
{
|
|
rc = cwLogError(kInvalidArgRC,"The 'midi' record field was not found.");
|
|
goto errLabel;
|
|
}
|
|
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
rc_t _destroy( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
recd_array_destroy(p->recd_array);
|
|
|
|
return rc;
|
|
}
|
|
|
|
rc_t _value( proc_t* proc, inst_t* p, variable_t* var )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
return rc;
|
|
}
|
|
|
|
rc_t _exec( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
rbuf_t* out_rbuf = nullptr;
|
|
mbuf_t* in_mbuf = nullptr;
|
|
|
|
// get the output record buffer
|
|
if((rc = var_get(proc,kOutPId,kAnyChIdx,out_rbuf)) != kOkRC )
|
|
{
|
|
rc = cwLogError(kInvalidStateRC,"The output recd buf variable access failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// get the input MIDI buffer
|
|
if((rc = var_get(proc,kInPId,kAnyChIdx,in_mbuf)) != kOkRC )
|
|
{
|
|
rc = cwLogError(kInvalidStateRC,"The input MIDI buf variable access failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// copy a pointer to each input MIDI record to the 'midi' field of the assoc'd recd
|
|
for(unsigned i=0; i<in_mbuf->msgN; ++i)
|
|
{
|
|
//recd_t* r = p->recd_array->recdA + i;
|
|
//recd_set( p->recd_array->type, r, p->midiFieldIdx, in_mbuf->msgA + i );
|
|
}
|
|
|
|
// the rbuf is just a proxy to the first 'in_mbuf->msgN' records in the p->recd_array.
|
|
rbuf_setup(out_rbuf,p->recd_array->type,p->recd_array->recdA, in_mbuf->msgN );
|
|
|
|
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>,
|
|
.value = std_value<inst_t>,
|
|
.exec = std_exec<inst_t>,
|
|
.report = std_report<inst_t>
|
|
};
|
|
|
|
} // midi_to_msg
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
//
|
|
// Score Follower
|
|
//
|
|
namespace score_follower
|
|
{
|
|
|
|
enum
|
|
{
|
|
kInPId,
|
|
kDynTblFnamePId,
|
|
kFnamePId,
|
|
kScoreWndCntPId,
|
|
kMidiWndCntPId,
|
|
kPrintFlPId,
|
|
kBacktrackFlPId,
|
|
kLocPId,
|
|
kOutPId,
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
cw::dyn_ref_tbl::handle_t dynRefH;
|
|
cw::score_parse::handle_t scParseH;
|
|
cw::sfscore::handle_t scoreH;
|
|
cw::score_follower::handle_t sfH;
|
|
unsigned midi_field_idx;
|
|
unsigned loc_field_idx;
|
|
|
|
} inst_t;
|
|
|
|
|
|
rc_t _create( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
rbuf_t* in_rbuf = nullptr;
|
|
const char* score_fname = nullptr;
|
|
const char* dyn_tbl_fname = nullptr;
|
|
bool printParseWarningsFl = true;
|
|
cw::score_follower::args_t args;
|
|
|
|
if((rc = var_register_and_get(proc,kAnyChIdx,
|
|
kInPId, "in", kBaseSfxId, in_rbuf,
|
|
kFnamePId, "fname", kBaseSfxId, score_fname,
|
|
kDynTblFnamePId,"dyn_ref_fname", kBaseSfxId, dyn_tbl_fname,
|
|
kScoreWndCntPId,"score_wnd", kBaseSfxId, args.scoreWndLocN,
|
|
kMidiWndCntPId, "midi_wnd", kBaseSfxId, args.midiWndLocN,
|
|
kPrintFlPId, "print_fl", kBaseSfxId, args.trackPrintFl,
|
|
kBacktrackFlPId,"back_track_fl", kBaseSfxId, args.trackResultsBacktrackFl )) != kOkRC )
|
|
{
|
|
goto errLabel;
|
|
}
|
|
|
|
// get the input record 'midi' field index
|
|
if((p->midi_field_idx = recd_type_field_index( in_rbuf->type, "midi")) == kInvalidIdx )
|
|
{
|
|
rc = cwLogError(rc,"The input record type on '%s:%i' does not have a 'midi' field.",cwStringNullGuard(proc->label),proc->label_sfx_id);
|
|
goto errLabel;
|
|
}
|
|
|
|
// get the input record 'loc' field index
|
|
if((p->loc_field_idx = recd_type_field_index( in_rbuf->type, "loc")) == kInvalidIdx )
|
|
{
|
|
rc = cwLogError(rc,"The input record type on '%s:%i' does not have a 'loc' field.",cwStringNullGuard(proc->label),proc->label_sfx_id);
|
|
goto errLabel;
|
|
}
|
|
|
|
// parse the dynamics reference array
|
|
if((rc = dyn_ref_tbl::create(p->dynRefH,dyn_tbl_fname)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"The reference dynamics array parse failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// parse the score
|
|
if((rc = create( p->scParseH, score_fname, proc->ctx->sample_rate, p->dynRefH, printParseWarningsFl )) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Score parse failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// create the SF score
|
|
if((rc = create( p->scoreH, p->scParseH, printParseWarningsFl)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"SF Score create failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
args.enableFl = true;
|
|
args.scoreH = p->scoreH;
|
|
|
|
// create the score follower
|
|
if((rc = create( p->sfH, args )) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Score follower create failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
rc_t _destroy( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
destroy(p->sfH);
|
|
destroy(p->scoreH);
|
|
destroy(p->scParseH);
|
|
destroy(p->dynRefH);
|
|
|
|
return rc;
|
|
}
|
|
|
|
rc_t _value( proc_t* proc, inst_t* p, variable_t* var )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
return rc;
|
|
}
|
|
|
|
rc_t _exec( proc_t* proc, inst_t* p )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
|
|
unsigned sample_idx = proc->ctx->cycleIndex * proc->ctx->framesPerCycle;
|
|
double sec = ((double)sample_idx) / proc->ctx->sample_rate;
|
|
rbuf_t* rbuf = nullptr;
|
|
unsigned result_recd_idx = kInvalidIdx;
|
|
|
|
if((rc = var_get(proc,kInPId,kAnyChIdx,rbuf)) == kOkRC)
|
|
goto errLabel;
|
|
|
|
// for each incoming record
|
|
for(unsigned i=0; i<rbuf->recdN; ++i)
|
|
{
|
|
bool match_fl= false;
|
|
midi::ch_msg_t* m = nullptr;
|
|
|
|
if((rc = recd_get( rbuf->type, rbuf->recdA+i, p->midi_field_idx, m)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"The 'midi' field read failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = exec( p->sfH, sec, sample_idx, m->uid, m->status, m->d0,m->d1, match_fl )) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"Score follower exec failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
if( match_fl )
|
|
result_recd_idx = i;
|
|
|
|
}
|
|
|
|
if( result_recd_idx != kInvalidIdx )
|
|
{
|
|
unsigned resultIdxN = 0;
|
|
const unsigned* resultIdxA = current_result_index_array(p->sfH, resultIdxN );
|
|
const sftrack::result_t* resultA = cw::score_follower::track_result(p->sfH);
|
|
|
|
for(unsigned i=0; i<resultIdxN; ++i)
|
|
{
|
|
const sftrack::result_t* r = resultA + resultIdxA[i];
|
|
const sfscore::event_t* e = event(p->scoreH, r->scEvtIdx );
|
|
|
|
// store the performance data in the score
|
|
set_perf( p->scoreH, r->scEvtIdx, r->sec, r->pitch, r->vel, r->cost );
|
|
|
|
if( i+1 == resultIdxN )
|
|
{
|
|
//recd_set( rbuf->type, rbuf->recdA + result_recd_idx, p->loc_field_idx, e->oLocId );
|
|
var_set( proc, kLocPId, kAnyChIdx, e->oLocId );
|
|
}
|
|
|
|
}
|
|
|
|
var_set( proc, kOutPId, kAnyChIdx, rbuf );
|
|
}
|
|
|
|
|
|
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>,
|
|
.value = std_value<inst_t>,
|
|
.exec = std_exec<inst_t>,
|
|
.report = std_report<inst_t>
|
|
};
|
|
|
|
} // score_follower
|
|
|
|
|
|
|
|
} // flow
|
|
} //cw
|