libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmScore.c 17KB

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