Browse Source

cmProc4.h/c Major score-follower development.

master
kevin 11 years ago
parent
commit
698f987558
2 changed files with 156 additions and 74 deletions
  1. 128
    52
      cmProc4.c
  2. 28
    22
      cmProc4.h

+ 128
- 52
cmProc4.c View File

@@ -25,11 +25,11 @@
25 25
 
26 26
 
27 27
 
28
-cmScFol* cmScFolAlloc( cmCtx* c, cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH )
28
+cmScFol* cmScFolAlloc( cmCtx* c, cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
29 29
 {
30 30
   cmScFol* op = cmObjAlloc(cmScFol,c,p);
31 31
   if( srate != 0 )
32
-    if( cmScFolInit(op,srate,bufN,wndMs,scH) != cmOkRC )
32
+    if( cmScFolInit(op,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel) != cmOkRC )
33 33
       cmScFolFree(&op);
34 34
   return op;
35 35
 }
@@ -73,12 +73,38 @@ void _cmScFolPrint( cmScFol* p )
73 73
   }
74 74
 }
75 75
 
76
-cmRC_t   cmScFolInit(  cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH )
76
+unsigned* _cmScFolAllocEditDistMtx(unsigned maxN)
77
+{
78
+  maxN += 1;
79
+
80
+  unsigned* m = cmMemAllocZ(unsigned,maxN*maxN);
81
+  unsigned* p = m;
82
+  unsigned i;
83
+
84
+  // initialize the comparison matrix with the default costs in the
85
+  // first row and column
86
+  // (Note that this matrix is not oriented in column major order like most 'cm' matrices.)
87
+  for(i=0; i<maxN; ++i)
88
+  {
89
+    p[i]          = i;		// 0th row
90
+    p[ i * maxN ] = i;		// 0th col
91
+  }
92
+
93
+  return m;
94
+}
95
+
96
+cmRC_t   cmScFolInit(  cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
77 97
 {
78 98
   cmRC_t rc;
79 99
   if((rc = cmScFolFinal(p)) != cmOkRC )
80 100
     return rc;
81 101
 
102
+  if( bufN > maxWndCnt )
103
+    return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower buffer count (%i) must be less than the max. window length (%i).",bufN,maxWndCnt );
104
+
105
+  if( minWndLookAhead > maxWndCnt )
106
+    return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower look-ahead count (%i) must be less than the max. window length (%i).",minWndLookAhead,maxWndCnt); 
107
+
82 108
   p->srate          = srate;
83 109
   p->scH            = scH;
84 110
   p->bufN           = bufN;
@@ -87,14 +113,19 @@ cmRC_t   cmScFolInit(  cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs
87 113
   p->loc            = cmMemResizeZ(cmScFolLoc_t,p->loc,p->locN);
88 114
   p->sbi            = cmInvalidIdx;
89 115
   p->sei            = cmInvalidIdx;
90
-  p->msln           = 7;
91
-  p->mswn           = 30;
92
-  p->edWndMtx       = cmVOU_LevEditDistAllocMtx(p->bufN);
93
-  p->minVel         = 5;
116
+  p->msln           = minWndLookAhead;
117
+  p->mswn           = maxWndCnt;
118
+  p->forwardCnt     = 2;
119
+  p->maxDist        = 4;
120
+  p->edWndMtx       = _cmScFolAllocEditDistMtx(p->bufN);
121
+  p->minVel         = minVel;
122
+  p->printFl        = true;
123
+  p->noBackFl       = true;
94 124
   p->missCnt        = 0;
95 125
   p->matchCnt       = 0;
96
-  p->printFl        = true;
97 126
   p->eventIdx       = 0;
127
+  p->skipCnt        = 0;
128
+  p->ret_idx        = cmInvalidIdx;
98 129
 
99 130
   int i,n;
100 131
   double        maxDSecs = 0;   // max time between score entries to be considered simultaneous
@@ -171,7 +202,6 @@ cmRC_t   cmScFolReset(   cmScFol* p, unsigned scoreIndex )
171 202
 {
172 203
   int i;
173 204
 
174
-
175 205
   // empty the event buffer
176 206
   memset(p->bufV,0,sizeof(cmScFolBufEle_t)*p->bufN);
177 207
 
@@ -179,8 +209,13 @@ cmRC_t   cmScFolReset(   cmScFol* p, unsigned scoreIndex )
179 209
   if( scoreIndex < p->loc[0].scIdx )
180 210
     scoreIndex = p->loc[0].scIdx;
181 211
 
182
-  p->sei = cmInvalidIdx;
183
-  p->sbi = cmInvalidIdx;
212
+  p->sei      = cmInvalidIdx;
213
+  p->sbi      = cmInvalidIdx;
214
+  p->missCnt  = 0;
215
+  p->matchCnt = 0;
216
+  p->eventIdx = 0;
217
+  p->skipCnt  = 0;
218
+  p->ret_idx  = cmInvalidIdx;
184 219
 
185 220
   // locate the score element in svV[] that is closest to,
186 221
   // and possibly after, scoreIndex.
@@ -212,19 +247,19 @@ bool  _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
212 247
   return false;
213 248
 }
214 249
 
215
-int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const unsigned* pitch, unsigned pi )
250
+int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const cmScFolBufEle_t* pitch, unsigned pi )
216 251
 {
217
-  if( _cmScFolIsMatch(loc+li,pitch[pi]) )
252
+  if( _cmScFolIsMatch(loc+li,pitch[pi].val) )
218 253
     return 0;
219 254
   
220 255
   if( li>0 && pi>0 )
221
-    if( _cmScFolIsMatch(loc+li-1,pitch[pi]) && _cmScFolIsMatch(loc+li,pitch[pi-1]) )
256
+    if( _cmScFolIsMatch(loc+li-1,pitch[pi].val) && _cmScFolIsMatch(loc+li,pitch[pi-1].val) )
222 257
       return 0;
223 258
 
224 259
   return 1;
225 260
 }
226 261
 
227
-int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFolLoc_t* s0, int n )
262
+int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const cmScFolBufEle_t* s1, const cmScFolLoc_t* s0, int n )
228 263
 {
229 264
   mtxMaxN += 1;
230 265
 
@@ -253,7 +288,7 @@ int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFo
253 288
   return v;		
254 289
 }
