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.

cmScore.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmMidi.h"
  10. #include "cmLex.h"
  11. #include "cmCsv.h"
  12. #include "cmMidiFile.h"
  13. #include "cmAudioFile.h"
  14. #include "cmTimeLine.h"
  15. #include "cmScore.h"
  16. /*
  17. #include "cmComplexTypes.h"
  18. #include "cmLinkedHeap.h"
  19. #include "cmSymTbl.h"
  20. #include "cmProcObj.h"
  21. #include "cmProc.h"
  22. #include "cmProcTemplate.h"
  23. */
  24. #include "cmVectOpsTemplateMain.h"
  25. cmScH_t cmScNullHandle = cmSTATIC_NULL_HANDLE;
  26. enum
  27. {
  28. kLabelCharCnt = 7,
  29. kInvalidDynScId = 0,
  30. };
  31. enum
  32. {
  33. kTypeLabelColScIdx = 3,
  34. kDSecsColScIdx = 5,
  35. kPitchColScIdx = 11,
  36. kBarColScIdx = 13,
  37. kSkipColScIdx = 14,
  38. kEvenColScIdx = 15,
  39. kTempoColScIdx = 16,
  40. kDynColScIdx = 17
  41. };
  42. typedef struct
  43. {
  44. unsigned id;
  45. cmChar_t label[ kLabelCharCnt + 1 ];
  46. } cmScEvtRef_t;
  47. typedef struct
  48. {
  49. cmErr_t err;
  50. cmScoreEvt_t* array;
  51. unsigned cnt;
  52. cmCsvH_t cH;
  53. } cmSc_t;
  54. cmScEvtRef_t _cmScEvtRefArray[] =
  55. {
  56. { kTimeSigEvtScId, "tsg" },
  57. { kKeySigEvtScId, "ksg" },
  58. { kTempoEvtScId, "tmp" },
  59. { kTrackEvtScId, "trk" },
  60. { kTextEvtScId, "txt" },
  61. { kEOTrackEvtScId, "eot" },
  62. { kCopyEvtScId, "cpy"},
  63. { kBlankEvtScId, "blk"},
  64. { kBarEvtScId, "bar"},
  65. { kPgmEvtScId, "pgm" },
  66. { kCtlEvtScId, "ctl" },
  67. { kNonEvtScId, "non" },
  68. { kInvalidEvtScId, "***" }
  69. };
  70. cmScEvtRef_t _cmScDynRefArray[] =
  71. {
  72. { 1, "pppp" },
  73. { 2, "ppp" },
  74. { 3, "pp" },
  75. { 4, "p" },
  76. { 5, "mp" },
  77. { 6, "m" },
  78. { 7, "mf" },
  79. { 8, "f" },
  80. { 9, "ff" },
  81. { 10, "fff" },
  82. { 11, "ffff"},
  83. { kInvalidDynScId, "***" },
  84. };
  85. cmSc_t* _cmScHandleToPtr( cmScH_t h )
  86. {
  87. cmSc_t* p = (cmSc_t*)h.h;
  88. assert( p != NULL );
  89. return p;
  90. }
  91. unsigned _cmScEvtTypeLabelToId( const cmChar_t* label )
  92. {
  93. cmScEvtRef_t* r = _cmScEvtRefArray;
  94. for(; r->id != kInvalidEvtScId; ++r )
  95. if( strcmp(label,r->label) == 0 )
  96. return r->id;
  97. return kInvalidEvtScId;
  98. }
  99. const cmChar_t* _cmScEvtTypeIdToLabel( unsigned id )
  100. {
  101. cmScEvtRef_t* r = _cmScEvtRefArray;
  102. for(; r->id != kInvalidEvtScId; ++r )
  103. if( r->id == id )
  104. return r->label;
  105. return NULL;
  106. }
  107. unsigned _cmScDynLabelToId( const cmChar_t* label )
  108. {
  109. cmScEvtRef_t* r = _cmScDynRefArray;
  110. for(; r->id != kInvalidEvtScId; ++r )
  111. if( strcmp(label,r->label) == 0 )
  112. return r->id;
  113. return kInvalidDynScId;
  114. }
  115. const cmChar_t* _cmScDynIdToLabel( unsigned id )
  116. {
  117. cmScEvtRef_t* r = _cmScDynRefArray;
  118. for(; r->id != kInvalidDynScId; ++r )
  119. if( r->id == id )
  120. return r->label;
  121. return NULL;
  122. }
  123. unsigned _cmScLexSciPitchMatcher( const cmChar_t* cp, unsigned cn )
  124. {
  125. // first char must be "A-G"
  126. if( strspn(cp,"ABCDEFG") != 1 )
  127. return 0;
  128. unsigned i = 1;
  129. // next char could be accidental
  130. if( cp[i] == '#' || cp[i] == 'b' )
  131. ++i; // i==2
  132. // the 2nd or 3rd char must be a digit
  133. if( isdigit(cp[i]) == false )
  134. return 0;
  135. ++i; // i==2 or i==3
  136. // the 3rd or 4th char must be a digit or EOS
  137. if( isdigit(cp[i]) == false )
  138. return i;
  139. ++i;
  140. return i;
  141. }
  142. cmScRC_t _cmScFinalize( cmSc_t* p )
  143. {
  144. cmScRC_t rc = kOkScRC;
  145. if( cmCsvFinalize(&p->cH) != kOkCsvRC )
  146. return rc;
  147. cmMemFree(p->array);
  148. cmMemFree(p);
  149. return rc;
  150. }
  151. cmScRC_t _cmScParseBar( cmSc_t* p, unsigned rowIdx, int* barNumb )
  152. {
  153. if((*barNumb = cmCsvCellInt(p->cH,rowIdx,kBarColScIdx)) == INT_MAX )
  154. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to parse the bar number.");
  155. return kOkScRC;
  156. }
  157. cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barNumb, unsigned barNoteIdx )
  158. {
  159. cmScRC_t rc = kOkScRC;
  160. unsigned flags = 0;
  161. unsigned dynVal = kInvalidDynScId;
  162. const cmChar_t* sciPitch;
  163. cmMidiByte_t midiPitch;
  164. const cmChar_t* attr;
  165. double dsecs;
  166. if((sciPitch = cmCsvCellText(p->cH,rowIdx,kPitchColScIdx)) == NULL )
  167. return cmErrMsg(&p->err,kSyntaxErrScRC,"Expected a scientific pitch value");
  168. if((midiPitch = cmSciPitchToMidi(sciPitch)) == kInvalidMidiPitch)
  169. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to convert the scientific pitch '%s' to a MIDI value. ");
  170. // it is possible that note delta-secs field is empty - so default to 0
  171. if((dsecs = cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
  172. dsecs = 0;
  173. if((attr = cmCsvCellText(p->cH,rowIdx,kSkipColScIdx)) != NULL && *attr == 's' )
  174. flags += kSkipScFl;
  175. if((attr = cmCsvCellText(p->cH,rowIdx,kEvenColScIdx)) != NULL && *attr == 'e' )
  176. flags += kEvenScFl;
  177. if((attr = cmCsvCellText(p->cH,rowIdx,kTempoColScIdx)) != NULL && *attr == 't' )
  178. flags += kTempoScFl;
  179. if((attr = cmCsvCellText(p->cH,rowIdx,kDynColScIdx)) != NULL )
  180. {
  181. if((dynVal = _cmScDynLabelToId(attr)) == kInvalidDynScId )
  182. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown dynamic label '%s'.",cmStringNullGuard(attr));
  183. flags += kDynScFl;
  184. }
  185. s->type = kNonEvtScId;
  186. s->pitch = midiPitch;
  187. s->flags = flags;
  188. s->dynVal = dynVal;
  189. s->barNumb = barNumb;
  190. s->barNoteIdx = barNoteIdx;
  191. return rc;
  192. }
  193. cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
  194. {
  195. cmScRC_t rc = kOkScRC;
  196. unsigned barNoteIdx;
  197. int barNumb;
  198. if( cmCsvInitialize(&p->cH, ctx ) != kOkCsvRC )
  199. {
  200. rc = cmErrMsg(&p->err,kCsvFailScRC,"Score file initialization failed.");
  201. goto errLabel;
  202. }
  203. if( cmCsvLexRegisterMatcher(p->cH, cmCsvLexNextAvailId(p->cH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
  204. {
  205. rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV token matcher registration failed.");
  206. goto errLabel;
  207. }
  208. if( cmCsvParseFile(p->cH, fn, 0 ) != kOkCsvRC )
  209. {
  210. rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV file parsing failed on the file '%s'.",cmStringNullGuard(fn));
  211. goto errLabel;
  212. }
  213. p->cnt = cmCsvRowCount(p->cH);
  214. p->array = cmMemAllocZ(cmScoreEvt_t,p->cnt);
  215. unsigned i,j;
  216. // skip labels line - start on line 1
  217. for(i=1,j=0; i<p->cnt && rc==kOkScRC; ++i)
  218. {
  219. // get the row 'type' label
  220. const char* typeLabel;
  221. if((typeLabel = cmCsvCellText(p->cH,i,kTypeLabelColScIdx)) == NULL )
  222. {
  223. rc = cmErrMsg(&p->err,kSyntaxErrScRC,"No type label.");
  224. break;
  225. }
  226. // convert the row 'type' label to an id
  227. unsigned tid;
  228. if((tid = _cmScEvtTypeLabelToId(typeLabel)) == kInvalidEvtScId)
  229. {
  230. rc = cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown type '%s'.",cmStringNullGuard(typeLabel));
  231. break;
  232. }
  233. switch(tid)
  234. {
  235. case kBarEvtScId:
  236. // parse bar lines
  237. if((rc = _cmScParseBar(p,i,&barNumb)) == kOkScRC )
  238. barNoteIdx = 0;
  239. break;
  240. case kNonEvtScId:
  241. // parse note-on events
  242. if((rc = _cmScParseNoteOn(p, i, p->array + j, barNumb, barNoteIdx )) == kOkScRC )
  243. {
  244. if( cmIsFlag(p->array[j].flags,kSkipScFl) == false )
  245. ++j;
  246. ++barNoteIdx;
  247. }
  248. break;
  249. default:
  250. break;
  251. }
  252. }
  253. if( rc == kSyntaxErrScRC )
  254. {
  255. cmErrMsg(&p->err,rc,"Syntax error on line %i in '%s'.",i+1,cmStringNullGuard(fn));
  256. goto errLabel;
  257. }
  258. p->cnt = i;
  259. errLabel:
  260. return rc;
  261. }
  262. cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn )
  263. {
  264. cmScRC_t rc = kOkScRC;
  265. if((rc = cmScoreFinalize(hp)) != kOkScRC )
  266. return rc;
  267. cmSc_t* p = cmMemAllocZ(cmSc_t,1);
  268. cmErrSetup(&p->err,&ctx->rpt,"Score");
  269. if((rc = _cmScParseFile(p,ctx,fn)) != kOkScRC )
  270. goto errLabel;
  271. hp->h = p;
  272. errLabel:
  273. if( rc != kOkScRC )
  274. _cmScFinalize(p);
  275. return rc;
  276. }
  277. cmScRC_t cmScoreFinalize( cmScH_t* hp )
  278. {
  279. cmScRC_t rc = kOkScRC;
  280. if( hp == NULL || cmScoreIsValid(*hp) == false )
  281. return kOkScRC;
  282. cmSc_t* p = _cmScHandleToPtr(*hp);
  283. if((rc = _cmScFinalize(p)) != kOkScRC )
  284. return rc;
  285. hp->h = NULL;
  286. return rc;
  287. }
  288. bool cmScoreIsValid( cmScH_t h )
  289. { return h.h != NULL; }
  290. unsigned cmScoreEvtCount( cmScH_t h )
  291. {
  292. cmSc_t* p = _cmScHandleToPtr(h);
  293. return p->cnt;
  294. }
  295. cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx )
  296. {
  297. cmSc_t* p = _cmScHandleToPtr(h);
  298. if( idx >= p->cnt )
  299. {
  300. cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i records.",idx,p->cnt);
  301. return NULL;
  302. }
  303. return p->array + idx;
  304. }
  305. void cmScorePrint( cmScH_t h, cmRpt_t* rpt )
  306. {
  307. cmSc_t* p = _cmScHandleToPtr(h);
  308. unsigned i;
  309. for(i=0; i<20 /*p->cnt*/; ++i)
  310. {
  311. cmScoreEvt_t* r = p->array + i;
  312. switch(r->type)
  313. {
  314. case kNonEvtScId:
  315. cmRptPrintf(rpt,"%5i %3i %3i %s 0x%2x %c%c%c %s\n",
  316. i,
  317. r->barNumb,
  318. r->barNoteIdx,
  319. _cmScEvtTypeIdToLabel(r->type),
  320. r->pitch,
  321. cmIsFlag(r->flags,kEvenScFl) ? 'e' : ' ',
  322. cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ',
  323. cmIsFlag(r->flags,kDynScFl) ? 'd' : ' ',
  324. cmIsFlag(r->flags,kDynScFl) ? _cmScDynIdToLabel(r->dynVal) : "");
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. }
  331. // Each time line note-on object is decorated (via cmTlObj_t.userDataPtr) with a
  332. // cmScSyncState_t record.
  333. typedef struct
  334. {
  335. unsigned cnt; // count of candidate sync locations
  336. double dist; // edit distance to the closest sync location
  337. unsigned scEvtIdx; // score record this note-on is assigned to
  338. } cmScSyncState_t;
  339. void _cmScSyncTimeLineAllocFree( cmTlH_t tlH, bool allocFl )
  340. {
  341. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  342. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
  343. if( mep->msg->status == kNoteOnMdId )
  344. {
  345. if( allocFl )
  346. mep->obj.userDataPtr = cmMemAllocZ(cmScSyncState_t,1);
  347. else
  348. cmMemPtrFree(&mep->obj.userDataPtr);
  349. }
  350. }
  351. void _cmScPrintSyncState( cmSc_t* p, cmTlH_t tlH )
  352. {
  353. unsigned i = 0;
  354. double sr = cmTimeLineSampleRate(tlH);
  355. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  356. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
  357. if( mep->msg->status == kNoteOnMdId )
  358. {
  359. cmScSyncState_t* ssp = (cmScSyncState_t*)mep->obj.userDataPtr;
  360. cmRptPrintf(p->err.rpt,"%5.3f pit:0x%2x (%3i) bar:%3i bni:%3i cnt:%3i dst:%1.6f ref:%s\n",
  361. (mep->obj.ref->begSmpIdx - mep->obj.begSmpIdx) / (sr*60),
  362. mep->msg->u.chMsgPtr->d0,
  363. mep->msg->u.chMsgPtr->d0,
  364. ssp->cnt ? p->array[ ssp->scEvtIdx ].barNumb : 0,
  365. ssp->cnt ? p->array[ ssp->scEvtIdx ].barNoteIdx : 0,
  366. ssp->cnt,
  367. ssp->dist,
  368. cmStringNullGuard(mep->obj.ref->name));
  369. ++i;
  370. if( i>=300)
  371. break;
  372. }
  373. }
  374. double _cmScWndEditDist( cmSc_t* p, unsigned* mtx, const unsigned* tlWnd, cmScSyncState_t* tlObjWnd[], unsigned wndCnt )
  375. {
  376. unsigned scWnd[ wndCnt ];
  377. unsigned scIdxWnd[ wndCnt ];
  378. unsigned i;
  379. unsigned wn = 0;
  380. double minDist = DBL_MAX;
  381. // for each note-on score event
  382. for(i=0; i<p->cnt; ++i)
  383. if( p->array[i].type == kNonEvtScId )
  384. {
  385. // shift the score event window to the the left
  386. memmove(scWnd, scWnd+1, (wndCnt-1)*sizeof(scWnd[0]));
  387. memmove(scIdxWnd,scIdxWnd+1,(wndCnt-1)*sizeof(scIdxWnd[0]));
  388. // insert new score event data on right
  389. scWnd[wndCnt-1] = p->array[i].pitch;
  390. scIdxWnd[wndCnt-1] = i;
  391. ++wn;
  392. // if the window is full
  393. if(wn >= wndCnt )
  394. {
  395. // score the edit distance between the time line window and the edit window
  396. double dist = cmVOU_LevEditDist(wndCnt,mtx,scWnd,wndCnt,tlWnd,wndCnt,wndCnt);
  397. if( dist < minDist )
  398. minDist = dist;
  399. // update the match information in the time line window
  400. unsigned j;
  401. for(j=0; j<wndCnt; ++j)
  402. {
  403. // if the pitch matches and the score is less than the previous score
  404. if( scWnd[j] == tlWnd[j] && (tlObjWnd[j]->cnt == 0 || dist < tlObjWnd[j]->dist) )
  405. {
  406. tlObjWnd[j]->cnt += 1;
  407. tlObjWnd[j]->dist = dist;
  408. tlObjWnd[j]->scEvtIdx = scIdxWnd[j];
  409. }
  410. }
  411. }
  412. }
  413. return minDist;
  414. }
  415. cmScRC_t cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned edWndCnt, cmReal_t maxSecs )
  416. {
  417. cmSc_t* p = _cmScHandleToPtr(scH);
  418. unsigned* edWndMtx = cmVOU_LevEditDistAllocMtx(edWndCnt);
  419. unsigned maxMicroSecs = floor(maxSecs*1000000);
  420. unsigned edWndData[ edWndCnt ];
  421. cmScSyncState_t* edWndObj[ edWndCnt ];
  422. // alloc a sync state record for each MIDI note-on in the time line
  423. _cmScSyncTimeLineAllocFree(tlH, true );
  424. // get the first time line object
  425. cmTlObj_t* rfp = cmTimeLineNextTypeObj(tlH,NULL,cmInvalidId,kMidiFileTlId);
  426. // interate through the time line in search of MIDI file objects
  427. for(; rfp != NULL; rfp = cmTimeLineNextTypeObj(tlH,rfp,cmInvalidId,kMidiFileTlId))
  428. {
  429. cmTlMidiFile_t* mfp = cmTimeLineMidiFileObjPtr(tlH,rfp);
  430. unsigned curEdWndCnt = 0;
  431. double prog = 0.1;
  432. unsigned progIdx = 0;
  433. cmRptPrintf(p->err.rpt,"MIDI File:%s\n", cmMidiFileName( mfp->h ));
  434. // get first midi event object
  435. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  436. // iterate through the time line in search of MIDI note-on events with belong to mfp
  437. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId) )
  438. {
  439. if( mep->obj.ref == rfp && mep->msg->status == kNoteOnMdId )
  440. {
  441. // If this notes inter-onset time is greater than maxMicroSecs
  442. // then dispose of the current window and begin refilling it again.
  443. if( mep->msg->dtick > maxMicroSecs )
  444. curEdWndCnt = 0;
  445. // shift window one slot to left
  446. unsigned i;
  447. for(i=0; i<edWndCnt-1; ++i)
  448. {
  449. edWndData[i] = edWndData[i+1];
  450. edWndObj[i] = edWndObj[i+1];
  451. }
  452. // fill window on right
  453. edWndData[edWndCnt-1] = mep->msg->u.chMsgPtr->d0; // d0=pitch
  454. edWndObj[ edWndCnt-1] = (cmScSyncState_t*)mep->obj.userDataPtr;
  455. ++curEdWndCnt;
  456. // if a complete window exists then update the time-line / score match state
  457. if( curEdWndCnt >= edWndCnt )
  458. _cmScWndEditDist( p, edWndMtx, edWndData, edWndObj, edWndCnt );
  459. // print the progress
  460. ++progIdx;
  461. if( progIdx >= prog * mfp->noteOnCnt )
  462. {
  463. cmRptPrintf(p->err.rpt,"%i ",(unsigned)round(prog*10));
  464. prog += 0.1;
  465. }
  466. }
  467. }
  468. cmRptPrintf(p->err.rpt,"\n");
  469. }
  470. _cmScPrintSyncState(p,tlH );
  471. // free sync state records
  472. _cmScSyncTimeLineAllocFree(tlH,false);
  473. cmMemFree(edWndMtx);
  474. return kOkScRC;
  475. }
  476. cmScRC_t cmScoreSyncTimeLineTest( cmCtx_t* ctx, const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn )
  477. {
  478. cmScRC_t rc = kOkScRC;
  479. cmTlH_t tlH = cmTimeLineNullHandle;
  480. cmScH_t scH = cmScNullHandle;
  481. unsigned edWndCnt = 7;
  482. cmReal_t maxSecs = 2.0;
  483. if((rc = cmTimeLineInitialize(ctx,&tlH,NULL,NULL)) != kOkTlRC )
  484. return cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line initialization failed.");;
  485. if((rc = cmTimeLineReadJson(tlH,timeLineJsFn)) != kOkTlRC )
  486. {
  487. rc = cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line parse failed.");;
  488. goto errLabel;
  489. }
  490. //cmTimeLinePrint(tlH,&ctx->rpt);
  491. if(1)
  492. {
  493. if((rc = cmScoreInitialize(ctx,&scH,scoreCsvFn)) != kOkScRC )
  494. goto errLabel;
  495. rc = cmScoreSyncTimeLine(scH, tlH, edWndCnt, maxSecs );
  496. }
  497. //cmScorePrint(scH, ctx->err.rpt );
  498. errLabel:
  499. cmScoreFinalize(&scH);
  500. cmTimeLineFinalize(&tlH);
  501. return rc;
  502. }
  503. void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
  504. {
  505. cmScH_t h = cmScNullHandle;
  506. if( cmScoreInitialize(ctx,&h,fn) != kOkScRC )
  507. return;
  508. cmScorePrint(h,&ctx->rpt);
  509. cmScoreFinalize(&h);
  510. }