#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 "cwPresetSel.h" #include "cwFlowPerf.h" namespace cw { namespace flow { //------------------------------------------------------------------------------------------------------------------ // // Score Player // namespace score_player { enum { kScoreFNamePId, kStoppingMsPId, kDoneFlPId, kOutPId, kStartPId, kStopPId, kBLocPId, kELocPId, kBMeasPId, kEMeasPId, }; enum { kDampPedalDownFl = 0x01, kSostPedalDownFl = 0x02, kSoftPedalDownFl = 0x04 }; typedef enum { kIdleStateId = 0, kPlayStateId, kStoppingStateId } state_t; enum { kAllNotesOffMsgIdx, kResetAllCtlsMsgIdx, kDampPedalDownMsgIdx, kSostPedalDownMsgIdx, kMidiMsgN }; typedef struct { unsigned flags; 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 { unsigned msgAllocN; // allocated size of msgA[] and chMsgA unsigned msgN; // actual count of records in msgA[] and chMsgA[] msg_t* msgA; // msgA[ msgN ] - score messages [ meas,loc, ch_msg, ... ] midi::ch_msg_t* chMsgA; // chMsgA[ msgN ] - ch_msg_t part of the score messages // pre-computed special midi msg's: all ctl's,all notes off, dampler down, sostenuto down msg_t midiMsgA[ kMidiMsgN ]; midi::ch_msg_t midiChMsgA[ kMidiMsgN ]; recd_array_t* recd_array; // output record array for 'out'. unsigned midi_fld_idx; // pre-computed record field indexes unsigned loc_fld_idx; // unsigned meas_fld_idx; // unsigned score_end_loc; // last score location unsigned score_end_meas; // last measure number unsigned sample_idx; // next score sample index - increments during playback unsigned msg_idx; // next score msg index - increments during playback unsigned end_msg_idx; // last msg to play before going into 'stopping' state or kInvalidIdx to play entire score unsigned cur_meas; // measure number of the measure currently being played unsigned bVId; // set to variable->vid when the begin loc or begin measure is changed unsigned eVId; // set to variable->vid when the end loc or end measure is changed bool start_trig_fl; // the start btn was clicked bool stop_trig_fl; // the stop btn was clicked unsigned note_cnt; // count of currently active notes (based on note-on messages) state_t state; // idle,play,stopping unsigned stopping_ms; // max time in milliseconds to wait for all notes to end before sending all-note-off unsigned stopping_sample_idx; // 0 if not 'stopping', otherwise the max sample index after which the player will enter 'idle' state. } 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; unsigned pedalStateFlags = 0; p->score_end_loc = 0; p->score_end_meas = 0; 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(p->msgAllocN); p->chMsgA = mem::allocZ(p->msgAllocN); for(; p->msgNmsgAllocN && 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; if( m->loc > p->score_end_loc ) p->score_end_loc = m->loc; if( m->meas > p->score_end_meas ) p->score_end_meas = m->meas; time::fracSecondsToSpec( mm->timeStamp, score_evt->sec ); mm->devIdx = kInvalidIdx; mm->portIdx= kInvalidIdx; mm->uid = score_evt->loc; 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; // track the initial d1 before vel. mapping is applied if( midi::isSustainPedal( mm->status, mm->d0 ) ) { bool down_fl = pedalStateFlags & kDampPedalDownFl; pedalStateFlags = cwEnaFlag(pedalStateFlags, kDampPedalDownFl, midi::isPedalDown( mm->d1 ) ); if( (pedalStateFlags & kDampPedalDownFl) == down_fl ) cwLogError(kInvalidStateRC,"Two damper pedal %s msg's without an intervening %s msg. meas:%i", down_fl ? "down" : "up", down_fl ? "up" : "down", m->meas ); } if( midi::isSostenutoPedal( mm->status, mm->d0 ) ) { bool down_fl = pedalStateFlags & kDampPedalDownFl; pedalStateFlags = cwEnaFlag(pedalStateFlags, kSostPedalDownFl, midi::isPedalDown( mm->d1 ) ); if( (pedalStateFlags & kDampPedalDownFl) == down_fl ) cwLogError(kInvalidStateRC,"Two sostenuto pedal %s msg's without an intervening %s msg. meas:%i", down_fl ? "down" : "up", down_fl ? "up" : "down", m->meas ); } m->flags = pedalStateFlags; 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 _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* score_fname = nullptr; double stopping_secs = 5.0; if((rc = var_register_and_get(proc,kAnyChIdx, kScoreFNamePId, "fname", kBaseSfxId, score_fname, kStoppingMsPId, "stopping_ms", kBaseSfxId, p->stopping_ms)) != kOkRC ) { goto errLabel; } // load the score if((rc = _load_score( proc, p, score_fname )) != kOkRC ) { goto errLabel; } if((rc = var_register(proc,kAnyChIdx, kStartPId, "start", kBaseSfxId, kStopPId, "stop", kBaseSfxId )) != kOkRC ) { goto errLabel; } if((rc = var_register_and_set(proc,kAnyChIdx, kDoneFlPId,"done_fl", kBaseSfxId, false, kBLocPId, "b_loc", kBaseSfxId, 0, kBMeasPId, "b_meas", kBaseSfxId, 0, kELocPId, "e_loc", kBaseSfxId, p->score_end_loc, kEMeasPId, "e_meas", kBaseSfxId, p->score_end_meas + 1)) != kOkRC ) { goto errLabel; } // allocate the output recd array if((rc = _alloc_recd_array( proc, "out", kBaseSfxId, kAnyChIdx, nullptr, p->recd_array )) != kOkRC ) { goto errLabel; } // create one output record buffer rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, p->recd_array->type, nullptr, 0 ); p->midi_fld_idx = recd_type_field_index( p->recd_array->type, "midi"); p->loc_fld_idx = recd_type_field_index( p->recd_array->type, "loc"); p->meas_fld_idx = recd_type_field_index( p->recd_array->type, "meas"); p->bVId = kInvalidId; p->eVId = kInvalidId; p->end_msg_idx = kInvalidIdx; p->midiChMsgA[kAllNotesOffMsgIdx] = { .timeStamp={ .tv_sec=0, .tv_nsec=0}, .devIdx=kInvalidIdx, .portIdx=kInvalidIdx, .uid=0, .ch=0, .status=midi::kCtlMdId, .d0=midi::kAllNotesOffMdId, .d1=0 }; p->midiChMsgA[kResetAllCtlsMsgIdx] = { .timeStamp={ .tv_sec=0, .tv_nsec=0}, .devIdx=kInvalidIdx, .portIdx=kInvalidIdx, .uid=0, .ch=0, .status=midi::kCtlMdId, .d0=midi::kResetAllCtlsMdId, .d1=0 }; p->midiChMsgA[kDampPedalDownMsgIdx] = { .timeStamp={ .tv_sec=0, .tv_nsec=0}, .devIdx=kInvalidIdx, .portIdx=kInvalidIdx, .uid=0, .ch=0, .status=midi::kCtlMdId, .d0=midi::kSustainCtlMdId, .d1=64 }; p->midiChMsgA[kSostPedalDownMsgIdx] = { .timeStamp={ .tv_sec=0, .tv_nsec=0}, .devIdx=kInvalidIdx, .portIdx=kInvalidIdx, .uid=0, .ch=0, .status=midi::kCtlMdId, .d0=midi::kSostenutoCtlMdId, .d1=64 }; p->midiMsgA[kAllNotesOffMsgIdx].midi = p->midiChMsgA + kAllNotesOffMsgIdx; p->midiMsgA[kResetAllCtlsMsgIdx].midi = p->midiChMsgA + kResetAllCtlsMsgIdx; p->midiMsgA[kDampPedalDownMsgIdx].midi = p->midiChMsgA + kDampPedalDownMsgIdx; p->midiMsgA[kSostPedalDownMsgIdx].midi = p->midiChMsgA + kSostPedalDownMsgIdx; p->state = kIdleStateId; errLabel: return rc; } rc_t _destroy( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; recd_array_destroy(p->recd_array); mem::release(p->msgA); mem::release(p->chMsgA); return rc; } rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) { rc_t rc = kOkRC; if( proc->ctx->isInRuntimeFl ) { switch( var->vid ) { case kStartPId: p->start_trig_fl = true; printf("Start Clicked\n"); break; case kStopPId: p->stop_trig_fl = true; printf("Stop Clicked\n"); break; case kBLocPId: case kBMeasPId: p->bVId = var->vid; break; case kELocPId: case kEMeasPId: p->eVId = var->vid; break; } } return rc; } rc_t _on_new_begin_loc( proc_t* proc, inst_t* p, unsigned vid ) { rc_t rc = kOkRC; unsigned i = 0; const char* label = ""; unsigned value = 0; unsigned bmeas; unsigned bloc; var_get( proc, kBMeasPId, kAnyChIdx, bmeas ); var_get( proc, kBLocPId, kAnyChIdx, bloc ); for(i=0; imsgN; ++i) if( (vid==kBLocPId && p->msgA[i].loc >= bloc) || (vid==kBMeasPId && p->msgA[i].meas>=bmeas) ) break; switch( vid ) { case kBLocPId: if( i < p->msgN ) var_set(proc,kBMeasPId,kAnyChIdx,p->msgA[i].meas+1); else var_set(proc,kBLocPId,kAnyChIdx,0); label = "location"; value = bloc; break; case kBMeasPId: if( i < p->msgN ) var_set(proc,kBLocPId,kAnyChIdx,p->msgA[i].loc); else var_set(proc,kBMeasPId,kAnyChIdx,1); label = "measure"; value = bmeas; break; } if( i >= p->msgN ) rc = cwLogError(kInvalidArgRC,"Invalid begin %s %i.",label,value); return rc; } rc_t _on_new_end_loc( proc_t* proc, inst_t* p, unsigned vid ) { rc_t rc = kOkRC; unsigned i = 0; unsigned emeas; unsigned eloc; var_get( proc, kEMeasPId, kAnyChIdx, emeas ); var_get( proc, kELocPId, kAnyChIdx, eloc ); p->end_msg_idx = kInvalidIdx; for(i=0; imsgN; ++i) if( (vid==kELocPId && p->msgA[i].loc >= eloc) || (vid==kEMeasPId && p->msgA[i].meas>=emeas) ) { p->end_msg_idx = i; break; } // NOTE: we allow the end-loc/end-meas to be set to the loc/meas after the last measure switch( vid ) { case kELocPId: if( i < p->msgN ) var_set(proc,kEMeasPId,kAnyChIdx,p->msgA[i].meas); else var_set(proc,kEMeasPId,kAnyChIdx,p->msgA[p->msgN-1].meas+1); break; case kEMeasPId: if( i < p->msgN ) var_set(proc,kELocPId,kAnyChIdx,p->msgA[i].loc); else var_set(proc,kELocPId,kAnyChIdx,p->msgA[p->msgN-1].loc+1); break; } return rc; } rc_t _do_begin_stopping( proc_t* proc, inst_t* p, unsigned stopping_ms ) { p->state = kStoppingStateId; p->stopping_sample_idx = p->sample_idx + int((proc->ctx->sample_rate * stopping_ms)/1000.0); cwLogInfo("Stopping ..."); return kOkRC; } rc_t _set_output_record( inst_t* p, rbuf_t* rbuf, const msg_t* m, recd_t* r ) { 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, r, p->midi_fld_idx, m->midi ); recd_set( rbuf->type, nullptr, r, p->loc_fld_idx, m->loc ); recd_set( rbuf->type, nullptr, r, p->meas_fld_idx, m->meas ); rbuf->recdN += 1; errLabel: return rc; } rc_t _do_stop_now( proc_t* proc, inst_t* p, rbuf_t* rbuf ) { rc_t rc = kOkRC; // copy the 'all-note-off','all-ctl-off' msg into output record array _set_output_record(p,rbuf,p->midiMsgA + kAllNotesOffMsgIdx, p->recd_array->recdA + rbuf->recdN); _set_output_record(p,rbuf,p->midiMsgA + kResetAllCtlsMsgIdx, p->recd_array->recdA + rbuf->recdN); p->state = kIdleStateId; // set the 'done' output flag var_set(proc, kDoneFlPId, kAnyChIdx, true ); cwLogInfo("Stopped."); return kOkRC; } rc_t _on_start_clicked( proc_t* proc, inst_t* p, rbuf_t* rbuf ) { rc_t rc = kOkRC; unsigned bloc = 1; unsigned i = 0; if( p->state != kIdleStateId ) if((rc = _do_stop_now(proc,p,rbuf)) != kOkRC ) goto errLabel; var_get( proc, kBLocPId, kAnyChIdx, bloc ); // Rewind the current position to the begin location for(i=0; imsgN; ++i) if( p->msgA[i].loc == bloc ) { p->sample_idx = p->msgA[i].sample_idx; p->msg_idx = i; p->cur_meas = p->msgA[i].meas; // if the damper pedal is down at the start location if( p->msgA[i].flags & kDampPedalDownFl ) _set_output_record(p,rbuf,p->midiMsgA + kDampPedalDownMsgIdx, p->recd_array->recdA + rbuf->recdN); // if the sostenuto pedal was put down at the start location if( p->msgA[i].flags & kSostPedalDownFl ) _set_output_record(p,rbuf,p->midiMsgA + kSostPedalDownMsgIdx, p->recd_array->recdA + rbuf->recdN); cwLogInfo("New current: msg_idx:%i meas:%i loc:%i",p->msg_idx, p->msgA[i].meas, p->msgA[i].loc ); break; } p->stopping_sample_idx = 0; p->note_cnt = 0; p->state = kPlayStateId; errLabel: return kOkRC; } rc_t _on_stop_clicked( proc_t* proc, inst_t* p, rbuf_t* rbuf ) { // begin stopping with the stopping time set to 0. return _do_stop_now(proc,p,rbuf); } rc_t _exec( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; rbuf_t* rbuf = nullptr; // get the output variable if((rc = var_get(proc,kOutPId,kAnyChIdx,rbuf)) != kOkRC ) { rc = cwLogError(kInvalidStateRC,"The score player '%s' does not have a validoutput buffer.",proc->label); goto errLabel; } rbuf->recdA = p->recd_array->recdA; rbuf->recdN = 0; // if the begin loc/meas was changed if( p->bVId != kInvalidId ) { _on_new_begin_loc(proc,p,p->bVId); p->bVId = kInvalidId; } // if the end loc/meas was changed if( p->eVId != kInvalidId ) { _on_new_end_loc(proc,p,p->eVId); p->eVId = kInvalidId; } // if the start button was clicked if( p->start_trig_fl ) { _on_start_clicked(proc,p,rbuf); p->start_trig_fl = false; } // if the stop button was clicked if( p->stop_trig_fl ) { _on_stop_clicked(proc,p,rbuf); p->stop_trig_fl = false; } // if in idle state then there is noting to d if( p->state == kIdleStateId ) goto errLabel; // advance sample_idx to the end sample associated with this cycle p->sample_idx += proc->ctx->framesPerCycle; // transmit all msgs, beginning with the msg at p->msg_idx, whose 'sample_idx' is <= p->sample_idx while( p->msg_idx < p->msgN && p->sample_idx >= p->msgA[p->msg_idx].sample_idx ) { recd_t* r = p->recd_array->recdA + rbuf->recdN; msg_t* m = p->msgA + p->msg_idx; // if the end-loc was encountered if( p->state==kPlayStateId && p->end_msg_idx != kInvalidIdx && p->msg_idx > p->end_msg_idx ) { _do_begin_stopping(proc,p,p->stopping_ms); } // if the base pointer of the output recd array has not yet been set if( rbuf->recdA == nullptr ) rbuf->recdA = r; bool note_on_fl = midi::isNoteOn(m->midi->status, m->midi->d1); // fill the output record with this msg but filter out note-on's when in stopping-state if( p->state == kPlayStateId || (p->state==kStoppingStateId && note_on_fl==false) ) { _set_output_record(p, rbuf, m, r ); if( note_on_fl ) p->note_cnt += 1; if( midi::isNoteOff(m->midi->status, m->midi->d1 ) && p->note_cnt > 0) p->note_cnt -= 1; } p->msg_idx += 1; // track the current measure if( m->meas > p->cur_meas ) { cwLogInfo("meas:%i",m->meas); p->cur_meas = m->meas; } } // end-while // if the end of the stopping state has been reached or if there are no more msg's in the score if( (p->state==kStoppingStateId && (p->note_cnt == 0 || p->sample_idx> p->stopping_sample_idx)) || p->msg_idx >= p->msgN ) { cwLogInfo("End-of-stopping: note_cnt:%i %s %s.",p->note_cnt,p->sample_idx> p->stopping_sample_idx ? "timed-out":"", p->msg_idx>=p->msgN ? "score-done":""); _do_stop_now(proc,p,rbuf); } errLabel: return rc; } /* rc_t _exec( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; rbuf_t* rbuf = nullptr; // if the begin loc/meas was changed if( p->bVId != kInvalidId ) { _on_new_begin_loc(proc,p,p->bVId); p->bVId = kInvalidId; } // if the end loc/meas was changed if( p->eVId != kInvalidId ) { _on_new_end_loc(proc,p,p->eVId); p->eVId = kInvalidId; } // if the start button was clicked if( p->start_trig_fl ) { _on_start_clicked(proc,p); p->start_trig_fl = false; } // if the stop button was clicked if( p->stop_trig_fl ) { _on_stop_clicked(proc,p); p->stop_trig_fl = false; } // if in idle state then there is noting to do if( p->state == kIdleStateId ) goto errLabel; // advance sample_idx to the end sample associated with this cycle p->sample_idx += proc->ctx->framesPerCycle; // get the output variable if((rc = var_get(proc,kOutPId,kAnyChIdx,rbuf)) != kOkRC ) rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",proc->label); else { if( p->state == kStoppedStateId ) { rc = _do_stop_now(proc,p,rbuf); } else { rbuf->recdA = nullptr; rbuf->recdN = 0; // transmit all msgs, beginning with the msg at p->msg_idx, whose 'sample_idx' is <= p->sample_idx while( p->msg_idx < p->msgN && p->sample_idx >= p->msgA[p->msg_idx].sample_idx ) { recd_t* r = p->recd_array->recdA + rbuf->recdN; msg_t* m = p->msgA + p->msg_idx; bool note_on_fl = false; // if the end-loc was encountered if( p->state==kPlayStateId && p->end_msg_idx != kInvalidIdx && p->msg_idx > p->end_msg_idx ) { _do_begin_stopping(proc,p,p->stopping_ms); } // if the base pointer of the output recd array has not yet been set if( rbuf->recdA == nullptr ) rbuf->recdA = r; // if we are in play-state and this is a note-on if( p->state==kPlayStateId && (note_on_fl = midi::isNoteOn(m->midi->status, m->midi->d1)) ) p->note_cnt += 1; // if this is a note-off if( midi::isNoteOff(m->midi->status, m->midi->d1 ) && p->note_cnt>0 ) p->note_cnt -= 1; // fill the output record with this msg but filter out note-on's when in stopping-state if( p->state == kPlayStateId || (p->state==kStoppingStateId && note_on_fl==false) ) { _set_output_record(p, rbuf, m, r ); } p->msg_idx += 1; // track the current measure if( m->meas > p->cur_meas ) { cwLogInfo("meas:%i",m->meas); p->cur_meas = m->meas; } } // end-while if( (p->state==kStoppingStateId && (p->note_cnt == 0 || p->sample_idx> p->stopping_sample_idx)) || p->msg_idx == p->msgN ) { p->state = kStoppedStateId; // this will be noticed in the next execution } } } errLabel: return rc; } */ rc_t _report( proc_t* proc, inst_t* p ) { return kOkRC; } class_members_t members = { .create = std_create, .destroy = std_destroy, .value = std_value, .exec = std_exec, .report = std_report }; } // score_player //------------------------------------------------------------------------------------------------------------------ // // vel_table // namespace vel_table { enum { kVelTblFnamePId, kVelTblLabelPId, kInPId, kOutPId, kRecdBufNPId }; typedef struct vel_tbl_str { unsigned* tblA; unsigned tblN; char* label; struct vel_tbl_str* link; } vel_tbl_t; typedef struct { vel_tbl_t* velTblL; vel_tbl_t* activeVelTbl; unsigned i_midi_fld_idx; unsigned o_midi_fld_idx; recd_array_t* recd_array; // output record array midi::ch_msg_t* midiA; // midiA[midiN] output MIDI msg array unsigned midiN; } inst_t; 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; ichild_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(); 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(vt->tblN); for(unsigned j=0; jtblN; ++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 _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* vel_tbl_fname = nullptr; const char* vel_tbl_label = nullptr; const rbuf_t* rbuf = nullptr; unsigned recdBufN = 128; if((rc = var_register_and_get(proc,kAnyChIdx, kVelTblFnamePId, "vel_tbl_fname", kBaseSfxId, vel_tbl_fname, kVelTblLabelPId, "vel_tbl_label", kBaseSfxId, vel_tbl_label, kRecdBufNPId, "recdbufN", kBaseSfxId, recdBufN, kInPId, "in", kBaseSfxId, rbuf)) != 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; } // create the output recd_array using the 'in' record type as the base type if((rc = _alloc_recd_array( proc, "out", kBaseSfxId, kAnyChIdx, rbuf->type, p->recd_array )) != kOkRC ) { goto errLabel; } // get the record field index for the incoming record if((p->i_midi_fld_idx = recd_type_field_index( rbuf->type, "midi")) == kInvalidIdx ) { rc = cwLogError(kInvalidArgRC,"The incoming record does not have a 'midi' field."); goto errLabel; } // get the record field index for the outgoing record if((p->o_midi_fld_idx = recd_type_field_index( p->recd_array->type, "midi")) == kInvalidIdx ) { rc = cwLogError(kInvalidArgRC,"The outgoing record does not have a 'midi' field."); goto errLabel; } // create one output record buffer rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, p->recd_array->type, nullptr, 0 ); p->midiN = p->recd_array->allocRecdN; p->midiA = mem::allocZ(p->midiN); errLabel: return rc; } rc_t _destroy( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; vel_tbl_t* vt=p->velTblL; while( vt!=nullptr ) { vel_tbl_t* vt0 = vt->link; mem::release(vt->label); mem::release(vt->tblA); mem::release(vt); vt = vt0; } mem::release(p->midiA); 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; const rbuf_t* i_rbuf = nullptr; rbuf_t* o_rbuf = nullptr; if((rc = var_get(proc,kInPId,kAnyChIdx,i_rbuf)) != kOkRC ) goto errLabel; if((rc = var_get(proc,kOutPId,kAnyChIdx,o_rbuf)) != kOkRC ) goto errLabel; // for each incoming record for(unsigned i=0; irecdN; ++i) { const recd_t* i_r = i_rbuf->recdA + i; const midi::ch_msg_t* i_m = nullptr; // verify that there is space in the output array if( i >= p->midiN || i >= p->recd_array->allocRecdN ) { rc = cwLogError(kBufTooSmallRC,"The velocity table MIDI processing buffers overflow (%i).",i); goto errLabel; } // Get pointers to the output records recd_t* o_r = p->recd_array->recdA + i; midi::ch_msg_t* o_m = p->midiA + i; // get a pointer to the incoming MIDI record if((rc = recd_get(i_rbuf->type,i_r,p->i_midi_fld_idx,i_m)) != kOkRC ) { rc = cwLogError(rc,"Record 'midi' field read failed."); goto errLabel; } // copy the incoming MIDI record to the output array *o_m = *i_m; // if this is a note on if( midi::isNoteOn(i_m->status,i_m->d1) ) { // and the velocity is valid if( i_m->d1 >= p->activeVelTbl->tblN ) { rc = cwLogError(kInvalidArgRC,"The pre-mapped velocity value %i is outside of the range (%i) of the velocity table '%s'.",i_m->d1,p->activeVelTbl->tblN,cwStringNullGuard(p->activeVelTbl->label)); goto errLabel; } // map the velocity through the active table o_m->d1 = p->activeVelTbl->tblA[ i_m->d1 ]; //printf("%i %i %s\n",i_m->d1,o_m->d1,p->activeVelTbl->label); } // update the MIDI pointer in the output record recd_set(o_rbuf->type,i_r,o_r,p->o_midi_fld_idx, o_m ); } //printf("RECDN:%i\n",i_rbuf->recdN); o_rbuf->recdA = p->recd_array->recdA; o_rbuf->recdN = i_rbuf->recdN; errLabel: if( rc != kOkRC ) rc = cwLogError(rc,"Vel table exec failed."); return rc; } rc_t _report( proc_t* proc, inst_t* p ) { return kOkRC; } class_members_t members = { .create = std_create, .destroy = std_destroy, .value = std_value, .exec = std_exec, .report = std_report }; } //------------------------------------------------------------------------------------------------------------------ // // preset_select // namespace preset_select { enum { kInPId, kInitCfgPId, kXfCntPId, kFNamePId, kLocPId, kOutIdxPId, kPresetLabelPId, }; typedef struct { const char* preset_proc_label; // proc containing preset label->value mapping unsigned xf_cnt; // count of transform processors preset_sel::handle_t psH; // location->preset map unsigned loc_fld_idx; // unsigned loc; // Last received location unsigned out_idx; // Current transform processor index (0:xf_cnt) unsigned presetN; // Count of preset labels. Same as preset_count(psH). unsigned preset_idx; // Index (0:presetN) of last selected preset. } inst_t; rc_t _create( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; const char* fname = nullptr; rbuf_t* rbuf; const object_t* cfg = nullptr; char* exp_fname = nullptr; if((rc = var_register_and_get(proc,kAnyChIdx, kInitCfgPId, "cfg", kBaseSfxId, cfg, kInPId, "in", kBaseSfxId, rbuf, kFNamePId, "fname", kBaseSfxId, fname, kLocPId, "loc", kBaseSfxId, p->loc)) != kOkRC ) { goto errLabel; } if((rc = var_register_and_set(proc,kAnyChIdx,kPresetLabelPId,"preset_label", kBaseSfxId, "")) != kOkRC ) { goto errLabel; } if((exp_fname = proc_expand_filename(proc,fname)) == nullptr ) { rc = cwLogError(kOpFailRC,"Preset filename expansion failed."); goto errLabel; } // create the cwPresetSel object if(cfg==nullptr || (rc = preset_sel::create(p->psH,cfg)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The preset select object could not be initialized."); goto errLabel; } // read in the loc->preset map file if((rc = preset_sel::read(p->psH,exp_fname)) != kOkRC ) { rc = cwLogError(rc,"The preset_sel data file '%s' could not be read.",cwStringNullGuard(exp_fname)); goto errLabel; } // The location is coming from a 'record', get the location field. if((p->loc_fld_idx = recd_type_field_index( rbuf->type, "loc")) == kInvalidIdx ) { rc = cwLogError(kInvalidArgRC,"The 'in' record does not have a 'loc' field."); goto errLabel; } p->presetN = preset_count(p->psH); errLabel: mem::release(exp_fname); return rc; } rc_t _destroy( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; preset_sel::destroy(p->psH); 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* in_rbuf = nullptr; unsigned loc = kInvalidIdx; if((rc = var_get(proc,kInPId,kAnyChIdx,in_rbuf)) != kOkRC) goto errLabel; // for each incoming record for(unsigned i=0; irecdN; ++i) { // get the 'loc' field if((rc = recd_get( in_rbuf->type, in_rbuf->recdA+i, p->loc_fld_idx, loc)) != kOkRC ) { rc = cwLogError(rc,"The 'loc' field read failed."); goto errLabel; } } if( loc != kInvalidIdx ) { //rbuf_t* xf_rbuf = nullptr; const preset_sel::frag_t* frag = nullptr; unsigned preset_idx = kInvalidIdx; // lookup the fragment associated with the location if( preset_sel::track_loc( p->psH, loc, frag ) && frag != nullptr ) { // get the preset index associated with the current frag if((preset_idx = fragment_play_preset_index(p->psH, frag )) == kInvalidIdx ) { rc = cwLogError(kInvalidArgRC,"The current frag does not have valid preset associated with it."); goto errLabel; } // validate the preset index if( preset_idx >= p->presetN ) { rc = cwLogError(kAssertFailRC,"The selected preset index %i is out of range.",preset_idx); goto errLabel; } if( preset_idx != p->preset_idx ) { if( preset_idx != kInvalidIdx ) { //printf("PRE-SEL:loc:%i %i %s\n",loc,preset_idx,preset_sel::preset_label(p->psH,preset_idx)); if((rc = var_set(proc, kPresetLabelPId, kAnyChIdx, preset_sel::preset_label(p->psH,preset_idx))) != kOkRC ) goto errLabel; } } } } errLabel: return rc; } rc_t _report( proc_t* proc, inst_t* p ) { return kOkRC; } class_members_t members = { .create = std_create, .destroy = std_destroy, .value = std_value, .exec = std_exec, .report = std_report }; } //------------------------------------------------------------------------------------------------------------------ // // gutim_ps // namespace gutim_ps { enum { kPolyCntPId, kInitCfgPId, kPresetMapCfgPId, kFNamePId, kInPId, kLocPId, kResetPId, kPerNoteFlPId, kPriProbFlPId, kPriUniformFlPId, kPriDryOnPlayFlPId, kPriAllowAllFlPId, kPriDryOnSelFlPId, kInterpFlPId, kInterpDistPId, kInterpRandFlPId, kSecProbFlPId, kSecUniformFlPId, kSecDryOnPlayFlPId, kSecAllowAllFlPId, kSecDryOnSelFlPId, kMinPId, kMidiInPId = kMinPId, kWndSmpCntPId, kCeilingPId, kExpoPId, kThreshPId, kUprPId, kLwrPId, kMixPId, kCIGainPId, kCOGainPId, kDryGainPId, kMaxPId }; enum { kPresetVarN = kMaxPId - kMinPId, kMaxChN = 2 // all output variables are assume stereo values }; typedef enum { kNoPresetValTId, kUIntPresetValTId, kCoeffPresetValTId, } value_tid_t; typedef struct var_cfg_str { const char* var_label; // gutim_ps var label unsigned var_pid; // gutim_ps var pid const char* cls_label; // preset class proc label const char* cls_var_label; // preset class proc var label value_tid_t tid; // gutim_ps var data type } var_cfg_t; typedef struct { value_tid_t tid; // k???PresetValTId union { unsigned uint; coeff_t coeff; } u; } preset_value_t; typedef struct preset_var_str { preset_value_t chA[ kMaxChN ]; } preset_var_t; typedef struct { const char* ps_label; const char* cls_label; preset_var_t varA[ kPresetVarN ]; } preset_t; // One var_cfg record for each transform parameter that gutim_ps outputs var_cfg_t _var_cfgA[] = { { "wnd_smp_cnt", kWndSmpCntPId, "pv_analysis", "wndSmpN", kUIntPresetValTId }, { "ceiling", kCeilingPId, "spec_dist", "ceiling", kCoeffPresetValTId }, { "expo", kExpoPId, "spec_dist", "expo", kCoeffPresetValTId }, { "thresh", kThreshPId, "spec_dist", "thresh", kCoeffPresetValTId }, { "upr", kUprPId, "spec_dist", "upr", kCoeffPresetValTId }, { "lwr", kLwrPId, "spec_dist", "lwr", kCoeffPresetValTId }, { "mix", kMixPId, "spec_dist", "mix", kCoeffPresetValTId }, { "c_igain", kCIGainPId, "compressor", "igain", kCoeffPresetValTId }, { "c_ogain", kCOGainPId, "compressor", "ogain", kCoeffPresetValTId }, { "dry_gain", kDryGainPId, "gutim_ps", "dry_gain",kCoeffPresetValTId }, { nullptr, kMaxPId, nullptr, nullptr, kNoPresetValTId }, }; // One record for each of the gutim presets. preset_t.varA[] contains the value for each preset preset_t _presetA[] = { { "dry","dry",{} }, { "a","a",{} }, { "b","b",{} }, { "c","c",{} }, { "d","d",{} }, { "f1","f_1",{} }, { "f2","f_2",{} }, { "f3","f_3",{} }, { "f4","f_4",{} }, { "g","g",{} }, { "ga","g_a",{} }, { "g1a","g_1_a",{} }, { "g1d","g_1_d",{} }, }; typedef struct { unsigned polyN; preset_sel::handle_t psH; // location->preset map unsigned loc_fld_idx; unsigned base[ kMaxPId ]; // base PId's for the poly var's: kMinPId - kMaxPId) unsigned psPresetCnt; preset_t* presetA; // presetA[ presetN ] Preset variable values associated with each of the preset labels (e.g. a,b,c, ... g,ga,g1a,g1d) unsigned presetN; // const preset_sel::frag_t* cur_frag; unsigned cur_pri_preset_idx; unsigned cur_sec_preset_idx; coeff_t cur_interp_dist; bool per_note_fl; bool pri_prob_fl; bool pri_uniform_fl; bool pri_dry_on_play_fl; bool pri_allow_all_fl; bool pri_dry_on_sel_fl; bool sec_prob_fl; bool sec_uniform_fl; bool sec_dry_on_play_fl; bool sec_allow_all_fl; bool sec_dry_on_sel_fl; bool interp_fl; bool interp_rand_fl; } inst_t; const char* _preset_index_to_label( inst_t* p, unsigned preset_idx ) { const char* label = ""; assert( preset_idx == kInvalidIdx || preset_idx < p->presetN ); if( preset_idx != kInvalidIdx && preset_idx < p->presetN ) label = _presetA[ preset_idx ].ps_label; return label; } template< typename T > rc_t _read_class_preset_value( proc_t* proc, preset_t* preset, var_cfg_t* var_cfg, unsigned ch_idx, T& val_ref ) { rc_t rc; if((rc = class_preset_value( proc->ctx, var_cfg->cls_label, preset->cls_label, var_cfg->cls_var_label, ch_idx, val_ref )) != kOkRC ) { rc = cwLogError(rc,"The preset value could not be accessed for the preset '%s' from '%s:%s' ch:%i.",preset->cls_label,var_cfg->cls_label,var_cfg->cls_var_label,ch_idx); goto errLabel; } errLabel: return rc; } rc_t _read_class_preset_value( proc_t* proc, preset_t* preset, var_cfg_t* var_cfg, unsigned var_idx ) { rc_t rc = kOkRC; unsigned chN = 0; bool preset_has_var_fl = false; // // TODO: It should be an error if a given preset does not always reference a variable that it may reference in another preset // This would imply that the value is simply left in it's current state - which might lead to unpredicitable results. // // Therefore class_preset_has_var() should always be true. // if((rc = class_preset_has_var( proc->ctx, var_cfg->cls_label, preset->cls_label, var_cfg->cls_var_label, preset_has_var_fl )) != kOkRC ) { rc = cwLogError(rc,"The class preset variable list could not be accessed for the preset '%s' from '%s:%s'.",preset->cls_label,var_cfg->cls_label,var_cfg->cls_var_label); goto errLabel; } if( preset_has_var_fl ) { if((rc = class_preset_value_channel_count( proc->ctx, var_cfg->cls_label, preset->cls_label, var_cfg->cls_var_label, chN )) != kOkRC ) { rc = cwLogError(rc,"The class preset channel count could not be accessed for the preset '%s' from '%s:%s'.",preset->cls_label,var_cfg->cls_label,var_cfg->cls_var_label); goto errLabel; } if( chN > kMaxChN ) { rc = cwLogError(rc,"Thethe preset '%s' from '%s:%s' has more channels (%i) than can be processed (%i).",preset->cls_label,var_cfg->cls_label,var_cfg->cls_var_label,chN,kMaxChN); goto errLabel; } } // We always set all the preset->varA[].chA[] values - even if a // particular preset does not specify multiple channels. for(unsigned i=0; ivarA[var_idx].chA[i]; // if this preset does not reference this variable v->tid = preset_has_var_fl ? var_cfg->tid : kNoPresetValTId; switch( v->tid ) { case kNoPresetValTId: break; case kUIntPresetValTId: rc = _read_class_preset_value( proc, preset, var_cfg, ch_idx, v->u.uint ); break; case kCoeffPresetValTId: rc = _read_class_preset_value( proc, preset, var_cfg, ch_idx, v->u.coeff ); break; default: rc = cwLogError(kInvalidDataTypeRC,"An invalid variable value data type (%i) was encountered.",var_cfg->tid); } } if(rc != kOkRC ) { rc = cwLogError(rc,"The preset value for the variable '%s' for preset '%s' from '%s:%s' could not be accessed.", var_cfg->var_label, preset->cls_label, var_cfg->cls_label, var_cfg->cls_var_label ); goto errLabel; } errLabel: return rc; } rc_t _create_and_fill_preset_array( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; p->presetA = _presetA; p->presetN = sizeof(_presetA)/sizeof(_presetA[0]); // BUG BUG BUG: what is the point of storing both psPresetCnt and presetN if they are the same value? assert( p->presetN == p->psPresetCnt ); // for each preset for(unsigned i=0; i< p->presetN; ++i) { preset_t* preset = p->presetA + i; // for each value of interest in this preset for(unsigned j=0; _var_cfgA[j].var_label!=nullptr; ++j) { assert( j < kPresetVarN ); // get the value of the preset variable if((rc = _read_class_preset_value( proc, preset, _var_cfgA + j, j)) != kOkRC ) { goto errLabel; } } } errLabel: if( rc != kOkRC ) rc = cwLogError(rc,"Preset value initialization failed."); return rc; } rc_t _apply_preset_no_interp(proc_t* proc, inst_t* p, unsigned voice_idx) { rc_t rc = kOkRC; if( p->cur_pri_preset_idx == kInvalidIdx || p->cur_pri_preset_idx >= p->presetN ) { rc = cwLogError(kInvalidArgRC,"The primary preset is invalid."); goto errLabel; } for(unsigned var_idx=0; _var_cfgA[var_idx].var_label!=nullptr; ++var_idx) { const var_cfg_t* var_cfg = _var_cfgA + var_idx; assert( p->cur_pri_preset_idx < p->presetN ); for(unsigned ch_idx=0; ch_idxpresetA[ p->cur_pri_preset_idx ].varA[ var_idx ].chA + ch_idx; variable_t* varb; var_find(proc, p->base[var_cfg->var_pid] + voice_idx,ch_idx, varb); switch( v->tid ) { case kNoPresetValTId: // this preset does not reference this variable break; case kUIntPresetValTId: //printf("PS: %i %s %s %i\n",ch_idx,var_cfg->var_label,varb->label,v->u.uint); var_set(proc, p->base[var_cfg->var_pid] + voice_idx, ch_idx, v->u.uint ); break; case kCoeffPresetValTId: //printf("PS: %i %s %s %f\n",ch_idx,var_cfg->var_label,varb->label,v->u.coeff); var_set(proc, p->base[var_cfg->var_pid] + voice_idx, ch_idx, v->u.coeff ); break; default: rc = cwLogError(kInvalidArgRC,"Unknown preset value type:%i on %s.",v->tid,cwStringNullGuard(var_cfg->var_label)); goto errLabel; } } } errLabel: return rc; } rc_t _apply_preset_with_interp(proc_t* proc, inst_t* p, unsigned voice_idx) { rc_t rc = kOkRC; if( p->cur_pri_preset_idx == kInvalidIdx || p->cur_pri_preset_idx >= p->presetN ) { rc = cwLogError(kInvalidArgRC,"The primary preset is invalid."); goto errLabel; } if( p->cur_sec_preset_idx == kInvalidIdx || p->cur_sec_preset_idx >= p->presetN ) { rc = cwLogError(kInvalidArgRC,"The secondary preset is invalid."); goto errLabel; } for(unsigned var_idx=0; _var_cfgA[var_idx].var_label!=nullptr; ++var_idx) { const var_cfg_t* var_cfg = _var_cfgA + var_idx; assert( p->cur_pri_preset_idx < p->presetN ); for(unsigned ch_idx=0; ch_idxpresetA[ p->cur_pri_preset_idx ].varA[ var_idx ].chA + ch_idx; const preset_value_t* c1 = p->presetA[ p->cur_sec_preset_idx ].varA[ var_idx ].chA + ch_idx; switch( var_cfg->tid ) { case kNoPresetValTId: // this preset does not reference this variable break; case kUIntPresetValTId: { uint_t v0 = std::min(c0->u.uint,c1->u.uint); uint_t v1 = std::max(c0->u.uint,c1->u.uint); uint_t v = (unsigned)(v0 + p->cur_interp_dist * (v1 - v0)); var_set(proc, p->base[var_cfg->var_pid] + voice_idx, ch_idx, v ); } break; case kCoeffPresetValTId: { coeff_t v0 = std::min(c0->u.coeff,c1->u.coeff); coeff_t v1 = std::max(c0->u.coeff,c1->u.coeff); coeff_t v = v0 + p->cur_interp_dist * (v1 - v0); var_set(proc, p->base[var_cfg->var_pid] + voice_idx, ch_idx, v ); } break; default: rc = cwLogError(kInvalidArgRC,"Unknown preset value type:%i on %s.",var_cfg->tid,cwStringNullGuard(var_cfg->var_label)); goto errLabel; } } } errLabel: return rc; } void _report_preset( proc_t* proc, inst_t* p, const char* label, unsigned preset_idx ) { cwLogPrint("%s : ",label); if( p->cur_frag == nullptr ) { cwLogPrint("No location (frag) selected."); goto errLabel; } if( preset_idx == kInvalidIdx ) { cwLogPrint("No preset selected."); goto errLabel; } for(unsigned i=0; icur_frag->probDomN; ++i) { bool fl = p->cur_frag->probDomA[i].index == preset_idx; cwLogPrint("%s%s%s ",fl?"(":"",p->presetA[ preset_idx ].ps_label,fl?")":""); } errLabel: return; } void _print_preset( proc_t* proc, inst_t* p ) { _report_preset( proc, p, "Pri", p->cur_pri_preset_idx ); if( p->cur_sec_preset_idx != kInvalidIdx ) _report_preset( proc, p, "Sec", p->cur_sec_preset_idx ); } rc_t _apply_preset( proc_t* proc, inst_t* p, const midi::ch_msg_t* m, unsigned voice_idx ) { rc_t rc = kOkRC; if( p->cur_frag == nullptr || p->cur_pri_preset_idx == kInvalidIdx ) { rc = cwLogError(kInvalidStateRC,"No current preset has been selected."); goto errLabel; } if( p->cur_sec_preset_idx == kInvalidIdx ) { rc = _apply_preset_no_interp(proc, p, voice_idx); } else { rc = _apply_preset_with_interp(proc, p, voice_idx); } errLabel: if( rc != kOkRC ) cwLogError(rc,"Preset application failed."); return rc; } rc_t _update_cur_preset_idx( proc_t* proc, inst_t* p, const preset_sel::frag_t* f ) { if( f == nullptr ) return cwLogError(kInvalidArgRC,"Cannot update current selected preset if no location value has been set."); rc_t rc = kOkRC; unsigned flags = 0; p->cur_pri_preset_idx = kInvalidIdx; p->cur_sec_preset_idx = kInvalidIdx; var_get(proc, kInterpFlPId, kAnyChIdx, p->interp_fl); if( p->pri_prob_fl ) flags += preset_sel::kUseProbFl; if( p->pri_uniform_fl ) flags += preset_sel::kUniformFl; if( p->pri_dry_on_play_fl ) flags += preset_sel::kDryOnPlayFl; if( p->pri_allow_all_fl ) flags += preset_sel::kAllowAllFl; if( p->pri_dry_on_sel_fl ) flags += preset_sel::kDryOnSelFl; p->cur_pri_preset_idx = prob_select_preset_index( p->psH, f, flags ); if( p->interp_fl ) { flags = 0; if( p->sec_prob_fl ) flags += preset_sel::kUseProbFl; if( p->sec_uniform_fl ) flags += preset_sel::kUniformFl; if( p->sec_dry_on_play_fl ) flags += preset_sel::kDryOnPlayFl; if( p->sec_allow_all_fl ) flags += preset_sel::kAllowAllFl; if( p->sec_dry_on_sel_fl ) flags += preset_sel::kDryOnSelFl; p->cur_sec_preset_idx = prob_select_preset_index( p->psH, f, flags, p->cur_pri_preset_idx ); if( p->interp_rand_fl ) p->cur_interp_dist = std::max(0.0f,std::min(1.0f, (coeff_t)rand() / RAND_MAX )); else var_get(proc, kInterpDistPId, kAnyChIdx, p->cur_interp_dist ); } cwLogInfo("Preset:%s%s%s",_preset_index_to_label(p,p->cur_pri_preset_idx), p->interp_fl ? "->":" ",_preset_index_to_label(p,p->cur_sec_preset_idx)); return rc; } rc_t _create( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; const char* fname = nullptr; unsigned loc = kInvalidIdx; rbuf_t* rbuf = nullptr; char* exp_fname = nullptr; const object_t* cfg = nullptr; bool resetFl = false; if((rc = var_register_and_get(proc,kAnyChIdx, kInitCfgPId, "cfg", kBaseSfxId, cfg, // TODO: clean up the contents of this CFG kInPId, "in", kBaseSfxId, rbuf, kFNamePId, "fname", kBaseSfxId, fname, kLocPId, "loc", kBaseSfxId, loc, kResetPId, "reset", kBaseSfxId, resetFl, kPerNoteFlPId, "per_note_fl",kBaseSfxId, p->per_note_fl, kPriProbFlPId, "pri_prob_fl", kBaseSfxId, p->pri_prob_fl, kPriUniformFlPId, "pri_uniform_fl", kBaseSfxId, p->pri_uniform_fl, kPriDryOnPlayFlPId,"pri_dry_on_play_fl", kBaseSfxId, p->pri_dry_on_play_fl, kPriAllowAllFlPId, "pri_allow_all_fl", kBaseSfxId, p->pri_allow_all_fl, kPriDryOnSelFlPId, "pri_dry_on_sel_fl", kBaseSfxId, p->pri_dry_on_sel_fl, kInterpFlPId, "interp_fl", kBaseSfxId, p->interp_fl, kInterpDistPId, "interp_dist", kBaseSfxId, p->cur_interp_dist, kInterpRandFlPId, "interp_rand_fl", kBaseSfxId, p->interp_rand_fl, kSecProbFlPId, "sec_prob_fl", kBaseSfxId, p->sec_prob_fl, kSecUniformFlPId, "sec_uniform_fl", kBaseSfxId, p->sec_uniform_fl, kSecDryOnPlayFlPId,"sec_dry_on_play_fl", kBaseSfxId, p->sec_dry_on_play_fl, kSecAllowAllFlPId, "sec_allow_all_fl", kBaseSfxId, p->sec_allow_all_fl, kSecDryOnSelFlPId, "sec_dry_on_sel_fl", kBaseSfxId, p->sec_dry_on_sel_fl )) != kOkRC ) { goto errLabel; } if( (p->polyN = var_mult_count(proc,"midi_in")) == kInvalidCnt || p->polyN == 0 ) { rc = cwLogError(kInvalidArgRC,"The 'midi_in' must be connected to a 'mult' source with at least one 'mult' instance."); goto errLabel; } if((exp_fname = proc_expand_filename(proc,fname)) == nullptr ) { rc = cwLogError(kOpFailRC,"Preset filename expansion failed."); goto errLabel; } // create the cwPresetSel object if(cfg==nullptr || (rc = preset_sel::create(p->psH,cfg)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The preset select object could not be initialized."); goto errLabel; } // read in the loc->preset map file if((rc = preset_sel::read(p->psH,exp_fname)) != kOkRC ) { rc = cwLogError(rc,"The preset_sel data file '%s' could not be read.",cwStringNullGuard(exp_fname)); goto errLabel; } // The location is coming from a 'record', get the location field. if((p->loc_fld_idx = recd_type_field_index( rbuf->type, "loc")) == kInvalidIdx ) { rc = cwLogError(kInvalidArgRC,"The 'in' record does not have a 'loc' field."); goto errLabel; } // Initialize the base pid's for each of the poly variables for(unsigned i=kMinPId; ibase[i] = kMinPId + (i*p->polyN); // register the poly variables for(unsigned i=0; ipolyN; ++i) { if((rc = var_register(proc, kAnyChIdx, p->base[ kMidiInPId ] + i, "midi_in", kBaseSfxId + i )) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"The 'midi_in' registration failed."); goto errLabel; } for(unsigned ch_idx=0; ch_idxbase[ kMidiInPId ] + i, "midi_in", kBaseSfxId + i, p->base[ kWndSmpCntPId ] + i, "wnd_smp_cnt", kBaseSfxId + i, p->base[ kCeilingPId ] + i, "ceiling", kBaseSfxId + i, p->base[ kExpoPId ] + i, "expo", kBaseSfxId + i, p->base[ kThreshPId ] + i, "thresh", kBaseSfxId + i, p->base[ kUprPId ] + i, "upr", kBaseSfxId + i, p->base[ kLwrPId ] + i, "lwr", kBaseSfxId + i, p->base[ kMixPId ] + i, "mix", kBaseSfxId + i, p->base[ kCIGainPId ] + i, "c_igain", kBaseSfxId + i, p->base[ kCOGainPId ] + i, "c_ogain", kBaseSfxId + i, p->base[ kDryGainPId ] + i, "dry_gain", kBaseSfxId + i )) != kOkRC ) { goto errLabel; } } } p->psPresetCnt = preset_count(p->psH); // get the count of preset class (~13) // Get the values for all the presets required by the transform parameter variables rc = _create_and_fill_preset_array( proc, p ); errLabel: mem::release(exp_fname); return rc; } rc_t _destroy( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; // Custom clean-up code goes here return rc; } rc_t _exec_note_on( proc_t* proc, inst_t* p, const midi::ch_msg_t* m, unsigned voice_idx ) { rc_t rc = kOkRC; bool per_note_fl = false; if( var_get(proc,kPerNoteFlPId,kAnyChIdx,per_note_fl) != kOkRC ) goto errLabel; if( per_note_fl ) if((rc = _update_cur_preset_idx( proc, p, p->cur_frag )) != kOkRC ) goto errLabel; rc = _apply_preset( proc, p, m, voice_idx ); errLabel: return rc; } rc_t _exec_midi_in( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; mbuf_t* mbuf = nullptr; for(unsigned voice_idx=0; voice_idxpolyN; ++voice_idx) { // get the input MIDI buffer if((rc = var_get(proc,p->base[ kMidiInPId ]+voice_idx,kAnyChIdx,mbuf)) != kOkRC ) goto errLabel; for(unsigned j=0; jmsgN; ++j) { const midi::ch_msg_t* m = mbuf->msgA + j; // if this is a note-on msg if( m->status == midi::kNoteOnMdId && m->d1 > 0 ) { _exec_note_on(proc,p,m,voice_idx); } } } errLabel: return rc; } rc_t _exec_on_new_location( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; bool per_note_fl = false; if( var_get(proc,kPerNoteFlPId,kAnyChIdx,per_note_fl) != kOkRC ) goto errLabel; if( !per_note_fl ) if((rc = _update_cur_preset_idx( proc, p, p->cur_frag )) != kOkRC ) goto errLabel; errLabel: return rc; } rc_t _exec_track_loc( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; rbuf_t* in_rbuf = nullptr; unsigned loc = kInvalidIdx; if((rc = var_get(proc,kInPId,kAnyChIdx,in_rbuf)) != kOkRC) goto errLabel; // for each incoming MIDI record for(unsigned i=0; irecdN; ++i) { unsigned tmp; // get the 'loc' field if((rc = recd_get( in_rbuf->type, in_rbuf->recdA+i, p->loc_fld_idx, tmp)) != kOkRC ) { rc = cwLogError(rc,"The 'loc' field read failed."); goto errLabel; } if( tmp != kInvalidIdx ) loc = tmp; } // if a location value was received if( loc != kInvalidIdx ) { const preset_sel::frag_t* frag = nullptr; // lookup the fragment associated with the location if( preset_sel::track_loc( p->psH, loc, frag ) && frag != nullptr ) { p->cur_frag = frag; cwLogInfo("LOC:%i",loc); rc = _exec_on_new_location(proc,p); } } errLabel: return rc; } /* bool _update( proc_t* proc, unsigned vid, bool& fl_ref, rc_t rc_ref) { rc_t rc = kOkRC; bool fl_value = false; bool value_changed_fl = false; if((rc = var_get(proc, vid, kAnyChIdx, fl_value)) != kOkRC ) { rc_ref = rc; return false; } value_changed_fl = (fl_value != fl_ref); //if( vid == kPriProbFlPId ) // printf("%i : %i %i %i\n",vid,fl_value,fl_ref,value_changed_fl); fl_ref = fl_value; return value_changed_fl; } rc_t _exec_update_state( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; if( _update( proc, kPriProbFlPId, p->pri_prob_fl, rc) ) { var_send_to_ui_enable(proc, kPriUniformFlPId, kAnyChIdx, p->pri_prob_fl ); var_send_to_ui_enable(proc, kPriDryOnPlayFlPId, kAnyChIdx, p->pri_prob_fl ); var_send_to_ui_enable(proc, kPriAllowAllFlPId, kAnyChIdx, p->pri_prob_fl ); var_send_to_ui_enable(proc, kPriDryOnSelFlPId, kAnyChIdx, p->pri_prob_fl && p->pri_allow_all_fl ); } if( _update( proc, kPriAllowAllFlPId, p->pri_allow_all_fl, rc ) ) { var_send_to_ui_enable(proc, kPriDryOnSelFlPId, kAnyChIdx, p->pri_allow_all_fl ); } _update( proc, kSecUniformFlPId, p->pri_uniform_fl, rc); _update( proc, kPriDryOnPlayFlPId, p->pri_dry_on_play_fl, rc); _update( proc, kPriDryOnSelFlPId, p->pri_dry_on_sel_fl, rc); if( _update( proc, kInterpFlPId, p->interp_fl, rc ) ) { var_send_to_ui_enable(proc, kInterpRandFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kInterpDistPId, kAnyChIdx, p->interp_fl & (!p->interp_rand_fl) ); var_send_to_ui_enable(proc, kSecProbFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kSecUniformFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kSecDryOnPlayFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kSecAllowAllFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->interp_fl && p->sec_allow_all_fl ); } if( _update( proc, kInterpRandFlPId, p->interp_rand_fl, rc ) ) { var_send_to_ui_enable(proc, kInterpDistPId, kAnyChIdx, p->interp_fl & (!p->interp_rand_fl) ); } if( _update( proc, kSecProbFlPId, p->sec_prob_fl, rc) ) { var_send_to_ui_enable(proc, kSecUniformFlPId, kAnyChIdx, p->sec_prob_fl ); var_send_to_ui_enable(proc, kSecDryOnPlayFlPId, kAnyChIdx, p->sec_prob_fl ); var_send_to_ui_enable(proc, kSecAllowAllFlPId, kAnyChIdx, p->sec_prob_fl ); var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->sec_prob_fl && p->sec_allow_all_fl ); } if( _update( proc, kSecAllowAllFlPId, p->sec_allow_all_fl, rc ) ) { var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->sec_allow_all_fl ); } _update( proc, kSecUniformFlPId, p->sec_uniform_fl, rc); _update( proc, kSecDryOnPlayFlPId, p->sec_dry_on_play_fl, rc); _update( proc, kSecDryOnSelFlPId, p->sec_dry_on_sel_fl, rc); return rc; } */ rc_t _update_ui_state( proc_t* proc, inst_t* p, variable_t* var ) { rc_t rc = kOkRC; // if the ui has not yet been initialized then there is nothing to do if( var->ui_var == NULL ) return rc; switch(var->vid) { case kPriProbFlPId: var_get(var,p->pri_prob_fl); var_send_to_ui_enable(proc, kPriUniformFlPId, kAnyChIdx, p->pri_prob_fl ); var_send_to_ui_enable(proc, kPriDryOnPlayFlPId, kAnyChIdx, p->pri_prob_fl ); var_send_to_ui_enable(proc, kPriAllowAllFlPId, kAnyChIdx, p->pri_prob_fl && p->pri_uniform_fl ); var_send_to_ui_enable(proc, kPriDryOnSelFlPId, kAnyChIdx, p->pri_prob_fl && p->pri_uniform_fl && p->pri_allow_all_fl ); break; case kPriUniformFlPId: var_get(var,p->pri_uniform_fl); var_send_to_ui_enable(proc, kPriAllowAllFlPId, kAnyChIdx, p->pri_uniform_fl ); var_send_to_ui_enable(proc, kPriDryOnSelFlPId, kAnyChIdx, p->pri_uniform_fl && p->pri_allow_all_fl ); break; case kPriDryOnPlayFlPId: var_get(var,p->pri_dry_on_play_fl); break; case kPriAllowAllFlPId: var_get(var,p->pri_allow_all_fl); var_send_to_ui_enable(proc, kPriDryOnSelFlPId, kAnyChIdx, p->pri_allow_all_fl ); break; case kPriDryOnSelFlPId: var_get(var,p->pri_dry_on_sel_fl); break; case kInterpFlPId: var_get(var,p->interp_fl); var_send_to_ui_enable(proc, kSecProbFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kInterpRandFlPId, kAnyChIdx, p->interp_fl ); var_send_to_ui_enable(proc, kInterpDistPId, kAnyChIdx, p->interp_fl & (!p->interp_rand_fl) ); break; case kInterpDistPId: var_get(var,p->cur_interp_dist); break; case kInterpRandFlPId: var_get(var,p->interp_rand_fl); var_send_to_ui_enable(proc, kInterpDistPId, kAnyChIdx, p->interp_fl & (!p->interp_rand_fl) ); break; case kSecProbFlPId: var_get(var,p->sec_prob_fl); var_send_to_ui_enable(proc, kSecUniformFlPId, kAnyChIdx, p->sec_prob_fl ); var_send_to_ui_enable(proc, kSecDryOnPlayFlPId, kAnyChIdx, p->sec_prob_fl ); var_send_to_ui_enable(proc, kSecAllowAllFlPId, kAnyChIdx, p->sec_prob_fl && p->sec_uniform_fl); var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->sec_prob_fl && p->sec_uniform_fl && p->sec_allow_all_fl ); break; case kSecUniformFlPId: var_get(var,p->sec_uniform_fl); var_send_to_ui_enable(proc, kSecAllowAllFlPId, kAnyChIdx, p->sec_uniform_fl ); var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->sec_uniform_fl && p->sec_allow_all_fl ); break; case kSecDryOnPlayFlPId: var_get(var,p->sec_dry_on_play_fl); break; case kSecAllowAllFlPId: var_get(var,p->sec_allow_all_fl); var_send_to_ui_enable(proc, kSecDryOnSelFlPId, kAnyChIdx, p->sec_allow_all_fl ); break; case kSecDryOnSelFlPId: var_get(var,p->sec_dry_on_sel_fl); break; } return rc; } rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) { rc_t rc = kOkRC; //if((rc = _exec_update_state(proc, p )) != kOkRC ) // goto errLabel; _update_ui_state( proc, p, var ); if( var->vid == kResetPId ) { if( p->psH.isValid() ) track_loc_reset( p->psH); } //errLabel: return rc; } rc_t _exec( proc_t* proc, inst_t* p ) { rc_t rc; if((rc = _exec_track_loc(proc,p)) != kOkRC ) goto errLabel; if((rc = _exec_midi_in(proc,p)) != kOkRC ) goto errLabel; errLabel: return rc; } rc_t _report( proc_t* proc, inst_t* p ) { return kOkRC; } class_members_t members = { .create = std_create, .destroy = std_destroy, .value = std_value, .exec = std_exec, .report = std_report }; } //------------------------------------------------------------------------------------------------------------------ // // 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; irecdN; ++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; iscoreH, 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, .destroy = std_destroy, .value = std_value, .exec = std_exec, .report = std_report }; } // score_follower } // flow } //cw