255 290
 
256
-void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, unsigned* b, unsigned bn, unsigned min_idx )
291
+void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, const cmScFolBufEle_t* b, unsigned bn, unsigned min_idx )
257 292
 {
258 293
   unsigned i;
259 294
   int      n;
@@ -288,28 +323,24 @@ void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, unsigned* b, unsi
288 323
     printf("     ");
289 324
 
290 325
   for(i=0; i<bn; ++i)
291
-    printf("%4s ",cmMidiToSciPitch(b[i],NULL,0));
326
+    printf("%4s ",cmMidiToSciPitch(b[i].val,NULL,0));
292 327
 
293 328
   printf("\n");
294 329
 }
295 330
 
296 331
 void _cmScFolRpt1( cmScFol*p, unsigned minDist, unsigned ret_idx, unsigned d1, unsigned missCnt, unsigned matchCnt )
297 332
 {
298
-
299
-  printf("dist:%i miss:%i match:%i ",minDist,missCnt,matchCnt);
300
-  if( ret_idx == cmInvalidIdx )
301
-    printf("vel:%i ", d1 );
302
-  else
333
+  printf("dist:%i miss:%i match:%i skip:%i vel:%i ",minDist,missCnt,matchCnt,p->skipCnt,d1);
334
+  if( ret_idx != cmInvalidIdx )
303 335
     printf("ret_idx:%i ",ret_idx);
304 336
   printf("\n");
305
-
306 337
 }
307 338
 
308 339
 unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
