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

208
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->msln = 7;
p->mswn = 30; p->mswn = 30;
p->edWndMtx = cmVOU_LevEditDistAllocMtx(p->bufN); p->edWndMtx = cmVOU_LevEditDistAllocMtx(p->bufN);
p->minVel = 5;
p->missCnt = 0;
p->matchCnt = 0;
p->printFl = true;
p->eventIdx = 0;
int i,n; int i,n;
double maxDSecs = 0; // max time between score entries to be considered simultaneous 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; 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 ) int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFolLoc_t* s0, int n )
{ {
mtxMaxN += 1; mtxMaxN += 1;
@ -226,7 +242,8 @@ int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFo
for( j=1; j<n+1; ++j) for( j=1; j<n+1; ++j)
{ {
//int cost = s0[i-1] == s1[j-1] ? 0 : 1; //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 ) ); //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; return v;
} }
/* void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, unsigned* b, unsigned bn, unsigned min_idx )
unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
{ {
assert( p->sri != cmInvalidIdx ); unsigned i;
int n;
unsigned ret_idx = cmInvalidIdx; printf("--------------- event:%i ------------- \n",p->eventIdx);
unsigned ewnd[ p->wndN ];
if( status != kNoteOnMdId && d1>0 ) printf("loc: ");
return ret_idx; for(i=0; i<locN; ++i)
printf("%4i ",i+locIdx);
// 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("\n"); printf("\n");
// en is the count of valid elements in ewnd[]. for(n=0,i=0; i<locN; ++i)
// ewnd[i] is the first valid element if( p->loc[locIdx+i].pitchCnt > n )
n = p->loc[locIdx+i].pitchCnt;
int j = 0; --n;
int dist; for(; n>=0; --n)
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 )
{
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]))
{ {
p->sbi = p->sbi + minIdx; printf("sc%1i: ",n);
p->sei = cmMin(p->sei+minIdx,p->locN-1); for(i=0; i<locN; ++i)
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 ) 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 ret_idx = cmInvalidIdx;
unsigned ebuf[ p->bufN ]; 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; return ret_idx;
// left shift bufV[] to make the right-most element available - then copy in the new element // 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) // shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
assert( p->sei-p->sbi+1 >= en ); assert( p->sei-p->sbi+1 >= en );
for(j=0; p->sbi+en+j <= p->sei; ++j) for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) < minDist ) {
// 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; minDist = dist;
minIdx = j; minIdx = j;
} }
}
// The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ] // 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 a perfect match occurred
if( minDist == 0 ) if( minDist == 0 )
{ {
// we had a perfect match - shrink the window to it's minumum size // 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->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 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 - // BUG BUG BUG - is the window length valid -
// - sbi and sei must be inside 0:locN // - sbi and sei must be inside 0:locN
// - sei-sbi + 1 must be >= en // - sei-sbi + 1 must be >= en
ret_idx = p->sbi + minIdx + en - 1;
p->missCnt = 0;
} }
else else
{ {
// if the last event matched - then return the match location as the current score location // 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]) ) if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ebuf[p->bufN-1]) )
ret_idx = p->sbi + minIdx + en - 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 // even though we didn't match move the end of the score window forward
// this will enlarge the score window by one // 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 // BUG BUG BUG - this is not currently guarded against
assert( p->sei < p->locN ); assert( p->sei < p->locN );
assert( p->sbi < p->locN ); assert( p->sbi < p->locN );
return ret_idx; return ret_idx;
} }

View File

@ -19,33 +19,10 @@ typedef struct
{ {
unsigned pitchCnt; // unsigned pitchCnt; //
unsigned* pitchV; // pitchV[pitchCnt] unsigned* pitchV; // pitchV[pitchCnt]
unsigned scIdx; // index of the score event (into cmScoreEvt[]) at this location unsigned scIdx; // index of the score event (into cmScoreEvt[]) at this location
int barNumb; // bar number of this location int barNumb; // bar number of this location
} cmScFolLoc_t; } 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 typedef struct
{ {
@ -56,21 +33,23 @@ typedef struct
cmScFolBufEle_t* bufV; // event buffer bufV[bufN] - bufV[bufN-1] newest event, bufV[boi] oldest event cmScFolBufEle_t* bufV; // event buffer bufV[bufN] - bufV[bufN-1] newest event, bufV[boi] oldest event
int locN; // count of score locations int locN; // count of score locations
cmScFolLoc_t* loc; // score loc[locN] cmScFolLoc_t* loc; // score loc[locN]
unsigned sbi; // oldest score window index unsigned sbi; // oldest score window index
unsigned sei; // newest score window index unsigned sei; // newest score window index
unsigned msln; // minimum score look ahead count unsigned msln; // minimum score look ahead count
unsigned mswn; // maximum score window length unsigned mswn; // maximum score window length
unsigned minVel; // notes < minVel are ignored
//unsigned evalWndN; // (dflt:5) count of elements to use for refined match window unsigned missCnt; // current consecutive unmatched notes
//unsigned allowedMissCnt; // (dflt:1) count of non-match elements in refined match where a match is still signaled 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; unsigned* edWndMtx;
} cmScFol; } 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 // 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 ); cmScFol* cmScFolAlloc( cmCtx* ctx, cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH );
cmRC_t cmScFolFree( cmScFol** pp ); cmRC_t cmScFolFree( cmScFol** pp );

View File

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