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.

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. }