cmProc4.h/c: Refinements to cmScAlign and initial interface for cmScMeas.

This commit is contained in:
kevin 2013-01-14 15:15:53 -08:00
parent 78c9c3064b
commit 85393d61c9
2 changed files with 199 additions and 57 deletions

202
cmProc4.c
View File

@ -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; j<lp->evtCnt; ++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; i<p->rn; ++i)
for(j=1; j<p->cn; ++j)
for(j=1; j<p->cn; ++j)
for(i=1; i<p->rn; ++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; i<p->ri; ++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; j<an; ++j)
{
pp = a + j;
@ -2402,36 +2433,45 @@ void _cmScAlignPrintResult( cmScAlign* p )
if( rp->mni > 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; i<an; ++i)
if( cmIsFlag(a[i].flags,kScNoteSaFl) && (a[i].mni == cmInvalidIdx || cmIsFlag(a[i].flags,kSubsErrSaFl)))
++skipCnt;
//_cmScAlignPrintList(a,an);
//_cmScAlignPrintReport(p,a,an,bsi,esi);
printf("score notes:%i match:%i wrong:%i \n",scNoteCnt,matchCnt,wrongCnt);
double prec = (double)2.0 * matchCnt / (matchCnt + wrongCnt);
double rcal = (double)2.0 * matchCnt / (matchCnt + skipCnt);
double fmeas = prec * rcal / (prec + rcal);
printf("midi:%i scans:%i score notes:%i match:%i skip:%i wrong:%i : %f\n",p->mni,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);

View File

@ -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