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

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. }