libcm is a C development framework with an emphasis on audio signal processing applications.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

cmXScore.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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 "cmXml.h"
  11. #include "cmText.h"
  12. #include "cmXScore.h"
  13. #include "cmTime.h"
  14. #include "cmMidi.h"
  15. cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
  16. enum
  17. {
  18. kRestXsFl = 0x0001,
  19. kGraceXsFl = 0x0002,
  20. kDotXsFl = 0x0004
  21. };
  22. typedef struct cmXsNote_str
  23. {
  24. unsigned flags;
  25. unsigned pitch; // midi pitch
  26. unsigned duration; // duration in ticks
  27. unsigned rvalue; // 1/type = rythmic value (1/4=quarter note, 1/8=eighth note, ...)
  28. struct cmXsNote_str* link;
  29. } cmXsNote_t;
  30. typedef struct cmXsVoice_str
  31. {
  32. unsigned id; // Voice id
  33. cmXsNote_t* noteL; // List of notes in this voice
  34. struct cmXsVoice_str* link; // Link to other voices in this measure
  35. } cmXsVoice_t;
  36. typedef struct cmXsMeas_str
  37. {
  38. unsigned number; // Measure number
  39. unsigned divisions;
  40. unsigned beats;
  41. unsigned beat_type;
  42. cmXsVoice_t* voiceL; // List of voices in this measure
  43. struct cmXsMeas_str* link; // Link to other measures in this part.
  44. } cmXsMeas_t;
  45. typedef struct cmXsPart_str
  46. {
  47. const cmChar_t* idStr; // Id of this part
  48. cmXsMeas_t* measL; // List of measures in this part.
  49. struct cmXsPart_str* link; // Link to other parts in this score
  50. } cmXsPart_t;
  51. typedef struct
  52. {
  53. cmErr_t err;
  54. cmXmlH_t xmlH;
  55. cmLHeapH_t lhH;
  56. cmXsPart_t* partL;
  57. } cmXScore_t;
  58. cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
  59. {
  60. cmXScore_t* p = (cmXScore_t*)h.h;
  61. assert( p != NULL );
  62. return p;
  63. }
  64. cmXsRC_t _cmXScoreFinalize( cmXScore_t* p )
  65. {
  66. cmXsRC_t rc = kOkXsRC;
  67. // release the XML file
  68. if( cmXmlIsValid(p->xmlH) )
  69. cmXmlFree( &p->xmlH );
  70. // release the local linked heap memory
  71. if( cmLHeapIsValid(p->lhH) )
  72. cmLHeapDestroy(&p->lhH);
  73. cmMemFree(p);
  74. return rc;
  75. }
  76. cmXsRC_t _cmXScoreMissingNode( cmXScore_t* p, const cmChar_t* label, const cmXmlAttr_t* attr )
  77. {
  78. if( attr == NULL )
  79. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s'.",label);
  80. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s' - Attribute:%s=%s",label,attr->label,attr->value);
  81. }
  82. cmXsRC_t _cmXScoreMissingAttribute( cmXScore_t* p, const cmXmlNode_t* np, const cmChar_t* attrLabel )
  83. {
  84. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML attribute '%s' from node '%s'.",attrLabel,np->label);
  85. }
  86. cmXsRC_t _cmXScoreParsePartList( cmXScore_t* p )
  87. {
  88. cmXsRC_t rc = kOkXsRC;
  89. cmXsPart_t* lastPartPtr = NULL;
  90. const cmXmlNode_t* xnp;
  91. // find the 'part-list'
  92. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part-list", NULL, 0)) == NULL )
  93. return _cmXScoreMissingNode(p,"part-list",NULL);
  94. const cmXmlNode_t* cnp = xnp->children;
  95. // for each child of the 'part-list'
  96. for(; cnp!=NULL; cnp=cnp->sibling)
  97. if( cmTextCmp( cnp->label, "score-part" ) == 0 )
  98. {
  99. const cmXmlAttr_t* a;
  100. // find the 'score-part' id
  101. if((a = cmXmlFindAttrib(cnp,"id")) == NULL )
  102. return _cmXScoreMissingAttribute(p,cnp,"id");
  103. // allocate a new part record
  104. cmXsPart_t* pp = cmLhAllocZ(p->lhH,cmXsPart_t,1);
  105. pp->idStr = a->value; // set the part id
  106. // link the new part record to the end of the part list
  107. if(lastPartPtr == NULL)
  108. p->partL = pp;
  109. else
  110. lastPartPtr->link = pp;
  111. lastPartPtr = pp;
  112. }
  113. return rc;
  114. }
  115. cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, unsigned* midiPitchRef )
  116. {
  117. cmXsRC_t rc = kOkXsRC;
  118. unsigned octave = 0;
  119. double alter = 0;
  120. const cmChar_t* step = NULL;
  121. if((step = cmXmlNodeValue(nnp,"pitch","step",NULL)) == NULL )
  122. return _cmXScoreMissingNode(p,"step",NULL);
  123. if((rc = cmXmlNodeUInt( nnp,&octave,"pitch","octave",NULL)) != kOkXmlRC )
  124. return _cmXScoreMissingNode(p,"octave",NULL);
  125. cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
  126. cmChar_t buf[3] = { *step, '0', '\0'};
  127. unsigned midi = cmSciPitchToMidi(buf);
  128. midi += (12 * octave);
  129. midi += alter;
  130. *midiPitchRef = midi;
  131. return rc;
  132. }
  133. unsigned _cmXScoreParseNoteType( const cmXmlNode_t* nnp )
  134. {
  135. typedef struct map_str
  136. {
  137. unsigned rvalue;
  138. const cmChar_t* label;
  139. } map_t;
  140. map_t mapV[] =
  141. {
  142. { 1, "whole" },
  143. { 2, "half" },
  144. { 4, "quarter" },
  145. { 8, "eighth" },
  146. { 16,"16th"},
  147. { 32,"32nd"},
  148. { 64,"64th"},
  149. {128,"128th"},
  150. {0,""}
  151. };
  152. if( cmXmlNodeHasChild(nnp,"type") )
  153. {
  154. const cmChar_t* str;
  155. if((str = cmXmlNodeValue(nnp,"type",NULL)) == NULL)
  156. {
  157. unsigned i;
  158. for(i=0; mapV[i].rvalue!=0; ++i)
  159. if( cmTextCmp(mapV[i].label,str) == 0 )
  160. return mapV[i].rvalue;
  161. }
  162. }
  163. return 0;
  164. }
  165. cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
  166. {
  167. cmXsVoice_t* v = meas->voiceL;
  168. for(; v!=NULL; v=v->link)
  169. if( v->id == voiceId )
  170. return v;
  171. return NULL;
  172. }
  173. cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
  174. {
  175. cmXsVoice_t* v;
  176. if((v = _cmXScoreIdToVoice(meas,voiceId)) == NULL)
  177. {
  178. v = cmLhAllocZ(p->lhH,cmXsVoice_t,1);
  179. v->id = voiceId;
  180. if( meas->voiceL == NULL )
  181. meas->voiceL = v;
  182. else
  183. {
  184. cmXsVoice_t* vp = meas->voiceL;
  185. while( vp->link!=NULL )
  186. vp = vp->link;
  187. vp->link = v;
  188. }
  189. }
  190. if( v->noteL == NULL )
  191. v->noteL = note;
  192. else
  193. {
  194. cmXsNote_t* n = v->noteL;
  195. while( n != NULL )
  196. n = n->link;
  197. n->link = note;
  198. }
  199. return kOkXsRC;
  200. }
  201. cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* nnp)
  202. {
  203. cmXsRC_t rc = kOkXsRC;
  204. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  205. unsigned voiceId;
  206. // get the voice id for this node
  207. if( cmXmlNodeUInt(nnp,&voiceId,"voice",NULL) != kOkXmlRC )
  208. return _cmXScoreMissingNode(p,"voice",NULL);
  209. // if this note has a pitch
  210. if( cmXmlNodeHasChild(nnp,"pitch") )
  211. if((rc = _cmXScoreParsePitch(p,nnp,&note->pitch)) != kOkXsRC )
  212. return rc;
  213. // is 'rest'
  214. if( cmXmlNodeHasChild(nnp,"rest") )
  215. note->flags |= kRestXsFl;
  216. // is 'grace'
  217. if( cmXmlNodeHasChild(nnp,"grace") )
  218. note->flags |= kGraceXsFl;
  219. // is 'dot'
  220. if( cmXmlNodeHasChild(nnp,"dot") )
  221. note->flags |= kDotXsFl;
  222. if((note->rvalue = _cmXScoreParseNoteType(nnp)) == 0 )
  223. return _cmXScoreMissingNode(nnp,"type",NULL);
  224. }
  225. cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t* mnp)
  226. {
  227. cmXsRC_t rc = kOkXsRC;
  228. // allocate the 'measure' record
  229. cmXsMeas_t* meas = cmLhAllocZ(p->lhH,cmXsMeas_t,1);
  230. const cmXmlNode_t* np;
  231. // get measure number
  232. if( cmXmlAttrUInt(mnp,"number", &meas->number) != kOkXmlRC )
  233. return _cmXScoreMissingAttribute(p,mnp,"number");
  234. if( pp->measL == NULL )
  235. pp->measL = meas;
  236. else
  237. {
  238. cmXsMeas_t* m = pp->measL;
  239. while( m->link != NULL )
  240. m = m->link;
  241. m->link = meas;
  242. }
  243. // get measure attributes node
  244. if((np = cmXmlSearch(mnp,"attributes",NULL,0)) == NULL)
  245. return rc; // (this measure does not have any attributes)
  246. cmXmlNodeUInt(np,&meas->divisions,"divisions",NULL);
  247. cmXmlNodeUInt(np,&meas->beats,"time","beats",NULL);
  248. cmXmlNodeUInt(np,&meas->beat_type,"time","beat-type",NULL);
  249. int tick = 0;
  250. np = mnp->children;
  251. for(; np!=NULL; np=np->sibling)
  252. if( cmTextCmp(np->label,"note") )
  253. {
  254. rc = _cmXScoreParseNote(p,meas,mnp);
  255. }
  256. else
  257. if( cmTextCmp(np->label,"backup") )
  258. {
  259. }
  260. return rc;
  261. }
  262. cmXsRC_t _cmXScoreParsePart( cmXScore_t* p, cmXsPart_t* pp )
  263. {
  264. cmXsRC_t rc = kOkXsRC;
  265. const cmXmlNode_t* xnp;
  266. cmXmlAttr_t partAttr = { "id", pp->idStr };
  267. // find the 'part'
  268. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part", &partAttr, 1)) == NULL )
  269. return _cmXScoreMissingNode(p,"part",&partAttr);
  270. // for each child of this part - find each measure
  271. const cmXmlNode_t* cnp = xnp->children;
  272. for(; cnp!=NULL; cnp=cnp->sibling)
  273. if( cmTextCmp(cnp->label,"measure") == 0 )
  274. if((rc = _cmXScoreParseMeasure(p,pp,cnp)) != kOkXsRC )
  275. return rc;
  276. return rc;
  277. }
  278. cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
  279. {
  280. cmXsRC_t rc = kOkXsRC;
  281. if((rc = cmXScoreFinalize(hp)) != kOkXsRC )
  282. return rc;
  283. cmXScore_t* p = cmMemAllocZ(cmXScore_t,1);
  284. cmErrSetup(&p->err,&ctx->rpt,"XScore");
  285. // create a local linked heap
  286. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  287. return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
  288. // open the music xml file
  289. if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
  290. {
  291. rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn));
  292. goto errLabel;
  293. }
  294. // parse the part-list
  295. if((rc = _cmXScoreParsePartList( p )) != kOkXsRC )
  296. goto errLabel;
  297. cmXsPart_t* pp = p->partL;
  298. for(; pp!=NULL; pp=pp->link)
  299. if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC )
  300. goto errLabel;
  301. errLabel:
  302. if( rc != kOkXsRC )
  303. _cmXScoreFinalize(p);
  304. else
  305. hp->h = p;
  306. return rc;
  307. }
  308. cmXsRC_t cmXScoreFinalize( cmXsH_t* hp )
  309. {
  310. cmXsRC_t rc = kOkXsRC;
  311. if( hp == NULL || cmXScoreIsValid(*hp)==false )
  312. return kOkXsRC;
  313. cmXScore_t* p = _cmXScoreHandleToPtr(*hp);
  314. if((rc = _cmXScoreFinalize(p)) != kOkXsRC )
  315. return rc;
  316. hp->h = NULL;
  317. return rc;
  318. }
  319. bool cmXScoreIsValid( cmXsH_t h )
  320. { return h.h != NULL; }
  321. void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt )
  322. {
  323. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  324. cmXsPart_t* pp = p->partL;
  325. for(; pp!=NULL; pp=pp->link)
  326. {
  327. cmRptPrintf(rpt,"Part:%s\n",pp->idStr);
  328. const cmXsMeas_t* meas = pp->measL;
  329. for(; meas!=NULL; meas=meas->link)
  330. cmRptPrintf(rpt," %i : div:%i beat:%i beat-type:%i\n",meas->number,meas->divisions,meas->beats,meas->beat_type);
  331. }
  332. }
  333. cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
  334. {
  335. cmXsRC_t rc;
  336. cmXsH_t h = cmXsNullHandle;
  337. if((rc = cmXScoreInitialize( ctx, &h, fn)) != kOkXsRC )
  338. return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
  339. cmXScoreReport(h,&ctx->rpt);
  340. return cmXScoreFinalize(&h);
  341. }