From 16ba4d6a3f44afb4e35074e2412438feaeb32c96 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 12 Sep 2023 17:56:50 -0400 Subject: [PATCH] cwSfScore.h/cpp : Added initial performance update functionality. Changed location identifiers to consistently use oLocId and eLocId. Added many fields to event_t. --- cwSfScore.cpp | 706 ++++++++++++++++++++++++++++++++++++++++++-------- cwSfScore.h | 108 ++++---- 2 files changed, 655 insertions(+), 159 deletions(-) diff --git a/cwSfScore.cpp b/cwSfScore.cpp index fc908f9..6efbe6a 100644 --- a/cwSfScore.cpp +++ b/cwSfScore.cpp @@ -47,7 +47,6 @@ namespace cw event_t* event; loc_t* loc; section_t* section; - set_t* setA[ score_parse::kVarCnt ]; } rpt_event_t; @@ -57,7 +56,7 @@ namespace cw void _destroy_set( set_t* s ) { - mem::release(s->eleArray); + mem::release(s->evtArray); mem::release(s->sectArray); mem::release(s); } @@ -79,7 +78,7 @@ namespace cw for(unsigned i=0; isetN; ++i) { - mem::release(p->setA[i].eleArray); + mem::release(p->setA[i].evtArray); mem::release(p->setA[i].sectArray); } mem::release(p->setA); @@ -87,6 +86,10 @@ namespace cw for(unsigned i=0; isectionN; ++i) _destroy_section( p->sectionA + i ); mem::release(p->sectionA); + + + for(unsigned i=0; ieventN; ++i) + mem::release(p->eventA[i].varA); mem::release(p->eventA); @@ -138,41 +141,53 @@ namespace cw for(unsigned i=0; ieventAllocN; ++i) { - event_t* e = p->eventA + p->eventN; + event_t* e = p->eventA + p->eventN; const score_parse::event_t* pe = pe_array + i; if( cwIsFlag(pe->flags,score_parse::kOnsetFl) ) { - e->type = pe->opId; - e->secs = pe->sec; - e->index = p->eventN; - e->locIdx = pe->oloc; - e->pitch = pe->d0; - e->vel = pe->d1; - e->flags = 0; - e->dynVal = pe->dynLevel; - e->frac = _calc_frac(pe->rval, pe->dotCnt); - e->barNumb = pe->barNumb; - e->barNoteIdx = pe->barPitchIdx; - e->csvRowNumb = pe->csvRowNumb; - e->line = pe->csvRowNumb; - e->csvEventId = kInvalidId; // pe->csvId; - e->hash = pe->hash; + e->type = pe->opId; + e->secs = pe->sec; + e->index = p->eventN; + e->oLocId = pe->oLocId; + e->pitch = pe->d0; + e->vel = pe->d1; + e->flags = pe->flags; + e->dynLevel = pe->dynLevel; + e->frac = _calc_frac(pe->rval, pe->dotCnt); + e->barNumb = pe->barNumb; + e->barNoteIdx = pe->barPitchIdx; + e->csvRowNumb = pe->csvRowNumb; + e->line = pe->csvRowNumb; + e->parseEvtIdx = pe->index; + e->hash = pe->hash; + e->bpm = pe->bpm; + e->bpm_rval = pe->bpm_rval; - for(unsigned i = score_parse::kMinVarIdx; ivarA[i] = pe->varA[i].flags; - e->flags |= pe->varA[i].flags; + e->varN = std::count_if( pe->varA, pe->varA + score_parse::kVarCnt, [](const score_parse::event_var_t& x){ return x.flags!=0; }); + e->varA = mem::allocZ(e->varN); + + for(unsigned k = score_parse::kMinVarIdx,j=0; kvarN; ++k) + { + if( pe->varA[k].flags != 0 ) + { + assert( k == pe->varA[k].set->varTypeId ); + + e->varA[j].flags = pe->varA[k].flags; + e->varA[j].varId = pe->varA[k].set->varTypeId; + e->flags |= pe->varA[k].flags; + ++j; + } } - if( e->locIdx > p->locN ) - p->locN = e->locIdx; + if( e->oLocId > p->locN ) + p->locN = e->oLocId; p->eventN += 1; } } - p->locN += 1; // add one to convert locN from index to count + p->locN += 1; // add one to convert locN from index to count cwLogInfo("%i locations.",p->locN); @@ -182,10 +197,10 @@ namespace cw rc_t _create_loc_array( sfscore_t* p ) { - rc_t rc = kOkRC; - unsigned ebi = 0; + rc_t rc = kOkRC; + unsigned ebi = 0; - if( p->locN == 0) + if( p->locN == 0) { rc = cwLogError(kInvalidStateRC,"No locations were found."); goto errLabel; @@ -193,42 +208,39 @@ namespace cw p->locA = mem::allocZ(p->locN); - for(unsigned i=0; ieventN; ++i) + for(unsigned i = 0; ieventN; ++i) { const event_t* e = p->eventA + i; - if( e->locIdx != p->eventA[ebi].locIdx || i==p->eventN-1 ) + if( e->oLocId != p->eventA[ebi].oLocId || i == p->eventN-1 ) { - unsigned locIdx = p->eventA[ebi].locIdx; + unsigned oLocId = p->eventA[ebi].oLocId; - assert( locIdx < p->locN); + assert( oLocId < p->locN); - loc_t* loc = p->locA + locIdx; - loc->index = p->eventA[ebi].locIdx; + loc_t* loc = p->locA + oLocId; + loc->index = p->eventA[ebi].oLocId; loc->secs = p->eventA[ebi].secs; loc->barNumb = p->eventA[ebi].barNumb; - loc->evtCnt = (i - ebi) + (i==p->eventN-1 ? 1 : 0); + loc->evtCnt = (i - ebi) + (i == p->eventN-1 ? 1 : 0); loc->evtArray = mem::allocZ( loc->evtCnt ); - - for(unsigned j=0; jevtCnt; ++j) + + for(unsigned j = 0; jevtCnt; ++j) { assert( ebi + j < p->eventN ); loc->evtArray[j] = p->eventA + (ebi+j); } ebi = i; - } - + } } - - errLabel: return rc; } rc_t _create_section_array( sfscore_t* p ) { - rc_t rc = kOkRC; + rc_t rc = kOkRC; p->sectionN = score_parse::section_count(p->parserH); // the location array must have already been created. @@ -244,12 +256,25 @@ namespace cw p->sectionA = mem::allocZ(p->sectionN); const score_parse::section_t* ps = score_parse::section_list(p->parserH); - for(unsigned i=0; isectionN; ++i) + for(unsigned i = 0; isectionN; ++i) { if( ps->begEvent != nullptr ) { - section_t* section = p->sectionA + i; - event_t* begEvt = _hash_to_event(p,ps->begEvent->hash); + section_t* section = p->sectionA + i; + unsigned beg_evt_idx = ps->begEvent->index; + unsigned end_evt_idx = ps->endEvent->index; + const score_parse::event_t* eventA = event_array( p->parserH ); + unsigned eventN = event_count( p->parserH ); + event_t* begEvt = nullptr; + + // advance to the first onset event + for(unsigned i = beg_evt_idx; i<=end_evt_idx && ilabel = mem::duplStr(ps->label); section->index = i; section->begEvtIndex = begEvt->index; - section->locPtr = p->locA + p->eventA[begEvt->index].locIdx; + section->locPtr = p->locA + p->eventA[begEvt->index].oLocId; section->locPtr->begSectPtr = section; - for(unsigned j=0; jvars[j] = DBL_MAX; + //for(unsigned j = 0; jvars[j] = DBL_MAX; } ps = ps->link; @@ -277,7 +302,7 @@ namespace cw section_t* _label_to_section( sfscore_t* p, const char* label ) { - for(unsigned i=0; isectionN; ++i) + for(unsigned i = 0; isectionN; ++i) if( textIsEqual(p->sectionA[i].label,label) ) return p->sectionA + i; @@ -286,10 +311,10 @@ namespace cw rc_t _create_set_array( sfscore_t* p ) { - rc_t rc = kOkRC; + rc_t rc = kOkRC; const score_parse::set_t* ps = set_list(p->parserH); - p->setN = score_parse::set_count(p->parserH); + p->setN = score_parse::set_count(p->parserH); if( p->setN == 0 ) { rc = cwLogError(kInvalidStateRC,"No sets were found."); @@ -300,38 +325,72 @@ namespace cw p->setA = mem::allocZ( p->setN ); // for each set - for(unsigned i=0; isetN; ++i,ps=ps->link) + for(unsigned i = 0; isetN; ++i,ps=ps->link) { assert(ps != nullptr); - section_t* section = nullptr; - set_t* set = p->setA + i; + + section_t* section = nullptr; + set_t* set = p->setA + i; + unsigned evtIdx0 = kInvalidIdx; + unsigned oLocId0 = kInvalidIdx; + + set->id = ps->id; + set->varId = ps->varTypeId; - set->id = ps->id; - set->varId = ps->varTypeId; - - // fill in the events belonging to this list - set->eleCnt = ps->eventN; - set->eleArray = mem::allocZ(set->eleCnt); - for(unsigned j=0; jeleCnt; ++j) + // fill in the events belonging to this set + set->evtCnt = ps->eventN; + set->evtArray = mem::allocZ(set->evtCnt); + for(unsigned j=0; jevtCnt; ++j) { - set->eleArray[j] = _hash_to_event(p, ps->eventA[j]->hash ); - - if( set->eleArray[j] == nullptr ) + event_t* e = nullptr; + unsigned k = 0; + + // locate the jth event + if((e = _hash_to_event(p, ps->eventA[j]->hash )) == nullptr ) { rc = cwLogError(kInvalidStateRC,"The '%s' set event in measure:%i with hash %x (CSV Row:%i) could not be found.",score_parse::var_index_to_char(ps->varTypeId),ps->eventA[j]->barNumb,ps->eventA[j]->hash,ps->eventA[j]->csvRowNumb); goto errLabel; } + + // the set events must be in time order + if( evtIdx0!=kInvalidIdx && e->index < evtIdx0 ) + { + rc = cwLogError(kInvalidStateRC,"The '%s' set event in measure:%i with hash %x (CSV Row:%i) is out of time order.",score_parse::var_index_to_char(ps->varTypeId),ps->eventA[j]->barNumb,ps->eventA[j]->hash,ps->eventA[j]->csvRowNumb); + goto errLabel; + } + evtIdx0 = e->index; + + // Track the count of locations used by this set. + if( oLocId0==kInvalidIdx || e->oLocId != oLocId0 ) + set->locN += 1; + oLocId0 = e->oLocId; + + set->evtArray[j] = e; + + // set the set pointer on this event to point back to this set + for(k=0; kvarN; ++k) + if( e->varA[k].varId == set->varId ) + { + e->varA[k].set = set; + break; + } + + if( k == e->varN ) + { + rc = cwLogError(kInvalidStateRC,"The event set slots at location '%i' (CSV row:%i) was not found for var type:%i.",e->oLocId,e->csvRowNumb,set->varId); + goto errLabel; + } } - + // add this set to the setList for the set's end loc - if( set->eleCnt > 0 ) + if( set->evtCnt > 0 ) { - loc_t* end_loc = p->locA + set->eleArray[set->eleCnt-1]->locIdx; + loc_t* end_loc = p->locA + set->evtArray[set->evtCnt-1]->oLocId; set->llink = end_loc->setList; end_loc->setList = set; } - // set the target-section related fields fro this set + // set the target-section related fields for this set if( ps->targetSection != nullptr ) { if((section = _label_to_section(p,ps->targetSection->label)) == nullptr ) @@ -347,6 +406,16 @@ namespace cw section->setCnt += 1; section->setArray = mem::resizeZ(section->setArray,section->setCnt); section->setArray[ section->setCnt-1 ] = set; + + + if( set->evtCnt>0 ) + { + // track the location of the last event in the last set that is applied to this section + unsigned oLocId = set->evtArray[ set->evtCnt-1 ]->oLocId; + + if( section->measLocPtr == nullptr || oLocId > section->measLocPtr->index ) + section->measLocPtr = p->locA + oLocId; + } } } @@ -355,9 +424,327 @@ namespace cw return rc; } + rc_t _set_tempo( sfscore_t* p ) + { + rc_t rc = kOkRC; + const score_parse::event_t* eventA = event_array(p->parserH); + unsigned eventN = event_count(p->parserH); + + // Get the min BPM + auto min_evt = std::min_element(eventA,eventA+eventN,[](auto& x0, auto& x1){ return x0.bpmbpm,min_evt->csvRowNumb); + + if( min_evt->bpm == 0 ) + { + cwLogError(kInvalidArgRC,"The minimum tempo must be greater than zero."); + goto errLabel; + } + + // Set event.relTempo + std::for_each(p->eventA, p->eventA+p->eventN, [min_evt](auto& x){ x.relTempo=(double)x.bpm / min_evt->bpm; } ); + + errLabel: + return rc; + } + + rc_t _validate_dyn_set( sfscore_t* p, const set_t* set ) + { + rc_t rc = kOkRC; + for(event_t* const * ee=set->evtArray; eeevtArray+set->evtCnt; ++ee) + { + assert( ee != nullptr && *ee != nullptr ); + const event_t* e = *ee; + if( e->dynLevel == kInvalidIdx ) + rc = cwLogError(kInvalidArgRC,"No dynamic level has been assigned to the note (%) at score loc:%i CSV row:%i.",e->oLocId,e->csvRowNumb); + } + return rc; + } + + // Given a list of note events calc the standard deviation of the inter-onset time between the notes. + rc_t _calc_delta_time_std_dev( unsigned locN, event_t* const* evtA, unsigned evtN, double& stdRef ) + { + rc_t rc = kOkRC; + + assert( locN > 1 ); + + double locSecV[ locN ]; + unsigned locCntV[ locN ]; + unsigned evtIdxV[ evtN ]; + + vop::fill(locSecV,locN,0.0); + vop::fill(locCntV,locN,0); + vop::fill(evtIdxV,evtN,0); + + unsigned li = 0; + for(unsigned ei=0; ei0 && evtA[ei]->secs != evtA[ei-1]->secs ) + ++li; + + locSecV[li] += evtA[ei]->secs; + locCntV[li] += 1; + evtIdxV[ei] = li; + } + + for(unsigned li=0; lievtCnt; ++i) + { + const event_t* e = set->evtArray[i]; + + double dsec = -1; + if( i>0 && e->oLocId != set->evtArray[i-1]->oLocId ) + dsec = e->secs - set->evtArray[i-1]->secs; + + printf("%3i loc:%5i d:%6.3f f:%f %s\n", + e->barNumb, + e->oLocId, + dsec, + e->frac, + score_parse::event_array(p->parserH)[ e->parseEvtIdx ].sciPitch ); + } + } + + rc_t _validate_even_set( sfscore_t* p, const set_t* set, bool show_warnings_fl ) + { + rc_t rc; + double std = 0; + + if( set->locN < 3 ) + { + rc = cwLogError(kInvalidArgRC,"The even set id %i has less than 3 locations.",set->id); + goto errLabel; + } + + if((rc = _calc_delta_time_std_dev(set->locN,set->evtArray,set->evtCnt,std)) != kOkRC ) + { + cwLogError(rc,"Even set score time validation failed."); + goto errLabel; + } + + if( std > 0.05 && show_warnings_fl ) + { + printf("Even set periodcity out of range. set:%3i %3i : std:%6.4f\n",set->id,set->evtCnt,std); + _print_even_set(p,set); + } + errLabel: + return rc; + } + + rc_t _calc_score_tempo( const set_t* set ) + { + rc_t rc = kOkRC; + + // Both of these assertions should have been previously verified + // by the score validation process. + assert( set->locN >= 2 ); + assert( set->evtCnt >= 0 ); + + bool printFl = false; //set->evtArray[0]->barNumb == 272; + + double locSecV[ set->locN ]; + unsigned locCntV[ set->locN ]; + double locFracV[ set->locN ]; + double bpmV[ set->locN-1 ]; + double bpm = 0.0; + + vop::fill(locSecV,set->locN,0); + vop::fill(locCntV,set->locN,0); + vop::fill(locFracV,set->locN,0); + + + // Calc the oneset time at each location - this involves taking the mean time of all notes that that location. + // Notes + // 1. For the score this step is not necessary because all notes will fall on exactly the same time. + // 2. It might be better to take the median rather than the mean to prevent outlier problems, + unsigned cur_loc_idx = set->evtArray[0]->oLocId; + unsigned li = 0; + for(unsigned i=0; ievtCnt; ++i) + { + if( set->evtArray[i]->oLocId != cur_loc_idx ) + { + cur_loc_idx = set->evtArray[i]->oLocId; + ++li; + } + + assert( li < set->locN); + + locSecV[ li ] += set->evtArray[i]->secs; + locCntV[ li ] += 1; + + //if( locFracV[li]!=0 && set->evtArray[i]->frac != locFracV[li] ) + // cwLogWarning("Frac mismatch."); + + locFracV[ li ] = set->evtArray[i]->bpm_rval; + } + + // Convert onset time sum to avg. + for(unsigned i=0; ilocN; ++i) + if( locCntV[i] != 0 ) + locSecV[i] /= locCntV[i]; + + // Calc the BPM between each two notes in the sequence. + for(unsigned i=1; ilocN; ++i) + { + double d = locSecV[i] - locSecV[i-1]; + double secs_per_beat = d; + bpmV[i-1] = 60.0/(secs_per_beat * locFracV[i-1]); + + // bpm = 60 / (spb*x) + // bpm/(60) + // 60/(bpm*sbp) = x + double fact = 60/(set->evtArray[0]->bpm * secs_per_beat); + double est_bpm = 60.0/(secs_per_beat * fact); + + if( printFl ) + printf("%3i : %f : %i d:%f frac:%f spb:%f fact:%f bpm:%f %f\n", + set->id, + locSecV[i-1], + locCntV[i-1],d, + locFracV[i-1], + secs_per_beat, + fact, + est_bpm, + bpmV[i-1]); + + } + + // take the avg bpm as the + unsigned bpmN = 0; + for(unsigned i=0; ilocN-1; ++i) + if( bpmV[i] > 0 ) + { + bpm += bpmV[i]; + bpmN += 1; + } + + if( bpmN > 0 ) + bpm /= bpmN; + + if( printFl ) + printf("meas:%i locN:%i BPM:%i est:%f\n",set->evtArray[0]->barNumb,set->locN,set->evtArray[0]->bpm,bpm); + + return rc; + } + + rc_t _validate_tempo_set( sfscore_t* p, const set_t* set ) + { + rc_t rc = kOkRC; + + if( set->locN < 2 ) + { + rc = cwLogError(kInvalidArgRC,"The tempo set id %i has less than 2 locations.",set->id); + goto errLabel; + } + + if( set->evtCnt > 0 ) + { + // all events in a tempo set must share the same tempo marking + unsigned bpm = set->evtArray[0]->bpm; + + // Note we do not check the tempo of the last event (i.e. ievtCnt-1) because it may + // land on a tempo change. We therefore must take the tempo of the first event as the tempo + // for all successive notes. + + for(unsigned i=1; ievtCnt-1; ++i) + if(set->evtArray[i]->bpm != bpm ) + { + rc = cwLogError(kInvalidStateRC,"Tempo mismatch at tempo event loc:%i (CSV row:%i) in set %i",set->evtArray[i]->oLocId, set->evtArray[i]->csvRowNumb, set->id ); + goto errLabel; + } + + + _calc_score_tempo( set ); + } + + errLabel: + return rc; + } + + rc_t _validate_sets( sfscore_t* p, bool show_warnings_fl ) + { + rc_t rc = kOkRC; + + for(const set_t* set = p->setA; setsetA + p->setN; ++set ) + { + rc_t rc0 = kOkRC; + + if( set->evtCnt==0 || set->evtArray[0]==nullptr ) + { + rc = cwLogError(kInvalidStateRC, "Set id %i of type %s has no events.",cwStringNullGuard(score_parse::var_index_to_char(set->varId))); + + } + else + { + switch( set->varId ) + { + case score_parse::kDynVarIdx: + rc0 = _validate_dyn_set(p,set); + break; + + case score_parse::kEvenVarIdx: + rc0 = _validate_even_set(p,set,show_warnings_fl); + break; + + case score_parse::kTempoVarIdx: + rc0 = _validate_tempo_set(p,set); + break; + } + + if( rc0 != kOkRC ) + { + cwLogError(rc0,"Validation failed on set id %i of type %s. The set starts at loc:%i (CSV row:%i).", + set->id, + cwStringNullGuard(score_parse::var_index_to_char(set->varId)), + set->evtArray[0]->oLocId, + set->evtArray[0]->csvRowNumb); + rc = rc0; + } + } + } + + return rc; + } + rc_t _create( handle_t& hRef, score_parse::handle_t spH, + bool show_warnings_fl, bool deleteParserH_Fl ) { rc_t rc; @@ -382,6 +769,11 @@ namespace cw if((rc = _create_set_array( p )) != kOkRC ) goto errLabel; + if((rc = _set_tempo(p)) != kOkRC ) + goto errLabel; + + if((rc = _validate_sets(p,show_warnings_fl)) != kOkRC ) + goto errLabel; hRef.set(p); @@ -403,8 +795,8 @@ namespace cw const char* sec_str = "S:"; const char* bar_str = "B:"; - printf("e idx oloc secs op sectn sdx bar bdx scip vel frac\n"); - printf("----- ----- ------- --- ------ --- ----- --- ---- --- -----\n"); + printf("e idx oloc secs bpm b_rval rtmpo op sectn sdx bar bdx scip vel frac g\n"); + printf("----- ----- ------- --- ------ ----- --- ------ --- ----- --- ---- --- ----- -\n"); for(rpt_event_t* r=rptA; rbarNumb; sec0 = section->label; - printf("%5i %5i %7.3f %3s %2s%4s %3i %2s%3i %3i %4s %3i %5.3f ", + printf("%5i %5i %7.3f %3i %6.4f %5.3f %3s %2s%4s %3i %2s%3i %3i %4s %3i %5.3f %c ", e->index, - e->locIdx, + e->oLocId, e->secs, + e->bpm, + e->bpm_rval, + e->relTempo, score_parse::opcode_id_to_label(e->type), d_sec_str, section==nullptr ? " " : cwStringNullGuard(section->label), @@ -434,17 +829,22 @@ namespace cw e->barNoteIdx, sciPitch, e->vel, - e->frac ); + e->frac, + cwIsFlag(e->flags,score_parse::kGraceFl) ? 'g' : ' '); - for(unsigned vi=score_parse::kMinVarIdx; visetA[vi]; - if( set == nullptr || set->sectCnt==0 ) + // locate the associated var spec in event.varA[] + var_t* var = std::find_if( e->varA, e->varA+e->varN, [vi](const var_t& x){return x.varId==vi;}); + + // if this event is not a included in a set of type 'vi' + if( var >= e->varA+e->varN ) printf(" "); else { - const char* sect_label = set->sectArray[0]==nullptr ? "****" : set->sectArray[0]->label; - printf("%s-%03i-%s ",score_parse::var_flags_to_char(e->varA[vi]), set->id, sect_label); + const char* sect_label = var->set->sectArray[0]==nullptr ? "****" : var->set->sectArray[0]->label; + printf("%s-%03i-%s ",score_parse::var_flags_to_char(var->flags), var->set->id, sect_label); } } @@ -485,19 +885,7 @@ namespace cw } } - - for(unsigned i=0; isetN; ++i) - { - set_t* set = p->setA + i; - for(unsigned j=0; jeleCnt; ++j) - { - event_t* e = set->eleArray[j]; - rpt_event_t* r = rptA + e->index; - assert( r->setA[ set->varId ] == nullptr ); - r->setA[ set->varId ] = set; - } - } - + return rptA; } @@ -513,7 +901,6 @@ namespace cw _report_print(p,rptA,p->eventN); mem::release(rptA); } - return rc; } @@ -521,16 +908,17 @@ namespace cw } cw::rc_t cw::sfscore::create( handle_t& hRef, - score_parse::handle_t spH ) + score_parse::handle_t spH, + bool show_warnings_fl) { - return _create(hRef,spH,false); - + return _create(hRef,spH,show_warnings_fl,false); } cw::rc_t cw::sfscore::create( handle_t& hRef, const char* fname, double srate, - dyn_ref_tbl::handle_t dynRefH) + dyn_ref_tbl::handle_t dynRefH, + bool show_warnings_fl) { rc_t rc; @@ -541,9 +929,8 @@ cw::rc_t cw::sfscore::create( handle_t& hRef, rc = cwLogError(rc,"sfscore parse failed."); goto errLabel; } - - rc = _create(hRef,spH,true); + rc = _create(hRef,spH,show_warnings_fl,true); errLabel: return rc; @@ -570,6 +957,79 @@ cw::rc_t cw::sfscore::destroy( handle_t& hRef ) return rc; } +void cw::sfscore::clear_all_performance_data( handle_t h ) +{ + sfscore_t* p = _handleToPtr(h); + std::for_each( p->eventA, p->eventA + p->eventN, [](event_t& e){ e.perfFl=false; e.perfVel=0, e.perfSec=0, e.perfMatchCost=std::numeric_limits::max(); } ); + std::for_each( p->setA, p->setA + p->setN, [](set_t& s){ s.perfEventCnt=0; s.perfUpdateCnt=false; } ); +} + +cw::rc_t cw::sfscore::set_perf( handle_t h, unsigned event_idx, double secs, uint8_t pitch, uint8_t vel, double cost ) +{ + rc_t rc = kOkRC; + sfscore_t* p = _handleToPtr(h); + event_t* e = p->eventA + event_idx;; + + if( event_idx >= p->eventN ) + { + rc = cwLogError(kInvalidIdRC,"The performance event index %i is invalid.",event_idx); + goto errLabel; + } + + if( e->pitch != pitch ) + { + rc = cwLogError(kInvalidStateRC,"The performance event pitch %x is not a match.",pitch); + goto errLabel; + } + + for(unsigned i=0; ivarN; ++i) + { + e->varA[i].set->perfUpdateCnt += 1; + + if( e->perfFl == false ) + { + e->varA[i].set->perfEventCnt += 1; + + if( e->varA[i].set->perfEventCnt > e->varA[i].set->evtCnt ) + { + rc = cwLogError(kInvalidStateRC,"The perf. count of a set (id:%i) exeeded it's event count.", e->varA[i].set->evtCnt ); + goto errLabel; + } + } + } + + e->perfFl = true; + e->perfVel = vel; + e->perfSec = secs; + e->perfDynLevel = dyn_ref_vel_to_level(p->parserH,vel); + e->perfMatchCost= cost; + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"The performance score update failed."); + + return rc; +} + +bool cw::sfscore::are_all_loc_set_events_performed( handle_t h, unsigned locId ) +{ + sfscore_t* p = _handleToPtr(h); + + if( locId >= p->locN ) + { + cwLogError(kInvalidIdRC,"An invalid loc id %i was encountered while testing for performed events.",locId); + assert(0); + return false; + } + + const loc_t* loc = p->locA + locId; + for(unsigned i=0; ievtCnt; ++i) + if( loc->evtArray[i]->varN > 0 && loc->evtArray[i]->perfFl == false) + return false; + + return true; +} + double cw::sfscore::sample_rate( handle_t& h ) { @@ -577,21 +1037,26 @@ double cw::sfscore::sample_rate( handle_t& h ) return sample_rate(p->parserH); } - - unsigned cw::sfscore::event_count( handle_t h ) { sfscore_t* p = _handleToPtr(h); return p->eventN; } -cw::sfscore::event_t* cw::sfscore::event( handle_t h, unsigned idx ) +const cw::sfscore::event_t* cw::sfscore::event( handle_t h, unsigned idx ) { sfscore_t* p = _handleToPtr(h); + + if( idx > p->eventN ) + { + cwLogError(kInvalidIdRC,"The event index '%i' is not valid.",idx); + return nullptr; + } + return p->eventA + idx; } -cw::sfscore::event_t* cw::sfscore::hash_to_event( handle_t h, unsigned hash ) +const cw::sfscore::event_t* cw::sfscore::hash_to_event( handle_t h, unsigned hash ) { sfscore_t* p = _handleToPtr(h); for(unsigned i=0; p->eventN; ++i) @@ -600,17 +1065,40 @@ cw::sfscore::event_t* cw::sfscore::hash_to_event( handle_t h, unsigned hash ) return nullptr; } - unsigned cw::sfscore::loc_count( handle_t h ) { sfscore_t* p = _handleToPtr(h); return p->locN; } -cw::sfscore::loc_t* cw::sfscore::loc( handle_t h, unsigned idx ) +const cw::sfscore::loc_t* cw::sfscore::loc_base( handle_t h ) { sfscore_t* p = _handleToPtr(h); - return p->locA + idx; + return p->locA; +} + +unsigned cw::sfscore::set_count( handle_t h ) +{ + sfscore_t* p = _handleToPtr(h); + return p->setN; +} + +const cw::sfscore::set_t* cw::sfscore::set_base( handle_t h ) +{ + sfscore_t* p = _handleToPtr(h); + return p->setA; +} + +unsigned cw::sfscore::section_count( handle_t h ) +{ + sfscore_t* p = _handleToPtr(h); + return p->sectionN; +} + +const cw::sfscore::section_t* cw::sfscore::section_base( handle_t h ) +{ + sfscore_t* p = _handleToPtr(h); + return p->sectionA; } void cw::sfscore::report( handle_t h, const char* out_fname ) @@ -620,9 +1108,3 @@ void cw::sfscore::report( handle_t h, const char* out_fname ) _report(p); } -void cw::sfscore::parse_report( handle_t h ) -{ - sfscore_t* p = _handleToPtr(h); - report(p->parserH); -} - diff --git a/cwSfScore.h b/cwSfScore.h index 772ac0f..cf3479a 100644 --- a/cwSfScore.h +++ b/cwSfScore.h @@ -14,35 +14,54 @@ namespace cw { const char* label; // section label unsigned index; // index of this record in the internal section array + struct loc_str* measLocPtr; // last location of last set to be applied to this section struct loc_str* locPtr; // location where this section starts unsigned begEvtIndex; // score element index where this section starts unsigned setCnt; // Count of elements in setArray[] struct set_str** setArray; // Ptrs to sets which are applied to this section. - double vars[ score_parse::kVarCnt ]; // Set to DBL_MAX by default. + + //double vars[ score_parse::kVarCnt ]; // Set to DBL_MAX by default. } section_t; + typedef struct var_str + { + unsigned flags; + unsigned varId; + struct set_str* set; + } var_t; + typedef struct event_str { unsigned type; // See score_parse ???TId double secs; // Time location in seconds double durSecs; // Duration in seconds unsigned index; // Index of this event in the event array. - unsigned locIdx; // Index of the onset location (oloc) containing this event + unsigned oLocId; // Index of the onset location (oloc) containing this event midi::byte_t pitch; // MIDI pitch of this note or the MIDI pedal id of pedal down/up msg (64=sustain 65=sostenuto 66=soft) midi::byte_t vel; // MIDI velocity of this note unsigned flags; // Attribute flags for this event - unsigned dynVal; // Dynamcis value pppp to ffff (1 to 11) for this note. + unsigned dynLevel; // Dynamcis value pppp to ffff (1 to 11) for this note. double frac; // Note's time value for tempo and non-grace evenness notes. unsigned barNumb; // Bar id of the measure containing this event. unsigned barNoteIdx; // Index of this note in this bar unsigned csvRowNumb; // File row number (not index) from which this record originated - unsigned perfSmpIdx; // Time this event was performed or cmInvalidIdx if the event was not performed. - unsigned perfVel; // Velocity of the performed note or 0 if the note was not performed. - unsigned perfDynLvl; // Index into dynamic level ref. array assoc'd with perfVel unsigned line; // Line number of this event in the score file. - unsigned csvEventId; // EventId from CSV 'evt' column. + unsigned parseEvtIdx; // Index of event from score_parse event index. unsigned hash; // unique hash id for this note - unsigned varA[ score_parse::kVarCnt ]; + + var_t* varA; // varA[varN] set's this event belongs to + unsigned varN; // Length of varA[] + + unsigned bpm; // beats per minute + double bpm_rval; + double relTempo; // relative tempo (1=min tempo) + + bool perfFl; // has this event been performed + double perfSec; // performance event time + uint8_t perfVel; // performance event velocity + unsigned perfDynLevel; // performance dynamic level + double perfMatchCost; // performance match cost (or DBL_MAX if not valid) + } event_t; // A 'set' is a collection of events that are grouped in time and all marked with a given attribute. @@ -50,50 +69,29 @@ namespace cw typedef struct set_str { unsigned id; // Unique id for this set - unsigned varId; // See kXXXVarScId flags above - event_t** eleArray; // Events that make up this set in time order - unsigned eleCnt; // + unsigned varId; // See score_parse::k???VarIdx + event_t** evtArray; // Events that make up this set in time order + unsigned evtCnt; // + unsigned locN; // count of locations coverted by this set section_t** sectArray; // Sections this set will be applied to unsigned sectCnt; // - unsigned* symArray; // symArray[sectCnt] - symbol name of all variables represented by this set (e.g '1a-e', '1b-e', '2-t', etc) - unsigned* costSymArray; // costSymArray[sectCnt] - same as symbols in symArray[] with 'c' prepended to front - bool doneFl; - double value; struct set_str* llink; // loc_t setList link + + unsigned perfEventCnt; // count of events in this set that have been performed (never greater than eleCnt) + unsigned perfUpdateCnt; // count of event updates this has received (incr'd every time a event is performed - may be greater than eleCnt) } set_t; - - typedef enum - { - kInvalidScMId, - kRecdBegScMId, - kRecdEndScMId, - kFadeScMId, - kPlayBegScMId, - kPlayEndScMId - } markerId_t; - - // score markers - typedef struct marker_str - { - markerId_t markTypeId; // marker type - unsigned labelSymId; // marker label - struct loc_str* scoreLocPtr; // score location of the marker - unsigned csvRowIdx; // score CSV file line assoc'd w/ this marker - struct marker_str* link; // loc_t.markList links - } marker_t; - + // All events which are simultaneous are collected into a single // locc_t record. typedef struct loc_str - { - unsigned index; // index of this location record + { + unsigned index; // oloc id and index of this location record double secs; // Time of this location unsigned evtCnt; // Count of events in evtArray[]. event_t** evtArray; // Events which occur at this time. unsigned barNumb; // Bar number this event is contained by. set_t* setList; // Set's which end on this time location (linked through set_t.llink) section_t* begSectPtr; // NULL if this location does not start a section - marker_t* markList; // List of markers assigned to this location } loc_t; typedef dyn_ref_tbl::dyn_ref_t dyn_ref_t; @@ -101,29 +99,45 @@ namespace cw // Create the score from a provided score parser rc_t create( handle_t& h, - score_parse::handle_t spH ); + score_parse::handle_t spH, + bool show_warnings_fl = false); // Create an internal score parser. rc_t create( handle_t& h, const char* fname, double srate, - dyn_ref_tbl::handle_t dynRefH); + dyn_ref_tbl::handle_t dynRefH, + bool show_warnings_fl = false); rc_t destroy( handle_t& h ); + + void clear_all_performance_data( handle_t h ); + rc_t set_perf( handle_t h, unsigned event_idx, double secs, uint8_t pitch, uint8_t vel, double cost ); + + // Return true if all events that are assigned to a set performed at the given location. + bool are_all_loc_set_events_performed( handle_t h, unsigned locId ); + + double sample_rate( handle_t& h ); - unsigned event_count( handle_t h ); - event_t* event( handle_t h, unsigned idx ); - event_t* hash_to_event( handle_t h, unsigned hash ); + unsigned event_count( handle_t h ); + const event_t* event( handle_t h, unsigned idx ); + + const event_t* hash_to_event( handle_t h, unsigned hash ); - unsigned loc_count( handle_t h ); - loc_t* loc( handle_t h, unsigned idx ); + unsigned loc_count( handle_t h ); + const loc_t* loc_base( handle_t h ); + + unsigned set_count( handle_t h ); + const set_t* set_base( handle_t h ); + + unsigned section_count( handle_t h ); + const section_t* section_base( handle_t h ); void report( handle_t h, const char* out_fname=nullptr ); - void parse_report( handle_t h ); // see score_test::test() for testing this object