libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

cmXScore.c 29KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. #include "cmLex.h"
  16. #include "cmCsv.h"
  17. cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
  18. enum
  19. {
  20. kSectionXsFl = 0x0001, // rvalue holds section number
  21. kBarXsFl = 0x0002,
  22. kRestXsFl = 0x0004,
  23. kGraceXsFl = 0x0008,
  24. kDotXsFl = 0x0010,
  25. kChordXsFl = 0x0020,
  26. kDynXsFl = 0x0040,
  27. kEvenXsFl = 0x0080,
  28. kTempoXsFl = 0x0100,
  29. kHeelXsFl = 0x0200,
  30. kPedalDnXsFl = 0x0400,
  31. kPedalUpXsFl = 0x0800,
  32. kPedalUpDnXsFl = 0x1000,
  33. kMetronomeXsFl = 0x2000 // duration holds BPM
  34. };
  35. typedef struct cmXsNote_str
  36. {
  37. unsigned flags; // See k???XsFl
  38. unsigned pitch; // midi pitch
  39. unsigned tick; //
  40. unsigned duration; // duration in ticks
  41. double rvalue; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...)
  42. const cmChar_t* tvalue; // text value
  43. struct cmXsNote_str* mlink; // measure note list
  44. struct cmXsNote_str* slink; // time sorted event list
  45. } cmXsNote_t;
  46. typedef struct cmXsVoice_str
  47. {
  48. unsigned id; // Voice id
  49. cmXsNote_t* noteL; // List of notes in this voice
  50. struct cmXsVoice_str* link; // Link to other voices in this measure
  51. } cmXsVoice_t;
  52. typedef struct cmXsMeas_str
  53. {
  54. unsigned number; // Measure number
  55. unsigned divisions; // ticks-per-quarter-note
  56. unsigned beats; // beats per measure
  57. unsigned beat_type; // whole/half/quarter/eighth ...
  58. cmXsVoice_t* voiceL; // List of voices in this measure
  59. cmXsNote_t* noteL; // List of time sorted notes in this measure
  60. struct cmXsMeas_str* link; // Link to other measures in this part.
  61. } cmXsMeas_t;
  62. typedef struct cmXsPart_str
  63. {
  64. const cmChar_t* idStr; // Id of this part
  65. cmXsMeas_t* measL; // List of measures in this part.
  66. struct cmXsPart_str* link; // Link to other parts in this score
  67. } cmXsPart_t;
  68. typedef struct
  69. {
  70. cmErr_t err;
  71. cmXmlH_t xmlH;
  72. cmLHeapH_t lhH;
  73. cmXsPart_t* partL;
  74. cmCsvH_t csvH;
  75. } cmXScore_t;
  76. cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
  77. {
  78. cmXScore_t* p = (cmXScore_t*)h.h;
  79. assert( p != NULL );
  80. return p;
  81. }
  82. cmXsRC_t _cmXScoreFinalize( cmXScore_t* p )
  83. {
  84. cmXsRC_t rc = kOkXsRC;
  85. // release the XML file
  86. cmXmlFree( &p->xmlH );
  87. // release the local linked heap memory
  88. cmLHeapDestroy(&p->lhH);
  89. // release the CSV output object
  90. cmCsvFinalize(&p->csvH);
  91. cmMemFree(p);
  92. return rc;
  93. }
  94. cmXsRC_t _cmXScoreMissingNode( cmXScore_t* p, const cmXmlNode_t* parent, const cmChar_t* label )
  95. {
  96. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s'. Parent line:%i",label,parent->line);
  97. }
  98. cmXsRC_t _cmXScoreMissingAttribute( cmXScore_t* p, const cmXmlNode_t* np, const cmChar_t* attrLabel )
  99. {
  100. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML attribute '%s' from node '%s'.",attrLabel,np->label);
  101. }
  102. cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
  103. {
  104. cmXsVoice_t* v = meas->voiceL;
  105. for(; v!=NULL; v=v->link)
  106. if( v->id == voiceId )
  107. return v;
  108. return NULL;
  109. }
  110. cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
  111. {
  112. cmXsVoice_t* v;
  113. // get the voice recd
  114. if((v = _cmXScoreIdToVoice(meas,voiceId)) == NULL)
  115. {
  116. // the voice recd doesn't exist for this voiceId - allocate one
  117. v = cmLhAllocZ(p->lhH,cmXsVoice_t,1);
  118. v->id = voiceId;
  119. // add the voice record to the meas->voiceL
  120. if( meas->voiceL == NULL )
  121. meas->voiceL = v;
  122. else
  123. {
  124. cmXsVoice_t* vp = meas->voiceL;
  125. while( vp->link!=NULL )
  126. vp = vp->link;
  127. vp->link = v;
  128. }
  129. }
  130. // add the note record to the end of meas->voiceL
  131. if( v->noteL == NULL )
  132. v->noteL = note;
  133. else
  134. {
  135. cmXsNote_t* n = v->noteL;
  136. while( n->mlink != NULL )
  137. n = n->mlink;
  138. n->mlink = note;
  139. }
  140. return kOkXsRC;
  141. }
  142. cmXsRC_t _cmXScoreParsePartList( cmXScore_t* p )
  143. {
  144. cmXsRC_t rc = kOkXsRC;
  145. cmXsPart_t* lastPartPtr = NULL;
  146. const cmXmlNode_t* xnp;
  147. // find the 'part-list'
  148. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part-list", NULL, 0)) == NULL )
  149. return _cmXScoreMissingNode(p,cmXmlRoot(p->xmlH),"part-list");
  150. const cmXmlNode_t* cnp = xnp->children;
  151. // for each child of the 'part-list'
  152. for(; cnp!=NULL; cnp=cnp->sibling)
  153. if( cmTextCmp( cnp->label, "score-part" ) == 0 )
  154. {
  155. const cmXmlAttr_t* a;
  156. // find the 'score-part' id
  157. if((a = cmXmlFindAttrib(cnp,"id")) == NULL )
  158. return _cmXScoreMissingAttribute(p,cnp,"id");
  159. // allocate a new part record
  160. cmXsPart_t* pp = cmLhAllocZ(p->lhH,cmXsPart_t,1);
  161. pp->idStr = a->value; // set the part id
  162. // link the new part record to the end of the part list
  163. if(lastPartPtr == NULL)
  164. p->partL = pp;
  165. else
  166. lastPartPtr->link = pp;
  167. lastPartPtr = pp;
  168. }
  169. return rc;
  170. }
  171. cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, unsigned* midiPitchRef )
  172. {
  173. cmXsRC_t rc = kOkXsRC;
  174. unsigned octave = 0;
  175. double alter = 0;
  176. const cmChar_t* step = NULL;
  177. if((step = cmXmlNodeValue(nnp,"pitch","step",NULL)) == NULL )
  178. return _cmXScoreMissingNode(p,nnp,"step");
  179. if((rc = cmXmlNodeUInt( nnp,&octave,"pitch","octave",NULL)) != kOkXmlRC )
  180. return _cmXScoreMissingNode(p,nnp,"octave");
  181. cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
  182. cmChar_t buf[3] = { *step, '0', '\0'};
  183. unsigned midi = cmSciPitchToMidi(buf);
  184. midi += (12 * octave);
  185. midi += alter;
  186. *midiPitchRef = midi;
  187. return rc;
  188. }
  189. cmXsRC_t _cmXScoreParseNoteRValue( cmXScore_t* p, const cmXmlNode_t* nnp, const cmChar_t* label, double* rvalueRef )
  190. {
  191. typedef struct map_str
  192. {
  193. double rvalue;
  194. const cmChar_t* label;
  195. } map_t;
  196. map_t mapV[] =
  197. {
  198. {-1.0, "measure" }, // whole measure rest
  199. { 0.5, "breve" }, // double whole
  200. { 1.0, "whole" },
  201. { 2.0, "half" },
  202. { 4.0, "quarter" },
  203. { 8.0, "eighth" },
  204. {16.0, "16th" },
  205. {32.0, "32nd" },
  206. { 64.0, "64th" },
  207. {128.0, "128th" },
  208. { 0.0, "" }
  209. };
  210. const cmChar_t* str;
  211. // get the XML rvalue label
  212. if((str = cmXmlNodeValue(nnp,label,NULL)) == NULL)
  213. {
  214. if((nnp = cmXmlSearch(nnp,"rest",NULL,0)) != NULL )
  215. {
  216. const cmXmlAttr_t* a;
  217. if((a = cmXmlFindAttrib(nnp,"measure")) != NULL && cmTextCmp(a->value,"yes")==0)
  218. {
  219. *rvalueRef = -1;
  220. return kOkXsRC;
  221. }
  222. }
  223. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'beat-unit' metronome value is missing on line %i.",nnp->line);
  224. }
  225. unsigned i;
  226. // lookup the rvalue numeric value from the mapV[] table
  227. for(i=0; mapV[i].rvalue!=0; ++i)
  228. if( cmTextCmp(mapV[i].label,str) == 0 )
  229. {
  230. *rvalueRef = mapV[i].rvalue;
  231. return kOkXsRC;
  232. }
  233. // the rvalue label was not found
  234. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unknown rvalue type='%s'.",str);
  235. }
  236. cmXsRC_t _cmXScoreParseColor( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* note )
  237. {
  238. cmXsRC_t rc = kOkXsRC;
  239. const cmXmlAttr_t* a;
  240. typedef struct map_str
  241. {
  242. unsigned value;
  243. const cmChar_t* label;
  244. } map_t;
  245. map_t mapV[] =
  246. {
  247. { kEvenXsFl, "#0000FF" }, // blue (even)
  248. { kTempoXsFl, "#00FF00" }, // green (tempo)
  249. { kDynXsFl, "#FF0000" }, // red (dynamics)
  250. { kTempoXsFl | kEvenXsFl, "#00FFFF" }, // green + blue (turquoise)
  251. { kDynXsFl | kEvenXsFl, "#FF00FF" }, // red + blue
  252. { kDynXsFl | kEvenXsFl, "#FF0CF7" }, // magenta (even+dyn)
  253. { kDynXsFl | kTempoXsFl, "#FF7F00" }, // red + green (brown)
  254. { kTempoXsFl | kEvenXsFl | kDynXsFl, "#996633" }, // (purple)
  255. { kDynXsFl, "#FF6A03" }, // 176 orange (dynamics)
  256. { kEvenXsFl, "#2F00E8" }, // 1001 blue (even)
  257. { kTempoXsFl, "#01CD1F" }, // 1196 green (tempo)
  258. { kEvenXsFl, "#3600E8" }, // 1627 blue (even)
  259. { kDynXsFl | kTempoXsFl, "#9E8F15" }, // 8827 brown (dyn + tempo)
  260. { kEvenXsFl, "#2E00E6" }, // 5393 blue (even)
  261. { kEvenXsFl, "#2C00DD" }, // 5895 blue (even)
  262. { kDynXsFl, "#FF5B03" }, // 6498 orange (dyn)
  263. { kDynXsFl, "#FF6104" }, // 6896 orange
  264. { kEvenXsFl, "#2A00E6" }, // 7781 blue
  265. { kEvenXsFl, "#2300DD" }, // 8300 blue (even)
  266. { kTempoXsFl, "#03CD22" }, // 10820 green (tempo)
  267. { kEvenXsFl, "#3400DB" }, // 11627 blue (dyn)
  268. { -1, "" }
  269. };
  270. /*
  271. orange #FF6A03
  272. magenta #FF0CF7
  273. blue #2F00E8
  274. green #01CD1F
  275. gold #9E8F15
  276. green #03CD22
  277. */
  278. if((a = cmXmlFindAttrib(nnp, "color" )) != NULL )
  279. {
  280. unsigned i;
  281. for(i=0; mapV[i].value != -1; ++i)
  282. if( cmTextCmp(a->value,mapV[i].label) == 0 )
  283. {
  284. note->flags += mapV[i].value;
  285. break;
  286. }
  287. if( mapV[i].value == -1 )
  288. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The note color '%s' was not found on line %i.",a->value,nnp->line);
  289. }
  290. return rc;
  291. }
  292. cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* nnp, unsigned* tickRef )
  293. {
  294. cmXsRC_t rc = kOkXsRC;
  295. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  296. unsigned voiceId;
  297. // get the voice id for this node
  298. if( cmXmlNodeUInt(nnp,&voiceId,"voice",NULL) != kOkXmlRC )
  299. return _cmXScoreMissingNode(p,nnp,"voice");
  300. // if this note has a pitch
  301. if( cmXmlNodeHasChild(nnp,"pitch",NULL) )
  302. if((rc = _cmXScoreParsePitch(p,nnp,&note->pitch)) != kOkXsRC )
  303. return rc;
  304. // get the note duration
  305. cmXmlNodeUInt(nnp,&note->duration,"duration",NULL);
  306. // is 'rest'
  307. if( cmXmlNodeHasChild(nnp,"rest",NULL) )
  308. note->flags |= kRestXsFl;
  309. // is 'grace'
  310. if( cmXmlNodeHasChild(nnp,"grace",NULL) )
  311. note->flags |= kGraceXsFl;
  312. // is 'dot'
  313. if( cmXmlNodeHasChild(nnp,"dot",NULL) )
  314. note->flags |= kDotXsFl;
  315. // is 'chord'
  316. if( cmXmlNodeHasChild(nnp,"chord",NULL) )
  317. note->flags |= kChordXsFl;
  318. // has 'heel' mark
  319. if( cmXmlNodeHasChild(nnp,"notations","technical","heel",NULL) )
  320. note->flags |= kHeelXsFl;
  321. // set color coded flags
  322. if((rc = _cmXScoreParseColor(p, nnp, note )) != kOkXsRC )
  323. return rc;
  324. // get the note's rythmic value
  325. if((rc = _cmXScoreParseNoteRValue(p,nnp,"type",&note->rvalue)) != kOkXsRC )
  326. return rc;
  327. note->tick = *tickRef;
  328. if( cmIsNotFlag(note->flags,kChordXsFl) )
  329. *tickRef += note->duration;
  330. return _cmXScorePushNote(p, meas, voiceId, note );
  331. }
  332. cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned tick, unsigned duration, double rvalue, const cmChar_t* tvalue, unsigned flags )
  333. {
  334. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  335. unsigned voiceId = 0; // non-note's are always assigned to voiceId=0;
  336. note->tick = tick;
  337. note->flags = flags;
  338. note->rvalue = rvalue;
  339. note->tvalue = tvalue;
  340. note->duration = duration;
  341. return _cmXScorePushNote(p, meas, voiceId, note );
  342. }
  343. cmXsRC_t _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* dnp, unsigned tick)
  344. {
  345. cmXsRC_t rc = kOkXsRC;
  346. const cmXmlNode_t* np = NULL;
  347. const cmXmlAttr_t* a = NULL;
  348. unsigned flags = 0;
  349. int offset = 0;
  350. double rvalue = 0;
  351. const cmChar_t* tvalue = NULL;
  352. unsigned duration = 0;
  353. cmXmlNodeInt(dnp, &offset, "offset", NULL );
  354. // if this is a metronome direction
  355. if((np = cmXmlSearch( dnp, "metronome", NULL, 0)) != NULL )
  356. {
  357. if( cmXmlNodeUInt(np,&duration,"per-minute",NULL) != kOkXmlRC )
  358. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'per-minute' metronome value is missing on line %i.",np->line);
  359. if((rc = _cmXScoreParseNoteRValue(p,np,"beat-unit",&rvalue)) != kOkXsRC )
  360. return rc;
  361. flags = kMetronomeXsFl;
  362. }
  363. else
  364. // if this is a pedal direction
  365. if((np = cmXmlSearch( dnp, "pedal",NULL,0)) != NULL )
  366. {
  367. if((a = cmXmlFindAttrib(np,"type")) == NULL )
  368. return _cmXScoreMissingAttribute(p, np, "type" );
  369. if( cmTextCmp(a->value,"start") == 0 )
  370. flags = kPedalDnXsFl;
  371. else
  372. if( cmTextCmp(a->value,"change") == 0 )
  373. flags = kPedalUpDnXsFl;
  374. else
  375. if( cmTextCmp(a->value,"stop") == 0 )
  376. flags = kPedalUpXsFl;
  377. else
  378. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unrecognized pedal type:'%s'.",cmStringNullGuard(a->value));
  379. }
  380. else
  381. // if this is a 'words' direction
  382. if((np = cmXmlSearch( dnp, "words", NULL, 0)) != NULL )
  383. {
  384. if((a = cmXmlFindAttrib(np,"enclosure")) != NULL && cmTextCmp(a->value,"rectangle")==0 )
  385. {
  386. if( cmTextIsEmpty( tvalue = np->dataStr ) )
  387. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Section number is blank or missing on line %i.",np->line);
  388. flags = kSectionXsFl;
  389. }
  390. }
  391. return _cmXScorePushNonNote(p,meas,tick+offset,duration,rvalue,tvalue,flags);
  392. }
  393. cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t* mnp)
  394. {
  395. cmXsRC_t rc = kOkXsRC;
  396. const cmXmlNode_t* np = NULL;
  397. unsigned tick = 0;
  398. // allocate the 'measure' record
  399. cmXsMeas_t* meas = cmLhAllocZ(p->lhH,cmXsMeas_t,1);
  400. // get measure number
  401. if( cmXmlAttrUInt(mnp,"number", &meas->number) != kOkXmlRC )
  402. return _cmXScoreMissingAttribute(p,mnp,"number");
  403. if( pp->measL == NULL )
  404. pp->measL = meas;
  405. else
  406. {
  407. cmXsMeas_t* m = pp->measL;
  408. while( m->link != NULL )
  409. m = m->link;
  410. m->link = meas;
  411. }
  412. // get measure attributes node
  413. if((np = cmXmlSearch(mnp,"attributes",NULL,0)) != NULL)
  414. {
  415. cmXmlNodeUInt(np,&meas->divisions,"divisions",NULL);
  416. cmXmlNodeUInt(np,&meas->beats, "time","beats",NULL);
  417. cmXmlNodeUInt(np,&meas->beat_type,"time","beat-type",NULL);
  418. }
  419. // store the bar line
  420. if((rc = _cmXScorePushNonNote(p,meas,tick,0,0,NULL,kBarXsFl)) != kOkXsRC )
  421. return rc;
  422. np = mnp->children;
  423. // for each child of the 'meas' XML node
  424. for(; rc==kOkXsRC && np!=NULL; np=np->sibling)
  425. {
  426. // if this is a 'note' node
  427. if( cmTextCmp(np->label,"note") == 0 )
  428. {
  429. rc = _cmXScoreParseNote(p,meas,np,&tick);
  430. }
  431. else
  432. // if this is a 'backup' node
  433. if( cmTextCmp(np->label,"backup") == 0 )
  434. {
  435. unsigned backup;
  436. cmXmlNodeUInt(np,&backup,"duration",NULL);
  437. tick -= backup;
  438. }
  439. else
  440. // if this is a 'direction' node
  441. if( cmTextCmp(np->label,"direction") == 0 )
  442. {
  443. rc = _cmXScoreParseDirection(p,meas,np,tick);
  444. }
  445. }
  446. return rc;
  447. }
  448. cmXsRC_t _cmXScoreParsePart( cmXScore_t* p, cmXsPart_t* pp )
  449. {
  450. cmXsRC_t rc = kOkXsRC;
  451. const cmXmlNode_t* xnp;
  452. cmXmlAttr_t partAttr = { "id", pp->idStr };
  453. // find the 'part'
  454. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part", &partAttr, 1)) == NULL )
  455. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The part '%s' was not found.",pp->idStr);
  456. // for each child of this part - find each measure
  457. const cmXmlNode_t* cnp = xnp->children;
  458. for(; cnp!=NULL; cnp=cnp->sibling)
  459. if( cmTextCmp(cnp->label,"measure") == 0 )
  460. if((rc = _cmXScoreParseMeasure(p,pp,cnp)) != kOkXsRC )
  461. return rc;
  462. return rc;
  463. }
  464. cmXsNote_t* _cmXScoreInsertSortedNote( cmXsNote_t* s0, cmXsNote_t* np )
  465. {
  466. if( s0 == NULL )
  467. return np;
  468. if( np->tick < s0->tick )
  469. {
  470. np->slink = s0;
  471. return np;
  472. }
  473. cmXsNote_t* s1 = s0;
  474. cmXsNote_t* s2 = s0->slink;
  475. while( s2 != NULL )
  476. {
  477. if( s2->tick > np->tick )
  478. {
  479. s1->slink = np;
  480. np->slink = s2;
  481. return s0;
  482. }
  483. s1 = s2;
  484. s2 = s2->slink;
  485. }
  486. s1->slink = np;
  487. return s0;
  488. }
  489. void _cmXScoreSort( cmXScore_t* p )
  490. {
  491. cmXsPart_t* pp = p->partL;
  492. for(; pp!=NULL; pp=pp->link)
  493. {
  494. cmXsMeas_t* mp = pp->measL;
  495. for(; mp!=NULL; mp=mp->link)
  496. {
  497. // for each voice in this measure
  498. cmXsVoice_t* vp = mp->voiceL;
  499. for(; vp!=NULL; vp=vp->link)
  500. {
  501. cmXsNote_t* np = vp->noteL;
  502. for(; np!=NULL; np=np->mlink)
  503. mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np);
  504. }
  505. }
  506. }
  507. }
  508. cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
  509. {
  510. cmXsRC_t rc = kOkXsRC;
  511. if((rc = cmXScoreFinalize(hp)) != kOkXsRC )
  512. return rc;
  513. cmXScore_t* p = cmMemAllocZ(cmXScore_t,1);
  514. cmErrSetup(&p->err,&ctx->rpt,"XScore");
  515. // create a local linked heap
  516. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  517. return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
  518. // open the music xml file
  519. if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
  520. {
  521. rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn));
  522. goto errLabel;
  523. }
  524. //cmXmlPrint(p->xmlH,&ctx->rpt);
  525. // parse the part-list
  526. if((rc = _cmXScoreParsePartList( p )) != kOkXsRC )
  527. goto errLabel;
  528. // parse each score 'part'
  529. cmXsPart_t* pp = p->partL;
  530. for(; pp!=NULL; pp=pp->link)
  531. if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC )
  532. goto errLabel;
  533. // fill in the note->slink chain to link the notes in each measure in time order
  534. _cmXScoreSort(p);
  535. // CSV output initialize failed.
  536. if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC )
  537. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed.");
  538. errLabel:
  539. if( rc != kOkXsRC )
  540. _cmXScoreFinalize(p);
  541. else
  542. hp->h = p;
  543. return rc;
  544. }
  545. cmXsRC_t cmXScoreFinalize( cmXsH_t* hp )
  546. {
  547. cmXsRC_t rc = kOkXsRC;
  548. if( hp == NULL || cmXScoreIsValid(*hp)==false )
  549. return kOkXsRC;
  550. cmXScore_t* p = _cmXScoreHandleToPtr(*hp);
  551. if((rc = _cmXScoreFinalize(p)) != kOkXsRC )
  552. return rc;
  553. hp->h = NULL;
  554. return rc;
  555. }
  556. bool cmXScoreIsValid( cmXsH_t h )
  557. { return h.h != NULL; }
  558. void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note )
  559. {
  560. const cmChar_t* B = cmIsFlag(note->flags,kBarXsFl) ? "|" : "-";
  561. const cmChar_t* R = cmIsFlag(note->flags,kRestXsFl) ? "R" : "-";
  562. const cmChar_t* G = cmIsFlag(note->flags,kGraceXsFl) ? "G" : "-";
  563. const cmChar_t* D = cmIsFlag(note->flags,kDotXsFl) ? "D" : "-";
  564. const cmChar_t* C = cmIsFlag(note->flags,kChordXsFl) ? "C" : "-";
  565. const cmChar_t* e = cmIsFlag(note->flags,kEvenXsFl) ? "e" : "-";
  566. const cmChar_t* d = cmIsFlag(note->flags,kDynXsFl) ? "d" : "-";
  567. const cmChar_t* t = cmIsFlag(note->flags,kTempoXsFl) ? "t" : "-";
  568. const cmChar_t* P = cmIsFlag(note->flags,kPedalDnXsFl) ? "V" : "-";
  569. P = cmIsFlag(note->flags,kPedalUpXsFl) ? "^" : P;
  570. P = cmIsFlag(note->flags,kPedalUpDnXsFl) ? "X" : P;
  571. const cmChar_t* N = note->pitch==0 ? " " : cmMidiToSciPitch( note->pitch, NULL, 0 );
  572. cmRptPrintf(rpt," %5i %5i %4.1f %3s %s%s%s%s%s%s%s%s%s",note->tick,note->duration,note->rvalue,N,B,R,G,D,C,e,d,t,P);
  573. if( cmIsFlag(note->flags,kSectionXsFl) )
  574. cmRptPrintf(rpt," %i",cmStringNullGuard(note->tvalue));
  575. printf("\n");
  576. }
  577. /*
  578. kMidiFileIdColScIdx= 0,
  579. kTypeLabelColScIdx = 3,
  580. kDSecsColScIdx = 4,
  581. kSecsColScIdx = 5,
  582. kD0ColScIdx = 9,
  583. kD1ColScIdx = 10,
  584. kPitchColScIdx = 11,
  585. kBarColScIdx = 13,
  586. kSkipColScIdx = 14,
  587. kEvenColScIdx = 15,
  588. kGraceColScIdx = 16,
  589. kTempoColScIdx = 17,
  590. kFracColScIdx = 18,
  591. kDynColScIdx = 19,
  592. kSectionColScIdx = 20,
  593. kRecdPlayColScIdx = 21,
  594. kRemarkColScIdx = 22
  595. */
  596. cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p )
  597. {
  598. const cmChar_t* s[] =
  599. {
  600. "id","trk","evt","opcode","dticks","micros","status",
  601. "meta","ch","d0","d1","arg0","arg1","bar","skip",
  602. "even","grace","tempo","t frac","dyn","section","play_recd","remark",NULL
  603. };
  604. cmCsvCell_t* lcp = NULL;
  605. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymText(p->csvH,s[0]), 0, 0 ) != kOkCsvRC )
  606. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  607. unsigned i;
  608. for(i=1; s[i]!=NULL; ++i)
  609. {
  610. if( cmCsvInsertTextColAfter(p->csvH, lcp, &lcp, s[i], 0 ) != kOkCsvRC )
  611. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV error inserting CSV title %i.\n",i);
  612. }
  613. return kOkXsRC;
  614. }
  615. cmXsRC_t _cmXScoreWriteCsvBlankCols( cmXScore_t* p, unsigned cnt, cmCsvCell_t** leftCellPtrPtr )
  616. {
  617. unsigned i;
  618. for(i=0; i<cnt; ++i)
  619. if( cmCsvInsertTextColAfter(p->csvH,*leftCellPtrPtr,leftCellPtrPtr,0,0) != kOkCsvRC )
  620. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV output failed on blank column.");
  621. return kOkCsvRC;
  622. }
  623. cmXsRC_t _cmXScoreWriteCsvRow(
  624. cmXScore_t* p,
  625. unsigned rowIdx,
  626. unsigned bar,
  627. const cmChar_t* sectionStr,
  628. const cmChar_t* opCodeStr,
  629. double dsecs,
  630. double secs,
  631. unsigned d0,
  632. unsigned d1,
  633. unsigned pitch,
  634. double frac,
  635. unsigned flags )
  636. {
  637. cmXsRC_t rc = kOkXsRC;
  638. cmCsvCell_t* lcp = NULL;
  639. // append an empty row to the CSV object
  640. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymUInt(p->csvH, rowIdx ), 0, 0 ) != kOkCsvRC )
  641. {
  642. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  643. goto errLabel;
  644. }
  645. /*
  646. // col 0 : blanks
  647. if( cmCsvInsertUIntColAfter(p->csvH, lcp, &lcp, rowIdx, 0 ) != kOkCsvRC )
  648. {
  649. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output row index failed.");
  650. goto errLabel;
  651. }
  652. */
  653. // cols 1,2
  654. if((rc = _cmXScoreWriteCsvBlankCols(p,2,&lcp)) != kOkXsRC )
  655. goto errLabel;
  656. // col 3 : output the opcode
  657. if( cmCsvInsertTextColAfter(p->csvH,lcp,&lcp,opCodeStr,0) != kOkCsvRC )
  658. {
  659. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on opcode label.");
  660. goto errLabel;
  661. }
  662. // col 4 : dsecs
  663. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,dsecs,0) != kOkCsvRC )
  664. {
  665. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dsecs'.");
  666. goto errLabel;
  667. }
  668. // col 5 : secs
  669. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,secs,0) != kOkCsvRC )
  670. {
  671. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'secs'.");
  672. goto errLabel;
  673. }
  674. // cols 6,7,8 blanks
  675. if((rc = _cmXScoreWriteCsvBlankCols(p,3,&lcp)) != kOkXsRC )
  676. goto errLabel;
  677. // col 9 : d0
  678. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d0,0) != kOkCsvRC )
  679. {
  680. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  681. goto errLabel;
  682. }
  683. // col 10 : d1
  684. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d1,0) != kOkCsvRC )
  685. {
  686. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd1'.");
  687. goto errLabel;
  688. }
  689. // col 11 : pitch
  690. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,pitch,0) != kOkCsvRC )
  691. {
  692. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  693. goto errLabel;
  694. }
  695. // col 12 : blanks
  696. if((rc = _cmXScoreWriteCsvBlankCols(p,1 + (cmIsFlag(flags,kBarXsFl) ? 0 : 1), &lcp)) != kOkXsRC )
  697. goto errLabel;
  698. // col 13 : bar number
  699. if( cmIsFlag(flags,kBarXsFl) )
  700. {
  701. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,bar,0) != kOkCsvRC )
  702. {
  703. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  704. goto errLabel;
  705. }
  706. }
  707. // col 14 : skip (blank for now)
  708. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  709. goto errLabel;
  710. // col 15: even
  711. if( cmCsvInsertTextColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kEvenXsFl) ? "e" : "",0) != kOkCsvRC )
  712. {
  713. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  714. goto errLabel;
  715. }
  716. // col 16: grace
  717. if( cmCsvInsertTextColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kGraceXsFl) ? "g" : "",0) != kOkCsvRC )
  718. {
  719. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  720. goto errLabel;
  721. }
  722. // col 17: tempo
  723. if( cmCsvInsertTextColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kTempoXsFl) ? "t" : "",0) != kOkCsvRC )
  724. {
  725. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  726. goto errLabel;
  727. }
  728. // col 18: frac
  729. if( frac == 0 )
  730. {
  731. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  732. goto errLabel;
  733. }
  734. else
  735. {
  736. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,1.0/frac,0) != kOkCsvRC )
  737. {
  738. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 't frac'.");
  739. goto errLabel;
  740. }
  741. }
  742. // col 19: dynamic marking (blank for now)
  743. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  744. goto errLabel;
  745. // col 20: section
  746. if( cmCsvInsertTextColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kSectionXsFl) ? sectionStr : "",0) != kOkCsvRC )
  747. {
  748. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  749. goto errLabel;
  750. }
  751. // col 21, 22 : recd-play, remark (blank for now)
  752. if((rc = _cmXScoreWriteCsvBlankCols(p,2,&lcp)) != kOkXsRC )
  753. goto errLabel;
  754. errLabel:
  755. return rc;
  756. }
  757. cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn )
  758. {
  759. cmXsRC_t rc = kOkXsRC;
  760. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  761. unsigned rowIdx = 1;
  762. double tpqn = 0; // ticks per quarter note
  763. double tps = 0; // ticks per second
  764. double sec = 0; // current time in seconds
  765. const cmChar_t* sectionIdStr = NULL;
  766. if( !cmCsvIsValid(p->csvH) )
  767. return cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output object is not initialized.");
  768. if((rc = _cmXScoreWriteCsvHdr( p )) != kOkXsRC )
  769. goto errLabel;
  770. cmXsPart_t* pp = p->partL;
  771. for(; pp!=NULL; pp=pp->link)
  772. {
  773. cmXsMeas_t* mp = pp->measL;
  774. for(; mp!=NULL; mp=mp->link)
  775. {
  776. if( mp->divisions != 0 )
  777. tpqn = mp->divisions;
  778. cmXsNote_t* np = mp->noteL;
  779. double sec0 = sec;
  780. for(; np!=NULL; np=np->slink)
  781. {
  782. //
  783. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  784. {
  785. double bpm = np->duration;
  786. double bps = bpm / 60.0;
  787. tps = bps * tpqn;
  788. }
  789. double meas_sec = tps == 0 ? 0 : np->tick / tps;
  790. double sec1 = sec + meas_sec;
  791. double dsecs = sec1 - sec0;
  792. sec0 = sec1;
  793. // if this is a section event
  794. if( cmIsFlag(np->flags,kSectionXsFl) )
  795. sectionIdStr = np->tvalue;
  796. // if this is a bar event
  797. if( cmIsFlag(np->flags,kBarXsFl) )
  798. {
  799. _cmXScoreWriteCsvRow(p,rowIdx,mp->number,NULL,"bar",dsecs,sec1,0,0,0,0,np->flags);
  800. }
  801. else
  802. // if this is a pedal event
  803. if( cmIsFlag(np->flags,kPedalDnXsFl|kPedalUpXsFl|kPedalUpDnXsFl) )
  804. {
  805. unsigned d0 = 64; // pedal MIDI ctl id
  806. unsigned d1 = cmIsFlag(np->flags,kPedalDnXsFl) ? 64 : 0; // pedal-dn: d1>=64 pedal-up:<64
  807. _cmXScoreWriteCsvRow(p,rowIdx,mp->number,NULL,"ctl",dsecs,sec1,d0,d1,0,0,np->flags);
  808. }
  809. else
  810. // if this is a sounding note event
  811. if( cmIsNotFlag(np->flags,kRestXsFl) )
  812. {
  813. double frac = np->rvalue + (cmIsFlag(np->flags,kDotXsFl) ? (np->rvalue/2) : 0);
  814. //
  815. _cmXScoreWriteCsvRow(p,rowIdx,mp->number,sectionIdStr,"non",dsecs,sec1,0,0,np->pitch,frac,np->flags);
  816. sectionIdStr = NULL;
  817. }
  818. rowIdx += 1;
  819. }
  820. sec = sec0;
  821. }
  822. }
  823. if( cmCsvWrite( p->csvH, csvFn ) != kOkCsvRC )
  824. rc = cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output write failed on file '%s'.",csvFn);
  825. errLabel:
  826. return rc;
  827. }
  828. void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
  829. {
  830. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  831. cmXsPart_t* pp = p->partL;
  832. for(; pp!=NULL; pp=pp->link)
  833. {
  834. cmRptPrintf(rpt,"Part:%s\n",pp->idStr);
  835. const cmXsMeas_t* meas = pp->measL;
  836. for(; meas!=NULL; meas=meas->link)
  837. {
  838. cmRptPrintf(rpt," %i : div:%i beat:%i beat-type:%i\n",meas->number,meas->divisions,meas->beats,meas->beat_type);
  839. if( sortFl )
  840. {
  841. const cmXsNote_t* note = meas->noteL;
  842. for(; note!=NULL; note=note->slink)
  843. _cmXScoreReportNote(rpt,note);
  844. }
  845. else
  846. {
  847. const cmXsVoice_t* v = meas->voiceL;
  848. for(; v!=NULL; v=v->link)
  849. {
  850. const cmXsNote_t* note = v->noteL;
  851. cmRptPrintf(rpt," voice:%i\n",v->id);
  852. for(; note!=NULL; note=note->mlink)
  853. _cmXScoreReportNote(rpt,note);
  854. }
  855. }
  856. }
  857. }
  858. }
  859. cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
  860. {
  861. cmXsRC_t rc;
  862. cmXsH_t h = cmXsNullHandle;
  863. if((rc = cmXScoreInitialize( ctx, &h, fn)) != kOkXsRC )
  864. return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
  865. cmXScoreWriteCsv(h,"/Users/kevin/temp/a0.csv");
  866. cmXScoreReport(h,&ctx->rpt,true);
  867. return cmXScoreFinalize(&h);
  868. }