309 340
 {
310 341
 
311 342
   unsigned ret_idx = cmInvalidIdx;
312
-  unsigned ebuf[ p->bufN ];
343
+  //unsigned ebuf[ p->bufN ];
313 344
 
314 345
   if( status != kNoteOnMdId )
315 346
     return ret_idx;
@@ -318,7 +349,10 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
318 349
 
319 350
   // reject notes with very low velocity
320 351
   if( d1 < p->minVel )
352
+  {
353
+    ++p->skipCnt;
321 354
     return ret_idx;
355
+  }
322 356
 
323 357
   // left shift bufV[] to make the right-most element available - then copy in the new element
324 358
   memmove(p->bufV, p->bufV+1, sizeof(cmScFolBufEle_t)*(p->bufN-1));
@@ -327,7 +361,11 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
327 361
   p->bufV[ p->bufN-1 ].validFl= true;
328 362
 
329 363
   // fill in ebuf[] with the valid values in bufV[]
330
-  int i = p->bufN-1;
364
+  int en = cmMin(p->eventIdx,p->bufN);
365
+  int i  = p->eventIdx>=p->bufN ? 0 : p->bufN-p->eventIdx-1;
366
+
367
+  /*
368
+  int i  = p->bufN-1;
331 369
   int en = 0;
332 370
   for(; i>=0; --i,++en)
333 371
   {
@@ -337,6 +375,8 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
337 375
       break;
338 376
   }
339 377
   ++i;  // increment i to the first valid element in ebuf[].
378
+  */
379
+
340 380
 
341 381
 
342 382
   // en is the count of valid elements in ebuf[].
@@ -356,8 +396,12 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
356 396
   for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
357 397
   {
358 398
     // use <= minDist to choose the latest window with the lowest match
359
-    if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) <= minDist )
399
+    if((dist = _cmScFolDist(p->bufN, p->edWndMtx, p->bufV+i, p->loc + p->sbi+j, en )) < minDist )
360 400
     {
401
+      // only make an eql match if the posn is greater than the last location 
402
+      if( dist==minDist && p->ret_idx != cmInvalidId && p->ret_idx >= p->sbi+minIdx+en-1 )
403
+        continue;
404
+
361 405
       minDist = dist;
362 406
       minIdx  = j;
363 407
     }
@@ -366,7 +410,7 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
366 410
   // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
367 411
 
368 412
   if( p->printFl )
369
-    _cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, ebuf+i, en, minIdx );
413
+    _cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, p->bufV+i, en, minIdx );
370 414
     
371 415
   // save current missCnt for later printing
372 416
   unsigned missCnt = p->missCnt;
@@ -374,31 +418,34 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
374 418
   // if a perfect match occurred
375 419
   if( minDist == 0 )
376 420
   {
421
+    ret_idx = p->sbi + minIdx + en - 1;       
422
+    p->missCnt = 0;
423
+
377 424
     // we had a perfect match - shrink the window to it's minumum size
378 425
     p->sbi += (en==p->bufN) ? minIdx + 1 : 0;  // move wnd begin forward to just past first match
379
-    p->sei  = p->sbi + minIdx + en + p->msln;  // move wnd end forward to just past last match
426
+    p->sei  = p->sbi + minIdx + en + p->msln;  // move wnd end forward to lead by the min look-ahead
380 427
     
381
-    // BUG BUG BUG - is the window length valid - 
382
-    //  - sbi and sei must be inside 0:locN
383
-    //  - sei-sbi + 1 must be >= en
384
-    ret_idx = p->sbi + minIdx + en - 1;       
385
-    p->missCnt = 0;
386 428
 
387 429
   }
388 430
   else
389 431
   {
432
+    if( minDist > p->maxDist )
433
+      ret_idx = cmInvalidIdx;
434
+    else
390 435
     // if the last event matched - then return the match location as the current score location
391
-    if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ebuf[p->bufN-1]) )
436
+    if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),p->bufV[p->bufN-1].val) )
392 437
     {
393 438
       ret_idx    = p->sbi + minIdx + en - 1;
394 439
       p->missCnt = 0;
395 440
 
441
+      // this is probably a pretty good match reduce the part of the window prior to 
442
+      // the first match (bring the end of the window almost up to the end of the 
443
+      // buffers sync position)
396 444
       if( en >= p->bufN-1 && (en+2) <= ret_idx )
397 445
         p->sbi = ret_idx - (en+2);
398
-        
399
-      
446
+              
400 447
     }
