From 40379d174f35e1eecce6b2b0d5926d6a428f6bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=98=8C?= <392871505@qq.com> Date: Sun, 17 May 2026 22:19:40 +0800 Subject: [PATCH] perf(acoustic): Optimize classifier and demo, remove dead code - Cache Ort::MemoryInfo and I/O names in gunshot_classifier (avoid per-inference alloc) - Reuse single FeatureExtractor across all files in demo_offline - Add per-file and total inference timing to demo_offline - Remove third_party/kiss_fft directory (fully replaced by inline FFT) - Update fft_utils.h comments to remove stale kiss_fft references - Verify 20/20 validation accuracy maintained, ~160ms avg per file --- .../src/acoustic/build/demo_offline.exe | Bin 211439 -> 214524 bytes .../acoustic_analyzer/core/fft_utils.h | 4 +- .../acoustic/src/core/gunshot_classifier.cpp | 12 +- .../src/acoustic/tests/demo_offline.cpp | 18 +- .../acoustic/third_party/kiss_fft/kiss_fft.c | 424 ------------------ .../acoustic/third_party/kiss_fft/kiss_fft.h | 184 -------- 6 files changed, 25 insertions(+), 617 deletions(-) delete mode 100644 src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.c delete mode 100644 src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.h diff --git a/src/drone-software/src/acoustic/build/demo_offline.exe b/src/drone-software/src/acoustic/build/demo_offline.exe index 4aa585407872771226605da7c6b787b986a531c2..085c1e9374f69517d1a0d2350e8c6ee01cc7acde 100644 GIT binary patch delta 53430 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 mKI5j{Xmzzili4 delta 51709 zcmcG%3wTUd_dkC2B$JCsW`ZGZ$%G*8iMS-LlSr5_2z4zb>QZ+p38`YDNm|n}8r|y9 zs)hzpN?KK+T8Ud+N>jA1)lk(l4Q0qe7yLAuwn7*agn27AboTgWm&b}rF`;XX z!qdMTCS>_O4GMaV5F`egH1$?Qcrp_o%<6fWe_%qX=Zeqb%2xkWKCRvT#{0-nt-){f2nD~*77Tm4I<4bifq(6?&Z;fqUBNGV zb+%3$!UVgJS;^9B>)z%kp35!X!B zX?Qf4SQ@iK`|cB$3z1T*ZAg&43AZ2=z@~k7J0$GdObFVA>>_Jw%{^4r(r9E`USp4} ze-LQtz*fq{(2eY`vmN2L^oZSxGMepz874MDa4iq@t^)kw^KxtnuE?MZb|Ofmis&Hx4>LB|k`prkEZ6dt%W0 z3{Anb*!?~E;F<-j8`t>1*saKm2^p_N3#s*W-wf_;wq1}G+fZn9ugpUcqn%UbfF&$k zdWR@GsJ%3pEDmiSF8as$CL+Dr_Pun8U-KWpe&P`Up{ze25^%Imhc_W+k4FMCB|ude z-{kFdp#hJx1wrPSZMUTg{*ta;K=D)71?daEMi&|I##7dPDUF}kwF>CxW?5|ar5;?b zZ>6i{W|?hHNt?|R^=$)w%u@ARp#K=p(zgm&_mt(7R`7lLUbP2C6S|c}@0`?RCSwQqnWigP zG@lfM~SD4m=eas)UIONlOCK5TcoB5IO*&6Rzyi3czY#q0@jAavfamzji!wj-6 zJ}kx3veFzAA2w2#UH5n1s8v*8+_KUwp4`g7zT)#*N$eaS*SbM;W*a8N z7J8Fe4ui2FiJzi=-*6f@+4po=@5>h7mKrPv!*&O8iMAr?5&x`poe{rHrHnnyc9Ueb zf8kLJ!7)GJGZ-&dm=rO62hf(u!d=}c?M(#k>PBiMvNrQj_(5F=!g}qu)xwi?e-@R$ zgJDt7HY(Bg6!beRVM&rWMWG_3538sNC`49~F3XR+t7Ar3ve_{-EP^`8U@D&Nce7(z z7`Z3uHgD6;#47mAcFh~^nT$C5%CHP7L-uL3Go&aW?cm$nH8w1fKIlwTagK@uRh$8wJh`1N1OpR)u_I!K#}11fI&8%77tBj;7Rt89 z>Mf4(5l3S6&~(CVV`fKelG%37VmoZM#hTzYZ`4N(mBjjx28(UH5p+1wM`C$F`&w3M zFd1okK&B@{qvU*y>C)ccXt8I7O_3*?EIA@E=39#+FDwNtUDjGHl*N?kvRZr5$n767 zIZ-#O$ZYFv)YZ*WWCDAKo3oGUvfhEH*`_GB07=0Sm;^_I{H#!OH8s$T%qT)0B$X3U z*20XQ&)PNQMT%C9+&D&QFNdcf#M|@2GLV*<`;JcUe;bPwb4gZM29*+jx|SFf6q%5H zJzh63FKi`*@5Jg_9lE0%X)T(pYhSF(e9r?si>^%(H9V#VHJ?*sktvoQSq{X@!;8jN zx-M%G!X@!x(KiRD_lmb&i`_%hXKbN>Y6d$(5FR8@drv_?u5F;v1Y@LSy%kr7Jcfo% zFqE^-Grq-kHIbD{ zjgFJ#vM?jD9AC&NeS};QFKq^zVP=FZe~pH-Ek4X7)hi)=D`h@4W;EhSNdaYRvxJ$= zwxMAosm$n|@fd~8@3~y)V%D%il0ZsD(kYFFWwC|6in-s^A#;&OiPT57_yzP!bS!w! z&ei2|pUMS;Pkonag!EFSk-S%R`2}uyYl-y7%OvQH9&!wFcXcl=ldINjPm;{+fjutB zToUJldKH>I#7Z*rsHZJ1NOD%!*2S%@L|Xs6vi=ig&sY-g?Smd7NCk32)DE?VPo3DR z@=Z}vDt>gEH?^)g`<5R3G}83(B(_p+Y*OGuli_4FEp-k`tFN9` z4#;WSkGE3dWlB{;u8eF)9`dl312p&f%2}x0y-_x)!~l_+^ultx%N^aAVtM=}>h@Ol za=!G)<$abaHrZQ(h8|u{D@_{s&q}+r5wc*4+kA*3@ z+R(XTz%Fwb_%#f+NZ$_Y1ozhY&5?VE#&|Oe>6-1s!C8Q%4X+3m0hvS(+7h;m# zaC{aP@<`d+DEr~+KGb@y; zj+2#VKq<`5k{fnO^!S-CPt|3oJ#@L2qB3?1tvM6HUJ({>E+90cZMmzj6CpcYofIe^>tlK#dAr< zILgNomLeVb8-56VR(2B(tCCa~epCuZ?*meAp#Co{PEg$K#41iBC~>BtE_Gijv)5AQ z)S=?wt(@zBLfr4TQJ_%8O)9QXakh$vD~epqmBtEjK#i!QMzjPvfBA_UyR>K7JgG}C zTh3>9d5v}F&Mt|XI~lxVbnk#km=o2ts*1SDL&Y2IPoHeh^ z&eIUxfEIJ0xgX!Vab-5+o=J{a)3PB+XIx|WHQG%6@HSukeS1$_kE9rac1tVDNEsf z%q?p7g{boib!7X{uxMKNcH$Z4XqL+NnD2$2#I^wq)B^Ncnr(+!s6SkgUQ#iemnL@C zlwIWk{pxCtT6pt*&DaWV>DQn^T}p`*oBAs$wyXGKf1c5=E*s9pe!;8-|FU01k5$l4 zqRmy2c0dc(yrzW6V+oN(X4^#zhGGQ8!a0&D>V$ArI?MWymF`CpX2y27Idgkda<9MG7(!siWW&XV|t172kB@yY?A znuC3K!+|X{pB&@y1Dk8EeZgl8{LK305vrOa&hyV%F_oA##8k1!HYCMP3DP1l9-*pKg6MVJc+k_A&G6}3tm{n z+VaX5k~Ehm@_s`_)|+1&nxJ_+fwvqc#}Dh%^D?8Er!_T=!_eY3mFvFQwh-&Emob}S zF{8{iYt>c6C0*8>BFr_f(<)+=fe#$sxI-f)@skQUu~h3nq-$K2?mYi+c(ApVlI*ma z>_E?dpWTi9?(9Zje$;*QLT{RKO*G|vM>%1R+YgK_)GPWvRrSq*zIZ&0K<+JB?nu?d zxz-CZ-sp=cO(wrR+|X~Hs%bJIsjpI(tg5R6br@$@a^9E1|D8TXT?kJaVQ5-URpT0R#cJZYndRiBwzVG7kq2#V^>`^QikC96-N$()9SXwAhn-u;j7ZTk~>f3{?^HN)c z3ZY~=<{NhvC1L|st|EAYQP4uQlSrtP$)KowMOJxVs{c=Siu%_kZpO!stXIQZRq#&_ zZXMY`v$cYMFmg6)!2L!I(Ugqi<3^>k0sQ=^B+a9-yw!_+0_NSK3~!?>|42n$`QjHN ztjoJrTYGl2EjsefrSs~Hn5+lYnDKWlf+9~;LouJ?{>U`XnO z1h6gUpf3BSyi|sqRm7;72_c+vH2UJqcgGK{Cq+@cX_OYqzM)8+iQ?18ucdka z?3d%%OZ@1|gP9+1Fkz9#F^oq}YR`{O2w^?AG{LI*dMKYdaS-abPd0eWdg0neY*6tmU0liG%M#%!f{qxC47;vT3jr$;D; zjphv|H}UnkisC)QYYnP@7RAR+u2;iLmAKcQXH0I!y6{btn{=)N8Cp_iK$%ohNY%DX zeiV{wY*qqBiaEJU2~<$E7470rCfk^fFMTCUQ`wGhf5pt&@qb?FqPdsEBc`;|97*EI zQ`)gr{GBOH*&M!eN(#`2vnU*?w z=0T(M;#QfN_NQ~Z!j*)!WTgjsz&9&+!PI7c-?ecw9K395bH8;};W0dLT64ddRpHh= zZd&u2-K)Y6a(L>rW;Ok)7{_w>#%ay`E@DbhvRKW3fQBtq;a9lV^yYr)RpHLO3&MS? z!dgCM`sm$DCZbUEMpVW-h?I_3ftw>@F1l8DeoP%*IlsSuo!}(@=8?(qQ3Wu}4TR@v@mKYlK6}JmH~K z)SEA!)zPaJf>JgwoYhq`=OX`WR>qz};z17X?$ zbBRe$r8(lwq<$Ft&VOh>%;6tEiMBkGsNug&WZ;Ff!?7~II=e9o=C0Xsbs9E9J)Ne^ zXvGtO$CWj|@nI>gStGtACCVo!3})LKGrlh+IH3k)&5og%D&5Z=BN3fwABNFe6K0VH zlk}IIgmx%pg&zGRd6TLWYA*7$6wE_jZE2k-5AwD0!;3kyNU!YEET@!Ey%f5n6|x%b zizEp}=}*PF$T58+vn`b}Q67OTwuPQ#eLs@N(&6gn|ce7;d(k zjkGIJ7n?f~R3YKCHDGr~?;S}x@x1g_nkNl;X?kDHu7*47zZSt7_ePE0)r~9F zg&&Z-v1XxN_9&S*Qrn(SUeHl1o4Ji`71Vzzq&ZcFahy=D9PXdd&$pSPb^AGKj$L~JB4C+_l?yK+RUoN_-sCu?1X)Y%*g~p0+V;CHGt`|^ zk2!Dxw`KQoRx7&~e?;HQ3M*oKU7%>-}kSEP8I>CXcDI|o)JjfDbF6iF)VLiW=8Rgsa6tY7VzQu#H z>U%XpNJ`;7vRZ3uY~a(fzR;Y~abva#&$Z*T8?#y5n%$`Oj*rx}P<9uZY0!1O`S$Fu z*?Kw^9SJv4-b!OUvJ-h7<=YQq0}(Ag#+Y_E2#o(3vg=O3c_mll?EMIKsJHVLE4##G$qPn5~iCRfAOG!A@S}ADhnq zwk0$>Ais4>J3=q;FxXhx6X_1p3ab5uC0?BgGb>r@M!7aH z?PnfJCaG(cB z?r2=6u{UXb!tmm0Mp!uR%`P3{4;?Mq#$jn?p*q0!j!Hs}ig%)v@HH9Thd|2d07@?m5$prV zXN97O&Md;-x;9*9YeV@mFs*%$i-SWEPtTOtwcUc{ zqbRx(ZCl=Ebyr>I{n#SFDI5(|nl_xLt&Y(Ac7bnR-9hu!1%7Gu_k#*(ez7G~mJfhG zIp7YM5cts@h(f^W4m3x=6Az1Wn)(QYxC1p2F!P6NOsqcd{{DQ;g*|-N`~BHI&OT_t zR^r~s%N%5}Yv+Kh$6x*+1kZ?Xe9%fS$f^%WN9p;{?#~b%q;u54gJ431*%oI6?;`KD zwgG?qK|>6TUTf>|h_wN1F{pOB79HeMR_GeNAQuZ>Bfc83&H3iFFKG(va}&fHe;A-y zA(MJV?|JCO=^p=S#H`O}e%K&F8%c_wrvWM|2RrSqQY@Dp+DW^qti{p~*wwUa>)~~C zW)ZV#1NiO_n`*8FQFJ2_P@o|cr=kkU8YjFR3HeqD)l)QF3T1r)`s3?*T(a! zo4eG$9^lTdi5!Wh;RRb8@xJ+8x@|%n%26BE&#e;M(u}Ms(Po1~^jKH&+@`E zS(YQND$6H$LPq*Vct(yb>_c)G4o-AFyY{XfU6A_6G+ujaRKS-A;<=|%W<~O$TNnAY zc1vt%!Y^-a(((zG)o>VUvUI#-bd}n*61Z}r5i$e0_2DhHjk8{7Z=`a& zPb1p3lb#DFJQL2m2?GY`VSi(uv$K`vj>pb@J7+Ou(0W&&8Ygn)iC=pnmlqtUUnBjN z%Y|uQo1e?q?OK9$nDNU<&BKo=0X<#(3FlGh8$d}(~?Al6-&)34#MNOk;O4ga@zN0oo@@6p=q`3Oy2bxrX9h_zJt z{|g(aatbzdHG7s(EzG~#b2;Q!EPW@-)!mR9>_>Mn4NuY9qaW}u_x5Dz{O`T-*qrS8 z)rf$*@541EdC4Q@-242auO`;BfpteWHbFnxtXo{;E#s20#N5IK75bvsRuCC#` z_BGLbx`toa*T{b}#oWZ&6|$LJ^L3PeFXC)dIK6q?*G;W`!I`WZQG7=im{3t(0|qAO zWtvQVuv)RaVu5UVxjQIZ{u4sZIOw7@6}b7IkmK7I&ufK&6$Ddf6-h1Nzu<7Gs9#Wm zKqd3lL~A_-e*NpZ{tGDZ8(slGXfgNxW)7>(7ktx@)#IzaS;fq}?fy=zE1$5xlji0s z{{H@s{zq0JL2W#OA;J6n(*CCY>xlCU#y>ahUuP37hfio{& ziulXrDv?juBM;ee1S5j-3w{@lpbmd@$lveb3Ru&bn7+Ku;ej=eBRp9*w3vn`Uw8QB z)~;qR`zQluh>CG4c2Kd2inVq=Jlu?77b&!4Nyr8q7E!u&BT~-Y!8CsgVr^2=LO&5q zrDw^^mw07K7&G$5M<;16F6UWCn`*vZ&bJ(G9I$CQidY9NN@-W{vq#6G#iNe3V%~i0 zu~wS14*vGBHo7kzkfQG;NNT-@RCTdn;VW}+TTK!w&@*)IhcE;VLbO#Ll(*RoeHlVj+K&> zNlGX^9^9~*`p>CQP2O_e;Je@se;uZn3(`M84eb;;FCvDVrtY2#A)USJNGuh{D7HG zRq?`C;K*;9Fy~;Eu`(9V3Q;$jQ; zkc7i9dyZ^kF2H3N!na;*(Q5A%tRjYzEOhI#`oSAji3cmpf+?(^~I>+c+KiF;_~tmHSZOt*eIOU~d1z-3tdCmDQVfbDQ>;Vkk*4ueC9KO)Dc zBi>@4LGPE<=vhkiW;uG)za}53#J?rS$51@2!V6?Cw~Thqe*C#vuO*7mC$i9Af1u;F z=X9p27|;7A3+<%P!O3vx*bj2aP4wkJguGpaMr!;4?^@Q_+Dy?w`Jo)p5nG66r4sll zF^A-s#noeO&m;kVCEtm1%)3gOb4rXLr|E|nDf?ZQOUlJ9wot3oOvBl7Y74++ur{x* zc#V>Lh@8Bp9HS=s4J>3CBqzFa3jy1~a_oBhFQ1M~rhY+Ph;|eaVfT?@&(o^{rxXLE z4uweu2`-pzccoRmNn~YMKC$eU-eJ*Fmdv7EFz|E1m*x-$U319yNN+n*zE3j!fns#S zjq)ubUM@1PMVa5?tFKNBD};e}<1rI%B|@i<)yLbXdg4XaSnOAw#6j~T(%0O0tuEel zcDdHsw?CxltAg+NylWlV1HScInC8|@{{6K`e0bvVOAAed&v~0)TC^DaquRsq^2ZhH zNMOKQP~U7Qs_L$;)gsEPasQnQei_GbH5t7gQEtn;R7T&o<0RbL=QNN-fy|hfu zNIQ%~!JMM_D)dvS1NW0!;|ql@Qfz~;rttnX@qpnz>gPzr=F|bHW>fyI6y{rNisE+y z_xQiD*ig3* zkC1{WfXb>o>T;FWr^eob8(C(QQjx0sDDgfa@05V|vr;r}PCD=ohg$ut4o=tOX#mFs zzmQ9>Yq?K;zm;hF&TKm~Xn^fN#)4!)m-RAr*vthfAV+{4U^n|D+SB!k6N=3f{-N#W zk!IbFBlHf9B|O5xP%x)Rr%xfdZ3ZQ!ra-pv8H$o@mu@+p1%-IW zd{<;qqGLEqz}fue5)@i&`@)j()dWogk+%{euUl~JBq`SMs(iW>pC53a^2Wh$MHZFU zPN)-6wm4GCE|jx(G0E^lJ}13S>Pn9V%Q%GW8PiH^Q_C^8(CJK6n2o43Ug z=PfN?feu*G*GR?Dg{xH@u2iwbR!MIUVGg!tpPsj+gRa%Sgh-4zlIjOLH0GECX?Jjx z)vk3~`=cz_hbGq$d0PH_&y^ESOHEq|x!>%oWE^}b`prIBYEKYE>19GG5II89eQcbX z4(F*-s$Nz6l8@KEwt_bm9uRQBsRzTi8*dE3IQ@yU&|vg=LUJc(pQMtS&Gx z(H4v85$$L}6GbGpETWgqc5M{Q=F4w}TK#47vY<&249eA>JR5+|_>Y+l( zG}$vcv;k@`7qL<>ETtmb2go91pxI&aA4Ptmrs$Iex z-D+k%rmXpg_Cu0mt{fi^=(1}Q5TxVP4&`*9D`zz-PuYIfWzSQRgvv5sC~pNIScpuJ z20G*xIV`uxi;%V0^VCS~twhK`wulU}U3KMHC0Vf|93U&kEJ2!Jr5U5CyDobUs?B2W z^f{%?T!=-CE-Q%4ES?x<#z#prXy`em@=ery5{{2M+Jw{63Y+0eRYfML?Fl(H(f(!y zGPJZt^ClKodYA{m`I^X*QaYhSeQ1GN1ISIbkg~GarzctL6U+l*ZtAj<&4uIW` zjyw^zTVj6KWsc!D{|xTjqOt6ty)YjJ_BGO!gwb(9s>mZhOk)8HJb6Wkx-OP3$ZTw4 zn<@;}eM(?hUo?qb>2j_dL9Ru-KHEzpP+G0XM9MOl$Xz)VD1baxsM52p$?vs>!eK)D zbO<|3iGw9;5|YbD5llolZ$3RuZowE&rq7F<<#OtomGZ|FJLOkwlH!PysCTIc5U59u zq{Y=pNrN2RzV@Iw^D)z9e~4t%YR=P?9vRE|sr`k{%)*IKccMS14`krp^l7uE&*JrO zx3IoXvltR3qX!Gl<}fnyw9^bGpqzvY=OF@q^3*s2fA3^kTLt1o0M)@1s<#Xhz<{UG zEO}psK8#;^XuFwrF3F1~5+V;GU8@RP6~^f*h7vyTT=?_n!W$5VF&?l<3cwZ-&76T? z+!+hT;|*v9dDUpwj!GcgZo6_MwAx+W;%X*O*3Br=SpyZXD&4M0CDOKnd^K;I{3ROK#c^RQo3kmV&{z(!x+4><~3c zW1#cjSm`!$0E~1_mUqMsIwKS;UrvSB(k&qJbmTE92s)e-DblVTqZ&6wsQ_m$H4XNn z{g1wcy0V^GFAH`gL43X-*Ao(kAzbA|oYRzK&LlN?9m*|?ZindBlWqf%>W|)}<-VHP zUFuqRy$>cFf?IX<;?9aRVHaTqgm%d(RUs#R%LHbw%v6nX-T)ITA@?_inK#k-+G=^y zUt{sQXvbes_2Y7pN*NsTY>36p@6d84jz9dX8QaD~|E|ma=FxvQ#+RkT|L$9>ecZEU z4v7lG-*DYO;W$(t{ZEE<46*5W#9oRv zXeqHgTcSOU=WwC^FA^Ci4JU7Bjyp zw$3fayHp04ep^eLv>7t9Ke}8QWk_S? zB(&NQ%JHS|Vd~q5%Y%@jCNHk6r}?Yr&Wn|QG4_Oi_h3GI6ZbGa{b4B1*gGCteGAt> zxkGzlJU2WVjZY?CeUu(ijwfw%Hyk`kjnApJP=i?Fc(MVXAHxAkjVLJ^`4F%FxCsm7 z-5>YXoJ{6R9@oZh;d@}c#|s~a_zx4+-4vvs%CA3e+SmXNRyC4=@^KgjnyIwdpGmYG zmR{pwPnt!ZfCRQs;uV1(MpLf$rT11*I(086A0p)*6hw@jFL=^}@_T~(ASLgqDEU2! z(|E1oxhI3LZxZVY!nuPNUCq(Em$*9M&FvSi_Wp_Q$r}q<=rMvICi}96eEQplmFyqp zzpIkXXY4~UMZ-?|el`#eK!fLJoSp0{W_Yj(EL6Pi!75m^_=6`~+vwd`2%LI{jJ!=B zHrQqIzVr}JD@YS17HQce_Ve!MUaU7`AMc*#%|OCG)t5j|iNBgl+tU%o5#}={EVpL7mN%Lr%IISjY z#g2;a)nqN%KyiOf7TIiBcO)3JRIL>pv9ldD+kNTioB>DY=z}`v{kKGeKZ|D`;*0*Q zJL@cp{_H{h@<0@YTyoVXGRg8$lnLS|0nEta#1jFmm}Q6#9eb4x7hO7bp?jWAO+)8c zjLHWzN~#7~N=$?$=DsfLTX-jBy@3NU$^nZwWH{cYaM03N z3gk%VoQe_?8eX~s9vZnREl;a`6MvW3=$8{P#&e_|iWcSTRjJGGltle*ae?d)M=$oYTVx#t1wsn!fYI76bi_bd_NMg ztGTA-DJiE&ua8A9w@;*x9%qE5B*xrK?Iy4El}{b5c-4mkjV&@Do_RQ^yo??Mpb;w} zL5iFQF3LEnknq0rn(S$4;{=IupkLj&L;ByW7$+rCG}K5@q{}XUz*{1HNdf4im6H_e zu2<<5u<78K&I%T$G-(7F&M5dG;y#cRW*jkt!9;H=mS!N2s;|}NBOt}$MorXD#tYI# z_&Vx&#gd6+G2+o=7o`3qTUV70R%N#ECmLDT|AC;9voZZB^ z_Zz6MiMmPDPSQ3FvCf4qm{iG_YjEw$c93CPWM{oWHf#z--nwMt9{mE(O1z^qmu5FRYw zFA1cWyyv#CB1n{HBFA- zK`1+!MGvl0H`zv-`)^ND)TK)ya#7GJC4)?EeY6X$`J67K~ud}NdB za?umzaBQN)$K2Ar^C<{YXQC9yD~bA71O@PL5e=k2%3S6_Fqz^IP9iLfii+xmMU2cz zAr2(u7;Fut98uKDsz%DHtT^GWs6YzrX-MPH53AC+eSx~+A;rm&Yt?O6`hf&`p6^AP zQ2k_toTY6H+Ol1W(k6Ydh^C*^U{o%9<9~XPO@=#Y0BSL7#OoMY=(Cli1Nq6dh~b+8 zK8^XxJ5;-d)2cipHoBKd`dRKPDL!UMF%xG^wJ;jI)O3w@OiG7>0A3>WnC?@q=`iQsy zD0#Ifo3hmHi)~dWAn_&SVOY*c+ApA$CIn%mafhx%dx za-grt9X$2t&Iwj&6#UNLk0bO`_;LStyk5=f>mX?3e58M<#@W9q>skyM* zXSL}c!$zYnYt2Iu$97v=@q{axkP+<)(NK{yM>9~r0c~-Z^cEXF%)f^HASw}^zFDcZ z5FHA`dkRGib!$f5?t7+gNe$Jlyr}@BKX*M_Y$l`Gik(TbIp%>bt4vGUlZ>QNsd0SP zfbCl|F{sL+A6vw5U~+!uB%k4 zWe8YoNoEU11m!}hsSW85Igl&~<1^`UUDkM6i*lAa(UFEC|C8vLsULvv;x9l>-Y6Em zL$HQ5iakPDSJp{P4`C7EV`#ZzA6RORIZ2yKeahta?L%D0@MyI06m!fOT~;TtG=#OS z`3|0gV(%4s>axG`6n%}XtF=!{GUB!rin#&j7!t_IE*G)+l4wtT1V@hL3Sww51i{`! z+if@ij33mcI_D%KtIUI%n4fg-bV4%cXx!p$<>i0C<5cN5v4*U~>h7rm#jC-=N+Hlt z{x!}S-sl2b9EOr`Fgv9Ed&f5id~#L~Za#5+CD)+r?zX<9WRNahAOY}Fr0--(HS>VV zPLc{$Hu)~8!B&LiAFrr{vS&8a#n(ews8@Pbko6^`;&oqY;+L}6d&Q7j4@QFRSe$P6&>BxB$JvJZKEcy@kkbMF z(_KxoU1|9N*w=S_MDr*eV#f(mI@8<|hnJo7rZZYPJC|xjUPGw+M{@TyvkKX%S=_%S zBKJf3WFrp=sHx=Mi{}Qj@@pg1yXB6Oh4=xszn!)R^xDZJ?r6fI*Z{F>Q`U$@tZK?) zSUWkO34A1uY|1)o43ET|rmUg0@*!pr9P^fcMCr!eJmzi|spnHivwdqZs6%%(0F{aLJU$V5uQYCSYubIf;(3Q?RXg7U|LN#&(qZU1l?M zh)-Ts`rUaNoEXK_7S!K<2cK{bydg{w6Puyge}-;*-2_{aEileL)awzNmp#IUOQl?& z_;AY|rLBrO@9a!A;`4zkcNN^P;x!qMIJ1GyxhivqikH>Mn`-y~FsGfo>@UJfLfVO2 zj&s}qsC3Yy(+Sz!3sTf_g=3P6UlkiSXAMT|!Y8gYPYG1_ne@S%`c;~zn&hTKS@+TN zjt@P;PJnLO3y?qFl7^`Zm%}86wc_H|sI~MIad~qV9?^LvhVm`9Z=jusFmSMjkiThr zD7}gGCEhMN{5w-BV~TjAIcsIwH(QpG+B~20$A~y6{Vhiyq(N;&PLKr^E-hCLxwlMA zYQY)|=%xWkDXOA=6>B(+Ep zceh~S8vhRB)fTKy$T*6@LZ~nH5~vgGn1w7h;FjY|8IA3Qg13UiHsLI^_3UeCyE4p| z1F$A^80;veoR9x1D(@1cfJa5+OFd_c>EX;6epZkCu@hE(ck8wkOoH~gc;vh%f4j9^ zFYgEXR?Z~8c78J;uQU>yrW{fg>>G#CMT4lRCLX?|s+2S;x*dK}ha5Tt5xFem)r5$$e-m~?uE z%e5yEd>49<_6zXJf5u}det)PHO4=RDz%Gd(96jK=aS5x`+*m=_7w6WBkCOH)bQ!jY1#BcL zf8xF<7Ph-0icRvVIdxV_TBTY!FQJ zBe->T7xiHH9o!7@b}VZ@xG?uw3rjzGHk^L$Sv48Yg;zfpE))mHv5?54&#_CN3!i^3 z94!_jS!ncgMn}BlIk~ z>9bXkA`XscA+d$3$wiZt%nDR=w~jl$%pLz+k17;bK#jFPRg*kfRilP8)UaDkt{N_V zUX9xfcl!Tih8myrY`j${QWY3qd0J^$X$U!LMnuzJ?sH*P%X*0pfpT`ih2*Gbm_ztcP05KWh(VnFnqm| zB1y#v70oI}t7ufwB(5;CdWjWob-t=PRqg+?y4|gjcS+a zeDF)F(FH1v+7M8MRWd2n=%ng&wY0GRsr*D{l>->Lf*!|a$cS{%f=Xkk3P~!BrV%1k z8vR5tsq|{lMwLDfIz^?)=p5Au3~?d38b3lc!sXIqg2tb|gHoFr9mKcCus(sKzEm2A zJQKO#mbecz+aTLC^x;PYuRf-m@eFyoReL9=Ixi+vnpawLupE-&!CD;h=MXp^K0< zpDGT-I9w7Ul7UuS6`(VKzv8L{T>*T8OW1c4-7U2{_e?TQmYGK<68RU;-^g=-|} z6yQc&V?Y-GjoE@Q5j5fQHw7UDbPmw-E%XG?dSD2yg(L$E!<7NrWCd{tS55q0w17t& zy$x1_CJe?E0on-c{I(!;1Z@JEaalko0Y~CW0!_ah`x>sHpfiB2an^eb=m_9GTxFn( zfK%QRgtgmI0N^fM)_eqtK-|Q&4|D}EL==QWppC%gxJp6i0C%hrg!7;afYLgo0iCg4 z5ccC*xP!^qWdq6qns6Mhm7tS>({bg3MpqPSek=(2pn+DQB?4a{K==x-eIx{2n}^98 zbS_Z4Q4p?!Cj4bH`Uhyjukr=q5okiq7D4dA0}^4zHq;_$!mv-#JfI_h@8fC%n(z>= z=mKozVloo?VaqEX5lO(?xRO8=!e)B};WB8#a$FK< z!fty7;V;mHc3i?|_{nu(m#R_A?OI;2e_7kCiL2mJ^@+} zbnZv{=OR#nz>s1=*akWoxE@y_=mOx+xDJ6X1L_W-RzT~4y>MLyjRl}E1D6Ck1-Jv( zU!Vyu;}Skc+JlfegjxYj81yYJ(8g~u?juj4S`aV+PvVLIO*s8K)C%Yn;4)lhN>GZ@ z;7S6W3k>)k{R6Zf_!h1ypb33{Kur)I*cn#_XcKTUu4SN8enhjLL;tWMkPE`=JgnV> z1i+>jVBM#vI^aB9deE4dg*~`@KoLPwS<`Sy(6VR98^~%vypo@SP{=}$( zOd0U;T@;LD{t|?-Pr!$sWT3UKz=ZAy7=ivCOjrpTv%X-!l||kPxJS!`FJK(r*5d0q zCgkU0)Bw-n$^%^n`~_Do=n7!5H$G2=Jk|)pLtOj77ku!ey|@ZN>w%kaE!zbDUjU*Q z*L6w&e2A-n68Itku5FY6Sb!^hvw}f2n6MCrnt*TlgN7q20-n;LFpv@Sa4z@|b_!?{ zFdtVV&_%#?2J`{=j9g&XdKmv}^C47^3FC43fKCDWW7n)B66k^T@J)3FXd|#Wu8w&M zMu9eg?g*TWYba>K8Mu~#COnI4Eoj1nAxt;~`VjC>T%H?ITUHPajZBC~pb@Yyt}M`m zXK)X}!jfrqYz-rJ!Bx<1pngcYJ6GBg1E4QPhzyY{|b}(TeFb!8U=nP;suDs9X z|Nn!Q7O$+D#_Dt`!`Op5p}8Kz2p73oLSG*bt%zRPMpSGVH*8M z;^)&@9O1p`tc|scP&2|Wxkid_l24IWsb`s{LjMsSVO{Lym$$L^^uj8QwoE5H){OX7 zC*1JR-qHzAJhXr7gm*kAo!1FjTJ1)?uuQ9U=!HD3_Cvk!gI1fZ7j9`k(|oEIKChug zvJ$`33_5r~3$PvrXp!zio#C+#C8%Ft?+cgnyb0)KFmncLr? z!Hi^lt(mM3;fpibD#AN6*&BrOX0fjQ)gpNIX@V?-S{Rx=F)n*x8HM8shq_@d?+Pap zgM~#j=Ke0I&0fe+F1BtSG7_0~w6KeZ&g;_+V`389z%u)m{(1h>gOf+z3Uy}m8?ziEYg%B z*L5kZ3FUeqMafzYYvGz^s++vVUPO5J;@8+4p5!4Kty;`#4io-h0{t&RIi=KK%3u1Q zn3upyKJ%1U3SPu{_`hnrg%wOV3SKm1-G9r#^AX1{X7#K=7Zr9J72P87YB)>9LKQ1i zjJ~9#o1$XAie)MWT~^|Us<>Lk%PI!_B*$BY?g}B~sCZq)@GFXf?kZYT9HU~oiYryj zRk2h>^Uq59g(}`uu}zu6pCKbwHMuIIRK-UsHoB?^PE>KVibqwfRI$x9MQ)^u=_)Q$ z@vMr*UsQc6<`W`+p+aRuUsnXuRV-4`^H+s$QE`fj87i(;u|UO26{Bw`>5^2;RdJt+ z6+p^g2>VSDj8}1@idib=t5~XH9Z8Wls@O)wwJM%h(Z{LCMW{GN#ic41I`4i3Tb*Q88S_4k~t4F>v^ z|H6OCe&N4yVAL=E2mVX(OaFxfqx}B|{!8^s|F;ACANen<>L>nB2PT*Q|9}5w{cj!E z|HyxJ75+;H_87oNL2u45yy?euTNQ@v3}|Lob|cu3)a*B@K^`&rcceECVe{c(*d8_ks^YZfw@(S~c@=Ej0=auDEMGOwUR; z2j(#iE8Sgd6Kh=u*mEzt#(<&w*nTf@!y(p4d~|@l$x_6t2k;&-xr}*go1l+pJPmu9 zsn6FDD-N`xiZx z)x>GUjDxHR8!5Iw%$jOb=+WS5wAPMrZnf}wgmbEecgyNR%UFo`&0&Uj;nm{mdtj5P zS~wPAQ?>9^gmXknyLrwL#xzAiVp*uB$?jDpYyrcI;J{-zz+Wa#KgOB@-XSOzA0A^N z+9jS;U~odTMIeia+mscFA;(!$jc{MAJkEl>-oyh4=$vDy!9$>vMLVLQVh2f;R?0D6 zOT8dfNhz9WfU@*W@JhvACrIocnI*n)f`xk+1DN>z3D#IVPNC*%Xm_n|nHifDmExF_ z^k`QpW&l}|s5^&RIdBr99aT~7YuME=in>$KY`m*z9&!rRCdk#GWr5fe6hAt}rn6kp z?>ktVE4Byt6;vvZu^uc?{OlkzX#8r4r7&5Wj0dg?@dFaA5cd-ZRLl@jQ;K|NxDD=C z%EGik7*l#VBkxF%XWd4vMZhF}RmwuV>6cPX9!y+uj5XHvG(i5oJL6oD{mBeo(`2Sn zPdT7d#QxtSA^loxino%`t8-m+nn04XC>l;`mo#`vAqE{ zQM)P;`x@enj9y8h{jL!;@AT6w#4D~b$j5Tdt#^PX!_c_}4^|T$3Ee>bLywgIjKKt| zNp=MEKH{U(FsW+@(Q3pm2xRn>bM_LAFsZkkgZF*VeN`*Gl0%`XpAxYG^cf|hzWB!( z=Hro3OVQ==UR!C@LKqiad zk??SkIjZnnS(w<`9FV1Qm%^`*(U>I7nqy7Gn&(&(9sTOG5jBm9Kz)RYE6y^5Hn1tR z_lX~nx(CQy@lTLmBSEGZMEyAyteXou`MG3XAAy;q2zZ?UU1kt7euOX!1C5sTkn`vw z=b`BxCT9GAewd9&qgehU^4beBMYh!IPf8*?q6Xr?bF7wE;5d|mi8&oLb;Jed@bbT% zxc?m7PEs@c7k6U&^RRCY$UH%exXOZAq1gI7hRTy>V$ykJ(z`j{11VBVL2gr|v?oF5 z%WPIl{22+fBU<1kk|MYkWT7f}8g!8&=p)X)0KuN&g7B@(shtWE16=J=FL?^WBr#NC z2Jy-TRzsVDQu~Op4-wW}WJX;kq&q?eo&ps8q*h?@sa}cyOne z;Jv>i`DShL@<5ee19}Rw_y-=<4+m!?RrMlq+)of~+a9}z3M=X-&=?D94gTRL2!j-p zs@=;Wv)bETZz*DI3yneZpuJe}6F%rlCYRrE)dA)scD;hR$~S@>)3__}FX2SG{YxwY z8I$GV=rt8|g+gl`pwY`o{%az&>bU$3fr3}yW6!YQagk62gQ)D(lkpl0oDASo&WGUS zKEwG19OE;b<{dC}p5aUdCuf>$mG&Iy6xr2kYDS3>7;DNb-!}@mEBrU2#EjomzGh#P zm{Q7`cvm7mC0Oww^`>`3Gr&eN(?e*NIw?IBREBu4jJ=}$43R!!`>SvQ-**&~uA=&C ziu13szL*C0gX?=60&9Ix(_-;VP3@}g(WDdBu^w`N^r9}G5-ehGCs<1zdYd@t8Vf-; zo_&qAU?`|*s{XPVygHt8f6?s#eZ5Lw zqP!CEx(lXJ5zuzQD~ky6btg=o0kVz4_F4~mWsPUuRC+JGZ&#BIGSRs8`yG96o!I(! z)qd`QP;;sNLbO$6uvZ#rlgtkGT2qbw8g#PCzg~^@i$m3` z{0^WCRC*3*W15&;0fXXJ-9&qOzy$q#rNLT*HqIAK%`{js&J`^;*))yWPi)sk(^R~4 z6Vp{$ja9d>5KL0apm(9tpSWy(KWm`fzDOC zfY(U`O!MUdrtKb2W8eM9inC3ltIk|5w)8z(rN1e|+W)Z-T-gh$F%;aH}KCLk|@pp1Y@Mh=xFl_{3`kZDcU?y{~anj7q{CAFojwMDP%wr$Zr zx2W8rwih#B{=es*%bhtgpO3@6-}^kzIp;agbI!TvUT`*2LLQ6WSSK-I^DN!T^f#eIdn((6W}gQMi%`M zjtgYb!Ra@lO7ztCFu8{h&yfSGX)O79LIt!E#{u{Bv<5TD&l-T&jj%EqWNgfibV#}Dlr;WO9u)xCa@$+VE(?#8Mk z=Lhr5kR(WNjF3x%OdKE86O0FPUlOHo5_8fwMo=~%b8>eEQLj@Br?EZe(5Qg}`$mIb zP|F}~K_kut(Sa$#9#RO4VS_G9f3VzOIbA$~W2?MwEFIdxis{mixJPke!sm#ym^bsY z-yu=&-%-brZ>9*Mrb!}9_+*CT1>irNh3);Y z-61!SuG^;HYC-9G9>5XI7eC>My^hC7#0sF&Y3GBc1QNP zzn-S`n1_j1gUNjY4#~+zyE_I_{jYEuhh1he`EaZ)h;31QTJyDqT`J5XmA!z`^{n)eK7`sUvR-(IodCiR$g3KTR3V$7{&#u zm8RPpLf>7$@NL~n-D5;D>&+J9GwGg-SOi#UTA3Ibyx9RIZoyFLE-TeUVc73Bi@`e{ z5`)dBDOlkA<0w>hvH&~{_#WjmOt%7V2c!&$n_s0rF@2QjH%yzE?xzt!pl6t7S@88e zsA!I-<`7hARy4?7#@Wyi8{;WE6xhf3JmcRoE{oq826~R^f*>(?C$5Nsc*?gfE06n&g=}%03Oj~GpIOq>d-QoB;$8;mDVVYwD*vE7}6wJl+7Sk4{K@s>m04in< zr7;nxoEQeOlyMkq)biJMK3&2W+WBZ17`A`@9RVvZFdaM)Uq_i9rAY%pt?>YrOygML zUjEw4rz`lvaX#{Z33nWw8Hhq(W$YM)uak_^<7n0(VEb^8^^8YB#e9~Y8U*8YOy6U= zfoUuK!t@=cX_5Fk#k84jiv&$g0BB@74ho`S2px!o;RhK1ALGr8%Z5-`6mU1=shmuo zGtM4D%NS2i1lh?r6DlHT2pwg-opCSYU5q1LFlrAcq+*VO$(Tr+~##JDumtFEDh( z(73^X?RHv#pJatN#)O9li;?1pom!c#V78h5!>rX#;W1#ZGOME-V%Wu-L^RdJh#0aX zQAIAA$RZ(AQSzIdb#;tW(L}}tj8iy|-T_88-4YACK08&%;%X}7@fT2j$85Kq-eC3s zvu47^;z-eKC+85bZZJ_CO-pbhw&Rw9;bd493DNWd!zMd@$Z$KL`8-9%L3^{E#>atu z!>o(SnKjyJ3$rX_rSMtlRh)>8cIsicms2&8Q!Ne2A{y*8eJJpG#$}8f8Q0rsH{%5$ z;!G61gA-9_r=J+MGOUlHR2yKeo!t0I;jESwMGr8EkL4a5QQf|EQ_vA z0PA5kbr2OZn@O9PO-Hr~+aP+088+FO?FKV{L_-r{ZYoW|5BiD4ng>#KBE-^Z2QxR4 zOw1ohZ!jBA-!j|B%r=mmb}$#+WCsgE9+|tzi(_*tJ;P`zr`AdS`U?4BPN54-+W|yb z1dUIE=SSJeu?gNA3;}04mWcYCtM6tMY}41eCVgI>E@DRa`X^~!p{$=m z0)k==Of{o__K+Dr9*Fr|#w?5s|7}uIncmYd5N^FB%Hy8Z;($;!J-z`r_eg zdMZgIg$$aHJA`e#x{AeW>7Pkrct{e^F^L8yVW}cN*UTq-w>jCC!wNv5_-8TY=dUa* zUxjzJAEoXaQOR`QzpuahTD1fjD(8G#;|M04>&6g)}|bx@=c zVz#+SUYbUhWO+$j*UU*4S?EViYaX@fm-u!TVSOoCR0v-bFNFDY>qscN611Cf^J4l3 zpL$TYF(Q?kMvHh_l`O(3dz6S54Z76*^zbM#(%G7--wmKmb;bGXu_CmouI0# z`shgNxLS-NR4kl29Y_Sv4PpQ`Bl-E8^D8)8ZdiC~O4bxtU0q#LSyhx)#NEc9C(s4H z?Nsnr#Q5Loj#M$+whyQWmt+*P&dYU%93io+f9E( zQP9>hwTr%Xp{SWw-c?J+W}O7Bcvd!c zzCllczV)mey%resd(aQ)8vFqUYpwx97UH*nek*tRVsH!}&(!L-XCjX>w~LUdZ? zP~RYZ;-YQWiIG-*DDK~~OkcS3qB&MrTV!u!PC-X_(ov7K*P%BWG!^q<`r~z&|FThk z2x=v7Hd>eux>?ewEe2f$I+{jIMm#kc@IFBNe|CV@M-Au$oU%_^-EPo!(1#_B+G)_& zK_}56w%TmKe*soVxXXZN0YB0pwH`M|as%yC#0snrADp6j{5k&7#qCR=s`KWsQTs1n ztej80e1(9R#_D2Hs(*o?FE2X#DWC19oJGGrAd-hnwE4C5wJC}}M56S#o`ig(9$fd{(XnaHy zG7u`(=Zv0BqkS17PN0ZaGGq~-^G_HEzX3l{R2qJ2WmLnyEF)6e?~}_B2faN7?%H|+ zdpMtm(37kcU_YF`xj|%LIy0o4KcP`F*z(I)mDkpmRIki02^g@}p*^tRx(}W49PP?P z4^5?mSvWf@gSNwO3{MVMPeT~q2OZCI95D9aE$k0Yso6WHW95@YZzH=XWg6yWD{Y$& zJMr|#=}_7S!3JHa4adC!vquAsn4yhWnsViIC>KcORQhEGl;4L!ldk+3j=KYtn^<`y zHO@rwxw81y8L-f*S+LH)pzF`J#zmBUqeu=Bi!hZvCx?}5cv#jxPD!==y4IjKg1)QC z4v8{gxrEwu;Umi|RKI|#W}&}R!l-2|`o%O#n=X=XQ!3C7J@xbKB1*^-F3hBbDcDt; zHCtp+*Gx1ZlMbO_!Y%EXi|E(cI5i#6$Cv};&JinU)hvt^?O9kY9-fWq&ZkSSq2V_{ z+I2wY^>VG^HRY>{s%lD2Pe>aPt^<90;UCDtBeTUcZ7*9iMr+fo$kkiZJBl0|;O6`} zuxdRh1Lh#L%)u~MV5Q#ov3J{j(Tvp{cEYh7&_c)~TaY+SR;9d~(U>M(@%QvaHWUjEX-$Wt$KC>ew@b+eta%-|tw~;-3&}1iDZUJ)vU$QD{3f~>H;E8O7ap94@_VGTeIBG8S7_y1L-452 zAD3}?wDVTP<;T$V9MKh~2z6L!gEqW;UHq5p^$nK$xThaTiV=-fSG`n(tmu_7k zCQ=CR{=KvsU#F^Y&zO}!9Sg)P+Rr1e%-+K9hMC^CR=PxBtD+FjF>#bidB53S<5lU$bpxl{2X^U~~ zBi`fPi&AJmFjaD)8H?eBd-;L`<w!E#qB`pJjY8ft+^$$1}D;pOP2h20etHxdZow33UHL1d)%Cl}0QTvv-t2Bo>-v z&jXC7rx^0K;G+z8GfdVX9lnIOY^+eF_{I2og=?6Wk9Oqpg)Mxc85hL3MB1H?DqOq> zJP-ybZVB+@M2ar}KClU4ac>?5h3$-ICQ@9Xn1wxwpYu?)!FPaVC(?FiQ)$g&uqI~n z6KU`=uuS?Nv&f~eJC)n#TZSq$uf=XR#3(!;vXzO{1X+p)r45W58CNG#WD)T0H6Z61 zhZF#>Po(f-U_0aWYt>OK(Y}kP_fz#%p1gL?Li+Yz@c{ zj2js5)v=RtHpkUP#x02?N+I1+4N?uWRKE;gsc;`2#Jrp*CeRqZ)XSI7@uhuysZ(Cs z&k3>$cBrlhUny`SmH?Vo8eclYmwv*fm@fZ9Ltl)Xl5(w_Oe@%MZ879N^vmhXj#%1O zDn^bzi}kMz{JtkFojBD~K{we=hn8copRrtw=4_6p7gvjcVY;1UwlkbwI)@uEO|Swt zOWK1K%*_mF74AW|A9;VXg`p|_mECg+n;crL>C zQa@-@0um#-0;ZPIgbFbuWHRcJ`J${xj#iJ7I*(S5ztht!mr1WxpdK!*9&}~}qI*j@ zmd6gdvl5rFVF)Sl;{bZ45;L#on5>G22CT$bnKf9h!fo{3Mz~3P$mYg2h(2Wn@$GpS zmJ6S*M9i=EAkyR3q0{a~m(ym%q$p0L6&^%VCqD`KGsnz(=vrjc7alR2lKIg_I>+9v z2n@56N>+o`Gkt++7gHO3#`FWG<2kBxV8$Fyh1FuVIGRLzYq5*>1~U_V0!9zbZr%z%wgy|_<9Wb2y$0U+Enl$mX$DN0YiZtERC;$3_wJ+&hq~XE|FxIh!u`3gjr&jIR&4n)}QmqA!aql)nzySkNyUUH!5V zuSL`$8>8!xjj$6hX65R#y5{O%0=3E;Rg&QIK;gEbJeL3#&UzUrymnoS3&!jlCw9m^W$GYRZ?dT2@}mlQEf`L7@!Eoy z$9tpP4T;6RkMQfXs#ZZiQrVBx^&=av@?LJJsh`m9ex#)zIns}G^doQdBd4yy zZ(*wKINeXEw;ws*kA$UO-i+9O#LYK$ zWZ&Vy)1Ui!0K76vM0oM|pokwIcyy!k<(BW!eJNWx9vvo0LKkCmOE)J;4oELH7q|dP zDnR({V?Wsg;%W-C^ErrTuxuo>&}*jHC0K+EF%ZZMkOIX}1xQ~!$-*82=}=^jg0v3{ zEdIYBok}yt-GQ?7T}QTq^0R^X7c;o~r0Go{1#y974@jqBVu>C>6>A zIjeNpa*!TH%ew)nQ*q-VCdcGhB#Zw$$W#>QZ|j#J9;M4JfH-0TeU_9DFK!F$`y!A` zrHUReP&4fMO>Y3K9_+y%mauf(tR2$PP$4%!9cQ>u6pq)X|0$1+$|EW1FwYEsPv>QV|T1Zh-U{SZit zLXLx+^82Hxq>krE+w*~a|2OJ6U>7Q-Lb zKyo!mtJ3;4EQ1efsrUWEAiamt|Nb#UwPDS)^^)M!`+ z(zl8fY{0Yu1moZgyYv?AWP+ho`M2c%P}BIY9e9n#RicJO~hYgK}KBuIlYtE7XpDwST!S||*r^upGG zoKm{uWsr2G_~3H<1<4ep_(ec&qt8|rR^kyedRl4O2FRF{5%NWlvoxqlBzZ63ut6zz z+zKR&Qf>x_dq7~Tvq0(;navu%@P@8n+8d)qdlJkTopO^q)XnN^Q@9 z_>@*VE07(ELo+~58HJ&}rkg>~F99J}2*Mw~)2k{FPIXH04??I(DeMG@O{q{1NUzd+ zKZ5lA2A~X{_(}wiVtE2chvKuAN`zCol3%Ywh{F_`dSLvMi8XN8`3@vYF&(thWa?H@ z#|CmlY3n!;{w)^2ro>PiNSNZYP`r*~ zRjTL!IST&=^v88T{M)b=P+6flAk9i)i$F4!zUTjVJzXh`{}1*K#jhWL)G6bsqY6ox zit_!1jRPuEgqDM}DABN!WfbQh0`Vw5dkw^<_#?xE;uTU2(tQ~H4^6$Tn}8gO(2F3A zTLY)Ae}NPjZY*3@g_J_RC`ol5GCsva&eeEu>3yCWaOM+>)O;Hbr6hf>=P5dO#+yQa3fwhEEjrPS?x$TTzswx}1x+7UQ21y`eP zibIn@dX(vTz8A=&xMm%QPwBlEK-5tA5~NPiil~8%(!y&A;73dWlp<*RB- zO$AEpw?f8*X84n`4W!-?Sp54ST_NKFkqvw4uEBv7ngf!%Ezq%R*U?@Q&X^7*#kN4s zLH{Bo)KfT=Z#VRX3;&g89j2i8ozAgxM1XJBD>S}RC;$z)mpqAp@fK{8_kyKB>(NGOkjegh$g z()w3G(iQS=mO){%WjHYPkKZpL(`6(b%lr?d6+fjG?}ip)ob|=9?9iinMm>5`NtlHoR%Kk;3zDUf*Fee?@(qYfiK?i(;e4g_P7o`q zIL|*$rvr5>#oq#QN(rJ;Hlz%=J3yL@w&Es-4*5ik_xHe=it3+197;Xk1L-zg33r%2 z1!+;pH!P#X$dwx)sJJo!zP_)pO{DaH4{n@DKD45P@Tfb{(~us#Qa^c)Tx&u<2?D#dSL znPY*IM{@(hD$8(VX_co0>rhj!($;67);O#$MbK~Z1w%#VUC8hqhaC4|!t^y0rP5>X zLCz>0bR&rOtWi9x%?EPW`U+!G1k$9$M-xa8uK1m=E`v*JODZv7nHrQT{svkuqZw@Z zU64$#;-c_-5ywgo#e?t%P-`8Qq#$8Rg*+g+O2BRhNsaH*DlMw4swqJ}C>1&e8BZ|! z-*5UXP^(g*jQcL18nZ!imE!LLDN|hiB1n^B=TneIg+y)C;$s$umA;l;2jo)HBL^g1 zY1u}QFr&+~?IH}bEp&wsON4E>*r2%Yc@SQ(_}lmn6Q#D@AT5Rr#&RCYP3?-a#@>%` zQerm)Bv+kWyg==SArxbJ6r@0LRtHF9a9@SA$-eON`PcFQh7!fjO(3m`AMOM>t0Y|G z1L(^}rS;n(q%LFr2-0n|1H~8dmaRHTo`g)Z;bLyadmz1v<*z}|e*rlVw8`YWZ220u zNluymL>UFrO4sZ~U%7D@rucX( z{x2{bO3(ZNQh;C-u*d(fl4ykc6t!U>U55Ku=4y}*qd%F<2gwWztYRrhowC?{rV)Lm z_RK2~YBkifz+Q$dQX=dhkm)fB(`1U!SBlJ+kZ~I_T%n)`kyggU#{>^Myq7)gm;|9_ z#UEuLRtUoZxOGztm={yQ>zlyGa%$p+_)AbOljFR5VsQ5Z-VqHTK@s@C?ooi z&B&nr=zmmP50GS_W}NvQo6baWTK^6zgN#q{SuGQ#Fdv9psnB~MHufU?uNNQpNMI_; zXEvDhlzvTtOrzrJ`w9?@N_X4>nH0qzTS1zXDn0?i!F=fw%9l~y2E-qK54{D_rL_Jt z5UY}$S8PGV7~PRyQeC~O3SFjDdMaetss1>-9i&rM$g3Bh59CsW?gsHFXy0u z@j$)Nf$eaE^eBC{3q+l$j)I(0qQU$aLeFm+mM4Q07;WV$&H`x)$e@j+BAD-&Hk1M{Iz^a}GQFqIJV4@Ui-VT>5(Pab4QcAcPq(RYI2oh!_z*^o3 z^I|LP)boK=T@9fgLx@{=H%OyFaNkzK)T3y9%chl^fcPDVCCEV$8F>}X(n`wmOREc4 zmYC9tOO~x!o?lp9*w*z2aVdtLIx6Dm!ZA@!hdRaWZSQu7qiJo;y~1M-)&AN@ejas4 zSmJ2PIT79#{k?e7LODN*q_%5+7ELih`Q?WRE% zA6^;#yBN~8YN(|&s_hr2<@6Y=7{}^7P z-80eh@3t+IEU%ks^>vo}spzP%)Ai#l@okxtEivJAz-hrt$uljZ+uoaLNiqLF-uMe& diff --git a/src/drone-software/src/acoustic/include/acoustic_analyzer/core/fft_utils.h b/src/drone-software/src/acoustic/include/acoustic_analyzer/core/fft_utils.h index 7274f5a..41bb394 100644 --- a/src/drone-software/src/acoustic/include/acoustic_analyzer/core/fft_utils.h +++ b/src/drone-software/src/acoustic/include/acoustic_analyzer/core/fft_utils.h @@ -15,13 +15,13 @@ void compute_power_spectrum(const float* frame, int n_fft, float* power_out); /** * @brief FFT 计算工具类 * - * 封装 kiss_fft,提供复数 FFT 与功率谱计算。 + * 提供复数 FFT 与功率谱计算(内联实现,无外部库依赖)。 */ class FftUtils { public: /** * @brief 构造时预分配 FFT 计划 - * @param nfft FFT 长度(必须为 2 的幂或 kiss_fft 支持的长度) + * @param nfft FFT 长度 */ explicit FftUtils(std::size_t nfft); diff --git a/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp b/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp index 494a83e..8f6d33f 100644 --- a/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp +++ b/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp @@ -100,7 +100,9 @@ struct GunshotClassifier::Impl { } } if (!config.model_path.empty()) { - if (!LoadModel(config.model_path)) return false; + if (!LoadModel(config.model_path)) { + return false; + } } loaded = session != nullptr; return loaded; @@ -120,15 +122,17 @@ struct GunshotClassifier::Impl { } } - Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + // Cached memory info and I/O names (created once on first use) + static Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + static const char* input_names[] = {"input"}; + static const char* output_names[] = {"output"}; + std::vector input_shape = {1, n_frames, n_mels}; Ort::Value input_tensor = Ort::Value::CreateTensor( memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size()); // Run inference - const char* input_names[] = {"input"}; - const char* output_names[] = {"output"}; auto output_tensors = session->Run( Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, diff --git a/src/drone-software/src/acoustic/tests/demo_offline.cpp b/src/drone-software/src/acoustic/tests/demo_offline.cpp index f090700..a027bdc 100644 --- a/src/drone-software/src/acoustic/tests/demo_offline.cpp +++ b/src/drone-software/src/acoustic/tests/demo_offline.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "acoustic_analyzer/core/feature_extractor.h" #include "acoustic_analyzer/core/gunshot_classifier.h" @@ -50,7 +51,8 @@ float compute_spl(const std::vector& audio) { } Prediction process_file(const std::string& path, - GunshotClassifier& classifier) { + GunshotClassifier& classifier, + FeatureExtractor& extractor) { Prediction result; result.file_path = path; result.true_label = get_parent_folder_name(path); @@ -71,7 +73,6 @@ Prediction process_file(const std::string& path, return result; } - FeatureExtractor extractor(sr, 2048, 512, 64, 0.0f, 8000.0f, 0.97f); Eigen::MatrixXf mel = extractor.MelSpectrogram(audio[0]); auto [label, confidence] = classifier.Predict(mel); @@ -217,12 +218,23 @@ int main(int argc, char** argv) { } std::cout << "Found " << files.size() << " WAV file(s)." << std::endl; + FeatureExtractor extractor(16000, 2048, 512, 64, 0.0f, 8000.0f, 0.97f); + std::vector results; results.reserve(files.size()); + double total_ms = 0.0; for (const auto& f : files) { - results.push_back(process_file(f, classifier)); + auto t0 = std::chrono::steady_clock::now(); + results.push_back(process_file(f, classifier, extractor)); + auto t1 = std::chrono::steady_clock::now(); + total_ms += std::chrono::duration(t1 - t0).count(); } + std::cout << "\nTotal inference time: " << std::fixed << std::setprecision(2) + << total_ms << " ms" + << " | Avg per file: " << (files.empty() ? 0.0 : total_ms / files.size()) + << " ms" << std::endl; + print_report(results); return 0; } diff --git a/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.c b/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.c deleted file mode 100644 index aba63e0..0000000 --- a/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. - * This file is part of KISS FFT - https://github.com/mborgerding/kissfft - * - * SPDX-License-Identifier: BSD-3-Clause - * See COPYING file for more information. - */ - -#include -#include "_kiss_fft_guts.h" -/* The guts header contains all the multiplication and addition macros that are defined for - fixed or floating point complex numbers. It also delares the kf_ internal functions. - */ - -static void kf_bfly2( - kiss_fft_cpx * Fout, - const size_t fstride, - const kiss_fft_cfg st, - int m - ) -{ - kiss_fft_cpx * Fout2; - kiss_fft_cpx * tw1 = st->twiddles; - kiss_fft_cpx t; - Fout2 = Fout + m; - do{ - C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); - - C_MUL (t, *Fout2 , *tw1); - tw1 += fstride; - C_SUB( *Fout2 , *Fout , t ); - C_ADDTO( *Fout , t ); - ++Fout2; - ++Fout; - }while (--m); -} - -static void kf_bfly4( - kiss_fft_cpx * Fout, - const size_t fstride, - const kiss_fft_cfg st, - const size_t m - ) -{ - kiss_fft_cpx *tw1,*tw2,*tw3; - kiss_fft_cpx scratch[6]; - size_t k=m; - const size_t m2=2*m; - const size_t m3=3*m; - - - tw3 = tw2 = tw1 = st->twiddles; - - do { - C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); - - C_MUL(scratch[0],Fout[m] , *tw1 ); - C_MUL(scratch[1],Fout[m2] , *tw2 ); - C_MUL(scratch[2],Fout[m3] , *tw3 ); - - C_SUB( scratch[5] , *Fout, scratch[1] ); - C_ADDTO(*Fout, scratch[1]); - C_ADD( scratch[3] , scratch[0] , scratch[2] ); - C_SUB( scratch[4] , scratch[0] , scratch[2] ); - C_SUB( Fout[m2], *Fout, scratch[3] ); - tw1 += fstride; - tw2 += fstride*2; - tw3 += fstride*3; - C_ADDTO( *Fout , scratch[3] ); - - if(st->inverse) { - Fout[m].r = scratch[5].r - scratch[4].i; - Fout[m].i = scratch[5].i + scratch[4].r; - Fout[m3].r = scratch[5].r + scratch[4].i; - Fout[m3].i = scratch[5].i - scratch[4].r; - }else{ - Fout[m].r = scratch[5].r + scratch[4].i; - Fout[m].i = scratch[5].i - scratch[4].r; - Fout[m3].r = scratch[5].r - scratch[4].i; - Fout[m3].i = scratch[5].i + scratch[4].r; - } - ++Fout; - }while(--k); -} - -static void kf_bfly3( - kiss_fft_cpx * Fout, - const size_t fstride, - const kiss_fft_cfg st, - size_t m - ) -{ - size_t k=m; - const size_t m2 = 2*m; - kiss_fft_cpx *tw1,*tw2; - kiss_fft_cpx scratch[5]; - kiss_fft_cpx epi3; - epi3 = st->twiddles[fstride*m]; - - tw1=tw2=st->twiddles; - - do{ - C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); - - C_MUL(scratch[1],Fout[m] , *tw1); - C_MUL(scratch[2],Fout[m2] , *tw2); - - C_ADD(scratch[3],scratch[1],scratch[2]); - C_SUB(scratch[0],scratch[1],scratch[2]); - tw1 += fstride; - tw2 += fstride*2; - - Fout[m].r = Fout->r - HALF_OF(scratch[3].r); - Fout[m].i = Fout->i - HALF_OF(scratch[3].i); - - C_MULBYSCALAR( scratch[0] , epi3.i ); - - C_ADDTO(*Fout,scratch[3]); - - Fout[m2].r = Fout[m].r + scratch[0].i; - Fout[m2].i = Fout[m].i - scratch[0].r; - - Fout[m].r -= scratch[0].i; - Fout[m].i += scratch[0].r; - - ++Fout; - }while(--k); -} - -static void kf_bfly5( - kiss_fft_cpx * Fout, - const size_t fstride, - const kiss_fft_cfg st, - int m - ) -{ - kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; - int u; - kiss_fft_cpx scratch[13]; - kiss_fft_cpx * twiddles = st->twiddles; - kiss_fft_cpx *tw; - kiss_fft_cpx ya,yb; - ya = twiddles[fstride*m]; - yb = twiddles[fstride*2*m]; - - Fout0=Fout; - Fout1=Fout0+m; - Fout2=Fout0+2*m; - Fout3=Fout0+3*m; - Fout4=Fout0+4*m; - - tw=st->twiddles; - for ( u=0; ur += scratch[7].r + scratch[8].r; - Fout0->i += scratch[7].i + scratch[8].i; - - scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); - scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); - - scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); - scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); - - C_SUB(*Fout1,scratch[5],scratch[6]); - C_ADD(*Fout4,scratch[5],scratch[6]); - - scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); - scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); - scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); - scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); - - C_ADD(*Fout2,scratch[11],scratch[12]); - C_SUB(*Fout3,scratch[11],scratch[12]); - - ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; - } -} - -/* perform the butterfly for one stage of a mixed radix FFT */ -static void kf_bfly_generic( - kiss_fft_cpx * Fout, - const size_t fstride, - const kiss_fft_cfg st, - int m, - int p - ) -{ - int u,k,q1,q; - kiss_fft_cpx * twiddles = st->twiddles; - kiss_fft_cpx t; - int Norig = st->nfft; - - kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); - if (scratch == NULL){ - KISS_FFT_ERROR("Memory allocation failed."); - return; - } - - for ( u=0; u=Norig) twidx-=Norig; - C_MUL(t,scratch[q] , twiddles[twidx] ); - C_ADDTO( Fout[ k ] ,t); - } - k += m; - } - } - KISS_FFT_TMP_FREE(scratch); -} - -static -void kf_work( - kiss_fft_cpx * Fout, - const kiss_fft_cpx * f, - const size_t fstride, - int in_stride, - int * factors, - const kiss_fft_cfg st - ) -{ - kiss_fft_cpx * Fout_beg=Fout; - const int p=*factors++; /* the radix */ - const int m=*factors++; /* stage's fft length/p */ - const kiss_fft_cpx * Fout_end = Fout + p*m; - -#ifdef _OPENMP - // use openmp extensions at the - // top-level (not recursive) - if (fstride==1 && p<=5 && m!=1) - { - int k; - - // execute the p different work units in different threads -# pragma omp parallel for - for (k=0;k floor_sqrt) - p = n; /* no more factors, skip to end */ - } - n /= p; - *facbuf++ = p; - *facbuf++ = n; - } while (n > 1); -} - -/* - * - * User-callable function to allocate all necessary storage space for the fft. - * - * The return value is a contiguous block of memory, allocated with malloc. As such, - * It can be freed with free(), rather than a kiss_fft-specific function. - * */ -kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) -{ - KISS_FFT_ALIGN_CHECK(mem) - - kiss_fft_cfg st=NULL; - // check for overflow condition {memneeded > SIZE_MAX}. - if (nfft >= (SIZE_MAX - 2*sizeof(struct kiss_fft_state))/sizeof(kiss_fft_cpx)) - return NULL; - - size_t memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fft_state) - + sizeof(kiss_fft_cpx)*(nfft-1)); /* twiddle factors*/ - - if ( lenmem==NULL ) { - st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); - }else{ - if (mem != NULL && *lenmem >= memneeded) - st = (kiss_fft_cfg)mem; - *lenmem = memneeded; - } - if (st) { - int i; - st->nfft=nfft; - st->inverse = inverse_fft; - - for (i=0;iinverse) - phase *= -1; - kf_cexp(st->twiddles+i, phase ); - } - - kf_factor(nfft,st->factors); - } - return st; -} - - -void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) -{ - if (fin == fout) { - //NOTE: this is not really an in-place FFT algorithm. - //It just performs an out-of-place FFT into a temp buffer - if (fout == NULL){ - KISS_FFT_ERROR("fout buffer NULL."); - return; - } - - kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); - if (tmpbuf == NULL){ - KISS_FFT_ERROR("Memory allocation error."); - return; - } - - - - kf_work(tmpbuf,fin,1,in_stride, st->factors,st); - memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); - KISS_FFT_TMP_FREE(tmpbuf); - }else{ - kf_work( fout, fin, 1,in_stride, st->factors,st ); - } -} - -void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) -{ - kiss_fft_stride(cfg,fin,fout,1); -} - - -void kiss_fft_cleanup(void) -{ - // nothing needed any more -} - -int kiss_fft_next_fast_size(int n) -{ - while(1) { - int m=n; - while ( (m%2) == 0 ) m/=2; - while ( (m%3) == 0 ) m/=3; - while ( (m%5) == 0 ) m/=5; - if (m<=1) - break; /* n is completely factorable by twos, threes, and fives */ - n++; - } - return n; -} diff --git a/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.h b/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.h deleted file mode 100644 index 51b6306..0000000 --- a/src/drone-software/src/acoustic/third_party/kiss_fft/kiss_fft.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. - * This file is part of KISS FFT - https://github.com/mborgerding/kissfft - * - * SPDX-License-Identifier: BSD-3-Clause - * See COPYING file for more information. - */ - -#ifndef KISS_FFT_H -#define KISS_FFT_H - -#include -#include -#include -#include - -// Define KISS_FFT_SHARED macro to properly export symbols -#ifdef KISS_FFT_SHARED -# ifdef _WIN32 -# ifdef KISS_FFT_BUILD -# define KISS_FFT_API __declspec(dllexport) -# else -# define KISS_FFT_API __declspec(dllimport) -# endif -# else -# define KISS_FFT_API __attribute__ ((visibility ("default"))) -# endif -#else -# define KISS_FFT_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - ATTENTION! - If you would like a : - -- a utility that will handle the caching of fft objects - -- real-only (no imaginary time component ) FFT - -- a multi-dimensional FFT - -- a command-line utility to perform ffts - -- a command-line utility to perform fast-convolution filtering - - Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c - in the tools/ directory. -*/ - -/* User may override KISS_FFT_MALLOC and/or KISS_FFT_FREE. */ -#ifdef USE_SIMD -#ifdef HAVE_LASX -# include -# define kiss_fft_scalar __m256 -# ifndef KISS_FFT_MALLOC -# define KISS_FFT_MALLOC(nbytes) aligned_alloc(32, KISS_FFT_ALIGN_SIZE_UP(nbytes)) -# define KISS_FFT_ALIGN_CHECK(ptr) -# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 31UL) & ~0x1FUL) -# endif -# ifndef KISS_FFT_FREE -# define KISS_FFT_FREE free -# endif -#elif defined(HAVE_LSX) -# include -# define kiss_fft_scalar __m128 -# ifndef KISS_FFT_MALLOC -# define KISS_FFT_MALLOC(nbytes) aligned_alloc(16, KISS_FFT_ALIGN_SIZE_UP(nbytes)) -# define KISS_FFT_ALIGN_CHECK(ptr) -# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL) -# endif -# ifndef KISS_FFT_FREE -# define KISS_FFT_FREE free -# endif -#else -# include -# define kiss_fft_scalar __m128 -# ifndef KISS_FFT_MALLOC -# define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) -# define KISS_FFT_ALIGN_CHECK(ptr) -# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL) -# endif -# ifndef KISS_FFT_FREE -# define KISS_FFT_FREE _mm_free -# endif -#endif -#else -# define KISS_FFT_ALIGN_CHECK(ptr) -# define KISS_FFT_ALIGN_SIZE_UP(size) (size) -# ifndef KISS_FFT_MALLOC -# define KISS_FFT_MALLOC malloc -# endif -# ifndef KISS_FFT_FREE -# define KISS_FFT_FREE free -# endif -#endif - - -#ifdef FIXED_POINT -#include -# if (FIXED_POINT == 32) -# define kiss_fft_scalar int32_t -# else -# define kiss_fft_scalar int16_t -# endif -#else -# ifndef kiss_fft_scalar -/* default is float */ -# define kiss_fft_scalar float -# endif -#endif - -typedef struct { - kiss_fft_scalar r; - kiss_fft_scalar i; -}kiss_fft_cpx; - -typedef struct kiss_fft_state* kiss_fft_cfg; - -/* - * kiss_fft_alloc - * - * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. - * - * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); - * - * The return value from fft_alloc is a cfg buffer used internally - * by the fft routine or NULL. - * - * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. - * The returned value should be free()d when done to avoid memory leaks. - * - * The state can be placed in a user supplied buffer 'mem': - * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, - * then the function places the cfg in mem and the size used in *lenmem - * and returns mem. - * - * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), - * then the function returns NULL and places the minimum cfg - * buffer size in *lenmem. - * */ - -kiss_fft_cfg KISS_FFT_API kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); - -/* - * kiss_fft(cfg,in_out_buf) - * - * Perform an FFT on a complex input buffer. - * for a forward FFT, - * fin should be f[0] , f[1] , ... ,f[nfft-1] - * fout will be F[0] , F[1] , ... ,F[nfft-1] - * Note that each element is complex and can be accessed like - f[k].r and f[k].i - * */ -void KISS_FFT_API kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); - -/* - A more generic version of the above function. It reads its input from every Nth sample. - * */ -void KISS_FFT_API kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); - -/* If kiss_fft_alloc allocated a buffer, it is one contiguous - buffer and can be simply free()d when no longer needed*/ -#define kiss_fft_free KISS_FFT_FREE - -/* - Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up - your compiler output to call this before you exit. -*/ -void KISS_FFT_API kiss_fft_cleanup(void); - - -/* - * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) - */ -int KISS_FFT_API kiss_fft_next_fast_size(int n); - -/* for real ffts, we need an even size */ -#define kiss_fftr_next_fast_size_real(n) \ - (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) - -#ifdef __cplusplus -} -#endif - -#endif