libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmProc4.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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, unsigned bufN, cmReal_t wndMs, cmScH_t scH )
  26. {
  27. cmScFol* op = cmObjAlloc(cmScFol,c,p);
  28. if( srate != 0 )
  29. if( cmScFolInit(op,srate,bufN,wndMs,scH) != 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. cmRC_t cmScFolInit( cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH )
  66. {
  67. cmRC_t rc;
  68. if((rc = cmScFolFinal(p)) != cmOkRC )
  69. return rc;
  70. p->srate = srate;
  71. p->scH = scH;
  72. p->bufN = bufN;
  73. p->bufV = cmMemResizeZ(cmScFolBufEle_t,p->bufV,bufN);
  74. p->locN = cmScoreEvtCount(scH);
  75. p->loc = cmMemResizeZ(cmScFolLoc_t,p->loc,p->locN);
  76. p->sbi = cmInvalidIdx;
  77. p->sei = cmInvalidIdx;
  78. p->msln = 7;
  79. p->mswn = 30;
  80. p->edWndMtx = cmVOU_LevEditDistAllocMtx(p->bufN);
  81. p->minVel = 5;
  82. p->missCnt = 0;
  83. p->matchCnt = 0;
  84. p->printFl = true;
  85. p->eventIdx = 0;
  86. int i,n;
  87. double maxDSecs = 0; // max time between score entries to be considered simultaneous
  88. cmScoreEvt_t* e0p = NULL;
  89. int j0 = 0;
  90. // for each score event
  91. for(i=0,n=0; i<p->locN; ++i)
  92. {
  93. cmScoreEvt_t* ep = cmScoreEvt(scH,i);
  94. // if the event is not a note then ignore it
  95. if( ep->type == kNonEvtScId )
  96. {
  97. assert( j0+n < p->locN );
  98. p->loc[j0+n].scIdx = i;
  99. p->loc[j0+n].barNumb = ep->barNumb;
  100. // if the first event has not yet been selected
  101. if( e0p == NULL )
  102. {
  103. e0p = ep;
  104. n = 1;
  105. }
  106. else
  107. {
  108. // time can never reverse
  109. assert( ep->secs >= e0p->secs );
  110. // calc seconds between first event and current event
  111. double dsecs = ep->secs - e0p->secs;
  112. // if the first event and current event are simultaneous...
  113. if( dsecs <= maxDSecs )
  114. ++n; // ... incr. the count of simultaneous events
  115. else
  116. {
  117. int k;
  118. // ... a complete set of simultaneous events have been located
  119. // duplicate all the events at each of their respective time locations
  120. for(k=0; k<n; ++k)
  121. {
  122. int m;
  123. assert( j0+k < p->locN );
  124. p->loc[j0+k].pitchCnt = n;
  125. p->loc[j0+k].pitchV = cmMemAllocZ(unsigned,n);
  126. for(m=0; m<n; ++m)
  127. {
  128. cmScoreEvt_t* tp = cmScoreEvt(scH,p->loc[j0+m].scIdx);
  129. assert(tp!=NULL);
  130. p->loc[j0+k].pitchV[m] = tp->pitch;
  131. }
  132. }
  133. e0p = ep;
  134. j0 += n;
  135. n = 1;
  136. }
  137. }
  138. }
  139. }
  140. p->locN = j0;
  141. //_cmScFolPrint(p);
  142. return rc;
  143. }
  144. cmRC_t cmScFolReset( cmScFol* p, unsigned scoreIndex )
  145. {
  146. int i;
  147. // empty the event buffer
  148. memset(p->bufV,0,sizeof(cmScFolBufEle_t)*p->bufN);
  149. // don't allow the score index to be prior to the first note
  150. if( scoreIndex < p->loc[0].scIdx )
  151. scoreIndex = p->loc[0].scIdx;
  152. p->sei = cmInvalidIdx;
  153. p->sbi = cmInvalidIdx;
  154. // locate the score element in svV[] that is closest to,
  155. // and possibly after, scoreIndex.
  156. for(i=0; i<p->locN-1; ++i)
  157. if( p->loc[i].scIdx <= scoreIndex && scoreIndex < p->loc[i+1].scIdx )
  158. {
  159. p->sbi = i;
  160. break;
  161. }
  162. // locate the score element at the end of the look-ahead region
  163. for(; i<p->locN-1; ++i)
  164. if( p->loc[i].scIdx <= scoreIndex + p->msln && scoreIndex + p->msln < p->loc[i+1].scIdx )
  165. {
  166. p->sei = i;
  167. break;
  168. }
  169. return cmOkRC;
  170. }
  171. bool _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
  172. {
  173. unsigned i;
  174. for(i=0; i<loc->pitchCnt; ++i)
  175. if( loc->pitchV[i] == pitch )
  176. return true;
  177. return false;
  178. }
  179. int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const unsigned* pitch, unsigned pi )
  180. {
  181. if( _cmScFolIsMatch(loc+li,pitch[pi]) )
  182. return 0;
  183. if( li>0 && pi>0 )
  184. if( _cmScFolIsMatch(loc+li-1,pitch[pi]) && _cmScFolIsMatch(loc+li,pitch[pi-1]) )
  185. return 0;
  186. return 1;
  187. }
  188. int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFolLoc_t* s0, int n )
  189. {
  190. mtxMaxN += 1;
  191. assert( n < mtxMaxN );
  192. int v = 0;
  193. unsigned i;
  194. // Note that m[maxN,maxN] is not oriented in column major order like most 'cm' matrices.
  195. for(i=1; i<n+1; ++i)
  196. {
  197. unsigned ii = i * mtxMaxN; // current row
  198. unsigned i_1 = ii - mtxMaxN; // previous row
  199. unsigned j;
  200. for( j=1; j<n+1; ++j)
  201. {
  202. //int cost = s0[i-1] == s1[j-1] ? 0 : 1;
  203. //int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
  204. int cost = _cmScFolMatchCost(s0,i-1,s1,j-1);
  205. //m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
  206. m[ ii + j ] = v = cmMin( m[ i_1 + j] + 1, cmMin( m[ ii + j - 1] + 1, m[ i_1 + j - 1 ] + cost ) );
  207. }
  208. }
  209. return v;
  210. }
  211. void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, unsigned* b, unsigned bn, unsigned min_idx )
  212. {
  213. unsigned i;
  214. int n;
  215. printf("--------------- event:%i ------------- \n",p->eventIdx);
  216. printf("loc: ");
  217. for(i=0; i<locN; ++i)
  218. printf("%4i ",i+locIdx);
  219. printf("\n");
  220. for(n=0,i=0; i<locN; ++i)
  221. if( p->loc[locIdx+i].pitchCnt > n )
  222. n = p->loc[locIdx+i].pitchCnt;
  223. --n;
  224. for(; n>=0; --n)
  225. {
  226. printf("sc%1i: ",n);
  227. for(i=0; i<locN; ++i)
  228. {
  229. if( n < p->loc[locIdx+i].pitchCnt )
  230. printf("%4s ",cmMidiToSciPitch(p->loc[locIdx+i].pitchV[n],NULL,0));
  231. else
  232. printf(" ");
  233. }
  234. printf("\n");
  235. }
  236. printf("perf:");
  237. for(i=0; i<min_idx; ++i)
  238. printf(" ");
  239. for(i=0; i<bn; ++i)
  240. printf("%4s ",cmMidiToSciPitch(b[i],NULL,0));
  241. printf("\n");
  242. }
  243. void _cmScFolRpt1( cmScFol*p, unsigned minDist, unsigned ret_idx, unsigned d1, unsigned missCnt, unsigned matchCnt )
  244. {
  245. printf("dist:%i miss:%i match:%i ",minDist,missCnt,matchCnt);
  246. if( ret_idx == cmInvalidIdx )
  247. printf("vel:%i ", d1 );
  248. else
  249. printf("ret_idx:%i ",ret_idx);
  250. printf("\n");
  251. }
  252. unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  253. {
  254. unsigned ret_idx = cmInvalidIdx;
  255. unsigned ebuf[ p->bufN ];
  256. if( status != kNoteOnMdId )
  257. return ret_idx;
  258. ++p->eventIdx;
  259. // reject notes with very low velocity
  260. if( d1 < p->minVel )
  261. return ret_idx;
  262. // left shift bufV[] to make the right-most element available - then copy in the new element
  263. memmove(p->bufV, p->bufV+1, sizeof(cmScFolBufEle_t)*(p->bufN-1));
  264. p->bufV[ p->bufN-1 ].smpIdx = smpIdx;
  265. p->bufV[ p->bufN-1 ].val = d0;
  266. p->bufV[ p->bufN-1 ].validFl= true;
  267. // fill in ebuf[] with the valid values in bufV[]
  268. int i = p->bufN-1;
  269. int en = 0;
  270. for(; i>=0; --i,++en)
  271. {
  272. if( p->bufV[i].validFl)
  273. ebuf[i] = p->bufV[i].val;
  274. else
  275. break;
  276. }
  277. ++i; // increment i to the first valid element in ebuf[].
  278. // en is the count of valid elements in ebuf[].
  279. // ebuf[p->boi] is the first valid element
  280. int j = 0;
  281. int minDist = INT_MAX;
  282. int minIdx = cmInvalidIdx;
  283. int dist;
  284. // the score wnd must always be as long as the buffer n
  285. // at the end of the score this may not be the case
  286. // (once sei hits locN - at this point we must begin
  287. // shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
  288. assert( p->sei-p->sbi+1 >= en );
  289. for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
  290. {
  291. // use <= minDist to choose the latest window with the lowest match
  292. if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) <= minDist )
  293. {
  294. minDist = dist;
  295. minIdx = j;
  296. }
  297. }
  298. // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
  299. if( p->printFl )
  300. _cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, ebuf+i, en, minIdx );
  301. // save current missCnt for later printing
  302. unsigned missCnt = p->missCnt;
  303. // if a perfect match occurred
  304. if( minDist == 0 )
  305. {
  306. // we had a perfect match - shrink the window to it's minumum size
  307. p->sbi += (en==p->bufN) ? minIdx + 1 : 0; // move wnd begin forward to just past first match
  308. p->sei = p->sbi + minIdx + en + p->msln; // move wnd end forward to just past last match
  309. // BUG BUG BUG - is the window length valid -
  310. // - sbi and sei must be inside 0:locN
  311. // - sei-sbi + 1 must be >= en
  312. ret_idx = p->sbi + minIdx + en - 1;
  313. p->missCnt = 0;
  314. }
  315. else
  316. {
  317. // if the last event matched - then return the match location as the current score location
  318. if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ebuf[p->bufN-1]) )
  319. {
  320. ret_idx = p->sbi + minIdx + en - 1;
  321. p->missCnt = 0;
  322. if( en >= p->bufN-1 && (en+2) <= ret_idx )
  323. p->sbi = ret_idx - (en+2);
  324. }
  325. else // the last event does not match based on the optimal edit-distance alignment
  326. {
  327. // Look backward from the closest match location for a match to the current pitch.
  328. // The backward search scope is limited by the current value of 'missCnt'.
  329. j = p->sbi+minIdx+en-2;
  330. for(i=1; i+1 <= p->bufN && j>=p->sbi && i<=p->missCnt; ++i,--j)
  331. {
  332. // if this look-back location already matched then stop the backward search
  333. if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1-i]))
  334. break;
  335. // does this look-back location match the current pitch
  336. if(_cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]))
  337. {
  338. ret_idx = j;
  339. p->missCnt = i; // set missCnt to the cnt of steps backward necessary for a match
  340. break;
  341. }
  342. }
  343. // If the backward search did not find a match - look forward
  344. if( ret_idx == cmInvalidIdx )
  345. {
  346. j = p->sbi+minIdx+en;
  347. for(i=0; j<=p->sei && i<2; ++i,++j)
  348. if( _cmScFolIsMatch(p->loc+j,ebuf[p->bufN-1]) )
  349. {
  350. ret_idx = j;
  351. break;
  352. }
  353. p->missCnt = ret_idx == cmInvalidIdx ? p->missCnt + 1 : 0;
  354. }
  355. }
  356. // even though we didn't match move the end of the score window forward
  357. // this will enlarge the score window by one
  358. p->sei += 1;
  359. assert( p->sei > p->sbi );
  360. // if the score window length surpasses the max score window size
  361. // move the beginning index forward
  362. if( p->sei - p->sbi + 1 > p->mswn && p->sei > p->mswn )
  363. p->sbi = p->sei - p->mswn + 1;
  364. // BUG BUG BUG - is the window length valid -
  365. // - sbi and sei must be inside 0:locN
  366. // - sei-sbi + 1 must be >= en
  367. }
  368. if( p->printFl )
  369. _cmScFolRpt1(p, minDist, ret_idx, d1, missCnt, p->matchCnt );
  370. if( ret_idx == cmInvalidIdx )
  371. p->matchCnt = 0;
  372. else
  373. ++p->matchCnt;
  374. // BUG BUG BUG - this is not currently guarded against
  375. assert( p->sei < p->locN );
  376. assert( p->sbi < p->locN );
  377. return ret_idx;
  378. }