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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmFloatTypes.h"
  6. #include "cmComplexTypes.h"
  7. #include "cmRpt.h"
  8. #include "cmErr.h"
  9. #include "cmCtx.h"
  10. #include "cmMem.h"
  11. #include "cmMallocDebug.h"
  12. #include "cmLinkedHeap.h"
  13. #include "cmXml.h"
  14. #include "cmText.h"
  15. #include "cmFileSys.h"
  16. #include "cmXScore.h"
  17. #include "cmTime.h"
  18. #include "cmMidi.h"
  19. #include "cmMidiFile.h"
  20. #include "cmLex.h"
  21. #include "cmCsv.h"
  22. #include "cmSymTbl.h"
  23. #include "cmScore.h"
  24. #include "cmFile.h"
  25. #include "cmSymTbl.h"
  26. #include "cmAudioFile.h"
  27. #include "cmAudioFile.h"
  28. #include "cmProcObj.h"
  29. #include "cmProcTemplate.h"
  30. #include "cmProc.h"
  31. #include "cmProc2.h"
  32. #include "cmProc5.h"
  33. #include "cmSvgWriter.h"
  34. cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
  35. enum
  36. {
  37. kSectionXsFl = 0x00000001, // rvalue holds section number
  38. kBarXsFl = 0x00000002,
  39. kRestXsFl = 0x00000004,
  40. kGraceXsFl = 0x00000008,
  41. kDotXsFl = 0x00000010,
  42. kChordXsFl = 0x00000020,
  43. kDynXsFl = 0x00000040,
  44. kEvenXsFl = 0x00000080,
  45. kTempoXsFl = 0x00000100,
  46. kHeelXsFl = 0x00000200,
  47. kTieBegXsFl = 0x00000400,
  48. kTieEndXsFl = 0x00000800,
  49. kTieProcXsFl = 0x00001000,
  50. kDampDnXsFl = 0x00002000,
  51. kDampUpXsFl = 0x00004000,
  52. kDampUpDnXsFl = 0x00008000,
  53. kSostDnXsFl = 0x00010000,
  54. kSostUpXsFl = 0x00020000,
  55. kMetronomeXsFl = 0x00040000, // duration holds BPM
  56. kOnsetXsFl = 0x00080000, // this is a sounding note
  57. kBegGroupXsFl = 0x00100000,
  58. kEndGroupXsFl = 0x00200000,
  59. kBegGraceXsFl = 0x00400000, // (b) beg grace note group
  60. kEndGraceXsFl = 0x00800000, // end grace note group
  61. kAddGraceXsFl = 0x01000000, // (a) end grace note group operator flag - add time
  62. kSubGraceXsFl = 0x02000000, // (s) " " " " " " - subtract time
  63. kAFirstGraceXsFl = 0x04000000, // (A) add time after first note
  64. kNFirstGraceXsFl = 0x08000000, // (n) grace notes start as soon as possible after first note and add time
  65. kDeleteXsFl = 0x10000000,
  66. kDynBegForkXsFl = 0x20000000,
  67. kDynEndForkXsFl = 0x40000000,
  68. kDynEndXsFl = 0x100000000,
  69. kEvenEndXsFl = 0x200000000,
  70. kTempoEndXsFl = 0x400000000
  71. };
  72. struct cmXsMeas_str;
  73. struct cmXsVoice_str;
  74. // Values measured for each sounding note in the preceding time window....
  75. typedef struct cmXsComplexity_str
  76. {
  77. unsigned sum_d_vel; // sum of first order difference of cmXsNote_t.dynamics
  78. unsigned sum_d_rym; // sum of first order difference of cmXsNote_t.rvalue
  79. unsigned sum_d_lpch; // sum of first order difference of cmXsNote_t.pitch of note assigned to the bass cleff
  80. unsigned sum_n_lpch; // count of notes assigned to the bass cleff
  81. unsigned sum_d_rpch; // sum of first order difference of cmXsNote_t.pitch of note assigned to the treble cleff
  82. unsigned sum_n_rpch; // count of notes assigned to the treble cleff
  83. } cmXsComplexity_t;
  84. typedef struct cmXsNote_str
  85. {
  86. unsigned uid; // unique id of this note record
  87. unsigned long long flags; // See k???XsFl
  88. unsigned pitch; // midi pitch
  89. unsigned dynamics; // dynamic level 1=pppp 9=fff
  90. unsigned vel; // score specified MIDI velocity
  91. cmChar_t step; // A-G
  92. unsigned octave; // sci pitch octave
  93. int alter; // +n=sharps,-n=flats
  94. unsigned staff; // 1=treble 2=bass
  95. unsigned tick; //
  96. unsigned duration; // duration in ticks
  97. unsigned tied_dur; // duration in ticks (including all tied notes)
  98. double secs; // absolute time in seconds
  99. double dsecs; // delta time in seconds since previous event
  100. unsigned locIdx; // location index (chords share the same location index)
  101. 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, ...)
  102. const cmChar_t* tvalue; // text value
  103. const cmChar_t* editStr; // merged manual edit string
  104. unsigned evenGroupId; // eveness group id
  105. unsigned dynGroupId; // dynamics group id
  106. unsigned tempoGroupId; // tempo group id
  107. unsigned graceGroupId; // grace note group id
  108. struct cmXsVoice_str* voice; // voice this note belongs to
  109. struct cmXsMeas_str* meas; // measure this note belongs to
  110. const cmXmlNode_t* xmlNode; // note xml ptr
  111. struct cmXsNote_str* tied; // subsequent note tied to this note
  112. struct cmXsNote_str* grace; // grace note groups link backward in time from the anchor note
  113. struct cmXsNote_str* mlink; // measure note list
  114. struct cmXsNote_str* slink; // time sorted event list
  115. cmXsComplexity_t cplx;
  116. } cmXsNote_t;
  117. typedef struct cmXsVoice_str
  118. {
  119. unsigned id; // Voice id
  120. cmXsNote_t* noteL; // List of notes in this voice
  121. struct cmXsVoice_str* link; // Link to other voices in this measure
  122. } cmXsVoice_t;
  123. typedef struct cmXsSpan_str
  124. {
  125. unsigned staff;
  126. unsigned number;
  127. struct cmXsMeas_str* meas;
  128. unsigned tick0;
  129. unsigned tick1;
  130. int pitch_offset;
  131. struct cmXsSpan_str* link;
  132. } cmXsSpan_t;
  133. typedef struct cmXsMeas_str
  134. {
  135. unsigned number; // Measure number
  136. unsigned divisions; // ticks-per-quarter-note
  137. unsigned beats; // beats per measure
  138. unsigned beat_type; // whole/half/quarter/eighth ...
  139. cmXsVoice_t* voiceL; // List of voices in this measure
  140. cmXsNote_t* noteL; // List of time sorted notes in this measure
  141. struct cmXsMeas_str* link; // Link to other measures in this part.
  142. } cmXsMeas_t;
  143. typedef struct cmXsPart_str
  144. {
  145. const cmChar_t* idStr; // Id of this part
  146. cmXsMeas_t* measL; // List of measures in this part.
  147. struct cmXsPart_str* link; // Link to other parts in this score
  148. } cmXsPart_t;
  149. typedef struct
  150. {
  151. cmErr_t err;
  152. cmXmlH_t xmlH;
  153. cmLHeapH_t lhH;
  154. cmXsPart_t* partL;
  155. cmCsvH_t csvH;
  156. cmXsSpan_t* spanL;
  157. unsigned nextUid;
  158. cmXsComplexity_t cplx_max;
  159. } cmXScore_t;
  160. void _cmXScoreReport( cmXScore_t* p, cmRpt_t* rpt, bool sortFl );
  161. cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
  162. {
  163. cmXScore_t* p = (cmXScore_t*)h.h;
  164. assert( p != NULL );
  165. return p;
  166. }
  167. cmXsRC_t _cmXScoreFinalize( cmXScore_t* p )
  168. {
  169. cmXsRC_t rc = kOkXsRC;
  170. // release the XML file
  171. cmXmlFree( &p->xmlH );
  172. // release the local linked heap memory
  173. cmLHeapDestroy(&p->lhH);
  174. // release the CSV output object
  175. cmCsvFinalize(&p->csvH);
  176. cmMemFree(p);
  177. return rc;
  178. }
  179. cmXsRC_t _cmXScoreMissingNode( cmXScore_t* p, const cmXmlNode_t* parent, const cmChar_t* label )
  180. {
  181. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML node '%s'. Parent line:%i",label,parent->line);
  182. }
  183. cmXsRC_t _cmXScoreMissingAttribute( cmXScore_t* p, const cmXmlNode_t* np, const cmChar_t* attrLabel )
  184. {
  185. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Missing XML attribute '%s' from node '%s'.",attrLabel,np->label);
  186. }
  187. cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId )
  188. {
  189. cmXsVoice_t* v = meas->voiceL;
  190. for(; v!=NULL; v=v->link)
  191. if( v->id == voiceId )
  192. return v;
  193. return NULL;
  194. }
  195. cmXsMeas_t* _cmXsFindMeas( cmXsPart_t* part, unsigned measNumb )
  196. {
  197. cmXsMeas_t* m = part->measL;
  198. for(; m!=NULL; m=m->link)
  199. if( m->number == measNumb )
  200. return m;
  201. return NULL;
  202. }
  203. cmXsNote_t* _cmXsFindNote( cmXsMeas_t* m, unsigned idx, unsigned midi, double rval, unsigned durtn, unsigned* idxRef )
  204. {
  205. unsigned i;
  206. cmXsNote_t* np = m->noteL;
  207. for(i=0; np!=NULL; np=np->slink,++i)
  208. {
  209. //printf("idx:%i %i midi:%i %i rval:%f %f durtn:%i %i\n", i,idx, np->pitch,midi, np->rvalue,rval, np->tied_dur,durtn);
  210. if( i>=idx && np->pitch==midi && np->rvalue==rval && np->tied_dur==durtn )
  211. {
  212. *idxRef = i;
  213. return np;
  214. }
  215. }
  216. return NULL;
  217. }
  218. cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note )
  219. {
  220. cmXsVoice_t* v;
  221. // get the voice recd
  222. if((v = _cmXScoreIdToVoice(meas,voiceId)) == NULL)
  223. {
  224. // the voice recd doesn't exist for this voiceId - allocate one
  225. v = cmLhAllocZ(p->lhH,cmXsVoice_t,1);
  226. v->id = voiceId;
  227. // add the voice record to the meas->voiceL
  228. if( meas->voiceL == NULL )
  229. meas->voiceL = v;
  230. else
  231. {
  232. cmXsVoice_t* vp = meas->voiceL;
  233. while( vp->link!=NULL )
  234. vp = vp->link;
  235. vp->link = v;
  236. }
  237. }
  238. // add the note record to the end of meas->voiceL
  239. if( v->noteL == NULL )
  240. v->noteL = note;
  241. else
  242. {
  243. cmXsNote_t* n = v->noteL;
  244. while( n->mlink != NULL )
  245. n = n->mlink;
  246. n->mlink = note;
  247. }
  248. note->voice = v;
  249. note->uid = p->nextUid++;
  250. note->tied_dur = note->duration;
  251. return kOkXsRC;
  252. }
  253. cmXsRC_t _cmXScoreRemoveNote( cmXsNote_t* note )
  254. {
  255. cmXsNote_t* n0 = NULL;
  256. cmXsNote_t* n1 = note->voice->noteL;
  257. unsigned cnt = 0;
  258. for(; n1!=NULL; n1=n1->mlink)
  259. {
  260. if( n1->uid == note->uid )
  261. {
  262. if( n0 == NULL )
  263. note->voice->noteL = NULL;
  264. else
  265. n0->mlink = n1->mlink;
  266. cnt = 1;
  267. break;
  268. }
  269. n0 = n1;
  270. }
  271. n0 = NULL;
  272. n1 = note->meas->noteL;
  273. for(; n1!=NULL; n1=n1->slink)
  274. {
  275. if( n1->uid == note->uid )
  276. {
  277. if( n0 == NULL )
  278. note->voice->noteL = NULL;
  279. else
  280. n0->slink = n1->slink;
  281. cnt = 2;
  282. break;
  283. }
  284. n0 = n1;
  285. }
  286. return cnt == 2 ? kOkXsRC : kSyntaxErrorXsRC;
  287. }
  288. void _cmXScoreInsertNoteBefore( cmXsNote_t* note, cmXsNote_t* nn )
  289. {
  290. assert( note != NULL );
  291. // insert the new note into the voice list before 'note'
  292. cmXsNote_t* n0 = NULL;
  293. cmXsNote_t* n1 = note->voice->noteL;
  294. for(; n1 != NULL; n1=n1->mlink )
  295. {
  296. if( n1->uid == note->uid )
  297. {
  298. if( n0 == NULL )
  299. note->voice->noteL = nn;
  300. else
  301. n0->mlink = nn;
  302. nn->mlink = note;
  303. break;
  304. }
  305. n0 = n1;
  306. }
  307. assert( n1 != NULL );
  308. // insert the new note into the time sorted note list before 'note'
  309. n0 = NULL;
  310. n1 = note->meas->noteL;
  311. for(; n1!=NULL; n1=n1->slink)
  312. {
  313. if( n1->tick >= nn->tick )
  314. {
  315. if( n0 == NULL )
  316. note->meas->noteL = nn;
  317. else
  318. n0->slink = nn;
  319. nn->slink = n1;
  320. break;
  321. }
  322. n0 = n1;
  323. }
  324. assert( n1 != NULL );
  325. }
  326. void _cmXScoreInsertNoteAfter( cmXsNote_t* note, cmXsNote_t* nn )
  327. {
  328. // insert the new note into the voice list after 'note'
  329. cmXsNote_t* n0 = note->voice->noteL;
  330. for(; n0 != NULL; n0=n0->mlink )
  331. if( n0->uid == note->uid )
  332. {
  333. nn->mlink = note->mlink;
  334. note->mlink = nn;
  335. break;
  336. }
  337. assert(n0 != NULL );
  338. // insert the new note into the time sorted note list after 'note'
  339. n0 = note->meas->noteL;
  340. for(; n0!=NULL; n0=n0->slink)
  341. {
  342. if( n0->tick >= nn->tick )
  343. {
  344. nn->slink = n0->slink;
  345. n0->slink = nn;
  346. break;
  347. }
  348. // if n0 is the last ele in the list
  349. if( n0->slink == NULL )
  350. {
  351. n0->slink = nn;
  352. nn->slink = NULL;
  353. break;
  354. }
  355. }
  356. assert(n0 != NULL);
  357. }
  358. cmXsRC_t _cmXScoreParsePartList( cmXScore_t* p )
  359. {
  360. cmXsRC_t rc = kOkXsRC;
  361. cmXsPart_t* lastPartPtr = NULL;
  362. const cmXmlNode_t* xnp;
  363. // find the 'part-list'
  364. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part-list", NULL, 0)) == NULL )
  365. return _cmXScoreMissingNode(p,cmXmlRoot(p->xmlH),"part-list");
  366. const cmXmlNode_t* cnp = xnp->children;
  367. // for each child of the 'part-list'
  368. for(; cnp!=NULL; cnp=cnp->sibling)
  369. if( cmTextCmp( cnp->label, "score-part" ) == 0 )
  370. {
  371. const cmXmlAttr_t* a;
  372. // find the 'score-part' id
  373. if((a = cmXmlFindAttrib(cnp,"id")) == NULL )
  374. return _cmXScoreMissingAttribute(p,cnp,"id");
  375. // allocate a new part record
  376. cmXsPart_t* pp = cmLhAllocZ(p->lhH,cmXsPart_t,1);
  377. pp->idStr = a->value; // set the part id
  378. // link the new part record to the end of the part list
  379. if(lastPartPtr == NULL)
  380. p->partL = pp;
  381. else
  382. lastPartPtr->link = pp;
  383. lastPartPtr = pp;
  384. }
  385. return rc;
  386. }
  387. cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* np )
  388. {
  389. cmXsRC_t rc = kOkXsRC;
  390. unsigned octave = 0;
  391. double alter = 0;
  392. const cmChar_t* step = NULL;
  393. if((step = cmXmlNodeValue(nnp,"pitch","step",NULL)) == NULL )
  394. return _cmXScoreMissingNode(p,nnp,"step");
  395. if((rc = cmXmlNodeUInt( nnp,&octave,"pitch","octave",NULL)) != kOkXmlRC )
  396. return _cmXScoreMissingNode(p,nnp,"octave");
  397. cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
  398. int acc = alter;
  399. unsigned midi = cmSciPitchToMidiPitch(*step,acc,octave);
  400. np->pitch = midi;
  401. np->step = *step;
  402. np->octave = octave;
  403. np->alter = alter;
  404. np->flags |= kOnsetXsFl;
  405. return rc;
  406. }
  407. cmXsRC_t _cmXScoreParseNoteRValue( cmXScore_t* p, const cmXmlNode_t* nnp, const cmChar_t* label, double* rvalueRef )
  408. {
  409. typedef struct map_str
  410. {
  411. double rvalue;
  412. const cmChar_t* label;
  413. } map_t;
  414. map_t mapV[] =
  415. {
  416. {-1.0, "measure" }, // whole measure rest
  417. { 0.5, "breve" }, // double whole
  418. { 1.0, "whole" },
  419. { 2.0, "half" },
  420. { 4.0, "quarter" },
  421. { 8.0, "eighth" },
  422. {16.0, "16th" },
  423. {32.0, "32nd" },
  424. { 64.0, "64th" },
  425. {128.0, "128th" },
  426. { 0.0, "" }
  427. };
  428. const cmChar_t* str;
  429. // get the XML rvalue label
  430. if((str = cmXmlNodeValue(nnp,label,NULL)) == NULL)
  431. {
  432. if((nnp = cmXmlSearch(nnp,"rest",NULL,0)) != NULL )
  433. {
  434. const cmXmlAttr_t* a;
  435. if((a = cmXmlFindAttrib(nnp,"measure")) != NULL && cmTextCmp(a->value,"yes")==0)
  436. {
  437. *rvalueRef = -1;
  438. return kOkXsRC;
  439. }
  440. }
  441. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'beat-unit' metronome value is missing on line %i.",nnp->line);
  442. }
  443. unsigned i;
  444. // lookup the rvalue numeric value from the mapV[] table
  445. for(i=0; mapV[i].rvalue!=0; ++i)
  446. if( cmTextCmp(mapV[i].label,str) == 0 )
  447. {
  448. *rvalueRef = mapV[i].rvalue;
  449. return kOkXsRC;
  450. }
  451. // the rvalue label was not found
  452. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unknown rvalue type='%s'.",str);
  453. }
  454. cmXsRC_t _cmXScoreParseColor( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t* note )
  455. {
  456. cmXsRC_t rc = kOkXsRC;
  457. const cmXmlAttr_t* a;
  458. typedef struct map_str
  459. {
  460. unsigned long long value;
  461. const cmChar_t* label;
  462. } map_t;
  463. map_t mapV[] =
  464. {
  465. { kEvenXsFl, "#0000FF" }, // blue (even)
  466. { kEvenXsFl | kEvenEndXsFl, "#0000FE" }, // blue (even end)
  467. { kEvenXsFl | kEvenEndXsFl, "#0000FD" }, // blue (even end)
  468. { kTempoXsFl, "#00FF00" }, // green (tempo)
  469. { kTempoXsFl | kTempoEndXsFl, "#00FE00" }, // green (tempo end)
  470. { kDynXsFl, "#FF0000" }, // red (dynamics)
  471. { kDynXsFl | kDynEndXsFl, "#FE0000" }, // red (dynamics end)
  472. { kDynXsFl | kDynEndXsFl, "#FD0000" }, // red (dynamics end)
  473. { kTempoXsFl | kEvenXsFl, "#00FFFF" }, // green + blue (turquoise)
  474. { kTempoXsFl | kEvenXsFl | kEvenEndXsFl, "#00FFFE" }, // green + blue (turquoise) (end)
  475. { kDynXsFl | kEvenXsFl, "#FF00FF" }, // red + blue
  476. { kDynXsFl | kEvenXsFl | kEvenEndXsFl, "#FF00FE" }, // red + blue (end)
  477. { kDynXsFl | kEvenXsFl | kEvenEndXsFl, "#FF00FD" }, // red + blue (end)
  478. { kDynXsFl | kEvenXsFl, "#FF0CF7" }, // magenta (even+dyn)
  479. { kDynXsFl | kTempoXsFl, "#FF7F00" }, // red + green (brown)
  480. { kDynXsFl | kTempoXsFl, "#FE7F00" }, // red + green (brown) (end)
  481. { kDynXsFl | kTempoXsFl, "#FD7F00" }, // red + green (brown) (end)
  482. { kDynXsFl | kTempoXsFl | kTempoEndXsFl, "#FF7E00" }, //
  483. { kDynXsFl | kDynEndXsFl | kEvenXsFl | kEvenEndXsFl, "#FE00FE" }, //
  484. { kDynXsFl | kDynEndXsFl | kEvenXsFl, "#FE00FF" },
  485. { kTempoXsFl | kEvenXsFl | kDynXsFl, "#996633" }, // (purple)
  486. { kTempoXsFl | kEvenXsFl | kDynXsFl | kDynEndXsFl, "#996632" }, // (purple)
  487. { kDynXsFl, "#FF6A03" }, // 176 orange (dynamics)
  488. { kEvenXsFl, "#2F00E8" }, // 1001 blue (even)
  489. { kTempoXsFl, "#01CD1F" }, // 1196 green (tempo)
  490. { kEvenXsFl, "#3600E8" }, // 1627 blue (even)
  491. { kDynXsFl | kTempoXsFl, "#9E8F15" }, // 8827 brown (dyn + tempo)
  492. { kEvenXsFl, "#2E00E6" }, // 5393 blue (even)
  493. { kEvenXsFl, "#2C00DD" }, // 5895 blue (even)
  494. { kDynXsFl, "#FF5B03" }, // 6498 orange (dyn)
  495. { kDynXsFl, "#FF6104" }, // 6896 orange
  496. { kEvenXsFl, "#2A00E6" }, // 7781 blue
  497. { kEvenXsFl, "#2300DD" }, // 8300 blue (even)
  498. { kTempoXsFl, "#03CD22" }, // 10820 green (tempo)
  499. { kEvenXsFl, "#3400DB" }, // 11627 blue (dyn)
  500. { -1, "" }
  501. };
  502. /*
  503. orange #FF6A03
  504. magenta #FF0CF7
  505. blue #2F00E8
  506. green #01CD1F
  507. gold #9E8F15
  508. green #03CD22
  509. */
  510. if((a = cmXmlFindAttrib(nnp, "color" )) != NULL )
  511. {
  512. unsigned i;
  513. for(i=0; mapV[i].value != -1; ++i)
  514. if( cmTextCmp(a->value,mapV[i].label) == 0 )
  515. {
  516. note->flags += mapV[i].value;
  517. break;
  518. }
  519. if( mapV[i].value == -1 )
  520. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The note color '%s' was not found on line %i.",a->value,nnp->line);
  521. }
  522. return rc;
  523. }
  524. // On input tick0Ref is set to the tick of the previous event.
  525. // On input tickRef is set to the tick of this event.
  526. // On output tick0Ref is set to the tick of this event.
  527. // On output tickRef is set to the tick of the next event.
  528. cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* nnp, unsigned* tick0Ref, unsigned* tickRef )
  529. {
  530. cmXsRC_t rc = kOkXsRC;
  531. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  532. unsigned voiceId;
  533. note->pitch = kInvalidMidiPitch;
  534. note->meas = meas;
  535. note->xmlNode = nnp;
  536. // get the voice id for this node
  537. if( cmXmlNodeUInt(nnp,&voiceId,"voice",NULL) != kOkXmlRC )
  538. return _cmXScoreMissingNode(p,nnp,"voice");
  539. // if this note has a pitch
  540. if( cmXmlNodeHasChild(nnp,"pitch",NULL) )
  541. if((rc = _cmXScoreParsePitch(p,nnp,note)) != kOkXsRC )
  542. return rc;
  543. cmXmlNodeUInt(nnp,&note->duration,"duration",NULL); // get the note duration
  544. cmXmlNodeUInt(nnp,&note->staff,"staff",NULL); // get th staff number
  545. // is 'rest'
  546. if( cmXmlNodeHasChild(nnp,"rest",NULL) )
  547. note->flags |= kRestXsFl;
  548. // is 'grace'
  549. if( cmXmlNodeHasChild(nnp,"grace",NULL) )
  550. note->flags |= kGraceXsFl;
  551. // is 'dot'
  552. if( cmXmlNodeHasChild(nnp,"dot",NULL) )
  553. note->flags |= kDotXsFl;
  554. // is 'chord'
  555. if( cmXmlNodeHasChild(nnp,"chord",NULL) )
  556. note->flags |= kChordXsFl;
  557. // is this is first note in a tied pair
  558. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","start",NULL) )
  559. note->flags |= kTieBegXsFl;
  560. // is this is second note in a tied pair
  561. if( cmXmlNodeHasChildWithAttrAndValue(nnp,"tie","type","stop",NULL) )
  562. note->flags |= kTieEndXsFl;
  563. // has 'heel' mark
  564. if( cmXmlNodeHasChild(nnp,"notations","technical","heel",NULL) )
  565. note->flags |= kHeelXsFl;
  566. // set color coded flags
  567. if((rc = _cmXScoreParseColor(p, nnp, note )) != kOkXsRC )
  568. return rc;
  569. // get the note's rythmic value
  570. if((rc = _cmXScoreParseNoteRValue(p,nnp,"type",&note->rvalue)) != kOkXsRC )
  571. return rc;
  572. // if this is a chord note
  573. if( cmIsFlag(note->flags,kChordXsFl) )
  574. {
  575. note->tick = *tick0Ref; // then use the onset time from the previous note and do not advance time
  576. }
  577. else
  578. {
  579. *tick0Ref = *tickRef;
  580. note->tick = *tickRef;
  581. *tickRef += note->duration;
  582. }
  583. return _cmXScorePushNote(p, meas, voiceId, note );
  584. }
  585. cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* noteXmlNode, unsigned tick, unsigned duration, unsigned staff, double rvalue, const cmChar_t* tvalue, unsigned flags )
  586. {
  587. cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  588. unsigned voiceId = 0; // non-note's are always assigned to voiceId=0;
  589. note->pitch = kInvalidMidiPitch;
  590. note->tick = tick;
  591. note->staff = staff;
  592. note->flags = flags;
  593. note->rvalue = rvalue;
  594. note->tvalue = tvalue;
  595. note->duration = duration;
  596. note->tied_dur = duration;
  597. note->meas = meas;
  598. note->xmlNode = noteXmlNode;
  599. return _cmXScorePushNote(p, meas, voiceId, note );
  600. }
  601. cmXsSpan_t* _cmXScoreFindOpenOctaveShift( cmXScore_t* p, unsigned staff, unsigned number )
  602. {
  603. cmXsSpan_t* s = p->spanL;
  604. for(; s!=NULL; s=s->link)
  605. if( s->tick1 == -1 && s->staff == staff && s->number == number )
  606. return s;
  607. return NULL;
  608. }
  609. cmXsRC_t _cmXScorePushOctaveShift(cmXScore_t* p, cmXsMeas_t* meas, unsigned staff, unsigned span_number, const cmChar_t* type_str, unsigned tick)
  610. {
  611. assert( meas != NULL);
  612. cmXsSpan_t* s;
  613. if( cmTextCmp(type_str,"stop") == 0 )
  614. {
  615. if((s = _cmXScoreFindOpenOctaveShift(p,staff,span_number)) == NULL )
  616. return cmErrWarnMsg(&p->err,kUnterminatedOctaveShiftXsrRC,"An illegal octave shift was encounted in meas %i.\n",meas->number);
  617. s->tick1 = tick;
  618. }
  619. else
  620. {
  621. s = cmLhAllocZ(p->lhH,cmXsSpan_t,1);
  622. s->staff = staff;
  623. s->meas = meas;
  624. s->number = span_number;
  625. s->tick0 = tick;
  626. s->tick1 = -1;
  627. s->pitch_offset = cmTextCmp(type_str,"up")==0 ? -12 : 12;
  628. s->link = p->spanL;
  629. p->spanL = s;
  630. }
  631. return kOkXsRC;
  632. }
  633. cmXsRC_t _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* dnp, unsigned tick)
  634. {
  635. cmXsRC_t rc = kOkXsRC;
  636. const cmXmlNode_t* np = NULL;
  637. const cmXmlAttr_t* a = NULL;
  638. unsigned flags = 0;
  639. int offset = 0;
  640. double rvalue = 0;
  641. const cmChar_t* tvalue = NULL;
  642. unsigned duration = 0;
  643. bool pushFl = true;
  644. unsigned staff = 0;
  645. cmXmlNodeInt( dnp, &offset, "offset", NULL );
  646. cmXmlNodeUInt(dnp, &staff, "staff", NULL );
  647. // if this is a metronome direction
  648. if((np = cmXmlSearch( dnp, "metronome", NULL, 0)) != NULL )
  649. {
  650. if( cmXmlNodeUInt(np,&duration,"per-minute",NULL) != kOkXmlRC )
  651. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The 'per-minute' metronome value is missing on line %i.",np->line);
  652. if((rc = _cmXScoreParseNoteRValue(p,np,"beat-unit",&rvalue)) != kOkXsRC )
  653. return rc;
  654. flags = kMetronomeXsFl;
  655. }
  656. else
  657. // if this is a pedal direction
  658. if((np = cmXmlSearch( dnp, "pedal",NULL,0)) != NULL )
  659. {
  660. if((a = cmXmlFindAttrib(np,"type")) == NULL )
  661. return _cmXScoreMissingAttribute(p, np, "type" );
  662. if( cmTextCmp(a->value,"start") == 0 )
  663. flags = kDampDnXsFl;
  664. else
  665. if( cmTextCmp(a->value,"change") == 0 )
  666. flags = kDampUpDnXsFl;
  667. else
  668. if( cmTextCmp(a->value,"stop") == 0 )
  669. flags = kDampUpXsFl;
  670. else
  671. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unrecognized pedal type:'%s'.",cmStringNullGuard(a->value));
  672. }
  673. else
  674. // if this is a 'words' direction
  675. if((np = cmXmlSearch( dnp, "words", NULL, 0)) != NULL )
  676. {
  677. if((a = cmXmlFindAttrib(np,"enclosure")) != NULL && cmTextCmp(a->value,"rectangle")==0 )
  678. {
  679. if( cmTextIsEmpty( tvalue = np->dataStr ) )
  680. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Section number is blank or missing on line %i.",np->line);
  681. flags = kSectionXsFl;
  682. }
  683. else
  684. {
  685. // we only care about 'words' in 'enclosures'
  686. pushFl = false;
  687. }
  688. }
  689. else
  690. // if this is an 'octave-shift' direction
  691. if((np = cmXmlSearch( dnp, "octave-shift", NULL, 0)) != NULL )
  692. {
  693. unsigned span_number = -1;
  694. if( cmXmlAttrUInt(np,"number",&span_number) != kOkXmlRC )
  695. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'number' attribute.");
  696. if((a = cmXmlFindAttrib(np,"type")) == NULL)
  697. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Octave-shift is missing a 'type' attribute.");
  698. rc = _cmXScorePushOctaveShift(p,meas,staff,span_number,a->value,tick+offset);
  699. pushFl = false;
  700. }
  701. else
  702. {
  703. pushFl = false;
  704. }
  705. if( pushFl )
  706. rc = _cmXScorePushNonNote(p,meas,dnp,tick+offset,duration,staff,rvalue,tvalue,flags);
  707. return rc;
  708. }
  709. // On input tickRef is set to the absolute tick of the bar line and on output it is set
  710. // to the absolute tick of the next bar line.
  711. cmXsRC_t _cmXScoreParseMeasure(cmXScore_t* p, cmXsPart_t* pp, const cmXmlNode_t* mnp, unsigned* tickRef)
  712. {
  713. cmXsRC_t rc = kOkXsRC;
  714. const cmXmlNode_t* np = NULL;
  715. unsigned tick = *tickRef;
  716. unsigned tick0= 0;
  717. cmXsMeas_t* m = NULL;
  718. // allocate the 'measure' record
  719. cmXsMeas_t* meas = cmLhAllocZ(p->lhH,cmXsMeas_t,1);
  720. // get measure number
  721. if( cmXmlAttrUInt(mnp,"number", &meas->number) != kOkXmlRC )
  722. return _cmXScoreMissingAttribute(p,mnp,"number");
  723. if( pp->measL == NULL )
  724. pp->measL = meas;
  725. else
  726. {
  727. m = pp->measL;
  728. while( m->link != NULL )
  729. m = m->link;
  730. m->link = meas;
  731. meas->divisions = m->divisions;
  732. meas->beats = m->beats;
  733. meas->beat_type = m->beat_type;
  734. }
  735. // get measure attributes node
  736. if((np = cmXmlSearch(mnp,"attributes",NULL,0)) != NULL)
  737. {
  738. cmXmlNodeUInt(np,&meas->divisions,"divisions",NULL);
  739. cmXmlNodeUInt(np,&meas->beats, "time","beats",NULL);
  740. cmXmlNodeUInt(np,&meas->beat_type,"time","beat-type",NULL);
  741. }
  742. // store the bar line
  743. if((rc = _cmXScorePushNonNote(p,meas,mnp,tick,0,0,0,NULL,kBarXsFl)) != kOkXsRC )
  744. return rc;
  745. np = mnp->children;
  746. // for each child of the 'meas' XML node
  747. for(; rc==kOkXsRC && np!=NULL; np=np->sibling)
  748. {
  749. // if this is a 'note' node
  750. if( cmTextCmp(np->label,"note") == 0 )
  751. {
  752. rc = _cmXScoreParseNote(p,meas,np,&tick0,&tick);
  753. }
  754. else
  755. // if this is a 'backup' node
  756. if( cmTextCmp(np->label,"backup") == 0 )
  757. {
  758. unsigned backup;
  759. cmXmlNodeUInt(np,&backup,"duration",NULL);
  760. if( backup > tick )
  761. tick = 0;
  762. else
  763. tick -= backup;
  764. tick0 = tick;
  765. }
  766. else
  767. // if this is a 'direction' node
  768. if( cmTextCmp(np->label,"direction") == 0 )
  769. {
  770. rc = _cmXScoreParseDirection(p,meas,np,tick);
  771. }
  772. }
  773. *tickRef = tick;
  774. return rc;
  775. }
  776. cmXsRC_t _cmXScoreParsePart( cmXScore_t* p, cmXsPart_t* pp )
  777. {
  778. cmXsRC_t rc = kOkXsRC;
  779. const cmXmlNode_t* xnp;
  780. cmXmlAttr_t partAttr = { "id", pp->idStr };
  781. unsigned barTick = 0;
  782. // find the 'part'
  783. if((xnp = cmXmlSearch( cmXmlRoot(p->xmlH), "part", &partAttr, 1)) == NULL )
  784. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The part '%s' was not found.",pp->idStr);
  785. // for each child of this part - find each measure
  786. const cmXmlNode_t* cnp = xnp->children;
  787. for(; cnp!=NULL; cnp=cnp->sibling)
  788. if( cmTextCmp(cnp->label,"measure") == 0 )
  789. if((rc = _cmXScoreParseMeasure(p,pp,cnp,&barTick)) != kOkXsRC )
  790. return rc;
  791. return rc;
  792. }
  793. // Insert note 'np' into the sorted note list based at 's0'.
  794. // Return a pointer to the base of the list after the insertion.
  795. cmXsNote_t* _cmXScoreInsertSortedNote( cmXsNote_t* s0, cmXsNote_t* np )
  796. {
  797. assert( np != NULL );
  798. // np->slink is not NULL if the list is being resorted
  799. np->slink = NULL;
  800. // this list is empty so np is the first element on the list
  801. if( s0 == NULL )
  802. return np;
  803. // np is before the first element on the list
  804. if( np->tick < s0->tick )
  805. {
  806. np->slink = s0;
  807. return np;
  808. }
  809. cmXsNote_t* s1 = s0;
  810. cmXsNote_t* s2 = s0->slink;
  811. while( s2 != NULL )
  812. {
  813. if( s2->tick > np->tick )
  814. {
  815. s1->slink = np;
  816. np->slink = s2;
  817. return s0;
  818. }
  819. s1 = s2;
  820. s2 = s2->slink;
  821. }
  822. s1->slink = np;
  823. return s0;
  824. }
  825. // Set the cmXsNode_t.secs and dsecs.
  826. void _cmXScoreSetAbsoluteTime( cmXScore_t* p )
  827. {
  828. double tpqn = 0; // ticks per quarter note
  829. double tps = 0; // ticks per second
  830. unsigned metro_tick = 0;
  831. double metro_sec = 0;
  832. double sec0 = 0;
  833. cmXsPart_t* pp = p->partL;
  834. for(; pp!=NULL; pp=pp->link)
  835. {
  836. cmXsMeas_t* mp = pp->measL;
  837. for(; mp!=NULL; mp=mp->link)
  838. {
  839. if( mp->divisions != 0 )
  840. tpqn = mp->divisions;
  841. cmXsNote_t* np = mp->noteL;
  842. for(; np!=NULL; np=np->slink)
  843. {
  844. // Seconds are calculated as:
  845. // dticks = np->tick - metro_tick; // where metro_tick is the absolute tick of the last metro event
  846. // secs = (dticks/tps) + metro_secs; // where metro_secs is the absoute time of the last metro event
  847. unsigned dticks = np->tick - metro_tick;
  848. double secs = tps==0 ? 0 : (dticks/tps) + metro_sec;
  849. double dsecs = secs - sec0;
  850. //
  851. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  852. {
  853. double bpm = np->duration; // beats per minute
  854. double bps = bpm / 60.0; // beats per second
  855. tps = bps * tpqn; // ticks per second
  856. metro_tick = np->tick; // store tick of the last metronome marker
  857. metro_sec = secs; // store time of the last metronome marker
  858. }
  859. if( cmIsFlag(np->flags,kBarXsFl|kDampDnXsFl|kDampUpXsFl|kDampUpDnXsFl|kSostDnXsFl|kSostUpXsFl|kOnsetXsFl|kSectionXsFl) )
  860. {
  861. np->secs = secs;
  862. np->dsecs = dsecs;
  863. sec0 = secs;
  864. }
  865. }
  866. }
  867. }
  868. }
  869. void _cmXScoreSort( cmXScore_t* p )
  870. {
  871. // for each part
  872. cmXsPart_t* pp = p->partL;
  873. for(; pp!=NULL; pp=pp->link)
  874. {
  875. // for each measure in this part
  876. cmXsMeas_t* mp = pp->measL;
  877. for(; mp!=NULL; mp=mp->link)
  878. {
  879. // explicitely set noteL to NULL to in case we are re-sorting
  880. mp->noteL = NULL;
  881. // for each voice in this measure
  882. cmXsVoice_t* vp = mp->voiceL;
  883. for(; vp!=NULL; vp=vp->link)
  884. {
  885. // for each note in this measure
  886. cmXsNote_t* np = vp->noteL;
  887. for(; np!=NULL; np=np->mlink)
  888. mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np);
  889. }
  890. }
  891. }
  892. // The order of events may have changed update the absolute time.
  893. _cmXScoreSetAbsoluteTime( p );
  894. }
  895. // All notes in a[aN] are on the same tick
  896. unsigned _cmXsSpreadGraceNotes( cmXsNote_t** a, unsigned aN )
  897. {
  898. unsigned i;
  899. bool barFl = false;
  900. // set barFl to true if a bar marker is included in the notes
  901. for(i=0; i<aN; ++i)
  902. if( cmIsFlag(a[i]->flags,kBarXsFl) )
  903. barFl = true;
  904. // spread any grace notes by one tick
  905. unsigned nextGraceTick = UINT_MAX;
  906. for(i=0; i<aN; ++i)
  907. if( cmIsFlag(a[i]->flags,kGraceXsFl) )
  908. {
  909. if( nextGraceTick == UINT_MAX )
  910. nextGraceTick = a[i]->tick + 1;
  911. else
  912. {
  913. a[i]->tick = nextGraceTick;
  914. nextGraceTick += 1;
  915. }
  916. }
  917. // if this tick group includes the bar ...
  918. if( barFl && nextGraceTick != UINT_MAX )
  919. {
  920. // ... then move all non-grace note events (except the bar marker) after
  921. // the grace notes
  922. for(i=0; i<aN; ++i)
  923. if( cmIsNotFlag(a[i]->flags,kGraceXsFl) && cmIsNotFlag(a[i]->flags,kBarXsFl) )
  924. a[i]->tick = nextGraceTick;
  925. }
  926. return nextGraceTick==UINT_MAX ? 0 : nextGraceTick;
  927. }
  928. void _cmXScoreSpreadGraceNotes( cmXScore_t* p )
  929. {
  930. cmXsPart_t* pp = p->partL;
  931. for(; pp!=NULL; pp=pp->link)
  932. {
  933. // tick1 is the location of the minimum current tick
  934. // (or 0 if it should be ignored)
  935. unsigned tick1 = 0;
  936. cmXsMeas_t* mp = pp->measL;
  937. for(; mp!=NULL; mp=mp->link)
  938. {
  939. cmXsNote_t* np = mp->noteL;
  940. unsigned aN = 128;
  941. cmXsNote_t* a[ aN ];
  942. unsigned ai = 0;
  943. // The first event in a measure may have been forced ahead
  944. // by spreading at the end of the previous measure
  945. if( tick1 > np->tick )
  946. np->tick = tick1;
  947. else
  948. tick1 = 0;
  949. // tick0 is the tick of the current tick group we are examining
  950. // A tick group is a group of events that share the same tick.
  951. unsigned tick0 = np->tick;
  952. // for each note
  953. for(; np!=NULL; np=np->slink)
  954. {
  955. // if this event is the first of a new tick group (then a[] holds a group completed on the previous note)
  956. if( np->tick != tick0 )
  957. {
  958. // if there is more than one event in the completed tick group ...
  959. if( ai > 1 )
  960. tick1 = _cmXsSpreadGraceNotes(a,ai); // ... then process the group
  961. ai = 0; // empty the tick group array
  962. tick0 = np->tick; // update the current group's common tick
  963. }
  964. // if the min. current tick is ahead of this event then move the event ahead
  965. if( tick1 > np->tick )
  966. np->tick = tick1;
  967. else
  968. tick1 = 0; // otherwise disable tick1
  969. // add this event to the tick group
  970. assert(ai<aN);
  971. a[ai++] = np;
  972. }
  973. // if there are events in the group array then process them
  974. if( ai > 1 )
  975. tick1 = _cmXsSpreadGraceNotes(a,ai);
  976. }
  977. }
  978. }
  979. bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
  980. {
  981. cmXsNote_t* nbp = n0p;
  982. cmXsNote_t* nnp = n0p->slink; // begin w/ note following np
  983. unsigned measNumb = mp->number;
  984. cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' ');
  985. if( rptFl )
  986. printf("%i %i %s ",n0p->meas->number,n0p->tick,cmMidiToSciPitch(n0p->pitch,NULL,0));
  987. while(1)
  988. {
  989. // if we are at the end of a measure advance to the next measure
  990. if( nnp == NULL )
  991. {
  992. mp = mp->link;
  993. nnp = mp->noteL;
  994. // if a measure was completed and no end note was found ... then the tie is unterminated
  995. // (a tie must be continued in every measure which it passes through)
  996. if( mp->number > measNumb + 1 )
  997. break;
  998. }
  999. // for each note starting at nnp
  1000. for(; nnp!=NULL; nnp=nnp->slink)
  1001. {
  1002. // if this note is tied to the originating note (np)
  1003. if( nnp->voice->id == n0p->voice->id && nnp->step == n0p->step && nnp->octave == n0p->octave )
  1004. {
  1005. nnp->flags |= kTieProcXsFl;
  1006. nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl);
  1007. n0p->tied = nnp;
  1008. nbp->tied_dur += nnp->duration;
  1009. nnp->tied_dur = 0;
  1010. if( rptFl )
  1011. printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
  1012. // if this note is not tied to a subsequent note
  1013. if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
  1014. return true;
  1015. n0p = nnp;
  1016. // record the measure number of the last note with a tie-start
  1017. measNumb = mp->number;
  1018. }
  1019. }
  1020. }
  1021. cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i (tick:%i) was not terminated.",n0p->step,acc,n0p->octave,measNumb,n0p->tick);
  1022. return false;
  1023. }
  1024. void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
  1025. {
  1026. unsigned n = 0; // count of notes which begin a tie
  1027. unsigned m = 0; // count of tied notes that are correctly terminated.
  1028. bool rptFl = false;
  1029. cmXsPart_t* pp = p->partL;
  1030. // for each part
  1031. for(; pp!=NULL; pp=pp->link)
  1032. {
  1033. unsigned locIdx = 1;
  1034. cmXsMeas_t* mp = pp->measL;
  1035. // for each measure
  1036. for(; mp!=NULL; mp=mp->link)
  1037. {
  1038. cmXsNote_t* n0 = NULL;
  1039. cmXsNote_t* np = mp->noteL;
  1040. // for each note in this measure
  1041. for(; np!=NULL; np=np->slink)
  1042. {
  1043. // if this note begins a tie and has not yet been processed
  1044. // (A note that continues a tie and therefore has a kTieBegXsFl set
  1045. // may have already been processed by an earlier tied note.)
  1046. if( cmIsFlag(np->flags,kTieBegXsFl) && cmIsNotFlag(np->flags,kTieProcXsFl))
  1047. {
  1048. if( _cmXScoreFindTiedNote(p,mp,np,rptFl) )
  1049. m += 1;
  1050. if( rptFl )
  1051. printf("\n");
  1052. n += 1;
  1053. }
  1054. // Validate the tie state of the current note.
  1055. if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
  1056. {
  1057. cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' ');
  1058. 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);
  1059. }
  1060. //
  1061. // Set the score location of notes marked for onset and bar lines.
  1062. //
  1063. if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) )
  1064. {
  1065. // if this note does not share the same location as the previous 'located' note then increment the 'loc' index
  1066. if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
  1067. locIdx += 1;
  1068. np->locIdx = locIdx;
  1069. n0 = np;
  1070. }
  1071. }
  1072. }
  1073. }
  1074. printf("Tied notes found:%i Not found:%i\n",m,n-m);
  1075. }
  1076. cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p )
  1077. {
  1078. const cmXsSpan_t* s;
  1079. for(s=p->spanL; s!=NULL; s=s->link)
  1080. {
  1081. if( s->tick1 == -1)
  1082. {
  1083. cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"An unterminated octave shift span was encountered in measure %i staff=%i.",s->meas->number,s->staff);
  1084. }
  1085. else
  1086. {
  1087. cmXsMeas_t* m = p->partL->measL;
  1088. for(; m!=NULL; m=m->link)
  1089. if( m->number == s->meas->number )
  1090. break;
  1091. assert( m != NULL );
  1092. cmXsNote_t* note = m->noteL;
  1093. for(; note!=NULL; note=note->slink)
  1094. if( note->staff==s->staff && s->tick0 <= note->tick && note->tick < s->tick1 )
  1095. note->pitch += s->pitch_offset;
  1096. }
  1097. }
  1098. return kOkXsRC;
  1099. }
  1100. cmXsNote_t* _cmXScoreFindOverlappingNote( cmXScore_t* p, const cmXsNote_t* knp )
  1101. {
  1102. cmXsPart_t* pp = p->partL;
  1103. // for each part
  1104. for(; pp!=NULL; pp=pp->link)
  1105. {
  1106. cmXsMeas_t* mp = pp->measL;
  1107. // for each measure
  1108. for(; mp!=NULL; mp=mp->link)
  1109. {
  1110. cmXsNote_t* np = mp->noteL;
  1111. // for each note in this measure
  1112. for(; np!=NULL; np=np->slink)
  1113. if( np->uid != knp->uid
  1114. && cmIsFlag(np->flags,kOnsetXsFl)
  1115. && knp->pitch == np->pitch
  1116. && knp->tick >= np->tick
  1117. && knp->tick < (np->tick + np->tied_dur) )
  1118. {
  1119. return np;
  1120. }
  1121. }
  1122. }
  1123. return NULL;
  1124. }
  1125. void _cmXScoreProcessOverlappingNotes( cmXScore_t* p )
  1126. {
  1127. cmXsPart_t* pp = p->partL;
  1128. // for each part
  1129. for(; pp!=NULL; pp=pp->link)
  1130. {
  1131. cmXsMeas_t* mp = pp->measL;
  1132. // for each measure
  1133. for(; mp!=NULL; mp=mp->link)
  1134. {
  1135. cmXsNote_t* np = mp->noteL;
  1136. cmXsNote_t* fnp;
  1137. // for each note in this measure
  1138. for(; np!=NULL; np=np->slink)
  1139. if( cmIsFlag(np->flags,kOnsetXsFl) && (fnp = _cmXScoreFindOverlappingNote(p,np)) != NULL)
  1140. {
  1141. // is np entirely contained inside fnp
  1142. bool embeddedFl = fnp->tick + fnp->tied_dur > np->tick + np->tied_dur;
  1143. //printf("bar=%3i %4s voice:%2i %2i : %7i %7i : %7i %7i : %7i : %c \n",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0),np->voice->id,fnp->voice->id,fnp->tick,fnp->tick+fnp->duration,np->tick,np->tick+np->duration, (fnp->tick+fnp->duration) - np->tick, embeddedFl ? 'E' : 'O');
  1144. // turn off embedded notes
  1145. if( embeddedFl )
  1146. {
  1147. if( np->voice->id == fnp->voice->id )
  1148. cmErrWarnMsg(&p->err,kOverlapWarnXsRC,"A time embedded note (bar=%i %s) was removed even though it overlapped with a note in the same voice.",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0));
  1149. np->flags = cmClrFlag(np->flags,kOnsetXsFl);
  1150. }
  1151. else
  1152. {
  1153. int d = (fnp->tick+fnp->tied_dur) - np->tick;
  1154. // shorten the first note
  1155. if( d > 0 && d < fnp->tied_dur )
  1156. fnp->tied_dur -= d;
  1157. // move the second note just past it
  1158. np->tick = fnp->tick + fnp->tied_dur;
  1159. cmErrWarnMsg(&p->err,kOverlapWarnXsRC,"A shorten/shift operation was done to reconcile two overlapped %s (ticks:%i %i) notes in measure %i.",cmMidiToSciPitch(np->pitch,NULL,0),fnp->tick,np->tick,np->meas->number);
  1160. }
  1161. }
  1162. }
  1163. }
  1164. }
  1165. // The identical pitch may be notated to play simultaneously on different voices.
  1166. // As performed on the piano this will equate to a single sounding note.
  1167. // This function clears the onset flag on all except one of the duplicated notes.
  1168. void _cmXScoreRemoveDuplicateNotes( cmXScore_t* p )
  1169. {
  1170. cmXsPart_t* pp = p->partL;
  1171. // for each part
  1172. for(; pp!=NULL; pp=pp->link)
  1173. {
  1174. cmXsMeas_t* mp = pp->measL;
  1175. // for each measure
  1176. for(; mp!=NULL; mp=mp->link)
  1177. {
  1178. cmXsNote_t* np = mp->noteL;
  1179. // for each note in this measure
  1180. for(; np!=NULL; np=np->slink)
  1181. if( cmIsFlag(np->flags,kOnsetXsFl) )
  1182. {
  1183. cmXsNote_t* n0p = mp->noteL;
  1184. for(; n0p!=NULL; n0p=n0p->slink)
  1185. if( n0p!=np && cmIsFlag(n0p->flags,kOnsetXsFl) && np->locIdx==n0p->locIdx && np->pitch==n0p->pitch )
  1186. n0p->flags = cmClrFlag(n0p->flags,kOnsetXsFl);
  1187. }
  1188. }
  1189. }
  1190. }
  1191. void _cmXScoreSetMeasGroups( cmXScore_t* p, unsigned flag )
  1192. {
  1193. unsigned sectionId = 0;
  1194. cmXsNote_t* n0 = NULL;
  1195. cmXsPart_t* pp = p->partL;
  1196. // for each part
  1197. for(; pp!=NULL; pp=pp->link)
  1198. {
  1199. cmXsMeas_t* mp = pp->measL;
  1200. // for each measure
  1201. for(; mp!=NULL; mp=mp->link)
  1202. {
  1203. cmXsNote_t* np = mp->noteL;
  1204. // for each note in this measure
  1205. for(; np!=NULL; np=np->slink)
  1206. {
  1207. // if this note has a heel marker and we are looking for evenness events
  1208. if( cmIsFlag(flag,kEvenXsFl) && cmIsFlag(np->flags,kHeelXsFl) )
  1209. {
  1210. np->flags = cmSetFlag(np->flags,kBegGroupXsFl | kEndGroupXsFl );
  1211. np->evenGroupId = sectionId + 1;
  1212. }
  1213. // if this note is of the type we are looking for
  1214. if( cmIsFlag(np->flags,flag) )
  1215. {
  1216. if( n0 == NULL )
  1217. np->flags = cmSetFlag(np->flags,kBegGroupXsFl);
  1218. n0 = np;
  1219. }
  1220. // if this is a section marker
  1221. if( cmIsFlag(np->flags,kSectionXsFl) )
  1222. {
  1223. if( n0 != NULL )
  1224. {
  1225. np->flags = cmSetFlag(np->flags,kEndGroupXsFl);
  1226. switch( flag )
  1227. {
  1228. case kEvenXsFl: n0->evenGroupId = sectionId+1; break;
  1229. case kDynXsFl: n0->dynGroupId = sectionId+1; break;
  1230. case kTempoXsFl: n0->tempoGroupId= sectionId+1; break;
  1231. }
  1232. }
  1233. if( cmIsFlag(np->flags,kSectionXsFl) )
  1234. {
  1235. sectionId = 0;
  1236. if( np->tvalue == NULL )
  1237. cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"An apparent section label in measure '%i' is blank.",np->meas->number);
  1238. else
  1239. if( cmTextToUInt( np->tvalue, &sectionId, NULL ) != kOkTxRC )
  1240. cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"The section label '%s' is not an integer.",np->tvalue);
  1241. //sectionId = np->tvalue==NULL ? 0 : strtol(np->tvalue,NULL,10);
  1242. }
  1243. n0 = NULL;
  1244. }
  1245. }
  1246. }
  1247. }
  1248. }
  1249. cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn )
  1250. {
  1251. cmXsRC_t rc = kOkXsRC;
  1252. cmFileH_t fH = cmFileNullHandle;
  1253. double ticks_per_sec = 0;
  1254. double onset_secs = 0;
  1255. if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  1256. return cmErrMsg(&p->err,kFileFailXsRC,"Unable to create the file '%s'.",cmStringNullGuard(fn));
  1257. cmXsPart_t* pp = p->partL;
  1258. for(; pp!=NULL; pp=pp->link)
  1259. {
  1260. cmXsMeas_t* mp = pp->measL;
  1261. for(; mp!=NULL; mp=mp->link)
  1262. {
  1263. cmFilePrintf(fH,"b %f %i %s B\n",onset_secs,mp->number,"bar");
  1264. cmXsNote_t* np = mp->noteL;
  1265. unsigned tick0 = 0;
  1266. for(; np!=NULL; np=np->slink)
  1267. {
  1268. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  1269. {
  1270. double bps = np->tied_dur / 60.0;
  1271. // t b t
  1272. // - = - -
  1273. // s s b
  1274. ticks_per_sec = bps * mp->divisions;
  1275. }
  1276. else
  1277. {
  1278. if( cmIsFlag(np->flags,kOnsetXsFl) )
  1279. {
  1280. onset_secs += (np->tick - tick0) / ticks_per_sec;
  1281. tick0 = np->tick;
  1282. cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->tied_dur/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N");
  1283. }
  1284. }
  1285. }
  1286. onset_secs += (mp->divisions * mp->beats - tick0) / ticks_per_sec;
  1287. }
  1288. }
  1289. cmFileClose(&fH);
  1290. return rc;
  1291. }
  1292. // Force the bar event to be the first event in the measure.
  1293. void _cmXScoreFixBarLines( cmXScore_t* p )
  1294. {
  1295. cmXsPart_t* pp = p->partL;
  1296. for(; pp!=NULL; pp=pp->link)
  1297. {
  1298. cmXsMeas_t* mp = pp->measL;
  1299. for(; mp!=NULL; mp=mp->link)
  1300. {
  1301. cmXsNote_t* np = mp->noteL;
  1302. cmXsNote_t* ep = NULL;
  1303. for(; np!=NULL; np=np->slink )
  1304. {
  1305. if( cmIsFlag(np->flags,kBarXsFl) )
  1306. {
  1307. if( ep != NULL )
  1308. np->tick = ep->tick;
  1309. break;
  1310. }
  1311. if( ep == NULL )
  1312. ep = np;
  1313. }
  1314. }
  1315. }
  1316. }
  1317. // Assign pedal down durations to pedal down events.
  1318. cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p, bool reportFl )
  1319. {
  1320. cmXsRC_t rc = kOkXsRC;
  1321. cmXsPart_t* pp = p->partL;
  1322. for(; pp!=NULL; pp=pp->link)
  1323. {
  1324. cmXsNote_t* dnp = NULL; // pointer to last damper down event
  1325. cmXsNote_t* snp = NULL; // pointer to last sostenuto down event
  1326. cmXsMeas_t* mp = pp->measL;
  1327. for(; mp!=NULL; mp=mp->link)
  1328. {
  1329. cmXsNote_t* np = mp->noteL;
  1330. for(; np!=NULL; np=np->slink )
  1331. {
  1332. unsigned x = np->flags & (kDampDnXsFl|kDampUpXsFl|kDampUpDnXsFl|kSostUpXsFl|kSostDnXsFl);
  1333. switch( x )
  1334. {
  1335. case 0:
  1336. break;
  1337. case kDampDnXsFl:
  1338. if( dnp != NULL )
  1339. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper down not preceded by damper up in measure:%i tick:%i.",mp->number,np->tick);
  1340. else
  1341. dnp = np;
  1342. if( reportFl )
  1343. cmRptPrintf(p->err.rpt,"Damp Down : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
  1344. break;
  1345. case kDampUpXsFl:
  1346. if( dnp == NULL )
  1347. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up not preceded by damper down in measure:%i tick:%i.",mp->number,np->tick);
  1348. else
  1349. {
  1350. dnp->duration = np->tick - dnp->tick;
  1351. dnp = NULL;
  1352. }
  1353. if( reportFl )
  1354. cmRptPrintf(p->err.rpt,"Damp Up : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
  1355. break;
  1356. case kDampUpDnXsFl:
  1357. if( dnp == NULL )
  1358. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper up/down not preceded by damper down in measure:%i tick:%i.",mp->number,np->tick);
  1359. else
  1360. {
  1361. dnp->duration = np->tick - dnp->tick;
  1362. dnp = np;
  1363. }
  1364. if( reportFl )
  1365. cmRptPrintf(p->err.rpt,"Damp UpDn : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
  1366. break;
  1367. case kSostDnXsFl:
  1368. if( snp != NULL )
  1369. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto down not preceded by sostenuto up in measure:%i tick:%i.",mp->number, np->tick);
  1370. else
  1371. snp = np;
  1372. if( reportFl )
  1373. cmRptPrintf(p->err.rpt,"Sost Down : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
  1374. break;
  1375. case kSostUpXsFl:
  1376. if( snp == NULL )
  1377. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto up not preceded by sostenuto down in measure:%i tick:%i.",mp->number,np->tick);
  1378. else
  1379. {
  1380. snp->duration = np->tick - snp->tick;
  1381. snp = NULL;
  1382. }
  1383. if( reportFl )
  1384. cmRptPrintf(p->err.rpt,"Sost Up : staff:%i meas:%i tick:%i\n", np->staff, mp->number, np->tick);
  1385. break;
  1386. default:
  1387. {
  1388. assert(0);
  1389. }
  1390. } // switch
  1391. } // for notes
  1392. } // for measures
  1393. if( dnp != NULL )
  1394. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Damper left down at the end of a part.");
  1395. if( snp != NULL )
  1396. cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto left down at the end of a part.");
  1397. }
  1398. _cmXScoreSort(p);
  1399. return rc;
  1400. }
  1401. void _cmXScoreInsertTime( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* np, unsigned expand_ticks )
  1402. {
  1403. for(; mp!=NULL; mp=mp->link)
  1404. {
  1405. if( np == NULL )
  1406. np = mp->noteL;
  1407. for(; np!=NULL; np=np->slink)
  1408. np->tick += expand_ticks;
  1409. }
  1410. }
  1411. void _cmXScoreGraceInsertTimeBase( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN, unsigned initTick )
  1412. {
  1413. cmXsNote_t* np = NULL;
  1414. unsigned expand_ticks = 0;
  1415. unsigned ticks = initTick;
  1416. unsigned t0 = 0;
  1417. unsigned i;
  1418. for(i=0; i<aN; ++i)
  1419. if( cmIsFlag(aV[i]->flags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId )
  1420. {
  1421. // if this grace note falls on the same tick as the previous grace note
  1422. if( np != NULL && aV[i]->tick == t0 )
  1423. aV[i]->tick = np->tick;
  1424. else
  1425. {
  1426. t0 = aV[i]->tick; // store the unmodified tick value of this note
  1427. aV[i]->tick = ticks; // set the new tick value
  1428. ticks += aV[i]->duration; // calc the next grace not location
  1429. expand_ticks += aV[i]->duration; // track how much we are expanding time by
  1430. }
  1431. np = aV[i];
  1432. }
  1433. np = np->slink;
  1434. if( np != NULL )
  1435. _cmXScoreInsertTime(p,np->meas,np,expand_ticks);
  1436. }
  1437. // (a) Insert the grace notes in between the first and last note in the group
  1438. // by inserting time between the first and last note.
  1439. // Note that in effect his means that the last note is pushed back
  1440. // in time by the total duration of the grace notes.
  1441. void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
  1442. {
  1443. _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[aN-1]->tick );
  1444. }
  1445. // (s) Insert the grace notes in between the first and last note in the group
  1446. // but do not insert any additional time betwee the first and last note.
  1447. // In effect time is removed from the first note and taken by the grace notes.
  1448. // The time position of the last note is therefore unchanged.
  1449. void _cmXScoreGraceOverlayTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
  1450. {
  1451. assert(aN >= 3 );
  1452. int i = (int)aN-2;
  1453. cmXsNote_t* np = aV[aN-1];
  1454. unsigned t0 = -1;
  1455. for(; i>0; --i)
  1456. if( cmIsFlag(aV[i]->flags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId )
  1457. {
  1458. if( aV[i]->tick == t0)
  1459. aV[i]->tick = np->tick;
  1460. else
  1461. {
  1462. t0 = aV[i]->tick;
  1463. aV[i]->tick = np->tick - aV[i]->duration;
  1464. }
  1465. np = aV[i];
  1466. }
  1467. }
  1468. // (A) Play the first grace at the time of the first note in the group (which is a non-grace note)
  1469. // and then expand time while inserting the other grace notes.
  1470. void _cmXScoreGraceInsertAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
  1471. {
  1472. _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[0]->tick );
  1473. }
  1474. // (n) Play the first grace not shortly (one grace note duration) after the first note
  1475. // in the group (which is a non-grace note) and then expand time while inserting the other
  1476. // grace notes.
  1477. void _cmXScoreGraceInsertSoonAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
  1478. {
  1479. _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[0]->tick + aV[1]->duration );
  1480. }
  1481. // Adjust the locations of grace notes. Note that this must be done
  1482. // after reordering so that we can be sure that the order in time of
  1483. // the notes in each group has been set prior to building the
  1484. // grace note groups - which must be in reverse time order.
  1485. cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p, unsigned nextGraceGroupId )
  1486. {
  1487. cmXsRC_t rc = kOkXsRC;
  1488. unsigned graceGroupId = 1;
  1489. double graceDurSec = 1.0/15.0; // duration of all grace notes in seconds
  1490. for(; graceGroupId<nextGraceGroupId; ++graceGroupId)
  1491. {
  1492. cmXsNote_t* gn0p = NULL; // first note in the grace group
  1493. cmXsNote_t* gn1p = NULL; // last note in the grace group
  1494. unsigned gN = 0;
  1495. cmXsPart_t* pp = p->partL;
  1496. double ticksPerSec = 0;
  1497. // Build a note chain, using cmXsNote_t.grace, between gn0p and
  1498. // gn1p containing all the grace notes with
  1499. // cmXsNote_t.graceGroupId == graceGroupId.
  1500. for(; pp!=NULL; pp=pp->link)
  1501. {
  1502. cmXsMeas_t* mp = pp->measL;
  1503. for(; mp!=NULL; mp=mp->link)
  1504. {
  1505. cmXsNote_t* np = mp->noteL;
  1506. for(; np!=NULL; np=np->slink )
  1507. {
  1508. // notice change of tempo
  1509. if( cmIsFlag(np->flags,kMetronomeXsFl) )
  1510. {
  1511. // ticks/sec = ticks/qn * qn/sec
  1512. ticksPerSec = mp->divisions * np->duration / 60.0;
  1513. }
  1514. // if this note is part of the grace note group we are searching for
  1515. if( np->graceGroupId == graceGroupId )
  1516. {
  1517. // track the first note in the grace note list
  1518. if( gn0p == NULL )
  1519. gn0p = np;
  1520. // add the note to the end of the grace note list
  1521. if( gn1p != NULL )
  1522. gn1p->grace = np;
  1523. // track the last note in the grace note list
  1524. gn1p = np;
  1525. // set each grace note to have 1/20 of a second duration
  1526. if( cmIsFlag(np->flags,kGraceXsFl) )
  1527. np->duration = np->tied_dur = floor(ticksPerSec * graceDurSec);
  1528. gN += 1;
  1529. }
  1530. } // for each note in this meassure
  1531. } // for each measure
  1532. } // for each part
  1533. // no records were found for this grace id - this grace group id was not used
  1534. if( gn0p == NULL )
  1535. continue;
  1536. // grace note groups must have at least 3 members
  1537. if( gN < 3 )
  1538. {
  1539. rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The grace note group (groupid=%i) ending in meas %i has fewer than 3 (%i) members.", gn1p->graceGroupId, gn1p->meas->number, gN );
  1540. break;
  1541. }
  1542. // gn0p is now set to the first note in th group
  1543. // gn1p is now set to the last note in the group
  1544. // verify that the first note is marked with kBegGraceXsFl
  1545. if( cmIsNotFlag(gn0p->flags,kBegGraceXsFl) )
  1546. {
  1547. rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The first note in a grace note group in meas %i tick %i groupid:%i is not marked with a 'b'.", gn0p->meas->number, gn0p->tick, graceGroupId );
  1548. break;
  1549. }
  1550. // verify that the last note is marked with kEndGraceXsFl
  1551. if( cmIsNotFlag(gn1p->flags,kEndGraceXsFl) )
  1552. {
  1553. rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The last note in a grace note group in meas %i is not marked with a valid operator character.", gn1p->meas->number );
  1554. break;
  1555. }
  1556. // Count the total number of events between gn0p and gn1p
  1557. cmXsNote_t* n0p = NULL;
  1558. cmXsNote_t* n1p = gn0p;
  1559. cmXsMeas_t* mp = gn0p->meas;
  1560. unsigned aN = 0;
  1561. for(; n0p != gn1p; n1p=n1p->slink )
  1562. {
  1563. // if we are crossing a measure boundary
  1564. if( n1p == NULL )
  1565. {
  1566. mp = mp->link;
  1567. assert(mp!=NULL);
  1568. n1p = mp->noteL;
  1569. }
  1570. if(0)
  1571. {
  1572. bool fl = n0p != NULL && n0p->tick < n1p->tick;
  1573. unsigned type = n1p->flags & (kBegGraceXsFl|kEndGraceXsFl|kAddGraceXsFl|kSubGraceXsFl|kAFirstGraceXsFl|kNFirstGraceXsFl);
  1574. printf("%3i 0x%08x %i %3i %5i %i\n",n1p->graceGroupId,type,n1p->meas->number,n1p->tick,n1p->duration,fl);
  1575. }
  1576. ++aN;
  1577. n0p = n1p;
  1578. }
  1579. // create a vector of pointers to all events between gn0p and gn1p
  1580. cmXsNote_t* aV[ aN ];
  1581. unsigned i;
  1582. n1p = gn0p;
  1583. n0p = NULL;
  1584. mp = gn0p->meas;
  1585. for(i=0; n0p != gn1p; n1p=n1p->slink )
  1586. {
  1587. // if we are crossing a measure boundary
  1588. if( n1p == NULL )
  1589. {
  1590. mp = mp->link;
  1591. assert(mp!=NULL);
  1592. n1p = mp->noteL;
  1593. }
  1594. assert(i<aN);
  1595. aV[i++] = n1p;
  1596. n0p = n1p;
  1597. }
  1598. switch( gn1p->flags & (kAddGraceXsFl | kSubGraceXsFl | kAFirstGraceXsFl | kNFirstGraceXsFl ) )
  1599. {
  1600. case kAddGraceXsFl:
  1601. _cmXScoreGraceInsertTime(p, graceGroupId, aV, aN );
  1602. break;
  1603. case kSubGraceXsFl:
  1604. _cmXScoreGraceOverlayTime(p, graceGroupId, aV, aN );
  1605. break;
  1606. case kAFirstGraceXsFl:
  1607. _cmXScoreGraceInsertAfterFirst(p,graceGroupId,aV,aN);
  1608. break;
  1609. case kNFirstGraceXsFl:
  1610. _cmXScoreGraceInsertSoonAfterFirst(p,graceGroupId,aV,aN);
  1611. break;
  1612. default:
  1613. { assert(0); }
  1614. }
  1615. }
  1616. return rc;
  1617. }
  1618. cmXsNote_t* _cmXScoreDynamicForkEndNote( cmXScore_t* p, cmXsNote_t* bnp )
  1619. {
  1620. assert( cmIsFlag(bnp->flags,kDynBegForkXsFl) );
  1621. cmXsMeas_t* mp = bnp->meas;
  1622. cmXsNote_t* np = bnp->slink;
  1623. for(; mp!=NULL; mp=mp->link,np = mp==NULL?NULL : mp->noteL)
  1624. {
  1625. for(; np!=NULL; np=np->slink)
  1626. {
  1627. if( cmIsFlag(np->flags,kDynBegForkXsFl) )
  1628. {
  1629. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The a dynamic fork begin (meas:%i loc:%i) was found prior to a matched end.",np->meas->number,np->locIdx);
  1630. return NULL;
  1631. }
  1632. if( cmIsFlag(np->flags,kDynEndForkXsFl) /*&& np->voice->id == bnp->voice->id*/ )
  1633. return np;
  1634. }
  1635. }
  1636. return NULL;
  1637. }
  1638. cmXsRC_t _cmXScoreProcessDynamicFork( cmXScore_t* p, cmXsNote_t* bnp )
  1639. {
  1640. cmXsNote_t* enp;
  1641. if((enp = _cmXScoreDynamicForkEndNote(p,bnp)) == NULL)
  1642. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The dynamic fork beginning in measure %i at tick %i voice %i loc:%i was not terminated.", bnp->meas->number,bnp->tick,bnp->voice->id,bnp->locIdx);
  1643. //cmRptPrintf( p->err.rpt, "Dynamic Fork: meas:%i tick:%i loc:%i to meas:%i tick:%i loc:%i\n", bnp->meas->number, bnp->tick, bnp->locIdx, enp->meas->number, enp->tick, enp->locIdx );
  1644. double begDynFrac = bnp->dynamics;
  1645. double begVelFrac = bnp->vel;
  1646. double endDynFrac = enp->dynamics;
  1647. double endVelFrac = enp->vel;
  1648. cmXsMeas_t* mp = bnp->meas;
  1649. cmXsNote_t* np = bnp->slink;
  1650. while( mp != NULL )
  1651. {
  1652. for(; np!=NULL; np=np->slink)
  1653. {
  1654. //Conditioning this on np->dynamics==0 will may would cause 'silent' notes to be assigned
  1655. // a non-zero dynamics value - so we condition on np->vel instead.
  1656. if( cmIsFlag(np->flags,kOnsetXsFl) && np->vel == 0 )
  1657. {
  1658. double frac = ((double)np->tick - bnp->tick) / (enp->tick - bnp->tick);
  1659. np->dynamics = lround(begDynFrac + ((endDynFrac - begDynFrac) * frac));
  1660. np->vel = lround(begVelFrac + ((endVelFrac - begVelFrac) * frac));
  1661. }
  1662. if( np == enp )
  1663. break;
  1664. }
  1665. if( np == enp)
  1666. break;
  1667. if( (mp=mp->link) != NULL )
  1668. np=mp->noteL;
  1669. }
  1670. return kOkXsRC;
  1671. }
  1672. cmXsRC_t _cmXScoreProcessDynamicForks( cmXScore_t* p )
  1673. {
  1674. cmXsRC_t rc = kOkXsRC;
  1675. cmXsPart_t* pp = p->partL;
  1676. for(; pp!=NULL; pp=pp->link)
  1677. {
  1678. cmXsMeas_t* mp = pp->measL;
  1679. for(; mp!=NULL; mp=mp->link)
  1680. {
  1681. cmXsNote_t* np = mp->noteL;
  1682. for(; np!=NULL; np=np->slink )
  1683. {
  1684. if( cmIsFlag(np->flags,kDynBegForkXsFl) )
  1685. if((rc = _cmXScoreProcessDynamicFork(p,np)) != kOkXsRC )
  1686. return rc;
  1687. }
  1688. }
  1689. }
  1690. return rc;
  1691. }
  1692. //-------------------------------------------------------------------------------------------
  1693. typedef struct _cmXsReorder_str
  1694. {
  1695. unsigned idx; // Fields from the reordering input file which are
  1696. unsigned voice; // used to match the reorder record to
  1697. unsigned locIdx; // the associated a cmXsNode_t record.
  1698. unsigned tick; //
  1699. unsigned durtn; //
  1700. float rval; //
  1701. unsigned midi; //
  1702. cmXsNote_t* note; // The cmXsNote_t* associated with this cmXsReorder_t record
  1703. unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
  1704. unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
  1705. unsigned newTick; // 0=ignore >0 new tick value
  1706. unsigned graceFlags; // 0=ignore See kXXXGraceXsFl
  1707. unsigned graceGroupId; // 0=ignore >0=grace note group id
  1708. unsigned pitch; // 0=ignore >0 new pitch
  1709. const char* editStr;
  1710. struct _cmXsReorder_str* link;
  1711. } cmXsReorder_t;
  1712. typedef struct _cmXsReorderMeas_str
  1713. {
  1714. unsigned measNumb;
  1715. cmXsReorder_t* beg;
  1716. cmXsReorder_t* end;
  1717. struct _cmXsReorderMeas_str* link;
  1718. } cmXsReorderMeas_t;
  1719. typedef struct
  1720. {
  1721. cmXsReorderMeas_t* beg;
  1722. cmXsReorderMeas_t* end;
  1723. } cmXsReorderFile_t;
  1724. typedef struct _cmXScoreDynMark_str
  1725. {
  1726. const cmChar_t* mark; //
  1727. unsigned id; // (1-17) maps to velocity
  1728. unsigned dyn; // pppp - fff (1-9) as used by cmScore
  1729. int adjust; // {-1,0,+1}
  1730. unsigned vel; // associated MIDI velocity
  1731. } _cmXScoreDynMark_t;
  1732. _cmXScoreDynMark_t _cmXScoreDynMarkArray[] =
  1733. {
  1734. {"s", 1, 0, 0, 1}, // silent note
  1735. {"pppp-", 2, 1, -1, 3},
  1736. {"pppp", 3, 1, 0, 10},
  1737. {"pppp+", 4, 1, 1, 22},
  1738. {"ppp-", 4, 2, -1, 22},
  1739. {"ppp", 5, 2, 0, 29},
  1740. {"ppp+", 6, 2, 1, 36},
  1741. {"pp-", 6, 3, -1, 36},
  1742. {"pp", 7, 3, 0, 43},
  1743. {"pp+", 8, 3, 1, 50},
  1744. {"p-", 8, 4, -1, 50},
  1745. {"p", 9, 4, 0, 57},
  1746. {"p+", 10, 4, 1, 64},
  1747. {"mp-", 10, 5, -1, 64},
  1748. {"mp", 11, 5, 0, 71},
  1749. {"mp+", 12, 5, 1, 78},
  1750. {"mf-", 12, 6, -1, 78},
  1751. {"mf", 13, 6, 0, 85},
  1752. {"mf+", 14, 6, 1, 92},
  1753. {"f-", 14, 7, -1, 92},
  1754. {"f", 15, 7, 0, 99},
  1755. {"f+", 16, 7, 1, 106},
  1756. {"ff", 17, 8, 0, 113},
  1757. {"ff+", 18, 8, 1, 120},
  1758. {"fff", 19, 9, 0, 127},
  1759. {NULL,0,0,0,0}
  1760. };
  1761. cmXsReorderMeas_t* _cmXsReorderFileAllocMeas( cmXScore_t* p, cmXsReorderFile_t* rfp, unsigned measNumb )
  1762. {
  1763. cmXsReorderMeas_t* m = cmLhAllocZ(p->lhH,cmXsReorderMeas_t,1);
  1764. m->measNumb = measNumb;
  1765. if( rfp->end == NULL )
  1766. {
  1767. rfp->beg = m;
  1768. rfp->end = m;
  1769. }
  1770. else
  1771. {
  1772. rfp->end->link = m;
  1773. rfp->end = m;
  1774. }
  1775. return m;
  1776. }
  1777. cmXsReorderMeas_t* _cmXsReorderFileFindMeas( cmXsReorderFile_t* rfp, unsigned measNumb )
  1778. {
  1779. cmXsReorderMeas_t* m = rfp->beg;
  1780. for(; m!=NULL; m=m->link)
  1781. if( m->measNumb == measNumb )
  1782. return m;
  1783. return NULL;
  1784. }
  1785. cmXsReorder_t* _cmXsReorderMeasAllocEvent( cmXScore_t* p, cmXsReorderMeas_t* m )
  1786. {
  1787. cmXsReorder_t* r = cmLhAllocZ(p->lhH, cmXsReorder_t,1);
  1788. r->midi = kInvalidMidiPitch;
  1789. if( m->end == NULL )
  1790. {
  1791. m->beg = r;
  1792. m->end = r;
  1793. }
  1794. else
  1795. {
  1796. m->end->link = r;
  1797. m->end = r;
  1798. }
  1799. return r;
  1800. }
  1801. // find key in meas (m) by searching after event idx0.
  1802. cmXsReorder_t* _cmXsReorderFindEvent( cmXsReorderMeas_t* m, unsigned idx0, cmXsReorder_t* key )
  1803. {
  1804. cmXsReorder_t* r;
  1805. for(r=m->beg; r!=NULL; r=r->link)
  1806. if( r->idx >= idx0 && r->midi==key->midi && r->rval==key->rval && r->durtn==key->durtn )
  1807. return r;
  1808. return NULL;
  1809. }
  1810. cmXsReorder_t* _cmXsReorderMeasPop( cmXsReorderMeas_t* m )
  1811. {
  1812. cmXsReorder_t* r0 = NULL;
  1813. cmXsReorder_t* r = NULL;
  1814. for(r=m->beg; r!=NULL; r=r->link)
  1815. {
  1816. if( r == m->end )
  1817. break;
  1818. r0 = r;
  1819. }
  1820. if( r0 == NULL )
  1821. {
  1822. m->beg = NULL;
  1823. m->end = NULL;
  1824. }
  1825. else
  1826. {
  1827. m->end = r0;
  1828. m->end->link = NULL;
  1829. }
  1830. return r;
  1831. }
  1832. cmXsNote_t* _cmXsReorderFindNote( cmXScore_t* p, unsigned measNumb, const cmXsReorder_t* r, unsigned iii )
  1833. {
  1834. cmXsPart_t* pp = p->partL;
  1835. for(; pp!=NULL; pp=pp->link)
  1836. {
  1837. cmXsMeas_t* mp = pp->measL;
  1838. for(; mp!=NULL; mp=mp->link)
  1839. if( mp->number == measNumb)
  1840. {
  1841. cmXsNote_t* np = mp->noteL;
  1842. int index = 0;
  1843. for(; np!=NULL; np=np->slink,++index)
  1844. {
  1845. if( 0 /*mp->number==27*/ )
  1846. printf("voice: %i %i loc:%i %i tick:%i %i pitch:%i %i rval:%f %f idx:%i %i \n",
  1847. np->voice->id, r->voice,
  1848. np->locIdx , r->locIdx ,
  1849. np->tick , r->tick ,
  1850. np->pitch , r->midi ,
  1851. np->rvalue, r->rval,
  1852. index , r->idx);
  1853. if( np->voice->id == r->voice &&
  1854. np->locIdx == r->locIdx &&
  1855. np->tick == r->tick &&
  1856. //np->duration == r->durtn &&
  1857. np->rvalue == r->rval &&
  1858. np->pitch == r->midi &&
  1859. index == r->idx )
  1860. {
  1861. //printf("Found: %i \n", r->midi);
  1862. return np;
  1863. }
  1864. }
  1865. }
  1866. }
  1867. cmErrMsg(&p->err,kSyntaxErrorXsRC,"Reorder note not found meas:%i index:%i.",measNumb,iii);
  1868. return NULL;
  1869. }
  1870. void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned flags )
  1871. {
  1872. // Create a new score event record
  1873. cmXsNote_t* nn = cmLhAllocZ(p->lhH,cmXsNote_t,1);
  1874. nn->pitch = kInvalidMidiPitch;
  1875. nn->uid = p->nextUid++;
  1876. nn->voice = r->note->voice;
  1877. nn->meas = r->note->meas;
  1878. nn->flags = flags;
  1879. // Pedal down events occur after the event they are attached to
  1880. if( cmIsFlag(flags,kSostDnXsFl | kDampDnXsFl ) )
  1881. {
  1882. nn->tick = r->note->tick + 1;
  1883. _cmXScoreInsertNoteAfter(r->note,nn);
  1884. }
  1885. else
  1886. {
  1887. // Pedal up events occur before the event they are attached to
  1888. if( cmIsFlag(flags,kSostUpXsFl | kDampUpXsFl ) )
  1889. {
  1890. nn->tick = r->note->tick==0 ? 0 : r->note->tick - 1;
  1891. _cmXScoreInsertNoteBefore(r->note,nn);
  1892. }
  1893. else
  1894. { assert(0); }
  1895. }
  1896. }
  1897. cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, cmXsReorderMeas_t* m )
  1898. {
  1899. unsigned i;
  1900. cmXsReorder_t* r;
  1901. if( m->beg == NULL )
  1902. return kOkXsRC;
  1903. // set the 'note' field on each cmXsReorder_t record
  1904. for(r=m->beg,i=0; r!=NULL; r=r->link,++i)
  1905. if((r->note = _cmXsReorderFindNote(p,m->measNumb,r,i)) == NULL )
  1906. return kSyntaxErrorXsRC;
  1907. // remove deleted notes
  1908. for(r=m->beg; r!=NULL; r=r->link)
  1909. if( cmIsFlag(r->newFlags,kDeleteXsFl) )
  1910. if( _cmXScoreRemoveNote( r->note ) != kOkXsRC )
  1911. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Event marked to skip was not found in measure: %i",m->measNumb);
  1912. cmXsMeas_t* mp = m->beg->note->meas;
  1913. cmXsNote_t* n0p = NULL;
  1914. assert( mp->number == m->measNumb );
  1915. // Reassign the slink of the cmXsNote_t records in this measure
  1916. // according to their order in rV[].
  1917. for(r=m->beg; r!=NULL; r=r->link)
  1918. {
  1919. if( cmIsFlag(r->newFlags,kDeleteXsFl) )
  1920. continue;
  1921. if( n0p == NULL )
  1922. mp->noteL = r->note;
  1923. else
  1924. n0p->slink = r->note;
  1925. // if a new tick was specified
  1926. if( r->newTick != 0 )
  1927. r->note->tick = r->newTick;
  1928. // if a dynamic or velocity mark was included
  1929. if( r->dynIdx != cmInvalidIdx )
  1930. {
  1931. r->note->dynamics = _cmXScoreDynMarkArray[ r->dynIdx ].dyn;
  1932. r->note->vel = _cmXScoreDynMarkArray[ r->dynIdx ].vel;
  1933. }
  1934. // Set the dynamic fork begin/end flags for later _cmXScoreProcessDynamicForks()
  1935. if( cmIsFlag(r->newFlags,kDynBegForkXsFl) )
  1936. r->note->flags = cmSetFlag(r->note->flags,kDynBegForkXsFl);
  1937. if( cmIsFlag(r->newFlags,kDynEndForkXsFl) )
  1938. r->note->flags = cmSetFlag(r->note->flags,kDynEndForkXsFl);
  1939. // if the tie end flag was set
  1940. if( cmIsFlag(r->newFlags,kTieEndXsFl) )
  1941. {
  1942. r->note->flags |= kTieEndXsFl;
  1943. r->note->flags = cmClrFlag( r->note->flags, kOnsetXsFl );
  1944. r->newFlags = cmClrFlag( r->newFlags, kTieEndXsFl);
  1945. }
  1946. // if a new note value was specified
  1947. if( r->pitch != 0 )
  1948. r->note->pitch = r->pitch;
  1949. r->note->flags |= r->graceFlags;
  1950. r->note->graceGroupId = r->graceGroupId;
  1951. n0p = r->note;
  1952. n0p->slink = NULL;
  1953. }
  1954. // Insert new note records for pedal up/dn events.
  1955. for(r=m->beg; r!=NULL; r=r->link)
  1956. {
  1957. if( r->newFlags != 0 )
  1958. {
  1959. if( cmIsFlag(r->newFlags,kDampDnXsFl ) )
  1960. _cmXScoreInsertPedalEvent(p,r,kDampDnXsFl);
  1961. if( cmIsFlag(r->newFlags,kSostDnXsFl ) )
  1962. _cmXScoreInsertPedalEvent(p,r,kSostDnXsFl);
  1963. if( cmIsFlag(r->newFlags,kDampUpXsFl ) )
  1964. _cmXScoreInsertPedalEvent(p,r,kDampUpXsFl);
  1965. if( cmIsFlag(r->newFlags,kSostUpXsFl ) )
  1966. _cmXScoreInsertPedalEvent(p,r,kSostUpXsFl);
  1967. }
  1968. }
  1969. return kOkXsRC;
  1970. }
  1971. cmXsRC_t _cmXScoreReorderMeas0( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN )
  1972. {
  1973. unsigned i;
  1974. if( rN == 0 )
  1975. return kOkXsRC;
  1976. // set the 'note' field on each cmXsReorder_t record
  1977. for(i=0; i<rN; ++i)
  1978. if((rV[i].note = _cmXsReorderFindNote(p,measNumb,rV+i,i)) == NULL )
  1979. return kSyntaxErrorXsRC;
  1980. // remove deleted notes
  1981. for(i=0; i<rN; ++i)
  1982. if( cmIsFlag(rV[i].newFlags,kDeleteXsFl) )
  1983. if( _cmXScoreRemoveNote( rV[i].note ) != kOkXsRC )
  1984. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Event marked to skip was not found in measure: %i",measNumb);
  1985. cmXsMeas_t* mp = rV[0].note->meas;
  1986. cmXsNote_t* n0p = NULL;
  1987. assert( mp->number == measNumb );
  1988. // Reassign the slink of the cmXsNote_t records in this measure
  1989. // according to their order in rV[].
  1990. for(i=0; i<rN; ++i)
  1991. {
  1992. if( cmIsFlag(rV[i].newFlags,kDeleteXsFl) )
  1993. continue;
  1994. if( n0p == NULL )
  1995. mp->noteL = rV[i].note;
  1996. else
  1997. n0p->slink = rV[i].note;
  1998. // if a new tick was specified
  1999. if( rV[i].newTick != 0 )
  2000. rV[i].note->tick = rV[i].newTick;
  2001. // if a dynamic or velocity mark was included
  2002. if( rV[i].dynIdx != cmInvalidIdx )
  2003. {
  2004. rV[i].note->dynamics = _cmXScoreDynMarkArray[ rV[i].dynIdx ].dyn;
  2005. rV[i].note->vel = _cmXScoreDynMarkArray[ rV[i].dynIdx ].vel;
  2006. }
  2007. // Set the dynamic fork begin/end flags for later _cmXScoreProcessDynamicForks()
  2008. if( cmIsFlag(rV[i].newFlags,kDynBegForkXsFl) )
  2009. rV[i].note->flags = cmSetFlag(rV[i].note->flags,kDynBegForkXsFl);
  2010. if( cmIsFlag(rV[i].newFlags,kDynEndForkXsFl) )
  2011. rV[i].note->flags = cmSetFlag(rV[i].note->flags,kDynEndForkXsFl);
  2012. // if the tie end flag was set
  2013. if( cmIsFlag(rV[i].newFlags,kTieEndXsFl) )
  2014. {
  2015. rV[i].note->flags |= kTieEndXsFl;
  2016. rV[i].note->flags = cmClrFlag( rV[i].note->flags, kOnsetXsFl );
  2017. rV[i].newFlags = cmClrFlag( rV[i].newFlags, kTieEndXsFl);
  2018. }
  2019. // if a new note value was specified
  2020. if( rV[i].pitch != 0 )
  2021. rV[i].note->pitch = rV[i].pitch;
  2022. rV[i].note->flags |= rV[i].graceFlags;
  2023. rV[i].note->graceGroupId = rV[i].graceGroupId;
  2024. n0p = rV[i].note;
  2025. n0p->slink = NULL;
  2026. }
  2027. // Insert new note records for pedal up/dn events.
  2028. for(i=0; i<rN; ++i)
  2029. {
  2030. if( rV[i].newFlags != 0 )
  2031. {
  2032. if( cmIsFlag(rV[i].newFlags,kDampDnXsFl ) )
  2033. _cmXScoreInsertPedalEvent(p,rV + i,kDampDnXsFl);
  2034. if( cmIsFlag(rV[i].newFlags,kSostDnXsFl ) )
  2035. _cmXScoreInsertPedalEvent(p,rV + i,kSostDnXsFl);
  2036. if( cmIsFlag(rV[i].newFlags,kDampUpXsFl ) )
  2037. _cmXScoreInsertPedalEvent(p,rV + i,kDampUpXsFl);
  2038. if( cmIsFlag(rV[i].newFlags,kSostUpXsFl ) )
  2039. _cmXScoreInsertPedalEvent(p,rV + i,kSostUpXsFl);
  2040. }
  2041. }
  2042. return kOkXsRC;
  2043. }
  2044. cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, char** s0, unsigned* dynIdxRef, unsigned* flagsRef, int measNumb )
  2045. {
  2046. cmXsRC_t rc = kOkXsRC;
  2047. cmChar_t* s = NULL;
  2048. bool begForkFl = false;
  2049. bool endForkFl = false;
  2050. *dynIdxRef = cmInvalidIdx;
  2051. // locate the '!' which indicates the start of a dynamic marking
  2052. if( (s = strchr(b,'!')) == NULL )
  2053. return rc;
  2054. if( *s0==NULL || s<*s0 )
  2055. *s0 = s;
  2056. ++s; // increment past the '!'
  2057. if( *s == 0 )
  2058. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected end-of-line on dynamics parsing on line:%i.",lineNumb);
  2059. // some dynamic markings are surrounded by parenthesis (to indicate a dynamic level with greater uncertainty)
  2060. if( *s == '(' )
  2061. ++s; // skip the paren.
  2062. if( *s == '!')
  2063. {
  2064. //printf("E %3i %5i %s\n",measNumb,lineNumb,b);
  2065. endForkFl = true;
  2066. ++s;
  2067. }
  2068. if( *s == 0 )
  2069. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected end-of-line on dynamics parsing on line:%i.",lineNumb);
  2070. // check for beg/end fork dynamic
  2071. if( isupper(*s) )
  2072. {
  2073. if( !endForkFl)
  2074. {
  2075. begForkFl=true;
  2076. //printf("B %3i %5i %s\n",measNumb,lineNumb,b);
  2077. }
  2078. }
  2079. else
  2080. {
  2081. if( endForkFl )
  2082. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"A double exclaimation point (end-of-dynamic fork) must always be followed by an upper case M,P, or F. See line:%i",lineNumb);
  2083. }
  2084. unsigned i = 0;
  2085. unsigned j = 0;
  2086. unsigned n = 6;
  2087. bool doneFl = false;
  2088. cmChar_t mark[n+1];
  2089. memset(mark,0,n+1);
  2090. for(i=0; j<n && doneFl==false; ++i)
  2091. {
  2092. char c = tolower(s[i]);
  2093. switch(c)
  2094. {
  2095. case 's':
  2096. case 'm':
  2097. case 'p':
  2098. case 'f':
  2099. case '+':
  2100. case '-':
  2101. mark[j++] = c;
  2102. break;
  2103. case ')': // ending paren.
  2104. case 0: // end of string
  2105. case ' ': // end of mark
  2106. case '\n': // end of line
  2107. default: // anything else
  2108. doneFl = true;
  2109. break;
  2110. }
  2111. }
  2112. if( !doneFl )
  2113. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal dynamic mark (%s) syntax on line:%i.",mark,lineNumb);
  2114. // look up the dynamic mark in the reference table
  2115. for(j=0; _cmXScoreDynMarkArray[j].mark!=NULL; ++j)
  2116. if( strcmp(mark,_cmXScoreDynMarkArray[j].mark) == 0 )
  2117. break;
  2118. if( _cmXScoreDynMarkArray[j].mark == NULL )
  2119. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"The dynamic mark '%s' is not legal on line:%i.",mark,lineNumb);
  2120. *dynIdxRef = j;
  2121. if( begForkFl )
  2122. *flagsRef = cmSetFlag(*flagsRef,kDynBegForkXsFl);
  2123. if( endForkFl )
  2124. *flagsRef = cmSetFlag(*flagsRef,kDynEndForkXsFl);
  2125. return rc;
  2126. }
  2127. cmXsRC_t _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* newFlagsRef )
  2128. {
  2129. cmXsRC_t rc = kOkXsRC;
  2130. cmChar_t* s;
  2131. bool doneFl = false;
  2132. unsigned i = 0;
  2133. *newFlagsRef = 0;
  2134. // tilde indicates a pedal event
  2135. if((s = strchr(b,'~')) == NULL )
  2136. return rc;
  2137. if( *s0==NULL || s<*s0)
  2138. *s0 = s;
  2139. do
  2140. {
  2141. ++s;
  2142. switch( *s )
  2143. {
  2144. case 'd':
  2145. *newFlagsRef |= kSostDnXsFl; // sostenuto pedal down just after this note onset
  2146. break;
  2147. case 'u':
  2148. *newFlagsRef |= kSostUpXsFl; // sostenuto pedal up just before this event
  2149. break;
  2150. case 'x':
  2151. *newFlagsRef |= (kSostUpXsFl | kSostDnXsFl); // sostenuto pedal up just before this event and sost down just after it.
  2152. break;
  2153. case 'D':
  2154. *newFlagsRef |= kDampDnXsFl; // damper pedal down
  2155. break;
  2156. case 'U':
  2157. *newFlagsRef |= kDampUpXsFl; // damper pedal up
  2158. break;
  2159. case '_':
  2160. *newFlagsRef |= kTieEndXsFl; // set tie end flag
  2161. break;
  2162. case '&':
  2163. *newFlagsRef |= kDeleteXsFl; // delete this evetn
  2164. break;
  2165. default:
  2166. if( i == 0 )
  2167. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected flag marking '%c' on line %i.",*s,line);
  2168. doneFl = true;
  2169. }
  2170. ++i;
  2171. }while(!doneFl);
  2172. return rc;
  2173. }
  2174. cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* tickRef )
  2175. {
  2176. cmXsRC_t rc = kOkXsRC;
  2177. cmChar_t* s;
  2178. if((s = strchr(b,'@')) == NULL )
  2179. return rc;
  2180. if( *s0 == NULL || s<*s0 )
  2181. *s0 = s;
  2182. ++s;
  2183. if(!isdigit(*s))
  2184. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected tick reorder value '%c' on line %i.",*s,line);
  2185. if(sscanf(s,"%i",tickRef) != 1 )
  2186. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"tick reorder parse failed on line %i.",line);
  2187. return rc;
  2188. }
  2189. cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, cmXsReorder_t* r, unsigned* graceGroupIdRef )
  2190. {
  2191. cmXsRC_t rc = kOkXsRC;
  2192. cmChar_t* s;
  2193. if((s = strchr(b,'%')) == NULL )
  2194. return rc;
  2195. if( *s0==NULL || s<*s0 )
  2196. *s0 = s;
  2197. ++s;
  2198. r->graceGroupId = *graceGroupIdRef;
  2199. while(1)
  2200. {
  2201. switch(*s)
  2202. {
  2203. case 'b': r->graceFlags |= kBegGraceXsFl; break;
  2204. case 'a': r->graceFlags |= kAddGraceXsFl | kEndGraceXsFl; break;
  2205. case 's': r->graceFlags |= kSubGraceXsFl | kEndGraceXsFl; break;
  2206. case 'A': r->graceFlags |= kAFirstGraceXsFl| kEndGraceXsFl; break;
  2207. case 'n': r->graceFlags |= kNFirstGraceXsFl| kEndGraceXsFl; break;
  2208. case 'g': break;
  2209. case '1':
  2210. r->graceGroupId += 1;
  2211. ++s;
  2212. continue;
  2213. case '%':
  2214. *graceGroupIdRef += 1;
  2215. ++s;
  2216. continue;
  2217. default:
  2218. {
  2219. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Unexpected grace note reorder character code %c on line %i.",*s,line);
  2220. assert(0);
  2221. }
  2222. }
  2223. break;
  2224. }
  2225. return rc;
  2226. }
  2227. cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* pitchRef )
  2228. {
  2229. cmXsRC_t rc = kOkXsRC;
  2230. cmChar_t* s;
  2231. cmChar_t buf[4];
  2232. unsigned i,j;
  2233. memset(buf,0,sizeof(buf));
  2234. *pitchRef = 0;
  2235. if((s = strchr(b,'$')) == NULL )
  2236. return rc;
  2237. if( *s0==NULL || s<*s0)
  2238. *s0 = s;
  2239. ++s;
  2240. j=2;
  2241. for(i=0; i<j && *s; ++i,++s)
  2242. {
  2243. buf[i] = *s;
  2244. if( i==1 && (*s=='#' || *s=='b') )
  2245. j = 3;
  2246. if( i==0 && strchr("ABCDEFG",*s)==NULL )
  2247. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch letter ('%c')specification line %i.",*s,line);
  2248. if( i==1 && !isdigit(*s) && *s!='#' && *s!='b' )
  2249. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch level ('%c') specification line %i.",*s,line);
  2250. if( i==2 && !isdigit(*s) )
  2251. return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Illegal pitch octave ('%c') specification line %i.",*s,line);
  2252. }
  2253. unsigned pitch = cmSciPitchToMidi(buf);
  2254. if( pitch<kInvalidMidiByte)
  2255. *pitchRef = pitch;
  2256. else
  2257. {
  2258. rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"Pitch conversion from '%s' failed on line %i.",buf,line);
  2259. }
  2260. return rc;
  2261. }
  2262. cmXsRC_t _cmXsReadEditFile( cmXScore_t* p, const cmChar_t* fn, unsigned* graceGroupIdPtr, cmXsReorderFile_t* rfp )
  2263. {
  2264. typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
  2265. cmXsRC_t rc = kOkXsRC;
  2266. cmFileH_t fH = cmFileNullHandle;
  2267. cmChar_t* b = NULL;
  2268. unsigned bN = 0;
  2269. unsigned ln = 0;
  2270. stateId_t stateId = kFindMeasStId;
  2271. cmXsReorderMeas_t* curMeas = NULL;
  2272. *graceGroupIdPtr = 1;
  2273. if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
  2274. {
  2275. rc = cmErrMsg(&p->err,kFileFailXsRC,"The reordering file '%s' could not be opened.",cmStringNullGuard(fn));
  2276. return rc;
  2277. }
  2278. for(; cmFileGetLineAuto(fH,&b,&bN) == kOkFileRC; ++ln)
  2279. {
  2280. switch( stateId )
  2281. {
  2282. case kFindEventStId: // scanning past labels to an event line
  2283. {
  2284. unsigned voice,loc;
  2285. if( sscanf(b,"%i %i",&voice,&loc) != 2 )
  2286. continue;
  2287. stateId = kReadEventStId;
  2288. }
  2289. // fall through
  2290. case kReadEventStId:
  2291. {
  2292. cmXsReorder_t* r = _cmXsReorderMeasAllocEvent(p, curMeas );
  2293. char pitchStr[4];
  2294. char* s0 = NULL;
  2295. // parse an event line
  2296. if( sscanf(b,"%i %i %i %i %i %f",&r->idx,&r->voice,&r->locIdx,&r->tick,&r->durtn,&r->rval) == 6 )
  2297. {
  2298. assert( strlen(b)>=52);
  2299. int PC = 39; // text file column where first pitch char occurs
  2300. if( b[PC] == ' ')
  2301. r->midi = kInvalidMidiPitch;
  2302. else
  2303. {
  2304. pitchStr[0] = b[PC+0];
  2305. pitchStr[1] = b[PC+1];
  2306. pitchStr[2] = b[PC+2];
  2307. pitchStr[3] = 0;
  2308. if( !isdigit(pitchStr[2]) )
  2309. r->midi = kInvalidMidiPitch;
  2310. else
  2311. {
  2312. if( pitchStr[1] == ' ')
  2313. {
  2314. pitchStr[1] = pitchStr[2];
  2315. pitchStr[2] = 0;
  2316. }
  2317. r->midi = cmSciPitchToMidi(pitchStr);
  2318. //printf("%i %i %s %s\n",curMeas->measNumb,r->midi,pitchStr,fn);
  2319. }
  2320. }
  2321. // parse the flag edits following a '~'
  2322. if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r->newFlags)) != kOkXsRC )
  2323. goto errLabel;
  2324. // parse the dynamic marking following a '!'
  2325. if((rc = _cmXScoreReorderParseDyn(p,b,ln+1, &s0, &r->dynIdx, &r->newFlags, curMeas->measNumb)) != kOkXsRC )
  2326. goto errLabel;
  2327. // parse the @newtick marker
  2328. if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r->newTick)) != kOkXsRC )
  2329. goto errLabel;
  2330. // parse the %grace note marker
  2331. if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, r, graceGroupIdPtr)) != kOkXsRC )
  2332. goto errLabel;
  2333. // parse the $pitch marker
  2334. if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r->pitch )) != kOkXsRC )
  2335. goto errLabel;
  2336. if( s0 != NULL )
  2337. r->editStr = cmTextTrimEnd(cmLhAllocStrN( p->lhH, s0, strlen(s0)+1 ));
  2338. continue;
  2339. }
  2340. // remove the last reorder record because it was not filled
  2341. _cmXsReorderMeasPop(curMeas);
  2342. stateId = kFindMeasStId;
  2343. // fall through
  2344. }
  2345. case kFindMeasStId: // scanning for a bar-line
  2346. {
  2347. char colon;
  2348. unsigned measNumb = 0;
  2349. if( sscanf(b,"%i %c",&measNumb,&colon) == 2 && colon == ':' )
  2350. {
  2351. curMeas = _cmXsReorderFileAllocMeas( p, rfp, measNumb );
  2352. stateId = kFindEventStId;
  2353. }
  2354. }
  2355. break;
  2356. }
  2357. }
  2358. errLabel:
  2359. cmMemFree(b);
  2360. cmFileClose(&fH);
  2361. return rc;
  2362. }
  2363. cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn )
  2364. {
  2365. cmXsRC_t rc = kOkXsRC;
  2366. unsigned graceGroupId = 1;
  2367. cmXsReorderFile_t rf;
  2368. cmXsReorderMeas_t* m;
  2369. memset(&rf,0,sizeof(rf));
  2370. if((rc = _cmXsReadEditFile( p, fn, &graceGroupId, &rf )) != kOkXsRC )
  2371. return rc;
  2372. // reorder each measure
  2373. for(m=rf.beg; m!=NULL; m=m->link)
  2374. if((rc = _cmXScoreReorderMeas(p, m)) != kOkXsRC )
  2375. goto errLabel;
  2376. // the ticks may have changed so the 'secs' and 'dsecs' must be updated
  2377. _cmXScoreSetAbsoluteTime( p );
  2378. // the bar lines should be the first event in the measure
  2379. _cmXScoreFixBarLines(p);
  2380. // resort to force the links to be correct
  2381. _cmXScoreSort(p);
  2382. // process the grace notes.
  2383. _cmXScoreProcessGraceNotes( p, graceGroupId );
  2384. // inserting grace notes may have left the score unsorted
  2385. _cmXScoreSort(p);
  2386. // process the dynamic forks
  2387. _cmXScoreProcessDynamicForks(p);
  2388. //_cmXScoreReport(p, NULL, true );
  2389. errLabel:
  2390. return rc;
  2391. }
  2392. cmXsRC_t _cmXsMergeEditFiles( cmXScore_t* p, unsigned measNumb0, const cmChar_t* keyEditFn, unsigned keyMeasNumb, const cmChar_t* outFn )
  2393. {
  2394. cmXsRC_t rc = kOkXsRC;
  2395. unsigned graceGroup1Id = 1;
  2396. unsigned measNumb1 = keyMeasNumb;
  2397. cmXsReorderFile_t rf1;
  2398. memset(&rf1,0,sizeof(rf1));
  2399. if((rc = _cmXsReadEditFile( p, keyEditFn, &graceGroup1Id, &rf1 )) != kOkXsRC )
  2400. return rc;
  2401. while(1)
  2402. {
  2403. cmXsMeas_t* m0 = _cmXsFindMeas( p->partL, measNumb0 );
  2404. cmXsReorderMeas_t* m1 = _cmXsReorderFileFindMeas( &rf1, measNumb1 );
  2405. cmXsReorder_t* key = NULL;
  2406. unsigned idx0 = 0;
  2407. if( m1==NULL )
  2408. {
  2409. rc = cmErrMsg(&p->err,kEventNotFoundXsRC,"The measure %i was not found in the key edit file '%s'.",keyMeasNumb,cmStringNullGuard(keyEditFn));
  2410. break;
  2411. }
  2412. key = m1->beg;
  2413. for(; key!=NULL; key=key->link)
  2414. {
  2415. unsigned idx1 = cmInvalidIdx;
  2416. cmXsNote_t* np = _cmXsFindNote( m0, idx0, key->midi, key->rval, key->durtn, &idx1);
  2417. if( np==NULL )
  2418. {
  2419. if( key->editStr != NULL )
  2420. {
  2421. const char* sciPitch = key->midi!=kInvalidMidiPitch ? cmMidiToSciPitch(key->midi,NULL,0) : "<non-pitch>";
  2422. cmErrWarnMsg(&p->err,kEventNotFoundXsRC,"Sync error: meas: ref:%i key:%i index:%i %s (midi:%i) edit:%s did not match to the reference edit file.", measNumb0,m1->measNumb,key->idx, sciPitch, key->midi, key->editStr);
  2423. }
  2424. }
  2425. else
  2426. {
  2427. np->editStr = key->editStr;
  2428. if( key->editStr != NULL )
  2429. idx0 = idx1;
  2430. }
  2431. }
  2432. ++measNumb0;
  2433. ++measNumb1;
  2434. }
  2435. return rc;
  2436. }
  2437. cmXsRC_t _cmXsApplyEditFile0( cmXScore_t* p, const cmChar_t* fn )
  2438. {
  2439. typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
  2440. cmXsRC_t rc = kOkXsRC;
  2441. cmFileH_t fH = cmFileNullHandle;
  2442. cmChar_t* b = NULL;
  2443. unsigned bN = 0;
  2444. unsigned ln = 0;
  2445. stateId_t stateId = kFindMeasStId;
  2446. unsigned rN = 1024;
  2447. unsigned ri = 0;
  2448. unsigned measNumb = 0;
  2449. unsigned graceGroupId = 1;
  2450. cmXsReorder_t rV[ rN ];
  2451. if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
  2452. {
  2453. rc = cmErrMsg(&p->err,kFileFailXsRC,"The reordering file '%s' could not be opened.",cmStringNullGuard(fn));
  2454. return rc;
  2455. }
  2456. for(; cmFileGetLineAuto(fH,&b,&bN)==kOkFileRC; ++ln)
  2457. {
  2458. switch( stateId )
  2459. {
  2460. case kFindEventStId: // scanning past labels to an event line
  2461. {
  2462. unsigned voice,loc;
  2463. if( sscanf(b,"%i %i",&voice,&loc) != 2 )
  2464. continue;
  2465. stateId = kReadEventStId;
  2466. }
  2467. // fall through
  2468. case kReadEventStId:
  2469. {
  2470. cmXsReorder_t r;
  2471. char pitchStr[4];
  2472. char* s0 = NULL;
  2473. memset(&r,0,sizeof(r));
  2474. // parse an event line
  2475. if( sscanf(b,"%i %i %i %i %i %f",&r.idx,&r.voice,&r.locIdx,&r.tick,&r.durtn,&r.rval) == 6 )
  2476. {
  2477. assert( strlen(b)>=52);
  2478. int PC = 39; // text file column where first pitch char occurs
  2479. if( b[PC] == ' ')
  2480. r.midi = kInvalidMidiPitch;
  2481. else
  2482. {
  2483. pitchStr[0] = b[PC+0];
  2484. pitchStr[1] = b[PC+1];
  2485. pitchStr[2] = b[PC+2];
  2486. pitchStr[3] = 0;
  2487. if( !isdigit(pitchStr[2]) )
  2488. r.midi = kInvalidMidiPitch;
  2489. else
  2490. {
  2491. if( pitchStr[1] == ' ')
  2492. {
  2493. pitchStr[1] = pitchStr[2];
  2494. pitchStr[2] = 0;
  2495. }
  2496. r.midi = cmSciPitchToMidi(pitchStr);
  2497. }
  2498. }
  2499. // parse the flag edits following a '~'
  2500. if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r.newFlags)) != kOkXsRC )
  2501. goto errLabel;
  2502. // parse the dynamic marking following a '!'
  2503. if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&s0, &r.dynIdx, &r.newFlags, measNumb)) != kOkXsRC )
  2504. goto errLabel;
  2505. // parse the @newtick marker
  2506. if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r.newTick)) != kOkXsRC )
  2507. goto errLabel;
  2508. // parse the %grace note marker
  2509. if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, &r, &graceGroupId)) != kOkXsRC )
  2510. goto errLabel;
  2511. // parse the $pitch marker
  2512. if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r.pitch )) != kOkXsRC )
  2513. goto errLabel;
  2514. // store the record
  2515. assert( ri < rN );
  2516. rV[ri++] = r;
  2517. continue;
  2518. }
  2519. // the end of the measure was encountered -
  2520. // reorder the measure based on the cmXsReorder_t in rV[ri]
  2521. if((rc = _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC )
  2522. goto errLabel;
  2523. ri = 0;
  2524. stateId = kFindMeasStId;
  2525. // fall through
  2526. }
  2527. case kFindMeasStId: // scanning for a bar-line
  2528. {
  2529. char colon;
  2530. if( sscanf(b,"%i %c",&measNumb,&colon) == 2 && colon == ':' )
  2531. {
  2532. //printf("meas: %i \n",measNumb);
  2533. stateId = kFindEventStId;
  2534. }
  2535. }
  2536. break;
  2537. }
  2538. }
  2539. // If reorder records remain to be processed
  2540. if( ri > 0 )
  2541. if((rc = _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC )
  2542. goto errLabel;
  2543. // the ticks may have changed so the 'secs' and 'dsecs' must be updated
  2544. _cmXScoreSetAbsoluteTime( p );
  2545. // the bar lines should be the first event in the measure
  2546. _cmXScoreFixBarLines(p);
  2547. // resort to force the links to be correct
  2548. _cmXScoreSort(p);
  2549. // process the grace notes.
  2550. _cmXScoreProcessGraceNotes( p, graceGroupId );
  2551. // inserting grace notes may have left the score unsorted
  2552. _cmXScoreSort(p);
  2553. // process the dynamic forks
  2554. _cmXScoreProcessDynamicForks(p);
  2555. //_cmXScoreReport(p, NULL, true );
  2556. errLabel:
  2557. cmFileClose(&fH);
  2558. cmMemFree(b);
  2559. return rc;
  2560. }
  2561. cmXsRC_t cmXScoreAlloc( cmCtx_t* ctx, cmXsH_t* hp )
  2562. {
  2563. cmXsRC_t rc = kOkXsRC;
  2564. if((rc = cmXScoreFinalize(hp)) != kOkXsRC )
  2565. return rc;
  2566. cmXScore_t* p = cmMemAllocZ(cmXScore_t,1);
  2567. cmErrSetup(&p->err,&ctx->rpt,"XScore");
  2568. // create a local linked heap
  2569. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  2570. return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed.");
  2571. hp->h = p;
  2572. return rc;
  2573. }
  2574. cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl )
  2575. {
  2576. cmXsRC_t rc;
  2577. cmXScore_t* p = NULL;
  2578. if((rc = cmXScoreAlloc(ctx,hp)) != kOkXsRC )
  2579. goto errLabel;
  2580. p = _cmXScoreHandleToPtr(*hp);
  2581. // open the music xml file
  2582. if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC )
  2583. {
  2584. rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn));
  2585. goto errLabel;
  2586. }
  2587. //cmXmlPrint(p->xmlH,&ctx->rpt);
  2588. // parse the part-list
  2589. if((rc = _cmXScoreParsePartList( p )) != kOkXsRC )
  2590. goto errLabel;
  2591. // parse each score 'part'
  2592. cmXsPart_t* pp = p->partL;
  2593. for(; pp!=NULL; pp=pp->link)
  2594. if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC )
  2595. goto errLabel;
  2596. // fill in the note->slink chain to link the notes in each measure in time order
  2597. _cmXScoreSort(p);
  2598. // kpl: 4/19/17 fix problem where notes ended up out of order by one tick _cmXScoreSpreadGraceNotes(p);
  2599. _cmXScoreSort(p);
  2600. _cmXScoreResolveTiesAndLoc(p);
  2601. _cmXScoreRemoveDuplicateNotes(p);
  2602. _cmXScoreSetMeasGroups(p,kEvenXsFl);
  2603. _cmXScoreSetMeasGroups(p,kDynXsFl);
  2604. _cmXScoreSetMeasGroups(p,kTempoXsFl);
  2605. //_cmXScoreResolveOctaveShift(p);
  2606. // CSV output initialize failed.
  2607. if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC )
  2608. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed.");
  2609. if( editFn != NULL )
  2610. {
  2611. if((rc = _cmXsApplyEditFile(p,editFn)) != kOkXsRC )
  2612. {
  2613. cmErrMsg(&ctx->err,rc,"XScore reorder failed.");
  2614. goto errLabel;
  2615. }
  2616. }
  2617. // assign durations to pedal down events
  2618. _cmXScoreProcessPedals(p,damperRptFl);
  2619. // remove some notes which share a pitch and are overlapped or embedded within another note.
  2620. _cmXScoreProcessOverlappingNotes(p);
  2621. errLabel:
  2622. if( rc != kOkXsRC )
  2623. cmXScoreFinalize(hp);
  2624. return rc;
  2625. }
  2626. cmXsRC_t cmXScoreFinalize( cmXsH_t* hp )
  2627. {
  2628. cmXsRC_t rc = kOkXsRC;
  2629. if( hp == NULL || cmXScoreIsValid(*hp)==false )
  2630. return kOkXsRC;
  2631. cmXScore_t* p = _cmXScoreHandleToPtr(*hp);
  2632. if((rc = _cmXScoreFinalize(p)) != kOkXsRC )
  2633. return rc;
  2634. hp->h = NULL;
  2635. return rc;
  2636. }
  2637. bool cmXScoreIsValid( cmXsH_t h )
  2638. { return h.h != NULL; }
  2639. /* CSV score columns
  2640. kMidiFileIdColScIdx= 0,
  2641. kTypeLabelColScIdx = 3,
  2642. kDSecsColScIdx = 4,
  2643. kSecsColScIdx = 5,
  2644. kD0ColScIdx = 9,
  2645. kD1ColScIdx = 10,
  2646. kPitchColScIdx = 11,
  2647. kBarColScIdx = 13,
  2648. kSkipColScIdx = 14,
  2649. kEvenColScIdx = 15,
  2650. kGraceColScIdx = 16,
  2651. kTempoColScIdx = 17,
  2652. kFracColScIdx = 18,
  2653. kDynColScIdx = 19,
  2654. kSectionColScIdx = 20,
  2655. kRecdPlayColScIdx = 21,
  2656. kRemarkColScIdx = 22
  2657. */
  2658. cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p )
  2659. {
  2660. const cmChar_t* s[] =
  2661. {
  2662. "id","trk","evt","opcode","dticks","micros","status",
  2663. "meta","ch","d0","d1","arg0","arg1","bar","skip",
  2664. "even","grace","tempo","t_frac","dyn","section","play_recd","remark",NULL
  2665. };
  2666. cmCsvCell_t* lcp = NULL;
  2667. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymText(p->csvH,s[0]), 0, 0 ) != kOkCsvRC )
  2668. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  2669. unsigned i;
  2670. for(i=1; s[i]!=NULL; ++i)
  2671. {
  2672. if( cmCsvInsertIdentColAfter(p->csvH, lcp, &lcp, s[i], 0 ) != kOkCsvRC )
  2673. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV error inserting CSV title %i.\n",i);
  2674. }
  2675. return kOkXsRC;
  2676. }
  2677. cmXsRC_t _cmXScoreWriteCsvBlankCols( cmXScore_t* p, unsigned cnt, cmCsvCell_t** leftCellPtrPtr )
  2678. {
  2679. unsigned i;
  2680. for(i=0; i<cnt; ++i)
  2681. if( cmCsvInsertIdentColAfter(p->csvH,*leftCellPtrPtr,leftCellPtrPtr,"",0) != kOkCsvRC )
  2682. return cmErrMsg(&p->err,kCsvFailXsRC,"CSV output failed on blank column.");
  2683. return kOkCsvRC;
  2684. }
  2685. const cmChar_t* _cmXScoreTranslateDynamics( cmXScore_t* p, const cmXsNote_t* np, cmChar_t* buf, unsigned bufN )
  2686. {
  2687. if( cmIsFlag(np->flags,kDynXsFl) && np->dynamics != 0 )
  2688. {
  2689. const cmChar_t* dynStr = NULL;
  2690. switch(np->dynamics)
  2691. {
  2692. case 1: dynStr = "pppp"; break;
  2693. case 2: dynStr = "ppp"; break;
  2694. case 3: dynStr = "pp"; break;
  2695. case 4: dynStr = "p"; break;
  2696. case 5: dynStr = "mp"; break;
  2697. case 6: dynStr = "mf"; break;
  2698. case 7: dynStr = "f"; break;
  2699. case 8: dynStr = "ff"; break;
  2700. case 9: dynStr = "fff"; break;
  2701. default:
  2702. cmErrMsg(&p->err,kSyntaxErrorXsRC,"An invalid dynamic value (%i) was encountered.",np->dynamics);
  2703. goto errLabel;
  2704. }
  2705. if( np->dynGroupId == 0 )
  2706. snprintf(buf,bufN,"%s",dynStr);
  2707. else
  2708. snprintf(buf,bufN,"%s %i",dynStr,np->dynGroupId);
  2709. return buf;
  2710. }
  2711. errLabel:
  2712. return "";
  2713. }
  2714. const cmChar_t* cmXsFormatMeasurementCsvField( unsigned flags, unsigned fl, char abbrev, unsigned sectionId, char* buf, unsigned bufN )
  2715. {
  2716. assert( bufN > 1 );
  2717. buf[0] = ' ';
  2718. buf[1] = 0;
  2719. if( cmIsFlag(flags,fl) )
  2720. {
  2721. if( sectionId != 0 )
  2722. snprintf(buf,bufN-1,"%c %i%c",abbrev,sectionId, cmIsFlag(flags,kHeelXsFl)?'*':' ');
  2723. else
  2724. buf[0] = abbrev;
  2725. }
  2726. return buf;
  2727. }
  2728. cmXsRC_t _cmXScoreWriteCsvRow(
  2729. cmXScore_t* p,
  2730. unsigned rowIdx,
  2731. unsigned uid,
  2732. unsigned bar,
  2733. const cmChar_t* sectionStr,
  2734. const cmChar_t* opCodeStr,
  2735. double dsecs,
  2736. double secs,
  2737. unsigned d0,
  2738. unsigned d1,
  2739. unsigned pitch, // set to -1 if the pitch is not valid
  2740. double frac,
  2741. const cmChar_t* dynStr,
  2742. unsigned flags,
  2743. const cmChar_t* evenStr,
  2744. const cmChar_t* tempoStr)
  2745. {
  2746. cmXsRC_t rc = kOkXsRC;
  2747. cmCsvCell_t* lcp = NULL;
  2748. // append an empty row to the CSV object
  2749. if( cmCsvAppendRow( p->csvH, &lcp, cmCsvInsertSymUInt(p->csvH, rowIdx ), 0, 0 ) != kOkCsvRC )
  2750. {
  2751. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV append row failed.");
  2752. goto errLabel;
  2753. }
  2754. /*
  2755. // col 0 : blanks
  2756. if( cmCsvInsertUIntColAfter(p->csvH, lcp, &lcp, rowIdx, 0 ) != kOkCsvRC )
  2757. {
  2758. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output row index failed.");
  2759. goto errLabel;
  2760. }
  2761. */
  2762. // col 1 : track (always 1)
  2763. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,1,0) != kOkCsvRC )
  2764. {
  2765. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  2766. goto errLabel;
  2767. }
  2768. // col 2 : evt (set to event uid, or blank if uid == -1)
  2769. if( uid == -1 )
  2770. {
  2771. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  2772. goto errLabel;
  2773. }
  2774. else
  2775. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,uid,0) != kOkCsvRC )
  2776. {
  2777. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  2778. goto errLabel;
  2779. }
  2780. // col 3 : output the opcode
  2781. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,opCodeStr,0) != kOkCsvRC )
  2782. {
  2783. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on opcode label.");
  2784. goto errLabel;
  2785. }
  2786. // col 4 : dsecs
  2787. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,dsecs,0) != kOkCsvRC )
  2788. {
  2789. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dsecs'.");
  2790. goto errLabel;
  2791. }
  2792. // col 5 : secs
  2793. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,secs,0) != kOkCsvRC )
  2794. {
  2795. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'secs'.");
  2796. goto errLabel;
  2797. }
  2798. // cols 6,7,8 blanks
  2799. if((rc = _cmXScoreWriteCsvBlankCols(p,3,&lcp)) != kOkXsRC )
  2800. goto errLabel;
  2801. // col 9 : d0
  2802. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d0,0) != kOkCsvRC )
  2803. {
  2804. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd0'.");
  2805. goto errLabel;
  2806. }
  2807. // col 10 : d1
  2808. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,d1,0) != kOkCsvRC )
  2809. {
  2810. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'd1'.");
  2811. goto errLabel;
  2812. }
  2813. // col 11 : pitch
  2814. if( pitch == -1 )
  2815. {
  2816. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  2817. goto errLabel;
  2818. }
  2819. else
  2820. {
  2821. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmMidiToSciPitch(pitch,NULL,0),0) != kOkCsvRC )
  2822. {
  2823. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  2824. goto errLabel;
  2825. }
  2826. }
  2827. // col 12 : blanks
  2828. if((rc = _cmXScoreWriteCsvBlankCols(p,1 + (cmIsFlag(flags,kBarXsFl) ? 0 : 1), &lcp)) != kOkXsRC )
  2829. goto errLabel;
  2830. // col 13 : bar number
  2831. if( cmIsFlag(flags,kBarXsFl) )
  2832. {
  2833. if( cmCsvInsertUIntColAfter(p->csvH,lcp,&lcp,bar,0) != kOkCsvRC )
  2834. {
  2835. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'pitch'.");
  2836. goto errLabel;
  2837. }
  2838. }
  2839. // col 14 : skip (blank for now)
  2840. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  2841. goto errLabel;
  2842. // col 15: even (all grace notes are 'even' notes
  2843. if( cmCsvInsertQTextColAfter(p->csvH,lcp,&lcp, evenStr, 0) != kOkCsvRC )
  2844. {
  2845. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  2846. goto errLabel;
  2847. }
  2848. // col 16: grace
  2849. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,cmIsFlag(flags,kGraceXsFl) ? "g" : "",0) != kOkCsvRC )
  2850. {
  2851. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  2852. goto errLabel;
  2853. }
  2854. // col 17: tempo
  2855. if( cmCsvInsertQTextColAfter(p->csvH,lcp,&lcp,tempoStr,0) != kOkCsvRC )
  2856. {
  2857. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  2858. goto errLabel;
  2859. }
  2860. // col 18: frac
  2861. if( frac == 0 )
  2862. {
  2863. if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  2864. goto errLabel;
  2865. }
  2866. else
  2867. {
  2868. if( cmCsvInsertDoubleColAfter(p->csvH,lcp,&lcp,1.0/frac,0) != kOkCsvRC )
  2869. {
  2870. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 't frac'.");
  2871. goto errLabel;
  2872. }
  2873. }
  2874. // col 19: dynamic marking
  2875. if( cmCsvInsertQTextColAfter(p->csvH, lcp, &lcp, dynStr, 0 ) != kOkCsvRC )
  2876. {
  2877. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on 'dynamics'.");
  2878. goto errLabel;
  2879. }
  2880. //if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
  2881. // goto errLabel;
  2882. // col 20: section
  2883. if( cmCsvInsertIdentColAfter(p->csvH,lcp,&lcp,sectionStr!=NULL ? sectionStr : "",0) != kOkCsvRC )
  2884. {
  2885. rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV insert failed on eveness flag label.");
  2886. goto errLabel;
  2887. }
  2888. // col 21, 22 : recd-play, remark (blank for now)
  2889. if((rc = _cmXScoreWriteCsvBlankCols(p,2,&lcp)) != kOkXsRC )
  2890. goto errLabel;
  2891. errLabel:
  2892. return rc;
  2893. }
  2894. cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, int begMeasNumb, const cmChar_t* csvFn )
  2895. {
  2896. cmXsRC_t rc = kOkXsRC;
  2897. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  2898. unsigned rowIdx = 1;
  2899. const cmChar_t* sectionIdStr = NULL;
  2900. double baseSecs = -1;
  2901. if( !cmCsvIsValid(p->csvH) )
  2902. return cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output object is not initialized.");
  2903. if((rc = _cmXScoreWriteCsvHdr( p )) != kOkXsRC )
  2904. goto errLabel;
  2905. cmXsPart_t* pp = p->partL;
  2906. for(; pp!=NULL; pp=pp->link)
  2907. {
  2908. cmXsMeas_t* mp = pp->measL;
  2909. for(; mp!=NULL; mp=mp->link)
  2910. {
  2911. if( mp->number < begMeasNumb)
  2912. continue;
  2913. cmXsNote_t* np = mp->noteL;
  2914. if( baseSecs == -1 )
  2915. baseSecs = np->secs;
  2916. for(; np!=NULL; np=np->slink)
  2917. {
  2918. double thisSecs = np->secs - baseSecs;
  2919. // if this is a section event
  2920. if( cmIsFlag(np->flags,kSectionXsFl) )
  2921. sectionIdStr = np->tvalue;
  2922. // if this is a bar event
  2923. if( cmIsFlag(np->flags,kBarXsFl) )
  2924. {
  2925. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"bar",np->dsecs,thisSecs,0,0,-1,0,"",np->flags,"","");
  2926. sectionIdStr = NULL;
  2927. }
  2928. else
  2929. {
  2930. // if this is a pedal event
  2931. if( cmIsFlag(np->flags,kDampDnXsFl|kDampUpXsFl|kDampUpDnXsFl|kSostDnXsFl|kSostUpXsFl) )
  2932. {
  2933. unsigned d0 = cmIsFlag(np->flags,kSostDnXsFl |kSostUpXsFl) ? 66 : 64; // pedal MIDI ctl id
  2934. unsigned d1 = cmIsFlag(np->flags,kDampDnXsFl|kSostDnXsFl) ? 64 : 0; // pedal-dn: d1>=64 pedal-up:<64
  2935. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"ctl",np->dsecs,thisSecs,d0,d1,-1,0,"",np->flags,"","");
  2936. sectionIdStr = NULL;
  2937. if( cmIsFlag(np->flags,kDampUpDnXsFl) )
  2938. {
  2939. rowIdx += 1;
  2940. double millisecond = 0.0;
  2941. _cmXScoreWriteCsvRow(p,rowIdx,-1,mp->number,sectionIdStr,"ctl",millisecond,thisSecs+millisecond,d0,64,-1,0,"",np->flags,"","");
  2942. }
  2943. }
  2944. else
  2945. {
  2946. // if this is a sounding note event
  2947. if( cmIsFlag(np->flags,kOnsetXsFl) )
  2948. {
  2949. unsigned bufN = 128;
  2950. cmChar_t ebuf[ bufN+1]; ebuf[bufN] = 0;
  2951. cmChar_t dbuf[ bufN+1]; dbuf[bufN] = 0;
  2952. cmChar_t tbuf[ bufN+1]; tbuf[bufN] = 0;
  2953. double frac = np->rvalue + (cmIsFlag(np->flags,kDotXsFl) ? (np->rvalue/2) : 0);
  2954. const cmChar_t* dyn = _cmXScoreTranslateDynamics( p, np, dbuf, bufN );
  2955. unsigned vel = np->vel==0 ? 60 : np->vel;
  2956. //
  2957. _cmXScoreWriteCsvRow(p,rowIdx,np->uid,mp->number,sectionIdStr,"non",np->dsecs,thisSecs,np->pitch,vel,np->pitch,frac,dyn,np->flags,
  2958. cmXsFormatMeasurementCsvField(np->flags, kEvenXsFl, 'e', np->evenGroupId, ebuf, bufN ),
  2959. cmXsFormatMeasurementCsvField(np->flags, kTempoXsFl,'t', np->tempoGroupId, tbuf, bufN ));
  2960. sectionIdStr = NULL;
  2961. }
  2962. }
  2963. }
  2964. rowIdx += 1;
  2965. }
  2966. }
  2967. }
  2968. // Section labels are output on the next bar/pedal/note
  2969. // but what if there is no bar pedal note-on after the section label
  2970. if( sectionIdStr != NULL )
  2971. cmErrMsg(&p->err,kSyntaxErrorXsRC,"The section label '%s' was ignored because it was not followed by a score event.",sectionIdStr);
  2972. if( cmCsvWrite( p->csvH, csvFn ) != kOkCsvRC )
  2973. rc = cmErrMsg(&p->err,kCsvFailXsRC,"The CSV output write failed on file '%s'.",csvFn);
  2974. errLabel:
  2975. return rc;
  2976. }
  2977. bool _cmXsIsCsvValid(cmCtx_t* ctx, cmXsH_t h, const cmChar_t* outFn)
  2978. {
  2979. bool retFl = true;
  2980. cmScH_t scH = cmScNullHandle;
  2981. double srate = 44100.0;
  2982. cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx );
  2983. if( cmScoreInitialize( ctx, &scH, outFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC )
  2984. {
  2985. cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file (%s) could not be parsed.",cmStringNullGuard(outFn));
  2986. retFl = false;
  2987. }
  2988. else
  2989. {
  2990. //cmScorePrintSets(scH,&ctx->rpt);
  2991. //cmScorePrint(scH,&ctx->rpt);
  2992. cmScoreFinalize(&scH);
  2993. }
  2994. cmSymTblDestroy(&stH);
  2995. return retFl;
  2996. }
  2997. void _cmXScoreReportTitle( cmRpt_t* rpt )
  2998. {
  2999. cmRptPrintf(rpt," idx voc loc tick durtn rval flags\n");
  3000. cmRptPrintf(rpt," --- --- ----- ------- ----- ---- --- ---------------\n");
  3001. }
  3002. void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
  3003. {
  3004. const cmChar_t* B = cmIsFlag(note->flags,kBarXsFl) ? "|" : "-";
  3005. const cmChar_t* R = cmIsFlag(note->flags,kRestXsFl) ? "R" : "-";
  3006. const cmChar_t* G = cmIsFlag(note->flags,kGraceXsFl) ? "G" : "-";
  3007. const cmChar_t* D = cmIsFlag(note->flags,kDotXsFl) ? "." : "-";
  3008. const cmChar_t* C = cmIsFlag(note->flags,kChordXsFl) ? "C" : "-";
  3009. const cmChar_t* P = cmIsFlag(note->flags,kDampDnXsFl) ? "V" : "-";
  3010. const cmChar_t* s = cmIsFlag(note->flags,kSostDnXsFl) ? "{" : "-";
  3011. const cmChar_t* S = cmIsFlag(note->flags,kSectionXsFl) ? "S" : "-";
  3012. const cmChar_t* H = cmIsFlag(note->flags,kHeelXsFl) ? "H" : "-";
  3013. const cmChar_t* T0 = cmIsFlag(note->flags,kTieBegXsFl) ? "T" : "-";
  3014. const cmChar_t* T1 = cmIsFlag(note->flags,kTieEndXsFl) ? "_" : "-";
  3015. const cmChar_t* O = cmIsFlag(note->flags,kOnsetXsFl) ? "*" : "-";
  3016. const cmChar_t* e = cmIsFlag(note->flags,kEvenXsFl) ? "e" : "-";
  3017. const cmChar_t* d = cmIsFlag(note->flags,kDynXsFl) ? "d" : "-";
  3018. const cmChar_t* t = cmIsFlag(note->flags,kTempoXsFl) ? "t" : "-";
  3019. if( cmIsFlag(note->flags,kEvenEndXsFl) ) e="E";
  3020. if( cmIsFlag(note->flags,kDynEndXsFl) ) d="D";
  3021. if( cmIsFlag(note->flags,kTempoEndXsFl) ) t="T";
  3022. P = cmIsFlag(note->flags,kDampUpXsFl) ? "^" : P;
  3023. P = cmIsFlag(note->flags,kDampUpDnXsFl) ? "X" : P;
  3024. s = cmIsFlag(note->flags,kSostUpXsFl) ? "}" : s;
  3025. //const cmChar_t* N = note->pitch==0 ? " " : cmMidiToSciPitch( note->pitch, NULL, 0 );
  3026. cmChar_t N[] = {'\0','\0','\0','\0'};
  3027. cmChar_t acc = note->alter==-1?'b':(note->alter==1?'#':' ');
  3028. snprintf(N,4,"%c%c%1i",note->step,acc,note->octave);
  3029. 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",
  3030. index,
  3031. note->voice->id,
  3032. note->locIdx,
  3033. note->tick,
  3034. note->tied_dur,
  3035. note->rvalue,
  3036. N,B,R,G,D,C,e,d,t,P,s,S,H,T0,T1,O);
  3037. if( cmIsFlag(note->flags,kSectionXsFl) )
  3038. cmRptPrintf(rpt," %s",cmStringNullGuard(note->tvalue));
  3039. if( cmIsFlag(note->flags,kMetronomeXsFl) )
  3040. cmRptPrintf(rpt," %i bpm",note->duration);
  3041. if( note->evenGroupId != 0 )
  3042. cmRptPrintf(rpt," e=%i",note->evenGroupId);
  3043. if( note->dynGroupId != 0 )
  3044. cmRptPrintf(rpt," d=%i",note->dynGroupId);
  3045. if( note->tempoGroupId != 0 )
  3046. cmRptPrintf(rpt," t=%i",note->tempoGroupId);
  3047. if( note->graceGroupId != 0)
  3048. cmRptPrintf(rpt," g=%i",note->graceGroupId);
  3049. if( note->dynamics != 0)
  3050. cmRptPrintf(rpt," dyn=%i %i",note->dynamics,note->vel);
  3051. if( note->editStr != NULL )
  3052. cmRptPrintf(rpt, " %s", note->editStr );
  3053. /*
  3054. if( cmIsFlag(note->flags,kBegGraceXsFl) )
  3055. cmRptPrintf(rpt," B");
  3056. if( cmIsFlag(note->flags,kEndGraceXsFl) )
  3057. cmRptPrintf(rpt," E");
  3058. if( cmIsFlag(note->flags,kDynBegForkXsFl) )
  3059. cmRptPrintf(rpt," B");
  3060. if( cmIsFlag(note->flags,kDynEndForkXsFl) )
  3061. cmRptPrintf(rpt," E");
  3062. */
  3063. }
  3064. void _cmXScoreReport( cmXScore_t* p, cmRpt_t* rpt, bool sortFl )
  3065. {
  3066. if( rpt == NULL )
  3067. rpt = p->err.rpt;
  3068. cmXsPart_t* pp = p->partL;
  3069. for(; pp!=NULL; pp=pp->link)
  3070. {
  3071. cmRptPrintf(rpt,"Part:%s\n",pp->idStr);
  3072. const cmXsMeas_t* meas = pp->measL;
  3073. for(; meas!=NULL; meas=meas->link)
  3074. {
  3075. unsigned idx = 0;
  3076. 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);
  3077. _cmXScoreReportTitle(rpt);
  3078. if( sortFl )
  3079. {
  3080. const cmXsNote_t* note = meas->noteL;
  3081. unsigned t0 = 0;
  3082. unsigned t1 = 0;
  3083. for(; note!=NULL; note=note->slink,++idx)
  3084. {
  3085. _cmXScoreReportNote(rpt,note,idx);
  3086. t1 = note->slink==NULL ? note->tick : note->slink->tick;
  3087. // check that this note is in tick order
  3088. if( !(t0 <= note->tick && note->tick <= t1) )
  3089. {
  3090. cmRptPrintf(rpt," +");
  3091. }
  3092. t0 = note->tick;
  3093. if( note->slink!=NULL || note->voice->id==0)
  3094. cmRptPrintf(rpt,"\n");
  3095. else
  3096. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  3097. }
  3098. }
  3099. else
  3100. {
  3101. const cmXsVoice_t* v = meas->voiceL;
  3102. for(; v!=NULL; v=v->link)
  3103. {
  3104. const cmXsNote_t* note = v->noteL;
  3105. cmRptPrintf(rpt," voice:%i\n",v->id);
  3106. for(; note!=NULL; note=note->mlink)
  3107. {
  3108. _cmXScoreReportNote(rpt,note,idx);
  3109. if( note->mlink!=NULL || note->voice->id==0)
  3110. cmRptPrintf(rpt,"\n");
  3111. else
  3112. cmRptPrintf(rpt," %i\n", note->tick + note->duration);
  3113. }
  3114. }
  3115. }
  3116. }
  3117. }
  3118. }
  3119. void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
  3120. {
  3121. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3122. return _cmXScoreReport(p,rpt,sortFl);
  3123. }
  3124. void _cmXScoreGenEditFileWrite( void* arg, const cmChar_t* text )
  3125. {
  3126. if( text != NULL && arg != NULL )
  3127. {
  3128. cmFileH_t* hp = (cmFileH_t*)arg;
  3129. cmFilePrint(*hp,text);
  3130. }
  3131. }
  3132. cmXsRC_t _cmXScoreEditFileRpt( cmCtx_t* ctx, cmXScore_t* p, const cmChar_t* outFn, bool damperRptFl )
  3133. {
  3134. cmXsRC_t rc = kOkXsRC;
  3135. cmErr_t err;
  3136. cmRpt_t rpt;
  3137. cmFileH_t fH = cmFileNullHandle;
  3138. cmErrSetup(&err,&ctx->rpt,"cmXScoreGenEditFile");
  3139. cmRptSetup(&rpt,_cmXScoreGenEditFileWrite,_cmXScoreGenEditFileWrite,&fH);
  3140. if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  3141. {
  3142. cmErrMsg(&err,kFileFailXsRC,"Unable to open the output file '%s'.",cmStringNullGuard(outFn));
  3143. goto errLabel;
  3144. }
  3145. _cmXScoreReport(p,&rpt,true);
  3146. errLabel:
  3147. if( cmFileClose(&fH) != kOkFileRC )
  3148. rc = cmErrMsg(&err,kFileFailXsRC,"File close failed on '%s'.",cmStringNullGuard(outFn));
  3149. return rc;
  3150. }
  3151. cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl )
  3152. {
  3153. cmXsH_t xsH = cmXsNullHandle;
  3154. cmXsRC_t rc = kOkXsRC;
  3155. if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != kOkXsRC )
  3156. return rc;
  3157. rc = _cmXScoreEditFileRpt( ctx, _cmXScoreHandleToPtr(xsH), outFn, damperRptFl );
  3158. cmXScoreFinalize(&xsH);
  3159. return rc;
  3160. }
  3161. typedef struct
  3162. {
  3163. unsigned ival;
  3164. double fval;
  3165. unsigned cnt;
  3166. } cmXsHist_t;
  3167. void _cmXsHistUpdateI( cmXsHist_t* hist, unsigned histN, unsigned ival )
  3168. {
  3169. unsigned i;
  3170. for(i=0; i<histN && hist[i].cnt!=0; ++i)
  3171. if( hist[i].ival == ival )
  3172. break;
  3173. if( i==histN )
  3174. return;
  3175. hist[i].ival = ival;
  3176. hist[i].cnt += 1;
  3177. }
  3178. void _cmXsHistUpdateF( cmXsHist_t* hist, unsigned histN, double fval )
  3179. {
  3180. unsigned i;
  3181. for(i=0; i<histN && hist[i].cnt!=0; ++i)
  3182. if( hist[i].fval == fval )
  3183. break;
  3184. if( i==histN )
  3185. return;
  3186. hist[i].fval = fval;
  3187. hist[i].cnt += 1;
  3188. }
  3189. unsigned _cmXsHistValue( cmXsHist_t* hist, unsigned histN )
  3190. {
  3191. unsigned i,n;
  3192. for(i=0,n=0; i<histN && hist[i].cnt>0; ++i)
  3193. n += 1;
  3194. return n;
  3195. }
  3196. // Measure the score complexity for the the time window 'wndSecs' seconds
  3197. // prior to the note n1 and following n0.
  3198. const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t* n1, double wndSecs )
  3199. {
  3200. const cmXsNote_t* n2 = NULL;
  3201. unsigned l_pch_0 = 0;
  3202. unsigned l_pch_value = 0;
  3203. unsigned l_pch_cnt = n1->staff==1 ? 0 : 1;
  3204. unsigned r_pch_0 = n1->staff==1 ? 1 : 0;
  3205. unsigned r_pch_value = 0;
  3206. unsigned r_pch_cnt = 0;
  3207. unsigned i = 0;
  3208. unsigned histN = 100;
  3209. cmXsHist_t velHist[ histN ];
  3210. cmXsHist_t rymHist[ histN ];
  3211. memset(velHist,0,sizeof(velHist));
  3212. memset(rymHist,0,sizeof(rymHist));
  3213. const cmXsNote_t* n = n0;
  3214. while(n!=NULL && n != n1)
  3215. {
  3216. // if this event is less than wndSecs behind 'n1' and is not a sounding note ...
  3217. if( n1->secs - n->secs <= wndSecs && cmIsFlag(n->flags,kOnsetXsFl) )
  3218. {
  3219. _cmXsHistUpdateI( velHist, histN, n->dynamics );
  3220. _cmXsHistUpdateF( rymHist, histN, n->rvalue );
  3221. switch( n->staff )
  3222. {
  3223. case 1: // treble cleff
  3224. if( i > 0 )
  3225. {
  3226. r_pch_value += r_pch_0 > n->pitch ? r_pch_0-n->pitch : n->pitch-r_pch_0;
  3227. r_pch_cnt += 1;
  3228. }
  3229. r_pch_0 = n->pitch;
  3230. break;
  3231. case 2: // bass cleff
  3232. if( i > 0 )
  3233. {
  3234. l_pch_value += l_pch_0 > n->pitch ? l_pch_0-n->pitch : n->pitch-l_pch_0;
  3235. l_pch_cnt += 1;
  3236. }
  3237. l_pch_0 = n->pitch;
  3238. break;
  3239. default:
  3240. { assert(0); }
  3241. }
  3242. // track the first note that is inside the window
  3243. if( i == 0 )
  3244. n2 = n;
  3245. // count the number of notes in the window
  3246. i += 1;
  3247. }
  3248. cmXsMeas_t* m = n->meas;
  3249. // advance th note pointer
  3250. n = n->slink;
  3251. // if we have reached the end of a measure
  3252. if( n == NULL )
  3253. {
  3254. if( m != NULL )
  3255. {
  3256. m = m->link;
  3257. if( m != NULL )
  3258. n = m->noteL;
  3259. }
  3260. }
  3261. }
  3262. // update the cplx record in n1 with the results of this window analysis
  3263. n1->cplx.sum_d_vel = _cmXsHistValue( velHist, histN );
  3264. n1->cplx.sum_d_rym = _cmXsHistValue( rymHist, histN );
  3265. n1->cplx.sum_d_lpch = l_pch_value;
  3266. n1->cplx.sum_n_lpch = l_pch_cnt;
  3267. n1->cplx.sum_d_rpch = r_pch_value;
  3268. n1->cplx.sum_n_rpch = r_pch_cnt;
  3269. return n2;
  3270. }
  3271. // Measure the score complexity and fill in the cmXsComplexity_t record associated
  3272. // with the cmXsNote_t record of each sounding note.
  3273. cmXsRC_t _cmXsMeasComplexity( cmXsH_t h, double wndSecs )
  3274. {
  3275. cmXsRC_t rc = kOkXsRC;
  3276. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3277. cmXsPart_t* pp = p->partL;
  3278. memset(&p->cplx_max,0,sizeof(p->cplx_max));
  3279. const cmXsNote_t* n0 = NULL;
  3280. // for each part
  3281. for(; pp!=NULL; pp=pp->link)
  3282. {
  3283. cmXsMeas_t* mp = pp->measL;
  3284. // for each measure
  3285. for(; mp!=NULL; mp=mp->link)
  3286. {
  3287. cmXsNote_t* n1 = mp->noteL;
  3288. // for each note in this measure
  3289. for(; n1!=NULL; n1=n1->slink)
  3290. if( cmIsFlag(n1->flags,kOnsetXsFl) )
  3291. {
  3292. if( n0 == NULL )
  3293. n0 = n1;
  3294. else
  3295. if((n0 = _cmXsMeasComplexityInWindow(n0,n1,wndSecs)) == NULL )
  3296. n0 = n1;
  3297. // track the max value for all complexity values to allow
  3298. // eventual normalization of the complexity values
  3299. p->cplx_max.sum_d_vel = cmMax(p->cplx_max.sum_d_vel, n1->cplx.sum_d_vel);
  3300. p->cplx_max.sum_d_rym = cmMax(p->cplx_max.sum_d_rym, n1->cplx.sum_d_rym);
  3301. p->cplx_max.sum_d_lpch = cmMax(p->cplx_max.sum_d_lpch,n1->cplx.sum_d_lpch);
  3302. p->cplx_max.sum_n_lpch = cmMax(p->cplx_max.sum_n_lpch,n1->cplx.sum_n_lpch);
  3303. p->cplx_max.sum_d_rpch = cmMax(p->cplx_max.sum_d_rpch,n1->cplx.sum_d_rpch);
  3304. p->cplx_max.sum_n_rpch = cmMax(p->cplx_max.sum_n_rpch,n1->cplx.sum_n_rpch);
  3305. }
  3306. }
  3307. }
  3308. return rc;
  3309. }
  3310. cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, int beginMeasNumb, int beginBPM, const cmChar_t* dir, const cmChar_t* fn )
  3311. {
  3312. cmXsRC_t rc = kOkXsRC;
  3313. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3314. if( p->partL==NULL || p->partL->measL == NULL )
  3315. return rc;
  3316. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  3317. unsigned trkN = 2;
  3318. unsigned ticksPerQN = p->partL->measL->divisions;
  3319. const cmChar_t* outFn = cmFsMakeFn(dir,fn,"mid",NULL);
  3320. unsigned baseTick = -1;
  3321. unsigned bpm = beginBPM==0 ? 60 : beginBPM;
  3322. //if( cmMidiFileCreate( ctx, &mfH, trkN, ticksPerQN ) != kOkMfRC )
  3323. // return cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
  3324. cmXsPart_t* pp = p->partL;
  3325. // for each part
  3326. for(; pp!=NULL; pp=pp->link)
  3327. {
  3328. cmXsMeas_t* mp = pp->measL;
  3329. // for each measure
  3330. for(; mp!=NULL; mp=mp->link)
  3331. {
  3332. // skip all measures until we reach the first measure to output
  3333. if(mp->number < beginMeasNumb)
  3334. continue;
  3335. // if the MIDI file has not yet been created
  3336. if( !cmMidiFileIsValid(mfH) )
  3337. {
  3338. ticksPerQN = mp->divisions;
  3339. // create the MIDI file
  3340. if( cmMidiFileCreate( ctx, &mfH, trkN, ticksPerQN ) != kOkMfRC )
  3341. {
  3342. rc = cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
  3343. goto errLabel;
  3344. }
  3345. // set the starting tempo
  3346. cmMidFileInsertTrackTempoMsg(mfH, 0, 0, bpm );
  3347. }
  3348. cmXsNote_t* np = mp->noteL;
  3349. if( baseTick == -1 )
  3350. baseTick = np->tick;
  3351. if( mp->divisions != ticksPerQN )
  3352. cmErrWarnMsg(&p->err,kMidiFailXsRC,"The 'tick per quarter note' (divisions) field in measure %i does not match the value in the first measure (%i).",mp->divisions,ticksPerQN);
  3353. unsigned ni = 0;
  3354. // for each note in this measure
  3355. for(; np!=NULL; np=np->slink,++ni)
  3356. {
  3357. unsigned thisTick = np->tick - baseTick;
  3358. switch( np->flags & (kOnsetXsFl|kMetronomeXsFl|kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
  3359. {
  3360. case kOnsetXsFl:
  3361. if( cmMidiFileIsValid(mfH) )
  3362. {
  3363. if( np->tied_dur <= 0 )
  3364. cmErrWarnMsg(&p->err,kOkXsRC,"A zero length note was encountered bar:%i tick:%i (%i) %s",np->meas->number,np->tick,thisTick,cmMidiToSciPitch(np->pitch,NULL,0));
  3365. /*
  3366. if( mp->number == 20 )
  3367. {
  3368. _cmXScoreReportNote(ctx->err.rpt,np,ni);
  3369. cmRptPrintf(ctx->err.rpt,"\n");
  3370. }
  3371. */
  3372. if( np->vel == 0 )
  3373. cmErrWarnMsg(&p->err,kOkXsRC,"A sounding note with zero velocity was encountered at bar:%i tick:%i pitch:%s.",np->meas->number,np->tick,cmMidiToSciPitch(np->pitch,NULL,0));
  3374. if( cmMidiFileInsertTrackChMsg(mfH, 1, thisTick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC
  3375. ||cmMidiFileInsertTrackChMsg(mfH, 1, thisTick + np->tied_dur, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC )
  3376. {
  3377. rc = kMidiFailXsRC;
  3378. }
  3379. }
  3380. break;
  3381. case kDampDnXsFl:
  3382. case kDampUpDnXsFl:
  3383. case kSostDnXsFl:
  3384. if( cmMidiFileIsValid(mfH) )
  3385. {
  3386. if( np->duration <= 0 )
  3387. cmErrWarnMsg(&p->err,kOkXsRC,"A zero length pedal event was encountered bar:%i tick:%i (%i)",np->meas->number,np->tick,thisTick);
  3388. cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
  3389. if( (cmMidiFileInsertTrackChMsg(mfH, 1, thisTick, kCtlMdId, d0, 127 ) != kOkMfRC )
  3390. ||(cmMidiFileInsertTrackChMsg(mfH, 1, thisTick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) )
  3391. {
  3392. rc = kMidiFailXsRC;
  3393. }
  3394. }
  3395. break;
  3396. case kMetronomeXsFl:
  3397. bpm = np->duration;
  3398. if( cmMidiFileIsValid(mfH) )
  3399. if( cmMidFileInsertTrackTempoMsg(mfH, 0, thisTick, bpm ) != kOkMfRC )
  3400. rc = kMidiFailXsRC;
  3401. break;
  3402. case 0:
  3403. break;
  3404. default:
  3405. { assert(0); }
  3406. }
  3407. if( rc != kOkXsRC )
  3408. {
  3409. rc = cmErrMsg(&p->err,rc,"MIDI message insert failed on '%s'.",cmStringNullGuard(outFn));
  3410. goto errLabel;
  3411. }
  3412. }
  3413. }
  3414. }
  3415. if( cmMidiFileIsValid(mfH) )
  3416. if( cmMidiFileWrite(mfH,outFn) != kOkMfRC )
  3417. {
  3418. rc = cmErrMsg(&p->err,kMidiFailXsRC,"MIDI file write failed on '%s'.",cmStringNullGuard(outFn));
  3419. goto errLabel;
  3420. }
  3421. errLabel:
  3422. cmFsFreeFn(outFn);
  3423. if( cmMidiFileClose(&mfH) != kOkMfRC )
  3424. {
  3425. rc = cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
  3426. goto errLabel;
  3427. }
  3428. return rc;
  3429. }
  3430. bool _cmXsIsMidiFileValid( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
  3431. {
  3432. const cmChar_t* midiFn = cmFsMakeFn(dir,fn,"mid",NULL);
  3433. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  3434. if( cmMidiFileOpen( ctx, &mfH, midiFn ) == kOkMfRC )
  3435. {
  3436. cmMidiFileClose(&mfH);
  3437. return true;
  3438. }
  3439. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3440. cmErrMsg(&p->err,kMidiFailXsRC,"The generated MIDI file '%s' is not valid.", cmStringNullGuard(midiFn));
  3441. return false;
  3442. }
  3443. typedef struct cmXsSvgEvt_str
  3444. {
  3445. unsigned flags; // k???XsFl
  3446. unsigned tick; // start tick
  3447. unsigned durTicks; // dur-ticks
  3448. unsigned voice; // score voice number
  3449. unsigned d0; // MIDI d0 (barNumb)
  3450. unsigned d1; // MIDI d1
  3451. cmXsComplexity_t cplx;
  3452. struct cmXsSvgEvt_str* link;
  3453. } cmXsSvgEvt_t;
  3454. typedef struct cmXsMidiFile_str
  3455. {
  3456. cmXsSvgEvt_t* elist;
  3457. cmXsSvgEvt_t* eol;
  3458. unsigned pitch_min;
  3459. unsigned pitch_max;
  3460. } cmXsMidiFile_t;
  3461. void _cmXsWriteMidiSvgLegend( cmSvgH_t svgH, unsigned index, const cmChar_t* label, const cmChar_t* classStr )
  3462. {
  3463. double x = 100;
  3464. double y = 120*10 - 20*index;
  3465. cmSvgWriterText( svgH, x, y, label, "legend" );
  3466. x += 75;
  3467. cmSvgWriterLine( svgH, x, y, x+125, y, classStr );
  3468. }
  3469. void _cmXsWriteMidiSvgLinePoint( cmSvgH_t svgH, double x0, double y0, double x1, double y1, double y_max, const cmChar_t* classStr, const cmChar_t* label )
  3470. {
  3471. int bn = 255;
  3472. char b[bn+1];
  3473. double y_scale = 10;
  3474. double y_label = y1;
  3475. b[0] = 0;
  3476. y0 = (y0/y_max) * 127.0 * y_scale;
  3477. y1 = (y1/y_max) * 127.0 * y_scale;
  3478. cmSvgWriterLine(svgH, x0, y0, x1, y1, classStr );
  3479. if( y0 != y1 )
  3480. snprintf(b,bn,"%5.0f %s",y_label,label==NULL?"":label);
  3481. else
  3482. {
  3483. if( label != NULL )
  3484. snprintf(b,bn,"%s",label);
  3485. }
  3486. if( strlen(b) )
  3487. cmSvgWriterText(svgH, x1, y1, b, "pt_text");
  3488. }
  3489. cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* svgFn, bool standAloneFl, bool panZoomFl )
  3490. {
  3491. cmXsRC_t rc = kOkXsRC;
  3492. cmSvgH_t svgH = cmSvgNullHandle;
  3493. cmXsSvgEvt_t* e = mf->elist;
  3494. unsigned noteHeight = 10;
  3495. //cmChar_t* fn0 = cmMemAllocStr( fn );
  3496. //const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL);
  3497. const cmChar_t* cssFn = cmFsMakeFn(NULL,"score_midi_svg","css",NULL);
  3498. cmChar_t* t0 = NULL; // temporary dynamic string
  3499. unsigned i = 0;
  3500. const cmXsSvgEvt_t* e0 = NULL;
  3501. //cmMemFree(fn0);
  3502. // create the SVG writer
  3503. if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
  3504. {
  3505. rc = cmErrMsg(&p->err,kSvgFailXsRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(svgFn));
  3506. goto errLabel;
  3507. }
  3508. _cmXsWriteMidiSvgLegend( svgH, 0, "Velocity", "cplx_vel" );
  3509. _cmXsWriteMidiSvgLegend( svgH, 1, "Duration", "cplx_rym" );
  3510. _cmXsWriteMidiSvgLegend( svgH, 2, "Left Pitch", "cplx_lpch" );
  3511. _cmXsWriteMidiSvgLegend( svgH, 3, "Right Pitch", "cplx_rpch" );
  3512. _cmXsWriteMidiSvgLegend( svgH, 4, "Density", "cplx_density" );
  3513. // for each MIDI file element
  3514. for(; e!=NULL && rc==kOkXsRC; e=e->link)
  3515. {
  3516. switch( e->flags & (kOnsetXsFl|kBarXsFl|kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl))
  3517. {
  3518. // if this is a note
  3519. case kOnsetXsFl:
  3520. {
  3521. //const cmChar_t* classLabel = "note";
  3522. t0 = cmTsPrintfP(t0,"note_%i%s",e->voice, cmIsFlag(e->flags,kGraceXsFl) ? "_g":"");
  3523. //if( cmIsFlag(e->flags,kGraceXsFl) )
  3524. // classLabel = "grace";
  3525. if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC )
  3526. rc = kSvgFailXsRC;
  3527. else
  3528. {
  3529. t0 = cmTsPrintfP(t0,"%s",cmMidiToSciPitch( e->d0, NULL, 0));
  3530. if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC )
  3531. rc = kSvgFailXsRC;
  3532. else
  3533. {
  3534. if( e0 != NULL )
  3535. {
  3536. bool fl = (i % 10) == 0;
  3537. _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_vel, e->tick, e->cplx.sum_d_vel, p->cplx_max.sum_d_vel, "cplx_vel",fl?"V":NULL);
  3538. _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rym, e->tick, e->cplx.sum_d_rym, p->cplx_max.sum_d_rym, "cplx_rym",fl?"D":NULL);
  3539. _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_lpch, e->tick, e->cplx.sum_d_lpch, p->cplx_max.sum_d_lpch, "cplx_lpch",fl?"L":NULL);
  3540. _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rpch, e->tick, e->cplx.sum_d_rpch, p->cplx_max.sum_d_rpch, "cplx_rpch",fl?"R":NULL);
  3541. _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_n_lpch + e0->cplx.sum_n_rpch, e->tick, e->cplx.sum_n_lpch + e->cplx.sum_n_rpch, p->cplx_max.sum_n_lpch + p->cplx_max.sum_n_rpch, "cplx_density",fl?"N":NULL);
  3542. }
  3543. e0 = e;
  3544. }
  3545. }
  3546. i+=1;
  3547. }
  3548. break;
  3549. // if this is a bar
  3550. case kBarXsFl:
  3551. {
  3552. if( cmSvgWriterLine(svgH, e->tick, 0, e->tick, 127*noteHeight, "bar") != kOkSvgRC )
  3553. rc = kSvgFailXsRC;
  3554. else
  3555. {
  3556. if( cmSvgWriterText(svgH, e->tick, 10, t0 = cmTsPrintfP(t0,"%i",e->d0), "text" ) != kOkSvgRC )
  3557. rc = kSvgFailXsRC;
  3558. }
  3559. }
  3560. break;
  3561. // if this is a pedal event
  3562. case kDampDnXsFl:
  3563. case kDampUpDnXsFl:
  3564. case kSostDnXsFl:
  3565. {
  3566. const cmChar_t* classLabel = cmIsFlag(e->flags,kSostDnXsFl) ? "sost" : "damp";
  3567. unsigned y = (128 + cmIsFlag(e->flags,kSostDnXsFl)?1:0) * noteHeight;
  3568. cmSvgWriterRect(svgH, e->tick, y, e->durTicks, noteHeight-1, classLabel);
  3569. }
  3570. break;
  3571. }
  3572. }
  3573. if( rc != kOkXsRC )
  3574. cmErrMsg(&p->err,kSvgFailXsRC,"SVG element insert failed.");
  3575. if( rc == kOkXsRC )
  3576. if( cmSvgWriterWrite(svgH,cssFn,svgFn,standAloneFl, panZoomFl) != kOkSvgRC )
  3577. rc = cmErrMsg(&p->err,kSvgFailXsRC,"SVG file write to '%s' failed.",cmStringNullGuard(svgFn));
  3578. errLabel:
  3579. cmSvgWriterFree(&svgH);
  3580. //cmFsFreeFn(svgFn);
  3581. cmFsFreeFn(cssFn);
  3582. cmMemFree(t0);
  3583. return rc;
  3584. }
  3585. void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, const cmXsComplexity_t* cplx )
  3586. {
  3587. cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1);
  3588. e->flags = flags;
  3589. e->tick = tick;
  3590. e->durTicks = durTick;
  3591. e->voice = voice;
  3592. e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM
  3593. e->d1 = d1;
  3594. if( cplx != NULL )
  3595. e->cplx = *cplx;
  3596. if( mf->eol != NULL )
  3597. mf->eol->link = e;
  3598. else
  3599. mf->elist = e;
  3600. // track the min/max pitch
  3601. if( cmIsFlag(flags,kOnsetXsFl) )
  3602. {
  3603. mf->pitch_min = mf->eol==NULL ? d0 : cmMin(mf->pitch_min,d0);
  3604. mf->pitch_max = mf->eol==NULL ? d0 : cmMin(mf->pitch_max,d0);
  3605. }
  3606. mf->eol = e;
  3607. }
  3608. cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, int beginMeasNumb, const cmChar_t* svgFn, bool standAloneFl, bool panZoomFl )
  3609. {
  3610. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3611. cmXsPart_t* pp = p->partL;
  3612. cmXsMidiFile_t mf;
  3613. memset(&mf,0,sizeof(mf));
  3614. for(; pp!=NULL; pp=pp->link)
  3615. {
  3616. const cmXsMeas_t* meas = pp->measL;
  3617. for(; meas!=NULL; meas=meas->link)
  3618. {
  3619. if( meas->number < beginMeasNumb )
  3620. continue;
  3621. const cmXsNote_t* note = meas->noteL;
  3622. for(; note!=NULL; note=note->slink)
  3623. {
  3624. // if this is a metronome marker
  3625. if( cmIsFlag(note->flags,kMetronomeXsFl) )
  3626. {
  3627. // set BPM as d0
  3628. _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,NULL);
  3629. continue;
  3630. }
  3631. // if this is a sounding note
  3632. if( cmIsFlag(note->flags,kOnsetXsFl) )
  3633. {
  3634. unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave );
  3635. unsigned durTick = note->tied_dur;
  3636. if( note->tied != NULL )
  3637. {
  3638. cmXsNote_t* tn = note->tied;
  3639. for(; tn!=NULL; tn=tn->tied)
  3640. durTick += tn->tied_dur;
  3641. }
  3642. _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,&note->cplx);
  3643. continue;
  3644. }
  3645. // if this is a bar event
  3646. if( cmIsFlag(note->flags,kBarXsFl) )
  3647. {
  3648. _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0,NULL);
  3649. continue;
  3650. }
  3651. // if this is a pedal event
  3652. if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
  3653. {
  3654. unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
  3655. _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,NULL);
  3656. continue;
  3657. }
  3658. }
  3659. }
  3660. }
  3661. return _cmXsWriteMidiSvg( ctx, p, &mf, svgFn, standAloneFl, panZoomFl );
  3662. }
  3663. cmXsRC_t cmXScoreTest(
  3664. cmCtx_t* ctx,
  3665. const cmChar_t* xmlFn,
  3666. const cmChar_t* editFn,
  3667. const cmChar_t* csvOutFn,
  3668. const cmChar_t* midiOutFn,
  3669. const cmChar_t* svgOutFn,
  3670. bool reportFl,
  3671. int beginMeasNumb,
  3672. int beginBPM,
  3673. bool standAloneFl,
  3674. bool panZoomFl,
  3675. bool damperRptFl)
  3676. {
  3677. cmXsRC_t rc;
  3678. cmXsH_t h = cmXsNullHandle;
  3679. if( editFn!=NULL && !cmFsIsFile(editFn) )
  3680. {
  3681. cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.\n",editFn);
  3682. cmXScoreGenEditFile(ctx,xmlFn,editFn,damperRptFl);
  3683. editFn = NULL;
  3684. }
  3685. // Parse the XML file and apply the changes in editFn.
  3686. if((rc = cmXScoreInitialize( ctx, &h, xmlFn,editFn, damperRptFl )) != kOkXsRC )
  3687. return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
  3688. if( csvOutFn != NULL )
  3689. {
  3690. cmXScoreWriteCsv(h,beginMeasNumb,csvOutFn);
  3691. _cmXsIsCsvValid(ctx,h,csvOutFn);
  3692. }
  3693. // measure the score complexity
  3694. double wndSecs = 1.0;
  3695. _cmXsMeasComplexity(h,wndSecs);
  3696. if( midiOutFn != NULL )
  3697. {
  3698. cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
  3699. _cmXsWriteMidiFile(ctx, h, beginMeasNumb, beginBPM, pp->dirStr, pp->fnStr );
  3700. _cmXsIsMidiFileValid(ctx, h, pp->dirStr, pp->fnStr );
  3701. cmFsFreePathParts(pp);
  3702. }
  3703. if( svgOutFn != NULL )
  3704. _cmXScoreGenSvg( ctx, h, beginMeasNumb, svgOutFn, standAloneFl, panZoomFl );
  3705. if(reportFl)
  3706. cmXScoreReport(h,&ctx->rpt,true);
  3707. return cmXScoreFinalize(&h);
  3708. }
  3709. cmXsRC_t cmXScoreMergeEditFiles( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* refEditFn, unsigned refBegMeasNumb, const cmChar_t* editFn, unsigned keyMeasNumb, const cmChar_t* outFn )
  3710. {
  3711. cmXsH_t h = cmXsNullHandle;
  3712. cmXsRC_t rc;
  3713. bool damperRptFl = false;
  3714. if((rc = cmXScoreInitialize(ctx, &h, xmlFn, refEditFn, damperRptFl )) == kOkXsRC )
  3715. {
  3716. cmXScore_t* p = _cmXScoreHandleToPtr(h);
  3717. if((rc = _cmXsMergeEditFiles(p, refBegMeasNumb, editFn, keyMeasNumb, outFn )) == kOkXsRC )
  3718. rc = _cmXScoreEditFileRpt( ctx, p, outFn, damperRptFl );
  3719. cmXScoreFinalize(&h);
  3720. }
  3721. return rc;
  3722. }