cmProc4.h/c cmDspKr.c Score follower tweeking.

This commit is contained in:
kpl 2012-11-27 14:20:54 -08:00
parent d7578b9866
commit 24bcde77ac
4 changed files with 155 additions and 112 deletions

204
cmProc4.c
View File

@ -90,7 +90,11 @@ cmRC_t cmScFolInit( cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs
p->msln = 7;
p->mswn = 30;
p->edWndMtx = cmVOU_LevEditDistAllocMtx(p->bufN);
p->minVel = 5;
p->missCnt = 0;
p->matchCnt = 0;
p->printFl = true;
p->eventIdx = 0;
int i,n;
double maxDSecs = 0; // max time between score entries to be considered simultaneous
@ -208,6 +212,18 @@ bool _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
return false;
}
int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const unsigned* pitch, unsigned pi )
{
if( _cmScFolIsMatch(loc+li,pitch[pi]) )
return 0;
if( li>0 && pi>0 )
if( _cmScFolIsMatch(loc+li-1,pitch[pi]) && _cmScFolIsMatch(loc+li,pitch[pi-1]) )
return 0;
return 1;
}
int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFolLoc_t* s0, int n )
{
mtxMaxN += 1;
@ -226,7 +242,8 @@ int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFo
for( j=1; j<n+1; ++j)
{
//int cost = s0[i-1] == s1[j-1] ? 0 : 1;
int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
//int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
int cost = _cmScFolMatchCost(s0,i-1,s1,j-1);
//m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
@ -236,82 +253,57 @@ int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFo
return v;
}
/*
unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, unsigned* b, unsigned bn, unsigned min_idx )
{
assert( p->sri != cmInvalidIdx );
unsigned i;
int n;
unsigned ret_idx = cmInvalidIdx;
unsigned ewnd[ p->wndN ];
printf("--------------- event:%i ------------- \n",p->eventIdx);
if( status != kNoteOnMdId && d1>0 )
return ret_idx;
// left shift wndV[] to make the right-most element available - then copy in the new element
memmove(p->wndV, p->wndV+1, sizeof(cmScFolWndEle_t)*(p->wndN-1));
p->wndV[ p->wndN-1 ].smpIdx = smpIdx;
p->wndV[ p->wndN-1 ].val = d0;
p->wndV[ p->wndN-1 ].validFl= true;
// fill in ewnd[] with the valid values in wndV[]
int i = p->wndN-1;
int en = 0;
for(; i>=0; --i,++en)
{
//if( p->wndV[i].validFl && ((smpIdx-p->wnd[i].smpIdx)<=maxWndSmp))
if( p->wndV[i].validFl )
ewnd[i] = p->wndV[i].val;
else
break;
}
++i; // increment i to the first valid element in ewnd[].
int k;
printf("en:%i sbi:%i sei:%i pitch:%s : ",en,p->sbi,p->sei,cmMidiToSciPitch(d0,NULL,0));
for(k=i; k<p->wndN; ++k)
printf("%s ", cmMidiToSciPitch(ewnd[k],NULL,0));
printf("loc: ");
for(i=0; i<locN; ++i)
printf("%4i ",i+locIdx);
printf("\n");
// en is the count of valid elements in ewnd[].
// ewnd[i] is the first valid element
for(n=0,i=0; i<locN; ++i)
if( p->loc[locIdx+i].pitchCnt > n )
n = p->loc[locIdx+i].pitchCnt;
int j = 0;
int dist;
int minDist = INT_MAX;
int minIdx = cmInvalidIdx;
for(j=0; p->sbi+en+j <= p->sei; ++j)
if((dist = _cmScFolDist(p->wndN, p->edWndMtx, ewnd+i, p->loc + p->sbi+j, en )) < minDist )
--n;
for(; n>=0; --n)
{
minDist = dist;
minIdx = j;
}
// The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
int evalWndN = cmMin(en,p->evalWndN);
assert(evalWndN<p->wndN);
j = p->sbi+minIdx+en - evalWndN;
// Determine how many of the last evalWndN elements match
dist = _cmScFolDist(p->wndN, p->edWndMtx, ewnd+p->wndN-evalWndN, p->loc+j, evalWndN );
// a successful match has <= allowedMissCnt and an exact match on the last element
//if( dist <= p->allowedMissCnt && ewnd[p->wndN-1] == p->loc[p->sbi+minIdx+en-1] )
//if( dist <= p->allowedMissCnt && _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ewnd[p->wndN-1]))
if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ewnd[p->wndN-1]))
printf("sc%1i: ",n);
for(i=0; i<locN; ++i)
{
p->sbi = p->sbi + minIdx;
p->sei = cmMin(p->sei+minIdx,p->locN-1);
ret_idx = p->sbi+minIdx+en-1;
if( n < p->loc[locIdx+i].pitchCnt )
printf("%4s ",cmMidiToSciPitch(p->loc[locIdx+i].pitchV[n],NULL,0));
else
printf(" ");
}
printf("\n");
}
printf("minDist:%i minIdx:%i evalDist:%i sbi:%i sei:%i\n",minDist,minIdx,dist,p->sbi,p->sei);
printf("perf:");
for(i=0; i<min_idx; ++i)
printf(" ");
for(i=0; i<bn; ++i)
printf("%4s ",cmMidiToSciPitch(b[i],NULL,0));
printf("\n");
}
void _cmScFolRpt1( cmScFol*p, unsigned minDist, unsigned ret_idx, unsigned d1, unsigned missCnt, unsigned matchCnt )
{
printf("dist:%i miss:%i match:%i ",minDist,missCnt,matchCnt);
if( ret_idx == cmInvalidIdx )
printf("vel:%i ", d1 );
else
printf("ret_idx:%i ",ret_idx);
printf("\n");
return ret_idx;
}
*/
unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
{
@ -319,7 +311,13 @@ unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
unsigned ret_idx = cmInvalidIdx;
unsigned ebuf[ p->bufN ];
if( status != kNoteOnMdId && d1>0 )
if( status != kNoteOnMdId )
return ret_idx;
++p->eventIdx;
// reject notes with very low velocity
if( d1 < p->minVel )
return ret_idx;
// left shift bufV[] to make the right-most element available - then copy in the new element
@ -355,31 +353,87 @@ unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
// shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
assert( p->sei-p->sbi+1 >= en );
for(j=0; p->sbi+en+j <= p->sei; ++j)
if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) < minDist )
for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
{
// use <= minDist to choose the latest window with the lowest match
if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) <= minDist )
{
minDist = dist;
minIdx = j;
}
}
// The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
if( p->printFl )
_cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, ebuf+i, en, minIdx );
// save current missCnt for later printing
unsigned missCnt = p->missCnt;
// if a perfect match occurred
if( minDist == 0 )
{
// we had a perfect match - shrink the window to it's minumum size
p->sbi += (en==p->bufN) ? minIdx + 1 : 0; // move wnd begin forward to just past first match
p->sei = p->sbi + minIdx + en + p->msln; // move wnd end forward to just past last match
ret_idx = p->sbi + minIdx + en - 1;
// BUG BUG BUG - is the window length valid -
// - sbi and sei must be inside 0:locN
// - sei-sbi + 1 must be >= en
ret_idx = p->sbi + minIdx + en - 1;
p->missCnt = 0;
}
else
{
// if the last event matched - then return the match location as the current score location
if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ebuf[p->bufN-1]) )
{
ret_idx = p->sbi + minIdx + en - 1;
p->missCnt = 0;
if( en >= p->bufN-1 && (en+2) <= ret_idx )
p->sbi = ret_idx - (en+2);
}
else // the last event does not match based on the optimal edit-distance alignment
{
// Look backward from the closest match location for a match to the current pitch.
// The backward search scope is limited by the current value of 'missCnt'.
j = p->sbi+minIdx+en-2;
for(i=1; i+1 <= p->bufN && j>=p->sbi && i<=p->missCnt; ++i,--j)
{
// if this look-back location already matched then stop the backward search
if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1-i]))
break;
// does this look-back location match the current pitch
if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]))
{
ret_idx = j;
p->missCnt = i; // set missCnt to the cnt of steps backward necessary for a match
break;
}
}
// If the backward search did not find a match - look forward
if( ret_idx == cmInvalidIdx )
{
j = p->sbi+minIdx+en;
for(i=0; j<=p->sei && i<2; ++i,++j)
if( _cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]) )
{
ret_idx = j;
break;
}
p->missCnt = ret_idx == cmInvalidIdx ? p->missCnt + 1 : 0;
}
}
// even though we didn't match move the end of the score window forward
// this will enlarge the score window by one
@ -398,9 +452,19 @@ unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
}
if( p->printFl )
_cmScFolRpt1(p, minDist, ret_idx, d1, missCnt, p->matchCnt );
if( ret_idx == cmInvalidIdx )
p->matchCnt = 0;
else
++p->matchCnt;
// BUG BUG BUG - this is not currently guarded against
assert( p->sei < p->locN );
assert( p->sbi < p->locN );
return ret_idx;
}

