diff --git a/cmProc4.c b/cmProc4.c index aca2f0a..a1c2dd1 100644 --- a/cmProc4.c +++ b/cmProc4.c @@ -1246,11 +1246,11 @@ cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmRea p->loc[ei+i].evtV = cmMemAllocZ(cmScAlignScEvt_t,n); p->loc[ei+i].scLocIdx = li; p->loc[ei+i].barNumb = lp->barNumb; + for(j=0,k=0; jevtCnt; ++j) if( lp->evtArray[j]->type == kNonEvtScId ) { p->loc[ei+i].evtV[k].pitch = lp->evtArray[j]->pitch; - p->loc[ei+i].evtV[k].scEvtIdx = lp->evtArray[j]->index; ++k; } @@ -1311,6 +1311,8 @@ cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmRea //_cmScAlignPrint(p); + cmScAlignReset(p,0); + return rc; } @@ -1329,7 +1331,9 @@ void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx ) p->mbi = p->mn; p->mni = 0; p->begScanLocIdx = begScanLocIdx; + p->begSyncLocIdx = cmInvalidIdx; p->s_opt = DBL_MAX; + p->esi = cmInvalidIdx; p->missCnt = 0; p->scanCnt = 0; p->ri = 0; @@ -1391,8 +1395,9 @@ bool _cmScAlignCalcMtx( cmScAlign* p ) return false; unsigned i,j; - for(i=1; irn; ++i) - for(j=1; jcn; ++j) + + for(j=1; jcn; ++j) + for(i=1; irn; ++i) { cmScAlignLoc_t* loc = p->loc + p->begScanLocIdx + j - 1; unsigned pitch = p->midiBuf[i-1].pitch; @@ -1585,26 +1590,28 @@ double _cmScAlign( cmScAlign* p ) return p->s_opt; } -bool cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ) +cmRC_t cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ) { bool fl = p->mbi > 0; - bool retFl = true; + cmRC_t rc = cmOkRC; + // update the MIDI buffer with the incoming note cmScAlignInputMidi(p,smpIdx,status,d0,d1); + // if the MIDI buffer transitioned to full then perform an initial scan sync. if( fl && p->mbi == 0 ) { - if( cmScAlignScan(p,cmInvalidCnt) == cmInvalidIdx ) - retFl = false; + if( (p->begSyncLocIdx = cmScAlignScan(p,cmInvalidCnt)) == cmInvalidIdx ) + rc = cmInvalidArgRC; // signal init. scan sync. fail } else { - if( !fl && p->mbi == 0 ) - if( !cmScAlignStep(p) ) - retFl = false; + // if the MIDI buffer is full then perform a step sync. + if( !fl && p->mbi == 0 ) + rc = cmScAlignStep(p); } - return retFl; + return rc; } bool cmScAlignInputMidi( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ) @@ -1922,7 +1929,7 @@ unsigned cmScAlignScan( cmScAlign* p, unsigned scanCnt ) } -bool cmScAlignStep( cmScAlign* p ) +cmRC_t cmScAlignStep( cmScAlign* p ) { int i; unsigned pitch = p->midiBuf[ p->mn-1 ].pitch; @@ -1930,11 +1937,11 @@ bool cmScAlignStep( cmScAlign* p ) // the tracker must be sync'd to step if( p->esi == cmInvalidIdx ) - return false; - + return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The p->esi value must be valid to perform a step operation."); + // if the end of the score has been reached if( p->esi + 1 >= p->locN ) - return cmInvalidIdx; + return cmEofRC; // attempt to match to next location first if( _cmScAlignIsMatch(p->loc + p->esi + 1, pitch) ) @@ -1982,13 +1989,13 @@ bool cmScAlignStep( cmScAlign* p ) // if the scan failed find a match if( bsi == cmInvalidIdx ) - return false; + return cmCtxRtCondition( &p->obj, cmSubSysFailRC, "Scan resync. failed."); //if( bsi != cmInvalidIdx ) // _cmScAlignPrintPath(p, p->p_opt, bsi ); } - return true; + return cmOkRC; } @@ -2227,7 +2234,26 @@ void _cmScAlignPrintReport( cmScAlign* p, cmScAlignPrint_t* a, unsigned an, unsi } -void _cmScAlignPrintResult( cmScAlign* p ) +// The goal of this function is to create a cmScAlignPrint_t array containing +// one record for each score bar, score note and errant MIDI note. +// The function works by first creating a record for each score bar and note +// and then scanning the cmScAlignResult_t array (p->res[]) for each result +// record create by an earlier call to _cmScAlignCb(). A result record can +// uniquely indicates one of the following result states based on receiving +// a MIDI event. +// Match - locIdx!=cmInvalidIdx matchFl==true mni!=cmInvalidIdx +// Mis-match - locIdx!=cmInvalidIdx matchFl==false mni!=cmInvalidIdx +// Delete - locIdx==cmInvalidIdx matchFl==false mni!=cmInvalidIdx +// Insert - locIdx==cmInvalidIdx matchFl==false mni==cmInvalidIdx +// +// This is made slightly more complicated by the fact that a given MIDI event +// may generate more than one result record. This can occur when the +// tracker is in 'step' mode and generates a result record with a given state +// as a result of a given MIDI note and then reconsiders that MIDI note +// while during a subsequent 'scan' mode resync. operation. For example +// a MIDI note which generate a 'delete' result during a step operation +// may later generate a match result during a scan. +double _cmScAlignPrintResult( cmScAlign* p ) { // determine the scH score begin and end indexes unsigned bsi = cmScoreLocCount(p->scH); @@ -2256,9 +2282,10 @@ void _cmScAlignPrintResult( cmScAlign* p ) cmScAlignPrint_t* a = cmMemAllocZ(cmScAlignPrint_t,aan); unsigned an = 0; - unsigned scNoteCnt = 0; + unsigned scNoteCnt = 0; // notes in the score unsigned matchCnt = 0; // matched score notes - unsigned wrongCnt = 0; // unmatched midi notes + unsigned wrongCnt = 0; // errant midi notes + unsigned skipCnt = 0; // skipped score events // create a record for each score event for(i=bsi; i<=esi; ++i) @@ -2364,11 +2391,14 @@ void _cmScAlignPrintResult( cmScAlign* p ) //_cmScAlignPrintList(a,an); + // Insert records into the print record array (a[]( + // to represent errant MIDI notes. (Notes which + // were played but do not match any notes in the score.) + // for each result record ... for(i=0; iri; ++i) { cmScAlignResult_t* rp = p->res + i; - unsigned scLocIdx = cmInvalidIdx; cmScAlignPrint_t* pp = NULL; cmScAlignPrint_t* dpp = NULL; unsigned dmin; @@ -2377,6 +2407,7 @@ void _cmScAlignPrintResult( cmScAlign* p ) if(rp->foundFl) continue; + // find the print recd with the closest mni for(j=0; jmni > dpp->mni ) ++j; - - scLocIdx = dpp->scLocIdx; + assert( rp->locIdx == cmInvalidIdx ); + + // insert a print recd before or after the closest print recd an = _cmScAlignPrintExpand(a,aan,j,an); - - if( rp->locIdx != cmInvalidIdx ) - { - assert( rp->locIdx != cmInvalidIdx && rp->locIdx < p->locN ); - scLocIdx = p->loc[ rp->locIdx ].scLocIdx; - } + _cmScAlignPrintSet(a + j, rp, kMidiErrSaFl, dpp->scLocIdx ); ++wrongCnt; - - _cmScAlignPrintSet(a + j, rp, kMidiErrSaFl, scLocIdx ); } + for(i=0; imni,p->scanCnt,scNoteCnt,matchCnt,skipCnt,wrongCnt,fmeas); + + cmMemFree(a); + + return fmeas; } -unsigned cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx ) + +cmRC_t cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx ) { assert( top != NULL ); - cmTlMidiEvt_t* mep = NULL; + cmTlMidiEvt_t* mep = NULL; + cmRC_t rc = cmOkRC; - // get the next time MIDI msg - while( (mep = cmTlNextMidiEvtObjPtr(tlH, top, top->seqId )) != NULL ) + // as long as more MIDI events are available get the next MIDI msg + while( rc==cmOkRC && (mep = cmTlNextMidiEvtObjPtr(tlH, top, top->seqId )) != NULL ) { top = &mep->obj; @@ -2441,13 +2481,31 @@ unsigned cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* top // if the time line MIDI msg a note-on if( mep->msg->status == kNoteOnMdId ) - if( !cmScAlignExec(p, mep->obj.seqSmpIdx, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1 ) ) - return cmInvalidIdx; + { + rc = cmScAlignExec(p, mep->obj.seqSmpIdx, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1 ); + + switch( rc ) + { + case cmOkRC: // continue processing MIDI events + break; + + case cmEofRC: // end of the score was encountered + break; + + case cmInvalidArgRC: // p->esi was not set correctly + break; + + case cmSubSysFailRC: // scan resync failed + break; + } + } } - return p->esi; -} + if( rc == cmEofRC ) + rc = cmOkRC; + return rc; +} void cmScAlignCb( void* cbArg, unsigned scLocIdx, unsigned mni, unsigned pitch, unsigned vel ) { @@ -2467,6 +2525,12 @@ void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH ) unsigned markCharCnt = 31; cmChar_t markText[ markCharCnt+1 ]; + double scoreThresh = 0.5; + unsigned candCnt = 0; + unsigned initFailCnt = 0; + unsigned otherFailCnt = 0; + unsigned scoreFailCnt = 0; + p->cbArg = p; // set the callback arg. // for each marker @@ -2479,43 +2543,79 @@ void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH ) cmTlMarker_t* mp = cmTimeLineMarkerFind( tlH, markText ); if( mp == NULL ) { - printf("The marker '%s' was not found.\n",markText); + printf("The marker '%s' was not found.\n\n",markText); continue; } // skip markers which do not contain text if( cmTextIsEmpty(mp->text) ) { - printf("The marker '%s' is being skipped because it has no text.\n",markText); + printf("The marker '%s' is being skipped because it has no text.\n\n",markText); continue; } // reset the score follower to the beginnig of the score cmScAlignReset(p,0); - //printf("%s %5.2f %5.2f %5.2f\n",markText,(double)(mp->obj.begSmpIdx)/p->srate,(double)(mp->obj.durSmpCnt)/p->srate,(double)(mp->obj.begSmpIdx+mp->obj.durSmpCnt)/p->srate); + ++candCnt; // scan to the beginning of the marker - unsigned bsi = cmScAlignScanToTimeLineEvent(p,tlH,&mp->obj,mp->obj.seqSmpIdx+mp->obj.durSmpCnt); - - if( bsi != cmInvalidIdx ) + cmRC_t rc = cmScAlignScanToTimeLineEvent(p,tlH,&mp->obj,mp->obj.seqSmpIdx+mp->obj.durSmpCnt); + bool pfl = true; + + if( rc != cmOkRC || p->begSyncLocIdx==cmInvalidIdx) + { + if( p->begSyncLocIdx == cmInvalidIdx ) + rc = cmInvalidArgRC; + + if( p->mni == 0 ) + { + printf("mark:%i midi:%i Not enough MIDI notes to fill the scan buffer.\n",i,p->mni); + pfl = false; + } + else + { + switch(rc) + { + case cmInvalidArgRC: + printf("mark:%i INITIAL SYNC FAIL\n",i); + ++initFailCnt; + pfl = false; + break; + + case cmSubSysFailRC: + printf("mark:%i SCAN RESYNC FAIL\n",i); + ++otherFailCnt; + break; + + default: + printf("mark:%i UNKNOWN FAIL\n",i); + ++otherFailCnt; + } + } + } + + if( pfl ) { //_cmScAlignPrintMtx(p); - printf("mark:%i scans:%4i loc:%4i bar:%4i score:%5.2f miss:%i text:'%s'\n",i,p->scanCnt,bsi,p->loc[bsi].barNumb,p->s_opt,p->missCnt,mp->text); + printf("mark:%i scans:%4i loc:%4i bar:%4i score:%5.2f miss:%i text:'%s'\n",i,p->scanCnt,p->begSyncLocIdx,p->loc[p->begSyncLocIdx].barNumb,p->s_opt,p->missCnt,mp->text); //_cmScAlignPrintPath(p, p->p_opt, bsi ); - printf("mark:%i scans:%i midi:%i text:'%s'\n",i,p->scanCnt,p->mni,mp->text); + //printf("mark:%i scans:%i midi:%i text:'%s'\n",i,p->scanCnt,p->mni,mp->text); - _cmScAlignPrintResult(p); + if( _cmScAlignPrintResult(p) < scoreThresh ) + ++scoreFailCnt; - printf("\n"); } + //break; // ONLY USE ONE MARKER DURING TESTING + printf("\n"); + } - + printf("cand:%i fail:%i - init:%i score:%i other:%i\n\n",candCnt,initFailCnt+scoreFailCnt+otherFailCnt,initFailCnt,scoreFailCnt,otherFailCnt); cmScAlignFree(&p); cmCtxFree(&ctx); diff --git a/cmProc4.h b/cmProc4.h index 3157816..f7272aa 100644 --- a/cmProc4.h +++ b/cmProc4.h @@ -196,9 +196,10 @@ void ed_free(ed_r* r); // Main test function. void ed_main(); - //======================================================================================================================= + +//======================================================================================================================= enum { kSaMinIdx, @@ -208,13 +209,15 @@ enum kSaCnt }; +// Dynamic Programming (DP) matrix element typedef struct { - unsigned v[kSaCnt]; + unsigned v[kSaCnt]; // bool matchFl; // if this is a substitute; is it also a match? bool transFl; // if this is a substitute; is this the second element in a reversed pair? } cmScAlignVal_t; +// List record used to track a path through the DP matrix p->m[,] typedef struct cmScAlignPath_str { unsigned code; @@ -226,13 +229,13 @@ typedef struct cmScAlignPath_str struct cmScAlignPath_str* next; } cmScAlignPath_t; +// Score note event record typedef struct { unsigned pitch; - unsigned scEvtIdx; - bool matchFl; } cmScAlignScEvt_t; +// Score location record. typedef struct { unsigned evtCnt; // @@ -296,6 +299,7 @@ typedef struct bool printFl; unsigned begScanLocIdx; // begin the search at this score locations scWnd[begScanLocIdx:begScanLocIdx+p->cn-1] + unsigned begSyncLocIdx; // initial sync location unsigned resN; // count of records in res[] == 2*cmScoreEvtCount() cmScAlignResult_t* res; // res[resN] @@ -313,7 +317,7 @@ cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmRea cmRC_t cmScAlignFinal( cmScAlign* p ); void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx ); -bool cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); +cmRC_t cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); bool cmScAlignInputMidi( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); @@ -327,7 +331,9 @@ unsigned cmScAlignScan( cmScAlign* p, unsigned scanCnt ); // Step forward/back by p->stepCnt from p->esi. // If more than p->maxStepMissCnt consecutive MIDI events are // missed then automatically run cmScAlignScan(). -bool cmScAlignStep( cmScAlign* p ); +// Return cmEofRC if the end of the score is encountered. +// Return cmSubSysFailRC if an internal scan resync. failed. +cmRC_t cmScAlignStep( cmScAlign* p ); unsigned cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx ); @@ -336,6 +342,42 @@ unsigned cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* t // notes in each marker region and the score. void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH ); +//======================================================================================================================= + +typedef struct +{ + unsigned mni; + unsigned locIdx; + unsigned pitch; + unsigned vel; + unsigned smpIdx; +} cmScMeasMidi_t; + +typedef struct +{ + cmScoreSet_t* set; // A pointer to defining score set + unsigned bli; // Begin index into sap->loc[]. + unsigned eli; // End index into sap->loc[]. + unsigned bmi; // Begin index into midi[]. + unsigned emi; // End index into midi[]. + double* val; // val[sap->eleCnt] +} cmScMeasSet_t; + +typedef struct +{ + cmObj obj; + cmScAlign* sap; + unsigned mn; + cmScMeasMidi_t* midi; +} cmScMeas; + +cmScMeas* cmScMeasAlloc( cmCtx* c, cmScMeas* p, double srate, cmScH_t scH ); +cmRC_t cmScMeasFree( cmScMeas** pp ); +cmRC_t cmScMeasInit( cmScMeas* p, double srate, cmScH_t scH ); +cmRC_t cmScMeasFinal( cmScMeas* p ); +cmRC_t cmScMeasExec( cmScMeas* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1, unsigned scLocIdx ); + + #ifdef __cplusplus } #endif