libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmScoreProc.c 24KB

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