diff --git a/cwScoreFollower.cpp b/cwScoreFollower.cpp index c70402a..60f7ac9 100644 --- a/cwScoreFollower.cpp +++ b/cwScoreFollower.cpp @@ -8,59 +8,55 @@ #include "cwFileSys.h" #include "cwMidi.h" #include "cwMidiFile.h" -#include "cwCmInterface.h" + +#include "cwDynRefTbl.h" +#include "cwScoreParse.h" +#include "cwSfScore.h" +#include "cwSfMatch.h" +#include "cwSfTrack.h" +#include "cwPerfMeas.h" + #include "cwScoreFollowerPerf.h" #include "cwScoreFollower.h" #include "cwMidiState.h" -#include "cmGlobal.h" -#include "cmFloatTypes.h" -#include "cmRpt.h" -#include "cmErr.h" -#include "cmCtx.h" -#include "cmMem.h" -#include "cmSymTbl.h" -#include "cmLinkedHeap.h" -#include "cmTime.h" -#include "cmMidi.h" -#include "cmSymTbl.h" -#include "cmMidiFile.h" -#include "cmAudioFile.h" -#include "cmScore.h" -#include "cmTimeLine.h" -#include "cmProcObj.h" -#include "cmProc4.h" - #include "cwSvgScoreFollow.h" + namespace cw { namespace score_follower { + typedef struct meas_set_str + { + unsigned vsi; + unsigned nsi; + } meas_set_t; + typedef struct score_follower_str { bool enableFl; double srate; unsigned search_area_locN; unsigned key_wnd_locN; - char* cm_score_csv_fname; - cmCtx_t* cmCtx; - cmScH_t cmScoreH; - cmScMatcher* matcher; - - unsigned* cwLocToCmLocA; // cwLocToCmLocA[ cwLocToCmLocN ] - unsigned cwLocToCmLocN; + char* score_csv_fname; - unsigned* cmLocToCwLocA; // cmLocToCwLocA[ cmLocToCwLocN ] - unsigned cmLocToCwLocN; + bool deleteScoreFl; + sfscore::handle_t scoreH; + sftrack::handle_t trackH; + //perf_meas::handle_t measH; - unsigned* match_idA; - unsigned match_id_allocN; - unsigned match_id_curN; + unsigned* result_idxA; + unsigned result_idx_allocN; + unsigned result_idx_curN; - ssf_note_on_t* perfA; + ssf_note_on_t* perfA; // stored performance unsigned perfN; - unsigned perf_idx; + unsigned perf_idx; + + dyn_ref_tbl::handle_t dynRefH; + + unsigned track_flags; } score_follower_t; @@ -71,104 +67,43 @@ namespace cw // parse the score follower parameter record rc_t _parse_cfg( score_follower_t* p, const object_t* cfg ) { - rc_t rc = kOkRC; - - const char* cm_score_csv_fname; + rc_t rc = kOkRC; + const char* score_csv_fname = nullptr; + const object_t* dyn_ref_dict = nullptr; + bool track_print_fl = false; + bool track_results_backtrack_fl = false; - if((rc = cfg->getv("enable_flag", p->enableFl, - "cm_score_csv_fname", cm_score_csv_fname, - "search_area_locN", p->search_area_locN, - "key_wnd_locN", p->key_wnd_locN )) != kOkRC ) + if((rc = cfg->getv("enable_flag", p->enableFl, + "score_csv_fname", score_csv_fname, + "search_area_locN", p->search_area_locN, + "key_wnd_locN", p->key_wnd_locN, + "dyn_ref", dyn_ref_dict, + "track_print_fl", track_print_fl, + "track_results_backtrack_fl", track_results_backtrack_fl)) != kOkRC ) { rc = cwLogError(kInvalidArgRC, "Score follower argument parsing failed."); goto errLabel; } - if((p->cm_score_csv_fname = filesys::expandPath( cm_score_csv_fname )) == nullptr ) + if((p->score_csv_fname = filesys::expandPath( score_csv_fname )) == nullptr ) { rc = cwLogError(kOpFailRC,"Score follower score file expansion failed."); goto errLabel; } - errLabel: - return rc; - } - - // get the max cw loc value - rc_t _get_max_cw_loc( score_follower_t* p, unsigned& maxCwLocRef ) - { - rc_t rc = kOkRC; - - // Note: cmScoreEvt_t.csvEventId is the app score 'loc' value. - - unsigned cmEvtN = cmScoreEvtCount( p->cmScoreH ); - - maxCwLocRef = 0; - - for(unsigned i=0; idynRefH, dyn_ref_dict )) != kOkRC ) { - cmScoreEvt_t* cmEvt = cmScoreEvt( p->cmScoreH, i ); - - if( cmEvt == nullptr ) - { - cwLogError(kOpFailRC,"Unexpected missing cm event at index '%i while scanning cmScore.h",i); - goto errLabel; - } - - if( cmEvt->csvEventId > maxCwLocRef ) - maxCwLocRef = cmEvt->csvEventId; - } - - errLabel: - return rc; - } - - // Create cw-to-cm location map - rc_t _createCwLocToCmLocMap( score_follower_t* p ) - { - unsigned cmEvtN =0; - rc_t rc = kOkRC; - - if((rc = _get_max_cw_loc(p,p->cwLocToCmLocN)) != kOkRC ) + rc = cwLogError(rc,"Dynamics reference array parse failed."); goto errLabel; - - p->cwLocToCmLocN += 1; // incr. by 1 because _get_max_cw_loc() returns an index but we need a count - p->cwLocToCmLocA = mem::allocZ( p->cwLocToCmLocN ); - - cmEvtN = cmScoreEvtCount( p->cmScoreH ); - - for(unsigned i=0; icmScoreH, i ); - assert( cmEvt != nullptr ); - p->cwLocToCmLocA[ cmEvt->csvEventId ] = cmEvt->locIdx; } + p->track_flags += track_print_fl ? sftrack::kPrintFl : 0; + p->track_flags += track_results_backtrack_fl ? sftrack::kBacktrackResultsFl : 0; + errLabel: - - return rc; - } - - // create cm-to-cw location map - rc_t _createCmLocToCwLocMap( score_follower_t* p ) - { - rc_t rc = kOkRC; - - unsigned cmEvtN = cmScoreEvtCount( p->cmScoreH ); - - p->cmLocToCwLocN = cmEvtN; - p->cmLocToCwLocA = mem::allocZ( p->cmLocToCwLocN ); - - for(unsigned i=0; icmScoreH, i ); - assert( cmEvt != nullptr ); - p->cmLocToCwLocA[i] = cmEvt->csvEventId; - } - return rc; } - + rc_t _update_midi_state( score_follower_t* p, midi_state::handle_t msH ) { rc_t rc; @@ -187,49 +122,76 @@ namespace cw rc_t _destroy( score_follower_t* p) { - mem::release(p->cmLocToCwLocA); - mem::release(p->cwLocToCmLocA); - mem::release(p->cm_score_csv_fname); + destroy(p->dynRefH); + mem::release(p->score_csv_fname); mem::release(p->perfA); - mem::release(p->match_idA); - cmScMatcherFree(&p->matcher); - cmScoreFinalize(&p->cmScoreH); + mem::release(p->result_idxA); + destroy(p->trackH); + if( p->deleteScoreFl) + destroy(p->scoreH); + //destroy(p->measH); mem::release(p); return kOkRC; } - - extern "C" void _score_follower_cb( struct cmScMatcher_str* smp, void* arg, cmScMatcherResult_t* r ) + void _score_follower_cb( void* arg, sftrack::result_t* r ) { score_follower_t* p = (score_follower_t*)arg; - cmScoreEvt_t* cse; - //printf("%4i %4i %4i %3i %3i %4s : ", r->locIdx, r->mni, r->muid, r->flags, r->pitch, midi::midiToSciPitch( r->pitch, nullptr, 0 )); + //printf("%4i %4i %4i %3i %3i %4s : ", r->oLocId, r->mni, r->muid, r->flags, r->pitch, midi::midiToSciPitch( r->pitch, nullptr, 0 )); - if( r->scEvtIdx == cmInvalidIdx ) + if( r->scEvtIdx == kInvalidIdx ) { //cwLogInfo("Score Follower: MISS"); } else { - // get a pointer to the matched event - if((cse = cmScoreEvt( p->cmScoreH, r->scEvtIdx )) == nullptr ) + const sfscore::event_t* score_event; + + // get a pointer to the matched score event + if((score_event = event( p->scoreH, r->scEvtIdx )) == nullptr ) { cwLogError(kInvalidStateRC,"cm Score event index (%i) reported by the score follower is invalid.",r->scEvtIdx ); } else { - if( p->match_id_curN >= p->match_id_allocN ) + // verify that the matched event buffer has available space + if( p->result_idx_curN >= p->result_idx_allocN ) { cwLogError(kInvalidStateRC,"The score follower match id array is full."); } else { - // the csvEventId corresponds to the cwPianoScore location - p->match_idA[ p->match_id_curN++ ] = cse->csvEventId; + assert( score_event->index == r->scEvtIdx ); + + /* + + // store the performance data in the score + set_perf( p->scoreH, score_event->index, r->sec, r->pitch, r->vel, r->cost ); - //cwLogInfo("Score Follower: MATCH %i\n",cse->csvEventId); + perf_meas::result_t pmr = {0}; + + // Call performance measurement unit + if( perf_meas::exec( p->measH, score_event, pmr ) == kOkRC && pmr.loc != kInvalidIdx && pmr.valueA != nullptr ) + { + double v[ perf_meas::kValCnt ]; + for(unsigned i=0; i::max() ? -1 : pmr.valueA[i]; + + cwLogInfo("Section '%s' triggered loc:%i : dyn:%f even:%f tempo:%f cost:%f", + cwStringNullGuard(pmr.sectionLabel), + pmr.sectionLoc, + v[ perf_meas::kDynValIdx ], + v[ perf_meas::kEvenValIdx ], + v[ perf_meas::kTempoValIdx ], + v[ perf_meas::kMatchCostValIdx ] ); + } + */ + + + // store a pointer to the m matched sfscore event + p->result_idxA[ p->result_idx_curN++ ] = r->index; //score_event->index; } } } @@ -237,68 +199,92 @@ namespace cw } } -cw::rc_t cw::score_follower::create( handle_t& hRef, const object_t* cfg, cm::handle_t cmCtxH, double srate ) +cw::rc_t cw::score_follower::create( handle_t& hRef, const args_t& args ) { rc_t rc = kOkRC; - cmSymTblH_t cmSymTblH = cmSTATIC_NULL_HANDLE; - cmScRC_t scoreRC = cmOkRC; if((rc = destroy(hRef)) != kOkRC ) return rc; score_follower_t* p = mem::allocZ(); - p->cmCtx = context(cmCtxH); + unsigned track_flags = 0; + + track_flags += args.trackPrintFl ? sftrack::kPrintFl : 0; + track_flags += args.trackResultsBacktrackFl ? sftrack::kBacktrackResultsFl : 0; + + p->scoreH = args.scoreH; + // create the score tracker + if((rc = sftrack::create( p->trackH, args.scoreH, args.scoreWndLocN, args.midiWndLocN, track_flags, _score_follower_cb, p )) != kOkRC ) + { + cwLogError(kOpFailRC,"The score follower create failed."); + goto errLabel; + } + + p->srate = sample_rate(p->scoreH); + p->result_idx_allocN = event_count( p->scoreH )*2; // give plenty of extra space for the result_idxA[] + p->result_idxA = mem::allocZ(p->result_idx_allocN); + + p->perfN = event_count(p->scoreH)*2; + p->perfA = mem::allocZ( p->perfN ); + p->perf_idx = 0; + p->enableFl = args.enableFl; + hRef.set(p); + + + errLabel: + if(rc != kOkRC ) + _destroy(p); + + return rc; +} + + +cw::rc_t cw::score_follower::create( handle_t& hRef, const object_t* cfg, double srate ) +{ + rc_t rc = kOkRC; + + if((rc = destroy(hRef)) != kOkRC ) + return rc; + + score_follower_t* p = mem::allocZ(); + if((rc = _parse_cfg(p,cfg)) != kOkRC ) goto errLabel; - // create the the score follower reference score - if((scoreRC = cmScoreInitialize( p->cmCtx, &p->cmScoreH, p->cm_score_csv_fname, srate, nullptr, 0, nullptr, nullptr, cmSymTblH )) != cmOkRC ) + // create the score + if((rc = sfscore::create( p->scoreH, p->score_csv_fname, srate, p->dynRefH )) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The score could not be initialized from '%s'. cmRC:%i.",p->cm_score_csv_fname); - goto errLabel; + rc = cwLogError(kOpFailRC,"The score could not be initialized from '%s'. cmRC:%i.",cwStringNullGuard(p->score_csv_fname)); + goto errLabel; } - // create the score follower - if((p->matcher = cmScMatcherAlloc( proc_context(cmCtxH), // Program context. - nullptr, // Existing cmScMatcher to reallocate or NULL to allocate a new cmScMatcher. - srate, // System sample rate. - p->cmScoreH, // Score handle. See cmScore.h. - p->search_area_locN, // Length of the scores active search area. ** See Notes. - p->key_wnd_locN, // Length of the MIDI active note buffer. ** See Notes. - _score_follower_cb, // A cmScMatcherCb_t function to be called to notify the recipient of changes in the score matcher status. - p )) == nullptr ) // User argument to 'cbFunc'. - { - cwLogError(kOpFailRC,"The score follower allocation failed."); - goto errLabel; - } + p->deleteScoreFl = true; - // create the cw-to-cm loc idx map - if((rc = _createCwLocToCmLocMap( p )) != kOkRC ) + // create the score tracker + if((rc = sftrack::create( p->trackH, p->scoreH, p->search_area_locN, p->key_wnd_locN, p->track_flags, _score_follower_cb, p )) != kOkRC ) { - rc = cwLogError(rc,"cw-to-cm loc map creation failed."); + cwLogError(kOpFailRC,"The score follower create failed."); goto errLabel; } - - if((rc = _createCmLocToCwLocMap( p )) != kOkRC ) + /* + if((rc = perf_meas::create( p->measH, p->scoreH )) != kOkRC ) { - rc = cwLogError(rc,"cm-to-cw loc map creation failed."); - goto errLabel; + cwLogError(kOpFailRC,"The perf. measure object create failed."); + goto errLabel; } - - + */ p->srate = srate; - p->match_id_allocN = cmScoreEvtCount( p->cmScoreH )*2; // give plenty of extra space for the match_idA[] - p->match_idA = mem::allocZ(p->match_id_allocN); + p->result_idx_allocN = event_count( p->scoreH )*2; // give plenty of extra space for the result_idxA[] + p->result_idxA = mem::allocZ(p->result_idx_allocN); - p->perfN = cmScoreEvtCount(p->cmScoreH)*2; + p->perfN = event_count(p->scoreH)*2; p->perfA = mem::allocZ( p->perfN ); p->perf_idx = 0; - + hRef.set(p); - - + errLabel: if( rc != kOkRC ) { @@ -332,86 +318,93 @@ bool cw::score_follower::is_enabled( handle_t h ) return p->enableFl; } -cw::rc_t cw::score_follower::reset( handle_t h, unsigned cwLocId ) +cw::rc_t cw::score_follower::reset( handle_t h, unsigned locId ) { rc_t rc = kOkRC; - cmRC_t cmRC = cmOkRC; score_follower_t* p = _handleToPtr(h); - if( cwLocId != kInvalidId ) + if( locId != kInvalidId ) { - unsigned cmLocId = p->cwLocToCmLocA[cwLocId]; - if( cmLocId == kInvalidId ) - { - rc = cwLogWarning("The cw loc id:%i does not translate to a cm loc id.",cwLocId); - goto errLabel; - } - - printf("SF Reset: cw:%i cm:%i\n",cwLocId,cmLocId); + printf("SF Reset: loc:%i\n",locId); - if((cmRC = cmScMatcherReset( p->matcher, cmLocId )) != cmOkRC ) + if((rc = reset( p->trackH, locId )) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The score follower reset failed."); + rc = cwLogError(rc,"The score follower reset failed."); goto errLabel; } + /* + if((rc = reset( p->measH, locId )) != kOkRC ) + { + rc = cwLogError(rc,"The measurement unit reset failed."); + goto errLabel; + } + */ + p->perf_idx = 0; - clear_match_id_array(h); + clear_result_index_array(h); + clear_all_performance_data(p->scoreH); + } errLabel: return rc; } -cw::rc_t cw::score_follower::exec( handle_t h, double sec, unsigned smpIdx, unsigned muid, unsigned status, uint8_t d0, uint8_t d1, bool& newMatchFlRef ) +cw::rc_t cw::score_follower::exec( handle_t h, + double sec, + unsigned smpIdx, + unsigned muid, + unsigned status, + uint8_t d0, + uint8_t d1, + bool& newMatchFlRef ) { - rc_t rc = kOkRC; - score_follower_t* p = _handleToPtr(h); - unsigned scLocIdx = cmInvalidIdx; - unsigned pre_match_id_curN = p->match_id_curN; + rc_t rc = kOkRC; + score_follower_t* p = _handleToPtr(h); + unsigned scLocIdx = kInvalidIdx; + unsigned pre_result_idx_curN = p->result_idx_curN; if( !p->enableFl ) return cwLogError(kInvalidStateRC,"The score follower is not enabled."); - newMatchFlRef = false; // Note: pass p->perf_idx as 'muid' to the score follower - cmRC_t cmRC = cmScMatcherExec( p->matcher, smpIdx, p->perf_idx, status, d0, d1, &scLocIdx ); + rc = exec( p->trackH, sec, smpIdx, p->perf_idx, status, d0, d1, &scLocIdx ); - switch( cmRC ) + switch( rc ) { - case cmOkRC: - newMatchFlRef = p->match_id_curN != pre_match_id_curN; + case kOkRC: + newMatchFlRef = p->result_idx_curN != pre_result_idx_curN; //printf("NM_FL:%i\n",newMatchFlRef); break; - case cmEofRC: + case kEofRC: rc = cwLogInfo("Score match complete."); break; - case cmInvalidArgRC: + case kInvalidArgRC: rc = cwLogError(kInvalidStateRC,"Score follower state is invalid."); break; - case cmSubSysFailRC: + case kOpFailRC: rc = cwLogError(kOpFailRC,"The score follower failed during a resync attempt."); break; default: - rc = cwLogError(kOpFailRC,"The score follower failed with an unknown error. cmRC:%i",cmRC); + rc = cwLogError(rc,"The score follower failed with an the error:%i",rc); } // store note-on messages if( p->perf_idx < p->perfN && midi::isNoteOn(status,(unsigned)d1) ) { - ssf_note_on_t* pno = p->perfA + p->perf_idx; - pno->sec = sec; - pno->muid = muid; - pno->pitch = d0; - pno->vel = d1; + pno->sec = sec; + pno->muid = muid; + pno->pitch = d0; + pno->vel = d1; p->perf_idx += 1; if( p->perf_idx >= p->perfN ) cwLogWarning("The cw score follower performance cache is full."); @@ -420,37 +413,27 @@ cw::rc_t cw::score_follower::exec( handle_t h, double sec, unsigned smpIdx, uns return rc; } -const unsigned* cw::score_follower::current_match_id_array( handle_t h, unsigned& cur_match_id_array_cnt_ref ) +const unsigned* cw::score_follower::current_result_index_array( handle_t h, unsigned& cur_result_idx_array_cnt_ref ) { score_follower_t* p = _handleToPtr(h); - cur_match_id_array_cnt_ref = p->match_id_curN; - return p->match_idA; + cur_result_idx_array_cnt_ref = p->result_idx_curN; + return p->result_idxA; } -void cw::score_follower::clear_match_id_array( handle_t h ) +void cw::score_follower::clear_result_index_array( handle_t h ) { score_follower_t* p = _handleToPtr(h); - p->match_id_curN = 0; + p->result_idx_curN = 0; } - +/* cw::rc_t cw::score_follower::cw_loc_range( handle_t h, unsigned& minLocRef, unsigned& maxLocRef ) { rc_t rc = kOkRC; score_follower_t* p = _handleToPtr(h); - minLocRef = kInvalidId; - maxLocRef = kInvalidId; - - if( p->cwLocToCmLocA==nullptr || p->cwLocToCmLocN == 0 ) - { - rc = cwLogError(kInvalidStateRC,"The cw location range is not yet set."); - goto errLabel; - } - - minLocRef = 1; - maxLocRef = p->cwLocToCmLocN-1; + minLocRef = 0; + maxLocRef = loc_count(p->scoreH); - errLabel: return rc; } @@ -465,27 +448,32 @@ bool cw::score_follower::is_loc_in_range( handle_t h, unsigned loc ) return minLoc <= loc && loc <= maxLoc; } - +*/ unsigned cw::score_follower::has_stored_performance( handle_t h ) { score_follower_t* p = _handleToPtr(h); - return p->perf_idx > 0 && p->matcher->ri > 0; + return p->perf_idx > 0 && result_count(p->trackH) > 0; } cw::rc_t cw::score_follower::sync_perf_to_score( handle_t h ) { - rc_t rc = kOkRC; - score_follower_t* p = _handleToPtr(h); - + rc_t rc = kOkRC; + score_follower_t* p = _handleToPtr(h); + unsigned resultN = 0; + const sftrack::result_t* resultA = nullptr; + if( !has_stored_performance(h) ) { rc = cwLogError(kInvalidStateRC,"No performance to sync."); goto errLabel; } + + resultN = result_count(p->trackH); + resultA = result_base(p->trackH); - for(unsigned i=0; imatcher->ri; ++i) + for(unsigned i=0; imatcher->res[i].muid; + unsigned perf_idx = resultA[i].muid; // the matcher result 'muid' is the perf. array index of the matching perf. record if( perf_idx >= p->perf_idx ) @@ -495,26 +483,41 @@ cw::rc_t cw::score_follower::sync_perf_to_score( handle_t h ) } // verify the pitch of the matching records - if( p->perfA[ perf_idx ].pitch != p->matcher->res[i].pitch ) + if( p->perfA[ perf_idx ].pitch != resultA[i].pitch ) { - rc = cwLogError(kInvalidStateRC,"Inconsistent match to perf. map: pitch mismatch %i != %i.",p->perfA[ perf_idx ].pitch ,p->matcher->res[i].pitch); + rc = cwLogError(kInvalidStateRC,"Inconsistent match to perf. map: pitch mismatch %i != %i.",p->perfA[ perf_idx ].pitch ,resultA[i].pitch); goto errLabel; } - assert( p->matcher->res[i].scEvtIdx == kInvalidIdx || p->matcher->res[i].scEvtIdx < p->cmLocToCwLocN ); + //assert( resultA[i].scEvtIdx == kInvalidIdx || resultA[i].scEvtIdx < p->cmLocToCwLocN ); - if( p->matcher->res[i].scEvtIdx != kInvalidIdx ) - p->perfA[ perf_idx ].loc = p->cmLocToCwLocA[ p->matcher->res[i].scEvtIdx ]; + // if( resultA[i].scEvtIdx != kInvalidIdx ) + // p->perfA[ perf_idx ].loc = p->cmLocToCwLocA[ resultA[i].scEvtIdx ]; + + if( resultA[i].scEvtIdx != kInvalidIdx ) + p->perfA[ perf_idx ].loc = resultA[i].oLocId; } errLabel: return rc; } +unsigned cw::score_follower::track_result_count( handle_t h ) +{ + score_follower_t* p = _handleToPtr(h); + return result_count(p->trackH); +} + +const cw::sftrack::result_t* cw::score_follower::track_result( handle_t h ) +{ + score_follower_t* p = _handleToPtr(h); + return result_base(p->trackH); +} + unsigned cw::score_follower::perf_count( handle_t h ) { score_follower_t* p = _handleToPtr(h); - return p->perfN; + return p->perf_idx; } const cw::score_follower::ssf_note_on_t* cw::score_follower::perf_base( handle_t h ) @@ -527,13 +530,13 @@ cw::rc_t cw::score_follower::write_svg_file( handle_t h, const char* out_fname, { score_follower_t* p = _handleToPtr(h); - return svgScoreFollowWrite( p->cmScoreH, p->matcher, p->perfA, p->perf_idx, out_fname, show_muid_fl ); + return svgScoreFollowWrite( p->scoreH, p->trackH, p->perfA, p->perf_idx, out_fname, show_muid_fl ); } void cw::score_follower::score_report( handle_t h, const char* out_fname ) { score_follower_t* p = _handleToPtr(h); - cmScoreReport( p->cmCtx, p->cm_score_csv_fname, out_fname ); + report(p->scoreH,out_fname); } cw::rc_t cw::score_follower::midi_state_rt_report( handle_t h, const char* out_fname ) @@ -564,275 +567,4 @@ cw::rc_t cw::score_follower::midi_state_rt_report( handle_t h, const char* out_f } -namespace cw { - namespace score_follower { - typedef struct test_str - { - double srate; - char* out_dir; - const object_t* cfg; - const char* out_svg_fname; - const char* out_midi_csv_fname; - const char* out_cm_score_rpt_fname; - const object_t* perfL; - bool pre_test_fl; // insert a dummy note prior to the first perf. note to test the 'pre' functionality of the SVG generation - bool show_muid_fl; // true=show perf. note 'muid' in SVG file, false=show sequence id - } test_t; - - - rc_t _test_destroy( test_t* p ) - { - rc_t rc = kOkRC; - mem::release(p->out_dir); - - return rc; - } - - - rc_t _test_parse_cfg( test_t* p, const object_t* cfg ) - { - rc_t rc = kOkRC; - const char* out_dir = nullptr; - - // read the test cfg. - if((rc = cfg->getv("perfL", p->perfL, - "srate", p->srate, - "pre_test_fl", p->pre_test_fl, - "show_muid_fl", p->show_muid_fl, - "cfg", p->cfg, - "out_dir", out_dir, - "out_svg_fname",p->out_svg_fname, - "out_midi_csv_fname", p->out_midi_csv_fname, - "out_cm_score_rpt_fname", p->out_cm_score_rpt_fname)) != kOkRC ) - { - rc = cwLogError(rc,"Score follower test cfg. parse failed."); - goto errLabel; - } - - // expand the output directory - if((p->out_dir = filesys::expandPath( out_dir)) == nullptr ) - { - rc = cwLogError(kOpFailRC,"The output directory path expansion failed."); - goto errLabel; - } - - // create the output directory - if( !filesys::isDir(p->out_dir)) - { - if((rc = filesys::makeDir(p->out_dir)) != kOkRC ) - { - cwLogError(kOpFailRC,"The output directory '%s' could not be created.", cwStringNullGuard(p->out_dir)); - goto errLabel; - } - } - - - errLabel: - return rc; - } - - - rc_t _score_follow_one_perf( test_t* p, handle_t& sfH, unsigned perf_idx ) - { - rc_t rc = kOkRC; - bool pre_test_fl= p->pre_test_fl; - bool enable_fl = true; - const char* perf_label = nullptr; - const char* midi_fname = nullptr; - char* out_dir = nullptr; - char* fname = nullptr; - unsigned start_loc = 0; - const object_t* perf = nullptr; - unsigned msgN = 0; - midi::file::handle_t mfH; - const midi::file::trackMsg_t** msgA = nullptr; - - // get the perf. record - if((perf = p->perfL->child_ele(perf_idx)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Error accessing the cfg record for perf. record index:%i.",perf_idx); - goto errLabel; - } - - // parse the performance record - if((rc = perf->getv("label",perf_label, - "enable_fl",enable_fl, - "start_loc", start_loc, - "midi_fname", midi_fname)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"Error parsing cfg record for perf. record index:%i.",perf_idx); - goto errLabel; - } - - if( !enable_fl ) - goto errLabel; - - // create the output directory - if((out_dir = filesys::makeFn(p->out_dir,perf_label,nullptr,nullptr)) == nullptr ) - { - rc = cwLogError(kOpFailRC,"Directory name formation failed on '%s'.",cwStringNullGuard(out_dir)); - goto errLabel; - } - - // create the output directory - if((rc = filesys::makeDir(out_dir)) != kOkRC ) - { - rc = cwLogError(kOpFailRC,"mkdir failed on '%s'.",cwStringNullGuard(out_dir)); - goto errLabel; - } - - // expand the MIDI filename - if((fname = filesys::expandPath( midi_fname )) == nullptr ) - { - rc = cwLogError(kOpFailRC,"The MIDI file path expansion failed."); - goto errLabel; - } - - // open the midi file - if((rc = midi::file::open( mfH, fname )) != kOkRC ) - { - rc = cwLogError(rc,"MIDI file open failed on '%s'.",cwStringNullGuard(fname)); - goto errLabel; - } - - mem::release(fname); - - // set the score follower to the start location - if((rc = reset( sfH, start_loc)) != kOkRC ) - goto errLabel; - - - // get a pointer to a time sorted list of MIDI messages in the file - if(((msgN = msgCount( mfH )) == 0) || ((msgA = midi::file::msgArray( mfH )) == nullptr) ) - { - rc = cwLogError(rc,"MIDI file msg array is empty or corrupp->"); - goto errLabel; - } - - for(unsigned i=0; iamicro/1e6; - unsigned long smpIdx = (unsigned long)(p->srate * m->amicro/1e6); - bool newMatchFl = false; - - if( pre_test_fl ) - { - // test the 'pre' ref location by adding an extra note before the first note - exec(sfH, sec, smpIdx, m->uid-1, m->status, 60, m->u.chMsgPtr->d1, newMatchFl); - pre_test_fl = false; - } - - if((rc = exec(sfH, sec, smpIdx, m->uid, m->status, m->u.chMsgPtr->d0, m->u.chMsgPtr->d1, newMatchFl )) != kOkRC ) - { - rc = cwLogError(rc,"score follower exec failed."); - break; - } - - if( newMatchFl ) - { - unsigned matchIdN = 0; - const unsigned* matchIdA = current_match_id_array(sfH, matchIdN ); - - for(unsigned i=0; iout_svg_fname,nullptr,nullptr)) == nullptr ) - { - cwLogError(kOpFailRC,"The output SVG filename formation failed."); - goto errLabel; - } - - // write the score following result SVG - if((rc = write_svg_file(sfH,fname,p->show_muid_fl)) != kOkRC ) - { - rc = cwLogError(rc,"SVG report file create failed."); - goto errLabel; - } - - mem::release(fname); - - - // create the MIDI file as a CSV - if((fname = filesys::makeFn(out_dir,p->out_midi_csv_fname,nullptr,nullptr)) == nullptr ) - { - cwLogError(kOpFailRC,"The output MIDI CSV filename formation failed."); - goto errLabel; - } - - // convert the MIDI file to a CSV - if((rc = midi::file::genCsvFile( filename(mfH), fname, false)) != kOkRC ) - { - cwLogError(rc,"MIDI file to CSV failed on '%s'.",cwStringNullGuard(filename(mfH))); - } - - mem::release(fname); - - errLabel: - mem::release(out_dir); - mem::release(fname); - close(mfH); - - return rc; - } - - - } -} - -cw::rc_t cw::score_follower::test( const object_t* cfg ) -{ - rc_t rc = kOkRC; - - test_t t = {0}; - - cm::handle_t cmCtxH; - handle_t sfH; - char* fname = nullptr; - - // parse the test cfg - if((rc = _test_parse_cfg( &t, cfg )) != kOkRC ) - goto errLabel; - - // create a cm context record - if((rc = cm::create(cmCtxH)) != kOkRC ) - goto errLabel; - - // create the score follower - if((rc = create( sfH, t.cfg, cmCtxH, t.srate )) != kOkRC ) - goto errLabel; - - // create the cm score report filename - if((fname = filesys::makeFn(t.out_dir,t.out_cm_score_rpt_fname,nullptr,nullptr)) == nullptr ) - { - cwLogError(kOpFailRC,"The output cm score filename formation failed."); - goto errLabel; - } - - // write the cm score report - score_report(sfH,fname); - - // score follow each performance - for(unsigned perf_idx=0; perf_idxchild_count(); ++perf_idx) - _score_follow_one_perf(&t,sfH,perf_idx); - - - errLabel: - mem::release(fname); - destroy(sfH); - cm::destroy(cmCtxH); - _test_destroy(&t); - - return rc; -} diff --git a/cwScoreFollower.h b/cwScoreFollower.h index ff3e8ef..c8a5e62 100644 --- a/cwScoreFollower.h +++ b/cwScoreFollower.h @@ -6,8 +6,21 @@ namespace cw namespace score_follower { typedef handle< struct score_follower_str > handle_t; + + typedef struct + { + bool enableFl; + sfscore::handle_t scoreH; + unsigned scoreWndLocN; // size of the score search window + unsigned midiWndLocN; // count of MIDI locations to align inside the score search window + bool trackPrintFl; + bool trackResultsBacktrackFl; + + } args_t; - rc_t create( handle_t& hRef, const object_t* cfg, cm::handle_t cmCtxH, double srate ); + rc_t create( handle_t& hRef, const args_t& args ); + + rc_t create( handle_t& hRef, const object_t* cfg, double srate ); rc_t destroy( handle_t& hRef ); @@ -17,23 +30,35 @@ namespace cw rc_t reset( handle_t h, unsigned loc ); // If 'new_match_fl_ref' is returned as true then there are new match id's in the current_match_id_array[] - rc_t exec( handle_t h, double sec, unsigned smpIdx, unsigned muid, unsigned status, uint8_t d0, uint8_t d1, bool& new_match_fl_ref ); + rc_t exec( handle_t h, + double sec, + unsigned smpIdx, + unsigned muid, + unsigned status, + uint8_t d0, + uint8_t d1, + bool& new_match_fl_ref ); + + // Get the sftrack result_t indexes associated with the latest set of matches. + const unsigned* current_result_index_array( handle_t h, unsigned& cur_result_idx_array_cnt_ref ); - // Get a pointer to the event id's associated with the latest set of matches. - const unsigned* current_match_id_array( handle_t h, unsigned& cur_match_id_array_cnt_ref ); - - // Clear the match id array. This should be done to empty the current_match_id_array() - void clear_match_id_array( handle_t h ); + // Clear the result index array. This should be done to empty the current_result_index_array() + void clear_result_index_array( handle_t h ); // Get the min and max cw loc values for the current score. - rc_t cw_loc_range( handle_t h, unsigned& minLocRef, unsigned& maxLocRef ); - bool is_loc_in_range( handle_t h, unsigned loc ); + //rc_t cw_loc_range( handle_t h, unsigned& minLocRef, unsigned& maxLocRef ); + //bool is_loc_in_range( handle_t h, unsigned loc ); unsigned has_stored_performance( handle_t h ); // Set the 'loc' field on the stored performance from the stored score following info. rc_t sync_perf_to_score( handle_t h ); + + unsigned track_result_count( handle_t h ); + const sftrack::result_t* track_result( handle_t h ); + + // Return the count of stored performance records in the performance array. unsigned perf_count( handle_t h ); @@ -52,7 +77,6 @@ namespace cw // using midi_state::report_events(). rc_t midi_state_rt_report( handle_t h, const char* out_fname ); - rc_t test( const object_t* cfg ); } }