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

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