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

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