From 7634cbeb224a064868084639aa557be0f521743e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 8 Aug 2018 18:26:05 +0200 Subject: [PATCH] Updated mini_al Corrected issue with sound playing (pop sound at the end) --- release/include/raylib.h | 8 +- release/libs/win32/mingw32/libraylib.a | Bin 1237446 -> 1239504 bytes src/external/mini_al.c | 6 +- src/external/mini_al.h | 3762 +++++++++++++++++------- 4 files changed, 2780 insertions(+), 996 deletions(-) diff --git a/release/include/raylib.h b/release/include/raylib.h index a8e399da1..8d1389f73 100644 --- a/release/include/raylib.h +++ b/release/include/raylib.h @@ -71,6 +71,8 @@ #ifndef RAYLIB_H #define RAYLIB_H +#include // Required for: va_list - Only used by TraceLogCallback + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) @@ -727,6 +729,9 @@ typedef enum { HMD_SONY_PSVR } VrDeviceType; +// Callbacks to be implemented by users +typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -779,7 +784,7 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a r RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) -// Timing-related functions +// timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI int GetFPS(void); // Returns current FPS RLAPI float GetFrameTime(void); // Returns time in seconds for last frame drawn @@ -796,6 +801,7 @@ RLAPI Color Fade(Color color, float alpha); // Color fade- RLAPI void ShowLogo(void); // Activate raylib logo at startup (can be done with flags) RLAPI void SetConfigFlags(unsigned char flags); // Setup window configuration flags (view FLAGS) RLAPI void SetTraceLog(unsigned char types); // Enable trace log message types (bit flags based) +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set a trace log callback to enable custom logging bypassing raylib's one RLAPI void TraceLog(int logType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (saved a .png) RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) diff --git a/release/libs/win32/mingw32/libraylib.a b/release/libs/win32/mingw32/libraylib.a index c27c819e30eda1abd3ba8a2beaff6bb0cb6757d9..5c64af95a4bf922fee3ff4c59270c4ffa913e530 100644 GIT binary patch delta 213193 zcmeFa34BdgzsJ4zJ|_br0}(+&a1d$;Vyq!ZTSK%lr>HSevmjb+wZ{-DMGZ;2XrrZ# zp=c>8w6q~HB!aYt=8!}TB~;Zs6?uQ_zs~-P-sidR?fdDy&*%MontNq`*LVHbu-D#u z?X~wg&9g7PTh7sLi>lb0wg_$3xLLEHppYQ>Q}TCEP_tGcO`4M*Qd-m8ZJKuB#NYeR z{&!dS7wfbC?R)gsI$-~Mhx4yBj<)T8^YQ$v9r)+?|7TCYe~$lOJ!Ajg_jh>;FGF0{`6q|JwcEW$^Z3oIMe4zb}|LmH_Yw!IB|JnbOjiYV*|H9sv-91wK zpZ>G|PXqs#;_Uz9p8oH2!2UlUs{fS^*#E+@k{$4Bx9D&EXaBn^{EPM3|JJ?vi;bgg z``9W#9JsPUuXw85N&nh+f`c*LZk<6@k8@Z?Fu2M-@Q zd}K_?;1YIH%+P36VsgnMC2FSN$uV)LAa+XJ;K@TpL5fcq7iu+e#;HKn9f~SZ+2Gir zlPAZGoHSv|t0PNRTry>EIDl#*Q8A9POd@@zH~akDV|X1OKGl zgxHwzgNGu=JSP7>OZD%+EsXA#%pWl+2Ho$1-S03ZdeoMPl(SaRZ;4Mu@%tG*WdDKlDK2D=ax@5f996BFyB3~%4R=N zCElm8>e4vuUTQ?V&rtWoAJQ8nzL+tAcK@6CKIOLT%IKyiUN}%wOk3jfnRv9s$9>D$ z%y1oh=$aULu%n1u>scwWW%e@sE1%flbeY6A_f}54nw=%P?VtGTp)v|;9UkvGc=Gt@ zQ4>t5uJ@#&!($>QyxM8#*s;Tg4j+?v z_3{UmhfkOk({Mr~`refM)wo$x`j%Cb#v*5(r(fkJt(u0kq%T?xDIKA^Y4RsK-J-3t zez@M#uUw99X!UhXv;JXF)}eBu&|b>VEzCpH9vX)5N-R@7G4x7s>FM1yt&NwVxuzzL zxKhK|IWY0PE43}1_Zp)5aF^<(r!~{GxqA(*>h>)KR|;RX`{sw+0_KI={IRv+UpW4? zarCx%IKuHSEqC{+Ha16k?k|pkN+CNmFWOe$OgpkUIJ4lq0YzO-GdMP^cLN=9cz{wbZofKNRdPxar7pMEN^1 z9gzW!$RJ0g&9NtUkE1a6mLn?IkyVhHo8~AixPknr`i{sDM`WlYs?Dqn-}D8qX}xA$ z8JQn%i^mww8)%EouODxl56&NG>+DFw@8DC@Z2k_H-rY1UFTxg=A7Cqz!+mP3%|9Vm zr1sGR?OFM~ZDEc=2UEen1{pPi8?)ft1PN*yk^ zWO_6bEE~eGRKd9QvN{f#Sr}z!;R1Vxj4ODEVH+UFu9t_Sm$xj2-5($aFU$-Dw={Ww zCv)&_$%6jqO|Jk)uOLS+Id~Zuyc>=Ia_|m1vUvCg)OYj>ar6px48ZV>9FV?XYi&)# zXHyGy){%oU>xu`O@WsjFjcow_)pzX9J%=W(7!a#tcV4v3W~Pwa25EU@Rjx}E#u$Tk z%REUU-RbO5x$=x5D%ginC{%~s1H%wG#M^OD9%*0fLZmzioPlV78!Omt7737N37sOl zbK6GB7Nde?S4x!PF6|F%gA=U1=+RI2vwEbDSd==ppPpJvZzgPS>%(=Q)VXi#4|U_% zYhsRFf89Rd!{Xx4Z8-UXOg7-;moizElLutdi<9{>sd4h2OkPhi z$HPAu$+Jk>Y3#Sxlj)*;cB zY|fp3+C%==p+Lj(-u83_8`LgjQL{lhF3y5I4!LI`=mN$fooS z%%?-9*&-cbR!5jG)jLG)&Ot|*4SVG62n!_{D`hs!5OhlJbR>iwVUnlI%-w(-8Wwj) zm_PXyRLBt~Z?{2?u$Ck@NhwDw)>+dc6(uW5Gb<{jiaZ=)0aPYOmDy>#M!voytc~<` zTcON2EW02x4&f+oP%)~L*N|NxJEq}Oa~>SrL_-Q>+zJ#y<_0>L*vNCDAQNV+Eey97 ze;EiJDeK5ANOuOJ-LW=r2;89Uwjl)>xY0&gow2klh;^hx1W~E6wpgUGl5wX-=`vad zc;_CX?GP2{i@X)I1AsJDC3=S1$k>hJNti}v?-)z}$84-YpFhn@guo}uN zIEZ8jl6mQNkhd&4))twU={O{VgB;Wi+6Q?$&~C=c(tA<%F#heu^%RBm2ufjyP(`5` zwh3Vcht!Y;%P~TxJaT#RC>1&z!l=?1)OTds3ofG)=a_^5<+xCzXOI{oyAvu~G24)q z_PJ%j8Mx`xE>?{*GJMfW`hsl`+Kek!BvwY1rK!ywjO#N5gHqo#R=uM$CV(|8;n&Z8dwA=n?iVNVf1~ zrjsYNoWd~I$?1eHb()%nVA`WOOZ72C#${8;5Fk$Joi=$&+Z|y%*G*S3VZju(NT$>z zhiPw5?qRYkq}_wUo3PW4Fgd*;kp_$7C^xxBWSTPTgK|`k+)&PR%4tJ0{w7Fy{mE^? z5m{bl%JPM(l-$V2FPLk}$b*V20JC4gUTnkgTg`%W>C<&-PI@7-u$=VdWhAG*6|#0Y z^~qVyob&9qkUZQ~Xu^rbLEIx}DLFs6n;nyvcN&WFWQRjTbDx^@2Kk=#GY;Xr`QZj)eGHT>s(x1v^Yj| z*{-)J-4cme86KA+V^ZJSu770FH>B3usduxK?%Y9T3>hTe{6Wt*c5h96IZgl2%W$+! zy?#VL=xTVhP5u6ap5tM(u9v##oPN_q-z4HM>AO0Iq8}PP;A&c>JNrA~DNd%tgz zYDpQQ$8UO7QFvJ&X>9|o+#TuO{#BnWW?j{*h!zgrQ_=BR zwUM}sFihfR!f=Ue2s=wmCXA4{m@u-cSh}yan`%e;#9-@F1b6D6F#Re-{_B z-7WRwyZSn#R83dJoi1uqQ@r><&$5Rg@Kij_nF$x=eG_|+=QG;SyRTCX3gsKHEChuk z_UVvnF_YFyKV&qLjJpuAr=&OF`e z+yhUiv2dPmi$>|(c)4Jf%ZvH8kuoh;wlXnRC1kS12#FaF^=Isu;^lzR5Txa{rB_+W zHk-1G&^+eXG;DI^Iwu%f4QOtbBMWz38BlN^Ir4=rs*wYLcaPpcIVeDPIw6skBY<BpeMl2Rm9DF*eEG!mo_QDwf794j2fdew5hoqcfb^?X~z z2c?Vvdx_Hk?-)xAsyRLdnfcBau6bqCa6aTG)4d-{IXa|i7*#oTxtV{;z^ho?`wR1v z=t9oD{B+%pOk@19?vu7Z)^kOOQRLJ=3Ida@mf1D zMT$Qa!-&W;5rBQTr-=E~oJo#pQ=M}-LIHj?sa+yZ+O zQ-1@aBv+9C(YnJ$HyWjtaSQ>vD`D z@+4=AWfmhuzD)^yNNGm}t&H@J3RdUVU2f63hul{1rr*BuH|q13+bl<5fZA3FqFfvO z4wk=Bu0FQTd%=@aMIKW5T1c5H=GpWwZU>@|)o7=Oq$XL7Zw$NDirZ+ity=(H<;JP-(avjRzPFxyN4H;%yrt z5`1kV5Gk)CslI|bt&$Fx4c#=YWagL1%)9T%6#Lwbidc6SxEn1xcK&m%2mfoWS^r(F znc@d`qpE&DR@bZ&UgpXQFMua?g z*vMmnP4Jra-iD5q@&+H{8U0pju8(og1!v@s6^(sXWAOvAEWo&+-xJdVjmn0s{J#BLD9v8krn=FsW~x_HS=#lG*xJlkJ9H;5)&23d zJlwJ653taQ2^Wz#|HKzL5y$+PDs<&ZW<1JXc%Dw`kJ7Q$RCk#La!B)QVB9rb6x1;M ztQfzC;@f&gWh^^9TVOL*)NNtZMk=y}QP0SG5|$@6)-)hcNntB1ydu zOR~wAevtCr9yT_)DRS5g!SqT=R#0E%(fU)S<%$rOGA#^~ZMo=0LT|jrKsxDh!)FBIFDn-@aNXf!LU!bf2=nu3iv;yT4 z5!M|jD+78I^$bvF2WrLW#Tvph*a+0aL}+cJWsh(~IWpzL7P4jkI+H+uLnS2HTB=PO zwUKL0$og`VOs{^6)>SE4{U*`Au~FHjJ%XY|UTvegK2l6?Y*g*y-2Jh#q#V8|1&g16QMSda+7T9jZKm9R0G#!16B38Vo+mLK3`T|tK%XWE^D2pAer9yARRB$N%RH= zn>d+DwoS#RsDI^>-3}3DgHh`SQ9syd*?SWLN({+XRmlpqNs${|KXSN|ZEKZGQk$i6 zlWa@Irf4Ic)Z|6!9Cp<}j%*-M+zK|TbUh}6OAdXmsw6{g4p25~%U0>_YO_;rlIc!c zhEDC0{Tn;B`3NTCo&RnRIJaAF3gg zZmsH?Mv88&>b*KrbZd>LOAx7@s#19oB9*36@=8Q1L!}<7rH7o4aD!bfpRMC8I9fmUKaX8=ChO|7!Mfj&VcG@muLQ|x@kXk968_v7MOf4F+hJ|$n@?A2#Ia`Boy3~MCu`*TLew)5uOa&Q1qmn=4K zr`i`i;XuMv_k=g(t))nL8I|yY2gDUw{|nwTGIpj0bToQft9QQmxOfqLj|HFP1ecO; z0;jm$OkcDBufQ6oU8;LgEPmejy3Z9Xu(of)P<2iFH_Y)=;61TT+RH2s(q;%fBIdr`T#OP z!n3o{K~E5oosG?g-?mh17vm*ecM-k28q+ZC{@B%Mr3Z*I-HZU=AnZ8p4sMwAh?|ys zUbO9I)M=I>KPjdUwgbmv2`3*SV&ygqPsV|9lJL+nRprR>Km`T6#o}(pCl#Hey5FTU zYR9;1YE(s=2xF9=ev(~LRK30rrgiP3Q&y;%+@YV}-kF`Y6iqfzSSD%{+PTt?%C zaqM&Az3xVRznQOM-c(;?lHUgkb>Qk4bU-V0R=%6y%QopENJmswxoN;3BsHOuch z6&;ir@-c+oIHnGWH2ieq-E=XzhtWt6OZ~iup&NLsv#J-)rP$Qly^J8;IQN67*vFU? zG~kEF#rc6bxIkuPEX5cONH6LPl~=2ft{I!y)yH^N4-xnK7%hwg--{-FjZeKIaI&7d zxHvyIKeuS)uE)ju zT~?rK{J)kTC>y!79J$!rqHepy#(svs(Ri20>}Ldf^`@FmUCMX7Rt2A=ei&s`E+56t zYtG917&sSOYJ(^v#3=Xiw`Q+b%KDKZCiFKt)_4J4?clGU&T3nPEZu%o^+BFAMRmRv zNBbMyjoclnH3k?t7VO8(K}IcXDh)QKVEM3cuyI%g=A_b zMRJOu2k@#bz@m7@$Xtv-oCXT0WvUtbUV3Sxp%IJd~zc|VW@%2V4^0TVE z>&~}dryAslXGRqexq{cx zqVrhe8QGC3V~v+9te_Y*=u0KbK~Ft5)_BEQyXC6K#q%O;5ee540&EEnih^Z2+!hJf z8!ywb*uGZeyDD|WL?c8uCVh~)bdu3pH)=0VJvQ0c>|)$V7IWV)wi&IHMcA80m~ro8 zG4oC1JLBMd(f%#tQ=@W1YT;YP0^RtootW^p@q)2rY3la3jSl$aOFWovOfVYxh%qya zb?&{Y$`8QCri;c$Jt}z2m*-_wu*j(BmSSKQe8~{q78%3vB%iu*k#W=A*yk!zmKp2i zW7|hY(`xjtb|4;iGO={>$BS6J%%(+Dp2jWRC7`Hf z3_8(kro86m7cS)cgrev>kHz$4qk^Sqz+KV)W8)dOf@nnDpCKlGZ1gdHx+4yJjPqlO zcqiGYB)pRiYlZkbkBjBKeb$vWMZq$-(~QT(;`9#Pzj9Gu>}QGEUqGm%2we{rA>EB? zR`k4#tf`7od^`2yWTQxTi}JxeV0O>c_dYQy>FyQ0Wy;Gtb^WJCfbN#zDU;u)9$sOb zb=9L%zg}bP(v1gxsj*)eTXg-oRNH!Et8OeRFHUa2@jj0`iP!7`T^w0q)D_=+iHUcd z$o|skDe8V@3~uForHz-*@`H>BtfsK=qD?k^&!nU=zbbMEk%i*xFOBkb>BVe`82V)1 z{9?xySz8u;HgSu>#l;ndcV&FgQHbvz<9#vm@sKUg7x}z>X`cA~E2E_ghOc;$cxfZv zWx9xnjYbW~E^l?d%l$rF+6KB7-erb4DE!ac3_f)UloxOJMz!c%V$*AOZY4V-o;t`8P z-X^?km?JD-8?SiC=ay6W@-k){e=+uJif^|aw~ z++vi{-8N%^;5BY%Mgh8sRDskzgL6E$`k<;A+4Mi*aMBgT(j7^sJqQ{m#uPSh2& z%cx$a#9)YsT}DvXL-@AzQ{j1!@NMU0_0mGlmnCYVj{r)f>8lUwYwnm=#q=$B)|nT!YNH0*=bZ0N#7f_>?L~k zr&i4mM&#FhZsC;0$9w-xpXJ9CCHhEneu*@Hbx}TC(uX7}Q@p>$a5sBja9@=F!Khbi z$#rv868?h`?P+M~V%;GlxPH!UUA_Y;!VIacoQrv1u4&T7{R>$*Ry;mr^fa^wqUT}U z4#r#)_QOU4zcv=kX?NQ^b@dnR5ql0B;g%&1HOF`rCG0_f)pq;#?c$;@H>EZ>VtD8+ zs%^p=$|(~!78ifJe%-ot3HR=vopsjTYql;|hj|ED8IOL=PVwO624iuXsLFqS&Wjr9S(k@vPx@cEgjp z`hxLaGqjl(Ck`JsDyDvZ6gM1vF4y?D(J18Jx+j5S!Iv(`HPhDGa`=i)L41b`Mn3t` zj&@y_y6Cv^qFe%;K4Gj;n_6PS&&Djzx@(`*w0uo!+gxLWp#rLly(f*S_%27gQ^rhL zc6T1Fe_G_@J!h==FdwVOY2re@u~y$H78YPBvq4NgZNwYb%ZdA^@qY64kHxG)W3L`1 zx|}iE=$6zuXN=4J{gPaCEzDKdu3CLwYY;FEw~&mHn${D;t#$BppJjCNjI~7Qp6(se zJ+XfBg+7lM+h|R@3PxFPK^TKDBrF?_vI8F1DVfc|nBQX8P z!#yiSC(n@AQD+b^8a_kx*k!3{#7z;e?XuKp9RC{5Too2%Ecdj^;_~hh`nsk?DerFd zR=v)HPZF6Zu5T5;?XuJ{Y;Oy{?=3Y9+dHE5_vp_pG4y-nW{BC}BR4)?d?j;3=ZK$Z z`>wbnqmo4650;9q;R%|yB0y}K?Os{*{=pK}@%$=H3zKc)NA}|wZfdsGnr6sR>g8!O ziaf1Q+7p*)(@v_qrB=&P&Pzj{?R!o8S>?^OT5>2a6y@@eXH6Gb zX_n?)GtxD!5gbN4D8|zpzdOn5HQC+Ja+-Dv`Ix=wN4m~%)(lM>BX1AEn@JaCWEg__(D}>t$hYOj0V)6SHTt=oi zyW29U^7$-$J}7fKd3qQN)nT7r9z%FgjNfDNGkmkfoIRErm6NhHEl9@o^7QVE_QHY3 z5Vtj3?Ac>^-Z+0qluozcvx~!`p4{3Fixr99m0hQy!{JU&21g$j@5%i5!(yG>rW_Vo za(nEsxSVdOXG9+nei<_Vh-fai=Z}cKavPc>UX$CD99gfQ*pOjyH!^a>whYTx9g~i# zoyAU2=j4tr!gY`WkI!T1N4gZat#CNi=_dfcx5I@V6RY>4^0;GSAGS5D$1zB_?cxMU zu~>fdkVjCyU-GSpgLPKZM!JYao87C3>iaC;8F3dy!9JX9@ppyieoOO8;rH-`C#qi4 zu32lwMH_&yqKSD z@imTliqEofbgf?EaJHqZJ>E;#j+8=6=?S+{hJC0Ivcc{vwC`C0`};z4-@fz9DaG(oUe)lSxMIp$sO|(gAZGJh&G2Y zIyQW8^hCaF+^Q_TJ&ft@R+ZF(!(irqPu-(ABy=-QT2ls@#M<+O{no34E%zW&Kl*`DKOO!Pe9*2y!+ zE$ZKFqdn6of24E!=QyL6=LQOR)iZ;(ul&DkyL)E4J%&i?I+Rwgr%|GK-f=M z{MvRXV+{1PrtjOYP67E}D_Uc4$)ceAk*7rtoG%<^B>IWO@AGh>G2*8amY(+WV|5L) zmz4*d<c;4R;K#1sae)k@!~#=Q~pz&b9P(O^VmGiyk7Va+!*vt;15Ma{OXl>n9_-dj=WL ztJ~tDdf2nY;vI*@uU+Vpr-y7O^1|WFLnrqx2QIoqTqxXmxVRGcM=%>e@MR9cUaoCaUIJYS+lf*R`Rlwh*I-xI+@&0WXKXKD_mY&T9hc!r*-2%oAS@ zS#LNS+(=cg2X3ac%pVJ%1iwsP-B_hrEV_8Lz*4Dd=q=}6o9_6p(({5mCpMu#^eyp8 z0nUk(TOzB#Qlp;jwl2S;ML)6wz9HBvILt2eBg+NDMc)>Fr!Dndjv+5m{Jz4wq8NDE zGQ^0xE51K%8DZG&iQ0vjN2BkFu7$Gxd*Zc1OB2I(U#u&%RPEUN{*!aJk8=QSHO45y za89u#%!5d66zfV9Fyg=d7iw{TA}f8;>lIjXdASPgh(BXM>|@6frXH7F;ykb71)+ zoqH%QL^yNCk}d%57F@Ut!jE(zaN)%ihtGnXG8{45CIom!pWmD1TIcDQeQf6Y23(u z0hiP|Ts^MZ4N#O7M@1Vv+H^TgJICNboN}|qn)LHHlM-Xi66@nM?PZGdnqtyXrst=c zZs8kL!j6Ww0Q*ys6ChRis>()g?>9BAFXhf)diE`|xhd1k5=n2HE)Aq=+4+u10XPBF zZVm#e+@&D9l9yOorkjD|K`QWDcHL%}&V81-@3laxmQZ#tgH)%-g8EVSuYgp2d*jU= z{>=_l@-9eKlQ7$K&F7dd3Z$wT2~t(R&+Zd;-?KZ+?vr=1D4{N-fK(fiT@vte1$Qr3trycUkSZ|-^gHD)VD|yja;D83cZA(p(2I8J*FE4sB9HgX zs%@ZG$c2Jb7g{s52d$;kwm0Y+bzu?*jS~}=1(IJq0UG1v<42mH-15#bM3{qXV4^ksz%rnUibe~Fiv#S7lAe~*S z%CaU%{;?QWtv{f!T*R+(KjP+4;JV3rL)o+O*IdluwH zE}UH#kT}U%pmG#9O*uLL%>b4s`5p%@1o@I%%5FKR0=d=f z)`R@WZDqFuRFT{scKbp8@2iYr;EMR#KRGHircGp2w$UR_J3<@CUzQ7#nvYB7NBYr7{=~75Z={bj9z5d3sjxlKz2hxHOP%-Hvv?W+*EdN zgKXsHIGx>9n+L2#atY`X9kHb#HI=M!2D)mSn7#pBq`2?drGw#dN5GaOT^E`| zT?W*OoG-gdpw{GqK+h7@2C3UhV~%Uh)E?A^;ySbI4r)uT??TLfYL*%R*^Xp12aW=@ zCpU@R>!2`l)7i}ibs+aXyF^e&a>?vgf;y2~3koN)e+5)G!(AM>kLe)jISM?+&H;L! z+*x)PK`)TI#_l$#Gr7m?EQ>U)3po!Ec3sYYKESRdD=P_C4b+WXFuMkz2y)HYJqtQV z=SMiZE+F~Iv#ZvVT|dw->z0;lpJ2Q0$y-aa^*$n{oCl}3b6legsN$g$+4J0?6 z-E7b+c9QS2Oau)gm&|S@XfU~T>^6diklW5~7icKCee4c`hLJnQ&H)-u?(AY*|7wsf zLPnFk#(}p%mFT!XW@lM~`y;v1pz=gsAhkNI3R3meVX6;`p_-eqYYiG9on7n1vNPya z3hcqIFK8sW!R(?zqsWb8HwiSF+*|CXgT|0cVD~;~EIC1r=D%d%IFhS4a2;qoxh?Fr zgC>yM&2AqkmfR6`$3PRw<+D2rnndnbcGs5R`kzelKFi0TIC5@@=3sh&rjV<^t}^H~ za@9ev6Ey^>C3y>`PE3&?wG`>Y&OU-M0i;$X3)u;#WRNV7hb_t+&$-U2RDQGUaXN^U*7Z$Qh)eft5=e;JS;k<8@4qo9w;<+3{qN+x%a-F47% za(CEiA8Ohs1Nws8*Puc4nj#gX=7n7#H4SBh z)V?2QIw^_Dp8Js3e?^e%NnYo`JD?5Z9)Z3jGK4v)lm)4({g|qNzM?oAySku_(%H(AOj*K%0qrfYj79kmH7fwovXUkUD|mIQMO)nV_u{H;>&S&^P3kDJSQ@ zPk`G+pI+Cg!@u)7TUmfTHt_dxdVNEQQk z61glj=PUVVQ`7(lGSvVbpv&$Vb|Iila;@0417(qWfn5aXAh|y5`rCooB!{yc2|7e> zBD>c>hsntjs$21(BjgsaTLQ`<_c6N_pdZP7!R{;2PjHz3Qd#Z<9i_m%?6N?|$Q@;u z3p!5j47&@U6XbqpcMJ40xkv1bW%#-XxiTQ<^RGA1L9!yKfG7~8?g#Z{AU!8HV`>fR zMsc0kbq2|A(7I|p*!2Z= zA{t)BZXKu>xh?FrgL;$OZ92QQ57>v~5mRc%Kz+&OvpWmwNA6d4*FaI^?z4LgdYPQt zN9MSBfclfGz^*cAfI;(LO_sr+ffU%7U31VYkZmP*U-x#bvusc z+;L2kK;Ki`TOaZKHy!c^lJA1jh~|UT9bACa8L)zB6=*lbeZ_7IXb-ua>~@3F$z`!S z0?Hs~&t;hp+Dq~RyI(>3$lU<#C%Ow#>jmRuvs-1Dyg_RDT8Uj%kXpXhVOJl7<*S_k zngZ4OwIxU`9bVwTNTxoZLDYpo?1qB|lN-x!B4`M?H`&=i>e*;6y9FS-dN}%!<;S4m zR5*p*7occzo7ts;)U#0L$y3pkd_nWOIOB zLBq+F1w|8;2Yo=@s>*S7nCgQLP;N7JtwEXOIyoKYzs|rcl07)EFX$k-!R(?z+2qE6 z4iQZRspZ;qj!R&AA9R@F1iNI=5pt`fQ}6%Q0dq)h;lS;nAIa@zw-59axg+e3fsT^P zXLlBKjNGs6u7Qq|yU*@%GOqsRtjT3R;YxM!KdKn{xQ!tO=T zNpgKar-%lC)M9kB8HeXT6Sb+JJPMq_ZVo7)+(LFqpaOEs*?k5&O>RBAO`t+@JJ@{> zIzw(hIhy~nfoDk`=fIPobL7skD*~M-cb(lG&@beQ*;zlqJ0x;tL2BYE2f9eE3cH|B zaQzpNtOI$8r~yc=&f9R{b4*=9mnp6nyC~4F^6X|llz9kU#5CRI4M2IwB;)&Z$UlqQ_}EXeu!Zy4}C1$N==7eNom z^#T1s)E}gtYGOEUEa)NSP6T~QG>vm-fgX`d0I7$XrJTFc&bS)%m;yJl+X^Zsw~O5# z5WZ}H`^G_bIUt>!gIxj0Aa{}76_AD8ZKuQh_W&sW43Mj4Sz!*VJIIBc4?91QE4gay zY#=vs4cIjSxs!XAT^Ohoxi0Kp1eKPb|GH}ZSPlf0As54LG{}S8WOh?QWy#q=_@)GE znGI48YKuVXvRlUV3CN4$*09^K!j8}KNPffeTaXXAban?o<;eZW?gXejxzp^V&SpW>RbYYD1Et^>R0L6yn9 z#I84}3b|L<4Fd&OvHz~x7?!c1Kni?=-8-PFjE53EbFI?LLidgL0h3k5wxt}VNcp!(#xv5N#XAonu6L7;}@US&5H)X0kcch#n_ zd=nHxfwS1n1RI4LeZ&B`R0#8I~cS zw&Yr|YX@pa?ge%cp!VeYu^%UIGI}|to z0)$k3FD)A8H?CL69|Efsk zRc4VoAXTIZNPRn|BS>}dC6MaiAdsps8l<{99;CWD6{Nbl6r^g|3{quxgQ!G1?J!Uk z`5B~&+yto?XvS)jT$#!+l@p6wTg%(4u&B-y%G93eIi_w*J(&724PhG16vy-q(_BUJ z8eYOOnQ1lC2BytS+nG)=HC|)x?Ym4LFs)!(%jCTl=dc>8%0NXmnDC$4IVaf8pe|N< zmcPikuQH8XZ^mr}sqgu9*5a&NPQf5YZZDCCklBsZ2jHonF`s{-yRme8SqY6`dJL5#Ax0seNWr5T;upWa9i=3|f4w70^cdJ0^?cR2fDv<$FB_4v*cX#w0 zvxF~5#mPUes^U7co1cU0-<`U!0aD!svOy=P;U7V&;e4hGpdTprS9W)qkb-91NRCjjj9>P^fuE1rd1&It)^W}2SBPb$3be0 z3dzy@_Z!Q59O&|s8R!jCg#+0IvunmKj9qtjQS4&a#j<<*C(M7UH#1o-X8HuAj`kPq zwz1pI?l8L(>{_ww z!0ts5t_?Z=^45foV0UB3+c< zgBlW@0IB(}2&DGp9!Tv;zjNl7##ezXRN@wB2%Q0q&YLCLgH(wmP;0zF6Lu>hnVroRBG(fS6YD$HYdndt#jxl86r zSOcWWK0`$FUmMPj;OxHaMzD)zH=W&k>^@?*j@>qW2C5-+aP}3Z>rB>PaU-GwQ4XZ8=!)!WunT5t z!qk&#JkwM~a{imiavn%EvW(p)?7m>Pk!c6hai;4`e=xaRF+1%EQf&l)oa?{pEE{oP zD7%jAUSQXg-4dqNOs7C;)%_+Q z)kq6=oj|IM&g`avyzJD-OqNTSJ_4yBTnAF8=qAqH$&?9FV|19^Nst<&U)bFMsqWup zSLSzI|7wuDe>eLd3R3-V#qK$f>VH>u?|{@8C9wMtq#F5{-A0gVV=KE{5Vc{~@>yO2 zsYb4{t8vY2Bp9RyxFu5NR3fn&K<@y0i?z#j@@+7Q1u;-c`TQLRR5Q=`x>PB zzm45VkQ$@Y?0yBQ?q6ePyKZ*B5lD5v4M?@o;X2QMeIQjM{WL}g9oK$Yk6yn{#LKX7XpM%4B1z$JB(WHB*?K@p+~Qrru2bnT9cqWE#&D$25&8p6NZN zMNY!}_aVzqm{v1wVA{;IohgGUi|H`a8Bhhf=U)e@=j2ki&Hu8m639zE|JDWGrG?=nEzCf^PK$~ z(;bj1tlcr=+(D|aKfCH6755CgrX1IaT?EIyZ06dv7?zVvslCH49;7;$#JMXtcQxm3 z)*yPtD^=G=3fTSU3E{=3K7*1P7Olme-SD>79BseNz6aiN?W&bg7C+lO;wICmoF zF1xGQoR1G>?wSA9oeiWK2?5ctiXVGg``Fck&~Nw6CyufY%!PjgrtVDRn7#qI&`EO$ zq@GrO0rjTS>=8)4-)ZoN`6aUq51l! zDo8aq7o_T23R3m0V)r$>45!1Dn#1xgNbQNwBl9@b1`VKnY6?$(sZWf zpkXwjt^uhM4?(KtI*-koTY;S4w(0^@753%qZ$ZPU;Y^TPcNBuut>G?6-4Lo4o40|! zAayet0aCY`w?V2z5=fQU4pKLcYxEb%sS)}2)>V-jAXTIlNEI0ZQbm@6R3mFaszeq@ zl{f=ZC0ul~L|c$5(H*2pOaM^{yEX@?imU^vM$Up%ksF}hw2bt{pA%G#JOffCUIM8S z?}Ahb0a7Kt0jUz%AXVZnNR=oCt(E^P=Xm95(VfSs21p%{Eavj*8+sUpn}$3U^mK&_SJN-8d4qO-5_<+I6$h`zAk3313;=o1CT1w z8>Hgqaoj3)r`cKY=Py-U4<>sO<9?7Dno{`Vbn3q@oD5Pghh{P@W?I3tk?DJ;Lri&0 zzcM{gWXF4oQtoCWiJ(}zbh1E|XnHvgQtOk}rOdcdAayy-0I9b+n?dT$%s!ARaUE2R zYAIFPEMWtw@ofUKtE2rqa4j93X-um@>&Sft8cy9h42mW?0g53i1dSm29i$pAg}=D> zD!B@vkwh&)>!`lxL8{%wcA#o_7t>EnXF%#D?`3wk*){RdwS81!5-6Q$E9fB6L6BI!M*>1xW4OCZ=4b z%3kQI`lPZYP!$OWspei`H;SG7LxifrFF1E2Qvp-3w^?5_(=?`eOco!subM08V-{(| z)Qf2*NX0E?w}IVv>`t)zg`HN;EL#SocC#vob_8$gf$D>S&Y(g%b^|zYj2WoKaoh(S zx0d6!bKGu@JIismInJfLIf9;`6Keiz1XK-o1gReOh0inCwISOgRRm_I)C|zFn%ZDZQWw1??OCWoEzG}9%J zIuJM6{lR4MH^-<9lMhIhsK~AwQ!Sd5-Q=%j_|1D$rDbpIJjZCRbyO`3M4l;RFGRLR_Qy^0#eUQ3PUGY~|)gcK7sj@>s=O}It=)5F++Y%6f&3Q~qiD><|isfdeADFV4984FO zZZgR~=&2eh%T$@E4pS&ohibh38_V)trX;47OkXj5$8><{IMZ3C-QrK_90uE=C#YRuG*sT)%i zQw-B2kaPaCvz*VgjA=E~7N#_&Lrf=`E;8L>YFt}){^6J&AhmoQX$PL7^L83Yojyq* zwX$E%?f}zqrn5}HGd*M~RmZH)kEte8h?6k?wPo3rsUK4`(?q6snC3Bg2Aj=QVG3qy z#?+DNB}sPL_kk=&F}=n#i)j(la;9}msZ1G6KQc9GV9p;SLFx}9&2E79FVoZ821s>G zegLhdZsmd01JgYw_lD-=;}24CwV0YPwP%W8dYNg2N%*ggOw^_`EnxbHX${j>rrk`3 znNBekG2Ldu|E`m>(_Tb0{{^tD%ha5y6H_G9D@>!AUT2DDTFmqb(|V>Jjd}j-*VydW zD@?3R(wVZEeqp-GbeBnMZg#&EQ#qz8PQv_WW7&YIIa7P4&P+X+`ZEn@8p||?>3t@_ zv_g`a|Gr?kndv*G45mX&Czwt%6)|0Bdcb69VUC9f(`zlzzWM}V4y5{EU?oVs#oY-~ z|5^IaAoYUv21xxU?d4kP&i_8WE$C&sN}@pOjq+rWnorX}>P_)wka}BMx|NgcT0IlB zu1uquDzr1F+gczsRfmC8)x+CkPN#<70jUly3B%t4C-)gheVTCuq;ije)Jgq25zT-1 zfvTF79eCeC&V2_t_Z{Ti_l~^pAm_e=ocj)P?mNi2?;z*CcQUD7C(M6Jc4ax5>Bkq$ zeLn+I`~DE5s;<_>>~w38>fnoA&3zvPQu{s+q;eO5)V{B0vVQ|q`#zwXxtN{-QfELi zNR2`&NLBL_NR7gM&`UJbwg__++Jn?6^arV~ZUU)MI0;fa{6LYM|0;JkWoxGXOrt^S z1$>2`=6KWsInM=~vo{Pz}v%AT=}}GHqiz z!E_a*4vuS-d2p(N)WK;EQhV1Iq;_r&NL>-DL8@CjKqt^G%zv3cwNDR0!|8bXzHGia zw}I4+qYX&iYNA2vO>rD(H|^a9kh-(&WOs|5wZB=54W#a|0a62;#PkVB?a2vt*O^iVm=|&H&7R>S&*2d7kMS(>VSS0|Cet5GK10mTR%8lfs>xKBsS#5PrnXGa zDU#E2HF-T2j zZbQwHsm>I_)SRgUQ-6@EWwafrs+qvq?=dBT)bU=r0uGH~PiT`GS-4b>ynZDxO?dBHH> znICT1DP62Qqx2$peph_Qv_3ArdOFJGRdU1({^mYMZFlXKKOJl_?Um zjym`%yEvvfAa$7LgVs@8GUu*l`i?1!h*oboEKe{MGF@Q0&GZM8%NTQy%P^G(spDP= zw2nH{jHwIL;4%F6E{5e~ro~JvLFzDVX8Mk4H`9KmLrnQh=a_zHvg>2bfpY_?zSaY& z=0cfXVCum%l<765*&tO*BGadw`xU!1MRNYj;q1!e%!Z$1I>D6BbdISQq)yu!?hVYkcg_xtm{IiLBS=bZDL>&%?FtPwYn zX&BQeCi-DGS-2}8&-{0TMZXD#|F?joVGpKwrol`@nbMfXF->Ng&NPc@fgLFQ*IE34 z>1(E=OusPw#-wH#t~O$7!n6=1$HrT%t@LR4t(b2(cpr#9YPbLfSakiQK6lY;{%8vciZWaup*>CBxR+KWRU^-3}z}y0La2Ywv?(Eri>%4D7_z9VDHJ zXG-DF@l079x&$P%TM3e-+sJme%rMf60ZF@lAhN^!HxVek$z{XWSPPkHgxZ*5LDIqT zAerEF4xJB@E-YnQ$)Vpeg=6j+F5fd943sXc0m-DRKr)$=92%Z&*hPb+T>?njJpq!= z6tZ0j+ihd5oVBWKPWmc~jb<5{+y;`FbO*_#`-5b5PqW=vwtJDaDNM^jvJ-yAwACQH z@}q%DHPbIlbxhZo@ay)T1lxmTy~Tj!1Ez5x>GW`re3*11+f4=aN4w)uJkX2ut+a4u z7Enf62zo>uE*7)4jI~c#`2z?ABGnodGcJJmJ+UFpd${LU?`woySdnHq?o$)G@Hs3Jp zz|@_oKhvX3Pcuzqn$7eE(??7jJcRkLg2huzH<+{n!fJH9%RJfeQ?p4U!f27D!gyyR3Z#k`INi1j)Gn1<6WZ&vu(R^aN`c znQkn={5Mf}6ZnRaT_|Xh(3*lW1+@mriRM<2yekm{k`DG`8p$+`X(m%H)9VJ=l{XDk zwt*&#?Dm1C2s+52ehZDzj-aU`G#MmaNCiz3+IZG7K`#rfh_$7l=|XdYUXk6$y6R?tpFs8xe4TXb8kOTu2a*`HD?R2&w`{gzcF29 z@_*9^ZOqh~=@zDLOnsOJFg+-V=D*=Aj$|6gG?~fHDibJn6 zb$v@!<_NpNAkWxjdLAS_nFo@6ahW9Q2&*}G8`EBpOs1OY1k-7zKbZXAHexhk>cwFcMUc_Q>HkgSE99NKP)acbBNBu^K&E;F3j$@D$b zai(i_#ygiAQMxn5Fgz5vO_`7hJAOq-Z?Fja%(eT$YQa9TVn&>7T7zHpobl=p@n0m-EQ z!&Jyr#Pl}PUXV=i5NkC|*O>xV8!5M7YP}iFol^+^P+-EB9jkXJU33bk|NIaUsxVpAmUKQ~G{Gd*gsXE113nNw?Oq*766#uJ}hIlNCQ1RQj_) zlMWho`wsI$$dSW_*XKa8`qgTKEFgIVjsVHh-NT^+nI2`kr$Nia%sv(*W4yqW#blqw z_!`qfrni{hWBQos3#PA_zF{h3+R3z!>1Rna{~c!WIFp;{Jjfv?pNpVZ1<{KX@;oFA zB+o;dgJd~7GIeH(X6nUsFUT|hB?9wB(hqR(XwZD2jbm*NYx7zAn6=MX+rio)raC55 zjgfMQ9VoqS0g`394OAd9?#rS5m|kF-#FPV)edcwL>?w;l^lheMrjI;?`ENIi`P^NIER!lL+kiSg&QHb&kc{m5BF^yyz!!(ZR6_9j#E=W2(pF@i{ zbQx=(8FqH%OBTOo!(FT$FbtJS)@s6M zVnMHqD(oYIX~m=;hYkW2iqMBRbQp&|1zI3NpW)DP96AZ~h6tU;q1neV|H+K!L0l+; z3pjWYNQS-xDiWa|aOkHT`Xy+Q2rcE%?>KZj=uHt?&Y?eWsQnO77r{q4*bQ1Nw9BBk z1l<6=4GL5moiHjhoGF5-6VvTXeVOiM8Z3$CzeiYnifI&6AyX04+e{mnwt|)jSNDLH z3fd2nC!UpTcNp}J2t5w+%zthUt_R7!9DLF!rmkg4_pvyb z=@F*kOe2_{WqN^W64T2}vzX>G6)-I%lJnnNEWQhpW&aQ)j|ZzbbR*L?roA9J>m6Y2 zPPb934>3()`iN<@8~Mwnj`a{_b!`Pn7j}bIh>5orB*#G=+udNhW~YrNxeX*^JjOJK zX+F~$(5IBLU72>q@O2r}d8X*IM(EQZ5k)Bi$xJpf9b~(ZbB5jhAZhmyYtOJY6;v$G z|B8X~`sM^1)`4U#`~i~51f4f%9#bJmcE{zc?PdCrDdbn9Al;cp{fhZd4vs<=SAb-K z8$mMT%}i&RE;8L(XO#FkkW8u}ldrZ>~QOb;@RVVcA=o9S()4?!P@Ojd&ARc|Rs z_MXik*|3K|c3fA&g&ROwA?Du=Y5|f_`m**2Q!0m!1bry7o4}#7nHDhVOdo(`$}8+F ze#x|rshkZ@f@CJ4R}2T6fn+l6S?dInc6V~91GHQ?xCHc&piex6h2V1FK!|}#>{Y`s zlj#GHteI`BRe)qNhnY^Z-9^wxB9rSJ8gR{sp@C$2O^D?D*MY@;p!>4y)E)wTEJ9N`G#exPHNUUSqmHUA*!J#18Ge@yD9walF$=a)ISHjw1wyOt8 zXByowoM{Y_Us~x6lA*&v&&v641W$ceJX7MInE#~VNQiPMPG)+I4L@btLL{oN1~f#_6%K8D)5tEG=|QF!ndURu7czd! zRK;|YDYD*3x+Bv=Oyihlfn-5+*4|;-z_bN~=U3=6KEJSdj!9GSfWMfU!a*>ET~Clq zI*~&svzE)+SFCMhZ4Z+|uMUWmEui~6W0A$Spig8U1pA>`sw<6rf&9I zw;CVgx}WJ;rh)!OhZ@7QAk@$zS{dYKQgNV>XRo(m_LH6QK1^1UMu3(HM-^%$lTvzL zv1w>CyK^jkkt+)2rN4|uiW2p_w=x4t*f?(`A4<(6Z)FLT^h|H%3nN*NTVkMa|g z4Etp7Xtfk=innqDO7v83MGL|Yl=~=cpp;DWwz(5Z)C_OsAyQ^~E2E&)W#fXbq4d)r zIcIw-3!r4@dMhiSM9=Y7Hj*;eTd9CjIL})-2_?bdtz3apvs}@em703^MSY|{P-=<| zkN()(si}iKeT8?(41}bw^j79Usax%>ya7e|%3FCKN&RUsh4zP-1ZI%ii9LJv%inFOl@>3A4|LA9*d$De_K5(6>gP*cRe}0`Q)}Dc$ z+fb8%A*hp`-bxcF={`zZD1|;sS12VuN*olYkCF(bW|w!$4@1fBz1v%Q98y@hw=xb& zp^q{HN=dn%vL4RY?D4k!86nDEZ{=4gQ9g>F8FlHSG>4MzqjZLn?W6RCQtYEV2&KaA zBaNnnzW2_4G9~1rlkuCPHl2PNI4dMVdQY2>X0TWA&v@|M~_N)Gl`xG4g;L|A^o5e$%iHE* zC{ew=m8U74e%{J7D9T{m8)(SB08;TVZ=-jhR6Opjd*hqSOF(-Gdv?NGvel>ShneUxM<&UEisPeZBjQ6@r(8tHA54MkB$>!ZHKR2eo} zpGVv1(WR@tHH}fjYsO1k* zH9pD~D0M!H3yPBAopK$Nu%BO4H^tQ8qudR} z>7zUbrNT$afD$%aPuXPZ(5W!jNN>db&yBh7#ZgN30z-j|iUYPa3-qP1^^1N(cT${$ zH@qzlz#`p8IRz!-4LxWxj-O!*y=|L@!`4UX2qoJ`=>?_4M@fPbRpgz{x zALV5z#daU*RY)a1%9~JXe3TEMge~&U>`N$7K8h1cw2$&Vlyo1Z21?B$z4I2+KtJc3 zdd3#hPj=_q-sxU!hN3U?R&GKm_EF5up?u+O(;P~jkJ1@RNr|^j9F&CB-pYMYYI<+= zmWD&B_`zEl3&nZZTbTkSx>~=n)%3i*7;7vlpRjJG1X&qz|LDw==Uy1C;8eK5W*=ztNN<#7#va8^a^allX2DF5a&27 zGOp=?J7AH0O}`CWd$Es2&z5lh4;e?!M}webcocbc@C1}%TrW4I`vMdvE?*mzSD@7R zD085M;hIz0j2JX&@aUKSlM!uaiK=Z8jM9CSWl%DFloBY}KFSs-#XiabD0M!{aVTN9 zNk8(GZIv-^il!SWkq@RP5?W4?wQs|>BhEh`RbIe@{ zsm8}>6BK;%*~{w#P@;X5YA6{#iW^Fak8&MKosVK^g>-N=dVfQOL_$h{)Sz^OlI)}0 z1tr}_xerPPE@offWr_&cji5dHXS+<#s@bK@cAMNu&EsN}5tF7q_u{hyCQN=YbM(YJ zV`6&6MK7G$bz15C@PM`^zwUh&uI|<%?3u|ECystGGx0fC^yuBA$HHyhI+R{+84x$9 zQIFoSJ!4{G^@>kJo6mjV=>Yr}i2oYlzo60wo(}HaMw)c*P9`lPdi9L%9YufXAw$$a zszGIOoT3a+-dq{lym3t@{MY>#1b$GR|6>Vtd|` zWKh+3Thdx>j3dQnAB)Jc7*wRP$}+e}nNb#LvPk)v*g74VHdAF;G|6Em!%uXH4c@N$_0zJIqWE?sQ5K-hbk7HRhQx4&ed*@ z528G^NuERsOsyTOM4LI5iiUBlkxjM1R*1?&8l6RYRHL3WjS^D+)kj6DOGQ%x`#qnt zCF_Z&Mm*`Ry;NUcSg;1YN>R{2-c^jIbsV7PI?&Ks?zEfIzBFWM@aRUtN0EyOWjE>5 zJ`C>awfotJ!8^rnI6}j9`?BCZ4ZEdf%ka@>v7dtA6#d8L!3kcwZ9fWb-;t6Qp(sF5 z^>Np(!)dnE!>C9Xhm=13QE+U4nxr4y>EFs!sQL%#jqfyvH=zqs1!G1j!coB1T2a2& z`yKUfrC(kd+{W)xYdw5baEE{sk@%T#{rwcJr9O03@T~#i?G)vPeqpzNGree4@SOqS zQHt_4?YGdYS7A*35T$FM1-Bg;+g?$UkhU@)G}reBEDBa-n}I^_G60OT8uZ z#14u=BM9~_{T53-8u|(74@f=QZ(yhi6S|TFJyy^EEV#8gs`TT}fntdJ_)B^rZwU zw}I{v$s~a874$Sn+D&146C`7-0^K9*Hh^S|A3!q3Nsz3M)1Y`^r=lw)2#U7@?-rO0 zl3u5Qq}O9X((5H4>GfwInY5GX0Mluviy)b?f0U73Z;;GwFo;qf8m>$S%A^;7WYQln zm4Olzs^F36fIS2~imoNg-ngTo^#@69rCz!=cxsn3=m@g7m1sff$?vFwz9Na6@J@!V zE*)MPe8A5h1^b4B>oY2j$v(;yC^bl_!6q9@7}9A_=93cTt-J%J!bkZEN_2Z~n+hn| zNj?(p&ngKhWkb?9b}KbLN=qowD0PEP43u>J!q>sA1NLFYyIk7%8yp!uhu=Qp@Eab} zr+aL46#g0xzo+!?oWUJKmQ6;c_=`5_HTK{ZSZ+}=Z^M(vy16X)Vf6$3iL&7PgTri? zbskZbu+o)K*Q!@buTKs6*e|H>8gYc3So-PokQhI;tn^S;$VoMzPogbyO6j(lAukVB zhv*5rLSBwc-j3-s9p}238Iy6WO5Sd=1tDA7)qmU-@?~^)6eFe{X369Zjte%p;>HQ1 z;`qH_!>uI7Z?fjb`D44;np>im?hd)2ChMP+hin*@_q#RkJ6PZdIlVI1))len$T3|W zHKySRFbzk*Ol+s)sFkTAmp)mxAv4?MFBSO|OjXtE*0uieA=bRhf7aJKu28VPZ%>HL zuZ5~#*%LDOE*$%jx=0qM50u5frM^wgv?aUa>g($ZrYP`TAGkLpTkWoI-y8CtJ^QEn zdUq-gIJIy740?zTJ;R2sqm|8^vzhQ`9w>i}Et`bvA=V+41yp4wTP*}7Ki4)No3Hnj zmzO(YZ8$_mJ1VangN(IsrR=mn6dQ_2b)1CbW)k*I*iXUP2XW9N*hvLw*rs6{hHW|y zN?pO>;8C7PEx}`ZGI0fI84yywosFj zh~O;!yYA-uJ8eOf*f5pW$LZh#!__dr0SEupL@CN8gx#v@B|Xh8^Z`B0x2m1>aXrlU zs$=z)v{|8_>0utAp4Gd>n)^~1Ho>DXf=*!JY$+X5Tk6|m&FzAhLCbB6_r>*o6D_Uu z;GX6-&FJ8DqCGyxhP>#HrueIR-=0V*R-f9_+)W*&f85hNxJ?NL;FeCRQcqR`Lkm^E ztHMb-YL@HPUgj1NCtwoW8R?VBfY1cL;qoAPqx9Zh<|jJ!ZKB}|Y6uEf+M2EH<;p3GQ@~R#$zRd)-Y8t)iNnIM9~+l3vN+bGTt0%QEN)~ylj3(RipKoSIl3i zVWmf3F)wVS_9&g4WB$p{64zT9@$|?^&rQ0sXKY+)>KyYhKeWIX^UPh;xKh_V^Q|Ve zXQ^6Xe#0}(^$=GX&0F=1>(eWira7;9Zj4J&CWy<7U}d?y%qY^`ZlNwQ|>^QWp zn(*$jmJYGq<=B*ehmEWUM12Qc`6degVBAK>IKRT__z+v1Bg@vgK+ATtqSNUDEeTpz z*W)Jab;a0@&)1e>GLDDZ*;VVRJ+#TS%kFwSh>R(i!nXGhQA?0n5TP2eIxzt~Q zo{woduO8JFr%yO)?hue<%`NSrzj@UBj2*{-9q3fsb8Q=_LGf2Gq^ZfeR<*7jq?XkL zsXMSAxY)JSb_n6twT(%4ZLl4rZjmd*vSUH&E=QU5yZv4ESxbvV5OoDKM;u)<7TGFc zTz$=xh9{P*2+gVJWfrYP%zWDpO8kqmyx$%3ZFHuCQj8}l_wM%Ma6~@l&$lgwhWN4i z_G9MGdfhQ|$3bzB@^)HtpTw;USkP|HwJ2-uM;KhFt^LPvDS6(pjbb6_h!&9vj@o_BosE2=A33=bS7HB@AuN^9=@h>%}o3&Jk)ptGYsyR5k} z`nSi;ZPZozq2o9xyroB;Fy9?I2+3i@{(k0`LJU%U!U=P0`p&)L_^BYguXXJMTJ|+R zYivJm|N;wPvGd&?WEbJ-#B5Osy5LxPnvJ(SA#=2 z93)rz<_cF(S7*^~)`Ak8>x|nPiv4gT8P-vO)s%_(!6TTWO-mXx#O z2)fe^iitm*W39tRt)$_g|9Q&XzB!$HSRdN#_^Hnh>%(Qxr=hD%(_?=z-x?q>{TFk4 z`?N7|x`u4OYec=M4%i2VB)6n&a&0*DAth*iDy$E0g0KTNaF;fJ>27jS=7{@nrFG6o zRNfJp_Sdi^CZEP&Wq`KWMP98N?kQ{9NY`mE%f%TJ=d+`Cq)=U0GhW z3i+vSbJI|l6U!-QU3)9{K-)puIJJlVg4;aQZ=Y4)zowKakO9}q$3dShhY6~!f)2^+Vwf?a9a!dRaBcBX`{Cvq8P|RUvSpk zRGp_UKWk3#`!cNbm$T+*6@#kfd2?rM`kgo1)W`KP=gr+Qbl*B}9@L&D4OgnBX0&u% zbESr1+6cl~Ge-Il3ZVJM|5tNQze~sUyM8rKQyVOLJqP&}gy;Ad*=WACuJyC7 zeN;sxx}?a#oQ;hMGiXkkO|4jL{Q~FIP#pcP_L* zRuqgTKd+Fb2${8I)mW&s4z(E;zbHrYB7vh-7HGGct0WcVWO{Ag|CHUobblWd%x$8L=}=FcZ?Ze;r9t?qj%& zLBu3vBI;?-1#?gPpGP4s74?9y8wf)Tg(A)u^Oj>kxMOHbSeT%7*@udtQP5>GI$2z1 z5GM1yy%Scg+Qi zszM%DG}I2etaFxJp}}U1KPTpco$hjI#%kdqTCzp@JE%k(!~}_rx>%39XztWrsH;S! zUF$e5qJoqjza61Ll&ae=np-xKV`;Je?nQH3EaYvxXl`*g8CU&?2?ndK2lsn& zpoS#RFs}Avp;FF-Rj0)cjnW!Kg1Sy`e90VckCU;h_8W%ogZoe+w3fV8j7*w`D=`64 zRv58zN}Yw$xwamnh1jvKT}Mt+EzBUz2~)~|#xiY0{Y;_|cvZd0wNu7_i4^#t=*upd zJ9MCO82N7yHQ_R?Gm7IxCe!p<*tI1)oa*^BH@ZliYZLODr+2<=jzX;tx@>NTdFr{# z<`itcC2@y-y8T<=z(jePEX1}`^yv7wf4#(8Ze#f~3bq36pM=(9Q*Xx6?m~XXLV0=@RSmy}Z zjZ-cKXv2kA6mG1ty&5y7#eOJ${-W^6}YmkOGCiRC0*y|a3==s?uH z5m#(HHTex<_u1`!=^}azt`b~9Rr{VhW^l0M*^+fAbuRqq6 z!~ZZ3SBv%Wf0(y{!_!iVuh~WFpol_V{VvlM;CnKhB*^^js7(EQ$N)Q z{fV>49s09>n)`R~-v{61#DD_VAW0W|J68b}zG2mA$D3v?TtD)sxn;{CH>X)YrG~;_Le(9@p9VM)*2OYy z1bo{_|F|P+77fy#yfcoDs^qwF(8_b-9B>5$&w<058%YzjJojI;PYZ8>+fuSxJx8X> zyRQZ!zFzFFg$GyeK&MHyzUI_hs9HyRF?Bbb8W)%rAxX*vajAktMT)$rhoh=+K{XY| z1*YB(Ih)6v!!`C;5ev&>F;2&JY6Kj|=)ggLm_w-Izr~S>`u6!V6XX4@xxxNeB`{5X z1Z|y~uYaa${nT^%ud3F(TT~l^FEeSe_O*-Z>yP*!8Q|yEpy$=2c_=#daBJ>HI(d=x zfD;sISTxiS+cb5TKUNh?eRfU0BAP!jI{@b>I}q-5(U0ceE?Zu0hKN;YTWV1HgXeUD zfqA|cNvLtkvp}n(V&p68HpHd%(KRAQK}Nmxwf0z|a3x+a!2!A!%MZ}*z#)BofEF2= z>e!Z<_0Ab!6kNnaPJ zwX=^!bL|(FrEHOk4uWMd!yTI9(9aABM*vY-YBCZCrtAE6z}PXnMjZ1a(ek zs7$pCvN#xe#_@b{DGP%d7^r2asQI>VJg`X@b>aGxjkHMnEUfQ~b3>$rVTQ9-ss{W4 zDV9FET~Ln1eka}C*=Z0MMW zRQ-A*EgTgS7NoWCPpwt-?;B|mUFiZG7PZ*W&`v{A)Cu#>WsY{;*E0V;+Dj6L%h_G^ z2|-#jHC4|G(%LmmMK0tuT9e}9YPnJ`4bnOWQK`_XmHM$Dt&_T34-VFzYLirXv>NA< z)mtzr`yANdWJ^6aSnC^-7b`k9&!T6J7>+MA1fx0kh$>uJo3#Z z!wt3!%6FA0d5+U0F3iTFo`N7(1{}g%NmJyR)WelDhJ2kk;+{e`^OLCLIQ*PBTxFdd zI}ulvqTsLg#0r5muN(fwG!ZIhq#*Gxt;6)f&6V6&(2pnq{nHSwr#eor3DLqe8bX-G z=;E1M7W%{&xFvy42Lz; zp>T9h)w#omt#dA*SSZfe%5yln?Lb1+I|1wHNZS@=IdaZAr}*DZ^gqp7qwo*N_nqRT zX7(~LRgd0U(`NL`$6XhkH63*AqFeEKRUT)p^S)_}1r3~~SYKNW!qW*<)B@8x_?LQ} z*h>je{B`FkbGQjbnWtaSv~CaQ(Y(biMeRmQd$?|TZ3AL@7U!&`xpE~Aa{(^uQ_(A_ zsa@wWImDr3QOA$jPc5xauxN9JBw=$ z4kLJ}h~HPPG+FaLz~u>oXw1Q}bR9QfTB(Qhv{0@69XBBI(2!Fh@5*C``>x}-Wi)0(X{Z)wkBh0wtGB;Eox}avb*eS=jCx^3)VuS;CJtIk>HHpTQ= zbrv%$CA|mbnmbxQZ`E4ZS3wZnWVd(*h&l|-87S;v;re~}_e{{?Dx7@70%55~PSav$ z01|ZV#;E~K38J&p@JqvUHoLI666EP^s0e!8B+}-%fcd|+cHMf!@*W(06t@#ccvtzE z;6nUk!l_?5{G3Zi6iLHCV!Yx|FKQ5H9^|6_ZJ1{B8*o#9p{drsug3#=_{ii6r#X`< zu{KQRS0cyc--o7axBIM~&{%8LGU>=!>#KK(BGYYHYF{d5sXno>*7;tM{67|~cIrA& zq<_z1mpfH=HP)i+sdA2-9%#)S;5l88)gr1*R@%Rm<7%nMM05;UeQt%4*iDOn|8on- z`gdRaa}4THt^n+&W^~~qm}*-z>rVHx7)xHkNv_R4!OanzS7ptehtZI8Mjf{~64SK5 z9DCNhWTAbXgsmJ$D3=(C?x*$dn`+%7{w104wfebmt$k1Lm~{Ce>czd`4_wuz*v4X}!^!p} zx`^GaUUjDuq+#4;glnzBa5%pBzd zj_8$oaC0pxhyrOs)?=D$ZPh(`GHuT46Pn`;>3hT&lXDY;W-BOXQ&69MlfTQxaRB#I zChw+XkSVR;S#!r=7ZW$#c*02+C0dZ8cK57h((RuLYbjY%zj8kaV}4A6j_EXjKA;*y z(Tr1x3o(!HM{Q|h?xB+b+3jmB|HX3%)5w^fXdFsWuq;{WI6%{8^{(VsR+M%0TYSCC4>Df{n8ASOC@!ghMb1Zm&)lz#TP4q5W zOrTI~DA`*4M;FsH6=CB$+X*{)bVtE(F7m$|`1L6fS_E7ujL_O@=dn&wU|Lepb*}pr z{p$$rUVEzJ;NPmdVcKn0B3q|msOq@jX(D0n>8~^)cs0_gFVIMjAP=10;q;nLjNNT6 z;Xc9I{6;ItYPf`pc~}OHkMi(T$4q*IH@4!^XZWyK2kr_4SyU ziu4~_Y4g-N{i)X4{k9bjlmKTDs2az?+K>XVM;ER*EXfOs+ROUR)>;q0n0)#pUjqw4f<_1uiOrHsB=CxVXT)5=Un)UY!Mp zVsSnpuhra5&Lgs?iNq?~)K)q2mYV85cM!g>vOP@aRjPYE#w7OS&H)7B(kB|ls;|d@ z?M|1@n7z;lVrd_fn43-)xNQ@$Izx+uczmcdvYobEZ4pIJD24ma3s-S#y)T}~nOuj3 z$|$`uN^2V&g@{p(OzhTUSB=u!w%4xvbzGyj?x1zR?mZo}$45uu+BPUZ(UyQKSreL~ zcG4Q;h$SHSTX266Dj^y~PYuKs+-GuZMR!a?A56nT!+7SnxwUklU;aZTES^W@?G*VT zPdsG&Bwh3gFj;f==vO;vcRWr7qhxUlhBh(v)-UKegZqMFe#a97sa5n8?0DPbIA)}% zoBUI3@pMZTcZ+967Kdj^w%(#0 zY^2`taq%76t?EvCZnlm34n1xg5ll}eWCh{5H~nae)}qyk!HO~j68$CMKYH6Z7Q)GU z^o*avTA70VCxz*1GjlUsF9?IPFtFt-$^nz^d?qwPpYR{;mOv9Kw8-`+ zAdEr`@!ajukWkYD<^l95Q39m-!Faq~zwjSzKtRz_Mfs4PRBEX|G7PRJ>Msw|ZjXq4 zUq1eb*ioYptutb!qC1b$HzGLT2LvzH-+!b@YdvVV78Q~Cp`vVqiINA_6ptY>-Gk!agP8cWCs2Z*$60$8^boXgrHHkqOv{-* zXDVfKf>K1>tx}^esq6vb|48tZ6rr7Gy23WBU{H9+8(ChXN}O?#~L&RB;$U=w4EvZIpi;cAAZh=G7%&- zzvm4KW{LsbuaN&8#~Cp!;|=-{B>T*1kW6LJ3x@X2iw1oQ5_N7@+D6m_rcvwl|Cf3qg+v?KafgCr zj2Rvc^WP$%JRocbVg5ma$Cxg73IuokeL6j17&vRw+#Oq zgJgGX!PJJS15;XCdk|%t{1+sh+02@KiK5`RgUY-C z8Yig#QlnaPL9$wZ0Le!D>Rm;_5ePB*6&nRvz_gsn{uSdUrrk`JnEqr6eBVg$E~det z7Zl1anYEWd(wRKgUSnD>iRQmw*f0?1tO-aaPVz#rT-hP=CD4 z;G;YPB^q;ZgUw4&qVm0!Tqp{r#0Hx}O2QX9!CS}^M~|5}deYcOM`n(`Gp=V$cV)zw zj7db%`olwmTj)vR)`%N6GeXwZ!*2z13&65p70MTJSCs-zY(Or z{W30mi}Z~zYYzt$omP~IdgJL@NB@-HJ&*X0ZGvmN-*GusI&!+!E2B#@RPL;^tMG3D zUj4x({ubcHl~|c62jEgTvLIE(UWM*Ft=+9w=r>P`TjVXy;4XT({=gaS*H#tMQamh= zCviQG&EpEYTpxN?>laKZS-;qTG~PL@b!|%K)-N_YwmRx^wyAGj*ii1r_O7sJsV^9Tl@!SGyLEf-u%Iae8$3Lwp3^Z`r=9i&8Hx|O{!d;~vV{QAwFn7I>6#vu7y+ zy7Hgl!n0vRW2zjLv(9Sh2*@HH35nXW`Rfn=rZo@8`3QMWIv$q12MbH_sy?!nrCFOO zim^t7h?FiahaHKtwrKwP`rowM>=X;jlya#O%apZSa39EHd}c9~2nLQysZbl)5S)0o!s# zG`VPKT`TwEU&w51ecc^^5kAJ6R~(3C+^s4~M+>!BfDsAoKo+#VLRmFrkXh_;6w5Cf z<#UHg?>9Z9X}3gMEbZQhT+t*!6e$reXyuAh(VC4&2+Kv$LeuLnYVGY*V%$9x2~GBi zDa{K^cJF5*s<4DNA1NcF@#K~@@6%v3kjy%b6f9xqO@{`Djryt=86yf!W)wA!wqmKq z-cT-U9(81{JXv=lW<#A>b5+!s^|ez7jh|%Ay@;*#wP{p~ldZWYNu4rz)&H1ArZsml z>L&h0y!;oSMAE7_6$s^oV+3_qrOrQ{|^3D9G*v6@qDi7oDhr##jEa#wk%OVr0v)$2sIf^Hvz`NMe!PA zJe|iWXzI9z482^*n?plWb}91zHt9dNws+N4qN%N~Z^6IR&qt{^MG+{fQI7 zVXpo`SjiR1FVE}%p#Fd0q)cTqY4rOqEO6KcpaTrZ_S;hzjQ@wZl^kdG@c5QIQNoE zakav_YJv;**$EPH&mF^#>c|y=^~xkKJF1SY;LBy*Jv<$(sNDHuK?wKX#8He^}{3#{)q!hKkpV_M zCSJA9-3>$;{_POpUP*aR$_%kjj-O!7JBMRggSGu{M-2BK7{oto&3%Fj($Sh1imm%0 z{9}C6BL3OT?SAy4;MIp5{fP9`@elldGfN0Bivv{bR8 zLo>p!p7|%m7KTtB_m~Pq|8QR?r^VaMRO{%T(W-j`vXl$$Xv{ct-|>`^XC|g$(l8Sv zLYyj7nhhVv6rv@?xrb-W%okG=a;6SNskHOVP*~T8sb_}jjB2AUPScT?rLr(-Z4l!D zeb9zbF@I%YR@=}}hAg`+TxK1CLKt<3+0EmC7&HwzU-i_4oEvGZ{U7H>_t&U$z6I9D7+c5x2`cQluZC*p8krXf{VdF9E!D0`~Uh+Etu*kiS)`ZVr@`o`S(_n4LA zF+2KNZK^DHZN*d3?tXs>*i*R|neVJVi@Th@QFd12iGb>I{DU_} z8{&2}HuShRZnmW3zYN?;&DJ0OQ)_8Y@QOu+{HyEUsmkys$O#X_q6;Ou8Z*-avb&%S z@q{J2S_+#K%%7OUT`k>Dp|;VjVpaFgm(i@~erfrMcD$vE-t(Gl!va%TL9&UaC+aPt z4|orKOoBAediv$6-|OqW9lc%4uocsv}T6F1}RDL3)=`j+))HW(2+ zCvfD?3X}fQomx|SCf$0ht-kcvia)gb&`x@M5KnY`CLiXd(>sb8gpzn>YRtAY>BXF* z0*S&D$az!Dk}%FfD9yBV7DInDpSlNnb;`p8!95qI!kjkE*#>;dHXtwfDGaF z213mK?Ka^6l|=Q|IX@cr=6a#$t|{$buf?eCU#8o;s66r!<_$-V4Ym^Ou5YG4d|qpD zzc^R?|H|}x=f7vV6E}eW_e@*=J=3#r371Ejs!#E=^lD$X90PTmD3^jenkZUt6wQSG z7(?&Y1gZKSKg(n4TK!gk%U!{wV_E93-y_Smd<)+y;k3TQ1<^R|Ege1Ka-z{e< ze3-*Q?MlZqS|&&nkL-*uh)sYfj$QFzAz{UF5lx#>c0546j)2HgHOw+Oq}e}qpwj$S zmfOszv?n#qJ2^d}ljQ?7 zS3lm#($BsHa|y~|!@(aFUyL~rd!6N8h1}~Z_vk0O5Jumef@PN6iy^dp8ar4LrYbci$^BKP9uUb5V~AuVxbL6&uLFIDb!m6mC8 zFH-KM%RSAr*GR-uWV|%F7v_&kHe{bFcS|tYSKX0;k%WPnA~ny7PZI4pD|h07tqnFO z?8K8&p2eRb-hstNUv3Fph5m-mCtGZ18?+`USVwH^otZzH5_if5`(o<5{#m1@c z?QDr0xj`0zHizhCuBA3Fg}j1K!j;{4*UL9h9^-s9yb*>7!aW(cP9NOG64^TiN4-&~ zGz8XA{<5Ih2(NQ(8RRo&Wf}pUH>OHXiev>P>9BAp^P5IGy0%JRysvcPt#YVrY5gdNu* zxq|Q3d8**N$4oRIt$>NjbHc{+@LM!#n+$WArZ7%6j45N}Pab0@XCMdLw zaonT1K*Xn>y2?hMuo2xqTJFKM7fB!R5y)r~qT5!GU$0FL5`Av-ssiVDdVdBE7yHl7M8`^V}?r&v@RIgjr2st>Ok zM|h2Ni9EyLc9N46m71R1M0UxPFMY}(8c<#KS0@pWP8QU}8PdYHeK7th*=)ljN)NV9 zBU0f%MUrs}jW|)TL5C5I>N^^dGS#B!{?b>^Y*CE3qQUIs9x|kc!RB8|0SBcSHAO>F zr~`Rb7-n>;RF4gHNSOW)1t@_N?L-vHmYl$*nkti$sN9OQoO)q5OSrv6rlm-)(f`Ph zx`vYs%f+L?UXCDQ1}xA~N+@B>(zNjmP^NTCL%c1lpX zkW4XYyhRyMM@j6Wq34?f;?}_iQ9X4_g+jz^ib%dk3;2h;;UDrQ3r~6N|Azv5QcspK zGdwXHhJz^SKh+c!kld0HJsrbSK5`S~vnzkEV?5Kn#V7>cWc$ZrM9DN0u)#+KWNTql zfr`L8qW@Is#j=jlJrO-sWj6+*Cnh}!?%Uj=rqOR?ymX_A;X9S>U!#@C@Ddr#X+#tD zUYUqVk{VK0m15XAsUTi<E4}7WfBiPbEc3>vY4KTsN$JP7B#U43^Jvtf19%YQZ`EpO8RP6Y`lH8>-AuwL< zMajKLxkoM`EKBZDzSyJFW+a**_vl<0d&$_~5^5BV5?I&0LFcY*koYu z&`vw3Sn3L_aOx#r;Lm`om3zcs$|l?^szF>5IH_`=iQ_R9*YnOlmDLl43hIeM*A0lE zAP*5C3_x%6h&8TFRd&xYtX#|)(FjBmvis6ChR08ADiCytj$Kqgoij^`t0#f;~ zgTtXXA*QPmsD)5*AdoYkaDmDzlp24GKi?vM|JNeZ)r==HHvd{~8PBWSblrmp3i9&K zn~LwLuP8#hj3^2&>zFQ)JTb)KHvwkVm;|6D6YfRb(JYxxJLRr&Pie)q6wL(1sC$%& zW{^6V9NLQ(rL!MuQEW=E!3;r4Hjvssn^J-#zLh{%VbFXFimMW6H9jeLCNMJgBtQd& zuG*j|ND>?JgPJ4PKybE+acRRXq7nKybo@}>()(hww0mns0REvR?A zZzZN?qb4{Ktn0{HHX&VtLQxQmlX`hf^eR2R6SLE3M!P6H;>m-YxLn;Os*DPZkxWAl zBe^S5#~zI)>_y8x8X?%D@8uzk`g`>*?2EURAj{Br8A|=JnqDVQko%r)W~W+&nkZH3 zMc7M{d(;=ON3TL5eQH|lCCfc(ACX&%+^2d+XsXgW9b&a?W;2JL>Re; zFjU$Dv`a<6uBX8=6evB_`^$beP0u*w8g z2$}|>1wPk+EKo5~PB#86Eljbz>=%}d+aaikY%EC9^9Hl@h~bv0?X<&{UUvq!1@^g; z*(ZN+Q2u}@dJrL6Uq0Mo3+{{e(8RsM((i{`_B65Qe}rFT@%;D3iUu|`+9v#q?rrl= z9Oj9)@8iFx>&Z8iGtYoga%q=wmM_ChPSxM3H;y$&=vQB{;P+Sp^vEnrl;6jip5DRS z%0JL=yuP4^IU;bBhOfEg>Ln58){O>-mZ<)VRDE9$bGTlTWeM`L&H0CrI)nuNFc-f9 z;;&aknj-?T{rundm$9N}Sb_p_^A+W~K5K@hl?e%M)!RoQy$Lfc7OaiVm|Q}4= z1jnOUR$1s>pE88vExMW+mZ-oJSh)=h6FK;0`|0gxS~><^T83YI3!oxI=+Dfwv{!%7 z^JZFZS8e)g+GOg7XIgrwTlB_u%e{EB=n=c+_Wnhy6ywuhQJBe=+l59Mye*UrISwu3_}^l$B!<^d^qR4q?${BV2JA&bIV!W7?%CccHW48_UJ2e-D-3j0(qY7EBWL z1=*Gs5tnu=N;)L*0##b5iF{s#e^T_aY)ezW6Xp6(*_KXt5b#e}t0{W3S(a{XO#AR6 z6s1canD*Z!(v8IK5IkJ>gFbSWrCs0;mG~ip0Qqj0zyBV5)p}=GGkraR+S>3)!S}F6 z>>$70p(b+r5Mo|}K308mT2QziGTYKNa8!+=L7^C!la8VjA8l6O;s!p-+I&I}uvU+6qvP(0&AcD(EDKUIwic+8r;W9t8CS zNxLB+q@03`(||JRu~LMV!`hpyeahNK*7mSg1)3!?X`$c#uBEU2+v$ey)gbA~Y1V#Y z?GKRjDdrU;iT+FvF{Oe&6B*=!WQ-*sSwA0xWFA3TLW@Kx?gmORWTp|NJt$8%zh{ZC zCjn=R6o-RkI?sY+W#GP>N9zER$z*|KO@7MMA=|LK8zk+ZFaZEFr((;XD z_JU;Dd(AguJjnDUQ||&J#wd`C@gmbg58>sG5*9nZW+Zqo)5A=+;^h=s;x(WzM73T3 z$&xfGG^i!h^B|e=hebv&`x+!u>A480pa-FK>_C~!0;YGE?!xO2lSRetf6Ji3?;3Oh zB;#IY3VhGdmVspVE@9fhwBtRy5qt!q^y}gGjikT$z@Yn=8MGWEvs=fsn<-+sVb>lc zXR=2?C8C)7IP@q8y-x|ZD=j`UMr99>99V-uvcyj?jRUO~$xLN!E=YFPHLR7h-47r+ zMVtfWiMTh|PD8|xanX(HUksGltpLePc7VPVQBH8^RSvz$T5D9AjC(ss#!X@^jkU3? zEoWN40{P1*$5{N0qclPNNf)|;WG0Cq8RH?=Rxxei(7mkPjwAU zd5OhYAerC-rpJCT+#1jH!Tdx*a4tS8tGv8+XER@Y$0_XfD(Flfg#t3wqO!EgPHd zL(f_#a{cN%gC>`r{MfQR#Ga9Byxsx>#T=+8QFFYNwooeOdMn+aWaoP;cawqxj(kmq z(s=+%Iu0WZN*a`!B5&mdD0OxWp$4N@AXQ)_HYkNqqA(5{lqFCye3Z|jIDM3Fp(J1| zHpJQurO-#Igp$1xP1Rs@98xmsszJF3rN&1IXoSpAiw!m{peVb%l}=C+c6%$mq15>( z4?#&T$5hdf&@+%~FmxJ}EGUH-I1S1oiiQEypp-ySFjN|pZBQ!E*Bg{0q*QtMM)AQ7HYS$(WJP zjGlC7@0hr_g%_5FFN|Cg+G@(^iJ2tz!K+Uh_^ri}&rWzz?Dr5R@k=zrq7fhF9rV3hES=3Yu`sH_Pl+X|y1LcU-v5ME6~ETlI6V>{#0XQB$MHi_^jEA8 z-)gz{maOjLcZ&*4k2((G`N}Svtfgz3q(j7K@|rx2ZRIW5-AljOYU$$VH?O;XYP)5X z-~8^SMLR53lN}$zi!RWb_Du?MB$@2v9j>zL{@>LjarI)((MAQ6@LL(J)m;VsuO6s( z{Mi0_o^xhu!Sh#gPKV*eA9v2tt6lKx7@jYGA2^=l4#I0#!AGoXpQ^`qna<~&yxOJK zv2{Hf7VjE$aUZCCBKRtO)?GaK^hAvFDLZ!ff!~<=Gd~)?iA3egj;qf!$2eiVrW5U3 z*Cwg>uF{U2n!5P9%pOA>yJMU=$8Tm|KbC2FV)K|n>>Ulvyo^uP`#JVG-KOko*34gW ze$${I)2xe}Pi&SJ5e*i0n3%mxF7l5uQHH~Y;u*&Z$AQ{Oc#|KqI&qWZ zAbzUEui~`hC&$5@Q~o*U)tqBN^HOYn)U6W&%)9vRxjZYh$cKlX$%+%$# zad?jMj)J1@_~9YNu@}+sF&z7C>i%*2XIQ`3gl}m1Df!`Fr6JT>ROUQmI&j+I%DJHC z992R4V)kFT;MkON>;??|1K|UfrV_X3YzY!i?HNs!b2bC7Y$UXFC4}K^4Oc=G?y;Z1 z!*%jUHOnfG<4FKx@Bh*ECGbrZ+y8eB0h**hks?I`L~ONaON&w{G;PvSwfc&JprV2T z;uEz(s;KBqNejJ(ASxJKA1cp>iVsu-#D%0SEsG0_OCthhQ4l+w2`wEAMpny{>pH4= zK=c5qWFIy@o2sAyZFVG;6tLSQ*GcTyWz!>HhXZ%T1MpNS8Xj~JJ3$5;hQ)l7Pn7D8ikfX`Fzepk3NPEBTODo z1so@0rW~C3kK(bFp{s%LpL&(O@O8*ZS9z{W!8uL!rjipd-@7U%vZ_P~U}dQ5M0~)S z6tG%YZHhvzszJI`do*h|9Zoyd5)-a3n03ciIYhpeSC>aZhWSR@BNG!;fgQ!vpeR_a zf5?n34ImIzoxMg29He&<0OHaG4@8}y)?#E3sQ@Y;Q@PbbU-TEK*eY;j)QXzIbG}NAL+V2?@O+ov=3wLB3DKH zomyk$edQ~|cUIR4?4yOC+Kr9HP$A+=>KxCw-zc?WIAqZh{HK-A9hXp^)@&MosARvxEG{aBC!5iX zl49wF)8XHs;I0ZB-~uiwzc5@-1;n;ODaR9S1D=v49uI~bQp!XuEh((}-N*tU!k1km z^o~RbO-Dm_aW)?Q+HkF4N%6k(wP6fBkA7{)6cSUs!@j{gp7(uYSS;kHcu#y|cr0ra z$`FNu8StoKi2*sC-Vaxgv{KoIeQA6qAUh#Z4@9P)hQCxB>?z*W-x|y@Qz<--`k>Pt z?uG;j$kS51)!!O!6c&u|UcTEftH(6NH*H!w;NB756}t^{WBXvp2)oU*Q%ar`9Ipm^ z7_uG?yaw$t^o^PUcsPUl-hezi#cSPTm^L^GKFK~DbnzrAUqB`;3Y)BA$=6@mduoc; zzsK;gaSnpj7Bi_J&q>*|aIYbnzoaw-GQF(P6DKnuNcHf76z`gShS#wCKl?kwrXCJ> zV&dE7@hM*2_l7~j`QN-(d~di*cr55$^u6Kc&fgDyZ@3A@{lp&(4x;B*;HGwP6E=ej)&%@w~S>V922N{y!N`8!7SqIGQqyKB%fO=Zv)F0LfE zs^u(#>qJz5uYQ*^f|Q2&zzNJk+czruKoMtvXA$}i__TnL6@E~O5Ilehu%8GIU}U?d?b#56{9GECG2HKFv}^{}EZ?_c8iZt9pHyLkRNvzK+r?BjhPdxQT#BVF5*bp()aY?F@sx+#`VbIEskg5U5k%(HT9btdzB7YV;ozA2POqrVY zqp9RhNIqnNHhLhsx_st5wMKL1S*9nvYZp8QK@b@6o>bihL?+I98x*H*LxN9Duq8;8p+#HU4 zg0M;oznp`{dUXGAE4|X5?^7D6$nwKqTj(^7c~t+A-HBAugX$SLY55r*F?iH=TL!U1 zy*pxBm_@*}Qc;G#FcN3j{do!a z4?7ibZT=daC$FyF6DRl&>-k2t-v3)S$hcApjfWQcJ9w(l-ilrfy@Xdo-dg-228myqD=LP|OtKi4C!Ix!X37dq2BwvF!W_Br? z@uZ(827?(mNpE*JCB$5bcbvV6iC%DoMYk)+yK358J^|jlHE)8;`|2s+2F+&)Jzis^ z{*pv5B~D&ACQ-nG`C{zd@_bL1bk7|>L zd~k;Y-~&b3i4PuE4Ymd7=mkLeLon{s8a%Kvat1Q*#5+P^%K>WY;G%WSSn~`QaDi(o zT;Nr>=EDV;a4A#Z#LOc&9dKctI9z#fp>D!e02lVqhRX^UDji&N;KE+$aHV=|HF(U1 z2llduD;_Q!PzF~VGR=wsIVHT_uK_8+G{txcpqu?Fy$7TbO5jyGeKlZDk^-*=nQH`G zCzxv#T$I6peKcIh$R&+Jum9B`=}OXQ^|%{ZM`(J@lma}KxT6=iFi>2?5dc3{ui&Om zV+j-+tc$|JHWIhR$AAVpm97RwD%?e)0hNqU1Mp1R+GPfg6aui>&`#nb(25s!1zK$f zPyj|x6`;{l7c;P(p-Y8dGT;D6kYfUw(SSM!P_ZtW;RyNnZ@^x__g@13yffgqKxQle z7XYyJe}*nP=$)u>5x$l{rWs%z0Gsz;f&HJLn;6K92i*C9i}NhPh4t^~@ZTqIY9KQS zU>5-F)c*=<1YPCdlv@|_j&k{}fy@@-3-E~(e1asV{V55uSYVQJ2VaEQ?Ozrj8>2JpXuVB(+QDd?GWkdTA01%|7;VECU9{29j${D&N$&=6en9}rX`UBt&( znJoFw09!PG%l_L4rvFQLSZU8%e004OS8X)*;CBLIvyKQV3fq^%`vUTu zN`Ld|Zwmc6=r51{3h2*De{<+BmHuYaUp)QAMWYR1EizU%06vdG=gW#3SH++uK+>j? zV~y=fyGz^CfKEaF4x!LilS%+zYCiNu+U|m64j@?%Z)#5Tyw>vKQGr)Hs2Rn>r#&cl zcq2ODLG;IiiWQGl+Jo2-ZwgZ~G!s4OH)|*!qMP}Y6M2j{~3>DAI^-Y zP5Z$sTHg=tOcvcsI&8}LZ}akVebTBe@%NS9#Hoa5>PX?slLD{D80hYUZb zp1SwxLxv=wEXlj>kl}ivCV5lSAwy3d$H3|i8wSC~#va?X$GJ_j4jcN&m&Ss3OdAW5 zFuZt;T3;Gvas4*Pv~@{_GilR{zZyn}LY}axDg>J+zhqPQ6NY3-$p6Wke#$UX=>L;< z-YLUi!IA2H>69Ur%x_Aq`Vjun z$Qb5pbK3CU1xZj zXjD;$De(h$cr)rTG(ES%``3EIjd8#H0{svpQ(EeDyRI{O$5N&KWyd!+Jb<>+IhIC#y z`N5_`t%eVIVP%TJWKZ5*+yfk@Wy+a-Y^<(;3<*gCS#l6z%$zTy09$OtM)SfL+J63 zO(S|6Ka%OgulW(&5t$S$zQpgs^?Uwv(TlhUDHs=>`h)t{_vybcy3AcBW69}9M2wRqCx z&9B*L_?%VSgbK*Y&3Kng&(XG05ajFeh7rN@Rfy5yQRUQM%HAU(Jo6>hk0BVyy~ZMc z6h8Vxg0|HX;-ewhfQKt6DcGkJImT^KWAQ5t9NC@r;x_mN8i`pE%+7r_a(2ucXx3DTr+a(eqOO!f{7{!>Dm#OO+ z5~p0{d{J||9=-QxiuXF69D~6Pn>%vh_rzS~nHVjmEeFo9QJ9{a3h+ob*~gI6l7D8Q zwu9{J=ok`(`CypFlqu7aMdYnzI(Y}`d_2m|Ol7C;!nBcA1WHuT;5@$xz_%`25FC9> z8z&Jtsb{8<)9&ZXJ&H{ngRmygFaY!oUQ2XP#lU1BA06w#9v089@n2^IX@I_y9-LtRY#4FfWfoj6M!h1nn?;`pfi zT{XURpEk_{*s0?&?;;2DLT=-ILBUlcVm``o$rlpcQVC742v4gBs(U(I<1qsyhga)& z2N67>Jd((iC<3DH_8k{@2h|_IPXx~OD-T1*gLT7IKMXwzW`4v1eu5wJ-BX-Q6Fi_= z5R#^iQZ(;VbD}+j(PTbiEJ0z^m0_dTZ8a!1c#^hLIMk!*Ia{Lf7QA~YD;-verbK~g zGfW%viHb8^&6qlpizA)g;6u!NQGC8yowHd~4>)3-&HQFF0W+%GeL)G9bXQFj40Fio zVV$!PhpKHt^Ilh`Hx>VtWC19<`?%|f=sJ#>MmG!U5B^`Jgdfyhn7(7$xWiWq8ZJ*B zjtr@X)LPeXBEJ_2VRFsB<0!n6b%3>nCmWgCjFe5qzrv#!rFsz0FE4}tR{8^9#imoB zvuRad)WFtX`kG}G-c3tPz(K7H(H?Q(+T`3!<=k#6xtq*(%Nb+HV5dV&I+E5HEp!=n9&ofRjcAa9OK6r9$Ak`!!gN-=osnA5yynZ zkLOp|Wx@%E0r6*oXK|PqCB6e6={Q+G1wv9(la%DTPzLNV)S%OJC0|g)^c~kxSi*P+ zjyS7Ucvb;u#Ay7;mxEVbhiO()0%k=y8;N(-Do8j?B*Ga~kR6^dGk}sD-yXabo3?o- zcT=~8{exNXP%9rS?^mS&ECP|8DgMFsIN5p)+`tQmx%q2E&q`k+hvBrVnWq`iNwb5* zqD_*Mpvxj?o~L+J6e=pJ*9^SIh4kR82m(;gxZwBL46fK$YU^n#uIuy-PA;f?ijU5x zdJ3hss7hP!Zk4t^*qpYuQ!osSalsc6%3mXI5ee0fY&;F$Qd{3hV)QZi0Q0+~i{B*X z*AI5+kU!YNb$C#z&7=lPZ|vm{Dzd9)P~CW5UuugsmDrH(tfHs%XL*ws2**aC5UQ^7 zxS)XnYVGVDY{cPerM5Qp6u93XjB?eON^R{B+i}=^8+K^OW|2+eVp1j?6B2q$6k+?h zg4nZEn}p*Gc@#wiG8J5K6@43P=J8!;T1<*_Ds3?!M4!;gPOw-R2k`OCrMHf_V7i1P%W|HblgN=QNo?PqF3*6GZT2{9RgX zc@xN^&cMe47qKpOR8=gi;KpX|;9L}%rEmz;aly4sgmD@H@zRDTn$hf5nFBn$n5p@R zP_Vrebs1;EA#OYbwg7irsr?S&Y1-(xBUB(9ibAMoiuck~bq@3J+!GZRTtw@Oi6kXY zL83#p#G-b+g%DJw_=Cy|$FQ-9po4k_)bl00^6Q{eY6skAA-CH0Qm zu*x;e9)wD;2hvjgn{S&mX;OVi-R^I|$!HR)a05xMg2tr#EuRZfg`m#JINecqD40(`G<~*j&G$CnizBJIOuOk_xppi4M>u@_06| z@;7a~4E^-GUCq&^=S(O}&yDR{@!;j{u4oqZUO`J{8OfpAc*qP7Hh zk}$v&(`{1l9vlgoK*QfLD1?~6T`g&U6dpu+7Pmve?xxU-Hd99(^V$*`Fd7!;nezpq z72Cm(X5L{*g4t&jZz`EUJPLY(^obs6y{m!pmu@P?RC8*FNgGMi6fo0seK7ifLbXcB z&|3NqHw*|R-#{3!b1~hD))<>MF6)kVn$4V5F&7sqiO$i-b;QVK%{rR|HjfrHM(YjS zXq!#w*|xf7>F!wf#43Tr2n+C!lPv6S7S{<L=QGW~sQ zj1KLkAvBvh-c8p%ex2It_zmB z;NQ3pbJas*VcVmy(7=cdL8X=S0MC1(j`OF|wwsXl28HBr5XEr4#90{>Gf4EO%1ZIkZ;2<-fgjX*(zHC%I7lNTTgC5;}u1nra?7n+!JA{I1X#wi6XG0Q?lHE)`5P<}hZ-Yf~-nEzl4|VpE za6f5{Q0k?OnUoR>qJdJxV`vW;e033JJ#;^6uR9V$W#KXM>O|7+iMkfgQ<6GSY@r^0 zv-(rQLB7m?9CP>2{=N1?l;wPcBFfLGv-0|mz7;ym^GzOFXdaQziYA~z%YqJOCBz}S z-33&^=7V({rgfDM`r`P8k~$D=ifQR-uoeo9sD-pdRnD6>J=_S@DPP_1^>IZ1p$`+gDXDn6sCk z6g*$V6Kp#TSpg76ws@e2;)+Wu-tU+Xx7qcp-c<4d8YJWqABrUcrP)qQC3g6`nkAF# zBphfl40Yu~Wi}8gc?g7JU*6TeL?e0(eWi~_Kx75tIhN;PgWwMk=sh#2rw#l4d`%UZ zn(UjUW^4p~5{&5sub)mR*VDhgajx**xttJgg@B?#%== zVYjPcK1>UTOgHaV_ayB0HwswZvIh(l<2o#s_*e_qMaig+@^FZGvF@R4N^TeDD3(|) z{bE{n3FM}kHs)EdVu2+Js35j9YG2L&TB2wtXb<^bm^KyEyYuOMY~pkvGZE4$9n>G?Dd>j6_nWRig_S=3Hj!`hJkemkZJ zlm{z}X>lfHqqRMd(}H(w07HG;-9hN+X{4;=nr=Sjkxo-BBaH?GNu>6DfN85_EAc&% zx&{3N?!wTCx`+Q>E!+2->qxgx&G?eAM{R?^-S@kC8pf5a(v2m)`TJErDjDk#sFnUk z{l4E6{%iHD>!``qY%-O62C0c9GG-2k?7&H!kP2**)aHG)s^3*^+V>m3ZN6O%LaB&_ zMi}qf-T*x!yzaOj%-PmJG3nt|L}tCl5-VpWh7*k4N|4-=O1}AH93u zs1`0$pkjQDl{1KV&=0G-O!I@Qqe^P}{h9kq+N*awp$pV7%8~|)W)Iru3ctTi+V@)^ z4yRB1`E3{>ih;_;$LM4Po01^$5k>XS) z!@fM$AKbm~s;NM1qQ&Bz-xK{9Bx1@w1rCo<4=zKI#!&XKbU+f<=v9B=Fj%;h!y*{Y ze^df%#C>K)`U$&GiSV+oRzwU6OvTxa|90Om&=q5^!U5o(yl;bPd6ss|U z5x>A467$2NvETW^VT|PI=vTlVtN}jjC}cqMu)+r2P{Q51#@rczUIQB4*)NLum9)>l z8j)kw#%1VrqAuIO{oS{L|DQO;{7D`6OcjX(7F0C=bgZi^I^@^N9&Ij4bv9pw7Mm1V zjQ>&tdzwHGxt>C;Tbf77Eap~=W# zDhJGLOS=PRxOw*AKpix#BuX9xv^Wu31YVLn7)K^WfhxcP%amjv7$<%ZkZ>wd)c(WN zthMiaUATur)?5ijI@b|qq1)|=v4(?~HY&~@NTidY4o#KdaczKhi&c61eswCay`YUn zxtUER?bJDhBmlbcm<0}Q#55v#EuOW#w83Ym6FeJmPNRRjkgNLZ0|9#vJ7%J2vr({A zL&&8CG*~fXM)RhG7t!>v7go5+a;d=ofoz#U#3|4(ELhRCv7o18IP=3W-KRbnG>F5& z|LK;XX8|gQ)Q+zf&%|wmf=qX{v2hVo@dU`KiivSZ$-EW90e0FaOeFW?*vK~ZE2#%be3%!!>~ZR8q&28m zCxM?Sdnc3pBNCsZakrJSZSEFVjY@G#d@fGhr{& zCVOlf#+3)Qg&quhVeCvBT&uFjwg3XhW)IMC8kVQgC|W2JAw5q^YN#rkzC#Hfh=AaK zBofEQ+#(^HDRUapOdFj&sS^0fqK$yFVW&rnzrr^dV-Nm)t)6s_uk!yUe2Vhrs42w6 zUDL)5rZ^~1FV{D}T;Eo@Jc>%o72-|n4!RoK3lG9rlyKmQuX)TOud*u^Gp3}phlW#{ z5R||WxL^n-8+cvTf;_gUL7E1^SBK|7gr1~HiS?cw3QskXRBMB%W5<)kfEi62ul0OJ z7044)1^ohjc~kLJpho%hBz^*9(8D}WkK`R5Wg|H-U*FHWd{Hog2Bw?~*snzAy8*Kd zThdOW7rvkdOb{dTO@ttzm(&$*z6!vQHg)UiNxhClHPWp|cM|=@=Wa(|DD(++h#;02 zg&@3P9-IP|D<2wf0b(oI4-=%_cXZ#8eZQ(z=tJd0nsd2=%Lloi(3tkMQX4vpDVKn; zAn-eqvUt!T8O#KbHKd{^O7F-6B&C@N&?s6cHogJmxqTJchs$z?FXUC|vmHB4V54NjKvQ0PFl z>DlhgOT@ot0uXW?X>*kcwO1g9w1}b{Li(nX!}t%_7qK{s=~*PjQd4Z*Xa%Z|pdrjMI$}2FTq;f-R1doNIE1E!5H zQD;xB#BdAc7@AZ6y$Ofv_bLgE7!C8RT|yC1%^*G1Ceub_IpL?YozLjeV8z@|R-HUc z9v>(z?q$OO7qrH-9~~L)ycKBB5EKCq)qaoefDv~O+&WTN4peV`|GoF#tN#sSL`{2w zu;nC_sh=;WghH31K-5zxD`kpl(`^>_wV;0-)}{Z(QPX+N!KrFYO`xwH>eZE zzoIoYZPfZ(IN}7$kiJ60<2q{oX~%&;a$V_E^lYbE3Se?gC5+RKBQxfCQrb7lL;$sb z#v70{nhMf@u7lh&ZA`yT@x-~*26POI7XiG6@UKAlOc-JJ5{B?aK-Z^z^UPXSIHqC` zfbx3-(u!5o=1D77)1yp9&!!>SM+;iJnvZGKXeS@YKSXjt3&wH`7d0d}BqYv-4rp4H z@+(=}LU}|h4IG6mW}(oaVr8<6CJ(cBEyAJDAOxni07?tL8L+K;j_*5y!O$ZjbvhgB z&}NVgAf6O(LW8BR8MdpVR#2a_6LN%G5F0`QCLK!@*b%xMPSl42&vI5xs9lr-PG%yE zED-9j6&?v)hekAcM<^NEZ&!H^0|IG5q0@B=`$W2vC%+p4CBo%DE~-C-4xeO6#muTg zV}r0p$Z5!gi0%_wkMaW7SVC(LY5~zMlst13BJV|bsK1BSW2~73rxY3jU(=?Ex;E4Z zC(@mz!X2ta0ws#i^xSedLZ9LfNe!f=7XNYK5HVU^u9mw_Or?&dne-o5JoIXMJB@mt z?5lG*t>6Gti4(8x7ae%^P?CiB~|Wd#XnR}$XQ8t7HY(*9xuhH@8Ti7twT zttQwLYZ)^*?(ys0?)mU_yB6TTsFD0ki>wIE??%7s0>r$FAktBET>-+BTw2FPK%s6$ zgKkj_l{m#hpn}1lNsWLrv2cR>MV79caMDsw+i5Df7A0R)#=#Hxt1Jj0NI}45#CRP~ zmEqbseyCkjg3{fe5x9hU$B^W`wGt|Ry4y_|Q#^^iOdA~|IrUVj)Qbt0!Xx0YM3qIT za;1Z6^iy3{xbVfQx>7Zl9T>Yt!R^qgE<0G4=|8TDschQKgQo-n@*r`4-T}WH_#NEgcogDoK;TZPwnt<~4)kRZ zoz$7icGr*X=tIWy+tF4tv14%*sI4N>?S9@={1&hW*d*SKLWnE$9xBf5NC|6QkrHN@ zv6Hz2eH`ynBJWVMf=P^W@rVl_X*Gow43WrxK!}DUJNnfiZJgRfIqQB#Wdx~WBqQQM z>f>Kkg28^!AV`3)Azy@bRpce_Wqh=Z03f3ZT;PWRZ4iQLQ|(8n8ULglYLi~k;Q}XEu!27W=FvDcrOL(jN(I7IEOn62bgd^F(7TY3SEvw-Enf}#*{z> zdJaytiP#YPEm4JBKmkr_VcjbTN4J%ZNF7B8;+alQ@RS{4?b={st_SKMaoOFhkEOV8 z;`oV{1)DjLFL^Jromf>8$zoMWB-{0m>fIB_hDEX~IxREWl#>}vnPUk=SpZRLpMc43 zkfbb<<7qbole|BnEUj7HGq=a%QrelvNE2-)M0UbFm1J56CD?9SH_?&{(5djsgGQW4 zz$kM_iq4|6suATHQGj47pT{gM)?Q$~gm3yEQX9LUHkG7;^AIbKXX(O4)|m7^izLGO zvW_A_{euXJ(S;=hqAV!cTNp=y26Y5tVTMM+K!T5gBu}q>O&gY?u(ZV$o)H#26)EEo z&6J=31G)z2;1NYD<&w(~E>e#vd|cP?`yv1lJai^3=KdXmEC6wHc<-UfY$i84Lm-~H zWG~K_e-e|lV7(AnC$MT^Vhj}{SX!HYnS@t#9830}ec#asgtTBVsHfYerv-Nn{A0bq z`H<%KT-eRYErM??&{N}8!EKY&ED51ioLX&*QnRAmwrKUq81;r&H7gn**znV1-Kw&p zIi#QKSr`YZsTh(3`GPLU0)W@%iPNch8<;g1NJuCeJrKc6K1Z|~%|dj1$IemS=f@cP zdW|2L^u5)5N&6g*o$T{kS9Xt4;*kF&V44bXKMFN29hJKfV!gp}P02~ejc!{ke+4Id{VzxLRpvwn&@@{Yx*w$gP(ssBMxkb;n~Kk)SA=Y(gZQJ=pSthVSofW- zpgVBG!i6OS0J7v&idbxM;uSr8;(#@nRJQ>|vPQrG(Cn+iJ~OG1j^yItqN>NNQsKCV zU>KrLlr|cJsW6uW(B%b!vGj?&=>1@0#APbx0GpP2harGg@j!yA4=4nrX6rrk<7DCr z1z_?bG!F;&q<%o5N=}jj>WRB9;EV_Ej;w$W)S=9&)oLSuuzEOIvjaY|cKIcA?yIkF z!A>)|EoiguXi2LqJjNEyX#~(AIeETpBk|H@rhv8+0>;0GyeOx^cF3d~zft~wk za)f#qaf_)pvAggriCU&1LAVoXP@u>fjzs+jy8YeDOvQv7CMu_a$YIL3^Yw-6J<!NAJJD4!C!*v<5}pl9za!csyZxv^zYx)vUuRJI;z4LaE5isVPj3;Ha7At+jwsvs>olg;HL zI}IIK>O@S&St^>8d(eyNUgmfz>>n=ZaAhE)mrL2sW8}MtsADQQ2hr3usMgkYNy=j{ zVri+{JCbQcPYlWQ4aaq|{0pJK1KfdXpxTxByAauaS2+Uc|IxKJcQiyHJ77?NzjqCA zL6RKDr92@8^uHgxD(RPil!Kws-YU`yEno%3#sxW9+(%@q@LFAEW8{{ zFxWj`N*m=ah;m#3WoW3YjW0BYg<J&`Uh*WDVTS8f;gR@2Orwkc1qYEksktU&V|1q6MEJ0dt9MJ*?ZKeny=HAkIV=o^kfI2np+l>Q2-` z=fO8#A%0Zd)DQK*T1+_vLD*^b>tDu~C`<5iup`wwwL16{`rFX#*iM43iX;%X96-Q@ zEGZb1!kAq^w8}-eQQBMRkJYINg%MS+EciDnFo;xgFs!3I-8IbEh{6;40s%Vd6WXqC zq)k{+Z26$M9kkB{PvwD$&=@$Z;FfeX&O%8qvjS_@*8|!?!X1&~z#n;Fzg)1N9h9&l zT0Y8__$V|S$-y1&F{1AW{Usi$VZNKg(E&27Z6TEi&{T+ziC`8COgr-MOs!(;&L~Mm zbroARsiCs zya4GiVKo(4{sq#%(s6wlI4Gbn=!A)eEVbWc2+&AmMydp5S|s)1EOiU5l+zR-SA~Vh z(V_4l{aB=JMp~94#)A~sF;FuaAO~hwMeLm+N>Eet>9ss;2npj^yRTE3-bN%Q!GQJX ztdVy>J@PqnU7Lx)Idp{T99PNI(Nt2E;_ zuBajr7%=IDe8nPuTveRZ533rHUC-C_)u8IgtzDN08J-)&C8=pInvvL$a@XJ=<=MmSjdq z8qKOigeQocD8r65)>w*_XYI5U3%cVpypuTO`$3wy-zTyG?MUws^z~Yk1)5ZY(|{;! z$beG4pmUo+B1;$yf2vp07@{8uV`35p&9v|(;Qx3OSSA+p}Qo zg()OzF}9ty?{VW?Da}8RL_1R*F{u(GN2`xx0tyWo#sfMv39Nh&Kau)}@uqd#;TO;E zsh@!fNIP9koM|z$d!gFk!zeXU`OqQ$1xNr8_5aLaMZ>giH`zWgF$^E{9x`QxdmIR> zkVGN_3}ws$cRB)@)rRCV-FFwJM;LE`#ae%!TFgknBM=3otv`r%gu#1gkkuc%m#LVv z`Xx#-Ogm^dGY|seDb~%}7EIb94=nv9xs6G^<0XiKV4S96%%-sD=^zw2ffy?YwVOJb z<+Z4S%9$}OzKseeLg{n()xOYSe<4g~^|-*I=;>ateH?_0(f0pSI<_*LeB0*r03BJ( z2b--AufXFgJRkuc@t`^oR3_AsccjM==JW&|wp;P4896CKpblwW737OZ!JD)uE&+bK zwU8_n8V7xF1kk}TQ1I3mUXy#8Pnhi?R&Kj>^J)ZP=_OPE&IY^z&CPfKw`eU;3Ek_gCaf%(ylB> z#f;B{4^r<)WfDrZBazN&kcb}0brQtb?QA9d!i1LcqrE1xafC2Y=e3!QLxoizd3#xO zmw2Bs8z<`L>rl3mMe>dtY>e~PnX%0DE1frSu<_bUSL0J{L>735HV6btJ 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

