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.

cmScoreMatchGraphic.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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. kBarSmgFl = 0x0001,
  25. kNoteSmgFl = 0x0002,
  26. kMidiSmgFl = 0x0004,
  27. kNoMatchSmgFl = 0x0008
  28. };
  29. typedef struct cmSmgBox_str
  30. {
  31. unsigned flags;
  32. unsigned id; // csvEventId or midiUid
  33. unsigned left;
  34. unsigned top;
  35. unsigned width;
  36. unsigned height;
  37. cmChar_t* text;
  38. struct cmSmgBox_str* link;
  39. } cmSmgBox_t;
  40. typedef struct cmSmgLine_str
  41. {
  42. cmSmgBox_t* b0;
  43. cmSmgBox_t* b1;
  44. struct cmSmgLine_str* link;
  45. } cmSmgLine_t;
  46. typedef struct cmSmgLoc_str
  47. {
  48. cmSmgBox_t* bV;
  49. } cmSmgLoc_t;
  50. typedef struct
  51. {
  52. unsigned type;
  53. unsigned csvEventId;
  54. unsigned locIdx;
  55. cmSmgBox_t* box;
  56. } cmSmgSc_t;
  57. typedef struct cmSmgMatch_str
  58. {
  59. cmSmgSc_t* score;
  60. struct cmSmgMatch_str* link;
  61. } cmSmgMatch_t;
  62. typedef struct
  63. {
  64. unsigned uid;
  65. unsigned pitch;
  66. unsigned vel;
  67. cmSmgMatch_t* matchV;
  68. cmSmgBox_t* box;
  69. } cmSmgMidi_t;
  70. typedef struct
  71. {
  72. cmErr_t err;
  73. cmSmgSc_t* scV;
  74. unsigned scN;
  75. cmSmgMidi_t* mV;
  76. unsigned mN;
  77. cmSmgLoc_t* locV;
  78. unsigned locN;
  79. cmSmgLine_t* lines;
  80. } cmSmg_t;
  81. cmSmgH_t cmSmgNullHandle = cmSTATIC_NULL_HANDLE;
  82. cmSmg_t* _cmSmgHandleToPtr( cmSmgH_t h )
  83. {
  84. cmSmg_t* p = (cmSmg_t*)h.h;
  85. assert(p!=NULL);
  86. return p;
  87. }
  88. cmSmgRC_t _cmSmgFree( cmSmg_t* p )
  89. {
  90. unsigned i;
  91. for(i=0; i<p->mN; ++i)
  92. {
  93. cmSmgMatch_t* m0 = p->mV[i].matchV;
  94. cmSmgMatch_t* m1 = NULL;
  95. while(m0!=NULL)
  96. {
  97. m1 = m0->link;
  98. cmMemFree(m0);
  99. m0 = m1;
  100. }
  101. }
  102. for(i=0; i<p->locN; ++i)
  103. {
  104. cmSmgBox_t* b0 = p->locV[i].bV;
  105. cmSmgBox_t* b1 = NULL;
  106. while(b0!=NULL)
  107. {
  108. b1 = b0->link;
  109. cmMemFree(b0->text);
  110. cmMemFree(b0);
  111. b0 = b1;
  112. }
  113. }
  114. cmSmgLine_t* l0 = p->lines;
  115. cmSmgLine_t* l1 = NULL;
  116. while( l0 != NULL )
  117. {
  118. l1 = l0->link;
  119. cmMemFree(l0);
  120. l0 = l1;
  121. }
  122. cmMemFree(&p->scV);
  123. cmMemFree(&p->mV);
  124. cmMemFree(&p->locV);
  125. cmMemFree(p);
  126. return kOkSmgRC;
  127. }
  128. cmSmgBox_t* _cmSmgInsertBox( cmSmg_t* p, unsigned locIdx, unsigned flags, unsigned id, cmChar_t* text )
  129. {
  130. assert( locIdx < p->locN );
  131. cmSmgBox_t* b = cmMemAllocZ(cmSmgBox_t,1);
  132. b->flags = flags;
  133. b->id = id;
  134. b->text = text;
  135. if( p->locV[locIdx].bV == NULL )
  136. p->locV[locIdx].bV = b;
  137. else
  138. {
  139. cmSmgBox_t* b0 = p->locV[locIdx].bV;
  140. while( b0->link!=NULL )
  141. b0 = b0->link;
  142. b0->link = b;
  143. }
  144. return b;
  145. }
  146. cmSmgRC_t _cmSmgInitFromScore( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* scoreFn )
  147. {
  148. cmSmgRC_t rc = kOkSmgRC;
  149. cmScH_t scH = cmScNullHandle;
  150. unsigned i,j,k;
  151. if( cmScoreInitialize(ctx,&scH,scoreFn,44100.0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  152. return cmErrMsg(&p->err,kScoreFailSmgRC,"Score initializatio failed on '%s'.",cmStringNullGuard(scoreFn));
  153. p->scN = cmScoreEvtCount(scH);
  154. p->scV = cmMemAllocZ(cmSmgSc_t,p->scN);
  155. p->locN = cmScoreLocCount(scH);
  156. p->locV = cmMemAllocZ(cmSmgLoc_t,p->locN);
  157. // for each score location
  158. for(i=0,k=0; i<cmScoreLocCount(scH); ++i)
  159. {
  160. cmScoreLoc_t* l = cmScoreLoc(scH,i);
  161. // for each event in location i
  162. for(j=0; j<l->evtCnt; ++j)
  163. {
  164. const cmScoreEvt_t* e = l->evtArray[j];
  165. switch( e->type)
  166. {
  167. case kBarEvtScId:
  168. case kNonEvtScId:
  169. {
  170. unsigned flags = e->type==kNonEvtScId ? kNoteSmgFl : kBarSmgFl;
  171. cmChar_t* text = NULL;
  172. assert( k < p->scN );
  173. p->scV[k].type = e->type;
  174. p->scV[k].csvEventId = e->csvEventId;
  175. p->scV[k].locIdx = i;
  176. if( e->type == kBarEvtScId )
  177. text = cmTsPrintfP(NULL,"%i",e->barNumb);
  178. else
  179. text = cmMemAllocStr( cmMidiToSciPitch( e->pitch, NULL, 0));
  180. p->scV[k].box = _cmSmgInsertBox(p, i, flags, e->csvEventId, text );
  181. k += 1;
  182. }
  183. break;
  184. }
  185. }
  186. }
  187. cmScoreFinalize(&scH);
  188. return rc;
  189. }
  190. cmSmgRC_t _cmSmgInitFromMidi( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* midiFn )
  191. {
  192. cmSmgRC_t rc = kOkSmgRC;
  193. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  194. unsigned i,j;
  195. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  196. return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file open failed on '%s'.",cmStringNullGuard(midiFn));
  197. const cmMidiTrackMsg_t** mV = cmMidiFileMsgArray(mfH);
  198. unsigned mN = cmMidiFileMsgCount(mfH);
  199. p->mV = cmMemAllocZ(cmSmgMidi_t,mN);
  200. p->mN = mN;
  201. for(i=0,j=0; i<mN; ++i)
  202. if( (mV[i]!=NULL) && cmMidiIsChStatus(mV[i]->status) && cmMidiIsNoteOn(mV[i]->status) && (mV[i]->u.chMsgPtr->d1>0) )
  203. {
  204. p->mV[j].uid = mV[i]->uid;
  205. p->mV[j].pitch = mV[i]->u.chMsgPtr->d0;
  206. p->mV[j].vel = mV[i]->u.chMsgPtr->d1;
  207. }
  208. cmMidiFileClose(&mfH);
  209. return rc;
  210. }
  211. cmSmgRC_t cmScoreMatchGraphicAlloc( cmCtx_t* ctx, cmSmgH_t* hp, const cmChar_t* scoreFn, const cmChar_t* midiFn )
  212. {
  213. cmSmgRC_t rc;
  214. if((rc = cmScoreMatchGraphicFree(hp)) != kOkSmgRC )
  215. return rc;
  216. cmSmg_t* p = cmMemAllocZ(cmSmg_t,1);
  217. cmErrSetup(&p->err,&ctx->rpt,"ScoreMatchGraphic");
  218. if((rc = _cmSmgInitFromScore(ctx,p,scoreFn)) != kOkSmgRC )
  219. goto errLabel;
  220. if((rc = _cmSmgInitFromMidi(ctx,p,midiFn)) != kOkSmgRC )
  221. goto errLabel;
  222. hp->h = p;
  223. errLabel:
  224. if( rc != kOkSmgRC )
  225. _cmSmgFree(p);
  226. return rc;
  227. }
  228. cmSmgRC_t cmScoreMatchGraphicFree( cmSmgH_t* hp )
  229. {
  230. cmSmgRC_t rc = kOkSmgRC;
  231. if(hp==NULL || cmScoreMatchGraphicIsValid(*hp)==false)
  232. return kOkSmgRC;
  233. cmSmg_t* p = _cmSmgHandleToPtr(*hp);
  234. if((rc = _cmSmgFree(p)) != kOkSmgRC )
  235. return rc;
  236. hp->h = NULL;
  237. return rc;
  238. }
  239. bool cmScoreMatchGraphic( cmSmgH_t h )
  240. { return h.h != NULL; }
  241. cmSmgRC_t cmScoreMatchGraphicInsertMidi( cmSmgH_t h, unsigned midiUid, unsigned midiPitch, unsigned midiVel, unsigned csvScoreEventId )
  242. {
  243. cmSmg_t* p = _cmSmgHandleToPtr(h);
  244. unsigned i,j;
  245. // for each MIDI event
  246. for(i=0; i<p->mN; ++i)
  247. if( p->mV[i].uid == midiUid )
  248. {
  249. // for each score record
  250. for(j=0; j<p->scN; ++j)
  251. if( p->scV[j].csvEventId == csvScoreEventId )
  252. {
  253. cmSmgMatch_t* m = cmMemAllocZ(cmSmgMatch_t,1);
  254. m->score = p->scV + j;
  255. if( p->mV[i].matchV == NULL )
  256. p->mV[i].matchV = m;
  257. else
  258. {
  259. cmSmgMatch_t* m0 = p->mV[i].matchV;
  260. while( m0->link != NULL )
  261. m0 = m0->link;
  262. m0->link = m;
  263. }
  264. }
  265. return cmErrMsg(&p->err,kScoreFailSmgRC,"The score csv event id %i not found,",csvScoreEventId);
  266. }
  267. return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI uid %i not found.",midiUid);
  268. }
  269. // Create a box for each MIDI event and a line for each
  270. // match beyond the first.
  271. void _cmSmgResolveMidi( cmSmg_t* p )
  272. {
  273. unsigned prevLocIdx = 0;
  274. unsigned i;
  275. // for each midi record
  276. for(i=0; i<p->mN; ++i)
  277. {
  278. const cmSmgMatch_t* m = p->mV[i].matchV;
  279. // get the score location for this midi event
  280. unsigned locIdx = m==NULL ? prevLocIdx : m->score->locIdx;
  281. unsigned flags = kMidiSmgFl | (m==NULL ? kNoMatchSmgFl : 0);
  282. // set the text label for this event
  283. cmChar_t* text = cmMemAllocStr( cmMidiToSciPitch( p->mV[i].pitch, NULL, 0));
  284. // insert a box to represent this midi event
  285. cmSmgBox_t* box = _cmSmgInsertBox( p, locIdx, flags, p->mV[i].uid, text );
  286. prevLocIdx = locIdx;
  287. // if this midi event matched to multiple score positions
  288. if( m != NULL && m->link != NULL )
  289. {
  290. // insert a line for each match after the first
  291. m = m->link;
  292. for(; m!=NULL; m=m->link )
  293. {
  294. cmSmgLine_t* l = cmMemAllocZ(cmSmgLine_t,1);
  295. l->b0 = box;
  296. l->b1 = m->score->box;
  297. l->link = p->lines;
  298. p->lines = l;
  299. }
  300. }
  301. }
  302. }
  303. void _cmSmgLayout( cmSmg_t* p )
  304. {
  305. unsigned i;
  306. unsigned bordX = 5;
  307. unsigned bordY = 5;
  308. unsigned boxH = 30;
  309. unsigned boxW = 30;
  310. unsigned top = boxH + bordY;
  311. unsigned left = bordX;
  312. for(i=0; i<p->locN; ++i)
  313. {
  314. cmSmgLoc_t* l = p->locV + i;
  315. cmSmgBox_t* b = l->bV;
  316. for(; b!=NULL; b=b->link)
  317. {
  318. if( cmIsFlag(b->flags,kBarSmgFl) )
  319. b->top = bordY;
  320. else
  321. {
  322. b->top = top;
  323. top += boxH + bordY;
  324. }
  325. b->left = left;
  326. b->width = boxW;
  327. b->height = boxH;
  328. }
  329. left += boxW + bordX;
  330. }
  331. }
  332. void _cmSmgSvgSize( cmSmg_t* p, unsigned* widthRef, unsigned* heightRef )
  333. {
  334. unsigned i;
  335. unsigned maxWidth = 0;
  336. unsigned maxHeight = 0;
  337. for(i=0; i<p->locN; ++i)
  338. {
  339. cmSmgBox_t* b = p->locV[i].bV;
  340. while( b != NULL )
  341. {
  342. if( b->left + b->width > maxWidth )
  343. maxWidth = b->left + b->width;
  344. if( b->top + b->height > maxHeight )
  345. maxHeight = b->top + b->height;
  346. }
  347. }
  348. *widthRef = maxWidth;
  349. *heightRef = maxHeight;
  350. }
  351. cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn )
  352. {
  353. cmSmg_t* p = _cmSmgHandleToPtr(h);
  354. cmFileH_t fH = cmFileNullHandle;
  355. unsigned svgHeight = 0;
  356. unsigned svgWidth = 0;
  357. unsigned i;
  358. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  359. return cmErrMsg(&p->err,kFileFailScRC,"Graphic file create failed for '%s'.",cmStringNullGuard(fn));
  360. _cmSmgSvgSize(p,&svgWidth,&svgHeight);
  361. 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);
  362. for(i=0; i<p->locN; ++i)
  363. {
  364. cmSmgBox_t* b = p->locV[i].bV;
  365. while( b != NULL )
  366. {
  367. const cmChar_t* classStr = "score";
  368. if( cmIsFlag(b->flags,kMidiSmgFl) )
  369. classStr = "midi";
  370. if( cmIsFlag(b->flags,kNoMatchSmgFl) )
  371. if( cmIsFlag(b->flags,kMidiSmgFl) )
  372. classStr = "midi_miss";
  373. if( cmIsFlag(b->flags,kNoMatchSmgFl) )
  374. if( cmIsFlag(b->flags,kNoteSmgFl) )
  375. classStr = "score_miss";
  376. if( cmIsFlag(b->flags,kBarSmgFl) )
  377. classStr = "bar";
  378. 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 )
  379. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file output.");
  380. if( b->text != NULL )
  381. {
  382. unsigned tx = b->left + b->width/2;
  383. unsigned ty = b->top + 20; //g->height/2;
  384. if( cmFilePrintf(fH,"<text x=\"%i\" y=\"%i\" text-anchor=\"middle\" class=\"stext\">%s</text>\n",tx,ty,b->text) != kOkFileRC )
  385. return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file output.");
  386. }
  387. }
  388. }
  389. cmFilePrint(fH,"</svg>\n</body>\n</html>\n");
  390. cmFileClose(&fH);
  391. return kOkSmgRC;
  392. }