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

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