diff --git a/cmProc4.c b/cmProc4.c index 1aef8df..bf27b74 100644 --- a/cmProc4.c +++ b/cmProc4.c @@ -1218,6 +1218,7 @@ void _cmScMatchInitLoc( cmScMatch* p ) 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; } } @@ -1409,8 +1410,6 @@ void _cmScMatchPrintMtx( cmScMatch* r, unsigned rn, unsigned cn) } } - - void _cmScMatchPathPush( cmScMatch* r, unsigned code, unsigned ri, unsigned ci, unsigned flags ) { assert(r->p_avl != NULL ); @@ -1599,7 +1598,7 @@ double _cmScMatchAlign( cmScMatch* p, unsigned rn, unsigned cn, double min_cost } -cmRC_t cmScMatchExec( cmScMatch* p, unsigned scLocIdx, unsigned locN, const unsigned* midiPitchV, unsigned midiPitchN, double min_cost ) +cmRC_t cmScMatchExec( cmScMatch* p, unsigned locIdx, unsigned locN, const unsigned* midiPitchV, unsigned midiPitchN, double min_cost ) { cmRC_t rc; unsigned rn = midiPitchN + 1; @@ -1610,7 +1609,7 @@ cmRC_t cmScMatchExec( cmScMatch* p, unsigned scLocIdx, unsigned locN, const unsi return rc; // _cmScMatchCalcMtx() returns false if the score window exceeds the length of the score - if(!_cmScMatchCalcMtx(p,scLocIdx,midiPitchV, rn, cn) ) + if(!_cmScMatchCalcMtx(p,locIdx,midiPitchV, rn, cn) ) return cmEofRC; //_cmScMatchPrintMtx(p,rn,cn); @@ -1826,7 +1825,7 @@ bool cmScMatcherInputMidi( cmScMatcher* p, unsigned smpIdx, unsigned status, cm // shift the new MIDI event onto the end of the MIDI buffer memmove(p->midiBuf, p->midiBuf+1, sizeof(cmScMatcherMidi_t)*mi); p->midiBuf[mi].locIdx = cmInvalidIdx; - p->midiBuf[mi].cbCnt = 0; + //p->midiBuf[mi].cbCnt = 0; p->midiBuf[mi].mni = p->mni++; p->midiBuf[mi].smpIdx = smpIdx; p->midiBuf[mi].pitch = d0; @@ -1874,12 +1873,14 @@ void _cmScMatcherStoreResult( cmScMatcher* p, unsigned locIdx, unsigned flags, c rp->locIdx = locIdx; rp->mni = mp->mni; + rp->smpIdx = mp->smpIdx; rp->pitch = mp->pitch; rp->vel = mp->vel; rp->flags = flags | (tpFl ? kSmTruePosFl : 0) | (fpFl ? kSmFalsePosFl : 0); - //rp->tpFl = tpFl; - //rp->fpFl = fpFl; + if( p->cbFunc != NULL ) + p->cbFunc(p,p->cbArg,rp); + } void cmScMatcherPrintPath( cmScMatcher* p ) @@ -2070,7 +2071,6 @@ cmRC_t cmScMatcherStep( cmScMatcher* p ) // store the result _cmScMatcherStoreResult(p, locIdx, locIdx!=cmInvalidIdx ? kSmMatchFl : 0, p->midiBuf + p->mn - 1); - if( p->missCnt >= p->maxMissCnt ) { unsigned begScanLocIdx = p->esi > p->mn ? p->esi - p->mn : 0; @@ -2085,7 +2085,6 @@ cmRC_t cmScMatcherStep( cmScMatcher* p ) } return cmOkRC; - } @@ -2346,1318 +2345,225 @@ void cmScMatcherPrint( cmScMatcher* p ) } + //======================================================================================================================= -cmScAlign* cmScAlignAlloc( cmCtx* c, cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN ) +cmScMeas* cmScMeasAlloc( cmCtx* c, cmScMeas* p, cmScH_t scH ) { - cmScAlign* op = cmObjAlloc(cmScAlign,c,p); + cmScMeas* op = cmObjAlloc(cmScMeas,c,p); + + op->mp = cmScMatchAlloc( c, NULL, cmScNullHandle, 0, 0 ); + + if( cmScoreIsValid(scH) ) + if( cmScMeasInit(op,scH) != cmOkRC ) + cmScMeasFree(&op); - if( srate != 0 ) - if( cmScAlignInit(op,cbFunc,cbArg,srate,scH,midiN,scWndN) != cmOkRC ) - cmScAlignFree(&op); return op; } -cmRC_t cmScAlignFree( cmScAlign** pp ) +cmRC_t cmScMeasFree( cmScMeas** pp ) { cmRC_t rc = cmOkRC; if( pp==NULL || *pp==NULL ) return rc; - cmScAlign* p = *pp; - if((rc = cmScAlignFinal(p)) != cmOkRC ) + cmScMeas* p = *pp; + if((rc = cmScMeasFinal(p)) != cmOkRC ) return rc; + + cmScMatchFree(&p->mp); - cmMemFree(p->loc); cmMemFree(p->midiBuf); - cmMemFree(p->m); - cmMemFree(p->p_mem); - cmMemFree(p->res); + cmMemFree(p->set); cmObjFree(pp); return rc; } -void _cmScAlignPrint( cmScAlign* p ) +void _cmScMeasPrint( cmScMeas* p ) { - int i,j; - for(i=0; ilocN; ++i) + unsigned i; + for(i=0; isn; ++i) { - printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scLocIdx); - for(j=0; jloc[i].evtCnt; ++j) - printf("%s ",cmMidiToSciPitch(p->loc[i].evtV[j].pitch,NULL,0)); - printf("\n"); + cmScMeasSet_t* sp = p->set + i; + printf("%4i: sli:%4i %4i li:%4i %4i\n", i, sp->bsli, sp->esli, sp->bli, sp->eli ); } + } - -cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN ) +cmRC_t cmScMeasInit( cmScMeas* p, cmScH_t scH ) { - cmRC_t rc; - if((rc = cmScAlignFinal(p)) != cmOkRC ) + cmRC_t rc; + unsigned i,j; + unsigned si; + unsigned maxScWndN = 0; + + if((rc = cmScMeasFinal(p)) != cmOkRC ) return rc; - if( midiN > scWndN ) - return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score alignment MIDI event buffer length (%i) must be less than the score window length (%i).",midiN,scWndN); + p->mi = 0; + p->mn = 2 * cmScoreEvtCount(scH); + p->midiBuf = cmMemResizeZ(cmScMeasMidi_t,p->midiBuf,p->mn); + p->sn = cmScoreSetCount(scH); + p->set = cmMemResize(cmScMeasSet_t,p->set,p->sn); - p->cbFunc = cbFunc; - p->cbArg = cbArg; - p->srate = srate; - p->scH = scH; - p->locN = cmScoreEvtCount(scH); - p->loc = cmMemResizeZ(cmScAlignLoc_t,p->loc,p->locN); - p->mn = midiN; - p->midiBuf = cmMemResizeZ(cmScAlignMidiEvt_t,p->midiBuf,midiN); - p->mbi = midiN; - p->printFl = true; - - // Setup score structures + unsigned n = cmScoreLocCount(scH); // for each score location - unsigned li,ei; - - for(li=0,ei=0; liscH); ++li) + for(i=0,si=0; isetList; - const cmScoreLoc_t* lp = cmScoreLoc(p->scH,li); - - // count the number of note events at location li - for(n=0,i=0; ievtCnt; ++i) - if( lp->evtArray[i]->type == kNonEvtScId ) - ++n; - - assert( ei+n <= p->locN ); - - // duplicate each note at location li n times - for(i=0; illink,++si) { - unsigned j,k; + assert(si < p->sn); - p->loc[ei+i].evtCnt = n; - p->loc[ei+i].evtV = cmMemAllocZ(cmScAlignScEvt_t,n); - p->loc[ei+i].scLocIdx = li; - p->loc[ei+i].barNumb = lp->barNumb; + cmScMeasSet_t* msp = p->set + si; - for(j=0,k=0; jevtCnt; ++j) - if( lp->evtArray[j]->type == kNonEvtScId ) - { - p->loc[ei+i].evtV[k].pitch = lp->evtArray[j]->pitch; - ++k; - } + msp->bsli = cmScoreLocCount(scH); + msp->esli = 0; + msp->bsei = cmScoreEvtCount(scH); + msp->esei = 0; + msp->bli = cmInvalidIdx; + msp->eli = cmInvalidIdx; - } - - ei += n; - - } - - assert(ei<=p->locN); - p->locN = ei; - - // setup edit distance structures - p->rn = midiN+1; - p->cn = scWndN+1; - p->m = cmMemResizeZ(cmScAlignVal_t, p->m, p->rn*p->cn ); - p->pn = p->rn + p->cn; - p->p_mem = cmMemResizeZ(cmScAlignPath_t, p->p_mem, 2*p->pn ); - p->p_avl = p->p_mem; - p->p_cur = NULL; - p->p_opt = p->p_mem + p->pn; - p->s_opt = DBL_MAX; - p->resN = 2 * cmScoreEvtCount(scH); // make twice as many result records as there are score events - p->res = cmMemResizeZ(cmScAlignResult_t, p->res, p->resN); - p->stepCnt = 3; - p->maxStepMissCnt = 4; - - // fill in the default values for the first row - // and column of the DP matrix - unsigned i,j,k; - for(i=0; irn; ++i) - for(j=0; jcn; ++j) - { - unsigned v[] = {0,0,0,0}; - - if( i == 0 ) + for(j=0; jeleCnt; ++j) { - v[kSaMinIdx] = j; - v[kSaInsIdx] = j; + msp->bsli = cmMin(msp->bsli,sp->eleArray[j]->locIdx); + msp->esli = cmMax(msp->esli,sp->eleArray[j]->locIdx); + + msp->bsei = cmMin(msp->bsei,sp->eleArray[j]->index); + msp->esei = cmMax(msp->esei,sp->eleArray[j]->index); } - else - if( j == 0 ) - { - v[kSaMinIdx] = i; - v[kSaDelIdx] = i; - } - - for(k=0; km[ i + (j*p->rn) ].v[k] = v[k]; - } - - // put pn path records on the available list - for(i=0; ipn; ++i) - { - p->p_mem[i].next = ipn-1 ? p->p_mem + i + 1 : NULL; - p->p_opt[i].next = ipn-1 ? p->p_opt + i + 1 : NULL; + } } - //_cmScAlignPrint(p); + // initialize p->mp so that mp->loc[] is loaded - use dummy scWndN and midiN + if((rc = cmScMatchInit(p->mp, scH, 11, 10 )) != cmOkRC ) + return rc; - cmScAlignReset(p,0); + // assign set[].bli and set[].eli + for(j=0; jsn; ++j) + { + cmScMeasSet_t* msp = p->set + j; + + for(i=0; imp->locN; ++i) + { + if( msp->bli==cmInvalidIdx && msp->bsli==p->mp->loc[i].scLocIdx ) + msp->bli = i; + + if( msp->esli==p->mp->loc[i].scLocIdx ) + msp->eli = i; + } + + assert( msp->eli > msp->bli ); + + maxScWndN = cmMax( maxScWndN, msp->eli - msp->bli + 1 ); + + } + + // setup the match + if((rc = cmScMatchInit(p->mp, scH, 2*maxScWndN+1, 2*maxScWndN )) != cmOkRC ) + return rc; + + //_cmScMeasPrint(p); return rc; } -cmRC_t cmScAlignFinal( cmScAlign* p ) -{ - unsigned i; - for(i=0; ilocN; ++i) - cmMemPtrFree(&p->loc[i].evtV); +cmRC_t cmScMeasFinal( cmScMeas* p ) +{ return cmScMatchFinal(p->mp); } - return cmOkRC; +cmRC_t cmScMeasReset( cmScMeas* p ) +{ + cmRC_t rc = cmOkRC; + p->nsi = 0; + p->nsli = 0; + return rc; } -void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx ) +void _cmScMeasMatch( cmScMeas* p, cmScMeasSet_t* sp ) { - assert( begScanLocIdx < p->locN ); - 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; -} + double min_cost = DBL_MAX; -cmScAlignVal_t* _cmScAlignValPtr( cmScAlign* p, unsigned i, unsigned j ) -{ - assert( i < p->rn ); - assert( j < p->cn ); - - return p->m + i + (j*p->rn); -} - -bool _cmScAlignIsMatch( const cmScAlignLoc_t* loc, unsigned pitch ) -{ - unsigned i; - for(i=0; ievtCnt; ++i) - if( loc->evtV[i].pitch == pitch ) - return true; - return false; -} - -bool _cmScAlignIsTrans( cmScAlign* p, const cmScAlignVal_t* v1p, unsigned i, unsigned j ) -{ - bool fl = false; - cmScAlignVal_t* v0p = _cmScAlignValPtr(p,i,j); - - if( i>=1 && j>=1 - && v1p->v[kSaMinIdx] == v1p->v[kSaSubIdx] - && v1p->matchFl == false - && v0p->v[kSaMinIdx] == v0p->v[kSaSubIdx] - && v0p->matchFl == false ) - { - unsigned c00 = p->midiBuf[i-1].pitch; - unsigned c01 = p->midiBuf[i ].pitch; - cmScAlignLoc_t* c10 = p->loc + p->begScanLocIdx + j - 1; - cmScAlignLoc_t* c11 = p->loc + p->begScanLocIdx + j; - fl = _cmScAlignIsMatch(c11,c00) && _cmScAlignIsMatch(c10,c01); - } - return fl; -} - -unsigned _cmScAlignMin( cmScAlign* p, unsigned i, unsigned j ) -{ - assert( irn && jcn ); - //return p->m[ i + (j*p->rn) ].v[kSaMinIdx]; - return _cmScAlignValPtr(p,i,j)->v[kSaMinIdx]; -} - -// Returns 'false' if the score window goes past the end of the score -// (i.e. p->begScanLocIdx + p->cn > p->locN ) -bool _cmScAlignCalcMtx( cmScAlign* p ) -{ - // the midi buffer must be full - assert( p->mbi == 0 ); - - // loc[begScanLocIdx:begScanLocIdx+p->cn-1] must be valid - if( p->begScanLocIdx + p->cn > p->locN ) - return false; - - unsigned i,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; - cmScAlignVal_t* vp = _cmScAlignValPtr(p,i,j); - vp->matchFl = _cmScAlignIsMatch(loc,pitch); - unsigned cost = vp->matchFl ? 0 : 1; - vp->v[kSaSubIdx] = _cmScAlignMin(p,i-1,j-1) + cost; - vp->v[kSaDelIdx] = _cmScAlignMin(p,i-1,j ) + 1; - vp->v[kSaInsIdx] = _cmScAlignMin(p,i, j-1) + 1; - vp->v[kSaMinIdx] = cmMin( vp->v[kSaSubIdx], cmMin(vp->v[kSaDelIdx],vp->v[kSaInsIdx])); - vp->transFl = _cmScAlignIsTrans(p,vp,i-1,j-1); - } - - return true; -} - -void _cmScAlignPathPush( cmScAlign* r, unsigned code, unsigned ri, unsigned ci, bool matchFl, bool transFl ) -{ - assert(r->p_avl != NULL ); - cmScAlignPath_t* p = r->p_avl; - r->p_avl = r->p_avl->next; - - p->code = code; - p->ri = ri; - p->ci = ci; - p->matchFl = code==kSaSubIdx ? matchFl : false; - p->transFl = transFl; - p->next = r->p_cur; - r->p_cur = p; -} - -void _cmScAlignPathPop( cmScAlign* r ) -{ - assert( r->p_cur != NULL ); - cmScAlignPath_t* tp = r->p_cur->next; - r->p_cur->next = r->p_avl; - r->p_avl = r->p_cur; - r->p_cur = tp; -} - -double _cmScAlignScoreCandidate( cmScAlign* r ) -{ - cmScAlignPath_t* cp = r->p_cur; - cmScAlignPath_t* bp = r->p_cur; - cmScAlignPath_t* ep = NULL; - - for(; cp!=NULL; cp=cp->next) - if( cp->code != kSaInsIdx ) - { - bp = cp; - break; - } - - for(; cp!=NULL; cp=cp->next) - if( cp->code!=kSaInsIdx ) - ep = cp; - - assert( ep!=NULL && bp!=ep); - unsigned n=1; - for(cp=bp; cp!=ep; cp=cp->next) - ++n; - - double gapCnt = 0; - double penalty = 0; - bool pfl = bp->matchFl; - unsigned i; - - cp = bp; - for(i=0; inext) - { - // a gap is a transition from a matching subst. to an insert or deletion - //if( pc != cp->code && cp->code != kSaSubIdx && pc==kSaSubIdx && pfl==true ) - if( pfl==true && cp->matchFl==false ) - ++gapCnt; - - // - switch( cp->code ) - { - case kSaSubIdx: - penalty += cp->matchFl ? 0 : 1; - penalty -= cp->transFl ? 1 : 0; - break; - - case kSaDelIdx: - penalty += 1; - break; - - case kSaInsIdx: - penalty += 1; - break; - } - - pfl = cp->matchFl; - - } - - double score = gapCnt/n + penalty; - - //printf("n:%i gaps:%f gap_score:%f penalty:%f score:%f\n",n,gapCnt,gapCnt/n,penalty,score); - - return score; - -} - -void _cmScAlignEvalCandidate( cmScAlign* r, double score ) -{ - - if( r->s_opt == DBL_MAX || score < r->s_opt) - { - // copy the p_cur to p_opt[] - cmScAlignPath_t* cp = r->p_cur; - unsigned i; - for(i=0; cp!=NULL && ipn; cp=cp->next,++i) - { - r->p_opt[i].code = cp->code; - r->p_opt[i].ri = cp->ri; - r->p_opt[i].ci = cp->ci; - r->p_opt[i].matchFl = cp->matchFl; - r->p_opt[i].transFl = cp->transFl; - r->p_opt[i].next = cp->next==NULL ? NULL : r->p_opt + i + 1; - } - - assert( i < r->pn ); - r->p_opt[i].code = 0; // terminate with code=0 - r->s_opt = score; - } -} - - - -// traverse the solution matrix from the lower-right to -// the upper-left. -void _cmScAlignGenPaths( cmScAlign* r, int i, int j ) -{ - unsigned m; - - // stop when the upper-right is encountered - if( i==0 && j==0 ) - { - _cmScAlignEvalCandidate(r, _cmScAlignScoreCandidate(r) ); + if( p->mi == 0 ) return; + + unsigned pn = 0; + int i = p->mi-1; + + // determine the count of MIDI notes to match to the set score + for(; i>=0; --i) + { + ++pn; + + if( p->midiBuf[i].locIdx != cmInvalidIdx ) + { + unsigned scLocIdx = p->mp->loc[ p->midiBuf[i].locIdx ].scLocIdx; + if( scLocIdx <= sp->bsli ) + break; + } } - cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j); - - // for each possible dir: up,left,up-left - for(m=1; mv[m] == vp->v[kSaMinIdx] ) - { - // prepend to the current candidate path: r->p_cur - _cmScAlignPathPush(r,m,i,j,vp->matchFl,vp->transFl); - - int ii = i-1; - int jj = j-1; - - switch(m) - { - case kSaSubIdx: - break; - - case kSaDelIdx: - jj = j; - break; - - case kSaInsIdx: - ii = i; - break; - } - - // recurse! - _cmScAlignGenPaths(r,ii,jj); - - // remove the first element from the current path - _cmScAlignPathPop(r); - } + assert(pn>0); + // fill the pitch vector + unsigned pitchV[ pn ]; + unsigned mniV[ pn ]; + unsigned j; + for(j=0; imn && jmidiBuf[i].pitch; + mniV[j] = p->midiBuf[i].mni; + } + + cmScMatchExec(p->mp,sp->bli, sp->eli-sp->bli+1, pitchV, pn, min_cost ); + + _cmScMatchPrintPath(p->mp, p->mp->p_opt, sp->bli, pitchV, mniV ); + } -double _cmScAlign( cmScAlign* p ) +cmRC_t cmScMeasExec( cmScMeas* p, unsigned mni, unsigned locIdx, unsigned smpIdx, unsigned pitch, unsigned vel ) { - int i = p->rn-1; - int j = p->cn-1; - unsigned m = _cmScAlignMin(p,i,j); //p->m[i + (j*p->rn)].v[kSaMinIdx]; - - if( m==cmMax(p->rn,p->cn) ) - printf("Edit distance is at max: %i. No Match.\n",m); - else - _cmScAlignGenPaths(p,i,j); - - return p->s_opt; -} - -cmRC_t cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ) -{ - bool fl = p->mbi > 0; cmRC_t rc = cmOkRC; - // update the MIDI buffer with the incoming note - cmScAlignInputMidi(p,smpIdx,status,d0,d1); + // if the midi buffer is full + if( p->mi >= p->mn ) + return cmCtxRtCondition( &p->obj, cmEofRC, "The MIDI buffer is full."); - // if the MIDI buffer transitioned to full then perform an initial scan sync. - if( fl && p->mbi == 0 ) + // store the MIDI event + p->midiBuf[p->mi].mni = mni; + p->midiBuf[p->mi].locIdx = locIdx; + p->midiBuf[p->mi].smpIdx = smpIdx; + p->midiBuf[p->mi].pitch = pitch; + p->midiBuf[p->mi].vel = vel; + + ++p->mi; + + if( locIdx == cmInvalidIdx ) + return cmOkRC; + + // + unsigned scLocIdx = p->mp->loc[ locIdx ].scLocIdx; + unsigned maxScLocIdx = cmScoreLocCount(p->mp->scH)-1; + + // + for(; p->nsli<=scLocIdx && p->nsi < p->sn; ++p->nsli) { - if( (p->begSyncLocIdx = cmScAlignScan(p,cmInvalidCnt)) == cmInvalidIdx ) - rc = cmInvalidArgRC; // signal init. scan sync. fail - } - else - { - // if the MIDI buffer is full then perform a step sync. - if( !fl && p->mbi == 0 ) - rc = cmScAlignStep(p); + while( cmMin(maxScLocIdx,p->set[p->nsi].esli+1) == p->nsli ) + { + _cmScMeasMatch(p, p->set + p->nsi ); + + // advance the set index + ++p->nsi; + } } return rc; } -bool cmScAlignInputMidi( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ) -{ - if( status != kNoteOnMdId ) - return false; - - unsigned mi = p->mn-1; - - //printf("%3i %5.2f %4s\n",p->mni,(double)smpIdx/p->srate,cmMidiToSciPitch(d0,NULL,0)); - - // shift the new MIDI event onto the end of the MIDI buffer - memmove(p->midiBuf,p->midiBuf+1,sizeof(cmScAlignMidiEvt_t)*mi); - p->midiBuf[mi].locIdx = cmInvalidIdx; - p->midiBuf[mi].cbCnt = 0; - p->midiBuf[mi].mni = p->mni++; - p->midiBuf[mi].smpIdx = smpIdx; - p->midiBuf[mi].pitch = d0; - p->midiBuf[mi].vel = d1; - if( p->mbi > 0 ) - --p->mbi; - - return true; -} - -// If mep==NULL then the identified score location was not matched (this is an 'insert') -// these only occurr during 'scan' not 'step'. -// -// If locIdx == cmInvalidIdx then the MIDI event did not match a score location -// When this occurrs during a scan then this is a 'deleted' MIDI note otherwise -// the note was not found inside loc[esi-stepCnt:esi+stepCnt]. -// -// If mep!=NULL && scLocIdx!=cmInvalidIdx but matchFl==false then this is a -// 'substitute' with a mismatch. These only occur during 'scan'. -void _cmScAlignCb( cmScAlign* p, unsigned locIdx, cmScAlignMidiEvt_t* mep, bool matchFl, bool transFl ) -{ - // verify that the result buffer is not full - if( p->ri >= p->resN ) - { - cmCtxRtCondition( &p->obj, cmArgAssertRC, "The score alignment result buffer is full."); - return; - } - // don't report unmatched score locations - if( mep == NULL ) - return; - - ++mep->cbCnt; - - cmScAlignResult_t* rp = NULL; - - // if this is the first time this MIDI event has generated a callback ... - if( mep->cbCnt == 1 ) - rp = p->res + p->ri++; // ... then create a new record in p->res[] ... - else - if( mep->cbCnt > 1 && matchFl ) // ... otherwise if it was matched ... - { - unsigned i; - for(i=0; iri; ++i) - if(p->res[i].mni == mep->mni ) - { - if( p->res[i].matchFl == false ) // ... and it's previous recd was not matched then update the record with the match info. - rp = p->res + i; - } - } - - if(rp == NULL ) - return; - - assert( locIdx != cmInvalidIdx || mep != NULL ); - - rp->locIdx = locIdx; - rp->smpIdx = mep==NULL ? cmInvalidIdx : mep->smpIdx; - rp->mni = mep==NULL ? cmInvalidIdx : mep->mni; - rp->pitch = mep==NULL ? kInvalidMidiPitch : mep->pitch; - rp->vel = mep==NULL ? kInvalidMidiVelocity : mep->vel; - rp->matchFl = mep==NULL ? false : matchFl; - rp->transFl = mep==NULL ? false : transFl; -} - -void _cmScAlignPrintPath( cmScAlign* p, cmScAlignPath_t* cp, unsigned bsi ) -{ - assert( bsi != cmInvalidIdx ); - - cmScAlignPath_t* pp = cp; - int polyN = 0; - int i; - - printf("loc: "); - - // get the polyphony count for the score window - for(i=0; pp!=NULL; pp=pp->next) - { - cmScAlignLoc_t* lp = p->loc + bsi + pp->ci; - if( pp->code!=kSaDelIdx ) - { - if(lp->evtCnt > polyN) - polyN = lp->evtCnt; - - printf("%4i ",bsi+i); - ++i; - } - else - printf("%4s "," "); - } - - printf("\n"); - - // print the score notes - for(i=polyN; i>0; --i) - { - printf("%3i: ",i); - for(pp=cp; pp!=NULL; pp=pp->next) - { - int locIdx = bsi + pp->ci - 1; - assert(0 <= locIdx && locIdx <= p->locN); - cmScAlignLoc_t* lp = p->loc + locIdx; - if( pp->code!=kSaDelIdx && lp->evtCnt >= i ) - printf("%4s ",cmMidiToSciPitch(lp->evtV[i-1].pitch,NULL,0)); - else - printf("%4s ", pp->code==kSaDelIdx? "-" : " "); - } - printf("\n"); - } - - printf("mid: "); - - // print the MIDI buffer - for(pp=cp; pp!=NULL; pp=pp->next) - { - if( pp->code!=kSaInsIdx ) - printf("%4s ",cmMidiToSciPitch(p->midiBuf[pp->ri-1].pitch,NULL,0)); - else - printf("%4s ",pp->code==kSaInsIdx?"-":" "); - } - - printf("\nmni: "); - - // print the MIDI buffer index (mni) - for(pp=cp; pp!=NULL; pp=pp->next) - { - if( pp->code!=kSaInsIdx ) - printf("%4i ",p->midiBuf[pp->ri-1].mni); - else - printf("%4s ",pp->code==kSaInsIdx?"-":" "); - } - - printf("\n op: "); - - // print the substitute/insert/delete operation - for(pp=cp; pp!=NULL; pp=pp->next) - { - char c = ' '; - switch( pp->code ) - { - case kSaSubIdx: c = 's'; break; - case kSaDelIdx: c = 'd'; break; - case kSaInsIdx: c = 'i'; break; - default: - { assert(0); } - } - - printf("%4c ",c); - } - - printf("\n "); - - // give substitute attribute (match or transpose) - for(pp=cp; pp!=NULL; pp=pp->next) - { - cmChar_t s[3]; - int k = 0; - if( pp->matchFl ) - s[k++] = 'm'; - - if( pp->transFl ) - s[k++] = 't'; - - s[k] = 0; - - printf("%4s ",s); - } - - printf("\nscl: "); - - // print the stored location index - for(pp=cp; pp!=NULL; pp=pp->next) - { - if( pp->locIdx == cmInvalidIdx ) - printf("%4s "," "); - else - printf("%4i ",p->loc[pp->locIdx].scLocIdx); - } - - printf("\n\n"); -} - -// Returns the p->loc[] index at the start of the min cost score window -// based on the current MIDI buffer. -// scanCnt is the number of time the score window will be shifted one -// location to the left -unsigned cmScAlignScan( cmScAlign* p, unsigned scanCnt ) -{ - unsigned bsi = cmInvalidIdx; - - assert( p->mbi == 0 ); - - // if the MIDI buf is full - if( p->mbi == 0 ) - { - double s_opt = DBL_MAX; - unsigned i; - - // Loop as long as the score window is inside the score. - // Fill the Dyn Pgm matrix: MIDI_buf to score[begScanLocIdx:begScanLocIdx+scWndN-1]. - for(i=0; _cmScAlignCalcMtx(p) && (scanCnt==cmInvalidCnt || ibegScanLocIdx; - } - - // increment the score window - p->begScanLocIdx += 1; - } - - // store the cost assoc'd with bsi - p->s_opt = s_opt; - - } - - assert( bsi != cmInvalidIdx ); - - - // Traverse the least cost path and: - // 1) Set p->esi to the score location index of the last MIDI note - // which has a positive match with the score and assign - // the internal score index to cp->locIdx. - // - // 2) Set cmScAlignPath_t.locIdx - index into p->loc[] associated - // with each path element that is a 'substitute' or an 'insert'. - // - // 3) Set p->missCnt: the count of trailing non-positive matches. - // p->missCnt is eventually used in cmScAlignStep() to track the number - // of consecutive trailing missed notes. - // - cmScAlignPath_t* cp = p->p_opt; - unsigned i = bsi; - - p->missCnt = 0; - p->esi = cmInvalidIdx; - for(i=0; cp!=NULL; cp=cp->next) - { - - if( cp->code != kSaInsIdx ) - { - assert( cp->ri > 0 ); - p->midiBuf[ cp->ri-1 ].locIdx = cmInvalidIdx; - } - - switch( cp->code ) - { - case kSaSubIdx: - if( cp->matchFl || cp->transFl) - { - p->esi = bsi + i; - p->missCnt = 0; - - if( cp->matchFl ) - p->midiBuf[ cp->ri-1 ].locIdx = bsi + i; - } - else - { - ++p->missCnt; - } - - cp->locIdx = bsi + i; - ++i; - break; - - case kSaInsIdx: - cp->locIdx = bsi + i; - ++i; - break; - - case kSaDelIdx: - cp->locIdx = cmInvalidIdx; - ++p->missCnt; - break; - - } - } - - // if no positive matches were found - if( p->esi == cmInvalidIdx ) - bsi = cmInvalidIdx; - else - { - - // report matches - for(cp=p->p_opt; cp!=NULL; cp=cp->next) - { - unsigned locIdx = cp->locIdx; - cmScAlignMidiEvt_t* mep = NULL; - - if( cp->code != kSaInsIdx ) - mep = p->midiBuf + cp->ri - 1; - - _cmScAlignCb(p,locIdx,mep,cp->matchFl,cp->transFl); - } - } - - return bsi; -} - - -cmRC_t cmScAlignStep( cmScAlign* p ) -{ - int i; - unsigned pitch = p->midiBuf[ p->mn-1 ].pitch; - unsigned locIdx = cmInvalidIdx; - - // the tracker must be sync'd to step - if( p->esi == cmInvalidIdx ) - 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 cmEofRC; - - // attempt to match to next location first - if( _cmScAlignIsMatch(p->loc + p->esi + 1, pitch) ) - { - locIdx = p->esi + 1; - } - else - { - // - for(i=2; istepCnt; ++i) - { - // go forward - if( p->esi+i < p->locN && _cmScAlignIsMatch(p->loc + p->esi + i, pitch) ) - { - locIdx = p->esi + i; - break; - } - - // go backward - if( p->esi >= (i-1) && _cmScAlignIsMatch(p->loc + p->esi - (i-1), pitch) ) - { - locIdx = p->esi - (i-1); - break; - } - } - } - - p->midiBuf[ p->mn-1 ].locIdx = locIdx; - - if( locIdx == cmInvalidIdx ) - ++p->missCnt; - else - { - p->missCnt = 0; - p->esi = locIdx; - _cmScAlignCb(p,locIdx, p->midiBuf + p->mn - 1,true,false); - } - - if( p->missCnt >= p->maxStepMissCnt ) - { - p->begScanLocIdx = p->esi > p->rn ? p->esi - p->rn : 0; - p->s_opt = DBL_MAX; - unsigned bsi = cmScAlignScan(p,p->rn*2); - ++p->scanCnt; - - // if the scan failed find a match - if( bsi == cmInvalidIdx ) - return cmCtxRtCondition( &p->obj, cmSubSysFailRC, "Scan resync. failed."); - - //if( bsi != cmInvalidIdx ) - // _cmScAlignPrintPath(p, p->p_opt, bsi ); - } - - return cmOkRC; -} - - -void _cmScAlignPrintMtx( cmScAlign* r) -{ - unsigned i,j,k; - for(i=0; irn; ++i) - { - for(j=0; jcn; ++j) - { - printf("("); - - const cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j); - - for(k=0; kv[k]); - if( ktransFl?'t':' '); - - } - printf("\n"); - } -} - - -void cmScAlignPrintOpt( cmScAlign* p ) -{ - unsigned i; - for(i=0; p->p_opt[i].code!=0; ++i) - { - cmScAlignPath_t* cp = p->p_opt + i; - char c0 = cp->matchFl ? 'm' : ' '; - char c1 = cp->transFl ? 't' : ' '; - printf("%2i code:%i ri:%2i ci:%2i %c%c\n",i,cp->code,cp->ri,cp->ci,c0,c1); - } - printf("score:%f\n",p->s_opt); -} - - - - -enum -{ - kBarSaFl = 0x01, // this is a score bar - kScNoteSaFl = 0x02, // this is a score reference note (if mni != cmInvalidIdx then it was matched) - kSubsErrSaFl = 0x04, // 'subs' mismatch midi note - kMidiErrSaFl = 0x08, // 'deleted' Midi note -}; - -typedef struct cmScAlignPrint_str -{ - unsigned flags; - unsigned scLocIdx; - unsigned smpIdx; - unsigned pitch; - unsigned vel; - unsigned mni; - bool matchFl; - bool transFl; -} cmScAlignPrint_t; - -void _cmScAlignPrintList( cmScAlignPrint_t* a, unsigned an ) -{ - cmScAlignPrint_t* pp; - unsigned i; - printf("----------------------------------------------------\n"); - printf("idx scl mni pit flg \n"); - for(i=0; iscLocIdx,pp->mni, - pp->pitch==kInvalidMidiPitch ? " " : cmMidiToSciPitch(pp->pitch,NULL,0), - pp->flags); - } - printf("\n"); -} - -// insert a black record at a[i] -unsigned _cmScAlignPrintExpand( cmScAlignPrint_t* a, unsigned aan, unsigned i, unsigned an ) -{ - assert( an < aan ); - memmove( a + i + 1, a + i, (an-i)*sizeof(cmScAlignPrint_t)); - memset( a + i, 0, sizeof(cmScAlignPrint_t)); - return an + 1; -} - -void _cmScAlignPrintOutResult( cmScAlign* p, cmScAlignResult_t* rp, const cmChar_t* label ) -{ - printf("loc:%4i scloc:%4i smp:%10i mni:%4i %4s %c %c %s\n", - rp->locIdx, - rp->locIdx==cmInvalidIdx ? -1 : p->loc[rp->locIdx].scLocIdx, - rp->smpIdx, - rp->mni, - rp->pitch<=127 ? cmMidiToSciPitch(rp->pitch,NULL,0) : " ", - rp->matchFl ? 'm' : ' ', - rp->transFl ? 't' : ' ', - label); -} - -void _cmScAlignPrintSet( cmScAlignPrint_t* pp, const cmScAlignResult_t* rp, unsigned flags, unsigned scLocIdx ) -{ - pp->scLocIdx = scLocIdx; - pp->flags = flags; - pp->smpIdx = rp->smpIdx; - pp->pitch = rp->pitch; - pp->vel = rp->vel; - pp->mni = rp->mni; - - assert( pp->scLocIdx!=cmInvalidIdx || pp->mni != cmInvalidIdx ); - -} - -unsigned _cmScAlignPrintPoly( cmScAlignPrint_t* a, unsigned an, unsigned scLocIdx ) -{ - unsigned polyN = 0; - unsigned i; - for(i=0; i polyN ) - polyN = pn; - } - - // print titles - if( titleFl ) - { - printf(" "); - for(j=bli; j0; --i) - { - printf("%3i: ",i); - for(j=bli; jmni == cmInvalidIdx && cmIsNotFlag(pp->flags,kBarSaFl) ) - printf("| %4s %4s %3s %1s "," ",cmMidiToSciPitch(pp->pitch,NULL,0)," "," "); - else - { - if( cmIsFlag(pp->flags,kBarSaFl) ) - printf("| %4s %4s %3i %1s "," "," | ",pp->pitch,"b"); - else - { - const cmChar_t* op = cmIsFlag(pp->flags,kMidiErrSaFl) ? "d" : " "; - op = cmIsFlag(pp->flags,kSubsErrSaFl) ? "s" : op; - printf("| %4i %4s %3i %1s ",pp->mni,cmMidiToSciPitch(pp->pitch,NULL,0),pp->vel,op); - } - } - } - } - printf("\n"); - } - printf("\n"); - - bli = eli; - } - -} - -// 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); - unsigned esi = 0; - unsigned i,j; - for(i=0; iri; ++i) - { - cmScAlignResult_t* rp = p->res + i; - - assert( rp->locIdx==cmInvalidIdx || rp->locIdxlocN); - if( rp->locIdx != cmInvalidIdx ) - { - bsi = cmMin(bsi,p->loc[ rp->locIdx ].scLocIdx); - esi = cmMax(esi,p->loc[ rp->locIdx ].scLocIdx); - } - } - - // get a count of MIDI events + score events - unsigned aan = p->ri; - for(i=bsi; i<=esi; ++i) - { - cmScoreLoc_t* lp = cmScoreLoc( p->scH, i); - - aan += lp->evtCnt; - } - - cmScAlignPrint_t* a = cmMemAllocZ(cmScAlignPrint_t,aan); - unsigned an = 0; - unsigned scNoteCnt = 0; // notes in the score - unsigned matchCnt = 0; // matched score 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) - { - cmScoreLoc_t* lp = cmScoreLoc( p->scH, i); - - for(j=0; jevtCnt; ++j,++an) - { - assert( an < aan ); - cmScAlignPrint_t* pp = a + an; - - assert( lp->index != cmInvalidIdx ); - - pp->scLocIdx = lp->index; - pp->mni = cmInvalidIdx; - pp->pitch = kInvalidMidiPitch; - pp->vel = cmInvalidIdx; - - switch( lp->evtArray[j]->type ) - { - case kBarEvtScId: - pp->flags = kBarSaFl; - pp->pitch = lp->evtArray[j]->barNumb; - pp->mni = cmInvalidIdx; - break; - - case kNonEvtScId: - pp->flags = kScNoteSaFl; - pp->pitch = lp->evtArray[j]->pitch; - ++scNoteCnt; - break; - } - } - } - - //_cmScAlignPrintList(a,an); - - // Update the score with matching MIDI notes - - // for each result record ... - for(i=0; iri; ++i) - { - cmScAlignResult_t* rp = p->res + i; - - rp->foundFl = false; - - // ... if this is not an errant MIDI note (delete) - if( rp->locIdx != cmInvalidIdx ) - { - assert( rp->locIdx != cmInvalidIdx && rp->locIdx < p->locN ); - - unsigned scLocIdx = p->loc[rp->locIdx].scLocIdx; - cmScAlignPrint_t* pp; - - - // ... find the score location matching the result record score location - for(j=0; jscLocIdx ) - { - // if this is a matching midi node - if( rp->matchFl && cmIsFlag(pp->flags,kScNoteSaFl) && pp->pitch == rp->pitch ) - { - //_cmScAlignPrintOutResult(p,rp,"match"); - rp->foundFl = true; - _cmScAlignPrintSet(pp, rp, pp->flags, pp->scLocIdx ); - ++matchCnt; - break; - } - - // if this is a 'substitute' non-matching note - if( rp->matchFl == false && rp->mni != cmInvalidIdx ) - { - //_cmScAlignPrintOutResult(p,rp,"mis-match"); - ++j; // insert after the a[j] - an = _cmScAlignPrintExpand(a,aan,j,an); - _cmScAlignPrintSet(a + j, rp, kSubsErrSaFl, scLocIdx ); - rp->foundFl = true; - ++wrongCnt; - break; - } - - // if this is a 'skipped' score note ('insert') alert - if( rp->mni == cmInvalidIdx ) - { - //_cmScAlignPrintOutResult(p,rp,"skip"); - rp->foundFl = true; - break; - } - - } - } - } - - if( rp->foundFl == false ) - { - // _cmScAlignPrintOutResult(p,rp,"not-found"); - } - } - - //_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; - cmScAlignPrint_t* pp = NULL; - cmScAlignPrint_t* dpp = NULL; - unsigned dmin; - - // if this result did not have a matching score event - if(rp->foundFl) - continue; - - // find the print recd with the closest mni - for(j=0; jmni!=cmInvalidId ) - { - unsigned d; - if( pp->mni > rp->mni ) - d = pp->mni - rp->mni; - else - d = rp->mni - pp->mni; - - if( dpp == NULL || d < dmin ) - { - dpp = pp; - dmin = d; - } - } - } - - assert( dpp != NULL ); - - j = dpp - a; - - if( rp->mni > dpp->mni ) - ++j; - - assert( rp->locIdx == cmInvalidIdx ); - - // insert a print recd before or after the closest print recd - an = _cmScAlignPrintExpand(a,aan,j,an); - _cmScAlignPrintSet(a + j, rp, kMidiErrSaFl, dpp->scLocIdx ); - - ++wrongCnt; - - } - - for(i=0; imni,p->scanCnt,scNoteCnt,matchCnt,skipCnt,wrongCnt,fmeas); - - cmMemFree(a); - - return fmeas; -} - - +//======================================================================================================================= cmRC_t cmScAlignScanToTimeLineEvent( cmScMatcher* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx ) { assert( top != NULL ); @@ -3701,35 +2607,36 @@ cmRC_t cmScAlignScanToTimeLineEvent( cmScMatcher* p, cmTlH_t tlH, cmTlObj_t* top return rc; } -void cmScAlignCb( void* cbArg, unsigned scLocIdx, unsigned mni, unsigned pitch, unsigned vel ) +void cmScMatcherCb( cmScMatcher* p, void* arg, cmScMatcherResult_t* rp ) { - //cmScAlign* p = (cmScAlign*)cbArg; - + //cmScMeas* mp = (cmScMeas*)arg; + //cmScMeasExec(mp, rp->mni, rp->locIdx, rp->smpIdx, rp->pitch, rp->vel ); } void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH ) { - unsigned i; - double srate = 96000; - unsigned midiN = 7; - unsigned scWndN = 10; - unsigned markN = 291; - cmCtx* ctx = cmCtxAlloc(NULL, rpt, cmLHeapNullHandle, cmSymTblNullHandle ); - cmScMatcher* p = cmScMatcherAlloc(ctx,NULL,srate,scH,scWndN,midiN); - unsigned markCharCnt = 31; - cmChar_t markText[ markCharCnt+1 ]; - - double scoreThresh = 0.5; - unsigned candCnt = 0; - unsigned initFailCnt = 0; - unsigned otherFailCnt = 0; - unsigned scoreFailCnt = 0; - bool printFl = false; + unsigned i; + double srate = 96000; + unsigned midiN = 7; + unsigned scWndN = 10; + unsigned markN = 291; + cmCtx* ctx = cmCtxAlloc(NULL, rpt, cmLHeapNullHandle, cmSymTblNullHandle ); + cmScMeas* mp = cmScMeasAlloc(ctx,NULL,scH); + cmScMatcher* p = cmScMatcherAlloc(ctx,NULL,srate,scH,scWndN,midiN); + double scoreThresh = 0.5; + unsigned candCnt = 0; + unsigned initFailCnt = 0; + unsigned otherFailCnt = 0; + unsigned scoreFailCnt = 0; + bool printFl = false; + unsigned markCharCnt = 31; + cmChar_t markText[ markCharCnt+1 ]; cmTimeSpec_t t0,t1; cmTimeGet(&t0); - //p->cbArg = p; // set the callback arg. + p->cbArg = mp; // set the callback arg. + p->cbFunc = cmScMatcherCb; // for each marker for(i=0; im[,] typedef struct cmScMatchPath_str { - unsigned code; - unsigned ri; - unsigned ci; - unsigned flags; - unsigned locIdx; - struct cmScMatchPath_str* next; + unsigned code; // kSmXXXIdx + unsigned ri; // matrix row index + unsigned ci; // matrix col index + unsigned flags; // cmSmMatchFl | cmSmTransFl + unsigned locIdx; // p->loc index or cmInvalidIdx + struct cmScMatchPath_str* next; // } cmScMatchPath_t; typedef struct cmScMatchEvt_str { - unsigned pitch; + unsigned pitch; // + unsigned scEvtIdx; // scScore event index } cmScMatchEvt_t; // Score location record. @@ -251,8 +252,8 @@ typedef struct typedef struct { - cmObj obj; - cmScH_t scH; // + cmObj obj; // + cmScH_t scH; // cmScore handle unsigned locN; // cmScMatchLoc_t* loc; // loc[locN] unsigned mrn; // max row count (midi) @@ -275,17 +276,17 @@ cmRC_t cmScMatchFree( cmScMatch** pp ); cmRC_t cmScMatchInit( cmScMatch* p, cmScH_t scH, unsigned maxScWndN, unsigned maxMidiWndN ); cmRC_t cmScMatchFinal( cmScMatch* p ); -// Returns cmEofRC if scLocIdx + locN > p->locN - note that this is not necessarily an error. +// Returns cmEofRC if locIdx + locN > p->locN - note that this is not necessarily an error. // The optimal path p_opt[] will only be updated if the edit cost is less than min_cost. // Set min_cost to DBL_MAX to force p_opt[] to be updated. -cmRC_t cmScMatchExec( cmScMatch* p, unsigned scLocIdx, unsigned locN, const unsigned* midiPitchV, unsigned midiPitchN, double min_cost ); +cmRC_t cmScMatchExec( cmScMatch* p, unsigned locIdx, unsigned locN, const unsigned* midiPitchV, unsigned midiPitchN, double min_cost ); //======================================================================================================================= typedef struct { unsigned locIdx; // location assoc'd with this MIDI evt (cmInvalidIdx if not a positive-match) - unsigned cbCnt; // count of times this event has been sent via the callback + //unsigned cbCnt; // count of times this event has been sent via the callback unsigned mni; // unique identifier for this event since previous call to cmScAlignReset(). unsigned smpIdx; // time stamp of this event unsigned pitch; // MIDI note pitch @@ -296,40 +297,47 @@ cmRC_t cmScMatchExec( cmScMatch* p, unsigned scLocIdx, unsigned locN, const { unsigned locIdx; unsigned mni; + unsigned smpIdx; unsigned pitch; unsigned vel; unsigned flags; } cmScMatcherResult_t; - typedef struct +struct cmScMatcher_str; +typedef void (*cmScMatcherCb_t)( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp ); + + typedef struct cmScMatcher_str { - cmObj obj; - cmScMatch* mp; - unsigned mn; - cmScMatcherMidi_t* midiBuf; // midiBuf[mn] + cmObj obj; + cmScMatcherCb_t cbFunc; + void* cbArg; + cmScMatch* mp; + unsigned mn; + cmScMatcherMidi_t* midiBuf; // midiBuf[mn] cmScMatcherResult_t* res; // res[rn] unsigned rn; // length of res[] unsigned ri; // next avail res[] recd. - double s_opt; // - unsigned missCnt; // count of consecutive trailing non-matches - unsigned esi; // index into loc[] of the last positive match. - unsigned mni; // track the count of MIDI events since the last call to cmScMatcherReset() - unsigned mbi; // index of oldest MIDI event in midiBuf[]; 0 when the buffer is full. - unsigned begSyncLocIdx; // start of score window, in mp->loc[], of best match in previous scan - unsigned stepCnt; // count of forward/backward score loc's to examine for a match during cmScMatcherStep(). - unsigned maxMissCnt; // max. number of consecutive non-matches during step prior to executing a scan. - unsigned scanCnt; // count of time scan was executed inside cmScMatcherStep() + double s_opt; // + unsigned missCnt; // count of consecutive trailing non-matches + unsigned esi; // index into loc[] of the last positive match. + unsigned mni; // track the count of MIDI events since the last call to cmScMatcherReset() + unsigned mbi; // index of oldest MIDI event in midiBuf[]; 0 when the buffer is full. + unsigned begSyncLocIdx; // start of score window, in mp->loc[], of best match in previous scan + unsigned stepCnt; // count of forward/backward score loc's to examine for a match during cmScMatcherStep(). + unsigned maxMissCnt; // max. number of consecutive non-matches during step prior to executing a scan. + unsigned scanCnt; // count of time scan was executed inside cmScMatcherStep() } cmScMatcher; + cmScMatcher* cmScMatcherAlloc( cmCtx* c, cmScMatcher* p, double srate, cmScH_t scH, unsigned scWndN, unsigned midiWndN ); cmRC_t cmScMatcherFree( cmScMatcher** pp ); cmRC_t cmScMatcherInit( cmScMatcher* p, double srate, cmScH_t scH, unsigned scWndN, unsigned midiWndN ); cmRC_t cmScMatcherFinal( cmScMatcher* p ); void cmScMatcherReset( cmScMatcher* p ); -bool cmScMatcherInputMidi( cmScMatcher* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); +bool cmScMatcherInputMidi( cmScMatcher* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); // Slide a score window scanCnt times, beginning at 'bsi', // looking for the best match to p->midiBuf[]. The score window @@ -350,142 +358,57 @@ cmRC_t cmScMatcherStep( cmScMatcher* p ); cmRC_t cmScMatcherExec( cmScMatcher* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 ); - //======================================================================================================================= -enum -{ - kSaMinIdx, - kSaSubIdx, // 'substitute' - may or may not match - kSaDelIdx, // 'delete' - delete a MIDI note - kSaInsIdx, // 'insert' - insert a space in the score - kSaCnt -}; -// Dynamic Programming (DP) matrix element -typedef struct -{ - 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; - unsigned ri; - unsigned ci; - bool matchFl; - bool transFl; - unsigned locIdx; - struct cmScAlignPath_str* next; -} cmScAlignPath_t; - -// Score note event record typedef struct { + unsigned mni; + unsigned scEvtIdx; + unsigned locIdx; + unsigned smpIdx; unsigned pitch; -} cmScAlignScEvt_t; + unsigned vel; +} cmScMeasMidi_t; -// Score location record. -typedef struct -{ - unsigned evtCnt; // - cmScAlignScEvt_t* evtV; // evtV[evtCnt] - unsigned scLocIdx; // scH score location index - int barNumb; // bar number of this location -} cmScAlignLoc_t; typedef struct { - unsigned locIdx; // location assoc'd with this MIDI evt (cmInvalidIdx if not a positive-match) - unsigned cbCnt; // count of times this event has been sent via the callback - unsigned mni; // unique identifier for this event since previous call to cmScAlignReset(). - unsigned smpIdx; // time stamp of this event - unsigned pitch; // MIDI note pitch - unsigned vel; // " " velocity -} cmScAlignMidiEvt_t; + unsigned bsei; // begin score event index + unsigned esei; // end score event index + + unsigned bsli; // + unsigned esli; // end score loc index + + unsigned bli; // + unsigned eli; // + +} cmScMeasSet_t; typedef struct { - unsigned locIdx; // loc[] sync. location - unsigned smpIdx; // - unsigned mni; // MIDI event unique index - unsigned pitch; // MIDI event pitch which may not match the score event pitch - unsigned vel; // " " velocity - bool matchFl; - bool transFl; - bool foundFl; -} cmScAlignResult_t; + cmObj obj; + cmScMatch* mp; + unsigned mi; // next avail recd in midiBuf[] + unsigned mn; // length of of midiBuf[] + cmScMeasMidi_t* midiBuf; // midiBuf[mn] -// -typedef void (*cmScAlignCb_t)( void* cbArg, unsigned scLocIdx, unsigned mni, unsigned pitch, unsigned vel ); + unsigned sn; // length of set[] + cmScMeasSet_t* set; // set[sn] -typedef struct -{ - cmObj obj; - cmScAlignCb_t cbFunc; // - void* cbArg; // - cmScH_t scH; // - double srate; // - unsigned locN; // length of loc[] - cmScAlignLoc_t* loc; // loc[locN] score array - unsigned rn; // length of midiBuf[] (mn+1) - unsigned cn; // length of score window (scWndN+1) - unsigned mn; // length of midiBuf[] (rn-1) - cmScAlignMidiEvt_t* midiBuf; // midiBuf[ mn ] - unsigned mbi; // index of first element in midiBuf[] - this is required because the MIDI buf fills from the end to the beginning - unsigned mni; // index of event in midiBuf[p->mn] - increments on each note inserted into midiBuf[] - zeroed by cmScAlignReset(). + unsigned nsi; // next set index + unsigned nsli; // next score location index + - cmScAlignVal_t* m; // m[rn,cn] - unsigned pn; // rn+cn - cmScAlignPath_t* p_mem; // pmem[ 2*pn ]; - cmScAlignPath_t* p_avl; // available path record linked list - cmScAlignPath_t* p_cur; // current path linked list - cmScAlignPath_t* p_opt; // p_opt[pn] current best alignment - double s_opt; // score of the current best alignment - unsigned esi; // loc[] index of latest positive match - unsigned missCnt; // count of consecutive trailing MIDI events without positive matches - unsigned scanCnt; +} cmScMeas; - 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] - unsigned ri; // - - int stepCnt; // count of loc[] locations to step ahead/back during a cmScAlignStep() operation. - int maxStepMissCnt; // max. consecutive trailing non-positive matches before a scan takes place. +cmScMeas* cmScMeasAlloc( cmCtx* c, cmScMeas* p, cmScH_t scH ); +cmRC_t cmScMeasFree( cmScMeas** pp ); +cmRC_t cmScMeasInit( cmScMeas* p, cmScH_t scH ); +cmRC_t cmScMeasFinal( cmScMeas* p ); +cmRC_t cmScMeasExec( cmScMeas* p, unsigned mni, unsigned locIdx, unsigned smpIdx, unsigned pitch, unsigned vel ); -} cmScAlign; - -cmScAlign* cmScAlignAlloc( cmCtx* ctx, cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN ); -cmRC_t cmScAlignFree( cmScAlign** pp ); -cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN ); -cmRC_t cmScAlignFinal( cmScAlign* p ); -void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx ); - -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 ); - -// Scan from p->begScanLocIdx to the end of the score looking -// for the best match to p->midiBuf[]. -// Returns the score location index which best matches the -// first note p->midiBuf[]. The score associated -// with this match is stored in s_opt. -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(). -// 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( cmScMatcher* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx ); @@ -494,42 +417,6 @@ unsigned cmScAlignScanToTimeLineEvent( cmScMatcher* p, cmTlH_t tlH, cmTlObj_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