cwScoreFollower.h/cpp : Changed create() to use args_t record.
Replaced 'match_index_array' with 'result_index_array'. Moved test() to cwScoreFollowTest. Removed last parts of libcm dependencies. Moved Perf. Meas. unit out of callback.
This commit is contained in:
parent
fb8c99e0c4
commit
0193deb9e2
@ -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; i<cmEvtN; ++i)
|
||||
if((rc = dyn_ref_tbl::create( p->dynRefH, 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<unsigned>( p->cwLocToCmLocN );
|
||||
|
||||
cmEvtN = cmScoreEvtCount( p->cmScoreH );
|
||||
|
||||
for(unsigned i=0; i<cmEvtN; ++i)
|
||||
{
|
||||
const cmScoreEvt_t* cmEvt = cmScoreEvt( p->cmScoreH, 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<unsigned>( p->cmLocToCwLocN );
|
||||
|
||||
for(unsigned i=0; i<cmEvtN; ++i)
|
||||
{
|
||||
const cmScoreEvt_t* cmEvt = cmScoreEvt( p->cmScoreH, 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<perf_meas::kValCnt; ++i)
|
||||
v[i] = pmr.valueA[i] == std::numeric_limits<double>::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<score_follower_t>();
|
||||
|
||||
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<unsigned>(p->result_idx_allocN);
|
||||
|
||||
p->perfN = event_count(p->scoreH)*2;
|
||||
p->perfA = mem::allocZ<ssf_note_on_t>( 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<score_follower_t>();
|
||||
|
||||
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<unsigned>(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<unsigned>(p->result_idx_allocN);
|
||||
|
||||
p->perfN = cmScoreEvtCount(p->cmScoreH)*2;
|
||||
p->perfN = event_count(p->scoreH)*2;
|
||||
p->perfA = mem::allocZ<ssf_note_on_t>( 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; i<p->matcher->ri; ++i)
|
||||
for(unsigned i=0; i<resultN; ++i)
|
||||
{
|
||||
unsigned perf_idx = p->matcher->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; i<msgN; ++i)
|
||||
{
|
||||
const midi::file::trackMsg_t* m = msgA[i];
|
||||
|
||||
if( midi::file::isNoteOn( m ) )
|
||||
{
|
||||
double sec = (double)m->amicro/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; i<matchIdN; ++i)
|
||||
cwLogInfo("Match:%i",matchIdA[i]);
|
||||
|
||||
clear_match_id_array( sfH );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// create the SVG output filename
|
||||
if((fname = filesys::makeFn(out_dir,p->out_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_idx<t.perfL->child_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;
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user