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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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. enum
  22. {
  23. kOkSpRC,
  24. kJsonFailSpRC,
  25. kScoreFailSpRC,
  26. kTimeLineFailSpRC,
  27. kScoreMatchFailSpRC,
  28. kFileFailSpRC,
  29. };
  30. typedef struct _cmScMeas_t
  31. {
  32. cmTlMarker_t* markPtr; // time-line marker in which this 'set' exists
  33. cmScoreSet_t* setPtr; // score set on which this measurment is based
  34. double value; // the value of the measurement
  35. double cost; // the quality of the perf->score match
  36. struct _cmScMeas_t* link;
  37. } _cmScMeas_t;
  38. typedef struct
  39. {
  40. cmErr_t err;
  41. cmCtx* ctx;
  42. cmScH_t scH;
  43. const cmChar_t* tlFn;
  44. cmTlH_t tlH;
  45. cmJsonH_t jsH;
  46. unsigned* dynArray;
  47. unsigned dynCnt;
  48. double srate;
  49. cmScMeas* meas;
  50. cmScMatcher* match;
  51. cmTlMarker_t* curMarkPtr;
  52. _cmScMeas_t* list_beg;
  53. _cmScMeas_t* list_end;
  54. _cmScMeas_t* slist_beg;
  55. } cmSp_t;
  56. // read the dynamics reference array from the time-line project file.
  57. cmSpRC_t _cmJsonReadDynArray( cmJsonH_t jsH, unsigned** dynArray, unsigned* dynCnt )
  58. {
  59. cmJsonNode_t* np;
  60. int i;
  61. if( cmJsonPathToArray(jsH, NULL, NULL, "dynRef", &np ) != kOkJsRC )
  62. return kJsonFailSpRC;
  63. *dynCnt = cmJsonChildCount(np);
  64. *dynArray = cmMemAllocZ(unsigned,*dynCnt);
  65. for(i=0; i<*dynCnt; ++i)
  66. if( cmJsonUIntValue( cmJsonArrayElement(np,i), (*dynArray)+i ) != kOkJsRC )
  67. return kJsonFailSpRC;
  68. return kOkSpRC;
  69. }
  70. cmSpRC_t _cmScoreProcInit( cmCtx_t* ctx, cmSp_t* p, const cmChar_t* rsrcFn )
  71. {
  72. cmSpRC_t rc = kOkSpRC;
  73. const cmChar_t* scFn = NULL;
  74. const cmChar_t* tlFn = NULL;
  75. const cmChar_t* tlPrefixPath = NULL;
  76. p->srate = 96000;
  77. cmErrSetup(&p->err,&ctx->rpt,"ScoreProc");
  78. // open the resource file
  79. if( cmJsonInitializeFromFile( &p->jsH, rsrcFn, ctx ) != kOkJsRC )
  80. {
  81. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to load the main resource file:%s.",cmStringNullGuard(rsrcFn));
  82. goto errLabel;
  83. }
  84. // get the time line fn
  85. if( cmJsonPathToString( p->jsH, NULL, NULL, "timeLineFn", &tlFn ) != kOkJsRC )
  86. {
  87. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the time line file name in the main resource file:%s",cmStringNullGuard(rsrcFn));
  88. goto errLabel;
  89. }
  90. // get the score file name
  91. if( cmJsonPathToString( p->jsH, NULL, NULL, "scoreFn", &scFn ) != kOkJsRC )
  92. {
  93. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the score file name in the main resource file:%s",cmStringNullGuard(rsrcFn));
  94. goto errLabel;
  95. }
  96. // get the time line data file prefix path
  97. if( cmJsonPathToString( p->jsH, NULL, NULL, "tlPrefixPath", &tlPrefixPath ) != kOkJsRC )
  98. {
  99. rc = cmErrMsg(&p->err,kJsonFailSpRC,"Unable to locate the time line data file prefix path in the main resource file:%s",cmStringNullGuard(rsrcFn));
  100. goto errLabel;
  101. }
  102. // read the dynamics reference array
  103. if((rc = _cmJsonReadDynArray( p->jsH, &p->dynArray, &p->dynCnt )) != kOkSpRC )
  104. {
  105. rc = cmErrMsg(&p->err,rc,"Unable to read dynamics reference array resource from the main resource file:%s",cmStringNullGuard(rsrcFn));
  106. goto errLabel;
  107. }
  108. // load the score file
  109. if( cmScoreInitialize(ctx, &p->scH, scFn, p->srate, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  110. {
  111. rc = cmErrMsg(&p->err,kScoreFailSpRC,"Score load failed for score file:%s.",cmStringNullGuard(scFn));
  112. goto errLabel;
  113. }
  114. // load the time-line file
  115. if( cmTimeLineInitializeFromFile(ctx, &p->tlH, NULL, NULL, tlFn, tlPrefixPath ) != kOkTlRC )
  116. {
  117. rc = cmErrMsg(&p->err,kTimeLineFailSpRC,"Time line load failed for time line file:%s.",cmStringNullGuard(tlFn));
  118. goto errLabel;
  119. }
  120. p->ctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  121. errLabel:
  122. return rc;
  123. }
  124. cmSpRC_t _cmScoreProcFinal( cmSp_t* p )
  125. {
  126. cmSpRC_t rc = kOkSpRC;
  127. cmCtxFree(&p->ctx);
  128. if( cmScoreFinalize(&p->scH) != kOkScRC )
  129. cmErrMsg(&p->err,kScoreFailSpRC,"Score finalize failed.");
  130. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  131. cmErrMsg(&p->err,kTimeLineFailSpRC,"Time line finalize failed.");
  132. if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  133. cmErrMsg(&p->err,kJsonFailSpRC,"JSON finalize failed.");
  134. cmMemFree(p->dynArray);
  135. return rc;
  136. }
  137. unsigned _cmScMeasSectCount( cmSp_t* sp )
  138. {
  139. const _cmScMeas_t* mp = sp->list_beg;
  140. unsigned n = 0;
  141. for(; mp != NULL; mp=mp->link)
  142. n += mp->setPtr->sectCnt;
  143. return n;
  144. }
  145. typedef struct
  146. {
  147. unsigned srcSeqId;
  148. const cmChar_t* srcMarkNameStr;
  149. unsigned srcTypeId;
  150. const cmChar_t* srcTypeLabelStr;
  151. unsigned dstScLocIdx;
  152. unsigned dstEvtIdx;
  153. const cmChar_t* dstSectLabelStr;
  154. double value;
  155. double cost;
  156. } _cmScMeasSect_t;
  157. int _cmScMeasSectCompare( const void* p0, const void* p1 )
  158. {
  159. _cmScMeasSect_t* m0 = (_cmScMeasSect_t*)p0;
  160. _cmScMeasSect_t* m1 = (_cmScMeasSect_t*)p1;
  161. return (int)m0->dstScLocIdx - (int)m1->dstScLocIdx;
  162. }
  163. cmSpRC_t _cmScWriteMeasFile( cmCtx_t* ctx, cmSp_t* sp, const cmChar_t* outFn )
  164. {
  165. cmFileH_t fH = cmFileNullHandle;
  166. cmSpRC_t rc = kOkSpRC;
  167. unsigned i,j,k;
  168. _cmScMeas_t* mp = sp->list_beg;
  169. unsigned scnt = _cmScMeasSectCount(sp);
  170. _cmScMeasSect_t sarray[ scnt ];
  171. for(i=0,k=0; k<scnt && mp!=NULL; ++i,mp=mp->link)
  172. {
  173. const cmChar_t* typeLabel = NULL;
  174. switch(mp->setPtr->varId)
  175. {
  176. case kEvenVarScId: typeLabel="even"; break;
  177. case kDynVarScId: typeLabel="dyn"; break;
  178. case kTempoVarScId:typeLabel="tempo";break;
  179. default:
  180. { assert(0); }
  181. }
  182. for(j=0; j<mp->setPtr->sectCnt; ++j,++k)
  183. {
  184. _cmScMeasSect_t* r = sarray + k;
  185. r->srcSeqId = mp->markPtr->obj.seqId,
  186. r->srcMarkNameStr = cmStringNullGuard(mp->markPtr->obj.name),
  187. r->srcTypeId = mp->setPtr->varId,
  188. r->srcTypeLabelStr = typeLabel,
  189. r->dstScLocIdx = mp->setPtr->sectArray[j]->locPtr->index,
  190. r->dstEvtIdx = mp->setPtr->sectArray[j]->begEvtIndex,
  191. r->dstSectLabelStr = cmStringNullGuard(mp->setPtr->sectArray[j]->label),
  192. r->value = mp->value,
  193. r->cost = mp->cost;
  194. }
  195. }
  196. assert(mp==NULL && k==scnt);
  197. qsort(sarray,scnt,sizeof(sarray[0]),_cmScMeasSectCompare);
  198. if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  199. {
  200. rc = cmErrMsg(&sp->err,kFileFailSpRC,"Unable to create the output file '%s'.",cmStringNullGuard(outFn));
  201. goto errLabel;
  202. }
  203. cmFilePrintf(fH,"{\n meas : \n[\n[ \"sec\" \"typeLabel\" \"val\" \"cost\" \"loc\" \"evt\" \"seq\" \"mark\" \"typeId\" ]\n");
  204. for(i=0; i<scnt; ++i)
  205. {
  206. _cmScMeasSect_t* r = sarray + i;
  207. cmFilePrintf(fH,"[ \"%s\" \"%s\" %f %f %i %i %i \"%s\" %i ]\n",
  208. r->dstSectLabelStr,
  209. r->srcTypeLabelStr,
  210. r->value,
  211. r->cost,
  212. r->dstScLocIdx,
  213. r->dstEvtIdx,
  214. r->srcSeqId,
  215. r->srcMarkNameStr,
  216. r->srcTypeId
  217. );
  218. }
  219. /*
  220. mp = sp->list_beg;
  221. for(; mp!=NULL; mp=mp->link)
  222. {
  223. for(i=0; i<mp->setPtr->sectCnt; ++i)
  224. {
  225. cmFilePrintf(fH,"[ %i \"%s\" %i \"%s\" %i %i \"%s\" %f %f ]\n",
  226. mp->markPtr->obj.seqId,
  227. cmStringNullGuard(mp->markPtr->obj.name),
  228. mp->setPtr->varId,
  229. typeLabel,
  230. mp->setPtr->sectArray[i]->locPtr->index,
  231. mp->setPtr->sectArray[i]->begEvtIndex,
  232. cmStringNullGuard(mp->setPtr->sectArray[i]->label),
  233. mp->value,
  234. mp->cost );
  235. }
  236. }
  237. */
  238. cmFilePrintf(fH,"\n]\n}\n");
  239. errLabel:
  240. if( cmFileClose(&fH) != kOkFileRC )
  241. cmErrMsg(&sp->err,kFileFailSpRC,"The output file close failed on '%s'.",cmStringNullGuard(outFn));
  242. return rc;
  243. }
  244. void _cmScMatchCb( cmScMatcher* p, void* arg, cmScMatcherResult_t* rp )
  245. {
  246. cmSp_t* sp = (cmSp_t*)arg;
  247. cmScMeas* sm = sp->meas;
  248. if( cmScMeasExec(sm, rp->mni, rp->locIdx, rp->scEvtIdx, rp->flags, rp->smpIdx, rp->pitch, rp->vel ) == cmOkRC )
  249. {
  250. unsigned i;
  251. for(i=sm->vsi; i<sm->nsi; ++i)
  252. // ignore set's which did not produce a valid value
  253. if(sm->set[i].value != DBL_MAX )
  254. {
  255. _cmScMeas_t* r = cmMemAllocZ(_cmScMeas_t,1);
  256. r->markPtr = sp->curMarkPtr;
  257. r->setPtr = sm->set[i].sp;
  258. r->value = sm->set[i].value;
  259. r->cost = sm->set[i].match_cost;
  260. if( sp->list_beg == NULL )
  261. {
  262. sp->list_beg = r;
  263. sp->list_end = r;
  264. }
  265. else
  266. {
  267. sp->list_end->link = r;
  268. sp->list_end = r;
  269. }
  270. }
  271. }
  272. }
  273. cmSpRC_t _cmScoreGenAllMeasurements(cmCtx_t* ctx, cmSp_t* sp, const cmChar_t* outFn)
  274. {
  275. cmSpRC_t rc = kOkSpRC;
  276. unsigned midiN = 7;
  277. unsigned scWndN = 10;
  278. unsigned seqN = cmTimeLineSeqCount(sp->tlH);
  279. double srate = cmTimeLineSampleRate(sp->tlH);
  280. unsigned seqId;
  281. assert( sp->srate == srate);
  282. // allocate the performance eval. object
  283. sp->meas = cmScMeasAlloc( sp->ctx, NULL, sp->scH, sp->srate, sp->dynArray, sp->dynCnt );
  284. assert( sp->meas != NULL );
  285. // allocate the score matcher
  286. sp->match = cmScMatcherAlloc(sp->ctx,NULL,sp->srate,sp->scH,scWndN,midiN,_cmScMatchCb,sp);
  287. assert(sp->match != NULL );
  288. // for each time line sequence
  289. for(seqId=0; seqId<seqN; ++seqId)
  290. {
  291. cmTlObj_t* o0p = NULL;
  292. // for each 'marker' in this time line sequence
  293. while( (o0p = cmTimeLineNextTypeObj(sp->tlH, o0p, seqId, kMarkerTlId)) != NULL )
  294. {
  295. // get the 'marker' recd
  296. cmTlMarker_t* markPtr = cmTimeLineMarkerObjPtr(sp->tlH,o0p);
  297. assert( markPtr != NULL );
  298. // if the marker does not have a valid start bar location
  299. if( markPtr->bar == 0 )
  300. continue;
  301. // set the marker ptr (which is used in _cmScMatchCb())
  302. sp->curMarkPtr = markPtr;
  303. // get the end-of-marker time as a sample index
  304. unsigned markEndSmpIdx = markPtr->obj.seqSmpIdx + markPtr->obj.durSmpCnt;
  305. // get the score event associated with the marker's bar number.
  306. const cmScoreEvt_t* evtPtr = cmScoreBarEvt(sp->scH,markPtr->bar);
  307. assert( evtPtr != NULL );
  308. // get the score location associated with the markers bar score event
  309. const cmScoreLoc_t* locPtr = cmScoreEvtLoc(sp->scH,evtPtr);
  310. assert( locPtr != NULL );
  311. cmRptPrintf(&ctx->rpt,"Processing loc:%i seq:%i %s %s\n",locPtr->index,seqId,cmStringNullGuard(markPtr->obj.name),cmStringNullGuard(markPtr->text));
  312. // reset the performance evaluation object
  313. if( cmScMeasReset(sp->meas) != cmOkRC )
  314. {
  315. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score performance evaluation object failed on reset.");
  316. continue;
  317. }
  318. // reset the score matcher to begin searching at the bar location
  319. if( cmScMatcherReset(sp->match, locPtr->index ) != cmOkRC )
  320. {
  321. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher reset failed on location: %i.",locPtr->index);
  322. continue;
  323. }
  324. cmTlObj_t* o1p = o0p;
  325. // as long as more MIDI events are available get the next MIDI msg
  326. while( (rc == kOkSpRC) && (o1p = cmTimeLineNextTypeObj(sp->tlH, o1p, seqId, kMidiEvtTlId )) != NULL )
  327. {
  328. cmTlMidiEvt_t* mep = cmTimeLineMidiEvtObjPtr(sp->tlH,o1p);
  329. assert(mep != NULL );
  330. // if the msg falls after the end of the marker then we are done
  331. if( mep->obj.seqSmpIdx != cmInvalidIdx && mep->obj.seqSmpIdx > markEndSmpIdx )
  332. break;
  333. // if the time line MIDI msg a note-on
  334. if( mep->msg->status == kNoteOnMdId )
  335. {
  336. cmRC_t cmRC = cmScMatcherExec(sp->match, mep->obj.seqSmpIdx, mep->msg->status, mep->msg->u.chMsgPtr->d0, mep->msg->u.chMsgPtr->d1, NULL );
  337. switch( cmRC )
  338. {
  339. case cmOkRC: // continue processing MIDI events
  340. break;
  341. case cmEofRC: // end of the score was encountered
  342. break;
  343. case cmInvalidArgRC: // p->eli was not set correctly
  344. rc = cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher failed due to an invalid argument.");
  345. goto errLabel;
  346. break;
  347. case cmSubSysFailRC: // scan resync failed
  348. rc = cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher failed on resync.");
  349. cmScMatcherPrint(sp->match);
  350. //goto errLabel;
  351. break;
  352. default:
  353. { assert(0); }
  354. }
  355. }
  356. }
  357. rc = kOkSpRC;
  358. }
  359. }
  360. errLabel:
  361. if((rc = _cmScWriteMeasFile(ctx, sp, outFn )) != kOkSpRC )
  362. cmErrMsg(&sp->err,kFileFailSpRC,"The measurement output did not complete without errors.");
  363. _cmScMeas_t* mp = sp->list_beg;
  364. while(mp!=NULL)
  365. {
  366. _cmScMeas_t* np = mp->link;
  367. cmMemFree(mp);
  368. mp = np;
  369. }
  370. if( cmScMatcherFree(&sp->match) != cmOkRC )
  371. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The score matcher release failed.");
  372. if( cmScMeasFree(&sp->meas) != cmOkRC )
  373. cmErrMsg(&sp->err,kScoreMatchFailSpRC,"The performance evaluation object failed.");
  374. return rc;
  375. }
  376. unsigned cmScoreProc(cmCtx_t* ctx)
  377. {
  378. cmSpRC_t rc = kOkSpRC;
  379. const cmChar_t* rsrcFn = "/home/kevin/.kc/time_line.js";
  380. const cmChar_t* outFn = "/home/kevin/src/cmkc/src/kc/data/meas0.js";
  381. cmSp_t sp;
  382. memset(&sp,0,sizeof(sp));
  383. cmRptPrintf(&ctx->rpt,"Score Proc Start\n");
  384. if((rc = _cmScoreProcInit(ctx,&sp,rsrcFn)) != kOkSpRC )
  385. goto errLabel;
  386. _cmScoreGenAllMeasurements(ctx,&sp,outFn);
  387. //cmScorePrint(sp.scH,&ctx->rpt);
  388. //cmScorePrintLoc(sp.scH);
  389. errLabel:
  390. _cmScoreProcFinal(&sp);
  391. cmRptPrintf(&ctx->rpt,"Score Proc End\n");
  392. return rc;
  393. }