libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmScoreProc.c 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. #include "cmGlobal.h"
  2. #include "cmFloatTypes.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 "cmSymTbl.h"
  10. #include "cmJson.h"
  11. #include "cmFile.h"
  12. #include "cmTime.h"
  13. #include "cmMidi.h"
  14. #include "cmMidiFile.h"
  15. #include "cmAudioFile.h"
  16. #include "cmScore.h"
  17. #include "cmTimeLine.h"
  18. #include "cmScoreProc.h"
  19. #include "cmProcObj.h"
  20. #include "cmProc4.h"
  21. typedef enum
  22. {
  23. kBeginTakeSpId, // tlObjPtr points to a cmTlMarker_t object.
  24. kEndTakeSpId, // tlObjPtr is NULL.
  25. kNoteOnSpId, // tlObjPtr points to a cmTlMidiEvt_t note-on object.
  26. kFailSpId // tlObjPtr points to a cmTlMarker_t object (This takes score tracking failed.)
  27. } cmScoreProcSelId_t;
  28. struct cmSp_str;
  29. typedef cmSpRC_t (*cmScoreProcCb_t)( void* arg, struct cmSp_str* p, cmScoreProcSelId_t id, cmTlObj_t* tlObjPtr );
  30. typedef struct cmSp_str
  31. {
  32. cmErr_t err; // score proc object error state
  33. cmCtx* ctx; // application context
  34. cmScH_t scH; // score object
  35. cmTlH_t tlH; // time-line object
  36. cmJsonH_t jsH; //
  37. unsigned* dynArray; // dynArray[dynCnt] dynamics reference array
  38. unsigned dynCnt; //
  39. double srate; //
  40. cmScMatcher* match; // score follower
  41. cmScoreProcCb_t procCb; // score processor callback - called whenever a new 'marker' take or note-on is about to be processed
  42. cmScMatcherCb_t matchCb; // score follower callback - called whenever the score follower detects a matched event
  43. void* cbArg; // callback arg. for both matchCb and procCb.
  44. } cmSp_t;
  45. // read the dynamics reference array from the time-line project file.
  46. cmSpRC_t _cmJsonReadDynArray( cmJsonH_t jsH, unsigned** dynArray, unsigned* dynCnt )
  47. {
  48. cmJsonNode_t* np;
  49. int i;
  50. if( cmJsonPathToArray(jsH, NULL, NULL, "dynRef", &np ) != kOkJsRC )
  51. return kJsonFailSpRC;
  52. *dynCnt = cmJsonChildCount(np);
  53. *dynArray = cmMemAllocZ(unsigned,*dynCnt);
  54. for(i=0; i<*dynCnt; ++i)
  55. if( cmJsonUIntValue( cmJsonArrayElement(np,i), (*dynArray)+i ) != kOkJsRC )
  56. return kJsonFailSpRC;
  57. return kOkSpRC;
  58. }
  59. cmSpRC_t _cmScoreProcInit(
  60. cmCtx_t* ctx,
  61. cmSp_t* p,
  62. const cmChar_t* rsrcFn,
  63. cmScoreProcCb_t procCb,
  64. cmScMatcherCb_t matchCb,
  65. void* cbArg )
  66. {
  67. cmSpRC_t rc = kOkSpRC;
  68. const cmChar_t* scFn = NULL;
  69. const cmChar_t* tlFn = NULL;
  70. const cmChar_t* tlPrefixPath = NULL;
  71. cmErrSetup(&p->err,&ctx->rpt,"ScoreProc");
  72. // open the resource file
  73. if( cmJsonInitializeFromFile( &p->jsH, rsrcFn, ctx ) != kOkJsRC )
  74. {
  75. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to load the main resource file:%s.",cmStringNullGuard(rsrcFn));
  76. goto errLabel;
  77. }
  78. // get the time line fn
  79. if( cmJsonPathToString( p->jsH, NULL, NULL, "timeLineFn", &tlFn ) != kOkJsRC )
  80. {
  81. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the time line file name in the main resource file:%s",cmStringNullGuard(rsrcFn));
  82. goto errLabel;
  83. }
  84. // get the score file name
  85. if( cmJsonPathToString( p->jsH, NULL, NULL, "scoreFn", &scFn ) != kOkJsRC )
  86. {
  87. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the score file name in the main resource file:%s",cmStringNullGuard(rsrcFn));
  88. goto errLabel;
  89. }
  90. // get the time line data file prefix path
  91. if( cmJsonPathToString( p->jsH, NULL, NULL, "tlPrefixPath", &tlPrefixPath ) != kOkJsRC )
  92. {
  93. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the time line data file prefix path in the main resource file:%s",cmStringNullGuard(rsrcFn));
  94. goto errLabel;
  95. }
  96. // read the dynamics reference array
  97. if((rc = _cmJsonReadDynArray( p->jsH, &p->dynArray, &p->dynCnt )) != kOkSpRC )
  98. {
  99. rc = cmErrMsg(&p->err,rc,"Unable to read dynamics reference array resource from the main resource file:%s",cmStringNullGuard(rsrcFn));
  100. goto errLabel;
  101. }
  102. // load the time-line file
  103. if( cmTimeLineInitializeFromFile(ctx, &p->tlH, NULL, NULL, tlFn, tlPrefixPath ) != kOkTlRC )
  104. {
  105. rc = cmErrMsg(&p->err,kTimeLineFailSpRC,"Time line load failed for time line file:%s.",cmStringNullGuard(tlFn));
  106. goto errLabel;
  107. }
  108. p->srate = cmTimeLineSampleRate(p->tlH);
  109. // load the score file
  110. if( cmScoreInitialize(ctx, &p->scH, scFn, p->srate, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  111. {
  112. rc = cmErrMsg(&p->err,kScoreFailSpRC,"Score load failed for score file:%s.",cmStringNullGuard(scFn));
  113. goto errLabel;
  114. }
  115. p->ctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  116. p->matchCb = matchCb;
  117. p->procCb = procCb;
  118. p->cbArg = cbArg;
  119. errLabel:
  120. return rc;
  121. }
  122. // This function iterates through each sequence and advances
  123. // to each 'begin-marker' position.
  124. cmSpRC_t _cmScoreProcProcess(cmCtx_t* ctx, cmSp_t* sp)
  125. {
  126. cmSpRC_t rc = kOkSpRC;
  127. unsigned midiN = 7;
  128. unsigned scWndN = 10;
  129. unsigned seqN = cmTimeLineSeqCount(sp->tlH);
  130. double srate = cmTimeLineSampleRate(sp->tlH);
  131. unsigned seqId;
  132. assert( sp->srate == srate);
  133. // allocate the score matcher
  134. sp->match = cmScMatcherAlloc(sp->ctx,NULL,sp->srate,sp->scH,scWndN,midiN,sp->matchCb,sp->cbArg);
  135. assert(sp->match != NULL );
  136. // for each time line sequence
  137. for(seqId=0; seqId<seqN; ++seqId)
  138. {
  139. cmTlObj_t* o0p = NULL;
  140. // for each 'marker' in this time line sequence
  141. while( (o0p = cmTimeLineNextTypeObj(sp->tlH, o0p, seqId, kMarkerTlId)) != NULL )
  142. {
  143. // get the 'marker' recd
  144. cmTlMarker_t* markPtr = cmTimeLineMarkerObjPtr(sp->tlH,o0p);
  145. assert( markPtr != NULL );
  146. // if the marker does not have a valid start bar location
  147. if( markPtr->bar == 0 )
  148. continue;
  149. // get the end-of-marker time as a sample index
  150. unsigned markEndSmpIdx = markPtr->obj.seqSmpIdx + markPtr->obj.durSmpCnt;
  151. // get the score event associated with the marker's bar number.
  152. const cmScoreEvt_t* evtPtr = cmScoreBarEvt(sp->scH,markPtr->bar);
  153. assert( evtPtr != NULL );
  154. // get the score location associated with the markers bar score event
  155. const cmScoreLoc_t* locPtr = cmScoreEvtLoc(sp->scH,evtPtr);
  156. assert( locPtr != NULL );
  157. cmRptPrintf(&ctx->rpt,"Processing loc:%i seq:%i %s %s\n",locPtr->index,seqId,cmStringNullGuard(markPtr->obj.name),cmStringNullGuard(markPtr->text));
  158. // reset the score matcher to begin searching at the bar location
  159. if( cmScMatcherReset(sp->match, locPtr->index ) != cmOkRC )
  160. {
  161. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher reset failed on location: %i.",locPtr->index);
  162. continue;
  163. }
  164. // inform the score processor that we are about to start a new take
  165. if( sp->procCb( sp->cbArg, sp, kBeginTakeSpId, o0p ) != kOkSpRC )
  166. {
  167. cmErrMsg(&sp->err,kProcFailSpRC,"The score process object failed on reset.");
  168. continue;
  169. }
  170. cmTlObj_t* o1p = o0p;
  171. bool errFl = false;
  172. // as long as more MIDI events are available get the next MIDI msg
  173. while( (rc == kOkSpRC) && (o1p = cmTimeLineNextTypeObj(sp->tlH, o1p, seqId, kMidiEvtTlId )) != NULL )
  174. {
  175. cmTlMidiEvt_t* mep = cmTimeLineMidiEvtObjPtr(sp->tlH,o1p);
  176. assert(mep != NULL );
  177. // if the msg falls after the end of the marker then we are done
  178. if( mep->obj.seqSmpIdx != cmInvalidIdx && mep->obj.seqSmpIdx > markEndSmpIdx )
  179. break;
  180. // if the time line MIDI msg is a note-on
  181. if( mep->msg->status == kNoteOnMdId )
  182. {
  183. sp->procCb( sp->cbArg, sp, kNoteOnSpId, o1p );
  184. cmRC_t cmRC = cmScMatcherExec(sp->match, mep->obj.seqSmpIdx, mep->msg->uid, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1, NULL );
  185. switch( cmRC )
  186. {
  187. case cmOkRC: // continue processing MIDI events
  188. break;
  189. case cmEofRC: // end of the score was encountered
  190. break;
  191. case cmInvalidArgRC: // p->eli was not set correctly
  192. rc = cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher failed due to an invalid argument.");
  193. errFl = true;
  194. break;
  195. case cmSubSysFailRC: // scan resync failed
  196. rc = cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher failed on resync.");
  197. sp->procCb( sp->cbArg, sp, kFailSpId, o0p );
  198. //cmScMatcherPrint(sp->match);
  199. //goto errLabel;
  200. break;
  201. default:
  202. { assert(0); }
  203. }
  204. }
  205. }
  206. // inform the score processor that we done processing a take
  207. if( sp->procCb( sp->cbArg, sp, kEndTakeSpId, NULL ) != kOkSpRC )
  208. cmErrMsg(&sp->err,kProcFailSpRC,"The score process object failed on reset.");
  209. // error flag is used to break out of the loop after the 'end-take' is called
  210. // so that the user defined processes has a chance to clean-up
  211. if( errFl )
  212. goto errLabel;
  213. rc = kOkSpRC;
  214. }
  215. }
  216. errLabel:
  217. if( cmScMatcherFree(&sp->match) != cmOkRC )
  218. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher release failed.");
  219. return rc;
  220. }
  221. cmSpRC_t _cmScoreProcFinal( cmSp_t* p )
  222. {
  223. cmSpRC_t rc = kOkSpRC;
  224. cmCtxFree(&p->ctx);
  225. if( cmScoreFinalize(&p->scH) != kOkScRC )
  226. cmErrMsg(&p->err,kScoreFailSpRC,"Score finalize failed.");
  227. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  228. cmErrMsg(&p->err,kTimeLineFailSpRC,"Time line finalize failed.");
  229. if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  230. cmErrMsg(&p->err,kJsonFailSpRC,"JSON finalize failed.");
  231. cmMemFree(p->dynArray);
  232. return rc;
  233. }
  234. //==================================================================================================
  235. typedef struct _cmSpMeas_t
  236. {
  237. cmTlMarker_t* markPtr; // time-line marker in which this 'set' exists
  238. cmScoreSet_t* setPtr; // score set on which this measurment is based
  239. double value; // the value of the measurement
  240. double cost; // the quality of the perf->score match
  241. struct _cmSpMeas_t* link;
  242. } _cmSpMeas_t;
  243. typedef struct
  244. {
  245. struct cmSp_str* sp;
  246. cmScMeas* meas; // performance analyzer
  247. cmTlMarker_t* curMarkPtr; //
  248. _cmSpMeas_t* list_beg; //
  249. _cmSpMeas_t* list_end; //
  250. _cmSpMeas_t* slist_beg; //
  251. } _cmSpMeasProc_t;
  252. typedef struct
  253. {
  254. unsigned srcSeqId;
  255. const cmChar_t* srcMarkNameStr;
  256. unsigned srcTypeId;
  257. const cmChar_t* srcTypeLabelStr;
  258. unsigned dstScLocIdx;
  259. unsigned dstEvtIdx;
  260. const cmChar_t* dstSectLabelStr;
  261. double value;
  262. double cost;
  263. } _cmSpMeasSect_t;
  264. unsigned _cmSpMeasSectCount( _cmSpMeasProc_t* m )
  265. {
  266. const _cmSpMeas_t* mp = m->list_beg;
  267. unsigned n = 0;
  268. unsigned M = 0;
  269. for(; mp != NULL; mp=mp->link,++M)
  270. n += mp->setPtr->sectCnt;
  271. return n;
  272. }
  273. int _cmSpMeasSectCompare( const void* p0, const void* p1 )
  274. {
  275. _cmSpMeasSect_t* m0 = (_cmSpMeasSect_t*)p0;
  276. _cmSpMeasSect_t* m1 = (_cmSpMeasSect_t*)p1;
  277. return (int)m0->dstScLocIdx - (int)m1->dstScLocIdx;
  278. }
  279. cmSpRC_t _cmScWriteMeasFile( cmCtx_t* ctx, cmSp_t* sp, _cmSpMeasProc_t* m, const cmChar_t* outFn )
  280. {
  281. cmFileH_t fH = cmFileNullHandle;
  282. cmSpRC_t rc = kOkSpRC;
  283. unsigned i,j,k;
  284. unsigned scnt = _cmSpMeasSectCount(m);
  285. _cmSpMeas_t* mp = m->list_beg;
  286. _cmSpMeasSect_t sarray[ scnt ];
  287. for(i=0,k=0; k<scnt && mp!=NULL; ++i,mp=mp->link)
  288. {
  289. const cmChar_t* typeLabel = NULL;
  290. switch(mp->setPtr->varId)
  291. {
  292. case kEvenVarScId: typeLabel="even"; break;
  293. case kDynVarScId: typeLabel="dyn"; break;
  294. case kTempoVarScId:typeLabel="tempo";break;
  295. default:
  296. { assert(0); }
  297. }
  298. for(j=0; j<mp->setPtr->sectCnt; ++j,++k)
  299. {
  300. assert(k<scnt);
  301. _cmSpMeasSect_t* r = sarray + k;
  302. r->srcSeqId = mp->markPtr->obj.seqId;
  303. r->srcMarkNameStr = cmStringNullGuard(mp->markPtr->obj.name);
  304. r->srcTypeId = mp->setPtr->varId;
  305. r->srcTypeLabelStr = typeLabel;
  306. r->dstScLocIdx = mp->setPtr->sectArray[j]->locPtr->index;
  307. r->dstEvtIdx = mp->setPtr->sectArray[j]->begEvtIndex;
  308. r->dstSectLabelStr = cmStringNullGuard(mp->setPtr->sectArray[j]->label);
  309. r->value = mp->value;
  310. r->cost = mp->cost;
  311. }
  312. }
  313. // assert(mp==NULL && k==scnt); if the ending mp->setPtr->sectCnt==0 then this assert will not work correctly even though there would be no data inconsistency
  314. qsort(sarray,scnt,sizeof(sarray[0]),_cmSpMeasSectCompare);
  315. if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  316. {
  317. rc = cmErrMsg(&sp->err,kFileFailSpRC,"Unable to create the output file '%s'.",cmStringNullGuard(outFn));
  318. goto errLabel;
  319. }
  320. cmFilePrintf(fH,"{\n meas : \n[\n[ \"sec\" \"typeLabel\" \"val\" \"cost\" \"loc\" \"evt\" \"seq\" \"mark\" \"typeId\" ]\n");
  321. for(i=0; i<scnt; ++i)
  322. {
  323. _cmSpMeasSect_t* r = sarray + i;
  324. cmFilePrintf(fH,"[ \"%s\" \"%s\" %f %f %i %i %i \"%s\" %i ]\n",
  325. r->dstSectLabelStr,
  326. r->srcTypeLabelStr,
  327. r->value,
  328. r->cost,
  329. r->dstScLocIdx,
  330. r->dstEvtIdx,
  331. r->srcSeqId,
  332. r->srcMarkNameStr,
  333. r->srcTypeId
  334. );
  335. }
  336. /*
  337. mp = sp->list_beg;
  338. for(; mp!=NULL; mp=mp->link)
  339. {
  340. for(i=0; i<mp->setPtr->sectCnt; ++i)
  341. {
  342. cmFilePrintf(fH,"[ %i \"%s\" %i \"%s\" %i %i \"%s\" %f %f ]\n",
  343. mp->markPtr->obj.seqId,
  344. cmStringNullGuard(mp->markPtr->obj.name),
  345. mp->setPtr->varId,
  346. typeLabel,
  347. mp->setPtr->sectArray[i]->locPtr->index,
  348. mp->setPtr->sectArray[i]->begEvtIndex,
  349. cmStringNullGuard(mp->setPtr->sectArray[i]->label),
  350. mp->value,
  351. mp->cost );
  352. }
  353. }
  354. */
  355. cmFilePrintf(fH,"\n]\n}\n");
  356. errLabel:
  357. if( cmFileClose(&fH) != kOkFileRC )
  358. cmErrMsg(&sp->err,kFileFailSpRC,"The output file close failed on '%s'.",cmStringNullGuard(outFn));
  359. return rc;
  360. }
  361. // score matcher callback
  362. void _cmSpMatchMeasCb( cmScMatcher* p, void* arg, cmScMatcherResult_t* rp )
  363. {
  364. _cmSpMeasProc_t* m = (_cmSpMeasProc_t*)arg;
  365. cmScMeas* sm = m->meas;
  366. if( cmScMeasExec(sm, rp->mni, rp->locIdx, rp->scEvtIdx, rp->flags, rp->smpIdx, rp->pitch, rp->vel ) == cmOkRC )
  367. {
  368. unsigned i;
  369. for(i=sm->vsi; i<sm->nsi; ++i)
  370. // ignore set's which did not produce a valid value
  371. if(sm->set[i].value != DBL_MAX )
  372. {
  373. _cmSpMeas_t* r = cmMemAllocZ(_cmSpMeas_t,1);
  374. r->markPtr = m->curMarkPtr;
  375. r->setPtr = sm->set[i].sp;
  376. r->value = sm->set[i].value;
  377. r->cost = sm->set[i].match_cost;
  378. if( m->list_beg == NULL )
  379. {
  380. m->list_beg = r;
  381. m->list_end = r;
  382. }
  383. else
  384. {
  385. m->list_end->link = r;
  386. m->list_end = r;
  387. }
  388. }
  389. }
  390. }
  391. // measurement proc callback
  392. cmSpRC_t _cmSpProcMeasCb( void* arg, cmSp_t* sp, cmScoreProcSelId_t id, cmTlObj_t* tlObjPtr )
  393. {
  394. cmSpRC_t rc = kOkSpRC;
  395. _cmSpMeasProc_t* m = (_cmSpMeasProc_t*)arg;
  396. switch( id )
  397. {
  398. case kBeginTakeSpId:
  399. // reset the performance evaluation object
  400. if( cmScMeasReset(m->meas) != cmOkRC )
  401. rc = cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score performance evaluation object failed on reset.");
  402. m->curMarkPtr = cmTimeLineMarkerObjPtr(sp->tlH,tlObjPtr);
  403. break;
  404. case kNoteOnSpId:
  405. break;
  406. case kEndTakeSpId:
  407. break;
  408. case kFailSpId:
  409. break;
  410. }
  411. return rc;
  412. }
  413. cmSpRC_t _cmScoreProcGenAllMeasurementsMain(cmCtx_t* ctx, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn)
  414. {
  415. //const cmChar_t* rsrcFn = "/home/kevin/.kc/time_line.js";
  416. //const cmChar_t* outFn = "/home/kevin/src/cmkc/src/kc/data/meas0.js";
  417. cmSpRC_t rc = kOkSpRC;
  418. _cmSpMeasProc_t* m = cmMemAllocZ(_cmSpMeasProc_t,1);
  419. cmSp_t s;
  420. cmSp_t* sp = &s;
  421. memset(sp,0,sizeof(s));
  422. cmRptPrintf(&ctx->rpt,"Score Performance Evaluation Start\n");
  423. // initialize the score processor
  424. if((rc = _cmScoreProcInit(ctx,sp,pgmRsrcFn,_cmSpProcMeasCb,_cmSpMatchMeasCb,m)) != kOkSpRC )
  425. goto errLabel;
  426. // allocate the performance evaluation measurement object
  427. m->meas = cmScMeasAlloc( sp->ctx, NULL, sp->scH, sp->srate, sp->dynArray, sp->dynCnt );
  428. m->sp = sp;
  429. // run the score processor
  430. _cmScoreProcProcess(ctx,sp);
  431. // write the results of the performance evaluation
  432. if((rc = _cmScWriteMeasFile(ctx, sp, m, outFn )) != kOkSpRC )
  433. cmErrMsg(&sp->err,kFileFailSpRC,"The measurement output did not complete without errors.");
  434. // free the measurement linked list
  435. _cmSpMeas_t* mp = m->list_beg;
  436. while(mp!=NULL)
  437. {
  438. _cmSpMeas_t* np = mp->link;
  439. cmMemFree(mp);
  440. mp = np;
  441. }
  442. // free the performance evaluation object
  443. if( cmScMeasFree(&m->meas) != cmOkRC )
  444. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The performance evaluation object failed.");
  445. //cmScorePrint(sp.scH,&ctx->rpt);
  446. //cmScorePrintLoc(sp.scH);
  447. errLabel:
  448. _cmScoreProcFinal(sp);
  449. cmMemFree(m);
  450. cmRptPrintf(&ctx->rpt,"Score Proc End\n");
  451. return rc;
  452. }
  453. //==================================================================================================
  454. typedef struct cmSpAssoc_str
  455. {
  456. unsigned scEvtIdx; // score event index
  457. unsigned tlUid; // time-line MIDI note-on object id
  458. struct cmSpAssoc_str* link;
  459. } cmSpAssoc_t;
  460. typedef struct cmSpNoteMap_str
  461. {
  462. unsigned tlUid; // time-line MIDI note-on object id
  463. unsigned mni; // assocated 'mni' returned in a cmScMatcherResult_t record
  464. unsigned muid; // MIDI file msg unique id for this event (see cmMidiTrackMsg_t.uid)
  465. struct cmSpNoteMap_str* link;
  466. } cmSpNoteMap_t;
  467. typedef struct
  468. {
  469. cmCtx_t* ctx;
  470. cmSp_t* sp;
  471. unsigned mni;
  472. bool failFl;
  473. cmJsonH_t jsH;
  474. cmJsonNode_t* takeArray;
  475. cmJsonNode_t* takeObj;
  476. cmJsonNode_t* array;
  477. cmSpAssoc_t* bap;
  478. cmSpAssoc_t* eap;
  479. cmSpNoteMap_t* bmp;
  480. cmSpNoteMap_t* emp;
  481. } cmSpAssocProc_t;
  482. void _cmSpMatchAssocCb( cmScMatcher* p, void* arg, cmScMatcherResult_t* rp )
  483. {
  484. cmSpAssocProc_t* m = (cmSpAssocProc_t*)arg;
  485. if( cmJsonCreateFilledObject(m->jsH, m->array,
  486. "mni", kIntTId, rp->mni,
  487. "muid", kIntTId, rp->muid,
  488. "scEvtIdx", kIntTId, rp->scEvtIdx,
  489. "flags", kIntTId, rp->flags,
  490. NULL ) == NULL )
  491. {
  492. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"JSON association record create failed.");
  493. }
  494. //cmScoreEvt_t* sep = rp->scEvtIdx == -1 ? NULL : cmScoreEvt( m->sp->scH, rp->scEvtIdx );
  495. //printf("%3i loc:%4i pitch=%3i %3i flags=0x%x\n",rp->mni,rp->locIdx,rp->pitch,sep==NULL ? -1 : sep->pitch,rp->flags );
  496. }
  497. cmSpRC_t _cmSpProcAssocCb( void* arg, cmSp_t* sp, cmScoreProcSelId_t id, cmTlObj_t* tlObjPtr )
  498. {
  499. cmSpRC_t rc = kOkSpRC;
  500. cmSpAssocProc_t* m = (cmSpAssocProc_t*)arg;
  501. switch( id )
  502. {
  503. case kBeginTakeSpId:
  504. {
  505. cmTlMarker_t* markPtr = cmTimeLineMarkerObjPtr( sp->tlH, tlObjPtr );
  506. assert( markPtr != NULL );
  507. m->mni = 0;
  508. m->failFl = false;
  509. // insert a take object
  510. if((m->takeObj = cmJsonCreateObject(m->jsH, m->takeArray )) == NULL )
  511. {
  512. rc = cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Take insert failed on seq:%i '%s' : '%s'.", tlObjPtr->seqId, cmStringNullGuard(tlObjPtr->text),cmStringNullGuard(markPtr->text));
  513. goto errLabel;
  514. }
  515. // set the section time-line UID
  516. if( cmJsonInsertPairInt(m->jsH, m->takeObj,"markerUid", tlObjPtr->uid ) != kOkJsRC )
  517. {
  518. rc = cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Marker uid field insert failed on seq:%i '%s' : '%s'.", tlObjPtr->seqId, cmStringNullGuard(tlObjPtr->text),cmStringNullGuard(markPtr->text));
  519. goto errLabel;
  520. }
  521. // create an array to hold the assoc results
  522. if(( m->array = cmJsonInsertPairArray(m->jsH, m->takeObj, "array")) == NULL )
  523. {
  524. rc = cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Marker array field insert failed on seq:%i '%s' : '%s'.", tlObjPtr->seqId, cmStringNullGuard(tlObjPtr->text),cmStringNullGuard(markPtr->text));
  525. goto errLabel;
  526. }
  527. }
  528. break;
  529. case kEndTakeSpId:
  530. {
  531. while( m->bmp != NULL )
  532. {
  533. cmSpNoteMap_t* nmp = m->bmp->link;
  534. cmMemFree(m->bmp);
  535. m->bmp = nmp;
  536. }
  537. m->bmp = NULL;
  538. m->emp = NULL;
  539. if( cmJsonInsertPairInt( m->jsH, m->takeObj, "failFl", m->failFl ) != kOkJsRC )
  540. {
  541. rc = cmErrMsg(&m->ctx->err,kJsonFailSpRC,"JSON fail flag insert failed.");
  542. goto errLabel;
  543. }
  544. }
  545. break;
  546. case kNoteOnSpId:
  547. {
  548. // create a cmSpNoteMap_t record ...
  549. cmSpNoteMap_t* map = cmMemAllocZ(cmSpNoteMap_t,1);
  550. map->tlUid = tlObjPtr->uid;
  551. map->mni = m->mni;
  552. // .. and insert it in the note-map list
  553. if( m->emp == NULL )
  554. {
  555. m->bmp = map;
  556. m->emp = map;
  557. }
  558. else
  559. {
  560. m->emp->link = map;
  561. m->emp = map;
  562. }
  563. m->mni += 1;
  564. }
  565. break;
  566. case kFailSpId:
  567. m->failFl = true;
  568. break;
  569. }
  570. errLabel:
  571. return rc;
  572. }
  573. cmSpRC_t _cmScoreProcGenAssocMain(cmCtx_t* ctx, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn )
  574. {
  575. //const cmChar_t* pgmRsrcFn = "/home/kevin/.kc/time_line.js";
  576. //const cmChar_t* outFn = "/home/kevin/src/cmkc/src/kc/data/takeSeqBldr0.js";
  577. cmSpRC_t rc = kOkSpRC;
  578. cmSpAssocProc_t* m = cmMemAllocZ(cmSpAssocProc_t,1);
  579. cmSp_t s;
  580. cmSp_t* sp = &s;
  581. memset(sp,0,sizeof(s));
  582. m->ctx = ctx;
  583. cmRptPrintf(&ctx->rpt,"Score Association Start\n");
  584. // create a JSON object to hold the results
  585. if( cmJsonInitialize(&m->jsH, ctx ) != kOkJsRC )
  586. {
  587. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Score association JSON output object create failed.");
  588. goto errLabel;
  589. }
  590. // create the JSON root object
  591. if( cmJsonCreateObject(m->jsH, NULL ) == NULL )
  592. {
  593. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Create JSON root object.");
  594. goto errLabel;
  595. }
  596. // initialize the score processor
  597. if((rc = _cmScoreProcInit(ctx,sp,pgmRsrcFn,_cmSpProcAssocCb,_cmSpMatchAssocCb, m)) != kOkSpRC )
  598. goto errLabel;
  599. m->sp = sp;
  600. // store the time-line and score file name
  601. if( cmJsonInsertPairs(m->jsH, cmJsonRoot(m->jsH),
  602. "timeLineFn", kStringTId, cmTimeLineFileName( sp->tlH),
  603. "scoreFn", kStringTId, cmScoreFileName( sp->scH ),
  604. "tlPrefixPath", kStringTId, cmTimeLinePrefixPath( sp->tlH ),
  605. NULL ) != kOkJsRC )
  606. {
  607. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"File name JSON field insertion failed.");
  608. goto errLabel;
  609. }
  610. // create an array to hold each take
  611. if((m->takeArray = cmJsonInsertPairArray(m->jsH, cmJsonRoot(m->jsH), "takeArray" )) == NULL )
  612. {
  613. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"JSON take-array create failed.");
  614. goto errLabel;
  615. }
  616. // run the score processor
  617. _cmScoreProcProcess(ctx,sp);
  618. cmRptPrintf(&ctx->rpt,"Writing results to '%s'.",outFn);
  619. // write the results to a JSON file
  620. if(cmJsonWrite(m->jsH, NULL, outFn ) != kOkJsRC )
  621. {
  622. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"Score association output file write failed.");
  623. goto errLabel;
  624. }
  625. errLabel:
  626. if( cmJsonFinalize(&m->jsH) != kOkJsRC )
  627. {
  628. cmErrMsg(&m->ctx->err,kJsonFailSpRC,"JSON finalize failed.");
  629. }
  630. _cmScoreProcFinal(sp);
  631. cmMemFree(m);
  632. cmRptPrintf(&ctx->rpt,"Score Proc End\n");
  633. return rc;
  634. }
  635. //==================================================================================================
  636. cmSpRC_t cmScoreProc(cmCtx_t* ctx, const cmChar_t* sel, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn)
  637. {
  638. cmSpRC_t rc = kOkSpRC;
  639. if( strcmp(sel,"meas") == 0 )
  640. _cmScoreProcGenAllMeasurementsMain(ctx,pgmRsrcFn,outFn);
  641. else
  642. if( strcmp(sel,"assoc") == 0 )
  643. _cmScoreProcGenAssocMain(ctx,pgmRsrcFn,outFn);
  644. else
  645. cmErrMsg(&ctx->err,kSelectorFailSpRC,"Unknown selector %s.", cmStringNullGuard(sel));
  646. return rc;
  647. }