View File

@ -23,29 +23,6 @@ typedef struct
int barNumb; // bar number of this location
} cmScFolLoc_t;
/*
typedef struct
{
cmObj obj;
cmReal_t srate;
cmScH_t scH;
unsigned maxWndSmp;
unsigned wndN;
cmScFolWndEle_t* wndV; // wnd[wndN]
int wni; // oldest value in wnd[]
int locN;
cmScFolLoc_t* loc;
unsigned sri; // last reset score index
unsigned sbi; // first (oldest) score index
unsigned sei; // last (newest) score index
unsigned evalWndN; // (dflt:5) count of elements to use for refined match window
unsigned allowedMissCnt; // (dflt:1) count of non-match elements in refined match where a match is still signaled
unsigned* edWndMtx;
} cmScFol1;
*/
typedef struct
{
@ -60,16 +37,18 @@ typedef struct
unsigned sei; // newest score window index
unsigned msln; // minimum score look ahead count
unsigned mswn; // maximum score window length
//unsigned evalWndN; // (dflt:5) count of elements to use for refined match window
//unsigned allowedMissCnt; // (dflt:1) count of non-match elements in refined match where a match is still signaled
unsigned minVel; // notes < minVel are ignored
unsigned missCnt; // current consecutive unmatched notes
unsigned matchCnt;// current consecutive matched notes
bool printFl; // true if pitch tracker reporting should be included
unsigned aheadCnt;// count of score loc's to look ahead for a match to the current pitch when the optimal edit-dist alignment does not produce a match for the current pitch
unsigned eventIdx;// events since init
unsigned* edWndMtx;
} cmScFol;
// wndN = max count of elements in the score following window.
// bufN = max count of elements in the event buffer.
// wndMs = max length of the score following window in time
cmScFol* cmScFolAlloc( cmCtx* ctx, cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH );

View File

@ -704,8 +704,8 @@ struct cmDspClass_str* cmMidiFilePlayClassCons( cmDspCtx_t* ctx )
enum
{
kFnSfId,
kWndCntSfId,
kWndMsSfId,
kBufCntSfId,
kBufMsSfId,
kIndexSfId,
kStatusSfId,
kD0SfId,
@ -727,8 +727,8 @@ cmDspInst_t* _cmDspScFolAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned
cmDspVarArg_t args[] =
{
{ "fn", kFnSfId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Score file." },
{ "wndcnt",kWndCntSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event window element count." },
{ "wndms", kWndMsSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event window length milliseconds."},
{ "bufcnt",kBufCntSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event buffer element count." },
{ "bufms", kBufMsSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event buffer length milliseconds."},
{ "index", kIndexSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Tracking start location."},
{ "status",kStatusSfId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI status byte"},
{ "d0", kD0SfId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI data byte 0"},
@ -745,8 +745,8 @@ cmDspInst_t* _cmDspScFolAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned
p->sfp = cmScFolAlloc(ctx->cmProcCtx, NULL, 0, 0, 0, cmScNullHandle );
cmDspSetDefaultUInt( ctx, &p->inst, kWndCntSfId, 0, 10);
cmDspSetDefaultUInt( ctx, &p->inst, kWndMsSfId, 0, 5000);
cmDspSetDefaultUInt( ctx, &p->inst, kBufCntSfId, 0, 10);
cmDspSetDefaultUInt( ctx, &p->inst, kBufMsSfId, 0, 5000);
cmDspSetDefaultUInt( ctx, &p->inst, kIndexSfId, 0, 0);
cmDspSetDefaultUInt( ctx, &p->inst, kOutSfId, 0, 0);
@ -785,7 +785,7 @@ cmDspRC_t _cmDspScFolReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t*
return rc;
if( cmScoreIsValid(p->scH) )
if( cmScFolInit(p->sfp, cmDspSampleRate(ctx), cmDspUInt(inst,kWndCntSfId), cmDspUInt(inst,kWndMsSfId), p->scH) != cmOkRC )
if( cmScFolInit(p->sfp, cmDspSampleRate(ctx), cmDspUInt(inst,kBufCntSfId), cmDspUInt(inst,kBufMsSfId), p->scH) != cmOkRC )
rc = cmErrMsg(&inst->classPtr->err, kSubSysFailDspRC, "Internal score follower allocation failed.");
return rc;

View File

@ -110,7 +110,7 @@ cmDspRC_t _cmDspSysPgm_TimeLine(cmDspSysH_t h, void** userPtrPtr )
cmDspSysInstallCb(h, tlp, "afn", prp, "in", NULL );
cmDspSysInstallCb(h, tlp, "mfn", prp, "in", NULL );
cmDspSysInstallCb(h, tlp, "sel", prp, "in", NULL );
cmDspSysInstallCb(h, sfp, "out", prp, "in", NULL );
//cmDspSysInstallCb(h, sfp, "out", prp, "in", NULL );
return rc;