libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmXScore.c 73KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732
  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 "cmFileSys.h"
  14. #include "cmXScore.h"
  15. #include "cmTime.h"
  16. #include "cmMidi.h"
  17. #include "cmMidiFile.h"
  18. #include "cmLex.h"
  19. #include "cmCsv.h"
  20. #include "cmSymTbl.h"
  21. #include "cmScore.h"
  22. #include "cmFile.h"
  23. #include "cmSymTbl.h"
  24. #include "cmAudioFile.h"
  25. #include "cmAudioFile.h"
  26. #include "cmProcObj.h"
  27. #include "cmProcTemplate.h"
  28. #include "cmProc.h"
  29. #include "cmProc2.h"
  30. #include "cmProc5.h"
  31. #include "cmSvgWriter.h"
  32. cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
  33. enum
  34. {
  35. kSectionXsFl = 0x000001, // rvalue holds section number
  36. kBarXsFl = 0x000002,
  37. kRestXsFl = 0x000004,
  38. kGraceXsFl = 0x000008,
  39. kDotXsFl = 0x000010,
  40. kChordXsFl = 0x000020,
  41. kDynXsFl = 0x000040,
  42. kEvenXsFl = 0x000080,
  43. kTempoXsFl = 0x000100,
  44. kHeelXsFl = 0x000200,
  45. kTieBegXsFl = 0x000400,
  46. kTieEndXsFl = 0x000800,
  47. kTieProcXsFl = 0x001000,
  48. kPedalDnXsFl = 0x002000,
  49. kPedalUpXsFl = 0x004000,
  50. kPedalUpDnXsFl = 0x008000,
  51. kSostDnXsFl = 0x010000,
  52. kSostUpXsFl = 0x020000,
  53. kMetronomeXsFl = 0x040000, // duration holds BPM
  54. kOnsetXsFl = 0x080000, // this is a sounding note
  55. kBegGroupXsFl = 0x100000,
  56. kEndGroupXsFl = 0x200000
  57. };
  58. struct cmXsMeas_str;
  59. struct cmXsVoice_str;
  60. typedef struct cmXsNote_str
  61. {
  62. unsigned uid; // unique id of this note record
  63. unsigned flags; // See k???XsFl
  64. unsigned pitch; // midi pitch
  65. unsigned velocity; // midi velocity
  66. unsigned dynamics; // dynamic level 1=pppp 9=fff
  67. unsigned vel; // score specified MIDI velocity
  68. cmChar_t step; // A-G
  69. unsigned octave; // sci pitch octave
  70. int alter; // +n=sharps,-n=flats
  71. unsigned staff; // 1=treble 2=bass
  72. unsigned tick; //
  73. unsigned duration; // duration in ticks
  74. double secs; // absolute time in seconds
  75. double dsecs; // delta time in seconds since previous event
  76. unsigned locIdx; // location index (chords share the same location index)
  77. 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, ...)
  78. const cmChar_t* tvalue; // text value
  79. unsigned evenGroupId; // eveness group id
  80. unsigned dynGroupId; // dynamics group id
  81. unsigned tempoGroupId; // tempo group id
  82. struct cmXsVoice_str* voice; // voice this note belongs to
  83. struct cmXsMeas_str* meas; // measure this note belongs to
  84. const cmXmlNode_t* xmlNode; // note xml ptr
  85. struct cmXsNote_str* tied; // subsequent note tied to this note
  86. struct cmXsNote_str* mlink; // measure note list
  87. struct cmXsNote_str* slink; // time sorted event list
  88. } cmXsNote_t;
  89. typedef struct cmXsVoice_str
  90. {
  91. unsigned id; // Voice id
  92. cmXsNote_t* noteL; // List of notes in this voice
  93. struct cmXsVoice_str* link; // Link to other voices in this measure
  94. } cmXsVoice_t;
  95. typedef struct cmXsSpan_str
  96. {
  97. unsigned staff;
  98. unsigned number;
  99. struct cmXsMeas_str* meas;
  100. unsigned tick0;
  101. unsigned tick1;
  102. int pitch_offset;
  103. struct cmXsSpan_str* link;
  104. } cmXsSpan_t;
  105. typedef struct cmXsMeas_str
  106. {
  107. unsigned number; // Measure number
  108. unsigned divisions; // ticks-per-quarter-note
  109. unsigned beats; // beats per measure
  110. unsigned beat_type; // whole/half/quarter/eighth ...
  111. cmXsVoice_t* voiceL; // List of voices in this measure
  112. cmXsNote_t* noteL; // List of time sorted notes in this measure
  113. struct cmXsMeas_str* link; // Link to other measures in this part.
  114. } cmXsMeas_t;
  115. typedef struct cmXsPart_str
  116. {
  117. const cmChar_t* idStr; // Id of this part
  118. cmXsMeas_t* measL; // List of measures in this part.
  119. struct cmXsPart_str* link; // Link to other parts in this score
  120. } cmXsPart_t;
  121. typedef struct
  122. {
  123. cmErr_t err;
  124. cmXmlH_t xmlH;
  125. cmLHeapH_t lhH;
  126. cmXsPart_t* partL;
  127. cmCsvH_t csvH;
  128. cmXsSpan_t* spanL;
  129. unsigned nextUid;
  130. } cmXScore_t;
  131. cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
  132. {
  133. cmXScore_t* p = (cmXScore_t*)h.h;
  134. assert( p != NULL );
  135. return p;
  136. }
  137. cmXsRC_t _cmXScoreFinalize( cmXScore_t* p )
  138. {
  139. cmXsRC_t rc = kOkXsRC;
  140. // release the XML file
  141. cmXmlFree( &p->xmlH );
  142. // release the local linked heap memory
  143. cmLHeapDestroy(&p->lhH);
  144. // release the CSV output object
  145. cmCsvFinalize(&p->csvH);
  146. cmMemFree(p);
  147. return rc;
  148. }
  149. cmXsRC_t _cmXScoreMissingNode( cmXScore_t* p, const cmXmlNode_t* parent, const cmChar_t* label )
  150. {
  151. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s'. Parent line:%i",label,parent->line);
  152. }
  153. cmXsRC_t _cmXScoreMissingAttribute( cmXScore_t* p, const cmXmlNode_t* np, const cmChar_t* attrLabel )
  154. {
  155. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML attribute '%s' from node '%s'.",attrLabel,np->label);
  156. }
  157. cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
  158. {
  159. cmXsVoice_t* v = meas->voiceL;
  160. for(; v!=NULL; v=v->link)
  161. if( v->id == voiceId )
  162. return v;
  163. return NULL;
  164. }
  165. cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
  166. {
  167. cmXsVoice_t* v;
  168. // get the voice recd
  169. if((v = _cmXScoreIdToVoice(meas,voiceId)) == NULL)
  170. {
  171. // the voice recd doesn't exist for this voiceId - allocate one
  172. v = cmLhAllocZ(p->lhH,cmXsVoice_t,1);
  173. v->id = voiceId;
  174. // add the voice record to the meas->voiceL
  175. if( meas->voiceL == NULL )
  176. meas->voiceL = v;
  177. else
  178. {
  179. cmXsVoice_t* vp = meas->voiceL;
  180. while( vp->link!=NULL )
  181. vp = vp->link;
  182. vp->link = v;
  183. }
  184. }
  185. // add the note record to the end of meas->voiceL
  186. if( v->noteL == NULL )
  187. v->noteL = note;
  188. else
  189. {
  190. cmXsNote_t* n = v->noteL;
  191. while( n->mlink != NULL )
  192. n = n->mlink;
  193. n->mlink = note;
  194. }
  195. note->voice = v;
  196. note->uid = p->nextUid++;
  197. return kOkXsRC;
  198. }
  199. void _cmXScoreInsertNoteBefore( cmXsNote_t* note, cmXsNote_t* nn )
  200. {
  201. // insert the new note into the voice list before 'note'
  202. cmXsNote_t* n0 = NULL;
  203. cmXsNote_t* n1 = note->voice->noteL;
  204. for(; n1 != NULL; n1=n1->mlink )
  205. {
  206. if( n1->uid == note->uid )
  207. {
  208. if( n0 == NULL )
  209. note->voice->noteL = nn;
  210. else
  211. n0->mlink = nn;
  212. nn->mlink = note;
  213. break;
  214. }
  215. n0 = n1;
  216. }
  217. assert( n1 != NULL );
  218. // insert the new note into the time sorted note list before 'note'
  219. n0 = NULL;
  220. n1 = note->meas->noteL;
  221. for(; n1!=NULL; n1=n1->slink)
  222. {
  223. if( n1->tick >= nn->tick )
  224. {
  225. if( n0 == NULL )
  226. note->meas->noteL = nn;
  227. else
  228. n0->slink = nn;
  229. nn->slink = n1;
  230. break;
  231. }
  232. n0 = n1;
  233. }
  234. assert( n1 != NULL );
  235. }
  236. void _cmXScoreInsertNoteAfter( cmXsNote_t* note, cmXsNote_t* nn )
  237. {
  238. // insert the new note into the voice list after 'note'
  239. cmXsNote_t* n0 = note->voice->noteL;
  240. for(; n0 != NULL; n0=n0->mlink )
  241. if( n0->uid == note->uid )
  242. {
  243. nn->mlink = note->mlink;
  244. note->mlink = nn;
  245. break;
  246. }
  247. assert(n0 != NULL );
  248. // insert the new note into the time sorted note list after 'note'
  249. n0 = note->meas->noteL;
  250. for(; n0!=NULL; n0=n0->slink)
  251. {
  252. if( n0->tick >= nn->tick )
  253. {
  254. nn->slink = n0->slink;
  255. n0->slink = nn;
  256. break;
  257. }
  258. // if n0 is the last ele in the list
  259. if( n0->slink == NULL )
  260. {
  261. n0->slink = nn;
  262. nn->slink = NULL;
  263. break;
  264. }
  265. }
  266. assert(n0 != NULL);
  267. }
  268. cmXsRC_t _cmXScoreParsePartList( cmXScore_t* p )
  269. {
  270. cmXsRC_t rc = kOkXsRC;
  271. cmXsPart_t* lastPartPtr = NULL;
  272. const cmXmlNode_t* xnp;
  273. // find the 'part-list'
  274. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part-list", NULL, 0)) == NULL )
  275. return _cmXScoreMissingNode(p,cmXmlRoot(p->xmlH),"part-list");
  276. const cmXmlNode_t* cnp = xnp->children;
  277. // for each child of the 'part-list'
  278. for(; cnp!=NULL; cnp=cnp->sibling)
  279. if( cmTextCmp( cnp->label, "score-part" ) == 0 )
  280. {
  281. const cmXmlAttr_t* a;
  282. // find the 'score-part' id
  283. if((a = cmXmlFindAttrib(cnp,"id")) == NULL )
  284. return _cmXScoreMissingAttribute(p,cnp,"id");
  285. // allocate a new part record
  286. cmXsPart_t* pp = cmLhAllocZ(p->lhH,cmXsPart_t,1);
  287. pp->idStr = a->value; // set the part id
  288. // link the new part record to the end of the part list
  289. if(lastPartPtr == NULL)
  290. p->partL = pp;
  291. else
  292. lastPartPtr->link = pp;
  293. lastPartPtr = pp;
  294. }
  295. return rc;
  296. }
  297. cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* np )
  298. {
  299. cmXsRC_t rc = kOkXsRC;
  300. unsigned octave = 0;
  301. double alter = 0;
  302. const cmChar_t* step = NULL;
  303. if((step = cmXmlNodeValue(nnp,"pitch","step",NULL)) == NULL )
  304. return _cmXScoreMissingNode(p,nnp,"step");
  305. if((rc = cmXmlNodeUInt( nnp,&octave,"pitch","octave",NULL)) != kOkXmlRC )
  306. return _cmXScoreMissingNode(p,nnp,"octave");
  307. cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
  308. int acc = alter;
  309. unsigned midi = cmSciPitchToMidiPitch(*step,acc,octave);
  310. np->pitch = midi;
  311. np->step = *step;
  312. np->octave = octave;
  313. np->alter = alter;
  314. np->flags |= kOnsetXsFl;
  315. return rc;
  316. }
  317. cmXsRC_t _cmXScoreParseNoteRValue( cmXScore_t* p, const cmXmlNode_t* nnp, const cmChar_t* label, double* rvalueRef )
  318. {
  319. typedef struct map_str
  320. {
  321. double rvalue;
  322. const cmChar_t* label;
  323. } map_t;
  324. map_t mapV[] =
  325. {
  326. {-1.0, "measure" }, // whole measure rest
  327. { 0.5, "breve" }, // double whole
  328. { 1.0, "whole" },
  329. { 2.0, "half" },
  330. { 4.0, "quarter" },
  331. { 8.0, "eighth" },
  332. {16.0, "16th" },
  333. {32.0, "32nd" },
  334. { 64.0, "64th" },
  335. {128.0, "128th" },
  336. { 0.0, "" }
  337. };
  338. const cmChar_t* str;
  339. // get the XML rvalue label
  340. if((str = cmXmlNodeValue(nnp,label,NULL)) == NULL)
  341. {
  342. if((nnp = cmXmlSearch(nnp,"rest",NULL,0)) != NULL )
  343. {
  344. const cmXmlAttr_t* a;
  345. if((a = cmXmlFindAttrib(nnp,"measure")) != NULL && cmTextCmp(a->value,"yes")==0)
  346. {
  347. *rvalueRef = -1;
  348. return kOkXsRC;
  349. }
  350. }
  351. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'beat-unit' metronome value is missing on line %i.",nnp->line);
  352. }
  353. unsigned i;
  354. // lookup the rvalue numeric value from the mapV[] table
  355. for(i=0; mapV[i].rvalue!=0; ++i)
  356. if( cmTextCmp(mapV[i].label,str) == 0 )
  357. {
  358. *rvalueRef = mapV[i].rvalue;
  359. return kOkXsRC;
  360. }
  361. // the rvalue label was not found
  362. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unknown rvalue type='%s'.",str);
  363. }
  364. cmXsRC_t _cmXScoreParseColor( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* note )
  365. {
  366. cmXsRC_t rc = kOkXsRC;
  367. const cmXmlAttr_t* a;
  368. typedef struct map_str
  369. {
  370. unsigned value;
  371. const cmChar_t* label;
  372. } map_t;
  373. map_t mapV[] =
  374. {
  375. { kEvenXsFl, "#0000FF" }, // blue (even)
  376. { kTempoXsFl, "#00FF00" }, // green (tempo)
  377. { kDynXsFl, "#FF0000" }, // red (dynamics)
  378. { kTempoXsFl | kEvenXsFl, "#00FFFF" }, // green + blue (turquoise)
  379. { kDynXsFl | kEvenXsFl, "#FF00FF" }, // red + blue
  380. { kDynXsFl | kEvenXsFl, "#FF0CF7" }, // magenta (even+dyn)
  381. { kDynXsFl | kTempoXsFl, "#FF7F00" }, // red + green (brown)
  382. { kTempoXsFl | kEvenXsFl | kDynXsFl, "#996633" }, // (purple)
  383. { kDynXsFl, "#FF6A03" }, // 176 orange (dynamics)
  384. { kEvenXsFl, "#2F00E8" }, // 1001 blue (even)
  385. { kTempoXsFl, "#01CD1F" }, // 1196 green (tempo)
  386. { kEvenXsFl, "#3600E8" }, // 1627 blue (even)
  387. { kDynXsFl | kTempoXsFl, "#9E8F15" }, // 8827 brown (dyn + tempo)
  388. { kEvenXsFl, "#2E00E6" }, // 5393 blue (even)
  389. { kEvenXsFl, "#2C00DD" }, // 5895 blue (even)
  390. { kDynXsFl, "#FF5B03" }, // 6498 orange (dyn)
  391. { kDynXsFl, "#FF6104" }, // 6896 orange
  392. { kEvenXsFl, "#2A00E6" }, // 7781 blue
  393. { kEvenXsFl, "#2300DD" }, // 8300 blue (even)
  394. { kTempoXsFl, "#03CD22" }, // 10820 green (tempo)
  395. { kEvenXsFl, "#3400DB" }, // 11627 blue (dyn)
  396. { -1, "" }
  397. };
  398. /*
  399. orange #FF6A03
  400. magenta #FF0CF7
  401. blue #2F00E8
  402. green #01CD1F
  403. gold #9E8F15
  404. green #03CD22
  405. */
  406. if((a = cmXmlFindAttrib(nnp, "color" )) != NULL )
  407. {
  408. unsigned i;
  409. for(i=0; mapV[i].value != -1; ++i)
  410. if( cmTextCmp(a->value,mapV[i].label) == 0 )
  411. {
  412. note->flags += mapV[i].value;
  413. break;
  414. }
  415. if( mapV[i].value == -1 )
  416. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The note color '%s' was not found on line %i.",a->value,nnp->line);
  417. }
  418. return rc;
  419. }
  420. // On input tick0Ref is set to the tick of the previous event.
  421. // On input tickRef is set to the tick of this event.
  422. // On output tick0Ref is set to the tick of this event.
  423. // On output tickRef is set to the tick of the next event.
  424. cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* nnp, unsigned* tick0Ref, unsigned* tickRef )
  425. {
  426. cmXsRC_t rc = kOkXsRC;
  427. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  428. unsigned voiceId;
  429. note->meas = meas;
  430. note->xmlNode = nnp;
  431. // get the voice id for this node
  432. if( cmXmlNodeUInt(nnp,&voiceId,"voice",NULL) != kOkXmlRC )
  433. return _cmXScoreMissingNode(p,nnp,"voice");
  434. // if this note has a pitch
  435. if( cmXmlNodeHasChild(nnp,"pitch",NULL) )
  436. if((rc = _cmXScoreParsePitch(p,nnp,note)) != kOkXsRC )
  437. return rc;
  438. cmXmlNodeUInt(nnp,&note->duration,"duration",NULL); // get the note duration
  439. cmXmlNodeUInt(nnp,&note->staff,"staff",NULL); // get th staff number
  440. // is 'rest'
  441. if( cmXmlNodeHasChild(nnp,"rest",NULL) )
  442. note->flags |= kRestXsFl;
  443. // is 'grace'
  444. if( cmXmlNodeHasChild(nnp,"grace",NULL) )
  445. note->flags |= kGraceXsFl;
  446. // is 'dot'
  447. if( cmXmlNodeHasChild(nnp,"dot",NULL) )
  448. note->flags |= kDotXsFl;
  449. // is 'chord'
  450. if( cmXmlNodeHasChild(nnp,"chord",NULL) )
  451. note->flags |= kChordXsFl;
  452. // is this is first note in a tied pair
  453. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","start",NULL) )
  454. note->flags |= kTieBegXsFl;
  455. // is this is second note in a tied pair
  456. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","stop",NULL) )
  457. note->flags |= kTieEndXsFl;
  458. // has 'heel' mark
  459. if( cmXmlNodeHasChild(nnp,"notations","technical","heel",NULL) )
  460. note->flags |= kHeelXsFl;
  461. // set color coded flags
  462. if((rc = _cmXScoreParseColor(p, nnp, note )) != kOkXsRC )
  463. return rc;
  464. // get the note's rythmic value
  465. if((rc = _cmXScoreParseNoteRValue(p,nnp,"type",&note->rvalue)) != kOkXsRC )
  466. return rc;
  467. // if this is a chord note
  468. if( cmIsFlag(note->flags,kChordXsFl) )
  469. {
  470. note->tick = *tick0Ref; // then use the onset time from the previous note and do not advance time
  471. }
  472. else
  473. {
  474. *tick0Ref = *tickRef;
  475. note->tick = *tickRef;
  476. *tickRef += note->duration;
  477. }
  478. return _cmXScorePushNote(p, meas, voiceId, note );
  479. }
  480. 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 )
  481. {
  482. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  483. unsigned voiceId = 0; // non-note's are always assigned to voiceId=0;
  484. note->tick = tick;
  485. note->flags = flags;
  486. note->rvalue = rvalue;
  487. note->tvalue = tvalue;
  488. note->duration = duration;
  489. note->meas = meas;
  490. note->xmlNode = noteXmlNode;
  491. return _cmXScorePushNote(p, meas, voiceId, note );
  492. }
  493. cmXsSpan_t* _cmXScoreFindOpenOctaveShift( cmXScore_t* p, unsigned staff, unsigned number )
  494. {
  495. cmXsSpan_t* s = p->spanL;
  496. for(; s!=NULL; s=s->link)
  497. if( s->tick1 == -1 && s->staff == staff && s->number == number )
  498. return s;
  499. return NULL;
  500. }
  501. cmXsRC_t _cmXScorePushOctaveShift(cmXScore_t* p, cmXsMeas_t* meas, unsigned staff, unsigned span_number, const cmChar_t* type_str, unsigned tick)
  502. {
  503. assert( meas != NULL);
  504. cmXsSpan_t* s;
  505. if( cmTextCmp(type_str,"stop") == 0 )
  506. {
  507. if((s = _cmXScoreFindOpenOctaveShift(p,staff,span_number)) == NULL )
  508. return cmErrWarnMsg(&p->err,kUnterminatedOctaveShiftXsrRC,"An illegal octave shift was encounted in meas %i.\n",meas->number);
  509. s->tick1 = tick;
  510. }
  511. else
  512. {
  513. s = cmLhAllocZ(p->lhH,cmXsSpan_t,1);
  514. s->staff = staff;
  515. s->meas = meas;
  516. s->number = span_number;
  517. s->tick0 = tick;
  518. s->tick1 = -1;
  519. s->pitch_offset = cmTextCmp(type_str,"up")==0 ? -12 : 12;
  520. s->link = p->spanL;
  521. p->spanL = s;
  522. }
  523. return kOkXsRC;
  524. }
  525. cmXsRC_t _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* dnp, unsigned tick)
  526. {
  527. cmXsRC_t rc = kOkXsRC;
  528. const cmXmlNode_t* np = NULL;
  529. const cmXmlAttr_t* a = NULL;
  530. unsigned flags = 0;
  531. int offset = 0;
  532. double rvalue = 0;
  533. const cmChar_t* tvalue = NULL;
  534. unsigned duration = 0;
  535. bool pushFl = true;
  536. unsigned staff = 0;
  537. cmXmlNodeInt( dnp, &offset, "offset", NULL );
  538. cmXmlNodeUInt(dnp, &staff, "staff", NULL );
  539. // if this is a metronome direction
  540. if((np = cmXmlSearch( dnp, "metronome", NULL, 0)) != NULL )
  541. {
  542. if( cmXmlNodeUInt(np,&duration,"per-minute",NULL) != kOkXmlRC )
  543. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'per-minute' metronome value is missing on line %i.",np->line);
  544. if((rc = _cmXScoreParseNoteRValue(p,np,"beat-unit",&rvalue)) != kOkXsRC )
  545. return rc;
  546. flags = kMetronomeXsFl;
  547. }
  548. else
  549. // if this is a pedal direction
  550. if((np = cmXmlSearch( dnp, "pedal",NULL,0)) != NULL )
  551. {
  552. if((a = cmXmlFindAttrib(np,"type")) == NULL )
  553. return _cmXScoreMissingAttribute(p, np, "type" );
  554. if( cmTextCmp(a->value,"start") == 0 )
  555. flags = kPedalDnXsFl;
  556. else
  557. if( cmTextCmp(a->value,"change") == 0 )
  558. flags = kPedalUpDnXsFl;
  559. else
  560. if( cmTextCmp(a->value,"stop") == 0 )
  561. flags = kPedalUpXsFl;
  562. else
  563. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unrecognized pedal type:'%s'.",cmStringNullGuard(a->value));
  564. }
  565. else
  566. // if this is a 'words' direction
  567. if((np = cmXmlSearch( dnp, "words", NULL, 0)) != NULL )
  568. {
  569. if((a = cmXmlFindAttrib(np,"enclosure")) != NULL && cmTextCmp(a->value,"rectangle")==0 )
  570. {
  571. if( cmTextIsEmpty( tvalue = np->dataStr ) )
  572. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Section number is blank or missing on line %i.",np->line);
  573. flags = kSectionXsFl;
  574. }
  575. }
  576. else
  577. // if this is an 'octave-shift' direction
  578. if((np = cmXmlSearch( dnp, "octave-shift", NULL, 0)) != NULL )
  579. {
  580. unsigned span_number = -1;
  581. if( cmXmlAttrUInt(np,"number",&span_number) != kOkXmlRC )
  582. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'number' attribute.");
  583. if((a = cmXmlFindAttrib(np,"type")) == NULL)
  584. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'type' attribute.");
  585. rc = _cmXScorePushOctaveShift(p,meas,staff,span_number,a->value,tick+offset);
  586. pushFl = false;
  587. }
  588. else
  589. {
  590. pushFl = false;
  591. }
  592. if( pushFl )
  593. rc = _cmXScorePushNonNote(p,meas,dnp,tick+offset,duration,rvalue,tvalue,flags);
  594. return rc;
  595. }
  596. // On input tickRef is set to the absolute tick of the bar line and on output it is set
  597. // to the absolute tick of the next bar line.
  598. cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t* mnp, unsigned* tickRef)
  599. {
  600. cmXsRC_t rc = kOkXsRC;
  601. const cmXmlNode_t* np = NULL;
  602. unsigned tick = *tickRef;
  603. unsigned tick0= 0;
  604. cmXsMeas_t* m = NULL;
  605. // allocate the 'measure' record
  606. cmXsMeas_t* meas = cmLhAllocZ(p->lhH,cmXsMeas_t,1);
  607. // get measure number
  608. if( cmXmlAttrUInt(mnp,"number", &meas->number) != kOkXmlRC )
  609. return _cmXScoreMissingAttribute(p,mnp,"number");
  610. if( pp->measL == NULL )
  611. pp->measL = meas;
  612. else
  613. {
  614. m = pp->measL;
  615. while( m->link != NULL )
  616. m = m->link;
  617. m->link = meas;
  618. meas->divisions = m->divisions;
  619. meas->beats = m->beats;
  620. meas->beat_type = m->beat_type;
  621. }
  622. // get measure attributes node
  623. if((np = cmXmlSearch(mnp,"attributes",NULL,0)) != NULL)
  624. {
  625. cmXmlNodeUInt(np,&meas->divisions,"divisions",NULL);
  626. cmXmlNodeUInt(np,&meas->beats, "time","beats",NULL);
  627. cmXmlNodeUInt(np,&meas->beat_type,"time","beat-type",NULL);
  628. }
  629. // store the bar line
  630. if((rc = _cmXScorePushNonNote(p,meas,mnp,tick,0,0,NULL,kBarXsFl)) != kOkXsRC )
  631. return rc;
  632. np = mnp->children;
  633. // for each child of the 'meas' XML node
  634. for(; rc==kOkXsRC && np!=NULL; np=np->sibling)
  635. {
  636. // if this is a 'note' node
  637. if( cmTextCmp(np->label,"note") == 0 )
  638. {
  639. rc = _cmXScoreParseNote(p,meas,np,&tick0,&tick);
  640. }
  641. else
  642. // if this is a 'backup' node
  643. if( cmTextCmp(np->label,"backup") == 0 )
  644. {
  645. unsigned backup;
  646. cmXmlNodeUInt(np,&backup,"duration",NULL);
  647. if( backup > tick )
  648. tick = 0;
  649. else
  650. tick -= backup;
  651. tick0 = tick;
  652. }
  653. else
  654. // if this is a 'direction' node
  655. if( cmTextCmp(np->label,"direction") == 0 )
  656. {
  657. rc = _cmXScoreParseDirection(p,meas,np,tick);
  658. }
  659. }
  660. *tickRef = tick;
  661. return rc;
  662. }
  663. cmXsRC_t _cmXScoreParsePart( cmXScore_t* p, cmXsPart_t* pp )
  664. {
  665. cmXsRC_t rc = kOkXsRC;
  666. const cmXmlNode_t* xnp;
  667. cmXmlAttr_t partAttr = { "id", pp->idStr };
  668. unsigned barTick = 0;
  669. // find the 'part'
  670. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part", &partAttr, 1)) == NULL )
  671. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The part '%s' was not found.",pp->idStr);
  672. // for each child of this part - find each measure
  673. const cmXmlNode_t* cnp = xnp->children;
  674. for(; cnp!=NULL; cnp=cnp->sibling)
  675. if( cmTextCmp(cnp->label,"measure") == 0 )
  676. if((rc = _cmXScoreParseMeasure(p,pp,cnp,&barTick)) != kOkXsRC )
  677. return rc;
  678. return rc;
  679. }
  680. // Insert note 'np' into the sorted note list based at 's0'.
  681. // Return a pointer to the base of the list after the insertion.
  682. cmXsNote_t* _cmXScoreInsertSortedNote( cmXsNote_t* s0, cmXsNote_t* np )
  683. {
  684. assert( np != NULL );
  685. // np->slink is not NULL if the list is being resorted
  686. np->slink = NULL;
  687. // this list is empty so np is the first element on the list
  688. if( s0 == NULL )
  689. return np;
  690. // np is before the first element on the list
  691. if( np->tick < s0->tick )
  692. {
  693. np->slink = s0;
  694. return np;
  695. }
  696. cmXsNote_t* s1 = s0;
  697. cmXsNote_t* s2 = s0->slink;
  698. while( s2 != NULL )
  699. {
  700. if( s2->tick > np->tick )
  701. {
  702. s1->slink = np;
  703. np->slink = s2;
  704. return s0;
  705. }
  706. s1 = s2;
  707. s2 = s2->slink;
  708. }
  709. s1->slink = np;
  710. return s0;
  711. }
  712. void _cmXScoreSort( cmXScore_t* p )
  713. {
  714. // for each part
  715. cmXsPart_t* pp = p->partL;
  716. for(; pp!=NULL; pp=pp->link)
  717. {
  718. // for each measure in this part
  719. cmXsMeas_t* mp = pp->measL;
  720. for(; mp!=NULL; mp=mp->link)
  721. {
  722. // explicitely set noteL to NULL to in case we are re-sorting
  723. mp->noteL = NULL;
  724. // for each voice in this measure
  725. cmXsVoice_t* vp = mp->voiceL;
  726. for(; vp!=NULL; vp=vp->link)
  727. {
  728. // for each note in this measure
  729. cmXsNote_t* np = vp->noteL;
  730. for(; np!=NULL; np=np->mlink)
  731. mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np);
  732. }
  733. }
  734. }
  735. }
  736. void _cmXScoreSetAbsoluteTime( cmXScore_t* p )
  737. {
  738. double tpqn = 0; // ticks per quarter note
  739. double tps = 0; // ticks per second
  740. unsigned metro_tick = 0;
  741. double metro_sec = 0;
  742. double sec0 = 0;
  743. cmXsPart_t* pp = p->partL;
  744. for(; pp!=NULL; pp=pp->link)
  745. {
  746. cmXsMeas_t* mp = pp->measL;
  747. for(; mp!=NULL; mp=mp->link)
  748. {
  749. if( mp->divisions != 0 )
  750. tpqn = mp->divisions;
  751. cmXsNote_t* np = mp->noteL;
  752. for(; np!=NULL; np=np->slink)
  753. {
  754. // Seconds are calculated as:
  755. // dticks = np->tick - metro_tick; // where metro_tick is the absolute tick of the last metro event
  756. // secs = (dticks/tps) + metro_secs; // where metro_secs is the absoute time of the last metro event
  757. unsigned dticks = np->tick - metro_tick;
  758. double secs = tps==0 ? 0 : (dticks/tps) + metro_sec;
  759. double dsecs = secs - sec0;
  760. //
  761. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  762. {
  763. double bpm = np->duration;
  764. double bps = bpm / 60.0;
  765. tps = bps * tpqn;
  766. metro_tick = np->tick;
  767. metro_sec = secs;
  768. }
  769. if( cmIsFlag(np->flags,kBarXsFl|kPedalDnXsFl|kPedalUpXsFl|kPedalUpDnXsFl|kSostDnXsFl|kSostUpXsFl|kOnsetXsFl|kSectionXsFl) )
  770. {
  771. np->secs = secs;
  772. np->dsecs = dsecs;
  773. sec0 = secs;
  774. }
  775. }
  776. }
  777. }
  778. }
  779. // All notes in a[aN] are on the same tick
  780. unsigned _cmXsSpreadGraceNotes( cmXsNote_t** a, unsigned aN )
  781. {
  782. unsigned i;
  783. bool barFl = false;
  784. // set barFl to true if a bar marker is included in the notes
  785. for(i=0; i<aN; ++i)
  786. if( cmIsFlag(a[i]->flags,kBarXsFl) )
  787. barFl = true;
  788. // spread any grace notes by one tick
  789. unsigned nextGraceTick = UINT_MAX;
  790. for(i=0; i<aN; ++i)
  791. if( cmIsFlag(a[i]->flags,kGraceXsFl) )
  792. {
  793. if( nextGraceTick == UINT_MAX )
  794. nextGraceTick = a[i]->tick + 1;
  795. else
  796. {
  797. a[i]->tick = nextGraceTick;
  798. nextGraceTick += 1;
  799. }
  800. }
  801. // if this tick group includes the bar ...
  802. if( barFl && nextGraceTick != UINT_MAX )
  803. {
  804. // ... then move all non-grace note events (except the bar marker) after
  805. // the grace notes
  806. for(i=0; i<aN; ++i)
  807. if( cmIsNotFlag(a[i]->flags,kGraceXsFl) && cmIsNotFlag(a[i]->flags,kBarXsFl) )
  808. a[i]->tick = nextGraceTick;
  809. }
  810. return nextGraceTick==UINT_MAX ? 0 : nextGraceTick;
  811. }
  812. void _cmXScoreSpreadGraceNotes( cmXScore_t* p )
  813. {
  814. cmXsPart_t* pp = p->partL;
  815. for(; pp!=NULL; pp=pp->link)
  816. {
  817. // tick1 is the location of the minimum current tick
  818. // (or 0 if it should be ignored)
  819. unsigned tick1 = 0;
  820. cmXsMeas_t* mp = pp->measL;
  821. for(; mp!=NULL; mp=mp->link)
  822. {
  823. cmXsNote_t* np = mp->noteL;
  824. unsigned aN = 128;
  825. cmXsNote_t* a[ aN ];
  826. unsigned ai = 0;
  827. // The first event in a measure may have been forced ahead
  828. // by spreading at the end of the previous measure
  829. if( tick1 > np->tick )
  830. np->tick = tick1;
  831. else
  832. tick1 = 0;
  833. // tick0 is the tick of the current tick group we are examining
  834. // A tick group is a group of events that share the same tick.
  835. unsigned tick0 = np->tick;
  836. for(; np!=NULL; np=np->slink)
  837. {
  838. // if this event is the first of a new tick group
  839. if( np->tick != tick0 )
  840. {
  841. // if there is more than one event in the completed tick group ...
  842. if( ai > 1 )
  843. tick1 = _cmXsSpreadGraceNotes(a,ai); // ... then process the group
  844. ai = 0; // empty the tick group array
  845. tick0 = np->tick; // update the current group's common tick
  846. }
  847. // if the min. current tick is ahead of this event then move the event ahead
  848. if( tick1 > np->tick )
  849. np->tick = tick1;
  850. else
  851. tick1 = 0; // otherwise disable tick1
  852. // add this event to the tick group
  853. assert(ai<aN);
  854. a[ai++] = np;
  855. }
  856. // if there are events in the group array then process them
  857. if( ai > 1 )
  858. tick1 = _cmXsSpreadGraceNotes(a,ai);
  859. }
  860. }
  861. }
  862. bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
  863. {
  864. cmXsNote_t* nnp = n0p->slink; // begin w/ note following np
  865. unsigned measNumb = mp->number;
  866. cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' ');
  867. if( rptFl )
  868. printf("%i %i %s ",n0p->meas->number,n0p->tick,cmMidiToSciPitch(n0p->pitch,NULL,0));
  869. while(1)
  870. {
  871. // if we are at the end of a measure advance to the next measure
  872. if( nnp == NULL )
  873. {
  874. mp = mp->link;
  875. nnp = mp->noteL;
  876. // if a measure was completed and no end note was found ... then the tie is unterminated
  877. // (a tie must be continued in every measure which it passes through)
  878. if( mp->number > measNumb + 1 )
  879. break;
  880. }
  881. // for each note starting at nnp
  882. for(; nnp!=NULL; nnp=nnp->slink)
  883. {
  884. // if this note is tied to the originating note (np)
  885. if( nnp->voice->id == n0p->voice->id && nnp->step == n0p->step && nnp->octave == n0p->octave )
  886. {
  887. nnp->flags |= kTieProcXsFl;
  888. nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl);
  889. n0p->tied = nnp;
  890. if( rptFl )
  891. printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
  892. // if this note is not tied to a subsequent note
  893. if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
  894. return true;
  895. n0p = nnp;
  896. // record the measure number of the last note with a tie-start
  897. measNumb = mp->number;
  898. }
  899. }
  900. }
  901. cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i was not terminated.",n0p->step,acc,n0p->octave,measNumb);
  902. return false;
  903. }
  904. void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
  905. {
  906. unsigned n = 0;
  907. unsigned m = 0;
  908. bool rptFl = false;
  909. cmXsPart_t* pp = p->partL;
  910. // for each part
  911. for(; pp!=NULL; pp=pp->link)
  912. {
  913. unsigned locIdx = 1;
  914. cmXsMeas_t* mp = pp->measL;
  915. // for each measure
  916. for(; mp!=NULL; mp=mp->link)
  917. {
  918. cmXsNote_t* n0 = NULL;
  919. cmXsNote_t* np = mp->noteL;
  920. // for each note in this measure
  921. for(; np!=NULL; np=np->slink)
  922. {
  923. // if this note begins a tie and has not yet been processed
  924. // (A note that continues a tie and therefore has a kTieBegXsFl set
  925. // may have already been processed by an earlier tied note.)
  926. if( cmIsFlag(np->flags,kTieBegXsFl) && cmIsNotFlag(np->flags,kTieProcXsFl))
  927. {
  928. if( _cmXScoreFindTiedNote(p,mp,np,rptFl) )
  929. m += 1;
  930. if( rptFl )
  931. printf("\n");
  932. n += 1;
  933. }
  934. // Validate the tie state of the current note.
  935. if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
  936. {
  937. cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' ');
  938. 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);
  939. }
  940. //
  941. // Set the score location of notes marked for onset and bar lines.
  942. //
  943. if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) )
  944. {
  945. // if this note does not share the same location as the previous 'located' note then increment the 'loc' index
  946. if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
  947. locIdx += 1;
  948. np->locIdx = locIdx;
  949. n0 = np;
  950. }
  951. }
  952. }
  953. }
  954. printf("Tied notes found:%i Not found:%i\n",m,n-m);
  955. }
  956. cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p )
  957. {
  958. const cmXsSpan_t* s;
  959. for(s=p->spanL; s!=NULL; s=s->link)
  960. {
  961. if( s->tick1 == -1)
  962. {
  963. cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"An unterminated octave shift span was encountered in measure %i staff=%i.",s->meas->number,s->staff);
  964. }
  965. else
  966. {
  967. cmXsMeas_t* m = p->partL->measL;
  968. for(; m!=NULL; m=m->link)
  969. if( m->number == s->meas->number )
  970. break;
  971. assert( m != NULL );
  972. cmXsNote_t* note = m->noteL;
  973. for(; note!=NULL; note=note->slink)
  974. if( note->staff==s->staff && s->tick0 <= note->tick && note->tick < s->tick1 )
  975. note->pitch += s->pitch_offset;
  976. }
  977. }
  978. return kOkXsRC;
  979. }
  980. // The identical pitch may be notated to play simultaneously on different voices.
  981. // As performed on the piano this will equate to a single sounding note.
  982. // This function clears the onset flag on all except one of the duplicated notes.
  983. void _cmXScoreRemoveDuplicateNotes( cmXScore_t* p )
  984. {
  985. cmXsPart_t* pp = p->partL;
  986. // for each part
  987. for(; pp!=NULL; pp=pp->link)
  988. {
  989. cmXsMeas_t* mp = pp->measL;
  990. // for each measure
  991. for(; mp!=NULL; mp=mp->link)
  992. {
  993. cmXsNote_t* np = mp->noteL;
  994. // for each note in this measure
  995. for(; np!=NULL; np=np->slink)
  996. if( cmIsFlag(np->flags,kOnsetXsFl) )
  997. {
  998. cmXsNote_t* n0p = mp->noteL;
  999. for(; n0p!=NULL; n0p=n0p->slink)
  1000. if( n0p!=np && cmIsFlag(n0p->flags,kOnsetXsFl) && np->locIdx==n0p->locIdx && np->pitch==n0p->pitch )
  1001. n0p->flags = cmClrFlag(n0p->flags,kOnsetXsFl);
  1002. }
  1003. }
  1004. }
  1005. }
  1006. void _cmXScoreSetMeasGroups( cmXScore_t* p, unsigned flag )
  1007. {
  1008. unsigned sectionId = 0;
  1009. cmXsNote_t* n0 = NULL;
  1010. cmXsPart_t* pp = p->partL;
  1011. // for each part
  1012. for(; pp!=NULL; pp=pp->link)
  1013. {
  1014. cmXsMeas_t* mp = pp->measL;
  1015. // for each measure
  1016. for(; mp!=NULL; mp=mp->link)
  1017. {
  1018. cmXsNote_t* np = mp->noteL;
  1019. // for each note in this measure
  1020. for(; np!=NULL; np=np->slink)
  1021. {
  1022. // if this note has a heel marker and we are looking for evenness events
  1023. if( cmIsFlag(flag,kEvenXsFl) && cmIsFlag(np->flags,kHeelXsFl) )
  1024. {
  1025. np->flags = cmSetFlag(np->flags,kBegGroupXsFl | kEndGroupXsFl );
  1026. np->evenGroupId = sectionId + 1;
  1027. }
  1028. // if this note is of the type we are looking for
  1029. if( cmIsFlag(np->flags,flag) )
  1030. {
  1031. if( n0 == NULL )
  1032. np->flags = cmSetFlag(np->flags,kBegGroupXsFl);
  1033. n0 = np;
  1034. }
  1035. // if this is a section marker
  1036. if( cmIsFlag(np->flags,kSectionXsFl) )
  1037. {
  1038. if( n0 != NULL )
  1039. {
  1040. np->flags = cmSetFlag(np->flags,kEndGroupXsFl);
  1041. switch( flag )
  1042. {
  1043. case kEvenXsFl: n0->evenGroupId = sectionId+1; break;
  1044. case kDynXsFl: n0->dynGroupId = sectionId+1; break;
  1045. case kTempoXsFl: n0->tempoGroupId= sectionId+1; break;
  1046. }
  1047. }
  1048. if( cmIsFlag(np->flags,kSectionXsFl) )
  1049. sectionId = np->tvalue==NULL ? 0 : strtol(np->tvalue,NULL,10);
  1050. n0 = NULL;
  1051. }
  1052. }
  1053. }
  1054. }
  1055. }
  1056. cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn )
  1057. {
  1058. cmXsRC_t rc = kOkXsRC;
  1059. cmFileH_t fH = cmFileNullHandle;
  1060. double ticks_per_sec = 0;
  1061. double onset_secs = 0;
  1062. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  1063. return cmErrMsg(&p->err,kFileFailXsRC,"Unable to create the file '%s'.",cmStringNullGuard(fn));
  1064. cmXsPart_t* pp = p->partL;
  1065. for(; pp!=NULL; pp=pp->link)
  1066. {
  1067. cmXsMeas_t* mp = pp->measL;
  1068. for(; mp!=NULL; mp=mp->link)
  1069. {
  1070. cmFilePrintf(fH,"b %f %i %s B\n",onset_secs,mp->number,"bar");
  1071. cmXsNote_t* np = mp->noteL;
  1072. unsigned tick0 = 0;
  1073. for(; np!=NULL; np=np->slink)
  1074. {
  1075. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  1076. {
  1077. double bps = np->duration / 60.0;
  1078. // t b t
  1079. // - = - -
  1080. // s s b
  1081. ticks_per_sec = bps * mp->divisions;
  1082. }
  1083. else
  1084. {
  1085. if( cmIsFlag(np->flags,kOnsetXsFl) )
  1086. {
  1087. onset_secs += (np->tick - tick0) / ticks_per_sec;
  1088. tick0 = np->tick;
  1089. 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");
  1090. }
  1091. }
  1092. }
  1093. onset_secs += (mp->divisions * mp->beats - tick0) / ticks_per_sec;
  1094. }
  1095. }
  1096. cmFileClose(&fH);
  1097. return rc;
  1098. }
  1099. // Force the bar event to be the first event in the measure.
  1100. void _cmXScoreFixBarLines( cmXScore_t* p )
  1101. {
  1102. cmXsPart_t* pp = p->partL;
  1103. for(; pp!=NULL; pp=pp->link)
  1104. {
  1105. cmXsMeas_t* mp = pp->measL;
  1106. for(; mp!=NULL; mp=mp->link)
  1107. {
  1108. cmXsNote_t* np = mp->noteL;
  1109. cmXsNote_t* ep = NULL;
  1110. for(; np!=NULL; np=np->slink )
  1111. {
  1112. if( cmIsFlag(np->flags,kBarXsFl) )
  1113. {
  1114. if( ep != NULL )
  1115. np->tick = ep->tick;
  1116. break;
  1117. }
  1118. if( ep == NULL )
  1119. ep = np;
  1120. }
  1121. }
  1122. }
  1123. }
  1124. cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
  1125. {
  1126. cmXsRC_t rc = kOkXsRC;
  1127. if((rc = cmXScoreFinalize(hp)) != kOkXsRC )
  1128. return rc;
  1129. cmXScore_t* p = cmMemAllocZ(cmXScore_t,1);
  1130. cmErrSetup(&p->err,&ctx->rpt,"XScore");
  1131. // create a local linked heap
  1132. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  1133. return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
  1134. // open the music xml file
  1135. if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
  1136. {
  1137. rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn));
  1138. goto errLabel;
  1139. }
  1140. //cmXmlPrint(p->xmlH,&ctx->rpt);
  1141. // parse the part-list
  1142. if((rc = _cmXScoreParsePartList( p )) != kOkXsRC )
  1143. goto errLabel;
  1144. // parse each score 'part'
  1145. cmXsPart_t* pp = p->partL;
  1146. for(; pp!=NULL; pp=pp->link)
  1147. if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC )
  1148. goto errLabel;
  1149. // fill in the note->slink chain to link the notes in each measure in time order
  1150. _cmXScoreSort(p);
  1151. _cmXScoreSpreadGraceNotes(p);
  1152. _cmXScoreSort(p);
  1153. _cmXScoreSetAbsoluteTime(p);
  1154. _cmXScoreResolveTiesAndLoc(p);
  1155. _cmXScoreRemoveDuplicateNotes(p);
  1156. _cmXScoreSetMeasGroups(p,kEvenXsFl);
  1157. _cmXScoreSetMeasGroups(p,kDynXsFl);
  1158. _cmXScoreSetMeasGroups(p,kTempoXsFl);
  1159. //_cmXScoreResolveOctaveShift(p);
  1160. // CSV output initialize failed.
  1161. if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC )
  1162. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed.");
  1163. errLabel:
  1164. if( rc != kOkXsRC )
  1165. _cmXScoreFinalize(p);
  1166. else
  1167. hp->h = p;
  1168. return rc;
  1169. }
  1170. cmXsRC_t cmXScoreFinalize( cmXsH_t* hp )
  1171. {
  1172. cmXsRC_t rc = kOkXsRC;
  1173. if( hp == NULL || cmXScoreIsValid(*hp)==false )
  1174. return kOkXsRC;
  1175. cmXScore_t* p = _cmXScoreHandleToPtr(*hp);
  1176. if((rc = _cmXScoreFinalize(p)) != kOkXsRC )
  1177. return rc;
  1178. hp->h = NULL;
  1179. return rc;
  1180. }
  1181. bool cmXScoreIsValid( cmXsH_t h )
  1182. { return h.h != NULL; }
  1183. //-------------------------------------------------------------------------------------------
  1184. typedef struct
  1185. {
  1186. unsigned idx; // Fields from the reordering input file which are
  1187. unsigned voice; // used to match the reorder record to
  1188. unsigned locIdx; // the associated a cmXsNode_t record.
  1189. unsigned tick; //
  1190. unsigned durtn; //
  1191. float rval; //
  1192. unsigned midi; //
  1193. cmXsNote_t* note; // The cmXsNode_t* associated with this cmXsReorder_t record
  1194. unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
  1195. unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kPedalUp/DnXsFl | kTieEndXsFl
  1196. unsigned newTick; // 0=ignore >0 new tick value
  1197. unsigned pitch; // 0=ignore >0 new pitch
  1198. } cmXsReorder_t;
  1199. typedef struct _cmXScoreDynMark_str
  1200. {
  1201. const cmChar_t* mark; //
  1202. unsigned id; // (1-17) maps to velocity
  1203. unsigned dyn; // pppp - fff (1-9) as used by cmScore
  1204. int adjust; // {-1,0,+1}
  1205. unsigned vel; // associated MIDI velocity
  1206. } _cmXScoreDynMark_t;
  1207. _cmXScoreDynMark_t _cmXScoreDynMarkArray[] =
  1208. {
  1209. {"pppp-", 1, 1, -1, 3},
  1210. {"pppp", 2, 1, 0, 10},
  1211. {"pppp+", 3, 1, 1, 22},
  1212. {"ppp-", 3, 2, -1, 22},
  1213. {"ppp", 4, 2, 0, 29},
  1214. {"ppp+", 5, 2, 1, 36},
  1215. {"pp-", 5, 3, -1, 36},
  1216. {"pp", 6, 3, 0, 43},
  1217. {"pp+", 7, 3, 1, 50},
  1218. {"p-", 7, 4, -1, 50},
  1219. {"p", 8, 4, 0, 57},
  1220. {"p+", 9, 4, 1, 64},
  1221. {"mp-", 9, 5, -1, 64},
  1222. {"mp", 10, 5, 0, 71},
  1223. {"mp+", 11, 5, 1, 78},
  1224. {"mf-", 11, 6, -1, 78},
  1225. {"mf", 12, 6, 0, 85},
  1226. {"mf+", 13, 6, 1, 92},
  1227. {"f-", 13, 7, -1, 92},
  1228. {"f", 14, 7, 0, 99},
  1229. {"f+", 15, 7, 1, 106},
  1230. {"ff", 16, 8, 0, 113},
  1231. {"ff+", 17, 8, 1, 120},
  1232. {"fff", 18, 9, 0, 127},
  1233. {NULL,0,0,0,0}
  1234. };
  1235. cmXsNote_t* _cmXsReorderFindNote( cmXScore_t* p, unsigned measNumb, const cmXsReorder_t* r, unsigned iii )
  1236. {
  1237. cmXsPart_t* pp = p->partL;
  1238. for(; pp!=NULL; pp=pp->link)
  1239. {
  1240. cmXsMeas_t* mp = pp->measL;
  1241. for(; mp!=NULL; mp=mp->link)
  1242. if( mp->number == measNumb)
  1243. {
  1244. cmXsNote_t* np = mp->noteL;
  1245. int index = 0;
  1246. for(; np!=NULL; np=np->slink,++index)
  1247. {
  1248. if( np->voice->id == r->voice &&
  1249. np->locIdx == r->locIdx &&
  1250. np->tick == r->tick &&
  1251. np->duration == r->durtn &&
  1252. np->rvalue == r->rval &&
  1253. np->pitch == r->midi &&
  1254. index == r->idx )
  1255. {
  1256. return np;
  1257. }
  1258. }
  1259. }
  1260. }
  1261. cmErrMsg(&p->err,kSyntaxErrorXsRC,"Reorder note not found meas:%i index:%i.",measNumb,iii);
  1262. return NULL;
  1263. }
  1264. void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned flags )
  1265. {
  1266. // Create a new score event record
  1267. cmXsNote_t* nn = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  1268. nn->uid = p->nextUid++;
  1269. nn->voice = r->note->voice;
  1270. nn->meas = r->note->meas;
  1271. nn->flags = flags;
  1272. // Pedal down events occur after the event they are attached to
  1273. if( cmIsFlag(flags,kSostDnXsFl | kPedalDnXsFl ) )
  1274. {
  1275. nn->tick = r->note->tick + 1;
  1276. _cmXScoreInsertNoteAfter(r->note,nn);
  1277. }
  1278. else
  1279. {
  1280. // Pedal up events occur before the event they are attached to
  1281. if( cmIsFlag(flags,kSostUpXsFl | kPedalUpXsFl ) )
  1282. {
  1283. nn->tick = r->note->tick==0 ? 0 : r->note->tick - 1;
  1284. _cmXScoreInsertNoteBefore(r->note,nn);
  1285. }
  1286. else
  1287. { assert(0); }
  1288. }
  1289. }
  1290. cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN )
  1291. {
  1292. unsigned i;
  1293. if( rN == 0 )
  1294. return kOkXsRC;
  1295. // set the 'note' field on each cmXsReorder_t record
  1296. for(i=0; i<rN; ++i)
  1297. {
  1298. if((rV[i].note = _cmXsReorderFindNote(p,measNumb,rV+i,i)) == NULL )
  1299. return kSyntaxErrorXsRC;
  1300. }
  1301. cmXsMeas_t* mp = rV[0].note->meas;
  1302. cmXsNote_t* n0p = NULL;
  1303. assert( mp->number == measNumb );
  1304. // Reassign the slink of the cmXsNote_t records in this measure
  1305. // according to their order in rV[].
  1306. for(i=0; i<rN; ++i)
  1307. {
  1308. if( n0p == NULL )
  1309. mp->noteL = rV[i].note;
  1310. else
  1311. n0p->slink = rV[i].note;
  1312. // if a new tick was specified
  1313. if( rV[i].newTick != 0 )
  1314. rV[i].note->tick = rV[i].newTick;
  1315. // if a dynamic or velocity mark was included
  1316. if( rV[i].dynIdx != cmInvalidIdx )
  1317. {
  1318. rV[i].note->dynamics = _cmXScoreDynMarkArray[ rV[i].dynIdx ].dyn;
  1319. rV[i].note->vel = _cmXScoreDynMarkArray[ rV[i].dynIdx ].vel;
  1320. }
  1321. // if the tie end flag was set
  1322. if( cmIsFlag(rV[i].newFlags,kTieEndXsFl) )
  1323. {
  1324. rV[i].note->flags |= kTieEndXsFl;
  1325. rV[i].note->flags = cmClrFlag(rV[i].note->flags, kOnsetXsFl);
  1326. rV[i].newFlags = cmClrFlag(rV[i].newFlags,kTieEndXsFl );
  1327. }
  1328. // if a new note value was specified
  1329. if( rV[i].pitch != 0 )
  1330. rV[i].note->pitch = rV[i].pitch;
  1331. n0p = rV[i].note;
  1332. n0p->slink = NULL;
  1333. }
  1334. // Insert new note records for pedal up/dn events.
  1335. for(i=0; i<rN; ++i)
  1336. {
  1337. if( rV[i].newFlags != 0 )
  1338. {
  1339. if( cmIsFlag(rV[i].newFlags,kPedalDnXsFl ) )
  1340. _cmXScoreInsertPedalEvent(p,rV + i,kPedalDnXsFl);
  1341. if( cmIsFlag(rV[i].newFlags,kSostDnXsFl ) )
  1342. _cmXScoreInsertPedalEvent(p,rV + i,kSostDnXsFl);
  1343. if( cmIsFlag(rV[i].newFlags,kPedalUpXsFl ) )
  1344. _cmXScoreInsertPedalEvent(p,rV + i,kPedalUpXsFl);
  1345. if( cmIsFlag(rV[i].newFlags,kSostUpXsFl ) )
  1346. _cmXScoreInsertPedalEvent(p,rV + i,kSostUpXsFl);
  1347. }
  1348. }
  1349. return kOkXsRC;
  1350. }
  1351. cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, unsigned* dynIdxRef )
  1352. {
  1353. cmXsRC_t rc = kOkXsRC;
  1354. const cmChar_t* s;
  1355. *dynIdxRef = cmInvalidIdx;
  1356. // locate the '!' which indicates the start of a dynamic marking
  1357. if( (s = strchr(b,'!')) == NULL )
  1358. return rc;
  1359. ++s; // increment past the '!'
  1360. if( *s == 0 )
  1361. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected end-of-line on dynamics parsing on line:%i.",lineNumb);
  1362. // some dynamic markings are surrounded by parenthesis (to indicate a dynamic level with greater uncertainty)
  1363. if( *s == '(' )
  1364. ++s; // skip the paren.
  1365. if( *s == 0 )
  1366. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected end-of-line on dynamics parsing on line:%i.",lineNumb);
  1367. unsigned i = 0;
  1368. unsigned j = 0;
  1369. unsigned n = 6;
  1370. bool doneFl = false;
  1371. cmChar_t mark[n+1];
  1372. memset(mark,0,n+1);
  1373. for(i=0; j<n && doneFl==false; ++i)
  1374. {
  1375. switch(s[i])
  1376. {
  1377. case 'm':
  1378. case 'p':
  1379. case 'f':
  1380. case '+':
  1381. case '-':
  1382. mark[j++] = s[i];
  1383. break;
  1384. case ')': // ending paren.
  1385. case 0: // end of string
  1386. case ' ': // end of mark
  1387. case '\n': // end of line
  1388. default: // anything else
  1389. doneFl = true;
  1390. break;
  1391. }
  1392. }
  1393. if( !doneFl )
  1394. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal dynamic mark (%s) syntax on line:%i.",mark,lineNumb);
  1395. // look up the dynamic mark in the reference table
  1396. for(j=0; _cmXScoreDynMarkArray[j].mark!=NULL; ++j)
  1397. if( strcmp(mark,_cmXScoreDynMarkArray[j].mark) == 0 )
  1398. break;
  1399. if( _cmXScoreDynMarkArray[j].mark == NULL )
  1400. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The dynamic mark '%s' is not legal on line:%i.",mark,lineNumb);
  1401. *dynIdxRef = j;
  1402. return rc;
  1403. }
  1404. cmXsRC_t _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* newFlagsRef )
  1405. {
  1406. cmXsRC_t rc = kOkXsRC;
  1407. const cmChar_t* s;
  1408. bool doneFl = false;
  1409. unsigned i = 0;
  1410. *newFlagsRef = 0;
  1411. // tilde indicates a pedal event
  1412. if((s = strchr(b,'~')) == NULL )
  1413. return rc;
  1414. do
  1415. {
  1416. ++s;
  1417. switch( *s )
  1418. {
  1419. case 'd':
  1420. *newFlagsRef |= kSostDnXsFl; // sostenuto pedal down just after this note onset
  1421. break;
  1422. case 'u':
  1423. *newFlagsRef |= kSostUpXsFl; // sostenuto pedal up just before this event
  1424. break;
  1425. case 'x':
  1426. *newFlagsRef |= (kSostUpXsFl | kSostDnXsFl); // sostenuto pedal up just before this event and sost down just after it.
  1427. break;
  1428. case 'D':
  1429. *newFlagsRef |= kPedalDnXsFl; // damper pedal down
  1430. break;
  1431. case 'U':
  1432. *newFlagsRef |= kPedalUpXsFl; // damper pedal up
  1433. break;
  1434. case '_':
  1435. *newFlagsRef |= kTieEndXsFl; // set tie end flag
  1436. break;
  1437. default:
  1438. if( i == 0 )
  1439. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected flag marking '%c' on line %i.",*s,line);
  1440. doneFl = true;
  1441. }
  1442. ++i;
  1443. }while(!doneFl);
  1444. return rc;
  1445. }
  1446. cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* tickRef )
  1447. {
  1448. cmXsRC_t rc = kOkXsRC;
  1449. const cmChar_t* s;
  1450. if((s = strchr(b,'@')) == NULL )
  1451. return rc;
  1452. ++s;
  1453. if(!isdigit(*s))
  1454. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected tick reorder value '%c' on line %i.",*s,line);
  1455. if(sscanf(s,"%i",tickRef) != 1 )
  1456. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"tick reorder parse failed on line %i.",line);
  1457. return rc;
  1458. }
  1459. cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* pitchRef )
  1460. {
  1461. cmXsRC_t rc = kOkXsRC;
  1462. cmChar_t* s;
  1463. cmChar_t buf[4];
  1464. unsigned i,j;
  1465. memset(buf,0,sizeof(buf));
  1466. *pitchRef = 0;
  1467. if((s = strchr(b,'$')) == NULL )
  1468. return rc;
  1469. ++s;
  1470. j=2;
  1471. for(i=0; i<j && s[i]; ++i,++s)
  1472. {
  1473. buf[i] = *s;
  1474. if( i==1 && (*s=='#' || *s=='b') )
  1475. j = 3;
  1476. if( i==0 && strchr("ABCDEFG",*s)==NULL )
  1477. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch letter ('%c')specification line %i.",*s,line);
  1478. if( i==1 && !isdigit(*s) && *s!='#' && *s!='b' )
  1479. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch level ('%c') specification line %i.",*s,line);
  1480. if( i==2 && !isdigit(*s) )
  1481. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch octave ('%c') specification line %i.",*s,line);
  1482. }
  1483. unsigned pitch = cmSciPitchToMidi(buf);
  1484. if( pitch<kInvalidMidiByte)
  1485. *pitchRef = pitch;
  1486. else
  1487. rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"Pitch conversion from '%s' failed on line %i.",buf,line);
  1488. return rc;
  1489. }
  1490. cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
  1491. {
  1492. typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
  1493. cmXsRC_t rc = kOkXsRC;
  1494. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1495. cmFileH_t fH = cmFileNullHandle;
  1496. cmChar_t* b = NULL;
  1497. unsigned bN = 0;
  1498. unsigned ln = 0;
  1499. stateId_t stateId = kFindMeasStId;
  1500. unsigned rN = 1024;
  1501. unsigned ri = 0;
  1502. unsigned measNumb = 0;
  1503. cmXsReorder_t rV[ rN ];
  1504. if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
  1505. {
  1506. rc = cmErrMsg(&p->err,kFileFailXsRC,"The reordering file '%s' could not be opened.",cmStringNullGuard(fn));
  1507. return rc;
  1508. }
  1509. for(; cmFileGetLineAuto(fH,&b,&bN)==kOkFileRC; ++ln)
  1510. {
  1511. switch( stateId )
  1512. {
  1513. case kFindEventStId: // scanning past labels to an event line
  1514. {
  1515. unsigned voice,loc;
  1516. if( sscanf(b,"%i %i",&voice,&loc) != 2 )
  1517. continue;
  1518. stateId = kReadEventStId;
  1519. }
  1520. // fall through
  1521. case kReadEventStId:
  1522. {
  1523. cmXsReorder_t r;
  1524. char pitchStr[4];
  1525. memset(&r,0,sizeof(r));
  1526. // parse an event line
  1527. if( sscanf(b,"%i %i %i %i %i %f",&r.idx,&r.voice,&r.locIdx,&r.tick,&r.durtn,&r.rval) == 6 )
  1528. {
  1529. assert( strlen(b)>=52);
  1530. int PC = 39; // text file column where first pitch char occurs
  1531. if( b[PC] == ' ')
  1532. r.midi = 0;
  1533. else
  1534. {
  1535. pitchStr[0] = b[PC+0];
  1536. pitchStr[1] = b[PC+1];
  1537. pitchStr[2] = b[PC+2];
  1538. pitchStr[3] = 0;
  1539. if( !isdigit(pitchStr[2]) )
  1540. r.midi = 0;
  1541. else
  1542. {
  1543. if( pitchStr[1] == ' ')
  1544. {
  1545. pitchStr[1] = pitchStr[2];
  1546. pitchStr[2] = 0;
  1547. }
  1548. r.midi = cmSciPitchToMidi(pitchStr);
  1549. }
  1550. }
  1551. // parse the dynamic marking following a '!'
  1552. if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&r.dynIdx)) != kOkXsRC )
  1553. goto errLabel;
  1554. // parse the flag edits
  1555. if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &r.newFlags)) != kOkXsRC )
  1556. goto errLabel;
  1557. // parse the @newtick marker
  1558. if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &r.newTick)) != kOkXsRC )
  1559. goto errLabel;
  1560. // parse the $pitch marker
  1561. if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &r.pitch )) != kOkXsRC )
  1562. goto errLabel;
  1563. // store the record
  1564. assert( ri < rN );
  1565. rV[ri++] = r;
  1566. continue;
  1567. }
  1568. // the end of the measure was encountered -
  1569. // reorder the measure based on the cmXsReorder_t in rV[ri]
  1570. if((rc = _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC )
  1571. goto errLabel;
  1572. ri = 0;
  1573. stateId = kFindMeasStId;
  1574. // fall through
  1575. }
  1576. case kFindMeasStId: // scanning for a bar-line
  1577. {
  1578. char colon;
  1579. if( sscanf(b,"%i %c",&measNumb,&colon) == 2 && colon == ':' )
  1580. {
  1581. //printf("meas: %i \n",measNumb);
  1582. stateId = kFindEventStId;
  1583. }
  1584. }
  1585. break;
  1586. }
  1587. }
  1588. // the ticks may have changed so the 'secs' and 'dsecs' must be updated
  1589. _cmXScoreSetAbsoluteTime( p );
  1590. // the bar lines should be the first event in the measure
  1591. _cmXScoreFixBarLines(p);
  1592. // resort to force the links to be correct
  1593. _cmXScoreSort(p);
  1594. errLabel:
  1595. cmFileClose(&fH);
  1596. cmMemFree(b);
  1597. return rc;
  1598. }
  1599. /*
  1600. kMidiFileIdColScIdx= 0,
  1601. kTypeLabelColScIdx = 3,
  1602. kDSecsColScIdx = 4,
  1603. kSecsColScIdx = 5,
  1604. kD0ColScIdx = 9,
  1605. kD1ColScIdx = 10,
  1606. kPitchColScIdx = 11,
  1607. kBarColScIdx = 13,
  1608. kSkipColScIdx = 14,
  1609. kEvenColScIdx = 15,
  1610. kGraceColScIdx = 16,
  1611. kTempoColScIdx = 17,
  1612. kFracColScIdx = 18,
  1613. kDynColScIdx = 19,
  1614. kSectionColScIdx = 20,
  1615. kRecdPlayColScIdx = 21,
  1616. kRemarkColScIdx = 22
  1617. */
  1618. cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p )
  1619. {
  1620. const cmChar_t* s[] =
  1621. {
  1622. "id","trk","evt","opcode","dticks","micros","status",
  1623. "meta","ch","d0","d1","arg0","arg1","bar","skip",
  1624. "even","grace","tempo","t frac","dyn","section","play_recd","remark",NULL
  1625. };
  1626. cmCsvCell_t* lcp = NULL;
  1627. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymText(p->csvH,s[0]), 0, 0 ) != kOkCsvRC )
  1628. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  1629. unsigned i;
  1630. for(i=1; s[i]!=NULL; ++i)
  1631. {
  1632. if( cmCsvInsertIdentColAfter(p->csvH, lcp, &lcp, s[i], 0 ) != kOkCsvRC )
  1633. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV error inserting CSV title %i.\n",i);
  1634. }
  1635. return kOkXsRC;
  1636. }
  1637. cmXsRC_t _cmXScoreWriteCsvBlankCols( cmXScore_t* p, unsigned cnt, cmCsvCell_t** leftCellPtrPtr )
  1638. {
  1639. unsigned i;
  1640. for(i=0; i<cnt; ++i)
  1641. if( cmCsvInsertIdentColAfter(p->csvH,*leftCellPtrPtr,leftCellPtrPtr,"",0) != kOkCsvRC )
  1642. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV output failed on blank column.");
  1643. return kOkCsvRC;
  1644. }
  1645. const cmChar_t* _cmXScoreTranslateDynamics( cmXScore_t* p, const cmXsNote_t* np, cmChar_t* buf, unsigned bufN )
  1646. {
  1647. if( cmIsFlag(np->flags,kDynXsFl) && np->dynamics != 0 )
  1648. {
  1649. const cmChar_t* dynStr = NULL;
  1650. switch(np->dynamics)
  1651. {
  1652. case 1: dynStr = "pppp"; break;
  1653. case 2: dynStr = "ppp"; break;
  1654. case 3: dynStr = "pp"; break;
  1655. case 4: dynStr = "p"; break;
  1656. case 5: dynStr = "mp"; break;
  1657. case 6: dynStr = "mf"; break;
  1658. case 7: dynStr = "f"; break;
  1659. case 8: dynStr = "ff"; break;
  1660. case 9: dynStr = "fff"; break;
  1661. default:
  1662. cmErrMsg(&p->err,kSyntaxErrorXsRC,"An invalid dynamic value (%i) was encountered.",np->dynamics);
  1663. goto errLabel;
  1664. }
  1665. if( np->dynGroupId == 0 )
  1666. snprintf(buf,bufN,"%s",dynStr);
  1667. else
  1668. snprintf(buf,bufN,"%s %i",dynStr,np->dynGroupId);
  1669. return buf;
  1670. }
  1671. errLabel:
  1672. return "";
  1673. }
  1674. const cmChar_t* cmXsFormatMeasurementCsvField( unsigned flags, unsigned fl, char abbrev, unsigned sectionId, char* buf, unsigned bufN )
  1675. {
  1676. assert( bufN > 1 );
  1677. buf[0] = ' ';
  1678. buf[1] = 0;
  1679. if( cmIsFlag(flags,fl) )
  1680. {
  1681. if( sectionId != 0 )
  1682. snprintf(buf,bufN-1,"%c %i%c",abbrev,sectionId, cmIsFlag(flags,kHeelXsFl)?'*':' ');
  1683. else
  1684. buf[0] = abbrev;
  1685. }
  1686. return buf;
  1687. }
  1688. cmXsRC_t _cmXScoreWriteCsvRow(
  1689. cmXScore_t* p,
  1690. unsigned rowIdx,
  1691. unsigned uid,
  1692. unsigned bar,
  1693. const cmChar_t* sectionStr,
  1694. const cmChar_t* opCodeStr,
  1695. double dsecs,
  1696. double secs,
  1697. unsigned d0,
  1698. unsigned d1,
  1699. unsigned pitch, // set to -1 if the pitch is not valid
  1700. double frac,
  1701. const cmChar_t* dynStr,
  1702. unsigned flags,
  1703. const cmChar_t* evenStr,
  1704. const cmChar_t* tempoStr)
  1705. {
  1706. cmXsRC_t rc = kOkXsRC;
  1707. cmCsvCell_t* lcp = NULL;
  1708. // append an empty row to the CSV object
  1709. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymUInt(p->csvH, rowIdx ), 0, 0 ) != kOkCsvRC )
  1710. {
  1711. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  1712. goto errLabel;
  1713. }
  1714. /*
  1715. // col 0 : blanks
  1716. if( cmCsvInsertUIntColAfter(p->csvH, lcp, &lcp, rowIdx, 0 ) != kOkCsvRC )
  1717. {
  1718. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output row index failed.");
  1719. goto errLabel;
  1720. }
  1721. */
  1722. // col 1 : track (always 1)
  1723. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,1,0) != kOkCsvRC )
  1724. {
  1725. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1726. goto errLabel;
  1727. }
  1728. // col 2 : evt (set to event uid, or blank if uid == -1)
  1729. if( uid == -1 )
  1730. {
  1731. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1732. goto errLabel;
  1733. }
  1734. else
  1735. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,uid,0) != kOkCsvRC )
  1736. {
  1737. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1738. goto errLabel;
  1739. }
  1740. // col 3 : output the opcode
  1741. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,opCodeStr,0) != kOkCsvRC )
  1742. {
  1743. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on opcode label.");
  1744. goto errLabel;
  1745. }
  1746. // col 4 : dsecs
  1747. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,dsecs,0) != kOkCsvRC )
  1748. {
  1749. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dsecs'.");
  1750. goto errLabel;
  1751. }
  1752. // col 5 : secs
  1753. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,secs,0) != kOkCsvRC )
  1754. {
  1755. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'secs'.");
  1756. goto errLabel;
  1757. }
  1758. // cols 6,7,8 blanks
  1759. if((rc = _cmXScoreWriteCsvBlankCols(p,3,&lcp)) != kOkXsRC )
  1760. goto errLabel;
  1761. // col 9 : d0
  1762. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d0,0) != kOkCsvRC )
  1763. {
  1764. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  1765. goto errLabel;
  1766. }
  1767. // col 10 : d1
  1768. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d1,0) != kOkCsvRC )
  1769. {
  1770. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd1'.");
  1771. goto errLabel;
  1772. }
  1773. // col 11 : pitch
  1774. if( pitch == -1 )
  1775. {
  1776. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1777. goto errLabel;
  1778. }
  1779. else
  1780. {
  1781. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmMidiToSciPitch(pitch,NULL,0),0) != kOkCsvRC )
  1782. {
  1783. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  1784. goto errLabel;
  1785. }
  1786. }
  1787. // col 12 : blanks
  1788. if((rc = _cmXScoreWriteCsvBlankCols(p,1 + (cmIsFlag(flags,kBarXsFl) ? 0 : 1), &lcp)) != kOkXsRC )
  1789. goto errLabel;
  1790. // col 13 : bar number
  1791. if( cmIsFlag(flags,kBarXsFl) )
  1792. {
  1793. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,bar,0) != kOkCsvRC )
  1794. {
  1795. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  1796. goto errLabel;
  1797. }
  1798. }
  1799. // col 14 : skip (blank for now)
  1800. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1801. goto errLabel;
  1802. // col 15: even (all grace notes are 'even' notes
  1803. if( cmCsvInsertQTextColAfter(p->csvH,lcp,&lcp, evenStr, 0) != kOkCsvRC )
  1804. {
  1805. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1806. goto errLabel;
  1807. }
  1808. // col 16: grace
  1809. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kGraceXsFl) ? "g" : "",0) != kOkCsvRC )
  1810. {
  1811. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1812. goto errLabel;
  1813. }
  1814. // col 17: tempo
  1815. if( cmCsvInsertQTextColAfter(p->csvH,lcp,&lcp,tempoStr,0) != kOkCsvRC )
  1816. {
  1817. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1818. goto errLabel;
  1819. }
  1820. // col 18: frac
  1821. if( frac == 0 )
  1822. {
  1823. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1824. goto errLabel;
  1825. }
  1826. else
  1827. {
  1828. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,1.0/frac,0) != kOkCsvRC )
  1829. {
  1830. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 't frac'.");
  1831. goto errLabel;
  1832. }
  1833. }
  1834. // col 19: dynamic marking
  1835. if( cmCsvInsertQTextColAfter(p->csvH, lcp, &lcp, dynStr, 0 ) != kOkCsvRC )
  1836. {
  1837. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dynamics'.");
  1838. goto errLabel;
  1839. }
  1840. //if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  1841. // goto errLabel;
  1842. // col 20: section
  1843. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,sectionStr!=NULL ? sectionStr : "",0) != kOkCsvRC )
  1844. {
  1845. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  1846. goto errLabel;
  1847. }
  1848. // col 21, 22 : recd-play, remark (blank for now)
  1849. if((rc = _cmXScoreWriteCsvBlankCols(p,2,&lcp)) != kOkXsRC )
  1850. goto errLabel;
  1851. errLabel:
  1852. return rc;
  1853. }
  1854. cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn )
  1855. {
  1856. cmXsRC_t rc = kOkXsRC;
  1857. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1858. unsigned rowIdx = 1;
  1859. const cmChar_t* sectionIdStr = NULL;
  1860. if( !cmCsvIsValid(p->csvH) )
  1861. return cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output object is not initialized.");
  1862. if((rc = _cmXScoreWriteCsvHdr( p )) != kOkXsRC )
  1863. goto errLabel;
  1864. cmXsPart_t* pp = p->partL;
  1865. for(; pp!=NULL; pp=pp->link)
  1866. {
  1867. cmXsMeas_t* mp = pp->measL;
  1868. for(; mp!=NULL; mp=mp->link)
  1869. {
  1870. cmXsNote_t* np = mp->noteL;
  1871. for(; np!=NULL; np=np->slink)
  1872. {
  1873. // if this is a section event
  1874. if( cmIsFlag(np->flags,kSectionXsFl) )
  1875. sectionIdStr = np->tvalue;
  1876. // if this is a bar event
  1877. if( cmIsFlag(np->flags,kBarXsFl) )
  1878. {
  1879. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"bar",np->dsecs,np->secs,0,0,-1,0,"",np->flags,"","");
  1880. sectionIdStr = NULL;
  1881. }
  1882. else
  1883. {
  1884. // if this is a pedal event
  1885. if( cmIsFlag(np->flags,kPedalDnXsFl|kPedalUpXsFl|kPedalUpDnXsFl|kSostDnXsFl|kSostUpXsFl) )
  1886. {
  1887. unsigned d0 = cmIsFlag(np->flags,kSostDnXsFl |kSostUpXsFl) ? 66 : 64; // pedal MIDI ctl id
  1888. unsigned d1 = cmIsFlag(np->flags,kPedalDnXsFl|kSostDnXsFl) ? 64 : 0; // pedal-dn: d1>=64 pedal-up:<64
  1889. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"ctl",np->dsecs,np->secs,d0,d1,-1,0,"",np->flags,"","");
  1890. sectionIdStr = NULL;
  1891. if( cmIsFlag(np->flags,kPedalUpDnXsFl) )
  1892. {
  1893. rowIdx += 1;
  1894. double millisecond = 0.0;
  1895. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"ctl",millisecond,np->secs+millisecond,d0,64,-1,0,"",np->flags,"","");
  1896. }
  1897. }
  1898. else
  1899. {
  1900. // if this is a sounding note event
  1901. if( cmIsFlag(np->flags,kOnsetXsFl) )
  1902. {
  1903. unsigned bufN = 128;
  1904. cmChar_t ebuf[ bufN+1]; ebuf[bufN] = 0;
  1905. cmChar_t dbuf[ bufN+1]; dbuf[bufN] = 0;
  1906. cmChar_t tbuf[ bufN+1]; tbuf[bufN] = 0;
  1907. double frac = np->rvalue + (cmIsFlag(np->flags,kDotXsFl) ? (np->rvalue/2) : 0);
  1908. const cmChar_t* dyn = _cmXScoreTranslateDynamics( p, np, dbuf, bufN );
  1909. unsigned vel = np->vel==0 ? 60 : np->vel;
  1910. //
  1911. _cmXScoreWriteCsvRow(p,rowIdx,np->uid,mp->number,sectionIdStr,"non",np->dsecs,np->secs,np->pitch,vel,np->pitch,frac,dyn,np->flags,
  1912. cmXsFormatMeasurementCsvField(np->flags, kEvenXsFl, 'e', np->evenGroupId, ebuf, bufN ),
  1913. cmXsFormatMeasurementCsvField(np->flags, kTempoXsFl,'t', np->tempoGroupId, tbuf, bufN ));
  1914. sectionIdStr = NULL;
  1915. }
  1916. }
  1917. }
  1918. rowIdx += 1;
  1919. }
  1920. }
  1921. }
  1922. // Section labels are output on the next bar/pedal/note
  1923. // but what if there is no bar pedal note-on after the section label
  1924. if( sectionIdStr != NULL )
  1925. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The section label '%s' was ignored because it was not followed by a score event.",sectionIdStr);
  1926. if( cmCsvWrite( p->csvH, csvFn ) != kOkCsvRC )
  1927. rc = cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output write failed on file '%s'.",csvFn);
  1928. errLabel:
  1929. return rc;
  1930. }
  1931. void _cmXScoreReportTitle( cmRpt_t* rpt )
  1932. {
  1933. cmRptPrintf(rpt," idx voc loc tick durtn rval flags\n");
  1934. cmRptPrintf(rpt," --- --- ----- ------- ----- ---- --- ---------------\n");
  1935. }
  1936. void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
  1937. {
  1938. const cmChar_t* B = cmIsFlag(note->flags,kBarXsFl) ? "|" : "-";
  1939. const cmChar_t* R = cmIsFlag(note->flags,kRestXsFl) ? "R" : "-";
  1940. const cmChar_t* G = cmIsFlag(note->flags,kGraceXsFl) ? "G" : "-";
  1941. const cmChar_t* D = cmIsFlag(note->flags,kDotXsFl) ? "." : "-";
  1942. const cmChar_t* C = cmIsFlag(note->flags,kChordXsFl) ? "C" : "-";
  1943. const cmChar_t* e = cmIsFlag(note->flags,kEvenXsFl) ? "e" : "-";
  1944. const cmChar_t* d = cmIsFlag(note->flags,kDynXsFl) ? "d" : "-";
  1945. const cmChar_t* t = cmIsFlag(note->flags,kTempoXsFl) ? "t" : "-";
  1946. const cmChar_t* P = cmIsFlag(note->flags,kPedalDnXsFl) ? "V" : "-";
  1947. const cmChar_t* s = cmIsFlag(note->flags,kSostDnXsFl) ? "{" : "-";
  1948. const cmChar_t* S = cmIsFlag(note->flags,kSectionXsFl) ? "S" : "-";
  1949. const cmChar_t* H = cmIsFlag(note->flags,kHeelXsFl) ? "H" : "-";
  1950. const cmChar_t* T0 = cmIsFlag(note->flags,kTieBegXsFl) ? "T" : "-";
  1951. const cmChar_t* T1 = cmIsFlag(note->flags,kTieEndXsFl) ? "_" : "-";
  1952. const cmChar_t* O = cmIsFlag(note->flags,kOnsetXsFl) ? "*" : "-";
  1953. P = cmIsFlag(note->flags,kPedalUpXsFl) ? "^" : P;
  1954. P = cmIsFlag(note->flags,kPedalUpDnXsFl) ? "X" : P;
  1955. s = cmIsFlag(note->flags,kSostUpXsFl) ? "}" : s;
  1956. //const cmChar_t* N = note->pitch==0 ? " " : cmMidiToSciPitch( note->pitch, NULL, 0 );
  1957. cmChar_t N[] = {'\0','\0','\0','\0'};
  1958. cmChar_t acc = note->alter==-1?'b':(note->alter==1?'#':' ');
  1959. snprintf(N,4,"%c%c%1i",note->step,acc,note->octave);
  1960. cmRptPrintf(rpt," %3i %3i %5i %7i %5i %4.1f %3s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
  1961. index,
  1962. note->voice->id,
  1963. note->locIdx,
  1964. note->tick,
  1965. note->duration,
  1966. note->rvalue,
  1967. N,B,R,G,D,C,e,d,t,P,s,S,H,T0,T1,O);
  1968. if( cmIsFlag(note->flags,kSectionXsFl) )
  1969. cmRptPrintf(rpt," %s",cmStringNullGuard(note->tvalue));
  1970. if( cmIsFlag(note->flags,kMetronomeXsFl) )
  1971. cmRptPrintf(rpt," %i bpm",note->duration);
  1972. if( note->evenGroupId != 0 )
  1973. cmRptPrintf(rpt," e=%i",note->evenGroupId);
  1974. if( note->dynGroupId != 0 )
  1975. cmRptPrintf(rpt," d=%i",note->dynGroupId);
  1976. if( note->tempoGroupId != 0 )
  1977. cmRptPrintf(rpt," t=%i",note->tempoGroupId);
  1978. }
  1979. void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
  1980. {
  1981. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  1982. cmXsPart_t* pp = p->partL;
  1983. for(; pp!=NULL; pp=pp->link)
  1984. {
  1985. cmRptPrintf(rpt,"Part:%s\n",pp->idStr);
  1986. const cmXsMeas_t* meas = pp->measL;
  1987. for(; meas!=NULL; meas=meas->link)
  1988. {
  1989. unsigned idx = 0;
  1990. 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);
  1991. _cmXScoreReportTitle(rpt);
  1992. if( sortFl )
  1993. {
  1994. const cmXsNote_t* note = meas->noteL;
  1995. unsigned t0 = 0;
  1996. unsigned t1 = 0;
  1997. for(; note!=NULL; note=note->slink,++idx)
  1998. {
  1999. _cmXScoreReportNote(rpt,note,idx);
  2000. t1 = note->slink==NULL ? note->tick : note->slink->tick;
  2001. if( !(t0 <= note->tick && note->tick <= t1) )
  2002. {
  2003. cmRptPrintf(rpt," +");
  2004. }
  2005. t0 = note->tick;
  2006. if( note->slink!=NULL || note->voice->id==0)
  2007. cmRptPrintf(rpt,"\n");
  2008. else
  2009. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  2010. }
  2011. }
  2012. else
  2013. {
  2014. const cmXsVoice_t* v = meas->voiceL;
  2015. for(; v!=NULL; v=v->link)
  2016. {
  2017. const cmXsNote_t* note = v->noteL;
  2018. cmRptPrintf(rpt," voice:%i\n",v->id);
  2019. for(; note!=NULL; note=note->mlink)
  2020. {
  2021. _cmXScoreReportNote(rpt,note,idx);
  2022. if( note->mlink!=NULL || note->voice->id==0)
  2023. cmRptPrintf(rpt,"\n");
  2024. else
  2025. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  2026. }
  2027. }
  2028. }
  2029. }
  2030. }
  2031. }
  2032. typedef struct cmXsMidiEvt_str
  2033. {
  2034. unsigned flags; // k???XsFl
  2035. unsigned tick; // start tick
  2036. unsigned durTicks; // dur-ticks
  2037. unsigned voice; // score voice number
  2038. unsigned d0; // MIDI d0 (barNumb)
  2039. unsigned d1; // MIDI d1
  2040. struct cmXsMidiEvt_str* link;
  2041. } cmXsMidiEvt_t;
  2042. typedef struct cmXsMidiFile_str
  2043. {
  2044. cmXsMidiEvt_t* elist;
  2045. cmXsMidiEvt_t* eol;
  2046. unsigned pitch_min;
  2047. unsigned pitch_max;
  2048. } cmXsMidiFile_t;
  2049. cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn )
  2050. {
  2051. cmXsRC_t rc = kOkXsRC;
  2052. cmSvgH_t svgH = cmSvgNullHandle;
  2053. cmXsMidiEvt_t* e = mf->elist;
  2054. unsigned noteHeight = 10;
  2055. const cmChar_t* svgFn = cmFsMakeFn(dir,fn,"html",NULL);
  2056. const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL);
  2057. cmChar_t* t0 = NULL; // temporary dynamic string
  2058. // create the SVG writer
  2059. if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
  2060. {
  2061. rc = cmErrMsg(&p->err,kSvgFailXsRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(svgFn));
  2062. goto errLabel;
  2063. }
  2064. // for each MIDI file element
  2065. for(; e!=NULL && rc==kOkXsRC; e=e->link)
  2066. {
  2067. // if this is a note
  2068. if( cmIsFlag(e->flags,kOnsetXsFl) )
  2069. {
  2070. const cmChar_t* classLabel = "note";
  2071. t0 = cmTsPrintfP(t0,"note_%i%s",e->voice, cmIsFlag(e->flags,kGraceXsFl) ? "_g":"");
  2072. if( cmIsFlag(e->flags,kGraceXsFl) )
  2073. classLabel = "grace";
  2074. if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC )
  2075. rc = kSvgFailXsRC;
  2076. else
  2077. if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, cmMidiToSciPitch( e->d0, NULL, 0), "pitch") != kOkSvgRC )
  2078. rc = kSvgFailXsRC;
  2079. }
  2080. // if this is a bar
  2081. if( cmIsFlag(e->flags,kBarXsFl) )
  2082. {
  2083. if( cmSvgWriterLine(svgH, e->tick, 0, e->tick, 127*noteHeight, "bar") != kOkSvgRC )
  2084. rc = kSvgFailXsRC;
  2085. else
  2086. {
  2087. if( cmSvgWriterText(svgH, e->tick, 10, t0 = cmTsPrintfP(t0,"%i",e->d0), "text" ) != kOkSvgRC )
  2088. rc = kSvgFailXsRC;
  2089. }
  2090. }
  2091. }
  2092. if( rc != kOkXsRC )
  2093. cmErrMsg(&p->err,kSvgFailXsRC,"SVG element insert failed.");
  2094. if( rc == kOkXsRC )
  2095. if( cmSvgWriterWrite(svgH,cssFn,svgFn) != kOkSvgRC )
  2096. rc = cmErrMsg(&p->err,kSvgFailXsRC,"SVG file write to '%s' failed.",cmStringNullGuard(svgFn));
  2097. errLabel:
  2098. cmSvgWriterFree(&svgH);
  2099. cmFsFreeFn(svgFn);
  2100. cmFsFreeFn(cssFn);
  2101. cmMemFree(t0);
  2102. return rc;
  2103. }
  2104. void _cmXsPushMidiEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
  2105. {
  2106. cmXsMidiEvt_t* e = cmLhAllocZ(p->lhH,cmXsMidiEvt_t,1);
  2107. e->flags = flags;
  2108. e->tick = tick;
  2109. e->durTicks = durTick;
  2110. e->voice = voice;
  2111. e->d0 = d0;
  2112. e->d1 = d1;
  2113. if( mf->eol != NULL )
  2114. mf->eol->link = e;
  2115. else
  2116. mf->elist = e;
  2117. // track the min/max pitch
  2118. if( cmIsFlag(flags,kOnsetXsFl) )
  2119. {
  2120. mf->pitch_min = mf->eol==NULL ? d0 : cmMin(mf->pitch_min,d0);
  2121. mf->pitch_max = mf->eol==NULL ? d0 : cmMin(mf->pitch_max,d0);
  2122. }
  2123. mf->eol = e;
  2124. }
  2125. cmXsRC_t _cmXScoreWriteMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
  2126. {
  2127. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  2128. cmXsPart_t* pp = p->partL;
  2129. cmXsMidiFile_t mf;
  2130. memset(&mf,0,sizeof(mf));
  2131. for(; pp!=NULL; pp=pp->link)
  2132. {
  2133. const cmXsMeas_t* meas = pp->measL;
  2134. for(; meas!=NULL; meas=meas->link)
  2135. {
  2136. const cmXsNote_t* note = meas->noteL;
  2137. for(; note!=NULL; note=note->slink)
  2138. {
  2139. // if this is a sounding note
  2140. if( cmIsFlag(note->flags,kOnsetXsFl) )
  2141. {
  2142. unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave );
  2143. unsigned durTick = note->duration;
  2144. if( note->tied != NULL )
  2145. {
  2146. cmXsNote_t* tn = note->tied;
  2147. for(; tn!=NULL; tn=tn->tied)
  2148. durTick += tn->duration;
  2149. }
  2150. _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
  2151. continue;
  2152. }
  2153. // if this is a bar event
  2154. if( cmIsFlag(note->flags,kBarXsFl) )
  2155. {
  2156. _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
  2157. continue;
  2158. }
  2159. }
  2160. }
  2161. }
  2162. return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn );
  2163. }
  2164. cmXsRC_t cmXScoreTest(
  2165. cmCtx_t* ctx,
  2166. const cmChar_t* xmlFn,
  2167. const cmChar_t* reorderFn,
  2168. const cmChar_t* csvOutFn,
  2169. const cmChar_t* midiOutFn)
  2170. {
  2171. cmXsRC_t rc;
  2172. cmXsH_t h = cmXsNullHandle;
  2173. if((rc = cmXScoreInitialize( ctx, &h, xmlFn)) != kOkXsRC )
  2174. return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
  2175. if( reorderFn != NULL )
  2176. cmXScoreReorder(h,reorderFn);
  2177. if( csvOutFn != NULL )
  2178. {
  2179. cmScH_t scH = cmScNullHandle;
  2180. double srate = 44100.0;
  2181. cmXScoreWriteCsv(h,csvOutFn);
  2182. cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx );
  2183. if( cmScoreInitialize( ctx, &scH, csvOutFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC )
  2184. cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed.");
  2185. else
  2186. {
  2187. //cmScorePrintSets(scH,&ctx->rpt);
  2188. //cmScorePrint(scH,&ctx->rpt);
  2189. cmScoreFinalize(&scH);
  2190. }
  2191. cmSymTblDestroy(&stH);
  2192. }
  2193. if( midiOutFn != NULL )
  2194. {
  2195. cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
  2196. _cmXScoreWriteMidi( ctx, h, pp->dirStr, pp->fnStr );
  2197. cmFsFreePathParts(pp);
  2198. }
  2199. //cmXScoreReport(h,&ctx->rpt,true);
  2200. return cmXScoreFinalize(&h);
  2201. }