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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627
  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. ++k;
  987. }
  988. }
  989. ei += n;
  990. }
  991. assert(ei<=p->locN);
  992. p->locN = ei;
  993. }
  994. cmRC_t cmScMatchInit( cmScMatch* p, cmScH_t scH, unsigned maxScWndN, unsigned maxMidiWndN )
  995. {
  996. unsigned i;
  997. cmRC_t rc;
  998. if((rc = cmScMatchFinal(p)) != cmOkRC )
  999. return rc;
  1000. p->scH = scH;
  1001. p->mrn = maxMidiWndN + 1;
  1002. p->mcn = maxScWndN + 1;
  1003. p->mmn = maxMidiWndN;
  1004. p->msn = maxScWndN;
  1005. _cmScMatchInitLoc(p);
  1006. p->m = cmMemResizeZ(cmScMatchVal_t, p->m, p->mrn*p->mcn );
  1007. p->pn = p->mrn + p->mcn;
  1008. p->p_mem = cmMemResizeZ(cmScMatchPath_t, p->p_mem, 2*p->pn );
  1009. p->p_avl = p->p_mem;
  1010. p->p_cur = NULL;
  1011. p->p_opt = p->p_mem + p->pn;
  1012. // put pn path records on the available list
  1013. for(i=0; i<p->pn; ++i)
  1014. {
  1015. p->p_mem[i].next = i<p->pn-1 ? p->p_mem + i + 1 : NULL;
  1016. p->p_opt[i].next = i<p->pn-1 ? p->p_opt + i + 1 : NULL;
  1017. }
  1018. return rc;
  1019. }
  1020. cmRC_t cmScMatchFinal( cmScMatch* p )
  1021. {
  1022. unsigned i;
  1023. if( p != NULL )
  1024. for(i=0; i<p->locN; ++i)
  1025. cmMemPtrFree(&p->loc[i].evtV);
  1026. return cmOkRC;
  1027. }
  1028. cmRC_t _cmScMatchInitMtx( cmScMatch* p, unsigned rn, unsigned cn )
  1029. {
  1030. if( rn >p->mrn && cn > p->mcn )
  1031. 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);
  1032. // if the size of the mtx is not changing then there is nothing to do
  1033. if( rn == p->rn && cn == p->cn )
  1034. return cmOkRC;
  1035. // update the mtx size
  1036. p->rn = rn;
  1037. p->cn = cn;
  1038. // fill in the default values for the first row
  1039. // and column of the DP matrix
  1040. unsigned i,j,k;
  1041. for(i=0; i<rn; ++i)
  1042. for(j=0; j<cn; ++j)
  1043. {
  1044. unsigned v[] = {0,0,0,0};
  1045. if( i == 0 )
  1046. {
  1047. v[kSmMinIdx] = j;
  1048. v[kSmInsIdx] = j;
  1049. }
  1050. else
  1051. if( j == 0 )
  1052. {
  1053. v[kSmMinIdx] = i;
  1054. v[kSmDelIdx] = i;
  1055. }
  1056. for(k=0; k<kSmCnt; ++k)
  1057. p->m[ i + (j*rn) ].v[k] = v[k];
  1058. }
  1059. return cmOkRC;
  1060. }
  1061. cmScMatchVal_t* _cmScMatchValPtr( cmScMatch* p, unsigned i, unsigned j, unsigned rn, unsigned cn )
  1062. {
  1063. assert( i < rn && j < cn );
  1064. return p->m + i + (j*rn);
  1065. }
  1066. bool _cmScMatchIsMatch( const cmScMatchLoc_t* loc, unsigned pitch )
  1067. {
  1068. unsigned i;
  1069. for(i=0; i<loc->evtCnt; ++i)
  1070. if( loc->evtV[i].pitch == pitch )
  1071. return true;
  1072. return false;
  1073. }
  1074. bool _cmScMatchIsTrans( cmScMatch* p, const unsigned* pitchV, const cmScMatchVal_t* v1p, unsigned bsi, unsigned i, unsigned j, unsigned rn, unsigned cn )
  1075. {
  1076. bool fl = false;
  1077. cmScMatchVal_t* v0p = _cmScMatchValPtr(p,i,j,rn,cn);
  1078. if( i>=1 && j>=1
  1079. && v1p->v[kSmMinIdx] == v1p->v[kSmSubIdx]
  1080. && cmIsNotFlag(v1p->flags,kSmMatchFl)
  1081. && v0p->v[kSmMinIdx] == v0p->v[kSmSubIdx]
  1082. && cmIsNotFlag(v0p->flags,kSmMatchFl)
  1083. )
  1084. {
  1085. unsigned c00 = pitchV[i-1];
  1086. unsigned c01 = pitchV[i ];
  1087. cmScMatchLoc_t* c10 = p->loc + bsi + j - 1;
  1088. cmScMatchLoc_t* c11 = p->loc + bsi + j;
  1089. fl = _cmScMatchIsMatch(c11,c00) && _cmScMatchIsMatch(c10,c01);
  1090. }
  1091. return fl;
  1092. }
  1093. unsigned _cmScMatchMin( cmScMatch* p, unsigned i, unsigned j, unsigned rn, unsigned cn )
  1094. {
  1095. return _cmScMatchValPtr(p,i,j,rn,cn)->v[kSmMinIdx];
  1096. }
  1097. // Return false if bsi + cn > p->locN
  1098. // pitchV[rn-1]
  1099. bool _cmScMatchCalcMtx( cmScMatch* p, unsigned bsi, const unsigned* pitchV, unsigned rn, unsigned cn )
  1100. {
  1101. // loc[begScanLocIdx:begScanLocIdx+cn-1] must be valid
  1102. if( bsi + cn > p->locN )
  1103. return false;
  1104. unsigned i,j;
  1105. for(j=1; j<cn; ++j)
  1106. for(i=1; i<rn; ++i)
  1107. {
  1108. cmScMatchLoc_t* loc = p->loc + bsi + j - 1;
  1109. unsigned pitch = pitchV[i-1];
  1110. cmScMatchVal_t* vp = _cmScMatchValPtr(p,i,j,rn,cn);
  1111. vp->flags = _cmScMatchIsMatch(loc,pitch) ? kSmMatchFl : 0;
  1112. unsigned cost = cmIsFlag(vp->flags,kSmMatchFl) ? 0 : 1;
  1113. vp->v[kSmSubIdx] = _cmScMatchMin(p,i-1,j-1, rn, cn) + cost;
  1114. vp->v[kSmDelIdx] = _cmScMatchMin(p,i-1,j , rn, cn) + 1;
  1115. vp->v[kSmInsIdx] = _cmScMatchMin(p,i, j-1, rn, cn) + 1;
  1116. vp->v[kSmMinIdx] = cmMin( vp->v[kSmSubIdx], cmMin(vp->v[kSmDelIdx],vp->v[kSmInsIdx]));
  1117. vp->flags |= _cmScMatchIsTrans(p,pitchV,vp,bsi,i-1,j-1,rn,cn) ? kSmTransFl : 0;
  1118. }
  1119. return true;
  1120. }
  1121. void _cmScMatchPrintMtx( cmScMatch* r, unsigned rn, unsigned cn)
  1122. {
  1123. unsigned i,j,k;
  1124. for(i=0; i<rn; ++i)
  1125. {
  1126. for(j=0; j<cn; ++j)
  1127. {
  1128. printf("(");
  1129. const cmScMatchVal_t* vp = _cmScMatchValPtr(r,i,j,rn,cn);
  1130. for(k=0; k<kSmCnt; ++k)
  1131. {
  1132. printf("%i",vp->v[k]);
  1133. if( k<kSmCnt-1)
  1134. printf(", ");
  1135. else
  1136. printf(" ");
  1137. }
  1138. printf("%c%c)",cmIsFlag(vp->flags,kSmMatchFl)?'m':' ',cmIsFlag(vp->flags,kSmTransFl)?'t':' ');
  1139. }
  1140. printf("\n");
  1141. }
  1142. }
  1143. void _cmScMatchPathPush( cmScMatch* r, unsigned code, unsigned ri, unsigned ci, unsigned flags )
  1144. {
  1145. assert(r->p_avl != NULL );
  1146. cmScMatchPath_t* p = r->p_avl;
  1147. r->p_avl = r->p_avl->next;
  1148. p->code = code;
  1149. p->ri = ri;
  1150. p->ci = ci;
  1151. p->flags = code==kSmSubIdx && cmIsFlag(flags,kSmMatchFl) ? kSmMatchFl : 0;
  1152. p->flags |= cmIsFlag(flags,kSmTransFl);
  1153. p->next = r->p_cur;
  1154. r->p_cur = p;
  1155. }
  1156. void _cmScMatchPathPop( cmScMatch* r )
  1157. {
  1158. assert( r->p_cur != NULL );
  1159. cmScMatchPath_t* tp = r->p_cur->next;
  1160. r->p_cur->next = r->p_avl;
  1161. r->p_avl = r->p_cur;
  1162. r->p_cur = tp;
  1163. }
  1164. double _cmScMatchCalcCandidateCost( cmScMatch* r )
  1165. {
  1166. cmScMatchPath_t* cp = r->p_cur;
  1167. cmScMatchPath_t* bp = r->p_cur;
  1168. cmScMatchPath_t* ep = NULL;
  1169. // skip leading inserts
  1170. for(; cp!=NULL; cp=cp->next)
  1171. if( cp->code != kSmInsIdx )
  1172. {
  1173. bp = cp;
  1174. break;
  1175. }
  1176. // skip to trailing inserts
  1177. for(; cp!=NULL; cp=cp->next)
  1178. if( cp->code!=kSmInsIdx )
  1179. ep = cp;
  1180. // count remaining path length
  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 = cmIsFlag(bp->flags,kSmMatchFl);
  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 != kSmSubIdx && pc==kSmSubIdx && pfl==true )
  1194. if( pfl==true && cmIsFlag(cp->flags,kSmMatchFl)==false )
  1195. ++gapCnt;
  1196. //
  1197. switch( cp->code )
  1198. {
  1199. case kSmSubIdx:
  1200. penalty += cmIsFlag(cp->flags,kSmMatchFl) ? 0 : 1;
  1201. penalty -= cmIsFlag(cp->flags,kSmTransFl) ? 1 : 0;
  1202. break;
  1203. case kSmDelIdx:
  1204. penalty += 1;
  1205. break;
  1206. case kSmInsIdx:
  1207. penalty += 1;
  1208. break;
  1209. }
  1210. pfl = cmIsFlag(cp->flags,kSmMatchFl);
  1211. }
  1212. double cost = 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 cost;
  1215. }
  1216. double _cmScMatchEvalCandidate( cmScMatch* r, double min_cost, double cost )
  1217. {
  1218. if( min_cost == DBL_MAX || cost < min_cost)
  1219. {
  1220. // copy the p_cur to p_opt[]
  1221. cmScMatchPath_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].flags = cp->flags;
  1229. r->p_opt[i].next = cp->next==NULL ? NULL : r->p_opt + i + 1;
  1230. }
  1231. assert( i < r->pn );
  1232. r->p_opt[i].code = 0; // terminate with code=0
  1233. min_cost = cost;
  1234. }
  1235. return min_cost;
  1236. }
  1237. // traverse the solution matrix from the lower-right to
  1238. // the upper-left.
  1239. double _cmScMatchGenPaths( cmScMatch* r, int i, int j, unsigned rn, unsigned cn, double min_cost )
  1240. {
  1241. unsigned m;
  1242. // stop when the upper-right is encountered
  1243. if( i==0 && j==0 )
  1244. return _cmScMatchEvalCandidate(r, min_cost, _cmScMatchCalcCandidateCost(r) );
  1245. cmScMatchVal_t* vp = _cmScMatchValPtr(r,i,j,rn,cn);
  1246. // for each possible dir: up,left,up-left
  1247. for(m=1; m<kSmCnt; ++m)
  1248. if( vp->v[m] == vp->v[kSmMinIdx] )
  1249. {
  1250. // prepend to the current candidate path: r->p_cur
  1251. _cmScMatchPathPush(r,m,i,j,vp->flags);
  1252. int ii = i-1;
  1253. int jj = j-1;
  1254. switch(m)
  1255. {
  1256. case kSmSubIdx:
  1257. break;
  1258. case kSmDelIdx:
  1259. jj = j;
  1260. break;
  1261. case kSmInsIdx:
  1262. ii = i;
  1263. break;
  1264. default:
  1265. { assert(0); }
  1266. }
  1267. // recurse!
  1268. min_cost = _cmScMatchGenPaths(r,ii,jj,rn,cn,min_cost);
  1269. // remove the first element from the current path
  1270. _cmScMatchPathPop(r);
  1271. }
  1272. return min_cost;
  1273. }
  1274. double _cmScMatchAlign( cmScMatch* p, unsigned rn, unsigned cn, double min_cost )
  1275. {
  1276. int i = rn-1;
  1277. int j = cn-1;
  1278. unsigned m = _cmScMatchMin(p,i,j,rn,cn);
  1279. if( m==cmMax(rn,cn) )
  1280. printf("Edit distance is at max: %i. No Match.\n",m);
  1281. else
  1282. min_cost = _cmScMatchGenPaths(p,i,j,rn,cn,min_cost);
  1283. return min_cost;
  1284. }
  1285. cmRC_t cmScMatchExec( cmScMatch* p, unsigned scLocIdx, unsigned locN, const unsigned* midiPitchV, unsigned midiPitchN, double min_cost )
  1286. {
  1287. cmRC_t rc;
  1288. unsigned rn = midiPitchN + 1;
  1289. unsigned cn = locN + 1;
  1290. // set the DP matrix default values
  1291. if((rc = _cmScMatchInitMtx(p, rn, cn )) != cmOkRC )
  1292. return rc;
  1293. // _cmScMatchCalcMtx() returns false if the score window exceeds the length of the score
  1294. if(!_cmScMatchCalcMtx(p,scLocIdx,midiPitchV, rn, cn) )
  1295. return cmEofRC;
  1296. //_cmScMatchPrintMtx(p,rn,cn);
  1297. // locate the path through the DP matrix with the lowest edit distance (cost)
  1298. p->opt_cost = _cmScMatchAlign(p, rn, cn, min_cost);
  1299. return rc;
  1300. }
  1301. void _cmScMatchPrintPath( cmScMatch* p, cmScMatchPath_t* cp, unsigned bsi, const unsigned* pitchV, const unsigned* mniV )
  1302. {
  1303. assert( bsi != cmInvalidIdx );
  1304. cmScMatchPath_t* pp = cp;
  1305. int polyN = 0;
  1306. int i;
  1307. printf("loc: ");
  1308. // get the polyphony count for the score window
  1309. for(i=0; pp!=NULL; pp=pp->next)
  1310. {
  1311. cmScMatchLoc_t* lp = p->loc + bsi + pp->ci;
  1312. if( pp->code!=kSmDelIdx )
  1313. {
  1314. if(lp->evtCnt > polyN)
  1315. polyN = lp->evtCnt;
  1316. printf("%4i ",bsi+i);
  1317. ++i;
  1318. }
  1319. else
  1320. printf("%4s "," ");
  1321. }
  1322. printf("\n");
  1323. // print the score notes
  1324. for(i=polyN; i>0; --i)
  1325. {
  1326. printf("%3i: ",i);
  1327. for(pp=cp; pp!=NULL; pp=pp->next)
  1328. {
  1329. int locIdx = bsi + pp->ci - 1;
  1330. assert(0 <= locIdx && locIdx <= p->locN);
  1331. cmScMatchLoc_t* lp = p->loc + locIdx;
  1332. if( pp->code!=kSmDelIdx && lp->evtCnt >= i )
  1333. printf("%4s ",cmMidiToSciPitch(lp->evtV[i-1].pitch,NULL,0));
  1334. else
  1335. printf("%4s ", pp->code==kSmDelIdx? "-" : " ");
  1336. }
  1337. printf("\n");
  1338. }
  1339. printf("mid: ");
  1340. // print the MIDI buffer
  1341. for(pp=cp; pp!=NULL; pp=pp->next)
  1342. {
  1343. if( pp->code!=kSmInsIdx )
  1344. printf("%4s ",cmMidiToSciPitch(pitchV[pp->ri-1],NULL,0));
  1345. else
  1346. printf("%4s ",pp->code==kSmInsIdx?"-":" ");
  1347. }
  1348. printf("\nmni: ");
  1349. // print the MIDI buffer index (mni)
  1350. for(pp=cp; pp!=NULL; pp=pp->next)
  1351. {
  1352. if( pp->code!=kSmInsIdx )
  1353. printf("%4i ",mniV[pp->ri-1]);
  1354. else
  1355. printf("%4s ",pp->code==kSmInsIdx?"-":" ");
  1356. }
  1357. printf("\n op: ");
  1358. // print the substitute/insert/delete operation
  1359. for(pp=cp; pp!=NULL; pp=pp->next)
  1360. {
  1361. char c = ' ';
  1362. switch( pp->code )
  1363. {
  1364. case kSmSubIdx: c = 's'; break;
  1365. case kSmDelIdx: c = 'd'; break;
  1366. case kSmInsIdx: c = 'i'; break;
  1367. default:
  1368. { assert(0); }
  1369. }
  1370. printf("%4c ",c);
  1371. }
  1372. printf("\n ");
  1373. // give substitute attribute (match or transpose)
  1374. for(pp=cp; pp!=NULL; pp=pp->next)
  1375. {
  1376. cmChar_t s[3];
  1377. int k = 0;
  1378. if( cmIsFlag(pp->flags,kSmMatchFl) )
  1379. s[k++] = 'm';
  1380. if( cmIsFlag(pp->flags,kSmTransFl) )
  1381. s[k++] = 't';
  1382. s[k] = 0;
  1383. printf("%4s ",s);
  1384. }
  1385. printf("\nscl: ");
  1386. // print the stored location index
  1387. for(pp=cp; pp!=NULL; pp=pp->next)
  1388. {
  1389. if( pp->locIdx == cmInvalidIdx )
  1390. printf("%4s "," ");
  1391. else
  1392. printf("%4i ",p->loc[pp->locIdx].scLocIdx);
  1393. }
  1394. printf("\n\n");
  1395. }
  1396. //=======================================================================================================================
  1397. cmScMatcher* cmScMatcherAlloc( cmCtx* c, cmScMatcher* p, double srate, cmScH_t scH, unsigned scWndN, unsigned midiWndN )
  1398. {
  1399. cmScMatcher* op = cmObjAlloc(cmScMatcher,c,p);
  1400. if( op != NULL )
  1401. op->mp = cmScMatchAlloc(c,NULL,cmScNullHandle,0,0);
  1402. if( srate != 0 )
  1403. {
  1404. if( cmScMatcherInit(op,srate,scH,scWndN,midiWndN) != cmOkRC )
  1405. cmScMatcherFree(&op);
  1406. }
  1407. return op;
  1408. }
  1409. cmRC_t cmScMatcherFree( cmScMatcher** pp )
  1410. {
  1411. cmRC_t rc = cmOkRC;
  1412. if( pp==NULL || *pp==NULL )
  1413. return rc;
  1414. cmScMatcher* p = *pp;
  1415. if((rc = cmScMatcherFinal(p)) != cmOkRC )
  1416. return rc;
  1417. cmScMatchFree(&p->mp);
  1418. cmMemFree(p->midiBuf);
  1419. cmMemFree(p->res);
  1420. cmObjFree(pp);
  1421. return rc;
  1422. }
  1423. cmRC_t cmScMatcherInit( cmScMatcher* p, double srate, cmScH_t scH, unsigned scWndN, unsigned midiWndN )
  1424. {
  1425. cmRC_t rc;
  1426. if((rc = cmScMatcherFinal(p)) != cmOkRC )
  1427. return rc;
  1428. if( midiWndN > scWndN )
  1429. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score alignment MIDI event buffer length (%i) must be less than the score window length (%i).",midiWndN,scWndN);
  1430. if(( rc = cmScMatchInit(p->mp,scH,scWndN,midiWndN)) != cmOkRC )
  1431. return rc;
  1432. p->mn = midiWndN;
  1433. p->midiBuf = cmMemResize(cmScMatcherMidi_t,p->midiBuf,p->mn);
  1434. p->stepCnt = 3;
  1435. p->maxMissCnt = p->stepCnt+1;
  1436. p->rn = 2 * cmScoreEvtCount(scH);
  1437. p->res = cmMemResizeZ(cmScMatcherResult_t,p->res,p->rn);
  1438. cmScMatcherReset(p);
  1439. return rc;
  1440. }
  1441. cmRC_t cmScMatcherFinal( cmScMatcher* p )
  1442. {
  1443. return cmScMatchFinal(p->mp);
  1444. }
  1445. void cmScMatcherReset( cmScMatcher* p )
  1446. {
  1447. p->mbi = p->mp->mmn;
  1448. p->mni = 0;
  1449. p->begSyncLocIdx = cmInvalidIdx;
  1450. p->s_opt = DBL_MAX;
  1451. p->missCnt = 0;
  1452. p->scanCnt = 0;
  1453. p->ri = 0;
  1454. }
  1455. bool cmScMatcherInputMidi( cmScMatcher* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1456. {
  1457. if( status != kNoteOnMdId )
  1458. return false;
  1459. unsigned mi = p->mn-1;
  1460. //printf("%3i %5.2f %4s\n",p->mni,(double)smpIdx/p->srate,cmMidiToSciPitch(d0,NULL,0));
  1461. // shift the new MIDI event onto the end of the MIDI buffer
  1462. memmove(p->midiBuf,p->midiBuf+1,sizeof(cmScMatcherMidi_t)*mi);
  1463. p->midiBuf[mi].locIdx = cmInvalidIdx;
  1464. p->midiBuf[mi].cbCnt = 0;
  1465. p->midiBuf[mi].mni = p->mni++;
  1466. p->midiBuf[mi].smpIdx = smpIdx;
  1467. p->midiBuf[mi].pitch = d0;
  1468. p->midiBuf[mi].vel = d1;
  1469. if( p->mbi > 0 )
  1470. --p->mbi;
  1471. return true;
  1472. }
  1473. void _cmScMatcherStoreResult( cmScMatcher* p, unsigned locIdx, bool matchFl, const cmScMatcherMidi_t* mp )
  1474. {
  1475. // don't store missed score note results
  1476. assert( mp != NULL );
  1477. bool tpFl = locIdx!=cmInvalidIdx && matchFl;
  1478. bool fpFl = locIdx==cmInvalidIdx || matchFl==false;
  1479. cmScMatcherResult_t * rp = NULL;
  1480. unsigned i;
  1481. assert( tpFl==false || (tpFl==true && locIdx != cmInvalidIdx ) );
  1482. // it is possible that the same MIDI event is reported more than once
  1483. // (due to step->scan back tracking) - try to find previous result records
  1484. // associated with this MIDI event
  1485. for(i=0; i<p->ri; ++i)
  1486. if( p->res[i].mni == mp->mni )
  1487. {
  1488. // if the new
  1489. if( tpFl )
  1490. {
  1491. rp = p->res + i;
  1492. break;
  1493. }
  1494. // a match was found but this was not a true-pos so ignore it
  1495. return;
  1496. }
  1497. if( rp == NULL )
  1498. {
  1499. rp = p->res + p->ri;
  1500. ++p->ri;
  1501. }
  1502. rp->locIdx = locIdx;
  1503. rp->mni = mp->mni;
  1504. rp->pitch = mp->pitch;
  1505. rp->vel = mp->vel;
  1506. rp->tpFl = tpFl;
  1507. rp->fpFl = fpFl;
  1508. }
  1509. unsigned cmScMatcherScan( cmScMatcher* p, unsigned bsi, unsigned scanCnt )
  1510. {
  1511. assert( p->mp != NULL && p->mp->mmn > 0 );
  1512. unsigned i_opt = cmInvalidIdx;
  1513. double s_opt = DBL_MAX;
  1514. cmRC_t rc = cmOkRC;
  1515. unsigned i;
  1516. // initialize the internal values set by this function
  1517. p->missCnt = 0;
  1518. p->esi = cmInvalidIdx;
  1519. p->s_opt = DBL_MAX;
  1520. // if the MIDI buf is not full
  1521. if( p->mbi != 0 )
  1522. return cmInvalidIdx;
  1523. // load a temporary MIDI pitch buffer for use by cmScMatch.
  1524. unsigned pitchV[p->mp->mmn];
  1525. for(i=0; i<p->mp->mmn; ++i)
  1526. pitchV[i] = p->midiBuf[i].pitch;
  1527. // calc the edit distance from pitchV[] to a sliding score window
  1528. for(i=0; rc==cmOkRC && (scanCnt==cmInvalidCnt || i<scanCnt); ++i)
  1529. {
  1530. rc = cmScMatchExec(p->mp, bsi + i, p->mp->msn, pitchV, p->mp->mmn, s_opt );
  1531. switch(rc)
  1532. {
  1533. case cmOkRC: // normal result
  1534. if( p->mp->opt_cost < s_opt )
  1535. {
  1536. s_opt = p->mp->opt_cost;
  1537. i_opt = bsi + i;
  1538. }
  1539. break;
  1540. case cmEofRC: // score window encountered the end of the score
  1541. break;
  1542. default: // error state
  1543. return cmInvalidIdx;
  1544. }
  1545. }
  1546. // store the cost assoc'd with i_opt
  1547. p->s_opt = s_opt;
  1548. if( i_opt == cmInvalidIdx )
  1549. return cmInvalidIdx;
  1550. // Traverse the least cost path and:
  1551. // 1) Set p->esi to the score location index of the last MIDI note
  1552. // which has a positive match with the score and assign
  1553. // the internal score index to cp->locIdx.
  1554. //
  1555. // 2) Set cmScAlignPath_t.locIdx - index into p->loc[] associated
  1556. // with each path element that is a 'substitute' or an 'insert'.
  1557. //
  1558. // 3) Set p->missCnt: the count of trailing non-positive matches.
  1559. // p->missCnt is eventually used in cmScAlignStep() to track the number
  1560. // of consecutive trailing missed notes.
  1561. //
  1562. cmScMatchPath_t* cp = p->mp->p_opt;
  1563. for(i=0; cp!=NULL; cp=cp->next)
  1564. {
  1565. if( cp->code != kSmInsIdx )
  1566. {
  1567. assert( cp->ri > 0 );
  1568. p->midiBuf[ cp->ri-1 ].locIdx = cmInvalidIdx;
  1569. }
  1570. switch( cp->code )
  1571. {
  1572. case kSmSubIdx:
  1573. if( cmIsFlag(cp->flags,kSmMatchFl) || cmIsFlag(cp->flags,kSmTransFl))
  1574. {
  1575. p->esi = i_opt + i;
  1576. p->missCnt = 0;
  1577. if( cmIsFlag(cp->flags,kSmMatchFl) )
  1578. p->midiBuf[ cp->ri-1 ].locIdx = i_opt + i;
  1579. }
  1580. else
  1581. {
  1582. ++p->missCnt;
  1583. }
  1584. // fall through
  1585. case kSmInsIdx:
  1586. cp->locIdx = i_opt + i;
  1587. ++i;
  1588. break;
  1589. case kSmDelIdx:
  1590. cp->locIdx = cmInvalidIdx;
  1591. ++p->missCnt;
  1592. break;
  1593. }
  1594. }
  1595. // if no positive matches were found
  1596. if( p->esi == cmInvalidIdx )
  1597. i_opt = cmInvalidIdx;
  1598. else
  1599. {
  1600. // record result
  1601. for(cp=p->mp->p_opt; cp!=NULL; cp=cp->next)
  1602. if( cp->code != kSmInsIdx )
  1603. _cmScMatcherStoreResult(p, cp->locIdx, cmIsFlag(cp->flags,kSmMatchFl), p->midiBuf + cp->ri - 1);
  1604. }
  1605. return i_opt;
  1606. }
  1607. cmRC_t cmScMatcherStep( cmScMatcher* p )
  1608. {
  1609. int i;
  1610. unsigned pitch = p->midiBuf[ p->mn-1 ].pitch;
  1611. unsigned locIdx = cmInvalidIdx;
  1612. // the tracker must be sync'd to step
  1613. if( p->esi == cmInvalidIdx )
  1614. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The p->esi value must be valid to perform a step operation.");
  1615. // if the end of the score has been reached
  1616. if( p->esi + 1 >= p->mp->locN )
  1617. return cmEofRC;
  1618. // attempt to match to next location first
  1619. if( _cmScMatchIsMatch(p->mp->loc + p->esi + 1, pitch) )
  1620. {
  1621. locIdx = p->esi + 1;
  1622. }
  1623. else
  1624. {
  1625. //
  1626. for(i=2; i<p->stepCnt; ++i)
  1627. {
  1628. // go forward
  1629. if( p->esi+i < p->mp->locN && _cmScMatchIsMatch(p->mp->loc + p->esi + i, pitch) )
  1630. {
  1631. locIdx = p->esi + i;
  1632. break;
  1633. }
  1634. // go backward
  1635. if( p->esi >= (i-1) && _cmScMatchIsMatch(p->mp->loc + p->esi - (i-1), pitch) )
  1636. {
  1637. locIdx = p->esi - (i-1);
  1638. break;
  1639. }
  1640. }
  1641. }
  1642. p->midiBuf[ p->mn-1 ].locIdx = locIdx;
  1643. if( locIdx == cmInvalidIdx )
  1644. ++p->missCnt;
  1645. else
  1646. {
  1647. p->missCnt = 0;
  1648. p->esi = locIdx;
  1649. }
  1650. // store the result
  1651. _cmScMatcherStoreResult(p, locIdx, locIdx!=cmInvalidIdx, p->midiBuf + p->mn - 1);
  1652. if( p->missCnt >= p->maxMissCnt )
  1653. {
  1654. unsigned begScanLocIdx = p->esi > p->mn ? p->esi - p->mn : 0;
  1655. p->s_opt = DBL_MAX;
  1656. unsigned bsi = cmScMatcherScan(p,begScanLocIdx,p->mn*2);
  1657. ++p->scanCnt;
  1658. // if the scan failed find a match
  1659. if( bsi == cmInvalidIdx )
  1660. return cmCtxRtCondition( &p->obj, cmSubSysFailRC, "Scan resync. failed.");
  1661. }
  1662. return cmOkRC;
  1663. }
  1664. void cmScMatcherPrintPath( cmScMatcher* p )
  1665. {
  1666. unsigned pitchV[ p->mn ];
  1667. unsigned mniV[ p->mn ];
  1668. unsigned i;
  1669. for(i=0; i<p->mn; ++i)
  1670. {
  1671. pitchV[i] = p->midiBuf[i].pitch;
  1672. mniV[i] = p->midiBuf[i].mni;
  1673. }
  1674. _cmScMatchPrintPath(p->mp, p->mp->p_opt, p->begSyncLocIdx, pitchV, mniV );
  1675. }
  1676. cmRC_t cmScMatcherExec( cmScMatcher* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1677. {
  1678. bool fl = p->mbi > 0;
  1679. cmRC_t rc = cmOkRC;
  1680. // update the MIDI buffer with the incoming note
  1681. cmScMatcherInputMidi(p,smpIdx,status,d0,d1);
  1682. // if the MIDI buffer transitioned to full then perform an initial scan sync.
  1683. if( fl && p->mbi == 0 )
  1684. {
  1685. if( (p->begSyncLocIdx = cmScMatcherScan(p,0,cmInvalidCnt)) == cmInvalidIdx )
  1686. rc = cmInvalidArgRC; // signal init. scan sync. fail
  1687. else
  1688. {
  1689. //cmScMatcherPrintPath(p);
  1690. }
  1691. }
  1692. else
  1693. {
  1694. // if the MIDI buffer is full then perform a step sync.
  1695. if( !fl && p->mbi == 0 )
  1696. rc = cmScMatcherStep(p);
  1697. }
  1698. return rc;
  1699. }
  1700. double cmScMatcherFMeas( cmScMatcher* p )
  1701. {
  1702. unsigned bli = p->mp->locN;
  1703. unsigned eli = 0;
  1704. unsigned scNoteCnt = 0; // total count of score notes
  1705. unsigned matchCnt = 0; // count of matched notes (true positives)
  1706. unsigned wrongCnt = 0; // count of incorrect notes (false positives)
  1707. unsigned missCnt = 0; // count of missed score notes (false negatives)
  1708. unsigned i;
  1709. for(i=0; i<p->ri; ++i)
  1710. if( p->res[i].locIdx != cmInvalidIdx )
  1711. {
  1712. bli = cmMin(bli,p->res[i].locIdx);
  1713. eli = cmMax(eli,p->res[i].locIdx);
  1714. if( p->res[i].tpFl )
  1715. ++matchCnt;
  1716. if( p->res[i].fpFl )
  1717. ++wrongCnt;
  1718. }
  1719. scNoteCnt = eli - bli + 1;
  1720. missCnt = scNoteCnt - matchCnt;
  1721. double prec = (double)2.0 * matchCnt / (matchCnt + wrongCnt);
  1722. double rcal = (double)2.0 * matchCnt / (matchCnt + missCnt);
  1723. double fmeas = prec * rcal / (prec + rcal);
  1724. //printf("total:%i match:%i wrong:%i miss:%i\n",scNoteCnt,matchCnt,wrongCnt,missCnt);
  1725. return fmeas;
  1726. }
  1727. //=======================================================================================================================
  1728. cmScAlign* cmScAlignAlloc( cmCtx* c, cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN )
  1729. {
  1730. cmScAlign* op = cmObjAlloc(cmScAlign,c,p);
  1731. if( srate != 0 )
  1732. if( cmScAlignInit(op,cbFunc,cbArg,srate,scH,midiN,scWndN) != cmOkRC )
  1733. cmScAlignFree(&op);
  1734. return op;
  1735. }
  1736. cmRC_t cmScAlignFree( cmScAlign** pp )
  1737. {
  1738. cmRC_t rc = cmOkRC;
  1739. if( pp==NULL || *pp==NULL )
  1740. return rc;
  1741. cmScAlign* p = *pp;
  1742. if((rc = cmScAlignFinal(p)) != cmOkRC )
  1743. return rc;
  1744. cmMemFree(p->loc);
  1745. cmMemFree(p->midiBuf);
  1746. cmMemFree(p->m);
  1747. cmMemFree(p->p_mem);
  1748. cmMemFree(p->res);
  1749. cmObjFree(pp);
  1750. return rc;
  1751. }
  1752. void _cmScAlignPrint( cmScAlign* p )
  1753. {
  1754. int i,j;
  1755. for(i=0; i<p->locN; ++i)
  1756. {
  1757. printf("%2i %5i ",p->loc[i].barNumb,p->loc[i].scLocIdx);
  1758. for(j=0; j<p->loc[i].evtCnt; ++j)
  1759. printf("%s ",cmMidiToSciPitch(p->loc[i].evtV[j].pitch,NULL,0));
  1760. printf("\n");
  1761. }
  1762. }
  1763. cmRC_t cmScAlignInit( cmScAlign* p, cmScAlignCb_t cbFunc, void* cbArg, cmReal_t srate, cmScH_t scH, unsigned midiN, unsigned scWndN )
  1764. {
  1765. cmRC_t rc;
  1766. if((rc = cmScAlignFinal(p)) != cmOkRC )
  1767. return rc;
  1768. if( midiN > scWndN )
  1769. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The score alignment MIDI event buffer length (%i) must be less than the score window length (%i).",midiN,scWndN);
  1770. p->cbFunc = cbFunc;
  1771. p->cbArg = cbArg;
  1772. p->srate = srate;
  1773. p->scH = scH;
  1774. p->locN = cmScoreEvtCount(scH);
  1775. p->loc = cmMemResizeZ(cmScAlignLoc_t,p->loc,p->locN);
  1776. p->mn = midiN;
  1777. p->midiBuf = cmMemResizeZ(cmScAlignMidiEvt_t,p->midiBuf,midiN);
  1778. p->mbi = midiN;
  1779. p->printFl = true;
  1780. // Setup score structures
  1781. // for each score location
  1782. unsigned li,ei;
  1783. for(li=0,ei=0; li<cmScoreLocCount(p->scH); ++li)
  1784. {
  1785. unsigned i,n;
  1786. const cmScoreLoc_t* lp = cmScoreLoc(p->scH,li);
  1787. // count the number of note events at location li
  1788. for(n=0,i=0; i<lp->evtCnt; ++i)
  1789. if( lp->evtArray[i]->type == kNonEvtScId )
  1790. ++n;
  1791. assert( ei+n <= p->locN );
  1792. // duplicate each note at location li n times
  1793. for(i=0; i<n; ++i)
  1794. {
  1795. unsigned j,k;
  1796. p->loc[ei+i].evtCnt = n;
  1797. p->loc[ei+i].evtV = cmMemAllocZ(cmScAlignScEvt_t,n);
  1798. p->loc[ei+i].scLocIdx = li;
  1799. p->loc[ei+i].barNumb = lp->barNumb;
  1800. for(j=0,k=0; j<lp->evtCnt; ++j)
  1801. if( lp->evtArray[j]->type == kNonEvtScId )
  1802. {
  1803. p->loc[ei+i].evtV[k].pitch = lp->evtArray[j]->pitch;
  1804. ++k;
  1805. }
  1806. }
  1807. ei += n;
  1808. }
  1809. assert(ei<=p->locN);
  1810. p->locN = ei;
  1811. // setup edit distance structures
  1812. p->rn = midiN+1;
  1813. p->cn = scWndN+1;
  1814. p->m = cmMemResizeZ(cmScAlignVal_t, p->m, p->rn*p->cn );
  1815. p->pn = p->rn + p->cn;
  1816. p->p_mem = cmMemResizeZ(cmScAlignPath_t, p->p_mem, 2*p->pn );
  1817. p->p_avl = p->p_mem;
  1818. p->p_cur = NULL;
  1819. p->p_opt = p->p_mem + p->pn;
  1820. p->s_opt = DBL_MAX;
  1821. p->resN = 2 * cmScoreEvtCount(scH); // make twice as many result records as there are score events
  1822. p->res = cmMemResizeZ(cmScAlignResult_t, p->res, p->resN);
  1823. p->stepCnt = 3;
  1824. p->maxStepMissCnt = 4;
  1825. // fill in the default values for the first row
  1826. // and column of the DP matrix
  1827. unsigned i,j,k;
  1828. for(i=0; i<p->rn; ++i)
  1829. for(j=0; j<p->cn; ++j)
  1830. {
  1831. unsigned v[] = {0,0,0,0};
  1832. if( i == 0 )
  1833. {
  1834. v[kSaMinIdx] = j;
  1835. v[kSaInsIdx] = j;
  1836. }
  1837. else
  1838. if( j == 0 )
  1839. {
  1840. v[kSaMinIdx] = i;
  1841. v[kSaDelIdx] = i;
  1842. }
  1843. for(k=0; k<kSaCnt; ++k)
  1844. p->m[ i + (j*p->rn) ].v[k] = v[k];
  1845. }
  1846. // put pn path records on the available list
  1847. for(i=0; i<p->pn; ++i)
  1848. {
  1849. p->p_mem[i].next = i<p->pn-1 ? p->p_mem + i + 1 : NULL;
  1850. p->p_opt[i].next = i<p->pn-1 ? p->p_opt + i + 1 : NULL;
  1851. }
  1852. //_cmScAlignPrint(p);
  1853. cmScAlignReset(p,0);
  1854. return rc;
  1855. }
  1856. cmRC_t cmScAlignFinal( cmScAlign* p )
  1857. {
  1858. unsigned i;
  1859. for(i=0; i<p->locN; ++i)
  1860. cmMemPtrFree(&p->loc[i].evtV);
  1861. return cmOkRC;
  1862. }
  1863. void cmScAlignReset( cmScAlign* p, unsigned begScanLocIdx )
  1864. {
  1865. assert( begScanLocIdx < p->locN );
  1866. p->mbi = p->mn;
  1867. p->mni = 0;
  1868. p->begScanLocIdx = begScanLocIdx;
  1869. p->begSyncLocIdx = cmInvalidIdx;
  1870. p->s_opt = DBL_MAX;
  1871. p->esi = cmInvalidIdx;
  1872. p->missCnt = 0;
  1873. p->scanCnt = 0;
  1874. p->ri = 0;
  1875. }
  1876. cmScAlignVal_t* _cmScAlignValPtr( cmScAlign* p, unsigned i, unsigned j )
  1877. {
  1878. assert( i < p->rn );
  1879. assert( j < p->cn );
  1880. return p->m + i + (j*p->rn);
  1881. }
  1882. bool _cmScAlignIsMatch( const cmScAlignLoc_t* loc, unsigned pitch )
  1883. {
  1884. unsigned i;
  1885. for(i=0; i<loc->evtCnt; ++i)
  1886. if( loc->evtV[i].pitch == pitch )
  1887. return true;
  1888. return false;
  1889. }
  1890. bool _cmScAlignIsTrans( cmScAlign* p, const cmScAlignVal_t* v1p, unsigned i, unsigned j )
  1891. {
  1892. bool fl = false;
  1893. cmScAlignVal_t* v0p = _cmScAlignValPtr(p,i,j);
  1894. if( i>=1 && j>=1
  1895. && v1p->v[kSaMinIdx] == v1p->v[kSaSubIdx]
  1896. && v1p->matchFl == false
  1897. && v0p->v[kSaMinIdx] == v0p->v[kSaSubIdx]
  1898. && v0p->matchFl == false )
  1899. {
  1900. unsigned c00 = p->midiBuf[i-1].pitch;
  1901. unsigned c01 = p->midiBuf[i ].pitch;
  1902. cmScAlignLoc_t* c10 = p->loc + p->begScanLocIdx + j - 1;
  1903. cmScAlignLoc_t* c11 = p->loc + p->begScanLocIdx + j;
  1904. fl = _cmScAlignIsMatch(c11,c00) && _cmScAlignIsMatch(c10,c01);
  1905. }
  1906. return fl;
  1907. }
  1908. unsigned _cmScAlignMin( cmScAlign* p, unsigned i, unsigned j )
  1909. {
  1910. assert( i<p->rn && j<p->cn );
  1911. //return p->m[ i + (j*p->rn) ].v[kSaMinIdx];
  1912. return _cmScAlignValPtr(p,i,j)->v[kSaMinIdx];
  1913. }
  1914. // Returns 'false' if the score window goes past the end of the score
  1915. // (i.e. p->begScanLocIdx + p->cn > p->locN )
  1916. bool _cmScAlignCalcMtx( cmScAlign* p )
  1917. {
  1918. // the midi buffer must be full
  1919. assert( p->mbi == 0 );
  1920. // loc[begScanLocIdx:begScanLocIdx+p->cn-1] must be valid
  1921. if( p->begScanLocIdx + p->cn > p->locN )
  1922. return false;
  1923. unsigned i,j;
  1924. for(j=1; j<p->cn; ++j)
  1925. for(i=1; i<p->rn; ++i)
  1926. {
  1927. cmScAlignLoc_t* loc = p->loc + p->begScanLocIdx + j - 1;
  1928. unsigned pitch = p->midiBuf[i-1].pitch;
  1929. cmScAlignVal_t* vp = _cmScAlignValPtr(p,i,j);
  1930. vp->matchFl = _cmScAlignIsMatch(loc,pitch);
  1931. unsigned cost = vp->matchFl ? 0 : 1;
  1932. vp->v[kSaSubIdx] = _cmScAlignMin(p,i-1,j-1) + cost;
  1933. vp->v[kSaDelIdx] = _cmScAlignMin(p,i-1,j ) + 1;
  1934. vp->v[kSaInsIdx] = _cmScAlignMin(p,i, j-1) + 1;
  1935. vp->v[kSaMinIdx] = cmMin( vp->v[kSaSubIdx], cmMin(vp->v[kSaDelIdx],vp->v[kSaInsIdx]));
  1936. vp->transFl = _cmScAlignIsTrans(p,vp,i-1,j-1);
  1937. }
  1938. return true;
  1939. }
  1940. void _cmScAlignPathPush( cmScAlign* r, unsigned code, unsigned ri, unsigned ci, bool matchFl, bool transFl )
  1941. {
  1942. assert(r->p_avl != NULL );
  1943. cmScAlignPath_t* p = r->p_avl;
  1944. r->p_avl = r->p_avl->next;
  1945. p->code = code;
  1946. p->ri = ri;
  1947. p->ci = ci;
  1948. p->matchFl = code==kSaSubIdx ? matchFl : false;
  1949. p->transFl = transFl;
  1950. p->next = r->p_cur;
  1951. r->p_cur = p;
  1952. }
  1953. void _cmScAlignPathPop( cmScAlign* r )
  1954. {
  1955. assert( r->p_cur != NULL );
  1956. cmScAlignPath_t* tp = r->p_cur->next;
  1957. r->p_cur->next = r->p_avl;
  1958. r->p_avl = r->p_cur;
  1959. r->p_cur = tp;
  1960. }
  1961. double _cmScAlignScoreCandidate( cmScAlign* r )
  1962. {
  1963. cmScAlignPath_t* cp = r->p_cur;
  1964. cmScAlignPath_t* bp = r->p_cur;
  1965. cmScAlignPath_t* ep = NULL;
  1966. for(; cp!=NULL; cp=cp->next)
  1967. if( cp->code != kSaInsIdx )
  1968. {
  1969. bp = cp;
  1970. break;
  1971. }
  1972. for(; cp!=NULL; cp=cp->next)
  1973. if( cp->code!=kSaInsIdx )
  1974. ep = cp;
  1975. assert( ep!=NULL && bp!=ep);
  1976. unsigned n=1;
  1977. for(cp=bp; cp!=ep; cp=cp->next)
  1978. ++n;
  1979. double gapCnt = 0;
  1980. double penalty = 0;
  1981. bool pfl = bp->matchFl;
  1982. unsigned i;
  1983. cp = bp;
  1984. for(i=0; i<n; ++i,cp=cp->next)
  1985. {
  1986. // a gap is a transition from a matching subst. to an insert or deletion
  1987. //if( pc != cp->code && cp->code != kSaSubIdx && pc==kSaSubIdx && pfl==true )
  1988. if( pfl==true && cp->matchFl==false )
  1989. ++gapCnt;
  1990. //
  1991. switch( cp->code )
  1992. {
  1993. case kSaSubIdx:
  1994. penalty += cp->matchFl ? 0 : 1;
  1995. penalty -= cp->transFl ? 1 : 0;
  1996. break;
  1997. case kSaDelIdx:
  1998. penalty += 1;
  1999. break;
  2000. case kSaInsIdx:
  2001. penalty += 1;
  2002. break;
  2003. }
  2004. pfl = cp->matchFl;
  2005. }
  2006. double score = gapCnt/n + penalty;
  2007. //printf("n:%i gaps:%f gap_score:%f penalty:%f score:%f\n",n,gapCnt,gapCnt/n,penalty,score);
  2008. return score;
  2009. }
  2010. void _cmScAlignEvalCandidate( cmScAlign* r, double score )
  2011. {
  2012. if( r->s_opt == DBL_MAX || score < r->s_opt)
  2013. {
  2014. // copy the p_cur to p_opt[]
  2015. cmScAlignPath_t* cp = r->p_cur;
  2016. unsigned i;
  2017. for(i=0; cp!=NULL && i<r->pn; cp=cp->next,++i)
  2018. {
  2019. r->p_opt[i].code = cp->code;
  2020. r->p_opt[i].ri = cp->ri;
  2021. r->p_opt[i].ci = cp->ci;
  2022. r->p_opt[i].matchFl = cp->matchFl;
  2023. r->p_opt[i].transFl = cp->transFl;
  2024. r->p_opt[i].next = cp->next==NULL ? NULL : r->p_opt + i + 1;
  2025. }
  2026. assert( i < r->pn );
  2027. r->p_opt[i].code = 0; // terminate with code=0
  2028. r->s_opt = score;
  2029. }
  2030. }
  2031. // traverse the solution matrix from the lower-right to
  2032. // the upper-left.
  2033. void _cmScAlignGenPaths( cmScAlign* r, int i, int j )
  2034. {
  2035. unsigned m;
  2036. // stop when the upper-right is encountered
  2037. if( i==0 && j==0 )
  2038. {
  2039. _cmScAlignEvalCandidate(r, _cmScAlignScoreCandidate(r) );
  2040. return;
  2041. }
  2042. cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j);
  2043. // for each possible dir: up,left,up-left
  2044. for(m=1; m<kSaCnt; ++m)
  2045. if( vp->v[m] == vp->v[kSaMinIdx] )
  2046. {
  2047. // prepend to the current candidate path: r->p_cur
  2048. _cmScAlignPathPush(r,m,i,j,vp->matchFl,vp->transFl);
  2049. int ii = i-1;
  2050. int jj = j-1;
  2051. switch(m)
  2052. {
  2053. case kSaSubIdx:
  2054. break;
  2055. case kSaDelIdx:
  2056. jj = j;
  2057. break;
  2058. case kSaInsIdx:
  2059. ii = i;
  2060. break;
  2061. }
  2062. // recurse!
  2063. _cmScAlignGenPaths(r,ii,jj);
  2064. // remove the first element from the current path
  2065. _cmScAlignPathPop(r);
  2066. }
  2067. }
  2068. double _cmScAlign( cmScAlign* p )
  2069. {
  2070. int i = p->rn-1;
  2071. int j = p->cn-1;
  2072. unsigned m = _cmScAlignMin(p,i,j); //p->m[i + (j*p->rn)].v[kSaMinIdx];
  2073. if( m==cmMax(p->rn,p->cn) )
  2074. printf("Edit distance is at max: %i. No Match.\n",m);
  2075. else
  2076. _cmScAlignGenPaths(p,i,j);
  2077. return p->s_opt;
  2078. }
  2079. cmRC_t cmScAlignExec( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  2080. {
  2081. bool fl = p->mbi > 0;
  2082. cmRC_t rc = cmOkRC;
  2083. // update the MIDI buffer with the incoming note
  2084. cmScAlignInputMidi(p,smpIdx,status,d0,d1);
  2085. // if the MIDI buffer transitioned to full then perform an initial scan sync.
  2086. if( fl && p->mbi == 0 )
  2087. {
  2088. if( (p->begSyncLocIdx = cmScAlignScan(p,cmInvalidCnt)) == cmInvalidIdx )
  2089. rc = cmInvalidArgRC; // signal init. scan sync. fail
  2090. }
  2091. else
  2092. {
  2093. // if the MIDI buffer is full then perform a step sync.
  2094. if( !fl && p->mbi == 0 )
  2095. rc = cmScAlignStep(p);
  2096. }
  2097. return rc;
  2098. }
  2099. bool cmScAlignInputMidi( cmScAlign* p, unsigned smpIdx, unsigned status, cmMidiByte_t d0, cmMidiByte_t d1 )
  2100. {
  2101. if( status != kNoteOnMdId )
  2102. return false;
  2103. unsigned mi = p->mn-1;
  2104. //printf("%3i %5.2f %4s\n",p->mni,(double)smpIdx/p->srate,cmMidiToSciPitch(d0,NULL,0));
  2105. // shift the new MIDI event onto the end of the MIDI buffer
  2106. memmove(p->midiBuf,p->midiBuf+1,sizeof(cmScAlignMidiEvt_t)*mi);
  2107. p->midiBuf[mi].locIdx = cmInvalidIdx;
  2108. p->midiBuf[mi].cbCnt = 0;
  2109. p->midiBuf[mi].mni = p->mni++;
  2110. p->midiBuf[mi].smpIdx = smpIdx;
  2111. p->midiBuf[mi].pitch = d0;
  2112. p->midiBuf[mi].vel = d1;
  2113. if( p->mbi > 0 )
  2114. --p->mbi;
  2115. return true;
  2116. }
  2117. // If mep==NULL then the identified score location was not matched (this is an 'insert')
  2118. // these only occurr during 'scan' not 'step'.
  2119. //
  2120. // If locIdx == cmInvalidIdx then the MIDI event did not match a score location
  2121. // When this occurrs during a scan then this is a 'deleted' MIDI note otherwise
  2122. // the note was not found inside loc[esi-stepCnt:esi+stepCnt].
  2123. //
  2124. // If mep!=NULL && scLocIdx!=cmInvalidIdx but matchFl==false then this is a
  2125. // 'substitute' with a mismatch. These only occur during 'scan'.
  2126. void _cmScAlignCb( cmScAlign* p, unsigned locIdx, cmScAlignMidiEvt_t* mep, bool matchFl, bool transFl )
  2127. {
  2128. // verify that the result buffer is not full
  2129. if( p->ri >= p->resN )
  2130. {
  2131. cmCtxRtCondition( &p->obj, cmArgAssertRC, "The score alignment result buffer is full.");
  2132. return;
  2133. }
  2134. // don't report unmatched score locations
  2135. if( mep == NULL )
  2136. return;
  2137. ++mep->cbCnt;
  2138. cmScAlignResult_t* rp = NULL;
  2139. // if this is the first time this MIDI event has generated a callback ...
  2140. if( mep->cbCnt == 1 )
  2141. rp = p->res + p->ri++; // ... then create a new record in p->res[] ...
  2142. else
  2143. if( mep->cbCnt > 1 && matchFl ) // ... otherwise if it was matched ...
  2144. {
  2145. unsigned i;
  2146. for(i=0; i<p->ri; ++i)
  2147. if(p->res[i].mni == mep->mni )
  2148. {
  2149. if( p->res[i].matchFl == false ) // ... and it's previous recd was not matched then update the record with the match info.
  2150. rp = p->res + i;
  2151. }
  2152. }
  2153. if(rp == NULL )
  2154. return;
  2155. assert( locIdx != cmInvalidIdx || mep != NULL );
  2156. rp->locIdx = locIdx;
  2157. rp->smpIdx = mep==NULL ? cmInvalidIdx : mep->smpIdx;
  2158. rp->mni = mep==NULL ? cmInvalidIdx : mep->mni;
  2159. rp->pitch = mep==NULL ? kInvalidMidiPitch : mep->pitch;
  2160. rp->vel = mep==NULL ? kInvalidMidiVelocity : mep->vel;
  2161. rp->matchFl = mep==NULL ? false : matchFl;
  2162. rp->transFl = mep==NULL ? false : transFl;
  2163. }
  2164. void _cmScAlignPrintPath( cmScAlign* p, cmScAlignPath_t* cp, unsigned bsi )
  2165. {
  2166. assert( bsi != cmInvalidIdx );
  2167. cmScAlignPath_t* pp = cp;
  2168. int polyN = 0;
  2169. int i;
  2170. printf("loc: ");
  2171. // get the polyphony count for the score window
  2172. for(i=0; pp!=NULL; pp=pp->next)
  2173. {
  2174. cmScAlignLoc_t* lp = p->loc + bsi + pp->ci;
  2175. if( pp->code!=kSaDelIdx )
  2176. {
  2177. if(lp->evtCnt > polyN)
  2178. polyN = lp->evtCnt;
  2179. printf("%4i ",bsi+i);
  2180. ++i;
  2181. }
  2182. else
  2183. printf("%4s "," ");
  2184. }
  2185. printf("\n");
  2186. // print the score notes
  2187. for(i=polyN; i>0; --i)
  2188. {
  2189. printf("%3i: ",i);
  2190. for(pp=cp; pp!=NULL; pp=pp->next)
  2191. {
  2192. int locIdx = bsi + pp->ci - 1;
  2193. assert(0 <= locIdx && locIdx <= p->locN);
  2194. cmScAlignLoc_t* lp = p->loc + locIdx;
  2195. if( pp->code!=kSaDelIdx && lp->evtCnt >= i )
  2196. printf("%4s ",cmMidiToSciPitch(lp->evtV[i-1].pitch,NULL,0));
  2197. else
  2198. printf("%4s ", pp->code==kSaDelIdx? "-" : " ");
  2199. }
  2200. printf("\n");
  2201. }
  2202. printf("mid: ");
  2203. // print the MIDI buffer
  2204. for(pp=cp; pp!=NULL; pp=pp->next)
  2205. {
  2206. if( pp->code!=kSaInsIdx )
  2207. printf("%4s ",cmMidiToSciPitch(p->midiBuf[pp->ri-1].pitch,NULL,0));
  2208. else
  2209. printf("%4s ",pp->code==kSaInsIdx?"-":" ");
  2210. }
  2211. printf("\nmni: ");
  2212. // print the MIDI buffer index (mni)
  2213. for(pp=cp; pp!=NULL; pp=pp->next)
  2214. {
  2215. if( pp->code!=kSaInsIdx )
  2216. printf("%4i ",p->midiBuf[pp->ri-1].mni);
  2217. else
  2218. printf("%4s ",pp->code==kSaInsIdx?"-":" ");
  2219. }
  2220. printf("\n op: ");
  2221. // print the substitute/insert/delete operation
  2222. for(pp=cp; pp!=NULL; pp=pp->next)
  2223. {
  2224. char c = ' ';
  2225. switch( pp->code )
  2226. {
  2227. case kSaSubIdx: c = 's'; break;
  2228. case kSaDelIdx: c = 'd'; break;
  2229. case kSaInsIdx: c = 'i'; break;
  2230. default:
  2231. { assert(0); }
  2232. }
  2233. printf("%4c ",c);
  2234. }
  2235. printf("\n ");
  2236. // give substitute attribute (match or transpose)
  2237. for(pp=cp; pp!=NULL; pp=pp->next)
  2238. {
  2239. cmChar_t s[3];
  2240. int k = 0;
  2241. if( pp->matchFl )
  2242. s[k++] = 'm';
  2243. if( pp->transFl )
  2244. s[k++] = 't';
  2245. s[k] = 0;
  2246. printf("%4s ",s);
  2247. }
  2248. printf("\nscl: ");
  2249. // print the stored location index
  2250. for(pp=cp; pp!=NULL; pp=pp->next)
  2251. {
  2252. if( pp->locIdx == cmInvalidIdx )
  2253. printf("%4s "," ");
  2254. else
  2255. printf("%4i ",p->loc[pp->locIdx].scLocIdx);
  2256. }
  2257. printf("\n\n");
  2258. }
  2259. // Returns the p->loc[] index at the start of the min cost score window
  2260. // based on the current MIDI buffer.
  2261. // scanCnt is the number of time the score window will be shifted one
  2262. // location to the left
  2263. unsigned cmScAlignScan( cmScAlign* p, unsigned scanCnt )
  2264. {
  2265. unsigned bsi = cmInvalidIdx;
  2266. assert( p->mbi == 0 );
  2267. // if the MIDI buf is full
  2268. if( p->mbi == 0 )
  2269. {
  2270. double s_opt = DBL_MAX;
  2271. unsigned i;
  2272. // Loop as long as the score window is inside the score.
  2273. // Fill the Dyn Pgm matrix: MIDI_buf to score[begScanLocIdx:begScanLocIdx+scWndN-1].
  2274. for(i=0; _cmScAlignCalcMtx(p) && (scanCnt==cmInvalidCnt || i<scanCnt); ++i)
  2275. {
  2276. // locate the path through the DP matrix with the lowest edit distance (cost)
  2277. double cost = _cmScAlign(p);
  2278. // if it is less than any previous score window
  2279. if(cost < s_opt)
  2280. {
  2281. s_opt = cost;
  2282. bsi = p->begScanLocIdx;
  2283. }
  2284. // increment the score window
  2285. p->begScanLocIdx += 1;
  2286. }
  2287. // store the cost assoc'd with bsi
  2288. p->s_opt = s_opt;
  2289. }
  2290. assert( bsi != cmInvalidIdx );
  2291. // Traverse the least cost path and:
  2292. // 1) Set p->esi to the score location index of the last MIDI note
  2293. // which has a positive match with the score and assign
  2294. // the internal score index to cp->locIdx.
  2295. //
  2296. // 2) Set cmScAlignPath_t.locIdx - index into p->loc[] associated
  2297. // with each path element that is a 'substitute' or an 'insert'.
  2298. //
  2299. // 3) Set p->missCnt: the count of trailing non-positive matches.
  2300. // p->missCnt is eventually used in cmScAlignStep() to track the number
  2301. // of consecutive trailing missed notes.
  2302. //
  2303. cmScAlignPath_t* cp = p->p_opt;
  2304. unsigned i = bsi;
  2305. p->missCnt = 0;
  2306. p->esi = cmInvalidIdx;
  2307. for(i=0; cp!=NULL; cp=cp->next)
  2308. {
  2309. if( cp->code != kSaInsIdx )
  2310. {
  2311. assert( cp->ri > 0 );
  2312. p->midiBuf[ cp->ri-1 ].locIdx = cmInvalidIdx;
  2313. }
  2314. switch( cp->code )
  2315. {
  2316. case kSaSubIdx:
  2317. if( cp->matchFl || cp->transFl)
  2318. {
  2319. p->esi = bsi + i;
  2320. p->missCnt = 0;
  2321. if( cp->matchFl )
  2322. p->midiBuf[ cp->ri-1 ].locIdx = bsi + i;
  2323. }
  2324. else
  2325. {
  2326. ++p->missCnt;
  2327. }
  2328. cp->locIdx = bsi + i;
  2329. ++i;
  2330. break;
  2331. case kSaInsIdx:
  2332. cp->locIdx = bsi + i;
  2333. ++i;
  2334. break;
  2335. case kSaDelIdx:
  2336. cp->locIdx = cmInvalidIdx;
  2337. ++p->missCnt;
  2338. break;
  2339. }
  2340. }
  2341. // if no positive matches were found
  2342. if( p->esi == cmInvalidIdx )
  2343. bsi = cmInvalidIdx;
  2344. else
  2345. {
  2346. // report matches
  2347. for(cp=p->p_opt; cp!=NULL; cp=cp->next)
  2348. {
  2349. unsigned locIdx = cp->locIdx;
  2350. cmScAlignMidiEvt_t* mep = NULL;
  2351. if( cp->code != kSaInsIdx )
  2352. mep = p->midiBuf + cp->ri - 1;
  2353. _cmScAlignCb(p,locIdx,mep,cp->matchFl,cp->transFl);
  2354. }
  2355. }
  2356. return bsi;
  2357. }
  2358. cmRC_t cmScAlignStep( cmScAlign* p )
  2359. {
  2360. int i;
  2361. unsigned pitch = p->midiBuf[ p->mn-1 ].pitch;
  2362. unsigned locIdx = cmInvalidIdx;
  2363. // the tracker must be sync'd to step
  2364. if( p->esi == cmInvalidIdx )
  2365. return cmCtxRtCondition( &p->obj, cmInvalidArgRC, "The p->esi value must be valid to perform a step operation.");
  2366. // if the end of the score has been reached
  2367. if( p->esi + 1 >= p->locN )
  2368. return cmEofRC;
  2369. // attempt to match to next location first
  2370. if( _cmScAlignIsMatch(p->loc + p->esi + 1, pitch) )
  2371. {
  2372. locIdx = p->esi + 1;
  2373. }
  2374. else
  2375. {
  2376. //
  2377. for(i=2; i<p->stepCnt; ++i)
  2378. {
  2379. // go forward
  2380. if( p->esi+i < p->locN && _cmScAlignIsMatch(p->loc + p->esi + i, pitch) )
  2381. {
  2382. locIdx = p->esi + i;
  2383. break;
  2384. }
  2385. // go backward
  2386. if( p->esi >= (i-1) && _cmScAlignIsMatch(p->loc + p->esi - (i-1), pitch) )
  2387. {
  2388. locIdx = p->esi - (i-1);
  2389. break;
  2390. }
  2391. }
  2392. }
  2393. p->midiBuf[ p->mn-1 ].locIdx = locIdx;
  2394. if( locIdx == cmInvalidIdx )
  2395. ++p->missCnt;
  2396. else
  2397. {
  2398. p->missCnt = 0;
  2399. p->esi = locIdx;
  2400. _cmScAlignCb(p,locIdx, p->midiBuf + p->mn - 1,true,false);
  2401. }
  2402. if( p->missCnt >= p->maxStepMissCnt )
  2403. {
  2404. p->begScanLocIdx = p->esi > p->rn ? p->esi - p->rn : 0;
  2405. p->s_opt = DBL_MAX;
  2406. unsigned bsi = cmScAlignScan(p,p->rn*2);
  2407. ++p->scanCnt;
  2408. // if the scan failed find a match
  2409. if( bsi == cmInvalidIdx )
  2410. return cmCtxRtCondition( &p->obj, cmSubSysFailRC, "Scan resync. failed.");
  2411. //if( bsi != cmInvalidIdx )
  2412. // _cmScAlignPrintPath(p, p->p_opt, bsi );
  2413. }
  2414. return cmOkRC;
  2415. }
  2416. void _cmScAlignPrintMtx( cmScAlign* r)
  2417. {
  2418. unsigned i,j,k;
  2419. for(i=0; i<r->rn; ++i)
  2420. {
  2421. for(j=0; j<r->cn; ++j)
  2422. {
  2423. printf("(");
  2424. const cmScAlignVal_t* vp = _cmScAlignValPtr(r,i,j);
  2425. for(k=0; k<kSaCnt; ++k)
  2426. {
  2427. printf("%i",vp->v[k]);
  2428. if( k<kSaCnt-1)
  2429. printf(", ");
  2430. else
  2431. printf(" ");
  2432. }
  2433. printf("%c)",vp->transFl?'t':' ');
  2434. }
  2435. printf("\n");
  2436. }
  2437. }
  2438. void cmScAlignPrintOpt( cmScAlign* p )
  2439. {
  2440. unsigned i;
  2441. for(i=0; p->p_opt[i].code!=0; ++i)
  2442. {
  2443. cmScAlignPath_t* cp = p->p_opt + i;
  2444. char c0 = cp->matchFl ? 'm' : ' ';
  2445. char c1 = cp->transFl ? 't' : ' ';
  2446. printf("%2i code:%i ri:%2i ci:%2i %c%c\n",i,cp->code,cp->ri,cp->ci,c0,c1);
  2447. }
  2448. printf("score:%f\n",p->s_opt);
  2449. }
  2450. enum
  2451. {
  2452. kBarSaFl = 0x01, // this is a score bar
  2453. kScNoteSaFl = 0x02, // this is a score reference note (if mni != cmInvalidIdx then it was matched)
  2454. kSubsErrSaFl = 0x04, // 'subs' mismatch midi note
  2455. kMidiErrSaFl = 0x08, // 'deleted' Midi note
  2456. };
  2457. typedef struct cmScAlignPrint_str
  2458. {
  2459. unsigned flags;
  2460. unsigned scLocIdx;
  2461. unsigned smpIdx;
  2462. unsigned pitch;
  2463. unsigned vel;
  2464. unsigned mni;
  2465. bool matchFl;
  2466. bool transFl;
  2467. } cmScAlignPrint_t;
  2468. void _cmScAlignPrintList( cmScAlignPrint_t* a, unsigned an )
  2469. {
  2470. cmScAlignPrint_t* pp;
  2471. unsigned i;
  2472. printf("----------------------------------------------------\n");
  2473. printf("idx scl mni pit flg \n");
  2474. for(i=0; i<an; ++i)
  2475. {
  2476. pp = a + i;
  2477. printf("%3i %3i %3i %4s 0x%x\n",i,pp->scLocIdx,pp->mni,
  2478. pp->pitch==kInvalidMidiPitch ? " " : cmMidiToSciPitch(pp->pitch,NULL,0),
  2479. pp->flags);
  2480. }
  2481. printf("\n");
  2482. }
  2483. // insert a black record at a[i]
  2484. unsigned _cmScAlignPrintExpand( cmScAlignPrint_t* a, unsigned aan, unsigned i, unsigned an )
  2485. {
  2486. assert( an < aan );
  2487. memmove( a + i + 1, a + i, (an-i)*sizeof(cmScAlignPrint_t));
  2488. memset( a + i, 0, sizeof(cmScAlignPrint_t));
  2489. return an + 1;
  2490. }
  2491. void _cmScAlignPrintOutResult( cmScAlign* p, cmScAlignResult_t* rp, const cmChar_t* label )
  2492. {
  2493. printf("loc:%4i scloc:%4i smp:%10i mni:%4i %4s %c %c %s\n",
  2494. rp->locIdx,
  2495. rp->locIdx==cmInvalidIdx ? -1 : p->loc[rp->locIdx].scLocIdx,
  2496. rp->smpIdx,
  2497. rp->mni,
  2498. rp->pitch<=127 ? cmMidiToSciPitch(rp->pitch,NULL,0) : " ",
  2499. rp->matchFl ? 'm' : ' ',
  2500. rp->transFl ? 't' : ' ',
  2501. label);
  2502. }
  2503. void _cmScAlignPrintSet( cmScAlignPrint_t* pp, const cmScAlignResult_t* rp, unsigned flags, unsigned scLocIdx )
  2504. {
  2505. pp->scLocIdx = scLocIdx;
  2506. pp->flags = flags;
  2507. pp->smpIdx = rp->smpIdx;
  2508. pp->pitch = rp->pitch;
  2509. pp->vel = rp->vel;
  2510. pp->mni = rp->mni;
  2511. assert( pp->scLocIdx!=cmInvalidIdx || pp->mni != cmInvalidIdx );
  2512. }
  2513. unsigned _cmScAlignPrintPoly( cmScAlignPrint_t* a, unsigned an, unsigned scLocIdx )
  2514. {
  2515. unsigned polyN = 0;
  2516. unsigned i;
  2517. for(i=0; i<an; ++i)
  2518. if( a[i].scLocIdx == scLocIdx )
  2519. break;
  2520. if( i < an )
  2521. {
  2522. for(; i<an; ++i,++polyN)
  2523. if( a[i].scLocIdx != scLocIdx )
  2524. break;
  2525. // identical scLocIdx values must be consecutive
  2526. for(; i<an; ++i)
  2527. {
  2528. if( a[i].scLocIdx == scLocIdx )
  2529. _cmScAlignPrintList(a,an);
  2530. assert( a[i].scLocIdx != scLocIdx );
  2531. }
  2532. }
  2533. return polyN;
  2534. }
  2535. cmScAlignPrint_t* _cmScAlignPrintRecd(cmScAlignPrint_t* a, unsigned an, unsigned scLocIdx, unsigned polyIdx )
  2536. {
  2537. unsigned i,j;
  2538. for(i=0; i<an; ++i)
  2539. {
  2540. if( a[i].scLocIdx == scLocIdx )
  2541. for(j=0; i<an; ++j,++i)
  2542. {
  2543. if( a[i].scLocIdx != scLocIdx )
  2544. break;
  2545. if( j == polyIdx )
  2546. return a + i;
  2547. }
  2548. }
  2549. return NULL;
  2550. }
  2551. void _cmScAlignPrintReport( cmScAlign* p, cmScAlignPrint_t* a, unsigned an, unsigned bsi, unsigned esi )
  2552. {
  2553. unsigned colN = 5;
  2554. unsigned bli = bsi;
  2555. bool titleFl = true;
  2556. while( bli < esi )
  2557. {
  2558. unsigned i,j;
  2559. // get ending scLocIdx
  2560. unsigned eli = cmMin(bli+colN, esi);
  2561. // get the max poly count
  2562. unsigned polyN = 0;
  2563. for(i=bli; i<eli; ++i)
  2564. {
  2565. unsigned pn = _cmScAlignPrintPoly(a,an,i);
  2566. if( pn > polyN )
  2567. polyN = pn;
  2568. }
  2569. // print titles
  2570. if( titleFl )
  2571. {
  2572. printf(" ");
  2573. for(j=bli; j<eli; ++j)
  2574. printf("| %4s %4s %3s %1s ","mni"," ","vel"," ");
  2575. printf("\n");
  2576. titleFl = false;
  2577. }
  2578. // print 'loc' index line
  2579. printf("scl: ");
  2580. for(j=bli; j<eli; ++j)
  2581. printf("| %4i %4s %3s %1s ",j," "," "," ");
  2582. printf("\n");
  2583. for(i=polyN; i>0; --i)
  2584. {
  2585. printf("%3i: ",i);
  2586. for(j=bli; j<eli; ++j)
  2587. {
  2588. cmScAlignPrint_t* pp;
  2589. if((pp = _cmScAlignPrintRecd(a,an,j,i-1)) == NULL )
  2590. printf("| %4s %4s %3s %1s "," "," "," "," ");
  2591. else
  2592. {
  2593. if( pp->mni == cmInvalidIdx && cmIsNotFlag(pp->flags,kBarSaFl) )
  2594. printf("| %4s %4s %3s %1s "," ",cmMidiToSciPitch(pp->pitch,NULL,0)," "," ");
  2595. else
  2596. {
  2597. if( cmIsFlag(pp->flags,kBarSaFl) )
  2598. printf("| %4s %4s %3i %1s "," "," | ",pp->pitch,"b");
  2599. else
  2600. {
  2601. const cmChar_t* op = cmIsFlag(pp->flags,kMidiErrSaFl) ? "d" : " ";
  2602. op = cmIsFlag(pp->flags,kSubsErrSaFl) ? "s" : op;
  2603. printf("| %4i %4s %3i %1s ",pp->mni,cmMidiToSciPitch(pp->pitch,NULL,0),pp->vel,op);
  2604. }
  2605. }
  2606. }
  2607. }
  2608. printf("\n");
  2609. }
  2610. printf("\n");
  2611. bli = eli;
  2612. }
  2613. }
  2614. // The goal of this function is to create a cmScAlignPrint_t array containing
  2615. // one record for each score bar, score note and errant MIDI note.
  2616. // The function works by first creating a record for each score bar and note
  2617. // and then scanning the cmScAlignResult_t array (p->res[]) for each result
  2618. // record create by an earlier call to _cmScAlignCb(). A result record can
  2619. // uniquely indicates one of the following result states based on receiving
  2620. // a MIDI event.
  2621. // Match - locIdx!=cmInvalidIdx matchFl==true mni!=cmInvalidIdx
  2622. // Mis-match - locIdx!=cmInvalidIdx matchFl==false mni!=cmInvalidIdx
  2623. // Delete - locIdx==cmInvalidIdx matchFl==false mni!=cmInvalidIdx
  2624. // Insert - locIdx==cmInvalidIdx matchFl==false mni==cmInvalidIdx
  2625. //
  2626. // This is made slightly more complicated by the fact that a given MIDI event
  2627. // may generate more than one result record. This can occur when the
  2628. // tracker is in 'step' mode and generates a result record with a given state
  2629. // as a result of a given MIDI note and then reconsiders that MIDI note
  2630. // while during a subsequent 'scan' mode resync. operation. For example
  2631. // a MIDI note which generate a 'delete' result during a step operation
  2632. // may later generate a match result during a scan.
  2633. double _cmScAlignPrintResult( cmScAlign* p )
  2634. {
  2635. // determine the scH score begin and end indexes
  2636. unsigned bsi = cmScoreLocCount(p->scH);
  2637. unsigned esi = 0;
  2638. unsigned i,j;
  2639. for(i=0; i<p->ri; ++i)
  2640. {
  2641. cmScAlignResult_t* rp = p->res + i;
  2642. assert( rp->locIdx==cmInvalidIdx || rp->locIdx<p->locN);
  2643. if( rp->locIdx != cmInvalidIdx )
  2644. {
  2645. bsi = cmMin(bsi,p->loc[ rp->locIdx ].scLocIdx);
  2646. esi = cmMax(esi,p->loc[ rp->locIdx ].scLocIdx);
  2647. }
  2648. }
  2649. // get a count of MIDI events + score events
  2650. unsigned aan = p->ri;
  2651. for(i=bsi; i<=esi; ++i)
  2652. {
  2653. cmScoreLoc_t* lp = cmScoreLoc( p->scH, i);
  2654. aan += lp->evtCnt;
  2655. }
  2656. cmScAlignPrint_t* a = cmMemAllocZ(cmScAlignPrint_t,aan);
  2657. unsigned an = 0;
  2658. unsigned scNoteCnt = 0; // notes in the score
  2659. unsigned matchCnt = 0; // matched score notes
  2660. unsigned wrongCnt = 0; // errant midi notes
  2661. unsigned skipCnt = 0; // skipped score events
  2662. // create a record for each score event
  2663. for(i=bsi; i<=esi; ++i)
  2664. {
  2665. cmScoreLoc_t* lp = cmScoreLoc( p->scH, i);
  2666. for(j=0; j<lp->evtCnt; ++j,++an)
  2667. {
  2668. assert( an < aan );
  2669. cmScAlignPrint_t* pp = a + an;
  2670. assert( lp->index != cmInvalidIdx );
  2671. pp->scLocIdx = lp->index;
  2672. pp->mni = cmInvalidIdx;
  2673. pp->pitch = kInvalidMidiPitch;
  2674. pp->vel = cmInvalidIdx;
  2675. switch( lp->evtArray[j]->type )
  2676. {
  2677. case kBarEvtScId:
  2678. pp->flags = kBarSaFl;
  2679. pp->pitch = lp->evtArray[j]->barNumb;
  2680. pp->mni = cmInvalidIdx;
  2681. break;
  2682. case kNonEvtScId:
  2683. pp->flags = kScNoteSaFl;
  2684. pp->pitch = lp->evtArray[j]->pitch;
  2685. ++scNoteCnt;
  2686. break;
  2687. }
  2688. }
  2689. }
  2690. //_cmScAlignPrintList(a,an);
  2691. // Update the score with matching MIDI notes
  2692. // for each result record ...
  2693. for(i=0; i<p->ri; ++i)
  2694. {
  2695. cmScAlignResult_t* rp = p->res + i;
  2696. rp->foundFl = false;
  2697. // ... if this is not an errant MIDI note (delete)
  2698. if( rp->locIdx != cmInvalidIdx )
  2699. {
  2700. assert( rp->locIdx != cmInvalidIdx && rp->locIdx < p->locN );
  2701. unsigned scLocIdx = p->loc[rp->locIdx].scLocIdx;
  2702. cmScAlignPrint_t* pp;
  2703. // ... find the score location matching the result record score location
  2704. for(j=0; j<an; ++j)
  2705. {
  2706. pp = a + j;
  2707. // if this score location matches the result score location
  2708. if( scLocIdx == pp->scLocIdx )
  2709. {
  2710. // if this is a matching midi node
  2711. if( rp->matchFl && cmIsFlag(pp->flags,kScNoteSaFl) && pp->pitch == rp->pitch )
  2712. {
  2713. //_cmScAlignPrintOutResult(p,rp,"match");
  2714. rp->foundFl = true;
  2715. _cmScAlignPrintSet(pp, rp, pp->flags, pp->scLocIdx );
  2716. ++matchCnt;
  2717. break;
  2718. }
  2719. // if this is a 'substitute' non-matching note
  2720. if( rp->matchFl == false && rp->mni != cmInvalidIdx )
  2721. {
  2722. //_cmScAlignPrintOutResult(p,rp,"mis-match");
  2723. ++j; // insert after the a[j]
  2724. an = _cmScAlignPrintExpand(a,aan,j,an);
  2725. _cmScAlignPrintSet(a + j, rp, kSubsErrSaFl, scLocIdx );
  2726. rp->foundFl = true;
  2727. ++wrongCnt;
  2728. break;
  2729. }
  2730. // if this is a 'skipped' score note ('insert') alert
  2731. if( rp->mni == cmInvalidIdx )
  2732. {
  2733. //_cmScAlignPrintOutResult(p,rp,"skip");
  2734. rp->foundFl = true;
  2735. break;
  2736. }
  2737. }
  2738. }
  2739. }
  2740. if( rp->foundFl == false )
  2741. {
  2742. // _cmScAlignPrintOutResult(p,rp,"not-found");
  2743. }
  2744. }
  2745. //_cmScAlignPrintList(a,an);
  2746. // Insert records into the print record array (a[](
  2747. // to represent errant MIDI notes. (Notes which
  2748. // were played but do not match any notes in the score.)
  2749. // for each result record ...
  2750. for(i=0; i<p->ri; ++i)
  2751. {
  2752. cmScAlignResult_t* rp = p->res + i;
  2753. cmScAlignPrint_t* pp = NULL;
  2754. cmScAlignPrint_t* dpp = NULL;
  2755. unsigned dmin;
  2756. // if this result did not have a matching score event
  2757. if(rp->foundFl)
  2758. continue;
  2759. // find the print recd with the closest mni
  2760. for(j=0; j<an; ++j)
  2761. {
  2762. pp = a + j;
  2763. if( pp->mni!=cmInvalidId )
  2764. {
  2765. unsigned d;
  2766. if( pp->mni > rp->mni )
  2767. d = pp->mni - rp->mni;
  2768. else
  2769. d = rp->mni - pp->mni;
  2770. if( dpp == NULL || d < dmin )
  2771. {
  2772. dpp = pp;
  2773. dmin = d;
  2774. }
  2775. }
  2776. }
  2777. assert( dpp != NULL );
  2778. j = dpp - a;
  2779. if( rp->mni > dpp->mni )
  2780. ++j;
  2781. assert( rp->locIdx == cmInvalidIdx );
  2782. // insert a print recd before or after the closest print recd
  2783. an = _cmScAlignPrintExpand(a,aan,j,an);
  2784. _cmScAlignPrintSet(a + j, rp, kMidiErrSaFl, dpp->scLocIdx );
  2785. ++wrongCnt;
  2786. }
  2787. for(i=0; i<an; ++i)
  2788. if( cmIsFlag(a[i].flags,kScNoteSaFl) && (a[i].mni == cmInvalidIdx || cmIsFlag(a[i].flags,kSubsErrSaFl)))
  2789. ++skipCnt;
  2790. //_cmScAlignPrintList(a,an);
  2791. //_cmScAlignPrintReport(p,a,an,bsi,esi);
  2792. double prec = (double)2.0 * matchCnt / (matchCnt + wrongCnt);
  2793. double rcal = (double)2.0 * matchCnt / (matchCnt + skipCnt);
  2794. double fmeas = prec * rcal / (prec + rcal);
  2795. printf("midi:%i scans:%i score notes:%i match:%i skip:%i wrong:%i : %f\n",p->mni,p->scanCnt,scNoteCnt,matchCnt,skipCnt,wrongCnt,fmeas);
  2796. cmMemFree(a);
  2797. return fmeas;
  2798. }
  2799. cmRC_t cmScAlignScanToTimeLineEvent( cmScMatcher* p, cmTlH_t tlH, cmTlObj_t* top, unsigned endSmpIdx )
  2800. {
  2801. assert( top != NULL );
  2802. cmTlMidiEvt_t* mep = NULL;
  2803. cmRC_t rc = cmOkRC;
  2804. // as long as more MIDI events are available get the next MIDI msg
  2805. while( rc==cmOkRC && (mep = cmTlNextMidiEvtObjPtr(tlH, top, top->seqId )) != NULL )
  2806. {
  2807. top = &mep->obj;
  2808. // if the msg falls after the end of the marker then we are through
  2809. if( mep->obj.seqSmpIdx != cmInvalidIdx && mep->obj.seqSmpIdx > endSmpIdx )
  2810. break;
  2811. // if the time line MIDI msg a note-on
  2812. if( mep->msg->status == kNoteOnMdId )
  2813. {
  2814. rc = cmScMatcherExec(p, mep->obj.seqSmpIdx, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1 );
  2815. switch( rc )
  2816. {
  2817. case cmOkRC: // continue processing MIDI events
  2818. break;
  2819. case cmEofRC: // end of the score was encountered
  2820. break;
  2821. case cmInvalidArgRC: // p->esi was not set correctly
  2822. break;
  2823. case cmSubSysFailRC: // scan resync failed
  2824. break;
  2825. }
  2826. }
  2827. }
  2828. if( rc == cmEofRC )
  2829. rc = cmOkRC;
  2830. return rc;
  2831. }
  2832. void cmScAlignCb( void* cbArg, unsigned scLocIdx, unsigned mni, unsigned pitch, unsigned vel )
  2833. {
  2834. //cmScAlign* p = (cmScAlign*)cbArg;
  2835. }
  2836. void cmScAlignScanMarkers( cmRpt_t* rpt, cmTlH_t tlH, cmScH_t scH )
  2837. {
  2838. unsigned i;
  2839. double srate = 96000;
  2840. unsigned midiN = 7;
  2841. unsigned scWndN = 10;
  2842. unsigned markN = 291;
  2843. cmCtx* ctx = cmCtxAlloc(NULL, rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  2844. cmScMatcher* p = cmScMatcherAlloc(ctx,NULL,srate,scH,scWndN,midiN);
  2845. unsigned markCharCnt = 31;
  2846. cmChar_t markText[ markCharCnt+1 ];
  2847. double scoreThresh = 0.5;
  2848. unsigned candCnt = 0;
  2849. unsigned initFailCnt = 0;
  2850. unsigned otherFailCnt = 0;
  2851. unsigned scoreFailCnt = 0;
  2852. cmTimeSpec_t t0,t1;
  2853. cmTimeGet(&t0);
  2854. //p->cbArg = p; // set the callback arg.
  2855. // for each marker
  2856. for(i=0; i<markN; ++i)
  2857. {
  2858. // form the marker text
  2859. snprintf(markText,markCharCnt,"Mark %i",i);
  2860. // locate the marker
  2861. cmTlMarker_t* mp = cmTimeLineMarkerFind( tlH, markText );
  2862. if( mp == NULL )
  2863. {
  2864. printf("The marker '%s' was not found.\n\n",markText);
  2865. continue;
  2866. }
  2867. // skip markers which do not contain text
  2868. if( cmTextIsEmpty(mp->text) )
  2869. {
  2870. printf("The marker '%s' is being skipped because it has no text.\n\n",markText);
  2871. continue;
  2872. }
  2873. // reset the score follower to the beginnig of the score
  2874. cmScMatcherReset(p);
  2875. ++candCnt;
  2876. // scan to the beginning of the marker
  2877. cmRC_t rc = cmScAlignScanToTimeLineEvent(p,tlH,&mp->obj,mp->obj.seqSmpIdx+mp->obj.durSmpCnt);
  2878. bool pfl = true;
  2879. if( rc != cmOkRC || p->begSyncLocIdx==cmInvalidIdx)
  2880. {
  2881. if( p->begSyncLocIdx == cmInvalidIdx )
  2882. rc = cmInvalidArgRC;
  2883. if( p->mni == 0 )
  2884. {
  2885. printf("mark:%i midi:%i Not enough MIDI notes to fill the scan buffer.\n",i,p->mni);
  2886. pfl = false;
  2887. }
  2888. else
  2889. {
  2890. switch(rc)
  2891. {
  2892. case cmInvalidArgRC:
  2893. printf("mark:%i INITIAL SYNC FAIL\n",i);
  2894. ++initFailCnt;
  2895. pfl = false;
  2896. break;
  2897. case cmSubSysFailRC:
  2898. printf("mark:%i SCAN RESYNC FAIL\n",i);
  2899. ++otherFailCnt;
  2900. break;
  2901. default:
  2902. printf("mark:%i UNKNOWN FAIL\n",i);
  2903. ++otherFailCnt;
  2904. }
  2905. }
  2906. }
  2907. if( pfl )
  2908. {
  2909. // kpl printf("mark:%i scans:%4i loc:%4i bar:%4i score:%5.2f miss:%i text:'%s'\n",i,p->scanCnt,p->begSyncLocIdx,p->loc[p->begSyncLocIdx].barNumb,p->s_opt,p->missCnt,mp->text);
  2910. double fmeas = cmScMatcherFMeas(p);
  2911. 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);
  2912. //printf("mark:%i scans:%i midi:%i text:'%s'\n",i,p->scanCnt,p->mni,mp->text);
  2913. if( fmeas < scoreThresh )
  2914. ++scoreFailCnt;
  2915. }
  2916. //break; // ONLY USE ONE MARKER DURING TESTING
  2917. printf("\n");
  2918. }
  2919. printf("cand:%i fail:%i - init:%i score:%i other:%i\n\n",candCnt,initFailCnt+scoreFailCnt+otherFailCnt,initFailCnt,scoreFailCnt,otherFailCnt);
  2920. cmTimeGet(&t1);
  2921. printf("elapsed:%f\n", (double)cmTimeElapsedMicros(&t0,&t1)/1000000.0 );
  2922. cmScMatcherFree(&p);
  2923. cmCtxFree(&ctx);
  2924. }