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 47KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmComplexTypes.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmLinkedHeap.h"
  11. #include "cmXml.h"
  12. #include "cmText.h"
  13. #include "cmXScore.h"
  14. #include "cmTime.h"
  15. #include "cmMidi.h"
  16. #include "cmMidiFile.h"
  17. #include "cmLex.h"
  18. #include "cmCsv.h"
  19. #include "cmFile.h"
  20. #include "cmSymTbl.h"
  21. #include "cmAudioFile.h"
  22. #include "cmAudioFile.h"
  23. #include "cmProcObj.h"
  24. #include "cmProcTemplate.h"
  25. #include "cmProc.h"
  26. #include "cmProc2.h"
  27. #include "cmProc5.h"
  28. cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
  29. enum
  30. {
  31. kSectionXsFl = 0x00001, // rvalue holds section number
  32. kBarXsFl = 0x00002,
  33. kRestXsFl = 0x00004,
  34. kGraceXsFl = 0x00008,
  35. kDotXsFl = 0x00010,
  36. kChordXsFl = 0x00020,
  37. kDynXsFl = 0x00040,
  38. kEvenXsFl = 0x00080,
  39. kTempoXsFl = 0x00100,
  40. kHeelXsFl = 0x00200,
  41. kTieBegXsFl = 0x00400,
  42. kTieEndXsFl = 0x00800,
  43. kTieProcXsFl = 0x01000,
  44. kPedalDnXsFl = 0x02000,
  45. kPedalUpXsFl = 0x04000,
  46. kPedalUpDnXsFl = 0x08000,
  47. kMetronomeXsFl = 0x10000, // duration holds BPM
  48. kOnsetXsFl = 0x20000 // this is a sounding note
  49. };
  50. struct cmXsMeas_str;
  51. struct cmXsVoice_str;
  52. typedef struct cmXsNote_str
  53. {
  54. unsigned uid; // unique id of this note record
  55. unsigned flags; // See k???XsFl
  56. unsigned pitch; // midi pitch
  57. unsigned velocity; // midi velocity
  58. cmChar_t step; // A-G
  59. unsigned octave; // sci pitch octave
  60. int alter; // +n=sharps,-n=flats
  61. unsigned staff; // 1=treble 2=bass
  62. unsigned tick; //
  63. unsigned duration; // duration in ticks
  64. unsigned locIdx; // location index (chords share the same location index)
  65. 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, ...)
  66. const cmChar_t* tvalue; // text value
  67. struct cmXsVoice_str* voice; // voice this note belongs to
  68. struct cmXsMeas_str* meas; // measure this note belongs to
  69. const cmXmlNode_t* xmlNode; // note xml ptr
  70. struct cmXsNote_str* mlink; // measure note list
  71. struct cmXsNote_str* slink; // time sorted event list
  72. } cmXsNote_t;
  73. typedef struct cmXsVoice_str
  74. {
  75. unsigned id; // Voice id
  76. cmXsNote_t* noteL; // List of notes in this voice
  77. struct cmXsVoice_str* link; // Link to other voices in this measure
  78. } cmXsVoice_t;
  79. typedef struct cmXsSpan_str
  80. {
  81. unsigned staff;
  82. unsigned number;
  83. struct cmXsMeas_str* meas;
  84. unsigned tick0;
  85. unsigned tick1;
  86. int pitch_offset;
  87. struct cmXsSpan_str* link;
  88. } cmXsSpan_t;
  89. typedef struct cmXsMeas_str
  90. {
  91. unsigned number; // Measure number
  92. unsigned divisions; // ticks-per-quarter-note
  93. unsigned beats; // beats per measure
  94. unsigned beat_type; // whole/half/quarter/eighth ...
  95. cmXsVoice_t* voiceL; // List of voices in this measure
  96. cmXsNote_t* noteL; // List of time sorted notes in this measure
  97. struct cmXsMeas_str* link; // Link to other measures in this part.
  98. } cmXsMeas_t;
  99. typedef struct cmXsPart_str
  100. {
  101. const cmChar_t* idStr; // Id of this part
  102. cmXsMeas_t* measL; // List of measures in this part.
  103. struct cmXsPart_str* link; // Link to other parts in this score
  104. } cmXsPart_t;
  105. typedef struct
  106. {
  107. cmErr_t err;
  108. cmXmlH_t xmlH;
  109. cmLHeapH_t lhH;
  110. cmXsPart_t* partL;
  111. cmCsvH_t csvH;
  112. cmXsSpan_t* spanL;
  113. unsigned nextUid;
  114. } cmXScore_t;
  115. cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
  116. {
  117. cmXScore_t* p = (cmXScore_t*)h.h;
  118. assert( p != NULL );
  119. return p;
  120. }
  121. cmXsRC_t _cmXScoreFinalize( cmXScore_t* p )
  122. {
  123. cmXsRC_t rc = kOkXsRC;
  124. // release the XML file
  125. cmXmlFree( &p->xmlH );
  126. // release the local linked heap memory
  127. cmLHeapDestroy(&p->lhH);
  128. // release the CSV output object
  129. cmCsvFinalize(&p->csvH);
  130. cmMemFree(p);
  131. return rc;
  132. }
  133. cmXsRC_t _cmXScoreMissingNode( cmXScore_t* p, const cmXmlNode_t* parent, const cmChar_t* label )
  134. {
  135. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s'. Parent line:%i",label,parent->line);
  136. }
  137. cmXsRC_t _cmXScoreMissingAttribute( cmXScore_t* p, const cmXmlNode_t* np, const cmChar_t* attrLabel )
  138. {
  139. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML attribute '%s' from node '%s'.",attrLabel,np->label);
  140. }
  141. cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
  142. {
  143. cmXsVoice_t* v = meas->voiceL;
  144. for(; v!=NULL; v=v->link)
  145. if( v->id == voiceId )
  146. return v;
  147. return NULL;
  148. }
  149. cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
  150. {
  151. cmXsVoice_t* v;
  152. // get the voice recd
  153. if((v = _cmXScoreIdToVoice(meas,voiceId)) == NULL)
  154. {
  155. // the voice recd doesn't exist for this voiceId - allocate one
  156. v = cmLhAllocZ(p->lhH,cmXsVoice_t,1);
  157. v->id = voiceId;
  158. // add the voice record to the meas->voiceL
  159. if( meas->voiceL == NULL )
  160. meas->voiceL = v;
  161. else
  162. {
  163. cmXsVoice_t* vp = meas->voiceL;
  164. while( vp->link!=NULL )
  165. vp = vp->link;
  166. vp->link = v;
  167. }
  168. }
  169. // add the note record to the end of meas->voiceL
  170. if( v->noteL == NULL )
  171. v->noteL = note;
  172. else
  173. {
  174. cmXsNote_t* n = v->noteL;
  175. while( n->mlink != NULL )
  176. n = n->mlink;
  177. n->mlink = note;
  178. }
  179. note->voice = v;
  180. note->uid = p->nextUid++;
  181. return kOkXsRC;
  182. }
  183. cmXsRC_t _cmXScoreParsePartList( cmXScore_t* p )
  184. {
  185. cmXsRC_t rc = kOkXsRC;
  186. cmXsPart_t* lastPartPtr = NULL;
  187. const cmXmlNode_t* xnp;
  188. // find the 'part-list'
  189. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part-list", NULL, 0)) == NULL )
  190. return _cmXScoreMissingNode(p,cmXmlRoot(p->xmlH),"part-list");
  191. const cmXmlNode_t* cnp = xnp->children;
  192. // for each child of the 'part-list'
  193. for(; cnp!=NULL; cnp=cnp->sibling)
  194. if( cmTextCmp( cnp->label, "score-part" ) == 0 )
  195. {
  196. const cmXmlAttr_t* a;
  197. // find the 'score-part' id
  198. if((a = cmXmlFindAttrib(cnp,"id")) == NULL )
  199. return _cmXScoreMissingAttribute(p,cnp,"id");
  200. // allocate a new part record
  201. cmXsPart_t* pp = cmLhAllocZ(p->lhH,cmXsPart_t,1);
  202. pp->idStr = a->value; // set the part id
  203. // link the new part record to the end of the part list
  204. if(lastPartPtr == NULL)
  205. p->partL = pp;
  206. else
  207. lastPartPtr->link = pp;
  208. lastPartPtr = pp;
  209. }
  210. return rc;
  211. }
  212. cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* np )
  213. {
  214. cmXsRC_t rc = kOkXsRC;
  215. unsigned octave = 0;
  216. double alter = 0;
  217. const cmChar_t* step = NULL;
  218. if((step = cmXmlNodeValue(nnp,"pitch","step",NULL)) == NULL )
  219. return _cmXScoreMissingNode(p,nnp,"step");
  220. if((rc = cmXmlNodeUInt( nnp,&octave,"pitch","octave",NULL)) != kOkXmlRC )
  221. return _cmXScoreMissingNode(p,nnp,"octave");
  222. cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
  223. cmChar_t buf[3] = { *step, '0', '\0'};
  224. unsigned midi = cmSciPitchToMidi(buf);
  225. midi += (12 * octave);
  226. midi += alter;
  227. np->pitch = midi;
  228. np->step = *step;
  229. np->octave = octave;
  230. np->alter = alter;
  231. np->flags |= kOnsetXsFl;
  232. return rc;
  233. }
  234. cmXsRC_t _cmXScoreParseNoteRValue( cmXScore_t* p, const cmXmlNode_t* nnp, const cmChar_t* label, double* rvalueRef )
  235. {
  236. typedef struct map_str
  237. {
  238. double rvalue;
  239. const cmChar_t* label;
  240. } map_t;
  241. map_t mapV[] =
  242. {
  243. {-1.0, "measure" }, // whole measure rest
  244. { 0.5, "breve" }, // double whole
  245. { 1.0, "whole" },
  246. { 2.0, "half" },
  247. { 4.0, "quarter" },
  248. { 8.0, "eighth" },
  249. {16.0, "16th" },
  250. {32.0, "32nd" },
  251. { 64.0, "64th" },
  252. {128.0, "128th" },
  253. { 0.0, "" }
  254. };
  255. const cmChar_t* str;
  256. // get the XML rvalue label
  257. if((str = cmXmlNodeValue(nnp,label,NULL)) == NULL)
  258. {
  259. if((nnp = cmXmlSearch(nnp,"rest",NULL,0)) != NULL )
  260. {
  261. const cmXmlAttr_t* a;
  262. if((a = cmXmlFindAttrib(nnp,"measure")) != NULL && cmTextCmp(a->value,"yes")==0)
  263. {
  264. *rvalueRef = -1;
  265. return kOkXsRC;
  266. }
  267. }
  268. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'beat-unit' metronome value is missing on line %i.",nnp->line);
  269. }
  270. unsigned i;
  271. // lookup the rvalue numeric value from the mapV[] table
  272. for(i=0; mapV[i].rvalue!=0; ++i)
  273. if( cmTextCmp(mapV[i].label,str) == 0 )
  274. {
  275. *rvalueRef = mapV[i].rvalue;
  276. return kOkXsRC;
  277. }
  278. // the rvalue label was not found
  279. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unknown rvalue type='%s'.",str);
  280. }
  281. cmXsRC_t _cmXScoreParseColor( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* note )
  282. {
  283. cmXsRC_t rc = kOkXsRC;
  284. const cmXmlAttr_t* a;
  285. typedef struct map_str
  286. {
  287. unsigned value;
  288. const cmChar_t* label;
  289. } map_t;
  290. map_t mapV[] =
  291. {
  292. { kEvenXsFl, "#0000FF" }, // blue (even)
  293. { kTempoXsFl, "#00FF00" }, // green (tempo)
  294. { kDynXsFl, "#FF0000" }, // red (dynamics)
  295. { kTempoXsFl | kEvenXsFl, "#00FFFF" }, // green + blue (turquoise)
  296. { kDynXsFl | kEvenXsFl, "#FF00FF" }, // red + blue
  297. { kDynXsFl | kEvenXsFl, "#FF0CF7" }, // magenta (even+dyn)
  298. { kDynXsFl | kTempoXsFl, "#FF7F00" }, // red + green (brown)
  299. { kTempoXsFl | kEvenXsFl | kDynXsFl, "#996633" }, // (purple)
  300. { kDynXsFl, "#FF6A03" }, // 176 orange (dynamics)
  301. { kEvenXsFl, "#2F00E8" }, // 1001 blue (even)
  302. { kTempoXsFl, "#01CD1F" }, // 1196 green (tempo)
  303. { kEvenXsFl, "#3600E8" }, // 1627 blue (even)
  304. { kDynXsFl | kTempoXsFl, "#9E8F15" }, // 8827 brown (dyn + tempo)
  305. { kEvenXsFl, "#2E00E6" }, // 5393 blue (even)
  306. { kEvenXsFl, "#2C00DD" }, // 5895 blue (even)
  307. { kDynXsFl, "#FF5B03" }, // 6498 orange (dyn)
  308. { kDynXsFl, "#FF6104" }, // 6896 orange
  309. { kEvenXsFl, "#2A00E6" }, // 7781 blue
  310. { kEvenXsFl, "#2300DD" }, // 8300 blue (even)
  311. { kTempoXsFl, "#03CD22" }, // 10820 green (tempo)
  312. { kEvenXsFl, "#3400DB" }, // 11627 blue (dyn)
  313. { -1, "" }
  314. };
  315. /*
  316. orange #FF6A03
  317. magenta #FF0CF7
  318. blue #2F00E8
  319. green #01CD1F
  320. gold #9E8F15
  321. green #03CD22
  322. */
  323. if((a = cmXmlFindAttrib(nnp, "color" )) != NULL )
  324. {
  325. unsigned i;
  326. for(i=0; mapV[i].value != -1; ++i)
  327. if( cmTextCmp(a->value,mapV[i].label) == 0 )
  328. {
  329. note->flags += mapV[i].value;
  330. break;
  331. }
  332. if( mapV[i].value == -1 )
  333. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The note color '%s' was not found on line %i.",a->value,nnp->line);
  334. }
  335. return rc;
  336. }
  337. cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* nnp, unsigned* tick0Ref, unsigned* tickRef )
  338. {
  339. cmXsRC_t rc = kOkXsRC;
  340. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  341. unsigned voiceId;
  342. note->meas = meas;
  343. note->xmlNode = nnp;
  344. // get the voice id for this node
  345. if( cmXmlNodeUInt(nnp,&voiceId,"voice",NULL) != kOkXmlRC )
  346. return _cmXScoreMissingNode(p,nnp,"voice");
  347. // if this note has a pitch
  348. if( cmXmlNodeHasChild(nnp,"pitch",NULL) )
  349. if((rc = _cmXScoreParsePitch(p,nnp,note)) != kOkXsRC )
  350. return rc;
  351. cmXmlNodeUInt(nnp,&note->duration,"duration",NULL); // get the note duration
  352. cmXmlNodeUInt(nnp,&note->staff,"staff",NULL); // get th staff number
  353. // is 'rest'
  354. if( cmXmlNodeHasChild(nnp,"rest",NULL) )
  355. note->flags |= kRestXsFl;
  356. // is 'grace'
  357. if( cmXmlNodeHasChild(nnp,"grace",NULL) )
  358. note->flags |= kGraceXsFl;
  359. // is 'dot'
  360. if( cmXmlNodeHasChild(nnp,"dot",NULL) )
  361. note->flags |= kDotXsFl;
  362. // is 'chord'
  363. if( cmXmlNodeHasChild(nnp,"chord",NULL) )
  364. note->flags |= kChordXsFl;
  365. // is this is first note in a tied pair
  366. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","start",NULL) )
  367. note->flags |= kTieBegXsFl;
  368. // is this is second note in a tied pair
  369. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","stop",NULL) )
  370. note->flags |= kTieEndXsFl;
  371. // has 'heel' mark
  372. if( cmXmlNodeHasChild(nnp,"notations","technical","heel",NULL) )
  373. note->flags |= kHeelXsFl;
  374. // set color coded flags
  375. if((rc = _cmXScoreParseColor(p, nnp, note )) != kOkXsRC )
  376. return rc;
  377. // get the note's rythmic value
  378. if((rc = _cmXScoreParseNoteRValue(p,nnp,"type",&note->rvalue)) != kOkXsRC )
  379. return rc;
  380. // if this is a chord note
  381. if( cmIsFlag(note->flags,kChordXsFl) )
  382. {
  383. note->tick = *tick0Ref; // then use the onset time from the previous note and do not advance time
  384. }
  385. else
  386. {
  387. *tick0Ref = *tickRef;
  388. note->tick = *tickRef;
  389. *tickRef += note->duration;
  390. }
  391. return _cmXScorePushNote(p, meas, voiceId, note );
  392. }
  393. cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* noteXmlNode, unsigned tick, unsigned duration, double rvalue, const cmChar_t* tvalue, unsigned flags )
  394. {
  395. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  396. unsigned voiceId = 0; // non-note's are always assigned to voiceId=0;
  397. note->tick = tick;
  398. note->flags = flags;
  399. note->rvalue = rvalue;
  400. note->tvalue = tvalue;
  401. note->duration = duration;
  402. note->meas = meas;
  403. note->xmlNode = noteXmlNode;
  404. return _cmXScorePushNote(p, meas, voiceId, note );
  405. }
  406. cmXsSpan_t* _cmXScoreFindOpenOctaveShift( cmXScore_t* p, unsigned staff, unsigned number )
  407. {
  408. cmXsSpan_t* s = p->spanL;
  409. for(; s!=NULL; s=s->link)
  410. if( s->tick1 == -1 && s->staff == staff && s->number == number )
  411. return s;
  412. return NULL;
  413. }
  414. cmXsRC_t _cmXScorePushOctaveShift(cmXScore_t* p, cmXsMeas_t* meas, unsigned staff, unsigned span_number, const cmChar_t* type_str, unsigned tick)
  415. {
  416. assert( meas != NULL);
  417. cmXsSpan_t* s;
  418. if( cmTextCmp(type_str,"stop") == 0 )
  419. {
  420. if((s = _cmXScoreFindOpenOctaveShift(p,staff,span_number)) == NULL )
  421. return cmErrWarnMsg(&p->err,kUnterminatedOctaveShiftXsrRC,"An illegal octave shift was encounted in meas %i.\n",meas->number);
  422. s->tick1 = tick;
  423. }
  424. else
  425. {
  426. s = cmLhAllocZ(p->lhH,cmXsSpan_t,1);
  427. s->staff = staff;
  428. s->meas = meas;
  429. s->number = span_number;
  430. s->tick0 = tick;
  431. s->tick1 = -1;
  432. s->pitch_offset = cmTextCmp(type_str,"up")==0 ? -12 : 12;
  433. s->link = p->spanL;
  434. p->spanL = s;
  435. }
  436. return kOkXsRC;
  437. }
  438. cmXsRC_t _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* dnp, unsigned tick)
  439. {
  440. cmXsRC_t rc = kOkXsRC;
  441. const cmXmlNode_t* np = NULL;
  442. const cmXmlAttr_t* a = NULL;
  443. unsigned flags = 0;
  444. int offset = 0;
  445. double rvalue = 0;
  446. const cmChar_t* tvalue = NULL;
  447. unsigned duration = 0;
  448. bool pushFl = true;
  449. unsigned staff = 0;
  450. cmXmlNodeInt( dnp, &offset, "offset", NULL );
  451. cmXmlNodeUInt(dnp, &staff, "staff", NULL );
  452. // if this is a metronome direction
  453. if((np = cmXmlSearch( dnp, "metronome", NULL, 0)) != NULL )
  454. {
  455. if( cmXmlNodeUInt(np,&duration,"per-minute",NULL) != kOkXmlRC )
  456. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'per-minute' metronome value is missing on line %i.",np->line);
  457. if((rc = _cmXScoreParseNoteRValue(p,np,"beat-unit",&rvalue)) != kOkXsRC )
  458. return rc;
  459. flags = kMetronomeXsFl;
  460. }
  461. else
  462. // if this is a pedal direction
  463. if((np = cmXmlSearch( dnp, "pedal",NULL,0)) != NULL )
  464. {
  465. if((a = cmXmlFindAttrib(np,"type")) == NULL )
  466. return _cmXScoreMissingAttribute(p, np, "type" );
  467. if( cmTextCmp(a->value,"start") == 0 )
  468. flags = kPedalDnXsFl;
  469. else
  470. if( cmTextCmp(a->value,"change") == 0 )
  471. flags = kPedalUpDnXsFl;
  472. else
  473. if( cmTextCmp(a->value,"stop") == 0 )
  474. flags = kPedalUpXsFl;
  475. else
  476. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unrecognized pedal type:'%s'.",cmStringNullGuard(a->value));
  477. }
  478. else
  479. // if this is a 'words' direction
  480. if((np = cmXmlSearch( dnp, "words", NULL, 0)) != NULL )
  481. {
  482. if((a = cmXmlFindAttrib(np,"enclosure")) != NULL && cmTextCmp(a->value,"rectangle")==0 )
  483. {
  484. if( cmTextIsEmpty( tvalue = np->dataStr ) )
  485. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Section number is blank or missing on line %i.",np->line);
  486. flags = kSectionXsFl;
  487. }
  488. }
  489. else
  490. // if this is an 'octave-shift' direction
  491. if((np = cmXmlSearch( dnp, "octave-shift", NULL, 0)) != NULL )
  492. {
  493. unsigned span_number = -1;
  494. if( cmXmlAttrUInt(np,"number",&span_number) != kOkXmlRC )
  495. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'number' attribute.");
  496. if((a = cmXmlFindAttrib(np,"type")) == NULL)
  497. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'type' attribute.");
  498. rc = _cmXScorePushOctaveShift(p,meas,staff,span_number,a->value,tick+offset);
  499. pushFl = false;
  500. }
  501. else
  502. {
  503. pushFl = false;
  504. }
  505. if( pushFl )
  506. rc = _cmXScorePushNonNote(p,meas,dnp,tick+offset,duration,rvalue,tvalue,flags);
  507. return rc;
  508. }
  509. cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t* mnp)
  510. {
  511. cmXsRC_t rc = kOkXsRC;
  512. const cmXmlNode_t* np = NULL;
  513. unsigned tick = 0;
  514. unsigned tick0= 0;
  515. cmXsMeas_t* m = NULL;
  516. // allocate the 'measure' record
  517. cmXsMeas_t* meas = cmLhAllocZ(p->lhH,cmXsMeas_t,1);
  518. // get measure number
  519. if( cmXmlAttrUInt(mnp,"number", &meas->number) != kOkXmlRC )
  520. return _cmXScoreMissingAttribute(p,mnp,"number");
  521. if( pp->measL == NULL )
  522. pp->measL = meas;
  523. else
  524. {
  525. m = pp->measL;
  526. while( m->link != NULL )
  527. m = m->link;
  528. m->link = meas;
  529. meas->divisions = m->divisions;
  530. meas->beats = m->beats;
  531. meas->beat_type = m->beat_type;
  532. }
  533. // get measure attributes node
  534. if((np = cmXmlSearch(mnp,"attributes",NULL,0)) != NULL)
  535. {
  536. cmXmlNodeUInt(np,&meas->divisions,"divisions",NULL);
  537. cmXmlNodeUInt(np,&meas->beats, "time","beats",NULL);
  538. cmXmlNodeUInt(np,&meas->beat_type,"time","beat-type",NULL);
  539. }
  540. // store the bar line
  541. if((rc = _cmXScorePushNonNote(p,meas,mnp,tick,0,0,NULL,kBarXsFl)) != kOkXsRC )
  542. return rc;
  543. np = mnp->children;
  544. // for each child of the 'meas' XML node
  545. for(; rc==kOkXsRC && np!=NULL; np=np->sibling)
  546. {
  547. // if this is a 'note' node
  548. if( cmTextCmp(np->label,"note") == 0 )
  549. {
  550. rc = _cmXScoreParseNote(p,meas,np,&tick0,&tick);
  551. }
  552. else
  553. // if this is a 'backup' node
  554. if( cmTextCmp(np->label,"backup") == 0 )
  555. {
  556. unsigned backup;
  557. cmXmlNodeUInt(np,&backup,"duration",NULL);
  558. if( backup > tick )
  559. tick = 0;
  560. else
  561. tick -= backup;
  562. tick0 = tick;
  563. }
  564. else
  565. // if this is a 'direction' node
  566. if( cmTextCmp(np->label,"direction") == 0 )
  567. {
  568. rc = _cmXScoreParseDirection(p,meas,np,tick);
  569. }
  570. }
  571. return rc;
  572. }
  573. cmXsRC_t _cmXScoreParsePart( cmXScore_t* p, cmXsPart_t* pp )
  574. {
  575. cmXsRC_t rc = kOkXsRC;
  576. const cmXmlNode_t* xnp;
  577. cmXmlAttr_t partAttr = { "id", pp->idStr };
  578. // find the 'part'
  579. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part", &partAttr, 1)) == NULL )
  580. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The part '%s' was not found.",pp->idStr);
  581. // for each child of this part - find each measure
  582. const cmXmlNode_t* cnp = xnp->children;
  583. for(; cnp!=NULL; cnp=cnp->sibling)
  584. if( cmTextCmp(cnp->label,"measure") == 0 )
  585. if((rc = _cmXScoreParseMeasure(p,pp,cnp)) != kOkXsRC )
  586. return rc;
  587. return rc;
  588. }
  589. // Insert note 'np' into the sorted note list based at 's0'.
  590. // Return a pointer to the base of the list after the insertion.
  591. cmXsNote_t* _cmXScoreInsertSortedNote( cmXsNote_t* s0, cmXsNote_t* np )
  592. {
  593. if( s0 == NULL )
  594. return np;
  595. if( np->tick < s0->tick )
  596. {
  597. np->slink = s0;
  598. return np;
  599. }
  600. cmXsNote_t* s1 = s0;
  601. cmXsNote_t* s2 = s0->slink;
  602. while( s2 != NULL )
  603. {
  604. if( s2->tick > np->tick )
  605. {
  606. s1->slink = np;
  607. np->slink = s2;
  608. return s0;
  609. }
  610. s1 = s2;
  611. s2 = s2->slink;
  612. }
  613. s1->slink = np;
  614. return s0;
  615. }
  616. void _cmXScoreSort( cmXScore_t* p )
  617. {
  618. // for each part
  619. cmXsPart_t* pp = p->partL;
  620. for(; pp!=NULL; pp=pp->link)
  621. {
  622. // for each measure in this part
  623. cmXsMeas_t* mp = pp->measL;
  624. for(; mp!=NULL; mp=mp->link)
  625. {
  626. // for each voice in this measure
  627. cmXsVoice_t* vp = mp->voiceL;
  628. for(; vp!=NULL; vp=vp->link)
  629. {
  630. // for each note in this measure
  631. cmXsNote_t* np = vp->noteL;
  632. for(; np!=NULL; np=np->mlink)
  633. mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np);
  634. }
  635. }
  636. }
  637. }
  638. bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
  639. {
  640. cmXsNote_t* nnp = n0p->slink; // begin w/ note following np
  641. unsigned measNumb = mp->number;
  642. cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' ');
  643. if( rptFl )
  644. printf("%i %i %s ",n0p->meas->number,n0p->tick,cmMidiToSciPitch(n0p->pitch,NULL,0));
  645. while(1)
  646. {
  647. // if we are at the end of a measure advance to the next measure
  648. if( nnp == NULL )
  649. {
  650. mp = mp->link;
  651. nnp = mp->noteL;
  652. // if a measure was completed and no end note was found ... then the tie is unterminated
  653. // (a tie must be continued in every measure which it passes through)
  654. if( mp->number > measNumb + 1 )
  655. break;
  656. }
  657. // for each note starting at nnp
  658. for(; nnp!=NULL; nnp=nnp->slink)
  659. {
  660. // if this note is tied to the originating note (np)
  661. if( nnp->voice->id == n0p->voice->id && nnp->step == n0p->step && nnp->octave == n0p->octave )
  662. {
  663. nnp->flags |= kTieProcXsFl;
  664. nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl);
  665. if( rptFl )
  666. printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
  667. // if this note is not tied to a subsequent note
  668. if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
  669. return true;
  670. // record the measure number of the last note with a tie-start
  671. measNumb = mp->number;
  672. }
  673. }
  674. }
  675. cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i was not terminated.",n0p->step,acc,n0p->octave,measNumb);
  676. return false;
  677. }
  678. bool _cmXScoreFindTiedNote1( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* np, bool rptFl )
  679. {
  680. cmXsNote_t* nnp = np->slink; // begin w/ note following np
  681. unsigned measNumb = mp->number;
  682. unsigned measNumb0 = measNumb;
  683. cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' ');
  684. if( rptFl )
  685. printf("%i %i %s ",np->meas->number,np->tick,cmMidiToSciPitch(np->pitch,NULL,0));
  686. // for each successive measure
  687. for(; mp!=NULL; mp=mp->link)
  688. {
  689. //if( nnp == NULL )
  690. // nnp = mp->noteL;
  691. // for each note starting at nnp
  692. for(; nnp!=NULL; nnp=nnp->slink)
  693. {
  694. // if this note is tied to the originating note (np)
  695. if( nnp->voice->id == np->voice->id && nnp->step == np->step && nnp->octave == np->octave )
  696. {
  697. nnp->flags |= kTieProcXsFl;
  698. nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl);
  699. if( rptFl )
  700. printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
  701. // if this note is not tied to a subsequent note
  702. if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
  703. {
  704. return true;
  705. }
  706. measNumb0 = mp->number;
  707. }
  708. }
  709. // if a measure was completed and no end note was found ... then the tie is unterminated
  710. // (a tie must be continued in every measure which it passes through)
  711. if( measNumb0 < mp->number )
  712. break;
  713. }
  714. cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i was not terminated.",np->step,acc,np->octave,measNumb0);
  715. return false;
  716. }
  717. void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
  718. {
  719. unsigned n = 0;
  720. unsigned m = 0;
  721. bool rptFl = false;
  722. cmXsPart_t* pp = p->partL;
  723. // for each part
  724. for(; pp!=NULL; pp=pp->link)
  725. {
  726. unsigned locIdx = 1;
  727. cmXsMeas_t* mp = pp->measL;
  728. // for each measure
  729. for(; mp!=NULL; mp=mp->link)
  730. {
  731. cmXsNote_t* n0 = NULL;
  732. cmXsNote_t* np = mp->noteL;
  733. // for each note in this measure
  734. for(; np!=NULL; np=np->slink)
  735. {
  736. // if this note begins a tie and has not yet been processed
  737. // (A note that continues a tie and therefore has a kTieBegXsFl set
  738. // may have already been processed by an earlier tied note.)
  739. if( cmIsFlag(np->flags,kTieBegXsFl) && cmIsNotFlag(np->flags,kTieProcXsFl))
  740. {
  741. if( _cmXScoreFindTiedNote(p,mp,np,rptFl) )
  742. m += 1;
  743. if( rptFl )
  744. printf("\n");
  745. n += 1;
  746. }
  747. if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
  748. {
  749. cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' ');
  750. cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i marked as a tied note but is also marked to sound.",np->step,acc,np->octave,mp->number);
  751. }
  752. // set the location
  753. if( cmIsFlag(np->flags,kOnsetXsFl) )
  754. {
  755. if( n0!=NULL && n0->tick!=np->tick)
  756. locIdx += 1;
  757. np->locIdx = locIdx;
  758. n0 = np;
  759. }
  760. }
  761. }
  762. }
  763. printf("Found:%i Not Found:%i\n",m,n-m);
  764. }
  765. cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p )
  766. {
  767. const cmXsSpan_t* s;
  768. for(s=p->spanL; s!=NULL; s=s->link)
  769. {
  770. if( s->tick1 == -1)
  771. {
  772. cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"An unterminated octave shift span was encountered in measure %i staff=%i.",s->meas->number,s->staff);
  773. }
  774. else
  775. {
  776. cmXsMeas_t* m = p->partL->measL;
  777. for(; m!=NULL; m=m->link)
  778. if( m->number == s->meas->number )
  779. break;
  780. assert( m != NULL );
  781. cmXsNote_t* note = m->noteL;
  782. for(; note!=NULL; note=note->slink)
  783. if( note->staff==s->staff && s->tick0 <= note->tick && note->tick < s->tick1 )
  784. note->pitch += s->pitch_offset;
  785. }
  786. }
  787. return kOkXsRC;
  788. }
  789. cmXsMeas_t* _cmXScoreNextNonEmptyMeas( cmXsPart_t* pp, cmXsMeas_t* meas )
  790. {
  791. if( meas == NULL )
  792. {
  793. if( pp==NULL || pp->measL==NULL )
  794. return NULL;
  795. meas = pp->measL;
  796. }
  797. else
  798. {
  799. meas = meas->link;
  800. }
  801. while( meas != NULL && meas->noteL == NULL )
  802. meas=meas->link;
  803. return meas;
  804. }
  805. cmXsNote_t* _cmXScoreNextNote( cmXsPart_t* pp, cmXsNote_t* note )
  806. {
  807. // meas should always be valid (unless this is the first note in the score)
  808. cmXsMeas_t* meas = note==NULL ? NULL : note->meas;
  809. do
  810. {
  811. if( note == NULL || note->slink==NULL )
  812. {
  813. if((meas = _cmXScoreNextNonEmptyMeas(pp,meas)) == NULL)
  814. return NULL;
  815. assert( meas->noteL != NULL );
  816. note = meas->noteL;
  817. }
  818. else
  819. {
  820. note = note->slink;
  821. }
  822. assert( note != NULL );
  823. meas = note->meas;
  824. // note is now set to a non-NULL candidate note - advance to a sounding note
  825. while( note!=NULL && cmIsNotFlag(note->flags,kOnsetXsFl) )
  826. note = note->slink;
  827. // if no note was found in this measure
  828. }while( note == NULL );
  829. return note;
  830. }
  831. cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn )
  832. {
  833. cmXsRC_t rc = kOkXsRC;
  834. cmFileH_t fH = cmFileNullHandle;
  835. double ticks_per_sec = 0;
  836. double onset_secs = 0;
  837. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  838. return cmErrMsg(&p->err,kFileFailXsRC,"Unable to create the file '%s'.",cmStringNullGuard(fn));
  839. cmXsPart_t* pp = p->partL;
  840. for(; pp!=NULL; pp=pp->link)
  841. {
  842. cmXsMeas_t* mp = pp->measL;
  843. for(; mp!=NULL; mp=mp->link)
  844. {
  845. cmFilePrintf(fH,"b %f %i %s B\n",onset_secs,mp->number,"bar");
  846. cmXsNote_t* np = mp->noteL;
  847. unsigned tick0 = 0;
  848. for(; np!=NULL; np=np->slink)
  849. {
  850. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  851. {
  852. double bps = np->duration / 60.0;
  853. // t b t
  854. // - = - -
  855. // s s b
  856. ticks_per_sec = bps * mp->divisions;
  857. }
  858. else
  859. {
  860. if( cmIsFlag(np->flags,kOnsetXsFl) )
  861. {
  862. onset_secs += (np->tick - tick0) / ticks_per_sec;
  863. tick0 = np->tick;
  864. cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->duration/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N");
  865. }
  866. }
  867. }
  868. onset_secs += (mp->divisions * mp->beats - tick0) / ticks_per_sec;
  869. }
  870. }
  871. cmFileClose(&fH);
  872. return rc;
  873. }
  874. cmXsRC_t _cmXScoreWriteMidiPlotFile( cmXScore_t* p, cmChar_t* fn, const cmMidiTrackMsg_t** m, unsigned mN )
  875. {
  876. cmXsRC_t rc = kOkXsRC;
  877. cmFileH_t fH = cmFileNullHandle;
  878. unsigned i = 0;
  879. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  880. return cmErrMsg(&p->err,kFileFailXsRC,"Unable to create the file '%s'.",cmStringNullGuard(fn));
  881. for(i=0; i<mN; ++i)
  882. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  883. cmFilePrintf(fH,"n %f %f %i %s\n",m[i]->amicro/1000000.0,m[i]->u.chMsgPtr->durMicros/1000000.0,m[i]->uid,cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  884. cmFileClose(&fH);
  885. return rc;
  886. }
  887. cmXsRC_t _cmXScoreProcessMidi(cmXScore_t* p, cmCtx_t* ctx, const cmChar_t* midiFn)
  888. {
  889. cmXsRC_t rc = kOkXsRC;
  890. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  891. const cmMidiTrackMsg_t** m = NULL;
  892. unsigned mN = 0;
  893. unsigned i = 0;
  894. unsigned j = 0;
  895. cmXsNote_t* note = NULL;
  896. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  897. return cmErrMsg(&p->err,kMidiFailXsRC,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  898. //cmMidiFilePrintMsgs(mfH, p->err.rpt );
  899. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  900. {
  901. rc = cmErrMsg(&p->err,kMidiFailXsRC,"The MIDI file object appears to be empty.");
  902. goto errLabel;
  903. }
  904. if((note = _cmXScoreNextNote(p->partL,NULL)) == NULL)
  905. {
  906. rc = cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"No MIDI processing to be done. The score appears to be empty.");
  907. goto errLabel;
  908. }
  909. cmCtx* c = cmCtxAlloc( NULL, p->err.rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  910. cmSeqAlign_t* s = cmSeqAlignAlloc(c,NULL);
  911. unsigned offs = 0;
  912. for(; note!=NULL; note=_cmXScoreNextNote(p->partL,note))
  913. {
  914. if( cmIsFlag(note->flags,kGraceXsFl) )
  915. offs += 1;
  916. cmSeqAlignInsert(s,0,note->locIdx+offs,note->pitch);
  917. }
  918. unsigned locIdx = 1;
  919. for(i=0,j=0; i<mN; ++i)
  920. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  921. {
  922. if( m[j]->atick != m[i]->atick )
  923. locIdx += 1;
  924. cmSeqAlignInsert(s,1,locIdx,m[i]->u.chMsgPtr->d0);
  925. //printf("%i : %s\n",locIdx,cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  926. j = i;
  927. }
  928. cmMidiFileCalcNoteDurations( mfH );
  929. _cmXScoreWriteScorePlotFile(p, "/Users/kevin/temp/score.txt" );
  930. _cmXScoreWriteMidiPlotFile(p, "/Users/kevin/temp/midi.txt", m, mN );
  931. cmSeqAlignReport(s,p->err.rpt);
  932. cmSeqAlignFree(&s);
  933. cmCtxFree(&c);
  934. goto errLabel;
  935. printf(" i j score midi\n");
  936. printf("---- ---- --- ---- --- ----\n");
  937. for(j=0; note!=NULL; note=_cmXScoreNextNote(p->partL,note),++j)
  938. {
  939. unsigned midiPitch = 0;
  940. for(; i<mN; ++i)
  941. if( m[i]!=NULL && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && m[i]->u.chMsgPtr->d1>0 )
  942. {
  943. midiPitch = m[i]->u.chMsgPtr->d0;
  944. ++i;
  945. break;
  946. }
  947. char buf[6];
  948. printf("%4i %4i %3i %4s %3i %4s\n",j,i,
  949. note->pitch,
  950. cmMidiToSciPitch(note->pitch,NULL,0),
  951. midiPitch,
  952. cmMidiToSciPitch(midiPitch,buf,5));
  953. }
  954. errLabel:
  955. cmMidiFileClose(&mfH);
  956. return rc;
  957. }
  958. cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* midiFn )
  959. {
  960. cmXsRC_t rc = kOkXsRC;
  961. if((rc = cmXScoreFinalize(hp)) != kOkXsRC )
  962. return rc;
  963. cmXScore_t* p = cmMemAllocZ(cmXScore_t,1);
  964. cmErrSetup(&p->err,&ctx->rpt,"XScore");
  965. // create a local linked heap
  966. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  967. return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
  968. // open the music xml file
  969. if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
  970. {
  971. rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn));
  972. goto errLabel;
  973. }
  974. //cmXmlPrint(p->xmlH,&ctx->rpt);
  975. // parse the part-list
  976. if((rc = _cmXScoreParsePartList( p )) != kOkXsRC )
  977. goto errLabel;
  978. // parse each score 'part'
  979. cmXsPart_t* pp = p->partL;
  980. for(; pp!=NULL; pp=pp->link)
  981. if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC )
  982. goto errLabel;
  983. // fill in the note->slink chain to link the notes in each measure in time order
  984. _cmXScoreSort(p);
  985. _cmXScoreResolveTiesAndLoc(p);
  986. //_cmXScoreResolveOctaveShift(p);
  987. //if( midiFn != NULL )
  988. // _cmXScoreProcessMidi(p,ctx,midiFn);
  989. // CSV output initialize failed.
  990. if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC )
  991. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed.");
  992. errLabel:
  993. if( rc != kOkXsRC )
  994. _cmXScoreFinalize(p);
  995. else
  996. hp->h = p;
  997. return rc;
  998. }
  999. cmXsRC_t cmXScoreFinalize( cmXsH_t* hp )
  1000. {
  1001. cmXsRC_t rc = kOkXsRC;
  1002. if( hp == NULL || cmXScoreIsValid(*hp)==false )
  1003. return kOkXsRC;
  1004. cmXScore_t* p = _cmXScoreHandleToPtr(*hp);
  1005. if((rc = _cmXScoreFinalize(p)) != kOkXsRC )
  1006. return rc;
  1007. hp->h = NULL;
  1008. return rc;
  1009. }
  1010. bool cmXScoreIsValid( cmXsH_t h )
  1011. { return h.h != NULL; }
  1012. void _cmXScoreReportTitle( cmRpt_t* rpt )
  1013. {
  1014. cmRptPrintf(rpt," voc loc tick durtn rval flags\n");
  1015. cmRptPrintf(rpt," --- ----- ----- ----- ---- --- -------------\n");
  1016. }
  1017. void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note )
  1018. {
  1019. const cmChar_t* B = cmIsFlag(note->flags,kBarXsFl) ? "|" : "-";
  1020. const cmChar_t* R = cmIsFlag(note->flags,kRestXsFl) ? "R" : "-";
  1021. const cmChar_t* G = cmIsFlag(note->flags,kGraceXsFl) ? "G" : "-";
  1022. const cmChar_t* D = cmIsFlag(note->flags,kDotXsFl) ? "D" : "-";
  1023. const cmChar_t* C = cmIsFlag(note->flags,kChordXsFl) ? "C" : "-";
  1024. const cmChar_t* e = cmIsFlag(note->flags,kEvenXsFl) ? "e" : "-";
  1025. const cmChar_t* d = cmIsFlag(note->flags,kDynXsFl) ? "d" : "-";
  1026. const cmChar_t* t = cmIsFlag(note->flags,kTempoXsFl) ? "t" : "-";
  1027. const cmChar_t* P = cmIsFlag(note->flags,kPedalDnXsFl) ? "V" : "-";
  1028. const cmChar_t* S = cmIsFlag(note->flags,kSectionXsFl) ? "S" : "-";
  1029. const cmChar_t* H = cmIsFlag(note->flags,kHeelXsFl) ? "H" : "-";
  1030. const cmChar_t* T0 = cmIsFlag(note->flags,kTieBegXsFl) ? "T" : "-";
  1031. const cmChar_t* T1 = cmIsFlag(note->flags,kTieEndXsFl) ? "_" : "-";
  1032. P = cmIsFlag(note->flags,kPedalUpXsFl) ? "^" : P;
  1033. P = cmIsFlag(note->flags,kPedalUpDnXsFl) ? "X" : P;
  1034. //const cmChar_t* N = note->pitch==0 ? " " : cmMidiToSciPitch( note->pitch, NULL, 0 );
  1035. cmChar_t N[] = {'\0','\0','\0','\0'};
  1036. cmChar_t acc = note->alter==-1?'b':(note->alter==1?'#':' ');
  1037. snprintf(N,4,"%c%c%1i",note->step,acc,note->octave);
  1038. cmRptPrintf(rpt," %3i %5i %5i %5i %4.1f %3s %s%s%s%s%s%s%s%s%s%s%s%s%s",
  1039. note->voice->id,
  1040. note->locIdx,
  1041. note->tick,
  1042. note->duration,
  1043. note->rvalue,
  1044. N,B,R,G,D,C,e,d,t,P,S,H,T0,T1);
  1045. if( cmIsFlag(note->flags,kSectionXsFl) )
  1046. cmRptPrintf(rpt," %s",cmStringNullGuard(note->tvalue));
  1047. if( cmIsFlag(note->flags,kMetronomeXsFl) )
  1048. cmRptPrintf(rpt," %i bpm",note->duration);
  1049. }
  1050. /*
  1051. kMidiFileIdColScIdx= 0,
  1052. kTypeLabelColScIdx = 3,
  1053. kDSecsColScIdx = 4,
  1054. kSecsColScIdx = 5,
  1055. kD0ColScIdx = 9,
  1056. kD1ColScIdx = 10,
  1057. kPitchColScIdx = 11,
  1058. kBarColScIdx = 13,
  1059. kSkipColScIdx = 14,
  1060. kEvenColScIdx = 15,
  1061. kGraceColScIdx = 16,
  1062. kTempoColScIdx = 17,
  1063. kFracColScIdx = 18,
  1064. kDynColScIdx = 19,
  1065. kSectionColScIdx = 20,
  1066. kRecdPlayColScIdx = 21,
  1067. kRemarkColScIdx = 22
  1068. */
  1069. cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p )
  1070. {
  1071. const cmChar_t* s[] =
  1072. {
  1073. "id","trk","evt","opcode","dticks","micros","status",
  1074. "meta","ch","d0","d1","arg0","arg1","bar","skip",
  1075. "even","grace","tempo","t frac","dyn","section","play_recd","remark",NULL
  1076. };
  1077. cmCsvCell_t* lcp = NULL;
  1078. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymText(p->csvH,s[0]), 0, 0 ) != kOkCsvRC )
  1079. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  1080. unsigned i;
  1081. for(i=1; s[i]!=NULL; ++i)
  1082. {
  1083. if( cmCsvInsertIdentColAfter(p->csvH, lcp, &lcp, s[i], 0 ) != kOkCsvRC )
  1084. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV error inserting CSV title %i.\n",i);
  1085. }
  1086. return kOkXsRC;
  1087. }
  1088. cmXsRC_t _cmXScoreWriteCsvBlankCols( cmXScore_t* p, unsigned cnt, cmCsvCell_t** leftCellPtrPtr )
  1089. {
  1090. unsigned i;
  1091. for(i=0; i<cnt; ++i)
  1092. if( cmCsvInsertIdentColAfter(p->csvH,*leftCellPtrPtr,leftCellPtrPtr,"",0) != kOkCsvRC )
  1093. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV output failed on blank column.");
  1094. return kOkCsvRC;
  1095. }
  1096. cmXsRC_t _cmXScoreWriteCsvRow(
  1097. cmXScore_t* p,
  1098. unsigned rowIdx,
  1099. unsigned uid,
  1100. unsigned bar,
  1101. const cmChar_t* sectionStr,
  1102. const cmChar_t* opCodeStr,
  1103. double dsecs,
  1104. double secs,
  1105. unsigned d0,
  1106. unsigned d1,
  1107. unsigned pitch, // set to -1 if the pitch is not valid
  1108. double frac,
  1109. unsigned flags )
  1110. {
  1111. cmXsRC_t rc = kOkXsRC;
  1112. cmCsvCell_t* lcp = NULL;
  1113. // append an empty row to the CSV object
  1114. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymUInt(p->csvH, rowIdx ), 0, 0 ) != kOkCsvRC )
  1115. {
  1116. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  1117. goto errLabel;
  1118. }
  1119. /*
  1120. // col 0 : blanks
  1121. if( cmCsvInsertUIntColAfter(p->csvH, lcp, &lcp, rowIdx, 0 ) != kOkCsvRC )
  1122. {
  1123. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output row index failed.");
  1124. goto errLabel;
  1125. }
  1126. */
  1127. // col 1 : track (always 1)
  1128. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,1,0) != kOkCsvRC )
  1129. {
  1130. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1131. goto errLabel;
  1132. }
  1133. // col 2 : evt (set to event uid, or blank if uid == -1)
  1134. if( uid == -1 )
  1135. {
  1136. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1137. goto errLabel;
  1138. }
  1139. else
  1140. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,uid,0) != kOkCsvRC )
  1141. {
  1142. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1143. goto errLabel;
  1144. }
  1145. // col 3 : output the opcode
  1146. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,opCodeStr,0) != kOkCsvRC )
  1147. {
  1148. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on opcode label.");
  1149. goto errLabel;
  1150. }
  1151. // col 4 : dsecs
  1152. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,dsecs,0) != kOkCsvRC )
  1153. {
  1154. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dsecs'.");
  1155. goto errLabel;
  1156. }
  1157. // col 5 : secs
  1158. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,secs,0) != kOkCsvRC )
  1159. {
  1160. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'secs'.");
  1161. goto errLabel;
  1162. }
  1163. // cols 6,7,8 blanks
  1164. if((rc = _cmXScoreWriteCsvBlankCols(p,3,&lcp)) != kOkXsRC )
  1165. goto errLabel;
  1166. // col 9 : d0
  1167. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d0,0) != kOkCsvRC )
  1168. {
  1169. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1170. goto errLabel;
  1171. }
  1172. // col 10 : d1
  1173. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d1,0) != kOkCsvRC )
  1174. {
  1175. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd1'.");
  1176. goto errLabel;
  1177. }
  1178. // col 11 : pitch
  1179. if( pitch == -1 )
  1180. {
  1181. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1182. goto errLabel;
  1183. }
  1184. else
  1185. {
  1186. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmMidiToSciPitch(pitch,NULL,0),0) != kOkCsvRC )
  1187. {
  1188. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  1189. goto errLabel;
  1190. }
  1191. }
  1192. // col 12 : blanks
  1193. if((rc = _cmXScoreWriteCsvBlankCols(p,1 + (cmIsFlag(flags,kBarXsFl) ? 0 : 1), &lcp)) != kOkXsRC )
  1194. goto errLabel;
  1195. // col 13 : bar number
  1196. if( cmIsFlag(flags,kBarXsFl) )
  1197. {
  1198. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,bar,0) != kOkCsvRC )
  1199. {
  1200. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  1201. goto errLabel;
  1202. }
  1203. }
  1204. // col 14 : skip (blank for now)
  1205. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1206. goto errLabel;
  1207. // col 15: even (all grace notes are 'even' notes
  1208. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp, cmIsFlag(flags,kGraceXsFl) | cmIsFlag(flags,kEvenXsFl) ? "e" : "",0) != kOkCsvRC )
  1209. {
  1210. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1211. goto errLabel;
  1212. }
  1213. // col 16: grace
  1214. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kGraceXsFl) ? "g" : "",0) != kOkCsvRC )
  1215. {
  1216. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1217. goto errLabel;
  1218. }
  1219. // col 17: tempo
  1220. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kTempoXsFl) ? "t" : "",0) != kOkCsvRC )
  1221. {
  1222. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1223. goto errLabel;
  1224. }
  1225. // col 18: frac
  1226. if( frac == 0 )
  1227. {
  1228. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1229. goto errLabel;
  1230. }
  1231. else
  1232. {
  1233. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,1.0/frac,0) != kOkCsvRC )
  1234. {
  1235. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 't frac'.");
  1236. goto errLabel;
  1237. }
  1238. }
  1239. // col 19: dynamic marking (blank for now)
  1240. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1241. goto errLabel;
  1242. // col 20: section
  1243. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kSectionXsFl) ? sectionStr : "",0) != kOkCsvRC )
  1244. {
  1245. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1246. goto errLabel;
  1247. }
  1248. // col 21, 22 : recd-play, remark (blank for now)
  1249. if((rc = _cmXScoreWriteCsvBlankCols(p,2,&lcp)) != kOkXsRC )
  1250. goto errLabel;
  1251. errLabel:
  1252. return rc;
  1253. }
  1254. cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn )
  1255. {
  1256. cmXsRC_t rc = kOkXsRC;
  1257. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1258. unsigned rowIdx = 1;
  1259. double tpqn = 0; // ticks per quarter note
  1260. double tps = 0; // ticks per second
  1261. double sec = 0; // current time in seconds
  1262. const cmChar_t* sectionIdStr = NULL;
  1263. if( !cmCsvIsValid(p->csvH) )
  1264. return cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output object is not initialized.");
  1265. if((rc = _cmXScoreWriteCsvHdr( p )) != kOkXsRC )
  1266. goto errLabel;
  1267. cmXsPart_t* pp = p->partL;
  1268. for(; pp!=NULL; pp=pp->link)
  1269. {
  1270. cmXsMeas_t* mp = pp->measL;
  1271. for(; mp!=NULL; mp=mp->link)
  1272. {
  1273. if( mp->divisions != 0 )
  1274. tpqn = mp->divisions;
  1275. cmXsNote_t* np = mp->noteL;
  1276. double sec0 = sec;
  1277. for(; np!=NULL; np=np->slink)
  1278. {
  1279. //
  1280. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  1281. {
  1282. double bpm = np->duration;
  1283. double bps = bpm / 60.0;
  1284. tps = bps * tpqn;
  1285. }
  1286. double meas_sec = tps == 0 ? 0 : np->tick / tps;
  1287. double sec1 = sec + meas_sec;
  1288. double dsecs = sec1 - sec0;
  1289. sec0 = sec1;
  1290. // if this is a section event
  1291. if( cmIsFlag(np->flags,kSectionXsFl) )
  1292. sectionIdStr = np->tvalue;
  1293. // if this is a bar event
  1294. if( cmIsFlag(np->flags,kBarXsFl) )
  1295. {
  1296. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,NULL,"bar",dsecs,sec1,0,0,-1,0,np->flags);
  1297. }
  1298. else
  1299. // if this is a pedal event
  1300. if( cmIsFlag(np->flags,kPedalDnXsFl|kPedalUpXsFl|kPedalUpDnXsFl) )
  1301. {
  1302. unsigned d0 = 64; // pedal MIDI ctl id
  1303. unsigned d1 = cmIsFlag(np->flags,kPedalDnXsFl) ? 64 : 0; // pedal-dn: d1>=64 pedal-up:<64
  1304. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,NULL,"ctl",dsecs,sec1,d0,d1,-1,0,np->flags);
  1305. }
  1306. else
  1307. // if this is a sounding note event
  1308. if( cmIsFlag(np->flags,kOnsetXsFl) )
  1309. {
  1310. double frac = np->rvalue + (cmIsFlag(np->flags,kDotXsFl) ? (np->rvalue/2) : 0);
  1311. //
  1312. _cmXScoreWriteCsvRow(p,rowIdx,np->uid,mp->number,sectionIdStr,"non",dsecs,sec1,np->pitch,60,np->pitch,frac,np->flags);
  1313. sectionIdStr = NULL;
  1314. }
  1315. rowIdx += 1;
  1316. }
  1317. sec = sec0;
  1318. }
  1319. }
  1320. if( cmCsvWrite( p->csvH, csvFn ) != kOkCsvRC )
  1321. rc = cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output write failed on file '%s'.",csvFn);
  1322. errLabel:
  1323. return rc;
  1324. }
  1325. void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
  1326. {
  1327. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1328. cmXsPart_t* pp = p->partL;
  1329. for(; pp!=NULL; pp=pp->link)
  1330. {
  1331. cmRptPrintf(rpt,"Part:%s\n",pp->idStr);
  1332. const cmXsMeas_t* meas = pp->measL;
  1333. for(; meas!=NULL; meas=meas->link)
  1334. {
  1335. cmRptPrintf(rpt," %i : div:%i beat:%i beat-type:%i (%i)\n",meas->number,meas->divisions,meas->beats,meas->beat_type,meas->divisions*meas->beats);
  1336. if( sortFl )
  1337. {
  1338. _cmXScoreReportTitle(rpt);
  1339. const cmXsNote_t* note = meas->noteL;
  1340. for(; note!=NULL; note=note->slink)
  1341. {
  1342. _cmXScoreReportNote(rpt,note);
  1343. if( note->slink!=NULL || note->voice->id==0)
  1344. cmRptPrintf(rpt,"\n");
  1345. else
  1346. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  1347. }
  1348. }
  1349. else
  1350. {
  1351. _cmXScoreReportTitle(rpt);
  1352. const cmXsVoice_t* v = meas->voiceL;
  1353. for(; v!=NULL; v=v->link)
  1354. {
  1355. const cmXsNote_t* note = v->noteL;
  1356. cmRptPrintf(rpt," voice:%i\n",v->id);
  1357. for(; note!=NULL; note=note->mlink)
  1358. {
  1359. _cmXScoreReportNote(rpt,note);
  1360. if( note->mlink!=NULL || note->voice->id==0)
  1361. cmRptPrintf(rpt,"\n");
  1362. else
  1363. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  1364. }
  1365. }
  1366. }
  1367. }
  1368. }
  1369. }
  1370. cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn )
  1371. {
  1372. assert(0); // function not implemented
  1373. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1374. cmXsPart_t* pp = p->partL;
  1375. for(; pp!=NULL; pp=pp->link)
  1376. {
  1377. const cmXsMeas_t* meas = pp->measL;
  1378. for(; meas!=NULL; meas=meas->link)
  1379. {
  1380. const cmXsNote_t* note = meas->noteL;
  1381. for(; note!=NULL; note=note->slink)
  1382. {
  1383. }
  1384. }
  1385. }
  1386. }
  1387. cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* midiFn, const cmChar_t* outFn )
  1388. {
  1389. cmXsRC_t rc;
  1390. cmXsH_t h = cmXsNullHandle;
  1391. if((rc = cmXScoreInitialize( ctx, &h, xmlFn, midiFn)) != kOkXsRC )
  1392. return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
  1393. if( outFn != NULL )
  1394. cmXScoreWriteCsv(h,outFn);
  1395. cmXScoreReport(h,&ctx->rpt,true);
  1396. return cmXScoreFinalize(&h);
  1397. }