From e17767d42a5b49068850152c3ce642af5abefc5f Mon Sep 17 00:00:00 2001 From: YohananDiamond Date: Sun, 27 Nov 2022 23:09:36 -0300 Subject: [PATCH 01/34] Added demo song 'Midnight Dog Orchestra' --- demos/Midnight_Dog_Orchestra.fur | Bin 0 -> 7191 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/Midnight_Dog_Orchestra.fur diff --git a/demos/Midnight_Dog_Orchestra.fur b/demos/Midnight_Dog_Orchestra.fur new file mode 100644 index 0000000000000000000000000000000000000000..caa705d48ed42eabfb0775b87ae2a5af56afef09 GIT binary patch literal 7191 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 N_vebtM=(VH{{Y3lF3 Date: Wed, 30 Nov 2022 02:07:44 -0500 Subject: [PATCH 02/34] fix oneTickCut overriding ECxx --- src/engine/playback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 442cd7bf..b8d88e07 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1064,7 +1064,7 @@ void DivEngine::nextRow() { } } } - if (doPrepareCut) chan[i].cut=ticks; + if (doPrepareCut && chan[i].cut<=0) chan[i].cut=ticks; } } } From 992fefd9d2719ceb2fe4eee78f902965bacc7b4d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 30 Nov 2022 02:14:02 -0500 Subject: [PATCH 03/34] C64: fix glitches when one-tick gap is on --- src/engine/playback.cpp | 6 ++++-- src/gui/compatFlags.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index b8d88e07..d21f8739 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1045,8 +1045,10 @@ void DivEngine::nextRow() { if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) { if (!chan[i].legato) { + bool wantPreNote=false; if (disCont[dispatchOfChan[i]].dispatch!=NULL) { - if (disCont[dispatchOfChan[i]].dispatch->getWantPreNote()) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks)); + wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote(); + if (wantPreNote) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks)); } if (song.oneTickCut) { @@ -1064,7 +1066,7 @@ void DivEngine::nextRow() { } } } - if (doPrepareCut && chan[i].cut<=0) chan[i].cut=ticks; + if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks; } } } diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 93053135..165af74f 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -77,7 +77,7 @@ void FurnaceGUI::drawCompatFlags() { } ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); + ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines.\n\nineffective on C64."); } ImGui::Checkbox("Broken speed alternation",&e->song.brokenSpeedSel); if (ImGui::IsItemHovered()) { From fa564dbe78fefda8bbf673401280eda9be06e0c8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 30 Nov 2022 17:20:04 -0500 Subject: [PATCH 04/34] GUI: and here is the mobile orders bar --- src/gui/gui.cpp | 17 ++++++++++++++++- src/gui/gui.h | 4 ++-- src/gui/orders.cpp | 45 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ebd7dafa..b42345b8 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2197,6 +2197,11 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { sampleSelEnd=x; } } + if (orderScrollLocked) { + orderScroll=(orderScrollSlideOrigin-dragX)/(40.0*dpiScale); + if (orderScroll<0.0f) orderScroll=0.0f; + if (orderScroll>(float)e->curSubSong->ordersLen-1) orderScroll=e->curSubSong->ordersLen-1; + } } #define checkExtension(x) \ @@ -2906,6 +2911,13 @@ void FurnaceGUI::pointUp(int x, int y, int button) { } } sampleDragActive=false; + if (orderScrollLocked) { + int targetOrder=round(orderScroll); + if (targetOrder<0) targetOrder=0; + if (targetOrder>e->curSubSong->ordersLen-1) targetOrder=e->curSubSong->ordersLen-1; + if (curOrder!=targetOrder) setOrder(targetOrder); + } + orderScrollLocked=false; if (selecting) { if (!selectingFull) cursor=selEnd; finishSelection(); @@ -2929,7 +2941,7 @@ void FurnaceGUI::pointMotion(int x, int y, int xrel, int yrel) { addScroll(1); } } - if (macroDragActive || macroLoopDragActive || waveDragActive || sampleDragActive) { + if (macroDragActive || macroLoopDragActive || waveDragActive || sampleDragActive || orderScrollLocked) { int distance=fabs((double)xrel); if (distance<1) distance=1; float start=x-xrel; @@ -5728,6 +5740,7 @@ FurnaceGUI::FurnaceGUI(): latchNibble(false), nonLatchNibble(false), keepLoopAlive(false), + orderScrollLocked(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING), @@ -5816,6 +5829,8 @@ FurnaceGUI::FurnaceGUI(): bindSetPending(false), nextScroll(-1.0f), nextAddScroll(0.0f), + orderScroll(0.0f), + orderScrollSlideOrigin(0.0f), layoutTimeBegin(0), layoutTimeEnd(0), layoutTimeDelta(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 23da5071..04811e08 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1419,7 +1419,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; - bool keepLoopAlive; + bool keepLoopAlive, orderScrollLocked; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; std::atomic curWindowThreadSafe; float peak[2]; @@ -1562,7 +1562,7 @@ class FurnaceGUI { int bindSetTarget, bindSetPrevValue; bool bindSetActive, bindSetPending; - float nextScroll, nextAddScroll; + float nextScroll, nextAddScroll, orderScroll, orderScrollSlideOrigin; int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; int renderTimeBegin, renderTimeEnd, renderTimeDelta; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 73fcb914..78992116 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -25,6 +25,20 @@ void FurnaceGUI::drawMobileOrderSel() { if (!portrait) return; + if (!orderScrollLocked) { + if (orderScroll>(float)curOrder-0.005f && orderScroll<(float)curOrder+0.005f) { + orderScroll=curOrder; + } else if (orderScrollcurOrder) orderScroll=curOrder; + WAKE_UP; + } else { + orderScroll-=MAX(0.05f,(orderScroll-curOrder)*0.2f); + if (orderScrollcurSubSong->ordersLen; i++) { + ImVec2 pos=centerPos; + ImVec4 color=uiColors[GUI_COLOR_TEXT]; + pos.x+=(i-orderScroll)*40.0*dpiScale; + if (pos.x<-200.0*dpiScale) continue; + if (pos.x>canvasW+200.0*dpiScale) break; + String text=fmt::sprintf("%.2X",i); + float targetSize=size.y-fabs(i-orderScroll)*2.0*dpiScale; + if (targetSize<8.0*dpiScale) targetSize=8.0*dpiScale; + color.w*=MIN(1.0f,targetSize/size.y); - ImVec2 pos=ImLerp(minArea,maxArea,ImVec2(0.5,0.0)); - ImGui::PushFont(bigFont); - ImVec2 textSize=ImGui::CalcTextSize(text.c_str()); - ImGui::PopFont(); + ImGui::PushFont(bigFont); + ImVec2 textSize=ImGui::CalcTextSize(text.c_str()); + ImGui::PopFont(); - pos.x-=textSize.x*0.5*(size.y/textSize.y); + pos.x-=textSize.x*0.5*(targetSize/textSize.y); + pos.y-=targetSize*0.5; - dl->AddText(bigFont,size.y,pos,col,text.c_str()); + dl->AddText(bigFont,targetSize,pos,ImGui::GetColorU32(color),text.c_str()); + } + } + if (ImGui::IsItemClicked()) { + orderScrollSlideOrigin=ImGui::GetMousePos().x+orderScroll*40.0f*dpiScale; + orderScrollLocked=true; } } ImGui::End(); From 56d2e4c507c8652fdbcb0c11e20ea06a856303aa Mon Sep 17 00:00:00 2001 From: TheDuccinator <66538032+TheDuccinator@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:28:56 -0800 Subject: [PATCH 05/34] Add yet another demo song Another one to the plie --- demos/The_Good_Times_Arcade.fur | Bin 0 -> 85263 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/The_Good_Times_Arcade.fur diff --git a/demos/The_Good_Times_Arcade.fur b/demos/The_Good_Times_Arcade.fur new file mode 100644 index 0000000000000000000000000000000000000000..f67b6d1d7fc0f3f237e892fc0bb035c6ed63b847 GIT binary patch literal 85263 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 z zSz*?hvP>UM_e>{DhfQlu(@gzM%}h=vt?{Svz45Mbzj2oFH)9uLBcrF08mbLBhWCa) z4Lb}g4ATuG4V?@j23vz!pQ(SUzpCG-U!tF^AFc1D@1k$6Z=(0n%k(B)p6;{mhVF!J zoo=RXgf3dwQ0Jhl)+R#sx&<+|Tf0rWL_1GAQQKMDKHhvlZqe=4a*#v(nPo^1EfF<+$YmM7`eP%D3TT z_+9*cK7-fsPC_e){k6hvAy-g|4aJ_~G;yc+P%IGbNi&H2Kgea0K#B;W?z9yhN>|fM z^fT2{IjWC(qVZ@gx`5uHQl!8^I2`xGF?bQafWKn_doeAU0n99BH}inWVleB>HfMil zSFmT<*K7rA%QfcuaO1hv+zIXtXX3mhO(oqVlO!7?=Or&C*%Bmmkv5Tblm0H9E8Qx+ zBmFGRmzt$^vQSyHY_e>v?7ZxyEK!yv)5_#>e|Z}~hGFul@+I;O@+0zV@(1$w@@%%gfwBb&}XXV0)3+1czswjrxx zE15*l@&)Dyvxw=*1TfZ26;8w7@JoCXABT7!1(pfNVb~LMxCX41gdU@7=n(n?jYU0B z6Qo8QBD9j`((m*Qy#+Y1gD$2qbO7x}JJL2Z2=G8jDIvs6YDg)`g-q}c@ZlOcP4<$V zWHV%lnPe&%Mh1~6(uH&+ZAfzxNYuohU{WKNi^XE9m??e}--xl|1;CI!;ALyXrQ#BC zhB!$a4Kz&bEp`>#ip|6zF;EN^{Y4iMiv}T6_#!+9j5#Un7j_6Mg|R}k&{1e2_zMn# ziT?rB;&VWmt^5LhB;T12;+=T{*pg@YY>Bm;wd}CW0aO`a>0)VS@v~T2bmmfXviXtu zta+Dtv3ZjDcXJnWYjYEGkhzXoX68+0P>p7rl1rFFEqfNa{ZB0#0Elpvj z`X(=vtI5G+Ym%BclVB8$dSkV*%2;45GZq1r02dfjjk(4`qsdrltbtZED&Y$cQv*|k zX`pGOX}oE=X^ClrX}{@D(*x5Rh}b++HDqU{+1VUyjxY~2Pd2YHA2eSy-!~_ii_8*0 zLmx{+OSq+vWu#@kWwm9e<$~pj<(oxo;dnPbkZ%F&9>Xu;5AnD7cs>b`*TSm=AEAK| zE({Q+g3h)BLO&J~g&aW#QC?SUDRvQ~#fjodald#IY?21?UM8AEDbbJsuw74x{8@nf zd%(U|z|OBp63HPY#6UReLj55>w1zCvk4~Tq=?c1rZif2&C_PK>(^oW|=2OeB|50a< zEwV>0kXyo_KInmlq6ugwS_GJ}4IM-m(M|LmC8D3G5+Uq_J#bwdid*1rcsSG!tMCDQ z4&TSGZ~{)oxwr!J*p{gSm1RSw1Jj)u%`9WKG8dUx<_nX@6f-)gDrBq!>&v!cdqPx< zV`l-@Y-abe=h%mUI4MwPl(7bu;iR03tHXtIZMfcCG&cY$kNMmNZaa4XkmwS3kBb95 z`pA9bQn+-kfGgqj9OY~zwd5l4l>|vbB+VqPB<&swB}1XtUD64@3752iZyN&MwURWKG?s))>PgfvwgXhqwh}vu zM1my(SIOmaKe->=ckToC5~BYJcbmHi67B{`Hv(!d;bw4SxiQ>GSV3p5B^Stfb55KU zU_%c3iG9UBV6U);*mdlDb`%@UhO>=XKhO@#nwUc72lJ7+!5m|@Gi#aI%vh!`(~b#a zyckzT#UQ2vXX5wxG5!;u#QX6UyacQ<9QOdr)Wi0e2lV)kUZXg43!OuI070gs;gAVB zqUI;>U+<6}s=^fdjy`~Fah&d?Yv}?yoermc!E3^4V;V*Ss5f<`R#XH$DIh5% z9=z-pxkwHJnyev9$wD%h%pjx4VA7pL0G2c%jYtUb2mf>?4#b*B2`}o!3NZs}k~r|; z3*ss9khnu!54Gz=aX4T}8!<%Gh}I$}3PP1o0yy#=YL$55g%Bq^1U$JaoD&YhK69C{ z6l#}o!eGFao>0L=2(5)+p^ji9NCYEq<}3LMzJSjMq)FyK@gE?X~PJ-kT}X-YZ( zYV;?=$!IbSY&RDyxRz`JYwjoKp@z6g?t+Em$p?~3vLS zP5VH`8A3s%hvD&fHeQNX<8^o!K7>!< z^Y{vQ{&Sp&Q*k~n#MRh{IY!FZ069Q(I5VD1AjC=wrY#fBbYprk{a{}c1$E9qW*9RZ zVreWhk(tiSWadKUGY5DsvzS@HtYlU(>mb@TFzcC(%r<5hvxC_ObbvXnzfD~5&D^8#TXcr*=IbD*Z&?(iijzeGL9_onE9T!C&^%?cg~Z z!GBglZki7?lTM-Iz^g{m-)MjEvd&P+Hl+2y^XgDHYD*is=g0J@hY>Fh|kS|-1VB!bZ%0UdY7zfVYWT#8nV;OU32l z3UMvq*=lGPLw^D6y5>QmzrhMJ#4Ml! zpmMQB)QYtS2Wz!~6+03qSh+i?14s}6RbK<3ra)~-JJN}CCOx1g90IlBXwcm>s4EtL zE|-yYWFy%Dv=Krxj9ZrDQ| zqM<&tJ`Dx*XhGZ2PJkf&exb)WI*rbtbD@G=3x2SZ?xhEyIypzL(A(fA&uKjU0DG8k zG>v9Ktx`j^6iVA)m?A~i$N@Q_I)E%eP_;AxbZLt^fp7K$AN>uDMB^ae%|=VmGPE46 zM(e?!ccQ&$Kh)C4&`H<}T?KEy13v#4YMbZiB_PmCh=RAUNBV$1L$&h-Vj~r0Kn~4< zwg9LEs1#KL>5v{(0o5SG-`7=03-`>xwb5mSCpzF7xUPXG+Fz{!o@wEo+OI0$t1_sk zs-ahk3Q;aZV-B=AfP%G@j#5w>PzumDl#CKUs#ho;=mp650_ZV%3i98A-NALh&|10x zbQ+@fAgpmetadl7dB?AnuSXluO2FI2&@Kdh%!l3_(9T@Y(F`;ZO#yAyM)(-05QagN z4+fMT0Gft9%0og%4 zX#+NrA|~vCqljs-v zl_r2cyaOzWhb$9I?}C@yhV~j%FBhPyJxfmjvK*xc!ISm?wrr&vz_b3K%jgoQf9KL! zP}@wQlVHCzj{Xi9(+{W{?Ls4=)`SB*vZXdu2L6mG z1C@}4)Z$PLsRSw}g|N%Yga}9n{tmH_M7}~yd<1?E{08=4@1ZV#L!Oc6ws+(cWbiNKGf0#O^cDKwfRafXd>W-6;^}w)_@JS z0PTP(V;|V|5LBuspxQVKdE`&PiW}eqcfb=KLVk$@)QE#j6A!!NcYmWtGWbviAV@Bd zmR5tWRe;awfz3d*zcu=|-|4VO2CW=g8^~w2;K|P5(;mPY?1QZDJ*MqhAL+sYB%Li8I1uLyxs~fNrM6wg)bw`L~JJ5tRuoaLT&T`O=>{nDv z{#{)E*P|Az1^QP#V5UZD0{z$gig+zpK@Sx|4Ve*20(*ae_C2)kf!+YU{>3kW<9}t8IN13< zhU+KvDR3-YKY;cD(A{5r3+j_QzkKQn^v==C;Ay7;Q;q{4qP0I-Th|z&{(bs2Y=lDG z|98JyQ~lD0Kz)#z?kBfVEV&rp=0@Z-k6Sqp**Kp#!=s0fQe)#{FT`$&YxSh-%f~OO zzHfQHuvivPrq*cZR7jZ zoK+cffB5gy_6#&cg!F&iq^NEEo^hcUMqO?bRkgB3-vCV=Z9CU7wo$8MygEM#8r5uV z;BA+U?Y|AI+v7t^MMn>HA@NsbcM`jtXK9tJFrR%{<(BX+_hH!#_HKU87j^O`?a-Va zIb-nYj8!E!3xCUg`FYi?vENsnYP{R=V&s?b>lcscpE#Xsuy^wfzq{#qvPXv=M7$bz z;b+1uJ+JC!w=8K6_j5wp&`80-rbCNWo*SjjgN|EI)v3Kl20pXz%sD$>t<#O^`zb5o zMM1%b4W&y{4_EY6%=3tAYSlsCsB?3FRhzcGdfNq+JM{43hW_a9WZloHYd}!s#f~le z4{y*ccuz!T#G|Ic^%L7HZ+E`&_HbpR$xW9!DXsR@fA4=x?O%0Y^)ND0S(w_nU|&HL z(w3{tWwQ49Cz9T0Zp_MiGA8>)I{qr&)jwYL(ecCD?-xG?zl)E1dgZtC$rU--y03mo zKiU?{^z21+*-Y<+lpLRnumB%M~iEEm4Ra@&i6wS#Sn&6QsL zT=ep_YHQ_>pD7<+JJXUGPBC>B<{0?{`dF85WqoUA1wQn-?>iM&%Q`hZ6ezUsX`TIj zX65mK10L_*ZZS=<{>VYKt8LO0vGffy z+uAK?N8K3JhI(__D;tIcA9QCqWAE0TueO@hGG0B}ql5pK2EnQ&-b1(*lKWnB!#A^| ztvk9+P)$>A^!5Kltb?s_Wtn<}c zb?MiN4p|HMSg%m!{xACqC!pKxA=Wp4UwT!hm>X2Hx^!_$bXg3$O?dwK)4l41{@-(T z&**||uGq@g#ku0!Z|Og+X6UD;>v7?Sr*CZDPI&t!Au}tvs%1%1(b)3v;=uB07Ny~= z*;CnBzgAdlF}A$bP+@<(=1SEjHrUg(@*>ly<P@igbw&AX#1-W;9U-_!^4#lB@f4wc8TJ&7eu_D7@#(vgbJm^(>uG0proo!L_1nvht(ZjG1dM1h%3)rY z=(-n5q8$$gshT?WN^f_%%)g#2%%^L4Q+3_z_Mha#RpEiSS&}3!uZ8}lZ)=Z+rj{xj z=LzD56f66s{tX?7`9t}-1Zi1Itj({;dRp{CIZ4_?N_i_yV0lv2KxU2Nn90HX-F+=T z@7oZ@%#;@s?VilokKZZ|=v)(DXxrs&%^GT0SXx&nB&sP zB(Ip`wwQlY($s0A%Hrr(wF-}POVfPGOUbVAZROV6seRalmbSKo>sEOMxZKzJx@>GU zzkZLHq*Pft0kgRo-^p@Ao`@RQ_f~f3_nP>R`-?ty;A+ILf?(#dtJmry!UP8c z-(Jz&bDVTB-az+Svcem+e^WP9664Xr;k8Uw@2PXN?e(ymu2ZY$NyoZxtk;q3aFkdc z8;6_DEA5T1vbI(~&F@zktAD2-X84)ZSBD&A`JSd#hMHtjB-XUZcdzbR!e+K^QYh5C=c4%Jj zj{kDopW*=ba;Iam&cwr^tIK>6Q?|v}R&m;V)DR;*i)<>!W;)koWRCvfB38?tO|MI0 zR8F{?J~yXv(U6+iKigLd)xp(nY;@^+<4{Q>mr+)UC8^bOTt(@)obAQ)Id9EF+a?uG zf@rmopKAMbZYf~yj&y5)unS6m&$h@4SAWe)K_I_aLt8_QQohNEZZByEz7 zwfh2jB74oL*ln)!1EIUv$?2@yF>ajyNX<0aZk4^`74C#f(>hMfTfxh7EZ0(+hCUgB z4Kisr!3{f@^fhVb<=Vo6YwQlMO=wav6}xJkO>N{=s{Qtn%z0Ui_I7n-bq|G`b0gCe z{oPdG>M)Np>OR`L`WMpSmZti6ac||ql+9U9iw~hX+-D9 zJj;z8FPW)Gb|^I8Gd<8{G1XWui;;E|uGMU_O|{v~bSagU&UF0U;jIwBK0wqaMDxUQ zQ8!yQ%eA#{l>Sx8aB&WnaMer$^1!l0GLOxazGkx&sV@B#f10MycYJ-T80A?$P?&3s z)cKqKkhm&RbstLB8n>t>s^%Iuk=f#7>~F`CbVW>gwsxVIQ>4@y?C-O~OIMY|>6Kz- z?i_pti*cRRH2ZRc8@9JNW{+dOdWEaz zST&IhL1tJ>8_h#C>4P_np>!#bx3eRZ%i3H$@ zbyA+F9#)uOx>wm&yR>q1<-UURRsBhNRkX2jL2T09ijUd>+#T+8`RnpX>ViVKu4n-B zNb0U|t@^F1Be|mt=AWCZEWL^v)XZW8;i;uDe_G!^m|n&66xqx;Xr~Y*y7;{9ZX(*#I$R^|TY{M@c=2w?WEvkbDw`3UArQ zs!w#OqMyvB%wFtiYmvTF} z!24OMnC4`H`K$J>C0&TFI4&%amP@Cbx|Mh5Q=}oRFS8w8qMytWs7ACh+gOLV%okkH zEz-)oS~h|Nn6@L6)d3+?+R{oC9u-Y4Udm1}zhDzYE9Dx8Z*(KSo{VLzlt#T5tUAaunAq|PvrPU6yM{>2-zuM16-li5cv{Fw`4_l1 z9;?q~wn(C_E}M=DW*ep5RFteW2}8M#%43T0^rxkaA16LDez!RDm)PTcc=h#~E0%-E z8QnISEniJyMLl6c&4|3yA!Jv z6#ZOuj{9xaS+xRdo)i{HyGl6pmN&5XY+A|oqdLkQ6jzlfePuHb%{Kcp9c`P*3oQ9| zoP&$REbU=+&iW^7E7svRF=M4Km`23LEHxt3S$2{OW_rrrD{k}a*p4*Za8R78sH@15 z_cZ^>57TYKBP~^By(+ttPL}nRH5JDU-}U*rE0)phGdLWSQ(YLy@tY;AOTGe^UksElpjjY2`NK zT}Eq)F&+_{^C!_jWXo<+2Fcp7dg8+eP-~_qk#IKlIW}YL^2O%WA%d;^IAftLBzAOz z^t*VCT;sp950!6;2j?$2p)fGfHOVr>ZL2=6?ZeKLtTkAQV~y!lE!GSO}KSa zbF5X6O@;Lh`#|fp(n#5Ju39?IvCxW@9pKt4{4D$FSlLB3+`w7(iucXuboTo5`g{8C z;xqAq_C6m;){Cjaa?3W$a>j`Zr3Jz%OAkw>`7z5V7LZ}07hYz_qpavl(nu#$a#4`> zEU&aQgv>ixc1C2i@5@4Jx>|ahE#}%^l-;0j%}@vhU06HwhkT6^b!S{#u!bH z&E-r6PuT_Xan`oz2J={PQa*^aru7tG8Cyw#pr`rD{?bu&0x7o~)(zEHhaEdvek2S`NW9UFpZM4RtWq0sp-%+Q_H-V_l@v8eDpt_f8dpPTBL58|mp7a>i1S$spXbdp})x)Pn3U`=CY+EhSiA$CSui9^^Cd4XmKiLNngxJLnT+V@69nb zHzbeQ9yCzwMyJW!$X;Vsnt?K{?WId-GT5S}Vhqz)79lH>+jG6BjZnkHn_P5D>1yT} zmmnS!Hu8fF71Ydc7553Znd2r;EwS9whE`TgY^59MC@81z!qii4$3t!(7|sRfO(xY0W@Bt}(bn%9sq^cj6FM6n0aWm3+D zSZegKY@(#A^d0YpW!yROhSXgtlPS>@_KR$XBm;-BORdya9VAhx11^&QREMu5 z<#;ryD>~uz=sdC~^Mnw~4045AC5FCVVWdEQXuv_>CH_)VA~R&sr?){>X%yd#^r8qYGzLiLOFnT7Dz|t_^5h%jXIU$rkj_Kn&8+2y)lEX@q!$*f%6Zh{H4ZN^v0W zhDzu{-cQ@fGLuzv=M>)DG?XpgX4*>2SR2V|#uhzb4oI4zee6l;4oujQxDLOZ*@zHz zC1t{4{cxPg9I}iyrW)FcEtp59G*c$i1Z4^>jSACnlz|%a_GA;|ggc>jw2Vyw--|>y z!PXN6Jt;-?@M&fy#7vd2kK_urLL{}O!!5z)MR+>hV3Cp)WD;J+D#R!*g^`moc0IR_ zWu$ru3|5V$A$i+p?F`+ zFr9?B>?_O{&j=Qt3JKy#aWB0tN-b@OnS8{UyG{<{Biti0k4#7P1T|TLXP^drI<=J# z#c^V5{8QLXhBB$*aJrX15jA8Zng#i!P>2<~BOkPsDJ9*+HaJWqmY3pOwuJq}h2h)m zMXpTtgK5kh;2hCv@fo?nd2?~>Q#y-=qg8YVn@0o4EIx|l;soZpIoZ5UBxYXRT>Ezu z(OP(H?ruIQJm))*P*a#nC7BX4*2-?cOr>n8OU@|)hgJ@r5&)?ABx3H)I^90>82AjqS)5I~BwzP^? zSnMID-;>|aYvusMF~6}{+%`!otfD{YH13QfR}vyw%PwOh*f@FMRzE$I%Qrk3Jwuo+G))Ptcj?m=gJ2VmQCwa$fwv$MA0aT;e94pcSHuEJVktGqJ+6 zx!brJY}Sh@rHOPqKE$7*kvStZ%G_~x@w`|`E~8}fCwm5Gqors**+Dq*n%Gl( zE0)qY+#bEBJxM(BW#1rQ&LW9NcbQa42J41+`iZV!wlN)WCmJg@!~-B7?k73)2|2{4 znL5#>;yY8GIF;t3V&p*rg*2L!((e4TX9B6vr{~IEnR?+=R%UPX}X9HjtYp z?JLP;i5lzKSN?MSxr_@pUE;+ZN87kYl7Z|PNhH@0J4h0w zr?_NffJ#OoBwJ3Qr+5d~h1o>>1uWVME<}Y+k_9A4NZ^l%WAItxE54=eP;2^=IAH~I z6Cu6BmFTe#yPti^?4-Tv9sC;K z5qF6G^qH7Vp5UE$GR!kqu%oBQWx(T!v^SH4eT5_xz#O4R=`0+>A{2}jU(reQ26m*Uuo}-}+tJ(fBvmu3kt=eipZT?zLQKx(|1%w=L!tf)7|U|ZYnbcZ$K=%K-b}RxEjS_SDcKN;b7{F?3qla zDPDrD7=P3TVLFi9p`UObN)dYsbJ1}27!!e4Atxlqk*JJmBmK-|f?eu!yu^le#!<9B zI>8KJK8u5qHQ69GrT!!q{Xy^WA3&}p;uztzST3C7mw_G*3G;-905w3$zr2^gl5^y$ z8Cm8FC&Zs*F`AG5AlFGSortgFHYkYc2b!3}Oc!?u)6fxgo4%qu=q56Qe1QH_T8Hi- zOHl(7MeNW$?2O(p?{O7)?@#7DXUFYj>oDH59dm;?^FVv1I9&6xz!3EANsv<0`vH^AeLlDVXqCex+pBt1d9iCx4mVmo3_ z1=1be#3Sfgeur?KoPuh8EM5mb*opQ*8yJMP&=Sy771($tb^(jF;HI$z4PxftZTSD< z?X9EZIGT6i?wMs2vt(JaC7GFGjv;m&LrgI{W~SJ23~|gcQ_Pr{VrFKxV`h>piZ+%ns^0GD-Oo=Yjp2Lhg&&CqvY`(p$p1Cb z9taeD44?W2Mwgu`V-u_`l>^2(&9cUG90u6y&sD%F)K`r`LoJTezuRJ6I zTGlQO5St0v!UAbsrVvUvR+sg|adCe!hPmK5+XdeJ2<&y|7QS#gk+lSlI%&uQILkamz4!{ByPBxM(GKeN(1Ud(8iAXP!&2%022(_%IjmaXi zgBocuG{hFW!e&~T3nh=B4VBgmHDNPY1E@*5kO0K(cQ70oY%8>bUZfp2m5d_auu-fH z3Fl7pqo|52fN|tL42BJG6c$J$12_S3z8G;YXu!Zmq9k?TIP-(<5IKDoH!ChWtTN z=}XuGtw~SfVO8KQNhVj}Pm<5~;J>C$l<5=7+LJ4T>M#)!@G5_HTwK8BLU-0t)Pfoc z#ii_+xKwPzMzdI!gi)y?gh3km!flodPw8&*5hg=N$i%uXzyks#g!CqL=~wI(`w^Ol49~>`sEC=OodvONY$MA-KKO`P)^q8;3h^JIoULc4Nfok| z`Pf=$#JXYzH5FP?f4CrygB7raJ%O9pzK<zpIL7pICn zn2W3?^U2Tn*#oi(d<3YQ_;F3S*R&(IfosTB!cot!^d^15wdYol!)Wh`^co4J3Fwh; z(OY_86f6WcB$5`Gk0e5Gu!ssVfxKp2#q%tK&0=Gb&4*#;FJcbWnvTkewOkr(cJ3EA2@D1re{(<|5jNi#?v_MO8iJU+l{|A}6 z8CixStyv@-uRcYdVXWK%AK)H+i-?UO3NitxkUPrT*jW>;~LNsm`kQnC-g%9UrdZ7 z0+}R(HDF2N5OKOVPuvQ#*iUQ@`;kS%6&TEZ#;hv_f?+3m(J4g34AjIys7+qMAdJ6H zQSumap6KW^I*shX5ls$0ZxYNr@v~#36KZ!Nm|+%rq8^6Ac5L+~mP;ky6E(d;1oDC& zAf3n-`U5wcJR?&nj{C6BBS?K(iIhVS+GZ_$gvIDN+o3;1L8{0x2dj>nNJ6#%_5;ae z)mZ}OBi%?e{J>7ZLl_Pwx`6aUM9v~xNG-CQ*vQw^NM2$z52a@MJ6T62l4^7)S;>xI z4~CJqL{GYdja7zIumsk_8q`cD9OKW1-^dV>2MUr6(hA{i#6@C}%NC+;v}88=SaVVW z4i5;Jm$UJ1DW#mut9P1lOgXkC< zMosj0s-kxC6g8LuQJ8ISs%E`-mpB@ z4(*zTc}h2U3qL{&&=Za<#OGM@0mlRv2;pkbo}`4lBCRN)FUUMPi5{Zu=^4_2j-#8U z^^as9`hW`a)uv=LB20-sxdyS*1~!wnWIL=zU5-Y54<+BiV*pr+{hUP}U{no-_vk76 zU>ugM2QS%hWZ+k9K0LulF&q?dkF9{;U@GPqJJ|(jK}u1_e_`%%8m=H;6~RSvpL~mY z{Yzf2>P1g_LbQ~TAJBUnkv@q1+vvr$$YN+j5{MG}xDrw^uk*ub{R(@%2Jr>#E5vpS zxPTI_!x7XEXzvKbfg1#fBn%=*ZPJ8(!gziVq!nP>AQ}<18%Gx1u&0E0nG>Vh5-7)L zIRH*VQxZ=$lO@DVlCgXM{S&SIHJ00qk}XC}oIrj3LZsL`iOj`FM941~=X;PJ@tV8f z51p{Cbk-RvK@O_~%b_+*WNx+uQrK1Y9>!y)aSi|C5O1ki|0k3~K+6ZBS0}?>=mO^; z9o|3=%5okmlLKTX*#Pg^R{74TbbP)-2|tpq#7(Y|1!M*hp+9MkT&7I(6htTvu~hFYGF zvY$YIu_5+n!)1AGVrx>5d=Iw}uM$ocV816~^z4T+%mzP15h2pL*&UFD{`d*vknO6I zGcXpBe+Let%mWbH(+Q>1X&rhVeP$bGaAWXX0CGkbQh;cxi=6!j>ZCiyeCaN+e9SGU zqDFhdJVebgm;}2}SNmWF#??(IodrTLTRDu@_n|e*5ybSOV=)hE$YqI!d_={WD#P2atf)-viBY1o|r^qju_|KR!lXd_bNLB$;pkBZ3Ak zbe~isgUK|~4bc=#m!Mr@P^y6#D|R5mv_ff@kl`qKU7|$B>rFnu189%h^CP)nMg|%z z@4VZYR78wU!Q4QKYFqGq3+#a&vHdHK;-{f@c3^gM9_<8d2WH?kFh9D?&aqbLn>lPM z_GvU)_B?v*I=G1W_%SF$TT6H2N8%_&TBZFJIZm>%l|keVwrV1AsHp^$b0S9mk!V>v za_MriifqNQPf=&7$ZyihY#UrhN!nq=^F!_WlUs=E_9%4??571|^-K72pVwZLZwKPU zj^20{MxZ~YvmqEQwj%--qkmt8S;!uCh{ay1kj>|y6$&s?Rz?2<%nhewH0p#{o`tV9 zF&g}W(sV{2BxF6taOrwNq!pWCBn7?xIO2B!V(6Ld1Ml$s@1Q~Lx-cWTi8YsDHljoi z$%Y2xXGGH9a0%Jq6xvThQ7$5E7oz4B#;G^RI*U+m+t6?ShDxLqOI=0F)RWi*!jZd< zV`S=zW1A!76r%AZxlLY>v*@dB$X?W^0`>Sca&08y^8(_hB3>aq?qKa(ab#n{8hc~b zvm0%A0`s4bsD+!@YGY)DoiGwMqAYPJcN(5Q2U5g-4Vy80JBa!`iMn5df7elWXR!x2 zF*1C@9viTKeo#HX$A#p;WgJY2PCM?n5@7iE>xNEO8QYZauVm z0*=dykRQttGs)QcGUWGXh?!-mO$%Dn38m=uj}iNXyv5@b)Xq&=%NZ!eRd|7#D#c?p zkyb^2*K&__bV0T8G~i?J0TV3}m>TsHG`r5$T@LK4b%WMNP!i z9kiW4YPdI%_Lxq>&)#4hDnRCVhL#nO5q^dnAe~!{`a6y|OP9x;_dsHGC1&Oh#Cj5u zbQ6v}l+J#Z*I{R3``J(jIf-BlDZ^UQaNKIb=%~PQMnsN)`OVjee>KLaP>eOR$!PT2 zZ}3b9G69)(9@@Stmac;g5{^hZD6b-ZhqC;ImKg;{&}!o`TbYm6I*+V05%ZE~@C({} z6huQc_5k+d6Uts)zPqF%T2Q*fq$z1lzC&5`C`$U^1bg&z6YoZ zV(A`Qp&Y$KiqexXCQ56=ci`V~^bILSEkc<;poibYXDzI=RQ5~{NOr7-?FJ*~c0>F2 zAx%))Sme;zsGHU(>35hf)x$EOC}%WY6N6&BCVRw#G_#OUfTP} z7_mfZDI43oF8ky&^aMNRQCmTZjrWlGC!>vzA`aeTnVWKcCVA|AEcFzuSSkf^K~PgB zw2J~+s{=-SxEPw?aT-cfiAbz$ zm02}UUTwb)b>c-yE8=xUAl>!w<=%%BM4l9RUGnY%$Iy>%qwJDx9f-j^Wa8I&{Tqz= zVe%CwOZO4Ppv=K2M_ojhWKZet0;yJq9ET)c$_I(L-Kc|)_>@-v zKf#tLp0mg?BvWP&$;u+?Lb@BkA8S%$1kA;&UZMSq(DI+4NY=0sOGzxKknc)xqGgCI zA%~H@H{v=PvyKGR!?(z-LHMo_T4(^;JxMMTir1_0k0LrAfTVSsd|!eUHS`(fdW+h6 zjro~$r@$>_&3{muTk<_5K5XX+wkx7WGN2Iim{Pfq0C&+>17#gqQJ0^vCcSJUJ4*fK ztaQIULG6XhQQL&?Dxo}0(X!ILBhnowF^Ileh`ttR(=R)9S3qffpeAm)4wrmCL_W4# zh{#Ds3}vJIR`imO$Y&YyotIL%kBF+gfA5+rLvN8#K#|YB-0kv3%TAEEPPzvpQ@&$^ z%9xPu)6&bB|Icp^=uobBlq?kA1>j$GlsN=-`o(Ae=R=b2%iaPKhe+1WMVl6(9Jv^U za^?FyT+;jtBK5yl>0~dGcU>pa2>#1M(r?7S^-KCQ;~y`#Ys9nMzxXvH^0e|Xk?&J6 z{=eRt^8d|4{D0g%^#AWeYFD}&Wl=04ZN3}ap>@wXq^jTl-Tf%a|3iH$^va3k9lOje zBSosr#=;a(|(2c*}x?cUeB0-nL(L zrg(^Rko`AtEHo1f^?PkKL(dwzI=&{Ojr)A}3^m0u9?tKnp{1=<^^Eis_mD)tN1o`g z+5X*A*4Vhq&Pp4++~|`d-F~B>Fl#NS~bi6q?%J@ zxYPZ%YaXc8!Bkb(GsW0T*r9k_WcBt8wS8KmUInc^mszUwxZzZ}-P%GMC5%$k@J?kr z)qnd$x}R_5=^9kiwmR71n5UekJLJj`iSVbcf#UnX?>u)jJrq7o57x~z**#6UE-1k< zj^1MhBr_l_?@!e@)d1&X&mwO{=iHoOG>P*s2vV+eXB58ST9*9=%bkCEYPzR|cJa(~ zd(8g6dhRm59qnQsqgu*#nvQX`Xg&XEj|MvHy19>&8OlrIVSO#*Hg8?=y>5%Py>^mT@wYjv5KHW0pJb&6}3 z`Ewl#Ta}%sdE6FjlBP;teJB+ajz2UPncfyos~aNAdhxf_`(TkWO*2ICmv#ud6ln04 zDo+bfO#KWs9NWcGei3=e&9gPr_Ei`?!PLTPh*OnCyCMdTd+i@!-ZLPK?t+;3ztH^G03FhwoRef-Haqra(_`yRPQ z8${p6lJ>4~?pXKNY=`TfZ;d;a-Vnxfjmm~`z0}&0y}}FeJ!{NWv)m|M80;*cC1k3; z^7eN#Tj4xXjeL{2rYwELBYoY-?@F=V>=7o0QwskLcgNoBGMDilPcv)ofKPbyuM; zxO?ge4N_Y)y?h~BE9;^>O(vGFHa+2Y=B_5ZI-gyKUmR^|npZFOr4K!0 z?MwNAzR%(eMF)qIAEP|(d#TdGZ`?cKTRx4N$wZ;E5rm;!572WuRc+ro)h+(I?xVL4 zJYm&Ywzn-CCq8xggp=^Z^Fiq6nMWpzBUm#1NcXTo^qSBJmXIc3@*6BPqwn-XVVXA9 zSC#vNzsY@8zoXApEugn1RJl_XLOYtW$w0cmJyQ9-dY$8da-vY*bAbDSvz7NSd0mmN zHgqmb_Z(GUb9!Cb>d9gvad1P$8<5LxF(3a6?Lv)em#&3SU0a#U=Bg-rbNNi8`$HY9 z=uOA*`FwSz;y}@j-Vya|5J~ZxJs!s>%RoA_Y>#l&)yuoWRw6_Tvps8AENO1P$af@Z zG*j5;8m=f1>u}%E2<0Yds%om*M{08_@|3fyTCsb)Q#ixf`6{%&%1UN(dHh+fRNTZ} zQr~1-6+470o~5)8Gm0OC%3cq*0qXJXiO9EBg!l%49~HTSs+s%=QK=q7Mv1@aE@@JH z`wc0YeD1Q6=Z_Jpna5iA>QMoHRqgUt7VYkJJ_Fk+?)E(43*m()$!q6Z`%e0Lvp2-$ zHgHwkQ^nR|zGs*3p0I_EE?Z&B5UzNa331#JeiE(6FXQ9+FvTglAEqgZH%l>@^SEDW zzk;j%5~lPmls#RTyU*j3%gd?qM%xGTYr++V(x%JGVHVVh7v z^uQK@L4D6%E>IC{8>n)#1HQ+kK)k?4sFGaG`B$Wa_pbPY%;0{6`P^Vm!ykoUc*fV` znm}vCV;mJ(=o!U(p)*wWWNB9VA|P8ao^@j;@dX_wUh|Gp+!Ry1d%f}EMY@Dl5p3c> zdV`%~glvO5tP}Kw8dL>O)n5yP0Y^&OUVMT0jGW}|_})=FUkqbwc;kN!7ZZw;j*F}F8F?7 z1#n8VK^(tUyhi8yCUUpP3Gp%Q&n>5kVis3}tc5u7u$VT4nmkUtL^z9UTvE}GNJj{Y3VLGf7^TivA<{TB~LlUH_G~71+I=N5p@+CBdn~QlJ zXh-{6D<_f`bUyu_tsr%2V`k(g&_CH--xF>f0pByp2mr(f5v<#Mb&IkRO#Dper|t&gB~NO~eeE z&mR$^xjTF}Z=qr~NmQN^0PQyz_Cha3Wp*5y=#razh0R zONYAR?_xTiC&c+OXhYG+8pAl@9M_#GFbl`=DPK?gi>}~e#02u5pDS1>6Kj!?ir(xB z7s8bZ+1yQTH4BH9h*S_$A%u>D#!Shzf`KHS52ELNMs7cOB8=ty#K!b2EaLtVext3~ zTNa=?>z&4@k;$YSZZHeelfw`Q4M~o$ncQS9i#`}#5aom&EH^A?2|ZF^pZ9Z&er01X`au=e_&SHMbJI!tRH2;<=(t0T7LZ?Y@C2!%*qusyI0CNU2&h(T;KykbEdj$&XCtwxuD4u=B4 zbh>bh%Z5taXQ)IQ(`_&uGT3GMj~K!A@hOC2t}o0GRzN550d+tXvX!hNN*c$sG?5;L zkqR?e!5WdLB$$n%E*vkd;_`4DZ{SZe9r*FLakO=xL~&zD6kAJPbKkI^aKJK)&80!~ z1NgD$YzJH?CxjhBXBx&{vCc3Tkf%f^jsvsBHqeJYfdr^P|Ds*VSULhBR9N)2={_G~b|z`;#^jE|SW$NDxpX%~&<|1N=(dY#3e3 zq#vN`4knnx;z$Ua%Jqhw;w;h59bxyx=Q!H^1Qu2a$8Zaf%Wgv=bR#0WNt=odnT4Ez zNHPo*^a5!P7a;{la+6^Y*#hIiPNtJ1^cUz)N6?0}1stT;$!sx*8%Bd5fU8f(kVdpF zdTtz@O(KvF){{nTB94@PhtA|zs0}OOGz?^a;+Xj;3nF7#TWEzij~CC7wy3EyIG$}s zHsg5xC=QHAazSJ+`2gd{HArU9;4!()2t2_I;T!k?g1L?~fUJZQB$xh*(zha;#np5u zw7`C!Bx8sTT4T?)l7{34yG#mTGCU_o(ARiU9|W!gG$kX@OTQz};b&3;r(hb)2Mv+t z)+dn#Vq=(v@gkT;p`{lQ={t@K@D|QMGLCYi;XIbo!zNh5GQi5}vA0kc@stJ%l=ry! zn)IWmpdBQk4AQsCyU0%37#yT3m3GV7L?6Kt7)mdZinJLyK$6M#bS}1V3CI77SsB?$ z=FmeJJGarFVLKedl7BE8>qd{WN#G@CK?9A*85~*0AzL)VsIU{|^*|7qSUFrKb735_ z!vK`xSF#$Z5KnR3>f6Lh>#7NXqGMSVr!W8N>?T69x-mFG*t) zNF4bQ-0&JBru3c3EY=9Rvx+1QK$PYcZo!W*18&0{m_g>lYz9z`tcJ0OI6{__t3)Kf zlbNWO0_^2qBpE2JNg~NDSPt_Lhr6K_t%4Fr->EF73!xWYH4%bHI%>WaN^uRn@F=m9 z5u`eD#R!amUN{7+NCERBzmc-Se@fJ(7IBg{uo}*hpXep9v+wCED1kuIk7OYBYe<$P z%h?LlZdd3ER^;o67)=J_s}VL41C2-NFOyPuh~EvDNNHR(jM zG0O=en^04i;VKLOCED?4I16jwK1_#uP|VJu-@b%>X#F}64eK#N^nf9-2TN?iqv7Zi zb>M5n{zGyZM5seo!B?a{t%$Z7MM9DBeuX&Fo(v~{Lq0r5J>38u`r$XyzB1VE2xyPm zZ4TYBjZ-ifc0)3RK_VN7Qg&u#_^N}^cx6lS9{PcvOoL}|9IBF4phRQ^k#W!fr0>5z zunEuwGs#OBdG13^Sj)y^tnh~vb_DBBXWzg=G8K8VD<1s}(l4&PM4c5vZ`Ab>>~$CU zJGu%ODeqt}&LZ}klMt+TG`71}p2=uJ_Ci}C?Kos2k5Ly*h_s7H1!SG&DBlf8g#h%7 z9#RCwsB;GUYQy&SV6XmxkI){rppN%q|2DvT>~#>ik2OUgnh3UE0Oe4GpDx2%li5?u z79V5({=oj8LOolcHZo;8>g@#x7`-XBnM9=dk~^>;Ij=fdjvO$BWXWr1x{%LsMDFJf z@S=R{;UHwAeWd+P?xK{#o&aANK1Eo?k5gg4Zqh1U(RCSD+_KQi!^# zM)Fa!9gqhJsZBqdsdTC<=Y%Jm~`M09rn>6^nGw9h+?tdFpsLF6tl zs6%Ryt|SawZbi%_5M#nIJnBXEq6|aG1kwyW#*1FA!T$S^B}AIJX-*Q5+dGk(SdSX7 z%0;`M25Ij9FDM0%yaqyzeC$M2*&&?FL#s?j8&$wan2HvzLKMU+_dZ?rMwY6IJ|ykpRSBcbA1I$RBij#|H59qTkGN2-QKSg1 z6o)-q4T;##LvR!28Hh|#2nkqz5n4>z5A>=0ZRQP(QWdbz*>DM_BHmtsG+*=pse4*Ke7N3+8%8!&95ks+YLmrUN~w!5Lw~`ws1|J!IWmp-pDgAYO+6|-F+`ftk@nZVDbL3l(FdjPv!9_R z0`TZ}^p-Y=;q&Of3FIwC)0gPAuR&T{@(`pM@j~?LYpA(Eatt1zmQ(TnVU+JZewK!H zUPY^ypw=(Jb(HA=mc5F7$UvPx#j6VBJ@KUd6>A`F@1q2JFgDweu_P->cuYoV1mtJw z7Ya+U%|dy{B)dEZa20*mBJZ9g?bx{wy^)af$N*>Ys?(^)OuSOsvoQ@byXCM0<#+*~ z(b6xGL2k%1FnjQ7f*C=&?CtmAF}7w$FMorja`3+!`&l5*#yC+YSt#2DWCjUesd!$6 z=r}DOl_IA&u^*@9869cgr%PD=6IwqX^{`9s*=6)=X)aBg!%oF#CNgIpwk-XX;1v=A z3Q^v>*m410n~v`vAjcA94KMcM9kx&eCGt$6wD(jwo|QOQV()VF;t#SUdC1>9k=6+n z%kyy3x8l;CRMK2cKI%Fe+bEK;CLvLprIU6h)RK2t^EGIU?D`O~=|SJR1Je3}h4`5j zHFf}P>jY^$Opy<`GJBNZu^rLo zz$g26#;!n~vHLO)=a2f(%Q%y4BxyB{M9RG(vL0gby6Yi-15qP-%$^w9G8p5RgbH51Mk?dLmNlqvE2PSpd5Fkzql6z3_hquLO8C7a&%C~q zJyF`vPr{cJZ(Ufr2sufV`OP6CE*H;Am@AO?`^%PF6XfhhKtC;)V}&#;X+rFk%Cg%2 zowt;F;gS(2{CnohSt9QdSO84^OnL~i{p~U@NHrG9>rX7GTPONL8cOyKQIU(x`wso= zGcwmrl)So_lXm zsvOkhCy5uTTJP|QF(}^j5+&~}#~Lki{_2rg z)+t9L341PlFWK5IvygyCWpWQo@l%i7pDcN=K)ZZilFlOU515T5?K0m>Jp2rwHd$IL zNZE}8^(Mts3${=uucRnO?Ux~Tm@JPSL>Vg}%j5d@DcMhwLo2_NtnQR`?Uc)kvK;i^ z6+c!yl29hGnY5z8B{P#`dy9OP^eDsHLB@jP9I+DlJkU8EFrDF za^t%%v+xd#idq>-dfD?O?gklg9Qh9hrILX%hv<;Q0PBjBeO!m7q|bt6elq^MM#E2L z3juQirM!YddM{urQikT0`zOu8>*Zq)NUY<ANqc0)xUoA#j_0f-;J#X z{wtMI?umhX*}ptcmdjtZi%RB1PL}*Xt7pD&q2z;3S-W02rZbf4OQe$YT=Z`*#Q$N* z|M_6>WmaF5pG@BC82;U%R9Y(~9e>$7_dlx+*uQ6`@&btR$|(5XwTSR#_suV7MEFn3 z0RNTYe?CCg-+yZR@@vcgXIcMyB*+p-c9Pa2Y^_I0o9?Z9bf`nlZT-JnieRJ%yeEB0 z!X{oD@?+i%wV4L8XR0jwSGtk<4<-Aw?P-p2q|2&$q}8wleX^r7T}sc8_nv-mpS+{b zJd<=!Z6kcmR0h$X23t#osnCuq7m{EDw@=7dd{jL4j8?58TNDb#Zgz}k{C!c$9acZJ z>0p<}?#p0C{){q*JEk1(vupa(;X<>8E-I^ET*gUnBHUcH}>^YFei_ z*)`HMg`42(0_}A(oa4neLKWpxMK6aS&iCF_J}8?>#q^fv*E!!j*Kw%Awb0Eo4B>*{5A8A4N@`X76k0E6ZrIAO z5#dYBago&wW@w{Kb9J)bN^fExUOGIhU#gH%FXKU>t)!{FVcE>$D{Ql17Y#i4&s3I2 zhQ=N=XNPSv4-ZdQ2S#%N19+o;73~;Qz4TA_M1gsihz5^MdEB{?tyf#7i znm$z;eQx^)rGmFu&lC@HAMn}Wm@D44FXNl+Jmp+gWC<@h<+`F&7XPOFM*T)VTYH+{ z8#*+6aQM%ry7~ack+9i(iH+ACrc;<#-^};iwa`6SH_ESj`E_a_n>et{DSGG@oGsaY z&wOqvlnQxbuu#Bvqu(kvE83AE=C|r3b6)TzZlTf}u#Skzuhh2`$DmQs%EGRtTP?gd zCEJm|Id4(^S?y(CymhK`wfBwsaQvId&AOfD?9h)je3c&=V}nEB3EA#fU-;VoM+sN7 zp}?x0=h{)ADGAFP;%qLo^ToLK=FRc04v$Uftv;tN4clkF9lR{4rM8#GtG1Xb>z9c` z6^ZI4(K)K#4aEd9MZg7#!pwEW<_A}(UXtP5>jokwkb#A^IZbwlrazg49k z2#qkCUg?TTf4AL^oUghmJc7y2rMg2wUekK#%h0AlrqaWug@Q_1y=+o`nx(JewNh=3 zbBrshLp~}-a7`7b#6MN-Oe35%RXL#={T1G0o*Nijp{Acvp>_=N?b4r7I9&Hiw~Lh( zsosgDJ99?nE-R~Fexh)wWwp@IT_>ZHHZ!;rea64=OASiW-_%bx|6HZ9vMMZ~^&%G~ zxU?J0)w~tW=Y`j$hm;Y8wJa(2gW>?kuIxVx+kWhv`^Y<4?JbM4#qnI|W79-#lk=qT zwA!bN1IabcpHu4kDz}yI7aNe(q^7y963nH_Wu{ViC-$@UhdeQlY^8B@oBx~QLVYuF zxN{kJysHfs@2S!%?gF~j-ba47bh3YjAHuL?xC(?U)>;&UjRR@G_Y(*&@5$#(%RlChA@-Hlfc$%Z;<1T{L~JL za<{)llOE~Xm^V&84XTt8S7CYO3b(n70d)*{x-xRrWk0rgg-->-#T3VczkgPe&FDdz@yqkTdDo3%cAUAoo zu+9`vT4a9nB0M6L>uc`k+|IQzD3qH^Jf>+;ZqM_WaNogzy%7z$a?OkU_Kup4jTNtG zz*Q$D!8Szy13%gGGNUexa<4Z3Q~unV>ugDX)rA_Lt8&uX=|-8q@~i2ICJhun_#b88 zQkSNewG4iZ>ZKW!-zm#P??f%u#;7*D=;vkC_897TbMtnY&R0$k{9W~nILNxtofIAw z{K0f3yRkzED#^TFe9St&AphO#%p1_7 zQ~TM%Q{JSUnU2V!v#~LTBJ&!}jk21C%1M6Omf~`|Nf#1dWcjGfQuZkfGc_sun+B?e zf7;=^$SqL*<~}AG)k)rN9&>m#=Wz8Rmgj3oMn)*D=L`dMHlL?Lr-GdUo{)Pl9+>oG zK!vKRO~sYnGgUiFYK330Kk_+C>iUNpmirR(t={~CpNuB)V)4&4MD8=)%^90jFJcUBAKTD2PK@*K8Tq$Rg@pCP@kWJ+a?cEK3_?K(<*>;K>bc@(w z+m7<8#o1B)!k2#@=4haw7NAyvrzPv}xugEzS5;h~@lV@ns1+Vr{8nt5eU=Jg^8(Da zK<^2w{D} zaN9d(xc&bKOU?yvjl5og%_{D+^m7gs43185T|7bVBc{6fCHnfxMeb|PQf*97l=X#r zv3ifGp{}8OI%!^ckGI$!l5+}vGVipBs$_RR>gOAvQo}5}2ujmFcNJePRsE8)>iV{| zY@L6pE4X-QN}%aMTyr>AKGU%!F_VjCG4!l{xo(YjePkw|mOIyRUv2fu&-(P;`~FbO6XXj zU&O}bguta$@|}V{sN!Xc=zpCak6ts)Trp-%-y+Z`eyNA{^Z>@SFgfpf!V@8&VH^bne7zO z^{RUhNNEJv1|{+1mo!s-SV=b#;Bs^UQ6Z{bKhA z<|rShd6juwJ7*JPdbxkV!~lKD3G-X!uX#g4juh0WGzXS>S41?mwih3UA1Rz=Tp#z> zhhKcd61v$mzHrCyw(m?jYmVlXF8j?iQ?}uTGuZa0b*k-lz$K&0zS?c%r{(VPhk)O5 zrqQjQ4Vr|?qXUMea>XPtKlm*l$^{f=LU+S8^?PM_mhQ7NDu&evPbfNRd0=c7Sjk&R zKRGul4n>yQhX#LbqFF|5hC)vp*%FI(8v_FhEPt{oVqAGszpKvC`Wc@GE6Za-%CZbi zHN)Qf>dq{x##K}NZ5yq)7t<)KwprzEierLQdq(kl|DoheAbWKkN3@qb&amkGMP*fj zk0*C&UC+?xL$i|5XggUE8~gC3vORZ1U0R)F=jYU^6qjcf{r$2u+a7KReHApotrfra zWtr{nCS)PI>1q|8UHl}Xx3Dg)j$xYl9T6PSj>^@nPKB3NzW3S~SW`H}&g!C_WBA=^ zJy_4kswDy3RCnJrV_0=v4?kmpx@?E;EvaDV^M8)Xvds`)2Lua2fz_Sg*scEWZFAxh zY7T#%V?7ggE9#`7f9bx$%Gu@B_?YCPI=;xyZGA%n8btJa-P>X4BP=_P1`)<=-3|}-D1)`9c{zoFZlA=ZuN}fF#$n&-Gs!@Yj>K& zEZ{?4UC?jfYveABzMNUllOwDx-m94&G}ilFz~{2>D`*0pWhV1-Wk<*ACigxJPgqxQ zKp4eMu`l8x=;~L+{@v-EjHO{_%esgg@ijTMeHnX6tmN~)mEQ$ z@OCcSUH#7OPQokQbw|Hw;l=NUigi!hUMuGDp-QWwz&f?=w34Wtjq$FJx4FgM=#(VS z)CfP-%H)jvD={zia|?frY^F%g=-?L@GTt<(Xe(R`Evq~(HyED#TKFf0clI8&#pyCi zzV@@_wbI^Iyh~}z=_8X1MwZ?#YS28(G3H5~cx6_+dt+jwOs)C3{k1yU5ceq|%wMb~`H^%&g$sYKri+Q~aW>UY;90*IV6{Q9I`IMlM71 zu;9-K^2ukuW2pbh(Xb#d?cvj;J$zf`UFGV+Ti*M{?-QGgNgw{E{X;c$p?hX&Z=qgr zj=3h+-PSF;LHKd!Lw|#RQQ7LUJ^=%*y=zQ(xvl6#lky$^P)W;D~j{-!tCZp2dIW`O_mU|1})_Wo{53w)%0VCj~Sp64;wi1_itHfm6eFc#V>mrV&! z8hgD63FxofY#Ly1;Vh02tY<1F`wk?Vf?Ilb32lNb_SeooT-^e1`PF*$DD!wibjnzD z4Rc=MF#9>Q?B&H9%=@Xe zU`<4-zJa5!V_?O#p&cw%k1BeirA_E08u__Z;$CZ!Fx6`0a*%k61H}SSf+VwKmyjvY1)F|9p^h4__nNLdN!k&6hhM!3tp=wpV zdPceCC;u5LBi*B^Pa3iIxtUcuruGc!D-20{piil5`6ybJtBw}duAJzcm0Z*CBD+z< zHK)^g+x^j224;?gl{>z-{~3BWVyAXkj*+a6ZVj~puQ^thrv?x4bg5{1f5VoZkejmb ztC5e6_-5*6=SD=gO^YwR?x!!`$T#Pmg}40^bh?s>L47}UjaxvP*b0KO?Hg@Q$Ot|e znvy;}Sb&A9S8feg&3-8HAItiJ`|%-)$)eGj@A|~84!oLC)A)BrQ>)U*yUJ8QWu2zk z!TE2u)G)CNg^T=V=+g@HX2Jyo^{}jrc@gy1vBERZJUOp+t?!+eKPQGeGCGFt4xauZ zz&OIw&r(HQ2u`=9Y@%ymV&(V!^L_rM>Yw;m0eiSs*|TE@hHE~p<0ojk>S`#Pxb&qb z#Nomx)q4Nedx$NxTP+Q(P?fd$p>}u}0WoS^<(i?e` z48LnCJT1`f@xHM&cCU8dR@L&{^Gpie_40Q0EdBkrnxvuLLjE6Pk=`9yyU^wOt|L&23R_=pt~$uwK$(!s+aDwxN_k^c1o`>K7|yCj z==R(8s=tB5MS|(HS}ZNfZ&RzIhNbCMyF&enYjKrr+lZZW4Hh+w8kYTDbnn`MKh2!#@T2j%f7?*Nphwmv3wnTi*WoD%$AE_I@(= z76%$`DQ~6CYq-<;t0y2JKDQrh@4wF0gqkvzn$LMFItCh)))DF^#`%w4SC6*ZvxX=A zl~tiaT0wcnu6lpuH6fgA!i=nRonL{gf_I1@C;UV=-RT@~0H$m}Zp&|FK zWAF?0h{6TF?VmP<_MtuBuL%Dq>uJfJc}_r%=BF;;8*yieC8ypGVv`pg*M9TK}P<@+!-^?y7sct=ohc@p3DMuX~G zxR1F5Og|8dbB+g)H&~j!jX~vyD5it;RiYgjq(0zmAz3F{)shbBnI0X{oDQ zbcX0pc;~>xLi+{ZO;7W(_2Tl-7cXxIdL7$STlx)mPK4DZXFjJ#%r`$S{iK;+&?f4v z=7zI7q!iAo|93#KeGT!cJc<+1e%=o{m-T4Ijmkyv+CC#H?b3sYTEYr0RQV)N_r5~N zU}d|o7pbFjqnzCmb=uDb--FveIW*rMqaRh_%3WG{xM+@kW8C>UC)QyJfR)u-JZl>< z+&RFqxsB$XJ-3hU(#P?2#+CII_5!6To|lDRNE*y%y z<`~ns$*0}ripa~#=G+zcJZq2mfO50%i1LH(8&&@zgJ12^tBRdbxo?(+Tq>KQT*xfK zW6yqTvFA7STI*!TSh0T45b>pDmDt(vhq5HVn0htIR$d__#(7Aq<)*oZ8_Htt=W`HQ zblrx+V6r zWR_j__0Uq=2pw^46n96DPd=ckTDjAw2Z3*d9ZGZPozGtVdm3ebA6wPdQ5&u9ZCk7O z&XlU|P?nnEjq6=@U-ihn&#p0^D2jDlGDa8QF;K3u_H{{>sNZnp+||_2mZY4d+gPLx zvKN#%o(6nhSgCwL)h_QJ>bfb~Di5n0^D5)|oLohG)X(Lc)Q{|yOlDhEWiQPp%OYB( zEGy`v{f#R*IzRii;+&#YK8etzr-{A8n`OAPGrXTQ=E^-v<5U}6KZo~LH6(|;0~B!@ zmpzNTHqOuYIL9b|(7du&ajmx)HQf~9+UvfWe39Z{X_~^{__6Sca*Velcx*lV7x^GZ zTY=DN{8x%&%ER_}HYe(ZdQ|!-(%<_^yI2ExLf{O~G)pVP60ouP>Gdl8R2HP}6S&_O z>t0>ZCG?qjhIg!^ig9=Lb!C%y#pi2uA9ui|WRJpfECY-0`g^nk%4%5i%)OkwjH0+q z72sFbQJK~>9?gqXbO{IjWY0*qU1+8cFYco*A-Bqg8cw*j*{E_K-J%?ud&x4yG}Ae~ zFsoYi=l+4cqdpcMR>xSq#%g}!Oy~3qvja^HBJQXTK&mF(J~!yD@3{LW|H7hCE%wzb z8W_+x(@%J%QG7N>MQ2pjj86Yw6rE#xU2V69r@g%5rcK+_w%w+-ZQD<6q_%DMt!*o9 zY9|d>lI3ad?B6iPxUTa&?hkiu`MkS1+cqx7SCnsC?vnD4M&o(H;S8SghRFwZ+nqah8qv zvY)48rm+7aABA@ZwzRaPTED1U6b3CXX z!^f)+fsbgCF9kknl!~mBQ!?F1Tm=vPmG^iBS%RuEr%N=>}3bsU4tAZ85 z<%Nzv0I3tQhsxTz`jadf>b>tQ^(106^C5868~{|OMg&&5nrMRD**aA(Z7gA4%NF@& z#2IOz$76j1)yh9WSYx6Jn#b+V=5Z34CrpzLV1BYH)Y!WmTduXEPAcjALiay&Y4)?o zYif5rRM5e`mg_D|w^v3Q@dx~|wuZ&;=6v$|tPk_v#|%S1!hb^~eKBS_+o(uSZJu|I z`OO89LzL@gMP*j@D#Vs>C-=giSw>?sGWJq|2+Wk)=4uhP=uM#k%6c|bg2V89N6f-- zb%4VU5C^rx`c~*Q_5@w&+hF;@{o_}yL1UU1`|f5;Xiq&~9N%+<3Wl|JB~O?={P_;YFiWHq7R+0y`+^jewr`QJy2Z?hkAgm zX)0%NnX29t+71eGu4@su6`#y`fn8EpdUhzo?9A~082p*{ROykb#-#-!b+`STJO+8| z2_QD~P&}A6fl1ds{kT>-)q5-+(61G=k85GBgmziRDZJbbz0a<5PlQ+G4MOY4sO3Y! z*KjZQUtjpJeTKS30nT%`a9;H<;A`^9v0W@*{r8x$`S++-w)yfLyfwQ5 zc<`@_eKkE$_)l#XIwRB~%D7fYb>-Pgf1y_0kKTcy(cWcvndru9W=f2MO%T%Xunu=!R#XkoU^GUjwa1^O5EFecwRkI^wmjDlx%fu+~ zk?}yEF9zl5^l7ub{xLHPnwJ>)=b!t31rwd;&BUxDjz7p%rXajTs9=|vQhyGWdWH67 zcaS;0icA_Q`f2p0-4_f;PUlOiuk;@ur`EtTe@2Z+tCEFQijj_ngILJy0(OY@eOXT} zhTf?oBjDe4VtOjenZ>?y)FSna%MLX2l(NqCFDp%at>QidE`-}T9_2NrF0zNHZEUYf zJ5tUw7X*hnpKKt_V#Ym+o2=iO4<8?M^H!4+se6lo`P!Pnuwle1z;81T?>;5 z#9ZgOf-Tf_BF|E}NUPV=tk#&`=E2N%L>J38Bv*d$^Vn-iHAzLj9hJ#SfA$YllE5AJB9#{S3e6i?%g zp_#xqE+F1=j|gpcUjBqR*CG$mX4+88#QaTMN9w73Ao72o`j@U8PWCv8pJgkdG`q=J zO($)MUn46N{a4MoUyloT6B}h!ariX5JYDS)`xu?!jZ^mESHw1?5EIN9s#LTMW`EO? z&qgNX)^&a7N5>q`?Ppr$E2bRrCa+A5+5Xpb0Yd{kH8Zjt|J!{ruVs9D<&6Ehyo(=-zQj_DK|&9%CGfb^hMY&l z3APQICmT!&ZGP4e{B>wasY}`I5ZWM(B=TUOdt6^`m8T;-HGZ*JDo`4^i{nw}e}9fi z!K&e9sD+q($>*7?#YuBZ6b{DQxhB1MWcRL!4gPO<3P(St+7GwPSz@B0+@ zIitALg1m2oEM`s}?LM~6evZr*O8sh8%%G9nbW2M931+-~^v}gbmIV@=nz=evEA=3} zMs4cizD856l)>=8KslG-KAkrVDr0S9jxnR6-xT~*$b<*rPj!>=mTC^Zz{auzqBmv3 z;&5XSA?TC-x{EY{Tl`7WSA%W!$M{$EjDEqhP&!(4Bz4nzG^-4GH+Cf9@-#O5Mb`hy zh-!eWk_mXOu@}28eg?GoCI7DYdcp_z1Ess9CE7NG_o8hjU1%7?=-Hvp!YZIP)F5)H z$4>MnI+D|f)p_3781I0nR_Yo$!uKVwSy{=niEVw4VTqA&;@n2884lPY3zRzGcEk8 zYKPo?fDZm4*P&DW|0~Y?8Ro8t-}WN@>naxcDerE~YmYAe*3;P?;2pMYLz95B zg&v$|{pi~OuwE86EZRS3W59>}3}67sx<tc_fz?K^aWky({5jw#j~#*4r^ zGRJWV(v2*>f4F$r-0=NinkzQk!f9qsf@hKs!M4)y&`Q@6>AIPjGr80_F-0s*Z7O8P z7&$fKia5z%TKAQn&)x_h_D+o$pV;^F!H5yMTf9kh$f$xM?nt#~$Rn0xf7oY{ZFIvI z4R&^Z^AEI5lKX_uV^&hHsyi^vR&ozX; zSUaE}uy}R6I?}OK=#2G2{K_7^vwDshfgDkeOL@}&!YAX)pmlsrvmQFCg5&Wj(&EtN zg!Lj5szy50){Y0hHpo-trYD-}4WFfsN*z2q9gAt3(KWc6+-4jM7mX^VR{$sZlaMk* zKH{q(GJ*DhsjAI#L4NMQ#cK;*)1`=5>0{}_F;MnZ&cy=Wk zu)ilfnF9mYV?*p!Z5*^p8Z6#~*MqmU+xkd#1C)+_B6o^=xZ!Y!yaL{4QVPTGi!_kD zMZ{@k+Va+{sT(_^0EVSMMm_NjMU;nv2y1%0uD*pU_7Nr|^|XocE7! zA@Z+CS?y^4Sb7538tMR4hGgIu{E0{;TxviHn!PNQl+zK5TwPxKF|=3#5R{K+c8O;B zhw3)IiN4hwEU&`q(z}1x7n;Q$f)Wcd(1p?S49P#lvMwWsy%d^puXjuavwvSDh&UI70IKNj<~=MF}uNc`OQeY`~<6xPx&)UJ`%m0 z4GH0JU-*S~)lnWE>0L}s!7c7{axL4k@Kf-KBU+h{TM=GqNiOtRRg!*X3=jVmk}U?n z2Rfn~`AXm-t+f$>ebdfo6qozE<~x)9QR)bwC2^LDmMVi0#m9&0y58vyPt~}8&^Z^B z^~7J-d1j}V)8n2F5-u7ek<=} z^(-EYFuaGHs5m{VWASO>Ls0|OTLtx9e7JteCjLyZiNzugrFI$*v38zs+T|Lf)5Zy- zxF=G%$St{Bm{RgXXi?y%xFr6q=O-4)--L@=PI29h$I1csHS01tM~+~#>66T1V6@zq z+AeJs?>ma--L@%K58=&uAn&!L;}-NsMk4vZJ%CyLx6EItjEkxZvY|@ety(4HHPOaw z7TOOyK{rta@&V6SopH<~nj#auBlQaWmS_ZOP0dy773A8I=~Tp*xiESKwG_NAf6|MR zTZP$q4V~pqqX`B3^%=I^uC>{N{@sgxPgLe^Qx~#ZIC>OR*6$Zj{@##U&AoJ| ziseHAw=ZvIYzNj4^&#>MhS;pNLi!TBLIsW-p>J>vR@Xr3RbW>?6=+4bgla&YTqb)< zxhWiTC3(wQ#<{nBD{d)=A1?He>a5FhjC_ZV7hegbtw7kb0>XMh*=VnUPRxidwZylbi^KNuQ0yA{ zhBm{a8k?De_+#G|MwXYugS2nzdgBQ6BQDv0kE5qSQ3WXTP$1HbiKnwpJf&ot2-9 z+z32Da)m)gEa}kw&>qh~`(0ZVum;uLKL+2gjK|A6x?|luC&&fr!>CBEaoz^}P;5!% zjdC)#1)mVNTD*tU=B;oY`h-zmnjkPSHg8#1HLh>YeA{?^inX@zFe)c+E7A=<6nN!Y zX{2M53{vfhr}}$1YOqDg<+eleAS7LAr&m;(fJt##d1KhcYCCX?Rx>i&?E531z-=AW zJMz7pvGxJ(&4^+=4b;-h;>mO*HXp7dSj76wEqM=`%>X&;nGX6Ksu!ip2YArvP9xA1 zvQx%o_K>3`L(r8o?kY4s8tl(>m5Uoi_y+27>mKBv(XL3BBXk;Ly)akv3l;A)&dNH}bU9p8a;=eMti*eWg$aK#~~8Gg>aDXKZ8|C$Hf zfr>gK3)^{Ed-78;$6p*#^PZk@w5Ge_vZwef-VSgO4p2x_~4+5YVb*SG<;XM7zt z*1lg1IQI)xg!0B@Y6w-gU@W#o{U2@zLhwALu9k#0K?f-PwVlFwYB)YHR9UM94523* zm%-~ovVI@O(5e6wNMt4jCX#8qg&2c)q$aKwVGVyDZf#cPr$hN77X& zAT0nD`?t_3*O2hi&}{gOc7s^ME~Q>0Ujn_5=G+*23(HyV6XFPj96yZz`6|XvGd`dw zFb5+wug8tXmM9wdBhFIR!(Mb27ecEFOD$VD*+iHzSxe}TT6ODuNC=cQYiS%tYZ@{c zeu(^!{Up1Ivu1ncIM|k`As)B4H|=r-pqAltO%eNINuJ~6<*0^%YhWA`9Jk0ca5;>|L`X8Q;)3wb=3xD0WY|j_*`m_K0z5{ zKEW=Csg~_ZQLzh(`@R)3fF)TQnRCD#__z+q2>izPfLVs#RIr;u9p@H1f z2nE%^ljL-{x-v0#0&?8fQbB+p&gOg)7mLu~WuSEc)plT0F{k-6I}@LmyVJRdY*o-G z6l3lvlb}8fUh?Im?drBHSPX5*pDL~@!^%2&*t<#@;9!VfP)w#HvZ|6{*#bQK7eNgZ zSHQ`ZkKwY;#sSsx(%%7);$NqSkbb$}$#2XnZlhywC_Okbp_|+Y4ut;*tF2k!K;sYB zP0g}409R5^`K@v#q9lGo8;m_;U#Jh*XGl3h4eW9!8a`+skf8g>xdo+TmvH5@+7atA z&eK=aPWF|~_kU(b?)99wb8jEFj&?BI5PIuHEI#K+bp2Uv>#Nc{t6V{a3Q1C|5 znfXQohlT5B4!1;!dtxbnhBA`c!@8&)xLZ7~QOIn03;Gs1YJLV9N+;gU)fq^zxKxH_>L`h#zvM}O^uEmaQTSKYnR`|6LmVbmpkwLC?&!2ht2F-Uk7 zOfhrZgPoO)gL;&B&OB%>j$c6kgaEuEsDYwUA^6lb8|V&RK=O0f;%~Y3;BrU1;68f4 z^k3d;%M&vN$^cray`duTV%~u^2J?+!)}lyxIf2enml_?d4}<5(Tz-~Z-bH3}G1I*j zkbHEYXNU8CL57XfwA@7zpF`DM8~pLcQHPDK6TOH#uK9)c*t-%p{R7O0VcAg$e=6}{ zO*u&qLtn}6xheQ{`7#klUeDf&%{K-yQ^L!DglNNeGwz{xK@eb0g~Z6l>Kf&K=$Gb< zAL!|cPB0AWnAy^=V>kHu&^=2*_zq+B&s5jBO8^DxTH{z`NvJ`(8k3y!9glVW&}U<2 zXa@3?41r^~6=-`s%^(RxYL8woXomVD7qZv2+u;yd6X1yUw0S7eF(K<6| z>uBzfHkz8JVpKIKM}g!l@g8=`H_!GQIUzm)TiCiNx5FJJ#-%cIeK~q{d&xjW>Is;} zh{XE8Pi)i3-h65EQ+NgSMx099!re?>8OBzKErrGVs+ue1jYK0{mPXMButTL~r-6ZySN|M8*gUTzo9rxMcLMC|U zX}hcxk!h?c&(#6wxTM#Qc-JKekWmbjvuufN|bO>MSUkZ#aw7v-B z2sZ-j4M*WLw^1Kww$PnMN1y@F#5hHR;sqfMnL{s9DX_G@jEL1I>x1E~TpHOlR6o|D zjS0?IkD|@dG_F8V%}kEMwnIDE3X$sr_tCx1lwc||T-;1RQ^L3nOtPHqz1jH_pOm;}jC62m*~RV5oSDbQa10v}Z>IvRz~60lws>Oz-A;|qEF zgH{$ZfC)Xir&^VpWUo6CZ7Bj3wckQ&(!X+QE&oN7t* z5Y`UVb*zcrId_u7ihig2q0X#_meK4q&}n(a4OPFhk1SJ_VWf#1;q7BNq8@N}nB#TomptX25Nz8+2v&Uw^ysT)C5_Ejph212p03i0R%@&@yaya3q0*wu7t87ixd? z3~*e#WWNjCkPRqLxa%m55=L}5&4?lznp46J?Io1Cwisx#oJ5unq=G|?KEMgGjVBc> zuT-IKT7SxSJ@1Hl{E6u8*eefc3=eKD+JlYoVx?CSmjn164bP)o>d}b#$q_uh4*9i1x!xxJ^C)JjX9vYx_H589~N+ z9+>R7seNMCN~iPjn8l$vvQ7>4Y^RR%ugPb|HYL(bg0jp__Iddw!4xBh=4|)%f68;d za!h@*H1tV*CZ8nQ2bQ~7ZXXrzoe%7?o|m%p_r?ZxmU#huz=p{gxhKHGu1C^SbiX%` z7-S8g>xgZ>G$WHahTO;P{-1b+R?!&inv}Z^eJYgJ-;l$EU8+w|00zu558yx4?%-hP zxON`U2rpd9_YZySOch6f$JkQj6yER9kbX*N4}>On1m%)&m_ zfa|h}j?A$^5zBwW~{J6wJJSB;B8aR!tAGg6@ZaCd6A4CuA+ z!}443i1GwG?y6|!N==}9+F<7yuBkc6B#dABCT6gd?S+}e&;^pL8^tV!}EbAp)BMBAM1Evig+<3Qk^e+vj5`OSZ0PeqZhiHGq8Q( z=jICZ74*(t)H_^r2W&P1sR3JkH=(*1Bj)fVQPR~NTO{n%hG?IW+E5<0QvC*5ZS#zn zU_3llwb_Ay75>XFGJB%gd@5c~DDEz)R}i*ohPX1KqOZB-1h*V)uFc}z%t-yZ8}J{3 z8X*?FG4Wn{AtsQ+^e)sTlhoH^NlI~|PC+#+O=r-?T4is9bu2mvJtCc#cLHn4hSGK8 zq4L;&(pCm&g_RG#7FRG7bPhW&F96c;0qR7(L3pwjL6x&q(_`dQN`NXawF2IwBSjr} zhfmN)`jW9W(F=tyXiZGvJ6MtmB>b7Ghz?tv)D1{y_u{iCUFZ(ZBLezr9zs6qkEPc@ z2WLcZs(u=^18(!2`5(5E?Sl7|DwsonS4tewKbQtSL?!gN*(rNCw1YeXuD}|p5bWlj zM@OTBecLfjeixqzdBk4gQSCmJ3PJKHH5W)_55SpHajGMEhaC=$6^?)-*l+L^tdH^- zDT?HH87NtgrjoF|;tDKTa-d6;WVIh<$1fRe^g&7`%WyMIUPHcOn?UY4wLBeBkI|am zE{A-7fggacUv^i7pscWhE^g*^O zzZ&4#6;=s9Np|xzz`K|oz%>>McF{dO7i}zm4cG~M4!xtMX}5^O!GYo7(pCj@clVWG zao1YTLu6~5{Eqpkohx$5KL;Ku&9MA%c9W892mBRX*U+unG`yxi3y0(`pha0K+}FG0 zPXgM~b+|M1-h7|A4eG0H!OI8QK)Q05X<$2>cT0aseT%Fu--i|cw55;s1v-SS1U&d> z<1X~vl;yVQdab|u1x@7-V1Kz2`Ku|nS7n!#AL6&5KH3M%a^VtIK#KfkVp_pQtRcoim*KMh ztKcqtxA;_RrHp1ag2%-6`X1P2EQCpZ0~Ti_=@ZeZ{+;+sU^v>6{^j4}IIeZpqQRa( zyTU#%3niNkm98i)ZUK*?pWr=83XmnXmg^96g^y?p>9+cSj1W4>4}dhf9+D}Y)te(O zWLU7f@zZfx-(e1fUm%mf-}W~E#_g2z(e|+`^2hjpN_Bwmt})Urpre?rWkQq1(*_z! zR=UA4<{~sI_?(;)dP7cT#;7%;8+q1S9w-S!$hlp!QFHi}2JZL(JqH&Bzko@WZ}9s- zv2YuCob?a9U)jtRXU9N;?K$3;p~lKg<`Z^~gMiiC2@8X?G)LRMa}V+H%1Cqz)CKB* zoiYgh7Wo8k%%8`y*gAL$Dj8ozclL(JokDNqoPmT~*tk$p>y9f(y0-<8 zOvNM3sCU>M{}l5v)`Kiz0ziz8tB~U=Jcu2uCkcP(G#}*HfyLl1q?iVBW35kx z{rE6NlQ_Jt_{+5mpAk*~>zf7>A^l)r-vBh0OqCz&9o5T)9%X?)k#=Z}a+G_GUDQ%Y z(_AXP<1D~g_zANg8P0)PcPk3L!$$;a>Rxjj@*!e#;4pt#yd-wDU**DjcX0!m503=v ziweIPjmH18^ySC1r-_a9ZfKT%9exLHSC``dRwH{;+S(jQ6z`dwAXY||FmjaPrSTv(N-E7AP+$_MCTvR7XuRE^DKq2caieXDkA~W_z067yby}w!Z+Ui$(Di@QHrN z%ry6KD^)O}1o1=N&wdD(uz#guQBHm@Hld{O1H2kI#GK0RU@Agi0}0?-HJ|JywU^dG z`+@23)xdJ{81#iifXC8(B*kbfM-XG=Mc5>rLzmDsyo2;5NESNL7z4y%N%_&pE3k;M zne~gkV1U9b=h^@GzfvztgNS7RIrN&i3^D1)ayc*pUIVm(D~1odQu3#oOD$1adE$TU za^SJzOMd~o30p0KfN3r$0bii=dTS*nycKp(zX}PuhPu&u%UB+o05ro_ z5`DCWT9i1Mzi01c+bGS+YvMQy;d(=P8#Np_tjEL4#d%Pyew};_uh560J@|fTOZAc2 zlWL+LkcO%MKn2wJ2=b=c9R8qLIGMbv{G!T+_ra?%UFf2BQ@<;9;5^G~d|Cctb+EH8 z{t9gnD#K2THjSOYCo$iY(N|g#OSb4%vQb_>4a_sA1{-4+;o*8QFh-pN4N$gQU#Xk4 z`@&}MD7Zl=1*_)nft1jEbpX$84_QQ?3*1z;h$LVI-XT|%cGx{%1;9dmLGMUkfl~T!Er4wl zbD&O`lWZkyR3=0|=IdGVkWq%kXaQ%#ZS|7s3&2FCYRwfz#^E8r5$|cJqi#v%ka=?5_nfe!1qULLRfPXY)T`JwvvM^Yy;wYhx0|dyS99OS%``nMh z8Ok#5TzI_Yl5iLtl(QD!=>DY_4fPFi^h|6q)xv1UO`ta!Gl*(HF}Vw!32w&4LM9r` z3^hlCQQSZ3H+T=4!AG%ksdVTjP+T0O?c~Rrom{g#KUD%5XC5JjDv79D`>2nF+JXu` zRi0=jiyR%2rqmdJXZ{1HiU;{Da1Xgfh*mpUHV1!V9W3V*5K&aqRsy&H)}z1j zI9QcvW!|^_lvbKgxFcF+Z6ZtDkS5gj{<9}TV+tAMxc zS<)vXq|CPDn+KIM3Zy-vmVoVzYG4N_LM+gi;~2ZZx>noImzRs^ORqTkSt zK&A>>Yp8k1X7#JFu+T2oNgu*(O7yYx)*8w)#nP@F=pSWhAd6ZeA=n__0JA#g(W0o& z;t#I2MB7$^s}x%L4b(#0O9!FR`aIhXX@+)|$kO+sBwH07%|F5++B9!N&-j5*8Fvb} z#K;rxs$-NM;29iJUiek8Kb?q{mM4QzbaeyInr>f$<{B@3<ZvecV%QGo zqHxprj9k*T86}JmSQ-DWHv~dvJ${z^REULwd^4gNf_aX~iDV9A1rN#>j7mT+h|mt8 z0(uKs!sBpfI?22(-2kKMwt@}Jr2h(Ml#XyV^2bc$C!4A4exx`#Mhz1k3SMgf@B;gb zQCUv-yxNd_Np=s;lYcmPup4w1+Mv`x8<7pIliB8Mv6z}-U!y&xq3=mWsRngX;x!sb z6%?{@7?4imqool7soCK7SiA5#ZXWfXKaX^Vp2>Ykr(PfJt=@v)!?DZDYb`a|Pv z)Ahd4EU_-OjQ9^g^sUllBp!Vv>{90hobHj9HKC8;vqBd}V!rzh1GM=PjAS_3}l-D5cwB6t0_7Mw!rtAdC*#MA6Q;40q?YVxD1#U#wZi% zrm}?3W0Qo*0IQBB8cS6;g6M}$SF0E9l?b$l+Myo`(|S8r0v-+gDs->mrp{E55|H`W zWJxf#>J!O5;6Ayo+Fjn_YQuC3{|PPuOY4=%OhaZ<`Nr0RNP-LztF!{CBD&641mA(S z@<$=BS_f!CK1Ig~PSr^SsEJ@vnTMK4H_Z%lsqmU=WF8HkaRh)1d@EptUJIRK?B*^D zQ=n|;XnkN#f@33@1os7Tt}pr*x<%<~SdY^;2Af3;!uu)^un|v_i~A4Y6k!t&`%9X< zbr$lC-5j`JQ*7PUkMcRKF5pFeVAYV}f$>Nq>?V8}93hu6BP@1(2mc6)#g6z;2qC%{ zU(APEtkTtVf?lMhevf@d?1KMEy|G_FMRHhpw_ejRU;P8vgfb9|)`Q2&zo0qPrQlNh zi;d<|Llv=c=t;erHbMQ0b^t}~E!+VARZv-68Mn_^{)S07TIHviHFaP*3VAF(J$u0W=V$3Q2MTHbgm3-OK@@ z9oDN-l$we~=3aLmP}};yJJyBH>X@7hbiyb4=HdI1s^}kcy}S>}0ox>R{ed#zMUJ)U4KeYvLAdPuf@JPvV&;UIiRHSFZA@Z2zMb0YXm>i;iLcfD`EDemC zh5axJ=mqW*iRH!RHvS)zO<0x5DSO({5aE8LDnBR9nz5&BN4f=|SOSOIoriBrrsGgAqg% z@nxub_`MsZG?_7%W!A(yVA-C1Noo0 zLhg<1D!kZT<~i&+R0ZAz=3M_bB4$pERE>J(w{)8o@(s~zLiFpr+oj= z99Ywm5LAc&-daW}pXFOrF(@efr&S;y5WVEhY8p8QIb+#SupAFz9f3SF1=yzd@l_-V zYdnPPFN4m?2Y=U}u?dJDCc*^WA|)fD#Ge-OirLfTiY zj9 z`5MrSPyqsP)D-PBz*@{lpgVMXbF43AB)?flc9#LG^+gn2N|4 z2-708OGrGhhj#LR@w-GL9!6R7uED7%@D<=})y7_bwHLN1+1focSJYsjPxAZIO8kdlPua43uaz)Mh#C9o}yrXJZF{Om~Kj*K&Q9#3rkT;AA{tDPu ztoES#>Lraz%3@?2(oVdine=PTYYItq13;?n06`axPo#`cI&TK0_IV zH{iPAA)~4>47dx<7Uo+X8$bBF^c}M?Hc|0g>hPa{59V5IskoUqMx2xz!d*j*&9777 zV}Cg+6|7lEByIc+@Gn3l4FK(hUBD3yu|(^ggt5>zsK0jwewuuvMM6Ea^~P}GyVNY$ z)GPxK?&=b*mDihrDXz-AQ|~~WH8+uwiY5)C=U|HvBm9@T05TelbmAW?op4;gu1e|) zZaliw+zcNSm#DvOm4GYKJ^u#kFR`A_r#A#kTbdi)fY~%Cw+C%tUHTZZ$Cn%4;+PHf zfQp2g>&f(f@jfz%jfhGTN{a7nyZMsHDB}myQ>hC60D419^qz%&bPI|wZ|ew_CF&{* zH#T`~JeVejp_4#cwikWeVEA9^5?i#nAh-t4WSZ$cGzTFXipv#TVe#7onn?R}3Vel7qjs0Qi!QUkj>KMmZ+ZSH*OP~YP z2b-)66QAMFwUgijwJbWmu(!Jt(efiGSDwY+wfqMQz+`a@U)|gUJw+BkH??}|3cNXy zX)ZJ`Aax0y4?xZI3-*UvO}-)NFYJW>sSwu=exRm9(a;WUT;QnY0yop!m7tm@PjfVf zGqnriYx%wtGrt%T)D?fHrdY3I!-H#q&sq`Yx!yY9VrZ=tbc4MKwSpE&+sOB!9{6>$ z9{3BGCzexRz}G>Cyut