P9Lstsv7It!Y$1*{p@4>2a$wsRu^u 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Ϗhp= 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 z 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 SKg&}W67wfbm>|aa$^QeT!J4K3 diff --git a/src/external/mini_al.c b/src/external/mini_al.c index 1ce944190..eabb63654 100644 --- a/src/external/mini_al.c +++ b/src/external/mini_al.c @@ -1,4 +1,8 @@ // The implementation of mini_al needs to #include windows.h which means it needs to go into // it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc. #define MINI_AL_IMPLEMENTATION -#include "mini_al.h" \ No newline at end of file +#define MAL_NO_JACK +#define MAL_NO_OPENAL +#define MAL_NO_SDL +#define MAL_NO_NULL +#include "mini_al.h" diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 4314343de..829a73965 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1,5 +1,5 @@ // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. -// mini_al - v0.8.3 - 2018-07-15 +// mini_al - v0.8.5-rc - 2018-xx-xx // // David Reid - davidreidsoftware@gmail.com @@ -20,7 +20,9 @@ // - ALSA // - PulseAudio // - JACK -// - OSS +// - sndio (OpenBSD) +// - audioio (NetBSD) +// - OSS (FreeBSD) // - OpenSL|ES (Android only) // - OpenAL // - SDL @@ -65,8 +67,8 @@ // // Building for BSD // ---------------- -// The BSD build uses OSS. Requires linking to -lpthread and -lm. Also requires linking to -lossaudio on {Open,Net}BSD, -// but not FreeBSD. +// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and +// FreeBSD uses OSS. // // Building for Android // -------------------- @@ -116,8 +118,9 @@ // - If mal_device_init() is called with a device that's not aligned to the platform's natural alignment // boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this // is that it depends on members of mal_device being correctly aligned for atomic assignments. -// - Sample data is always little-endian and interleaved. For example, mal_format_s16 means signed 16-bit +// - Sample data is always native-endian and interleaved. For example, mal_format_s16 means signed 16-bit // integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it. +// - The sndio backend is currently only enabled on OpenBSD builds. // // // @@ -180,6 +183,12 @@ // #define MAL_NO_COREAUDIO // Disables the Core Audio backend. // +// #define MAL_NO_SNDIO +// Disables the sndio backend. +// +// #define MAL_NO_AUDIOIO +// Disables the audioio backend. +// // #define MAL_NO_OSS // Disables the OSS backend. // @@ -207,6 +216,10 @@ // #define MAL_NO_DECODING // Disables the decoding APIs. // +// #define MAL_NO_DEVICE_IO +// Disables playback and recording. This will disable mal_context and mal_device APIs. This is useful if you only want to +// use mini_al's data conversion and/or decoding APIs. +// // #define MAL_NO_STDIO // Disables file IO APIs. // @@ -275,82 +288,6 @@ extern "C" { #endif #endif -// Some backends are only supported on certain platforms. -#if defined(MAL_WIN32) - #define MAL_SUPPORT_WASAPI - #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's. - #define MAL_SUPPORT_DSOUND - #define MAL_SUPPORT_WINMM - #define MAL_SUPPORT_JACK // JACK is technically supported on Windows, but I don't know how many people use it in practice... - #endif -#endif -#if defined(MAL_UNIX) - #if defined(MAL_LINUX) - #if !defined(MAL_ANDROID) // ALSA is not supported on Android. - #define MAL_SUPPORT_ALSA - #endif - #endif - #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) - #define MAL_SUPPORT_PULSEAUDIO - #define MAL_SUPPORT_JACK - #endif - #if defined(MAL_ANDROID) - #define MAL_SUPPORT_OPENSL - #endif - #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) - #define MAL_SUPPORT_OSS - #endif -#endif -#if defined(MAL_APPLE) - #define MAL_SUPPORT_COREAUDIO -#endif - -#define MAL_SUPPORT_SDL // All platforms support SDL. - -// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now. -#if !defined(MAL_EMSCRIPTEN) -#define MAL_SUPPORT_OPENAL -#define MAL_SUPPORT_NULL // All platforms support the null backend. -#endif - - -#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI) - #define MAL_ENABLE_WASAPI -#endif -#if !defined(MAL_NO_DSOUND) && defined(MAL_SUPPORT_DSOUND) - #define MAL_ENABLE_DSOUND -#endif -#if !defined(MAL_NO_WINMM) && defined(MAL_SUPPORT_WINMM) - #define MAL_ENABLE_WINMM -#endif -#if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA) - #define MAL_ENABLE_ALSA -#endif -#if !defined(MAL_NO_PULSEAUDIO) && defined(MAL_SUPPORT_PULSEAUDIO) - #define MAL_ENABLE_PULSEAUDIO -#endif -#if !defined(MAL_NO_JACK) && defined(MAL_SUPPORT_JACK) - #define MAL_ENABLE_JACK -#endif -#if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO) - #define MAL_ENABLE_COREAUDIO -#endif -#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS) - #define MAL_ENABLE_OSS -#endif -#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL) - #define MAL_ENABLE_OPENSL -#endif -#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL) - #define MAL_ENABLE_OPENAL -#endif -#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL) - #define MAL_ENABLE_SDL -#endif -#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL) - #define MAL_ENABLE_NULL -#endif - #include // For size_t. #ifndef MAL_HAS_STDINT @@ -479,122 +416,6 @@ typedef mal_uint16 wchar_t; #endif -// Thread priorties should be ordered such that the default priority of the worker thread is 0. -typedef enum -{ - mal_thread_priority_idle = -5, - mal_thread_priority_lowest = -4, - mal_thread_priority_low = -3, - mal_thread_priority_normal = -2, - mal_thread_priority_high = -1, - mal_thread_priority_highest = 0, - mal_thread_priority_realtime = 1, - mal_thread_priority_default = 0 -} mal_thread_priority; - -typedef struct -{ - mal_context* pContext; - - union - { -#ifdef MAL_WIN32 - struct - { - /*HANDLE*/ mal_handle hThread; - } win32; -#endif -#ifdef MAL_POSIX - struct - { - pthread_t thread; - } posix; -#endif - - int _unused; - }; -} mal_thread; - -typedef struct -{ - mal_context* pContext; - - union - { -#ifdef MAL_WIN32 - struct - { - /*HANDLE*/ mal_handle hMutex; - } win32; -#endif -#ifdef MAL_POSIX - struct - { - pthread_mutex_t mutex; - } posix; -#endif - - int _unused; - }; -} mal_mutex; - -typedef struct -{ - mal_context* pContext; - - union - { -#ifdef MAL_WIN32 - struct - { - /*HANDLE*/ mal_handle hEvent; - } win32; -#endif -#ifdef MAL_POSIX - struct - { - pthread_mutex_t mutex; - pthread_cond_t condition; - mal_uint32 value; - } posix; -#endif - - int _unused; - }; -} mal_event; - - -#define MAL_MAX_PERIODS_DSOUND 4 -#define MAL_MAX_PERIODS_OPENAL 4 - -// Standard sample rates. -#define MAL_SAMPLE_RATE_8000 8000 -#define MAL_SAMPLE_RATE_11025 11025 -#define MAL_SAMPLE_RATE_16000 16000 -#define MAL_SAMPLE_RATE_22050 22050 -#define MAL_SAMPLE_RATE_24000 24000 -#define MAL_SAMPLE_RATE_32000 32000 -#define MAL_SAMPLE_RATE_44100 44100 -#define MAL_SAMPLE_RATE_48000 48000 -#define MAL_SAMPLE_RATE_88200 88200 -#define MAL_SAMPLE_RATE_96000 96000 -#define MAL_SAMPLE_RATE_176400 176400 -#define MAL_SAMPLE_RATE_192000 192000 -#define MAL_SAMPLE_RATE_352800 352800 -#define MAL_SAMPLE_RATE_384000 384000 - -#define MAL_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 // For simplicity, mini_al does not support PCM samples that are not byte aligned. -#define MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8 -#define MAL_MIN_CHANNELS 1 -#define MAL_MAX_CHANNELS 32 -#define MAL_MIN_SAMPLE_RATE MAL_SAMPLE_RATE_8000 -#define MAL_MAX_SAMPLE_RATE MAL_SAMPLE_RATE_384000 -#define MAL_SRC_SINC_MIN_WINDOW_WIDTH 2 -#define MAL_SRC_SINC_MAX_WINDOW_WIDTH 32 -#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH 16 -#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8 -#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256 - typedef mal_uint8 mal_channel; #define MAL_CHANNEL_NONE 0 #define MAL_CHANNEL_MONO 1 @@ -688,38 +509,33 @@ typedef int mal_result; #define MAL_ACCESS_DENIED -32 #define MAL_TOO_LARGE -33 -typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); -typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); -typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); -typedef void (* mal_stop_proc)(mal_device* pDevice); +// Standard sample rates. +#define MAL_SAMPLE_RATE_8000 8000 +#define MAL_SAMPLE_RATE_11025 11025 +#define MAL_SAMPLE_RATE_16000 16000 +#define MAL_SAMPLE_RATE_22050 22050 +#define MAL_SAMPLE_RATE_24000 24000 +#define MAL_SAMPLE_RATE_32000 32000 +#define MAL_SAMPLE_RATE_44100 44100 +#define MAL_SAMPLE_RATE_48000 48000 +#define MAL_SAMPLE_RATE_88200 88200 +#define MAL_SAMPLE_RATE_96000 96000 +#define MAL_SAMPLE_RATE_176400 176400 +#define MAL_SAMPLE_RATE_192000 192000 +#define MAL_SAMPLE_RATE_352800 352800 +#define MAL_SAMPLE_RATE_384000 384000 -typedef enum -{ - mal_backend_null, - mal_backend_wasapi, - mal_backend_dsound, - mal_backend_winmm, - mal_backend_alsa, - mal_backend_pulseaudio, - mal_backend_jack, - mal_backend_coreaudio, - mal_backend_oss, - mal_backend_opensl, - mal_backend_openal, - mal_backend_sdl -} mal_backend; - -typedef enum -{ - mal_device_type_playback, - mal_device_type_capture -} mal_device_type; - -typedef enum -{ - mal_share_mode_shared = 0, - mal_share_mode_exclusive, -} mal_share_mode; +#define MAL_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 // For simplicity, mini_al does not support PCM samples that are not byte aligned. +#define MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8 +#define MAL_MIN_CHANNELS 1 +#define MAL_MAX_CHANNELS 32 +#define MAL_MIN_SAMPLE_RATE MAL_SAMPLE_RATE_8000 +#define MAL_MAX_SAMPLE_RATE MAL_SAMPLE_RATE_384000 +#define MAL_SRC_SINC_MIN_WINDOW_WIDTH 2 +#define MAL_SRC_SINC_MAX_WINDOW_WIDTH 32 +#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH 32 +#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8 +#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256 typedef enum { @@ -766,6 +582,7 @@ typedef enum mal_standard_channel_map_rfc3551, // Based off AIFF. mal_standard_channel_map_flac, mal_standard_channel_map_vorbis, + mal_standard_channel_map_sndio, // www.sndio.org/tips.html mal_standard_channel_map_default = mal_standard_channel_map_microsoft } mal_standard_channel_map; @@ -775,72 +592,6 @@ typedef enum mal_performance_profile_conservative } mal_performance_profile; -typedef union -{ -#ifdef MAL_SUPPORT_WASAPI - wchar_t wasapi[64]; // WASAPI uses a wchar_t string for identification. -#endif -#ifdef MAL_SUPPORT_DSOUND - mal_uint8 dsound[16]; // DirectSound uses a GUID for identification. -#endif -#ifdef MAL_SUPPORT_WINMM - /*UINT_PTR*/ mal_uint32 winmm; // When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. -#endif -#ifdef MAL_SUPPORT_ALSA - char alsa[256]; // ALSA uses a name string for identification. -#endif -#ifdef MAL_SUPPORT_PULSEAUDIO - char pulse[256]; // PulseAudio uses a name string for identification. -#endif -#ifdef MAL_SUPPORT_JACK - int jack; // JACK always uses default devices. -#endif -#ifdef MAL_SUPPORT_COREAUDIO - char coreaudio[256]; // Core Audio uses a string for identification. -#endif -#ifdef MAL_SUPPORT_OSS - char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device. -#endif -#ifdef MAL_SUPPORT_OPENSL - mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification. -#endif -#ifdef MAL_SUPPORT_OPENAL - char openal[256]; // OpenAL seems to use human-readable device names as the ID. -#endif -#ifdef MAL_SUPPORT_SDL - int sdl; // SDL devices are identified with an index. -#endif -#ifdef MAL_SUPPORT_NULL - int nullbackend; // The null backend uses an integer for device IDs. -#endif -} mal_device_id; - -typedef struct -{ - // Basic info. This is the only information guaranteed to be filled in during device enumeration. - mal_device_id id; - char name[256]; - - // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize - // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion - // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided - // here mainly for informational purposes or in the rare case that someone might find it useful. - // - // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices(). - mal_uint32 formatCount; - mal_format formats[mal_format_count]; - mal_uint32 minChannels; - mal_uint32 maxChannels; - mal_uint32 minSampleRate; - mal_uint32 maxSampleRate; -} mal_device_info; - -typedef struct -{ - mal_int64 counter; -} mal_timer; - - typedef struct mal_format_converter mal_format_converter; typedef mal_uint32 (* mal_format_converter_read_proc) (mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData); @@ -929,6 +680,12 @@ typedef enum mal_src_sinc_window_function_default = mal_src_sinc_window_function_hann } mal_src_sinc_window_function; +typedef struct +{ + mal_src_sinc_window_function windowFunction; + mal_uint32 windowWidth; +} mal_src_config_sinc; + typedef struct { mal_uint32 sampleRateIn; @@ -944,11 +701,7 @@ typedef struct void* pUserData; union { - struct - { - mal_src_sinc_window_function windowFunction; - mal_uint32 windowWidth; - } sinc; + mal_src_config_sinc sinc; }; } mal_src_config; @@ -1007,11 +760,7 @@ typedef struct void* pUserData; union { - struct - { - mal_src_sinc_window_function windowFunction; - mal_uint32 windowWidth; - } sinc; + mal_src_config_sinc sinc; }; } mal_dsp_config; @@ -1033,6 +782,637 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_dsp }; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DATA CONVERSION +// =============== +// +// This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel Maps +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Helper for retrieving a standard channel map. +void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Copies a channel map. +void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels); + + +// Determines whether or not a channel map is valid. +// +// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but +// is usually treated as a passthrough. +// +// Invalid channel maps: +// - A channel map with no channels +// - A channel map with more than one channel and a mono channel +mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Helper for comparing two channel maps for equality. +// +// This assumes the channel count is the same between the two. +mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]); + +// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE). +mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Helper for determining whether or not a channel is present in the given channel map. +mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion +// ================= +// The format converter serves two purposes: +// 1) Conversion between data formats (u8 to f32, etc.) +// 2) Interleaving and deinterleaving +// +// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for +// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You +// can implement both, but it's not recommended as it just introduces unnecessary complexity. +// +// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved(). +// +// Dithering +// --------- +// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so. +// +// pConfig->ditherMode = mal_dither_mode_rectangle; +// +// The different dithering modes include the following, in order of efficiency: +// - None: mal_dither_mode_none +// - Rectangle: mal_dither_mode_rectangle +// - Triangle: mal_dither_mode_triangle +// +// Note that even if the dither mode is set to something other than mal_dither_mode_none, it will be ignored for conversions where dithering is not needed. +// Dithering is available for the following conversions: +// - s16 -> u8 +// - s24 -> u8 +// - s32 -> u8 +// - f32 -> u8 +// - s24 -> s16 +// - s32 -> s16 +// - f32 -> s16 +// +// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a format converter. +mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter); + +// Reads data from the format converter as interleaved channels. +mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData); + +// Reads data from the format converter as deinterleaved channels. +mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + + +// Helper for initializing a format converter config. +mal_format_converter_config mal_format_converter_config_init_new(void); +mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData); +mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel Routing +// =============== +// There are two main things you can do with the channel router: +// 1) Rearrange channels +// 2) Convert from one channel count to another +// +// Channel Rearrangement +// --------------------- +// A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel +// count for both the input and output with channel maps that contain the same channels (in a different order). +// +// Channel Conversion +// ------------------ +// The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the +// router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do +// is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all +// input channels will be averaged, excluding any None and LFE channels. +// +// The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the +// channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes. +// 1) Simple +// Unmatched channels are silenced. +// 2) Planar Blending +// Channels are blended based on a set of planes that each speaker emits audio from. +// +// Planar Blending +// --------------- +// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker. +// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left +// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map +// contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio +// data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel +// positions. +// +// As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center +// speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane. +// Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their +// planar relationship (thus the name of this blending technique). +// +// Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total +// volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel +// position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio +// from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left +// channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume +// from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker. +// +// Usage +// ----- +// To use the channel router you need to specify three things: +// 1) The input channel count and channel map +// 2) The output channel count and channel map +// 3) The mixing mode to use in the case where a 1:1 mapping is unavailable +// +// Note that input and output data is always deinterleaved 32-bit floating point. +// +// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration, +// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. +// +// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a channel router where it is assumed that the input data is non-interleaved. +mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter); + +// Reads data from the channel router as deinterleaved channels. +mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + +// Helper for initializing a channel router config. +mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Sample Rate Conversion +// ====================== +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a sample rate conversion object. +mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC); + +// Dynamically adjusts the input sample rate. +// +// DEPRECATED. Use mal_src_set_sample_rate() instead. +mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// DEPRECATED. Use mal_src_set_sample_rate() instead. +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); + +// Dynamically adjusts the sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut); + +// Reads a number of frames. +// +// Returns the number of frames actually read. +mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + + +// Helper for creating a sample rate conversion config. +mal_src_config mal_src_config_init_new(void); +mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DSP +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a DSP object. +mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP); + +// Dynamically adjusts the input sample rate. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +// +// DEPRECATED. Use mal_dsp_set_sample_rate() instead. +mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +// +// DEPRECATED. Use mal_dsp_set_sample_rate() instead. +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut); + + +// Reads a number of frames and runs them through the DSP processor. +mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData); + +// Helper for initializing a mal_dsp_config object. +mal_dsp_config mal_dsp_config_init_new(void); +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData); +mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData); + + +// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to +// determine the required size of the output buffer. +// +// A return value of 0 indicates an error. +// +// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. +mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn); +mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Miscellaneous Helpers +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// malloc(). Calls MAL_MALLOC(). +void* mal_malloc(size_t sz); + +// realloc(). Calls MAL_REALLOC(). +void* mal_realloc(void* p, size_t sz); + +// free(). Calls MAL_FREE(). +void mal_free(void* p); + +// Performs an aligned malloc, with the assumption that the alignment is a power of 2. +void* mal_aligned_malloc(size_t sz, size_t alignment); + +// Free's an aligned malloc'd buffer. +void mal_aligned_free(void* p); + +// Retrieves a friendly name for a format. +const char* mal_get_format_name(mal_format format); + +// Blends two frames in floating point format. +void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); + +// Retrieves the size of a sample in bytes for the given format. +// +// This API is efficient and is implemented using a lookup table. +// +// Thread Safety: SAFE +// This is API is pure. +mal_uint32 mal_get_bytes_per_sample(mal_format format); +static MAL_INLINE mal_uint32 mal_get_bytes_per_frame(mal_format format, mal_uint32 channels) { return mal_get_bytes_per_sample(format) * channels; } + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DEVICE I/O +// ========== +// +// This section contains the APIs for device playback and capture. Here is where you'll find mal_device_init(), etc. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MAL_NO_DEVICE_IO +// Some backends are only supported on certain platforms. +#if defined(MAL_WIN32) + #define MAL_SUPPORT_WASAPI + #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's. + #define MAL_SUPPORT_DSOUND + #define MAL_SUPPORT_WINMM + #define MAL_SUPPORT_JACK // JACK is technically supported on Windows, but I don't know how many people use it in practice... + #endif +#endif +#if defined(MAL_UNIX) + #if defined(MAL_LINUX) + #if !defined(MAL_ANDROID) // ALSA is not supported on Android. + #define MAL_SUPPORT_ALSA + #endif + #endif + #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) + #define MAL_SUPPORT_PULSEAUDIO + #define MAL_SUPPORT_JACK + #endif + #if defined(MAL_ANDROID) + #define MAL_SUPPORT_OPENSL + #endif + #if defined(__OpenBSD__) // <-- Change this to "#if defined(MAL_BSD)" to enable sndio on all BSD flavors. + #define MAL_SUPPORT_SNDIO // sndio is only supported on OpenBSD for now. May be expanded later if there's demand. + #endif + #if defined(__NetBSD__) + #define MAL_SUPPORT_AUDIOIO // Only support audioio on platforms with known support. + #endif + #if defined(__FreeBSD__) || defined(__DragonFly__) + #define MAL_SUPPORT_OSS // Only support OSS on specific platforms with known support. + #endif +#endif +#if defined(MAL_APPLE) + #define MAL_SUPPORT_COREAUDIO +#endif + +#define MAL_SUPPORT_SDL // All platforms support SDL. + +// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now. +#if !defined(MAL_EMSCRIPTEN) +#define MAL_SUPPORT_OPENAL +#define MAL_SUPPORT_NULL // All platforms support the null backend. +#endif + + +#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI) + #define MAL_ENABLE_WASAPI +#endif +#if !defined(MAL_NO_DSOUND) && defined(MAL_SUPPORT_DSOUND) + #define MAL_ENABLE_DSOUND +#endif +#if !defined(MAL_NO_WINMM) && defined(MAL_SUPPORT_WINMM) + #define MAL_ENABLE_WINMM +#endif +#if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA) + #define MAL_ENABLE_ALSA +#endif +#if !defined(MAL_NO_PULSEAUDIO) && defined(MAL_SUPPORT_PULSEAUDIO) + #define MAL_ENABLE_PULSEAUDIO +#endif +#if !defined(MAL_NO_JACK) && defined(MAL_SUPPORT_JACK) + #define MAL_ENABLE_JACK +#endif +#if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO) + #define MAL_ENABLE_COREAUDIO +#endif +#if !defined(MAL_NO_SNDIO) && defined(MAL_SUPPORT_SNDIO) + #define MAL_ENABLE_SNDIO +#endif +#if !defined(MAL_NO_AUDIOIO) && defined(MAL_SUPPORT_AUDIOIO) + #define MAL_ENABLE_AUDIOIO +#endif +#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS) + #define MAL_ENABLE_OSS +#endif +#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL) + #define MAL_ENABLE_OPENSL +#endif +#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL) + #define MAL_ENABLE_OPENAL +#endif +#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL) + #define MAL_ENABLE_SDL +#endif +#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL) + #define MAL_ENABLE_NULL +#endif + + +typedef enum +{ + mal_backend_null, + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_alsa, + mal_backend_pulseaudio, + mal_backend_jack, + mal_backend_coreaudio, + mal_backend_sndio, + mal_backend_audioio, + mal_backend_oss, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl +} mal_backend; + +// Thread priorties should be ordered such that the default priority of the worker thread is 0. +typedef enum +{ + mal_thread_priority_idle = -5, + mal_thread_priority_lowest = -4, + mal_thread_priority_low = -3, + mal_thread_priority_normal = -2, + mal_thread_priority_high = -1, + mal_thread_priority_highest = 0, + mal_thread_priority_realtime = 1, + mal_thread_priority_default = 0 +} mal_thread_priority; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hThread; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_t thread; + } posix; +#endif + + int _unused; + }; +} mal_thread; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hMutex; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + } posix; +#endif + + int _unused; + }; +} mal_mutex; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hEvent; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + pthread_cond_t condition; + mal_uint32 value; + } posix; +#endif + + int _unused; + }; +} mal_event; + + +#define MAL_MAX_PERIODS_DSOUND 4 +#define MAL_MAX_PERIODS_OPENAL 4 + +typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); +typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); +typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); +typedef void (* mal_stop_proc)(mal_device* pDevice); + +typedef enum +{ + mal_device_type_playback, + mal_device_type_capture +} mal_device_type; + +typedef enum +{ + mal_share_mode_shared = 0, + mal_share_mode_exclusive, +} mal_share_mode; + +typedef union +{ +#ifdef MAL_SUPPORT_WASAPI + wchar_t wasapi[64]; // WASAPI uses a wchar_t string for identification. +#endif +#ifdef MAL_SUPPORT_DSOUND + mal_uint8 dsound[16]; // DirectSound uses a GUID for identification. +#endif +#ifdef MAL_SUPPORT_WINMM + /*UINT_PTR*/ mal_uint32 winmm; // When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. +#endif +#ifdef MAL_SUPPORT_ALSA + char alsa[256]; // ALSA uses a name string for identification. +#endif +#ifdef MAL_SUPPORT_PULSEAUDIO + char pulse[256]; // PulseAudio uses a name string for identification. +#endif +#ifdef MAL_SUPPORT_JACK + int jack; // JACK always uses default devices. +#endif +#ifdef MAL_SUPPORT_COREAUDIO + char coreaudio[256]; // Core Audio uses a string for identification. +#endif +#ifdef MAL_SUPPORT_SNDIO + char sndio[256]; // "snd/0", etc. +#endif +#ifdef MAL_SUPPORT_AUDIOIO + char audioio[256]; // "/dev/audio", etc. +#endif +#ifdef MAL_SUPPORT_OSS + char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device. +#endif +#ifdef MAL_SUPPORT_OPENSL + mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification. +#endif +#ifdef MAL_SUPPORT_OPENAL + char openal[256]; // OpenAL seems to use human-readable device names as the ID. +#endif +#ifdef MAL_SUPPORT_SDL + int sdl; // SDL devices are identified with an index. +#endif +#ifdef MAL_SUPPORT_NULL + int nullbackend; // The null backend uses an integer for device IDs. +#endif +} mal_device_id; + +typedef struct +{ + // Basic info. This is the only information guaranteed to be filled in during device enumeration. + mal_device_id id; + char name[256]; + + // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize + // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion + // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided + // here mainly for informational purposes or in the rare case that someone might find it useful. + // + // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices(). + mal_uint32 formatCount; + mal_format formats[mal_format_count]; + mal_uint32 minChannels; + mal_uint32 maxChannels; + mal_uint32 minSampleRate; + mal_uint32 maxSampleRate; +} mal_device_info; + +typedef struct +{ + mal_int64 counter; +} mal_timer; + typedef struct { mal_format format; @@ -1299,6 +1679,35 @@ struct mal_context mal_proc AudioUnitRender; } coreaudio; #endif +#ifdef MAL_SUPPORT_SNDIO + struct + { + mal_handle sndioSO; + mal_proc sio_open; + mal_proc sio_close; + mal_proc sio_setpar; + mal_proc sio_getpar; + mal_proc sio_getcap; + mal_proc sio_start; + mal_proc sio_stop; + mal_proc sio_read; + mal_proc sio_write; + mal_proc sio_onmove; + mal_proc sio_nfds; + mal_proc sio_pollfd; + mal_proc sio_revents; + mal_proc sio_eof; + mal_proc sio_setvol; + mal_proc sio_onvol; + mal_proc sio_initpar; + } sndio; +#endif +#ifdef MAL_SUPPORT_AUDIOIO + struct + { + int _unused; + } audioio; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -1591,6 +2000,24 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device /*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices. } coreaudio; #endif +#ifdef MAL_SUPPORT_SNDIO + struct + { + mal_ptr handle; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } sndio; +#endif +#ifdef MAL_SUPPORT_AUDIOIO + struct + { + int fd; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } audioio; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -1663,7 +2090,9 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device // - WASAPI // - DirectSound // - WinMM -// - Core Audio (macOS, iOS) +// - Core Audio (Apple) +// - sndio +// - audioio // - OSS // - PulseAudio // - ALSA @@ -1941,15 +2370,6 @@ mal_bool32 mal_device_is_started(mal_device* pDevice); // This is calculated from constant values which are set at initialization time and never change. mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice); -// Retrieves the size of a sample in bytes for the given format. -// -// This API is efficient and is implemented using a lookup table. -// -// Thread Safety: SAFE -// This is API is pure. -mal_uint32 mal_get_bytes_per_sample(mal_format format); -static MAL_INLINE mal_uint32 mal_get_bytes_per_frame(mal_format format, mal_uint32 channels) { return mal_get_bytes_per_sample(format) * channels; } - // Helper function for initializing a mal_context_config object. mal_context_config mal_context_config_init(mal_log_proc onLog); @@ -2044,236 +2464,6 @@ static MAL_INLINE mal_device_config mal_device_config_init_playback_ex(mal_forma static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback) { return mal_device_config_init_playback_ex(format, channels, sampleRate, NULL, onSendCallback); } -// Helper for retrieving a standard channel map. -void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]); - -// Copies a channel map. -void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels); - - -// Determines whether or not a channel map is valid. -// -// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -// is usually treated as a passthrough. -// -// Invalid channel maps: -// - A channel map with no channels -// - A channel map with more than one channel and a mono channel -mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); - -// Helper for comparing two channel maps for equality. -// -// This assumes the channel count is the same between the two. -mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]); - -// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE). -mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); - -// Helper for determining whether or not a channel is present in the given channel map. -mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition); - - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Format Conversion -// ================= -// The format converter serves two purposes: -// 1) Conversion between data formats (u8 to f32, etc.) -// 2) Interleaving and deinterleaving -// -// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for -// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You -// can implement both, but it's not recommended as it just introduces unnecessary complexity. -// -// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved(). -// -// Dithering -// --------- -// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so. -// -// pConfig->ditherMode = mal_dither_mode_rectangle; -// -// The different dithering modes include the following, in order of efficiency: -// - None: mal_dither_mode_none -// - Rectangle: mal_dither_mode_rectangle -// - Triangle: mal_dither_mode_triangle -// -// Note that even if the dither mode is set to something other than mal_dither_mode_none, it will be ignored for conversions where dithering is not needed. -// Dithering is available for the following conversions: -// - s16 -> u8 -// - s24 -> u8 -// - s32 -> u8 -// - f32 -> u8 -// - s24 -> s16 -// - s32 -> s16 -// - f32 -> s16 -// -// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored. -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Initializes a format converter. -mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter); - -// Reads data from the format converter as interleaved channels. -mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData); - -// Reads data from the format converter as deinterleaved channels. -mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); - - -// Helper for initializing a format converter config. -mal_format_converter_config mal_format_converter_config_init_new(void); -mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData); -mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); - - - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Channel Routing -// =============== -// There are two main things you can do with the channel router: -// 1) Rearrange channels -// 2) Convert from one channel count to another -// -// Channel Rearrangement -// --------------------- -// A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel -// count for both the input and output with channel maps that contain the same channels (in a different order). -// -// Channel Conversion -// ------------------ -// The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the -// router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do -// is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all -// input channels will be averaged, excluding any None and LFE channels. -// -// The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the -// channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes. -// 1) Simple -// Unmatched channels are silenced. -// 2) Planar Blending -// Channels are blended based on a set of planes that each speaker emits audio from. -// -// Planar Blending -// --------------- -// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker. -// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left -// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map -// contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio -// data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel -// positions. -// -// As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center -// speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane. -// Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their -// planar relationship (thus the name of this blending technique). -// -// Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total -// volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel -// position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio -// from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left -// channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume -// from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker. -// -// Usage -// ----- -// To use the channel router you need to specify three things: -// 1) The input channel count and channel map -// 2) The output channel count and channel map -// 3) The mixing mode to use in the case where a 1:1 mapping is unavailable -// -// Note that input and output data is always deinterleaved 32-bit floating point. -// -// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration, -// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. -// -// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point. -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Initializes a channel router where it is assumed that the input data is non-interleaved. -mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter); - -// Reads data from the channel router as deinterleaved channels. -mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); - -// Helper for initializing a channel router config. -mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData); - - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Sample Rate Conversion -// ====================== -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Initializes a sample rate conversion object. -mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC); - -// Dynamically adjusts the input sample rate. -mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn); - -// Dynamically adjusts the output sample rate. -// -// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this -// is not acceptable you will need to use your own algorithm. -mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); - -// Reads a number of frames. -// -// Returns the number of frames actually read. -mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); - - -// Helper for creating a sample rate conversion config. -mal_src_config mal_src_config_init_new(void); -mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); - - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// DSP -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Initializes a DSP object. -mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP); - -// Dynamically adjusts the input sample rate. -// -// This will fail is the DSP was not initialized with allowDynamicSampleRate. -mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); - -// Dynamically adjusts the output sample rate. -// -// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this -// is not acceptable you will need to use your own algorithm. -// -// This will fail is the DSP was not initialized with allowDynamicSampleRate. -mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); - -// Reads a number of frames and runs them through the DSP processor. -mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData); - -// Helper for initializing a mal_dsp_config object. -mal_dsp_config mal_dsp_config_init_new(void); -mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData); -mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData); - - -// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -// determine the required size of the output buffer. -// -// A return value of 0 indicates an error. -// -// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. -mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn); -mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn); - - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -2296,37 +2486,9 @@ void mal_mutex_lock(mal_mutex* pMutex); void mal_mutex_unlock(mal_mutex* pMutex); - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Miscellaneous Helpers -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// malloc(). Calls MAL_MALLOC(). -void* mal_malloc(size_t sz); - -// realloc(). Calls MAL_REALLOC(). -void* mal_realloc(void* p, size_t sz); - -// free(). Calls MAL_FREE(). -void mal_free(void* p); - -// Performs an aligned malloc, with the assumption that the alignment is a power of 2. -void* mal_aligned_malloc(size_t sz, size_t alignment); - -// Free's an aligned malloc'd buffer. -void mal_aligned_free(void* p); - // Retrieves a friendly name for a backend. const char* mal_get_backend_name(mal_backend backend); -// Retrieves a friendly name for a format. -const char* mal_get_format_name(mal_format format); - -// Blends two frames in floating point format. -void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); - // Calculates a scaling factor relative to speed of the system. // // This could be useful for dynamically determining the size of a device's internal buffer based on the speed of the system. @@ -2342,35 +2504,9 @@ mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale); // Calculates a buffer size in frames for the specified performance profile and scale factor. mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale); +#endif // MAL_NO_DEVICE_IO -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Format Conversion -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); -void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2395,10 +2531,17 @@ typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder); typedef struct { - mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format. - mal_uint32 channels; // Set to 0 to use the stream's internal channels. - mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate. + mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format. + mal_uint32 channels; // Set to 0 to use the stream's internal channels. + mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate. mal_channel channelMap[MAL_MAX_CHANNELS]; + mal_channel_mix_mode channelMixMode; + mal_dither_mode ditherMode; + mal_src_algorithm srcAlgorithm; + union + { + mal_src_config_sinc sinc; + } src; } mal_decoder_config; struct mal_decoder @@ -2660,9 +2803,23 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float* #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MAL_ANDROID) static MAL_INLINE void mal_cpuid(int info[4], int fid) { - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); + // It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the + // specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for + // supporting different assembly dialects. + // + // What's basically happening is that we're saving and restoring the ebx register manually. + #if defined(DRFLAC_X86) && defined(__PIC__) + __asm__ __volatile__ ( + "xchg{l} {%%}ebx, %k1;" + "cpuid;" + "xchg{l} {%%}ebx, %k1;" + : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #else + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #endif } static MAL_INLINE unsigned long long mal_xgetbv(int reg) @@ -2834,6 +2991,23 @@ static MAL_INLINE mal_bool32 mal_has_neon() } +static MAL_INLINE mal_bool32 mal_is_little_endian() +{ +#if defined(MAL_X86) || defined(MAL_X64) + return MAL_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static MAL_INLINE mal_bool32 mal_is_big_endian() +{ + return !mal_is_little_endian(); +} + + + #ifndef MAL_PI #define MAL_PI 3.14159265358979323846264f #endif @@ -2847,136 +3021,6 @@ static MAL_INLINE mal_bool32 mal_has_neon() #define MAL_TAU_D 6.28318530717958647693 #endif -// Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When -// using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing -// compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply -// disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am -// not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. -//#define MAL_USE_RUNTIME_LINKING_FOR_PTHREAD - -// Disable run-time linking on certain backends. -#ifndef MAL_NO_RUNTIME_LINKING - #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) - #define MAL_NO_RUNTIME_LINKING - #endif -#endif - -// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not -// certain unused functions and variables can be excluded from the build to avoid warnings. -#ifdef MAL_ENABLE_WASAPI - #define MAL_HAS_WASAPI // Every compiler should support WASAPI -#endif -#ifdef MAL_ENABLE_DSOUND - #define MAL_HAS_DSOUND // Every compiler should support DirectSound. -#endif -#ifdef MAL_ENABLE_WINMM - #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM. -#endif -#ifdef MAL_ENABLE_ALSA - #define MAL_HAS_ALSA - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_ALSA - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_PULSEAUDIO - #define MAL_HAS_PULSEAUDIO // Development packages are unnecessary for PulseAudio. - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_PULSEAUDIO - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_JACK - #define MAL_HAS_JACK - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_JACK - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_COREAUDIO - #define MAL_HAS_COREAUDIO -#endif -#ifdef MAL_ENABLE_OSS - #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. -#endif -#ifdef MAL_ENABLE_OPENSL - #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present. -#endif -#ifdef MAL_ENABLE_OPENAL - #define MAL_HAS_OPENAL - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_OPENAL - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_SDL - #define MAL_HAS_SDL - - // SDL headers are necessary if using compile-time linking. - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #ifdef MAL_EMSCRIPTEN - #if !__has_include() - #undef MAL_HAS_SDL - #endif - #else - #if !__has_include() - #undef MAL_HAS_SDL - #endif - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_NULL - #define MAL_HAS_NULL // Everything supports the null backend. -#endif - - -#ifdef MAL_WIN32 - #define MAL_THREADCALL WINAPI - typedef unsigned long mal_thread_result; -#else - #define MAL_THREADCALL - typedef void* mal_thread_result; -#endif -typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData); - -#ifdef MAL_WIN32 -typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MAL_PFN_CoUninitialize)(); -typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); -typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv); -typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar); -typedef int (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); - -typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)(); -typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); - -// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. -typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); -typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); -#endif - - -#define MAL_STATE_UNINITIALIZED 0 -#define MAL_STATE_STOPPED 1 // The device's default state after initialization. -#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data. -#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started. -#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped. - // The default format when mal_format_unknown (0) is requested when initializing a device. #ifndef MAL_DEFAULT_FORMAT @@ -3032,8 +3076,8 @@ mal_uint32 g_malStandardSampleRatePriorities[] = { }; mal_format g_malFormatPriorities[] = { - mal_format_f32, // Most common - mal_format_s16, + mal_format_s16, // Most common + mal_format_f32, //mal_format_s24_32, // Clean alignment mal_format_s32, @@ -3043,8 +3087,7 @@ mal_format g_malFormatPriorities[] = { mal_format_u8 // Low quality }; -#define MAL_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MAL_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" + /////////////////////////////////////////////////////////////////////////////// // @@ -3569,6 +3612,207 @@ void mal_split_buffer(void* pBuffer, size_t bufferSize, size_t splitCount, size_ #endif +mal_uint32 mal_get_standard_sample_rate_priority_index(mal_uint32 sampleRate) // Lower = higher priority +{ + for (mal_uint32 i = 0; i < mal_countof(g_malStandardSampleRatePriorities); ++i) { + if (g_malStandardSampleRatePriorities[i] == sampleRate) { + return i; + } + } + + return (mal_uint32)-1; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DEVICE I/O +// ========== +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MAL_NO_DEVICE_IO +// Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When +// using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing +// compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply +// disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am +// not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. +//#define MAL_USE_RUNTIME_LINKING_FOR_PTHREAD + +// Disable run-time linking on certain backends. +#ifndef MAL_NO_RUNTIME_LINKING + #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) + #define MAL_NO_RUNTIME_LINKING + #endif +#endif + +// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not +// certain unused functions and variables can be excluded from the build to avoid warnings. +#ifdef MAL_ENABLE_WASAPI + #define MAL_HAS_WASAPI // Every compiler should support WASAPI +#endif +#ifdef MAL_ENABLE_DSOUND + #define MAL_HAS_DSOUND // Every compiler should support DirectSound. +#endif +#ifdef MAL_ENABLE_WINMM + #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM. +#endif +#ifdef MAL_ENABLE_ALSA + #define MAL_HAS_ALSA + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_ALSA + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_PULSEAUDIO + #define MAL_HAS_PULSEAUDIO // Development packages are unnecessary for PulseAudio. + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_PULSEAUDIO + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_JACK + #define MAL_HAS_JACK + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_JACK + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_COREAUDIO + #define MAL_HAS_COREAUDIO +#endif +#ifdef MAL_ENABLE_SNDIO + #define MAL_HAS_SNDIO +#endif +#ifdef MAL_ENABLE_AUDIOIO + #define MAL_HAS_AUDIOIO // When enabled, always assume audioio is available. +#endif +#ifdef MAL_ENABLE_OSS + #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. +#endif +#ifdef MAL_ENABLE_OPENSL + #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present. +#endif +#ifdef MAL_ENABLE_OPENAL + #define MAL_HAS_OPENAL + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_OPENAL + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_SDL + #define MAL_HAS_SDL + + // SDL headers are necessary if using compile-time linking. + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #ifdef MAL_EMSCRIPTEN + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #else + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_NULL + #define MAL_HAS_NULL // Everything supports the null backend. +#endif + +const mal_backend g_malDefaultBackends[] = { + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_coreaudio, + mal_backend_sndio, + mal_backend_audioio, + mal_backend_oss, + mal_backend_pulseaudio, + mal_backend_alsa, + mal_backend_jack, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl, + mal_backend_null +}; + +const char* mal_get_backend_name(mal_backend backend) +{ + switch (backend) + { + case mal_backend_null: return "Null"; + case mal_backend_wasapi: return "WASAPI"; + case mal_backend_dsound: return "DirectSound"; + case mal_backend_winmm: return "WinMM"; + case mal_backend_alsa: return "ALSA"; + case mal_backend_pulseaudio: return "PulseAudio"; + case mal_backend_jack: return "JACK"; + case mal_backend_coreaudio: return "Core Audio"; + case mal_backend_sndio: return "sndio"; + case mal_backend_audioio: return "audioio"; + case mal_backend_oss: return "OSS"; + case mal_backend_opensl: return "OpenSL|ES"; + case mal_backend_openal: return "OpenAL"; + case mal_backend_sdl: return "SDL"; + default: return "Unknown"; + } +} + + + +#ifdef MAL_WIN32 + #define MAL_THREADCALL WINAPI + typedef unsigned long mal_thread_result; +#else + #define MAL_THREADCALL + typedef void* mal_thread_result; +#endif +typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData); + +#ifdef MAL_WIN32 +typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MAL_PFN_CoUninitialize)(); +typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); +typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv); +typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar); +typedef int (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); + +typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)(); +typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); + +// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. +typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); +typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); +#endif + + +#define MAL_STATE_UNINITIALIZED 0 +#define MAL_STATE_STOPPED 1 // The device's default state after initialization. +#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data. +#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started. +#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped. + +#define MAL_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" +#define MAL_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" + + /////////////////////////////////////////////////////////////////////////////// // // Timing @@ -4154,6 +4398,136 @@ mal_uint32 mal_get_closest_standard_sample_rate(mal_uint32 sampleRateIn) } +typedef struct +{ + mal_uint8* pInputFrames; + mal_uint32 framesRemaining; +} mal_calculate_cpu_speed_factor_data; + +mal_uint32 mal_calculate_cpu_speed_factor__on_read(mal_dsp* pDSP, mal_uint32 framesToRead, void* pFramesOut, void* pUserData) +{ + mal_calculate_cpu_speed_factor_data* pData = (mal_calculate_cpu_speed_factor_data*)pUserData; + mal_assert(pData != NULL); + + if (framesToRead > pData->framesRemaining) { + framesToRead = pData->framesRemaining; + } + + mal_copy_memory(pFramesOut, pData->pInputFrames, framesToRead*pDSP->formatConverterIn.config.channels * sizeof(*pData->pInputFrames)); + + pData->pInputFrames += framesToRead; + pData->framesRemaining -= framesToRead; + + return framesToRead; +} + +float mal_calculate_cpu_speed_factor() +{ + // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline. + + // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value. + double f = 1000; + + // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout. +#if !defined(NDEBUG) || defined(_DEBUG) + f /= 2; +#endif + + mal_uint32 sampleRateIn = 44100; + mal_uint32 sampleRateOut = 48000; + mal_uint32 channelsIn = 2; + mal_uint32 channelsOut = 6; + + // Using the heap here to avoid an unnecessary static memory allocation. Also too big for the stack. + mal_uint8* pInputFrames = NULL; + float* pOutputFrames = NULL; + + size_t inputDataSize = sampleRateIn * channelsIn * sizeof(*pInputFrames); + size_t outputDataSize = sampleRateOut * channelsOut * sizeof(*pOutputFrames); + + void* pData = mal_malloc(inputDataSize + outputDataSize); + if (pData == NULL) { + return 1; + } + + pInputFrames = (mal_uint8*)pData; + pOutputFrames = (float*)(pInputFrames + inputDataSize); + + + + + mal_calculate_cpu_speed_factor_data data; + data.pInputFrames = pInputFrames; + data.framesRemaining = sampleRateIn; + + mal_dsp_config config = mal_dsp_config_init(mal_format_u8, channelsIn, sampleRateIn, mal_format_f32, channelsOut, sampleRateOut, mal_calculate_cpu_speed_factor__on_read, &data); + + // Use linear sample rate conversion because it's the simplest and least likely to cause skewing as a result of tweaks to default + // configurations in the future. + config.srcAlgorithm = mal_src_algorithm_linear; + + // Experiment: Disable SIMD extensions when profiling just to try and keep things a bit more consistent. The idea is to get a general + // indication on the speed of the system, but SIMD is used more heavily in the DSP pipeline than in the general case which may make + // the results a little less realistic. + config.noSSE2 = MAL_TRUE; + config.noAVX2 = MAL_TRUE; + config.noAVX512 = MAL_TRUE; + config.noNEON = MAL_TRUE; + + mal_dsp dsp; + mal_result result = mal_dsp_init(&config, &dsp); + if (result != MAL_SUCCESS) { + mal_free(pData); + return 1; + } + + + int iterationCount = 2; + + mal_timer timer; + mal_timer_init(&timer); + double startTime = mal_timer_get_time_in_seconds(&timer); + { + for (int i = 0; i < iterationCount; ++i) { + mal_dsp_read(&dsp, sampleRateOut, pOutputFrames, &data); + data.pInputFrames = pInputFrames; + data.framesRemaining = sampleRateIn; + } + } + double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime; + executionTimeInSeconds /= iterationCount; + + + mal_free(pData); + + // Guard against extreme blowouts. + return (float)mal_clamp(executionTimeInSeconds * f, 0.1, 100.0); +} + +mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) +{ + return mal_max(1, (mal_uint32)(baseBufferSize*scale)); +} + +mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale) +{ + mal_uint32 baseLatency; + if (performanceProfile == mal_performance_profile_low_latency) { + baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; + } else { + baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; + } + + mal_uint32 sampleRateMS = (sampleRate/1000); + + mal_uint32 minBufferSize = sampleRateMS * mal_min(baseLatency / 5, 1); // <-- Guard against multiply by zero. + mal_uint32 maxBufferSize = sampleRateMS * (baseLatency * 40); + + mal_uint32 bufferSize = mal_scale_buffer_size((sampleRate/1000) * baseLatency, scale); + return mal_clamp(bufferSize, minBufferSize, maxBufferSize); +} + + const char* mal_log_level_to_string(mal_uint32 logLevel) { switch (logLevel) @@ -6191,7 +6565,7 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); -#if 1 +#if 0 if (pDevice->type == mal_device_type_playback) { mal_uint32 paddingFramesCount; HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount); @@ -6215,15 +6589,20 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice) } #else mal_uint32 paddingFramesCount; - HRESULT hr = mal_IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount); + HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount); if (FAILED(hr)) { return 0; } + // Slightly different rules for exclusive and shared modes. if (pDevice->exclusiveMode) { return paddingFramesCount; } else { - return pDevice->bufferSizeInFrames - paddingFramesCount; + if (pDevice->type == mal_device_type_playback) { + return pDevice->bufferSizeInFrames - paddingFramesCount; + } else { + return paddingFramesCount; + } } #endif } @@ -8843,16 +9222,6 @@ typedef size_t (* mal_snd_pcm_info_sizeof_proc) typedef const char* (* mal_snd_pcm_info_get_name_proc) (const mal_snd_pcm_info_t* info); typedef int (* mal_snd_config_update_free_global_proc) (); -mal_snd_pcm_format_t g_mal_ALSAFormats[] = { - MAL_SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown - MAL_SND_PCM_FORMAT_U8, // mal_format_u8 - MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 - MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24 - //MAL_SND_PCM_FORMAT_S24_LE, // mal_format_s24_32 - MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32 - MAL_SND_PCM_FORMAT_FLOAT_LE // mal_format_f32 -}; - // This array specifies each of the common devices that can be used for both playback and capture. const char* g_malCommonDeviceNamesALSA[] = { "default", @@ -8900,20 +9269,52 @@ float mal_find_default_buffer_size_scale__alsa(const char* deviceName) mal_snd_pcm_format_t mal_convert_mal_format_to_alsa_format(mal_format format) { - return g_mal_ALSAFormats[format]; + mal_snd_pcm_format_t ALSAFormats[] = { + MAL_SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown + MAL_SND_PCM_FORMAT_U8, // mal_format_u8 + MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 + MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24 + MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32 + MAL_SND_PCM_FORMAT_FLOAT_LE // mal_format_f32 + }; + + if (mal_is_big_endian()) { + ALSAFormats[0] = MAL_SND_PCM_FORMAT_UNKNOWN; + ALSAFormats[1] = MAL_SND_PCM_FORMAT_U8; + ALSAFormats[2] = MAL_SND_PCM_FORMAT_S16_BE; + ALSAFormats[3] = MAL_SND_PCM_FORMAT_S24_3BE; + ALSAFormats[4] = MAL_SND_PCM_FORMAT_S32_BE; + ALSAFormats[5] = MAL_SND_PCM_FORMAT_FLOAT_BE; + } + + + return ALSAFormats[format]; } mal_format mal_convert_alsa_format_to_mal_format(mal_snd_pcm_format_t formatALSA) { - switch (formatALSA) - { - case MAL_SND_PCM_FORMAT_U8: return mal_format_u8; - case MAL_SND_PCM_FORMAT_S16_LE: return mal_format_s16; - case MAL_SND_PCM_FORMAT_S24_3LE: return mal_format_s24; - //case MAL_SND_PCM_FORMAT_S24_LE: return mal_format_s24_32 - case MAL_SND_PCM_FORMAT_S32_LE: return mal_format_s32; - case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32; - default: return mal_format_unknown; + if (mal_is_little_endian()) { + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_S16_LE: return mal_format_s16; + case MAL_SND_PCM_FORMAT_S24_3LE: return mal_format_s24; + case MAL_SND_PCM_FORMAT_S32_LE: return mal_format_s32; + case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32; + default: break; + } + } else { + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_S16_BE: return mal_format_s16; + case MAL_SND_PCM_FORMAT_S24_3BE: return mal_format_s24; + case MAL_SND_PCM_FORMAT_S32_BE: return mal_format_s32; + case MAL_SND_PCM_FORMAT_FLOAT_BE: return mal_format_f32; + default: break; + } + } + + // Endian agnostic. + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_U8: return mal_format_u8; + default: return mal_format_unknown; } } @@ -9933,11 +10334,19 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co MAL_SND_PCM_FORMAT_FLOAT_LE, // mal_format_f32 MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32 MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24 - //MAL_SND_PCM_FORMAT_S24_LE, // mal_format_s24_32 MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 MAL_SND_PCM_FORMAT_U8 // mal_format_u8 }; + if (mal_is_big_endian()) { + preferredFormatsALSA[0] = MAL_SND_PCM_FORMAT_FLOAT_BE; + preferredFormatsALSA[1] = MAL_SND_PCM_FORMAT_S32_BE; + preferredFormatsALSA[2] = MAL_SND_PCM_FORMAT_S24_3BE; + preferredFormatsALSA[3] = MAL_SND_PCM_FORMAT_S16_BE; + preferredFormatsALSA[4] = MAL_SND_PCM_FORMAT_U8; + } + + formatALSA = MAL_SND_PCM_FORMAT_UNKNOWN; for (size_t i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) { if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) { @@ -10943,20 +11352,27 @@ mal_result mal_result_from_pulse(int result) #if 0 mal_pa_sample_format_t mal_format_to_pulse(mal_format format) { - switch (format) - { - case mal_format_u8: return MAL_PA_SAMPLE_U8; - case mal_format_s16: return MAL_PA_SAMPLE_S16LE; - //case mal_format_s16be: return MAL_PA_SAMPLE_S16BE; - case mal_format_s24: return MAL_PA_SAMPLE_S24LE; - //case mal_format_s24be: return MAL_PA_SAMPLE_S24BE; - //case mal_format_s24_32: return MAL_PA_SAMPLE_S24_32LE; - //case mal_format_s24_32be: return MAL_PA_SAMPLE_S24_32BE; - case mal_format_s32: return MAL_PA_SAMPLE_S32LE; - //case mal_format_s32be: return MAL_PA_SAMPLE_S32BE; - case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32LE; - //case mal_format_f32be: return PA_SAMPLE_FLOAT32BE; + if (mal_is_little_endian()) { + switch (format) { + case mal_format_s16: return MAL_PA_SAMPLE_S16LE; + case mal_format_s24: return MAL_PA_SAMPLE_S24LE; + case mal_format_s32: return MAL_PA_SAMPLE_S32LE; + case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32LE; + default: break; + } + } else { + switch (format) { + case mal_format_s16: return MAL_PA_SAMPLE_S16BE; + case mal_format_s24: return MAL_PA_SAMPLE_S24BE; + case mal_format_s32: return MAL_PA_SAMPLE_S32BE; + case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32BE; + default: break; + } + } + // Endian agnostic. + switch (format) { + case mal_format_u8: return MAL_PA_SAMPLE_U8; default: return MAL_PA_SAMPLE_INVALID; } } @@ -10964,20 +11380,27 @@ mal_pa_sample_format_t mal_format_to_pulse(mal_format format) mal_format mal_format_from_pulse(mal_pa_sample_format_t format) { - switch (format) - { - case MAL_PA_SAMPLE_U8: return mal_format_u8; - case MAL_PA_SAMPLE_S16LE: return mal_format_s16; - //case MAL_PA_SAMPLE_S16BE: return mal_format_s16be; - case MAL_PA_SAMPLE_S24LE: return mal_format_s24; - //case MAL_PA_SAMPLE_S24BE: return mal_format_s24be; - //case MAL_PA_SAMPLE_S24_32LE: return mal_format_s24_32; - //case MAL_PA_SAMPLE_S24_32BE: return mal_format_s24_32be; - case MAL_PA_SAMPLE_S32LE: return mal_format_s32; - //case MAL_PA_SAMPLE_S32BE: return mal_format_s32be; - case MAL_PA_SAMPLE_FLOAT32LE: return mal_format_f32; - //case MAL_PA_SAMPLE_FLOAT32BE: return mal_format_f32be; + if (mal_is_little_endian()) { + switch (format) { + case MAL_PA_SAMPLE_S16LE: return mal_format_s16; + case MAL_PA_SAMPLE_S24LE: return mal_format_s24; + case MAL_PA_SAMPLE_S32LE: return mal_format_s32; + case MAL_PA_SAMPLE_FLOAT32LE: return mal_format_f32; + default: break; + } + } else { + switch (format) { + case MAL_PA_SAMPLE_S16BE: return mal_format_s16; + case MAL_PA_SAMPLE_S24BE: return mal_format_s24; + case MAL_PA_SAMPLE_S32BE: return mal_format_s32; + case MAL_PA_SAMPLE_FLOAT32BE: return mal_format_f32; + default: break; + } + } + // Endian agnostic. + switch (format) { + case MAL_PA_SAMPLE_U8: return mal_format_u8; default: return mal_format_unknown; } } @@ -12967,9 +13390,9 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { return MAL_FORMAT_NOT_SUPPORTED; } - - // Big-endian formats are not currently supported, but will be added in a future version of mini_al. - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { + + // Only supporting native-endian. + if ((mal_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (mal_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { return MAL_FORMAT_NOT_SUPPORTED; } @@ -14565,6 +14988,1299 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) +/////////////////////////////////////////////////////////////////////////////// +// +// sndio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_SNDIO +#include +#include + +// Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due +// to mini_al's implementation or if it's some kind of system configuration issue, but basically the default device +// just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's +// demand for it or if I can get it tested and debugged more thoroughly. + +//#if defined(__NetBSD__) || defined(__OpenBSD__) +//#include +//#endif +//#if defined(__FreeBSD__) || defined(__DragonFly__) +//#include +//#endif + +#define MAL_SIO_DEVANY "default" +#define MAL_SIO_PLAY 1 +#define MAL_SIO_REC 2 +#define MAL_SIO_NENC 8 +#define MAL_SIO_NCHAN 8 +#define MAL_SIO_NRATE 16 +#define MAL_SIO_NCONF 4 + +struct mal_sio_hdl; // <-- Opaque + +struct mal_sio_par +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; + unsigned int bufsz; + unsigned int xrun; + unsigned int round; + unsigned int appbufsz; + int __pad[3]; + unsigned int __magic; +}; + +struct mal_sio_enc +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; +}; + +struct mal_sio_conf +{ + unsigned int enc; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; +}; + +struct mal_sio_cap +{ + struct mal_sio_enc enc[MAL_SIO_NENC]; + unsigned int rchan[MAL_SIO_NCHAN]; + unsigned int pchan[MAL_SIO_NCHAN]; + unsigned int rate[MAL_SIO_NRATE]; + int __pad[7]; + unsigned int nconf; + struct mal_sio_conf confs[MAL_SIO_NCONF]; +}; + +typedef struct mal_sio_hdl* (* mal_sio_open_proc) (const char*, unsigned int, int); +typedef void (* mal_sio_close_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_setpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*); +typedef int (* mal_sio_getpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*); +typedef int (* mal_sio_getcap_proc) (struct mal_sio_hdl*, struct mal_sio_cap*); +typedef size_t (* mal_sio_write_proc) (struct mal_sio_hdl*, const void*, size_t); +typedef size_t (* mal_sio_read_proc) (struct mal_sio_hdl*, void*, size_t); +typedef int (* mal_sio_start_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_stop_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_initpar_proc)(struct mal_sio_par*); + +mal_format mal_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) +{ + // We only support native-endian right now. + if ((mal_is_little_endian() && le == 0) || (mal_is_big_endian() && le == 1)) { + return mal_format_unknown; + } + + if (bits == 8 && bps == 1 && sig == 0) { + return mal_format_u8; + } + if (bits == 16 && bps == 2 && sig == 1) { + return mal_format_s16; + } + if (bits == 24 && bps == 3 && sig == 1) { + return mal_format_s24; + } + if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { + //return mal_format_s24_32; + } + if (bits == 32 && bps == 4 && sig == 1) { + return mal_format_s32; + } + + return mal_format_unknown; +} + +mal_format mal_find_best_format_from_sio_cap__sndio(struct mal_sio_cap* caps) +{ + mal_assert(caps != NULL); + + mal_format bestFormat = mal_format_unknown; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == mal_format_unknown) { + continue; // Format not supported. + } + + if (bestFormat == mal_format_unknown) { + bestFormat = format; + } else { + if (mal_get_format_priority_index(bestFormat) > mal_get_format_priority_index(format)) { // <-- Lower = better. + bestFormat = format; + } + } + } + } + + return mal_format_unknown; +} + +mal_uint32 mal_find_best_channels_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat) +{ + mal_assert(caps != NULL); + mal_assert(requiredFormat != mal_format_unknown); + + // Just pick whatever configuration has the most channels. + mal_uint32 maxChannels = 0; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + // The encoding should be of requiredFormat. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format != requiredFormat) { + continue; + } + + // Getting here means the format is supported. Iterate over each channel count and grab the biggest one. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (maxChannels < channels) { + maxChannels = channels; + } + } + } + } + + return maxChannels; +} + +mal_uint32 mal_find_best_sample_rate_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat, mal_uint32 requiredChannels) +{ + mal_assert(caps != NULL); + mal_assert(requiredFormat != mal_format_unknown); + mal_assert(requiredChannels > 0); + mal_assert(requiredChannels <= MAL_MAX_CHANNELS); + + mal_uint32 firstSampleRate = 0; // <-- If the device does not support a standard rate we'll fall back to the first one that's found. + + mal_uint32 bestSampleRate = 0; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + // The encoding should be of requiredFormat. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format != requiredFormat) { + continue; + } + + // Getting here means the format is supported. Iterate over each channel count and grab the biggest one. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (channels != requiredChannels) { + continue; + } + + // Getting here means we have found a compatible encoding/channel pair. + for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) { + mal_uint32 rate = (mal_uint32)caps->rate[iRate]; + + if (firstSampleRate == 0) { + firstSampleRate = rate; + } + + // Disregard this rate if it's not a standard one. + mal_uint32 ratePriority = mal_get_standard_sample_rate_priority_index(rate); + if (ratePriority == (mal_uint32)-1) { + continue; + } + + if (mal_get_standard_sample_rate_priority_index(bestSampleRate) > ratePriority) { // Lower = better. + bestSampleRate = rate; + } + } + } + } + } + + // If a standard sample rate was not found just fall back to the first one that was iterated. + if (bestSampleRate == 0) { + bestSampleRate = firstSampleRate; + } + + return bestSampleRate; +} + + +mal_bool32 mal_context_is_device_id_equal__sndio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->sndio, pID1->sndio) == 0; +} + +mal_result mal_context_enumerate_devices__sndio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + // sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating + // over default devices for now. + mal_bool32 isTerminating = MAL_FALSE; + struct mal_sio_hdl* handle; + + // Playback. + if (!isTerminating) { + handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_PLAY, 0); + if (handle != NULL) { + // Supports playback. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MAL_SIO_DEVANY); + mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME); + + isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + // Capture. + if (!isTerminating) { + handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_REC, 0); + if (handle != NULL) { + // Supports capture. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); + mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME); + + isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // We need to open the device before we can get information about it. + char devid[256]; + if (pDeviceID == NULL) { + mal_strcpy_s(devid, sizeof(devid), MAL_SIO_DEVANY); + mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == mal_device_type_playback) ? MAL_DEFAULT_PLAYBACK_DEVICE_NAME : MAL_DEFAULT_CAPTURE_DEVICE_NAME); + } else { + mal_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); + mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); + } + + struct mal_sio_hdl* handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0); + if (handle == NULL) { + return MAL_NO_DEVICE; + } + + struct mal_sio_cap caps; + if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { + return MAL_ERROR; + } + + for (unsigned int iConfig = 0; iConfig < caps.nconf; iConfig += 1) { + // The main thing we care about is that the encoding is supported by mini_al. If it is, we want to give + // preference to some formats over others. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps.enc[iEncoding].bits; + unsigned int bps = caps.enc[iEncoding].bps; + unsigned int sig = caps.enc[iEncoding].sig; + unsigned int le = caps.enc[iEncoding].le; + unsigned int msb = caps.enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == mal_format_unknown) { + continue; // Format not supported. + } + + // Add this format if it doesn't already exist. + mal_bool32 formatExists = MAL_FALSE; + for (mal_uint32 iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) { + if (pDeviceInfo->formats[iExistingFormat] == format) { + formatExists = MAL_TRUE; + break; + } + } + + if (!formatExists) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = format; + } + } + + // Channels. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps.confs[iConfig].pchan; + } else { + chan = caps.confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps.pchan[iChannel]; + } else { + channels = caps.rchan[iChannel]; + } + + if (pDeviceInfo->minChannels > channels) { + pDeviceInfo->minChannels = channels; + } + if (pDeviceInfo->maxChannels < channels) { + pDeviceInfo->maxChannels = channels; + } + } + + // Sample rates. + for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) { + if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { + unsigned int rate = caps.rate[iRate]; + if (pDeviceInfo->minSampleRate > rate) { + pDeviceInfo->minSampleRate = rate; + } + if (pDeviceInfo->maxSampleRate < rate) { + pDeviceInfo->maxSampleRate = rate; + } + } + } + } + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + return MAL_SUCCESS; +} + +void mal_device_uninit__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((mal_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + mal_free(pDevice->sndio.pIntermediaryBuffer); +} + +mal_result mal_device_init__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->sndio); + + const char* deviceName = MAL_SIO_DEVANY; +//#if defined(__FreeBSD__) || defined(__DragonFly__) +// deviceName = "rsnd/0"; +//#else + if (pDeviceID != NULL) { + deviceName = pDeviceID->sndio; + } + + pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceName, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0); + if (pDevice->sndio.handle == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // We need to retrieve the device caps to determine the most appropriate format to use. + struct mal_sio_cap caps; + if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)((struct mal_sio_hdl*)pDevice->sndio.handle, &caps) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MAL_ERROR); + } + + mal_format desiredFormat = pDevice->format; + if (pDevice->usingDefaultFormat) { + desiredFormat = mal_find_best_format_from_sio_cap__sndio(&caps); + } + + if (desiredFormat == mal_format_unknown) { + desiredFormat = pDevice->format; + } + + + // Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real + // way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this + // to the requested channels, regardless of whether or not the default channel count is requested. + // + // For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the + // value returned by mal_find_best_channels_from_sio_cap__sndio(). + mal_uint32 desiredChannels = pDevice->channels; + if (pDevice->usingDefaultChannels) { + if (strlen(deviceName) > strlen("rsnd/") && strncmp(deviceName, "rsnd/", strlen("rsnd/")) == 0) { + desiredChannels = mal_find_best_channels_from_sio_cap__sndio(&caps, deviceType, desiredFormat); + } + } + + if (desiredChannels == 0) { + desiredChannels = pDevice->channels; + } + + + mal_uint32 desiredSampleRate = pDevice->sampleRate; + if (pDevice->usingDefaultSampleRate) { + desiredSampleRate = mal_find_best_sample_rate_from_sio_cap__sndio(&caps, deviceType, desiredFormat, desiredChannels); + } + + if (desiredSampleRate == 0) { + desiredSampleRate = pDevice->sampleRate; + } + + + struct mal_sio_par par; + ((mal_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); + par.msb = 0; + par.le = mal_is_little_endian(); + + switch (desiredFormat) { + case mal_format_u8: + { + par.bits = 8; + par.bps = 1; + par.sig = 0; + } break; + + case mal_format_s24: + { + par.bits = 24; + par.bps = 3; + par.sig = 1; + } break; + + case mal_format_s32: + { + par.bits = 32; + par.bps = 4; + par.sig = 1; + } break; + + case mal_format_s16: + case mal_format_f32: + default: + { + par.bits = 16; + par.bps = 2; + par.sig = 1; + } break; + } + + if (deviceType == mal_device_type_playback) { + par.pchan = desiredChannels; + } else { + par.rchan = desiredChannels; + } + + par.rate = desiredSampleRate; + + // Try calculating an appropriate default buffer size after we have the sample rate. + mal_uint32 desiredBufferSizeInFrames = pDevice->bufferSizeInFrames; + if (pDevice->usingDefaultBufferSize) { + // CPU speed factor. + float fCPUSpeed = mal_calculate_cpu_speed_factor(); + + // Playback vs capture latency. + float fDeviceType = 1; + + // Backend tax. + float fBackend = 1; + + desiredBufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, par.rate, fCPUSpeed*fDeviceType*fBackend); + } + + par.round = desiredBufferSizeInFrames / pDevice->periods; + par.appbufsz = par.round * pDevice->periods; + + if (((mal_sio_setpar_proc)pContext->sndio.sio_setpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MAL_FORMAT_NOT_SUPPORTED); + } + if (((mal_sio_getpar_proc)pContext->sndio.sio_getpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); + + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = par.pchan; + } else { + pDevice->internalChannels = par.rchan; + } + + pDevice->internalSampleRate = par.rate; + + pDevice->periods = par.appbufsz / par.round; + if (pDevice->periods < 2) { + pDevice->periods = 2; + } + pDevice->bufferSizeInFrames = par.round * pDevice->periods; + pDevice->sndio.fragmentSizeInFrames = par.round; + + mal_get_standard_channel_map(mal_standard_channel_map_sndio, pDevice->internalChannels, pDevice->internalChannelMap); + + pDevice->sndio.pIntermediaryBuffer = mal_malloc(pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (pDevice->sndio.pIntermediaryBuffer == NULL) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + +#ifdef MAL_DEBUG_OUTPUT + printf("DEVICE INFO\n"); + printf(" Format: %s\n", mal_get_format_name(pDevice->internalFormat)); + printf(" Channels: %d\n", pDevice->internalChannels); + printf(" Sample Rate: %d\n", pDevice->internalSampleRate); + printf(" Buffer Size: %d\n", pDevice->bufferSizeInFrames); + printf(" Periods: %d\n", pDevice->periods); + printf(" appbufsz: %d\n", par.appbufsz); + printf(" round: %d\n", par.round); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (((mal_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct mal_sio_hdl*)pDevice->sndio.handle) == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + // The device is started by the next calls to read() and write(). For playback it's simple - just read + // data from the client, then write it to the device with write() which will in turn start the device. + // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first + // call to read(). + if (pDevice->type == mal_device_type_playback) { + // Playback. Need to load the entire buffer, which means we need to write a fragment for each period. + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) { + mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer); + + int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((mal_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct mal_sio_hdl*)pDevice->sndio.handle); + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->sndio.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->sndio.breakFromMainLoop = MAL_FALSE; + while (!pDevice->sndio.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer); + + int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } else { + // Capture. + int bytesRead = ((mal_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesRead == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->sndio.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__sndio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_sndio); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__sndio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + // libpulse.so + const char* libsndioNames[] = { + "libsndio.so" + }; + + for (size_t i = 0; i < mal_countof(libsndioNames); ++i) { + pContext->sndio.sndioSO = mal_dlopen(libsndioNames[i]); + if (pContext->sndio.sndioSO != NULL) { + break; + } + } + + if (pContext->sndio.sndioSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->sndio.sio_open = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_initpar"); +#else + pContext->sndio.sio_open = sio_open; + pContext->sndio.sio_close = sio_close; + pContext->sndio.sio_setpar = sio_setpar; + pContext->sndio.sio_getpar = sio_getpar; + pContext->sndio.sio_getcap = sio_getcap; + pContext->sndio.sio_write = sio_write; + pContext->sndio.sio_read = sio_read; + pContext->sndio.sio_start = sio_start; + pContext->sndio.sio_stop = sio_stop; + pContext->sndio.sio_initpar = sio_initpar; +#endif + + pContext->onUninit = mal_context_uninit__sndio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__sndio; + pContext->onEnumDevices = mal_context_enumerate_devices__sndio; + pContext->onGetDeviceInfo = mal_context_get_device_info__sndio; + pContext->onDeviceInit = mal_device_init__sndio; + pContext->onDeviceUninit = mal_device_uninit__sndio; + pContext->onDeviceStart = mal_device__start_backend__sndio; + pContext->onDeviceStop = mal_device__stop_backend__sndio; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__sndio; + pContext->onDeviceMainLoop = mal_device__main_loop__sndio; + + return MAL_SUCCESS; +} +#endif // sndio + + + +/////////////////////////////////////////////////////////////////////////////// +// +// audioio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_AUDIOIO +#include +#include +#include +#include +#include + +void mal_construct_device_id__audioio(char* id, size_t idSize, const char* base, int deviceIndex) +{ + mal_assert(id != NULL); + mal_assert(idSize > 0); + mal_assert(deviceIndex >= 0); + + size_t baseLen = strlen(base); + mal_assert(idSize > baseLen); + + mal_strcpy_s(id, idSize, base); + mal_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); +} + +mal_result mal_extract_device_index_from_id__audioio(const char* id, const char* base, int* pIndexOut) +{ + mal_assert(id != NULL); + mal_assert(base != NULL); + mal_assert(pIndexOut != NULL); + + size_t idLen = strlen(id); + size_t baseLen = strlen(base); + if (idLen <= baseLen) { + return MAL_ERROR; // Doesn't look like the id starts with the base. + } + + if (strncmp(id, base, baseLen) != 0) { + return MAL_ERROR; // ID does not begin with base. + } + + const char* deviceIndexStr = id + baseLen; + if (deviceIndexStr[0] == '\0') { + return MAL_ERROR; // No index specified in the ID. + } + + if (pIndexOut) { + *pIndexOut = atoi(deviceIndexStr); + } + + return MAL_SUCCESS; +} + +mal_bool32 mal_context_is_device_id_equal__audioio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->audioio, pID1->audioio) == 0; +} + +mal_format mal_format_from_encoding__audioio(unsigned int encoding, unsigned int precision) +{ + if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { + return mal_format_u8; + } else { + if (mal_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { + if (precision == 16) { + return mal_format_s16; + } else if (precision == 24) { + return mal_format_s24; + } else if (precision == 32) { + return mal_format_s32; + } + } else if (mal_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { + if (precision == 16) { + return mal_format_s16; + } else if (precision == 24) { + return mal_format_s24; + } else if (precision == 32) { + return mal_format_s32; + } + } + } + + return mal_format_unknown; // Encoding not supported. +} + +mal_format mal_format_from_prinfo__audioio(struct audio_prinfo* prinfo) +{ + return mal_format_from_encoding__audioio(prinfo->encoding, prinfo->precision); +} + +mal_result mal_context_get_device_info_from_fd__audioio(mal_context* pContext, mal_device_type deviceType, int fd, mal_device_info* pInfoOut) +{ + mal_assert(pContext != NULL); + mal_assert(fd >= 0); + mal_assert(pInfoOut != NULL); + + (void)pContext; + (void)deviceType; + + audio_device_t fdDevice; + if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { + return MAL_ERROR; // Failed to retrieve device info. + } + + // Name. + mal_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name); + + // Supported formats. We get this by looking at the encodings. + int counter = 0; + for (;;) { + audio_encoding_t encoding; + mal_zero_object(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + mal_format format = mal_format_from_encoding__audioio(encoding.encoding, encoding.precision); + if (format != mal_format_unknown) { + pInfoOut->formats[pInfoOut->formatCount++] = format; + } + + counter += 1; + } + + audio_info_t fdInfo; + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + return MAL_ERROR; + } + + if (deviceType == mal_device_type_playback) { + pInfoOut->minChannels = fdInfo.play.channels; + pInfoOut->maxChannels = fdInfo.play.channels; + pInfoOut->minSampleRate = fdInfo.play.sample_rate; + pInfoOut->maxSampleRate = fdInfo.play.sample_rate; + } else { + pInfoOut->minChannels = fdInfo.record.channels; + pInfoOut->maxChannels = fdInfo.record.channels; + pInfoOut->minSampleRate = fdInfo.record.sample_rate; + pInfoOut->maxSampleRate = fdInfo.record.sample_rate; + } + + return MAL_SUCCESS; +} + +mal_result mal_context_enumerate_devices__audioio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + const int maxDevices = 64; + + // Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" + // version here since we can open it even when another process has control of the "/dev/audioN" device. + char devpath[256]; + for (int iDevice = 0; iDevice < maxDevices; ++iDevice) { + mal_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); + mal_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); + + struct stat st; + if (stat(devpath, &st) < 0) { + break; + } + + // The device exists, but we need to check if it's usable as playback and/or capture. + int fd; + mal_bool32 isTerminating = MAL_FALSE; + + // Playback. + if (!isTerminating) { + fd = open(devpath, O_RDONLY, 0); + if (fd >= 0) { + // Supports playback. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_construct_device_id__audioio(deviceInfo.id.audioio, sizeof(deviceInfo.id.audioio), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audioio(pContext, mal_device_type_playback, fd, &deviceInfo) == MAL_SUCCESS) { + isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + close(fd); + } + } + + // Capture. + if (!isTerminating) { + fd = open(devpath, O_WRONLY, 0); + if (fd >= 0) { + // Supports capture. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_construct_device_id__audioio(deviceInfo.id.audioio, sizeof(deviceInfo.id.audioio), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audioio(pContext, mal_device_type_capture, fd, &deviceInfo) == MAL_SUCCESS) { + isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + + close(fd); + } + } + + if (isTerminating) { + break; + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__audioio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number + // from the device ID which will be in "/dev/audioN" format. + int fd = -1; + int deviceIndex = -1; + char ctlid[256]; + if (pDeviceID == NULL) { + // Default device. + mal_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); + } else { + // Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". + mal_result result = mal_extract_device_index_from_id__audioio(pDeviceID->audioio, "/dev/audio", &deviceIndex); + if (result != MAL_SUCCESS) { + return result; + } + + mal_construct_device_id__audioio(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); + } + + fd = open(ctlid, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (fd == -1) { + return MAL_NO_DEVICE; + } + + if (deviceIndex == -1) { + mal_strcpy_s(pDeviceInfo->id.audioio, sizeof(pDeviceInfo->id.audioio), "/dev/audio"); + } else { + mal_construct_device_id__audioio(pDeviceInfo->id.audioio, sizeof(pDeviceInfo->id.audioio), "/dev/audio", deviceIndex); + } + + mal_result result = mal_context_get_device_info_from_fd__audioio(pContext, deviceType, fd, pDeviceInfo); + + close(fd); + return result; +} + +void mal_device_uninit__audioio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + close(pDevice->audioio.fd); + mal_free(pDevice->audioio.pIntermediaryBuffer); +} + +mal_result mal_device_init__audioio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->audioio); + + // The first thing to do is open the file. + const char* deviceName = "/dev/audio"; + if (pDeviceID != NULL) { + deviceName = pDeviceID->audioio; + } + + pDevice->audioio.fd = open(deviceName, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (pDevice->audioio.fd == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + audio_info_t fdInfo; + AUDIO_INITINFO(&fdInfo); + + struct audio_prinfo* prinfo; + if (deviceType == mal_device_type_playback) { + prinfo = &fdInfo.play; + fdInfo.mode = AUMODE_PLAY; + } else { + prinfo = &fdInfo.record; + fdInfo.mode = AUMODE_RECORD; + } + + // Format. Note that it looks like audioio does not support floating point formats. In this case + // we just fall back to s16. + switch (pDevice->format) + { + case mal_format_u8: + { + prinfo->encoding = AUDIO_ENCODING_ULINEAR; + prinfo->precision = 8; + } break; + + case mal_format_s24: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 24; + } break; + + case mal_format_s32: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 32; + } break; + + case mal_format_s16: + case mal_format_f32: + default: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 16; + } break; + } + + // We always want to the use the devices native channel count and sample rate. + mal_device_info nativeInfo; + mal_result result = mal_context_get_device_info(pContext, deviceType, pDeviceID, pConfig->shareMode, &nativeInfo); + if (result != MAL_SUCCESS) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to retrieve device format.", result); + } + + prinfo->channels = nativeInfo.maxChannels; + prinfo->sample_rate = nativeInfo.maxSampleRate; + + // We need to apply the settings so far so we can get back the actual sample rate which we need for calculating + // the default buffer size below. + if (ioctl(pDevice->audioio.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to set device format. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (ioctl(pDevice->audioio.fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] AUDIO_GETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_prinfo__audioio(prinfo); + if (pDevice->internalFormat == mal_format_unknown) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] The device's internal device format is not supported by mini_al. The device is unusable.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalChannels = prinfo->channels; + pDevice->internalSampleRate = prinfo->sample_rate; + + + + // Try calculating an appropriate default buffer size. + if (pDevice->usingDefaultBufferSize) { + // CPU speed factor. + float fCPUSpeed = mal_calculate_cpu_speed_factor(); + + // Playback vs capture latency. + float fDeviceType = 1; + + // Backend tax. + float fBackend = 1; + + pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pDevice->internalSampleRate, fCPUSpeed*fDeviceType*fBackend); + } + + // What mini_al calls a fragment, audioio calls a block. + mal_uint32 fragmentSizeInBytes = pDevice->bufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + if (fragmentSizeInBytes < 16) { + fragmentSizeInBytes = 16; + } + + + AUDIO_INITINFO(&fdInfo); + fdInfo.blocksize = fragmentSizeInBytes; + fdInfo.hiwat = mal_max(pDevice->periods, 5); + fdInfo.lowat = (unsigned int)(fdInfo.hiwat * 0.75); + if (ioctl(pDevice->audioio.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to set internal buffer size. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->periods = fdInfo.hiwat; + pDevice->bufferSizeInFrames = (fdInfo.blocksize * fdInfo.hiwat) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + + pDevice->audioio.fragmentSizeInFrames = fdInfo.blocksize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + + + // For the channel map, I'm not sure how to query the channel map (or if it's even possible). I'm just + // using mini_al's default channel map for now. + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); + + + // When not using MMAP mode we need to use an intermediary buffer to the data transfer between the client + // and device. Everything is done by the size of a fragment. + pDevice->audioio.pIntermediaryBuffer = mal_malloc(fdInfo.blocksize); + if (pDevice->audioio.pIntermediaryBuffer == NULL) { + close(pDevice->audioio.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__audioio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // The device is started by the next calls to read() and write(). For playback it's simple - just read + // data from the client, then write it to the device with write() which will in turn start the device. + // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first + // call to read(). + if (pDevice->type == mal_device_type_playback) { + // Playback. Need to load the entire buffer, which means we need to write a fragment for each period. + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) { + mal_device__read_frames_from_client(pDevice, pDevice->audioio.fragmentSizeInFrames, pDevice->audioio.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (bytesWritten == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__audioio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (ioctl(pDevice->audioio.fd, AUDIO_FLUSH, 0) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to stop device. AUDIO_FLUSH failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__audioio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audioio.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__audioio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audioio.breakFromMainLoop = MAL_FALSE; + while (!pDevice->audioio.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->audioio.fragmentSizeInFrames, pDevice->audioio.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesWritten < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } else { + // Capture. + int bytesRead = read(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesRead < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->audioio.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__audioio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_audioio); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__audioio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->onUninit = mal_context_uninit__audioio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__audioio; + pContext->onEnumDevices = mal_context_enumerate_devices__audioio; + pContext->onGetDeviceInfo = mal_context_get_device_info__audioio; + pContext->onDeviceInit = mal_device_init__audioio; + pContext->onDeviceUninit = mal_device_uninit__audioio; + pContext->onDeviceStart = mal_device__start_backend__audioio; + pContext->onDeviceStop = mal_device__stop_backend__audioio; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audioio; + pContext->onDeviceMainLoop = mal_device__main_loop__audioio; + + return MAL_SUCCESS; +} +#endif // audioio + + /////////////////////////////////////////////////////////////////////////////// // // OSS Backend @@ -14747,10 +16463,10 @@ mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_ty if ((formatMask & AFMT_U8) != 0) { pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8; } - if ((formatMask & AFMT_S16_LE) != 0) { + if (((formatMask & AFMT_S16_LE) != 0 && mal_is_little_endian()) || (AFMT_S16_BE && mal_is_big_endian())) { pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16; } - if ((formatMask & AFMT_S32_LE) != 0) { + if (((formatMask & AFMT_S32_LE) != 0 && mal_is_little_endian()) || (AFMT_S32_BE && mal_is_big_endian())) { pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32; } @@ -14804,10 +16520,10 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con // Format. int ossFormat = AFMT_U8; switch (pDevice->format) { - case mal_format_s16: ossFormat = AFMT_S16_LE; break; - case mal_format_s24: ossFormat = AFMT_S32_LE; break; - case mal_format_s32: ossFormat = AFMT_S32_LE; break; - case mal_format_f32: ossFormat = AFMT_S32_LE; break; + case mal_format_s16: ossFormat = (mal_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case mal_format_s24: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case mal_format_s32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case mal_format_f32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; case mal_format_u8: default: ossFormat = AFMT_U8; break; } @@ -14817,14 +16533,24 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED); } - switch (ossFormat) { - case AFMT_U8: pDevice->internalFormat = mal_format_u8; break; - case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break; - case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break; - default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + if (ossFormat == AFMT_U8) { + pDevice->internalFormat = mal_format_u8; + } else { + if (mal_is_little_endian()) { + switch (ossFormat) { + case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break; + case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + } else { + switch (ossFormat) { + case AFMT_S16_BE: pDevice->internalFormat = mal_format_s16; break; + case AFMT_S32_BE: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + } } - // Channels. int ossChannels = (int)pConfig->channels; ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels); @@ -15538,7 +17264,7 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, pFormat->bitsPerSample = mal_get_bytes_per_sample(pDevice->format)*8; pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. pFormat->channelMask = mal_channel_map_to_channel_mask__opensl(pConfig->channelMap, pFormat->numChannels); - pFormat->endianness = SL_BYTEORDER_LITTLEENDIAN; + pFormat->endianness = (mal_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; // Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html // - Only mono and stereo is supported. @@ -15591,7 +17317,8 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, // Set the output device. if (pDeviceID != NULL) { - MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &pDeviceID->opensl); + SLuint32 deviceID_OpenSL = pDeviceID->opensl; + MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); } SLDataSource source; @@ -17449,6 +19176,62 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) mal_CoInitializeEx(pDevice->pContext, NULL, 0); // 0 = COINIT_MULTITHREADED #endif +#if 1 + // When the device is being initialized it's initial state is set to MAL_STATE_UNINITIALIZED. Before returning from + // mal_device_init(), the state needs to be set to something valid. In mini_al the device's default state immediately + // after initialization is stopped, so therefore we need to mark the device as such. mini_al will wait on the worker + // thread to signal an event to know when the worker thread is ready for action. + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); + + for (;;) { + // We wait on an event to know when something has requested that the device be started and the main loop entered. + mal_event_wait(&pDevice->wakeupEvent); + + // Default result code. + pDevice->workResult = MAL_SUCCESS; + + // If the reason for the wake up is that we are terminating, just break from the loop. + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) { + break; + } + + // Getting to this point means the device is wanting to get started. The function that has requested that the device + // be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event + // in both the success and error case. It's important that the state of the device is set _before_ signaling the event. + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING); + + pDevice->workResult = pDevice->pContext->onDeviceStart(pDevice); + if (pDevice->workResult != MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->startEvent); + continue; + } + + // At this point the device should be started. + mal_device__set_state(pDevice, MAL_STATE_STARTED); + mal_event_signal(&pDevice->startEvent); + + + // Now we just enter the main loop. When the main loop is terminated the device needs to be marked as stopped. This can + // be broken with mal_device__break_main_loop(). + pDevice->pContext->onDeviceMainLoop(pDevice); + + + // Getting here means we have broken from the main loop which happens the application has requested that device be stopped. + pDevice->pContext->onDeviceStop(pDevice); + + // After the device has stopped, make sure an event is posted. + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + // A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); + } +#else // This is only used to prevent posting onStop() when the device is first initialized. mal_bool32 skipNextStopEvent = MAL_TRUE; @@ -17500,6 +19283,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) // Now we just enter the main loop. The main loop can be broken with mal_device__break_main_loop(). pDevice->pContext->onDeviceMainLoop(pDevice); } +#endif // Make sure we aren't continuously waiting on a stop event. mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed? @@ -17668,20 +19452,6 @@ mal_result mal_context_uninit_backend_apis(mal_context* pContext) return result; } -const mal_backend g_malDefaultBackends[] = { - mal_backend_wasapi, - mal_backend_dsound, - mal_backend_winmm, - mal_backend_coreaudio, - mal_backend_oss, - mal_backend_pulseaudio, - mal_backend_alsa, - mal_backend_jack, - mal_backend_opensl, - mal_backend_openal, - mal_backend_sdl, - mal_backend_null -}; mal_bool32 mal_context_is_backend_asynchronous(mal_context* pContext) { @@ -17765,6 +19535,18 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun result = mal_context_init__coreaudio(pContext); } break; #endif + #ifdef MAL_HAS_SNDIO + case mal_backend_sndio: + { + result = mal_context_init__sndio(pContext); + } break; + #endif + #ifdef MAL_HAS_AUDIOIO + case mal_backend_audioio: + { + result = mal_context_init__audioio(pContext); + } break; + #endif #ifdef MAL_HAS_OSS case mal_backend_oss: { @@ -18387,19 +20169,6 @@ mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice) return pDevice->bufferSizeInFrames * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); } -mal_uint32 mal_get_bytes_per_sample(mal_format format) -{ - mal_uint32 sizes[] = { - 0, // unknown - 1, // u8 - 2, // s16 - 3, // s24 - 4, // s32 - 4, // f32 - }; - return sizes[format]; -} - mal_context_config mal_context_config_init(mal_log_proc onLog) { mal_context_config config; @@ -18462,6 +20231,7 @@ mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channe return config; } +#endif // MAL_NO_DEVICE_IO void mal_get_standard_channel_map_microsoft(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) @@ -18863,6 +20633,65 @@ void mal_get_standard_channel_map_vorbis(mal_uint32 channels, mal_channel channe } } +void mal_get_standard_channel_map_sndio(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 6: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + } break; + } + + // Remainder. + if (channels > 6) { + for (mal_uint32 iChannel = 6; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-6)); + } + } +} + void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) { switch (standardChannelMap) @@ -18886,6 +20715,11 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m { mal_get_standard_channel_map_vorbis(channels, channelMap); } break; + + case mal_standard_channel_map_sndio: + { + mal_get_standard_channel_map_sndio(channels, channelMap); + } break; case mal_standard_channel_map_microsoft: default: @@ -22555,6 +24389,23 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu return MAL_SUCCESS; } +mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut) +{ + if (pSRC == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + mal_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn); + mal_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut); + + return MAL_SUCCESS; +} + mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) { if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) { @@ -23056,8 +24907,8 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount; } - // If the last of the input data has been loaded, we need to ensure we don't read into it if it's configured such. - if (pSRC->config.neverConsumeEndOfInput && pSRC->isEndOfInputLoaded) { + // Never consume the tail end of the input data if requested. + if (pSRC->config.neverConsumeEndOfInput) { if (maxInputSamplesAvailableInCache >= pSRC->config.sinc.windowWidth) { maxInputSamplesAvailableInCache -= pSRC->config.sinc.windowWidth; } else { @@ -23249,6 +25100,15 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount } // Read more data from the client if required. + if (pSRC->isEndOfInputLoaded) { + pSRC->isEndOfInputLoaded = MAL_FALSE; + break; + } + + // Everything beyond this point is reloading. If we're at the end of the input data we do _not_ want to try reading any more in this function call. If the + // caller wants to keep trying, they can reload their internal data sources and call this function again. We should never be + mal_assert(pSRC->isEndOfInputLoaded == MAL_FALSE); + if (pSRC->sinc.inputFrameCount <= pSRC->config.sinc.windowWidth || availableOutputFrames == 0) { float* ppInputDst[MAL_MAX_CHANNELS] = {0}; for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) { @@ -23257,20 +25117,29 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount // Now read data from the client. mal_uint32 framesToReadFromClient = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount); - if (framesToReadFromClient > 0) { - mal_uint32 framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData); - if (framesReadFromClient != framesToReadFromClient) { - pSRC->isEndOfInputLoaded = MAL_TRUE; - } else { - pSRC->isEndOfInputLoaded = MAL_FALSE; - } - if (framesReadFromClient != 0) { - pSRC->sinc.inputFrameCount += framesReadFromClient; + mal_uint32 framesReadFromClient = 0; + if (framesToReadFromClient > 0) { + framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData); + } + + if (framesReadFromClient != framesToReadFromClient) { + pSRC->isEndOfInputLoaded = MAL_TRUE; + } else { + pSRC->isEndOfInputLoaded = MAL_FALSE; + } + + if (framesReadFromClient != 0) { + pSRC->sinc.inputFrameCount += framesReadFromClient; + } else { + // We couldn't get anything more from the client. If no more output samples can be computed from the available input samples + // we need to return. + if (pSRC->config.neverConsumeEndOfInput) { + if ((pSRC->sinc.inputFrameCount * inverseFactor) <= pSRC->config.sinc.windowWidth) { + break; + } } else { - // We couldn't get anything more from the client. If no more output samples can be computed from the available input samples - // we need to return. - if (((pSRC->sinc.timeIn - pSRC->sinc.inputFrameCount) * inverseFactor) < 1) { + if ((pSRC->sinc.inputFrameCount * inverseFactor) < 1) { break; } } @@ -23712,6 +25581,28 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu return mal_dsp_refresh_sample_rate(pDSP); } +mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut) +{ + if (pDSP == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + // Must have been initialized with allowDynamicSampleRate. + if (!pDSP->isDynamicSampleRateAllowed) { + return MAL_INVALID_OPERATION; + } + + mal_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn); + mal_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut); + + return mal_dsp_refresh_sample_rate(pDSP); +} + mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData) { if (pDSP == NULL || pFramesOut == NULL) return 0; @@ -23970,26 +25861,6 @@ void mal_aligned_free(void* p) mal_free(((void**)p)[-1]); } -const char* mal_get_backend_name(mal_backend backend) -{ - switch (backend) - { - case mal_backend_null: return "Null"; - case mal_backend_wasapi: return "WASAPI"; - case mal_backend_dsound: return "DirectSound"; - case mal_backend_winmm: return "WinMM"; - case mal_backend_alsa: return "ALSA"; - case mal_backend_pulseaudio: return "PulseAudio"; - case mal_backend_jack: return "JACK"; - case mal_backend_coreaudio: return "Core Audio"; - case mal_backend_oss: return "OSS"; - case mal_backend_opensl: return "OpenSL|ES"; - case mal_backend_openal: return "OpenAL"; - case mal_backend_sdl: return "SDL"; - default: return "Unknown"; - } -} - const char* mal_get_format_name(mal_format format) { switch (format) @@ -24012,136 +25883,19 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint } -typedef struct +mal_uint32 mal_get_bytes_per_sample(mal_format format) { - mal_uint8* pInputFrames; - mal_uint32 framesRemaining; -} mal_calculate_cpu_speed_factor_data; - -mal_uint32 mal_calculate_cpu_speed_factor__on_read(mal_dsp* pDSP, mal_uint32 framesToRead, void* pFramesOut, void* pUserData) -{ - mal_calculate_cpu_speed_factor_data* pData = (mal_calculate_cpu_speed_factor_data*)pUserData; - mal_assert(pData != NULL); - - if (framesToRead > pData->framesRemaining) { - framesToRead = pData->framesRemaining; - } - - mal_copy_memory(pFramesOut, pData->pInputFrames, framesToRead*pDSP->formatConverterIn.config.channels * sizeof(*pData->pInputFrames)); - - pData->pInputFrames += framesToRead; - pData->framesRemaining -= framesToRead; - - return framesToRead; + mal_uint32 sizes[] = { + 0, // unknown + 1, // u8 + 2, // s16 + 3, // s24 + 4, // s32 + 4, // f32 + }; + return sizes[format]; } -float mal_calculate_cpu_speed_factor() -{ - // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline. - - // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value. - double f = 1000; - - // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout. -#if !defined(NDEBUG) || defined(_DEBUG) - f /= 2; -#endif - - mal_uint32 sampleRateIn = 44100; - mal_uint32 sampleRateOut = 48000; - mal_uint32 channelsIn = 2; - mal_uint32 channelsOut = 6; - - // Using the heap here to avoid an unnecessary static memory allocation. Also too big for the stack. - mal_uint8* pInputFrames = NULL; - float* pOutputFrames = NULL; - - size_t inputDataSize = sampleRateIn * channelsIn * sizeof(*pInputFrames); - size_t outputDataSize = sampleRateOut * channelsOut * sizeof(*pOutputFrames); - - void* pData = mal_malloc(inputDataSize + outputDataSize); - if (pData == NULL) { - return 1; - } - - pInputFrames = (mal_uint8*)pData; - pOutputFrames = (float*)(pInputFrames + inputDataSize); - - - - - mal_calculate_cpu_speed_factor_data data; - data.pInputFrames = pInputFrames; - data.framesRemaining = sampleRateIn; - - mal_dsp_config config = mal_dsp_config_init(mal_format_u8, channelsIn, sampleRateIn, mal_format_f32, channelsOut, sampleRateOut, mal_calculate_cpu_speed_factor__on_read, &data); - - // Use linear sample rate conversion because it's the simplest and least likely to cause skewing as a result of tweaks to default - // configurations in the future. - config.srcAlgorithm = mal_src_algorithm_linear; - - // Experiment: Disable SIMD extensions when profiling just to try and keep things a bit more consistent. The idea is to get a general - // indication on the speed of the system, but SIMD is used more heavily in the DSP pipeline than in the general case which may make - // the results a little less realistic. - config.noSSE2 = MAL_TRUE; - config.noAVX2 = MAL_TRUE; - config.noAVX512 = MAL_TRUE; - config.noNEON = MAL_TRUE; - - mal_dsp dsp; - mal_result result = mal_dsp_init(&config, &dsp); - if (result != MAL_SUCCESS) { - mal_free(pData); - return 1; - } - - - int iterationCount = 2; - - mal_timer timer; - mal_timer_init(&timer); - double startTime = mal_timer_get_time_in_seconds(&timer); - { - for (int i = 0; i < iterationCount; ++i) { - mal_dsp_read(&dsp, sampleRateOut, pOutputFrames, &data); - data.pInputFrames = pInputFrames; - data.framesRemaining = sampleRateIn; - } - } - double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime; - executionTimeInSeconds /= iterationCount; - - - mal_free(pData); - - // Guard against extreme blowouts. - return (float)mal_clamp(executionTimeInSeconds * f, 0.1, 100.0); -} - -mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) -{ - return mal_max(1, (mal_uint32)(baseBufferSize*scale)); -} - -mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale) -{ - mal_uint32 baseLatency; - if (performanceProfile == mal_performance_profile_low_latency) { - baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; - } else { - baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; - } - - mal_uint32 sampleRateMS = (sampleRate/1000); - - mal_uint32 minBufferSize = sampleRateMS * mal_min(baseLatency / 5, 1); // <-- Guard against multiply by zero. - mal_uint32 maxBufferSize = sampleRateMS * (baseLatency * 40); - - mal_uint32 bufferSize = mal_scale_buffer_size((sampleRate/1000) * baseLatency, scale); - return mal_clamp(bufferSize, minBufferSize, maxBufferSize); -} - - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -24211,6 +25965,11 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config pDecoder->internalFormat, pDecoder->internalChannels, pDecoder->internalSampleRate, pDecoder->internalChannelMap, pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate, pDecoder->outputChannelMap, onRead, pDecoder); + dspConfig.channelMixMode = pConfig->channelMixMode; + dspConfig.ditherMode = pConfig->ditherMode; + dspConfig.srcAlgorithm = pConfig->srcAlgorithm; + dspConfig.sinc = pConfig->src.sinc; + return mal_dsp_init(&dspConfig, &pDecoder->dsp); } @@ -25624,6 +27383,21 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* // REVISION HISTORY // ================ // +// v0.8.5-rc - 2018-xx-xx +// - Fix a bug where an incorrect number of samples is returned from sinc resampling. +// +// v0.8.4 - 2018-08-06 +// - Add sndio backend for OpenBSD. +// - Add audioio backend for NetBSD. +// - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD. +// - Formats are now native-endian (were previously little-endian). +// - Mark some APIs as deprecated: +// - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate(). +// - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate(). +// - Fix a bug when capturing using the WASAPI backend. +// - Fix some aliasing issues with resampling, specifically when increasing the sample rate. +// - Fix warnings. +// // v0.8.3 - 2018-07-15 // - Fix a crackling bug when resampling in capture mode. // - Core Audio: Fix a bug where capture does not work.