//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwFile.h" #include "cwObject.h" #include "cwMidi.h" #include "cwDynRefTbl.h" #include "cwScoreParse.h" #include "cwSfScore.h" #include "cwPerfMeas.h" #include "cwVectOps.h" /* 1. if a performed event is part of a set then mark the set complete when all events in it have been performed. 2. when a set is marked as complete it should be evaluated immediately. 3. once a complete set is evaluated it is never evaluated again (although incomplete sets may be evaluated again) 4. if a performed location is marked as a 'calc' location and all sets have been evaluated then the calc. is executed. 5. if a performed location is past the next-calc-loc then the calc is executed. 6. if a calc has already been executed and an event that is part of the calc is recieved then the associated set is updated and the calc is executed again. 7. if a performed event occurs on a trigger location then the section update is executed. 8. if a performed event occurs on a trigger location that has already been executed then the section update is NOT performed again. */ namespace cw { namespace perf_meas { struct calc_str; typedef struct section_str { const struct section_str* prev_section; // section prior to this section const sfscore::section_t* section; // score section this section_t represents struct calc_str* calc; // calc record for this section bool triggeredFl; // This section has already been triggered } section_t; typedef struct meas_info_str { unsigned missEvtN; // count of skipped events unsigned missLocN; // count of missed locations unsigned transposeLocN; // count of transposed locations unsigned interpLocN; // count of interpolated locations unsigned vN; // vN=locN for even,tempo vN=evtN for dyn double* locSecV; // locSecV[vN] unsigned* locSecNV; // locSecNV[vN] count of performed and interpolated notes (interpolated notes will have a False in the associated statusV[] location) double* dSecV; // dSecV[vN] bool* statusV; // statusV[vN] - true if this location was performed by at least one note struct meas_info_str* link; } meas_info_t; struct loc_str; typedef struct set_str { struct loc_str* loc; // end location for this set const sfscore::set_t* set; // score set this set_t represents meas_info_t* meas_info; // unsigned lastPerfUpdateCnt; double value; // The value associated with this set. DBL_MAX on initialization struct calc_str* calc; // calc record to which this set belongs struct set_str* alink; // perf_meas_t* links struct set_str* slink; // loc_t.setL links struct set_str* clink; // calc_t.setL links } set_t; // The 'calc_t' record hold pointers to all the sets assigned to a given section. // The record is different from a section record because it has a location assignment // prior to the section which it is represents. This allows all the sets for // a given section to be evaluated prior to the section being triggered. // The 'calc' record is assigned to the location of the last event in it's last set. typedef struct calc_str { set_t* setL; // list of sets that this calc is applied to (links via set_t.clink) section_t* section; // section where this calc is applied double value[ kValCnt ]; // Aggregate var values for this section } calc_t; typedef struct loc_str { unsigned locId; // oloc location id and the index of this record into p->locA[] section_t* section; // section that begins on this location set_t* setL; // sets that end on this location (links via loc_t.slink) calc_t* calc; // calc that can be completed on this location. } loc_t; typedef struct perf_meas_str { params_t params; sfscore::handle_t scoreH; loc_t* locA; // locA[locN] unsigned locN; // length of locA[] set_t* setL; // sets linked on alink // Location index of the next section to be triggered. unsigned last_section_loc_idx; // Location of the next calc record to be evaluated. unsigned next_section_loc_idx; unsigned next_calc_loc_idx; } perf_meas_t; perf_meas_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } inline unsigned _loc_id_to_index( perf_meas_t* p, unsigned locId ) { if( locId >= p->locN ) return kInvalidIdx; assert( locId == p->locA[locId].locId ); return locId; } void _destroy_meas_info_records( set_t* set ) { meas_info_t* m = set->meas_info; while( m!=nullptr ) { meas_info_t* m0 = m->link; mem::release(m->locSecV); mem::release(m->locSecNV); mem::release(m->dSecV); mem::release(m->statusV); mem::release(m); m = m0; } } rc_t _destroy( perf_meas_t* p ) { rc_t rc = kOkRC; for(unsigned i=0; ilocN; ++i) { set_t* s = p->locA[i].setL; while(s!=nullptr) { set_t* s0 = s->slink; _destroy_meas_info_records(s); mem::release(s); s = s0; } mem::release(p->locA[i].section); mem::release(p->locA[i].calc); } mem::release(p->locA); mem::release(p); return rc; } set_t* _find_set( perf_meas_t* p, unsigned setId ) { for(set_t* s=p->setL; s!=nullptr; s=s->alink) if( s->set->id == setId ) return s; cwLogError(kInvalidIdRC,"The setId '%i' is not valid.", setId ); return nullptr; } meas_info_t* _create_meas_info_record( set_t* set ) { meas_info_t* m = mem::allocZ(); m->vN = set->set->varId==score_parse::kDynVarIdx ? set->set->evtCnt : set->set->locN; m->locSecV = mem::allocZ( m->vN ); m->locSecNV = mem::allocZ( m->vN ); m->dSecV = mem::allocZ( m->vN ); m->statusV = mem::allocZ( m->vN ); if( set->meas_info == nullptr ) set->meas_info = m; else { meas_info_t* m0 = set->meas_info; while( m0->link != nullptr ) m0 = m0->link; m0->link = m; } return m; } calc_t* _create_calc_record( perf_meas_t* p, sfscore::section_t* section ) { rc_t rc = kOkRC; calc_t* calc = mem::allocZ(); for(unsigned i=0; isetCnt; ++i) { set_t* s; if((s = _find_set(p,section->setArray[i]->id)) == nullptr ) { rc = cwLogError(kInvalidIdRC,"Error forming section set list for the section '%s'.",cwStringNullGuard(section->label)); goto errLabel; } s->calc = calc; s->clink = calc->setL; calc->setL = s; } errLabel: if( rc != kOkRC ) mem::release(calc); return calc; } void _advance_next_calc_and_section_indexes( perf_meas_t* p, unsigned cur_section_idx ) { p->last_section_loc_idx = cur_section_idx; p->next_calc_loc_idx = kInvalidIdx; p->next_section_loc_idx = kInvalidIdx; // Set 'last_section_loc_idx' and 'next_calc_loc_idx' for(unsigned i=cur_section_idx+1; ilocN && (p->next_section_loc_idx==kInvalidIdx || p->next_calc_loc_idx==kInvalidIdx); ++i) { if( p->next_section_loc_idx == kInvalidIdx && p->locA[i].section != nullptr ) p->next_section_loc_idx = i; if( p->next_calc_loc_idx == kInvalidIdx && p->locA[i].calc != nullptr ) p->next_calc_loc_idx = i; } if( p->next_section_loc_idx == kInvalidIdx ) cwLogInfo("End-of-score reached on section index."); else if( p->next_calc_loc_idx == kInvalidIdx ) cwLogWarning("No 'next calc index' was found for the section at location:%i",p->next_section_loc_idx); } void _reset_loc( loc_t& loc) { if( loc.section != nullptr ) loc.section->triggeredFl = false; if( loc.calc != nullptr ) { for(set_t* set = loc.calc->setL; set!=nullptr; set=set->clink) { set->value = std::numeric_limits::max(); set->lastPerfUpdateCnt = 0; } std::for_each(loc.calc->value,loc.calc->value+kValCnt,[](double& x){ x=std::numeric_limits::max(); }); } } rc_t _reset( perf_meas_t* p, unsigned init_locId ) { rc_t rc = kOkRC; unsigned i = 0; unsigned init_loc_idx = kInvalidIdx; for(; ilocN; ++i) { if( p->locA[i].locId == init_locId ) init_loc_idx = i; // reset all locations at and after the init. loc if( init_loc_idx != kInvalidIdx ) _reset_loc(p->locA[i]); } if( init_loc_idx == kInvalidIdx ) { rc = cwLogError(kInvalidIdRC,"The initial location id '%i' is not valid.",init_locId); goto errLabel; } _advance_next_calc_and_section_indexes(p,init_loc_idx); errLabel: return rc; } double _calc_set_list_mean( const set_t* setL, unsigned varTypeId ) { double sum = 0.0; unsigned n = 0; for(const set_t* s=setL; s!=nullptr; s=s->clink) if( s->set->varId == varTypeId && s->value != std::numeric_limits::max() ) { sum += s->value; n += 1; } return n>0 ? sum/n : 0.0; } void _eval_one_dynamic_set(perf_meas_t* p, set_t* set ) { double sum = 0; unsigned n = 0; for(unsigned i=0; iset->evtCnt; ++i) { const sfscore::event_t* e = set->set->evtArray[i]; if( e->perfFl ) { sum += e->dynLevel>e->perfDynLevel ? e->dynLevel - e->perfDynLevel : e->perfDynLevel - e->dynLevel; n += 1; } } set->value = n==0 ? 0 : sum/n; } // Calc chord onset time as the avg. onset over of all notes in the chord. rc_t _calc_loc_sec( set_t* pm_set ) { rc_t rc = kOkRC; const sfscore::set_t* set = pm_set->set; meas_info_t* m = pm_set->meas_info; m->missEvtN = 0; assert( set->locN == m->vN ); vop::zero(m->locSecV, set->locN); vop::zero(m->locSecNV, set->locN); // Get the time for each location by taking the mean // of all events on the same location. unsigned li = 0; unsigned curLocId = set->evtArray[0]->oLocId; for(unsigned i=0; ievtCnt; ++i) { if( !set->evtArray[i]->perfFl ) { ++m->missEvtN; } else { if( curLocId != set->evtArray[i]->oLocId ) { li += 1; curLocId = set->evtArray[i]->oLocId; } if( li >= set->locN ) { rc = cwLogError(kAssertFailRC,"An invalid location id was encountered %i >= %i.",li,set->locN); goto errLabel; } m->locSecV[ li] += set->evtArray[i]->perfSec; m->locSecNV[li] += 1; } } // Calc. mean. for(unsigned i=0; ilocN; ++i) if( m->locSecNV[i] > 0 ) m->locSecV[i] /= m->locSecNV[i]; errLabel: return rc; } void _interpolate_time_of_missing_notes( perf_meas_t* p, set_t* pm_set, unsigned& bi_ref, unsigned& ei_ref ) { bi_ref = kInvalidIdx; ei_ref = kInvalidIdx; const sfscore::set_t* set = pm_set->set; meas_info_t* m = pm_set->meas_info; unsigned missN = 0; unsigned ei = kInvalidIdx; unsigned bi = kInvalidIdx; assert( set->locN == m->vN ); vop::fill(m->statusV, set->locN, false); // for each location for(unsigned i=0; ilocN; ++i) { // if this location was missed if( m->locSecNV[i] == 0 ) { missN += 1; m->missLocN += 1; continue; } // if this location was not in time order if( ei != kInvalidIdx && (m->locSecV[i]-m->locSecV[ei])<0 ) { missN += 1; m->transposeLocN += 1; continue; } // if there are unplayed notes between this note // and the last played note at 'ei' then // fill in the missing note times by splitting // the gap time evenly - note that this will // bias the evenness std to be lower than it // should be. if( missN > 0 && ei!=kInvalidIdx ) { double dsec = (m->locSecV[i] - m->locSecV[ei])/(missN+1); for(unsigned j=ei+1; jlocSecV[j] = m->locSecV[j-1] + dsec; m->interpLocN += 1; } } if( bi == kInvalidIdx ) bi = i; m->statusV[i] = true; missN = 0; ei = i; // ei=last valid location in m->locSecV[] } bi_ref = bi; ei_ref = ei; //vop::print( m->locSecV, set->locN, "%f ", "locSecV:" ); //vop::print( m->locSecNV, set->locN, "%i ", "locSecN:" ); //vop::print( m->statusV, set->locN, "%i ", "ok:"); } rc_t _eval_one_even_set(perf_meas_t* p, set_t* pm_set ) { rc_t rc = kOkRC; const sfscore::set_t* set = pm_set->set; unsigned bi = kInvalidIdx; unsigned ei = kInvalidIdx; meas_info_t* m = pm_set->meas_info; assert( set->locN == m->vN ); if((rc = _calc_loc_sec(pm_set)) != kOkRC ) goto errLabel; _interpolate_time_of_missing_notes(p, pm_set, bi, ei ); vop::zero(m->dSecV, set->locN); // Calc the std. deviation of the note delta times // of all notes [bi:ei]. Note that if the notes // before bi/after ei were skipped then they are // left out of the calculation. However skipped notes // interal to the range bi:ei were interpolated // as perfectly even. if( (ei - bi)+1 > 2 ) { // calc the delta time for each time in locV[] unsigned stdN = 0; for(unsigned i=bi+1; i<=ei; ++i) m->dSecV[stdN++] = m->locSecV[i] - m->locSecV[i-1]; //printf("Even: skipped evt:%i skipped locs:%i interp:%i bi:%i ei:%i final N:%i\n",m->missEvtN,m->missLocN,m->interpLocN,bi,ei,stdN); //vop::print( m->dSecV, stdN, "%f ", "std:" ); pm_set->value = vop::std(m->dSecV, stdN ); } errLabel: return rc; } rc_t _eval_one_tempo_set(perf_meas_t* p, set_t* pm_set ) { rc_t rc = kOkRC; const sfscore::set_t* set = pm_set->set; meas_info_t* m = pm_set->meas_info; unsigned bi = kInvalidIdx; unsigned ei = kInvalidIdx; assert( set->locN == m->vN ); if((rc = _calc_loc_sec(pm_set)) != kOkRC ) goto errLabel; _interpolate_time_of_missing_notes(p, pm_set, bi, ei ); vop::zero(m->dSecV, set->locN); if( (ei - bi)+1 > 2 ) { // calc the delta time for each time in locV[] unsigned dsecN = 0; for(unsigned i=bi+1; i<=ei; ++i) m->dSecV[dsecN++] = m->locSecV[i] - m->locSecV[i-1]; //printf("Tempo: skipped evt:%i skipped locs:%i interp:%i bi:%i ei:%i final N:%i\n",m->missEvtN,m->missLocN,m->interpLocN,bi,ei,dsecN); //vop::print( m->dSecV, dsecN, "%f ", "dsecV:" ); double sec_per_beat = vop::mean(m->dSecV, dsecN ); double bpm = 60.0/sec_per_beat; pm_set->value = bpm; } errLabel: return rc; } void _aggregate_dynamic_meas_set_values( perf_meas_t* p, calc_t* calc ) { calc->value[ kDynValIdx ] = _calc_set_list_mean( calc->setL, score_parse::kDynVarIdx ); } void _aggregate_even_meas_set_values( perf_meas_t* p, calc_t* calc ) { calc->value[ kEvenValIdx ] = _calc_set_list_mean( calc->setL, score_parse::kEvenVarIdx ); } void _aggregate_tempo_meas_set_values( perf_meas_t* p, calc_t* calc ) { calc->value[ kTempoValIdx ] = _calc_set_list_mean( calc->setL, score_parse::kTempoVarIdx ); } void _aggregate_cost_meas_set_values( perf_meas_t* p, calc_t* calc ) { if( calc->section->prev_section != nullptr ) { unsigned beg_loc_idx = calc->section->prev_section->section->locPtr->index; unsigned end_loc_idx = calc->section->section->locPtr->index; const sfscore::loc_t* loc = loc_base(p->scoreH) + beg_loc_idx; const sfscore::loc_t* loc_end = loc_base(p->scoreH) + end_loc_idx; double sum = 0; unsigned n = 0; for(; locevtCnt; ++i) if( loc->evtArray[i]->perfFl && loc->evtArray[i]->perfMatchCost != std::numeric_limits::max() ) { sum += loc->evtArray[i]->perfMatchCost; n += 1; } calc->value[ kMatchCostValIdx ] = n==0 ? 0 : sum / n; } } set_t* _use_slink(set_t* s){ return s->slink; } set_t* _use_clink(set_t* s){ return s->clink; } // Set 'force_fl' to true if the sets should be evaluated even if they are not complete. void _eval_set_list( perf_meas_t* p, set_t* setL, set_t* (*link_func)(set_t*) ) { // for each set at this location for(set_t* s=setL; s!=nullptr; s=link_func(s)) { // if this set has not been eval'd or has been updated with new perf. information since it was last eval'd if( s->lastPerfUpdateCnt==0 || s->set->perfUpdateCnt > s->lastPerfUpdateCnt ) { s->lastPerfUpdateCnt = s->set->perfUpdateCnt; if( p->params.print_rt_events_fl ) cwLogInfo("Set %i eval.",s->set->id); switch( s->set->varId ) { case score_parse::kDynVarIdx: _eval_one_dynamic_set(p,s); break; case score_parse::kEvenVarIdx: _eval_one_even_set(p,s); break; case score_parse::kTempoVarIdx: _eval_one_tempo_set(p,s); break; default: cwLogError(kInvalidIdRC,"Unknown var type (%i) encountered while evaluating sets.",s->set->varId ); assert(0); } } } } rc_t _update_sets( perf_meas_t* p, unsigned loc_idx ) { rc_t rc = kOkRC; if( p->locA[loc_idx].setL != nullptr && are_all_loc_set_events_performed( p->scoreH, loc_idx ) ) _eval_set_list(p,p->locA[loc_idx].setL,_use_slink); return rc; } rc_t _update_calc( perf_meas_t* p, unsigned loc_idx ) { rc_t rc = kOkRC; // if the loc is past the next_calc_loc_idx or is equal to next_calc_loc_idx and all the set events have been performed for the location if( p->next_calc_loc_idx != kInvalidIdx && (loc_idx > p->next_calc_loc_idx || (loc_idx == p->next_calc_loc_idx && are_all_loc_set_events_performed( p->scoreH, loc_idx )))) { if( p->params.print_rt_events_fl ) cwLogInfo("Calc: Loc:%i nci:%i",loc_idx,p->next_calc_loc_idx ); calc_t* calc = p->locA[ p->next_calc_loc_idx ].calc; assert( calc != nullptr ); // eval any sets that have not already been evaluated - even if they are not complete _eval_set_list(p,calc->setL, _use_clink); // aggregate the measurements from each measurement type into a single scalar values. _aggregate_dynamic_meas_set_values(p,calc); _aggregate_even_meas_set_values(p,calc); _aggregate_tempo_meas_set_values(p,calc); _aggregate_cost_meas_set_values(p,calc); } return rc; } rc_t _update_section( perf_meas_t* p, unsigned loc_idx, result_t& resultRef ) { rc_t rc = kOkRC; // if next_section_loc_idx == kInvalidIdx then the end of the score has been encountered // (or we are in an invalid state) if( p->next_section_loc_idx == kInvalidIdx ) { cwLogWarning("End-of-score or invalid state encountered (next_section_loc_idx==kInvalidIdx)."); goto errLabel; } // TODO: Should the following be a loop which iterates // until p->next_section_loc_idx > loc_idx if( p->next_section_loc_idx <= loc_idx ) { assert( p->locA[p->next_section_loc_idx].section != nullptr ); section_t* section = p->locA[p->next_section_loc_idx].section; if( section->triggeredFl == false ) { assert( section->section != nullptr && section->section->locPtr != nullptr ); resultRef.loc = loc_idx; resultRef.sectionLoc = section->section->locPtr->index; resultRef.sectionLabel = section->section->label; if( section->calc == nullptr ) cwLogWarning("No sets assigned to section %s",cwStringNullGuard(section->section->label)); else { resultRef.valueA = section->calc->value; resultRef.valueN = kValCnt; } section->triggeredFl = true; _advance_next_calc_and_section_indexes(p,p->next_section_loc_idx); //cwLogInfo("Section %s triggered.",cwStringNullGuard(resultRef.sectionLabel)); } } errLabel: return rc; } template< typename T > rc_t _write_result_vector(file::handle_t fH, section_t* section, set_t* set, const char* opLabel, const char* fmt, const T* vect, unsigned locN) { rc_t rc; if((rc = file::printf(fH,"%s,%i,%s,%s,%i,", section->section->label, set==nullptr ? -1 : set->set->id, set==nullptr ? "" : score_parse::var_index_to_char(set->set->varId), opLabel, locN)) != kOkRC ) { rc = cwLogError(rc,"Perf. measure vector header write failed."); goto errLabel; } for(const T* v=vect; v::max() ? -1.0 : x; } } } cw::rc_t cw::perf_meas::create( handle_t& hRef, sfscore::handle_t scoreH, const params_t& params ) { rc_t rc; if((rc = destroy(hRef)) != kOkRC ) return rc; perf_meas_t* p = mem::allocZ(); const sfscore::loc_t* locA = sfscore::loc_base(scoreH); const section_t* prev_section = nullptr; p->scoreH = scoreH; p->params = params; p->locN = loc_count(scoreH); p->locA = mem::allocZ(p->locN); p->next_section_loc_idx = kInvalidIdx; p->next_calc_loc_idx = kInvalidIdx; // for each score location for(unsigned i=0; ilocN; ++i) { // the index of the record into p->locA[] // is the same as the locId of the associated location assert( i == locA[i].index ); p->locA[i].locId = locA[i].index; // if this location is the end of a set (or sets) ... for(sfscore::set_t* s=locA[i].setList; s!=nullptr; s=s->llink) { // ... link the sets onto this location record set_t* set = mem::allocZ(); set->loc = p->locA + i; set->set = s; set->slink = p->locA[i].setL; p->locA[i].setL = set; set->alink = p->setL; p->setL = set; _create_meas_info_record(set); } // if this location is the start of a new section if( locA[i].begSectPtr != nullptr ) { p->locA[i].section = mem::allocZ(); p->locA[i].section->prev_section = prev_section; p->locA[i].section->section = locA[i].begSectPtr; prev_section = p->locA[i].section; if(locA[i].begSectPtr->measLocPtr != nullptr ) { // get the loc of the last event in the last set that applies to this section // (this is where the sets that supply this section will be evaluated) unsigned calc_loc_idx = locA[i].begSectPtr->measLocPtr->index; assert( calc_loc_idx < p->locN); // verify that the 'calc' location is <= the section location if( calc_loc_idx > locA[i].index ) { rc = cwLogError(kInvalidStateRC,"The last loc %i of the last set in section '%s' is after the section start at loc: %i.", calc_loc_idx, cwStringNullGuard(locA[i].begSectPtr->label),i); goto errLabel; } // verify that that the calc location is available if( p->locA[ calc_loc_idx ].calc != nullptr ) { rc = cwLogError(kInvalidStateRC,"A given location (%i) may only have one 'calc' record.",calc_loc_idx); goto errLabel; } // create the 'calc' record preceding this section if(( p->locA[ calc_loc_idx ].calc = _create_calc_record( p, locA[i].begSectPtr )) == nullptr ) { rc = cwLogError(kInvalidIdRC,"The 'calc' object create failed at location %i",calc_loc_idx); goto errLabel; } // set the calc.section pointer and the section.calc pointer p->locA[ calc_loc_idx ].calc->section = p->locA[i].section; p->locA[i].section->calc = p->locA[ calc_loc_idx ].calc; } } } if((rc = _reset(p,0)) != kOkRC ) { rc = cwLogError(rc,"Perf. meas initial reset failed."); goto errLabel; } hRef.set(p); errLabel: if(rc != kOkRC ) _destroy(p); return rc; } cw::rc_t cw::perf_meas::destroy( handle_t& hRef ) { rc_t rc = kOkRC; if( !hRef.isValid() ) return rc; perf_meas_t* p = _handleToPtr(hRef); if((rc = _destroy(p)) != kOkRC ) { rc = cwLogError(rc,"Destroy failed."); return rc; } hRef.clear(); return rc; } cw::rc_t cw::perf_meas::reset( handle_t h, unsigned init_locId ) { perf_meas_t* p = _handleToPtr(h); return _reset(p,init_locId); } cw::rc_t cw::perf_meas::exec( handle_t h, const sfscore::event_t* event, result_t& resultRef ) { rc_t rc = kOkRC; perf_meas_t* p = _handleToPtr(h); unsigned loc_idx; resultRef = {}; resultRef.loc = kInvalidIdx; resultRef.sectionLoc = kInvalidIdx; // get the index of the location record associated with this event if((loc_idx = _loc_id_to_index(p,event->oLocId )) == kInvalidIdx ) { cwLogError(kInvalidIdRC,"The event loc %i is not valid.",event->oLocId); goto errLabel; } // if this event is prior to the last triggered section then ignore it if( p->last_section_loc_idx != kInvalidIdx && loc_idx < p->last_section_loc_idx ) { cwLogWarning("Backtrack before last triggered section loc:%i < last section:%i.",loc_idx,p->last_section_loc_idx); goto errLabel; } // if this location is attached to a set then eval the set if it is complete if((rc = _update_sets(p,loc_idx)) != kOkRC ) { rc = cwLogError(rc,"Set update failed."); goto errLabel; } // if this location is at or after 'next_calc_loc_idx' then evaluate the calc record if((rc = _update_calc(p,loc_idx)) != kOkRC ) { rc = cwLogError(rc,"Calc. update failed."); goto errLabel; } // if this location is at or after the next section then update the section from the calc. if((rc = _update_section(p,loc_idx,resultRef)) != kOkRC ) { rc = cwLogError(rc,"Section update failed."); goto errLabel; } errLabel: if( rc != kOkRC ) rc = cwLogError(rc,"Perf-meas exec failed."); return rc; } void cw::perf_meas::report( handle_t h ) { perf_meas_t* p = _handleToPtr(h); for(unsigned i=0; ilocN; ++i) { loc_t* loc = p->locA + i; bool fl = loc->section || loc->setL || loc->calc; if( fl ) printf("%i : ",loc->locId ); if( loc->section ) { printf("section:%s ", loc->section->section->label ); } set_t* s; if( loc->setL != nullptr ) { printf("set: "); for(s=loc->setL; s!=nullptr; s=s->slink) printf("%i ", s->set->id); } if( loc->calc != nullptr ) { printf("calc: "); for(s=loc->calc->setL; s!=nullptr; s=s->clink) printf("%i ", s->set->id); } if(fl) printf("\n"); } } cw::rc_t cw::perf_meas::write_result_json( handle_t h, const char* player_name, const char* perf_date, unsigned perf_take_numb, const char* out_fname ) { rc_t rc = kOkRC; perf_meas_t* p = _handleToPtr(h); object_t* root = newDictObject(); object_t* sectionL = root->insert_pair("sectionL",newListObject()); for(loc_t* loc=p->locA; loclocA+p->locN; ++loc) if( loc->section != nullptr && loc->section->calc != nullptr && loc->section->triggeredFl ) { object_t* sect = newDictObject(sectionL); const double* valueV = loc->section->calc->value; object_t* setL = newListObject(); sect->putv("label",loc->section->section->label, "player_name",player_name, "perf_date",perf_date, "perf_take_numb",perf_take_numb, "locId",loc->locId, "dyn",dbl_max_to_neg_one(valueV[kDynValIdx]), "even",dbl_max_to_neg_one(valueV[kEvenValIdx]), "tempo",dbl_max_to_neg_one(valueV[kTempoValIdx]), "cost",dbl_max_to_neg_one(valueV[kMatchCostValIdx]), "setL",setL); for(set_t* set=loc->section->calc->setL; set!=nullptr; set=set->clink) { object_t* set_o = newDictObject(setL); set_o->putv("type",score_parse::var_index_to_char(set->set->varId), "setId",set->set->id, "locId",set->loc->locId, "value",dbl_max_to_neg_one(set->value), "missEvtN",set->meas_info->missEvtN, "missLocN",set->meas_info->missLocN, "transposeLocN",set->meas_info->transposeLocN, "interpLocN",set->meas_info->interpLocN); set_o->put_numeric_list("locSecV", set->meas_info->locSecV, set->meas_info->vN ); set_o->put_numeric_list("locSecNV", set->meas_info->locSecNV, set->meas_info->vN ); set_o->put_numeric_list("dSecV", set->meas_info->dSecV, set->meas_info->vN ); set_o->put_numeric_list("statusV", set->meas_info->statusV, set->meas_info->vN ); } } if((rc = objectToFile(out_fname,root)) != kOkRC ) { rc = cwLogError(rc,"Perf. measurement JSON file write failed."); goto errLabel; } errLabel: root->free(); return rc; } cw::rc_t cw::perf_meas::write_result_csv( handle_t h, const char* out_fname ) { rc_t rc = kOkRC; perf_meas_t* p = _handleToPtr(h); file::handle_t fH; if((rc = file::open(fH,out_fname, file::kWriteFl)) != kOkRC ) { rc = cwLogError(rc,"Perf. measure result file create failed on '%s'.",cwStringNullGuard(out_fname)); goto errLabel; } if((rc = file::printf(fH,"section,set,type,op,value,missEvtN,missLocN,transposeLocN,interpLocN\n")) != kOkRC ) { rc = cwLogError(rc,"Perf. measure title write failed."); goto errLabel; } for(loc_t* loc=p->locA; loclocA+p->locN; ++loc) if( loc->section != nullptr && loc->section->calc != nullptr ) { if((rc = _write_result_vector(fH,loc->section,nullptr,"section","%f,",loc->section->calc->value,kValCnt)) != kOkRC ) { rc = cwLogError(rc,"Perf. measure file write result section header failed."); goto errLabel; } for(set_t* set=loc->section->calc->setL; set!=nullptr; set=set->clink) { if((rc = file::printf(fH,"%s,%i,%s,summary,%f,%i,%i,%i,%i\n", loc->section->section->label, set->set->id, score_parse::var_index_to_char(set->set->varId), set->value != std::numeric_limits::max() ? set->value : 0, set->meas_info->missEvtN, set->meas_info->missLocN, set->meas_info->transposeLocN, set->meas_info->interpLocN)) != kOkRC ) { rc = cwLogError(rc,"Perf. measure file write result failed."); goto errLabel; } if((rc = _write_result_vector(fH,loc->section,set,"locSecV","%f,",set->meas_info->locSecV,set->set->locN)) != kOkRC ) { rc = cwLogError(rc,"Perf.measure locSecV[] write result failed."); goto errLabel; } if((rc = _write_result_vector(fH,loc->section,set,"locSecNV","%i,",set->meas_info->locSecNV,set->set->locN)) != kOkRC ) { rc = cwLogError(rc,"Perf.measure locSecNV[] write result failed."); goto errLabel; } if((rc = _write_result_vector(fH,loc->section,set,"dSecV","%f,", set->meas_info->dSecV,set->set->locN)) != kOkRC ) { rc = cwLogError(rc,"Perf.measure dSecV[] write result failed."); goto errLabel; } if((rc = _write_result_vector(fH,loc->section,set,"statusV","%i,",set->meas_info->statusV,set->set->locN)) != kOkRC ) { rc = cwLogError(rc,"Perf.measure status[] write result failed."); goto errLabel; } } } if((rc = file::close(fH)) != kOkRC ) { rc = cwLogError(rc,"Perf. meas. file close failed."); goto errLabel; } errLabel: return rc; }