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.

cmScore.c 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  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. kMidiFileIdColScIdx= 0,
  34. kTypeLabelColScIdx = 3,
  35. kDSecsColScIdx = 4,
  36. kSecsColScIdx = 5,
  37. kPitchColScIdx = 11,
  38. kBarColScIdx = 13,
  39. kSkipColScIdx = 14,
  40. kEvenColScIdx = 15,
  41. kTempoColScIdx = 16,
  42. kDynColScIdx = 17
  43. };
  44. typedef struct
  45. {
  46. unsigned id;
  47. cmChar_t label[ kLabelCharCnt + 1 ];
  48. } cmScEvtRef_t;
  49. typedef struct
  50. {
  51. cmErr_t err;
  52. cmScoreEvt_t* array;
  53. unsigned cnt;
  54. cmCsvH_t cH;
  55. cmScCb_t cbFunc;
  56. void* cbArg;
  57. cmChar_t* fn;
  58. cmScoreLoc_t* loc;
  59. unsigned locCnt;
  60. } cmSc_t;
  61. cmScEvtRef_t _cmScEvtRefArray[] =
  62. {
  63. { kTimeSigEvtScId, "tsg" },
  64. { kKeySigEvtScId, "ksg" },
  65. { kTempoEvtScId, "tmp" },
  66. { kTrackEvtScId, "trk" },
  67. { kTextEvtScId, "txt" },
  68. { kEOTrackEvtScId, "eot" },
  69. { kCopyEvtScId, "cpy"},
  70. { kBlankEvtScId, "blk"},
  71. { kBarEvtScId, "bar"},
  72. { kPgmEvtScId, "pgm" },
  73. { kCtlEvtScId, "ctl" },
  74. { kNonEvtScId, "non" },
  75. { kInvalidEvtScId, "***" }
  76. };
  77. cmScEvtRef_t _cmScDynRefArray[] =
  78. {
  79. { 1, "pppp" },
  80. { 2, "ppp" },
  81. { 3, "pp" },
  82. { 4, "p" },
  83. { 5, "mp" },
  84. { 6, "m" },
  85. { 7, "mf" },
  86. { 8, "f" },
  87. { 9, "ff" },
  88. { 10, "fff" },
  89. { 11, "ffff"},
  90. { kInvalidDynScId, "***" },
  91. };
  92. cmSc_t* _cmScHandleToPtr( cmScH_t h )
  93. {
  94. cmSc_t* p = (cmSc_t*)h.h;
  95. assert( p != NULL );
  96. return p;
  97. }
  98. unsigned _cmScEvtTypeLabelToId( const cmChar_t* label )
  99. {
  100. cmScEvtRef_t* r = _cmScEvtRefArray;
  101. for(; r->id != kInvalidEvtScId; ++r )
  102. if( strcmp(label,r->label) == 0 )
  103. return r->id;
  104. return kInvalidEvtScId;
  105. }
  106. const cmChar_t* cmScEvtTypeIdToLabel( unsigned id )
  107. {
  108. cmScEvtRef_t* r = _cmScEvtRefArray;
  109. for(; r->id != kInvalidEvtScId; ++r )
  110. if( r->id == id )
  111. return r->label;
  112. return NULL;
  113. }
  114. unsigned _cmScDynLabelToId( const cmChar_t* label )
  115. {
  116. cmScEvtRef_t* r = _cmScDynRefArray;
  117. for(; r->id != kInvalidEvtScId; ++r )
  118. if( strcmp(label,r->label) == 0 )
  119. return r->id;
  120. return kInvalidDynScId;
  121. }
  122. const cmChar_t* cmScDynIdToLabel( unsigned id )
  123. {
  124. cmScEvtRef_t* r = _cmScDynRefArray;
  125. for(; r->id != kInvalidDynScId; ++r )
  126. if( r->id == id )
  127. return r->label;
  128. return NULL;
  129. }
  130. unsigned _cmScLexSciPitchMatcher( const cmChar_t* cp, unsigned cn )
  131. {
  132. // first char must be "A-G"
  133. if( strspn(cp,"ABCDEFG") != 1 )
  134. return 0;
  135. unsigned i = 1;
  136. // next char could be accidental
  137. if( cp[i] == '#' || cp[i] == 'b' )
  138. ++i; // i==2
  139. // the 2nd or 3rd char must be a digit
  140. if( isdigit(cp[i]) == false )
  141. return 0;
  142. ++i; // i==2 or i==3
  143. // the 3rd or 4th char must be a digit or EOS
  144. if( isdigit(cp[i]) == false )
  145. return i;
  146. ++i;
  147. return i;
  148. }
  149. cmScRC_t _cmScFinalize( cmSc_t* p )
  150. {
  151. cmScRC_t rc = kOkScRC;
  152. if( cmCsvFinalize(&p->cH) != kOkCsvRC )
  153. return rc;
  154. unsigned i;
  155. for(i=0; i<p->locCnt; ++i)
  156. cmMemFree(p->loc[i].evtArray);
  157. cmMemFree(p->loc);
  158. cmMemFree(p->fn);
  159. cmMemFree(p->array);
  160. cmMemFree(p);
  161. return rc;
  162. }
  163. cmScRC_t _cmScParseBar( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int* barNumb )
  164. {
  165. if((*barNumb = cmCsvCellInt(p->cH,rowIdx,kBarColScIdx)) == INT_MAX )
  166. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to parse the bar number.");
  167. s->type = kBarEvtScId;
  168. s->secs = 0;
  169. s->barNumb = *barNumb;
  170. return kOkScRC;
  171. }
  172. cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barNumb, unsigned barNoteIdx )
  173. {
  174. cmScRC_t rc = kOkScRC;
  175. unsigned flags = 0;
  176. unsigned dynVal = kInvalidDynScId;
  177. const cmChar_t* sciPitch;
  178. cmMidiByte_t midiPitch;
  179. const cmChar_t* attr;
  180. double secs;
  181. double durSecs;
  182. if((sciPitch = cmCsvCellText(p->cH,rowIdx,kPitchColScIdx)) == NULL )
  183. return cmErrMsg(&p->err,kSyntaxErrScRC,"Expected a scientific pitch value");
  184. if((midiPitch = cmSciPitchToMidi(sciPitch)) == kInvalidMidiPitch)
  185. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to convert the scientific pitch '%s' to a MIDI value. ");
  186. // it is possible that note delta-secs field is empty - so default to 0
  187. if((secs = cmCsvCellDouble(p->cH, rowIdx, kSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
  188. flags += kInvalidScFl;
  189. if((attr = cmCsvCellText(p->cH,rowIdx,kSkipColScIdx)) != NULL && *attr == 's' )
  190. flags += kSkipScFl;
  191. if((attr = cmCsvCellText(p->cH,rowIdx,kEvenColScIdx)) != NULL && *attr == 'e' )
  192. flags += kEvenScFl;
  193. if((attr = cmCsvCellText(p->cH,rowIdx,kTempoColScIdx)) != NULL && *attr == 't' )
  194. flags += kTempoScFl;
  195. if((attr = cmCsvCellText(p->cH,rowIdx,kDynColScIdx)) != NULL )
  196. {
  197. if((dynVal = _cmScDynLabelToId(attr)) == kInvalidDynScId )
  198. return cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown dynamic label '%s'.",cmStringNullGuard(attr));
  199. flags += kDynScFl;
  200. }
  201. // Returns DBL_MAX on error.
  202. if((durSecs = cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX)
  203. durSecs = 0.25;
  204. s->type = kNonEvtScId;
  205. s->secs = secs;
  206. s->pitch = midiPitch;
  207. s->flags = flags;
  208. s->dynVal = dynVal;
  209. s->barNumb = barNumb;
  210. s->barNoteIdx = barNoteIdx;
  211. s->durSecs = durSecs;
  212. return rc;
  213. }
  214. cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
  215. {
  216. cmScRC_t rc = kOkScRC;
  217. unsigned barNoteIdx = 0;
  218. int barEvtIdx = cmInvalidIdx;
  219. int barNumb = 0;
  220. double secs;
  221. double cur_secs = 0;
  222. if( cmCsvInitialize(&p->cH, ctx ) != kOkCsvRC )
  223. {
  224. rc = cmErrMsg(&p->err,kCsvFailScRC,"Score file initialization failed.");
  225. goto errLabel;
  226. }
  227. if( cmCsvLexRegisterMatcher(p->cH, cmCsvLexNextAvailId(p->cH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
  228. {
  229. rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV token matcher registration failed.");
  230. goto errLabel;
  231. }
  232. if( cmCsvParseFile(p->cH, fn, 0 ) != kOkCsvRC )
  233. {
  234. rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV file parsing failed on the file '%s'.",cmStringNullGuard(fn));
  235. goto errLabel;
  236. }
  237. p->cnt = cmCsvRowCount(p->cH);
  238. p->array = cmMemAllocZ(cmScoreEvt_t,p->cnt);
  239. unsigned i,j;
  240. // skip labels line - start on line 1
  241. for(i=1,j=0; i<p->cnt && rc==kOkScRC; ++i)
  242. {
  243. // get the row 'type' label
  244. const char* typeLabel;
  245. if((typeLabel = cmCsvCellText(p->cH,i,kTypeLabelColScIdx)) == NULL )
  246. {
  247. rc = cmErrMsg(&p->err,kSyntaxErrScRC,"No type label.");
  248. break;
  249. }
  250. // convert the row 'type' label to an id
  251. unsigned tid;
  252. if((tid = _cmScEvtTypeLabelToId(typeLabel)) == kInvalidEvtScId)
  253. {
  254. rc = cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown type '%s'.",cmStringNullGuard(typeLabel));
  255. break;
  256. }
  257. secs = DBL_MAX;
  258. switch(tid)
  259. {
  260. case kBarEvtScId: // parse bar lines
  261. if((rc = _cmScParseBar(p,i,p->array+j,&barNumb)) == kOkScRC )
  262. {
  263. barNoteIdx = 0;
  264. barEvtIdx = j;
  265. p->array[j].index = j;
  266. ++j;
  267. }
  268. break;
  269. case kNonEvtScId: // parse note-on events
  270. if((rc = _cmScParseNoteOn(p, i, p->array + j, barNumb, barNoteIdx )) == kOkScRC )
  271. {
  272. secs = p->array[j].secs;
  273. if( p->array[j].secs == DBL_MAX )
  274. p->array[j].secs = cur_secs;
  275. if( cmIsFlag(p->array[j].flags,kSkipScFl) == false )
  276. {
  277. p->array[j].index = j;
  278. ++j;
  279. }
  280. ++barNoteIdx;
  281. }
  282. break;
  283. default:
  284. // Returns DBL_MAX on error.
  285. secs = cmCsvCellDouble(p->cH, i, kSecsColScIdx );
  286. break;
  287. }
  288. if( secs != DBL_MAX )
  289. cur_secs = secs;
  290. // the bar lines don't have times so set the time of the bar line to the
  291. // time of the first event in the bar.
  292. if( barEvtIdx != cmInvalidIdx && secs != DBL_MAX )
  293. {
  294. assert( p->array[ barEvtIdx ].type == kBarEvtScId );
  295. p->array[ barEvtIdx ].secs = secs;
  296. // handle the case where the previous bar had no events
  297. if( p->array[ barEvtIdx-1].type == kBarEvtScId )
  298. p->array[ barEvtIdx-1].secs = secs;
  299. barEvtIdx = cmInvalidIdx;
  300. }
  301. }
  302. if( rc == kSyntaxErrScRC )
  303. {
  304. cmErrMsg(&p->err,rc,"Syntax error on line %i in '%s'.",i+1,cmStringNullGuard(fn));
  305. goto errLabel;
  306. }
  307. p->cnt = i;
  308. errLabel:
  309. return rc;
  310. }
  311. // This function does not currently work because there is no
  312. // guarantee that all the time values (secs field) have been filled in
  313. /// with valid times and that all event records have a valid 'type' id.
  314. cmScRC_t _cmScoreInitLocArray( cmSc_t* p )
  315. {
  316. cmScRC_t rc = kOkScRC;
  317. double minDSecs = 0;
  318. unsigned barNumb = 0;
  319. if( p->cnt==0)
  320. return rc;
  321. p->locCnt = 1;
  322. // count the number of unique time locations in the score
  323. int i,j,k;
  324. double secs = p->array[0].secs;
  325. for(i=0; i<p->cnt; ++i)
  326. {
  327. assert( p->array[i].secs >= secs );
  328. if( p->array[i].secs - secs <= minDSecs )
  329. {
  330. p->locCnt += 1;
  331. secs = p->array[i].secs;
  332. }
  333. }
  334. // allocate the loc. array
  335. p->loc = cmMemAllocZ(cmScoreLoc_t,p->locCnt);
  336. // fill in the location array
  337. for(i=0,k=0; i<p->cnt; ++k)
  338. {
  339. j = i+1;
  340. assert(p->array[j].secs > p->array[i].secs );
  341. // get the count of events at this location
  342. while( j<p->cnt && p->array[j].secs - p->array[i].secs < minDSecs )
  343. ++j;
  344. assert(k<p->locCnt);
  345. p->loc[k].evtCnt = j-i;
  346. p->loc[k].evtArray = cmMemAllocZ(cmScoreEvt_t*,p->loc[k].evtCnt);
  347. // fill in the location record event pointers
  348. for(j=0; j<p->loc[k].evtCnt; ++j)
  349. {
  350. p->loc[k].evtArray[j] = p->array + (i + j);
  351. if( p->array[i+j].type == kBarEvtScId )
  352. barNumb = p->array[i+j].barNumb;
  353. }
  354. // fill in the location record
  355. p->loc[k].secs = p->array[i].secs;
  356. p->loc[k].evtIdx = i;
  357. p->loc[k].barNumb = barNumb;
  358. i += p->loc[k].evtCnt;
  359. }
  360. return rc;
  361. }
  362. cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, cmScCb_t cbFunc, void* cbArg )
  363. {
  364. cmScRC_t rc = kOkScRC;
  365. if((rc = cmScoreFinalize(hp)) != kOkScRC )
  366. return rc;
  367. cmSc_t* p = cmMemAllocZ(cmSc_t,1);
  368. cmErrSetup(&p->err,&ctx->rpt,"Score");
  369. if((rc = _cmScParseFile(p,ctx,fn)) != kOkScRC )
  370. goto errLabel;
  371. // See note at function
  372. //if((rc = _cmScoreInitLocArray(p)) != kOkScRC )
  373. // goto errLabel;
  374. p->cbFunc = cbFunc;
  375. p->cbArg = cbArg;
  376. p->fn = cmMemAllocStr(fn);
  377. hp->h = p;
  378. errLabel:
  379. if( rc != kOkScRC )
  380. _cmScFinalize(p);
  381. return rc;
  382. }
  383. cmScRC_t cmScoreFinalize( cmScH_t* hp )
  384. {
  385. cmScRC_t rc = kOkScRC;
  386. if( hp == NULL || cmScoreIsValid(*hp) == false )
  387. return kOkScRC;
  388. cmSc_t* p = _cmScHandleToPtr(*hp);
  389. if((rc = _cmScFinalize(p)) != kOkScRC )
  390. return rc;
  391. hp->h = NULL;
  392. return rc;
  393. }
  394. const cmChar_t* cmScoreFileName( cmScH_t h )
  395. {
  396. cmSc_t* p = _cmScHandleToPtr(h);
  397. return p->fn;
  398. }
  399. bool cmScoreIsValid( cmScH_t h )
  400. { return h.h != NULL; }
  401. unsigned cmScoreEvtCount( cmScH_t h )
  402. {
  403. cmSc_t* p = _cmScHandleToPtr(h);
  404. return p->cnt;
  405. }
  406. cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx )
  407. {
  408. cmSc_t* p = _cmScHandleToPtr(h);
  409. if( idx >= p->cnt )
  410. {
  411. cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i records.",idx,p->cnt);
  412. return NULL;
  413. }
  414. return p->array + idx;
  415. }
  416. unsigned cmScoreLocCount( cmScH_t h )
  417. {
  418. cmSc_t* p = _cmScHandleToPtr(h);
  419. return p->locCnt;
  420. }
  421. cmScoreLoc_t* cmScoreLoc( cmScH_t h, unsigned idx )
  422. {
  423. cmSc_t* p = _cmScHandleToPtr(h);
  424. if( idx >= p->locCnt )
  425. {
  426. cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i location records.",idx,p->locCnt);
  427. return NULL;
  428. }
  429. return p->loc + idx;
  430. }
  431. cmScRC_t cmScoreSeqNotify( cmScH_t h )
  432. {
  433. cmScRC_t rc = kOkScRC;
  434. cmSc_t* p = _cmScHandleToPtr(h);
  435. cmScMsg_t m;
  436. unsigned i;
  437. if( p->cbFunc != NULL )
  438. {
  439. memset(&m.evt,0,sizeof(m.evt));
  440. m.typeId = kBeginMsgScId;
  441. p->cbFunc(p->cbArg,&m,sizeof(m));
  442. m.typeId = kEventMsgScId;
  443. for(i=0; i<p->cnt; ++i)
  444. {
  445. m.evt = p->array[i];
  446. p->cbFunc(p->cbArg,&m,sizeof(m));
  447. }
  448. memset(&m.evt,0,sizeof(m.evt));
  449. m.typeId = kEndMsgScId;
  450. p->cbFunc(p->cbArg,&m,sizeof(m));
  451. }
  452. return rc;
  453. }
  454. cmScRC_t cmScoreDecode( const void* msg, unsigned msgByteCnt, cmScMsg_t* m)
  455. {
  456. cmScMsg_t* mp = (cmScMsg_t*)msg;
  457. *m = *mp;
  458. return kOkScRC;
  459. }
  460. void cmScorePrint( cmScH_t h, cmRpt_t* rpt )
  461. {
  462. cmSc_t* p = _cmScHandleToPtr(h);
  463. unsigned i;
  464. for(i=0; i<20 /*p->cnt*/; ++i)
  465. {
  466. cmScoreEvt_t* r = p->array + i;
  467. switch(r->type)
  468. {
  469. case kNonEvtScId:
  470. cmRptPrintf(rpt,"%5i %3i %3i %s 0x%2x %c%c%c %s\n",
  471. i,
  472. r->barNumb,
  473. r->barNoteIdx,
  474. cmScEvtTypeIdToLabel(r->type),
  475. r->pitch,
  476. cmIsFlag(r->flags,kEvenScFl) ? 'e' : ' ',
  477. cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ',
  478. cmIsFlag(r->flags,kDynScFl) ? 'd' : ' ',
  479. cmIsFlag(r->flags,kDynScFl) ? cmScDynIdToLabel(r->dynVal) : "");
  480. break;
  481. default:
  482. break;
  483. }
  484. }
  485. }
  486. // Each time line note-on object is decorated (via cmTlObj_t.userDataPtr) with a
  487. // cmScSyncState_t record.
  488. typedef struct
  489. {
  490. unsigned cnt; // count of candidate sync locations
  491. double dist; // edit distance to the closest sync location
  492. unsigned scEvtIdx; // score record this note-on is assigned to
  493. } cmScSyncState_t;
  494. void _cmScSyncTimeLineAllocFree( cmTlH_t tlH, bool allocFl )
  495. {
  496. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  497. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
  498. if( mep->msg->status == kNoteOnMdId )
  499. {
  500. if( allocFl )
  501. mep->obj.userDataPtr = cmMemAllocZ(cmScSyncState_t,1);
  502. else
  503. cmMemPtrFree(&mep->obj.userDataPtr);
  504. }
  505. }
  506. void _cmScPrintSyncState( cmSc_t* p, cmTlH_t tlH )
  507. {
  508. unsigned i = 0;
  509. double sr = cmTimeLineSampleRate(tlH);
  510. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  511. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
  512. if( mep->msg->status == kNoteOnMdId )
  513. {
  514. cmScSyncState_t* ssp = (cmScSyncState_t*)mep->obj.userDataPtr;
  515. cmRptPrintf(p->err.rpt,"%5.3f pit:0x%2x (%3i) bar:%3i bni:%3i cnt:%3i dst:%1.6f ref:%s\n",
  516. (mep->obj.ref->begSmpIdx - mep->obj.begSmpIdx) / (sr*60),
  517. mep->msg->u.chMsgPtr->d0,
  518. mep->msg->u.chMsgPtr->d0,
  519. ssp->cnt ? p->array[ ssp->scEvtIdx ].barNumb : 0,
  520. ssp->cnt ? p->array[ ssp->scEvtIdx ].barNoteIdx : 0,
  521. ssp->cnt,
  522. ssp->dist,
  523. cmStringNullGuard(mep->obj.ref->name));
  524. ++i;
  525. if( i>=300)
  526. break;
  527. }
  528. }
  529. double _cmScWndEditDist( cmSc_t* p, unsigned* mtx, const unsigned* tlWnd, cmScSyncState_t* tlObjWnd[], unsigned wndCnt )
  530. {
  531. unsigned scWnd[ wndCnt ];
  532. unsigned scIdxWnd[ wndCnt ];
  533. unsigned i;
  534. unsigned wn = 0;
  535. double minDist = DBL_MAX;
  536. // for each note-on score event
  537. for(i=0; i<p->cnt; ++i)
  538. if( p->array[i].type == kNonEvtScId )
  539. {
  540. // shift the score event window to the the left
  541. memmove(scWnd, scWnd+1, (wndCnt-1)*sizeof(scWnd[0]));
  542. memmove(scIdxWnd,scIdxWnd+1,(wndCnt-1)*sizeof(scIdxWnd[0]));
  543. // insert new score event data on right
  544. scWnd[wndCnt-1] = p->array[i].pitch;
  545. scIdxWnd[wndCnt-1] = i;
  546. ++wn;
  547. // if the window is full
  548. if(wn >= wndCnt )
  549. {
  550. // score the edit distance between the time line window and the edit window
  551. double dist = cmVOU_LevEditDist(wndCnt,mtx,scWnd,wndCnt,tlWnd,wndCnt,wndCnt);
  552. if( dist < minDist )
  553. minDist = dist;
  554. // update the match information in the time line window
  555. unsigned j;
  556. for(j=0; j<wndCnt; ++j)
  557. {
  558. // if the pitch matches and the score is less than the previous score
  559. if( scWnd[j] == tlWnd[j] && (tlObjWnd[j]->cnt == 0 || dist < tlObjWnd[j]->dist) )
  560. {
  561. tlObjWnd[j]->cnt += 1;
  562. tlObjWnd[j]->dist = dist;
  563. tlObjWnd[j]->scEvtIdx = scIdxWnd[j];
  564. }
  565. }
  566. }
  567. }
  568. return minDist;
  569. }
  570. cmScRC_t cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned edWndCnt, cmReal_t maxSecs )
  571. {
  572. cmSc_t* p = _cmScHandleToPtr(scH);
  573. unsigned* edWndMtx = cmVOU_LevEditDistAllocMtx(edWndCnt);
  574. unsigned maxMicroSecs = floor(maxSecs*1000000);
  575. unsigned edWndData[ edWndCnt ];
  576. cmScSyncState_t* edWndObj[ edWndCnt ];
  577. // alloc a sync state record for each MIDI note-on in the time line
  578. _cmScSyncTimeLineAllocFree(tlH, true );
  579. // get the first time line object
  580. cmTlObj_t* rfp = cmTimeLineNextTypeObj(tlH,NULL,cmInvalidId,kMidiFileTlId);
  581. // interate through the time line in search of MIDI file objects
  582. for(; rfp != NULL; rfp = cmTimeLineNextTypeObj(tlH,rfp,cmInvalidId,kMidiFileTlId))
  583. {
  584. cmTlMidiFile_t* mfp = cmTimeLineMidiFileObjPtr(tlH,rfp);
  585. unsigned curEdWndCnt = 0;
  586. double prog = 0.1;
  587. unsigned progIdx = 0;
  588. cmRptPrintf(p->err.rpt,"MIDI File:%s\n", cmMidiFileName( mfp->h ));
  589. // get first midi event object
  590. cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
  591. // iterate through the time line in search of MIDI note-on events with belong to mfp
  592. for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId) )
  593. {
  594. if( mep->obj.ref == rfp && mep->msg->status == kNoteOnMdId )
  595. {
  596. // If this notes inter-onset time is greater than maxMicroSecs
  597. // then dispose of the current window and begin refilling it again.
  598. if( mep->msg->dtick > maxMicroSecs )
  599. curEdWndCnt = 0;
  600. // shift window one slot to left
  601. unsigned i;
  602. for(i=0; i<edWndCnt-1; ++i)
  603. {
  604. edWndData[i] = edWndData[i+1];
  605. edWndObj[i] = edWndObj[i+1];
  606. }
  607. // fill window on right
  608. edWndData[edWndCnt-1] = mep->msg->u.chMsgPtr->d0; // d0=pitch
  609. edWndObj[ edWndCnt-1] = (cmScSyncState_t*)mep->obj.userDataPtr;
  610. ++curEdWndCnt;
  611. // if a complete window exists then update the time-line / score match state
  612. if( curEdWndCnt >= edWndCnt )
  613. _cmScWndEditDist( p, edWndMtx, edWndData, edWndObj, edWndCnt );
  614. // print the progress
  615. ++progIdx;
  616. if( progIdx >= prog * mfp->noteOnCnt )
  617. {
  618. cmRptPrintf(p->err.rpt,"%i ",(unsigned)round(prog*10));
  619. prog += 0.1;
  620. }
  621. }
  622. }
  623. cmRptPrintf(p->err.rpt,"\n");
  624. }
  625. _cmScPrintSyncState(p,tlH );
  626. // free sync state records
  627. _cmScSyncTimeLineAllocFree(tlH,false);
  628. cmMemFree(edWndMtx);
  629. return kOkScRC;
  630. }
  631. cmScRC_t cmScoreSyncTimeLineTest( cmCtx_t* ctx, const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn )
  632. {
  633. cmScRC_t rc = kOkScRC;
  634. cmTlH_t tlH = cmTimeLineNullHandle;
  635. cmScH_t scH = cmScNullHandle;
  636. unsigned edWndCnt = 7;
  637. cmReal_t maxSecs = 2.0;
  638. if((rc = cmTimeLineInitialize(ctx,&tlH,NULL,NULL)) != kOkTlRC )
  639. return cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line initialization failed.");;
  640. if((rc = cmTimeLineReadJson(tlH,timeLineJsFn)) != kOkTlRC )
  641. {
  642. rc = cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line parse failed.");;
  643. goto errLabel;
  644. }
  645. //cmTimeLinePrint(tlH,&ctx->rpt);
  646. if(1)
  647. {
  648. if((rc = cmScoreInitialize(ctx,&scH,scoreCsvFn,NULL,NULL)) != kOkScRC )
  649. goto errLabel;
  650. rc = cmScoreSyncTimeLine(scH, tlH, edWndCnt, maxSecs );
  651. }
  652. //cmScorePrint(scH, ctx->err.rpt );
  653. errLabel:
  654. cmScoreFinalize(&scH);
  655. cmTimeLineFinalize(&tlH);
  656. return rc;
  657. }
  658. void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
  659. {
  660. cmScH_t h = cmScNullHandle;
  661. if( cmScoreInitialize(ctx,&h,fn,NULL,NULL) != kOkScRC )
  662. return;
  663. cmScorePrint(h,&ctx->rpt);
  664. cmScoreFinalize(&h);
  665. }
  666. // 1. Fix absolute message time which was incorrect on original score file.
  667. // 2.
  668. void cmScoreFix( cmCtx_t* ctx )
  669. {
  670. const cmChar_t* mfn = "/home/kevin/src/cmgv/src/gv/data/ImaginaryThemes.mid";
  671. const cmChar_t* crfn = "/home/kevin/src/cmgv/src/gv/data/mod0a.txt";
  672. const cmChar_t* cwfn = "/home/kevin/src/cmgv/src/gv/data/mod1.csv";
  673. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  674. cmCsvH_t csvH = cmCsvNullHandle;
  675. const cmMidiTrackMsg_t** msg = NULL;
  676. double secs = 0.0;
  677. int ci,mi,crn,mn;
  678. bool errFl = true;
  679. unsigned handCnt = 0;
  680. unsigned midiMissCnt = 0;
  681. if( cmCsvInitialize(&csvH,ctx) != kOkCsvRC )
  682. goto errLabel;
  683. if( cmCsvLexRegisterMatcher(csvH, cmCsvLexNextAvailId(csvH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
  684. goto errLabel;
  685. if( cmCsvParseFile(csvH, crfn, 0 ) != kOkCsvRC )
  686. goto errLabel;
  687. if( cmMidiFileOpen(mfn,&mfH,ctx) != kOkMfRC )
  688. goto errLabel;
  689. cmMidiFileTickToMicros(mfH);
  690. cmMidiFileCalcNoteDurations(mfH);
  691. mn = cmMidiFileMsgCount(mfH);
  692. msg = cmMidiFileMsgArray(mfH);
  693. crn = cmCsvRowCount(csvH);
  694. // for each row in the score file
  695. for(ci=1,mi=0; ci<crn && cmCsvLastRC(csvH)==kOkCsvRC; ++ci)
  696. {
  697. unsigned id;
  698. // zero the duration column
  699. if( cmCsvCellPtr(csvH, ci, kDSecsColScIdx ) != NULL )
  700. cmCsvSetCellUInt( csvH, ci, kDSecsColScIdx, 0 );
  701. // get the MIDI file event id for this row
  702. if((id = cmCsvCellUInt(csvH,ci,kMidiFileIdColScIdx)) == UINT_MAX)
  703. {
  704. // this is a hand-entered event - so it has no event id
  705. ++handCnt;
  706. }
  707. else
  708. {
  709. for(; mi<mn; ++mi)
  710. {
  711. const cmMidiTrackMsg_t* m = msg[mi];
  712. assert( mi+1 <= id );
  713. secs += m->dtick/1000000.0;
  714. if( mi+1 != id )
  715. {
  716. if( m->status == kNoteOnMdId && m->u.chMsgPtr->d1>0 )
  717. {
  718. // this MIDI note-on does not have a corresponding score event
  719. ++midiMissCnt;
  720. }
  721. }
  722. else
  723. {
  724. cmCsvSetCellDouble( csvH, ci, kSecsColScIdx, secs );
  725. ++mi;
  726. if( m->status == kNoteOnMdId )
  727. cmCsvSetCellDouble( csvH, ci, kDSecsColScIdx, m->u.chMsgPtr->durTicks/1000000.0 );
  728. break;
  729. }
  730. }
  731. if( mi==mn)
  732. printf("done on row:%i\n",ci);
  733. }
  734. }
  735. if( cmCsvLastRC(csvH) != kOkCsvRC )
  736. goto errLabel;
  737. if( cmCsvWrite(csvH,cwfn) != kOkCsvRC )
  738. goto errLabel;
  739. errFl = false;
  740. errLabel:
  741. if( errFl )
  742. printf("Score fix failed.\n");
  743. else
  744. printf("Score fix done! hand:%i miss:%i\n",handCnt,midiMissCnt);
  745. cmMidiFileClose(&mfH);
  746. cmCsvFinalize(&csvH);
  747. }