libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

cmProc4.c 61KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522
  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 "cmText.h"
  16. #include "cmProcObj.h"
  17. #include "cmProcTemplate.h"
  18. #include "cmMath.h"
  19. #include "cmProc.h"
  20. #include "cmVectOps.h"
  21. #include "cmMidi.h"
  22. #include "cmMidiFile.h"
  23. #include "cmTimeLine.h"
  24. #include "cmScore.h"
  25. #include "cmProc4.h"
  26. cmScFol* cmScFolAlloc( cmCtx* c, cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  27. {
  28. cmScFol* op = cmObjAlloc(cmScFol,c,p);
  29. if( srate != 0 )
  30. if( cmScFolInit(op,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel) != cmOkRC )
  31. cmScFolFree(&op);
  32. return op;
  33. }
  34. cmRC_t cmScFolFree( cmScFol** pp )
  35. {
  36. cmRC_t rc = cmOkRC;
  37. if( pp==NULL || *pp==NULL )
  38. return rc;
  39. cmScFol* p = *pp;
  40. if((rc = cmScFolFinal(p)) != cmOkRC )
  41. return rc;
  42. unsigned i;
  43. for(i=0; i<p->locN; ++i)
  44. cmMemFree(p->loc[i].evtV);
  45. cmMemFree(p->loc);
  46. cmMemFree(p->bufV);
  47. cmObjFree(pp);
  48. return rc;
  49. }
  50. cmRC_t cmScFolFinal( cmScFol* p )
  51. {
  52. cmMemFree(p->edWndMtx);
  53. return cmOkRC;
  54. }
  55. void _cmScFolPrint( cmScFol* p )
  56. {
  57. int i,j;
  58. for(i=0; i<p->locN; ++i)
  59. {
  60. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scIdx);
  61. for(j=0; j<p->loc[i].evtCnt; ++j)
  62. printf("%s ",cmMidiToSciPitch(p->loc[i].evtV[j].pitch,NULL,0));
  63. printf("\n");
  64. }
  65. }
  66. unsigned* _cmScFolAllocEditDistMtx(unsigned maxN)
  67. {
  68. maxN += 1;
  69. unsigned* m = cmMemAllocZ(unsigned,maxN*maxN);
  70. unsigned* p = m;
  71. unsigned i;
  72. // initialize the comparison matrix with the default costs in the
  73. // first row and column
  74. // (Note that this matrix is not oriented in column major order like most 'cm' matrices.)
  75. for(i=0; i<maxN; ++i)
  76. {
  77. p[i] = i; // 0th row
  78. p[ i * maxN ] = i; // 0th col
  79. }
  80. return m;
  81. }
  82. cmRC_t cmScFolInit( cmScFol* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  83. {
  84. cmRC_t rc;
  85. if((rc = cmScFolFinal(p)) != cmOkRC )
  86. return rc;
  87. if( bufN > maxWndCnt )
  88. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower buffer count (%i) must be less than the max. window length (%i).",bufN,maxWndCnt );
  89. if( minWndLookAhead > maxWndCnt )
  90. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower look-ahead count (%i) must be less than the max. window length (%i).",minWndLookAhead,maxWndCnt);
  91. p->srate = srate;
  92. p->scH = scH;
  93. p->bufN = bufN;
  94. p->bufV = cmMemResizeZ(cmScFolBufEle_t,p->bufV,bufN);
  95. p->locN = cmScoreEvtCount(scH);
  96. p->loc = cmMemResizeZ(cmScFolLoc_t,p->loc,p->locN);
  97. p->sbi = cmInvalidIdx;
  98. p->sei = cmInvalidIdx;
  99. p->msln = minWndLookAhead;
  100. p->mswn = maxWndCnt;
  101. p->forwardCnt = 2;
  102. p->maxDist = 4;
  103. p->edWndMtx = _cmScFolAllocEditDistMtx(p->bufN);
  104. p->minVel = minVel;
  105. p->printFl = true;
  106. p->noBackFl = true;
  107. p->missCnt = 0;
  108. p->matchCnt = 0;
  109. p->eventIdx = 0;
  110. p->skipCnt = 0;
  111. p->ret_idx = cmInvalidIdx;
  112. // for each score location
  113. unsigned li,ei;
  114. for(li=0,ei=0; li<cmScoreLocCount(p->scH); ++li)
  115. {
  116. unsigned i,n;
  117. const cmScoreLoc_t* lp = cmScoreLoc(p->scH,li);
  118. // count the number of note events at location li
  119. for(n=0,i=0; i<lp->evtCnt; ++i)
  120. if( lp->evtArray[i]->type == kNonEvtScId )
  121. ++n;
  122. assert( ei+n <= p->locN );
  123. // duplicate each note at location li n times
  124. for(i=0; i<n; ++i)
  125. {
  126. unsigned j,k;
  127. p->loc[ei+i].evtCnt = n;
  128. p->loc[ei+i].evtV = cmMemAllocZ(cmScFolEvt_t,n);
  129. p->loc[ei+i].scIdx = li;
  130. p->loc[ei+i].barNumb = lp->barNumb;
  131. for(j=0,k=0; j<lp->evtCnt; ++j)
  132. if( lp->evtArray[j]->type == kNonEvtScId )
  133. {
  134. p->loc[ei+i].evtV[k].pitch = lp->evtArray[j]->pitch;
  135. p->loc[ei+i].evtV[k].scEvtIdx = lp->evtArray[j]->index;
  136. ++k;
  137. }
  138. }
  139. ei += n;
  140. }
  141. p->locN = ei;
  142. //_cmScFolPrint(p);
  143. return rc;
  144. }
  145. cmRC_t cmScFolReset( cmScFol* p, unsigned scEvtIdx )
  146. {
  147. int i,j;
  148. // empty the event buffer
  149. memset(p->bufV,0,sizeof(cmScFolBufEle_t)*p->bufN);
  150. // don't allow the score index to be prior to the first note
  151. //if( scEvtIdx < p->loc[0].scIdx )
  152. // scEvtIdx = p->loc[0].scIdx;
  153. p->sei = cmInvalidIdx;
  154. p->sbi = cmInvalidIdx;
  155. p->missCnt = 0;
  156. p->matchCnt = 0;
  157. p->eventIdx = 0;
  158. p->skipCnt = 0;
  159. p->ret_idx = cmInvalidIdx;
  160. // locate the score element in svV[] that is closest to,
  161. // and possibly after, scEvtIdx.
  162. for(i=0; i<p->locN-1; ++i)
  163. {
  164. for(j=0; j<p->loc[i].evtCnt; ++j)
  165. if( p->loc[i].evtV[j].scEvtIdx <= scEvtIdx )
  166. p->sbi = i;
  167. else
  168. break;
  169. }
  170. // locate the score element at the end of the look-ahead region
  171. for(; i<p->locN; ++i)
  172. {
  173. for(j=0; j<p->loc[i].evtCnt; ++j)
  174. if( p->loc[i].evtV[j].scEvtIdx <= scEvtIdx + p->msln )
  175. p->sei = i;
  176. }
  177. return cmOkRC;
  178. }
  179. bool _cmScFolIsMatch( const cmScFolLoc_t* loc, unsigned pitch )
  180. {
  181. unsigned i;
  182. for(i=0; i<loc->evtCnt; ++i)
  183. if( loc->evtV[i].pitch == pitch )
  184. return true;
  185. return false;
  186. }
  187. int _cmScFolMatchCost( const cmScFolLoc_t* loc, unsigned li, const cmScFolBufEle_t* pitch, unsigned pi )
  188. {
  189. if( _cmScFolIsMatch(loc+li,pitch[pi].val) )
  190. return 0;
  191. if( li>0 && pi>0 )
  192. if( _cmScFolIsMatch(loc+li-1,pitch[pi].val) && _cmScFolIsMatch(loc+li,pitch[pi-1].val) )
  193. return 0;
  194. return 1;
  195. }
  196. int _cmScFolDist(unsigned mtxMaxN, unsigned* m, const cmScFolBufEle_t* s1, const cmScFolLoc_t* s0, int n )
  197. {
  198. mtxMaxN += 1;
  199. assert( n < mtxMaxN );
  200. int v = 0;
  201. unsigned i;
  202. // Note that m[maxN,maxN] is not oriented in column major order like most 'cm' matrices.
  203. for(i=1; i<n+1; ++i)
  204. {
  205. unsigned ii = i * mtxMaxN; // current row
  206. unsigned i_1 = ii - mtxMaxN; // previous row
  207. unsigned j;
  208. for( j=1; j<n+1; ++j)
  209. {
  210. //int cost = s0[i-1] == s1[j-1] ? 0 : 1;
  211. //int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
  212. int cost = _cmScFolMatchCost(s0,i-1,s1,j-1);
  213. //m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
  214. m[ ii + j ] = v = cmMin( m[ i_1 + j] + 1, cmMin( m[ ii + j - 1] + 1, m[ i_1 + j - 1 ] + cost ) );
  215. }
  216. }
  217. return v;
  218. }
  219. void _cmScFolRpt0( cmScFol* p, unsigned locIdx, unsigned locN, const cmScFolBufEle_t* b, unsigned bn, unsigned min_idx )
  220. {
  221. unsigned i;
  222. int n;
  223. printf("--------------- event:%i ------------- \n",p->eventIdx);
  224. printf("loc: ");
  225. for(i=0; i<locN; ++i)
  226. printf("%4i ",i+locIdx);
  227. printf("\n");
  228. for(n=0,i=0; i<locN; ++i)
  229. if( p->loc[locIdx+i].evtCnt > n )
  230. n = p->loc[locIdx+i].evtCnt;
  231. --n;
  232. for(; n>=0; --n)
  233. {
  234. printf("sc%1i: ",n);
  235. for(i=0; i<locN; ++i)
  236. {
  237. if( n < p->loc[locIdx+i].evtCnt )
  238. printf("%4s ",cmMidiToSciPitch(p->loc[locIdx+i].evtV[n].pitch,NULL,0));
  239. else
  240. printf(" ");
  241. }
  242. printf("\n");
  243. }
  244. printf("perf:");
  245. for(i=0; i<min_idx; ++i)
  246. printf(" ");
  247. for(i=0; i<bn; ++i)
  248. printf("%4s ",cmMidiToSciPitch(b[i].val,NULL,0));
  249. printf("\n");
  250. }
  251. void _cmScFolRpt1( cmScFol*p, unsigned minDist, unsigned ret_idx, unsigned d1, unsigned missCnt, unsigned matchCnt )
  252. {
  253. printf("dist:%i miss:%i match:%i skip:%i vel:%i ",minDist,missCnt,matchCnt,p->skipCnt,d1);
  254. if( ret_idx != cmInvalidIdx )
  255. printf("ret_idx:%i ",ret_idx);
  256. printf("\n");
  257. }
  258. unsigned cmScFolExec( cmScFol* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  259. {
  260. unsigned ret_idx = cmInvalidIdx;
  261. if( p->sbi == cmInvalidIdx )
  262. {
  263. cmCtxRtCondition( &p->obj, cmInvalidArgRC, "An initial score search location has not been set." );
  264. return ret_idx;
  265. }
  266. if( status != kNoteOnMdId )
  267. return ret_idx;
  268. ++p->eventIdx;
  269. // reject notes with very low velocity
  270. if( d1 < p->minVel )
  271. {
  272. ++p->skipCnt;
  273. return ret_idx;
  274. }
  275. // left shift bufV[] to make the right-most element available - then copy in the new element
  276. memmove(p->bufV, p->bufV+1, sizeof(cmScFolBufEle_t)*(p->bufN-1));
  277. p->bufV[ p->bufN-1 ].smpIdx = smpIdx;
  278. p->bufV[ p->bufN-1 ].val = d0;
  279. p->bufV[ p->bufN-1 ].validFl= true;
  280. // fill in ebuf[] with the valid values in bufV[]
  281. int en = cmMin(p->eventIdx,p->bufN);
  282. int bbi = p->eventIdx>=p->bufN ? 0 : p->bufN-p->eventIdx;
  283. // en is the count of valid elements in ebuf[].
  284. // ebuf[p->boi] is the first valid element
  285. int j = 0;
  286. int minDist = INT_MAX;
  287. int minIdx = cmInvalidIdx;
  288. int dist;
  289. // the score wnd must always be as long as the buffer n
  290. // at the end of the score this may not be the case
  291. // (once sei hits locN - at this point we must begin
  292. // shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
  293. assert( p->sei-p->sbi+1 >= en );
  294. for(j=0; p->sbi+en+j-1 <= p->sei; ++j)
  295. {
  296. // use <= minDist to choose the latest window with the lowest match
  297. if((dist = _cmScFolDist(p->bufN, p->edWndMtx, p->bufV+bbi, p->loc + p->sbi+j, en )) < minDist )
  298. {
  299. // only make an eql match if the posn is greater than the last location
  300. if( dist==minDist && p->ret_idx != cmInvalidId && p->ret_idx >= p->sbi+minIdx+en-1 )
  301. continue;
  302. minDist = dist;
  303. minIdx = j;
  304. }
  305. }
  306. // The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
  307. if( p->printFl )
  308. _cmScFolRpt0( p, p->sbi, p->sei-p->sbi+1, p->bufV+bbi, en, minIdx );
  309. // save current missCnt for later printing
  310. unsigned missCnt = p->missCnt;
  311. // if a perfect match occurred
  312. if( minDist == 0 )
  313. {
  314. ret_idx = p->sbi + minIdx + en - 1;
  315. p->missCnt = 0;
  316. // we had a perfect match - shrink the window to it's minumum size
  317. p->sbi += (en==p->bufN) ? minIdx + 1 : 0; // move wnd begin forward to just past first match
  318. p->sei = p->sbi + minIdx + en + p->msln; // move wnd end forward to lead by the min look-ahead
  319. }
  320. else
  321. {
  322. if( minDist > p->maxDist )
  323. ret_idx = cmInvalidIdx;
  324. else
  325. // if the last event matched - then return the match location as the current score location
  326. if( _cmScFolIsMatch(p->loc+(p->sbi+minIdx+en-1),p->bufV[p->bufN-1].val) )
  327. {
  328. ret_idx = p->sbi + minIdx + en - 1;
  329. p->missCnt = 0;
  330. // this is probably a pretty good match reduce the part of the window prior to
  331. // the first match (bring the end of the window almost up to the end of the
  332. // buffers sync position)
  333. if( en >= p->bufN-1 && (en+2) <= ret_idx )
  334. p->sbi = ret_idx - (en+2);
  335. }
  336. else // the last event does not match based on the optimal edit-distance alignment
  337. {
  338. // Look backward from the closest match location for a match to the current pitch.
  339. // The backward search scope is limited by the current value of 'missCnt'.
  340. unsigned i;
  341. j = p->sbi+minIdx+en-2;
  342. for(i=1; i+1 <= p->bufN && j>=p->sbi && i<=p->missCnt; ++i,--j)
  343. {
  344. // if this look-back location already matched then stop the backward search
  345. if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1-i].val))
  346. break;
  347. // does this look-back location match the current pitch
  348. if(_cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val))
  349. {
  350. ret_idx = j;
  351. p->missCnt = i; // set missCnt to the cnt of steps backward necessary for a match
  352. break;
  353. }
  354. }
  355. // If the backward search did not find a match - look forward
  356. if( ret_idx == cmInvalidIdx )
  357. {
  358. unsigned i;
  359. j = p->sbi+minIdx+en;
  360. for(i=0; j<=p->sei && i<p->forwardCnt; ++i,++j)
  361. if( _cmScFolIsMatch(p->loc+j,p->bufV[p->bufN-1].val) )
  362. {
  363. ret_idx = j;
  364. break;
  365. }
  366. p->missCnt = ret_idx == cmInvalidIdx ? p->missCnt + 1 : 0;
  367. }
  368. }
  369. // Adjust the end window position (sei) based on the match location
  370. if( ret_idx == cmInvalidIdx )
  371. {
  372. // even though we didn't match move the end of the score window forward
  373. // this will enlarge the score window by one
  374. p->sei += 1;
  375. }
  376. else
  377. {
  378. assert( p->sei>=ret_idx);
  379. // force sei to lead by min look-ahead
  380. if( p->sei - ret_idx < p->msln )
  381. p->sei = ret_idx + p->msln;
  382. }
  383. assert( p->sei > p->sbi );
  384. // Adjust the begin window position
  385. if( p->noBackFl && ret_idx != cmInvalidIdx && en>=p->bufN && p->sbi > p->bufN )
  386. p->sbi = ret_idx - p->bufN;
  387. // if the score window length surpasses the max score window size
  388. // move the beginning index forward
  389. if( p->sei - p->sbi + 1 > p->mswn && p->sei > p->mswn )
  390. p->sbi = p->sei - p->mswn + 1;
  391. }
  392. if( p->printFl )
  393. _cmScFolRpt1(p, minDist, ret_idx, d1, missCnt, p->matchCnt );
  394. // don't allow the returned location to repeat or go backwards
  395. if( p->noBackFl && p->ret_idx != cmInvalidIdx && ret_idx <= p->ret_idx )
  396. ret_idx = cmInvalidIdx;
  397. // track the number of consecutive matches
  398. if( ret_idx == cmInvalidIdx )
  399. p->matchCnt = 0;
  400. else
  401. {
  402. ++p->matchCnt;
  403. p->ret_idx = ret_idx;
  404. }
  405. // Force the window to remain valid when it is at the end of the score
  406. // - sbi and sei must be inside 0:locN
  407. // - sei-sbi + 1 must be >= en
  408. if( p->sei >= p->locN )
  409. {
  410. p->sei = p->locN - 1;
  411. p->sbi = p->sei - p->bufN + 1;
  412. }
  413. if( ret_idx != cmInvalidIdx )
  414. ret_idx = p->loc[ret_idx].scIdx;
  415. return ret_idx;
  416. }
  417. //=======================================================================================================================
  418. cmScTrk* cmScTrkAlloc( cmCtx* c, cmScTrk* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  419. {
  420. cmScTrk* op = cmObjAlloc(cmScTrk,c,p);
  421. op->sfp = cmScFolAlloc(c,NULL,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel);
  422. if( srate != 0 )
  423. if( cmScTrkInit(op,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel) != cmOkRC )
  424. cmScTrkFree(&op);
  425. return op;
  426. }
  427. cmRC_t cmScTrkFree( cmScTrk** pp )
  428. {
  429. cmRC_t rc = cmOkRC;
  430. if( pp==NULL || *pp==NULL )
  431. return rc;
  432. cmScTrk* p = *pp;
  433. if((rc = cmScTrkFinal(p)) != cmOkRC )
  434. return rc;
  435. cmScFolFree(&p->sfp);
  436. cmObjFree(pp);
  437. return rc;
  438. }
  439. void _cmScTrkPrint( cmScTrk* p )
  440. {
  441. int i,j;
  442. for(i=0; i<p->locN; ++i)
  443. {
  444. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scIdx);
  445. for(j=0; j<p->loc[i].evtCnt; ++j)
  446. printf("%s ",cmMidiToSciPitch(p->loc[i].evtV[j].pitch,NULL,0));
  447. printf("\n");
  448. }
  449. }
  450. cmRC_t cmScTrkInit( cmScTrk* p, cmReal_t srate, cmScH_t scH, unsigned bufN, unsigned minWndLookAhead, unsigned maxWndCnt, unsigned minVel )
  451. {
  452. cmRC_t rc;
  453. if((rc = cmScTrkFinal(p)) != cmOkRC )
  454. return rc;
  455. if( minWndLookAhead > maxWndCnt )
  456. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score follower look-ahead count (%i) must be less than the max. window length (%i).",minWndLookAhead,maxWndCnt);
  457. if((rc = cmScFolInit(p->sfp,srate,scH,bufN,minWndLookAhead,maxWndCnt,minVel)) != cmOkRC )
  458. return rc;
  459. p->srate = srate;
  460. p->scH = scH;
  461. p->locN = cmScoreLocCount(scH);
  462. p->loc = cmMemResizeZ(cmScTrkLoc_t,p->loc,p->locN);
  463. p->minVel = minVel;
  464. p->maxWndCnt = maxWndCnt;
  465. p->minWndLookAhead= 4; //minWndLookAhead;
  466. p->printFl = true;
  467. p->curLocIdx = cmInvalidIdx;
  468. p->evtIndex = 0;
  469. // for each score location
  470. unsigned li;
  471. for(li=0; li<cmScoreLocCount(p->scH); ++li)
  472. {
  473. unsigned i,j,k,n;
  474. const cmScoreLoc_t* lp = cmScoreLoc(p->scH,li);
  475. // count the number of note events at location li
  476. for(n=0,i=0; i<lp->evtCnt; ++i)
  477. if( lp->evtArray[i]->type == kNonEvtScId )
  478. ++n;
  479. p->loc[li].evtCnt = n;
  480. p->loc[li].evtV = cmMemAllocZ(cmScTrkEvt_t,n);
  481. p->loc[li].scIdx = li;
  482. p->loc[li].barNumb = lp->barNumb;
  483. for(j=0,k=0; j<lp->evtCnt; ++j)
  484. if( lp->evtArray[j]->type == kNonEvtScId )
  485. {
  486. p->loc[li].evtV[k].pitch = lp->evtArray[j]->pitch;
  487. p->loc[li].evtV[k].scEvtIdx = lp->evtArray[j]->index;
  488. ++k;
  489. }
  490. }
  491. //_cmScTrkPrint(p);
  492. return rc;
  493. }
  494. cmRC_t cmScTrkFinal( cmScTrk* p )
  495. {
  496. unsigned i;
  497. for(i=0; i<p->locN; ++i)
  498. cmMemPtrFree(&p->loc[i].evtV);
  499. return cmOkRC;
  500. }
  501. cmRC_t cmScTrkReset( cmScTrk* p, unsigned scEvtIdx )
  502. {
  503. unsigned i;
  504. cmScFolReset(p->sfp,scEvtIdx);
  505. p->curLocIdx = cmInvalidIdx;
  506. p->evtIndex = 0;
  507. // locate the score element in svV[] that is closest to,
  508. // and possibly after, scEvtIdx.
  509. for(i=0; i<p->locN; ++i)
  510. {
  511. unsigned j;
  512. for(j=0; j<p->loc[i].evtCnt; ++j)
  513. {
  514. p->loc[i].evtV[j].matchFl = false;
  515. // it is possible that scEvtIdx is before the first event included in p->loc[0]
  516. // using the p->curLocIdx==cmInvalidIdx forces the first evt in p->loc[0] to be
  517. // selected in this case
  518. if( p->loc[i].evtV[j].scEvtIdx <= scEvtIdx || p->curLocIdx==cmInvalidIdx )
  519. p->curLocIdx = i;
  520. }
  521. }
  522. if( p->curLocIdx == cmInvalidIdx )
  523. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The initial score search location event %i was not found.", scEvtIdx );
  524. return cmOkRC;
  525. }
  526. unsigned _cmScTrkIsMatch(cmScTrk* p, int d, unsigned pitch )
  527. {
  528. if( 0 <= p->curLocIdx + d && p->curLocIdx+1 < p->locN )
  529. {
  530. unsigned i;
  531. const cmScTrkLoc_t* lp = p->loc + p->curLocIdx + d;
  532. for(i=0; i<lp->evtCnt; ++i)
  533. if( lp->evtV[i].pitch == pitch && lp->evtV[i].matchFl==false)
  534. return i;
  535. }
  536. return cmInvalidIdx;
  537. }
  538. void _cmScTrkRpt0( cmScTrk* p, unsigned pitch, unsigned vel, unsigned nli, unsigned nei )
  539. {
  540. bool missFl = nli==cmInvalidIdx || nei==cmInvalidIdx;
  541. printf("------- event:%i %s vel:%i cur:%i new:%i %s-------\n",p->evtIndex,cmMidiToSciPitch(pitch,NULL,0),vel,p->curLocIdx,nli,missFl?"MISS ":"");
  542. int bi = p->curLocIdx < p->minWndLookAhead ? 0 : p->curLocIdx - p->minWndLookAhead;
  543. int ei = cmMin(p->locN-1,p->curLocIdx+p->minWndLookAhead);
  544. unsigned i,n=0;
  545. for(i=bi; i<=ei; ++i)
  546. if( p->loc[i].evtCnt>n )
  547. n = p->loc[i].evtCnt;
  548. printf("loc ");
  549. for(i=bi; i<=ei; ++i)
  550. printf("%4i ",i);
  551. printf("\n");
  552. for(i=0; i<n; ++i)
  553. {
  554. unsigned j;
  555. printf("sc%2i ",i);
  556. for(j=bi; j<=ei; ++j)
  557. {
  558. if( i < p->loc[j].evtCnt )
  559. {
  560. char* X = p->loc[j].evtV[i].matchFl ? "__" : " ";
  561. if( nli==j && nei==i)
  562. {
  563. X = "**";
  564. assert( p->loc[j].evtV[i].pitch == pitch );
  565. }
  566. printf("%4s%s ",cmMidiToSciPitch(p->loc[j].evtV[i].pitch,NULL,0),X);
  567. }
  568. else
  569. printf(" ");
  570. }
  571. printf("\n");
  572. }
  573. }
  574. unsigned cmScTrkExec( cmScTrk* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  575. {
  576. unsigned ret_idx = cmInvalidIdx;
  577. //cmScFolExec(p->sfp, smpIdx, status, d0, d1);
  578. if( status != kNoteOnMdId )
  579. return cmInvalidIdx;
  580. if( p->curLocIdx == cmInvalidIdx )
  581. {
  582. cmCtxRtCondition( &p->obj, cmInvalidArgRC, "An initial score search location has not been set." );
  583. return cmInvalidIdx;
  584. }
  585. int i,nei,nli=cmInvalidIdx;
  586. // try to match curLocIdx first
  587. if((nei = _cmScTrkIsMatch(p,0,d0)) != cmInvalidIdx )
  588. nli = p->curLocIdx;
  589. for(i=1; nei==cmInvalidIdx && i<p->minWndLookAhead; ++i)
  590. {
  591. // go forward
  592. if((nei = _cmScTrkIsMatch(p,i,d0)) != cmInvalidIdx )
  593. nli = p->curLocIdx + i;
  594. else
  595. // go backward
  596. if((nei = _cmScTrkIsMatch(p,-i,d0)) != cmInvalidIdx )
  597. nli = p->curLocIdx - i;
  598. }
  599. if( p->printFl )
  600. {
  601. _cmScTrkRpt0(p, d0, d1, nli, nei );
  602. }
  603. if( nli != cmInvalidIdx )
  604. {
  605. p->loc[nli].evtV[nei].matchFl = true;
  606. ret_idx = p->loc[nli].scIdx;
  607. if( nli > p->curLocIdx )
  608. p->curLocIdx = nli;
  609. }
  610. ++p->evtIndex;
  611. return ret_idx;
  612. }
  613. //=======================================================================================================================
  614. //----------------------------------------------------------------------------------------
  615. void ed_print_mtx( ed_r* r)
  616. {
  617. unsigned i,j,k;
  618. for(i=0; i<r->rn; ++i)
  619. {
  620. for(j=0; j<r->cn; ++j)
  621. {
  622. printf("(");
  623. const ed_val* vp = r->m + i + (j*r->rn);
  624. for(k=0; k<kEdCnt; ++k)
  625. {
  626. printf("%i",vp->v[k]);
  627. if( k<kEdCnt-1)
  628. printf(", ");
  629. else
  630. printf(" ");
  631. }
  632. printf("%c)",vp->transFl?'t':' ');
  633. }
  634. printf("\n");
  635. }
  636. }
  637. void ed_init( ed_r* r, const char* s0, const char* s1 )
  638. {
  639. unsigned i,j,k;
  640. r->rn = strlen(s0)+1;
  641. r->cn = strlen(s1)+1;
  642. r->m = cmMemAllocZ(ed_val, r->rn*r->cn );
  643. r->pn = r->rn + r->cn;
  644. r->p_mem = cmMemAllocZ(ed_path, 2*r->pn );
  645. r->p_avl = r->p_mem;
  646. r->p_cur = NULL;
  647. r->p_opt = r->p_mem + r->pn;
  648. r->s_opt = DBL_MAX;
  649. r->s0 = s0;
  650. r->s1 = s1;
  651. for(i=0; i<r->rn; ++i)
  652. for(j=0; j<r->cn; ++j)
  653. {
  654. unsigned v[] = {0,0,0,0};
  655. if( i == 0 )
  656. {
  657. v[kEdMinIdx] = j;
  658. v[kEdInsIdx] = j;
  659. }
  660. else
  661. if( j == 0 )
  662. {
  663. v[kEdMinIdx] = i;
  664. v[kEdDelIdx] = i;
  665. }
  666. for(k=0; k<kEdCnt; ++k)
  667. r->m[ i + (j*r->rn) ].v[k] = v[k];
  668. }
  669. // put pn path records on the available list
  670. for(i=0; i<r->pn; ++i)
  671. r->p_mem[i].next = i<r->pn-1 ? r->p_mem + i + 1 : NULL;
  672. }
  673. unsigned _ed_min( ed_r* r, unsigned i, unsigned j )
  674. {
  675. assert( i<r->rn && j<r->cn );
  676. return r->m[ i + (j*r->rn) ].v[kEdMinIdx];
  677. }
  678. bool _ed_is_trans( ed_r* r, const ed_val* v1p, unsigned i, unsigned j )
  679. {
  680. bool fl = false;
  681. ed_val* v0p = r->m + i + (j*r->rn);
  682. if( i>=1 && j>=1 &&
  683. v1p->v[kEdMinIdx] == v1p->v[kEdSubIdx]
  684. && v1p->matchFl == false
  685. && v0p->v[kEdMinIdx] == v0p->v[kEdSubIdx]
  686. && v0p->matchFl == false )
  687. {
  688. char c00 = r->s0[i-1];
  689. char c01 = r->s0[i ];
  690. char c10 = r->s1[j-1];
  691. char c11 = r->s1[j ];
  692. fl = c00==c11 && c01==c10;
  693. }
  694. return fl;
  695. }
  696. void ed_calc_mtx( ed_r* r )
  697. {
  698. unsigned i,j;
  699. for(i=1; i<r->rn; ++i)
  700. for(j=1; j<r->cn; ++j)
  701. {
  702. ed_val* vp = r->m + i + (j*r->rn);
  703. vp->matchFl = r->s0[i-1] == r->s1[j-1];
  704. unsigned cost = vp->matchFl ? 0 : 1;
  705. vp->v[kEdSubIdx] = _ed_min(r,i-1,j-1) + cost;
  706. vp->v[kEdDelIdx] = _ed_min(r,i-1,j ) + 1;
  707. vp->v[kEdInsIdx] = _ed_min(r,i, j-1) + 1;
  708. vp->v[kEdMinIdx] = cmMin( vp->v[kEdSubIdx], cmMin(vp->v[kEdDelIdx],vp->v[kEdInsIdx]));
  709. vp->transFl = _ed_is_trans(r,vp,i-1,j-1);
  710. }
  711. }
  712. void ed_path_push( ed_r* r, unsigned code, unsigned ri, unsigned ci, bool matchFl, bool transFl )
  713. {
  714. assert(r->p_avl != NULL );
  715. ed_path* p = r->p_avl;
  716. r->p_avl = r->p_avl->next;
  717. p->code = code;
  718. p->ri = ri;
  719. p->ci = ci;
  720. p->matchFl = matchFl;
  721. p->transFl = transFl;
  722. p->next = r->p_cur;
  723. r->p_cur = p;
  724. }
  725. void ed_path_pop( ed_r* r )
  726. {
  727. assert( r->p_cur != NULL );
  728. ed_path* tp = r->p_cur->next;
  729. r->p_cur->next = r->p_avl;
  730. r->p_avl = r->p_cur;
  731. r->p_cur = tp;
  732. }
  733. double ed_score_candidate( ed_r* r )
  734. {
  735. ed_path* cp = r->p_cur;
  736. ed_path* bp = r->p_cur;
  737. ed_path* ep = NULL;
  738. for(; cp!=NULL; cp=cp->next)
  739. if( cp->code != kEdInsIdx )
  740. {
  741. bp = cp;
  742. break;
  743. }
  744. for(; cp!=NULL; cp=cp->next)
  745. if( cp->code!=kEdInsIdx )
  746. ep = cp;
  747. assert( ep!=NULL && bp!=ep);
  748. unsigned n=1;
  749. for(cp=bp; cp!=ep; cp=cp->next)
  750. ++n;
  751. double gapCnt = 0;
  752. double penalty = 0;
  753. bool pfl = bp->matchFl;
  754. unsigned i;
  755. cp = bp;
  756. for(i=0; i<n; ++i,cp=cp->next)
  757. {
  758. // a gap is a transition from a matching subst. to an insert or deletion
  759. //if( pc != cp->code && cp->code != kEdSubIdx && pc==kEdSubIdx && pfl==true )
  760. if( pfl==true && cp->matchFl==false )
  761. ++gapCnt;
  762. //
  763. switch( cp->code )
  764. {
  765. case kEdSubIdx:
  766. penalty += cp->matchFl ? 0 : 1;
  767. penalty -= cp->transFl ? 1 : 0;
  768. break;
  769. case kEdDelIdx:
  770. penalty += 1;
  771. break;
  772. case kEdInsIdx:
  773. penalty += 1;
  774. break;
  775. }
  776. pfl = cp->matchFl;
  777. }
  778. double score = gapCnt/n + penalty;
  779. printf("n:%i gaps:%f gap_score:%f penalty:%f score:%f\n",n,gapCnt,gapCnt/n,penalty,score);
  780. return score;
  781. }
  782. void ed_eval_candidate( ed_r* r, double score )
  783. {
  784. if( r->s_opt == DBL_MAX || r->s_opt > score)
  785. {
  786. // copy the p_cur to p_opt[]
  787. ed_path* cp = r->p_cur;
  788. unsigned i;
  789. for(i=0; cp!=NULL && i<r->pn; cp=cp->next,++i)
  790. {
  791. r->p_opt[i].code = cp->code;
  792. r->p_opt[i].ri = cp->ri;
  793. r->p_opt[i].ci = cp->ci;
  794. r->p_opt[i].matchFl = cp->matchFl;
  795. r->p_opt[i].transFl = cp->transFl;
  796. }
  797. assert( i < r->pn );
  798. r->p_opt[i].code = 0; // terminate with code=0
  799. r->s_opt = score;
  800. }
  801. }
  802. void ed_print_opt( ed_r* r )
  803. {
  804. unsigned i;
  805. for(i=0; r->p_opt[i].code!=0; ++i)
  806. {
  807. ed_path* cp = r->p_opt + i;
  808. char c0 = cp->matchFl ? 'm' : ' ';
  809. char c1 = cp->transFl ? 't' : ' ';
  810. printf("%2i code:%i ri:%2i ci:%2i %c%c\n",i,cp->code,cp->ri,cp->ci,c0,c1);
  811. }
  812. printf("score:%f\n",r->s_opt);
  813. }
  814. void ed_print_candidate( ed_r* r )
  815. {
  816. ed_path* cp = r->p_cur;
  817. unsigned pn = r->pn;
  818. unsigned i;
  819. char s0[pn+1];
  820. char s1[pn+1];
  821. char s2[pn+1];
  822. char s3[pn+1];
  823. s0[pn] = 0;
  824. s1[pn] = 0;
  825. s2[pn] = 0;
  826. s3[pn] = 0;
  827. for(i=0; i<pn && cp!=NULL; ++i,cp=cp->next)
  828. {
  829. switch(cp->code)
  830. {
  831. case kEdSubIdx: // subst
  832. assert( 0 <= cp->ri && cp->ri <= r->rn );
  833. assert( 0 <= cp->ci && cp->ci <= r->cn );
  834. s0[i] = r->s0[cp->ri];
  835. s1[i] = r->s1[cp->ci];
  836. s2[i] = 's';
  837. s3[i] = cp->matchFl ? 'm' : ' ';
  838. break;
  839. case kEdDelIdx: // delete
  840. assert( 0 <= cp->ri && cp->ri <= r->rn );
  841. s0[i] = r->s0[cp->ri];
  842. s1[i] = ' ';
  843. s2[i] = 'd';
  844. s3[i] = ' ';
  845. break;
  846. case kEdInsIdx: // insert
  847. assert( 0 <= cp->ci && cp->ci <= r->cn );
  848. s0[i] = ' ';
  849. s1[i] = r->s1[cp->ci];
  850. s2[i] = 'i';
  851. s3[i] = ' ';
  852. break;
  853. }
  854. }
  855. if( i < pn )
  856. {
  857. s0[i] = 0;
  858. s1[i] = 0;
  859. s2[i] = 0;
  860. s3[i] = 0;
  861. }
  862. printf("\ns0:%s\n",s0);
  863. printf("s1:%s\n",s1);
  864. printf("s2:%s\n",s2);
  865. printf("s3:%s\n",s3);
  866. }
  867. // traverse the solution matrix from the lower-right to
  868. // the upper-left.
  869. void ed_node( ed_r* r, int i, int j )
  870. {
  871. unsigned m;
  872. // stop when the upper-right is encountered
  873. if( i==0 && j==0 )
  874. {
  875. ed_print_candidate(r);
  876. ed_eval_candidate(r, ed_score_candidate(r) );
  877. return;
  878. }
  879. ed_val* vp = r->m + i + (j*r->rn);
  880. // for each possible dir: up,left,up-left
  881. for(m=1; m<kEdCnt; ++m)
  882. if( vp->v[m] == vp->v[kEdMinIdx] )
  883. {
  884. unsigned ii = i-1;
  885. unsigned jj = j-1;
  886. switch(m)
  887. {
  888. case kEdSubIdx:
  889. break;
  890. case kEdDelIdx:
  891. jj = j;
  892. break;
  893. case kEdInsIdx:
  894. ii = i;
  895. break;
  896. }
  897. // prepend to the current candidate path: r->p_cur
  898. ed_path_push(r,m,ii,jj,vp->matchFl,vp->transFl);
  899. // recurse!
  900. ed_node(r,ii,jj);
  901. // remove the first element from the current path
  902. ed_path_pop(r);
  903. }
  904. }
  905. void ed_align( ed_r* r )
  906. {
  907. int i = r->rn-1;
  908. int j = r->cn-1;
  909. unsigned m = r->m[i + (j*r->rn)].v[kEdMinIdx];
  910. if( m==cmMax(r->rn,r->cn) )
  911. printf("Edit distance is at max: %i. No Match.\n",m);
  912. else
  913. ed_node(r,i,j);
  914. }
  915. void ed_free( ed_r* r )
  916. {
  917. cmMemFree(r->m);
  918. cmMemFree(r->p_mem);
  919. }
  920. void ed_main()
  921. {
  922. const char* s0 = "YHCQPGK";
  923. const char* s1 = "LAHYQQKPGKA";
  924. s0 = "ABCDE";
  925. s1 = "ABDCE";
  926. //s1 = "FGHIJK";
  927. ed_r r;
  928. ed_init(&r,s0,s1);
  929. ed_calc_mtx(&r);
  930. ed_print_mtx(&r);
  931. ed_align(&r);
  932. ed_print_opt(&r);
  933. ed_free(&r);
  934. }
  935. //=======================================================================================================================
  936. cmScAlign* cmScAlignAlloc( cmCtx* c, cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN )
  937. {
  938. cmScAlign* op = cmObjAlloc(cmScAlign,c,p);
  939. if( srate != 0 )
  940. if( cmScAlignInit(op,cbFunc,cbArg,srate,scH,midiN,scWndN) != cmOkRC )
  941. cmScAlignFree(&op);
  942. return op;
  943. }
  944. cmRC_t cmScAlignFree( cmScAlign** pp )
  945. {
  946. cmRC_t rc = cmOkRC;
  947. if( pp==NULL || *pp==NULL )
  948. return rc;
  949. cmScAlign* p = *pp;
  950. if((rc = cmScAlignFinal(p)) != cmOkRC )
  951. return rc;
  952. cmMemFree(p->loc);
  953. cmMemFree(p->midiBuf);
  954. cmMemFree(p->m);
  955. cmMemFree(p->p_mem);
  956. cmMemFree(p->res);
  957. cmObjFree(pp);
  958. return rc;
  959. }
  960. void _cmScAlignPrint( cmScAlign* p )
  961. {
  962. int i,j;
  963. for(i=0; i<p->locN; ++i)
  964. {
  965. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scLocIdx);
  966. for(j=0; j<p->loc[i].evtCnt; ++j)
  967. printf("%s ",cmMidiToSciPitch(p->loc[i].evtV[j].pitch,NULL,0));
  968. printf("\n");
  969. }
  970. }
  971. cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN )
  972. {
  973. cmRC_t rc;
  974. if((rc = cmScAlignFinal(p)) != cmOkRC )
  975. return rc;
  976. if( midiN > scWndN )
  977. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score alignment MIDI event buffer length (%i) must be less than the score window length (%i).",midiN,scWndN);
  978. p->cbFunc = cbFunc;
  979. p->cbArg = cbArg;
  980. p->srate = srate;
  981. p->scH = scH;
  982. p->locN = cmScoreEvtCount(scH);
  983. p->loc = cmMemResizeZ(cmScAlignLoc_t,p->loc,p->locN);
  984. p->mn = midiN;
  985. p->midiBuf = cmMemResizeZ(cmScAlignMidiEvt_t,p->midiBuf,midiN);
  986. p->mbi = midiN;
  987. p->printFl = true;
  988. // Setup score structures
  989. // for each score location
  990. unsigned li,ei;
  991. for(li=0,ei=0; li<cmScoreLocCount(p->scH); ++li)
  992. {
  993. unsigned i,n;
  994. const cmScoreLoc_t* lp = cmScoreLoc(p->scH,li);
  995. // count the number of note events at location li
  996. for(n=0,i=0; i<lp->evtCnt; ++i)
  997. if( lp->evtArray[i]->type == kNonEvtScId )
  998. ++n;
  999. assert( ei+n <= p->locN );
  1000. // duplicate each note at location li n times
  1001. for(i=0; i<n; ++i)
  1002. {
  1003. unsigned j,k;
  1004. p->loc[ei+i].evtCnt = n;
  1005. p->loc[ei+i].evtV = cmMemAllocZ(cmScAlignScEvt_t,n);
  1006. p->loc[ei+i].scLocIdx = li;
  1007. p->loc[ei+i].barNumb = lp->barNumb;
  1008. for(j=0,k=0; j<lp->evtCnt; ++j)
  1009. if( lp->evtArray[j]->type == kNonEvtScId )
  1010. {
  1011. p->loc[ei+i].evtV[k].pitch = lp->evtArray[j]->pitch;
  1012. p->loc[ei+i].evtV[k].scEvtIdx = lp->evtArray[j]->index;
  1013. ++k;
  1014. }
  1015. }
  1016. ei += n;
  1017. }
  1018. assert(ei<=p->locN);
  1019. p->locN = ei;
  1020. // setup edit distance structures
  1021. p->rn = midiN+1;
  1022. p->cn = scWndN+1;
  1023. p->m = cmMemResizeZ(cmScAlignVal_t, p->m, p->rn*p->cn );
  1024. p->pn = p->rn + p->cn;
  1025. p->p_mem = cmMemResizeZ(cmScAlignPath_t, p->p_mem, 2*p->pn );
  1026. p->p_avl = p->p_mem;
  1027. p->p_cur = NULL;
  1028. p->p_opt = p->p_mem + p->pn;
  1029. p->s_opt = DBL_MAX;
  1030. p->resN = 2 * cmScoreEvtCount(scH); // make twice as many result records as there are score events
  1031. p->res = cmMemResizeZ(cmScAlignResult_t, p->res, p->resN);
  1032. p->stepCnt = 3;
  1033. p->maxStepMissCnt = 4;
  1034. // fill in the default values for the first row
  1035. // and column of the DP matrix
  1036. unsigned i,j,k;
  1037. for(i=0; i<p->rn; ++i)
  1038. for(j=0; j<p->cn; ++j)
  1039. {
  1040. unsigned v[] = {0,0,0,0};
  1041. if( i == 0 )
  1042. {
  1043. v[kSaMinIdx] = j;
  1044. v[kSaInsIdx] = j;
  1045. }
  1046. else
  1047. if( j == 0 )
  1048. {
  1049. v[kSaMinIdx] = i;
  1050. v[kSaDelIdx] = i;
  1051. }
  1052. for(k=0; k<kSaCnt; ++k)
  1053. p->m[ i + (j*p->rn) ].v[k] = v[k];
  1054. }
  1055. // put pn path records on the available list
  1056. for(i=0; i<p->pn; ++i)
  1057. {
  1058. p->p_mem[i].next = i<p->pn-1 ? p->p_mem + i + 1 : NULL;
  1059. p->p_opt[i].next = i<p->pn-1 ? p->p_opt + i + 1 : NULL;
  1060. }
  1061. //_cmScAlignPrint(p);
  1062. return rc;
  1063. }
  1064. cmRC_t cmScAlignFinal( cmScAlign* p )
  1065. {
  1066. unsigned i;
  1067. for(i=0; i<p->locN; ++i)
  1068. cmMemPtrFree(&p->loc[i].evtV);
  1069. return cmOkRC;
  1070. }
  1071. void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx )
  1072. {
  1073. assert( begScanLocIdx < p->locN );
  1074. p->mbi = p->mn;
  1075. p->mni = 0;
  1076. p->begScanLocIdx = begScanLocIdx;
  1077. p->s_opt = DBL_MAX;
  1078. p->missCnt = 0;
  1079. p->scanCnt = 0;
  1080. p->ri = 0;
  1081. }
  1082. cmScAlignVal_t* _cmScAlignValPtr( cmScAlign* p, unsigned i, unsigned j )
  1083. {
  1084. assert( i < p->rn );
  1085. assert( j < p->cn );
  1086. return p->m + i + (j*p->rn);
  1087. }
  1088. bool _cmScAlignIsMatch( const cmScAlignLoc_t* loc, unsigned pitch )
  1089. {
  1090. unsigned i;
  1091. for(i=0; i<loc->evtCnt; ++i)
  1092. if( loc->evtV[i].pitch == pitch )
  1093. return true;
  1094. return false;
  1095. }
  1096. bool _cmScAlignIsTrans( cmScAlign* p, const cmScAlignVal_t* v1p, unsigned i, unsigned j )
  1097. {
  1098. bool fl = false;
  1099. cmScAlignVal_t* v0p = _cmScAlignValPtr(p,i,j);
  1100. if( i>=1 && j>=1
  1101. && v1p->v[kSaMinIdx] == v1p->v[kSaSubIdx]
  1102. && v1p->matchFl == false
  1103. && v0p->v[kSaMinIdx] == v0p->v[kSaSubIdx]
  1104. && v0p->matchFl == false )
  1105. {
  1106. unsigned c00 = p->midiBuf[i-1].pitch;
  1107. unsigned c01 = p->midiBuf[i ].pitch;
  1108. cmScAlignLoc_t* c10 = p->loc + p->begScanLocIdx + j - 1;
  1109. cmScAlignLoc_t* c11 = p->loc + p->begScanLocIdx + j;
  1110. fl = _cmScAlignIsMatch(c11,c00) && _cmScAlignIsMatch(c10,c01);
  1111. }
  1112. return fl;
  1113. }
  1114. unsigned _cmScAlignMin( cmScAlign* p, unsigned i, unsigned j )
  1115. {
  1116. assert( i<p->rn && j<p->cn );
  1117. //return p->m[ i + (j*p->rn) ].v[kSaMinIdx];
  1118. return _cmScAlignValPtr(p,i,j)->v[kSaMinIdx];
  1119. }
  1120. // Returns 'false' if the score window goes past the end of the score
  1121. // (i.e. p->begScanLocIdx + p->cn > p->locN )
  1122. bool _cmScAlignCalcMtx( cmScAlign* p )
  1123. {
  1124. // the midi buffer must be full
  1125. assert( p->mbi == 0 );
  1126. // loc[begScanLocIdx:begScanLocIdx+p->cn-1] must be valid
  1127. if( p->begScanLocIdx + p->cn > p->locN )
  1128. return false;
  1129. unsigned i,j;
  1130. for(i=1; i<p->rn; ++i)
  1131. for(j=1; j<p->cn; ++j)
  1132. {
  1133. cmScAlignLoc_t* loc = p->loc + p->begScanLocIdx + j - 1;
  1134. unsigned pitch = p->midiBuf[i-1].pitch;
  1135. cmScAlignVal_t* vp = _cmScAlignValPtr(p,i,j);
  1136. vp->matchFl = _cmScAlignIsMatch(loc,pitch);
  1137. unsigned cost = vp->matchFl ? 0 : 1;
  1138. vp->v[kSaSubIdx] = _cmScAlignMin(p,i-1,j-1) + cost;
  1139. vp->v[kSaDelIdx] = _cmScAlignMin(p,i-1,j ) + 1;
  1140. vp->v[kSaInsIdx] = _cmScAlignMin(p,i, j-1) + 1;
  1141. vp->v[kSaMinIdx] = cmMin( vp->v[kSaSubIdx], cmMin(vp->v[kSaDelIdx],vp->v[kSaInsIdx]));
  1142. vp->transFl = _cmScAlignIsTrans(p,vp,i-1,j-1);
  1143. }
  1144. return true;
  1145. }
  1146. void _cmScAlignPathPush( cmScAlign* r, unsigned code, unsigned ri, unsigned ci, bool matchFl, bool transFl )
  1147. {
  1148. assert(r->p_avl != NULL );
  1149. cmScAlignPath_t* p = r->p_avl;
  1150. r->p_avl = r->p_avl->next;
  1151. p->code = code;
  1152. p->ri = ri;
  1153. p->ci = ci;
  1154. p->matchFl = code==kSaSubIdx ? matchFl : false;
  1155. p->transFl = transFl;
  1156. p->next = r->p_cur;
  1157. r->p_cur = p;
  1158. }
  1159. void _cmScAlignPathPop( cmScAlign* r )
  1160. {
  1161. assert( r->p_cur != NULL );
  1162. cmScAlignPath_t* tp = r->p_cur->next;
  1163. r->p_cur->next = r->p_avl;
  1164. r->p_avl = r->p_cur;
  1165. r->p_cur = tp;
  1166. }
  1167. double _cmScAlignScoreCandidate( cmScAlign* r )
  1168. {
  1169. cmScAlignPath_t* cp = r->p_cur;
  1170. cmScAlignPath_t* bp = r->p_cur;
  1171. cmScAlignPath_t* ep = NULL;
  1172. for(; cp!=NULL; cp=cp->next)
  1173. if( cp->code != kSaInsIdx )
  1174. {
  1175. bp = cp;
  1176. break;
  1177. }
  1178. for(; cp!=NULL; cp=cp->next)
  1179. if( cp->code!=kSaInsIdx )
  1180. ep = cp;
  1181. assert( ep!=NULL && bp!=ep);
  1182. unsigned n=1;
  1183. for(cp=bp; cp!=ep; cp=cp->next)
  1184. ++n;
  1185. double gapCnt = 0;
  1186. double penalty = 0;
  1187. bool pfl = bp->matchFl;
  1188. unsigned i;
  1189. cp = bp;
  1190. for(i=0; i<n; ++i,cp=cp->next)
  1191. {
  1192. // a gap is a transition from a matching subst. to an insert or deletion
  1193. //if( pc != cp->code && cp->code != kSaSubIdx && pc==kSaSubIdx && pfl==true )
  1194. if( pfl==true && cp->matchFl==false )
  1195. ++gapCnt;
  1196. //
  1197. switch( cp->code )
  1198. {
  1199. case kSaSubIdx:
  1200. penalty += cp->matchFl ? 0 : 1;
  1201. penalty -= cp->transFl ? 1 : 0;
  1202. break;
  1203. case kSaDelIdx:
  1204. penalty += 1;
  1205. break;
  1206. case kSaInsIdx:
  1207. penalty += 1;
  1208. break;
  1209. }
  1210. pfl = cp->matchFl;
  1211. }
  1212. double score = gapCnt/n + penalty;
  1213. //printf("n:%i gaps:%f gap_score:%f penalty:%f score:%f\n",n,gapCnt,gapCnt/n,penalty,score);
  1214. return score;
  1215. }
  1216. void _cmScAlignEvalCandidate( cmScAlign* r, double score )
  1217. {
  1218. if( r->s_opt == DBL_MAX || score < r->s_opt)
  1219. {
  1220. // copy the p_cur to p_opt[]
  1221. cmScAlignPath_t* cp = r->p_cur;
  1222. unsigned i;
  1223. for(i=0; cp!=NULL && i<r->pn; cp=cp->next,++i)
  1224. {
  1225. r->p_opt[i].code = cp->code;
  1226. r->p_opt[i].ri = cp->ri;
  1227. r->p_opt[i].ci = cp->ci;
  1228. r->p_opt[i].matchFl = cp->matchFl;
  1229. r->p_opt[i].transFl = cp->transFl;
  1230. r->p_opt[i].next = cp->next==NULL ? NULL : r->p_opt + i + 1;
  1231. }
  1232. assert( i < r->pn );
  1233. r->p_opt[i].code = 0; // terminate with code=0
  1234. r->s_opt = score;
  1235. }
  1236. }
  1237. // traverse the solution matrix from the lower-right to
  1238. // the upper-left.
  1239. void _cmScAlignGenPaths( cmScAlign* r, int i, int j )
  1240. {
  1241. unsigned m;
  1242. // stop when the upper-right is encountered
  1243. if( i==0 && j==0 )
  1244. {
  1245. _cmScAlignEvalCandidate(r, _cmScAlignScoreCandidate(r) );
  1246. return;
  1247. }
  1248. cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j);
  1249. // for each possible dir: up,left,up-left
  1250. for(m=1; m<kSaCnt; ++m)
  1251. if( vp->v[m] == vp->v[kSaMinIdx] )
  1252. {
  1253. // prepend to the current candidate path: r->p_cur
  1254. _cmScAlignPathPush(r,m,i,j,vp->matchFl,vp->transFl);
  1255. int ii = i-1;
  1256. int jj = j-1;
  1257. switch(m)
  1258. {
  1259. case kSaSubIdx:
  1260. break;
  1261. case kSaDelIdx:
  1262. jj = j;
  1263. break;
  1264. case kSaInsIdx:
  1265. ii = i;
  1266. break;
  1267. }
  1268. // recurse!
  1269. _cmScAlignGenPaths(r,ii,jj);
  1270. // remove the first element from the current path
  1271. _cmScAlignPathPop(r);
  1272. }
  1273. }
  1274. double _cmScAlign( cmScAlign* p )
  1275. {
  1276. int i = p->rn-1;
  1277. int j = p->cn-1;
  1278. unsigned m = _cmScAlignMin(p,i,j); //p->m[i + (j*p->rn)].v[kSaMinIdx];
  1279. if( m==cmMax(p->rn,p->cn) )
  1280. printf("Edit distance is at max: %i. No Match.\n",m);
  1281. else
  1282. _cmScAlignGenPaths(p,i,j);
  1283. return p->s_opt;
  1284. }
  1285. bool cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1286. {
  1287. bool fl = p->mbi > 0;
  1288. bool retFl = true;
  1289. cmScAlignInputMidi(p,smpIdx,status,d0,d1);
  1290. if( fl && p->mbi == 0 )
  1291. {
  1292. if( cmScAlignScan(p,cmInvalidCnt) == cmInvalidIdx )
  1293. retFl = false;
  1294. }
  1295. else
  1296. {
  1297. if( !fl && p->mbi == 0 )
  1298. if( !cmScAlignStep(p) )
  1299. retFl = false;
  1300. }
  1301. return retFl;
  1302. }
  1303. bool cmScAlignInputMidi( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1304. {
  1305. if( status != kNoteOnMdId )
  1306. return false;
  1307. unsigned mi = p->mn-1;
  1308. //printf("%3i %5.2f %4s\n",p->mni,(double)smpIdx/p->srate,cmMidiToSciPitch(d0,NULL,0));
  1309. // shift the new MIDI event onto the end of the MIDI buffer
  1310. memmove(p->midiBuf,p->midiBuf+1,sizeof(cmScAlignMidiEvt_t)*mi);
  1311. p->midiBuf[mi].locIdx = cmInvalidIdx;
  1312. p->midiBuf[mi].cbCnt = 0;
  1313. p->midiBuf[mi].mni = p->mni++;
  1314. p->midiBuf[mi].smpIdx = smpIdx;
  1315. p->midiBuf[mi].pitch = d0;
  1316. p->midiBuf[mi].vel = d1;
  1317. if( p->mbi > 0 )
  1318. --p->mbi;
  1319. return true;
  1320. }
  1321. // If mep==NULL then the identified score location was not matched (this is an 'insert')
  1322. // these only occurr during 'scan' not 'step'.
  1323. //
  1324. // If locIdx == cmInvalidIdx then the MIDI event did not match a score location
  1325. // When this occurrs during a scan then this is a 'deleted' MIDI note otherwise
  1326. // the note was not found inside loc[esi-stepCnt:esi+stepCnt].
  1327. //
  1328. // If mep!=NULL && scLocIdx!=cmInvalidIdx but matchFl==false then this is a
  1329. // 'substitute' with a mismatch. These only occur during 'scan'.
  1330. void _cmScAlignCb( cmScAlign* p, unsigned locIdx, cmScAlignMidiEvt_t* mep, bool matchFl, bool transFl )
  1331. {
  1332. // verify that the result buffer is not full
  1333. if( p->ri >= p->resN )
  1334. {
  1335. cmCtxRtCondition( &p->obj, cmArgAssertRC, "The score alignment result buffer is full.");
  1336. return;
  1337. }
  1338. // don't report unmatched score locations
  1339. if( mep == NULL )
  1340. return;
  1341. ++mep->cbCnt;
  1342. cmScAlignResult_t* rp = NULL;
  1343. // if this is the first time this MIDI event has generated a callback ...
  1344. if( mep->cbCnt == 1 )
  1345. rp = p->res + p->ri++; // ... then create a new record in p->res[] ...
  1346. else
  1347. if( mep->cbCnt > 1 && matchFl ) // ... otherwise if it was matched ...
  1348. {
  1349. unsigned i;
  1350. for(i=0; i<p->ri; ++i)
  1351. if(p->res[i].mni == mep->mni )
  1352. {
  1353. if( p->res[i].matchFl == false ) // ... and it's previous recd was not matched then update the record with the match info.
  1354. rp = p->res + i;
  1355. }
  1356. }
  1357. if(rp == NULL )
  1358. return;
  1359. assert( locIdx != cmInvalidIdx || mep != NULL );
  1360. rp->locIdx = locIdx;
  1361. rp->smpIdx = mep==NULL ? cmInvalidIdx : mep->smpIdx;
  1362. rp->mni = mep==NULL ? cmInvalidIdx : mep->mni;
  1363. rp->pitch = mep==NULL ? kInvalidMidiPitch : mep->pitch;
  1364. rp->vel = mep==NULL ? kInvalidMidiVelocity : mep->vel;
  1365. rp->matchFl = mep==NULL ? false : matchFl;
  1366. rp->transFl = mep==NULL ? false : transFl;
  1367. }
  1368. void _cmScAlignPrintPath( cmScAlign* p, cmScAlignPath_t* cp, unsigned bsi )
  1369. {
  1370. assert( bsi != cmInvalidIdx );
  1371. cmScAlignPath_t* pp = cp;
  1372. int polyN = 0;
  1373. int i;
  1374. printf("loc: ");
  1375. // get the polyphony count for the score window
  1376. for(i=0; pp!=NULL; pp=pp->next)
  1377. {
  1378. cmScAlignLoc_t* lp = p->loc + bsi + pp->ci;
  1379. if( pp->code!=kSaDelIdx )
  1380. {
  1381. if(lp->evtCnt > polyN)
  1382. polyN = lp->evtCnt;
  1383. printf("%4i ",bsi+i);
  1384. ++i;
  1385. }
  1386. else
  1387. printf("%4s "," ");
  1388. }
  1389. printf("\n");
  1390. // print the score notes
  1391. for(i=polyN; i>0; --i)
  1392. {
  1393. printf("%3i: ",i);
  1394. for(pp=cp; pp!=NULL; pp=pp->next)
  1395. {
  1396. int locIdx = bsi + pp->ci - 1;
  1397. assert(0 <= locIdx && locIdx <= p->locN);
  1398. cmScAlignLoc_t* lp = p->loc + locIdx;
  1399. if( pp->code!=kSaDelIdx && lp->evtCnt >= i )
  1400. printf("%4s ",cmMidiToSciPitch(lp->evtV[i-1].pitch,NULL,0));
  1401. else
  1402. printf("%4s ", pp->code==kSaDelIdx? "-" : " ");
  1403. }
  1404. printf("\n");
  1405. }
  1406. printf("mid: ");
  1407. // print the MIDI buffer
  1408. for(pp=cp; pp!=NULL; pp=pp->next)
  1409. {
  1410. if( pp->code!=kSaInsIdx )
  1411. printf("%4s ",cmMidiToSciPitch(p->midiBuf[pp->ri-1].pitch,NULL,0));
  1412. else
  1413. printf("%4s ",pp->code==kSaInsIdx?"-":" ");
  1414. }
  1415. printf("\nmni: ");
  1416. // print the MIDI buffer index (mni)
  1417. for(pp=cp; pp!=NULL; pp=pp->next)
  1418. {
  1419. if( pp->code!=kSaInsIdx )
  1420. printf("%4i ",p->midiBuf[pp->ri-1].mni);
  1421. else
  1422. printf("%4s ",pp->code==kSaInsIdx?"-":" ");
  1423. }
  1424. printf("\n op: ");
  1425. // print the substitute/insert/delete operation
  1426. for(pp=cp; pp!=NULL; pp=pp->next)
  1427. {
  1428. char c = ' ';
  1429. switch( pp->code )
  1430. {
  1431. case kSaSubIdx: c = 's'; break;
  1432. case kSaDelIdx: c = 'd'; break;
  1433. case kSaInsIdx: c = 'i'; break;
  1434. default:
  1435. { assert(0); }
  1436. }
  1437. printf("%4c ",c);
  1438. }
  1439. printf("\n ");
  1440. // give substitute attribute (match or transpose)
  1441. for(pp=cp; pp!=NULL; pp=pp->next)
  1442. {
  1443. cmChar_t s[3];
  1444. int k = 0;
  1445. if( pp->matchFl )
  1446. s[k++] = 'm';
  1447. if( pp->transFl )
  1448. s[k++] = 't';
  1449. s[k] = 0;
  1450. printf("%4s ",s);
  1451. }
  1452. printf("\nscl: ");
  1453. // print the stored location index
  1454. for(pp=cp; pp!=NULL; pp=pp->next)
  1455. {
  1456. if( pp->locIdx == cmInvalidIdx )
  1457. printf("%4s "," ");
  1458. else
  1459. printf("%4i ",p->loc[pp->locIdx].scLocIdx);
  1460. }
  1461. printf("\n\n");
  1462. }
  1463. // Returns the p->loc[] index at the start of the min cost score window
  1464. // based on the current MIDI buffer.
  1465. // scanCnt is the number of time the score window will be shifted one
  1466. // location to the left
  1467. unsigned cmScAlignScan( cmScAlign* p, unsigned scanCnt )
  1468. {
  1469. unsigned bsi = cmInvalidIdx;
  1470. assert( p->mbi == 0 );
  1471. // if the MIDI buf is full
  1472. if( p->mbi == 0 )
  1473. {
  1474. double s_opt = DBL_MAX;
  1475. unsigned i;
  1476. // Loop as long as the score window is inside the score.
  1477. // Fill the Dyn Pgm matrix: MIDI_buf to score[begScanLocIdx:begScanLocIdx+scWndN-1].
  1478. for(i=0; _cmScAlignCalcMtx(p) && (scanCnt==cmInvalidCnt || i<scanCnt); ++i)
  1479. {
  1480. // locate the path through the DP matrix with the lowest edit distance (cost)
  1481. double cost = _cmScAlign(p);
  1482. // if it is less than any previous score window
  1483. if(cost < s_opt)
  1484. {
  1485. s_opt = cost;
  1486. bsi = p->begScanLocIdx;
  1487. }
  1488. // increment the score window
  1489. p->begScanLocIdx += 1;
  1490. }
  1491. // store the cost assoc'd with bsi
  1492. p->s_opt = s_opt;
  1493. }
  1494. assert( bsi != cmInvalidIdx );
  1495. // Traverse the least cost path and:
  1496. // 1) Set p->esi to the score location index of the last MIDI note
  1497. // which has a positive match with the score and assign
  1498. // the internal score index to cp->locIdx.
  1499. // 2) Set cmScAlignPath_t.locIdx - index into p->loc[] associated
  1500. // with each path element that is a 'substitute' or an 'insert'.
  1501. // 3) Set p->missCnt: the count of trailing non-positive matches.
  1502. // p->missCnt is eventually used in cmScAlignStep() to track the number
  1503. // of consecutive trailing missed notes.
  1504. cmScAlignPath_t* cp = p->p_opt;
  1505. unsigned i = bsi;
  1506. p->missCnt = 0;
  1507. p->esi = cmInvalidIdx;
  1508. for(i=0; cp!=NULL; cp=cp->next)
  1509. {
  1510. if( cp->code != kSaInsIdx )
  1511. {
  1512. assert( cp->ri > 0 );
  1513. p->midiBuf[ cp->ri-1 ].locIdx = cmInvalidIdx;
  1514. }
  1515. switch( cp->code )
  1516. {
  1517. case kSaSubIdx:
  1518. if( cp->matchFl || cp->transFl)
  1519. {
  1520. p->esi = bsi + i;
  1521. p->missCnt = 0;
  1522. if( cp->matchFl )
  1523. p->midiBuf[ cp->ri-1 ].locIdx = bsi + i;
  1524. }
  1525. else
  1526. {
  1527. ++p->missCnt;
  1528. }
  1529. cp->locIdx = bsi + i;
  1530. ++i;
  1531. break;
  1532. case kSaInsIdx:
  1533. cp->locIdx = bsi + i;
  1534. ++i;
  1535. break;
  1536. case kSaDelIdx:
  1537. cp->locIdx = cmInvalidIdx;
  1538. ++p->missCnt;
  1539. break;
  1540. }
  1541. }
  1542. // if no positive matches were found
  1543. if( p->esi == cmInvalidIdx )
  1544. bsi = cmInvalidIdx;
  1545. else
  1546. {
  1547. // report matches
  1548. for(cp=p->p_opt; cp!=NULL; cp=cp->next)
  1549. {
  1550. unsigned locIdx = cp->locIdx;
  1551. cmScAlignMidiEvt_t* mep = NULL;
  1552. if( cp->code != kSaInsIdx )
  1553. mep = p->midiBuf + cp->ri - 1;
  1554. _cmScAlignCb(p,locIdx,mep,cp->matchFl,cp->transFl);
  1555. }
  1556. }
  1557. return bsi;
  1558. }
  1559. bool cmScAlignStep( cmScAlign* p )
  1560. {
  1561. int i;
  1562. unsigned pitch = p->midiBuf[ p->mn-1 ].pitch;
  1563. unsigned locIdx = cmInvalidIdx;
  1564. // the tracker must be sync'd to step
  1565. if( p->esi == cmInvalidIdx )
  1566. return false;
  1567. // if the end of the score has been reached
  1568. if( p->esi + 1 >= p->locN )
  1569. return cmInvalidIdx;
  1570. // attempt to match to next location first
  1571. if( _cmScAlignIsMatch(p->loc + p->esi + 1, pitch) )
  1572. {
  1573. locIdx = p->esi + 1;
  1574. }
  1575. else
  1576. {
  1577. //
  1578. for(i=2; i<p->stepCnt; ++i)
  1579. {
  1580. // go forward
  1581. if( p->esi+i < p->locN && _cmScAlignIsMatch(p->loc + p->esi + i, pitch) )
  1582. {
  1583. locIdx = p->esi + i;
  1584. break;
  1585. }
  1586. // go backward
  1587. if( p->esi >= (i-1) && _cmScAlignIsMatch(p->loc + p->esi - (i-1), pitch) )
  1588. {
  1589. locIdx = p->esi - (i-1);
  1590. break;
  1591. }
  1592. }
  1593. }
  1594. p->midiBuf[ p->mn-1 ].locIdx = locIdx;
  1595. if( locIdx == cmInvalidIdx )
  1596. ++p->missCnt;
  1597. else
  1598. {
  1599. p->missCnt = 0;
  1600. p->esi = locIdx;
  1601. _cmScAlignCb(p,locIdx, p->midiBuf + p->mn - 1,true,false);
  1602. }
  1603. if( p->missCnt >= p->maxStepMissCnt )
  1604. {
  1605. p->begScanLocIdx = p->esi > p->rn ? p->esi - p->rn : 0;
  1606. p->s_opt = DBL_MAX;
  1607. unsigned bsi = cmScAlignScan(p,p->rn*2);
  1608. ++p->scanCnt;
  1609. // if the scan failed find a match
  1610. if( bsi == cmInvalidIdx )
  1611. return false;
  1612. //if( bsi != cmInvalidIdx )
  1613. // _cmScAlignPrintPath(p, p->p_opt, bsi );
  1614. }
  1615. return true;
  1616. }
  1617. void _cmScAlignPrintMtx( cmScAlign* r)
  1618. {
  1619. unsigned i,j,k;
  1620. for(i=0; i<r->rn; ++i)
  1621. {
  1622. for(j=0; j<r->cn; ++j)
  1623. {
  1624. printf("(");
  1625. const cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j);
  1626. for(k=0; k<kSaCnt; ++k)
  1627. {
  1628. printf("%i",vp->v[k]);
  1629. if( k<kSaCnt-1)
  1630. printf(", ");
  1631. else
  1632. printf(" ");
  1633. }
  1634. printf("%c)",vp->transFl?'t':' ');
  1635. }
  1636. printf("\n");
  1637. }
  1638. }
  1639. void cmScAlignPrintOpt( cmScAlign* p )
  1640. {
  1641. unsigned i;
  1642. for(i=0; p->p_opt[i].code!=0; ++i)
  1643. {
  1644. cmScAlignPath_t* cp = p->p_opt + i;
  1645. char c0 = cp->matchFl ? 'm' : ' ';
  1646. char c1 = cp->transFl ? 't' : ' ';
  1647. printf("%2i code:%i ri:%2i ci:%2i %c%c\n",i,cp->code,cp->ri,cp->ci,c0,c1);
  1648. }
  1649. printf("score:%f\n",p->s_opt);
  1650. }
  1651. enum
  1652. {
  1653. kBarSaFl = 0x01, // this is a score bar
  1654. kScNoteSaFl = 0x02, // this is a score reference note (if mni != cmInvalidIdx then it was matched)
  1655. kSubsErrSaFl = 0x04, // 'subs' mismatch midi note
  1656. kMidiErrSaFl = 0x08, // 'deleted' Midi note
  1657. };
  1658. typedef struct cmScAlignPrint_str
  1659. {
  1660. unsigned flags;
  1661. unsigned scLocIdx;
  1662. unsigned smpIdx;
  1663. unsigned pitch;
  1664. unsigned vel;
  1665. unsigned mni;
  1666. bool matchFl;
  1667. bool transFl;
  1668. } cmScAlignPrint_t;
  1669. void _cmScAlignPrintList( cmScAlignPrint_t* a, unsigned an )
  1670. {
  1671. cmScAlignPrint_t* pp;
  1672. unsigned i;
  1673. printf("----------------------------------------------------\n");
  1674. printf("idx scl mni pit flg \n");
  1675. for(i=0; i<an; ++i)
  1676. {
  1677. pp = a + i;
  1678. printf("%3i %3i %3i %4s 0x%x\n",i,pp->scLocIdx,pp->mni,
  1679. pp->pitch==kInvalidMidiPitch ? " " : cmMidiToSciPitch(pp->pitch,NULL,0),
  1680. pp->flags);
  1681. }
  1682. printf("\n");
  1683. }
  1684. // insert a black record at a[i]
  1685. unsigned _cmScAlignPrintExpand( cmScAlignPrint_t* a, unsigned aan, unsigned i, unsigned an )
  1686. {
  1687. assert( an < aan );
  1688. memmove( a + i + 1, a + i, (an-i)*sizeof(cmScAlignPrint_t));
  1689. memset( a + i, 0, sizeof(cmScAlignPrint_t));
  1690. return an + 1;
  1691. }
  1692. void _cmScAlignPrintOutResult( cmScAlign* p, cmScAlignResult_t* rp, const cmChar_t* label )
  1693. {
  1694. printf("loc:%4i scloc:%4i smp:%10i mni:%4i %4s %c %c %s\n",
  1695. rp->locIdx,
  1696. rp->locIdx==cmInvalidIdx ? -1 : p->loc[rp->locIdx].scLocIdx,
  1697. rp->smpIdx,
  1698. rp->mni,
  1699. rp->pitch<=127 ? cmMidiToSciPitch(rp->pitch,NULL,0) : " ",
  1700. rp->matchFl ? 'm' : ' ',
  1701. rp->transFl ? 't' : ' ',
  1702. label);
  1703. }
  1704. void _cmScAlignPrintSet( cmScAlignPrint_t* pp, const cmScAlignResult_t* rp, unsigned flags, unsigned scLocIdx )
  1705. {
  1706. pp->scLocIdx = scLocIdx;
  1707. pp->flags = flags;
  1708. pp->smpIdx = rp->smpIdx;
  1709. pp->pitch = rp->pitch;
  1710. pp->vel = rp->vel;
  1711. pp->mni = rp->mni;
  1712. assert( pp->scLocIdx!=cmInvalidIdx || pp->mni != cmInvalidIdx );
  1713. }
  1714. unsigned _cmScAlignPrintPoly( cmScAlignPrint_t* a, unsigned an, unsigned scLocIdx )
  1715. {
  1716. unsigned polyN = 0;
  1717. unsigned i;
  1718. for(i=0; i<an; ++i)
  1719. if( a[i].scLocIdx == scLocIdx )
  1720. break;
  1721. if( i < an )
  1722. {
  1723. for(; i<an; ++i,++polyN)
  1724. if( a[i].scLocIdx != scLocIdx )
  1725. break;
  1726. // identical scLocIdx values must be consecutive
  1727. for(; i<an; ++i)
  1728. {
  1729. if( a[i].scLocIdx == scLocIdx )
  1730. _cmScAlignPrintList(a,an);
  1731. assert( a[i].scLocIdx != scLocIdx );
  1732. }
  1733. }
  1734. return polyN;
  1735. }
  1736. cmScAlignPrint_t* _cmScAlignPrintRecd(cmScAlignPrint_t* a, unsigned an, unsigned scLocIdx, unsigned polyIdx )
  1737. {
  1738. unsigned i,j;
  1739. for(i=0; i<an; ++i)
  1740. {
  1741. if( a[i].scLocIdx == scLocIdx )
  1742. for(j=0; i<an; ++j,++i)
  1743. {
  1744. if( a[i].scLocIdx != scLocIdx )
  1745. break;
  1746. if( j == polyIdx )
  1747. return a + i;
  1748. }
  1749. }
  1750. return NULL;
  1751. }
  1752. void _cmScAlignPrintReport( cmScAlign* p, cmScAlignPrint_t* a, unsigned an, unsigned bsi, unsigned esi )
  1753. {
  1754. unsigned colN = 5;
  1755. unsigned bli = bsi;
  1756. bool titleFl = true;
  1757. while( bli < esi )
  1758. {
  1759. unsigned i,j;
  1760. // get ending scLocIdx
  1761. unsigned eli = cmMin(bli+colN, esi);
  1762. // get the max poly count
  1763. unsigned polyN = 0;
  1764. for(i=bli; i<eli; ++i)
  1765. {
  1766. unsigned pn = _cmScAlignPrintPoly(a,an,i);
  1767. if( pn > polyN )
  1768. polyN = pn;
  1769. }
  1770. // print titles
  1771. if( titleFl )
  1772. {
  1773. printf(" ");
  1774. for(j=bli; j<eli; ++j)
  1775. printf("| %4s %4s %3s %1s ","mni"," ","vel"," ");
  1776. printf("\n");
  1777. titleFl = false;
  1778. }
  1779. // print 'loc' index line
  1780. printf("scl: ");
  1781. for(j=bli; j<eli; ++j)
  1782. printf("| %4i %4s %3s %1s ",j," "," "," ");
  1783. printf("\n");
  1784. for(i=polyN; i>0; --i)
  1785. {
  1786. printf("%3i: ",i);
  1787. for(j=bli; j<eli; ++j)
  1788. {
  1789. cmScAlignPrint_t* pp;
  1790. if((pp = _cmScAlignPrintRecd(a,an,j,i-1)) == NULL )
  1791. printf("| %4s %4s %3s %1s "," "," "," "," ");
  1792. else
  1793. {
  1794. if( pp->mni == cmInvalidIdx && cmIsNotFlag(pp->flags,kBarSaFl) )
  1795. printf("| %4s %4s %3s %1s "," ",cmMidiToSciPitch(pp->pitch,NULL,0)," "," ");
  1796. else
  1797. {
  1798. if( cmIsFlag(pp->flags,kBarSaFl) )
  1799. printf("| %4s %4s %3i %1s "," "," | ",pp->pitch,"b");
  1800. else
  1801. {
  1802. const cmChar_t* op = cmIsFlag(pp->flags,kMidiErrSaFl) ? "d" : " ";
  1803. op = cmIsFlag(pp->flags,kSubsErrSaFl) ? "s" : op;
  1804. printf("| %4i %4s %3i %1s ",pp->mni,cmMidiToSciPitch(pp->pitch,NULL,0),pp->vel,op);
  1805. }
  1806. }
  1807. }
  1808. }
  1809. printf("\n");
  1810. }
  1811. printf("\n");
  1812. bli = eli;
  1813. }
  1814. }
  1815. void _cmScAlignPrintResult( cmScAlign* p )
  1816. {
  1817. // determine the scH score begin and end indexes
  1818. unsigned bsi = cmScoreLocCount(p->scH);
  1819. unsigned esi = 0;
  1820. unsigned i,j;
  1821. for(i=0; i<p->ri; ++i)
  1822. {
  1823. cmScAlignResult_t* rp = p->res + i;
  1824. assert( rp->locIdx==cmInvalidIdx || rp->locIdx<p->locN);
  1825. if( rp->locIdx != cmInvalidIdx )
  1826. {
  1827. bsi = cmMin(bsi,p->loc[ rp->locIdx ].scLocIdx);
  1828. esi = cmMax(esi,p->loc[ rp->locIdx ].scLocIdx);
  1829. }
  1830. }
  1831. // get a count of MIDI events + score events
  1832. unsigned aan = p->ri;
  1833. for(i=bsi; i<=esi; ++i)
  1834. {
  1835. cmScoreLoc_t* lp = cmScoreLoc( p->scH, i);
  1836. aan += lp->evtCnt;
  1837. }
  1838. cmScAlignPrint_t* a = cmMemAllocZ(cmScAlignPrint_t,aan);
  1839. unsigned an = 0;
  1840. unsigned scNoteCnt = 0;
  1841. unsigned matchCnt = 0; // matched score notes
  1842. unsigned wrongCnt = 0; // unmatched midi notes
  1843. // create a record for each score event
  1844. for(i=bsi; i<=esi; ++i)
  1845. {
  1846. cmScoreLoc_t* lp = cmScoreLoc( p->scH, i);
  1847. for(j=0; j<lp->evtCnt; ++j,++an)
  1848. {
  1849. assert( an < aan );
  1850. cmScAlignPrint_t* pp = a + an;
  1851. assert( lp->index != cmInvalidIdx );
  1852. pp->scLocIdx = lp->index;
  1853. pp->mni = cmInvalidIdx;
  1854. pp->pitch = kInvalidMidiPitch;
  1855. pp->vel = cmInvalidIdx;
  1856. switch( lp->evtArray[j]->type )
  1857. {
  1858. case kBarEvtScId:
  1859. pp->flags = kBarSaFl;
  1860. pp->pitch = lp->evtArray[j]->barNumb;
  1861. pp->mni = cmInvalidIdx;
  1862. break;
  1863. case kNonEvtScId:
  1864. pp->flags = kScNoteSaFl;
  1865. pp->pitch = lp->evtArray[j]->pitch;
  1866. ++scNoteCnt;
  1867. break;
  1868. }
  1869. }
  1870. }
  1871. //_cmScAlignPrintList(a,an);
  1872. // Update the score with matching MIDI notes
  1873. // for each result record ...
  1874. for(i=0; i<p->ri; ++i)
  1875. {
  1876. cmScAlignResult_t* rp = p->res + i;
  1877. rp->foundFl = false;
  1878. // ... if this is not an errant MIDI note (delete)
  1879. if( rp->locIdx != cmInvalidIdx )
  1880. {
  1881. assert( rp->locIdx != cmInvalidIdx && rp->locIdx < p->locN );
  1882. unsigned scLocIdx = p->loc[rp->locIdx].scLocIdx;
  1883. cmScAlignPrint_t* pp;
  1884. // ... find the score location matching the result record score location
  1885. for(j=0; j<an; ++j)
  1886. {
  1887. pp = a + j;
  1888. // if this score location matches the result score location
  1889. if( scLocIdx == pp->scLocIdx )
  1890. {
  1891. // if this is a matching midi node
  1892. if( rp->matchFl && cmIsFlag(pp->flags,kScNoteSaFl) && pp->pitch == rp->pitch )
  1893. {
  1894. //_cmScAlignPrintOutResult(p,rp,"match");
  1895. rp->foundFl = true;
  1896. _cmScAlignPrintSet(pp, rp, pp->flags, pp->scLocIdx );
  1897. ++matchCnt;
  1898. break;
  1899. }
  1900. // if this is a 'substitute' non-matching note
  1901. if( rp->matchFl == false && rp->mni != cmInvalidIdx )
  1902. {
  1903. //_cmScAlignPrintOutResult(p,rp,"mis-match");
  1904. ++j; // insert after the a[j]
  1905. an = _cmScAlignPrintExpand(a,aan,j,an);
  1906. _cmScAlignPrintSet(a + j, rp, kSubsErrSaFl, scLocIdx );
  1907. rp->foundFl = true;
  1908. ++wrongCnt;
  1909. break;
  1910. }
  1911. // if this is a 'skipped' score note ('insert') alert
  1912. if( rp->mni == cmInvalidIdx )
  1913. {
  1914. //_cmScAlignPrintOutResult(p,rp,"skip");
  1915. rp->foundFl = true;
  1916. break;
  1917. }
  1918. }
  1919. }
  1920. }
  1921. if( rp->foundFl == false )
  1922. {
  1923. // _cmScAlignPrintOutResult(p,rp,"not-found");
  1924. }
  1925. }
  1926. //_cmScAlignPrintList(a,an);
  1927. // for each result record ...
  1928. for(i=0; i<p->ri; ++i)
  1929. {
  1930. cmScAlignResult_t* rp = p->res + i;
  1931. unsigned scLocIdx = cmInvalidIdx;
  1932. cmScAlignPrint_t* pp = NULL;
  1933. cmScAlignPrint_t* dpp = NULL;
  1934. unsigned dmin;
  1935. // if this result did not have a matching score event
  1936. if(rp->foundFl)
  1937. continue;
  1938. for(j=0; j<an; ++j)
  1939. {
  1940. pp = a + j;
  1941. if( pp->mni!=cmInvalidId )
  1942. {
  1943. unsigned d;
  1944. if( pp->mni > rp->mni )
  1945. d = pp->mni - rp->mni;
  1946. else
  1947. d = rp->mni - pp->mni;
  1948. if( dpp == NULL || d < dmin )
  1949. {
  1950. dpp = pp;
  1951. dmin = d;
  1952. }
  1953. }
  1954. }
  1955. assert( dpp != NULL );
  1956. j = dpp - a;
  1957. if( rp->mni > dpp->mni )
  1958. ++j;
  1959. scLocIdx = dpp->scLocIdx;
  1960. an = _cmScAlignPrintExpand(a,aan,j,an);
  1961. if( rp->locIdx != cmInvalidIdx )
  1962. {
  1963. assert( rp->locIdx != cmInvalidIdx && rp->locIdx < p->locN );
  1964. scLocIdx = p->loc[ rp->locIdx ].scLocIdx;
  1965. }
  1966. ++wrongCnt;
  1967. _cmScAlignPrintSet(a + j, rp, kMidiErrSaFl, scLocIdx );
  1968. }
  1969. //_cmScAlignPrintList(a,an);
  1970. //_cmScAlignPrintReport(p,a,an,bsi,esi);
  1971. printf("score notes:%i match:%i wrong:%i \n",scNoteCnt,matchCnt,wrongCnt);
  1972. }
  1973. unsigned cmScAlignScanToTimeLineEvent( cmScAlign* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx )
  1974. {
  1975. assert( top != NULL );
  1976. cmTlMidiEvt_t* mep = NULL;
  1977. // get the next time MIDI msg
  1978. while( (mep = cmTlNextMidiEvtObjPtr(tlH, top, top->seqId )) != NULL )
  1979. {
  1980. top = &mep->obj;
  1981. // if the msg falls after the end of the marker then we are through
  1982. if( mep->obj.seqSmpIdx != cmInvalidIdx && mep->obj.seqSmpIdx > endSmpIdx )
  1983. break;
  1984. // if the time line MIDI msg a note-on
  1985. if( mep->msg->status == kNoteOnMdId )
  1986. if( !cmScAlignExec(p, mep->obj.seqSmpIdx, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1 ) )
  1987. return cmInvalidIdx;
  1988. }
  1989. return p->esi;
  1990. }
  1991. void cmScAlignCb( void* cbArg, unsigned scLocIdx, unsigned mni, unsigned pitch, unsigned vel )
  1992. {
  1993. //cmScAlign* p = (cmScAlign*)cbArg;
  1994. }
  1995. void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH )
  1996. {
  1997. unsigned i;
  1998. double srate = 96000;
  1999. unsigned midiN = 7;
  2000. unsigned scWndN = 10;
  2001. unsigned markN = 291;
  2002. cmCtx* ctx = cmCtxAlloc(NULL, rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  2003. cmScAlign* p = cmScAlignAlloc(ctx,NULL,cmScAlignCb,NULL,srate,scH,midiN,scWndN);
  2004. unsigned markCharCnt = 31;
  2005. cmChar_t markText[ markCharCnt+1 ];
  2006. p->cbArg = p; // set the callback arg.
  2007. // for each marker
  2008. for(i=0; i<markN; ++i)
  2009. {
  2010. // form the marker text
  2011. snprintf(markText,markCharCnt,"Mark %i",i);
  2012. // locate the marker
  2013. cmTlMarker_t* mp = cmTimeLineMarkerFind( tlH, markText );
  2014. if( mp == NULL )
  2015. {
  2016. printf("The marker '%s' was not found.\n",markText);
  2017. continue;
  2018. }
  2019. // skip markers which do not contain text
  2020. if( cmTextIsEmpty(mp->text) )
  2021. {
  2022. printf("The marker '%s' is being skipped because it has no text.\n",markText);
  2023. continue;
  2024. }
  2025. // reset the score follower to the beginnig of the score
  2026. cmScAlignReset(p,0);
  2027. //printf("%s %5.2f %5.2f %5.2f\n",markText,(double)(mp->obj.begSmpIdx)/p->srate,(double)(mp->obj.durSmpCnt)/p->srate,(double)(mp->obj.begSmpIdx+mp->obj.durSmpCnt)/p->srate);
  2028. // scan to the beginning of the marker
  2029. unsigned bsi = cmScAlignScanToTimeLineEvent(p,tlH,&mp->obj,mp->obj.seqSmpIdx+mp->obj.durSmpCnt);
  2030. if( bsi != cmInvalidIdx )
  2031. {
  2032. //_cmScAlignPrintMtx(p);
  2033. printf("mark:%i scans:%4i loc:%4i bar:%4i score:%5.2f miss:%i text:'%s'\n",i,p->scanCnt,bsi,p->loc[bsi].barNumb,p->s_opt,p->missCnt,mp->text);
  2034. //_cmScAlignPrintPath(p, p->p_opt, bsi );
  2035. printf("mark:%i scans:%i midi:%i text:'%s'\n",i,p->scanCnt,p->mni,mp->text);
  2036. _cmScAlignPrintResult(p);
  2037. printf("\n");
  2038. }
  2039. //break; // ONLY USE ONE MARKER DURING TESTING
  2040. }
  2041. cmScAlignFree(&p);
  2042. cmCtxFree(&ctx);
  2043. }