libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmProc4.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmFloatTypes.h"
  10. #include "cmComplexTypes.h"
  11. #include "cmFileSys.h"
  12. #include "cmJson.h"
  13. #include "cmSymTbl.h"
  14. #include "cmAudioFile.h"
  15. #include "cmProcObj.h"
  16. #include "cmProcTemplate.h"
  17. #include "cmMath.h"
  18. #include "cmProc.h"
  19. #include "cmVectOps.h"
  20. #include "cmMidi.h"
  21. #include "cmMidiFile.h"
  22. #include "cmTimeLine.h"
  23. #include "cmScore.h"
  24. #include "cmProc4.h"
  25. cmScFol* cmScFolAlloc( cmCtx* c, cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  26. {
  27. cmScFol* op = cmObjAlloc(cmScFol,c,p);
  28. if( srate != 0 )
  29. if( cmScFolInit(op,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel) != cmOkRC )
  30. cmScFolFree(&op);
  31. return op;
  32. }
  33. cmRC_t cmScFolFree( cmScFol** pp )
  34. {
  35. cmRC_t rc = cmOkRC;
  36. if( pp==NULL || *pp==NULL )
  37. return rc;
  38. cmScFol* p = *pp;
  39. if((rc = cmScFolFinal(p)) != cmOkRC )
  40. return rc;
  41. unsigned i;
  42. for(i=0; i<p->locN; ++i)
  43. cmMemFree(p->loc[i].pitchV);
  44. cmMemFree(p->loc);
  45. cmMemFree(p->bufV);
  46. cmObjFree(pp);
  47. return rc;
  48. }
  49. cmRC_t cmScFolFinal( cmScFol* p )
  50. {
  51. cmMemFree(p->edWndMtx);
  52. return cmOkRC;
  53. }
  54. void _cmScFolPrint( cmScFol* p )
  55. {
  56. int i,j;
  57. for(i=0; i<p->locN; ++i)
  58. {
  59. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scIdx);
  60. for(j=0; j<p->loc[i].pitchCnt; ++j)
  61. printf("%s ",cmMidiToSciPitch(p->loc[i].pitchV[j],NULL,0));
  62. printf("\n");
  63. }
  64. }
  65. unsigned* _cmScFolAllocEditDistMtx(unsigned maxN)
  66. {
  67. maxN += 1;
  68. unsigned* m = cmMemAllocZ(unsigned,maxN*maxN);
  69. unsigned* p = m;
  70. unsigned i;
  71. // initialize the comparison matrix with the default costs in the
  72. // first row and column
  73. // (Note that this matrix is not oriented in column major order like most 'cm' matrices.)
  74. for(i=0; i<maxN; ++i)
  75. {
  76. p[i] = i; // 0th row
  77. p[ i * maxN ] = i; // 0th col
  78. }
  79. return m;
  80. }
  81. cmRC_t cmScFolInit( cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  82. {
  83. cmRC_t rc;
  84. if((rc = cmScFolFinal(p)) != cmOkRC )
  85. return rc;
  86. if( bufN > maxWndCnt )
  87. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower buffer count (%i) must be less than the max. window length (%i).",bufN,maxWndCnt );
  88. if( minWndLookAhead > maxWndCnt )
  89. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower look-ahead count (%i) must be less than the max. window length (%i).",minWndLookAhead,maxWndCnt);
  90. p->srate = srate;
  91. p->scH = scH;
  92. p->bufN = bufN;
  93. p->bufV = cmMemResizeZ(cmScFolBufEle_t,p->bufV,bufN);
  94. p->locN = cmScoreEvtCount(scH);
  95. p->loc = cmMemResizeZ(cmScFolLoc_t,p->loc,p->locN);
  96. p->sbi = cmInvalidIdx;
  97. p->sei = cmInvalidIdx;
  98. p->msln = minWndLookAhead;
  99. p->mswn = maxWndCnt;
  100. p->forwardCnt = 2;
  101. p->maxDist = 4;
  102. p->edWndMtx = _cmScFolAllocEditDistMtx(p->bufN);
  103. p->minVel = minVel;
  104. p->printFl = true;
  105. p->noBackFl = true;
  106. p->missCnt = 0;
  107. p->matchCnt = 0;
  108. p->eventIdx = 0;
  109. p->skipCnt = 0;
  110. p->ret_idx = cmInvalidIdx;
  111. int i,n;
  112. double maxDSecs = 0; // max time between score entries to be considered simultaneous
  113. cmScoreEvt_t* e0p = NULL;
  114. int j0 = 0;
  115. // for each score event
  116. for(i=0,n=0; i<p->locN; ++i)
  117. {
  118. cmScoreEvt_t* ep = cmScoreEvt(scH,i);
  119. // if the event is not a note then ignore it
  120. if( ep->type == kNonEvtScId )
  121. {
  122. assert( j0+n < p->locN );
  123. p->loc[j0+n].scIdx = i;
  124. p->loc[j0+n].barNumb = ep->barNumb;
  125. // if the first event has not yet been selected
  126. if( e0p == NULL )
  127. {
  128. e0p = ep;
  129. n = 1;
  130. }
  131. else
  132. {
  133. // time can never reverse
  134. assert( ep->secs >= e0p->secs );
  135. // calc seconds between first event and current event
  136. double dsecs = ep->secs - e0p->secs;
  137. // if the first event and current event are simultaneous...
  138. if( dsecs <= maxDSecs )
  139. ++n; // ... incr. the count of simultaneous events
  140. else
  141. {
  142. int k;
  143. // ... a complete set of simultaneous events have been located
  144. // duplicate all the events at each of their respective time locations
  145. for(k=0; k<n; ++k)
  146. {
  147. int m;
  148. assert( j0+k < p->locN );
  149. p->loc[j0+k].pitchCnt = n;
  150. p->loc[j0+k].pitchV = cmMemAllocZ(unsigned,n);
  151. for(m=0; m<n; ++m)
  152. {
  153. cmScoreEvt_t* tp = cmScoreEvt(scH,p->loc[j0+m].scIdx);
  154. assert(tp!=NULL);
  155. p->loc[j0+k].pitchV[m] = tp->pitch;
  156. }
  157. }
  158. e0p = ep;
  159. j0 += n;
  160. n = 1;
  161. }
  162. }
  163. }
  164. }
  165. p->locN = j0;
  166. //_cmScFolPrint(p);
  167. return rc;
  168. }
  169. cmRC_t cmScFolReset( cmScFol* p, unsigned scoreIndex )
  170. {
  171. int i;
  172. // empty the event buffer
  173. memset(p->bufV,0,sizeof(cmScFolBufEle_t)*p->bufN);
  174. // don't allow the score index to be prior to the first note
  175. if( scoreIndex < p->loc[0].scIdx )
  176. scoreIndex = p->loc[0].scIdx;
  177. p->sei = cmInvalidIdx;
  178. p->sbi = cmInvalidIdx;
  179. p->missCnt = 0;
  180. p->matchCnt = 0;
  181. p->eventIdx = 0;
  182. p->skipCnt = 0;
  183. p->ret_idx = cmInvalidIdx;
  184. // locate the score element in svV[] that is closest to,
  185. // and possibly after, scoreIndex.
  186. for(i=0; i<p->locN-1; ++i)
  187. if( p->loc[i].scIdx <= scoreIndex && scoreIndex < p->loc[i+1].scIdx )
  188. {
  189. p->sbi = i;
  190. break;
  191. }
  192. // locate the score element at the end of the look-ahead region
  193. for(; i<p->locN-1; ++i)
  194. if( p->loc[i].scIdx <= scoreIndex + p->msln && scoreIndex + p->msln < p->loc[i+1].scIdx )
  195. {
  196. p->sei = i;
  197. break;
  198. }
  199. return cmOkRC;
  200. }
  201. bool _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
  202. {
  203. unsigned i;
  204. for(i=0; i<loc->pitchCnt; ++i)
  205. if( loc->pitchV[i] == pitch )
  206. return true;
  207. return false;
  208. }
  209. int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const cmScFolBufEle_t* pitch, unsigned pi )
  210. {
  211. if( _cmScFolIsMatch(loc+li,pitch[pi].val) )
  212. return 0;
  213. if( li>0 && pi>0 )
  214. if( _cmScFolIsMatch(loc+li-1,pitch[pi].val) && _cmScFolIsMatch(loc+li,pitch[pi-1].val) )
  215. return 0;
  216. return 1;
  217. }
  218. int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const cmScFolBufEle_t* s1, const cmScFolLoc_t* s0, int n )
  219. {
  220. mtxMaxN += 1;
  221. assert( n < mtxMaxN );
  222. int v = 0;
  223. unsigned i;
  224. // Note that m[maxN,maxN] is not oriented in column major order like most 'cm' matrices.
  225. for(i=1; i<n+1; ++i)
  226. {
  227. unsigned ii = i * mtxMaxN; // current row
  228. unsigned i_1 = ii - mtxMaxN; // previous row
  229. unsigned j;
  230. for( j=1; j<n+1; ++j)
  231. {
  232. //int cost = s0[i-1] == s1[j-1] ? 0 : 1;
  233. //int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
  234. int cost = _cmScFolMatchCost(s0,i-1,s1,j-1);
  235. //m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
  236. m[ ii + j ] = v = cmMin( m[ i_1 + j] + 1, cmMin( m[ ii + j - 1] + 1, m[ i_1 + j - 1 ] + cost ) );
  237. }
  238. }
  239. return v;
  240. }
  241. void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, const cmScFolBufEle_t* b, unsigned bn, unsigned min_idx )
  242. {
  243. unsigned i;
  244. int n;
  245. printf("--------------- event:%i ------------- \n",p->eventIdx);
  246. printf("loc: ");
  247. for(i=0; i<locN; ++i)
  248. printf("%4i ",i+locIdx);
  249. printf("\n");
  250. for(n=0,i=0; i<locN; ++i)
  251. if( p->loc[locIdx+i].pitchCnt > n )
  252. n = p->loc[locIdx+i].pitchCnt;
  253. --n;
  254. for(; n>=0; --n)
  255. {
  256. printf("sc%1i: ",n);
  257. for(i=0; i<locN; ++i)
  258. {
  259. if( n < p->loc[locIdx+i].pitchCnt )
  260. printf("%4s ",cmMidiToSciPitch(p->loc[locIdx+i].pitchV[n],NULL,0));
  261. else
  262. printf(" ");
  263. }
  264. printf("\n");
  265. }
  266. printf("perf:");
  267. for(i=0; i<min_idx; ++i)
  268. printf(" ");
  269. for(i=0; i<bn; ++i)
  270. printf("%4s ",cmMidiToSciPitch(b[i].val,NULL,0));
  271. printf("\n");
  272. }
  273. void _cmScFolRpt1( cmScFol*p, unsigned minDist, unsigned ret_idx, unsigned d1, unsigned missCnt, unsigned matchCnt )
  274. {
  275. printf("dist:%i miss:%i match:%i skip:%i vel:%i ",minDist,missCnt,matchCnt,p->skipCnt,d1);
  276. if( ret_idx != cmInvalidIdx )
  277. printf("ret_idx:%i ",ret_idx);
  278. printf("\n");
  279. }
  280. unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  281. {
  282. unsigned ret_idx = cmInvalidIdx;
  283. //unsigned ebuf[ p->bufN ];
  284. if( status != kNoteOnMdId )
  285. return ret_idx;
  286. ++p->eventIdx;
  287. // reject notes with very low velocity
  288. if( d1 < p->minVel )
  289. {
  290. ++p->skipCnt;
  291. return ret_idx;
  292. }
  293. // left shift bufV[] to make the right-most element available - then copy in the new element
  294. memmove(p->bufV, p->bufV+1, sizeof(cmScFolBufEle_t)*(p->bufN-1));
  295. p->bufV[ p->bufN-1 ].smpIdx = smpIdx;
  296. p->bufV[ p->bufN-1 ].val = d0;
  297. p->bufV[ p->bufN-1 ].validFl= true;
  298. // fill in ebuf[] with the valid values in bufV[]
  299. int en = cmMin(p->eventIdx,p->bufN);
  300. int i = p->eventIdx>=p->bufN ? 0 : p->bufN-p->eventIdx-1;
  301. /*
  302. int i = p->bufN-1;
  303. int en = 0;
  304. for(; i>=0; --i,++en)
  305. {
  306. if( p->bufV[i].validFl)
  307. ebuf[i] = p->bufV[i].val;
  308. else
  309. break;
  310. }
  311. ++i; // increment i to the first valid element in ebuf[].
  312. */
  313. // en is the count of valid elements in ebuf[].
  314. // ebuf[p->boi] is the first valid element
  315. int j = 0;
  316. int minDist = INT_MAX;
  317. int minIdx = cmInvalidIdx;
  318. int dist;
  319. // the score wnd must always be as long as the buffer n
  320. // at the end of the score this may not be the case
  321. // (once sei hits locN - at this point we must begin
  322. // shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
  323. assert( p->sei-p->sbi+1 >= en );
  324. for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
  325. {
  326. // use <= minDist to choose the latest window with the lowest match
  327. if((dist = _cmScFolDist(p->bufN, p->edWndMtx, p->bufV+i, p->loc + p->sbi+j, en )) < minDist )
  328. {
  329. // only make an eql match if the posn is greater than the last location
  330. if( dist==minDist && p->ret_idx != cmInvalidId && p->ret_idx >= p->sbi+minIdx+en-1 )
  331. continue;
  332. minDist = dist;
  333. minIdx = j;
  334. }
  335. }
  336. // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
  337. if( p->printFl )
  338. _cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, p->bufV+i, en, minIdx );
  339. // save current missCnt for later printing
  340. unsigned missCnt = p->missCnt;
  341. // if a perfect match occurred
  342. if( minDist == 0 )
  343. {
  344. ret_idx = p->sbi + minIdx + en - 1;
  345. p->missCnt = 0;
  346. // we had a perfect match - shrink the window to it's minumum size
  347. p->sbi += (en==p->bufN) ? minIdx + 1 : 0; // move wnd begin forward to just past first match
  348. p->sei = p->sbi + minIdx + en + p->msln; // move wnd end forward to lead by the min look-ahead
  349. }
  350. else
  351. {
  352. if( minDist > p->maxDist )
  353. ret_idx = cmInvalidIdx;
  354. else
  355. // if the last event matched - then return the match location as the current score location
  356. if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),p->bufV[p->bufN-1].val) )
  357. {
  358. ret_idx = p->sbi + minIdx + en - 1;
  359. p->missCnt = 0;
  360. // this is probably a pretty good match reduce the part of the window prior to
  361. // the first match (bring the end of the window almost up to the end of the
  362. // buffers sync position)
  363. if( en >= p->bufN-1 && (en+2) <= ret_idx )
  364. p->sbi = ret_idx - (en+2);
  365. }
  366. else // the last event does not match based on the optimal edit-distance alignment
  367. {
  368. // Look backward from the closest match location for a match to the current pitch.
  369. // The backward search scope is limited by the current value of 'missCnt'.
  370. j = p->sbi+minIdx+en-2;
  371. for(i=1; i+1 <= p->bufN && j>=p->sbi && i<=p->missCnt; ++i,--j)
  372. {
  373. // if this look-back location already matched then stop the backward search
  374. if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1-i].val))
  375. break;
  376. // does this look-back location match the current pitch
  377. if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val))
  378. {
  379. ret_idx = j;
  380. p->missCnt = i; // set missCnt to the cnt of steps backward necessary for a match
  381. break;
  382. }
  383. }
  384. // If the backward search did not find a match - look forward
  385. if( ret_idx == cmInvalidIdx )
  386. {
  387. j = p->sbi+minIdx+en;
  388. for(i=0; j<=p->sei && i<p->forwardCnt; ++i,++j)
  389. if( _cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val) )
  390. {
  391. ret_idx = j;
  392. break;
  393. }
  394. p->missCnt = ret_idx == cmInvalidIdx ? p->missCnt + 1 : 0;
  395. }
  396. }
  397. // Adjust the end window position (sei) based on the match location
  398. if( ret_idx == cmInvalidIdx )
  399. {
  400. // even though we didn't match move the end of the score window forward
  401. // this will enlarge the score window by one
  402. p->sei += 1;
  403. }
  404. else
  405. {
  406. assert( p->sei>=ret_idx);
  407. // force sei to lead by min look-ahead
  408. if( p->sei - ret_idx < p->msln )
  409. p->sei = ret_idx + p->msln;
  410. }
  411. assert( p->sei > p->sbi );
  412. // Adjust the begin window position
  413. if( p->noBackFl && ret_idx != cmInvalidIdx && en>=p->bufN && p->sbi > p->bufN )
  414. p->sbi = ret_idx - p->bufN;
  415. // if the score window length surpasses the max score window size
  416. // move the beginning index forward
  417. if( p->sei - p->sbi + 1 > p->mswn && p->sei > p->mswn )
  418. p->sbi = p->sei - p->mswn + 1;
  419. }
  420. if( p->printFl )
  421. _cmScFolRpt1(p, minDist, ret_idx, d1, missCnt, p->matchCnt );
  422. // don't allow the returned location to repeat or go backwards
  423. if( p->noBackFl && p->ret_idx != cmInvalidIdx && ret_idx <= p->ret_idx )
  424. ret_idx = cmInvalidIdx;
  425. // track the number of consecutive matches
  426. if( ret_idx == cmInvalidIdx )
  427. p->matchCnt = 0;
  428. else
  429. {
  430. ++p->matchCnt;
  431. p->ret_idx = ret_idx;
  432. }
  433. // Force the window to remain valid when it is at the end of the score
  434. // - sbi and sei must be inside 0:locN
  435. // - sei-sbi + 1 must be >= en
  436. if( p->sei >= p->locN )
  437. {
  438. p->sei = p->locN - 1;
  439. p->sbi = p->sei - p->bufN + 1;
  440. }
  441. return ret_idx;
  442. }