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.

cmScoreMatchGraphic.c 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  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 "cmLinkedHeap.h"
  10. #include "cmTime.h"
  11. #include "cmMidi.h"
  12. #include "cmLex.h"
  13. #include "cmCsv.h"
  14. #include "cmSymTbl.h"
  15. #include "cmMidiFile.h"
  16. #include "cmAudioFile.h"
  17. #include "cmTimeLine.h"
  18. #include "cmText.h"
  19. #include "cmFile.h"
  20. #include "cmScore.h"
  21. #include "cmScoreMatchGraphic.h"
  22. enum
  23. {
  24. kLocSmgFl = 0x0001,
  25. kBarSmgFl = 0x0002,
  26. kNoteSmgFl = 0x0004,
  27. kPedalSmgFl = 0x0008,
  28. kMidiSmgFl = 0x0010,
  29. kNoMatchSmgFl = 0x0020
  30. };
  31. // Graphic box representing a score label or MIDI event
  32. typedef struct cmSmgBox_str
  33. {
  34. unsigned flags;
  35. unsigned id; // csvEventId or midiUid
  36. unsigned left;
  37. unsigned top;
  38. unsigned width;
  39. unsigned height;
  40. cmChar_t* text0;
  41. cmChar_t* text1;
  42. struct cmSmgBox_str* link;
  43. } cmSmgBox_t;
  44. // Graphic line linking secondary MIDI matches to score events
  45. typedef struct cmSmgLine_str
  46. {
  47. cmSmgBox_t* b0;
  48. cmSmgBox_t* b1;
  49. struct cmSmgLine_str* link;
  50. } cmSmgLine_t;
  51. // Score Location
  52. typedef struct cmSmgLoc_str
  53. {
  54. cmSmgBox_t* bV; // List of graphic boxes assigned to this score location
  55. } cmSmgLoc_t;
  56. // Score label
  57. typedef struct
  58. {
  59. unsigned type; // kBarEvtScId | kNonEvtScId | kPedalEvtScId
  60. unsigned barNumb;
  61. unsigned csvEventId;
  62. unsigned locIdx;
  63. cmSmgBox_t* box;
  64. } cmSmgSc_t;
  65. // Link a MIDI event to it's matched score label.
  66. typedef struct cmSmgMatch_str
  67. {
  68. cmSmgSc_t* score;
  69. struct cmSmgMatch_str* link;
  70. } cmSmgMatch_t;
  71. // MIDI file event
  72. typedef struct
  73. {
  74. double secs;
  75. unsigned uid;
  76. unsigned pitch;
  77. unsigned vel;
  78. cmSmgMatch_t* matchV; // list of matches to score events
  79. cmSmgBox_t* box;
  80. } cmSmgMidi_t;
  81. typedef struct
  82. {
  83. cmErr_t err;
  84. cmChar_t* scFn;
  85. cmSmgSc_t* scV; // scV[scN] score bars and notes
  86. unsigned scN;
  87. cmSmgLoc_t* locV; // locV[locN] score locations (from the score file)
  88. unsigned locN;
  89. cmSmgLine_t* lines; // Graphic lines used to indicate that a midi event matches to multiple score notes.
  90. // (Each match after the first gets a line from the box representing the midi event
  91. // to the matching score event)
  92. cmChar_t* mfFn; // MIDI file name
  93. cmSmgMidi_t* mV; // mV[mN] midi note-on events
  94. unsigned mN;
  95. double mfDurSecs; // midi file duration in seconds
  96. unsigned boxW; // graphic box width and height
  97. unsigned boxH;
  98. } cmSmg_t;
  99. cmSmgH_t cmSmgNullHandle = cmSTATIC_NULL_HANDLE;
  100. cmSmg_t* _cmSmgHandleToPtr( cmSmgH_t h )
  101. {
  102. cmSmg_t* p = (cmSmg_t*)h.h;
  103. assert(p!=NULL);
  104. return p;
  105. }
  106. cmSmgRC_t _cmSmgFree( cmSmg_t* p )
  107. {
  108. unsigned i;
  109. for(i=0; i<p->mN; ++i)
  110. {
  111. cmSmgMatch_t* m0 = p->mV[i].matchV;
  112. cmSmgMatch_t* m1 = NULL;
  113. while(m0!=NULL)
  114. {
  115. m1 = m0->link;
  116. cmMemFree(m0);
  117. m0 = m1;
  118. }
  119. }
  120. for(i=0; i<p->locN; ++i)
  121. {
  122. cmSmgBox_t* b0 = p->locV[i].bV;
  123. cmSmgBox_t* b1 = NULL;
  124. while(b0!=NULL)
  125. {
  126. b1 = b0->link;
  127. cmMemFree(b0->text0);
  128. cmMemFree(b0->text1);
  129. cmMemFree(b0);
  130. b0 = b1;
  131. }
  132. }
  133. cmSmgLine_t* l0 = p->lines;
  134. cmSmgLine_t* l1 = NULL;
  135. while( l0 != NULL )
  136. {
  137. l1 = l0->link;
  138. cmMemFree(l0);
  139. l0 = l1;
  140. }
  141. cmMemFree(p->scFn);
  142. cmMemFree(p->mfFn);
  143. cmMemFree(p->scV);
  144. cmMemFree(p->mV);
  145. cmMemFree(p->locV);
  146. cmMemFree(p);
  147. return kOkSmgRC;
  148. }
  149. cmSmgBox_t* _cmSmgInsertBox( cmSmg_t* p, unsigned locIdx, unsigned flags, unsigned id, cmChar_t* text0, cmChar_t* text1 )
  150. {
  151. assert( locIdx < p->locN );
  152. cmSmgBox_t* b = cmMemAllocZ(cmSmgBox_t,1);
  153. b->flags = flags;
  154. b->id = id;
  155. b->text0 = text0;
  156. b->text1 = text1;
  157. if( p->locV[locIdx].bV == NULL )
  158. {
  159. p->locV[locIdx].bV = b;
  160. }
  161. else
  162. {
  163. cmSmgBox_t* b0 = p->locV[locIdx].bV;
  164. while( b0->link!=NULL )
  165. b0 = b0->link;
  166. b0->link = b;
  167. }
  168. return b;
  169. }
  170. cmSmgRC_t _cmSmgInitFromScore( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* scoreFn )
  171. {
  172. cmSmgRC_t rc = kOkSmgRC;
  173. cmScH_t scH = cmScNullHandle;
  174. unsigned i,j,k;
  175. if( cmScoreInitialize(ctx,&scH,scoreFn,44100.0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  176. return cmErrMsg(&p->err,kScoreFailSmgRC,"Score initializatio failed on '%s'.",cmStringNullGuard(scoreFn));
  177. p->scFn = cmMemAllocStr(scoreFn);
  178. p->scN = cmScoreEvtCount(scH);
  179. p->scV = cmMemAllocZ(cmSmgSc_t,p->scN);
  180. p->locN = cmScoreLocCount(scH);
  181. p->locV = cmMemAllocZ(cmSmgLoc_t,p->locN);
  182. // for each score location
  183. for(i=0,k=0; i<cmScoreLocCount(scH); ++i)
  184. {
  185. cmScoreLoc_t* l = cmScoreLoc(scH,i);
  186. // insert the location label box
  187. _cmSmgInsertBox(p, i, kLocSmgFl, cmInvalidId, cmTsPrintfP(NULL,"%i",i), NULL );
  188. // for each event in location i
  189. for(j=0; j<l->evtCnt; ++j)
  190. {
  191. const cmScoreEvt_t* e = l->evtArray[j];
  192. unsigned flags = kNoMatchSmgFl;
  193. cmChar_t* text = NULL;
  194. switch( e->type)
  195. {
  196. case kNonEvtScId:
  197. flags |= kNoteSmgFl;
  198. text = cmMemAllocStr( cmMidiToSciPitch( e->pitch, NULL, 0));
  199. break;
  200. case kBarEvtScId:
  201. flags |= kBarSmgFl;
  202. text = cmTsPrintfP(NULL,"%i",e->barNumb);
  203. break;
  204. case kPedalEvtScId:
  205. flags |= kPedalSmgFl;
  206. text = cmTsPrintfP(NULL,"%s", cmIsFlag(e->flags,kPedalDnScFl)?"v":"^");
  207. break;
  208. }
  209. // if e is a score event of interest then store a reference to it
  210. if( flags != kNoMatchSmgFl )
  211. {
  212. assert( k < p->scN );
  213. p->scV[k].type = e->type;
  214. p->scV[k].csvEventId = e->csvEventId;
  215. p->scV[k].locIdx = i;
  216. p->scV[k].barNumb = e->barNumb;
  217. p->scV[k].box = _cmSmgInsertBox(p, i, flags, e->csvEventId, text, NULL );
  218. k += 1;
  219. }
  220. }
  221. }
  222. p->scN = k;
  223. cmScoreFinalize(&scH);
  224. return rc;
  225. }
  226. cmSmgRC_t _cmSmgInitFromMidi( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* midiFn )
  227. {
  228. cmSmgRC_t rc = kOkSmgRC;
  229. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  230. unsigned i,j;
  231. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  232. return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file open failed on '%s'.",cmStringNullGuard(midiFn));
  233. const cmMidiTrackMsg_t** mV = cmMidiFileMsgArray(mfH);
  234. unsigned mN = cmMidiFileMsgCount(mfH);
  235. p->mV = cmMemAllocZ(cmSmgMidi_t,mN);
  236. p->mN = mN;
  237. p->mfDurSecs = cmMidiFileDurSecs(mfH);
  238. p->mfFn = cmMemAllocStr(midiFn);
  239. for(i=0,j=0; i<mN; ++i)
  240. if( (mV[i]!=NULL) && cmMidiIsChStatus(mV[i]->status) && cmMidiIsNoteOn(mV[i]->status) && (mV[i]->u.chMsgPtr->d1>0) )
  241. {
  242. assert(j < mN);
  243. p->mV[j].secs = mV[i]->amicro / 1000000.0;
  244. p->mV[j].uid = mV[i]->uid;
  245. p->mV[j].pitch = mV[i]->u.chMsgPtr->d0;
  246. p->mV[j].vel = mV[i]->u.chMsgPtr->d1;
  247. ++j;
  248. }
  249. p->mN = j;
  250. cmMidiFileClose(&mfH);
  251. return rc;
  252. }
  253. cmSmgRC_t cmScoreMatchGraphicAlloc( cmCtx_t* ctx, cmSmgH_t* hp, const cmChar_t* scoreFn, const cmChar_t* midiFn )
  254. {
  255. cmSmgRC_t rc;
  256. if((rc = cmScoreMatchGraphicFree(hp)) != kOkSmgRC )
  257. return rc;
  258. cmSmg_t* p = cmMemAllocZ(cmSmg_t,1);
  259. cmErrSetup(&p->err,&ctx->rpt,"ScoreMatchGraphic");
  260. if((rc = _cmSmgInitFromScore(ctx,p,scoreFn)) != kOkSmgRC )
  261. goto errLabel;
  262. if((rc = _cmSmgInitFromMidi(ctx,p,midiFn)) != kOkSmgRC )
  263. goto errLabel;
  264. p->boxW = 30;
  265. p->boxH = 50;
  266. hp->h = p;
  267. errLabel:
  268. if( rc != kOkSmgRC )
  269. _cmSmgFree(p);
  270. return rc;
  271. }
  272. bool cmScoreMatchGraphicIsValid( cmSmgH_t h )
  273. { return h.h != NULL; }
  274. cmSmgRC_t cmScoreMatchGraphicFree( cmSmgH_t* hp )
  275. {
  276. cmSmgRC_t rc = kOkSmgRC;
  277. if(hp==NULL || cmScoreMatchGraphicIsValid(*hp)==false)
  278. return kOkSmgRC;
  279. cmSmg_t* p = _cmSmgHandleToPtr(*hp);
  280. if((rc = _cmSmgFree(p)) != kOkSmgRC )
  281. return rc;
  282. hp->h = NULL;
  283. return rc;
  284. }
  285. bool cmScoreMatchGraphic( cmSmgH_t h )
  286. { return h.h != NULL; }
  287. cmSmgRC_t cmScoreMatchGraphicInsertMidi( cmSmgH_t h, unsigned midiUid, unsigned midiPitch, unsigned midiVel, unsigned csvScoreEventId )
  288. {
  289. cmSmg_t* p = _cmSmgHandleToPtr(h);
  290. unsigned i,j;
  291. // if this midi event did not match any score records
  292. if( csvScoreEventId == cmInvalidId )
  293. return kOkSmgRC;
  294. assert(midiUid != cmInvalidId );
  295. assert(midiPitch<128 && midiVel<128);
  296. // find the midi file record which matches the event
  297. for(i=0; i<p->mN; ++i)
  298. if( p->mV[i].uid == midiUid )
  299. {
  300. // find the score record which matches the score event id
  301. for(j=0; j<p->scN; ++j)
  302. if( p->scV[j].csvEventId == csvScoreEventId )
  303. {
  304. // create a match record
  305. cmSmgMatch_t* m = cmMemAllocZ(cmSmgMatch_t,1);
  306. m->score = p->scV + j;
  307. // mark the box associated with this score record as 'matched' by clearing the kNoMatchSmgFl
  308. p->scV[j].box->flags = cmClrFlag(p->scV[j].box->flags,kNoMatchSmgFl);
  309. // insert the match record in the midi files match list
  310. if( p->mV[i].matchV == NULL )
  311. p->mV[i].matchV = m;
  312. else
  313. {
  314. cmSmgMatch_t* m0 = p->mV[i].matchV;
  315. while( m0->link != NULL )
  316. m0 = m0->link;
  317. m0->link = m;
  318. }
  319. return kOkSmgRC;
  320. }
  321. return cmErrMsg(&p->err,kScoreFailSmgRC,"The score csv event id %i not found,",csvScoreEventId);
  322. }
  323. return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI uid %i not found.",midiUid);
  324. }
  325. // Create a box for each MIDI event and a line for each
  326. // match beyond the first.
  327. void _cmSmgResolveMidi( cmSmg_t* p )
  328. {
  329. unsigned prevLocIdx = 0;
  330. unsigned i;
  331. // for each midi record
  332. for(i=0; i<p->mN; ++i)
  333. {
  334. // get the first match record for this MIDI event
  335. const cmSmgMatch_t* m = p->mV[i].matchV;
  336. // get the score location for this midi event
  337. unsigned locIdx = m==NULL ? prevLocIdx : m->score->locIdx;
  338. unsigned flags = kMidiSmgFl | (m==NULL ? kNoMatchSmgFl : 0);
  339. // set the text label for this event
  340. cmChar_t* text = cmMemAllocStr( cmMidiToSciPitch( p->mV[i].pitch, NULL, 0));
  341. // insert a box to represent this midi event
  342. cmSmgBox_t* box = _cmSmgInsertBox( p, locIdx, flags, p->mV[i].uid, text, cmTsPrintfP(NULL,"%i",p->mV[i].uid) );
  343. prevLocIdx = locIdx;
  344. // if this midi event matched to multiple score positions
  345. if( m != NULL && m->link != NULL )
  346. {
  347. // insert a line for each match after the first
  348. m = m->link;
  349. for(; m!=NULL; m=m->link )
  350. {
  351. cmSmgLine_t* l = cmMemAllocZ(cmSmgLine_t,1);
  352. l->b0 = box;
  353. l->b1 = m->score->box;
  354. l->link = p->lines;
  355. p->lines = l;
  356. }
  357. }
  358. }
  359. }
  360. void _cmSmgLayout( cmSmg_t* p )
  361. {
  362. unsigned i;
  363. unsigned bordX = 5;
  364. unsigned bordY = 5;
  365. unsigned top = p->boxH + 2*bordY;
  366. unsigned left = bordX;
  367. for(i=0; i<p->locN; ++i)
  368. {
  369. cmSmgLoc_t* l = p->locV + i;
  370. cmSmgBox_t* b = l->bV;
  371. // for each box attached to this location
  372. for(; b!=NULL; b=b->link)
  373. {
  374. // bar boxes are always drawn at the top of the column
  375. if( cmIsFlag(b->flags,kBarSmgFl) )
  376. b->top = bordY;
  377. else
  378. {
  379. b->top = top;
  380. top += p->boxH + bordY;
  381. }
  382. b->left = left;
  383. b->width = p->boxW;
  384. b->height = p->boxH;
  385. }
  386. left += p->boxW + bordX;
  387. top = p->boxH + 2*bordY;
  388. }
  389. }
  390. void _cmSmgSvgSize( cmSmg_t* p, unsigned* widthRef, unsigned* heightRef )
  391. {
  392. unsigned i;
  393. unsigned maxWidth = 0;
  394. unsigned maxHeight = 0;
  395. for(i=0; i<p->locN; ++i)
  396. {
  397. cmSmgBox_t* b = p->locV[i].bV;
  398. for(; b != NULL; b=b->link )
  399. {
  400. if( b->left + b->width > maxWidth )
  401. maxWidth = b->left + b->width;
  402. if( b->top + b->height > maxHeight )
  403. maxHeight = b->top + b->height;
  404. }
  405. }
  406. *widthRef = maxWidth;
  407. *heightRef = maxHeight;
  408. }
  409. cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn )
  410. {
  411. cmSmg_t* p = _cmSmgHandleToPtr(h);
  412. cmFileH_t fH = cmFileNullHandle;
  413. unsigned svgHeight = 0;
  414. unsigned svgWidth = 0;
  415. unsigned i;
  416. // BUG BUG BUG : this can only be called once
  417. // create a box for each midi event
  418. _cmSmgResolveMidi( p );
  419. // layout the boxes
  420. _cmSmgLayout( p );
  421. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  422. return cmErrMsg(&p->err,kFileFailScRC,"Graphic file create failed for '%s'.",cmStringNullGuard(fn));
  423. _cmSmgSvgSize(p,&svgWidth,&svgHeight);
  424. svgHeight += 10; // add a right and lower border
  425. svgWidth += 10;
  426. cmFilePrintf(fH,"<!DOCTYPE html>\n<html>\n<head><link rel=\"stylesheet\" type=\"text/css\" href=\"score0.css\"></head><body>\n<svg width=\"%i\" height=\"%i\">\n",svgWidth,svgHeight);
  427. for(i=0; i<p->locN; ++i)
  428. {
  429. cmSmgBox_t* b = p->locV[i].bV;
  430. for(; b != NULL; b=b->link )
  431. {
  432. const cmChar_t* classStr = "score";
  433. if( cmIsFlag(b->flags,kLocSmgFl) )
  434. classStr = "loc";
  435. if( cmIsFlag(b->flags,kMidiSmgFl) )
  436. classStr = "midi";
  437. if( cmIsFlag(b->flags,kNoMatchSmgFl) )
  438. if( cmIsFlag(b->flags,kMidiSmgFl) )
  439. classStr = "midi_miss";
  440. if( cmIsFlag(b->flags,kNoMatchSmgFl) )
  441. if( cmIsFlag(b->flags,kNoteSmgFl) )
  442. classStr = "score_miss";
  443. if( cmIsFlag(b->flags,kBarSmgFl) )
  444. classStr = "bar";
  445. if( cmFilePrintf(fH,"<rect x=\"%i\" y=\"%i\" width=\"%i\" height=\"%i\" class=\"%s\"/>\n",b->left,b->top,b->width,b->height,classStr) != kOkFileRC )
  446. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file rect output.");
  447. if( b->text0 != NULL )
  448. {
  449. unsigned tx = b->left + b->width/2;
  450. unsigned ty = b->top + 20;
  451. if( cmFilePrintf(fH,"<text x=\"%i\" y=\"%i\" text-anchor=\"middle\" class=\"stext\">%s</text>\n",tx,ty,b->text0) != kOkFileRC )
  452. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file text output.");
  453. }
  454. if( b->text1 != NULL )
  455. {
  456. unsigned tx = b->left + b->width/2;
  457. unsigned ty = b->top + 20 + 20;
  458. if( cmFilePrintf(fH,"<text x=\"%i\" y=\"%i\" text-anchor=\"middle\" class=\"stext\">%s</text>\n",tx,ty,b->text1) != kOkFileRC )
  459. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file text output.");
  460. }
  461. }
  462. }
  463. cmSmgLine_t* l = p->lines;
  464. for(; l!=NULL; l=l->link)
  465. {
  466. unsigned x0 = l->b0->left + l->b0->width/2;
  467. unsigned y0 = l->b0->top + l->b0->height/2;
  468. unsigned x1 = l->b1->left + l->b1->width/2;
  469. unsigned y1 = l->b1->top + l->b1->height/2;
  470. if( cmFilePrintf(fH,"<line x1=\"%i\" y1=\"%i\" x2=\"%i\" y2=\"%i\" class=\"sline\"/>\n",x0,y0,x1,y1) != kOkFileRC )
  471. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file line output.");
  472. }
  473. cmFilePrint(fH,"</svg>\n</body>\n</html>\n");
  474. cmFileClose(&fH);
  475. return kOkSmgRC;
  476. }
  477. cmSmgRC_t cmScoreMatchGraphicGenTimeLineBars( cmSmgH_t h, const cmChar_t* fn, unsigned srate )
  478. {
  479. cmSmgRC_t rc = kOkSmgRC;
  480. cmFileH_t f = cmFileNullHandle;
  481. cmSmg_t* p = _cmSmgHandleToPtr(h);
  482. unsigned i = 0;
  483. if( cmFileOpen(&f,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  484. return cmErrMsg(&p->err,kFileSmgRC,"The time-line bar file '%s' could not be created.",cmStringNullGuard(fn));
  485. for(i=0; i<p->mN; ++i)
  486. if( p->mV[i].matchV != NULL && p->mV[i].matchV->score != NULL && p->mV[i].matchV->score > p->scV && p->mV[i].matchV->score[-1].type==kBarEvtScId )
  487. {
  488. unsigned bar = p->mV[i].matchV->score->barNumb;
  489. unsigned offset = p->mV[i].secs * srate;
  490. unsigned smpCnt = p->mfDurSecs * srate - offset;
  491. cmFilePrintf(f,"{ label: \"%i\" type: \"mk\" ref: \"mf-0\" offset: %8i smpCnt:%8i trackId: 0 textStr: \"Bar %3i\" bar: %3i sec:\"%3i\" }\n",bar,offset,smpCnt,bar,bar,bar);
  492. }
  493. cmFileClose(&f);
  494. return rc;
  495. }
  496. cmSmgRC_t cmScoreMatchGraphicUpdateMidiFromScore( cmCtx_t* ctx, cmSmgH_t h, const cmChar_t* newMidiFn )
  497. {
  498. cmSmgRC_t rc = kOkSmgRC;
  499. cmSmg_t* p = _cmSmgHandleToPtr(h);
  500. unsigned i = 0;
  501. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  502. cmScH_t scH = cmScNullHandle;
  503. if( cmMidiFileOpen(ctx, &mfH, p->mfFn ) != kOkMfRC )
  504. return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file open failed on '%s'.",cmStringNullGuard(p->mfFn));
  505. if( cmScoreInitialize(ctx,&scH,p->scFn,44100.0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  506. {
  507. rc = cmErrMsg(&p->err,kScoreFailSmgRC,"Score initializatio failed on '%s'.",cmStringNullGuard(p->scFn));
  508. goto errLabel;
  509. }
  510. for(i=0; i<p->mN; ++i)
  511. {
  512. cmSmgMidi_t* mr = p->mV + i;
  513. // only update midi events which were matched exactly once
  514. if( mr->matchV==NULL || mr->matchV->link!=NULL )
  515. continue;
  516. // locate the matched score event
  517. const cmScoreEvt_t* s= cmScoreIdToEvt( scH, mr->matchV->score->csvEventId );
  518. assert( s!=NULL );
  519. // assign the score velocity to the MIDI file
  520. if(cmMidiFileSetVelocity( mfH, mr->uid, s->vel ) != kOkMfRC )
  521. {
  522. rc = cmErrMsg(&p->err,kOpFailSmgRC,"Set velocify operation failed.");
  523. goto errLabel;
  524. }
  525. }
  526. // write the updated MIDI file
  527. if( cmMidiFileWrite( mfH, newMidiFn ) != kOkMfRC )
  528. {
  529. rc = cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file write failed on '%s'.",cmStringNullGuard(newMidiFn));
  530. goto errLabel;
  531. }
  532. errLabel:
  533. cmMidiFileClose(&mfH);
  534. cmScoreFinalize(&scH);
  535. return rc;
  536. }