j8j!L##kb8dhba-aq=8;pXG9Vqh+GRNb$ahvsPaPyw#v z^B^(GT&A;p(0drkC0sO@ko(M=g|0}Oo0v*c-Jle4N^6O4Q%iu| zs6}8ddkUh-BESK@n|O;FAT%L0{W(_(s3A;t+~C##-K7e~DaZ!{!4+5tAFek6$LUUM zNu$5!F&65*i6!A@&Q7{ZzscZIBQnL94*Rvig)Myz87#lyCL!C=EVYC{l7m!?KPn!C zFQXpsNCbguwljkMJg7l{pW_^5UHg{{#Du zYzys04-+ks5L*@*iLMcz+xDpQ_-&AtXy=2WKgefdp~Q!W;g5|(`juSSNCZ0SQTTn# ztF?hSeG|6JjFitZ{X((GJNT{|h-&CT7!@wS}|wY2p*ZWo-n`)h}~X9R=(vKm(T+ z8qjhvS^a>O(c6OUAirLM9ffWOOOh9a-N*#=05ZX7FVe1k2Ckd(SNbi}FmJbIhPVkX zgAW2w_NH1A+l!sitN4bIp9K!;6&j__045>B$r*w3DoR#m!di;#2LGU`zOF)vzJ^+>#g2H|ENKwve8!G#`Mc51+0QFbb&c6lx4!ueOxp< z@W+vdWJ|0H^1&dWU-U2Tg1wm5lx%=hR-S>2^hj%I{)R?#MF0Z2DD*)?!IIbn<%~8R zybb*)%u;pzvizT89~aFBDO!Uf-fMS&1+a%0v&z|C7fEG_zYdEmpDq%FBoQr~^z&ZJ?8?E~i1Yu}C$>d?~kLm;&2*Th%t)*gh52il`>eUrHyJnLKnvVOdy4$}KZ?#WJc_dm!{?Qm z+1&^d++B)8k>bVOwRmxNin|0T?(QxHiaW)NI}~@1k=>n{SI#%zkNinCyEA#ud7k@D za$OsYwQ|}+e#Bcx^mll{-^Di|lflK7*4A(V<$`xDJSzEBbQ(COI~qUisVP6wqQOex zStMPWZ8|2dxDrMwV~cV3OBUZaRm*WAYKM2R9>JS|QSNnJ*lZuVZ1!?C1xH6F`1{CX zqAjkBYHY{Kfq_3lcGRTgWsx~UJ9GeII7CtA>d$zJQs$oTWO7LlJwNzvX0O3Y{SxsKB1pe0%6 z=AuJG9l%yu@s0f#|Kc8|PPng8huybRVhw1mQks{nk4_Q)I+_U+=yJ6#sz%Zh^BiCA zx#-96%$Quk>$D#&FX#DpIdSAr__6-Qrfen+sA0iF@HVr zHs>aO@b!sEEeZ!3d><4sBQgeBsV!e7MKup@Nwq3eF!6iJMER?Ke_T#yuIgZng=}_a zr?HXRY@B!x+FM~ZP-ft^TE;3HwmpUK70ILXFn#33s<6Z+-IRp*a;G%PeJ;GExz>Zbc)A`Z$*iS z_U4`FCH8f=OnXvk)e}dZ+Nm<@e#wI(RtInRD0p1|euE-&KT3GR0D zTAQNxC!BMC4@`Gn2mImj^nz2$BH2(#4)p8?UqKyUMB2|mI2@Ca7aIAj;Jjwif@ff2L5CkEaomtYIv+cy=xSWEnvyd$!>pNISK zc=v}{!Cub)2-S=En|6~e%(7x^L>q0IX+x>alF6fJ3I9Cz7rUz3tkSyuV*K$ptefA; zSx zBq8vEZq$#GmPP#<4w|{?*zii;3U81uv}!Obxn-0H8)}Q#3gg);jB!G`4Lpr}YVT9Z zXpgIWU(pnEX*AzzHjaPDMP%4{LHC4P8dPT>^+@PbFFJ}>S7Sq^%y!r>RK~h3_DXFG zvU5T!_8ccgy-R-Ju8)c`YU2pE3HwVPH3s=d!o#@f+_lE5M5q}onKCT;h9~&1l-GQ-+^z0(xju5TicH!by)rmTRj}Vi_WE&>ZR5k@AN%(OXQqsErsE12 zrQ6c&nA%zq*Er>l&|bd5zF=ncwbM%=qZ=Q{7`h(#JCs>n(_Of0c8;6CQ_vBP4b?^t;9vRcP*G@T6fo=ApGj)EExcD> z;PdU5aui+Xrj?P#5^G|@b1RnZ*3DEs)?3Wbk@UPS?!S`U-OZV*5PiySyM#ru7x{8Bx}9|kn5Kb$4aSpy^?W%3H)-2VGOs0mBencxLYMKZvpQ`Txl<3pHGCd(ze(S)e}0Avf>n*ZftcH(l6#4)f%^ii8DPa zm6(LTdZ{y)%~j}+RT^6B59%J9>|Tx96mH4i*w^Si=r5a^G`W~>HEu9c$`6oH{pvmo zJV_#OT}?Jh+81>56t#8J&|mJK<}~LM54hiquO!Z{8hJ-Yv%0t;A{b1Mw&yQN@T>by z%_I4J_1%gpOgmFQbaFcK@p0#nun#1MTiK}!FC*$El@5K4>E?XHMAe(R*qBt{t%XQ& z&Xz3EsHgkuk}MOg<3#eM_8b_2v*|E6U>4Vp!b5$gvp`?7N3w_Xd6@XhVpw!>!)zli zRw+Z%j9Jbt*k)!{<1MXbqH>0s^+?_j3(O-+M6w~-SjQjrLJ&RQzr>kfpHVpit;18? zQDl$GPUfnHBFf6E<3ri>ajOD7>y)J#_$5dYnb!WIX8BvFrew2x&u)d@ldzscuNudL z0EbK;UlZy_s^AAz$DNlnBWe!@v7OlEyG;(z`_5ZuJ!?R6kc~LOTqtIe?Gba`?yy(? zL7Rn|yLEkK^%CA!O-TNWb)diTO+TLCe(R!9AvC~Qs8aN?*#qKf%nyBWx1EUIGlMW>}+ z%jU4rY?Nafb*z6dD^$|got*I0s6eXdY`$e)2Mwj;n3-6Kk5e5~39+0WU|Utg#5_3H zh=^F5U^|uk8OTihddaN8XgX3f}La)+zKz@G8!Qbo$V#O)8QXG!wlX@j&eiXJ@q{7vZHJ6MY+T zjhkQ2)H&HqGwSPA`>8*Jb=5BF&csx_tez(3sc#X#Iz6mA?plBOq`}t1z_-Mc?4%hJ z(OhnzBb--QHA;kk#v{&Dz1Cf2_Ex*uTvYZejKdGcGtpc>lV^=w*jW{Tm+*z}cl;4` z!bRx!Fyae3&YcGnA=w}AtaAUR`>gKnW_68M3_tNz3NC@?tS;&3jFX)sY*i6@vDA8{ zys0M`U*%2G%PArn=pE79^)a`N^%5eS>g1cbM$eI!Or$yK8u60+>aqTJC&{>AQoBO< zh#VW(j;rZlJZk?(BRx|cfv>EDFnC@N5@qO4_n|t;_n0$+<v%!K24?$F1G zX6!yW9^7hX_FWMF{r^p9MFUm`YvD{%SygspR9IFq(u;fQmHbWpp|+_Ph;pA!&laiq z5dq#W?!I-ClvD5EUDOlrd=K#JEM#55n9ycjz-X;Hx%cT{T}zBHF0!x5U&D9Iyrh#y z;|ak!tYY9na!PSBpp2_dM_tu;f)iB~xvm|tpK-cR_-Q)7VbA5i%jcq~FSBzJZ;O;njxT5=V8!F~Efnmoid$|3gqz`0;~w;c$7CESfg zo&8azY170tq@vk2a&~eGq>&B6|I%lCC7!fDMIQT+%`)X|wqF;9+H80j;aPZ^)!gnV zKl$#3j``X+zvCKcPc7$+F^7B!FQ!fC6?23r1fMalv6|E17h?d=;w9oewa1DO$$C6q zR6Ar3E1laDvXP#UmEXYK>~L^jV4Z5^mf(9`W~OlmtH%EFY+86Uc^+=5it~8A$vMrp zM#MXj=8Euq=|t5~i)BsmCogAw47QJ|?Y2_>h})se;iQP)*(HzPo;zFAV`GZC9aefr zpeJU;8x#WswPB#ZBZ`Kqh-KPdKJDf>uw|*m~$rrUp z*5GsG-|*0U8XDj>u>KU8nM<>($8=oiB|FU9=pl9?_lLh0q{XAw(1iU~6_2JT$XR-F zbW-p*+JQ+P`4zBJvTm9gPuw&ro#t#wc&5RzGJE0V)wB5Y;Bn)gb?3)ex=w8i7p9x| zP*s8U!nUFs)bUS+>o^XNIyXp%lo#z($(~La<*Y;cratMUz`SNxyOMbzkVSn=8gJb< zy5Q(gFa9vJMo;G@)LNVzI?Jw@I{9zkEBTgGqP@jn7HbqR#s|MU8;r5$Au||kE0(SM^teh5c!I`h`^Og4CP&HpWyo?XYG1*3xWz8azNcE(sz*PG$!}X7_w_x;- z1m8CQY&Twx41TmqK@*Q4pU|FeT=*Z~jPN$2Gs(^>$m6PP^cR^D-x)4TPU%X<75kf!gb`*j zn$4L4??^k7>JRoN>@3E!dS)c6F0$G?`98C%8%u76?0}M&+(Xi5qBfkR)_ELD9`Wk( zfU;OBecRn@P4TX_wp39wSmy8tW%3%%7Y8oHV|NrwhdpF<(#Xi9u7=7429f^ZY{nD1+AQK5o?J|q#+xbLIRjN+^?)w(dOo%9 zOZbS+h({ox{{MEZd7O{d9p}DUBhy%&!nxH}9Zr=p94*e^F4DsaY>4K-eJ7BZ?qR^7*qEE#3QgPHTP`-iL=lnAfHuh?AO}lc)Wy zuuSkLe>w4kmy#!_A`OD~NKcxmGRfvvn$Q$^!2BJu(kD(Eo)}u0q7SX=Hq;d%Zg`m!-WRaY%Y4Ty>8r}nUJzP6uo z_;#~;SPG1U)M_`6fJ3sc^g)S)lvedXcK$Ygl6&0$MV~Pn>1J+Z)P3>Uxu2%8mCgAU zo=Qeq!|;<;PEHe7eQtOw2s3NqS$GWJ$w78LVJ_UW-UlC&j1X(gvFE#uBTGUe|2_D` z`wY^t&Uey3(lur{)!f1C8MgDcPj1APc;s{!+xxV;DygvlH>hRjVCl#qd$@6xrNt~H zzu2UfE8_oRWlz4YGg@Ee&w9Hw=@(xudC@)3SDOcQOt7ViEC;GY%vi(&=cdsc)|A1;Hf6YgJKW_2U!|obJvo+J;%Q8A!Dg=E_>ex`amDFr&mz`X{VKO4##RG}))h`+B?S z=zKUwmwHANk3ShPVm94HCxr$WIYiaS3+BBcp>4>PZNlRstINgq zM&pW?58K@5PFHkDymXD4xh^!B}X?tub!0 zeg_sEuge?yBed%ZI|cptU~uO^-5H(ej}=$|gJQ-;5?~mdd~u{#Z$e^Pu{} zyx^>(Kl`S#GwLS45&lRk^R&Jh#)@Du%*wm++48@nhSAyJYT`fYADrcX9ImBm;WvA) zH3>(kcK8*hL2iFORnIBs?y;Ih#LME|+#HbWcnPxHEnp6XOu@`Dw^7)vlzarUh-6+} zmm^W~w)@=p9p*t*b|BbUUC%2bR(p5SNFR`p_YLp_YzYFvDF zV=tf2bGeon4zuZQdY0^ix6T|aXVvqc7SZ0%Vli~mIbbVW#IA(P^Pw<^7lm5k9P&Sw zNbitS;qv+~9F0rN2D+R339iFA_k=y)EM&&$+IBv=K`sld)^)^q<1?+}Yv^|2)!aiw z@)}sdu-vtHidV!|;TQfW@fv1@`?(?CK&Lcr3?+$nW_~(fm6Yj7b!$ZEwsFjIowed` z{>`jIAHaPxzgwT)b!Su87#rS163Hc20w+5=tqbb^|52&kwz!`?4Hx2jrHv6@hpy2= zO(UJ@4zCx9Ua#u2RH`fvv8svd?10#bpNu(9H{8Q&yW5FY{e7`^CUGnBkX+y-7^`># zQcWzRuk--3HMR+zv?ltBIZOTJ+&NfHl*I3}vQvc*l=Xu%{o6zPJw8qy-m6E5mDV+E z4t3a0{f$H$8J%AP7qPXAo)_T@ph&nWS)!KGVuq^=>KVyx_4Ge{#RW4L`?R&TS&GJ?p27;3_;Beu&ZRhp!A~ zu(QKPD8MSgO^@Qf$P{`Ed^StTdoWJt3p61GLZ`UnJ0jcS2i6gU$R;|fwq#dCIrY$v zfngQT*x%v1qz=zTmYE^7jr8VANi4hN z6!(?Yalui(^$-O))l7G%0r*Jz-05y*BfV?!yf{(&^(p5C5u~8Z!Thp%WP~v?yxb@P zIj}LkBOXTI53b=!#wjNj6+F<_`Fz=c?Qz=kt!Ay@I5Lu5WL0Tl)ZHb+DN1jo3AeHe?d7G!SF6iE57q4iZgZ_3-ZTQRhUWEYv;`wm0K8BW&*R$g* z?hrFs_0VOUL%wRE`cN4oWhHt+WH82BQ=Qd(Q}P+B9eoqLYPMvh@r+s8jxmSP0VH0P zQMpqTk;(dZbH7`iW|kw2Ir6)#;r##Is0)W*g)?9ce`9x!vk;DX_fQ{t@*~(CJJ`8& z4K~+u8CRKD75K^B=Nrw^g#TdA`6~Zl=bL@fvl_3RRTdTCwN$j;=~kwF&4*%k@B(e` z8PX}<6Ag@!Xn!plSkV?C?H5X<=mry6%95~_*B3EE8tc73n ze2uiexfia4?~%iS*3MDdO*IxT{kC*SKbWJslHQ^Zevm8GB|c466vbeR8e|MqMRjU6 zg`9vwvJ0=_8y?&#dPC8u>uRnu&pWlg{$aY2*d;6IT4b*thMiL`v)785WHX&&9QKI% zg_@x&T8;QbyS*^vKjvWbK=4?&hH=CgEjGw9w5W4P4!~6Mt&xp%Q^TPyyu$aml+34l zoQu{Lv$eaz>?ULJKdXm)fl9w??w5ALE zDb!YbucBl+X>TOa&EdLq2!D;gn`@nJx|zNixX%~Z?XVe4AieMdWu%OHFMbmhSuXM? zt0EtZf+DpMq|2R;vI+iWjC0?KOVCa$+TR#SKZcSb=CiBVz|Cvy^-Z9Q)qba*;cY+d zEgjJiHmCvm5%%Kk!e8}z=cZ8sj>yZ9(IsjiA8ZcA5y{QyX?~YglXbBJbHP@%*g4$D zO1n=(4Fg537_{vQ-u={}2h1H#URgt}_YLM}q~BJgm(d-DIa0`i3E{@j-kN08l7BkcoCj>K?;^}n*+fdxhJ1&n)?Bg9 zj18YthI+@p1isnBMM`d356K0)f}3O><3(i=`aq|GLLv=r#-mt{&1FV-lU#>8tSx-( zj~(ix$P_5W$A{ig0=;B&n7|+MQL3((rzb}Cpg**>_mO6p-xxt>ss7=@thCh>iaF`o zb9)us<{H+RC1|r?7S)Wb_1(9->ysiQKTl4%lf-2=rSYAwBSqEksw*tB%JS{9Bu(W$ zqq(rT-VcZLSoR9bOIP>Qi%A8tgdF4F!m;p;b&zR|!gLPyX7y+`npLGE-EpA$jqGL< z-AZs>ji4FLj;flyP4-r!QNt{m#i5tXL7ITdSmx8OA_=UD`6~1dSz){NqVSrx`4APn!qG7{FXVwckhn=NnlsrZ_aE7Z zOmpTNt>Gt;kzLkX$x|=W(-{f&MC}^ebp^UH_=;RNcE~Qi(K@8QdX(MQ9PJryOWhL3 z;2v=Ur_)7bB7F!w@RUmJ{HtTFBe2naDq7Okv^g8(*m#??F|*MV!g2fS7^{)dP?vU3 zd6Qm}QQtZFD}AG@%BQM@@l@uPd-Wxhv=weq?c{mAkq>7@;Zmp}&#H=&9%e5w8E-;& zm;*<69^&W|&NZVk?p0;Yr0^`Vffaxa-glPgd+B6yPSkYHkXn`rlSQmu*0bmY+9LE> zHsJ9d>4e;o`l}k_vglN-zRE`;oleFOBP}aI-^;P$1s>;J=%(-(@r$_=U42>X)3;%f zc1daZCt0B%IEVCAvzo52U2Cn_3OUIr`a~U+U#vcCi%bp<XApkru%{2p$N(e$giM%;2^%p!J5sOB3hs;elgBDrsLaBH${=-1EvX*{=DLnCE~ zKfzAydgv;>tq0MSz(dDG2mB8g^9YqvT+$bGHONTMKoPtW?!x}$7w9OJpmP}`jDJXK z_OIwHyRjKOSq~NsbOZ>xmu^*=u|7TNBF?8X;Y4VWm`HQ+l6X^ zmEFTw728<9((|ELe3N&|r^pp-A{L4cdMLYSJYsERv!r4AWAuAn#=S4E<6PqxF_I3T z4c!8&vyo&Bb_0*zNAs zTW}C`CIhh>KM1=#yS`z2zQvkaf725(?2h-PBZ;hr8jml$d*n2YXk{PJ8GXBq`=YwY z@3f`cX>HcQo6jfV?sPo~im~nh=xsKJEqtaf?s7U&{B1RlIICVe#hgPdWYuL6<~*@7 zm?Q9v7Jy=6g+0^XNfm(VP79qC*UJBlA#7K;t2pYV*KUuq)~niVuPn%W;B)Bv3R%`Q-dMyU)Gqk#Vg7t8R7>=LR6nCGIhP;x6FCt;m&j@enQmmkJ+hfwQWDNe~3}Ls(2eOX!78Rh0*#Yx<8_CH|$V6YN z;v^Aki`h`#n5;UXjTKl|a!@?OB)fzRvCOcROcmq8n^*!&WfSCc`QH4Xy2F05+10p_ zrV~r7#@Jq_GEXxfckwc0rh`;2C&Jej`Wb0-th_=~g%^^yG=n%TE|K-*gtNv0=ufh< zuROoAltmfCby@OV-q17oY4y~j$ICi5?aMFf7zDVco6`w87rUmTbxrbE|11yEa%!q) zi@&o+W=8uRf-Yh{g(xwe9@IH$G`Rr*k}Q|YhIpLyV3*w0*vjk6iF7>qnf7si(I2d5 znAz!~Vu>WBL@E`7&)G29TveetxX-ABF*pmCux{k7eh2OGCN7c!=X*s?$qHS2K#yb>U`q3=NwE?&+6=$o#qzd&hre`Q;38n{_abNfQpUPI|3#iqxm? z^<&WjhC=|_ljFJ+?39fBNfxueV6+;EeYLA+;jeIz0BqC4p`bZY9H9;mx*y~hqqTvu zwzJl2l5}zwT|Rz22KUoq0q8I;Qya)o5VMf@qIX0M$}>a?mPn~}?86s!ohw2NR>^PWs=Kc@>= zS|hc5&U&aGa0g?chk4m`^du`Q-hpG}8lIu0b#ExoZMjum5c9o8DTYVgT{@Ln(alaC z>I(2h&(nV1#bq!VvKj+PEETMJc!7Fjw&bW_*Z6x$YNs-mi5P%ionE9JU$1(IeQdjDJWt>xDKEF{l_biz%>t!V}K&|In za1pJevdb6H!T*D`@Hk_e%1pl*3&<|2_0KSe^~VhMbwQ|S z@-TP!A}o(%=tNqT-ceEf6=ZS~$sA&PB#;8<8z1x(`)91j8^Kpu#+j#I0L7kUB-|D+ zAl|seH|P~^I`b2K3&f+Tg6t9=C*Pozz6Qnh&$Jj8*ZEa7osE7`d7S<9fF6i1)B$S9 zd(J>MgbXKpWe3*LoTMgVC9h2b9(yXR$EH9%U5pfw+h|R_NVOx!+;#9uw^E>vU}jRm z@#9+Z-J9`UasqD#=}A7Fo^EH)b#`-!nn7NxSfc@KbK`VZQpFsMmEFm@DqMuNWEqZw z%2-I%Apb&h+F7Snhw(nyr6*!lJ4QVY2%PPl5S!rzizgr8I`)Kg><*u-Vlfe4lA2~C zd?Fjt`LwS`0AH;wWTey6sq8A_2JUihkqYvx7{~hIWQ=y6({#B2Q(>%^9drsN1qf*0W;(8VLd zotRTbncGw$k{(jK8DI|mivuinDnkNEiJeG!(cC1ot9Z-)fx>*FW01j;A>*x+MpbcDy}^Ht zeB`bkfLT3GE=f}09`%o=c$+SCE77{FJ1wmI;qRn5Ye_eY`DzYXs{c}D$r`>|&$ds1 zV6JCsmt;ri2Q$DN>C(I*sftE!@g>RGfW z-|bOHV;t-m$am#p z=ye?@3sCCKOuYU>|Lk#MA$=6v$klQ_T>%%cpSmm$nn+5)W0f8|@nx7E-(n9)Pb;!f zsNL3dGT)-_%SG@EdhrdoPF@!4V1P%TE7ep{4{y>Pgu=i2iykSvVlvr+rAa|Z)Z5t` z@=E2>1N90z7TeOMdVrom4oTCSp0R8%S&v)9uRMxnfj07;9!Iz7nfevphE#MkJ)&;Q z?bsbIv$Sf2tibM)S-ONcrh6IVV6J{aAIl}IfvinudoG!SrWNTRKsUl(T1&iFg{eh% zVGdCh1$n^-x+Uc@ItF&qdAObpB=^LAv`8;v(dBxp0{9Cqk}2?AJ&`%gRC<{=_4O#g z0n7=<@e0|kez>I}w>5|V?d;Vv^v`T4&L`K^QSkBEVgja>n@L4;v5r!O^%VGnUL-5! zD7BEZqZQRw^@?m^1^7WVh~)I{;sZJ)lJ1rTJU&Rzx5>5IOCWxVT=QO`InMKP;$NeY zeC}?eoB0y#hc=`)+QMD&r|YphnMOwIuDC{zAP>kZT!fZv!v4~^)d79Z&CSxWt(xFU z^;v0S04uA<>rc3Y?Nk4dKh-+ItWj=1rD;Wc2J^{&`H@Uf6=_jgA-u)ROpkk6o)R{} zYOJFsQOU}Ass1+>g~x20E`(3vJuQSEXkEQfSJ5N9-CwW1ku%_fC=3gO&R`eSQSZ^K zX-=|G4&bfjHc=6_<6$~b4wVB)8NLrEkV{ZRw8W)+CM?yvX%5djHn~f&fRO?Vu-I%K-Yf+vhitTFa>-+ScKjb?{9^&MA{1NsUX=#CUCfzc{ZPGp2IeeJpRA$bk1 z@seHwzJ-cjgXW~wy$pZmj-y#$8KO*GLiLw|KPP@X&X{&109R?+${ zKA-{S>fhzRbO{Z3PIVgdkUFrRe%F3@Bo@=j&`Q*YR9K2;S7k9DJBfAWM-|B{!7zQn znLr8aptBm^;gsm)x#%lK={t`h2J(UY8x)lZ{E}Xd&oG({#`caw7Z4M+sTfEJMMWfJ zp?gUYucb2yN8QFKl2e}~wW$faXd}Epy6QdJ(Hc7%r}b6VTCb$&5q-WBc zP?zO*)2eom8S2p4um|6}h4dh+JxsBuhX3UfW{^9i3e4oQ-A!PUAjZRSRh8ZMW~U$- zirMuG(hPGO>s2a6=^b1Gar!Vhi-Yl?+Z0k-z0lRS-RAfOnqX#}tw+<@#oG8v*er!^p=Wxe)sH5Uxpsdj%TBPZ*xW7540eQ^!^ODNyUN=bpv%3Tv)5kP$W6@Qo%B@fN4h})0Gv&0cz$|R1j&4M zh-_A)FqcP#o#_#8*C%*J-k6_|Ys5Mgg-4mA{?imzdH(&M&hL3@QM|84s$_^bq(4)1W+Zr~@3^h;pPKMq?Zm>R)O1wU?NHsbXeE5yrp$yZiuJkDFAvQQ?u?G31*VB1opDHM7v6V&^)mg+s zb2bBKU@1Mp^MSh%p%dA8Gq;NNCbA(pPfn>a=6U%JY8j1$EpN#)d@M=ptdj*aC~?4{(!8p%L8nH$CygH^DLpz#@HqqZyI{1OHMpkshU424bfnA~kaCnTJD2Ltf!2Jd< zXnOhzQs61l3NC8D=k;qjq1{9^%m&No8`|4_=P}4>o|?{d2I@`pBl}ISlnxE+-_<}e z7oMoPbhO@$Q}q~Qzdq@8*Fi7OqrL1XuXB-RG`-BF^B6Ja264cC!4lZ7*j`8IB-nz@ z;DyTOo$=q^zBJMMS$djUcF>zKC-22F$n|bUh_-6cRjQ@ifi|J@Nh?J8kTj&VVHu`W zoX^ll)E{`6*VpO932Z`!dc0Rbch-|hv9S72dS#71M3lLC-s)9ZEE%lMBpk36rR;#}h?{w6YLFdxYnJ&~-1Qlhh7 zX(@d`l_14fdVU53$t#-BSSX0+JdRnQfAy~6INW2ORS8kZYvecZ!efBU>H{0;`9e>9 z&C9@889_GTdvQhm&DyFOkdl`q#XV-*#%EKe3zM$uj3MYGxkC?RHOXAkNfmFN|gs$-;}ek#*@ zJ~)rObD!ZkT1HrU8tFoNieh*Y|6xIC>#X#genZ!KUWZT#ACuSgKXp~l@ph&*|5G3H z5_-QLDW1S?UIRynn>sb`k45o~xZwFd0~240L!>a?r!i!nn;IU%J@Om&2I=|dLO#f| zk7o2ByDMLlntYsYsFR)C2X{x z{H)vSbM&k?DO66ty2e~pNh#h_IlZo}t$Nd{*cda>wxlBY z%RQ_MfyJ)kc#j3=tNi4KzQ-<$$DW(Nqr2&Rl`I<5NY7&DdaQRCzIcAu8|KJuq@A2&%mAfxOYB28jC@wq-@y2QsZIRjB8|TxQ1KUV))N}NajI5)rz*%YcLK` z^x_}nQYQ!N=b7M5{Xn&+<6sLtNejSs(v%cKkMr~`&)W&xf-gNsX@-B>Rai~-3TDYm zx+hfyy{vfBYjqdMt)o0A@u2ntRHT)Sy<2Ky_ z5@7+IC$p-$ES81IdX<2S)KjBBj*?aMS-u#RqlkpDxp{7B7kDGVE4ppC)^BVFBjuI1KjP|xx9boaar>kKcw4&?D z9PEV`Jx*Fh>u3eS09cMU<#RRiGWcydtg@@Ql;c2-r$6%v3(bR-^Bw{SAssOn&Xn(KMbTkk5VZbH_; zZ=o&B)q(XP8gJ2pn3X@J{V)T4gTK%RD$?u2 z3F?sEK>m~&aI)&BS{SaHFTd$w@(0a_1*Gc{@etC7Oww=hIWcKZBc1$#ku({0lit_> z@{lALi++zIv%p%N5r8C%cZwKpkuZm+>#GLrZuGu?DJ&;t-90k)>W=|4jF? zyf7CF;!RQ+7O9Qic?~9>F^S9`sVv08IMh3_rFw~OY^K7x9v4{ZA9j=cg8yIeke56w>U;&oC;Wg?r{Z0N2w1A4FVMK6#8F+ItKf8cA6y&ljWATUUd%A4?& zJcoPkC_RRiAn$ZbkKtR9#njaqRTAmJQosatL8ru}?3RA5uIo0k0lNdO^%>U%-~xK$ zE8?gXWDdIkF|@C&1i5j&o~UEi}7jR#0tl?7hWrru^&5x;sl^gH~3D=Jnl zg|EDYH*fiH5Ey)$=YKxsvd=VyE}}Z%N4b!6fhA<1KBlvho8%oig9kOmx-=L4D7vs^ zsx}PPnV~aP@}ozgf0DMerbZkG9i$`=Jcl}_PCEzbcSzEGaEnZ2>tKS;LJL!YN4*j*P`WX?t2y zXM?KHjr4#I+V6!`N%aHQ@bn%}UQru$SvalwdHwN*ev#$mHnW0SrV~`GcNgbnEj`}L zw%SapES?=?$BqAM?>yk6sJb?O?raJPy;qSUNU?(GsmqL_mLf!Qb4Q4&2j7Km)chvI4`@e23uMUED3B3mPuW1y2g(Wye1Wbd*yxk zL7s8i1#3wQyO4NX;tr9wq^>?`Z?UaRL#L^GW$-qqI^J%A7HKF?;`Q!v4ZsAz_|W9+^_Ve%Yokjn#o^?5g5YnzGYU0G&V1)n!F zZ2>bT&@(v9{RmC{fPKpC2R^RSb*7el;CwDs-3BI~sYb;4(O?ZbDbU;$)rRtoMG?ghgnydrPV=Gp8HMtQy+_yBrp_Fx z2(z6npE-NoAn3kcm%D3pk}Sg>Kf2HAO?r*9(00%z&ga1i(!!~3YT1EuGis!(4mEE{ z33mn2avRm$eY(>=B#v{hyHiGh^SkUJ%`b21c3A;}oTR&TkW)slhw018YJI|-Z@=s6y(UPRFFBn0PIOwyOtR4i-om;UIuE=1?F>EB-Xkqd@8E6HO9z@6wxu3$*4q*8cV-r7GKGpYON~Mbd*gDtkRnD zwA|xduDxtM^IITA&$olLmzEB^ARU7T?FHs}_OwQs zJR_|%f%q#Z?E(wjwpz-qA}^TUaP$4HNEMT;&EdT{PA{jfRFRoxzk9vY-qd%$w5OUn zP5@6pu)j_FiJNHF>Z5^@n(SUKCFMfrd}(6um+tOhXOP)<_)4dgxyV`PPH{KLb@H0I zSPrqfKIS1=C%f%+a)(@n7tV9vu@AV_#iFDq>8){b+Ri%81T#thgZjwtRB(PKE{Dqk z)5HF5A9AiSo9$Efa#PDpO}!9}@wj`Rdm?&kxJ_}3yT99$q=Zh@>+}g{rn}s%GA~Gy zbFJQEK5`zkZ%JSITIWkUIYkDOW%tINW0z5tzUUs54|S-mrE}!0z)8~N@JeYZ@0s~} zOUgvKEU-4dVCwpmmI*Hf+M`69YCYLy#+w!Pk<`9U3%lB0DW}-?%|`o-%ygg8e9oxg zZ(6|pQuaAr?1%27X0qPo-0rkP5j<{xi0f^u$QM=OYwym6%<|nyDMg$uK_UOf$K&8AJ zE-Go(xU=m8dZkX#S+dCO&scBSEA>*jC~llCcF&O8%xz8&Tg$GK($w>DTG#e5H#>v$ zei_AebZi`_z|pY0QH%{{iCz1yv0`pbp*R%8WMD4&`l`%S$4*j*>DqgE!m z)odqcw3%Rby4O?f^p_nd^Z(f5`as}bGc-8Xjx+O3Nm~!S*BRfB(9f7N6%8?0N4RU` z4b;uZerloZyzAvb@@yW?ztJom<^QI@oU0>E;AkV@Au@ zVD*=Fw`rvx+q<-_xh_yVxL6n2Az=1eeNDCnAEG8b%h_iykf6B?uC3!tx5@6EPO|e1 z>aB;)(4n$M7V8(bDSFf~w>y`)E;ZUkW_?^Ew9gYdPk%AZ0==}Yt)Z`(m#L&CX;=HM zy8ummqp9FlaIXmT&;|BE+nIem>=rkT*4J&pxcT-MEh@jr zA{k*%Gc%0;Rb2s74ad@J?76{ToM!_Q zgHI6=#R3;NJLEZh@woGj87Dp6DRPcKHTs(^BaJkU2ra8u!FRpg`C$KD zcCwb0*WxzX!R|BWJ6km{!IlPdUJTA4CkMDgooe`~rk!mcGF8nKlww7 zf&CRTcbMt!xz1iwUP{Xb?H`zIQ-k?T8@&7;d9fV*_J*#P$!O%d*!~o4VB4E@%yEyr zB-^E}x!KHg$J@$I1F4`tY8&mW2h3^A(aSahLGII!wIo&3XU-wrZ@&LOvQBmT zyYB{W(*yQ3ohP82)C%;6PZFesRyEC~t-NVm_c8ZVbFX>OX@fqy)M?@jldZwoZW+BG zaJm+9E7;5AQ7NEP?KNhyOo4A-cH`pSHj5Zxx%rPtv9sh_+e})qXeV7Pv*bxT)LE(h zck{4ar5{ouoZ>97KWIJsyY@D>>n>B;xsym833_c8tLwG4J|-j0 zP1KrJnxgXe+f$wAbdOun`ABZE#q=zl3XT?L&ELaZ9fDt@!K$D!pQpC%4F)wZrJM`p z4*Q(EC}q)t?>YyZmG<>u{=fysIG^io*V+~2^hoWY!(gsYO%3_d{moXF>9WKfMAf$6 zT@yUXEHUS>g8Aaz#c5{t%j@)g$T?_>n8)>Lw|?9s@}Sl+w@4|cC0e_N8DNIURlzmZ zIIq|z^#tvpbpsE%b*bU@;(=@BRryS>6Ju_5Ue#JuD5pEWN>cD=`;7T9&>Mu`O@xj! zb%S@?6P=Rsu{~8QfEbPJLAR{jr?Yh#EK^(0mAPh{{v@}%4@)0se4x6n)&+?)*qtUN zP}7;Hwd{I11nbw8;#%FUWe!SRbFsb9jh7qk0hGY~ZcW?KUQa~q)ECjR7wb;BOkOf; zq@vy?ea%ieTOXk&o}_2Ehn#6>z&o^!-DEbJPI`lWC$E~*v{s-ldgvqa_0&LNr@2`a z9BMx|J*e<&N)vZ1bN-}vnf6*lE(s3QGS1Uxnj3HHP$$Qm8}0YDn|weG^oHDRcI!$R zCr$NRHG!86I^>sh`vXyx>Xyhy>UQ(!6<~h@K zrcLC!yUhqt;1}#V*8M5)vBaSUH<*deXYSp0oV^CdzD=vTugZO)AW^!svSsj>xdf05gb??$2jRqFm(y2-EtJ&lXuq{%lv;wv4 z^X>;W-YMf==Poo|oNJvH?mj$U&Awskqrb|4Ul&VIzoJ5GZoZIv-RglV&PO^D?5kil zOLu(^wY1(8ktWhw7TF?lpKYTnsX3-d8EvHnq#ipjC8O=-RFada+Rp*su9Y(!t2504 zy%;=wS)VeMP_>iwakR@oP+jC>Y`fk*Zp&+RIXAG=zGiFU!7-*G?6!pZV!2!|e@Icc zm6nivX1Gb>n&CQIcA?+TkqMyqyXIHfE$!?EZD>x{rRE7#OE;>FWEdn~&oDLR5__H9 zqBTrA_cLr>RX!tT2I|kwesjCjv$q+?{78NIj+Qc`&Esa09ct&G<-1T_pJE3%jRFs9 zBX^KI>eMw`>{)JKQ^467gzIdI`BekD+M-V!@42ex;=OxSL?3V$=|#>A{a#O{*7fR$ zil}1m+o)FSb*`%=0^6j4TS?o;{pMU_CxYFDoeiSyVEd%}W~R$|b_XaR(q0?cPUcoS z7tJ|aACyO^!2_~YXXzu(68nHHOm18(gKQ6-E2jni(2wj$d0u*gh38XU-fep6d~`rx zvy@8pWYeCyprZ8FlgwA{X#ExIovIIkbhY5QGt3D((l#^2^-fzrt~Ex>!6EnAnYv9n zi1&Wt65Wh-3&=gF$6n4j-C{QA=W?kGag&_k)csYpx4T~&nq}@BJ53)k%WXYd6}|ly z``BY^z(KD4NxSM7+J@*_uD9AxOw+g(AXtB?C>70}db%@5N>lM|*YZxBJR3YHg;fe>o#|utsB@Lcv-%6YiP5$Zq$OGkd0;ZQg@xx zLkh}`I@9eWm86b2LvFT9-MO}`(~pX@o;y;?n}Q%Lc^s&#d+m1V6}Llb+X6O!;Cts` zxzK&v{obt9UD{fE+hlWvnXX&i?)F0YK?c|>sW0}L8@0Grlozn+RFI&Ews8iVowkYH zZu*%orG~xM=5tF{ebr9ayJU>5i|RkY9DtiT>g6&YY};X4>R$IWndeM} ze@@l=WUZ+p&)FH$K+92mydy*GBE1^jmk8dh0VA(~7f+O*q^54tLeza%nvGIQ`${)c z!}*Qedd}%+-jfyf96ck@Ukgei+Pw*!nWzuyclx%zXFsxWVEHKXhh63lK*w}53xY|u zt@BVoWU76{{Y^eJ*W={{_Ei&Sy2`V5u3qI_qdW9>{P_`n8H_4i4ZD3H%k3jnGk2T8 z`lKB!#G@T!hs!UfH(sA+yPBXo*$y`|sD>xQ<@wE7D5Q81dmnmqij!Y{O}SPi?rWzJ zb>4;MF?m*N$!pTw#-jk=bk3A&!4~?xER`SZoNVdk%?hzFAY{cJ_qq=Vpu8#Tea!pwEeNV!9sVY8w7u)JaKlth!F z3AT#7MI=td)+LR#i}g8shb<8prwu^;XKg<;#O>M~bucc_U4FFvWF1)22K6;jHe=6# zIcP7j)%9gbMR!!vmsrDCz1vNJ=^k~*=^~vWmuO+>EK_Bgd}Ys*WmMK@7^*B8E|)m{ zsKH0tIp!AAMGDBtu+5!jtv#Q*X}^{;>ui#~DBgX|cikX<{Di7?32M5Z)6MjM0tQ3Pvktg#B7t9_C*=v zcA&;tgf2Nl%gb8OQcwl?Wxi8R6cjF`PdXFrG96*Bl(WqT?f`A4`3;o^75r5DlE$0m zSfYnL-CV8{Y=8Dw)g-f;J;7him8OQ>K^_-zUegU!65F*HS^JjGl-l^cxHdBnLqtR@pJA*!t!I zSq+~i!aFxJ*W0=f4k~A-OC|Fx)qFeCUFN&*+j~uY{Q$N9s6E>$BU^MReDk`rF+sD! zUZOSF(H+LCrSi$S>gpVmsx|DU;7jrrX!NU&aW@ee-^`d@F zZ?pk~oXx2Fpd^^FvQLf2w%X4;Vji=5 z?R3-9*{m1XWJbDy%HtyUCfn2b9W=XE-bEFhsYC1@lh2uoidau|T-$kH`)UPgCIifF zY*^L4qHmkxrW^4wm3{uCujuFIH0qo?^ck&Xdg}A`GU?3P{$smod()X(t&jN*rPmw0 zKHq#U7oqeVb3fTa~XAzx^Ushqa#qJPl=X^2_)E}mo!|>1QN2tq9=0U7{wJl~o zfeZViaZ}`H;=7Jq!WiD)XSF1@e@Cs`O}^Ln@!J)eAT#B4s*-jZFT?a+8LdlMOHrzZ zMN%1kIg6U+d^5%tldnt>T_@j>^^fTD>fPn&i~gPGPA95{F@m9^-LXVGu<&_!PdzeNko!?zQ)vl(DMwXez^`H##o>)m_EpaSM<8RTB5?@%GF zll=OcZnb^1w3()_y0aPoEL6uux|XauK(1Y*m8hCdCHtmm7hNS2(YjY+onhuPc~M4a z6Eb=Xb?^ZBKziuCo|d9|{X-|(PC87x$v~L(99xHa>|wX3UdZoXlRKw7&&etFZ8)%z zGDeE zyQM*sp5V*F{8n6dgZS@Jfjn(@>CNUmIcV$at?1O<>OIl?nq93wfqwqdcAlk_yZge=hTNEQpwSUT2v3oE;8)`9c8bQq2_-1kgTY|N^WA6`B7N~%@5?_ zXJGXRXO`|qo0Zb4&Xr_+Ls_ZA0~_oh+Zn{}i~T>)U-d4#0sirr`Ujf|BVGu*yia7W zF}ITg!{v6o^tO1I)K)*EW}l}IQ&XP^77w7Nn}E0aWBZS^ChK~es%^A9PWAPO+^TJ; zx8A}JPn*H^XZK9g%{0*gI!=p8Em;pU+^@@s*`WQxw4$CYA(QPW>1=*yH^m~d`~k<**W!V3Aj2U$QR@T_!P|%J$y&w)On`hGi*9}l z4F3;4E{v^rx{2D-Y?2Siy-Fy7XV}L#wwj)2&LumxVvAPhQR%`pr^pht(=fN89xy#n zYTxQ^`<ZS4!`90olrdmE*z6M3xOJ6L#lgyYzq;{r$X(I1CZ_j;E87)I4+1yMpYqTGH^b2aeKKOE#oF>Dh zpeD&qdl9PpV$f%ZT_uO)OMLb$O18AjfPb%n+l+YJH;=WQ#*P}J2wTWCG7v7WAUB(i z@)bK-qAjRt>VlH3(a;m(KhQg>>y$0TNfF107x z%gK@_^^m@*UBRLAwUZ1s?c@cRxdJGBx>M0PVDHu{QUVn=1T{>yX(@dl)l*Hc(iWPE zkMl`9dZ~c?M)s%L7TN>sDq=(~CnG1A52=BANE0(y8WOR$NxV}@cDS{4x|u@7F-mVh z`R*sLXMpb&i2QH)^C3Q2Ath`aUPzP%#N04YdSj?&7I=vXnn+1l4Z=)hw6L+@wHwq&>z6uAEguV*Z)XYUOekIT899>{fHgB8Xh{_lAY>gvozm_F>nQekEPk{l)+Othv zc}8B66-1=SID4|xLdhghyG^tOomtNHGQy41^#+92YACdO}U(6YW@;Z8n-4LDrY;NwDP4a*9sTsZ!K5MFr2+ zM*60%uq)BGC$f|7V8I;J<4>&TLTc$*<`-0X2V2mrMXi-^s>*Np`gJ1wVto$1bSHR| zqO+)pACxD|>)J;b5rsP?Sr=0i$H8%5fEYi3;v=bwhS@VgPR~~x>|!&}DT-d0q)Vio zBonvi5}D^>ff4Gu-{@#l5xsYxErtC*B(s{5op0LO($lopJ><|rU8Of@08HGW<(+=i zjbBqoU2jg-H|$a=YucI1t!uvmInOZ<%4=F1zkLZ4e~kL<3MZ98|1^eA-nBib6&)gC zl-;lAnlZFD=n(l%Pa-~7!+?wQ9$4>J*(ZDLAawRaXsSp3{(e)&<~ z&^7jM>0zEgi}jLXT=AjD0Wk*pjuA)9X4b^&!UhO@0XRQ5Fd=`i~V<{)_1$XtNM9)aC1 zM}-Mo)QA0DCX012h@M3BtkS-=Df;gR`0+G7>`s=Tls2v5=ym$Md02K~gXv}q3hp~y^e#od`Kd=;G9g2uT3 ztN%t#)>H3CP41?adPOhS?&c1Coov5K9@FvmVet3g8LAz2?*?gjH za?i=|q`s*6SH$#WSXrnaEgGbg;BFR$s6!spy{|ufC9L z?8iC}Z$2dP(o5#+1a{fm*@a&0NiNMKX5NOW#>;TqTW*uWavQ4c39R;^DT3BnZujb0 zBBrghpk8cmUWS2MnHt#bBzX$mP#f;;sADxA4~-S?{ixqS6*Pw?u;=SRVe~EP>}Txu zo~RsxD{e;3eh9*Cv!B~3cBcGF#n@X)p>;OnsSX zjQIoo*vWe;iZwn3KMA`ltX2!jeIR5v^w?BeP>M)#)WCdc3ZK7Bey5Us1*M~`rcO%c znnlF*LcK-SP>r|OI#frCwKZ5)$6yl3xK#ecVgVvDg~yy=HkQ?S@_MjN2dsTb$1&`U1(b(YK`zlO;bW`%xjSLz`B z3C8=>erql@gF&Gd=&YeqMUrK)y#m$!ly=gl)IHO*Cb9Rr595(h!WBV4F){XS9?+x0f}Knr`~3Jt*IsvZg4N zbW^f$t0c%tSZ5!xCe#HTU_y~f>XI!7sBy34w?#6Y>ei9RWIhPus`ovU59&Rj;aI7F zeb?BXGDPO+OR(J^aGxU&=mXfM0hynls{aitpkc(#S18Om?EEg2`;XGWlr!fu%O84$ zj7RNWEtBle)by4Wq-qf`tu589j01sm$9-QjC>z>-&(Gdr}^zW zI}V*N07Z2JYNe{#r(f9Fc9EQJ{-gWsOFG5eMSj%A`e(`6ME(shQeCh*L2i(bL8W#u z!xH(#+$6W4UGkZe`Fi1y{>F1Om-LEx#lcO;3+a^cSksQZkwBcmn3>Bj=erq=tUa_5HxIuk2Fz zX$ESfJiclSvYZ4HpM&)W6Coq1tGZLsj?x!+y_#u5-PW4=Y8DvP5ZwO`wb_>HsF|Vc9sgtc0j{T0DHQlM5kyk^8a<232v@{a|BG;tSj|N{B@Vv zz*)bqX$xMSB%RD-AlTW=+L9>ULv=9A>=y66EJypBk~+gKvZu)|qGCBV+9Xq`b>6_| zYnXXGdA?42n5E2EhItCW1B>jZ9!@8Oqy=%?y1 z`1A6+Hdhn`GjG$2?Bnq9Onnqq`AE+;Z=ulUu=f_M-_>Q*0aMUY^~wApdW}=SsV%AY zNw}*&cyS-K&1`bGxP0P0yTz5K=&6!~ioFr9t&J=Krh zD^MIoVX@OtpB|fB2A}MOHS)=R+f9BUJ6Ge+^?C~XI3K+~kQk5m?iT3;(Ee_!x)Ip8 zmAt6CY*#9|eYzMXo}->Fy#Tv^Ook33zXq9>@WlrEJv&>bm!d41pe=&>7@DCtdb2Q` za5K@CA_3}~mhu3#^V`JFdh&V*x^zCiLg&hDW;s0c1Ux)l&h+cqomlQn82btR9V|{V zjio2(bfxJaFW7-P6psm9QAFP%3+CxIBI6)up2U_P+a!6EXlhR#E^?I_${LEX>T9X( z6N#Et{2nh)(W9zF^Qp97iFQ^r6L>JAVwyeWm z%g8N^*h(&;9z9Ja>O89Sm-Hj9E2fuF$@OK2f53}3p*HRX;Tob9=aa8nP$uutBdDFJ z9pA#kjbZZ?eEPdSL@s-KFNp>gu%r?EJX9VA^{-$@d%>bIa)5k(%ckfL^1bY|&!BGl zpi|et>#yO3UgYu@`k;D#E=#1m&CXL`^kL*@U!waQ*3=o@^AlcP44ziNE`#~^L4An| zq=B5lH7i(yQiZ$+AKoK1!SKuYy^p+s#Y({H_nYzNBR$vNrk`N(b#}8|K-ToqSzK2I zj2z6*&#>7+-LG}^29WxBn0X2-?qNpBfAnFhx=!q+D{K5l3$nfz*tP>vUK@2Zh)8;s zd_PQ7bd^)d>GQ$3R@mTMxao5IbwKlR^_!sgO8rr9f)xi67u8_k^T4$F*n0;0xGpOH zApIxf^`=zp-Ld=@dl?Z|3Gcq5?WpP75m{aaE45vsY{zFUuum(qk@}+y<1Ju>y71*} z{RNx?<3Q&ViK=+1j*lwKw^Yrya{e;V;Yza#o=k;%ujEWMbgEDfc-#{Nkp}Rm29^6b z|9!Rm{4EDkTn)x-k(bRida8QQbetp?QJXzXyufk%o**09Z+}$zD(MSHb%t>oqRgJt z+ljkVvE>V}PJOu@hka&8ns?0=dN&$#344D8oSEmnv&=YSSnW&b%~sN!3P-40-Y3%U zq|)h24cJC*k>9CWTrEoFMb(aF&&0Nl3}F2CXc@41CVOp5cC{ly#!%hdO_k6L{5fRj z*|Xr(uh=a^!SrR_hzb>_oX-N&=zgA1&_Cpwahz$>aGD?wuJo(8hI0_Hb=|CAI157E44yZ z>^(s5)RWEcXs-U`=2%9573MD_-g^Q~&>g$UnzQi2$z0WnnA;Dx-bz+KZ7ai7FO#dy zwJLTFBTnwnTi}=bWsEH>!$6IN;J^}c zVIQ%!m%7Z_kI29x_@fl~mQ2-DjCfnas?WyPOLQ5CGZbBRCmdCqJr5xg$`Q@p-QD42 zMKx-qhUk>pc<~ooUA<59Y}WDYc_ys*nG|Ep_GIxK{TiLNfJ&z|yPE~RAA%KblXt++^BAoTnUrH5e4DC3DHmt<;JK(e(T1EiwuQXidMbv2i2$11p{gD!)Lzw-$bS2TOhfK6hmG z)Y|MSfn8L^s^|l>Ye^XDJ+#j>tt?lYo?O!fRzC@QkKo+5tZ$^E%Hi0nWtg4EUVmg= zo-%!h)fELpiZfmld5RaU0YIImbEHl}bCQF;@m&DU28fQFR z-AKn1y-QGsm!LmpbKQj~r?c7310dW`pGUvf7rAacxw*^BH&k2-KKY1U&C^?{cI)CD z50~en(fSexFG?k|39mGUAClp+wZ!*Q^1l;Rb#3W_Z-!yBAK2#*qVZ9NipUd;7q6Gf zby#W!D_acbUa#I~VdkUyz0VANKxWh@L(YVQ*P#Ta>r`~UqRlRYD?VYr)$wFEMsG{j zR*;>#04uzYw}N0)8|;1}sQo2cW}lpIUe=&}MMsIJZYKNxgKmDRbwkut_C8g`@3+7o z=W^9ztkIuyt>KXsc9#5U8(Q2#Yj^(VL+gZpDAYv;er-gIJ#m>vhF?h4IxBddRj%!*rF^biAk1bRO*a0Bnj1z99>88N!DkN;3D1y!O~Kb>D*B7yqo;}GZ}DOk{Rvye z!-rj9Kn2yFAOc&W+aD&rKBmIx3TvH&KE8oHzM|)lt0E1_s`KO#Dvq7Rdp>kqLF$Pc zh}AlB6|r^`QSH_E&C#cCli92A?M>*Rxm22mQH&zq`{+0OpWl9j2x~yZZ^r5m51`wqj7bGQ#)7El$xuA=B^B1sDCvV;3qg(~9l_ZQ+l12G#|4%=-5&kwVrq`)mk4?25Nn zp+NS*DosI^wanKP>8nF8qULM`wJC z#;uIj{R3X;2B(+90I-CiAxG8N}vVVtG5>-$H$~8@*E4e{Up6%$A2W1AZk? zkw{%l&R$9O%msV%!8|*dRYasC&tnPij&lbhs3?Et={TK0R^N_a*Q3IciG$_j((BAsm3qDanfW?i=>p20 z4qv{>xRs@!kCSJhDsDxWKPheLqcHdN;_+ueu&0&`hL^X&oiBk3>&OxBX*|cji_no7 zet_R^h9SQv)&>xZedRNGlzobvB7=zGy`bZH%s7sSp2}Re!21Wa5G?cv9JK^5rcg0I z=iRr32@4YkO|ZUT#WP{$`qURLXjPJxc<&jWiE4SA>+|FDHem4c>fJ$)19MN~nmBB{ zjdlEo(eA>&-uv3G!NAR>5y-Qf9Q+ZEZV4_fC-387wNg|9zw20Pp=4roFm`{MJUNNg zb_NH31P>FvZ~4Qk=fGKAA0 zsPAhxDuW?p*_EK^X0q{aqVWpqhDNMvD>Z&ou5Tbefer5y!}~zt1hC;WV#>pl6~xju z^xJyab2Rms5l`z0t7|JCf^JGo6u|!RTyv0GWfU1zi#YdNW8Av?B5b z=;f$}^Rd`gR$iOzTaDhY%zQyqaVzoOEnf!S#QV>S6=09Mv3e8nH0;gTXaKd41JC}> z*{`tNi{PmDoPl7W18|ggmwPE0+Zt_SN%PMwShaS&ksq3XY zI3i$8S-<)yNVVo^2(JP^RbC(mntI>m#z?)0j>#yngJk#R{QDX8MKx-++GJP-{!Ijz zy)0Y@5*5PIKQNBLI^R%vl_wfUv;OY*wI+Lcm2($k#d`k!&ZIKiK}}l4M~PMbU5P{3 zeU87cKVZ4B=(ZHF_;2?BvHf9s?F&HqZCMPQ7aCaujPJ*r6Kvu$U_FCe+5u zMEX6o z@pM#O;^13&`wTeO)5?$gkx~z|*?{8t(7%u5Jxl*8_|S!QmKX26t+qs(w-T>f*#uTt zyz@JX>^rX-B>v0d)3P9g7fV}EEcxYj-GHag^lkPTXX}vRi>aYbBpXVQe-2nX5QX(F zQCXBI8Q>#`8R1tlx*VgR7RmYs#8)DA3T0qf@FAWRd)nj@ zUhy)hFqZP(AE?B*H^DFq*h?>e^hUnDKOm2`6I(k`OS2fe3_E)d^;;5+d&nQRDiK`> z@6;!DIxuTx*t3K`b_HJndG9B=tULRT~t~M}#|| zTt!Ck{2j;YhGWBTHQA5S7VM?Hf4}Z#5VAP_UrI0UZsuMP$MF%v`&1samY=DOiJ_%L zSv53@r&gL#X|?fj=nS~_RHAJiwaMB3J<(tM|8vW`5B()snu^++vwzkt+<>|h3-^{U=!WT}I% z&%@82-YWxQ99A!qPY1WBpyr0+h0jrrN#I3(wDSGX3Bx^Eg7U(VNcUIlnE5uTrQJqwx?Wd&<>1bw>r8Go31ClvEd zi82?9G){~CePmA&e6t0fokCm(h}bi@!kd31IP2x63v+m}v4^#;VU_2C0rjxf9^I6t z2~wHSdn&#;whyMEq{o3xL8%n-{(RQ9#y?*XyB<{U&YGtje!+?sHPw;WJjB`Mpu|eQ zp4bdpRrVRB2v(`-=U#cdm7i#|@Lz2oVGCg=PxY?UR~V&)&vDCr9NO=_Z_OA1n9cF` z{uBMWvci4*^hPVp7y+!ep5MI)P2%ifMlOTxR-n?ocS-YeW}C0d7l7eaeGM=f`}brm z9+DXu00VC!>np<1Nq$}9V!I}w!_V|@gN=Uo-?tE-RVw>Cn+)zvL$2^;{vBRi`>9>o$7RPq4v%ibTs?ylD6bhs{e+m=5A!I%Q zJI#gbE}^mu&}Y7S46s|hcS~pZESv1BWS4c-@-48P+4A9w3(3F}v3yf-sv>x|)Mwm$ z%oOnRuzT0P zf4YlEFM<^etMF>)EnMZ@h4NV4(;UD0cPbm;*&~s)kk>NQ*-+@{2Qj(ANqlh=rV0T0O<;_#D&BA1@Wp3z=}hAJY-<^Oh(1yPxjgIZpPm$jdM9ou<=Kp@*2W zh=gKciT_G_@MpR%^s%BW`|{Mve&5c;7;%fg=M%_T@6&utDKyb}B@?)$>~ z7P~W;B|lf!We;y^ywArW8?L}-Uj7FBT=!O4jU3&~ifj6@vBtjxyo#&}`k8eYCOd@p zPV;@RK#zZ0#Rk61t8$A`y59`ie_xI8QK-oZ-d^(|=; zl}L2%^!=0IkMcWNxraz2e;&$P^-}Pm zp?L58)rNb#i1#8-d{p!N;6;*!BdWpr-q|8pVT*cq4Nhf6zxk-NnX7{u%BKxJyPwNd zUfh-A&o&~r0P8QwSS88m__V!y9-0ao1j&H1Fu14cii1@9e4XfJvh|-$34#Zns`uD? z9lP`5AlYA6JbuiF4eIfmw~~B518vlBW^eN`Z-MSj%e7DhSLVu?i#KVSoti2Q4R)dGi`+1Ro1q%6| z%kS$i*T;}^L7*Mfveg*V`q}Q;G#@#z4Lj@wb-W66uV0mVnppgGCHN~XlfH`+(|*py zTD4jIE_l<6w)V8Y_wNok?8?ht&l*WYkf;1Tx^L+O-}VXqt{lHo_3j`h_^WXJalAYz>pgSh`^vj>vw}P;0dg1gxzEED zPiJ~hOE&a>8mZ#vR#oOm(VwuarzRCH_eLtqx)(8S0d}^Vb0@=ViGKW_i1j1h%|*`I%Cb z-r-m&LSIj(ZXmKM(DJS-Cn;(1DE=xIvaduuT~Ivldm8G1ALq%wg*{X$)G`w?65TZ*+^zt=1WMkQ3*c>l4<2jtNFd%xu~#Y zVloIG$N9QsQ=0M&M@2|SdAqDB9$Q=gK6WUpi@*elzCKCtwV>livX@OBGL+!AU4Eq! zQr1?J)5qQCYy4#Xc-AZFKcVaOD4L%0Rq=K~AIqvRwzocqJz9Tl3H~c<{W-kep1Lgx z0_}$PO8D_szYOW<}GY z(R(!?KJk9v=HEBl|w-|g2g-jU*uQ4Wt6A}c~QO`IS37}LLxE&fw0@#O7Z z|EeVVZHG|`awZXPC;Au@KH{-S87xsk;{BYA=g)Wextz~PtNl+>C;Hkdl*_4U{J+;9 zxf)~t1|}Mx{dWLfo4!iXP=p+K5VJS&9!SioOhA{kJOGuw#P;{MtT`7B+~kEoCjyw!xS z4#jifw3T^rcUZlMNFb^bSyd5Mkm7s8q|M`@K!~-=@^de*IzA5V(EPrIL%quRRtv2v zl+*G4*j}Av{BsA%k~oLrhy1?vLRnqVkEGDPtRE4((sXTM|2@ir{z~@vR*LsO ztrS{uVfGj=lCETmrFqMHHK?9yPx37g@2@(72i9Lfv9#aHrthYh?9kn(Bqme2Rm z7EMU&vt2{%D{1=&J#cqn0NT0_$0%^WCQRns&%U(SdC*HjSCrvXY z`;0GXvw3?c>d(2~SKi^f&))e1YW+QXJyZP8c7@iLoc7bJq)ZxKgjm)1`wSi7%n8|a zw??e#jhE=FyHKU(#Z01vn6jilo~IZ?NODkK>FTYE8Ggewl#S@M`*yFAn+fksy+uEyexoy|t7|(+8fG%a?X#5y^Z6qS_%$ z4`ptOuN^|snwahrC&RUgKE8*mv9QI%b}{Lemh^m1lp~LL-`bupN~VuqAZiAa>59m@ zg^9<^YaCj)6KagMmgNF)vu(syps<_kpGCv36E4kYGEG9HmR^QT>->O`SN=EJ1_ zjZf?4{TnY~#EMUk&&+ci`HFXh?H|an=1?mk<{IMDTUo6%*6Q=|$D~~qJws+&nInFS zSU)a({D7Yup(8GR#H`0KY3ni)+G%{2y~0OA`W#31VqE%|j_;9J_J~jW893s#PC#?ZFL?d#HH;j+%MdY zOPf2?N{|qO1k(2<>3>7#1HNVAqcSlP$>F_cv~^tCd|^9>dtF^?R8vtb z2uUUcr4?TR(S#6iFeIQns`U}HNJ}7~5ivv~5khK>kSHbsVuc1W5UaLHr4Xnh2$n(; z9ww!fmjO}?B_N5BXCSXT*I8X_*38dy*E-+b=iB$&_pG(|Ih)|!ckkK8GO7n1@k%y8 zE2_Sf*}5-R=CQV@B4unoADLX6G4`3s$FH3nUE~$33r7Ni zvp%{X#uYMIY(+r;Sj&}6S=D*@f^TQjmS40J+Qc?IdV#+uL5>V?5FDko(ttRraYCnf zvtcBSGJgG3tdN6jxmUryFrJrFIu~)PwwMCL4k3do$!!M+R8E@dp03y&fcRde%#26N z0w`JXv(7+%W(T)uv{+dhy=hJIcqP|?n%sKa-+-PA_^M+RMmr5ql3G4-_$)Hkh%;)p z3!|n46M}pq;b2Pey0mYQIQUJMZrwNTc3CLD^+)k6bM9QgBOjNGeeJ*RwQk^K5gvU& zBM|Pa5|l$uN56j(Ulk?W4=~?I`Brb0EE6PIh?oQ5mpH>Ai!(2cl z*xky(V#5j>Ulosp?0M1QN34mJ4-@4obk?!Fg4FHbTvUq{XGm?WO(CcrNq57IgIXT% zy5I@YLSeUd#6-24L8CUJkNz^ATz>A*YY5^jN{(YebG;0o#{}ESHHOqANF&yGO$X@wN>VzM%ez;(w%=d;s4A&1w_BooVj>-aH0^g) zRY|J^MTS>_R^*hI;34p~v3tIwfF<+%cnZ1EHK}<-88v9yWBz`Z)vu-}x;ikeHm}lr zC+_D=;%E1BMw^JAepfN&zg03ZodLFvfJ5xJhqopsxGn8{T66vL6U6)(|wQln*6SB;_WJAELLotn8P%3o(M!J0IW?_-#izqE*B< zbgvS>FQRyLcaUm4)zszX9+VijT~-`mLNJaNGRuW<_I*mbLn)>CzkY0}+U23jRo{)W z*92uWeLc_(@%&kAic&lc*#i(ve%X&-smEz&PbLQoqx!&33DuRqdv%ALpxBGcpcwMv zNX)(Qj#;=JPKQxvP&-;U%zI#kn;4sKcATCaH2jcVJP07LKSB^vU{ z`jAAe20fF0PT#duFl#|PkSK%vqjK5Nw>R^fzH$*KyWPap(_KpdC9Tp_25-ORLHC|o zwc8_Sd`x*xP2j_Z**MpX`=pv4b6h;@9*gc8@6pS0_sqx{Bdi#AD7{_~n~`%VYkY4U zQ>`s}kzQYdojN2Fd?l=*9h?#Ft zETx>csJn{FwsgJbK7waFzA<#P5+nINQG8lz{#=mb6@9U7B>ckBI{}?3&7G+y^>_BS z*!hNato}bzVW|4KxUr{(emCd##Cf%u14t?EC2%yEp0qo>{DCSnDM#d&GNt}L zCZCl&=ZBe>wy#eIw$EAoh%x?)%GCDux_YcbdZK+Y-YQhVs1K?sUH0q1;9cbp#Ek2= zN(9YW@VLpRKpzB`(s$_;y#>sm(csMZv!8WLeri&zu-Jz4B;BIG;J&qz5fOiTP<-L~ zKY#sI?{sv(2)?@RS^6dyCss?4ZEygop`F(L^mV+bSq6pIsU~LM`%MKO1T5}4AH{^| zb|=kMY*D;kCjqL@>UC|7X_Nu(Mix%YPIoT|eDHs7$vAVD+=gL)8~;4d+{MS8_{Z1< z8;NgkumJJKq4z1ZP*-}vuiZwne~z~kY<(^L*++OGc8J>&XG()25QTPe;h==#aeewg` zyE*wj__2nE4*q-Nb+a!O$=E`dilGBh;&fBQ^)tXp=WQO$$d{TWQ0hoC z)vn00lDH+!wdE0EoN{MA>>rqTO>UF!W6APFEe>r7d!$YVNIIgRNHeeR`B*bt*fMtZ32cK8H_%Zn->nGivVA*`clDlo zcAB}WfCb)4>lkJXX=}?TXs>$GU~qc~e(c5$z!(khG_r>fisvxFp9^dh!dv@KYkInc zbPniQ!)qf&qTEq^i`8PZF@EBe(MfJH0aO* zpl3GK&CX8dHm+O-V2xx1CGnB(yXQ&hloewryp#dk@8wKg)0Xl`!Slh7=UFRPsHRp)*pN zV{qEJH5k6Au@cQZT+AE!rNnYUXEGobtQ+<<>r#e8KbGed**vZF7?FnNC})aYy;~ve zsP|)3$)4I&f`W9ShhyK%+g zBVin$o@tc})7ZF>a3*h^w{7`vjd|9Ymq3pZ)Zyk&_sv9(^CM9zH!f{N?2xJfnJ!CR zn{*LWE}B;_+>4So(oiZas=s7@vD%_|nSQv7sRN3q=~D_4<3^w<6qj=(3=ezL`EptH z-vXv)+fnZzeC6(;$0}P(_a3Vp+w+`^*#!z}p;)sohR<|>q(8m_HkSR+c2Gy$j34=h z1scb=9L8y;Bdc-D3qlzn>c=bA!=`DfqwRN{&}D=2-z?#m<&P|x?q3>6F3Zx zIaM~DA`iVkl^+o{*86Ua{|{3@Z@=BIqAJd0Wo$mg|G^O%Hz&9lvK0{)KCcZN4SiOj zW7!NU_}Zz^5rz7n{{``hoKQ*-f3frzG)N_ti;le01)Wvt%YX#*JHZ9wPhqRd%0X3J zopUE00SET6 literal 0 HcmV?d00001 From 704863533f61b9ad728052d4654862044df58016 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 30 Nov 2022 17:37:48 -0500 Subject: [PATCH 06/34] GUI: tap to change order in order bar --- src/gui/gui.cpp | 7 +++++++ src/gui/gui.h | 4 +++- src/gui/orders.cpp | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b42345b8..84739577 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2198,6 +2198,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { } } if (orderScrollLocked) { + if (fabs(orderScrollRealOrigin.x-dragX)>2.0f*dpiScale || fabs(orderScrollRealOrigin.y-dragY)>2.0f*dpiScale) orderScrollTolerance=false; orderScroll=(orderScrollSlideOrigin-dragX)/(40.0*dpiScale); if (orderScroll<0.0f) orderScroll=0.0f; if (orderScroll>(float)e->curSubSong->ordersLen-1) orderScroll=e->curSubSong->ordersLen-1; @@ -2913,11 +2914,15 @@ void FurnaceGUI::pointUp(int x, int y, int button) { sampleDragActive=false; if (orderScrollLocked) { int targetOrder=round(orderScroll); + if (orderScrollTolerance) { + targetOrder=round(orderScroll+(orderScrollRealOrigin.x-(canvasW/2))/(40.0f*dpiScale)); + } if (targetOrder<0) targetOrder=0; if (targetOrder>e->curSubSong->ordersLen-1) targetOrder=e->curSubSong->ordersLen-1; if (curOrder!=targetOrder) setOrder(targetOrder); } orderScrollLocked=false; + orderScrollTolerance=false; if (selecting) { if (!selectingFull) cursor=selEnd; finishSelection(); @@ -5741,6 +5746,7 @@ FurnaceGUI::FurnaceGUI(): nonLatchNibble(false), keepLoopAlive(false), orderScrollLocked(false), + orderScrollTolerance(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING), @@ -5831,6 +5837,7 @@ FurnaceGUI::FurnaceGUI(): nextAddScroll(0.0f), orderScroll(0.0f), orderScrollSlideOrigin(0.0f), + orderScrollRealOrigin(0.0f,0.0f), layoutTimeBegin(0), layoutTimeEnd(0), layoutTimeDelta(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 04811e08..128bbe26 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1419,7 +1419,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; - bool keepLoopAlive, orderScrollLocked; + bool keepLoopAlive, orderScrollLocked, orderScrollTolerance; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; std::atomic curWindowThreadSafe; float peak[2]; @@ -1564,6 +1564,8 @@ class FurnaceGUI { float nextScroll, nextAddScroll, orderScroll, orderScrollSlideOrigin; + ImVec2 orderScrollRealOrigin; + int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; int renderTimeBegin, renderTimeEnd, renderTimeDelta; int eventTimeBegin, eventTimeEnd, eventTimeDelta; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 78992116..d20232e8 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -81,7 +81,9 @@ void FurnaceGUI::drawMobileOrderSel() { } if (ImGui::IsItemClicked()) { orderScrollSlideOrigin=ImGui::GetMousePos().x+orderScroll*40.0f*dpiScale; + orderScrollRealOrigin=ImGui::GetMousePos(); orderScrollLocked=true; + orderScrollTolerance=true; } } ImGui::End(); From 285dae9239c08856910223fa80b3ffe55b528cb5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 30 Nov 2022 19:42:51 -0500 Subject: [PATCH 07/34] GUI: inertial scrolling experiments --- extern/imgui_patched/imgui.cpp | 39 +++++++++++++++++++++++++++ extern/imgui_patched/imgui.h | 3 +++ extern/imgui_patched/imgui_internal.h | 1 + src/gui/gui.cpp | 2 ++ 4 files changed, 45 insertions(+) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index d52945d4..216d7a67 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3313,6 +3313,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst TabId = GetID("#TAB"); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); + InertialScrollSpeed = ImVec2(0.0f, 0.0f); AutoFitFramesX = AutoFitFramesY = -1; AutoPosLastDirection = ImGuiDir_None; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; @@ -6893,6 +6894,43 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); + // Inertial scroll + if (g.IO.ConfigFlags & ImGuiConfigFlags_InertialScrollEnable) { + if (g.HoveredWindow == window) { + if (g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) { + // launch inertial scroll + if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { + g.HoveredWindow->InertialScrollSpeed=ImVec2(0.0f,0.0f); + } else { + g.HoveredWindow->InertialScrollSpeed=ImVec2(-g.IO.MouseDelta.x,-g.IO.MouseDelta.y); + } + } + } + + if (window->ScrollTarget.x == FLT_MAX && window->ScrollTarget.y == FLT_MAX) { + if (fabs(window->InertialScrollSpeed.x)>0.1f) { + window->Scroll.x=window->Scroll.x+window->InertialScrollSpeed.x; + window->InertialScrollSpeed.x*=0.95f; + } else { + window->InertialScrollSpeed.x=0.0f; + } + if (fabs(window->InertialScrollSpeed.y)>0.1f) { + window->Scroll.y=window->Scroll.y+window->InertialScrollSpeed.y; + window->InertialScrollSpeed.y*=0.95f; + } else { + window->InertialScrollSpeed.y=0.0f; + } + } else { + window->InertialScrollSpeed.x=0.0f; + window->InertialScrollSpeed.y=0.0f; + } + + if (g.IO.MouseDown[ImGuiMouseButton_Left]) { + window->InertialScrollSpeed.x=0.0f; + window->InertialScrollSpeed.y=0.0f; + } + } + // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); @@ -18612,6 +18650,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId); BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); + BulletText("InertialScrollSpeed: %.2f,%.2f",window->InertialScrollSpeed.x,window->InertialScrollSpeed.y); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 3d600ea2..3ff2deb7 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1573,6 +1573,9 @@ enum ImGuiConfigFlags_ // [BETA] Docking ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. + // [CUSTOM] Inertial scroll + ImGuiConfigFlags_InertialScrollEnable = 1 << 7, // Docking enable flags. + // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends) diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index d7270373..6d461076 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -2293,6 +2293,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. + ImVec2 InertialScrollSpeed; // current speed of inertial scroll (AKA "swipe") bool ScrollbarX, ScrollbarY; // Are scrollbars visible? bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 84739577..fecd377b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2629,9 +2629,11 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { mobileUI=enable; if (mobileUI) { ImGui::GetIO().IniFilename=NULL; + ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_InertialScrollEnable; } else { ImGui::GetIO().IniFilename=NULL; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); + ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable; } } } From 1ac507a39fefb777625f84dc5dba53fe99724472 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 30 Nov 2022 21:59:49 -0500 Subject: [PATCH 08/34] GUI: update credits --- src/gui/about.cpp | 1 + src/gui/songInfo.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 37c8ee7b..e7e96e3c 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -93,6 +93,7 @@ const char* aboutLine[]={ "Raijin", "SnugglyBun", "SuperJet Spade", + "SwapXFO", "TakuikaNinja", "The Blender Fiddler", "TheDuccinator", diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index 15199187..493ad597 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -43,10 +43,10 @@ void FurnaceGUI::drawSongInfo(bool asChild) { if (ImGui::InputText("##Name",&e->song.name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED updateWindowTitle(); } - if (e->song.insLen==2) { + if (e->song.insLen==1) { unsigned int checker=0x11111111; unsigned int checker1=0; - DivInstrument* ins=e->getIns(1); + DivInstrument* ins=e->getIns(0); if (ins->name.size()==15 && e->curSubSong->ordersLen==8) { for (int i=0; i<15; i++) { checker^=ins->name[i]< Date: Thu, 1 Dec 2022 02:02:33 -0500 Subject: [PATCH 09/34] GUI: improvements to inertial scrolling --- extern/imgui_patched/imgui.cpp | 10 +++++++--- extern/imgui_patched/imgui_internal.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 216d7a67..a603ac41 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -6896,13 +6896,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inertial scroll if (g.IO.ConfigFlags & ImGuiConfigFlags_InertialScrollEnable) { - if (g.HoveredWindow == window) { + if (g.IO.MouseClicked[ImGuiMouseButton_Left] && (g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { + g.InertialScrollId = window->ID; + printf("changing the ID to %d\n",window->ID); + } + if ((g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { if (g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) { // launch inertial scroll if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { - g.HoveredWindow->InertialScrollSpeed=ImVec2(0.0f,0.0f); + window->InertialScrollSpeed=ImVec2(0.0f,0.0f); } else { - g.HoveredWindow->InertialScrollSpeed=ImVec2(-g.IO.MouseDelta.x,-g.IO.MouseDelta.y); + window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseDelta.x:0.0f,window->ScrollbarY?-g.IO.MouseDelta.y:0.0f); } } } diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 6d461076..079ea0fd 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -1817,6 +1817,7 @@ struct ImGuiContext ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; float WheelingWindowTimer; + ImGuiID InertialScrollId; // The last window in where to apply inertial scroll // Item/widgets state and tracking information ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] From a04d6b8e0b58cc32c8c9c3a0cab10c69f544d7f7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 03:01:59 -0500 Subject: [PATCH 10/34] GUI: more inertial scrolling work --- extern/imgui_patched/imgui.cpp | 31 ++++++++++++++++++++++----- extern/imgui_patched/imgui.h | 5 +++++ extern/imgui_patched/imgui_internal.h | 1 - src/gui/debugWindow.cpp | 2 +- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index a603ac41..c85507c5 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -1178,6 +1178,9 @@ ImGuiIO::ImGuiIO() ConfigViewportsNoDecoration = true; ConfigViewportsNoDefaultParent = false; + // Inertial scrolling options (when ImGuiConfigFlags_InertialScrollEnable is set) + ConfigInertialScrollToleranceSqr = 36.0f; + // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -4156,6 +4159,8 @@ static void ImGui::UpdateMouseInputs() // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&io.MousePos)) io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); + + io.MouseDeltaPrev=io.MouseDelta; // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) @@ -4167,6 +4172,18 @@ static void ImGui::UpdateMouseInputs() if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; + // Update mouse speed + if (ImFabs(io.MouseDelta.x)>ImFabs(io.MouseDeltaPrev.x)) { + io.MouseSpeed.x=io.MouseDelta.x; + } else { + io.MouseSpeed.x=io.MouseDeltaPrev.x; + } + if (ImFabs(io.MouseDelta.y)>ImFabs(io.MouseDeltaPrev.y)) { + io.MouseSpeed.y=io.MouseDelta.y; + } else { + io.MouseSpeed.y=io.MouseDeltaPrev.y; + } + io.MousePosPrev = io.MousePos; for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { @@ -6896,17 +6913,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inertial scroll if (g.IO.ConfigFlags & ImGuiConfigFlags_InertialScrollEnable) { - if (g.IO.MouseClicked[ImGuiMouseButton_Left] && (g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { - g.InertialScrollId = window->ID; - printf("changing the ID to %d\n",window->ID); - } if ((g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { if (g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) { // launch inertial scroll if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); } else { - window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseDelta.x:0.0f,window->ScrollbarY?-g.IO.MouseDelta.y:0.0f); + if (g.IO.MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>g.IO.ConfigInertialScrollToleranceSqr) { + if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { + window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseSpeed.x:0.0f,window->ScrollbarY?-g.IO.MouseSpeed.y:0.0f); + } else { + window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseDelta.x:0.0f,window->ScrollbarY?-g.IO.MouseDelta.y:0.0f); + } + } else { + window->InertialScrollSpeed=ImVec2(0.0f,0.0f); + } } } } diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 3ff2deb7..240ebfc9 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -2022,6 +2022,9 @@ struct ImGuiIO bool ConfigViewportsNoDecoration; // = true // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). bool ConfigViewportsNoDefaultParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform backend to setup a parent/child relationship between the OS windows (some backend may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. + // Inertial scrolling options (when ImGuiConfigFlags_InertialScrollEnable is set) + float ConfigInertialScrollToleranceSqr;// = 36.0f // After a point moves past this distance, inertial scroll begins + // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. @@ -2099,6 +2102,8 @@ struct ImGuiIO int MetricsActiveWindows; // Number of active windows int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + ImVec2 MouseDeltaPrev; // Previous mouse delta. + ImVec2 MouseSpeed; // Average mouse speed in a short timeframe. Used for inertial scroll. // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 079ea0fd..6d461076 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -1817,7 +1817,6 @@ struct ImGuiContext ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; float WheelingWindowTimer; - ImGuiID InertialScrollId; // The last window in where to apply inertial scroll // Item/widgets state and tracking information ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 17ac924b..2149bfaf 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -42,7 +42,7 @@ void FurnaceGUI::drawDebug() { nextWindow=GUI_WINDOW_NOTHING; } if (!debugOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(canvasW,canvasH)); + ImGui::SetNextWindowSizeConstraints(ImVec2(100.0f*dpiScale,100.0f*dpiScale),ImVec2(canvasW,canvasH)); if (ImGui::Begin("Debug",&debugOpen,globalWinFlags|ImGuiWindowFlags_NoDocking)) { ImGui::Text("NOTE: use with caution."); if (ImGui::TreeNode("Debug Controls")) { From bb5b99ec96336ccb7355d8b9252b57c7b07d4f78 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 03:29:32 -0500 Subject: [PATCH 11/34] GUI: and a bit more of it --- extern/imgui_patched/imgui.cpp | 22 +++++++++++++++++++--- extern/imgui_patched/imgui_internal.h | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index c85507c5..fec5f145 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3540,6 +3540,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; if (!IsItemFocused()) return false; + if (window->InertialScroll) + return false; } else { @@ -3572,6 +3574,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if the item is disabled if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; + + // Test for inertial scroll + if (window->InertialScroll) + return false; // Special handling for calling after Begin() which represent the title bar or tab. // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) @@ -3609,8 +3615,9 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) SetHoveredID(id); // When disabled we'll return false but still set HoveredId + // Same thing if swiping ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); - if (item_flags & ImGuiItemFlags_Disabled) + if (item_flags & ImGuiItemFlags_Disabled || window->InertialScroll) { // Release active id if turning disabled if (g.ActiveId == id) @@ -6914,19 +6921,27 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inertial scroll if (g.IO.ConfigFlags & ImGuiConfigFlags_InertialScrollEnable) { if ((g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { - if (g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) { + if ((g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) && + g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_X) && + g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_Y)) { // launch inertial scroll if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); + window->InertialScroll=false; } else { if (g.IO.MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>g.IO.ConfigInertialScrollToleranceSqr) { if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseSpeed.x:0.0f,window->ScrollbarY?-g.IO.MouseSpeed.y:0.0f); + window->InertialScroll=false; } else { window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseDelta.x:0.0f,window->ScrollbarY?-g.IO.MouseDelta.y:0.0f); + if (window->ScrollbarX || window->ScrollbarY) { + window->InertialScroll=true; + } } } else { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); + window->InertialScroll=false; } } } @@ -6948,6 +6963,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { window->InertialScrollSpeed.x=0.0f; window->InertialScrollSpeed.y=0.0f; + window->InertialScroll=false; } if (g.IO.MouseDown[ImGuiMouseButton_Left]) { @@ -18677,7 +18693,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("InertialScrollSpeed: %.2f,%.2f",window->InertialScrollSpeed.x,window->InertialScrollSpeed.y); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); - BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); + BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d, InertialScroll: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems, window->InertialScroll); for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) { ImRect r = window->NavRectRel[layer]; diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 6d461076..a0056927 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -2307,6 +2307,7 @@ struct IMGUI_API ImGuiWindow bool IsFallbackWindow; // Set on the "Debug##Default" window. bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + bool InertialScroll; // Set when inertial scrolling is active signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. From 6b34b9fcab212cd156729fe38a73098990990127 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 04:28:12 -0500 Subject: [PATCH 12/34] GUI: add functions to inhibit inertial scrolling --- extern/imgui_patched/imgui.cpp | 25 ++++++++++++++++++++++++- extern/imgui_patched/imgui.h | 3 +++ extern/imgui_patched/imgui_internal.h | 4 +++- extern/imgui_patched/imgui_widgets.cpp | 2 +- src/gui/insEdit.cpp | 6 ++++++ src/gui/plot_nolerp.cpp | 6 +++--- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index fec5f145..07928bb3 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3447,6 +3447,15 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) { g.ActiveIdIsAlive = id; g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + // TODO: check whether this works + if (g.LastItemData.InFlags & ImGuiItemFlags_NoInertialScroll) { + if (window) { + window->InertialScrollInhibited=true; + printf("inhibiting scroll\n"); + } + } + } else { + if (window) window->InertialScrollInhibited=false; } // Clear declaration of inputs claimed by the widget @@ -6923,7 +6932,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if ((g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow) == window) { if ((g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) && g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_X) && - g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_Y)) { + g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_Y) && + !window->InertialScrollInhibited) { // launch inertial scroll if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); @@ -6970,6 +6980,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InertialScrollSpeed.x=0.0f; window->InertialScrollSpeed.y=0.0f; } + if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { + window->InertialScrollInhibited=false; + } } // Apply scrolling @@ -7534,6 +7547,16 @@ void ImGui::EndDisabled() g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } +// InhibitInertialScroll() + +void ImGui::InhibitInertialScroll() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window!=NULL) { + window->InertialScrollInhibited=true; + } +} + // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) { diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 240ebfc9..afc84eba 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -838,6 +838,9 @@ namespace ImGui IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); + // Inertial scroll + IMGUI_API void InhibitInertialScroll(); + // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index a0056927..83faaf6e 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -815,7 +815,8 @@ enum ImGuiItemFlags_ ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_Inputable = 1 << 8, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_NoInertialScroll = 1 << 9 // false // Disable inertial scroll when activated }; // Storage for LastItem data @@ -2294,6 +2295,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. ImVec2 InertialScrollSpeed; // current speed of inertial scroll (AKA "swipe") + bool InertialScrollInhibited; // Is inertial scroll inhibited? (e.g. by ImGuiItemFlags_NoInertialScroll) bool ScrollbarX, ScrollbarY; // Are scrollbars visible? bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index 4579400d..744ef29c 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -2949,7 +2949,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) + if (!ItemAdd(total_bb, id, &frame_bb, (temp_input_allowed ? ImGuiItemFlags_Inputable : 0) | ImGuiItemFlags_NoInertialScroll)) return false; // Default format string when passing NULL diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7dc01069..bf472751 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1378,6 +1378,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail PlotCustom("##IMacro",asFloat,totalFit,macroDragScroll,NULL,i.min+i.macro->vScroll,i.min+i.macro->vScroll+i.macro->vZoom,ImVec2(availableWidth,(i.macro->open&1)?(i.height*dpiScale):(32.0f*dpiScale)),sizeof(float),i.color,i.macro->len-macroDragScroll,i.hoverFunc,i.hoverFuncUser,i.blockMode,(i.macro->open&1)?genericGuide:NULL,doHighlight); } if ((i.macro->open&1) && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { + ImGui::InhibitInertialScroll(); macroDragStart=ImGui::GetItemRectMin(); macroDragAreaSize=ImVec2(availableWidth,i.height*dpiScale); if (i.isBitfield) { @@ -1466,6 +1467,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail if (i.bit30) { PlotCustom("##IMacroBit30",bit30Indicator,totalFit,macroDragScroll,NULL,0,1,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,¯oHoverBit30); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + ImGui::InhibitInertialScroll(); macroDragStart=ImGui::GetItemRectMin(); macroDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale); macroDragInitialValueSet=false; @@ -1486,6 +1488,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail // loop area PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,¯oHoverLoop); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + ImGui::InhibitInertialScroll(); macroLoopDragStart=ImGui::GetItemRectMin(); macroLoopDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale); macroLoopDragLen=totalFit; @@ -1498,6 +1501,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); } if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + ImGui::InhibitInertialScroll(); if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { i.macro->rel=255; } else { @@ -4423,6 +4427,7 @@ void FurnaceGUI::drawInsEdit() { macroDragLineMode=false; macroDragLineInitial=ImVec2(0,0); processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + ImGui::InhibitInertialScroll(); } ImGui::EndTabItem(); } @@ -4452,6 +4457,7 @@ void FurnaceGUI::drawInsEdit() { macroDragLineMode=false; macroDragLineInitial=ImVec2(0,0); processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + ImGui::InhibitInertialScroll(); } ImGui::EndDisabled(); diff --git a/src/gui/plot_nolerp.cpp b/src/gui/plot_nolerp.cpp index b75bda4f..f1120ed4 100644 --- a/src/gui/plot_nolerp.cpp +++ b/src/gui/plot_nolerp.cpp @@ -76,7 +76,7 @@ int PlotNoLerpEx(ImGuiPlotType plot_type, const char* label, float (*values_gett const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ImGui::ItemSize(total_bb, style.FramePadding.y); - if (!ImGui::ItemAdd(total_bb, 0, &frame_bb)) + if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; const bool hovered = ImGui::ItemHoverable(frame_bb, id); @@ -203,7 +203,7 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx), const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ImGui::ItemSize(total_bb, style.FramePadding.y); - if (!ImGui::ItemAdd(total_bb, 0, &frame_bb)) + if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; const bool hovered = ImGui::ItemHoverable(frame_bb, id); @@ -313,7 +313,7 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ImGui::ItemSize(total_bb, style.FramePadding.y); - if (!ImGui::ItemAdd(total_bb, 0, &frame_bb)) + if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll)) return -1; const bool hovered = ImGui::ItemHoverable(frame_bb, id); From 9c7e25f33c5f7809b07dd3d260cac7a17955c268 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 17:58:58 -0500 Subject: [PATCH 13/34] a bunch of instruments Uhrwerk's Patch Series --- instruments/FM/uhrwerk/B Series/b00.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b00a.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b00b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b01.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b01b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b02.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b02b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b02c MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b02c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b03.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b04.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b05.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b06.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b07.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b08.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b08b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b08c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b09.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b10.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b10b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b11.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b12.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b13.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b14.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b15.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b16.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b17.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b17b MUTE.dmp | Bin 0 -> 51 bytes .../uhrwerk/B Series/b17b SLAP (actual).dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b17b SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b17b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b18 MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b18 SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b18.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b19 MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b19 SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b19.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b20.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b21.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b21b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b21c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22 MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22 SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22c MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22c SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b22c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b23 MUTE.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b23 SLAP.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b23.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/B Series/b69.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p01.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p01b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p02.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p02b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p03.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p03b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p04.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p05.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p06.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p06b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p06c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p07.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p08.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p09.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p10.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p10b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/P Series/p11.dmp | Bin 0 -> 51 bytes .../uhrwerk/R Series/act 1 void of space.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r10.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r10b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r11.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r12.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r13.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r14.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r15.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r16.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r2.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r27.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r3.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r33.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r4.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r5.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r65.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r6b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r7.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r7b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r7c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r8.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r8b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r8c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r9.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/R Series/r9b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/README.txt | 23 ++++++++++++++++++ instruments/FM/uhrwerk/Z Series/z00.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z01.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z02.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z02b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z02c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z03.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z04.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z04b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z04c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z05.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z05b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z05c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z05d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z06.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z06b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z07.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z08.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z09.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z10.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z10b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z10c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z10d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z10e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z11.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z11b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z11c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z11d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z11e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z12.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z13.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z14.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z14b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z14c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z14d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z15.dmp | Bin 0 -> 51 bytes .../FM/uhrwerk/Z Series/z15b (quiet).dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z15b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z16.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z16b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z16c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z16d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z17.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z17b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z17c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z17d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z17e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z18.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z19.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z20.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z20b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z21.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z22.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z22b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z23.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z24.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z24b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z24c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z25.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z26.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z26b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z26c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z26d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z26e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z27.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z28.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z28b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z28c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z29.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z30.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z30b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z30c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z31.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z32.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z33.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z33b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z33c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z33d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z33e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z34.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z34b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z35.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z36.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z37.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z38.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z39.dmp | Bin 0 -> 51 bytes .../FM/uhrwerk/Z Series/z39b low vol.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z39b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z39c.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z39d.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z39e.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z40.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z41.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z41b.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/Z Series/z42.dmp | Bin 0 -> 51 bytes instruments/FM/uhrwerk/arbys.dmp | Bin 0 -> 51 bytes instruments/README.md | 1 + 191 files changed, 24 insertions(+) create mode 100644 instruments/FM/uhrwerk/B Series/b00.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b00a.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b00b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b01.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b01b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b02.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b02b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b02c MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b02c.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b03.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b04.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b05.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b06.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b07.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b08.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b08b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b08c.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b09.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b10.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b10b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b11.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b12.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b13.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b14.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b15.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b16.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b17.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b17b MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b17b SLAP (actual).dmp create mode 100644 instruments/FM/uhrwerk/B Series/b17b SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b17b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b18 MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b18 SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b18.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b19 MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b19 SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b19.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b20.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b21.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b21b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b21c.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22 MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22 SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22b.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22c MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22c SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b22c.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b23 MUTE.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b23 SLAP.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b23.dmp create mode 100644 instruments/FM/uhrwerk/B Series/b69.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p01.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p01b.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p02.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p02b.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p03.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p03b.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p04.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p05.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p06.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p06b.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p06c.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p07.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p08.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p09.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p10.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p10b.dmp create mode 100644 instruments/FM/uhrwerk/P Series/p11.dmp create mode 100644 instruments/FM/uhrwerk/R Series/act 1 void of space.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r10.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r10b.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r11.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r12.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r13.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r14.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r15.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r16.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r2.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r27.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r3.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r33.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r4.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r5.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r65.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r6b.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r7.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r7b.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r7c.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r8.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r8b.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r8c.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r9.dmp create mode 100644 instruments/FM/uhrwerk/R Series/r9b.dmp create mode 100644 instruments/FM/uhrwerk/README.txt create mode 100644 instruments/FM/uhrwerk/Z Series/z00.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z01.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z02.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z02b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z02c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z03.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z04.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z04b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z04c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z05.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z05b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z05c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z05d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z06.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z06b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z07.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z08.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z09.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z10.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z10b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z10c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z10d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z10e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z11.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z11b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z11c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z11d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z11e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z12.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z13.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z14.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z14b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z14c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z14d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z15.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z15b (quiet).dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z15b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z16.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z16b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z16c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z16d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z17.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z17b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z17c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z17d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z17e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z18.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z19.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z20.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z20b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z21.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z22.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z22b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z23.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z24.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z24b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z24c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z25.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z26.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z26b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z26c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z26d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z26e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z27.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z28.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z28b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z28c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z29.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z30.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z30b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z30c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z31.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z32.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z33.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z33b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z33c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z33d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z33e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z34.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z34b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z35.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z36.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z37.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z38.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39b low vol.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39c.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39d.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z39e.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z40.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z41.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z41b.dmp create mode 100644 instruments/FM/uhrwerk/Z Series/z42.dmp create mode 100644 instruments/FM/uhrwerk/arbys.dmp diff --git a/instruments/FM/uhrwerk/B Series/b00.dmp b/instruments/FM/uhrwerk/B Series/b00.dmp new file mode 100644 index 0000000000000000000000000000000000000000..0106bc0be5c98174505a35ba51dcbb4542760e81 GIT binary patch literal 51 xcmWm2K@I>A3;@Axs}V$maPI$qiQ8nF3hKy?=?<15pY;YSFrr(1OrI{F$^{|u0YU%( literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b00a.dmp b/instruments/FM/uhrwerk/B Series/b00a.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d41a11dd5ef252c922f2f53e42cf0503f714eb49 GIT binary patch literal 51 ycmWm3F%AGA2*AJs5~IdJqci{irMO+%p@KScV!4A=sCQ?jTU|?ovU=R@H1T#4p7~SQ0!AzhEZh3wNkODyl04wSNxBvhE literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b02c.dmp b/instruments/FM/uhrwerk/B Series/b02c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..97febb87e11b84509b6cd781478d81453f25d9e6 GIT binary patch literal 51 xcmd;PVq{=pVBi;(7i8sUU|?ovU=R@HWCk%g7#OYOdBIGe3T}B;29PKd0{|+L0h0g# literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b03.dmp b/instruments/FM/uhrwerk/B Series/b03.dmp new file mode 100644 index 0000000000000000000000000000000000000000..bb666c812c129e5ea17bb396d7386016b4be4250 GIT binary patch literal 51 ycmd;PVq{=sVBlAm7i8sUU|?ovU=R@HWCk%g7#Qv3dBIE;1_o|jq72;ftPBt)03&h%N&o-= literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b07.dmp b/instruments/FM/uhrwerk/B Series/b07.dmp new file mode 100644 index 0000000000000000000000000000000000000000..a3614deec34c96b357de5c17e8306954d78e911b GIT binary patch literal 51 pcmd;PVq{=vWnhqy=V#_;U|?ooVBizwgfr!NS)iia@~jLHCIBOY0Z0G< literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b08.dmp b/instruments/FM/uhrwerk/B Series/b08.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e8e80559d442703f6860f6c7233b45e9a9a7a0a4 GIT binary patch literal 51 ucmd;PVq{=vV_=YyXJzJRU;wh11mrmxAWQ}!d0q}669^c&`vyAWQ}!d0q}669l;ASwW&e001Pz0a5?} literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b08c.dmp b/instruments/FM/uhrwerk/B Series/b08c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..a4fc3fd47373951c13ebf2702de7e1a72d6fbffc GIT binary patch literal 51 wcmXBII}QL46h*;<_=G1ysk!^#C83*4^Nti@D~16{xT*YTu++<41obb*0V~b{tpET3 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b09.dmp b/instruments/FM/uhrwerk/B Series/b09.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3fe8966941b452688035b5c96b6ad1d342a74345 GIT binary patch literal 51 xcmd;PVq{=pW?)d2XJzJRU|?flV3L#PWB@UF85nrvc{zYgAYkN@X9bA@0RSi~0d4>Q literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b10.dmp b/instruments/FM/uhrwerk/B Series/b10.dmp new file mode 100644 index 0000000000000000000000000000000000000000..b33394c2bf0035bc2a54d17e5ba295d0b6227c3f GIT binary patch literal 51 xcmd;PVq{=vW?+!!;brD$U|(2E!ViR`~6!LO5t1d76mq7||t*BaZoVA`rqFLX=)dJiFa0Yv}+ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b11.dmp b/instruments/FM/uhrwerk/B Series/b11.dmp new file mode 100644 index 0000000000000000000000000000000000000000..59de7f61d2a3f920945bcdf01e8006802d3c71a2 GIT binary patch literal 51 vcmWm2$q@h`2*AMLtqj_M^xvhjuS_%vy#Suaf##&^lSl*I7Wb3oHSYHSCHVne literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b12.dmp b/instruments/FM/uhrwerk/B Series/b12.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7c9704d794907e9a4d10a08e7b8c1b33820d8d96 GIT binary patch literal 51 wcmd;PVq{=vVPKG!XXfB%U|^GHU=ZNrWB@UF85o54ctN5-z{n-f3K9hZ04D?iUH||9 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b13.dmp b/instruments/FM/uhrwerk/B Series/b13.dmp new file mode 100644 index 0000000000000000000000000000000000000000..12be33a7f2bd010442586cf99ecb517e8605f344 GIT binary patch literal 51 wcmd;PVq{=vVPIg8XXfB%U|^GH;Lw)mWB@V67#M`)c|oFJAkPL8W#C}|04mP`lK=n! literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b14.dmp b/instruments/FM/uhrwerk/B Series/b14.dmp new file mode 100644 index 0000000000000000000000000000000000000000..5bf58d03dd2b6ea2abd96dc4cc14ed3bb8538985 GIT binary patch literal 51 wcmXBII|=|G48YK+_%DcdbLstWspynPx_2ZG+hQ1y8cfTN0?V}CpiurrI49)+iU0rr literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b15.dmp b/instruments/FM/uhrwerk/B Series/b15.dmp new file mode 100644 index 0000000000000000000000000000000000000000..78d6748a0363105ff929bad387aeae22883ad87e GIT binary patch literal 51 wcmXBIK@I>A3;@BURA>{4gKPi)OTuk3mJ_0|qRIpv(4##%We0kfPO`r^2PB^XY5)KL literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b16.dmp b/instruments/FM/uhrwerk/B Series/b16.dmp new file mode 100644 index 0000000000000000000000000000000000000000..cfd1fdd25b06b51f8cf45a29a7dcc2483b34daba GIT binary patch literal 51 zcmWm2F%rNa2t~mMF&|?J3hf#1e@VwJ_jjX#R)r^z0(yOxxWv0_#$92fly^G^C-ecG literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b17.dmp b/instruments/FM/uhrwerk/B Series/b17.dmp new file mode 100644 index 0000000000000000000000000000000000000000..532f1edf34bd4b27d1811516dc24fa1ce249ced9 GIT binary patch literal 51 zcmWm2F%rNa2t~mMWNilk?}|0Nx_+}Di<>hJJkC?I=U;S#a?i@U;BtL`QTDXamg literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b17b MUTE.dmp b/instruments/FM/uhrwerk/B Series/b17b MUTE.dmp new file mode 100644 index 0000000000000000000000000000000000000000..0049b93bffa3da7ac75f6e3ea9ddc182258956e2 GIT binary patch literal 51 zcmWm2ISv3I2mrwa4R{p9SXdJO|4HMNGgYV%H(i5b4lTO`YKGt3{@0L^EB+oHEKvc^ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b17b SLAP (actual).dmp b/instruments/FM/uhrwerk/B Series/b17b SLAP (actual).dmp new file mode 100644 index 0000000000000000000000000000000000000000..552c7873744680b7cb14eff1bc26e117a11f9013 GIT binary patch literal 51 zcmWm2F%rNa2t~mMWNid3l>?|(_hE%$Zff%-eV7z)UqR=7m${^G8%)vCM60V;z5 AtpET3 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b17b SLAP.dmp b/instruments/FM/uhrwerk/B Series/b17b SLAP.dmp new file mode 100644 index 0000000000000000000000000000000000000000..0049b93bffa3da7ac75f6e3ea9ddc182258956e2 GIT binary patch literal 51 zcmWm2ISv3I2mrwa4R{p9SXdJO|4HMNGgYV%H(i5b4lTO`YKGt3{@0L^EB+oHEKvc^ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b17b.dmp b/instruments/FM/uhrwerk/B Series/b17b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..90ed59fcab5ea9b149e0f642ec3ff8f94915dadb GIT binary patch literal 51 ycmWm2xeWjy3p}-y~wf*=}^mTj5Dlz^G}7OT?`ce}%15{vA9k+X1xz literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b18 MUTE.dmp b/instruments/FM/uhrwerk/B Series/b18 MUTE.dmp new file mode 100644 index 0000000000000000000000000000000000000000..bf7c813db73706b84155f5e47d87ccd3422fcdd5 GIT binary patch literal 51 zcmd;PVq{=pVBk-ZXJ_JPU}j?nvgJ7$`5Bnl*cjL~<%QWnqHGL|T=K&FAW;qm064Y+ A5&!@I literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b18 SLAP.dmp b/instruments/FM/uhrwerk/B Series/b18 SLAP.dmp new file mode 100644 index 0000000000000000000000000000000000000000..b2a27f2075ebfaabf0ade778a0ce373fab58edbd GIT binary patch literal 51 zcmWm2DG~r63UVT=BQ@04qiT Axc~qF literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b19 SLAP.dmp b/instruments/FM/uhrwerk/B Series/b19 SLAP.dmp new file mode 100644 index 0000000000000000000000000000000000000000..abb3d8c5eeb626f980fa082bc074d2033a32dc44 GIT binary patch literal 51 ycmWm2*$Dt33XPU|?ooU=Wt)0x}udSQr=#XPU|?ooU=Wt)0x}udSQr?z<@rFO3_vCuM3kAC0RSgt0ek=e literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b22.dmp b/instruments/FM/uhrwerk/B Series/b22.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7f16bd2a63c7747f62d050f8abf0f35db982b46e GIT binary patch literal 51 xcmd;PVq{=sVBi#&7h>XPU|?ooU=Wt)0x}udSQr=#XPU|?ooU=Wh$0x}udSQr@0 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b22c MUTE.dmp b/instruments/FM/uhrwerk/B Series/b22c MUTE.dmp new file mode 100644 index 0000000000000000000000000000000000000000..1814402d22f5d7402e8afe9a15e750608a630cf0 GIT binary patch literal 51 ycmWm2F%AF_5Jkbah=PQm*z#Y&{ZGeslj+GYD6X9*WZXPU|?ooU=Wt)0x}udSQr?z<@rFO3_vCuM3kAC0RSjw0fGPk literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b22c.dmp b/instruments/FM/uhrwerk/B Series/b22c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e7358f05ad980c396a73ac84cf5b3aec30301d89 GIT binary patch literal 51 xcmd;PVq{=sU|XPU|?ooU=Wt)0x}udSQr=#A3;@Ax8@&)sgd_d`FEOX7Din~Ovjkvpw(F}aL{5J-Ls|~|U0fz~0gM0u literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b23 SLAP.dmp b/instruments/FM/uhrwerk/B Series/b23 SLAP.dmp new file mode 100644 index 0000000000000000000000000000000000000000..9d1d562b9e22d13aa008a4e345163636fa53d690 GIT binary patch literal 51 wcmWm0F%AGA2n4|eax|LQ&>H^#OL3}MZU{_WIzT|*^l3V*uAD|-yA{9U0VFj6V*mgE literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b23.dmp b/instruments/FM/uhrwerk/B Series/b23.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d8c212b5b9d5c88dbed24be42f57cd667e990f81 GIT binary patch literal 51 wcmWm0F%AGA2mrAI0*gi#R^|V{R6BL3;2^8Z6EHYC^feT+D_+fz*>1ni1tshOZvX%Q literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/B Series/b69.dmp b/instruments/FM/uhrwerk/B Series/b69.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f924e4f2c909861b0ccebc5403c5551bc0486085 GIT binary patch literal 51 ycmXBIF%AF_5Jkba`z?ipLZTw>e@hbGWU3nh*3L*B5Uid3F+0q^5>`&=t%3_Bg#l^+ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p01.dmp b/instruments/FM/uhrwerk/P Series/p01.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7770128ee370ede8f3aef9da5cb564b724bc84c8 GIT binary patch literal 51 tcmd;PVq{=pWnhwEVPN2AU|?foU=$JpGnp9}m}KBg1_2 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p01b.dmp b/instruments/FM/uhrwerk/P Series/p01b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..52cecdbed7822090e32134217f86df30cc2777cc GIT binary patch literal 51 mcmd;PVq{=pWnhwEVPIebA~ps_At5l6nSp^x2F_#_5c6? literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p02b.dmp b/instruments/FM/uhrwerk/P Series/p02b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..5056a3286994fe6c63de6f3c40e93582e7b179c6 GIT binary patch literal 51 mcmd;PVq{=rVPFsdVtxh&HZ}$Z86hx}nSp^>7|vwihcf{gumJS{ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p03.dmp b/instruments/FM/uhrwerk/P Series/p03.dmp new file mode 100644 index 0000000000000000000000000000000000000000..8ff7d73c0758893015863ab3dff60977d257b9b3 GIT binary patch literal 51 ncmd;PVq{=rVPNKGVPIfpU|?foV2}|4Gl4v2VK|e4AI<~-71034 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p03b.dmp b/instruments/FM/uhrwerk/P Series/p03b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..1b4ba05740e4c9b70636382caa6b501cc510097b GIT binary patch literal 51 ncmd;PVq{=qWnflgVPIfpU|?foV2}|4Gl4v2VK|e4AI<~-86W`L literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p04.dmp b/instruments/FM/uhrwerk/P Series/p04.dmp new file mode 100644 index 0000000000000000000000000000000000000000..637ce8e617e180e8b648d0cd12734786f5741011 GIT binary patch literal 51 rcmd;PVq{=oWnfleVPIebA~ps_1racliGhJZMhMJgW?*2H6NfPY8;Jn~ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p05.dmp b/instruments/FM/uhrwerk/P Series/p05.dmp new file mode 100644 index 0000000000000000000000000000000000000000..997ae297cb7b811c9ea3013e294ea8d489ea63a1 GIT binary patch literal 51 rcmd;PVq{=oWnh+KVPIebA~ps_1racliGhJZMhMJgW?*2H6NfPY8QTE* literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p06.dmp b/instruments/FM/uhrwerk/P Series/p06.dmp new file mode 100644 index 0000000000000000000000000000000000000000..c7445a98f3cdfa4a26971b2e60b6473815b46c08 GIT binary patch literal 51 ncmd;PVq{=vWnfg{9`FGo literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p06b.dmp b/instruments/FM/uhrwerk/P Series/p06b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..2f5891188622a7158d3c72b1cfa5a6026e47ff57 GIT binary patch literal 51 kcmd;PVq{=vWnh$L<7Z%I03k*pNnS<}lLg4+L@?o^034A45C8xG literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p06c.dmp b/instruments/FM/uhrwerk/P Series/p06c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..2f5891188622a7158d3c72b1cfa5a6026e47ff57 GIT binary patch literal 51 kcmd;PVq{=vWnh$L<7Z%I03k*pNnS<}lLg4+L@?o^034A45C8xG literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p07.dmp b/instruments/FM/uhrwerk/P Series/p07.dmp new file mode 100644 index 0000000000000000000000000000000000000000..4e44e71490c445114ab09f689531c7a881c50522 GIT binary patch literal 51 ocmd;PVq{=vV_;O^v2E%KyJ8Gpaqh3MFB?F)Cz7?+Jo~tkAfTGQS!;A%Ouf literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p10.dmp b/instruments/FM/uhrwerk/P Series/p10.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f3e63e4421635a17f085295d56330ffd6e5c9e52 GIT binary patch literal 51 kcmd;PVq{=tWnfZZV_;xqU|?fsU=$VtGnp9}m;|vg0T`74+W-In literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p10b.dmp b/instruments/FM/uhrwerk/P Series/p10b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f69b3d7d204884917b5d97482db007452b9d1e1b GIT binary patch literal 51 ocmd;PVq{=tWnxm0=ip*xWME@pU=$W&U|?lnU}j)o62!&?03Q+o0ssI2 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/P Series/p11.dmp b/instruments/FM/uhrwerk/P Series/p11.dmp new file mode 100644 index 0000000000000000000000000000000000000000..ae7d1800fcf64c380a3e7bb202a9dae881ae5cef GIT binary patch literal 51 kcmd<)U}RutVBlw9Wq<<+lMTd&FqwfY6(Ey|fdMEA01QR|bN~PV literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/act 1 void of space.dmp b/instruments/FM/uhrwerk/R Series/act 1 void of space.dmp new file mode 100644 index 0000000000000000000000000000000000000000..2fabf33d77f874fd3ad7263d84912a274e08b975 GIT binary patch literal 51 ycmWNHK?(pM48z)uO+`V4o&5hpE#weFrV)+kb%rM_f&R+PA*4GQFi6)8YEbUR`N3)c8b<*H literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r15.dmp b/instruments/FM/uhrwerk/R Series/r15.dmp new file mode 100644 index 0000000000000000000000000000000000000000..fd9623d947b70997397b2185efc0e704f9e303a2 GIT binary patch literal 51 scmd;PVq{=rWndBzVqoBBU|?fsVC01`nHdhg3=$6$ERECxoj9-0Nelo literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r2.dmp b/instruments/FM/uhrwerk/R Series/r2.dmp new file mode 100644 index 0000000000000000000000000000000000000000..a1fa7c6f0e10a918672d4107445972c59822a0dc GIT binary patch literal 51 rcmd;PVq{=oVqg>yVqjonU|?WnU{HlHfjlNIAqGy6C=&yN5L^@h7v2Ej literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r27.dmp b/instruments/FM/uhrwerk/R Series/r27.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3f2c23bda4e4618f75a7edc633cc15750b12d955 GIT binary patch literal 51 vcmW;AK@I>A3;@BcB9$s9|Np9T+ReBLT9MOX10@sdZC{9u(RP|bxPP5o8pQz) literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r3.dmp b/instruments/FM/uhrwerk/R Series/r3.dmp new file mode 100644 index 0000000000000000000000000000000000000000..6f88b474658e6fb3082322b370fb1fb684f5fd2d GIT binary patch literal 51 rcmd;PVq{=oWoG0TVqjon05TaE#34)|kBM7|fs+lWfQf-Y2rdc$7byVR literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r33.dmp b/instruments/FM/uhrwerk/R Series/r33.dmp new file mode 100644 index 0000000000000000000000000000000000000000..603919413ada1000aabf2855d23bfe5d2c23e15a GIT binary patch literal 51 pcmd;PVq{=sVPN2w7hvFLU|<6>dF6pL7%}hwnLtqx;D?9;SpXi70TBQI literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r4.dmp b/instruments/FM/uhrwerk/R Series/r4.dmp new file mode 100644 index 0000000000000000000000000000000000000000..40be7198dca5692c836bdf40a453cc5a4b753ed6 GIT binary patch literal 51 rcmXZRF$w@63;?mTU>66Wi~s+rh;B!2W*8+_QyA=*m2&o-1pPG}7l;7b literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r5.dmp b/instruments/FM/uhrwerk/R Series/r5.dmp new file mode 100644 index 0000000000000000000000000000000000000000..28359737b53571085393e92daab0e6a26f4172af GIT binary patch literal 51 ycmW;AF$w@648Xutsi3VDTwMJBJ1s(%ixr_NavNj}^vqzT0 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r8c.dmp b/instruments/FM/uhrwerk/R Series/r8c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..451de941dd3ff573d814d693552d949a44fa2680 GIT binary patch literal 51 ocmd;PVq{=rV_=dKVqoBBU|?flVB|$G`5{aOAW#G{fwDjV034420RR91 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r9.dmp b/instruments/FM/uhrwerk/R Series/r9.dmp new file mode 100644 index 0000000000000000000000000000000000000000..67d38c7f688a6958f84163cb38da0a0c76105e60 GIT binary patch literal 51 ocmd;PVq{=rV_*~zVqoBBU|?flVB|$Gg&<4@AW#G{fwDjV02)XD^#A|> literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/R Series/r9b.dmp b/instruments/FM/uhrwerk/R Series/r9b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..bf0b693920b902fd863e8114defe2da4046818b2 GIT binary patch literal 51 pcmd;PVq{=rV_*~zVqoBBU|?flVC01`nSo3pIFmsU$OOs)MFAQx0Qdj^ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/README.txt b/instruments/FM/uhrwerk/README.txt new file mode 100644 index 00000000..c5509381 --- /dev/null +++ b/instruments/FM/uhrwerk/README.txt @@ -0,0 +1,23 @@ +Here you will find my patches that are within a series. These are mostly all custom made, very few are actually modified patches. It all started with the R series, which was basically me fucking around with the operators and making what I thought was cool synth sounds. From there, I slowly got better at making sounds. As I make more _ Series categories, I will be redistributing a lot of other series patches into the new categories and removing them from the old categories. This way my patches are more organised. This README will also be updated constantly. + +Enjoy! +-Uhrwerk + + +--THE SERIES-- + +R Series: Mostly synths, but some can be used as bass patches. + +Z Series: More refined synths, some xylophone and marimbas, mostly softer synths. + +B Series: Bass patches, a mix between slap bass, acoustic upright, synth, and plucked guitar. + +--COMING SOON-- + +P Series: Soft pad synths. Lots and lots of soft pad synths. (COMING SOON) + +R2 Series: Hard unison leads. (COMING SOON) + +X Series: Xylophones, bells, marimbas, chimes, basically almost any mallet instrument. (COMING SOON) + +T Series: Trumpets and other brass instruments (COMING SOON) \ No newline at end of file diff --git a/instruments/FM/uhrwerk/Z Series/z00.dmp b/instruments/FM/uhrwerk/Z Series/z00.dmp new file mode 100644 index 0000000000000000000000000000000000000000..adafcee6cf1ffd4afd37fdcfe86cf1b42ce10dba GIT binary patch literal 51 wcmd<)U}RupWMHxuVqjonU|?ooU{VueVBuf@0VXb>C_9kB%D}_}VFJ|v03ike5dZ)H literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z01.dmp b/instruments/FM/uhrwerk/Z Series/z01.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e488963e83476a5472b76c81f664b0dbfaafc27b GIT binary patch literal 51 zcmd<)U}RutVqlWt)%f^WflM|A22KrLK4vbUC^rxbGc#~8FakvZBrX9? literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z02.dmp b/instruments/FM/uhrwerk/Z Series/z02.dmp new file mode 100644 index 0000000000000000000000000000000000000000..547da27ae1c6b8266e91ad80287d2a319b368b10 GIT binary patch literal 51 scmd<)U}RumWneOw<6z`uU}j`yVB(TxgECoUWZ|MhGK@e`21XVJ04I9^U;qFB literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z02b.dmp b/instruments/FM/uhrwerk/Z Series/z02b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..8d657deb8b35a61b9e01d8d7864fc91df27c545d GIT binary patch literal 51 scmd<)U}RumWneOt<6z`uU}j`yVB(TxgECoUWZ|MhGK@e`21XVJ048|>S^xk5 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z02c.dmp b/instruments/FM/uhrwerk/Z Series/z02c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..448805c0b03c868ad3019c62ea38d350fcbdbee1 GIT binary patch literal 51 scmd<)U}RumWnhw*<6z`uU}j`yVB(TxgECoUWZ|MhGK@e`21XVJ03&k&NB{r; literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z03.dmp b/instruments/FM/uhrwerk/Z Series/z03.dmp new file mode 100644 index 0000000000000000000000000000000000000000..17407ec1458c17319502aa2a4495be085e84b056 GIT binary patch literal 51 scmW;AF%AGA2mrAY5J=pZ{HxBvhE literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z04.dmp b/instruments/FM/uhrwerk/Z Series/z04.dmp new file mode 100644 index 0000000000000000000000000000000000000000..52c9b607b90545ba9dca266eb953606eb1027f65 GIT binary patch literal 51 tcmd<)U}RusXJ8kS=VIn$U|?foV3v?)Wr8qSp`r|I42(kZ%pg%9696XS0cQXJ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z04b.dmp b/instruments/FM/uhrwerk/Z Series/z04b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e534cff996ca546fd0d5556f0f6d767d27892f84 GIT binary patch literal 51 scmd<)U}RusXJ8eQ=VIn$U|?foV3v?)Wr8qaq6};dj6(9vAW8>`5{bZ1_l5gBmn;a literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z05c.dmp b/instruments/FM/uhrwerk/Z Series/z05c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d3a61d1a3ac08a8b2cac36f12c3fa131cce537da GIT binary patch literal 51 tcmd;PVq{=vVPKLHV&Gw8U|?fqU=$Yu(qP2M1!QtCfB+*ugvrdn000}d0PO$( literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z05d.dmp b/instruments/FM/uhrwerk/Z Series/z05d.dmp new file mode 100644 index 0000000000000000000000000000000000000000..4aa62ca1d14b847ef76bfd9863c235269e2d8fb4 GIT binary patch literal 51 tcmd;PVq{=vVPKXLV&Gw8U|?fqU{VnR(qP2M1!QtCfB+*ugvrdn0014M0Q>*| literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z06.dmp b/instruments/FM/uhrwerk/Z Series/z06.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7a37e0c25cf8c3f6df31def985a0e9342a74aaa2 GIT binary patch literal 51 ycmd;PVq{=oWnh$)<7MJxU}j@xVC0o$V+1l8nHiWQWjR1hAmo>k0gJM+F#rG~!U1Ri literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z06b.dmp b/instruments/FM/uhrwerk/Z Series/z06b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..cf3dfc927203aad86c55495cb07ba565570e8f27 GIT binary patch literal 51 wcmd;PVq{=oVPKS$<7MJxU}j@x;Mb7hV1zK4BxS)&24)6EURgGfC?hii04N{HPnTqS|8=p;h!a)4`2$R}u`QZwP)$lL4pz03yHvMF0Q* literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z11b.dmp b/instruments/FM/uhrwerk/Z Series/z11b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3f29f0101f3771c78f058ac87a561587487874f3 GIT binary patch literal 51 xcmd<)U}RumWnhw)6XD=wU}j`uVB(TxV+1jo8CYawIiO4?AsI#>lYxlYxlYxlYxpF literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z13.dmp b/instruments/FM/uhrwerk/Z Series/z13.dmp new file mode 100644 index 0000000000000000000000000000000000000000..306d31da39373683523e5c9e9ca9ff5229d509a4 GIT binary patch literal 51 scmd;PVq{=rVPFuD7hq;(U|?osU{;c6V+1kT7?`AhOfE2!K@`pe03=ueU;qFB literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z14.dmp b/instruments/FM/uhrwerk/Z Series/z14.dmp new file mode 100644 index 0000000000000000000000000000000000000000..67cf2beab9d29b1458ce088f725bdc7a2982fb4e GIT binary patch literal 51 qcmd;PVq{=rWnd7H7hncb%!~{ia`J49ASN3FlN6B21!gjc!kGXhs{vvF literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z14b.dmp b/instruments/FM/uhrwerk/Z Series/z14b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..002c329bdf6bb0652794301f4697bc14d2c2dbb0 GIT binary patch literal 51 qcmd;PVq{=rWnd7H7hncb%!~{ia`J49ASN3FvlNiY1!gjc!kGXh!2x3c literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z14c.dmp b/instruments/FM/uhrwerk/Z Series/z14c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..4b25048ae74feeb929e08f3a9097921bac30c418 GIT binary patch literal 51 qcmd;PVq{=rWnd7H7hncb%!~{iD)MZMASN3FvpA5+1!gjE!I=Og)B#%n literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z14d.dmp b/instruments/FM/uhrwerk/Z Series/z14d.dmp new file mode 100644 index 0000000000000000000000000000000000000000..b3752bf11a78f1b5e1894c53ddfc77615b29886c GIT binary patch literal 51 qcmd;PVq{=rWnd7H7hncb%!~}IV)AT^ASN3FvpA5+1!gjE!I=Oeu>nc| literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z15.dmp b/instruments/FM/uhrwerk/Z Series/z15.dmp new file mode 100644 index 0000000000000000000000000000000000000000..2f2cdc74607aa88cfe38401c842338c12e3d3ea5 GIT binary patch literal 51 xcmXBGF%AF_5JkbayZnNJMnt7_{~MC1W~v*?aCVP}j96RNAPJwZ1wt@>U0f)h0eJub literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z15b (quiet).dmp b/instruments/FM/uhrwerk/Z Series/z15b (quiet).dmp new file mode 100644 index 0000000000000000000000000000000000000000..cf1ba08b187cd3cef0e1fd8d066f6261cefc90bb GIT binary patch literal 51 xcmXBGF$w@63t1L;)c60YU%( literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z16c.dmp b/instruments/FM/uhrwerk/Z Series/z16c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e91fbacaa39dae2b4175a0e4c1449ee50a7b2ea9 GIT binary patch literal 51 rcmd;PVq{=vWndKLt9h2Ts8AS(eq literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z16d.dmp b/instruments/FM/uhrwerk/Z Series/z16d.dmp new file mode 100644 index 0000000000000000000000000000000000000000..0e7b9377d41b8b0237372bddd176e53839f75251 GIT binary patch literal 51 lcmd;PVq{=vWnh%#=VxMNU}9!xVC0eHW#nRDU}nL=1OOms0W|;s literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z17.dmp b/instruments/FM/uhrwerk/Z Series/z17.dmp new file mode 100644 index 0000000000000000000000000000000000000000..ba37c0b4e7a8b634efd93d694c900d3abfb629ea GIT binary patch literal 51 ucmd;PVq{=vV_=t-mto{*U|<$!VB(i&VBi8WxfvK$<=MbYHU>s1xF`TDl>wUo literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z17b.dmp b/instruments/FM/uhrwerk/Z Series/z17b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d9aeff98bc9d45462019f359669155fa670c1f53 GIT binary patch literal 51 ucmd;PVq{=vV_=t-mto{*U|<$!VC0u)VBi8WxfvK$<=MbYHU>s1xF`TDbOD+G literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z17c.dmp b/instruments/FM/uhrwerk/Z Series/z17c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f0cd8d361a1776ce2dfcff21d3718c54d8f7f354 GIT binary patch literal 51 ucmd;PVq{=vV_=t-mto{*U|<$!VBnW$VBi8WxfvK$<=MbYHU>s1xF`TDQvsO( literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z17d.dmp b/instruments/FM/uhrwerk/Z Series/z17d.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f7efff4b00d180813cea770be713fe826b65e601 GIT binary patch literal 51 ucmd;PVq{=vV_=t-mto{*U|<$!VCI)+VBi8WxfvK$<=MbYHU>s1xF`TDwgH>~ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z17e.dmp b/instruments/FM/uhrwerk/Z Series/z17e.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3f05945570654307d36ecc49fe6a2904a434b044 GIT binary patch literal 51 zcmWm2ISK$E5Cp*9F literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z22.dmp b/instruments/FM/uhrwerk/Z Series/z22.dmp new file mode 100644 index 0000000000000000000000000000000000000000..a43302d2d47efd750b8764e2b0ac70affeeff57a GIT binary patch literal 51 scmd;PVq{=rWnd7H7hncb%!~|-yz*>}TtFrp1Dhg*$-u_Izy%it03w3{OaK4? literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z22b.dmp b/instruments/FM/uhrwerk/Z Series/z22b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e3bb13fcbdcf74c4d82a01e027024aa5af4a29e8 GIT binary patch literal 51 scmd;PVq{=rWnd7H7iR`i%!~|-yz*>}TtFrp1Dhg*$-u_Izy%it03)ISQvd(} literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z23.dmp b/instruments/FM/uhrwerk/Z Series/z23.dmp new file mode 100644 index 0000000000000000000000000000000000000000..d32b697271b4c7eede6c9138a37bbbfa47c803d8 GIT binary patch literal 51 scmd;PVq{=rWnfT|7hncb%!~|-yz*>}TtFrp1Dhg*$-u_Izy%it043i6U;qFB literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z24.dmp b/instruments/FM/uhrwerk/Z Series/z24.dmp new file mode 100644 index 0000000000000000000000000000000000000000..61f87d45b492e906dbe616cccd0d1ab8c4092090 GIT binary patch literal 51 qcmd;PVq{=rWnkcx7hncb%!~|-yz*>}TtFrp1A`=l$pB<>!bJffAOSW2 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z24b.dmp b/instruments/FM/uhrwerk/Z Series/z24b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3abff36c9d3fe750969470ebeb671503cefea631 GIT binary patch literal 51 scmd;PVq{=rWnd7I7hnKV%!~|-eDZ9JTtFrp1BWDp$-u_IAOIHy03w$GP5=M^ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z24c.dmp b/instruments/FM/uhrwerk/Z Series/z24c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f342476d47712e2e5b576b61d2a08f38c499f52f GIT binary patch literal 51 scmd;PVq{=rWnkcv7hncb%!~|7yz*>}TtFrp1G5N($-u_IzzG)x03byHHUIzs literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z25.dmp b/instruments/FM/uhrwerk/Z Series/z25.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e86b0a114ff16bee2473ec9e3d9de910e6c47055 GIT binary patch literal 51 tcmd;PVq{=uVPIgDmu6>WU|?osVC0i$W8?xd*%&y)AWQ~c1_lARC;%c(0Z{+| literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z26.dmp b/instruments/FM/uhrwerk/Z Series/z26.dmp new file mode 100644 index 0000000000000000000000000000000000000000..701082a75fe18022d385d58968630c9c548ab377 GIT binary patch literal 51 vcmXBIF$w@63;sSCyL?DIFe1Mo0ftF1YC6zOxRx!9xmnqxc~qF literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z26c.dmp b/instruments/FM/uhrwerk/Z Series/z26c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..187f63aa8290879dd210b437d06d5f3153f4afd9 GIT binary patch literal 51 ycmXBIu?hen2tdJSB~cEJwSfe4h1mk@t+3L2>q=Z7casA A!vFvP literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z27.dmp b/instruments/FM/uhrwerk/Z Series/z27.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7092d91c1bff106b2bb71c0377515b8912371cfa GIT binary patch literal 51 zcmW;8I|=|G3`D^fql@^#!dh}P?|%!ysir4G4mTDBRPa+}3$n1ee_b#_)*y)oEpGvV literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z28.dmp b/instruments/FM/uhrwerk/Z Series/z28.dmp new file mode 100644 index 0000000000000000000000000000000000000000..24065a392476e2f2c9674c067cd39865fb3664c0 GIT binary patch literal 51 ycmXBGu?+wq2mrxHghFGOz%Y%S`_ChZSKVxaG`JZUxS=~;0=mQb)#|Da5a$6gX92B12UP#85o4*xmbV-fT92` C2m!YM literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z30b.dmp b/instruments/FM/uhrwerk/Z Series/z30b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..352e851e97e248789eb2381d548b1cb8e5ca9c1f GIT binary patch literal 51 zcmd;PVq{=oW#AW)=Lb>@Y!VENvhrMv{0t0?Yz%BF@`7wYCbKvLgOEHI3s3=26aX!f B0lEMH literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z30c.dmp b/instruments/FM/uhrwerk/Z Series/z30c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..1267ee24dc795a7fd7907b319eeea186fa0b06ff GIT binary patch literal 51 zcmd;PVq{=oWndMN=Lb>@Y!VENvhrMv{0t0?Yz%BF@`7wYCbKvLgOEHI3s3=26aXvQ B0k8l7 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z31.dmp b/instruments/FM/uhrwerk/Z Series/z31.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f5b3f5832b1539e86da38ffecd7aeb0aeabeb8c2 GIT binary patch literal 51 zcmd;PVq{=oWndMN=V#z&U|^GAVA7E1=KwMpr5KnvAfn8Y3=Bf@yqv5+Q4t0JFI@rF literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z32.dmp b/instruments/FM/uhrwerk/Z Series/z32.dmp new file mode 100644 index 0000000000000000000000000000000000000000..6af238778b6c11a1931b0df14b262135cfaa40aa GIT binary patch literal 51 pcmd;PVq{=vV_=Yw=V#z&U;wfh1m!tlOg?#D5EBR(x#d}*OaLUm0Zjk^ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z33.dmp b/instruments/FM/uhrwerk/Z Series/z33.dmp new file mode 100644 index 0000000000000000000000000000000000000000..27240addf981b95d564eb035ae94cf2efd9910cb GIT binary patch literal 51 qcmd;PVq{=vWnhxw*SETnr4%EDQ|na3-@5oCyFRa{)L2 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z33b.dmp b/instruments/FM/uhrwerk/Z Series/z33b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..3bb3d11750bf57198ae6629cf725b83d862812ad GIT binary patch literal 51 qcmd;PVq{=vWnhxwrUM literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z33c.dmp b/instruments/FM/uhrwerk/Z Series/z33c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..53704a5550ece06eb2acc2058849b886697fc0b7 GIT binary patch literal 51 qcmd;PVq{=vWnik8=Vt;^%p4g literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z34.dmp b/instruments/FM/uhrwerk/Z Series/z34.dmp new file mode 100644 index 0000000000000000000000000000000000000000..5279f358d52e8d66bfb0467192c8569c8eba30a5 GIT binary patch literal 51 vcmd;PVq{=oWo9yz<6z`uU}j`yVB(TxW8`E6F<6z`uU}j`yVB(TxgECoUWZ|MhGK`Fzj0}t{3;-h20Y(4- literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z35.dmp b/instruments/FM/uhrwerk/Z Series/z35.dmp new file mode 100644 index 0000000000000000000000000000000000000000..8afd3f705ce8ba15fac29e6808024cdb4b47424b GIT binary patch literal 51 ycmWm2I~D*C5CgzulL4pz0444LR{#J2 literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z38.dmp b/instruments/FM/uhrwerk/Z Series/z38.dmp new file mode 100644 index 0000000000000000000000000000000000000000..01c0892f72d42f4f3730b0523cc59f1e204d270c GIT binary patch literal 51 scmd;PVq{=vWnfa2=jQ-Y%mNHdLXy0UTnr4%EDVf5CI^Tq0A@l(0V!(%g8%>k literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39.dmp b/instruments/FM/uhrwerk/Z Series/z39.dmp new file mode 100644 index 0000000000000000000000000000000000000000..054e214c544c446471098b7291a46eeb030a2223 GIT binary patch literal 51 xcmd<)U}RupVPF!KXJFu9U|?ooV5%2lV1Y21`S=+afQXrgfuWv(fq@;Y001rl0m%RW literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39b low vol.dmp b/instruments/FM/uhrwerk/Z Series/z39b low vol.dmp new file mode 100644 index 0000000000000000000000000000000000000000..bb4d7983badaacd0c12b5627cbc1a72e1c456d3f GIT binary patch literal 51 ucmd<)U}RupVPF!KXJFu9U|?ooV5%2lV1Y21`S=+afCwncP!GiHU?u=9WdXea literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39b.dmp b/instruments/FM/uhrwerk/Z Series/z39b.dmp new file mode 100644 index 0000000000000000000000000000000000000000..bb4d7983badaacd0c12b5627cbc1a72e1c456d3f GIT binary patch literal 51 ucmd<)U}RupVPF!KXJFu9U|?ooV5%2lV1Y21`S=+afCwncP!GiHU?u=9WdXea literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39c.dmp b/instruments/FM/uhrwerk/Z Series/z39c.dmp new file mode 100644 index 0000000000000000000000000000000000000000..b04346542c3bd97d538268bf660189ead338fbdd GIT binary patch literal 51 tcmd<)U}RupVPF!KXJFu9U|?ooV5%2lV1Y9E_!$_02q?->55(+XCIBr@0lWYJ literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39d.dmp b/instruments/FM/uhrwerk/Z Series/z39d.dmp new file mode 100644 index 0000000000000000000000000000000000000000..7dec58ac3cbca32d83ca8a643ba349c65a44e6cb GIT binary patch literal 51 mcmd<)U}RupVPF!KXJFu9U|?oIVe;`aFaQxylo`Zi2QvX3YykWK literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/Z Series/z39e.dmp b/instruments/FM/uhrwerk/Z Series/z39e.dmp new file mode 100644 index 0000000000000000000000000000000000000000..ab0845c57b28dcf09eb179610e51ff167fa05988 GIT binary patch literal 51 ncmd<)U}RupVPF!KXJFu9U|F0O9}u literal 0 HcmV?d00001 diff --git a/instruments/FM/uhrwerk/arbys.dmp b/instruments/FM/uhrwerk/arbys.dmp new file mode 100644 index 0000000000000000000000000000000000000000..68fbd43044b43d4b02cd596d380442c0cbb2fa8f GIT binary patch literal 51 xcmW;AF$w@63 literal 0 HcmV?d00001 diff --git a/instruments/README.md b/instruments/README.md index 40cad422..949aab10 100644 --- a/instruments/README.md +++ b/instruments/README.md @@ -34,6 +34,7 @@ these are organized in the following categories: - theloredev - TheRealHedgehogSonic - tildearrow +- Uhrwerk Klockwerx - Weeppiko - Ygor G. - YohananDiamond From 152a95cb403c648dd7fef6c520a34f176a436362 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 18:33:48 -0500 Subject: [PATCH 14/34] GUI: fix inertial scroll when power saving is on --- extern/imgui_patched/imgui.cpp | 3 +++ extern/imgui_patched/imgui.h | 1 + src/gui/gui.cpp | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 07928bb3..a0298a3f 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -4469,6 +4469,7 @@ void ImGui::NewFrame() g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; + g.IO.IsSomethingHappening = false; UpdateViewportsNewFrame(); @@ -6961,12 +6962,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (fabs(window->InertialScrollSpeed.x)>0.1f) { window->Scroll.x=window->Scroll.x+window->InertialScrollSpeed.x; window->InertialScrollSpeed.x*=0.95f; + g.IO.IsSomethingHappening = true; } else { window->InertialScrollSpeed.x=0.0f; } if (fabs(window->InertialScrollSpeed.y)>0.1f) { window->Scroll.y=window->Scroll.y+window->InertialScrollSpeed.y; window->InertialScrollSpeed.y*=0.95f; + g.IO.IsSomethingHappening = true; } else { window->InertialScrollSpeed.y=0.0f; } diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index afc84eba..5359672e 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -2096,6 +2096,7 @@ struct ImGuiIO bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! + bool IsSomethingHappening; // This is set to true when inertial scrolling is happening. bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fecd377b..ca806a3d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3171,6 +3171,11 @@ bool FurnaceGUI::loop() { if (wantCaptureKeyboard) { WAKE_UP; } + + if (ImGui::GetIO().IsSomethingHappening) { + WAKE_UP; + } + if (ImGui::GetIO().MouseDown[0] || ImGui::GetIO().MouseDown[1] || ImGui::GetIO().MouseDown[2] || ImGui::GetIO().MouseDown[3] || ImGui::GetIO().MouseDown[4]) { WAKE_UP; } From 525f512bb162e38cddd921033385b20b6f06b61b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 18:34:08 -0500 Subject: [PATCH 15/34] GUI: no hover colors on mobile --- src/gui/settings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 90ce5bff..5fb8eda1 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3231,6 +3231,10 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); } + if (mobileUI) { // disable all hovered colors + primaryHover=primary; + secondaryHover=secondary; + } sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND]; sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP]; From 7070522bbe088128458754ba9ba09e5ffb7da389 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 18:34:22 -0500 Subject: [PATCH 16/34] QSound: ADPCM? --- src/engine/platform/qsound.cpp | 150 +++++++++++++++++++++++++++------ src/engine/platform/qsound.h | 3 + 2 files changed, 126 insertions(+), 27 deletions(-) diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index fce9f783..ea609a3d 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -245,6 +245,22 @@ const unsigned char q1_reg_map[Q1V_REG_COUNT][16] = { {0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e}, }; +const unsigned char q1a_start_map[3]={ + 0xca, 0xce, 0xd2 +}; + +const unsigned char q1a_end_map[3]={ + 0xcb, 0xcf, 0xd3 +}; + +const unsigned char q1a_bank_map[3]={ + 0xcc, 0xd0, 0xd4 +}; + +const unsigned char q1a_vol_map[3]={ + 0xcd, 0xd1, 0xd5 +}; + const char** DivPlatformQSound::getRegisterSheet() { return regCheatSheetQSound; } @@ -265,7 +281,7 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l } void DivPlatformQSound::tick(bool sysTick) { - for (int i=0; i<16; i++) { + for (int i=0; i<19; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { if (chan[i].isNewQSound) { @@ -277,7 +293,11 @@ void DivPlatformQSound::tick(bool sysTick) { } // Check if enabled and write volume if (chan[i].active) { - rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol); + if (i<16) { + rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol); + } else { + rWrite(q1a_vol_map[i-16],chan[i].resVol); + } } } uint16_t qsound_bank = 0; @@ -286,8 +306,13 @@ void DivPlatformQSound::tick(bool sysTick) { uint16_t qsound_end = 0; if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[i].sample); - qsound_bank = 0x8000 | (offPCM[chan[i].sample] >> 16); - qsound_addr = offPCM[chan[i].sample] & 0xffff; + if (i<16) { + qsound_bank = 0x8000 | (offPCM[chan[i].sample] >> 16); + qsound_addr = offPCM[chan[i].sample] & 0xffff; + } else { + qsound_bank = 0x8000 | (offBS[chan[i].sample] >> 16); + qsound_addr = offBS[chan[i].sample] & 0xffff; + } int loopStart=s->loopStart; int length = s->loopEnd; @@ -295,10 +320,18 @@ void DivPlatformQSound::tick(bool sysTick) { length = 65536 - 16; } if (loopStart == -1 || loopStart >= length) { - qsound_end = offPCM[chan[i].sample] + length + 15; + if (i<16) { + qsound_end = offPCM[chan[i].sample] + length + 15; + } else { + qsound_end = offBS[chan[i].sample] + length + 15; + } qsound_loop = 15; } else { - qsound_end = offPCM[chan[i].sample] + length; + if (i<16) { + qsound_end = offPCM[chan[i].sample] + length; + } else { + qsound_end = offBS[chan[i].sample] + length; + } qsound_loop = length - loopStart; } } @@ -310,7 +343,9 @@ void DivPlatformQSound::tick(bool sysTick) { } if (chan[i].isNewQSound && chan[i].std.duty.had) { chan[i].echo=CLAMP(chan[i].std.duty.val,0,32767); - immWrite(Q1_ECHO+i,chan[i].echo&0x7fff); + if (i<16) { + immWrite(Q1_ECHO+i,chan[i].echo&0x7fff); + } } if (chan[i].isNewQSound && chan[i].std.ex1.had) { immWrite(Q1_ECHO_FEEDBACK,chan[i].std.ex1.val&0x3fff); @@ -355,11 +390,17 @@ void DivPlatformQSound::tick(bool sysTick) { chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,440.0,4096.0); if (chan[i].freq>0xefff) chan[i].freq=0xefff; if (chan[i].keyOn) { - rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); - rWrite(q1_reg_map[Q1V_END][i], qsound_end); - rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop); - rWrite(q1_reg_map[Q1V_START][i], qsound_addr); - rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); + if (i<16) { + rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); + rWrite(q1_reg_map[Q1V_END][i], qsound_end); + rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop); + rWrite(q1_reg_map[Q1V_START][i], qsound_addr); + rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); + } else { + rWrite(q1a_bank_map[i-16], qsound_bank); + rWrite(q1a_end_map[i-16], qsound_end); + rWrite(q1a_start_map[i-16], qsound_addr); + } //logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop); // Write sample address. Enable volume if (!chan[i].std.vol.had) { @@ -368,16 +409,26 @@ void DivPlatformQSound::tick(bool sysTick) { } else { chan[i].resVol=chan[i].vol<<4; } - rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol); + if (i<16) { + rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol); + } else { + rWrite(q1a_vol_map[i-16],chan[i].resVol); + } } } if (chan[i].keyOff) { // Disable volume - rWrite(q1_reg_map[Q1V_VOL][i],0); - rWrite(q1_reg_map[Q1V_FREQ][i],0); + if (i<16) { + rWrite(q1_reg_map[Q1V_VOL][i],0); + rWrite(q1_reg_map[Q1V_FREQ][i],0); + } else { + rWrite(q1a_vol_map[i-16],0); + } } else if (chan[i].active) { //logV("ch %d frequency set to %04x, off=%f, note=%d, %04x!",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note)); - rWrite(q1_reg_map[Q1V_FREQ][i],chan[i].freq); + if (i<16) { + rWrite(q1_reg_map[Q1V_FREQ][i],chan[i].freq); + } } if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; @@ -441,8 +492,12 @@ int DivPlatformQSound::dispatch(DivCommand c) { } else { chan[c.chan].resVol=chan[c.chan].outVol<<4; } - if (chan[c.chan].active && c.chan<16) { - rWrite(q1_reg_map[Q1V_VOL][c.chan],chan[c.chan].resVol); + if (chan[c.chan].active) { + if (c.chan<16) { + rWrite(q1_reg_map[Q1V_VOL][c.chan],chan[c.chan].resVol); + } else { + rWrite(q1a_vol_map[c.chan-16],chan[c.chan].resVol); + } } } } @@ -459,6 +514,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { break; case DIV_CMD_QSOUND_ECHO_LEVEL: chan[c.chan].echo=c.value<<7; + if (c.chan>=16) break; immWrite(Q1_ECHO+c.chan,chan[c.chan].echo&0x7fff); break; case DIV_CMD_QSOUND_ECHO_FEEDBACK: @@ -532,7 +588,8 @@ void DivPlatformQSound::muteChannel(int ch, bool mute) { } void DivPlatformQSound::forceIns() { - for (int i=0; i<4; i++) { + // TODO: what? + for (int i=0; i<19; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; chan[i].sample=-1; @@ -552,7 +609,7 @@ DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) { } void DivPlatformQSound::reset() { - for (int i=0; i<16; i++) { + for (int i=0; i<19; i++) { chan[i]=DivPlatformQSound::Channel(); chan[i].std.setEngine(parent); } @@ -582,8 +639,7 @@ void DivPlatformQSound::notifyInsChange(int ins) { } void DivPlatformQSound::notifyWaveChange(int wave) { - // TODO when wavetables are added - // TODO they probably won't be added unless the samples reside in RAM + // yeah won't add wavetables } void DivPlatformQSound::notifyInsDeletion(void* ins) { @@ -633,27 +689,32 @@ int DivPlatformQSound::getRegisterPoolDepth() { } const void* DivPlatformQSound::getSampleMem(int index) { - return index == 0 ? sampleMem : NULL; + return (index == 0 || index == 1) ? sampleMem : NULL; } size_t DivPlatformQSound::getSampleMemCapacity(int index) { - return index == 0 ? 16777216 : 0; + return (index == 0 || index == 1) ? 16777216 : 0; } size_t DivPlatformQSound::getSampleMemUsage(int index) { - return index == 0 ? sampleMemLen : 0; + return index == 0 ? sampleMemLen : index == 1 ? sampleMemLenBS : 0; } bool DivPlatformQSound::isSampleLoaded(int index, int sample) { - if (index!=0) return false; + if (index<0 || index>1) return false; if (sample<0 || sample>255) return false; + if (index==1) return sampleLoadedBS[sample]; return sampleLoaded[sample]; } -// TODO: ADPCM... come on... +const char* DivPlatformQSound::getSampleMemName(int index) { + return index == 0 ? "PCM" : index == 1 ? "ADPCM" : NULL; +} + void DivPlatformQSound::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); memset(sampleLoaded,0,256*sizeof(bool)); + memset(sampleLoadedBS,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { @@ -689,6 +750,40 @@ void DivPlatformQSound::renderSamples(int sysID) { memPos+=length+16; } sampleMemLen=memPos+256; + + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + if (!s->renderOn[1][sysID]) { + offBS[i]=0; + continue; + } + + int length=s->lengthQSoundA; + if (length>65536-16) { + length=65536-16; + } + if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { + memPos=(memPos+0xffff)&0xff0000; + } + if (memPos>=getSampleMemCapacity()) { + logW("out of QSound ADPCM memory for sample %d!",i); + break; + } + if (memPos+length>=getSampleMemCapacity()) { + for (unsigned int i=0; idataQSoundA[i]; + } + logW("out of QSound ADPCM memory for sample %d!",i); + } else { + for (int i=0; idataQSoundA[i]; + } + sampleLoaded[i]=true; + } + offBS[i]=memPos^0x8000; + memPos+=length+16; + } + sampleMemLenBS=memPos+256; } int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { @@ -706,6 +801,7 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, const DivCo rate = qsound_start(&chip, chipClock); sampleMem=new unsigned char[getSampleMemCapacity()]; sampleMemLen=0; + sampleMemLenBS=0; chip.rom_data=sampleMem; chip.rom_mask=0xffffff; reset(); diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 1f911a83..d141d591 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -69,7 +69,9 @@ class DivPlatformQSound: public DivDispatch { unsigned char* sampleMem; size_t sampleMemLen; + size_t sampleMemLenBS; bool sampleLoaded[256]; + bool sampleLoadedBS[256]; struct qsound_chip chip; unsigned short regPool[512]; @@ -102,6 +104,7 @@ class DivPlatformQSound: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const void* getSampleMem(int index = 0); + const char* getSampleMemName(int index=0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); bool isSampleLoaded(int index, int sample); From 3b0ef7c096ffa89b93993216eb780ae4c1023ad0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 18:41:04 -0500 Subject: [PATCH 17/34] GUI: mobile channels view --- src/gui/channels.cpp | 8 ++++++++ src/gui/gui.cpp | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 353e9a55..e6113885 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -29,6 +29,14 @@ void FurnaceGUI::drawChannels() { nextWindow=GUI_WINDOW_NOTHING; } if (!channelsOpen) return; + if (mobileUI) { + patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f)); + patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH)); + ImGui::SetNextWindowPos(patWindowPos); + ImGui::SetNextWindowSize(patWindowSize); + } else { + //ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); + } if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags)) { if (ImGui::BeginTable("ChannelList",3)) { ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ca806a3d..ec913542 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3788,6 +3788,11 @@ bool FurnaceGUI::loop() { drawSampleEdit(); drawPiano(); break; + case GUI_SCENE_CHANNELS: + channelsOpen=true; + curWindow=GUI_WINDOW_CHANNELS; + drawChannels(); + break; case GUI_SCENE_CHIPS: sysManagerOpen=true; curWindow=GUI_WINDOW_SYS_MANAGER; From 8eecdd4b931d9a7445ac98f777fd347fac67927c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 19:05:54 -0500 Subject: [PATCH 18/34] GUI: fix piano allowing you to scroll on mobile --- extern/imgui_patched/imgui.cpp | 26 +++++++++++++------------- extern/imgui_patched/imgui_internal.h | 6 +++++- extern/imgui_patched/imgui_widgets.cpp | 2 +- src/gui/piano.cpp | 1 + 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index a0298a3f..742127d6 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3449,13 +3449,10 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; // TODO: check whether this works if (g.LastItemData.InFlags & ImGuiItemFlags_NoInertialScroll) { - if (window) { - window->InertialScrollInhibited=true; - printf("inhibiting scroll\n"); - } + g.InertialScrollInhibited=true; } } else { - if (window) window->InertialScrollInhibited=false; + g.InertialScrollInhibited=false; } // Clear declaration of inputs claimed by the widget @@ -5104,6 +5101,11 @@ void ImGui::EndFrame() g.DragDropWithinSource = false; } + // Check for inertial scroll inhibit status + if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { + g.InertialScrollInhibited=false; + } + // End frame g.WithinFrameScope = false; g.FrameCountEnded = g.FrameCount; @@ -6934,7 +6936,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if ((g.IO.MouseDown[ImGuiMouseButton_Left] || g.IO.MouseReleased[ImGuiMouseButton_Left]) && g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_X) && g.ActiveId!=GetWindowScrollbarID(window,ImGuiAxis_Y) && - !window->InertialScrollInhibited) { + !g.InertialScrollInhibited) { // launch inertial scroll if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); @@ -6955,6 +6957,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InertialScroll=false; } } + } else if (g.InertialScrollInhibited) { + window->InertialScrollSpeed=ImVec2(0.0f,0.0f); + window->InertialScroll=false; } } @@ -6983,9 +6988,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InertialScrollSpeed.x=0.0f; window->InertialScrollSpeed.y=0.0f; } - if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { - window->InertialScrollInhibited=false; - } } // Apply scrolling @@ -7554,10 +7556,8 @@ void ImGui::EndDisabled() void ImGui::InhibitInertialScroll() { - ImGuiWindow* window = GetCurrentWindow(); - if (window!=NULL) { - window->InertialScrollInhibited=true; - } + ImGuiContext& g = *GImGui; + g.InertialScrollInhibited=true; } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 83faaf6e..e4b01421 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -1936,6 +1936,9 @@ struct ImGuiContext float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; + // Inertial scroll + bool InertialScrollInhibited; // Is inertial scroll inhibited? (e.g. by ImGuiItemFlags_NoInertialScroll) + // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) ImGuiMouseCursor MouseCursor; @@ -2145,6 +2148,8 @@ struct ImGuiContext NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; + InertialScrollInhibited = false; + DimBgRatio = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; @@ -2295,7 +2300,6 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. ImVec2 InertialScrollSpeed; // current speed of inertial scroll (AKA "swipe") - bool InertialScrollInhibited; // Is inertial scroll inhibited? (e.g. by ImGuiItemFlags_NoInertialScroll) bool ScrollbarX, ScrollbarY; // Are scrollbars visible? bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index 744ef29c..d95ed1a3 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -3113,7 +3113,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id)) + if (!ItemAdd(frame_bb, id, NULL, ImGuiItemFlags_NoInertialScroll)) return false; // Default format string when passing NULL diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index 98e83f35..9688b51d 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -222,6 +222,7 @@ void FurnaceGUI::drawPiano() { bool canInput=false; if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) { canInput=true; + ImGui::InhibitInertialScroll(); } if (view) { int notes=oct*12; From d3dedd020f79003538edd4c7275bbaee5163a48d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Dec 2022 19:21:02 -0500 Subject: [PATCH 19/34] QSound: ADPCM, part 1 --- src/engine/platform/qsound.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index ea609a3d..cc411fa5 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -397,9 +397,11 @@ void DivPlatformQSound::tick(bool sysTick) { rWrite(q1_reg_map[Q1V_START][i], qsound_addr); rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); } else { + rWrite(Q1A_KEYON+(i-16),0); rWrite(q1a_bank_map[i-16], qsound_bank); rWrite(q1a_end_map[i-16], qsound_end); rWrite(q1a_start_map[i-16], qsound_addr); + rWrite(Q1A_KEYON+(i-16),1); } //logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop); // Write sample address. Enable volume @@ -423,6 +425,7 @@ void DivPlatformQSound::tick(bool sysTick) { rWrite(q1_reg_map[Q1V_FREQ][i],0); } else { rWrite(q1a_vol_map[i-16],0); + rWrite(Q1A_KEYON+(i-16),0); } } else if (chan[i].active) { //logV("ch %d frequency set to %04x, off=%f, note=%d, %04x!",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note)); @@ -751,6 +754,8 @@ void DivPlatformQSound::renderSamples(int sysID) { } sampleMemLen=memPos+256; + memPos=(memPos+0xffff)&0xff0000; + for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; if (!s->renderOn[1][sysID]) { @@ -759,8 +764,8 @@ void DivPlatformQSound::renderSamples(int sysID) { } int length=s->lengthQSoundA; - if (length>65536-16) { - length=65536-16; + if (length>65536) { + length=65536; } if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { memPos=(memPos+0xffff)&0xff0000; @@ -771,16 +776,16 @@ void DivPlatformQSound::renderSamples(int sysID) { } if (memPos+length>=getSampleMemCapacity()) { for (unsigned int i=0; idataQSoundA[i]; + sampleMem[(memPos+i)]=s->dataQSoundA[i]; } logW("out of QSound ADPCM memory for sample %d!",i); } else { for (int i=0; idataQSoundA[i]; + sampleMem[(memPos+i)]=s->dataQSoundA[i]; } sampleLoaded[i]=true; } - offBS[i]=memPos^0x8000; + offBS[i]=memPos; memPos+=length+16; } sampleMemLenBS=memPos+256; From 018d8379e64be94a31a0b4f6742b304364d46a08 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 02:49:52 -0500 Subject: [PATCH 20/34] GUI: fix mobile pattern but now it's impossible to select --- src/gui/gui.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ec913542..faba3919 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2928,11 +2928,13 @@ void FurnaceGUI::pointUp(int x, int y, int button) { if (selecting) { if (!selectingFull) cursor=selEnd; finishSelection(); - demandScrollX=true; - if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && - cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) { - if (!settings.cursorMoveNoScroll) { - updateScroll(cursor.y); + if (!mobileUI) { + demandScrollX=true; + if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && + cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) { + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } } } } From dc7aec2dc1a7707aead575c54c960c067be1217e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 04:13:26 -0500 Subject: [PATCH 21/34] GUI: long hold? --- extern/imgui_patched/imgui.cpp | 10 ++++++++++ extern/imgui_patched/imgui.h | 1 + src/gui/gui.cpp | 1 + src/gui/gui.h | 7 ++++++- src/gui/pattern.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 742127d6..37b0b5df 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -7552,6 +7552,16 @@ void ImGui::EndDisabled() g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } +// IsInertialScroll() + +bool ImGui::IsInertialScroll() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window==NULL) return false; + return window->InertialScroll; +} + + // InhibitInertialScroll() void ImGui::InhibitInertialScroll() diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 5359672e..58dad2b6 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -839,6 +839,7 @@ namespace ImGui IMGUI_API void EndDisabled(); // Inertial scroll + IMGUI_API bool IsInertialScroll(); IMGUI_API void InhibitInertialScroll(); // Clipping diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index faba3919..eadac6d4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5766,6 +5766,7 @@ FurnaceGUI::FurnaceGUI(): curWindowLast(GUI_WINDOW_NOTHING), curWindowThreadSafe(GUI_WINDOW_NOTHING), lastPatternWidth(0.0f), + longThreshold(0.4f), latchNote(-1), latchIns(-2), latchVol(-1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 128bbe26..8a9e1e44 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -53,6 +53,11 @@ _wi->std.waveMacro.vScroll=-1; \ } +#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>longThreshold && !ImGui::IsInertialScroll()) + +// for now +#define NOTIFY_LONG_HOLD logV("long hold"); + #define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() // TODO: @@ -1425,7 +1430,7 @@ class FurnaceGUI { float peak[2]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; - float lastPatternWidth; + float lastPatternWidth, longThreshold; String nextDesc; String nextDescName; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 483d66b5..89653daa 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -128,6 +128,10 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int if (ImGui::IsItemClicked()) { startSelection(0,0,i,true); } + if (CHECK_LONG_HOLD) { + ImGui::InhibitInertialScroll(); + NOTIFY_LONG_HOLD; + } ImGui::PopStyleColor(); // for each column for (int j=0; jdata[i][index+1]==-1) { @@ -323,6 +343,10 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { updateSelection(j,index,i); } + if (CHECK_LONG_HOLD) { + ImGui::InhibitInertialScroll(); + NOTIFY_LONG_HOLD; + } ImGui::PopStyleColor(); } } From 4e88a677d000b53d02a180f8dcecc5be4583eb9b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 16:52:47 -0500 Subject: [PATCH 22/34] GUI; mobile file dialog improvements --- .../org/tildearrow/furnace/MainActivity.java | 12 ++++++---- extern/igfd/ImGuiFileDialog.cpp | 8 ++++--- extern/igfd/ImGuiFileDialog.h | 1 + src/gui/fileDialog.cpp | 10 ++++---- src/gui/fileDialog.h | 4 +++- src/gui/gui.cpp | 23 +++++++++++++++++-- src/gui/gui.h | 11 +++++++-- src/gui/pattern.cpp | 12 +++++----- src/gui/settings.cpp | 1 + 9 files changed, 60 insertions(+), 22 deletions(-) diff --git a/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java b/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java index 0ac1448f..f1bc8bfd 100644 --- a/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java +++ b/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java @@ -1,25 +1,29 @@ package org.tildearrow.furnace; +import android.content.Context; import android.content.Intent; +import android.widget.Toast; import org.libsdl.app.SDLActivity; public class MainActivity extends SDLActivity { static final int TA_FILE_REQUEST=1000; - public boolean showFileDialog() { + public void showFileDialog() { Intent picker=new Intent(Intent.ACTION_GET_CONTENT); picker.setType("*/*"); picker=Intent.createChooser(picker,"test"); startActivityForResult(picker,TA_FILE_REQUEST); - - return true; } @Override protected void onActivityResult(int request, int result, Intent intent) { super.onActivityResult(request,result,intent); if (request==TA_FILE_REQUEST) { - // TODO: fire an event here + if (result==RESULT_OK) { + Context context=getApplicationContext(); + Toast toast=Toast.makeText(context,"Got a file",Toast.LENGTH_SHORT); + toast.show(); + } } } } diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 3a30ac2b..5717785d 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -79,6 +79,8 @@ SOFTWARE. #include #include +#define DOUBLE_CLICKED ((singleClickSel && ImGui::IsMouseReleased(0)) || (!singleClickSel && ImGui::IsMouseDoubleClicked(0))) + #ifdef USE_THUMBNAILS #ifndef DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION #ifndef STB_IMAGE_IMPLEMENTATION @@ -3319,7 +3321,7 @@ namespace IGFD //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f;} + IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false;} IGFD::FileDialog::~FileDialog() = default; ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3972,7 +3974,7 @@ namespace IGFD } else // no nav system => classic behavior { - if (ImGui::IsMouseDoubleClicked(0)) // 0 -> left mouse button double click + if (DOUBLE_CLICKED) // 0 -> left mouse button double click { isSelectingDir=true; fdi.puPathClicked = fdi.SelectDirectory(vInfos); @@ -3987,7 +3989,7 @@ namespace IGFD } else { - if (ImGui::IsMouseDoubleClicked(0)) { + if (DOUBLE_CLICKED) { fdi.SelectFileName(prFileDialogInternal, vInfos); prFileDialogInternal.puIsOk = true; return 2; diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index e5ba92d0..5b55a9ea 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -1142,6 +1142,7 @@ namespace IGFD public: bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove double DpiScale; + bool singleClickSel; public: static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 9b13fff0..3c913d74 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -116,7 +116,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c } jclass class_=jniEnv->GetObjectClass(activity); - jmethodID showFileDialog=jniEnv->GetMethodID(class_,"showFileDialog","()B"); + jmethodID showFileDialog=jniEnv->GetMethodID(class_,"showFileDialog","()V"); if (showFileDialog==NULL) { logE("method showFileDialog not found!"); @@ -126,12 +126,12 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c return false; } - jboolean mret=jniEnv->CallBooleanMethod(activity,showFileDialog); + jniEnv->CallVoidMethod(activity,showFileDialog); - if (!(bool)mret) { + /*if (!(bool)mret) { hasError=true; logW("could not open Android file picker..."); - } + }*/ jniEnv->DeleteLocalRef(class_); jniEnv->DeleteLocalRef(activity); @@ -142,6 +142,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c #endif } else { hasError=false; + ImGuiFileDialog::Instance()->singleClickSel=singleClickSel; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback); } @@ -178,6 +179,7 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, c #endif } else { hasError=false; + ImGuiFileDialog::Instance()->singleClickSel=false; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); } diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 0fbeeb46..a88c2873 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -47,6 +47,7 @@ class FurnaceGUIFileDialog { pfd::save_file* dialogS; #endif public: + bool singleClickSel; bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL, bool allowMultiple=false); bool openSave(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); bool accepted(); @@ -65,5 +66,6 @@ class FurnaceGUIFileDialog { jniEnv(NULL), #endif dialogO(NULL), - dialogS(NULL) {} + dialogS(NULL), + singleClickSel(false) {} }; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index eadac6d4..34a429e7 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2630,10 +2630,12 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { if (mobileUI) { ImGui::GetIO().IniFilename=NULL; ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_InertialScrollEnable; + fileDialog->singleClickSel=true; } else { ImGui::GetIO().IniFilename=NULL; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable; + fileDialog->singleClickSel=false; } } } @@ -5225,7 +5227,7 @@ bool FurnaceGUI::init() { #endif // initialize SDL - SDL_Init(SDL_INIT_VIDEO); + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_HAPTIC); const char* videoBackend=SDL_GetCurrentVideoDriver(); if (videoBackend!=NULL) { @@ -5448,6 +5450,17 @@ bool FurnaceGUI::init() { return curIns; }); + vibrator=SDL_HapticOpen(0); + if (vibrator==NULL) { + logD("could not open vibration device: %s",SDL_GetError()); + } else { + if (SDL_HapticRumbleInit(vibrator)==0) { + vibratorAvailable=true; + } else { + logD("vibration not available: %s",SDL_GetError()); + } + } + return true; } @@ -5578,6 +5591,10 @@ bool FurnaceGUI::finish() { SDL_DestroyRenderer(sdlRend); SDL_DestroyWindow(sdlWin); + if (vibrator) { + SDL_HapticClose(vibrator); + } + for (int i=0; istd.waveMacro.vScroll=-1; \ } -#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>longThreshold && !ImGui::IsInertialScroll()) +#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left] Date: Fri, 2 Dec 2022 18:16:41 -0500 Subject: [PATCH 23/34] GUI: mobile file dialog improvements, part 2 --- .../org/tildearrow/furnace/MainActivity.java | 14 ++++- src/gui/fileDialog.cpp | 54 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java b/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java index f1bc8bfd..01b58420 100644 --- a/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java +++ b/android/app/src/main/java/org/tildearrow/furnace/MainActivity.java @@ -2,12 +2,14 @@ package org.tildearrow.furnace; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.widget.Toast; import org.libsdl.app.SDLActivity; public class MainActivity extends SDLActivity { static final int TA_FILE_REQUEST=1000; + static final int TA_FILE_SAVE_REQUEST=1001; public void showFileDialog() { Intent picker=new Intent(Intent.ACTION_GET_CONTENT); @@ -16,12 +18,22 @@ public class MainActivity extends SDLActivity { startActivityForResult(picker,TA_FILE_REQUEST); } + public void showSaveFileDialog() { + Intent picker=new Intent(Intent.ACTION_CREATE_DOCUMENT); + picker.addCategory(Intent.CATEGORY_OPENABLE); + picker.setType("*/*"); + + startActivityForResult(picker,TA_FILE_SAVE_REQUEST); + } + @Override protected void onActivityResult(int request, int result, Intent intent) { super.onActivityResult(request,result,intent); if (request==TA_FILE_REQUEST) { if (result==RESULT_OK) { + Uri path=intent.getData(); + Context context=getApplicationContext(); - Toast toast=Toast.makeText(context,"Got a file",Toast.LENGTH_SHORT); + Toast toast=Toast.makeText(context,path.toString(),Toast.LENGTH_SHORT); toast.show(); } } diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 3c913d74..4eb00bc7 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -142,6 +142,13 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c #endif } else { hasError=false; + +#ifdef ANDROID + if (!SDL_AndroidRequestPermission("android.permission.READ_EXTERNAL_STORAGE")) { + return false; + } +#endif + ImGuiFileDialog::Instance()->singleClickSel=singleClickSel; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback); @@ -152,6 +159,13 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale) { if (opened) return false; + +#ifdef ANDROID + if (!SDL_AndroidRequestPermission("android.permission.WRITE_EXTERNAL_STORAGE")) { + return false; + } +#endif + saving=true; curPath=path; @@ -172,13 +186,51 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, c dialogS=new std::thread(_nfdThread,NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError); #endif #elif defined(ANDROID) - hasError=true; // TODO + hasError=false; + if (jniEnv==NULL) { + jniEnv=(JNIEnv*)SDL_AndroidGetJNIEnv(); + if (jniEnv==NULL) { + hasError=true; + logE("could not acquire JNI env!"); + return false; + } + } + + jobject activity=(jobject)SDL_AndroidGetActivity(); + if (activity==NULL) { + hasError=true; + logE("the Activity is NULL!"); + return false; + } + + jclass class_=jniEnv->GetObjectClass(activity); + jmethodID showSaveFileDialog=jniEnv->GetMethodID(class_,"showSaveFileDialog","()V"); + + if (showSaveFileDialog==NULL) { + logE("method showSaveFileDialog not found!"); + hasError=true; + jniEnv->DeleteLocalRef(class_); + jniEnv->DeleteLocalRef(activity); + return false; + } + + jniEnv->CallVoidMethod(activity,showSaveFileDialog); + + /*if (!(bool)mret) { + hasError=true; + logW("could not open Android file picker..."); + }*/ + + jniEnv->DeleteLocalRef(class_); + jniEnv->DeleteLocalRef(activity); + return true; #else dialogS=new pfd::save_file(header,path,filter); hasError=!pfd::settings::available(); #endif } else { hasError=false; + ImGuiFileDialog::Instance()->singleClickSel=false; ImGuiFileDialog::Instance()->DpiScale=dpiScale; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); From 53ed7da6f7a13f99f32b56745b8fe009e288f18a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 18:52:26 -0500 Subject: [PATCH 24/34] another demo song --- demos/Swaggin_Dragon.dmf | Bin 0 -> 22889 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/Swaggin_Dragon.dmf diff --git a/demos/Swaggin_Dragon.dmf b/demos/Swaggin_Dragon.dmf new file mode 100644 index 0000000000000000000000000000000000000000..20c358049890d0e27cfd7c9d97404260a5727c57 GIT binary patch literal 22889 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 Yy(k|tSw32_yh^Q6YR~)s0bPlI;GeV5*8l(j literal 0 HcmV?d00001 From 2741167331fe258679d5b7eaa654c7577e3d1699 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 18:53:03 -0500 Subject: [PATCH 25/34] GUI: update credits --- src/gui/about.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index e7e96e3c..011b3dcf 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -100,6 +100,7 @@ const char* aboutLine[]={ "theloredev", "TheRealHedgehogSonic", "tildearrow", + "Uhrwerk Klockwerx", "Ultraprogramer", "UserSniper", "Weeppiko", From e7cfde9a2bee7a89bd0798ee350158d922d415df Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 19:09:43 -0500 Subject: [PATCH 26/34] GUI: mobile padding --- src/gui/settings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index fdb46293..518d3d79 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3284,6 +3284,10 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { sty.FrameShading=(float)settings.guiColorsShading/100.0f; } + if (mobileUI) { + sty.FramePadding=ImVec2(8.0f,6.0f); + } + sty.ScaleAllSizes(dpiScale); ImGui::GetStyle()=sty; From f84469e4fd233eab9c070f9c096a343afb7db62e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Dec 2022 22:59:52 -0500 Subject: [PATCH 27/34] GUI: better inertial scroll inhibit logic also better pattern editor mute in mobile --- extern/imgui_patched/imgui.cpp | 23 +++++++++++++++--- extern/imgui_patched/imgui.h | 1 + extern/imgui_patched/imgui_internal.h | 2 ++ src/gui/gui.h | 2 ++ src/gui/pattern.cpp | 35 ++++++++++++++++++++------- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 37b0b5df..3539ca81 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3451,8 +3451,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (g.LastItemData.InFlags & ImGuiItemFlags_NoInertialScroll) { g.InertialScrollInhibited=true; } - } else { - g.InertialScrollInhibited=false; } // Clear declaration of inputs claimed by the widget @@ -5102,7 +5100,7 @@ void ImGui::EndFrame() } // Check for inertial scroll inhibit status - if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { + if (!g.IO.MouseDown[ImGuiMouseButton_Left]) { g.InertialScrollInhibited=false; } @@ -6941,25 +6939,32 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.IO.MouseClicked[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); window->InertialScroll=false; + g.InertialScroll=false; + g.WasInertialScroll=false; } else { if (g.IO.MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>g.IO.ConfigInertialScrollToleranceSqr) { if (g.IO.MouseReleased[ImGuiMouseButton_Left]) { window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseSpeed.x:0.0f,window->ScrollbarY?-g.IO.MouseSpeed.y:0.0f); window->InertialScroll=false; + g.InertialScroll=false; } else { window->InertialScrollSpeed=ImVec2(window->ScrollbarX?-g.IO.MouseDelta.x:0.0f,window->ScrollbarY?-g.IO.MouseDelta.y:0.0f); if (window->ScrollbarX || window->ScrollbarY) { window->InertialScroll=true; + g.InertialScroll=true; + g.WasInertialScroll=true; } } } else { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); window->InertialScroll=false; + g.InertialScroll=false; } } } else if (g.InertialScrollInhibited) { window->InertialScrollSpeed=ImVec2(0.0f,0.0f); window->InertialScroll=false; + g.InertialScroll=false; } } @@ -6982,6 +6987,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InertialScrollSpeed.x=0.0f; window->InertialScrollSpeed.y=0.0f; window->InertialScroll=false; + g.InertialScroll=false; } if (g.IO.MouseDown[ImGuiMouseButton_Left]) { @@ -7556,11 +7562,22 @@ void ImGui::EndDisabled() bool ImGui::IsInertialScroll() { + /* ImGuiWindow* window = GetCurrentWindow(); if (window==NULL) return false; return window->InertialScroll; + */ + ImGuiContext& g = *GImGui; + return g.InertialScroll; } +// WasInertialScroll() + +bool ImGui::WasInertialScroll() +{ + ImGuiContext& g = *GImGui; + return g.WasInertialScroll; +} // InhibitInertialScroll() diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 58dad2b6..68bdff80 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -840,6 +840,7 @@ namespace ImGui // Inertial scroll IMGUI_API bool IsInertialScroll(); + IMGUI_API bool WasInertialScroll(); IMGUI_API void InhibitInertialScroll(); // Clipping diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index e4b01421..c2e15764 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -1938,6 +1938,8 @@ struct ImGuiContext // Inertial scroll bool InertialScrollInhibited; // Is inertial scroll inhibited? (e.g. by ImGuiItemFlags_NoInertialScroll) + bool InertialScroll; // Is any window being scrolled? + bool WasInertialScroll; // Was ^? // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) diff --git a/src/gui/gui.h b/src/gui/gui.h index 0e50f517..93672acc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -61,6 +61,8 @@ if (SDL_HapticRumblePlay(vibrator,0.5f,20)!=0) { \ logV("could not vibrate: %s!",SDL_GetError()); \ } \ + } else { \ + fputc(7,stderr); /* bell */ \ } #define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index e59493bd..e7f1e2f4 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -734,19 +734,36 @@ void FurnaceGUI::drawPattern() { ImGui::SetTooltip("%s",e->getChannelName(i)); } if (settings.channelFont==0) ImGui::PopFont(); - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { - if (settings.soloAction!=1 && soloTimeout>0 && soloChan==i) { - e->toggleSolo(i); - soloTimeout=0; - } else { - e->toggleMute(i); - soloTimeout=20; - soloChan=i; + if (mobileUI) { + if (ImGui::IsItemHovered()) { + if (CHECK_LONG_HOLD) { + NOTIFY_LONG_HOLD; + e->toggleSolo(i); + soloChan=i; + } + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) && !ImGui::WasInertialScroll()) { + if (soloChan!=i) { + e->toggleMute(i); + } else { + soloChan=-1; + } + } + } + } else { + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + if (settings.soloAction!=1 && soloTimeout>0 && soloChan==i) { + e->toggleSolo(i); + soloTimeout=0; + } else { + e->toggleMute(i); + soloTimeout=20; + soloChan=i; + } } } if (muted) ImGui::PopStyleColor(); ImGui::PopStyleColor(4); - if (settings.soloAction!=2) if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + if (settings.soloAction!=2 && !mobileUI) if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { inhibitMenu=true; e->toggleSolo(i); } From 69059dc7027ddbbd5ba44beca911b9ae5f7c2fea Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Dec 2022 00:51:57 -0500 Subject: [PATCH 28/34] GUI: several mobile changes --- extern/igfd/ImGuiFileDialog.cpp | 15 ++++++++------- extern/igfd/ImGuiFileDialog.h | 1 + extern/imgui_patched/imgui.h | 1 + extern/imgui_patched/imgui_widgets.cpp | 6 +++--- src/gui/fileDialog.cpp | 4 +++- src/gui/fileDialog.h | 4 ++-- src/gui/gui.cpp | 25 +++++++++++++++---------- src/gui/gui.h | 3 ++- src/gui/orders.cpp | 2 +- src/gui/pattern.cpp | 14 +++++++------- src/gui/piano.cpp | 2 +- src/gui/settings.cpp | 8 ++++++-- 12 files changed, 50 insertions(+), 35 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 5717785d..36bd122b 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -74,6 +74,7 @@ SOFTWARE. #define IMGUI_DEFINE_MATH_OPERATORS #endif // IMGUI_DEFINE_MATH_OPERATORS #include "imgui_internal.h" +#include #include #include @@ -119,7 +120,7 @@ namespace IGFD #endif // IMGUI_BUTTON // locales #ifndef createDirButtonString -#define createDirButtonString "+" +#define createDirButtonString ICON_FA_PLUS #endif // createDirButtonString #ifndef okButtonString #define okButtonString "OK" @@ -128,13 +129,13 @@ namespace IGFD #define cancelButtonString "Cancel" #endif // cancelButtonString #ifndef resetButtonString -#define resetButtonString "R" +#define resetButtonString ICON_FA_REPEAT #endif // resetButtonString #ifndef drivesButtonString #define drivesButtonString "Drives" #endif // drivesButtonString #ifndef editPathButtonString -#define editPathButtonString "E" +#define editPathButtonString ICON_FA_PENCIL #endif // editPathButtonString #ifndef searchString #define searchString "Search" @@ -149,10 +150,10 @@ namespace IGFD #define fileEntryString "[File]" #endif // fileEntryString #ifndef fileNameString -#define fileNameString "File Name:" +#define fileNameString "Name:" #endif // fileNameString #ifndef dirNameString -#define dirNameString "Directory Path:" +#define dirNameString "Path:" #endif // dirNameString #ifndef buttonResetSearchString #define buttonResetSearchString "Reset search" @@ -3321,7 +3322,7 @@ namespace IGFD //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false;} + IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false; mobileMode=false;} IGFD::FileDialog::~FileDialog() = default; ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3938,7 +3939,7 @@ namespace IGFD vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args); va_end(args); - float h = 0.0f; + float h = /*mobileMode?(ImGui::GetFontSize()+10.0f*DpiScale):*/0.0f; #ifdef USE_THUMBNAILS if (prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST) h = DisplayMode_ThumbailsList_ImageHeight; diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 5b55a9ea..2725223f 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -1143,6 +1143,7 @@ namespace IGFD bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove double DpiScale; bool singleClickSel; + bool mobileMode; public: static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 68bdff80..131784f8 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1580,6 +1580,7 @@ enum ImGuiConfigFlags_ // [CUSTOM] Inertial scroll ImGuiConfigFlags_InertialScrollEnable = 1 << 7, // Docking enable flags. + ImGuiConfigFlags_NoHoverColors = 1 << 8, // Disable all "hovered" color changes (useful for mobile). // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index d95ed1a3..737c6e09 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -998,7 +998,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // Render const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : (hovered && !(g.IO.ConfigFlags&ImGuiConfigFlags_NoHoverColors)) ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags); ImRect grab_rect; if (axis == ImGuiAxis_X) @@ -6329,9 +6329,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Render if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) hovered = true; - if (hovered || selected) + if ((hovered && !(g.IO.ConfigFlags&ImGuiConfigFlags_NoHoverColors)) || selected) { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : (hovered && !(g.IO.ConfigFlags&ImGuiConfigFlags_NoHoverColors)) ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); } RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 4eb00bc7..94ffa94e 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -149,8 +149,9 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c } #endif - ImGuiFileDialog::Instance()->singleClickSel=singleClickSel; + ImGuiFileDialog::Instance()->singleClickSel=mobileUI; ImGuiFileDialog::Instance()->DpiScale=dpiScale; + ImGuiFileDialog::Instance()->mobileMode=mobileUI; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback); } opened=true; @@ -233,6 +234,7 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, c ImGuiFileDialog::Instance()->singleClickSel=false; ImGuiFileDialog::Instance()->DpiScale=dpiScale; + ImGuiFileDialog::Instance()->mobileMode=mobileUI; ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); } opened=true; diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index a88c2873..6e91eddf 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -47,7 +47,7 @@ class FurnaceGUIFileDialog { pfd::save_file* dialogS; #endif public: - bool singleClickSel; + bool mobileUI; bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL, bool allowMultiple=false); bool openSave(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); bool accepted(); @@ -67,5 +67,5 @@ class FurnaceGUIFileDialog { #endif dialogO(NULL), dialogS(NULL), - singleClickSel(false) {} + mobileUI(false) {} }; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 34a429e7..8a7623f0 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2630,12 +2630,14 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { if (mobileUI) { ImGui::GetIO().IniFilename=NULL; ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_InertialScrollEnable; - fileDialog->singleClickSel=true; + ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NoHoverColors; + fileDialog->mobileUI=true; } else { ImGui::GetIO().IniFilename=NULL; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable; - fileDialog->singleClickSel=false; + ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors; + fileDialog->mobileUI=false; } } } @@ -2643,14 +2645,16 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { void FurnaceGUI::pushToggleColors(bool status) { ImVec4 toggleColor=status?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF]; ImGui::PushStyleColor(ImGuiCol_Button,toggleColor); - if (settings.guiColorsBase) { - toggleColor.x*=0.8f; - toggleColor.y*=0.8f; - toggleColor.z*=0.8f; - } else { - toggleColor.x=CLAMP(toggleColor.x*1.3f,0.0f,1.0f); - toggleColor.y=CLAMP(toggleColor.y*1.3f,0.0f,1.0f); - toggleColor.z=CLAMP(toggleColor.z*1.3f,0.0f,1.0f); + if (!mobileUI) { + if (settings.guiColorsBase) { + toggleColor.x*=0.8f; + toggleColor.y*=0.8f; + toggleColor.z*=0.8f; + } else { + toggleColor.x=CLAMP(toggleColor.x*1.3f,0.0f,1.0f); + toggleColor.y=CLAMP(toggleColor.y*1.3f,0.0f,1.0f); + toggleColor.z=CLAMP(toggleColor.z*1.3f,0.0f,1.0f); + } } ImGui::PushStyleColor(ImGuiCol_ButtonHovered,toggleColor); if (settings.guiColorsBase) { @@ -3684,6 +3688,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("help")) { if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen; if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen; + if (ImGui::MenuItem("inspector",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) inspectorOpen=!inspectorOpen; if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset(); if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) { aboutOpen=true; diff --git a/src/gui/gui.h b/src/gui/gui.h index 93672acc..f26786bc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -53,7 +53,7 @@ _wi->std.waveMacro.vScroll=-1; \ } -#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]AddRectFilled(rect.Min,rect.Max,col); dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID); } @@ -633,13 +633,13 @@ void FurnaceGUI::drawPattern() { chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?0.25f:0.0f + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?0.25f:0.0f )); ImU32 fadeCol=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?0.5f:MIN(1.0f,chanHeadBase.w*keyHit[i]*4.0f) + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?0.5f:MIN(1.0f,chanHeadBase.w*keyHit[i]*4.0f) )); dl->AddRectFilledMultiColor(rect.Min,rect.Max,fadeCol0,fadeCol0,fadeCol,fadeCol); dl->AddLine(ImVec2(rect.Min.x,rect.Max.y),ImVec2(rect.Max.x,rect.Max.y),ImGui::GetColorU32(chanHeadBase),2.0f*dpiScale); @@ -655,13 +655,13 @@ void FurnaceGUI::drawPattern() { chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?0.5f:MIN(1.0f,0.3f+chanHeadBase.w*keyHit[i]*1.5f) + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?0.5f:MIN(1.0f,0.3f+chanHeadBase.w*keyHit[i]*1.5f) )); ImU32 fadeCol=ImGui::GetColorU32(ImVec4( chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?0.3f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*1.2f) + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?0.3f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*1.2f) )); ImVec2 rMin=rect.Min; ImVec2 rMax=rect.Max; @@ -691,7 +691,7 @@ void FurnaceGUI::drawPattern() { chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) )); ImVec2 rMin=rect.Min; ImVec2 rMax=rect.Max; @@ -712,7 +712,7 @@ void FurnaceGUI::drawPattern() { chanHeadBase.x, chanHeadBase.y, chanHeadBase.z, - hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) + (hovered && (!mobileUI || ImGui::IsMouseDown(ImGuiMouseButton_Left)))?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) )); ImVec2 rMin=rect.Min; ImVec2 rMax=rect.Max; diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index 9688b51d..7f1360cc 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -415,7 +415,7 @@ void FurnaceGUI::drawPiano() { e->synchronized([this,note]() { e->autoNoteOn(-1,curIns,note); }); - if (edit) noteInput(note,0); + if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0); break; } } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 518d3d79..1c8cebb5 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3215,7 +3215,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY]; - ImVec4 secondaryHover, secondary, secondarySemiActive; + ImVec4 secondaryHoverActual, secondaryHover, secondary, secondarySemiActive; secondarySemiActive.w=secondaryActive.w; secondaryHover.w=secondaryActive.w; secondary.w=secondaryActive.w; @@ -3231,6 +3231,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); } + secondaryHoverActual=secondaryHover; + if (mobileUI) { // disable all hovered colors primaryHover=primary; secondaryHover=secondary; @@ -3261,7 +3263,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; sty.Colors[ImGuiCol_TitleBgActive]=primary; sty.Colors[ImGuiCol_CheckMark]=primaryActive; - sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover; + sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHoverActual; sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; sty.Colors[ImGuiCol_Border]=uiColors[GUI_COLOR_BORDER]; @@ -3492,5 +3494,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { if (updateFonts) { if (fileDialog!=NULL) delete fileDialog; fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); + + fileDialog->mobileUI=mobileUI; } } From 2a17c1040f5bc6132eb8b48a450de7da1778db41 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Dec 2022 01:05:37 -0500 Subject: [PATCH 29/34] store sample chip select --- src/engine/sample.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 2c15f676..5460aed3 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -58,7 +58,11 @@ void DivSample::putSampleData(SafeWriter* w) { w->writeI(loop?loopEnd:-1); for (int i=0; i<4; i++) { - w->writeI(0xffffffff); + unsigned int out=0; + for (int j=0; j<32; j++) { + if (renderOn[i][j]) out|=1<writeI(out); } #ifdef TA_BIG_ENDIAN @@ -130,7 +134,10 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) { loop=(loopStart>=0)&&(loopEnd>=0); for (int i=0; i<4; i++) { - reader.readI(); + unsigned int outMask=(unsigned int)reader.readI(); + for (int j=0; j<32; j++) { + renderOn[i][j]=outMask&(1< Date: Sat, 3 Dec 2022 01:13:13 -0500 Subject: [PATCH 30/34] GUI: fix warning --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 8a7623f0..f0d96ee2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2923,7 +2923,7 @@ void FurnaceGUI::pointUp(int x, int y, int button) { if (orderScrollLocked) { int targetOrder=round(orderScroll); if (orderScrollTolerance) { - targetOrder=round(orderScroll+(orderScrollRealOrigin.x-(canvasW/2))/(40.0f*dpiScale)); + targetOrder=round(orderScroll+(orderScrollRealOrigin.x-((float)canvasW/2.0f))/(40.0f*dpiScale)); } if (targetOrder<0) targetOrder=0; if (targetOrder>e->curSubSong->ordersLen-1) targetOrder=e->curSubSong->ordersLen-1; From b647d17b12428171ff489b5a496451d8bd5565a8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Dec 2022 04:24:08 -0500 Subject: [PATCH 31/34] prepare for custom chip clock rates --- src/engine/dispatch.h | 12 ++++++++++++ src/engine/platform/abstract.cpp | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 4ea92e10..37a001b2 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -453,6 +453,18 @@ class DivDispatch { */ virtual bool getWantPreNote(); + /** + * get minimum chip clock. + * @return clock in Hz, or 0 if custom clocks are not supported. + */ + virtual unsigned int getClockRangeMin(); + + /** + * get maximum chip clock. + * @return clock in Hz, or 0 if custom clocks are not supported. + */ + virtual unsigned int getClockRangeMax(); + /** * set the chip flags. * @param flags a DivConfig containing chip flags. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 33dd3c17..8ee1d45a 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -97,6 +97,14 @@ bool DivDispatch::getWantPreNote() { return false; } +unsigned int DivDispatch::getClockRangeMin() { + return 0; +} + +unsigned int DivDispatch::getClockRangeMax() { + return 0; +} + void DivDispatch::setFlags(const DivConfig& flags) { } From eb657aef1876c1ae0612856c8a398c128835eeac Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Dec 2022 23:04:58 -0500 Subject: [PATCH 32/34] GUI: prepare for proper VB/WS/C64 tick rates --- src/gui/gui.h | 3 +- src/gui/presets.cpp | 78 ++++++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index f26786bc..8d233f9a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -934,8 +934,9 @@ struct FurnaceGUISysDefChip { struct FurnaceGUISysDef { const char* name; + const char* extra; String definition; - FurnaceGUISysDef(const char* n, std::initializer_list def); + FurnaceGUISysDef(const char* n, std::initializer_list def, const char* e=NULL); }; struct FurnaceGUISysCategory { diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 74d0e012..6072570e 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -198,12 +198,14 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "WonderSwan", { CH(DIV_SYSTEM_SWAN, 64, 0, "") - } + }, + "tickRate=75.47169811320754716981" ); ENTRY( "Virtual Boy", { CH(DIV_SYSTEM_VBOY, 64, 0, "") - } + }, + "tickRate=50.2734877734878" ); ENTRY( "Gamate", { @@ -220,7 +222,8 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Commodore PET", { CH(DIV_SYSTEM_PET, 64, 0, "") - } + }, + "tickRate=50" ); ENTRY( "Commodore VIC-20", { @@ -230,65 +233,76 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Commodore 64 (6581 SID)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (6581 SID + Sound Expander)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (6581 SID + Sound Expander in drums mode)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL_DRUMS, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID + Sound Expander)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID + Sound Expander in drums mode)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL_DRUMS, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (6581 SID + FM-YAM)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL2, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (6581 SID + FM-YAM in drums mode)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID + FM-YAM)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL2, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID + FM-YAM in drums mode)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1"), CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "") - } + }, + "tickRate=50.1245421" ); ENTRY( "Amiga", { - CH(DIV_SYSTEM_AMIGA, 64, 0, "") - } + CH(DIV_SYSTEM_AMIGA, 64, 0, "clockSel=1") + }, + "tickRate=50" ); ENTRY( "MSX", { @@ -1085,8 +1099,9 @@ void FurnaceGUI::initSystemPresets() { CATEGORY_BEGIN("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis."); ENTRY( "Amiga", { - CH(DIV_SYSTEM_AMIGA, 64, 0, "") - } + CH(DIV_SYSTEM_AMIGA, 64, 0, "clockSel=1") + }, + "tickRate=50" ); ENTRY( "SegaPCM", { @@ -1144,7 +1159,8 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Commodore PET (pseudo-wavetable)", { CH(DIV_SYSTEM_PET, 64, 0, "") - } + }, + "tickRate=50" ); ENTRY( "Konami Bubble System WSG", { @@ -1189,12 +1205,14 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "WonderSwan", { CH(DIV_SYSTEM_SWAN, 64, 0, "") - } + }, + "tickRate=75.47169811320754716981" ); ENTRY( "Virtual Boy", { CH(DIV_SYSTEM_VBOY, 64, 0, "") - } + }, + "tickRate=50.2734877734878" ); ENTRY( "Seta/Allumer X1-010", { @@ -1207,12 +1225,14 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "MOS Technology SID (6581)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "MOS Technology SID (8580)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "Microchip AY8930", { @@ -2161,12 +2181,14 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Commodore 64 (6581 SID)", { CH(DIV_SYSTEM_C64_6581, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "Commodore 64 (8580 SID)", { CH(DIV_SYSTEM_C64_8580, 64, 0, "clockSel=1") - } + }, + "tickRate=50.1245421" ); ENTRY( "Arcade (YM2151 and SegaPCM)", { @@ -2187,8 +2209,9 @@ void FurnaceGUI::initSystemPresets() { CATEGORY_END; } -FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list def): - name(n) { +FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list def, const char* e): + name(n), + extra(e) { std::vector uncompiled=def; int index=0; for (FurnaceGUISysDefChip& i: uncompiled) { @@ -2205,4 +2228,7 @@ FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list Date: Sun, 4 Dec 2022 01:11:32 -0500 Subject: [PATCH 33/34] custom tick rate for presets --- src/engine/engine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f85b62b3..585f3264 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1416,6 +1416,12 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64) { song.systemFlags[index].loadFromBase64(flags.c_str()); } song.systemLen=index; + + // extra attributes + song.subsong[0]->hz=c.getDouble("tickRate",60.0); + if (song.subsong[0]->hz!=60.0) { + song.subsong[0]->customTempo=true; + } } void DivEngine::createNew(const char* description, String sysName, bool inBase64) { From c6604ff581b035456fd37ce675a2889f9b6554c4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 4 Dec 2022 02:04:42 -0500 Subject: [PATCH 34/34] add ability to select custom clock rates --- src/engine/dispatch.h | 20 +++++++--------- src/engine/platform/abstract.cpp | 8 ------- src/engine/platform/ay.cpp | 1 + src/engine/platform/ay8930.cpp | 1 + src/engine/platform/bubsyswsg.cpp | 1 + src/engine/platform/c64.cpp | 9 ++++---- src/engine/platform/fds.cpp | 9 ++++---- src/engine/platform/gb.cpp | 1 + src/engine/platform/genesis.cpp | 1 + src/engine/platform/lynx.cpp | 1 + src/engine/platform/mmc5.cpp | 9 ++++---- src/engine/platform/msm5232.cpp | 1 + src/engine/platform/msm6258.cpp | 1 + src/engine/platform/msm6295.cpp | 1 + src/engine/platform/n163.cpp | 9 ++++---- src/engine/platform/namcowsg.cpp | 1 + src/engine/platform/nes.cpp | 9 ++++---- src/engine/platform/opl.cpp | 3 +++ src/engine/platform/opll.cpp | 1 + src/engine/platform/pce.cpp | 1 + src/engine/platform/pcspkr.cpp | 1 + src/engine/platform/pet.cpp | 1 + src/engine/platform/pong.cpp | 7 +++--- src/engine/platform/rf5c68.cpp | 1 + src/engine/platform/saa.cpp | 1 + src/engine/platform/scc.cpp | 1 + src/engine/platform/segapcm.cpp | 3 ++- src/engine/platform/sms.cpp | 1 + src/engine/platform/su.cpp | 1 + src/engine/platform/swan.cpp | 1 + src/engine/platform/t6w28.cpp | 1 + src/engine/platform/tia.cpp | 1 + src/engine/platform/vb.cpp | 1 + src/engine/platform/vera.cpp | 1 + src/engine/platform/vic20.cpp | 1 + src/engine/platform/vrc6.cpp | 9 ++++---- src/engine/platform/x1_010.cpp | 1 + src/engine/platform/ym2203.cpp | 1 + src/engine/platform/ym2608.cpp | 1 + src/engine/platform/ym2610shared.h | 1 + src/engine/platform/ymz280b.cpp | 1 + src/engine/platform/zxbeeper.cpp | 1 + src/gui/sysConf.cpp | 37 ++++++++++++++++++++++++++++-- 43 files changed, 113 insertions(+), 50 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 37a001b2..4574ebb9 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -453,18 +453,6 @@ class DivDispatch { */ virtual bool getWantPreNote(); - /** - * get minimum chip clock. - * @return clock in Hz, or 0 if custom clocks are not supported. - */ - virtual unsigned int getClockRangeMin(); - - /** - * get maximum chip clock. - * @return clock in Hz, or 0 if custom clocks are not supported. - */ - virtual unsigned int getClockRangeMax(); - /** * set the chip flags. * @param flags a DivConfig containing chip flags. @@ -591,6 +579,14 @@ class DivDispatch { virtual ~DivDispatch(); }; +// custom chip clock helper define. put in setFlags, but before rate is set. +#define CHECK_CUSTOM_CLOCK \ + if (flags.getInt("customClock",0)>0) { \ + chipClock=flags.getInt("customClock",1000000); \ + if (chipClock>20000000) chipClock=20000000; \ + if (chipClock<100000) chipClock=100000; \ + } + // pitch calculation: // - a DivDispatch usually contains four variables per channel: // - baseFreq: this changes on new notes, legato, arpeggio and slides. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 8ee1d45a..33dd3c17 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -97,14 +97,6 @@ bool DivDispatch::getWantPreNote() { return false; } -unsigned int DivDispatch::getClockRangeMin() { - return 0; -} - -unsigned int DivDispatch::getClockRangeMax() { - return 0; -} - void DivDispatch::setFlags(const DivConfig& flags) { } diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index ab2c2c72..b10cdfd8 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -826,6 +826,7 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC/2.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/8; } for (int i=0; i<3; i++) { diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 821fa4fe..2ec70048 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -804,6 +804,7 @@ void DivPlatformAY8930::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC/2.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/4; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index c00b4ce2..a43b8b1d 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -323,6 +323,7 @@ void DivPlatformBubSysWSG::notifyInsDeletion(void* ins) { void DivPlatformBubSysWSG::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC; + CHECK_CUSTOM_CLOCK; rate=chipClock; for (int i=0; i<2; i++) { oscBuf[i]->rate=rate/64; diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 9fb10d41..068dd1b3 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -535,17 +535,18 @@ void DivPlatformC64::setFP(bool fp) { void DivPlatformC64::setFlags(const DivConfig& flags) { switch (flags.getInt("clockSel",0)) { case 0x0: // NTSC C64 - rate=COLOR_NTSC*2.0/7.0; + chipClock=COLOR_NTSC*2.0/7.0; break; case 0x1: // PAL C64 - rate=COLOR_PAL*2.0/9.0; + chipClock=COLOR_PAL*2.0/9.0; break; case 0x2: // SSI 2001 default: - rate=14318180.0/16.0; + chipClock=14318180.0/16.0; break; } - chipClock=rate; + CHECK_CUSTOM_CLOCK; + rate=chipClock; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate/16; } diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 5859be56..ab4c228a 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -454,13 +454,14 @@ void DivPlatformFDS::setNSFPlay(bool use) { void DivPlatformFDS::setFlags(const DivConfig& flags) { int clockSel=flags.getInt("clockSel",0); if (clockSel==2) { // Dendy - rate=COLOR_PAL*2.0/5.0; + chipClock=COLOR_PAL*2.0/5.0; } else if (clockSel==1) { // PAL - rate=COLOR_PAL*3.0/8.0; + chipClock=COLOR_PAL*3.0/8.0; } else { // NTSC - rate=COLOR_NTSC/2.0; + chipClock=COLOR_NTSC/2.0; } - chipClock=rate; + CHECK_CUSTOM_CLOCK; + rate=chipClock; oscBuf->rate=rate/32; if (useNP) { fds_NP->SetClock(rate); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 6a88d0f7..0bdd2434 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -655,6 +655,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) { int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { chipClock=4194304; + CHECK_CUSTOM_CLOCK; rate=chipClock/16; for (int i=0; i<4; i++) { isMuted[i]=false; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 85074228..adfdf258 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1221,6 +1221,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) { ladder=flags.getBool("ladderEffect",false); noExtMacros=flags.getBool("noExtMacros",false); OPN2_SetChipType(ladder?ym3438_mode_ym2612:0); + CHECK_CUSTOM_CLOCK; if (useYMFM) { if (fm_ymfm!=NULL) delete fm_ymfm; if (ladder) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 66acdcd5..e6704b64 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -471,6 +471,7 @@ int DivPlatformLynx::init(DivEngine* p, int channels, int sugRate, const DivConf } chipClock = 16000000; + CHECK_CUSTOM_CLOCK; rate = chipClock/128; for (int i=0; i<4; i++) { diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index b52017e8..9feaabfc 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -389,13 +389,14 @@ bool DivPlatformMMC5::keyOffAffectsArp(int ch) { void DivPlatformMMC5::setFlags(const DivConfig& flags) { int clockSel=flags.getInt("clockSel",0); if (clockSel==2) { // Dendy - rate=COLOR_PAL*2.0/5.0; + chipClock=COLOR_PAL*2.0/5.0; } else if (clockSel==1) { // PAL - rate=COLOR_PAL*3.0/8.0; + chipClock=COLOR_PAL*3.0/8.0; } else { // NTSC - rate=COLOR_NTSC/2.0; + chipClock=COLOR_NTSC/2.0; } - chipClock=rate; + CHECK_CUSTOM_CLOCK; + rate=chipClock; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate/32; } diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp index 84e8d91b..a9a70092 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -372,6 +372,7 @@ void DivPlatformMSM5232::notifyInsDeletion(void* ins) { void DivPlatformMSM5232::setFlags(const DivConfig& flags) { chipClock=2119040; + CHECK_CUSTOM_CLOCK; detune=flags.getInt("detune",0); msm->set_clock(chipClock+detune*1024); rate=msm->get_rate(); diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 7111c84c..ef11c717 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -412,6 +412,7 @@ void DivPlatformMSM6258::setFlags(const DivConfig& flags) { chipClock=4000000; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/256; for (int i=0; i<1; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 71b8d7cf..3bf0b8eb 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -438,6 +438,7 @@ void DivPlatformMSM6295::setFlags(const DivConfig& flags) { chipClock=4000000/4; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/3; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate/22; diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 26d42e45..50da45f0 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -624,18 +624,19 @@ void DivPlatformN163::poke(std::vector& wlist) { void DivPlatformN163::setFlags(const DivConfig& flags) { switch (flags.getInt("clockSel",0)) { case 1: // PAL - rate=COLOR_PAL*3.0/8.0; + chipClock=COLOR_PAL*3.0/8.0; break; case 2: // Dendy - rate=COLOR_PAL*2.0/5.0; + chipClock=COLOR_PAL*2.0/5.0; break; default: // NTSC - rate=COLOR_NTSC/2.0; + chipClock=COLOR_NTSC/2.0; break; } + CHECK_CUSTOM_CLOCK; initChanMax=chanMax=flags.getInt("channels",0)&7; multiplex=!flags.getBool("multiplex",false); // not accurate in real hardware - chipClock=rate; + rate=chipClock; rate/=15; n163.set_multiplex(multiplex); rWrite(0x7f,initChanMax<<4); diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index aab625b1..c1466b90 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -532,6 +532,7 @@ void DivPlatformNamcoWSG::setDeviceType(int type) { void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) { chipClock=3072000; + CHECK_CUSTOM_CLOCK; rate=chipClock/32; namco->device_clock_changed(rate); for (int i=0; iapu.type=apuType; } - chipClock=rate; + CHECK_CUSTOM_CLOCK; + rate=chipClock; for (int i=0; i<5; i++) { oscBuf[i]->rate=rate/32; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 5353fa6c..4464508c 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1694,6 +1694,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/72; chipRateBase=rate; break; @@ -1715,6 +1716,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC*4.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/288; chipRateBase=rate; break; @@ -1730,6 +1732,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC*8.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/768; chipRateBase=chipClock/684; break; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index a8b51b10..77dea2e7 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -958,6 +958,7 @@ void DivPlatformOPLL::setFlags(const DivConfig& flags) { } else { chipClock=COLOR_NTSC; } + CHECK_CUSTOM_CLOCK; rate=chipClock/36; patchSet=flags.getInt("patchSet",0); for (int i=0; i<11; i++) { diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index e8a308f1..f6a8df51 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -565,6 +565,7 @@ void DivPlatformPCE::setFlags(const DivConfig& flags) { } else { chipClock=COLOR_NTSC; } + CHECK_CUSTOM_CLOCK; antiClickEnabled=!flags.getBool("noAntiClick",false); rate=chipClock/12; for (int i=0; i<6; i++) { diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 226ee98c..59154dca 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -598,6 +598,7 @@ void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC/3.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/PCSPKR_DIVIDER; speakerType=flags.getInt("speakerType",0)&3; oscBuf->rate=rate; diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 8a606ecf..3722d52f 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -300,6 +300,7 @@ int DivPlatformPET::init(DivEngine* p, int channels, int sugRate, const DivConfi dumpWrites=false; skipRegisterWrites=false; chipClock=1000000; + CHECK_CUSTOM_CLOCK; rate=chipClock/SAMP_DIVIDER; // = 250000kHz isMuted=false; oscBuf=new DivDispatchOscBuffer; diff --git a/src/engine/platform/pong.cpp b/src/engine/platform/pong.cpp index 1015fdda..a98fe7ba 100644 --- a/src/engine/platform/pong.cpp +++ b/src/engine/platform/pong.cpp @@ -21,7 +21,7 @@ #include "../engine.h" #include "../../ta-log.h" -#define CHIP_DIVIDER 16 +#define CHIP_DIVIDER 1024 void DivPlatformPong::acquire(short* bufL, short* bufR, size_t start, size_t len) { int out=0; @@ -226,8 +226,9 @@ bool DivPlatformPong::keyOffAffectsArp(int ch) { } void DivPlatformPong::setFlags(const DivConfig& flags) { - chipClock=15625; - rate=chipClock; + chipClock=1000000; + CHECK_CUSTOM_CLOCK; + rate=chipClock/64; oscBuf->rate=rate; } diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index e33cfbf3..f13a25aa 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -348,6 +348,7 @@ void DivPlatformRF5C68::setFlags(const DivConfig& flags) { case 2: chipClock=12500000; break; default: chipClock=8000000; break; } + CHECK_CUSTOM_CLOCK; chipType=flags.getInt("chipType",0); rate=chipClock/384; for (int i=0; i<8; i++) { diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 7f48bca4..c3e48f1f 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -438,6 +438,7 @@ void DivPlatformSAA1099::setFlags(const DivConfig& flags) { } else { chipClock=8000000; } + CHECK_CUSTOM_CLOCK; rate=chipClock/32; for (int i=0; i<6; i++) { diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index a8fbcd7c..a75496d5 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -375,6 +375,7 @@ void DivPlatformSCC::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC/2.0; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/8; for (int i=0; i<5; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 860f9926..25de3395 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -486,7 +486,8 @@ void DivPlatformSegaPCM::reset() { void DivPlatformSegaPCM::setFlags(const DivConfig& flags) { chipClock=8000000.0; - rate=31250; + CHECK_CUSTOM_CLOCK; + rate=chipClock/256; for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 9f65a953..0ce4c836 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -568,6 +568,7 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) { stereo=false; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/divider; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 931225b4..d85a9d94 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -511,6 +511,7 @@ void DivPlatformSoundUnit::setFlags(const DivConfig& flags) { } else { chipClock=1236000; } + CHECK_CUSTOM_CLOCK; rate=chipClock/4; for (int i=0; i<8; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 222af4a5..852450a7 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -514,6 +514,7 @@ int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, const DivConf dumpWrites=false; skipRegisterWrites=false; chipClock=3072000; + CHECK_CUSTOM_CLOCK; rate=chipClock/16; // = 192000kHz, should be enough for (int i=0; i<4; i++) { isMuted[i]=false; diff --git a/src/engine/platform/t6w28.cpp b/src/engine/platform/t6w28.cpp index c6317e11..0d294836 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -343,6 +343,7 @@ void DivPlatformT6W28::notifyInsDeletion(void* ins) { void DivPlatformT6W28::setFlags(const DivConfig& flags) { chipClock=3072000.0; + CHECK_CUSTOM_CLOCK; rate=chipClock/16; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index d4703e28..2a28cb0a 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -356,6 +356,7 @@ void DivPlatformTIA::setFlags(const DivConfig& flags) { } else { rate=COLOR_NTSC; } + CHECK_CUSTOM_CLOCK; chipClock=rate; mixingType=flags.getInt("mixingType",0)&3; for (int i=0; i<2; i++) { diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index a8baab3c..1523a153 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -486,6 +486,7 @@ void DivPlatformVB::notifyInsDeletion(void* ins) { void DivPlatformVB::setFlags(const DivConfig& flags) { chipClock=5000000.0; + CHECK_CUSTOM_CLOCK; rate=chipClock/16; for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 2966f9d1..9294a344 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -438,6 +438,7 @@ int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, const DivConf dumpWrites=false; skipRegisterWrites=false; chipClock=25000000; + CHECK_CUSTOM_CLOCK; rate=chipClock/512; for (int i=0; i<17; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index d6200a16..52a08836 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -307,6 +307,7 @@ void DivPlatformVIC20::setFlags(const DivConfig& flags) { } else { chipClock=COLOR_NTSC*2.0/7.0; } + CHECK_CUSTOM_CLOCK; rate=chipClock/4; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 52a9350c..56df0e83 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -482,13 +482,14 @@ bool DivPlatformVRC6::keyOffAffectsArp(int ch) { void DivPlatformVRC6::setFlags(const DivConfig& flags) { int clockSel=flags.getInt("clockSel",0); if (clockSel==2) { // Dendy - rate=COLOR_PAL*2.0/5.0; + chipClock=COLOR_PAL*2.0/5.0; } else if (clockSel==1) { // PAL - rate=COLOR_PAL*3.0/8.0; + chipClock=COLOR_PAL*3.0/8.0; } else { // NTSC - rate=COLOR_NTSC/2.0; + chipClock=COLOR_NTSC/2.0; } - chipClock=rate; + CHECK_CUSTOM_CLOCK; + rate=chipClock; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate/32; } diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 683b36a6..b7382c28 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -922,6 +922,7 @@ void DivPlatformX1_010::setFlags(const DivConfig& flags) { chipClock=16000000; break; } + CHECK_CUSTOM_CLOCK; rate=chipClock/512; stereo=flags.getBool("stereo",false); for (int i=0; i<16; i++) { diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index b3dc596c..3d6eaa8a 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -952,6 +952,7 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) { ayDiv=16; break; } + CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); rate=fm->sample_rate(chipClock); for (int i=0; i<6; i++) { diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 63c84ad5..e15bf6b4 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1407,6 +1407,7 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) { ayDiv=32; break; } + CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 065eb5f3..c2dce8aa 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -294,6 +294,7 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { chipClock=8000000.0; break; } + CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); rate=chipClock/16; for (int i=0; irate=rate; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 326c39a9..170e2d8b 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -24,6 +24,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) { bool altered=false; bool restart=settings.restartOnFlagChange && modifyOnChange; + bool supportsCustomRate=true; switch (type) { case DIV_SYSTEM_YM2612: @@ -765,6 +766,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo flags.set("echoFeedback",echoFeedback); }); } + + supportsCustomRate=false; break; } case DIV_SYSTEM_X1_010: { @@ -1401,6 +1404,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo }); } + supportsCustomRate=false; + break; } case DIV_SYSTEM_MSM5232: { @@ -1542,13 +1547,15 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo break; } case DIV_SYSTEM_SWAN: - case DIV_SYSTEM_VERA: case DIV_SYSTEM_BUBSYS_WSG: - case DIV_SYSTEM_YMU759: case DIV_SYSTEM_PET: case DIV_SYSTEM_VBOY: ImGui::Text("nothing to configure"); break; + case DIV_SYSTEM_VERA: + case DIV_SYSTEM_YMU759: + supportsCustomRate=false; + break; default: { bool sysPal=flags.getInt("clockSel",0); @@ -1565,6 +1572,32 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } } + if (supportsCustomRate) { + ImGui::Separator(); + int customClock=flags.getInt("customClock",0); + bool usingCustomClock=customClock>=100000; + + if (ImGui::Checkbox("Custom clock rate",&usingCustomClock)) { + if (usingCustomClock) { + customClock=1000000; + } else { + customClock=0; + } + altered=true; + } + if (ImGui::InputInt("Hz",&customClock)) { + if (customClock<100000) customClock=0; + if (customClock>20000000) customClock=20000000; + altered=true; + } + + if (altered) { + e->lockSave([&]() { + flags.set("customClock",customClock); + }); + } + } + if (altered) { if (chan>=0) { e->updateSysFlags(chan,restart);