diff --git a/Makefile.am b/Makefile.am index df15925..9acf7b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,8 +34,8 @@ libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp src/libcw/cwWa libcwHDR += src/libcw/cwAudioFileOps.h src/libcw/cwAudioTransforms.h src/libcw/cwDspTransforms.h src/libcw/cwAudioFileProc.h src/libcw/cwPvAudioFileProc.h libcwSRC += src/libcw/cwAudioFileOps.cpp src/libcw/cwAudioTransforms.cpp src/libcw/cwDspTransforms.cpp src/libcw/cwAudioFileProc.cpp src/libcw/cwPvAudioFileProc.cpp -libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h -libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp +libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowValue.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowPerf.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h +libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowValue.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowPerf.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp if cwWEBSOCK libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h diff --git a/cwFlowPerf.cpp b/cwFlowPerf.cpp new file mode 100644 index 0000000..da762ed --- /dev/null +++ b/cwFlowPerf.cpp @@ -0,0 +1,594 @@ +#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 { + kScoreFileNamePId, + kDoneFlPId, + kOutPId, + kLocPId + }; + + typedef struct + { + unsigned sample_idx; + unsigned loc; + unsigned meas; + midi::ch_msg_t* midi; // index of associated msg in chMsgA + } msg_t; + + typedef struct + { + + unsigned msgAllocN; + unsigned msgN; + msg_t* msgA; // msgA[ msgN ] + midi::ch_msg_t* chMsgA; // chMsgA[ msgN ] + + unsigned sample_idx; + unsigned msg_idx; + } inst_t; + + rc_t _load_score( proc_t* proc, inst_t* p, const char* fname ) + { + rc_t rc = kOkRC; + + perf_score::handle_t perfScoreH; + const perf_score::event_t* score_evt = nullptr; + + if( fname == nullptr || textLength(fname)==0 ) + { + rc = cwLogError(kInvalidArgRC,"The score filename is blank."); + 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(unsigned i=0; imsgAllocN; ++i,++score_evt) + { + 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; + + p->msgN += 1; + } + } + + + errLabel: + perf_score::destroy(perfScoreH); + + return rc; + } + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const char* score_fname = nullptr; + + if((rc = var_register_and_get(proc,kAnyChIdx,kScoreFileNamePId, "fname", kBaseSfxId, score_fname)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register(proc,kAnyChIdx, + kLocPId,"loc", kBaseSfxId, + kDoneFlPId,"done_fl", kBaseSfxId )) != 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; + + + 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 ) + 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, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } // 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; imsgN; ++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, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } // 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; 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 diff --git a/cwFlowPerf.h b/cwFlowPerf.h new file mode 100644 index 0000000..66eea83 --- /dev/null +++ b/cwFlowPerf.h @@ -0,0 +1,10 @@ +namespace cw +{ + namespace flow + { + namespace score_player { extern class_members_t members; } + namespace midi_to_msg { extern class_members_t members; } + namespace score_follower { extern class_members_t members; } + + } +}