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 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. cmObjFree(pp);
  46. return rc;
  47. }
  48. cmRC_t cmScFolFinal( cmScFol* p )
  49. { return cmOkRC; }
  50. void _cmScFolPrint( cmScFol* p )
  51. {
  52. int i,j;
  53. for(i=0; i<p->locN; ++i)
  54. {
  55. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scIdx);
  56. for(j=0; j<p->loc[i].pitchCnt; ++j)
  57. printf("%s ",cmMidiToSciPitch(p->loc[i].pitchV[j],NULL,0));
  58. printf("\n");
  59. }
  60. }
  61. cmRC_t cmScFolInit( cmScFol* p, cmReal_t srate, unsigned bufN, cmReal_t wndMs, cmScH_t scH )
  62. {
  63. cmRC_t rc;
  64. if((rc = cmScFolFinal(p)) != cmOkRC )
  65. return rc;
  66. p->srate = srate;
  67. p->scH = scH;
  68. p->bufN = bufN;
  69. p->bufV = cmMemResizeZ(cmScFolBufEle_t,p->bufV,bufN);
  70. p->locN = cmScoreEvtCount(scH);
  71. p->loc = cmMemResizeZ(cmScFolLoc_t,p->loc,p->locN);
  72. p->sbi = cmInvalidIdx;
  73. p->sei = cmInvalidIdx;
  74. p->msln = 7;
  75. p->mswn = 30;
  76. p->edWndMtx = cmVOU_LevEditDistAllocMtx(p->bufN);
  77. int i,n;
  78. double maxDSecs = 0; // max time between score entries to be considered simultaneous
  79. cmScoreEvt_t* e0p = NULL;
  80. int j0 = 0;
  81. // for each score event
  82. for(i=0,n=0; i<p->locN; ++i)
  83. {
  84. cmScoreEvt_t* ep = cmScoreEvt(scH,i);
  85. // if the event is not a note then ignore it
  86. if( ep->type == kNonEvtScId )
  87. {
  88. assert( j0+n < p->locN );
  89. p->loc[j0+n].scIdx = i;
  90. p->loc[j0+n].barNumb = ep->barNumb;
  91. // if the first event has not yet been selected
  92. if( e0p == NULL )
  93. {
  94. e0p = ep;
  95. n = 1;
  96. }
  97. else
  98. {
  99. // time can never reverse
  100. assert( ep->secs >= e0p->secs );
  101. // calc seconds between first event and current event
  102. double dsecs = ep->secs - e0p->secs;
  103. // if the first event and current event are simultaneous...
  104. if( dsecs <= maxDSecs )
  105. ++n; // ... incr. the count of simultaneous events
  106. else
  107. {
  108. int k;
  109. // ... a complete set of simultaneous events have been located
  110. // duplicate all the events at each of their respective time locations
  111. for(k=0; k<n; ++k)
  112. {
  113. int m;
  114. assert( j0+k < p->locN );
  115. p->loc[j0+k].pitchCnt = n;
  116. p->loc[j0+k].pitchV = cmMemAllocZ(unsigned,n);
  117. for(m=0; m<n; ++m)
  118. {
  119. cmScoreEvt_t* tp = cmScoreEvt(scH,p->loc[j0+m].scIdx);
  120. assert(tp!=NULL);
  121. p->loc[j0+k].pitchV[m] = tp->pitch;
  122. }
  123. }
  124. e0p = ep;
  125. j0 += n;
  126. n = 1;
  127. }
  128. }
  129. }
  130. }
  131. p->locN = j0;
  132. //_cmScFolPrint(p);
  133. return rc;
  134. }
  135. cmRC_t cmScFolReset( cmScFol* p, unsigned scoreIndex )
  136. {
  137. int i;
  138. // empty the event buffer
  139. memset(p->bufV,0,sizeof(cmScFolBufEle_t)*p->bufN);
  140. // don't allow the score index to be prior to the first note
  141. if( scoreIndex < p->loc[0].scIdx )
  142. scoreIndex = p->loc[0].scIdx;
  143. p->sei = cmInvalidIdx;
  144. p->sbi = cmInvalidIdx;
  145. // locate the score element in svV[] that is closest to,
  146. // and possibly after, scoreIndex.
  147. for(i=0; i<p->locN-1; ++i)
  148. if( p->loc[i].scIdx <= scoreIndex && scoreIndex < p->loc[i+1].scIdx )
  149. {
  150. p->sbi = i;
  151. break;
  152. }
  153. // locate the score element at the end of the look-ahead region
  154. for(; i<p->locN-1; ++i)
  155. if( p->loc[i].scIdx <= scoreIndex + p->msln && scoreIndex + p->msln < p->loc[i+1].scIdx )
  156. {
  157. p->sei = i;
  158. break;
  159. }
  160. return cmOkRC;
  161. }
  162. bool _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
  163. {
  164. unsigned i;
  165. for(i=0; i<loc->pitchCnt; ++i)
  166. if( loc->pitchV[i] == pitch )
  167. return true;
  168. return false;
  169. }
  170. int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const unsigned* s1, const cmScFolLoc_t* s0, int n )
  171. {
  172. mtxMaxN += 1;
  173. assert( n < mtxMaxN );
  174. int v = 0;
  175. unsigned i;
  176. // Note that m[maxN,maxN] is not oriented in column major order like most 'cm' matrices.
  177. for(i=1; i<n+1; ++i)
  178. {
  179. unsigned ii = i * mtxMaxN; // current row
  180. unsigned i_1 = ii - mtxMaxN; // previous row
  181. unsigned j;
  182. for( j=1; j<n+1; ++j)
  183. {
  184. //int cost = s0[i-1] == s1[j-1] ? 0 : 1;
  185. int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
  186. //m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
  187. m[ ii + j ] = v = cmMin( m[ i_1 + j] + 1, cmMin( m[ ii + j - 1] + 1, m[ i_1 + j - 1 ] + cost ) );
  188. }
  189. }
  190. return v;
  191. }
  192. /*
  193. unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  194. {
  195. assert( p->sri != cmInvalidIdx );
  196. unsigned ret_idx = cmInvalidIdx;
  197. unsigned ewnd[ p->wndN ];
  198. if( status != kNoteOnMdId && d1>0 )
  199. return ret_idx;
  200. // left shift wndV[] to make the right-most element available - then copy in the new element
  201. memmove(p->wndV, p->wndV+1, sizeof(cmScFolWndEle_t)*(p->wndN-1));
  202. p->wndV[ p->wndN-1 ].smpIdx = smpIdx;
  203. p->wndV[ p->wndN-1 ].val = d0;
  204. p->wndV[ p->wndN-1 ].validFl= true;
  205. // fill in ewnd[] with the valid values in wndV[]
  206. int i = p->wndN-1;
  207. int en = 0;
  208. for(; i>=0; --i,++en)
  209. {
  210. //if( p->wndV[i].validFl && ((smpIdx-p->wnd[i].smpIdx)<=maxWndSmp))
  211. if( p->wndV[i].validFl )
  212. ewnd[i] = p->wndV[i].val;
  213. else
  214. break;
  215. }
  216. ++i; // increment i to the first valid element in ewnd[].
  217. int k;
  218. printf("en:%i sbi:%i sei:%i pitch:%s : ",en,p->sbi,p->sei,cmMidiToSciPitch(d0,NULL,0));
  219. for(k=i; k<p->wndN; ++k)
  220. printf("%s ", cmMidiToSciPitch(ewnd[k],NULL,0));
  221. printf("\n");
  222. // en is the count of valid elements in ewnd[].
  223. // ewnd[i] is the first valid element
  224. int j = 0;
  225. int dist;
  226. int minDist = INT_MAX;
  227. int minIdx = cmInvalidIdx;
  228. for(j=0; p->sbi+en+j <= p->sei; ++j)
  229. if((dist = _cmScFolDist(p->wndN, p->edWndMtx, ewnd+i, p->loc + p->sbi+j, en )) < minDist )
  230. {
  231. minDist = dist;
  232. minIdx = j;
  233. }
  234. // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
  235. int evalWndN = cmMin(en,p->evalWndN);
  236. assert(evalWndN<p->wndN);
  237. j = p->sbi+minIdx+en - evalWndN;
  238. // Determine how many of the last evalWndN elements match
  239. dist = _cmScFolDist(p->wndN, p->edWndMtx, ewnd+p->wndN-evalWndN, p->loc+j, evalWndN );
  240. // a successful match has <= allowedMissCnt and an exact match on the last element
  241. //if( dist <= p->allowedMissCnt && ewnd[p->wndN-1] == p->loc[p->sbi+minIdx+en-1] )
  242. //if( dist <= p->allowedMissCnt && _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ewnd[p->wndN-1]))
  243. if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ewnd[p->wndN-1]))
  244. {
  245. p->sbi = p->sbi + minIdx;
  246. p->sei = cmMin(p->sei+minIdx,p->locN-1);
  247. ret_idx = p->sbi+minIdx+en-1;
  248. }
  249. printf("minDist:%i minIdx:%i evalDist:%i sbi:%i sei:%i\n",minDist,minIdx,dist,p->sbi,p->sei);
  250. return ret_idx;
  251. }
  252. */
  253. unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  254. {
  255. unsigned ret_idx = cmInvalidIdx;
  256. unsigned ebuf[ p->bufN ];
  257. if( status != kNoteOnMdId && d1>0 )
  258. return ret_idx;
  259. // left shift bufV[] to make the right-most element available - then copy in the new element
  260. memmove(p->bufV, p->bufV+1, sizeof(cmScFolBufEle_t)*(p->bufN-1));
  261. p->bufV[ p->bufN-1 ].smpIdx = smpIdx;
  262. p->bufV[ p->bufN-1 ].val = d0;
  263. p->bufV[ p->bufN-1 ].validFl= true;
  264. // fill in ebuf[] with the valid values in bufV[]
  265. int i = p->bufN-1;
  266. int en = 0;
  267. for(; i>=0; --i,++en)
  268. {
  269. if( p->bufV[i].validFl)
  270. ebuf[i] = p->bufV[i].val;
  271. else
  272. break;
  273. }
  274. ++i; // increment i to the first valid element in ebuf[].
  275. // en is the count of valid elements in ebuf[].
  276. // ebuf[p->boi] is the first valid element
  277. int j = 0;
  278. int minDist = INT_MAX;
  279. int minIdx = cmInvalidIdx;
  280. int dist;
  281. // the score wnd must always be as long as the buffer n
  282. // at the end of the score this may not be the case
  283. // (once sei hits locN - at this point we must begin
  284. // shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
  285. assert( p->sei-p->sbi+1 >= en );
  286. for(j=0; p->sbi+en+j <= p->sei; ++j)
  287. if((dist = _cmScFolDist(p->bufN, p->edWndMtx, ebuf+i, p->loc + p->sbi+j, en )) < minDist )
  288. {
  289. minDist = dist;
  290. minIdx = j;
  291. }
  292. // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
  293. // if a perfect match occurred
  294. if( minDist == 0 )
  295. {
  296. // we had a perfect match - shrink the window to it's minumum size
  297. p->sbi += (en==p->bufN) ? minIdx + 1 : 0; // move wnd begin forward to just past first match
  298. p->sei = p->sbi + minIdx + en + p->msln; // move wnd end forward to just past last match
  299. ret_idx = p->sbi + minIdx + en - 1;
  300. // BUG BUG BUG - is the window length valid -
  301. // - sbi and sei must be inside 0:locN
  302. // - sei-sbi + 1 must be >= en
  303. }
  304. else
  305. {
  306. // if the last event matched - then return the match location as the current score location
  307. if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),ebuf[p->bufN-1]) )
  308. ret_idx = p->sbi + minIdx + en - 1;
  309. // even though we didn't match move the end of the score window forward
  310. // this will enlarge the score window by one
  311. p->sei += 1;
  312. assert( p->sei > p->sbi );
  313. // if the score window length surpasses the max score window size
  314. // move the beginning index forward
  315. if( p->sei - p->sbi + 1 > p->mswn && p->sei > p->mswn )
  316. p->sbi = p->sei - p->mswn + 1;
  317. // BUG BUG BUG - is the window length valid -
  318. // - sbi and sei must be inside 0:locN
  319. // - sei-sbi + 1 must be >= en
  320. }
  321. // BUG BUG BUG - this is not currently guarded against
  322. assert( p->sei < p->locN );
  323. assert( p->sbi < p->locN );
  324. return ret_idx;
  325. }