401
-    else // the last event does not match based on the optimal  edit-distance alignment
448
+    else // the last event does not match based on the optimal edit-distance alignment
402 449
     {
403 450
       // Look backward from the closest match location for a match to the current pitch.
404 451
       // The backward search scope is limited by the current value of 'missCnt'.
@@ -407,11 +454,11 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
407 454
       for(i=1; i+1 <= p->bufN && j>=p->sbi && i<=p->missCnt; ++i,--j)
408 455
       {
409 456
         // if this look-back location already matched then stop the backward search
410
-        if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1-i]))
457
+        if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1-i].val))
411 458
           break;
412 459
         
413 460
         // does this look-back location match the current pitch
414
-        if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]))
461
+        if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val))
415 462
         {
416 463
           ret_idx    = j;
417 464
           p->missCnt = i;  // set missCnt to the cnt of steps backward necessary for a match
@@ -423,8 +470,8 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
423 470
       if( ret_idx == cmInvalidIdx )
424 471
       {
425 472
         j = p->sbi+minIdx+en;
426
-        for(i=0; j<=p->sei && i<2; ++i,++j)
427
-          if( _cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]) )
473
+        for(i=0; j<=p->sei && i<p->forwardCnt; ++i,++j)
474
+          if( _cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val) )
428 475
           {
429 476
             ret_idx = j;
430 477
             break;
@@ -435,36 +482,65 @@ unsigned   cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByt
435 482
         
436 483
     }
437 484
 
438
-    // even though we didn't match move the end of the score window forward
439
-    // this will enlarge the score window by one
440
-     p->sei += 1;
485
+    // Adjust the end window position (sei)  based on the match location
486
+    if( ret_idx == cmInvalidIdx )
487
+    {
488
+      // even though we didn't match move the end of the score window forward
489
+      // this will enlarge the score window by one
490
+      p->sei += 1;
491
+    }
492
+    else
493
+    {
494
+      assert( p->sei>=ret_idx);
495
+
496
+      // force sei to lead by min look-ahead
497
+      if( p->sei - ret_idx < p->msln )
498
+        p->sei = ret_idx + p->msln;
499
+
500
+    }
441 501
 
442 502
     assert( p->sei > p->sbi );
443 503
 
504
+    // Adjust the begin window position
505
+    if( p->noBackFl && ret_idx != cmInvalidIdx && en>=p->bufN && p->sbi > p->bufN )
506
+      p->sbi = ret_idx - p->bufN;
507
+
444 508
     // if the score window length surpasses the max score window size 
445 509
     // move the beginning index forward 
446 510
     if( p->sei - p->sbi + 1 > p->mswn && p->sei > p->mswn )
447 511
       p->sbi = p->sei - p->mswn + 1;
448 512
 
449
-    // BUG BUG BUG - is the window length valid - 
450
-    //  - sbi and sei must be inside 0:locN
451
-    //  - sei-sbi + 1 must be >= en
452 513
 
453 514
   }
454 515
 
455 516
   if( p->printFl )
456 517
     _cmScFolRpt1(p, minDist, ret_idx, d1, missCnt, p->matchCnt );
457 518
 
519
+  // don't allow the returned location to repeat or go backwards
520
+  if( p->noBackFl && p->ret_idx != cmInvalidIdx && ret_idx <= p->ret_idx )
521
+    ret_idx = cmInvalidIdx;
522
+
523
+
524
+  // track the number of consecutive matches
458 525
   if( ret_idx == cmInvalidIdx )
459 526
     p->matchCnt = 0;
460 527
   else
