From d0d66c4e86547e6eed1160255c253b3d0ad62014 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Sun, 15 Feb 2026 17:43:34 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 16 ++++++++ Cargo.toml | 7 ++++ output.wav | Bin 0 -> 1433294 bytes src/lib.rs | 42 +++++++++++++++++++++ src/main.rs | 5 +++ src/note.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/piece.rs | 74 +++++++++++++++++++++++++++++++++++++ src/wav.rs | 59 +++++++++++++++++++++++++++++ 9 files changed, 306 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 output.wav create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/note.rs create mode 100644 src/piece.rs create mode 100644 src/wav.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..94d8c2d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "wav" +version = "0.1.0" +dependencies = [ + "paste", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..049180a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wav" +version = "0.1.0" +edition = "2021" + +[dependencies] +paste = "1.0.15" diff --git a/output.wav b/output.wav new file mode 100644 index 0000000000000000000000000000000000000000..850db1f741ad866bc0d4f25936bc19fbf8ec40f4 GIT binary patch literal 1433294 zcmeFaF|KyW(v?}XT?P(*&!I-p5+LDX(Cs3%+G~$NFb-_Sfmc&t5;Xyku3qho%*c$D zJD!L-_y1mDoXp4-Ydve9^WBn=5dZN%{QH0ZKmO;x`R)Jyzy8y|`~Uv`|MGwS>)(F+ z?O*-x|MPGE`)|Mf*Z=2V{q}$S_OE~YfB*A;`Op9De{25y{qOldfBg0L{GV^z`Y-VJ z{AU07tG=0^@b~ofS?Z_XwjE!f{`cr5bTsoaJeK;IN76B?Z{{a-bXzlgmip;R(lM#; z^D=yz>ZwQ4F{|$7`*$?+GJKl)smD@3tMBC})Hm}Ie3tsD$I>yYZ{{U*H1je% zmin1T(lM)V<|lM?TQhu?`sqs2F{$t6Cv^D=yz>ZwQ4F{|$7`*$?+GJKl) zsmD@3tMBC})Hm}Ie3tsD$5KD5@8u`dH}eyGn(C>~Qa`Qk<@?w7@)LZT`l(M-J+1HM zCsgZ#9CKdtWN``7pK6MUNbsZUcqt?%V0RQK}zeU|#EPg6gwZ{{V`_wo}wmin2`Qa`P4 z<|ouQ^AdcP`l-iKKdbNMC)79d5`32WsmIbWt8eBdbTsoaJeK;IN76B?Z}yWCI=-lv z@%MZKkECPvZQJn$I)0ODMn^L*V8*DCFz)WBptJk zZeB)5w>85f>6on~9g~h`UPebZFT-Q0pLrx5v-)OULPs+%!(-`~c`Ws_j%Hp)eKRk? zBk7oVEcLUFW?n{pGe5y&sh{~Q_0#%hUP65@Kfz<^nE5o-(~f3dMs+XW-y`Xm`84&@ zj&5s4eJ?*@CFz*>EcMflZeB)xGcUm->6m#e^|Ov6rK|_0x`SUPgU0FTo?}n0YMq zvyNt7Mtw6c!6WIIc`O~Xj&5s4M>8*DCFz)WBptJkZeB)5w>85f>6on~9g~h`UPebZ zFT-Q0pLrx5v-)OULPs+%!(-`~c`Ws_j%Hp)eKRk?Bk7oVEFH6sZfizIGcRK$>6myV z9kY&ZUPec^HNzw6n5`rola6LyMn^X8+OW9gWAEcLUFW?n{p zGcUm->6m#e9kY&ZYeq*iFJmR?n0O=|vyN_SMn^L*V;As zEFH6+c5_NcGcRN2=(&3&9kZTxYf4AAHDl%InXDuolb&vCN=G*@V8*DCFz-XEcLUVZfij8HCFz)WBptJkZfizIGcRK$>6myV9kY&ZUPec^HNzw6n5`rola6Ly zMn|{$46j*cMiXaPyj_;hOUTSvx<7x(PAQulN1i)BgrC-^$z7 zlNF_xkeMl&H<}EKw|kQvYrTZbEKa;rWLUi2yYyJ?WxSuod3TEUB>v`IdaU+S-cIAZ zJH=a$-QHb#tls6koyUncinkQIy_;-ByUTeyS2A_YTZ-MDo~$W%IdA7$s;YWRvD;H~ zRoyP=OtujTB@$f zuz0(AvZC}7GBYLfMw4Ojc5kv3t(WnBrX=2I-jn#7H`$8TPkB345^pqbId*$D*@||T z^LDOe>YBF{yFEQwQ|@x!&b3rk^_F6{r{=1Z&y%H_?+dWsYXu)>2i~>YtmaxvDNlvu>-onr`*aO|(>9m7`g=^<+)CCS)dB zrmo7cc)NPCrt~u2&$LWk^`6Av)RQ%(pYndHrK+m;9R8-7tLpre_fs`j)4k{LH?>q< z<)^%#>dBh&J&CueW$G#~BQw*JHD!in-PJO6)tZo-=*gNgM>B70nYwEB&))T9O_}AH z_q9}2HM?i;YObow^33~cuBMyavv)03S7mwTeLY!I&i=W(mZ__9H0!pWtSQ%o%tXu7 zRT&mT5VA-4K%6 zk)iKQHn{QXYiN^h7&)zF=(}+ZZk+o1*`ymrQfn!?Ze)WSudaSJ=Z28fT8h3K*+KW} z>t~a6LQ-iN`fg;08?U~GW6}*FsT~>m&SZldufB#h>4uThT86$G*Wkvfub)l2VI;Md zqU%OBxbf=hXLD`{Nv);myOAAqufBdZNhc(gmZ9%PcF?{0`q?C%kX2fWt{dAyckAkB zb96pdX({?{Yzx(`ub)j)`BuYF}bV5>T8TxKy z2i>c$pH0#US*4}uy0IN}x2}FRN9SXemZI;*wou*r`q?CvkJVX*z8l*@ck637Ch2^v za%AW`vmJD|zJ@kQCnS}Yq3=d^(7pQl*(9BiR9c3<8`j zuc1x4VdS)yq3^~uxN++1XOnIiNv);mx{(cTyt?|?oEt(?Ybp9}WCz`=ub)lQ2}z}8 z=(~{}ZoK*$j!8F!q;_QJJChA=y!sm2q#H&~YZ>}(T!R~@zJ508hLO}-imn^k;Kr+~ zpUt@;B(;{J??!gez54puB%P2{T86$G+2O{kui=<jui=<zuiL zsr0Y+{Z{|%-|COQxZk1vEn?-DD*i?v#rIo%zC`@r_U%~W(zmj14!sTw^sQmToHF3Qs1l{8hOr$$OOQPD+N`n!^*YV_1t zsba;5MU-^&u4cx%o76}fr|F6)OMllhW2K%NDdQ|%5oPJ`dSF-g}&AXl&DQ{9EZIq?IM@cvD zdTOM+NsY8o*3EmArN7^#Mp`{JQbtKP?@^Ziev=w$_0&iiCEdJ7S^E1;YNXXuV`Y@3 zzsFg+LQjp9dSjOIPTrky6i$l~I=d9%tzaJvCD5nXxj;x_OV&RK-ndq*XIx zU6ge59;c~_o770FX2!ZG>E=C7Qx!LGKLd}eIdS90rB~8^tS^B%0sOa?6NU5Z$nkY+uR}&STo*F5Y zG*uI2-Mp)rvF;`{(#C13B1*b>S2JVXO=_f#(^N&2bn~ue#=4u-NE@f=iYQBe*E3_K zo*F6REL{<0>F;`GtkhE@Wt^oeqAdMg&y1CNYNU*_bVZbP^RA~x%A3?k8)fP5QPR!3 zo*F4{QX_4YrN2i>H}86Cq`XOuv{Ba0dz3Z5-*}VSnWO7YcF?`L+RZsSA*q}xy6$9$ z8?UZ@Hs^+r)LM$J8`g;zbIuJTshv4`?qq`-uby^u$_*i@ojJPhWCz`=tKFQV6OziAqU%m}(7n3a%{e+D ztDHG{?raC$t*70bqVuuJnWO8@wou)=+RZsCAFG=wy6$WX-L0#i&C&T-rKRY)u^n`` zu6}clPDmXO6Br+d_5gYB%Soe5`J! z=(@8lbhoa4Hb>`Um6oFG#&*!%y86vIIw7f?DZ1`t2i>cy-JGKnlFFH)>rQsK@#^Yl zb8ZMpt)=L?kqvIVy86vIH;kNirs%qJ4Q`ye+RZsPjHGtv=(&>(ZoGQh%_%p8q;}@$ zx|1Druda4;j!sA_XNs;n+2O{ktDnueAtbeyqU%OBxbf=hH|N|ia@v`q>&`W}aq4O} z=iD%o+L@#0PBysl>S;Hp+z^u5nWO7YcF?`L+RZsSA*q}xy6$9$8?UZ@Hs^+r)LM$J z8`QxgjLAmZI-QcF?{0`q?C%kW^ZRz8l%$#;dR4m~=x(YDb2?GuhzAtFNI= zx?$wBmZ9&)HMnu=>t~a07)h>&ZoIns*_<0fQfn#tZe$1DtFNC;(g{hWW$3$+ z9d5k(8jeXfgrs(4=sS}QZoK*$+N2vsPHP$ZZd`*Kr@nqR>4uTiT8ge4+2F>jtDnue zAtbeyqU%OBxbf=hH|N|ia@v`q>&`W}aq4O}=iD&;YX`}=;q%-8$lFZTQ8Sk`Tpj{cYX{d((f>}K+_f4e`M zj{e)D|80D~-p#*+Rm*FB>b`rk(GTdmvQueW{^ee!F4J^J6q_v`6@X{+-4 z_1154Ghx5qO|qh8>F9sC-~Xy9zr(%eXa9D8HXZ%9NB`USe!czQz4F3-KPB--!_v`z zd-T7Jn=hkizh6)JN;}Q3_4Vj~8{e;{9rM-sze*2tvFDxDXFZcUjwRgYO z&(_cW?fz^!`frc^xAFaYpTF{VKiTj1j$yyW($Rl=^uLY$cX;>mYkfWXxBLBi@BZrk zvGx6W->;|t4P?HRx2q>BN-rTZQ!;Nf85VE%COg)837J`(c&Es)c)NG$vD(XcKa2D3 z6z@s=&Aaqi?Weq*#(8&&w;a2@yYyJS%XvGG6K@o6DRz4|*@||T^LDOe>YBF{yFEQw zQ|@x!&b3rs^_Iim({nYYpYndHrRu8pB;KZ;t0}#V%uGwwRT-9bSI^ayYeH_KrRu62 z&AP28E6UYBH&HTgG&!1e`zAZquKu}6oOq|m(X88d>9KlE$V}qAJ4J@Y+r3MV)n3N? zS)6yLcu(SQ-lfNCKjr;2PP|dP=kPagvK6hL@_wph>YDc){-&O+DgBi9Q!Q0jy(jTD z^;}KqWn^Yrs;9N|& z$jsuz8%2i1+r7zFv|dJLrex}x42id^Cu>SCBQw)dbybFC-PLn7<(iP2XsNm?M>B8h zxtenJ&)&6EU6tjT_w{5&IlE`?O6H9w%QNraWXIasJ$sK6?-W^{dH*gwQqS($dz5$k zp5>YMZ}TJl?4P?wX}9k=nsxg&Khm!WnMss(`)*low<*1)=(|z;cRqdnY|2ax!|$(060#Z(aKO*`!-eN^dE;ZWR5kPgg&i zb4y9-Ek)mr;=l9h>t~bhC@HmN=(|z;w?2Ih$D~_IN6VkxTZXNSN^dFpZWRBWPhUTqbVo_4Ekoap;=l9h>t~bh zC^@yI=(=(IcTQdXY|b4cr?wP*H?F~rQ(r%ubi>GLEkoapYjEe(*Kka_W8~B$L*JR> zzjNwqXp`ryKxQfocbD$Nq3B#dSvK3bNqKseGP5W9VMl<41G6>|IVkcpG~@>q|}z7??&<8 z`t&s%lWr*~{m9UFrs!{d`Wo7#TTVuA8TxJv{jE!1Kbv&RN$D*`*NvjT_37$ob8aaq zy`|{8QT%s4ef@0G9VMl<41G6>|JJ9k;h1zwN$E$1zB5IC>(kfJCf#x}ddtvvW9V;P z`uf?VTTV)EDY|YH{jE<|Kbvz)N$D*`-;LtG^XcnnlkO-fwPonLQT(?)eGSK?TS`hl zGW4A(`dgpAhGWt#C8Zx3I?ojUolj@OG3$TLejh?@ZC(`t&s%lWr*~{m9UHrugrCIvb8zca)TR zBz0$!j|_chivHH8 zui=<7`*$&Z>8h_hX0Yck@$&`PFZ;MX3H0g7pieaE$B*WG{5}c% z&nJUD@uVL|mUDFbB+x&{4E9WuK7K6c}elqA2%lWv`oR8m=L7$!s_C#|&ek|wc_GGZ9F@rs`oR1qzIl4U=^l3~X&ot-b zM^aj^CxboN6!OM$j&3C7=rv}r=P`vnvz(6`OF6nd8T4sPAq*>>&akG zHif*goTD2_IeLv5?0HOK&n)NT#!`-MPX>J&Q^+&T`S_8P*6Yb&Pd0_Tv7C<^$!NWv z4EkhqxEsy+_;HN0)04rT<{a+Ma*l2sqjefH*t5+cZ!G8IMlxEjCxbrO9PUPQK7Jgd zb$Sx$lg%M-H0j5WWVBwN1pcSZ;ch(X$B|>4ojwWl&pC&?)1;3d$7r3N1o~ui$Qw=i z@go_n*C&DgX;a7>P5Stel%v;^K%d4G_Dqw0{8-A-?UTU&98<_MPx^5rDXrHhf&OVz z$Qw=i@go_n*C&DhX>+(6Px^7>7-y$X0{wH&;qEl)trze3v*&OmllYab2M(g!S z;D6d2?#7dT963ho^zr9^+8px6v;M=8WVBvY#NYq7apQNc`pRPld#)n>{#}f}r>d_! zX0Yck@$&`PFZ;MX3H0g7pieaE$B*WG{5}c%&nJUD@uVL|mUDFbB+x&{4E9WuK7K6c z}elqA2%lWv`oR8m=L7$!s_C#|& zek|wX_9W1!CxbrGq#r+;^YQy6@IRjn_QaEZ99hoM?UO+N95dK6P5St;oR8a+K%brr z`b3j{{AkX{?~_3Pd=lsrP5SuJq#wU0fj)f__@6ZC$B!rdIDQiNpPvN!Cr|ouq)8vY zPXhh(NuWElO}e*B&U`t(Vlf6|D4mWGbp7%;WnVr6;(>GaS-%Y1^uk@4I>3ceTlO^`ubefFP&t|6? zK6RHR_T5yLjLuJHrx`wVmnHVyRF;g+Po}3CLUosB_WV?qjLyrYXBj?qmuB|-RF;g+ z%cW--K7E&__WX33_ew99p1r5jH)&?iPp5gW^m6IhdpdoSX7>Gbp7%;WnVr6;(>GaS z-%Y1^uk@4I>3ceTlO^`ubefFP&t|6?K6RHR_T5yLjLuJHrx`wVmnHVyRF<61&t|7N zLT#HR_T4m+oXSsTr#V7xnG?JXk z%cW;ILUo&__WV?qjLyrYXBj?qmuB|-RF;g+%cW--K6RI7_We|vjLuJHrx`wVmnHVy zRF;g+PiChXK6RHR-npqX8Qm_MoM-sdU6Oa_rLtsnyKHiv;Zt`>-kq1qlGE+d$$5@Y z+a`H;UK&YGwaX^wIYMol<_NWIme_aGNOCGa znVsec)oqs9cT-t1IzO46X86=yme_YwSu#35nVn|%)LoW%=cdwRbh~VFp5aq>N#328 z%97FTvdMXdPu(SXcU~$>PPa=Z=Q%=co8;YjX(Tz-E}NX^2(@jJcju*%WL3L#a-L;W z_DSBIm%@?N?6S#umQmR!d3Rn4M^>}TX6IQ-WuGPX-4u?j#!qIaSw>}_CHCDEj;zK{ zW~W(3ZJ%ZK-87P%%1>seIYMolCHCDklAOvpgnY;v9>)V4|9otH+!sCEqeoZ(emc=ny6Lc?fw1pJ)gRa|)Xoufj-Xm$kroZ;16 zc>0~A#zUxf1pJ(!)jW9ioukG>sCESWoS@Y_c=ny6#zUxf1Qm0FSM#W#_dPWpLgnXI zObJ@eqk`V|)OZM$pIZ{N&nl-y>!tg3CRSi~Og@)0r*;NyUS97WAu=;8|glf&Mnh>;_M^%H>SK}d6 zYj)Lypw&F88mzt=520GK>m~%R=26#ScGh?Z)tp>+CulW~x(>6m#zUy)y#VKg?k^2YEgE|o2;w?e~cY;NU^;Z+TG%;!@XPc2;N@&753!XLuEtx(>6mLc?h0_Biv9x(->h$x76 zf-~|UI3tgUfq0J?hb2IiGwJ^3)CXO~kx^>!v;UMbd-4?}u( zIptGtX9DV#qCNRgtS6sSKJ|9Wr`{;mlMlsu@;T*GZ>N0fjbc6fP_!qXGXe8@%BS8K z(z6T2dh$6FFt4Y4>Wv{iyHKoWpECpVdM03A8Pc;0Lwa^O6ELr50_K$=J-aZZXO}Yp z^?GJtUMbd-4?}u(IptGtX9DJxVmN0fjbc6d zP^>4PQ$F=}%IDrF+LI4QyLUR}Q*Zlp?uBAK`Eay*r&B)lwom6?DAuzN$Gdkr6ELs) zbnb;AJ-cwUd#5u2^SV#xUKrA|3rBl$Ix{e@r+n&-Aw9cLtS6r{0rPswr`{OSvkS#~ z@;MVwuO|ZPjbc6dP^>4PQ$F=}%BS8a){_s#dh$8tQ*WpI+#5x>`sBFw+iMav*=J^7 zD8kt%%eCL1leqgnGxx#~&Q3Y5)ApQ1-A|dhH;Qoe$#Lzs*CcAP&&<70e5+56>$JUQ zF_Tkf?v3H|Tyk9dZ7++N?K8744BzUK<=XGBS=3~onR}u5R-YW#X?x9LCa28Y8^gD{ z3aGn8`Uc^UCmfE*YxRu9wBkPN}&!hHrJrQJuEeENXH} z&Ad^3t51gNw7VuzlT&K$jUt?Va#W}7If=WUQZsKH;p~*5I`7U&)cu^Ad8G(fpA6M$ zcTJ)ur_|gVML7H9s7~8+5_Lai=H4j6)hEZb-(Hib$v!jtLJ`hBS+4#5oW$MtnYkB^ zaCXXZownyB>VC@1y-|d-Pmb!eJtuMZQ)=doBb=QwROj6}iMpRtGp`ik>XV^5?XF4G z6i(-0hs2dF2>qrwr9;ch2GNr_|gV$7r2$RHtp5!`)7qxi^k+cFJ+>x91%0zR%3Q zaE#U|%eCLPIpl4hnR_7_tyhlgv~6>^+bJ{m#xc%LIj;TooWtGsnYkB^aCXXZownyB z>VC@1y-|d#PmXK9y(UqUeP;HBBAk7)T>JewiM#JJb1xj>?3Cl$Z_hd0eV>_q;TWw` zmTSLnbI991GxtI=TCW_}Y1`&-w^L^Bjbogha$NiEIfuLNGqW!oqjk!1?e}dCciSgs zUpU6uDNDB7pL4kTE-^pn7_C#DZ0BHe$lES4J0}^fSC(wQZ*#cYJ~8{kG0sj|vfcii z!`*j@**QlzJ7vlC`*RX?-zR2YD8khzOSaozlc>oqF+ZmWXP-RT&S6gC?z_b7oFkl_ zvSj=HIf=UO6SFTA;p~&;+V9Uv+!nnX=@iTOE2IQ!(ub`EnAci$yu=N#eelqK8m z&q>sMpO}522v?sh*=~PLq9(h=Uyu44>zcCEM+LSX_kEch^*YqOxRkJhkVTAyjw0TzaOmWOOvM=a=DA zcfDMCrm|#oG_&WI;nR1$WO}C4yjRl9p5J>qebdXOXFAP$CC%*ly{FSRy6Z1Rb$8CJxT`D~T?O~-nc-7+=gf+`%97DlaIc;jLUngd7_thnnm@0AtYv*&v{eRIyNxa%|-Wd-@{nc-7+=gf+`%97DlaL=9@ zK6Q7_thlQzIb8+$?3p9fw&%=>yGD{zRdCOqIYMoF&aAj=Bsopf7_3D@-RJYe$exkBubUgLynBh}**HnI@vSf5T_3D`6Q+LyGD{zRdCOqIYM=NPOZ4BEE!z|_w1SBQ+Magio42^(N%EIo*6!M zch0Q2t27y14Y_sA@Tt2tvF4_-WOOy;)-}VY?%Kqfo63^Y)v#OF9HF*t6Kif7NlsNm zZe4SP+O|!sxoIR>RSmm!%`z(cHnHZWaAY+#)pS z*UU5?LRG`>yAr&bho4_D)p!V11-8b(t=?|U-5ii@9LF;!?7O$EL0$?$3}K0(D);~`WP^u8xSt9khO6;q9e zP*u?To&>Gt;pbOOH6B7$LBI1Pcr}k50YBGx2o=x1b0larj~xL&*LVmO&%SdcXf=-= z0YBGx7!^;yb7XiGmmL8=S7;aw&%SeHcoml&0Y6u07!A+9b7XimmmLE?*LVmO&%Sdc zXf=-=0YBGx2o=x1b0larj~xL&*LVmO&%Spgcr}kb1O1}$5GtB{??=#T9(xA*MdKk< zH2L0-pw&F~4D^e}!>DNXy&uD?xa=9|7lnq=(BykRhF5XfGte&z4WpsS_kIko=CY@t zUo;*1#s@{=3GtGJvf*f)iS(XiYnH-=YnIZ?203Js%S zxle8kujX>%VBa(zLd9~Q+z49D<3z!}X*`6Af_>9?2o=kHb|ZK-k244P zs__silKSjL&}trM4)RswAyg#w*^8jnJkA{CtH#5qNba*2!>hQQImlOqhS8AJXD^0V zaXE94uL=#LA*s(^46ovH=3rkH9!A4*pWGN;#pOi7z9}?}hUGrFF}#Y)iGqDoXc!I4 zeRgAr6_+yy`Kr(`8j||##qcUFXAbgJpIQBVnkgqWdr68%#UKDZcbLL=QV;V}qa-ZBN;@Iazp}xf_9SW7UR@c!q z&ZzCEy2qRy3YE52*U>c2sO_k_$DADsm9|pX(KF7dY^Zz3oE-|Cw^G^BG|s4OsC&np z9SWVdQrXco%Bbw8dqcX#vZrg5QrS@VjwpNHb>2#4PuD1=vZ3xBQFgrRw3W(^ zrg27PL)|;(>`>^umCBB$aYkiB-8<&&Q0TmM%8s6KMqR%f=a{oYaYJ0E?PwZj)b+b@ zjyXFNH^g`>^umCBB$aYkiB-8<&2SLn2r z%KDy4LS;kUTOqC2=)9H6`kqQcWkcOtA+6WwymiX@mP$fhzZ++Tv|e*VT&Jz?sU+0( zyKz=X>oqsTb=vx#NJ%KDy4LS;kUTOqC2=)9H6`kqQcWkcOtA+6Tv zw3W*0mYRQML)}|rtyby0mCEXtntx?O-CJX=R_VNz%IcPie`P=2TOqC2=)9H6`kqQc zWkcOtA+6Wwyp_uOo=QSxL)Ba1tkXw>+ZAaBzW35)Hw6(hGmYRQUN7Y?ptyZbDwYut-ihpfG)mOU+*)t=DL*)vEfQN`UBij)}&`t2(-Q8GIAP(Ie@YDQvu^ zqnVe%H_;qDmin2-#;fX^c?o6mD2ysD#{m%%qt96gebnZm|vI+}SId=t&lW2v8MY`m(znU}!#&>TIM`kBVYtLmHi z349a9(PycjDr~%_zL%fCH_;q@n(C>>#;dA(`Tl$l&C#c+pK5fps=k+>K=;u6e3tsD zM#rn_n|TR*56#hIsh??VysEyLpTIX!9DSDhslvu<>U;SKd=t&lr>UN5Y`m(vm+#N_ z&>Vf5`l&`otLl6C33Lz5&u6KhYIMA+zL}T6_s|?Ymin2-#;fX^c?o6mD2ysD#{m%%qt96gebnZm|vI+}SId=t&l zW2v8MY`m(znU}!#&>TIMj+sVBt2&x_8FUZL&m-xWX>`1*quZLn_s|?yl8%YS#;ZEI zc^P~Y#nB__m?><$rlXmc!8g$yJ(l{J#>T7an|TR*56#hI>6mGBw5p?-mqGW?{5+D5 znMOyeI+}SIbPv_fW2v91bhNI%nU_GfQ2jiXj+x5G>N=Wv8EgyH%_HfUseH7qquZK6 zw^037l8%W=N9#Jec^PyM&Cet0m}zvhs-u~gLHAJoJeK;IN=NJJn|TRz3)RnK>6oc} ztgfS(m%+AB-8_`1*quZLn_s|?yl8%YS#;ZEI{hSQ``)Q7U&lk|xc-6OU#~0v#Z;E3_GcQA7 z<24>h$Bb{HId*hgGc-0{wUTs9_#T>LM>j7+qoY+GNym)tq4{+*^D;C#UgfdW&-fmi zV|_C(L1W`p9!tlJZ=yJMH1jePHeTbAbjLeKS8nqoY+mOZ}AYq50MK z@)I;VUggtNPx&62V|6dzUt{A{K280UZ=yKX_wo}IHeTbi)KB>)nqz%4FF|ADRUS+I zjPId2);IGLG&)-4v(!)N9-3c$FF!$}<5fOQ^_1_SIac@b{WUgT<RUgfdW&-fmiV|_C(L1W`p9!tlJZ=yJMH1jePHeTbAbjDM>8)&VdFI(Nym(DqB(YSTQf8^UbT{RO!yv}V@EeHL!+Zr9!bZH?xFd0 zH1jevI$q_m)X(@Hnqz%4FF|ADRUS*njBlbib~N)c6gFPtk#x-XCW>Q6GcQA7tI&8X z^|Oj5if4T@FF_%z(0D8zvx+XUXGb$HBPOd*cqAROiY|(0N4GUYA*;}=Bps8ACW>cA zH!nkB<24>h$Bb{HICeDiG8DE7jmJ_yt7xKl);IGK6tW79$I>yY=n{K&H1jfIvI>Pq z(lM*(qIh<6TQd~03e8H=F{x;xcy@I2G88smDM>8)&VdFI(Nym(DqB(YS zTQf8^UbT{RO!yv}V@J3846UoKqKWlnP08|UnYxOl`}21_SyQrnTBfdI>3;gxqxrsH zPyb6>mEW(oev_LC`~7Z`6)j6g|I7X3FP2aFA~zbA?x%k}n(zDdZvNJ0?f2`=U*zu2 ze!siySk2PW|8l?oyBz-pPYU+8drErq#?t-tzdf4o`}N-Z&3$&hU+?oJ-u-01-#do= z7E4F}%l-aWZU387UVip(_h-}5e|z-5jqle}{srtc?DyN}ynA8k=)XPs-^ROdwBP!E zz5Ta|Uw*BxNB`USe!ciFY_Ir!z5TaQUfA!aB;IIPI{IJk_rGd4zsptovwyokn~wh5 zqyKGuzux-yeluae-%YZjW$Ea@J^J5<@{mxyhQ4o8)Nb?HtRxJ^N?xYeH_4<+1xYmUY{@=WlC5 zZjvV&9daz|_UKKV*M!`JU>ZGgH1qc8Oq^%`?44j5J+eG@e{?3!);)hCm`0C0k#!io z^JnXwzvYRnL!QVwSoiGBdgpIho_Rk{WF2Pz?A^L&Z&{joKg%=k&;Hr_?4P@5Y3BVL z&AL7NXYXr5Zj$A(`#F|%+q&m(YeH_4CmJ1cEbI2@O`O+++=O5nJ#sYj_UKHUXaDS- zU>ZHLJa&I{CeGG9e~-czJ4|-PyWWiGW6Zvuo63c4aX#c{Ef%R(06`AO6>GCv`GZ} zy@zG!yWNu#8-4w362bn)!%}qJ-jEVIUHxnhLH@?WQuN*4u;zFA`q`vB`5Onz(06;o zO6>GC9Fqw0Hy$HH-}wzGvD4SkCK2rS9+si+c27!d^!2kz1p6BgOVM?ELrUy)^|Lty z`5O;Q(RX{pn&0W`XOr^eZyYQ`-|Y=+ey6XWP0EwMbFdU$w|A`ht*(AHC(r%P!BX_y z-qB{a`uf?VEcd&8%g}dwN1Na3Yd9w5x!*aA41MQ!tog0JhBhfr{>H&F^xfXD=6Cw~ z*`z%AI|obAb$iE}-|FgTbMoBp94tlO?Hz4)tFNC;%5uNkw+wx^ceMGfzJ_B`p8K7{ z$k2Cw$C}^jYiN`5zj3e(eYZEP#7rkenU#^^fk0e1pB>*W$3%zlM)+!{cIA!{>H;nblu*N z5<6Y}Yz{&G#=}zd-QKX~cl!F-q&)c>2g}fRd&5fX^ferl2=X@`BSYW$4JonH*KkZC z$lrL344vmUtofbJhGSNq{Efp%(0P8tO6+uYe6k4gHy#f`=jjb8vD4Xc%p%zDJw}4g z^F1lC(b;g!BG}(}j0}C}H>AW)U&AqpAb;aAGIXBbu;zC<8;)6d@;446LFf4mE3wnr z@yQ~{-*`L(ou@aX#7<|&F^gco_ZSH}&-bLnMrXq@i(r4_F*5X>-;feJeGSJXg8Yrg z$k2CwLrU!QHMB_t`@M%{=)2vM5*vN}Y!bo#_j=eqxf|Cc$6>d>RQ7wEZr^Wp`a1TF z`&Q)tf81Xx`&&BQzTfKfb?n>E{8oSb#c|obx!adY|9;dTOkUvh?>jOIPTrky6i$l~I=d9%tzaJvCD5nXxj;(%<7OU7@E& zNW~_^nZr54c_Rp^33MEa|sF|@&O;mJo znyOIJRE?Uc=+w+ur=+QxI89ZkiHc6mjCD$ys)^H7g_@}7)XZ3?q^X)XO;@Oyu};s7 zm2sM?h_iHsni=c#%vc$xsfsvDSE!k>PS1>$ahj@#vvh@;sp$07NU5Z$nkY+uR}&ST zo*F5YG*uI2>F;WyqSI3&rIMy~vJ z)y!CTlNxE`G+hy8>F;`GtkhE@Wt^oeqAdMg&y1CNYNU*_bVZb)JS=g8fl{}{XI&$dDl}T1PmPo}sgX9yx_OVX^!J<8NUNtt z$|&jPJ<8JGZ&D+zo*F5mq?`9BOMkyfjkJ1dtc!W zr>P1hP1UHGu})1?ba9%hP|{S5nyKj2%#Tj-tk8V&yMI=6i9Nepe)3|n3dJYA`)5TL z#k0HTKk^i^3eC4|#}}yh&BdM_&Ag15tU}?Dbj&KcD4reN)(nNLLbH-|Oe&fvo*mu1 z426x?cqAP&zKP=4(ag(G*eWz0OZ}{(iQ-w`%u7(nDl{HT$E>1D?Ag)G%ZSM;6dp;( ztfGtJ+0kvyP{=AYD@n(sqKV?!(apz5n<$PQ&AbeSjn{Z29W%a(;@Hv5%TU-VG#*R+tfGnH zS>Mb{P{=AY9!tlpqD$=A(ag(;$tn~cNyn_Bi{jbQZOu@~Dl{ue$E2c(;@Q#7%TU;O zjYrZkMc0P}q2l&r(0-n<$R;z5E1)twQ6|R8K3KD4x~5e1C4O`SI8i{e?|%u7(nDl{HT{j8#i z;#uF!OHjxvG#*RGtfEWo+0o3)h{-Ax9!bZnqD#Lg{iWyzKb!Ao)BXAT&*uBtbU*#; z(R{zwzufQF%l*E8_P^bqO-KLj{qa}scR6o=zux>s?(XdOyUUK%EFJx~NB`T1zsH^8 z*ZO+&Z}br=WjWhbvsXF9oB^0#JXp1IhJ)h%QNq<3AxGapS$N+*6ke4y1gdkCTl`& zlB1cob1duj?4P}_3Ass@$L{A?)@|#azpV+mNuJ0$z%*N{@Ht;$U0iH7mM5|fd7{z5dgpJWH*wArSqFk?^sw&P+vrT3vpn-Y!8Cfz{<-_;Oq_Ex>o&nO zdaMb#$>^Ow=UCS5Jdt%+6LJ&lp1tK**6l3MyuT*oCbNI`o}-z!vo!Pm?4P~Q{@Ht$ z$L?oo=6&m)zs>&Hd!EQTWO?kq_0Hd{d;XRuvJQEo(ZPD>Z=*MH&J$S&g6Z+F-uc_( z%$)K>)`6jVT&#Qc_V_ZVEYG~pP(3cQfA0SHGN&BPy3J5ME^9(=@;DQx9Lu^*FpVB- zLT)m8=g&EobvsXF9oB^0#JcBiIhuJp&tn~C|Lonm=WkgayPxN=4%R(?v+ns@p2#}n ziAD$OoxhFV#5qr79SEk!!+Pg$k27=16Ilm_>f>U)^S6&L^)pXo9Vo7kk9E)9KCaZy zEYG}8aeaJd|J?oKO8v~ytlJdV$7fB*O+LQN&m7CT%}_lqYeH`FI1{HF%eqZ4jUH=4 zZZdik=N!$vO%RP9vw!wJdK2d?kKHGTMi1+rzm49+IZtFA2&TuwdgpJCGjqxlSqFyd z<6^z@w~sINGf!k4D6Wr>b09vlLyoj%Hp) zS3jHMk#x)~Mc=KX+nUkW&nB%T9TUsYckAfpW%M;1lRT1+*~rj$?r7#^^fk0e9!veq zGW6Z*n|TR+{cMuQ(lN6XUAK;AUPf0xo8ysm%q&IUt)ttT(bvx=tt1^2%g}f0=(c9` z^|MJUNzcSmblrNottnmoY|hHjGqDtXx1M%$N?$*lG;{RaEkoa}r`?*;*KkZ)IeI1| zL*Kcl+nUnX&?c=U9TUsYckAf3X7u&5Nh?Xu#8PzKdb+JCUHxp%%F#2i6n(dzc5_N! zKbtgj^xQ2&->s+Jn$p*BOjUAMojaO&8GQ|HlE+d%vkZN=`et52Uq74Vv2@HVMc1vPnU~Sk z&*pd}9WzVOckAf3X7u&5Nh?Xm#4_~VI=XooeGSJXkECNZGW4B0nt2(04Q-OgQa`f{ zeYg5%UP50#o8+-{%q&IMt)rQj(bdo9cqAP&OVM}h=(c9`^|MJUNyo%8^xZnTc^Q2T z$0U!WV>UAMojaO&8GQ}MB#)$HHZpXcJG!kIoejsVm84@b5_FzBx_KF$9iJ?Zq+|9F zbe=kzc^RD@$1IPfel`+xp6i==37rkcERUsQHZt^`JDPbJeGSJXkECNZGIX9hx~&D?be=o9c^RD@pDd50WA+eqo;sR&8J!)+ERUsrHWGB6>zjEAoejqQR^qo7Jc^Q2TZIZ`QKeG&dxB6yYLSH|d<>>Zg z(5EqlJky+yA4zGwo(%S6Q^*_3Il7UQqt}?hp2rmS%yK?%Eam9-WYDKEg*?-oj~_`n zdOZpBX-r|yH0j5Wr5xQp3H;A7g*@}5A4ihXdVLb;pEiZO(WH+bNjZ8w3G``9A)nLu;(#_J+qvT8%sI5JsI?AOd-!S=i^6GTCXRAJ=qlU z#&SMxB%}3uGU$`d;chhN-UM&-xEXlF@oq5r6;R#*N>(>MM^K?752g z`*$(^o~pj`n8BXE#LpLCzwG1oB+#cPgFexuA3vJ&@%tq3Kc5Wt#FKs;SJ7e|{3^ zpFHWuktTioJ_+>CCxJfEq>mp>`tf@b=+h^G{z-H8`O#m(^Xq8i<GVyW+H=%--YdOa zdiI`9-=vv6Kb_{i(#xf1@9FeSn%Vc$dEP7iWOn+VPTypSeK(!vz0yx+r|;?XO_tbq z(`hnFKbxIq_|#pN*mqM|GCDt*oo4vdU6$B)Q&}=PKbf9p2-RJh+4ECbGCD7po@Myd zU7FeRQ&}=PFPENW`1D{uKcADYScUflNO{aOU^pn}?dpdoSCHCEPn)gaSnVr6;(>Gb-otw_{ zUb)LA=kMwCO_F!#rPI7u?y|}GdpdoS zN#328%97LV(#d&_P}?SXcU~GvPPNM>=Q%=co8;YjX(Tz-E}Na_7`1Jd*mu)Naw_V}GCDt*oo4vdU6$B)Q&}=PKbf6o_|#pNc;}|l zWOTc1a-QK+cS+uzm&%gS?Xt;vhELrkd3RnaOHQ{-C+9grZJXrXd1)j$)h?Ty=LofJ zl6U8&kz`f7baI|$RQ5^UotMIq)$Fp#d6rSxCwX^X3P)D6%Vy_UN@brV_T3bYtj14f zr&&g2pC$I)6ppOMPiCiCMs1&E_T4m+oXSsTr#V7xn-mXp!YpB9zx~kS4;_7&7*?e_tba@m7iZRC1^E|3VPpD<6%@jLB*8eRa`3QeNTmk z(fIilQ-)V@si5~g6&gn4=T}S_Ud^R~-}lsb2$i2-F(qgdDQTGt{M-a^3tnj1h3{%)nN72cnH;+T{R(SHIJ$WtFOjGsMhSN2|=rQ zR5e(AH6BK_rdLfEUd5%V!Ro8fFq$>HYQpd;E>#UyUxkLztl3o)hF5c`>ahB1JcMe^ zu9^_EnnzWG)mP&oRBLwCgrLqv8b8_9Cpw&F;I?T=*522cqEAIra=26+gdTTs{ z%H~$y2wKgfvW4~5cnFovt-KMmnnz^|>#gxHDw|w+V|W#p$`;mJpXDqGCn3J;^1lk4sbui{eIVRlw%7|onqcV~DNm%0wKvqHmY=H$vd zL#((|wy@p`4WqHSl{bc0aj9%!y%icpV{kXXFtv5bqHK^G=XP9t`Q(C1N1n&jidX!5Milq-U3ifp|X?Fs}q>?7^^} zU1Ey7;!ME2<|$(bhV<+bQ{)wA0_HVO89OkfkxR^xS40fNd!90OAUGqBm?E!;7>M^g zW$Zw3MjkN*p@=AmcY-tWAUGqBh=F*I7>IX*Gx8ufBaeuId5=H2$?`Hz$mEep#7}B###6Y~C37A)cGxA_aBbSJRc#jx}cY-tWAUGqBh=F*I7>IX* zGx8ufBaeuId5)gq-YC|y z4@G)gq-YC|S55;=&IptGtr+n^>qCNR=w0oyhKJ~Uw=Uyn*lMhF`cRJ-$Z~Jua zg43(z6RkyLUPhFt7V`?u8*eyKuB8r!xcdddjEX7}B!~#d`8N z6ELr*eCmxMJ-bk>C!aF`^?D+p-YC|S55;=&IptGtr+n&-Vm zownC3W^&5Ry)k^NOOEQay=GC9Q)=do;#++(ROj6_ijxB6tLPP=OoH94i`-YCM^Cr5SKo|CxyDK+!P5zbB-s`Kug zMBUG+nOBN%^~q43cGo0oa!Sp;QG~Nkj_R~MCsFrPX6}t5Tzzs}`|UM}n(Q;PFBIYI zljYj)&q>^UpP75%2xq4p*J*oBqVA{6+#5wW`{bxj+jA0kKc!~gIKtT}Lv`Mrlc@VS zHSa;uOaQ9Pc?u}!#PC2U6 zw#^}Lr_{_F$!NVYROelr!`;rQnOBZ+cFItlcIO=KeoD=~ag5d}M|Ik^Io$1(nS0|H zXQv$3etXX0?)%K_3&&`kvRwOpn?v6AnYkB|(R$^$PTMwzyPYy~Zye+7l;hfO&pF(E zpP75%2xq4p*J*oBqVA{6+#5x>`sBFw+iMav*=J^7D8kt%%eCL1leqgnGxx#~&Q3Y5 z{q~&0-S?T<7mm?7Wx4kIHix|JGjlH_qxH&howjWbcROX~-Z;kDDaW`!iTOFhx4PuXb`EP6HQ6O*=M>-SlO@~luUX7wpO}4N_*R!J*Wb6lcHt)- z?Re_d@!LcH0_)e~Cpt|=$#buc89sG)P30#lOGd|2uZ|f$b$3nWCn`%u$5VTb8A5f} z%cW;3OGZaCdwv-{b=S+KXDUlZM>BhV89sg2OQvT!&3h%y?D@T?(>J|bdZyF7SJKR$ z-+MZJ)63;&I?sD0PrW+6r_(prRDPnoo6`72LDudpdn{&aAlWG#OX_kEch^*YqOxRkJoW0B;nR25Tz;a{yjSwntK)k*eRECaCpyi0 zB~QINzNgbS*VKxM&huVb!99Dvr_(p*%!<2C^IloOJ$t^V(>LeLn!C>PURgtKUEkB` zn>MlLrqjGv){tA*_jLNEO{}@;G#OH1Cxa+_UF7_ zthnnm@0AtYv*&v{eRIyNx$8Xdl{Mtn^*x=wX%lO1I?a1!4Y_rFPp5C%#G0EyGD{zRdCOqIYM=NPOZ4BEE!z|_w1SB zQ+Magio42^(N%EIo*6!Mch0Q2t27y14Y_sA@Tt2tvF4_-WOOy;)-}VY?%Kqfo63^Y z)v#OF9HF*t6Kif7NlsNmZe4SP+O|!sxoIR>RSmm!%`z(cHnHZWaAY+#)pS*UU5?LRG`>yAr&bho4_D)p!V11-8b(t=?|U-5ii@9LF;!?7O$EL0$?$3}K0(D) z;~`WP^u8xSt9khO6;q9eP*u?To&>Gt;pbOOH6B7$LBI1Pcr}k50YBGx2o=x1b0lar zj~xL&*LVmO&%SdcXf=-=0YBGx7!^;yb7XiGmmL8=S7;aw&%SeHcoml&0Y6u07!A+9 zb7XimmmLE?*LVmO&%SdcXf=-=0YBGx2o=x1b0larj~xL&*LVmO&%Spgcr}kb1O1}$ z5GtB{??=#T9(xA*MdKkDNXy&uD?xa=9|7lnq=(BykRhF5Xf zGte&z4WpsS_kIko=CY@tUo;*1#s@{=3GtGJvf*f)iS z(XiYnH-=YnIZ?203Js%Sxle8kujX>%VBa(zLd9~Q+z49D<3z!}X*`6A zf_>9?2o=kHb|ZK-k244Ps__silKSjL&}trM4)RswAyg#w*^8jnJkA{CtH#5qNba*2 z!>hQQImlOqhS8AJXD^0VaXE94uL=#LA*s(^46ovH=3rkH9!A4*pWGN;#pOi7z9}?} zhUGrFF}#Y)iGqDoXc!I4eRgAr6_+yy`Kr(`8j||##qcUFXAbgJpIQBVnkgqWdr68%#UKDZcbLL=QV;V}qa-ZBN z;@Iazp}xf_9SW7UR@c!q&ZzCEy2qRy3YE52*U>c2sO_k_$DADsm9|pX(KF7dY^Zz3 zoE-|Cw^G^BG|s4OsC&np9SWVdQrXco%Bbw8dqcX#vZrg5QrS@VjwpNHb>2#4 zPuD1=vZ3xBQFgrRw3W(^rg27PL)|;(>`>^umCBB$aYkiB-8<&&Q0TmM%8s6KMqR%f z=a{oYaYJ0E?PwZj)b+b@jyXFNH^g`>^u zmCBB$aYkiB-8<&2SLn2r%KDy4LS;kUTOqC2=)9H6`kqQcWkcOtA+6WwymiX@mP$fh zzZ++Tv|e*VT&Jz?sU+0(yKz=X>oqsTb=vx#NJ%KDy4LS;kUTOqC2 z=)9H6`kqQcWkcOtA+6Tvw3W*0mYRQML)}|rtyby0mCEXtntx?O-CJX=R_VNz%IcPi ze`P=2TOqC2=)9H6`kqQcWkcOtA+6Wwyp_uOo=QSxL)Ba1tkXw>+ZAaBzW35)Hw6(hGmYRQUN7Y?ptyZbDwYut- zihpfG)mOU+*) zt=DL*)vEfQN`UBij)}&` zt2(-Q8GIAP(Ie@YDQvu^qnVe%H_;qDmin2-#;fX^c?o6mD2ysD#{m%%qt96gebnZm|vI+}SId=t&lW2v8MY`m(z znU}!#&>TIM`kBVYtLmHi349a9(PycjDr~%_zL%fCH_;q@n(C>>#;dA(`Tl$l&C#c+ zpK5fps=k+>K=;u6e3tsDM#rn_n|TR*56#hIsh??VysEyLpTIX!9DSDhslvu<>U;SK zd=t&lr>UN5Y`m(vm+#N_&>Vf5`l&`otLl6C33Lz5&u6KhYIMA+zL}T6_s|?Ymin2- z#;fX^c?o6mD2ysD#{m%%qt z96gebnZm|vI+}SId=t&lW2v8MY`m(znU}!#&>TIMj+sVBt2&x_8FUZL&m-xWX>`1* zquZLn_s|?yl8%YS#;ZEIc^P~Y#nB__m?><$rlXmc!8g$yJ(l{J#>T7an|TR*56#hI z>6mGBw5p?-mqGW?{5+D5nMOyeI+}SIbPv_fW2v91bhNI%nU_GfQ2jiXj+x5G>N=Wv z8EgyH%_HfUseH7qquZK6w^037l8%W=N9#Jec^PyM&Cet0m}zvhs-u~gLHAJoJeK;I zN=NJJn|TRz3)RnK>6oc}tgfS(m%+AB-8_`1*quZLn_s|?yl8%YS#;ZEI{hSQ``)Q7U&lk|x zc-6OU#~0v#Z;E3_GcQA7<24>h$Bb{HId*hgGc-0{wUTs9_#T>LM>j7+qoY+GNym)t zq4{+*^D;C#UgfdW&-fmiV|_C(L1W`p9!tlJZ=yJMH1jePHeTbAbjL zeKS8nqoY+mOZ}AYq50MK@)I;VUggtNPx&62V|6dzUt{A{K280UZ=yKX_wo}IHeTbi z)KB>)nqz%4FF|ADRUS+IjPId2);IGLG&)-4v(!)N9-3c$FF!$}<5fOQ^_1_SIac@b z{WUgT<RUgfdW&-fmiV|_C(L1W`p9!tlJ zZ=yJMH1jePHeTbAbjDM>8)&VdFI(Nym(DqB(YSTQf8^UbT{RO!yv} zV@EeHL!+Zr9!bZH?xFd0H1jevI$q_m)X(@Hnqz%4FF|ADRUS*njBlbib~N)c6gFPt zk#x-XCW>Q6GcQA7tI&8X^|Oj5if4T@FF_%z(0D8zvx+XUXGb$HBPOd*cqAROiY|(0 zN4GUYA*;}=Bps8ACW>cAH!nkB<24>h$Bb{HICeDiG8DE7jmJ_yt7xKl);IGK6tW79 z$I>yY=n{K&H1jfIvI>Pq(lM*(qIh<6TQd~03e8H=F{x;xcy@I2G88smD zM>8)&VdFI(Nym(DqB(YSTQf8^UbT{RO!yv}V@J3846UoKqKWlnP08|UnYxOl`}21_ zSyQrnTBfdI>3;gxqxrsHPyb6>mEW(oev_LC`~7Z`6)j6g|I7X3FP2aFA~zbA?x%k} zn(zDdZvNJ0?f2`=U*zu2e!siySk2PW|8l?oyBz-pPYU+8drErq#?t-tzdf4o`}N-Z z&3$&hU+?oJ-u-01-#do=7E4F}%l-aWZU387UVip(_h-}5e|z-5jqle}{srtc?DyN} zynA8k=)XPs-^ROdwBP!Ez5Ta|Uw*BxNB`USe!ciFY_Ir!z5TaQUfA!aB;IIPI{IJk z_rGd4zsptovwyokn~wh5qyKGuzux-yeluae-%YZjW$Ea@J^J5<@{mxyhQ4o8)Nb?HtRxJ^N?x zYeH_4<+1xYmUY{@=WlC5ZjvV&9daz|_UKKV*M!`JU>ZGgH1qc8Oq^%`?44j5J+eG@ ze{?3!);)hCm`0C0k#!io^JnXwzvYRnL!QVwSoiGBdgpIho_Rk{WF2Pz?A^L&Z&{jo zKg%=k&;Hr_?4P@5Y3BVL&AL7NXYXr5Zj$A(`#F|%+q&m(YeH_4CmJ1cEbI2@O`O++ z+=O5nJ#sYj_UKHUXaDS-U>ZHLJa&I{CeGG9e~-czJ4|-PyWWiGW6Zvuo63c4aX#c z{Ef%R(06`AO6>GCv`GZ}y@zG!yWNu#8-4w362bn)!%}qJ-jEVIUHxnhLH@?WQuN*4 zu;zFA`q`vB`5Onz(06;oO6>GC9Fqw0Hy$HH-}wzGvD4SkCK2rS9+si+c27!d^!2kz z1p6BgOVM?ELrUy)^|Lty`5O;Q(RX{pn&0W`XOr^eZyYQ`-|Y=+ey6XWP0EwMbFdU$ zw|A`ht*(AHC(r%P!BX_y-qB{a`uf?VEcd&8%g}dwN1Na3Yd9w5x!*aA41MQ!tog0J zhBhfr{>H&F^xfXD=6Cw~*`z%AI|obAb$iE}-|FgTbMoBp94tlO?Hz4)tFNC;%5uNk zw+wx^ceMGfzJ_B`p8K7{$k2Cw$C}^jYiN`5zj3e(eYZEP z#7rkenU#^^fk0e1pB>*W$3%z zlM)+!{cIA!{>H;nblu*N5<6Y}Yz{&G#=}zd-QKX~cl!F-q&)c>2g}fRd&5fX^ferl z2=X@`BSYW$4JonH*KkZC$lrL344vmUtofbJhGSNq{Efp%(0P8tO6+uYe6k4gHy#f` z=jjb8vD4Xc%p%zDJw}4g^F1lC(b;g!BG}(}j0}C}H>AW)U&AqpAb;aAGIXBbu;zC< z8;)6d@;446LFf4mE3wnr@yQ~{-*`L(ou@aX#7<|&F^gco_ZSH}&-bLnMrXq@i(r4_ zF*5X>-;feJeGSJXg8Yrg$k2CwLrU!QHMB_t`@M%{=)2vM5*vN}Y!bo#_j=eqxf|Cc z$6>d>RQ7wEZr^Wp`a1TF`&Q)tf81Xx`&&BQzTfKfb?n>E{8oSb#c|obx!adY|9;dTOkUvh?>jOIPTrky6i$l~I=d9%tza zJvCD5nXxj;(%<7OU7@E&NW~_^nZr54c_Rp^33MEa|sF|@&O;mJonyOIJRE?Uc=+w+ur=+QxI89ZkiHc6mjCD$ys)^H7g_@}7 z)XZ3?q^X)XO;@Oyu};s7m2sM?h_iHsni=c#%vc$xsfsvDSE!k>PS1>$ahj@#vvh@; zsp$07NU5Z$nkY+uR}&STo*F5YG*uI2>F;WyqSI3&rIMy~vJ)y!CTlNxE`G+hy8>F;`GtkhE@Wt^oeqAdMg&y1CNYNU*_ zbVZb)JS=g8fl{}{XI&$dDl}T1PmPo} zsgX9yx_OVX^!J<8NUNtt$|&jPJ<8JGZ&D+zo*F5mq?`9BOMkyfjkJ1dtc!Wr>P1hP1UHGu})1?ba9%hP|{S5nyKj2%#Tj-tk8V&yMI=6 zi9Nepe)3|n3dJYA`)5TL#k0HTKk^i^3eC4|#}}yh&BdM_&Ag15tU}?Dbj&KcD4reN z)(nNLLbH-|Oe&fvo*mu1426x?cqAP&zKP=4(ag(G*eWz0OZ}{(iQ-w`%u7(nDl{HT z$E>1D?Ag)G%ZSM;6dp;(tfGtJ+0kvyP{=AYD@n(sqKV?!(apz5n<$PQ&AbeSjn{Z29W%a( z;@Hv5%TU-VG#*R+tfGnHS>Mb{P{=AY9!tlpqD$=A(ag(;$tn~cNyn_Bi{jbQZOu@~ zDl{ue$E2c(;@Q#7%TU;OjYrZkMc0P}q2l&r(0-n<$R;z5E1)twQ6|R8K3K zD4x~5e1C4O`SI8 zi{e?|%u7(nDl{HT{j8#i;#uF!OHjxvG#*RGtfEWo+0o3)h{-Ax9!bZnqD#Lg{iWyz zKb!Ao)BXAT&*uBtbU*#;(R{zwzufQF%l*E8_P^bqO-KLj{qa}scR6o=zux>s?(XdO zyUUK%EFJx~NB`T1zsH^8*ZO+&Z}br=WjWhbvsXF9oB^0#JXp1IhJ)h%QNq<3AxGa zpS$N+*6ke4y1gdkCTl`&lB1cob1duj?4P}_3Ass@$L{A?)@|#azpV+mNuJ0$z%*N{@Ht;$U0iH7mM5|fd7{z5dgpJWH*wArSqFk?^sw&P+vrT3vpn-Y z!8Cfz{<-_;Oq_Ex>o&nOdaMb#$>^Ow=UCS5Jdt%+6LJ&lp1tK**6l3MyuT*oCbNI` zo}-z!vo!Pm?4P~Q{@Ht$$L?oo=6&m)zs>&Hd!EQTWO?kq_0Hd{d;XRuvJQEo(ZPD> zZ=*MH&J$S&g6Z+F-uc_(%$)K>)`6jVT&#Qc_V_ZVEYG~pP(3cQfA0SHGN&BPy3J5M zE^9(=@;DQx9Lu^*FpVB-LT)m8=g&EobvsXF9oB^0#JcBiIhuJp&tn~C|Lonm=Wkga zyPxN=4%R(?v+ns@p2#}niAD$OoxhFV#5qr79SEk!!+Pg$k27=16Ilm_>f>U)^S6&L z^)pXo9Vo7kk9E)9KCaZyEYG}8aeaJd|J?oKO8v~ytlJdV$7fB*O+LQN&m7CT%}_lq zYeH`FI1{HF%eqZ4jUH=4ZZdik=N!$vO%RP9vw!wJdK2d?kKHGTMi1+rzm49+IZtFA z2&TuwdgpJCGjqxlSqFyd<6^z@w~sINGf!k4D6Wr>b09vlLyoj%Hp)S3jHMk#x)~Mc=KX+nUkW&nB%T9TUsYckAfpW%M;1lRT1+ z*~rj$?r7#^^fk0e9!veqGW6Z*n|TR+{cMuQ(lN6XUAK;AUPf0xo8ysm%q&IUt)ttT z(bvx=tt1^2%g}f0=(c9`^|MJUNzcSmblrNottnmoY|hHjGqDtXx1M%$N?$*lG;{Ra zEkoa}r`?*;*KkZ)IeI1|L*Kcl+nUnX&?c=U9TUsYckAf3X7u&5Nh?Xu#8PzKdb+JC zUHxp%%F#2i6n(dzc5_N!Kbtgj^xQ2&->s+Jn$p*BOjUAMojaO&8GQ|HlE+d%vkZN=`et52 zUq74Vv2@HVMc1vPnU~Sk&*pd}9WzVOckAf3X7u&5N&l<8x67F(HS8$)6y@tK1^`?diIDl0m1rMRm6ZIKYRqn{-TOhrUzCZc#_yhGSD?(lHwy`o23h zi#qx?oSPz-`q}KzcdFkkYUtZWt7EgMqjSfxD{`ryjRu|H^_xWvog0o_kxR#Hbm;r;*evSk z+i+}(Ogd(xL+5wLZhJ@PhGW-m(lHqgI=?%1i#j@Ye7ho(j@fI_`PH#m)X}-)*cG|d z&qjmJ@A}Q6hRzMguE?chHahfucWf4Q^ldmcMJ64y(V_3VW3#BEZ^OAMa;cxq4t=Nk z&7y|B{pO~~=zs45woA!Kpq~*+dJJ_$eg*>sIBaO5iSz`zLJ+`pl ztmjK(El1khL4S=cps@PFqX?!=ou zM9y&~Jq`5VwTC;?rZ10koJns3{WbTHC))Jkk&ZL#X`uhkE#!$deR-ti$a)*-ud#*w zrcECnYdO-M2LA8ZLVok650SK-Sx*D~cWxn1wCTem9cR|l!2g|lxD#*s5IM({^fb_a z*B@`|r-A=F_i!iP^dWMNGwI>;f9D?Z#Jm2FNIK4} zD&yb(+qm&}uKJb74)(js`1kL{_|K{8R~|dq@1ODe3$VZJOM4sWueXE#qD>zj?fLSa z2LA8c!G7_k50Uj8X-@KxUtmjL68|bgMgZ`pTA0F-b@}36z@7q9s(WWntHhp++1O4?h z@PBF3hsT>f#HWG(`!vvhdDDkTo4&lKf&Tk8&|kFa%cD&n-rGQbJq`3<+Ov;G{}K_e zW1Fa#p8fXFe*yZpk9fL`lZ=|_nd7IvrJW<5>f+?0UV7&Esc&iLh^M+Zxu}<)Ifm+6 z-g+mhE>3Q5f}+gtgGOS8mqh5OUy`BD&c8++u&G*Tum!5rZr@y3~Bc5*KB%@|}=J=^^Y3GQi zx;VM0m!3I(>RZ}5;;AlPE^4M{o}u=dc8++OiI<9c>6vG!y{4Tbo@U~uqF#RHIcl$Y z>z$~Xc&WXWpLmAaYuf+?~R(|66 zsc(7fh^X2)xu}<(IezL}+BxE>E>14$rDu+x`j&Q%d8&<*i<{Z0TB9M;;Al9F6yOcj-UFLc8++ei<66b>6zoFzNMXG zo@(Rd;%0X0_^EGM!`e^og<#&;bx*KSUUS>qE;bMaEAZgTD!YOhJ(6H7DkQm1Zm?ip&YN#7Go zGjUU=c5?1ID(^|(6HD=MGpBBH?m8;(N#7Go@o+PzZg%ckD(_k2n5KBRnYfvqx{k_w z);Ojq9&RRXW~Z*B_MUZ)X_|?biksP~XQ;hqjboZ-;-%tdcIp{wuUX?0O>^;5r*3lY z8EUUd-xEtS@lvO5a_$*wuSwq%OEY0oCkKAc_$n0M{X|r3n9Rw5pEJG+g?B#@6&ogV zGT`TouSVhRPejd!NSzG$Ibo|Yc=r=g^C40v1Ab1}Y7E}}MAUqU)XAV?PWWn!3j3I; z`4FkNzhX+*YK#i|n5p>?skpylO4w?Q3j3I;`7o*2pkm7SDwGQQn5oz>nYh1V%J?dj z3j3I;*f5#6zhcVxYLp89n5p>?skpylO4w?Q3j3I;`4FkNzhX+*YK#i|n5p>?skpyp zO89Dw8vlr@`4Fk7y=F$(YK$8Hh^qMzsi?hXM%ZeM8vlr@`7o)dzh=hxDwG=kh^p8y znW(*H#`r3f8vlr@*f5!>y=KPvYLuD{BC6&?q@wnk8DXn2YWyRr=0l{S_L>=Ct1)W) zBdX>@q@wn!8R4rjsy5iUnh%lMyQ?OIt;VR@VCQN+L~8G@nh>@cqiTbltNAdgy}fF} z_$rjD4R)?#!({gEstMz(P^vcAxrz;w*}JPIjITzi+F|EvK16Eou9^_G8l!51ovZl} zslB^uLfC4IsttCo=0l|R?z#!#t1;?!Sjn0Xky@MU&V;STsM}#BYd%D3ZLT{Lwi=^u zhn1}PFsZe>?#%cql)4>OvSPzz*5&}d?MycCkC2KxJYHhAN z6Sf+oZikhu`4FkKx$aEZYK*!aRJQ21UqjHOxt@$vixw-Ph_$rjjEoQc2!(`^($`j+OP%5{W*@_L5nR_cwjITne z++t-bK1^n9t~)cn3Z-s`m8{q>nYFp@%=jvlx*b-sV#8$C=E^f;tWYYqnAwUAlbL%f zPmHfZsoY{_D>h7K?yWpAz6zysi8@ykPRSgI`?~>iOL*oC#`R9gE&RmYfVuT8a{^;} zr{orX;%>m)`j$C?G0iBsho8tChp-G z#I2vY^DT1%<$CAj9)99(z})(lIe{^~Q*sMGaW`OYeaoD{nBFP51(CQrFt>zf#$ZhE zl+1y+zZ)>OglEQJOz)J;fw{jsFt?29ox+&jDR%?r_HMx3GNyM5V|u6D4Vc@z0d>o` z-YJypjdM3(Zg2h69ba9%z?PS z^;36*XU3ph@0`qnxW5}Pw}fZLU`+3n%z?PS8!)$oXU1Sm@084exxYIww~XnX!kFGE zcLV13Zou3!rgsWsdZ*kCnA^Jnb<4QkDU|Dtb2ngaZ~fFAV|u4ht~buzfVsW(b9apE zox<6kNp}P0_T0|hFs643XL}~y4Vc?=J9opFW)#l%Ov)UH`*S;YLwIHk&h|{o9EkgK zJ9k5PW(>~uM#?OR`&&PCM|fro%Js&{9Ekf{KXpfVW(>;p#>pI*`W0cSqUY zIGpX7bnB77Ek-Z*yy>h@+p-BGSL4&{2| z-1@1zTR(M2x!yRG>y2~kr|xe3-W_GQb3Cu}>%EDZoIBkc%5delUg!5~6L&s$dN-Wm zO7gsJuh%B(eCzb?D8rrOd7WSHP1NMv>D^F%JIC|7z23W+$*t47WBekD=XHJ^buqJZ zr+dTr?G)GR{C@AECg)D?hVt7vp4aX5-o;FAo!%Yew^KZ;+v~lHn%r8QJIZh8I9B(! zdlxggw>r0sUqo@NZf~P5W_D}!?ijzF;#u8Z?_Jd7*6Q3*emlppy1m_-sL8F>yQ2(O zj%Rgyy*6>@TdQ-&8LlM9>i%|ZqR#hL=aw?uIgZus?cPL9Zmr%OWw>%YtJ~|fi8|jp zy*tWq=XhS{*LxE+Id{4@l;O&8z0U8~ChmOh^lmuAmE?KdUaw8m`PS*(QHCqWv%0-r zo4E6>)w$ygSCV6Of4eqO=Xo_n~{t<$^X99NR(b$-3}aOZQUd&4=-B-iWwe(oVp=T7g2bevh9*X{M( z!<}xO-W}(-l02{T>$QhFpF6!9&Tu7pUbok46Lr3IdUurJ&hfm?ulFWua_)3*D8rTG zdY#{|P2Bn1>D_RKE6MXZzg~N|^SRT#;T&g@>veuV_mHP^r*}g-&MeRC_ImE&PPb0) zj&oc|p4a*H+QXgCo$d|iIFnqj^ZU7nJDnTd8_sbhxn`&LYY%rmHTrYTaVGg@C&JuA zo=%PKoOGO7uG#th+{2yDjqVNSxRPA6)BClDJD(ceIcK<%T(k50wTU{P8{HeqaOb#Y zr}ujkH90l-k{ z?_JE~-00pgei6krJH3y(nAxe(pEG_t#Wy<<_AY92YINt6-_CK(&hPgwW^!(HZy3Lw z;(Gmc@AqB!Nym1)^-lclq5lH=ug6bx8z;$o??lH>eY>~v6V=7Z@zy)h@l)UKt^7oF zadNzMM05<*w^1)WQ(c@K?HutOKlN?YOV3mnCr3L+JjYLe8#U82-RAow?HuvGx6@xn zz4T1C`94WIN4)Rt^p{aDKhu4_Px96~@q0V{<=)Cqber#!y!B4}-cEnHxAGI+#!2$t zJJIn|-|nsaM0Ig;y!B3W{M5I5D?d?Pyd3Yn6Fo!i_1?-)G!rkyTkk~AP=oLq(X%5?nH zw`*s`S#@!872Ye;@l)TfofT)*#miNguT0NSd%bp6oHY|KRpGrdJwxsF+F5bdOuSTu z_s;Yjwby$qKhaFQ6mPu~JwxsF-pWrj6EDSE??lf~eZBYc6V=7Z@zy)h@l)UKt^7oF zadN!%PIUa#w|i^FM742p72Ye;@l)TfofT)*#mQB8uS~~JeY9hLWUW6eqNa5FXLGu3re-p`FSC&k0f)R?bS z*HU@Ec2=Af4>wccy)s=#<^9@OaaKIsOojK#bRD(#Yiq?>Gx1Ut-Ye5H)LyTh6=%)F zOI3KUOwUkzy>`}|H5V^cV?I+oL+$n4SaZ@$yi|?(O!W-4*K=ddNi$(mH5)`!##f<4 z?KLyShRM|UM^wgFp+xO9GsT9<)c8kK##f_6{WUYqhe*}Wj7IT`SC#fHi7?k6JSt5Hr4{9N-PQoQ?# zNZ4wOlL0^1e25h9ej*aK8slWZ&ov(+#k-%0gs;XpJJ2_p50RqH&pg6bW1Jo68_kDE z(dK6!VXHCD4)l%Y!=z~UGmr6AC}#)yMzLWswE3CG_$rjM1AU{|Fd5qX%wv2t%GrXx z(R_#$ZGPqvwi@H?K;LLSM2a>)^9Wmwadx0@G#?^Go8NeZug17puy>jdkz&1XG{RP6 z+$`8T&4);_-ZvUyt1)gC?49Prq*(JCjqz0|Hw*Sov0*Z-_l?H*DwLZAd#Bhi8P@wo zV|+Er&4azue25h5eWMY!8slcc-f2EWiuJzH2wRPDvtaKuA0owi-)V%e#<+Wsx0(-; zBCYQ%!d7G4J;+(j|Nbfs~@l_~y5As&AVKSukoyGVnl)DFc ztJp9Z()!L~d=<*wgS}OJm<;QEqcOe;?jGc=Vnby}?>mb!&N+7v@-}y&5~TH= zMH%Ouy9arjyHE+z`p%+^bI#p^z0GZ?1nYgHQN}svW})8YEgcEfwl>$XY2H!0qe{;` zI})mGZLVX}yrXtUm7aTcBvjkVT*sbyN9Be(JNN8J=)RT7j!pB9$_;gP?%9#heJhh4 zn`RxA`|0e=vgcm+txWdpnzdAJsIxQ6o_pQ5GTF0h)>65l&dw}5?sePBWXGm?N9Be( zJNN8J=)RT7j!pB9$_;gP?%9#hee04Pd*&T=`+Y*rJv$Pg7}sSxHqATg_WOjKdv+u~ zF|NyYY?^n}?WdD-%Z`NZTbbvVKpc zp>ji=t(ewlbl=Kk{hmrg<%T+2F|E(&zIDm^EtQ74{XQWpruCUmjO()Xdnyfe`+Y)I zOzShB7}sU%_f#6{_S4CVXMINZtxVSMsWen>sIwK*`i$;dnXKPaX{g*#XDg=F8Qr!r zS-qvkuiQ{)Yp&HP-M2DXy`{#l+)!t0uGJ~sw=!A1rNXb=PiHHp^%>o_GFiW;(ongf z&Q?t8GrDhOvVKpcp>ji&t$5aFRNLBI{hmrg?T#v4F|E(2wzawXJ(Y&q9aXwwTAfi{ zYjf3GYW&(ARl4R{olN>R2ph`)aZ(5eMWPwPSx+JG*oY?@fFkhjOJRMs^3#-sNPcJE2i}s z&9ydFzopVpyQ4~1OzShMZEdc8Po<%DN0qLa)@M}P+Fbpfc|+}nDn0k?NT{~8xsFZq zj@lhndhXegP;F~-9h>GIwL5C`+_EE~xmKq-HqAS#x77H#=ifh}{StiYuleQpJv8EP z+m2sA`)5I?IyQ?s=sh%EWYRIy?D$m2ZhHs6heq5@IwqPMpX%5x>fkp~h>=OhOmX8g z9h*fR{3aSPa;cwbZhWeKv#5dJLnB5m9W%|2PIYV+b8Zox1o@#D6mGDe5zx&y@TIFBkm?06U~iJb?g>(@S7;a$fRSYxbc~e&7ux| z6O9fkp~h>=OhOmX8g9h*fR{3aSPa;cwbZhWeKv#5dJLnB5m9W%|2PIYV+b6r1GD8!DZkM`8n1q@xIweyQ?X6;l;1-mR__(_H8(yL+tg3_O%!7NUU7rs#%E%e`YFGOMy%f~ zYS7&HROC`WKrUm-;EciAJp7ENal)_*CRlKjZh%i1nLA4VoLDid;Ho{3Z&qW3#A3 zapN6r1GD8!DH6OR*{HII%XBSD9n!C_726Y zBD0%xOe!`}m>s)C9f}*DiA*|X{3Z&qW3#A3ajVEgF7>mDO%!JRW>JG;R*{KZI%XBS zs+A9g0~+W;f}WRBWO!J9djY6gNH-nRLwfO%!6sW>JUY z#%Cgvjv2p+M(o&a@6g=%)Nax-;rGyp9lO>({ziVsu_v@{{$Y*Ev_xr3nPqTLPf4RT^UC#dr-V*FT-CNT)cdXr?{_h^`_xts} z`FHMl^ZWIlU*fyJ?C`HzPn-V=zsU<|2Dq+jn1!rzux(`$#4EyzaIVH#_!k5{|!4Qe!t%Nw^44`-%n}$ zB*WU#|KuqyO9Z{d)Vq@1IQA-|v%VCCl2;|L)QMZ79FU zCz*fNuSfsW{r!5M{JXYhf4|=Pi+pna{d%ALT6>oL{dT@LVeRPu-TVDtwg1ombpKiZ z?$Q5k{C>SZ>AwxnWqYtLnWz4G1j-r!AKpZUG# zvcI0`{%LRUCce>3@NCxCGuuC{eD~}d%>>tHem}GQ(@ghIzR^tZjb_43cTY3hKe;~Z zd*5g#tbFf0v;C82v%mI@X2Ra!O=h}#@?7@UuFv{@Z}287-#dFQ`)ki;f4w(&lfA*4 zcsA>6&t-qT^4;^^;7weg`Mu||znaf*zR^hVT=v%^oA}-vyb0knGCZ60^++bZ zSH62DoJNN0Gru3n#P>}1PlVIR@Qr4|$o9Wywtw=CW`b`t6K1-5n%Vxz^;zHhMl)gM zyXTqio?M&tz3a2SU-|BN<$Gt>W_|D3?5|h8d)^zoiR&}J_gwbZGu=P!4c^2z8VR1u z{(58+-+O~MA)H2rXS2Q@$;9`{ch7{=$Z&n;_amA3p6ULHa2grD(MTA{#P`T1z6qz1 zK{$Jcf zo4yUlCc^w1#^})Z{f#N{rfSj!uKng7 z!u%V?Y|(f6#?^n*x8L04n}0)?9r{k+xDs#rHXNG>^KTfVL*Mr|ro@}R4d*7p{d30b z(0BUWlz7p%-`qsFf5Vt9x=!Di5^uWpn|lcJZy2*h-{~7y|4rY1bCYlW4PkcZJALEo zzvWd7#;e)zjO6p^=&ve`R3mcW{1AhH?IDhzWwGV-~2nmY|(Z4&eea_wcp(1dw)lm zE&5L1*}AX#_M4kr@9*Bv4t=NZZ2eb#8;(uB_jiQRq3`=USN~PthI5l|{taPv=sSJm z>c8pRZ*KCjVayg?r*BM&H(mS9J%sr;jM<{^^o^_krft9O@#S3jM1U<`x{sPP3MMVmv8GJf)4Ai@MVNoXcnvzg zzA+`NeMuX1p&rOLJog0o_g!?y)(V_4A8&l#<--croVg3zcbm;s3#*}!| zx8dAGxPQ)=9r{k6n-VYj_M4js_rI1g@8g}gO+1hJ_0MYlJ#MGpZ*}{1<~#1UBLDx# z{j-|?Dcw%L-|F`3%y)n0Z}sp0a$fVk-sxwh|9-#U>i1i{{|@NCMV5S-{#Vwe-}^2v zL2u2HFVp|Zy7YVBGx`*lG9tW zq|&BlvM&8zZB%l4YnD{n)J)d($-UZ{=RRr8vU!`D$eKR6S3C3EC#_jFZ&MRl(C4S35>I>g z--=xnX7`@|>r>1sGQVv*eu0XAayheOv#29Ct4Ks99kYsE6lTY6dxv6Hk=ac;CKa0~ z%#Pin4#kbnL?#_GeiMb*v02oixK(5#m-<=7CJM8Dv#3EatH?wy9kYsEa%RV7QAci8 zk%&w>W)-_A%#Pjm4#lh@vzv5GDmGD=9lJ#xiW{GaOgd)#CJM1*v#3LH<1>*-$Bf@Z zBX;bzcW7>WYB%Ya@Ox;)j@_aT&5lk*CLJ?+4~^HcS=6D~@u|qAe#Y;i5$iXL8ZKqpla3j`iAL<$ZSTnh#k8{9hx1TicC6Y^d1_o zW3#A3v*S~dOZ|-BLnGF27By&Yd@6G3nDLt^#E#9P4#kbnL?#_GeiMb*v02oixK(5# zm-<=7CJM8Dv#3EatH?wy9kYsEa%RV7QAci8k%&w>W)-_A%#Pjm4#lh@vzv5GDmGD= z9lJ#xiW{GaOgd)#CJM1*v#3LHtH?wy^|Oji6lVQqQG;Suk%?S7W)-{S%#O{Xj@+yw z5t($%Dt1wr9lPxvidjWwH|dyEY@#qbc8fX`H$D@YbjsSDmGD=^_#^FiW{GaUFxU&CJM2Bued>RtH{JQ)zgYi6lV2aF<&vO z$iz1F(~4bkX8m4qLvB`)h+XQZ6}u?R`pu#S#jGL|xzx`pHc^=Mo5c-^8=r|?>ZklB z3bB5#xIuBN$iz0)(~35*Z}-o(qyOFe_kX>Amh0=^uebgppPkv?@3Zba z&Dzob?$Q5kuqyO9Z{d#}We;c05{`#NwPj}znoBXqWz2E<`c>lb;f7jO6 zp3DAv<-6y-!JD`~^Lx)_e?8Ov)861se50A**{rW;wtrgr?%6k*39irlerEfpneLx_ zqnY3v&4iimo@Taxa(&kKzR^rr`QCYE`zOz4f9)I1guTI=%yjqUx$Li9pY{FT;7wM( zclKQN*PhM(dT;P1dxJOeY}VJF%l>-hyXU>Zo47vnd(UNmJ=6Wu-r!AqqnY5@tgmOb ze_HwO**BUAuFw2_X8Wg^?w@?4ncy3ZgqiK1MmF*78_fj5X=KcF_cW4;Z`WsiPdJT? zmG7NLGV$%%?5_!@k+C;;lacLzdoKHH-)JW64c=s?yC=_Of9?9L@An38vhv-tXS2R` zZPxcI-#xE<_w4%2?_Hbq{Y>{yE8ji)Ml-?nncvTB|1{J6lW#N=e4~*tv;EV^Ccb^6 znLs$bjG679UNZCL8_fj9>ZQzd_w;g^FV|;%&se>bmG7NjF7xHt?5`QCm$El_lb1|< zc`o~F!f9me4c=sA``@0+{@OR1344P#nd$z?vsqvJJ~LtEyXTqipIo2$z3(#Hf(#nhCzqNSN9FX=D@MzR^q|oL9|ccYdhUU(aTLO?f?>y}_A0RO+we z61}Co9!}KZ%pNZD*D;CSGFC4o>TqT+nfP){qPMeCl<1hv7Ja9V-S&>Y{pP0Kq+>EW z^qo3(i#qx?9GfDOj@jtY_ua8s)X}%$+!VRg&t`|dQ~hR9L*IULQ{>Vyn=QId9h*fR zUHi>Fkx9pFw&*)`?6!CG?Kd~=CLNR6q3_hOTh!6F;n)`|=cdS| zel|Pwo$5D>8v6E|newvm=-O}YiA*|Xvqj&jW4FDdZ@;-|H|dzn4t=MN z-S&>Y{pP0Kq-Qc)be(#3+grN!n|pSTp2=*{ck0=0ZRy)@ZdyHh&a*?`sb{;rrEkNr zY4_-vj1GO@J-h8KeH+eAyGh4ncIZ2G?6!CG?Kd~=COwncqU+SN+uqW(-`ul%^h{=p zzEjV3YfImLbJOb4bDkagPCeW0Eqxn~O}j_WWOV5J?%8c`>DzE_+D$qpvqRshW4FDd zZ@;-|H|dzn4t=MN-J*`Z4acU)q+>QZ^nG`17IpM(I5$Nu^|RTb?^M58)X=xz+!VQV z%w~(OQ^#geN7sIHPh`?Dn=SfI9lPxvef!N#yGh4ncIZ2G>=t$OZ8$bXCLOcUq3^q6 zv#6tQ!?`JPsh`abeW&`(qK3Zx=BCJ{V>VlKojNv)I=c3odm@vL*=*5w>ey}X=-Y2@ z+D$qpvqRshW4EZIZ^N-EGU=F&4t?Jpn?)Uc8;(tpNylt-==|>3ZSUyZaO~PmIwqq* z=Xb|$QAg*FZ&zf}F?$U(7EB*6}fcGMu)!dj?JQu zz75Bw$fRR7I&^+_?6!AwZa8-BCLNQ}p!2(9x2U6Y$G0mo>6pC+onIZBMID_xj$M&U z{cJSo{I1_DYUtc>?224EW}`#jcgJQ?N8g5HQ)JRH8y)(-J2s0t`Zk=KBA5Ev?9g|r z-z;kA+iz})T>j3pc{}n{^{bGMGpow@KmVG0xYOUy_}~Ana^`;3l~iT?`}bn}rK^7B zv4j2o8Na^(`^&zxw}Jk8JLoUk^x@H-FYjsK|GpjU7jOCySp9Zi4)$y8V82< zd93G1dpp>#v4j0)JzpAYInv$^`fF?uI3> z&MoALHhp=d<;Z#)=&!Mb{H8r$9%(tV-VXL_ZXr*s=SU+hN7mTEevd8eH|zP*Sj&<2 zcF_IvIjPps!lBOPbf+d+TLJ=}@*e0iMXOnMvWuepaj(WVcNbevgF1OIpK;ZD5i zL*yJ+($hfyU3<7QZTj*!$C>mt&|h;8d7@1p9_cuoPrU2@h@|7p zsxtolzl|Gz=c-?M>|npEjDP=LjQ^afe&w-){r(xhzX1EozO=W2{(3v;FWU6s(Vj2w zY2g399qbox`Vd*qk@hsuf5#5?n>KxUtmjL68|bgMgZ-jCUmoi@(%ugCYwTdZS=>Dg}&{THBr`-rF8ILWA)o;iN%TiQ9|sV+_~>ZNCnpZb<|j(DnzlZ$%!nPaHF z<*j$3>f+?~R(|66sc(7fov6AvxxJO2IDYzD-g_tNHs2@rR(|rmo&J)y-if-+_sPAL zpL}npzvQhWqVDs3GU}yg-`nXgY3GQi+kBsldg1f zx88}GiI>`2`H5$!z2>cVqGsZy_Evu48LF>&@13Z+IJv!*pE!Q%Ti$vnsxD4$Z{;VB zpZb=!j)f+?0UV7&Esc&iLn5Wt}xwx5~I)3U~);OlAE>13P zW~Yvy`j$10X{w8ti@VvWXQ;hqjboZ-;-%tdcIp{wuUX@mrkQxDxS5`Mj@oP5IpS$1 zUMlLPXP%+RZ}5 z=BYMLE^cP0j-UFLHI8Yji<66+*{S2FzGaPLn(E@^;%;{88EUUtL%x&q4t{eJ+U+sFLmlB=boYVn)E%f zG!r*SpJzrShINj%kXAn~9s*sq3h` zXN_Z;;^AiEW_Id2YVTR+n5LO{skoV)dWPC-);OkVCSEFTW~ZK^_L?<5(KHt?b?PSP zo}u=d^gXdO6EAh@Cg+}^_L}rPu{0AVb#mb6jIToB-A_cthRK`^_&MXNPH6J2%GT`Tgt;XQpPejd!NSzES=7g`tsIZTj znh%kR`zxk|t;VRZkC~bek&62(ri87=sIZTjnh%qT4JxLLuR^J?kC}=MlZpE)ri`yb zsj!ckiVc&A`zxl5uSTixkC~bek&62(ri87=sIZTjnh%kR`zxk|t;VRZkC~bek&63k zri8D?sPT`enh%kR+G}Qnt;VSFkEogtk&4=DW`wQAsPT`enh%qT`fFy4uR^KukEn_b zlZo1EW{j^wsqv4fiVc&A+G}QvuSTiaAfjqML@H{pnGv=cqsBj?YCc3NYOk3Qwi=_x zKcZ?rL@H{pni0MlqiTbltN9SAy}N2c*lLWb4R)^PL!|cZstIAMF{(D$xtb4?+S{up zjITne+F<7@HcV#ku9`5u3Z-g;ovYX|nZ3Je!uV>GsvUN&=0l|R?y3o4t1+rJ*twby zk=nbfCWNiVsM=uXYCc42@2;B=z8a%$hn1}P5UI7f?o8NfjJh3GvgSji*5&}d?LaEzfB`Y>eW^JxJGrkI?Zikhu*f5#3x$eyPYLvPyRULPknh%j$o9oVmt;VR^VI^xmL~3oWJQKbeqjHOxt@#kCxwrB}*lLW*EoQdn zL!{>3$`fI$F)Fv1*_scNnwu+6jITne++t=cHcV#jtvoTl3Z-(3nXT9`nYp*}#P}+d z$}Lv5;=^Rt=DIWEt5E88SjmbFlUbYV&Wx`@soP;CD>h7KZLT~s#tNl!i=X4VYWsGAA&m zcS>&IC+-H!t#6qV7}JcBd-#dWfw=cAa{}R+F>(t(kvS0ezGY4zJTpdaK_oH@;-2u# z7=&lW$Q+3K%z?NkJTnI2nK3d4;y!a=?g`V3!I<7DnFDcuH(+iF&y2yC-YJ;_aep_U zZVA(jLAl;InFDcu>!yk zPRSgI`?~>iOL%4s#`I3f9GLsN19Qum-YJafopLu|Ztn)nEn|A8Fs66P-GI5h8&J25 z>zzWm-Z*yy=JwW4-7%(j3gvp^+zpu9TR(Njm}V5p_QuH^i2GYVbw_w+49fM!$sCCL zTR(M2cxDXB_0GvGi2J(%b4z$;494_M$sCCLy8&}ccxDX7^iIhfnESf}bIX|CDU9iz zayMXZ?*_~*V|u4BrgzHSfVsUJP`8ZhokF?ZIClf)_SR3`F{XD4<$B}X4Vc?oKX=Et z-YJ~znRGW`ZqM!94P$zzaJFaC-GI40w{tg)X-46E&!o(OxIedZH-u-#;B3#N%z?N+ zw{tgyXU5=cZ=}qExWDyNcZ6rgpj>a9%z?PS^;36*XU3phZ=B45xxX1ucZ}(sLb={J zcLV13)=%9rrgsYEdgI&;sN0(Xbw|10IF##+bL*$>ZvE69<$B{#t~buDpSrvCb9a>O zjl7Bya-bi-`=JwW4-7%(j3gvp^+zpu9TR(NjnBFOr>y2|apl)vl)E(t|<4~?Q z&aI!iyY*9dlvev=HgV^3r+32{ zt|ZUv_Ihoi&bLnQjxyXip4a*H-b78#o!$-Qw{twN+v~lHncO-%fG8&hPgwYI5%MZYaN<<9Xd)?_JE~*6H0bemljpy1m}JsL8F>xug7cj$?Iy zyLT~@d#iKH_(c@Q>h?D3VrI8i?~d`?DW28s_1;BIZmrH8<+pPjtJ~YXiJIJ6y*tWq z<#<-N*J~4ZzO_1coZ(7xtnP2uChB}|b#5uco#R;D-tJA*$!(J-8#KH&T%DqUgy_q4|hIyx;LETOme->@8=%!bnf(SNXMDwdEH*m zJ>2Qm>D_UTE6MXZzg~N|^SRTz;S5)j=XHC%Hc{tWr*}sg?i|nS{CaPqCg)D~hB90^ zuGjhf+QgmDo!$*+xRN}t^Xs*TJD)q<8_sbixnAe@a}RkscX~IZoU9A}bmb|TC@ z&DvlC%$;?Ac=cg`8EB-iZxer=-8=SKI2GTb??+3Ee>L`_bO?ws=5Ij-6H z{och)&W-L3;}=m}v(x*ii9`RyFn?EHT3VkYNC_lEJ? zDX!OF_kQ1npLA@;Tkpi*9{Mk^|9bpHw{eoZ_fB;D)VF&pKT%zr9B;i79Y6K$-pWr@ z7bnMCM?}X^eH-=CGu6e((asUi@l)SMz4T0VadNbC#B==gw^1`a(`~*_(#{d@dprGQ z)JxBFo9~mfbHw}JPJbEo@-yA%`y_9@6Ti39U+%5^M7Q}q$y@Kl@9p%Ldn-TDZJZ?U zy%QZj_3hruPgEBt$6N12$4`B`xAGI!#mn*DJJB=LUhl2^L^JVHy!B4>47JyLD?iap zycBO85j{ukb<|7GG!rjHJ4ZavPeY>~v6V=7Z@zy)h@l)UKtrZj1#>rK9uS~~J zeYeY>|-OjH{uSK+-f9Y6K$+F5Z{U7TEn_sVqq)VFJA#aVUnauwz)(=*gw zubmZV&BRMpc&|*)P@+zfBM6J1B`{ocz@G!rkyTkk~AP_1al;)?B<)jrmOV47JyD zW6eo3@lrMBGu1QHUeAp+C(Xo7)ws`8*HL*tH`bgK4>wa|K2u#s<^9}Pb5cCqOpWW` zC2TcD)Lt{we27$ye@rEOHAdWDG1YvCRE2%aBy2TC++Q)(e27$qeas|mHAdWDG1YvS zRE2-cWPBA$++Q(OY?w@ieavKh6-wM+F;#4sOoe^SWPCMBY)~=Pe27$qeas|mHAdWD zG1YvCRE2%aBy2TC++Q)(e27$q{fSBVYK)TsKi7PS6z_f_61E!SWWdigA0oxOpNNF5 z#yA=9bIpfI@%ASoBJS8SLJ?|vdOz8dA^z|S=wBE`F( zh=i@iI2rJB&4)_FdWK1_;sKl2!0g>rVFZxkCQLz|y@jITmDJJ2_Z4U?hG&pgIgqns`1 z8_kDE(dK6!VXHCD4)l%YL!@Z)Gmo&<7-t9iM)M(3wE2xk_-c%s1$(FY5GmICMk8!B z#?6Ag(|m{&>wTjUwi@GR!QN>;Oo}zX(HLKaa+&tJj&4);_-ZvUyt1)gC?49OAq*(78jj+`iHw*So^C42K_nk)gYK*%Fd8_#l zDbo7RB5XCr-GjWF}*5*1k%{yv$ROz{AM?$r&%ysOUcT{euvvbdmgzj6J?ASE#sN7Iz=bjx2-M2E? zv1!&(xu4F?EPL*C-^ygqu31avhB`a5?77!{E0aCDW-XN)>g>$2<6gI|Om=LVcT{eu zvvbdmgzj6J?ASE#sN7Iz=bjx2-M22;v1i^ z6XUvU$EJBl-F`Ybx9mvhzLm+2P4kY*4Rvg?RJKB3!IChPZ9 z8Y(x`*@|g>M)$2u*6*n_RBouV71R2R?pv3v-%@F)+wT*yVp^a1#JDb7zo*hrx8EmZ z#k4;2iE&-Feov*LZal{K^e=w&q%$(tRtF)mv)($_;h4=31T7eJhjITPpm@{dBfsTA$HTJcdKBN0qChPZ98Y(wb*@|aQw!nN<;OQ8ecK3&uFgIsro&YhUzUfzG7OR z(Ohd&^;;?pwL7YG#k4-7+Scak_f#5ccU0+$X?;eutey}X;P=po zyGh4HbK_GTyG0%RCJHe!>6j^Qe5PZwsDs}`BStRuGtG@p)o&Ix@Ox;)$fbU!x$&v` z&Ef`r6NMPN)K3*RK2yI}+`w<55o4R`spiI~s`rZd{2m%HwyB?Lc66$KuegEUL*vCR z^;6A`Pt|W0HSl|A#K@(7rn&K{`px17eiMZlyVOq=H$GFpSKPpFq7h@8>Z#_&r>ggg z`TQOlF}A6nYIbz0ey_NJ-b3TXF7;E*j!)HZ7B%pDXvD~+ex|wcsrt>L27V8X7`b%J zG&?%gv02nX@1gM`la85Y$EP}W+dKF@G~#a3G11)kRL5>n2fv9zj7&OaiW{Hl*evSc zH_?cZOZ`l9<5TsUMGgEO8ZmO|m}z!&s$;XLgWf~qMJ63H&5loX?6!CCduYVnq+_DF z@u`m8q7Hr&g&3K1%oI01)3I69!Ed4wBbWM_=EkS$H;WqhJv3tE(lOKQ=v2pMQ3t(; z#*0ikW||$H>ewvmp!ZODkxTtdwWD+On?()u7Ah}t>6od0Y_4OosDs@?r9~zkGu4mI zb?mlx&|9dy-K1lp+R?d=-J%Y94~-X@bj&n6I@PgR)Isl|@*NkrT=q*%U zE*bg-VM|I%cXLo$J_b@1VC(dAmu+M75)H9lJ#x^d1^7GU=FUc66#^ lv#5jKL*qpz9W%|2Pj&3Jckp{?#NDK0qPg*@j@|Z-{{vgBG%o-E literal 0 HcmV?d00001 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..51de56a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,42 @@ +pub mod note; +pub mod piece; +pub mod wav; + +use std::fs::File; +use std::io::{self, BufWriter}; + +use note::notes::*; +use piece::{NoteEvent, Piece}; + +#[rustfmt::skip] +pub fn main() -> io::Result<()> { + let mut piece = Piece::new(120); + let mut time = 0.0; + + for _ in 0..4 { + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(D4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; + piece.notes.push(NoteEvent::new(E3, time, 0.5)); time += 0.5; + } + + piece.notes.push(NoteEvent::new(FS3, time, 0.5)); + + let file = File::create("output.wav")?; + let mut writer = BufWriter::new(file); + piece.generate_wav(&mut writer)?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6e1f9f4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +fn main() { + if let Err(e) = wav::main() { + eprintln!("error: {}", e); + } +} diff --git a/src/note.rs b/src/note.rs new file mode 100644 index 0000000..70071bd --- /dev/null +++ b/src/note.rs @@ -0,0 +1,102 @@ +#[derive(Copy, Clone)] +pub enum Key { + A, + B, + C, + D, + E, + F, + G, +} + +impl Key { + pub fn semitones(self) -> i32 { + match self { + Key::A => 0, + Key::B => 2, + Key::C => 3, + Key::D => 5, + Key::E => 7, + Key::F => 8, + Key::G => 10, + } + } +} + +#[derive(Copy, Clone)] +pub enum Modifier { + Flat, + Natural, + Sharp, +} + +impl Modifier { + pub fn semitones(self) -> i32 { + match self { + Modifier::Flat => -1, + Modifier::Natural => 0, + Modifier::Sharp => 1, + } + } +} + +#[derive(Copy, Clone)] +pub struct Note { + pub key: Key, + pub modifier: Modifier, + pub octave: i32, +} + +impl Note { + pub const fn new(key: Key, modifier: Modifier, octave: i32) -> Note { + Note { + key, + modifier, + octave, + } + } +} + +impl Note { + pub fn freq(self) -> f64 { + let semitones = self.key.semitones() + self.modifier.semitones(); + let n = semitones + ((self.octave as i32 - 4) * 12); + 440.0 * 2f64.powf(n as f64 / 12.0) + } +} + +#[rustfmt::skip] +pub mod notes { + use crate::note::{Key, Modifier, Note}; + use paste::paste; + + macro_rules! notes_octave { + ($oct:expr) => { + paste! { + pub const []: Note = Note::new(Key::C, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::C, Modifier::Sharp, $oct); + pub const []: Note = Note::new(Key::D, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::D, Modifier::Sharp, $oct); + pub const []: Note = Note::new(Key::E, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::F, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::F, Modifier::Sharp, $oct); + pub const []: Note = Note::new(Key::G, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::G, Modifier::Sharp, $oct); + pub const []: Note = Note::new(Key::A, Modifier::Natural, $oct); + pub const []: Note = Note::new(Key::A, Modifier::Sharp, $oct); + pub const []: Note = Note::new(Key::B, Modifier::Natural, $oct); + } + }; + } + + notes_octave!(0); + notes_octave!(1); + notes_octave!(2); + notes_octave!(3); + notes_octave!(4); + notes_octave!(5); + notes_octave!(6); + notes_octave!(7); + notes_octave!(8); + notes_octave!(9); +} diff --git a/src/piece.rs b/src/piece.rs new file mode 100644 index 0000000..7de789d --- /dev/null +++ b/src/piece.rs @@ -0,0 +1,74 @@ +use std::io::{self, Write}; + +use crate::note::*; +use crate::wav; + +pub struct NoteEvent { + pub note: Note, + pub start: f64, + pub duration: f64, +} + +impl NoteEvent { + pub fn new(note: Note, start: f64, duration: f64) -> NoteEvent { + NoteEvent { + note, + start, + duration, + } + } +} + +pub struct Piece { + pub notes: Vec, + pub bpm: u32, +} + +impl Piece { + pub fn new(bpm: u32) -> Piece { + Piece { bpm, notes: vec![] } + } + + pub fn generate_wav(&self, writer: &mut W) -> io::Result<()> { + let amplitude = 0.1; + let seconds_per_beat = 60.0 / self.bpm as f64; + let params = wav::Params::new(); + + let max_beat = self + .notes + .iter() + .map(|x| x.start + x.duration) + .max_by(|x, y| x.total_cmp(y)) + .unwrap_or(0.0); + + let total_samples = + (max_beat * seconds_per_beat * params.sampling_rate as f64).ceil() as u32; + + let data_size = total_samples * params.channels as u32 * params.bytes_per_sample as u32; + let total_size = 44 + data_size; + + wav::write_header(total_size, ¶ms, writer)?; + + writer.write_all(b"data")?; + writer.write_all(&data_size.to_le_bytes())?; + + for i in 0..total_samples { + let t = i as f64 / params.sampling_rate as f64; + let mut value: f64 = 0.0; + for event in &self.notes { + let start_time = event.start * seconds_per_beat; + let end_time = start_time + event.duration * seconds_per_beat; + if t >= start_time && t < end_time { + let freq = event.note.freq(); + let sin = (std::f64::consts::TAU * freq * (t - start_time)).sin(); + value += if sin >= 0.0 { 1.0 } else { -1.0 }; + } + } + + let sample = (value * (i16::MAX as f64) * amplitude).round() as i16; + writer.write_all(&sample.to_le_bytes())?; + } + + Ok(()) + } +} diff --git a/src/wav.rs b/src/wav.rs new file mode 100644 index 0000000..b79f8f0 --- /dev/null +++ b/src/wav.rs @@ -0,0 +1,59 @@ +use std::io::{self, Write}; + +pub struct Params { + pub sampling_rate: u32, + pub channels: u16, + pub bytes_per_sample: u16, +} + +impl Params { + pub fn new() -> Params { + Params { + sampling_rate: 44100, + channels: 1, + bytes_per_sample: 2, + } + } +} + +pub fn write_header(total_size: u32, p: &Params, writer: &mut W) -> io::Result<()> { + // Premier bloc : déclaration du fichier + // FileTypeBlocID (4 octets) : Constante « RIFF » (0x52,0x49,0x46,0x46) + writer.write_all(b"RIFF")?; + + // FileSize (4 octets) : Taille du fichier moins 8 octets + writer.write_all(&(total_size - 8).to_le_bytes())?; + + // FileSize (4 octets) : Taille du fichier moins 8 octets + writer.write_all(b"WAVE")?; + + // Deuxième bloc : format audio + // FormatBlocID (4 octets) : Identifiant « fmt␣ » (0x66,0x6D,0x74,0x20) + writer.write_all(b"fmt ")?; + + // BlocSize (4 octets) : Nombre d'octets du bloc moins 8 octets, soit ici 16 octets (0x10) + writer.write_all(&16_u32.to_le_bytes())?; + + // AudioFormat (2 octets) : Format du stockage dans le fichier (1: PCM entier, 3: PCM flottant, 65534: WAVE_FORMAT_EXTENSIBLE) + writer.write_all(&1_u16.to_le_bytes())?; + + // NbrCanaux (2 octets) : Nombre de canaux (de 1 à 6, cf. ci-dessous) + writer.write_all(&p.channels.to_le_bytes())?; + + // Frequence (4 octets) : Fréquence d'échantillonnage (en hertz) [Valeurs standardisées : 11 025, 22 050, 44 100 et éventuellement 48 000 et 96 000] + writer.write_all(&p.sampling_rate.to_le_bytes())?; + + // BytePerSec (4 octets) : Nombre d'octets à lire par seconde (c.-à-d., Frequence * BytePerBloc). + let byte_per_sec = p.sampling_rate * p.channels as u32 * p.bytes_per_sample as u32; + writer.write_all(&byte_per_sec.to_le_bytes())?; + + // BytePerBloc (2 octets) : Nombre d'octets par bloc d'échantillonnage (c.-à-d., tous canaux confondus : NbrCanaux * BitsPerSample/8). + let byte_per_block = p.channels * p.bytes_per_sample; + writer.write_all(&byte_per_block.to_le_bytes())?; + + // BitsPerSample (2 octets) : Nombre de bits utilisés pour le codage de chaque échantillon (8, 16, 24) + let bits_per_sample = p.bytes_per_sample * 8; + writer.write_all(&bits_per_sample.to_le_bytes())?; + + Ok(()) +}