528
+  {
461 529
     ++p->matchCnt;
462 530
 
463
-  
464
-  // BUG BUG BUG - this is not currently guarded against
465
-  assert( p->sei < p->locN );
466
-  assert( p->sbi < p->locN );
467
-    
531
+    p->ret_idx = ret_idx;
532
+  }
533
+
534
+  // Force the window to remain valid when it is at the end of the score 
535
+  //  - sbi and sei must be inside 0:locN
536
+  //  - sei-sbi + 1 must be >= en
537
+  if( p->sei >= p->locN )
538
+  {
539
+    p->sei = p->locN - 1;
540
+    p->sbi = p->sei  - p->bufN + 1;
541
+  }
542
+
543
+
468 544
 
469 545
   return ret_idx;
470 546
 }

+ 28
- 22
cmProc4.h View File

@@ -27,35 +27,41 @@ typedef struct
27 27
 typedef struct
28 28
 {
29 29
   cmObj            obj;
30
-  cmReal_t         srate;   //
31
-  cmScH_t          scH;     // score handle
32
-  unsigned         bufN;    // event buffer count 
33
-  cmScFolBufEle_t* bufV;    // event buffer bufV[bufN] - bufV[bufN-1] newest event, bufV[boi] oldest event
34
-  int              locN;    // count of score locations
35
-  cmScFolLoc_t*    loc;     // score loc[locN]
36
-  unsigned         sbi;     // oldest score window index
37
-  unsigned         sei;     // newest score window index
38
-  unsigned         msln;    // minimum score look ahead count
39
-  unsigned         mswn;    // maximum score window length
40
-  unsigned         minVel;  // notes < minVel are ignored
41
-  unsigned         missCnt; // current consecutive unmatched notes
42
-  unsigned         matchCnt;// current consecutive matched notes
43
-  bool             printFl; // true if pitch tracker reporting should be included
44
-  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
45
-  unsigned         eventIdx;// events since init
30
+  cmReal_t         srate;      //
31
+  cmScH_t          scH;        // score handle
32
+  unsigned         bufN;       // event buffer count 
33
+  cmScFolBufEle_t* bufV;       // event buffer bufV[bufN] - bufV[bufN-1] newest event, bufV[boi] oldest event
34
+  int              locN;       // count of score locations
35
+  cmScFolLoc_t*    loc;        // score loc[locN]
36
+  unsigned         sbi;        // oldest score window index
37
+  unsigned         sei;        // newest score window index
38
+  unsigned         msln;       // minimum score look ahead count
39
+  unsigned         mswn;       // maximum score window length
40
+  unsigned         forwardCnt; // 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
41
+  unsigned         maxDist;    // max. dist allowed to still  consider matching
42
+  unsigned         minVel;     // notes < minVel are ignored
43
+  bool             printFl;    // true if pitch tracker reporting should be included
44
+  bool             noBackFl;   // prevent the tracker from going backwards in time
46 45
   unsigned*        edWndMtx;
46
+
47
+  unsigned         missCnt;    // current consecutive unmatched notes
48
+  unsigned         matchCnt;   // current consecutive matched notes
49
+  unsigned         eventIdx;   // events since reset
50
+  unsigned         skipCnt;    // notes skipped due to velocity
51
+  unsigned         ret_idx;    // last tracked location
47 52
   
48 53
 } cmScFol;
49 54
 
50
-
51
-// bufN   = max count of elements in the event buffer.
52
-// wndMs  = max length of the score following window in time
53
-
54
-cmScFol* cmScFolAlloc( cmCtx* ctx, cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH );
55
+cmScFol* cmScFolAlloc( cmCtx* ctx, cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel );
55 56
 cmRC_t   cmScFolFree(  cmScFol** pp );
56
-cmRC_t   cmScFolInit(  cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH );
57
+cmRC_t   cmScFolInit(  cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel );
57 58
 cmRC_t   cmScFolFinal( cmScFol* p );
59
+
60
+// Jump to a score location and reset the internal state of the follower.
58 61
 cmRC_t   cmScFolReset( cmScFol* p, unsigned scoreIndex );
62
+
63
+// Give the follower a MIDI performance event. Only MIDI note-on events are acted upon;
64
+// all others are ignored.
59 65
 unsigned cmScFolExec(  cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 );
60 66
   
61 67
 

Loading…
Cancel
Save