From 1595c2728e4652d75f1ffc512cd0e227bbb68f74 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 10 Dec 2014 08:44:25 +0000 Subject: [PATCH 01/52] re initial, no working fft, faders, checkboxes --- plugins/CMakeLists.txt | 1 + plugins/eq/CMakeLists.txt | 3 + plugins/eq/artwork.png | Bin 0 -> 50584 bytes plugins/eq/eqcontrols.cpp | 201 ++++++++++++++++++++ plugins/eq/eqcontrols.h | 127 +++++++++++++ plugins/eq/eqcontrolsdialog.cpp | 164 ++++++++++++++++ plugins/eq/eqcontrolsdialog.h | 94 +++++++++ plugins/eq/eqeffect.cpp | 314 ++++++++++++++++++++++++++++++ plugins/eq/eqeffect.h | 80 ++++++++ plugins/eq/eqfader.cpp | 0 plugins/eq/eqfader.h | 111 +++++++++++ plugins/eq/eqfilter.cpp | 5 + plugins/eq/eqfilter.h | 315 +++++++++++++++++++++++++++++++ plugins/eq/eqhighshelffileter.h | 1 + plugins/eq/eqhp12.h | 0 plugins/eq/eqlowshelffilter.h | 3 + plugins/eq/eqlp12.h | 0 plugins/eq/eqparameterwidget.cpp | 197 +++++++++++++++++++ plugins/eq/eqparameterwidget.h | 158 ++++++++++++++++ plugins/eq/eqpeekfilter.cpp | 3 + plugins/eq/eqpeekfilter.h | 3 + plugins/eq/eqspectrumview.h | 80 ++++++++ plugins/eq/logo.png | Bin 0 -> 3225 bytes 23 files changed, 1860 insertions(+) create mode 100644 plugins/eq/CMakeLists.txt create mode 100644 plugins/eq/artwork.png create mode 100644 plugins/eq/eqcontrols.cpp create mode 100644 plugins/eq/eqcontrols.h create mode 100644 plugins/eq/eqcontrolsdialog.cpp create mode 100644 plugins/eq/eqcontrolsdialog.h create mode 100644 plugins/eq/eqeffect.cpp create mode 100644 plugins/eq/eqeffect.h create mode 100644 plugins/eq/eqfader.cpp create mode 100644 plugins/eq/eqfader.h create mode 100644 plugins/eq/eqfilter.cpp create mode 100644 plugins/eq/eqfilter.h create mode 100644 plugins/eq/eqhighshelffileter.h create mode 100644 plugins/eq/eqhp12.h create mode 100644 plugins/eq/eqlowshelffilter.h create mode 100644 plugins/eq/eqlp12.h create mode 100644 plugins/eq/eqparameterwidget.cpp create mode 100644 plugins/eq/eqparameterwidget.h create mode 100644 plugins/eq/eqpeekfilter.cpp create mode 100644 plugins/eq/eqpeekfilter.h create mode 100644 plugins/eq/eqspectrumview.h create mode 100644 plugins/eq/logo.png diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4df760ecd..e08e37079 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -9,6 +9,7 @@ ADD_SUBDIRECTORY(carlarack) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) +ADD_SUBDIRECTORY(eq) ADD_SUBDIRECTORY(flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt new file mode 100644 index 000000000..72dc8bb5e --- /dev/null +++ b/plugins/eq/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4a0418b87ed04c2670276d2cd4b4e5c506439a GIT binary patch literal 50584 zcmXtf2RPf`7q?NWMb)T^Rjs0mS`DR%YVD#%QIS$Bg4nAfwc20pQ7cAkj~aetxc>VQmbD6@B2P^zE7U#KFPiJobx%KbI!fz8?OICgZV1=RVpefW-U!sLnY#(;z>ot$bRuqQ>A_3Iv>33rKPKO znaFg7l}X-s-$d$s2;ik=>ZRi9>QP%*$ZfB>FU1@b3xI}>}-0|tA8!;@TaGzJqQx%NaGeNFfj0)`7M&at0*0RWWat+5B^!; z#CC#8iwo{m;N>HX{ptc5COrqoHH-K4kVq{P6H8|#{G&v=042rC2isE%O?zV#3+93i zTkXGp5k*wa|Ug-g1-tjwA#tP->L$T4%9J|cV1ns1ty)DUyG$0?5#35|GSGi-qQp@!W{j> zjUN@&Z*4RcAHK6Ia|C#9-kFehMp#a$tMwg|Nu)Cu$5I^9gs%MNJBY^2y`PO0e8&UN+oZYbyQEBk-b`ZWtTgKAi311o6|(o5R(?+j}t%lp1GH-g7H(w zeSXV;0wydYU!NT{xEGEr4ax||*A8~9{>uSRU;S#D_)SjfQ{5of6s~E{J{DTmXHx{w z-~O1q`x?F<^zdT{Ck43rbo+7O?$?0gck{0PIqwdtG9|W~_Fe#NqNFpn{?@u4*Y5hy zQT&%0TaFvEZ~l!}k8Yc$9Rtwbl8)0cH2v{UQwq#`O+&vXC7pllF*|1suTo$6)HEPL ze0)YG+eHWo2Y+ojY=81PU_bC^xPmc8x>FXAd9o9Z++%*{pUxq#GuiVQeyE!Ow;5&Ij1XG z<#$9)L~V>ZJHwu63D~OTPNF!|L&-{FKKSLUzLYmgVr3)h&4KDh&qbGh>{hO_L=x7h z7c<&|#5?1tXb4g1(tsfV%5_XM`%0S?xlUvrPxd*<< zEx5ZoQu(@z%O*!kX&0WrROvzl4Un35zdKD;k1E zIYY^}l!q|(+KsSeCl_LL4?n{TZ|HiaE?;KTgf33G$Lnf|X52pLI3+(fK3Ugz< zH$l@N$1SeX&Nkv8eaZVn4ge-%5ixjB$S}rb=eu<71)Vj~Ctq{8?SpgQmiD6rRX+L) zYsx*&L1W>r)OGK`y|R|OH||LOjBiLft7T&*uT&dZ_ajQwSo)zbQ=D4Yda(xozjN9C zcsxBNH18357fiYaXhr!1H70_URn&JM2!uZAAB^G^Wjw+oBA^XV$5)%XQEoKM)`}x_Gl+dpY263oDtbJMITx&r)|OA z_J_#2iMPZ_GnM8)OlbvZd|l_y^~Gf~Q!F2#&5x>%MYaBv?Mp|xMux`7909WsxzqsR z{u&LeW$v`64}bRL>97i1g4*tx0E=Rpa!tlTSo*neRO5(ua3jxVV-$Wm*e_2OHwJDG z`a^)YQObi(2N|?errpLI#@f5kU#B zqBz*=f$tZw&FVLRf;#)`KrWrno@L&Yrn-O-SKWH*Tra<|nD|SCsxQ&cYuihg!hQ8q z7GSLe8k2RC`)P#Vs&C)W&X2%qfon2;Tvsjr9gFvY9ix-SGsZ<;<(fxcf_Gt+Cpr`U5J>$nSPzqx$$Qq5zRZEHkVl0L%IC*N6PX zsYtU>t+x_R3`t9acGY#ZMY37jLVEu*F^KL%uKbRgwM4gDk$2)J)slZPn238vuQ}cx zSg-6Jc}di-i00k}dc}HUnm*Rpk4tAI%**&~S=>oaUQ4E?Z2ZT2p_gwDWy@~_35wi5 z%Fr2*-{rub5{?7yTw|D6N>GroP=EjJ(^Fhg(cmHzM%M{r6IW4@Q5LgJTF8HUtEN6W zo)tEGtTmNp5;huSp@Pf}SU34@KBi~R!&5??!yNZNC@|e~p)SYV*8l0kK~4Qr`wR6W z7B^G34XZJ!byvb$S(&;KYGf$p|WH})zfQ2E&gFOG@lMnRXr`Zip8s6SH zPrsJvfdZ&3%v_a{0OQ!@taF(5@%!nm|Axs@Jpd~s%2Yp>9~6pH?Hm}VZY2EyLEQSd zE*qRGs@#B5v_y8-@b!(2AKabRW>FK7gQ-O-8ry%kxRi{Wb~+>Sb}yuw*o%^_EtV%=?EmPXE1_SFxz^#>3F4 z=xgto7urCA+k1cos^L8s!!LB*5Pdt*s~n+mgd8$o$!}>Y|0rpTgB?(fcu@Jghy_+| zS07rHs*Ma|MtM&f$kh-4l=)$gTZ1z^i>nT;<~XO4{37Ez32AETQ$JmxuVqJG)#;F| z`c3P*X6+nSXp~XrR&WDMcP-Sx;c(r@e^-P5$;A@%SrasmXujBw)~!0YzI25#dF5i5 z{F|Hg(C!Xj@8=N7{G}fanog+olKtsCB`V)ix;}qK+5=yE2fxqy8uTM(rFZ;UdnwUn?d0)Y#W-N0;^#u>t zkxQ#b`UQ$PI0?Zk!)FGE+@rS#tT}Q%RsF~hGZ@<5^AM>V`6kN3XAWXNv!hP0um+nE z%|I78>;sI{7f#WBvW!@fhOM_qSoVwwk#qw$>z%2qZg$x@SgOhkDr;ojd%8|0O?X*z z(||j<%AYZ9cqVO+_ZBMFzu>=_R3)86LpswoNoyN=m_?z{GJ4Wgy`nNUdA?!kkjq~- znmMi%WNq)>o5Q3qB(UOWQujxymf%NKlZ%h~zU^=$ds?8<9+zA=Y1^c(9?8m@37g1P zM0H1XSxDXHEEjB*Y#8NMtdAMAlX&qWvRk#UW)TlaR~D|W8IU59@WPiv;r zd*kJ8bxi4Fy_Hg*zn@0>6>x+WX7nc`(b{8A2NF_iK;Ei*{|h_pV<=xTWSUqUXQ!OE zbdsOIF4PY3^1s9c?DdSxg&T~S5O6Wsxj3*Rb|6Iay2)BzBljOZl51h-6U7N}vQibJ zddctZMQX7H(E5l;qub@6i)q14yW#?Z0xr~=)e{in*?s#Dt*W$lt?8OnADE0?LQ*_7 zd>Rub-4^rv@ESc{zVwrj~pAWm1OB_x3i#af%OcSN@)mB%CH&U$3Vs8 zLi2xQg6qkD%_N`XauP=J+MhpCx$3BSwMM-eD7fZ`b8VSRemmzc-``tDWv=+2lMJ6A z@x8JsUSw=y#`?<8A05QbJkY^30RBc`u=+O{!iFI-GfuJvzV=q-h583o*@RxQ-8!5zc<}V%0PPmTb)eX zsm;jr9CM6C@(nW;N=MRrll!%%L5e1D@( z?#pO$Ny^gx*@P7D~Ph%Ad13oHLVC+-32dNP2zdnbA2-TqG{0+f#aCVyo9IVr}X?+03z*-U@lWNh^=%`34uW@8ae zkwHTz8>VmExvFn&^@?^IUG?3#9N$}W4_Owk%pxB`%vs8ycC&ScKS5lm0%A!ttPRO~ zgQ$#__3o7jiHOQ2rPO;%5}Z6X=_bRl*G40L}v=v0Nj15l9UKyrn^t54xe5U?W ztUcyp^dTNXINl<*DNHG{R>}A9ch*H)@Af@at{V&DQYNHr^PA>!78ZIJ%U%B;p>la- z)+ZWdiTQ!3V&dnyOj>hFa7`klwEc`?M4MpN?(=R?R)4K|QrGiTC`TaWs2t^N2?m(V z4m@a3C+z&$;*&9Ks(+r09gnEZ;_yFoojoWG0qit1MW2IM5?);zxCpNC{R^S}o?DC} z7Z0&vDxuu>y-PN^ZX$68A8vHIl`GGp3R0UG${r3qcZqz+V5%HzYX=dX22kf~ICq;A zSXh7Vr>So8yfoNXnTX8LRj%{PW>G%3Su))`k)B9Xim4kQWaWxGpDLL2PwdV+e)h2! z{4r`;SJ|9;P^crwE$7fLn`X;nY;NO-j0qh%{hy0g6_Xfx8)qqmIW*@q4a5&S$*MK8b54p7m+x)|0CX*1~Q;iJ@ivm zD#eevU(5{}fiR=jgMwJ^p{f;L>cz-B#`*+Y=%bJBuQuOXQIp^v7yRpz%l%Qkyo}{F z^w$}QSUyti7ZwSk65~JgA0IZAxoygI{d{@sf^yoOUB{afw_|PiAO0*1xNwkEWn%8F zBP^4caKslb`v01~$tCm~Gx}sa^wqYv5xX%=z>mq)u?j$+``m-XQ|~8m9bH{D0gH?{ zOrR4u<|f!xQL6-0+}pXANQaB*GO~!!*UKX-1736p*d6iOMC0km)jv_1di!ZhToyDb z&=MiPAHT49&Gs%|ggUSmRpX<)kdTp=8qyTw1{SU9+keswdwbk*t|u2}c?X(C5A4Su zhlwx2DrX?Qj}7l6%(-5f&)NlgWyY~jT=meo<%ZXW%brdBMGd?*^OUI^XNhyu+yAmu zmEsM5$Z?qxe@ox+v$6G^=*y@4&e^_cy%(xG&z#*SJeWXMsV43>@hY+Fjbx>b&3cbB zf|IkhxWY?9=RarC-^XdM#5XgBWp+|N!yx>|!~H!_ z`|)P*zDu20<7|wgMoD;d6TXVBl1U#iL|_!}b*$q!k3$`~zb*>ezj81sf> zltbtqg*ev!!y?9j0)o7sh)9me3I0Im60_K0`|uWuZ$ZNy@%w6tE4thF;8Q<3zoh z>_v}p^6VtpQUxCd8nxLXCkh)xBh>1s9IIR4tB0#6ek)=+qechE{>alK?|Zj4dW!1$ zOURU{PsF`m*kLLsPJ_?k?)ZaPb7p!z#xVY!o&GMag(YTTD-r>`kA4(j0*RiI#I;C! zR9HYq%*vWNkv7xgE?{|ydb3!ciE#@cMTOh8YNDCB!-vZ%)%dC&58W<~tzSqb>oZQ3 z5#_sf5qY1^8dlmqStuGN1A_`E24w~e(@o&psO24+@-(wZpY|{@Wz2Qy!)H~-J z^LT2${1^1G5?G=Jzf_%ZSDZ%c6e!W5Zy#aiacGx{~7)DL`VjT;H zL^D+R7aLpG7UO!@t@{Y3>A$05cBr<_ch~N5$2^NAKX2tM;kj#QpL z!Z4L<_zsOGY~5~sX(J`oc^e^Kt~ED(T~Q1=3fVySXVR^P`M@1eW!Gj&aY|;z=VxPm zd{K+`19p5#Ow#=}n-sr{lnHi_AWvl88!)FJNeCrCLY@&@5X$M&At}f#>7hQJgzEZN z*2}Xz_~VIfHTBL4g-Dn&l3!tWV}dd}H`jPN1XiXvj-~!Ry>Rp<+%7*R{goZcL5h+% zRxNK2GtkI-Krvl3?^_ou6J;&!xN5CxaIXl~TAAIR&Vb45Zx`}1@ z%KjN_X})mKa3vh3>6_KOw5Qw|M~6X!Ttco`I{wXdLBuV$Y8if6xlzTh=-7WpB>!!j zMBqi9l0h-0-ZVkk$XfDKH*7Tv1t*aQyfJPW*l3g-Q`csn!3o(OVsehzbSv8JfAL==&EYIiR=3Q8$x+H5rqBuTP@?8 zPAgaIg)iKz{zz`&!eBs@ca;`RnNSE|nnJA1!oGaopomats60|uR6ogxK>ywwYoCcF|*mH!PSrbj=4^ltB-@si4_-1%wGuI$fX-xnp8_v5AWG_O*u&1Wzx(Q^JQ&3>tG9+d= z|2054mWrKvl@Jnja}SN7q48S$Ba6Rxzaw%>_Hpc;Wx!GjY}rDWHls?>%gY+W2E8kF zQB_5`OW^;;uM9uCRrm04a+hE2xPkK4)>evd+KdWcyZ~RHT(Z)TDpm?0+Es6<0jra_MBr>%S~J?i(;AXRPeM&D5g{!zX*2_QurZY z+tj~$Bsg?_B~$Py>PF?KAW@;d)axPtoH)y|Ew`mVpVyLu9#_jsu0r!X93 zBUaQydRE}zr1$3aKWDnzmuA2qZxSfS)?r&wVkdGiK?1s0IRU0lzNbeWtjI&O%zr~h zBIn%ii02}DmUx;Jy&xiM-Wog?23-YK4;f>Fj7!=LXg{I3l8w!l#|M7U2ATcROzSQ&+0~Kf#ys=e`$` z{cuhQZRu;ct<4_r_DkU^CHBj)y_?rlG`5F-GlqD)IUBU!bj@BW<#XWd$NvYjO}Md` z4kb1i*M?w*EEu`8B(BL>o+o#mL&v>B69Jd9Fz}D7mWlm0kZnv-c1+hD9-1km9!``d>p1;Vio=|(Sv_h2UB0TCETFq^qUeud) zbgL8l-3X9OBGnCKtfrJ5!V2I&n2{1icVaP>X53s*^cOSFO`}#I#$!VzeMlWA>j-#J z$TZTM@M#;&j{YMarStBpfm$5UdHAvpXs+1*f~+hEtH#(T`;|b^WW!OCJo52Soo1VY*UwS&>E@r-7<)8Czl= zUT3HtcCXZ7Df;1H?iC3uZ=D$lhTHt(_Qa?vA|DV?O@|-v(}A%5oyv?qzsd-)qdOd6 zoM!Z`ekzMC*lmpjCLH`64!-}WnqiOi_bEDtoX2&muT3qZ;|Wcum84Y2wTuZLBUuLS zL@Y@y%7tR>y+D$nOv(TD(iH_QL|kW|9+WVN%bie-`NEPrUD*N?8UNUtk@ST zcDWbfS@`jk;H?|$kMVwe_Az6&YbZQey9J-o02K}x?Ch^1Z>=_ly!{^=Q;{`8fA+oD zy4}|6Lx?}l9lWbiVapswO{U8N7S_0rz(1+y;LtTYX7*yJ<~Qd0rh7x;-$| z7^S3OsHh_Fm4cFZUL}L=R#E30#dL~KGsZM+a;pfCqH=6x2CN=Nvij4fz1jG(uk3H( zz-ts*8qBThrcG->)9_CB!{gD~@%wQ+TJGmoR`1)1PZp28;hcv_PFLu>pHLNa&0E5T zs+GhoV)L6nOJm71ZpI;`xRkZBr@PKBsw3@$LAYYTPs!=TzZUen`}@=H4?Lb>+<7*$ z%eE6ZSbMD}Lg;cr|8MOJ5ui9u=W;2&Z0RpX6Z0nbP|hMD+6L#+9B#!$CHop=8rH-+pe!WQxkp2=o`f4_>EF0eogv25F+TVE)Q0DY)U{I#|S6LFSbbFd4Wt9YzG=EF)TPNHfJf4LW$s+;6e+utYR+e?3k*Ydidb zO4Ysr#!#LBjZI*kpJPsG}a7;&^b&w2_#jeG}s@yru&y|e^8sy{whV|+(Yjh za*Ht~s`%D!miOA*F&Puaaob*=+e`c~*t)I4C88gjqw7y?dzI8JG_2Pr@8XiRvk=I1 zCyUZ5zhEw%x{ZG2q@btiJ(v&&OT(y!D34hR6_=6C0(%m&!cg%*}VqmUf-6lm{8MToy~8sAk~O>8IBg{8U-QaXE_<9Q9?P>Q-;!vzEh|v zwikb-sMp${U2uo3e%!z{6Z8SYDr-KUt~!Yk8(Pf%8zcrus_MZH_4xeHq*T8%c)lhh zw~(R0a5vTk0Mgmu{VUZ})KbeT${>Ml7NcsH+_zUX-;R0y8ow(mH`Q2>>2G^UhCfjE zTPL??UmnG5rKgfE(XFmIkUWMBkM#N_>rrtrH5Hk`Rzi~JG;lNI=e)Np%>xFeVI@G) zdxu!?eb0oJe8VsQpqxXpW?Sgrmi|u|$7-TjMu$sbFH(XYRLziWqp3zoMpa7sVg!kW zCyK=2OR${G|4U}{stD2$*>2Oh8CyHt%D$r2>4!!wh^6d13mvj)z|S1LEioyb}K z={?~r4Z{-7^5+P=w&Na#@pQ+F`Ay#D1wBR$J6^AL8pH}fX_o=6$#mcE{o~hazr*rM z(Oqn49Z~IAm5YzffL>bF$#}{Cb8{Zs>Uk6I-#o9FT` zEJaU)+u3ZaKqaet7~Q&p$YgRriPm#fTbS{pXU(BvWriuhKjmXJ}Ywv8r zeN$+7%N^LXD|`An^*N?{XV_Ya^fqf(#kr}w1+VEl$F>~X1kZa2NA9wEmkK!yTXW%t z&x`XQWMnA6kfho}Z^@}aCeK#CFR6JCRRgQ=!J7;PDiz!>o1FfH<&*PVs_-I=Gk`1P z!p%~csepoI57U1mWl{DtIk&F=LV2%pX662E)S15ssGX};clX0$>#o&8A)ReK>rr*Y z$3Hl`^B&FyMFK59X_WG5(Z8!wza!&wa14ft%S2s1<|UvPHIy|7|A}zh@YxJ=Scm}e zDs}f5I6N=dFXSzey*oGNmEICjN_ktsJ~hfUaS3ZPwIK`pUZ*+aRHC{TrqF{PRHHYm zRYQozo(|!-6j3&&4f*GdCmsL0+M`|;50lkdM0cvPl&fbYu>Om<>xh_+QGf1!@AdU1 zM&EpR)HM(rizD)Zfe`i2A0i0L^TQnonwc#vRyb)qzw5sF-h*dd&(%*4&n2u#g7YJ5 zQakTJeFJO6sY{q+KxEBfS;e=wIl=4r#HQQseEtu@+F-nc==wz<{%IERj$eXRlgYhyQ2eC?$Jh$ zV-5qfK1QS%keB*_w|0B+*NUDWBU6=ikPpR572_W?5@r(qtD%D4XNUOX|93QVGg4t^ zy8NP6k({t8o2AXsnP<9wKo!Lg+y+W}ZO;KGZu;NMWqKvZ!!z)a`$M7ki<&_mb*dGH zRbsLOQiW77)+vy)z_s@3%6V0PLDzd37D{40)9na~V-Z+#78O{_B=+28@ao5GQbHEi z!J2f^q`JYqEL?nb)u88B#f`#O@0e{CupbmWE1kd@`^{B;8y4)rPb<#3pX!fa|%M;oU=GOswv%SjI_DOL||Euvfvt7+gIg#r?exdd2H<8OVL@62}mNAfE zpA8!b4;90hn?0riH%3{qh>6>R>!x;daxeFf6`(d>*1Z2BbSL+7WA_9hki{zXTx+?Z z$%9mClAkQSrUheX5YTk#*Oj>0)EL_utfhn=e{jx(bF{x5#PKumYFx1WZ}@5g06EOzbcPQ zG>{q|rTm(3nOUr-)pWg|xK?CbiWSoA`PyPVzA`E*p9sb3n|Ee`_las}7uMJ#mqQQ2 zG_`FQuj8rg?v6)*fTXnRd-{xW!>7aG$r=%7q26)ZZ&1TZhW(rr)TS)q26P%gaPGkF z^=8Dv`upPv<@WAnLNf$+C+(|4h!Vx~-7!g(k^3Q%f7vYgMPrZ|C~W9f2OQCU&E)Y& z+Om|vvo9-`Ds`P|cL#GR)m%5>K`zv`wo^!xY>xa2#~y>1oU>kPvh?WksFSEN-(#`f z$J@=aJ`Lw7Z^459HtNmI^PQdufykV{EXl(FOIL$Vm2BEIFiG6aKSQ&`JTe^uSRx+! z!mI!|zXN_L=a(q|WZUzu!OHWSZAN;7Cy1L|jERCBb;rlYRW`XVoNpZsh4y|`IxVUo zo!<}0^4&V@){vw2G}7|ax(>g$ok1fHw6N3VF)d+fG$vwHDp=1e$g-L&0Bs>|ei{Ae zyBC!7hxZi|=sD4m@yhT_s-lONcut?bkN@(sv-H>iuMOYxoHVM7<)oFxi5?@NrG*tp z8Sqkd<(=Lu zWsRwcD3Kf9!YY}gw5ClVLIf1>Q?8AHUHuCe6@Gi>zTe12%#Jp?CcKv&VgHBw;Jczo z;@{Qr1?^nR7?(tPM9J*^-JvPDV!x~c3AXP1r{w{iZ;nd>vF{e-0}4-m=$<MrIzr zMy{l@zC0(_ub}4BpDnm|XOzIe!^tis`t+FT?4l`d%k7F`*`N-)fZQK4voT({0AVX8 zB<|j!&RL_7?QbEADrU&_djy#W=kbvD*@l`)dMx#cEXyp79-mv?y&$wtLI3eS(t&E? z%;L%3+{s>G^U25UlfeYfC~L|tF6>AdaBvi0Iuy?<5XyM(2vsQz<4MgdUvh(#?0csuW#VblgT6tH6!&m ztv^K!#NSv%)+A@8G|$-BmOi!PUwvMkx@O4keysDQ322v%)n%L{U$SP7!2Eim2-l{* zceIfKV^5xbjqW6^6=l!NAMVR235G;cD?paoxVw@!2GWO>0`JpmCCPXl3#|yyE=R3i zrFE-0W?k@C?bYsms~y~Z=|71(Ff3@4XT zhk@l){H8tyGT9@q6?WJQXFF@trvCB6BJ*x2vxK&5*(B@Opv9r$__yRltV!8Xvxev0 z_WNc!mq*|H!d&yupXB2HMCWQEs1RX*Idk=%f2zAVMJRPT5n2dWqx@$@?`XDW&fTwl z$CE;E^hJG8Xg`rMYU~QA$ZDojwj;&4wvCor=}qoL6&REuu?%C^M~6my3(&?@X+NZT zT};?+@$9vHDcGHax@^t>aSE<}eR zx*orQR4!0L)Jn%gKUbZM%Ujfb_#d@VUBp^Z-TcI*sh1>xrJbzJ@bZkKfRnR;`tvwF z)-i+60}_{%{S=r|bzJab8X;7>7+K4$@%L5P4aTo^ZuNON{M_^@r{z&pSFAWXfndGG zsxtEjs#Xxvx}y(qYoUV?ji!)4W^0#&k%a5zL63WP)e?>f_D|$WPSK!)qQqT$4W;G! z6-a#Yr30q|>0QgxwvBTBqnDTWG!JBDs>|Y*2B{iKPu7eTmjM|>IJ_~x0?DKUxJC0C zj~GOMbFduPSFPL9X3pvh{H*1^t83WWQNlct(Dl48(Dgm}c2x@wF?v_av9l4hPBC*R6V6t2^sC z-aqwn<6O<&t3{@&4ul0GHabhqzbU0d!B4)I!>7?b#z zrk6Z0tfmz^yVq-Q=7r~e7U5;kRnx7qjk^s=D6HiuRjZ!2{`_+uIxp$$Titjwwl9Dv zM5-3FM8@E+J6CchctqEiQcM9a3Gbl|FgQucNGu|L&7qXp3QH>~Et;@2D9o&mGE3xI zkv6rzhD30|Dxoo>tV46CZSaIj9f!Lk=G3W@M{u%@m>BkKP3soHcg2F{YDASrtJ@XD zv$mJ9HnI35GdQ5Iu=KM*P{i_t(AoiXe+-c!^m!>Gn(mFqwL6?wD%1h4)xx5s!u^im z55@vofoG28s`S*_^%^e>3f?F*a0;s^9bK~vc+lsO!SVH@l&v+*G-it9SVEq<_6m*T zt;vRN$AtNm%V^wkA9DS*$&M%8-t<5nJ!nc7N12Xj1sU%9-Nz@!%{KLN(;k0(8v|r| zrfc{`o&~xcYl0t36Xee-%K+1+F_C)o=Opf@n1u81{WRN?Wec^k_KNyqvska{f#Tl3 z(~w(G=E`!PHHo6euQ-x$0BRxypQLft89BS@u`VLyMNA$5ur>YDjn(h~Q9DZ#!Y2QX zD{M=hM9j?@erJUk0;|weXcOM9>*pY0CZN$&*RQ)`T!?q0K*6-ynSo7VQX z@XiFo#7=+awnJ|=e)X{Jh`?OdXK&v!sO%(U3+ijIuMt28`80nsD!VL5(RKFqZ`hSH zJu$r0iqjzMA%cwiwqF(ga0WtUPu|ES-;#!E_L<8kRHO~~QX$rg&pSyw*jA74o;Ode z_jx{?NJA9_fZ3{24W1<@Ima|bbuU;pd1+UOIHni#)S9|iq!IkYaSq`93=;~$#NV&t zMaNAK$t7X(6e{!OLk-Mz6I-qc`}&E}vNSSlxYKL)6U16TEz`Ei_bZU83GI(9AgPIv zSkBO=W?P)C=>Tb%D9j*GOdE2Iakd7dGn%@)bX3oAYL1q<@uZr2JX=GYmBy1ax93vl zsohd7Zmyeq7TAZ!(AG82W_@KMQ$%w+vD)A=->D%N_zsT>MqHHLj#p zX^iGwTiokg=I+ARtw^n z%o!GMv9+|OulG(=x`uLVRUnlAty$>>e=eSS79=`vg*+M- zzAtJp#yNCV&o{&GQfYtQP7}s{!McIIEec;b^ciY$J=8cTA_&?N;^>Y-Qyiq~()7zC zLM@)`Gkn2Oq;=7bO>Y6hk&`0KG6pSSVfBUM?#fq+Fub0NH53}}$Wk+)e>b#Zudf$cET1&hoB~k)Ml(D!jSY`_Dw9fIdFFF zRxJ3?-}(_K;0^1qCh>e;{_6p3bLKx*o@6Y3J`22bL^~BF)E;GuZ?|cJtB*^>7w(_G zM_^N-JVm3)*p0Oz|5s-{JH;6o=2-pEyDGIUjvrOl+|Sy9&b|?@?{~8d#JG&1BvO;n zg#6*xsy%MgzL@NIoOkFm>S2EjvsKM1UiR=RFyn1VXB1&psOax>B{UWW<5-k&eyPXw zvz-RFe|bkdUp&+^?J$T3{pFPRn(Kk!FRe`f&fhZ%kh{nC0`y`f_4+(^!6FaaMjoK} z1{CPW@Rc* z-J1wkZj}q}yRAkBk9z=BJyAN>a>T0M^Pp*L7Jru^)DRlJJQ}sl{HNxn>Gy0uQJ+X; zx?bv!v+^%8)a~;ey!!(yWQ@5~5|AmRAa-Nz(;o%hSWkoZB|Dh&GPiHm5Z!LHWg_rk zNxa}hcVdox%@MG*O8(l* zh8SRxjT`B|N;&4p;{EDh!wTAIvMR0w3wX)b=pf;BhfSoN&ddMWt$o!9E$0KbHe1dc4pfzh7CZWvD9WS1kTg$SgGOw~pgBe{JoHSTs4#hFU)Y zNY3_({h>-%F!@Kw`1AW5&a*^GFfFdNW}4?`{)VGa2zwdG{`U>B9rjI1)eFBpnVZ-n zvl5G@r2-j1YTVbLCeJd~0h4^48kWSK5cE7FtMPWtj7?RWcf5mo?1>q(L=ysj38?=e zrw~^p^ts=@O*yq6nShR0llfg_cWb;-)^rh+R`8o|#azSAxJNX+o-2O+?+ zL0bmvOdZ>6PgKQUCdO?I7jDdq%@sAitC*KFKp&e>O{HliJFoVFlH}j{zpchOPUZHLO3wI(ZG2|6*)23ug&>ZEaX4vvQCVp==f&K{y-)RD|E_`&@5@!C1=yVv9Bh#x@BfU$E#` z!q?cfX6+nqJ%3o3n6O%ZdD*G214?_yJzlY~A|;K$4_U&ra|f;tJVxtUdt9kClZ9dw zT=~pj(x9z4B(50u3Dzi&kwmj8ToO}`qDcN&=-_ttlJ|KFoDbB7kC!0zQBOShCdooD=hMq@ie_A5g|XC7UhAk8HQ|SJhgHz#(}+KMTf=d7u-=Mw`JD|s^3m` zhuob9?!#nAWoy|Qlx`c&gX2@JE4oHk0pMGm*G*gMI=@vnCu~k-Y<3C6#(I$;!t*0k zsZ$`qvURoY}N-KeSsdgB5Fv!auG7 zKtPM*8o5q}F$uErzJfjNz`;v6`>)CCpI$wiq**^Y&Jm~dE4e#JY3y%ER6PfDYRT#X z@*klOls1h_dVdEUqtCO-+tg*Y1JDVK-||sozLWFR&|jO0HK!hm8abD(+kj7zN2RX$ ztBI*FjXaakgP(`A--vI$xb+!E6rLS&yKHQ&&^-@@K^uanzfo)7Ewd@Cm34L1`Z_qbECg=5(jv5^v9UkPS{|a+(28xfv6&9yNOu zz8Tckrh}hejR$Scp4x4W*|5&h z(l1j4WJ%4r>rtiUF3|&adi!uW?Ne4%#taecj!P^`Y+NhlP!Ww8=5MAej<4B$)!w!1 z2G%q0^A|n}d)B1hy8}eJ+ueg2=XRMNj3AZ_= zpJ|%XBPm@!4FhF7Km!8fT}flICDSoVD9}FH;LV3`d|g`_di_8CCLd zHVBsi+GHKzFP{L}hW(dodu`EK+Pz4-V^~&tE9$(j;OTtNqf|;1t_7+ek-QVp^a3?y z>b|dt+TMSwA>o5)AR`qwFxjU@y@Zrf+X+PqnqC5zN?l#Y`#2>WovYoG6bb3fhI`pg zKhyB2B)bJ%TZRxu5Y@2X2}MgWtp+3f{NEfuVaX)@9rnz=QM+@EdO zu<6=Lo|3PVrQ;=ml5t!0$x!I3d5|} zt+k@mpC-ctW{1B#Vhp5QBDL}>*Fj5-VJL4 z_6qCORGY$NG6kbOB~|mFhVWFTF~)^~7KIuEy!Hg;i`AXV-{Inq+qOF5yyU0lQ*=)C zMPssga{oSh3^>2(a?FmHhlZht;y;+pGW2v-_UZ%8R~_dfxk51(b#<0Q+X8O8i$ns{e%x(@nvEd3R zfwnK(LK?PTY~_AwRtxR^5Hw*PCR?1*3)>`*Yk%u#@G_y7Yj~n#9vdo9@l!78@MDEIEc8_*I7zRTADyr-3hjD%!Q0?5&zBF`Vf{#WdSt zC8Y#!Cy{~lL%Gp+P?b6fT>IO`sLAJBkw@8ZXc^(fz68IE@QWMB%I;meqB`hgVWuPV zyVx0aAAj=J##>#%rFg^x8>y=A!cLRS#<8Y}H7AW61HD1@F5gCMbfxL9kG>Xa(Bsd_ zJ@2@@f#3>x=XB$-#Brrl`Ay%bd^?H3v?^2nhW+YqdSd^-7r+dxH$h`l(i(`+P9K<5 z`=cWEyNh%jFc4j={yN0`6Z~_i0qP$whyZQKAnx_#MrHm44-ju+Z)8Y^l{xE39W5G^ zz8_q$-(;~^=~$>8m(a1Pb&_DeU%?y^@YrTK1EG!#INq$Atpv^nGZ~Ce%a}<#aa~x) zNr{&j8nUNFP}oYVS6qa|dVCjwM~YS6iBA2JAeRQE!nz*?Wz5sSd}mzKpVh4tm}4(p@6G`z%rV`NkKkkfd82YaJTz%?wnM7tZuv zrumeOXPBmMy(+W7{TUMM(;>-1`TZjQTR@N9@iRZzff=oHwWWUD5Vb2(&N}iN-W$*l zs>?H)C?1pR{693Ehd*0?*#1$pN~^S0t5lU*sTsah(3&-BwJK7oMuHf%B{8d|_71W4 z4mBb*LTksKv7@cMYZt{Y&-1)~|G_!0bI#}7_jO;_`@x_nkda; zj|t(yPp)4L(gP?(!jtTcFflqnE-MCZR!VvlrlJ?hTuWyoFfhkzP-|ANFN_fzUD{%+ z41Jvrw=J49Do?()dMcL?^II5Y{yQCDO%i!A8W6)C##ui(%(o@#(neaGG^n-uJ zsQQ3_ZaC_2Jt=JxO+2k@c%IYoRlv_2b-|OtGEV&t&RT7bdib7$%_mx+{|&vX#>_u9 z#RIH#7aUv(Y0#YDM8@=Zv^EH?j(y7T_(!0;E#6xRWeGbR!hr8jGIV9TzDs==a@jv$ z;u|Hv{Nq+ALBN*z9ewsB#Es&bktjo>^us?>BPp4gs`O)VhbaZJl{Z!~NV73}k%N!D zugJA;eH6%NxF5a|Pz^}nKb^gyn&04wW_=Dxf!>IHD)URy&m>{nC{l1&%8^Kfs@dA< z<&?>+MafKB%T8lbPL0M4Cban?CbItlSoy$OprZ=ZivOawWT zr9)EzGnDMmn2joGdt$bzN*?`<0~5(4r2APjueO*h2aa~U<~ zT?uW{Ovdepyq<7<<>7WL)AV8cFkJ@by095*Go#|Cd zIot74qq=<6f_LI281*)BUB;VKJbb)cesiw+cT@8s?mIe%4B-_#^-Wx=z}`QY1_UEI za~*Xqz5xt&UW|j@=zG}U-P2aFb;pNPgQLT-@HglZtg3AJbNV3Y6BH87_T)fn%qI5% z{J_U@`y&#;FFI;m6ig{s><2zlovpSG*36gy^X}XOr1QKIpHU$zKwo#NMt(|u)KzCZ zd%bwL^-oVjbw4M$$-919qf@40(kfvUaX(o%(YklSB31GZ&Ic-YqEoSp>x*{!lbcp! z%HU%5s;}-3Ek?|j;9K-Axzp0C9#DIpH2TG1RpOy97U-yTWL@<5FlJXC{;6guAo#A^Z zLyhL-pHN^~mDP4b?QhNlnNel=(Ml!7odvZS`f0esGyJz?g33PU_YCm6iNEQ9sI+pD zH};@xmNyqSnyc)*)s>lFvZC(5@Mf)wa%`V8uOBW4Ccu2uZYJDIgVFkwZ+o!tFL*<4 z-=7%U{$}*2S~{&9lEtK2tcF{KpL`9^8)PvxYn9TSN*yl&KBCgQr@8|{K#Ed z*jXf$(x$lGs(cZwk>eGLg`4rH_|aWTtHd-hiu*eY#AD5;%NIycK~B4+Gmo}=V~w*k zOO(lg+h*$Dpz4Xklbp{my=#eytIzlDE~gxkHf>a$Hav_}K8;3Y$Qcw}}+@{WV@OpS(tGle}aT>if50 zX%m0YpWK$ijaCJ-5&e`x{+pO+-#NTLy4QXE%SK9Gf6rbsDCSLYb-@i@`yUfRV|*sh zvaZm0+*gfZOSYw!{^^j_Q9h|0Qc0-1&0vCv+P4r zQwh!NAM25*okP}{Y7KZLPFe4QCpf*@Q$H!wOM_cbicLyk+tFv|%j4GQ_ojoXpvf8j z6@VPeSm#&-!AH_w(5uZSA9K;kaXkif#2MxRn45UARN!K-B~e{BXjm*bstsHp^SaR{ z06`{kn8|z={x$O5vR`<4GBa=+Xo4u+YgQ;Cw%*?1d67qiKV28#f$sUdEKf0--S%R- zktLbB_-mgnf-(}aOQ(no0Ac;Mk-J z+m^1GJ7T0Y0j9w#$E@d1sBf{PjgNFD+S`33MGwTr$Zm2<3?f&>mUxDDlQSr~v@UZ)ny!wuHc>0cUk<;W#>gT25!OYOuttRLk)!fI|`t(j1TeG&F z%~La%#(M?dl8*f%^d3LqTj*)uU|&D14?^fQcLSRa0ZhM(m3=7waQX*V*30AWhs`(+ zC;0U$?O^;@6`oC%iag&i4RIwfdG-oLEZ06>Y(xIK_7{FcNDSQuFS1BTQ>B{MDi{is zvE6Z+jMMz?D`@9tkl#kE{+Fmoot#7)N<%7fj*r;T@7c(tS zg9|F@5D?(qxT{f$9|-a=*{aB^tcXR7I_~c0!&g7j>%|F4$5Ij3?Vl=px-@-uvSh~r zw(nk9##+f2-)l(T_$B{n>f$aYUK{RWlQLp)rWJ$R<`4tW`)_b-(pVdI{U>-Ldx}cr z?%ED65%V4s1nzZXblqQ_ny4~E35%N{ZlY<5qm8q@hTl3*NRD?VVn_WL%vR75`JDtS@4V-8E*t*8D?tN)Tv1WM^EZVufF-$)?qX{*P?u zzFex^B4_f_7t#rY=qfge>LPv3D1KfBt$aU~^MnTp_>TN79cT6WfP?fq##x*BeH71e z@tc2f8IE|WM38-hNj6kyyohN%QbVn7jRA5>nvq{URBAUGFBVwu;j*EUtZoAjM%tTx zZcf8~+HD%)p;wgZPD!WjysfEm`^u@|M=sS2l!H5Ywit77ts2;Sec;JMalYtt??-x5 z{6WGaRndyOoUR;zAJa7J`V4(}QJvb*wIAGv^od2kF~<#cItO*liGNw+m7^_2qd0pr<4OwJ9%b(hO?&PWi9Obac$`=^3Uk7f0;98S8d4V#9+
-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 literal 0 HcmV?d00001 diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp new file mode 100644 index 000000000..efb89a245 --- /dev/null +++ b/plugins/eq/eqcontrols.cpp @@ -0,0 +1,201 @@ +/* + * eqcontrols.cpp - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "eqcontrols.h" +#include "eqeffect.h" + + + + +EqControls::EqControls( EqEffect *effect ) : + EffectControls( effect ), + m_effect( effect ), + m_inGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Input gain") ), + m_outGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Output gain" ) ), + m_lowShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "Low shelf gain" ) ), + m_para1GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 1 gain" ) ), + m_para2GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 2 gain" ) ), + m_para3GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 3 gain" ) ), + m_para4GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 4 gain" ) ), + m_highShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "High Shelf gain" ) ), + m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), + m_lowShelfResModel( 1.4,0.0, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), + m_para1ResModel( 1.4 ,0.55, 10.0 , 0.001, this , tr( "Peak 1 res" ) ), + m_para2ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 2 res" ) ), + m_para3ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 3 res" ) ), + m_para4ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 4 res" ) ), + m_highShelfResModel( 1.4, 0.001, 10.0 , 0.001, this , tr( "High Shelf res" ) ), + m_lpResModel( 0.707,0.003, 10.0 , 0.001, this , tr( "LP res" ) ), + m_hpFeqModel( 31.0, 30.0, 20000, 0.001, this , tr( "HP freq" ) ), + m_lowShelfFreqModel( 80.0, 25.0, 20000, 0.001, this , tr( "Low Shelf freq" ) ), + m_para1FreqModel( 120.0, 27.0, 20000, 0.001, this , tr( "Peak 1 freq" ) ), + m_para2FreqModel( 250.0, 27.0, 20000, 0.001, this, tr( "Peak 2 freq" ) ), + m_para3FreqModel( 2000.0, 27.0, 20000, 0.001, this , tr( "Peak 3 freq" ) ), + m_para4FreqModel( 4000.0, 27.0, 20000, 0.001, this , tr( "Peak 4 freq" ) ), + m_highShelfFreqModel( 12000.0, 27.0, 20000, 0.001, this , tr( "High shelf freq" ) ), + m_lpFreqModel( 18000.0, 27.0, 20000, 0.001, this , tr( "LP freq" ) ), + m_hpActiveModel( false, this , tr( "HP active" ) ), + m_lowShelfActiveModel( false, this , tr( "Low shelf active" ) ), + m_para1ActiveModel(false, this , tr( "Peak 1 active" ) ), + m_para2ActiveModel( false, this , tr( "Peak 2 active" ) ), + m_para3ActiveModel( false, this , tr( "Peak 3 active" ) ), + m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), + m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), + m_lpActiveModel( false, this , tr( "LP active" ) ), + m_lp12Model( true, this , tr( "LP 12" ) ), + m_lp24Model( false, this , tr( "LP 24" ) ), + m_lp48Model( false, this , tr( "LP 48" ) ), + m_hp12Model( true, this , tr( "HP 12" ) ), + m_hp24Model( false, this , tr( "HP 24" ) ), + m_hp48Model( false, this , tr( "HP 48" ) ), + m_analyzeModel( true, this , tr( "Analyze enable" ) ) + +{ + m_hpFeqModel.setScaleLogarithmic( true ); + m_lowShelfFreqModel.setScaleLogarithmic( true ); + m_para1FreqModel.setScaleLogarithmic( true ); + m_para2FreqModel.setScaleLogarithmic( true ); + m_para3FreqModel.setScaleLogarithmic( true ); + m_para4FreqModel.setScaleLogarithmic( true ); + m_highShelfFreqModel.setScaleLogarithmic( true ); + m_lpFreqModel.setScaleLogarithmic( true ); + + m_para1GainModel.setScaleLogarithmic( true ); + m_inPeakL = 0; + m_inPeakR = 0; + m_lowShelfPeakL = 0; m_lowShelfPeakR = 0; + m_para1PeakL = 0; m_para1PeakR = 0; + m_para2PeakL = 0; m_para2PeakR = 0; + m_para3PeakL = 0; m_para3PeakR = 0; + m_para4PeakL = 0; m_para4PeakR = 0; + m_highShelfPeakL = 0; m_highShelfPeakR = 0; + +} + + + + +void EqControls::loadSettings( const QDomElement &_this ) +{ + m_inGainModel.loadSettings( _this, "Inputgain" ); + m_outGainModel.loadSettings( _this, "Outputgain"); + m_lowShelfGainModel.loadSettings( _this , "Lowshelfgain" ); + m_para1GainModel.loadSettings( _this, "Peak1gain" ); + m_para2GainModel.loadSettings( _this, "Peak2gain" ); + m_para3GainModel.loadSettings( _this, "Peak3gain" ); + m_para4GainModel.loadSettings( _this, "Peak4gain" ); + m_highShelfGainModel.loadSettings( _this , "HighShelfgain"); + + m_hpResModel.loadSettings( _this ,"HPres"); + m_lowShelfResModel.loadSettings( _this, "LowShelfres" ); + m_para1ResModel.loadSettings( _this ,"Peak1res" ); + m_para2ResModel.loadSettings( _this ,"Peak2res" ); + m_para3ResModel.loadSettings( _this ,"Peak3res" ); + m_para4ResModel.loadSettings( _this ,"Peak4res" ); + m_highShelfResModel.loadSettings( _this, "HighShelfres" ); + m_lpResModel.loadSettings( _this, "LPres"); + + m_hpFeqModel.loadSettings( _this, "HPfreq" ); + m_lowShelfFreqModel.loadSettings( _this, "LowShelffreq" ); + m_para1FreqModel.loadSettings( _this, "Peak1freq" ); + m_para2FreqModel.loadSettings( _this, "Peak2freq" ); + m_para3FreqModel.loadSettings( _this, "Peak3freq" ); + m_para4FreqModel.loadSettings( _this, "Peak4freq" ); + m_highShelfFreqModel.loadSettings( _this, "Highshelffreq" ); + m_lpFreqModel.loadSettings( _this, "LPfreq" ); + + m_hpActiveModel.loadSettings( _this, "HPactive" ); + m_lowShelfActiveModel.loadSettings( _this, "Lowshelfactive" ); + m_para1ActiveModel.loadSettings( _this, "Peak1active"); + m_para2ActiveModel.loadSettings( _this, "Peak2active"); + m_para3ActiveModel.loadSettings( _this, "Peak3active"); + m_para4ActiveModel.loadSettings( _this, "Peak4active"); + m_highShelfActiveModel.loadSettings( _this, "Highshelfactive" ); + m_lpActiveModel.loadSettings( _this, "LPactive" ); + + m_lp12Model.loadSettings( _this , "LP12" ); + m_lp24Model.loadSettings( _this , "LP24" ); + m_lp48Model.loadSettings( _this , "LP48" ); + + m_hp12Model.loadSettings( _this , "HP12" ); + m_hp24Model.loadSettings( _this , "HP24" ); + m_hp48Model.loadSettings( _this , "HP48" ); + m_analyzeModel.loadSettings( _this, "Analyzeenable"); +} + + + + +void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) +{ + + m_inGainModel.saveSettings( doc, parent, "Inputgain" ); + m_outGainModel.saveSettings( doc, parent, "Outputgain"); + m_lowShelfGainModel.saveSettings( doc, parent , "Lowshelfgain" ); + m_para1GainModel.saveSettings( doc, parent, "Peak1gain" ); + m_para2GainModel.saveSettings( doc, parent, "Peak2gain" ); + m_para3GainModel.saveSettings( doc, parent, "Peak3gain" ); + m_para4GainModel.saveSettings( doc, parent, "Peak4gain" ); + m_highShelfGainModel.saveSettings( doc, parent, "HighShelfgain"); + + m_hpResModel.saveSettings( doc, parent ,"HPres"); + m_lowShelfResModel.saveSettings( doc, parent, "LowShelfres" ); + m_para1ResModel.saveSettings( doc, parent,"Peak1res" ); + m_para2ResModel.saveSettings( doc, parent,"Peak2res" ); + m_para3ResModel.saveSettings( doc, parent,"Peak3res" ); + m_para4ResModel.saveSettings( doc, parent,"Peak4res" ); + m_highShelfResModel.saveSettings( doc, parent, "HighShelfres" ); + m_lpResModel.saveSettings( doc, parent, "LPres"); + + m_hpFeqModel.saveSettings( doc, parent, "HPfreq" ); + m_lowShelfFreqModel.saveSettings( doc, parent, "LowShelffreq" ); + m_para1FreqModel.saveSettings( doc, parent, "Peak1freq" ); + m_para2FreqModel.saveSettings( doc, parent, "Peak2freq" ); + m_para3FreqModel.saveSettings( doc, parent, "Peak3freq" ); + m_para4FreqModel.saveSettings( doc, parent, "Peak4freq" ); + m_highShelfFreqModel.saveSettings( doc, parent, "Highshelffreq" ); + m_lpFreqModel.saveSettings( doc, parent, "LPfreq" ); + + m_hpActiveModel.saveSettings( doc, parent, "HPactive" ); + m_lowShelfActiveModel.saveSettings( doc, parent, "Lowshelfactive" ); + m_para1ActiveModel.saveSettings( doc, parent, "Peak1active" ); + m_para2ActiveModel.saveSettings( doc, parent, "Peak2active" ); + m_para3ActiveModel.saveSettings( doc, parent, "Peak3active" ); + m_para4ActiveModel.saveSettings( doc, parent, "Peak4active" ); + m_highShelfActiveModel.saveSettings( doc, parent, "Highshelfactive" ); + m_lpActiveModel.saveSettings( doc, parent, "LPactive" ); + + m_lp12Model.saveSettings( doc, parent, "LP12" ); + m_lp24Model.saveSettings( doc, parent, "LP24" ); + m_lp48Model.saveSettings( doc, parent, "LP48" ); + + m_hp12Model.saveSettings( doc, parent, "HP12" ); + m_hp24Model.saveSettings( doc, parent, "HP24" ); + m_hp48Model.saveSettings( doc, parent, "HP48" ); + m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); + +} + diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h new file mode 100644 index 000000000..f6703919e --- /dev/null +++ b/plugins/eq/eqcontrols.h @@ -0,0 +1,127 @@ +/* + * eqcontrols.h - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EQCONTROLS_H +#define EQCONTROLS_H + +#include "EffectControls.h" +#include "eqcontrolsdialog.h" +#include "Knob.h" + +class EqEffect; + +class EqControls : public EffectControls +{ + Q_OBJECT +public: + EqControls( EqEffect* effect ); + virtual ~EqControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Eq"; + } + virtual int controlCount() + { + return 39; + } + virtual EffectControlDialog* createView() + { + printf("create dialog\n"); + return new EqControlsDialog( this ); + } + + float m_inPeakL; + float m_inPeakR; + float m_outPeakL; + float m_outPeakR; + float m_lowShelfPeakL, m_lowShelfPeakR; + float m_para1PeakL, m_para1PeakR; + float m_para2PeakL, m_para2PeakR; + float m_para3PeakL, m_para3PeakR; + float m_para4PeakL, m_para4PeakR; + float m_highShelfPeakL, m_highShelfPeakR; + + + + + +private: + EqEffect* m_effect; + + FloatModel m_inGainModel; + FloatModel m_outGainModel; + FloatModel m_lowShelfGainModel; + FloatModel m_para1GainModel; + FloatModel m_para2GainModel; + FloatModel m_para3GainModel; + FloatModel m_para4GainModel; + FloatModel m_highShelfGainModel; + + FloatModel m_hpResModel; + FloatModel m_lowShelfResModel; + FloatModel m_para1ResModel; + FloatModel m_para2ResModel; + FloatModel m_para3ResModel; + FloatModel m_para4ResModel; + FloatModel m_highShelfResModel; + FloatModel m_lpResModel; + + FloatModel m_hpFeqModel; + FloatModel m_lowShelfFreqModel; + FloatModel m_para1FreqModel; + FloatModel m_para2FreqModel; + FloatModel m_para3FreqModel; + FloatModel m_para4FreqModel; + FloatModel m_highShelfFreqModel; + FloatModel m_lpFreqModel; + + BoolModel m_hpActiveModel; + BoolModel m_lowShelfActiveModel; + BoolModel m_para1ActiveModel; + BoolModel m_para2ActiveModel; + BoolModel m_para3ActiveModel; + BoolModel m_para4ActiveModel; + BoolModel m_highShelfActiveModel; + BoolModel m_lpActiveModel; + + BoolModel m_lp12Model; + BoolModel m_lp24Model; + BoolModel m_lp48Model; + + BoolModel m_hp12Model; + BoolModel m_hp24Model; + BoolModel m_hp48Model; + + BoolModel m_analyzeModel; + + friend class EqControlsDialog; + friend class EqEffect; + +}; + +#endif // EQCONTROLS_H diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp new file mode 100644 index 000000000..704e81f00 --- /dev/null +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -0,0 +1,164 @@ +/* + * eqcontrolsdialog.cpp - defination of EqControlsDialog class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "eqcontrolsdialog.h" +#include "eqcontrols.h" +#include "embed.h" +#include "Fader.h" +#include "eqfader.h" +#include "Engine.h" +#include "AutomatableButton.h" +#include "QWidget" +#include "MainWindow.h" +#include "LedCheckbox.h" + + + + +EqControlsDialog::EqControlsDialog(EqControls *controls) : + EffectControlDialog( controls ) + +{ + m_controls = controls; + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 350, 275 ); + + m_parameterWidget = new EqParameterWidget( this ); + m_parameterWidget->move( 50, 5 ); +// m_inSpec = new EqSpectrumView( controls->m_effect, this); + + setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); + setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); + setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255, 173, 115), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); + setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255, 255, 0), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); + setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(0, 255, 0), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); + setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(0, 186, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); + setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(222, 0, 222 ), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); + setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(156, 156, 156 ), tr( "LP" ) ,0,0); + int cw = width()/8; //the chanel width in pixels + int ko = ( cw * 0.5 ) - ((new Knob( knobBright_26, 0 ))->width() * 0.5 ); + + m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); + m_inGainFader->move( 10, 5 ); + + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); + m_outGainFader->move( 315, 5 ); + //gain faders + + int fo = (cw * 0.5) - (m_outGainFader->width() * 0.5 ); + + for( int i = 1; i < m_parameterWidget->bandCount() - 1; i++) + { + m_gainFader = new EqFader( m_parameterWidget->getBandModels(i)->gain, tr( "" ), this ,m_parameterWidget->getBandModels( i )->peakL, m_parameterWidget->getBandModels( i )->peakR ); + m_gainFader->move( cw * i + fo , 123 ); + m_gainFader->setMinimumHeight(80); + m_gainFader->resize(m_gainFader->width() , 80); + } + + for( int i = 0; i < m_parameterWidget->bandCount() ; i++) + { + m_resKnob = new Knob( knobBright_26, this ); + m_resKnob->move(cw * i + ko , 205 ); + m_resKnob->setVolumeKnob(false); + m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); + + m_freqKnob = new Knob( knobBright_26, this ); + m_freqKnob->move(cw * i + ko, 235 ); + m_freqKnob->setVolumeKnob( false ); + m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); + + m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); + m_activeBox->move( cw * i + fo + 3, 260 ); + m_activeBox->setModel( m_parameterWidget->getBandModels( i )->active ); + } + + //hp filter type + + m_hp12Box = new LedCheckBox( tr( "12dB" ), this ); + m_hp12Box->move( cw*0 + ko, 175 ); + m_hp12Box->setModel( &controls->m_hp12Model ); + m_hp24Box = new LedCheckBox( tr( "24dB" ), this ); + m_hp24Box->move( cw*0 + ko, 155 ); + m_hp24Box->setModel( &controls->m_hp24Model ); + + m_hp48Box = new LedCheckBox( tr( "48dB" ), this ); + m_hp48Box->move( cw*0 + ko, 135 ); + m_hp48Box->setModel( &controls->m_hp48Model ); + + //LP filter type + + m_lp12Box = new LedCheckBox( tr( "12dB"), this ); + m_lp12Box->move( cw*7 + ko -5 , 175 ); + m_lp12Box->setModel( &controls->m_lp12Model ); + m_lp24Box = new LedCheckBox( tr( "24dB"), this ); + m_lp24Box->move( cw*7 + ko - 5, 155 ); + m_lp24Box->setModel( &controls->m_lp24Model ); + m_lp48Box = new LedCheckBox( tr( "48dB"), this ); + m_lp48Box->move( cw*7 + ko - 5, 135 ); + m_lp48Box->setModel( &controls->m_lp48Model ); + + automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); + lpBtnGrp->addButton(m_lp12Box); + lpBtnGrp->addButton(m_lp24Box ); + lpBtnGrp->addButton(m_lp48Box ); + connect( m_lp12Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( m_lp24Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( m_lp48Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( &controls->m_lp12Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_lp24Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_lp48Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + + + + automatableButtonGroup *hpBtnGrp = new automatableButtonGroup( this, tr( "hp grp" ) ); + hpBtnGrp->addButton(m_hp12Box ); + hpBtnGrp->addButton(m_hp24Box ); + hpBtnGrp->addButton(m_hp48Box ); + connect( m_hp12Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( m_hp24Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( m_hp48Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp12Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp24Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp48Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + + + + + //Analize Box + m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); + m_analyzeBox->move( cw*1 + ko + 5, 10 ); + m_analyzeBox->setModel( &controls->m_analyzeModel ); + +} + +void EqControlsDialog::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_originalHeight = parentWidget()->height() == 150 ? m_originalHeight : parentWidget()->height() ; + parentWidget()->setFixedHeight( parentWidget()->height() == m_originalHeight ? 150 : m_originalHeight ); + update(); +} diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h new file mode 100644 index 000000000..bb4c0cfad --- /dev/null +++ b/plugins/eq/eqcontrolsdialog.h @@ -0,0 +1,94 @@ +/* + * eqcontrolsdialog.h - defination of EqControlsDialog class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EQCONTROLSDIALOG_H +#define EQCONTROLSDIALOG_H + +#include "EffectControlDialog.h" +#include "Fader.h" +#include "Knob.h" +#include "LedCheckbox.h" +#include "eqparameterwidget.h" +#include "MainWindow.h" +#include "qpushbutton.h" +//#include "eqspectrumview.h" + +class EqControls; + +class EqControlsDialog : public EffectControlDialog +{ + +public: + EqControlsDialog( EqControls* controls ); + virtual ~EqControlsDialog() + { + } + + EqBand * setBand(EqControls *controls); + +private slots: + void updateVuMeters(); + +private: + EqControls * m_controls; + + Fader* m_inGainFader; + Fader* m_outGainFader; + Fader* m_gainFader; + Knob* m_resKnob; + Knob* m_freqKnob; + LedCheckBox* m_activeBox; + LedCheckBox* m_lp12Box; + LedCheckBox* m_lp24Box; + LedCheckBox* m_lp48Box; + LedCheckBox* m_hp12Box; + LedCheckBox* m_hp24Box; + LedCheckBox* m_hp48Box; + + LedCheckBox* m_analyzeBox; + + EqParameterWidget* m_parameterWidget; +// EqSpectrumView* m_inSpec; + + virtual void mouseDoubleClickEvent(QMouseEvent *event); + + EqBand* setBand( int index, BoolModel* active, FloatModel* freq, FloatModel* res, FloatModel* gain, QColor color, QString name, float* peakL, float* peakR) + { + EqBand* filterModels = m_parameterWidget->getBandModels( index ); + filterModels->active = active; + filterModels->freq = freq; + filterModels->res = res; + filterModels->color = color; + filterModels->gain = gain; + filterModels->peakL = peakL; + filterModels->peakR = peakR; + return filterModels; + } + + int m_originalHeight; + + +}; + +#endif // EQCONTROLSDIALOG_H diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp new file mode 100644 index 000000000..20be28000 --- /dev/null +++ b/plugins/eq/eqeffect.cpp @@ -0,0 +1,314 @@ +/* + * eqeffect.cpp - defination of EqEffect class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "eqeffect.h" +#include "embed.cpp" +#include "lmms_math.h" +#include "BasicFilters.h" +#include "interpolation.h" +#include "Engine.h" +#include "MainWindow.h" + +//TODO +//re write to store data from each filter(models,name, storeage name ) in a class, stored in array +//then just loop ever array for each section + +const int MAX_BANDS = 249; + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Eq", + QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + "Dave French ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + + + + +EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : + Effect( &eq_plugin_descriptor, parent, key ), + m_eqControls( this ) + + + +{ + m_dFilterCount = 4; + m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; + for( int i = 0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].setFrequency(20000); + m_downsampleFilters[i].setQ(0.66); + m_downsampleFilters[i].setGain(0); + m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); + } + m_upBuf = 0; +} + + + + +EqEffect::~EqEffect() +{ + +} + + + + +bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + double outSum = 0.0; + const float d = dryLevel(); + const float w = wetLevel(); + const float outGain = m_eqControls.m_outGainModel.value(); + const int sampleRate = Engine::mixer()->processingSampleRate() * 2; + sample_t dryS[2]; + sampleFrame m_inPeak; + + //TODO UPSAMPLE + upsample( buf, frames ); + + gain(m_upBuf , m_upBufFrames, m_eqControls.m_inGainModel.value(), &m_inPeak ); + m_eqControls.m_inPeakL = m_eqControls.m_inPeakL < m_inPeak[0] ? m_inPeak[0] : m_eqControls.m_inPeakL; + m_eqControls.m_inPeakR = m_eqControls.m_inPeakR < m_inPeak[1] ? m_inPeak[0] : m_eqControls.m_inPeakR; + + if(m_eqControls.m_hpActiveModel.value() ){ + m_hp12.setSampleRate( sampleRate ); + m_hp12.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp12.setQ( m_eqControls.m_hpResModel.value() ); + m_hp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_hp24Model.value() || m_eqControls.m_hp48Model.value() ) + { + m_hp24.setSampleRate( sampleRate ); + m_hp24.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp24.setQ( m_eqControls.m_hpResModel.value() ); + m_hp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_hp48Model.value() ) + { + m_hp480.setSampleRate( sampleRate ); + m_hp480.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp480.setQ( m_eqControls.m_hpResModel.value() ); + m_hp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_hp481.setSampleRate( sampleRate ); + m_hp481.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp481.setQ( m_eqControls.m_hpResModel.value() ); + m_hp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + if( m_eqControls.m_lowShelfActiveModel.value() ) + { + m_lowShelf.setSampleRate( sampleRate ); + m_lowShelf.setFrequency( m_eqControls.m_lowShelfFreqModel.value() ); + m_lowShelf.setQ( m_eqControls.m_lowShelfResModel .value() ); + m_lowShelf.setGain( m_eqControls.m_lowShelfGainModel.value() ); + m_lowShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para1ActiveModel.value() ) + { + m_para1.setSampleRate(sampleRate ); + m_para1.setFrequency( m_eqControls.m_para1FreqModel.value() ); + m_para1.setQ( m_eqControls.m_para1ResModel.value() ); + m_para1.setGain( m_eqControls.m_para1GainModel.value() ); + m_para1.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para2ActiveModel.value() ) + { + m_para2.setSampleRate( sampleRate ); + m_para2.setFrequency( m_eqControls.m_para2FreqModel.value() ); + m_para2.setQ( m_eqControls.m_para2ResModel.value() ); + m_para2.setGain( m_eqControls.m_para2GainModel.value() ); + m_para2.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para3ActiveModel.value() ) + { + m_para3.setSampleRate( sampleRate); + m_para3.setFrequency( m_eqControls.m_para3FreqModel.value() ); + m_para3.setQ( m_eqControls.m_para3ResModel.value() ); + m_para3.setGain( m_eqControls.m_para3GainModel.value() ); + m_para3.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para4ActiveModel.value() ) + { + m_para4.setSampleRate( sampleRate ); + m_para4.setFrequency( m_eqControls.m_para4FreqModel.value() ); + m_para4.setQ( m_eqControls.m_para4ResModel.value() ); + m_para4.setGain( m_eqControls.m_para4GainModel.value() ); + m_para4.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_highShelfActiveModel.value() ) + { + m_highShelf.setSampleRate( sampleRate ); + m_highShelf.setFrequency( m_eqControls.m_highShelfFreqModel.value() ); + m_highShelf.setQ( m_eqControls.m_highShelfResModel.value() ); + m_highShelf.setGain( m_eqControls.m_highShelfGainModel.value() ); + m_highShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if(m_eqControls.m_lpActiveModel.value() ){ + m_lp12.setSampleRate( sampleRate ); + m_lp12.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp12.setQ( m_eqControls.m_lpResModel.value() ); + m_lp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_lp24Model.value() || m_eqControls.m_lp48Model.value() ) + { + m_lp24.setSampleRate( sampleRate ); + m_lp24.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp24.setQ( m_eqControls.m_lpResModel.value() ); + m_lp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_lp48Model.value() ) + { + m_lp480.setSampleRate( sampleRate ); + m_lp480.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp480.setQ( m_eqControls.m_lpResModel.value() ); + m_lp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_lp481.setSampleRate( sampleRate ); + m_lp481.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp481.setQ( m_eqControls.m_lpResModel.value() ); + m_lp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + sampleFrame outPeak; + gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); + m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; + m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; + + //TODO lp filter before downsample + for( int i =0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].processBuffer(m_upBuf , m_upBufFrames ); + } + + downSample( buf, frames ); + + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); + buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + checkGate( outSum / frames ); + + return isRunning(); +} + + + + +void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak) +{ + peak[0][0] = 0.0f; peak[0][1] = 0.0f; + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] *= scale; + buf[f][1] *= scale; + + if( fabs( buf[f][0] ) > peak[0][0] ) + { + peak[0][0] = fabs( buf[f][0] ); + } + if( fabs( buf[f][1] ) > peak[0][1] ) + { + peak[0][1] = fabs( buf[f][0] ); + } + } + +} + +sampleFrame m_lastUpFrame; +void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) +{ + + if(m_upBufFrames != frames * 2 ) + { + if( m_upBuf ) + { + delete m_upBuf; + } + m_upBuf = new sampleFrame[frames * 2]; + m_upBufFrames = frames * 2; + } + + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + m_upBuf[f2][0] = linearInterpolate(m_lastUpFrame[0],buf[f][0], 0.5); + m_upBuf[f2][1] = linearInterpolate(m_lastUpFrame[1],buf[f][1], 0.5); + m_upBuf[f2+1][0] = buf[f][0]; + m_upBuf[f2+1][1] = buf[f][1]; + + m_lastUpFrame[0] = buf[f][0]; + m_lastUpFrame[1] = buf[f][1]; + } +} + +void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) +{ + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + buf[f][0] = m_upBuf[f2][0]; + buf[f][1] = m_upBuf[f2][1]; + } +} + + + + + +extern "C" +{ + +//needed for getting plugin out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new EqEffect( parent , static_cast( data ) ); +} + +}} diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h new file mode 100644 index 000000000..987eac9c4 --- /dev/null +++ b/plugins/eq/eqeffect.h @@ -0,0 +1,80 @@ +/* eqeffect.h - defination of EqEffect class. +* +* Copyright (c) 2014 David French +* +* This file is part of LMMS - http://lmms.io +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program (see COPYING); if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301 USA. +* +*/ + + +#ifndef EQEFFECT_H +#define EQEFFECT_H + +#include "Effect.h" +#include "eqcontrols.h" +#include "lmms_math.h" +#include "BasicFilters.h" +#include "eqfilter.h" + +class EqEffect : public Effect +{ +public: + EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~EqEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_eqControls; + } + void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); + +private: + EqControls m_eqControls; + + EqHp12Filter m_hp12; + EqHp12Filter m_hp24; + EqHp12Filter m_hp480; + EqHp12Filter m_hp481; + + EqLowShelfFilter m_lowShelf; + + EqPeakFilter m_para1; + EqPeakFilter m_para2; + EqPeakFilter m_para3; + EqPeakFilter m_para4; + + EqHighShelfFilter m_highShelf; + + EqLp12Filter m_lp12; + EqLp12Filter m_lp24; + EqLp12Filter m_lp480; + EqLp12Filter m_lp481; + EqLp12Filter* m_downsampleFilters; + int m_dFilterCount; + sampleFrame* m_upBuf; + fpp_t m_upBufFrames; + +// const static int MAX_BANDS = 249; + void upsample( sampleFrame *buf, const fpp_t frames ); + void downSample( sampleFrame *buf, const fpp_t frames ); +// float m_bands[MAX_BANDS]; +// float m_energy; + +}; + +#endif // EQEFFECT_H diff --git a/plugins/eq/eqfader.cpp b/plugins/eq/eqfader.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqfader.h b/plugins/eq/eqfader.h new file mode 100644 index 000000000..65c7a55a0 --- /dev/null +++ b/plugins/eq/eqfader.h @@ -0,0 +1,111 @@ +/* eqfader.h - defination of EqFader class. +* +* Copyright (c) 2014 David French +* +* This file is part of LMMS - http://lmms.io +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program (see COPYING); if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301 USA. +* +*/ + +#ifndef EQFADER_H +#define EQFADER_H +#include "Fader.h" +#include "EffectControls.h" +#include "MainWindow.h" +#include "qwidget.h" +#include "TextFloat.h" +#include "qlist.h" + + + +class EqFader : public Fader +{ + +public: + Q_OBJECT +public: + EqFader( FloatModel * model, const QString & name, QWidget * parent, float* lPeak, float* rPeak ) : + Fader( model, name, parent) + { + setMinimumSize( 23, 116 ); + setMaximumSize( 23, 116 ); + resize( 23, 116 ); + m_lPeak = lPeak; + m_rPeak = rPeak; + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); + m_text = 0; + QList tfs = Engine::mainWindow()->findChildren(); + m_text = tfs.last(); + if(m_text) + { + printf("found text float\n"); + } + connect(model , SIGNAL (dataChanged()) , this, SLOT (updateText () )); + m_model = model; + } + + + + + ~EqFader() + { + } + + +private slots: + + void updateVuMeters() + { + const float opl = getPeak_L(); + const float opr = getPeak_R(); + const float fall_off = 1.2; + if( *m_lPeak > opl ) + { + setPeak_L( *m_lPeak ); + *m_lPeak = 0; + } + else + { + setPeak_L( opl/fall_off ); + } + + if( *m_rPeak > opr ) + { + setPeak_R( *m_rPeak ); + *m_rPeak = 0; + } + else + { + setPeak_R( opr/fall_off ); + } + update(); + } + + void updateText() + { + m_text->setText(QString()+m_model->value()); + } + + +private: + float* m_lPeak; + float* m_rPeak; + TextFloat* m_text; + FloatModel* m_model; + +}; +#endif // EQFADER_H diff --git a/plugins/eq/eqfilter.cpp b/plugins/eq/eqfilter.cpp new file mode 100644 index 000000000..7407377b9 --- /dev/null +++ b/plugins/eq/eqfilter.cpp @@ -0,0 +1,5 @@ +#include "eqfilter.h" + + + + diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h new file mode 100644 index 000000000..69ee49c8b --- /dev/null +++ b/plugins/eq/eqfilter.h @@ -0,0 +1,315 @@ +/* + * eqfilter.cpp - defination of EqFilterclass. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EQFILTER_H +#define EQFILTER_H + +#include "BasicFilters.h" +#include "lmms_math.h" + +/// +/// \brief The EqFilter class. +/// A wrapper for the StereoBiQuad class, giving it freq, res, and gain controls. +/// It is designed to process periods in one pass, with recalculation of coefficents +/// upon parameter changes. The intention is to use this as a bass class, children override +/// the calcCoefficents() function, providing the coefficents a1, a2, b0, b1, b2. +/// +class EqFilter : public StereoBiQuad +{ +public: + EqFilter() + { + + } + + virtual inline void setSampleRate( int sampleRate ) + { + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + calcCoefficents(); + } + } + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + calcCoefficents(); + } + } + + virtual void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } + + virtual void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } + + /// + /// \brief processBuffer + /// \param buf Audio Buffer + /// \param frames Count of sampleFrames in Audio Buffer + /// + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } + +protected: + /// + /// \brief calcCoefficents + /// Override this in child classes to provide the coefficents, based on + /// Freq, Res and Gain + virtual void calcCoefficents(){ + setCoeffs( 0, 0, 0, 0, 0 ); + + } + + float m_sampleRate; + float m_freq; + float m_res; + float m_gain; +}; + + + + +/// +/// \brief The EqHp12Filter class +/// A 2 pole High Pass Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +class EqHp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 + c ) * 0.5; + b1 = ( -( 1 + c ) ); + b2 = ( 1 + c ) * 0.5; + a0 = 1 + alpha; + a1 = ( -2 * c ); + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + + +/// +/// \brief The EqLp12Filter class. +/// A 2 pole low pass filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqLp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 - c ) * 0.5; + b1 = 1 - c; + b2 = ( 1 - c ) * 0.5; + a0 = 1 + alpha; + a1 = -2 * c; + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + +/// +/// \brief The EqPeakFilter class +/// A Peak Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqPeakFilter : public EqFilter +{ +public: + + + virtual void calcCoefficents() + { + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = 1 + alpha*A; + b1 = -2*c; + b2 = 1 - alpha*A; + a0 = 1 + alpha/A; + a1 = -2*c; + a2 = 1 - alpha/A; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + +class EqLowShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); +// float alpha = s / ( 2 * m_res ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; + b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); + a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + +class EqHighShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025 ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); + a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + + +#endif // EQFILTER_H diff --git a/plugins/eq/eqhighshelffileter.h b/plugins/eq/eqhighshelffileter.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/plugins/eq/eqhighshelffileter.h @@ -0,0 +1 @@ + diff --git a/plugins/eq/eqhp12.h b/plugins/eq/eqhp12.h new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqlowshelffilter.h b/plugins/eq/eqlowshelffilter.h new file mode 100644 index 000000000..b28b04f64 --- /dev/null +++ b/plugins/eq/eqlowshelffilter.h @@ -0,0 +1,3 @@ + + + diff --git a/plugins/eq/eqlp12.h b/plugins/eq/eqlp12.h new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp new file mode 100644 index 000000000..eb329460b --- /dev/null +++ b/plugins/eq/eqparameterwidget.cpp @@ -0,0 +1,197 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "eqparameterwidget.h" +#include "QPainter" +#include "qwidget.h" +#include "lmms_math.h" + +#include "MainWindow.h" +#include "QMouseEvent" + +EqParameterWidget::EqParameterWidget( QWidget *parent ) : + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) +{ + m_bands = new EqBand[8]; + resize( 250, 116 ); + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + float totalLength = log10( 21000 ); + m_pixelsPerUnitWidth = width( ) / totalLength ; + float totalHeight = 40; + m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); + m_scale = 1.5; + m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); +} + + + + +EqParameterWidget::~EqParameterWidget() +{ + +} + + + + +void EqParameterWidget::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + for( int i = 0 ; i < bandCount() ; i++ ) + { + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + float x = freqToXPixel( m_bands[i].freq->value() ); + float y = height() * 0.5; + float gain = 1; + if( m_bands[i].gain ) + { + gain = m_bands[i].gain->value(); + } + y = gainToYPixel( gain ) + 3; + float bw = m_bands[i].freq->value() / m_bands[i].res->value(); + m_bands[i].x = x; m_bands[i].y = y; + painter.drawPoint( x, y ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + if(i == 0 || i == bandCount() - 1 ){ + painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + } + else + { + painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); + } + } + //Draw color band + int sectionLength = width() / bandCount(); + for( int i = 0; i < bandCount(); i++) + { + m_bands[i].color.setAlpha( 255 ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + } +} + + + + +void EqParameterWidget::mousePressEvent( QMouseEvent *event ) +{ + m_oldX = event->x(); m_oldY = event->y(); + m_selectedBand = selectNearestHandle( event->x(), event->y() ); + m_mouseAction = none; + if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; + if ( event->button() == Qt::RightButton ) m_mouseAction = res; +} + + + + +void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) +{ + m_selectedBand = 0; + m_mouseAction = none; +} + + + + +void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) +{ + int deltaX = event->x() - m_oldX; + int deltaR = event->y() - m_oldY; + m_oldX = event->x(); m_oldY = event->y(); + if(m_selectedBand && m_selectedBand->active->value() ) + { + switch ( m_mouseAction ) { + case none : + break; + case drag: + if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); + if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); + break; + case res: + if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); + if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); + break; + default: + break; + } + } +} + + + + +void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) +{ + EqBand* selected = selectNearestHandle( event->x() , event->y() ); + if( selected ) + { + selected->active->setValue( selected->active->value() ? 0 : 1 ); + } +} + + + + +EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) +{ + EqBand* selectedModel = 0; + float* distanceToHandles = new float[bandCount()]; + //calc distance to each handle + for( int i = 0 ; i < bandCount() ; i++) + { + int xOffset = m_bands[i].x - x; + int yOffset = m_bands[i].y - y; + distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + } + //select band + int shortestBand = 0; + for (int i = 1 ; i < bandCount() ; i++ ) + { + if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ + shortestBand = i; + } + } + if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) + { + selectedModel = &m_bands[shortestBand]; + } + delete distanceToHandles; + return selectedModel; +} + + + + +EqBand::EqBand() : + gain ( 0 ), + res ( 0 ), + freq ( 0 ), + color ( QColor( 255, 255, 255 ) ), + name ( QString( "" ) ) +{ +} diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h new file mode 100644 index 000000000..cb14154b3 --- /dev/null +++ b/plugins/eq/eqparameterwidget.h @@ -0,0 +1,158 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef EQPARAMETERWIDGET_H +#define EQPARAMETERWIDGET_H +#include +#include "EffectControls.h" + + +class EqBand +{ +public : + EqBand(); + FloatModel* gain; + FloatModel* res; + FloatModel* freq; + BoolModel* active; + QColor color; + int x; + int y; + QString name; + float* peakL; + float* peakR; + +}; + + + +class EqParameterWidget : public QWidget +{ + +public: + explicit EqParameterWidget( QWidget *parent = 0 ); + ~EqParameterWidget(); + const int bandCount() + { + return 8; + } + + + + const int maxDistanceFromHandle() + { + return 20; + } + + + + + EqBand* getBandModels( int i ) + { + return &m_bands[i]; + } + + + + + const int activeAplha() + { + return 200; + } + + + + + const int inactiveAlpha() + { + return 100; + } + + + + + const float resPixelMultiplyer() + { + return 100; + } + + +signals: + +public slots: + +protected: + virtual void paintEvent ( QPaintEvent * event ); + virtual void mousePressEvent(QMouseEvent * event ); + virtual void mouseReleaseEvent(QMouseEvent * event); + virtual void mouseMoveEvent(QMouseEvent * event); + virtual void mouseDoubleClickEvent(QMouseEvent * event); + +private: + EqBand* m_bands; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; + + EqBand* selectNearestHandle( const int x, const int y ); + + enum MouseAction { none, drag, res } m_mouseAction; + int m_oldX, m_oldY; + + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } + + + + + inline float xPixelToFreq( int x ) + { + return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); + } + + + + + inline int gainToYPixel( float gain ) + { + return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); + } + + + + + inline float yPixelToGain( int y ) + { + return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; + } + +}; + + +#endif // EQPARAMETERWIDGET_H diff --git a/plugins/eq/eqpeekfilter.cpp b/plugins/eq/eqpeekfilter.cpp new file mode 100644 index 000000000..1cc60cf4c --- /dev/null +++ b/plugins/eq/eqpeekfilter.cpp @@ -0,0 +1,3 @@ +#include "eqpeekfilter.h" + + diff --git a/plugins/eq/eqpeekfilter.h b/plugins/eq/eqpeekfilter.h new file mode 100644 index 000000000..b28b04f64 --- /dev/null +++ b/plugins/eq/eqpeekfilter.h @@ -0,0 +1,3 @@ + + + diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h new file mode 100644 index 000000000..542ba6b52 --- /dev/null +++ b/plugins/eq/eqspectrumview.h @@ -0,0 +1,80 @@ +#ifndef EQSPECTRUMVIEW_H +#define EQSPECTRUMVIEW_H + +#include "qpainter.h" +#include "eqeffect.h" +#include "qwidget.h" + +class EqParams +{ +public: + const static int MAX_BANDS = 249; + float m_bands[MAX_BANDS]; + float m_energy; +}; + + +class EqSpectrumView : public QWidget +{ +public: + EqSpectrumView( EqParams* s, QWidget * _parent = 0) : + QWidget( _parent ), + m_sa( s ) + { + setFixedSize( 240, 116 ); + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + setAttribute( Qt::WA_OpaquePaintEvent, true ); + } + + virtual ~EqSpectrumView() + { + } +EqParams *m_sa; + virtual void paintEvent( QPaintEvent* event ) + { + const int MAX_BANDS = 249; + QPainter p( this ); + const float e = m_sa->m_energy; + if( e <= 0 ) + { + //dont draw anything + return; + } + float * b = m_sa->m_bands; + const int LOWER_Y = -60; // dB + int h; + const int fh = height(); + bool linX = true; + if( linX ) + { + for( int x = 0; x < MAX_BANDS; ++x, ++b ) + { + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; + p.drawPoint( x, fh-h ); + } + } + else + { + for( int x = 0; x < 31; ++x, ++b ) + { + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3; + p.drawPoint(x * 8, fh-h ); + } + } + } + + + +private: + + +//EqParams *m_sa; +// QImage m_backgroundPlain; +// QImage m_background; + +} ; + + +#endif // EQSPECTRUMVIEW_H diff --git a/plugins/eq/logo.png b/plugins/eq/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..89e9f3680118931dd86f065a8f6bc0b4c631a585 GIT binary patch literal 3225 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 Date: Thu, 11 Dec 2014 00:24:52 +0100 Subject: [PATCH 02/52] New SnareMarch preset and updated KickPower The snare sounds like it would be used for marching. If you can make it brighter without making it sound funny, please do... The kick was too dark for general purpose. Shortened it, raised it in the frequency range and added more noise. --- .../Kicker/{Kick power.xpf => KickPower.xpf} | 20 +-- data/presets/Kicker/SnareMarch.xpf | 161 ++++++++++++++++++ 2 files changed, 171 insertions(+), 10 deletions(-) rename data/presets/Kicker/{Kick power.xpf => KickPower.xpf} (54%) create mode 100644 data/presets/Kicker/SnareMarch.xpf diff --git a/data/presets/Kicker/Kick power.xpf b/data/presets/Kicker/KickPower.xpf similarity index 54% rename from data/presets/Kicker/Kick power.xpf rename to data/presets/Kicker/KickPower.xpf index b4e1daba3..27259ce25 100644 --- a/data/presets/Kicker/Kick power.xpf +++ b/data/presets/Kicker/KickPower.xpf @@ -1,20 +1,20 @@ - + - + - + - - - - + + + + - - - + + + diff --git a/data/presets/Kicker/SnareMarch.xpf b/data/presets/Kicker/SnareMarch.xpf new file mode 100644 index 000000000..d4d1ad0db --- /dev/null +++ b/data/presets/Kicker/SnareMarch.xpf @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9c9e9db1642fab44e8384c4a4333cbf1694cb044 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 11 Dec 2014 00:08:42 +0000 Subject: [PATCH 03/52] spectrum analysis implemented --- include/Fader.h | 6 ++ plugins/eq/CMakeLists.txt | 6 +- plugins/eq/eqcontrols.cpp | 12 ++-- plugins/eq/eqcontrols.h | 8 ++- plugins/eq/eqcontrolsdialog.cpp | 13 +++- plugins/eq/eqcontrolsdialog.h | 5 +- plugins/eq/eqeffect.cpp | 102 ++++++++++++++++++++++++++++--- plugins/eq/eqeffect.h | 63 +++++++++++-------- plugins/eq/eqparameterwidget.cpp | 6 +- plugins/eq/eqspectrumview.h | 76 +++++++++++++++++++---- src/gui/widgets/Fader.cpp | 4 +- 11 files changed, 237 insertions(+), 64 deletions(-) diff --git a/include/Fader.h b/include/Fader.h index f8048e98c..96c3ae732 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -82,6 +82,12 @@ public: { m_displayConversion = b; } + inline void setHintText( const QString & _txt_before, + const QString & _txt_after ) + { + setDescription( _txt_before ); + setUnit( _txt_after ); + } private: virtual void contextMenuEvent( QContextMenuEvent * _me ); diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index 72dc8bb5e..b537ab632 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -1,3 +1,5 @@ INCLUDE(BuildPlugin) - -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) +LINK_LIBRARIES(${FFTW3F_LIBRARIES}) +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index efb89a245..5fe6b6c32 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -35,12 +35,12 @@ EqControls::EqControls( EqEffect *effect ) : m_effect( effect ), m_inGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Input gain") ), m_outGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Output gain" ) ), - m_lowShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "Low shelf gain" ) ), - m_para1GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 1 gain" ) ), - m_para2GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 2 gain" ) ), - m_para3GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 3 gain" ) ), - m_para4GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 4 gain" ) ), - m_highShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "High Shelf gain" ) ), + m_lowShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "Low shelf gain" ) ), + m_para1GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 1 gain" ) ), + m_para2GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 2 gain" ) ), + m_para3GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 3 gain" ) ), + m_para4GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 4 gain" ) ), + m_highShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "High Shelf gain" ) ), m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), m_lowShelfResModel( 1.4,0.0, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), m_para1ResModel( 1.4 ,0.55, 10.0 , 0.001, this , tr( "Peak 1 res" ) ), diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h index f6703919e..413bf4176 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/eq/eqcontrols.h @@ -33,9 +33,9 @@ class EqEffect; class EqControls : public EffectControls { - Q_OBJECT + Q_OBJECT public: - EqControls( EqEffect* effect ); + explicit EqControls( EqEffect* effect ); virtual ~EqControls() { } @@ -51,7 +51,6 @@ public: } virtual EffectControlDialog* createView() { - printf("create dialog\n"); return new EqControlsDialog( this ); } @@ -66,6 +65,9 @@ public: float m_para4PeakL, m_para4PeakR; float m_highShelfPeakL, m_highShelfPeakR; + FftBands m_inFftBands; + FftBands m_outFftBands; + diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 704e81f00..1527f59fd 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -33,6 +33,7 @@ #include "QWidget" #include "MainWindow.h" #include "LedCheckbox.h" +//#include "eqspectrumview.h" @@ -48,9 +49,14 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : setPalette( pal ); setFixedSize( 350, 275 ); + m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); + m_inSpec->move( 50, 5 ); + m_inSpec->color = QColor( 200, 200, 100, 100 ); + m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); + m_outSpec->move( 50, 5 ); + m_outSpec->color = QColor(100, 200, 200, 100); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); -// m_inSpec = new EqSpectrumView( controls->m_effect, this); setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); @@ -66,6 +72,7 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); m_inGainFader->move( 10, 5 ); + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); m_outGainFader->move( 315, 5 ); //gain faders @@ -78,6 +85,8 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_gainFader->move( cw * i + fo , 123 ); m_gainFader->setMinimumHeight(80); m_gainFader->resize(m_gainFader->width() , 80); + m_gainFader->setDisplayConversion( false ); + m_gainFader->setHintText( tr( "Gain") , "dB"); } for( int i = 0; i < m_parameterWidget->bandCount() ; i++) @@ -86,11 +95,13 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_resKnob->move(cw * i + ko , 205 ); m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); + m_resKnob->setHintText( tr( "Resonance:") , ""); m_freqKnob = new Knob( knobBright_26, this ); m_freqKnob->move(cw * i + ko, 235 ); m_freqKnob->setVolumeKnob( false ); m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); + m_freqKnob->setHintText( tr( "Frequency:" ), "Hz" ); m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); m_activeBox->move( cw * i + fo + 3, 260 ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index bb4c0cfad..fe1326231 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -32,7 +32,7 @@ #include "eqparameterwidget.h" #include "MainWindow.h" #include "qpushbutton.h" -//#include "eqspectrumview.h" +#include "eqspectrumview.h" class EqControls; @@ -69,7 +69,8 @@ private: LedCheckBox* m_analyzeBox; EqParameterWidget* m_parameterWidget; -// EqSpectrumView* m_inSpec; + EqSpectrumView* m_inSpec; + EqSpectrumView* m_outSpec; virtual void mouseDoubleClickEvent(QMouseEvent *event); diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 20be28000..3808f8bf6 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -34,7 +34,7 @@ //re write to store data from each filter(models,name, storeage name ) in a class, stored in array //then just loop ever array for each section -const int MAX_BANDS = 249; + extern "C" { @@ -58,16 +58,13 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) - - - { - m_dFilterCount = 4; + m_dFilterCount = 10; m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(20000); - m_downsampleFilters[i].setQ(0.66); + m_downsampleFilters[i].setFrequency(22000); + m_downsampleFilters[i].setQ(0.85); m_downsampleFilters[i].setGain(0); m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); } @@ -99,6 +96,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) sample_t dryS[2]; sampleFrame m_inPeak; + analyze( buf, frames, &m_eqControls.m_inFftBands) ; //TODO UPSAMPLE upsample( buf, frames ); @@ -236,6 +234,8 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); + analyze( buf, frames, &m_eqControls.m_outFftBands) ; + setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); return isRunning(); } @@ -298,6 +298,94 @@ void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) } } +void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, FftBands* fft) +{ + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + fft->m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + fft->m_buffer[fft->m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++fft->m_framesFilledUp; + } + + if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) + { + return; + } + + fft->m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = fft->m_sr / 2; + + fftwf_execute( fft->m_fftPlan ); + absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); + fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); + fft->m_framesFilledUp = 0; +} + +float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) +{ + float peak = -60; + float * b = fft->m_bands; + float h = 0; + for(int x = 0; x < MAX_BANDS; x++, b++) + { + if( bandToFreq( x ,sr) >= minF && bandToFreq( x,sr ) <= maxF ) + { + h = 20*( log10( *b / fft->m_energy ) ); + peak = h > peak ? h : peak; + } + } + return (peak+100)/100; +} + +void EqEffect::setBandPeaks(FftBands *fft, int samplerate ) +{ + m_eqControls.m_lowShelfPeakR = m_eqControls.m_lowShelfPeakL = + peakBand( 0, + m_eqControls.m_lowShelfFreqModel.value(), fft , samplerate ); + + m_eqControls.m_para1PeakL = m_eqControls.m_para1PeakR = + peakBand( m_eqControls.m_para1FreqModel.value() + - (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), + m_eqControls.m_para1FreqModel.value() + + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = + peakBand( m_eqControls.m_para2FreqModel.value() + - (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + m_eqControls.m_para2FreqModel.value() + + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = + peakBand( m_eqControls.m_para3FreqModel.value() + - (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + m_eqControls.m_para3FreqModel.value() + + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = + peakBand( m_eqControls.m_para4FreqModel.value() + - (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + m_eqControls.m_para4FreqModel.value() + + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = + peakBand( m_eqControls.m_highShelfFreqModel.value(), + samplerate * 0.5 , fft, samplerate ); +} + diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 987eac9c4..0ea793351 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -31,49 +31,60 @@ #include "BasicFilters.h" #include "eqfilter.h" + + class EqEffect : public Effect { public: - EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); - virtual ~EqEffect(); - virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); - virtual EffectControls* controls() - { - return &m_eqControls; - } - void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); + EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~EqEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_eqControls; + } + void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); private: - EqControls m_eqControls; + EqControls m_eqControls; - EqHp12Filter m_hp12; - EqHp12Filter m_hp24; - EqHp12Filter m_hp480; - EqHp12Filter m_hp481; + EqHp12Filter m_hp12; + EqHp12Filter m_hp24; + EqHp12Filter m_hp480; + EqHp12Filter m_hp481; - EqLowShelfFilter m_lowShelf; + EqLowShelfFilter m_lowShelf; - EqPeakFilter m_para1; - EqPeakFilter m_para2; - EqPeakFilter m_para3; - EqPeakFilter m_para4; + EqPeakFilter m_para1; + EqPeakFilter m_para2; + EqPeakFilter m_para3; + EqPeakFilter m_para4; - EqHighShelfFilter m_highShelf; + EqHighShelfFilter m_highShelf; - EqLp12Filter m_lp12; - EqLp12Filter m_lp24; - EqLp12Filter m_lp480; - EqLp12Filter m_lp481; + EqLp12Filter m_lp12; + EqLp12Filter m_lp24; + EqLp12Filter m_lp480; + EqLp12Filter m_lp481; EqLp12Filter* m_downsampleFilters; int m_dFilterCount; sampleFrame* m_upBuf; fpp_t m_upBufFrames; -// const static int MAX_BANDS = 249; + // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); -// float m_bands[MAX_BANDS]; -// float m_energy; + void analyze( sampleFrame *buf, const fpp_t frames, FftBands* fft ); + float peakBand(float minF, float maxF,FftBands*, int); + + inline float bandToFreq ( int index , int sampleRate ) + { + return index * sampleRate / (MAX_BANDS * 2); + } + + void setBandPeaks( FftBands *fft , int); + // float m_bands[MAX_BANDS]; + // float m_energy; }; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index eb329460b..39e1ab94f 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -40,7 +40,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); float totalLength = log10( 21000 ); m_pixelsPerUnitWidth = width( ) / totalLength ; - float totalHeight = 40; + float totalHeight = 80; m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); @@ -63,7 +63,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; float gain = 1; @@ -75,7 +75,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); if(i == 0 || i == bandCount() - 1 ){ painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 542ba6b52..ca63ff27d 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -2,38 +2,70 @@ #define EQSPECTRUMVIEW_H #include "qpainter.h" -#include "eqeffect.h" +//#include "eqeffect.h" #include "qwidget.h" +#include "fft_helpers.h" -class EqParams + +const int MAX_BANDS = 512; + +class FftBands { public: - const static int MAX_BANDS = 249; + + fftwf_plan m_fftPlan; + + fftwf_complex * m_specBuf; + float m_absSpecBuf[FFT_BUFFER_SIZE+1]; + float m_buffer[FFT_BUFFER_SIZE*2]; + int m_framesFilledUp; + float m_bands[MAX_BANDS]; float m_energy; + int m_sr; + + FftBands() : + m_framesFilledUp( 0 ), + m_energy( 0 ) + { + memset( m_buffer, 0, sizeof( m_buffer ) ); + + m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); + m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); + } }; class EqSpectrumView : public QWidget { + public: - EqSpectrumView( EqParams* s, QWidget * _parent = 0) : + explicit EqSpectrumView( FftBands * b, QWidget * _parent = 0) : QWidget( _parent ), - m_sa( s ) + m_sa( b ) { - setFixedSize( 240, 116 ); + setFixedSize( 250, 116 ); connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - setAttribute( Qt::WA_OpaquePaintEvent, true ); + setAttribute( Qt::WA_TranslucentBackground, true ); + m_skipBands = MAX_BANDS * 0.5; + float totalLength = log10( 21000); + m_pixelsPerUnitWidth = width( ) / totalLength ; + m_scale = 1.5; + color = QColor( 255, 255, 255, 255 ); + } virtual ~EqSpectrumView() { } -EqParams *m_sa; + QColor color; + FftBands *m_sa; + int m_lastY; virtual void paintEvent( QPaintEvent* event ) { - const int MAX_BANDS = 249; + int m_lastY = height(); QPainter p( this ); + p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); const float e = m_sa->m_energy; if( e <= 0 ) { @@ -51,7 +83,8 @@ EqParams *m_sa; { h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); if( h < 0 ) h = 0; else if( h >= fh ) continue; - p.drawPoint( x, fh-h ); + p.drawLine(freqToXPixel(bandToFreq(x -1 ) ),m_lastY, freqToXPixel(bandToFreq(x ) ), fh-h ); + m_lastY = fh-h; } } else @@ -67,12 +100,29 @@ EqParams *m_sa; + + inline int bandToXPixel( float band ) + { + return ( log10( band - m_skipBands ) * m_pixelsPerUnitWidth * m_scale ); + } + + inline float bandToFreq ( int index ) + { + return index * m_sa->m_sr / (MAX_BANDS * 2); + } + + + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } private: + float m_pixelsPerUnitWidth; + float m_scale; + int m_skipBands; + -//EqParams *m_sa; -// QImage m_backgroundPlain; -// QImage m_background; } ; diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 7f1352a57..1af7bb040 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -107,6 +107,7 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : setMaximumSize( 23, 116); resize( 23, 116 ); setModel( _model ); + setHintText( "Volume:","%"); } @@ -141,6 +142,7 @@ Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixma setMaximumSize( m_back->width(), m_back->height() ); resize( m_back->width(), m_back->height() ); setModel( model ); + setHintText( "Volume:","%"); } @@ -324,7 +326,7 @@ void Fader::updateTextFloat() } else { - s_textFloat->setText( QString("Volume: %1 %").arg( m_displayConversion ? m_model->value() * 100 : m_model->value() ) ); + s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? m_model->value() * 100 : m_model->value() ) + " " + m_unit ); } s_textFloat->moveGlobal( this, QPoint( width() - ( *m_knob ).width() - 5, knobPosY() - 46 ) ); } From a7831a7ccab1ec0537bfb520b72b4eb0efa84e3b Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 11 Dec 2014 16:52:06 +0000 Subject: [PATCH 04/52] Draw grid lines on widget --- plugins/eq/eqparameterwidget.cpp | 25 ++++++++++++++++++++++--- plugins/eq/eqparameterwidget.h | 2 ++ plugins/eq/eqspectrumview.h | 8 ++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 39e1ab94f..f1d53fa2e 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -44,6 +44,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + } @@ -59,9 +60,27 @@ EqParameterWidget::~EqParameterWidget() void EqParameterWidget::paintEvent( QPaintEvent *event ) { - QPainter painter( this ); + QPainter painter( this ); + //Draw Frequecy maker lines + painter.setPen( QPen( QColor( 100, 100, 100, 200 ), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + for( int x = 20 ; x < 100; x += 10) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + for( int x = 100 ; x < 1000; x += 100) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + for( int x = 1000 ; x < 11000; x += 1000) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + //draw 0dB line + painter.drawLine(0, gainToYPixel( 0 ) , width(), gainToYPixel( 0 ) ); + for( int i = 0 ; i < bandCount() ; i++ ) { + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); @@ -71,11 +90,11 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) { gain = m_bands[i].gain->value(); } - y = gainToYPixel( gain ) + 3; + y = gainToYPixel( gain ); float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if(i == 0 || i == bandCount() - 1 ){ painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index cb14154b3..766efad60 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -122,6 +122,8 @@ private: enum MouseAction { none, drag, res } m_mouseAction; int m_oldX, m_oldY; + int *m_xGridBands; + inline int freqToXPixel( float freq ) { diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index ca63ff27d..47490afa3 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -63,9 +63,15 @@ public: int m_lastY; virtual void paintEvent( QPaintEvent* event ) { + const int fh = height(); + const int LOWER_Y = -96; // dB int m_lastY = height(); QPainter p( this ); p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + //draw grid lines + int y33 = (int)( fh * 2.0 / 3.0 * (20*(log10( 0.33 ) ) - LOWER_Y ) / (-LOWER_Y ) ); + p.drawLine(0, y33, width(), y33); + const float e = m_sa->m_energy; if( e <= 0 ) { @@ -73,9 +79,7 @@ public: return; } float * b = m_sa->m_bands; - const int LOWER_Y = -60; // dB int h; - const int fh = height(); bool linX = true; if( linX ) { From 08821a7290ef64ae1dbbb07cb0036cee92381b0b Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 15:55:28 +0000 Subject: [PATCH 05/52] EQ update filters before downsampling changed ignore wet dry knob scale lines on widget filter type switch loading correctly analyser code all move to one location implment analyze button remove redundant code analyser display omprovments --- plugins/eq/eqcontrols.cpp | 15 +- plugins/eq/eqcontrols.h | 135 +++++----- plugins/eq/eqcontrolsdialog.cpp | 35 +-- plugins/eq/eqcontrolsdialog.h | 69 +++--- plugins/eq/eqeffect.cpp | 122 +++++---- plugins/eq/eqeffect.h | 8 +- plugins/eq/eqfader.h | 14 +- plugins/eq/eqfilter.h | 412 +++++++++++++++++-------------- plugins/eq/eqparameterwidget.cpp | 203 +++++++-------- plugins/eq/eqspectrumview.h | 148 ++++++++--- 10 files changed, 646 insertions(+), 515 deletions(-) diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index 5fe6b6c32..cb0300382 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -65,14 +65,15 @@ EqControls::EqControls( EqEffect *effect ) : m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), m_lpActiveModel( false, this , tr( "LP active" ) ), - m_lp12Model( true, this , tr( "LP 12" ) ), + m_lp12Model( false, this , tr( "LP 12" ) ), m_lp24Model( false, this , tr( "LP 24" ) ), m_lp48Model( false, this , tr( "LP 48" ) ), - m_hp12Model( true, this , tr( "HP 12" ) ), + m_hp12Model( false, this , tr( "HP 12" ) ), m_hp24Model( false, this , tr( "HP 24" ) ), m_hp48Model( false, this , tr( "HP 48" ) ), - m_analyzeModel( true, this , tr( "Analyze enable" ) ) - + m_analyzeModel( true, this , tr( "Analyze enable" ) ), + m_lpTypeModel(0,0,2,this, tr( "low pass type") ) , + m_hpTypeModel(0,0,2,this, tr( "high pass type") ) { m_hpFeqModel.setScaleLogarithmic( true ); m_lowShelfFreqModel.setScaleLogarithmic( true ); @@ -92,6 +93,7 @@ EqControls::EqControls( EqEffect *effect ) : m_para3PeakL = 0; m_para3PeakR = 0; m_para4PeakL = 0; m_para4PeakR = 0; m_highShelfPeakL = 0; m_highShelfPeakR = 0; + m_inProgress = false; } @@ -144,6 +146,9 @@ void EqControls::loadSettings( const QDomElement &_this ) m_hp24Model.loadSettings( _this , "HP24" ); m_hp48Model.loadSettings( _this , "HP48" ); m_analyzeModel.loadSettings( _this, "Analyzeenable"); + + m_lpTypeModel.loadSettings( _this, "LP" ); + m_hpTypeModel.loadSettings( _this, "HP" ); } @@ -197,5 +202,7 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_hp48Model.saveSettings( doc, parent, "HP48" ); m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); + m_lpTypeModel.saveSettings( doc, parent, "LP" ); + m_hpTypeModel.saveSettings( doc, parent, "HP" ); } diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h index 413bf4176..f3646cce6 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/eq/eqcontrols.h @@ -36,28 +36,28 @@ class EqControls : public EffectControls Q_OBJECT public: explicit EqControls( EqEffect* effect ); - virtual ~EqControls() - { - } - virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); - virtual void loadSettings ( const QDomElement &_this ); - inline virtual QString nodeName() const - { - return "Eq"; - } - virtual int controlCount() - { - return 39; - } - virtual EffectControlDialog* createView() - { - return new EqControlsDialog( this ); - } + virtual ~EqControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Eq"; + } + virtual int controlCount() + { + return 39; + } + virtual EffectControlDialog* createView() + { + return new EqControlsDialog( this ); + } - float m_inPeakL; - float m_inPeakR; - float m_outPeakL; - float m_outPeakR; + float m_inPeakL; + float m_inPeakR; + float m_outPeakL; + float m_outPeakR; float m_lowShelfPeakL, m_lowShelfPeakR; float m_para1PeakL, m_para1PeakR; float m_para2PeakL, m_para2PeakR; @@ -65,64 +65,69 @@ public: float m_para4PeakL, m_para4PeakR; float m_highShelfPeakL, m_highShelfPeakR; - FftBands m_inFftBands; - FftBands m_outFftBands; + EqAnalyser m_inFftBands; + EqAnalyser m_outFftBands; + + bool m_inProgress; private: - EqEffect* m_effect; + EqEffect* m_effect; - FloatModel m_inGainModel; - FloatModel m_outGainModel; - FloatModel m_lowShelfGainModel; - FloatModel m_para1GainModel; - FloatModel m_para2GainModel; - FloatModel m_para3GainModel; - FloatModel m_para4GainModel; - FloatModel m_highShelfGainModel; + FloatModel m_inGainModel; + FloatModel m_outGainModel; + FloatModel m_lowShelfGainModel; + FloatModel m_para1GainModel; + FloatModel m_para2GainModel; + FloatModel m_para3GainModel; + FloatModel m_para4GainModel; + FloatModel m_highShelfGainModel; - FloatModel m_hpResModel; - FloatModel m_lowShelfResModel; - FloatModel m_para1ResModel; - FloatModel m_para2ResModel; - FloatModel m_para3ResModel; - FloatModel m_para4ResModel; - FloatModel m_highShelfResModel; - FloatModel m_lpResModel; + FloatModel m_hpResModel; + FloatModel m_lowShelfResModel; + FloatModel m_para1ResModel; + FloatModel m_para2ResModel; + FloatModel m_para3ResModel; + FloatModel m_para4ResModel; + FloatModel m_highShelfResModel; + FloatModel m_lpResModel; - FloatModel m_hpFeqModel; - FloatModel m_lowShelfFreqModel; - FloatModel m_para1FreqModel; - FloatModel m_para2FreqModel; - FloatModel m_para3FreqModel; - FloatModel m_para4FreqModel; - FloatModel m_highShelfFreqModel; - FloatModel m_lpFreqModel; + FloatModel m_hpFeqModel; + FloatModel m_lowShelfFreqModel; + FloatModel m_para1FreqModel; + FloatModel m_para2FreqModel; + FloatModel m_para3FreqModel; + FloatModel m_para4FreqModel; + FloatModel m_highShelfFreqModel; + FloatModel m_lpFreqModel; - BoolModel m_hpActiveModel; - BoolModel m_lowShelfActiveModel; - BoolModel m_para1ActiveModel; - BoolModel m_para2ActiveModel; - BoolModel m_para3ActiveModel; - BoolModel m_para4ActiveModel; - BoolModel m_highShelfActiveModel; - BoolModel m_lpActiveModel; + BoolModel m_hpActiveModel; + BoolModel m_lowShelfActiveModel; + BoolModel m_para1ActiveModel; + BoolModel m_para2ActiveModel; + BoolModel m_para3ActiveModel; + BoolModel m_para4ActiveModel; + BoolModel m_highShelfActiveModel; + BoolModel m_lpActiveModel; - BoolModel m_lp12Model; - BoolModel m_lp24Model; - BoolModel m_lp48Model; + BoolModel m_lp12Model; + BoolModel m_lp24Model; + BoolModel m_lp48Model; - BoolModel m_hp12Model; - BoolModel m_hp24Model; - BoolModel m_hp48Model; + BoolModel m_hp12Model; + BoolModel m_hp24Model; + BoolModel m_hp48Model; - BoolModel m_analyzeModel; + BoolModel m_analyzeModel; - friend class EqControlsDialog; - friend class EqEffect; + IntModel m_lpTypeModel; + IntModel m_hpTypeModel; + + friend class EqControlsDialog; + friend class EqEffect; }; diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 1527f59fd..f6424788a 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,10 +51,10 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 200, 200, 100, 100 ); + m_inSpec->color = QColor( 255, 0, 255, 80 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(100, 200, 200, 100); + m_outSpec->color = QColor(00, 255, 255, 80); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); @@ -134,31 +134,16 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_lp48Box->setModel( &controls->m_lp48Model ); automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); - lpBtnGrp->addButton(m_lp12Box); - lpBtnGrp->addButton(m_lp24Box ); - lpBtnGrp->addButton(m_lp48Box ); - connect( m_lp12Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( m_lp24Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( m_lp48Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( &controls->m_lp12Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_lp24Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_lp48Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - - + lpBtnGrp->addButton( m_lp12Box); + lpBtnGrp->addButton( m_lp24Box ); + lpBtnGrp->addButton( m_lp48Box ); + lpBtnGrp->setModel( &m_controls->m_lpTypeModel, false); automatableButtonGroup *hpBtnGrp = new automatableButtonGroup( this, tr( "hp grp" ) ); - hpBtnGrp->addButton(m_hp12Box ); - hpBtnGrp->addButton(m_hp24Box ); - hpBtnGrp->addButton(m_hp48Box ); - connect( m_hp12Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( m_hp24Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( m_hp48Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp12Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp24Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp48Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - - - + hpBtnGrp->addButton( m_hp12Box ); + hpBtnGrp->addButton( m_hp24Box ); + hpBtnGrp->addButton( m_hp48Box ); + hpBtnGrp->setModel( &m_controls->m_hpTypeModel,false); //Analize Box m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index fe1326231..fd8327dce 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -33,6 +33,7 @@ #include "MainWindow.h" #include "qpushbutton.h" #include "eqspectrumview.h" +#include "qlist.h" class EqControls; @@ -40,56 +41,56 @@ class EqControlsDialog : public EffectControlDialog { public: - EqControlsDialog( EqControls* controls ); - virtual ~EqControlsDialog() - { - } + EqControlsDialog( EqControls* controls ); + virtual ~EqControlsDialog() + { + } - EqBand * setBand(EqControls *controls); + EqBand * setBand(EqControls *controls); private slots: - void updateVuMeters(); + void updateVuMeters(); private: - EqControls * m_controls; + EqControls * m_controls; - Fader* m_inGainFader; - Fader* m_outGainFader; - Fader* m_gainFader; - Knob* m_resKnob; - Knob* m_freqKnob; - LedCheckBox* m_activeBox; - LedCheckBox* m_lp12Box; - LedCheckBox* m_lp24Box; - LedCheckBox* m_lp48Box; - LedCheckBox* m_hp12Box; - LedCheckBox* m_hp24Box; - LedCheckBox* m_hp48Box; + Fader* m_inGainFader; + Fader* m_outGainFader; + Fader* m_gainFader; + Knob* m_resKnob; + Knob* m_freqKnob; + LedCheckBox* m_activeBox; + LedCheckBox* m_lp12Box; + LedCheckBox* m_lp24Box; + LedCheckBox* m_lp48Box; + LedCheckBox* m_hp12Box; + LedCheckBox* m_hp24Box; + LedCheckBox* m_hp48Box; - LedCheckBox* m_analyzeBox; + LedCheckBox* m_analyzeBox; - EqParameterWidget* m_parameterWidget; + EqParameterWidget* m_parameterWidget; EqSpectrumView* m_inSpec; EqSpectrumView* m_outSpec; - virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); EqBand* setBand( int index, BoolModel* active, FloatModel* freq, FloatModel* res, FloatModel* gain, QColor color, QString name, float* peakL, float* peakR) - { - EqBand* filterModels = m_parameterWidget->getBandModels( index ); - filterModels->active = active; - filterModels->freq = freq; - filterModels->res = res; - filterModels->color = color; - filterModels->gain = gain; + { + EqBand* filterModels = m_parameterWidget->getBandModels( index ); + filterModels->active = active; + filterModels->freq = freq; + filterModels->res = res; + filterModels->color = color; + filterModels->gain = gain; filterModels->peakL = peakL; filterModels->peakR = peakR; - return filterModels; - } - - int m_originalHeight; - + return filterModels; + } + int m_originalHeight; }; + + #endif // EQCONTROLSDIALOG_H diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 3808f8bf6..9c52d688b 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -59,14 +59,12 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 10; - m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; + m_dFilterCount = 20; + m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(22000); - m_downsampleFilters[i].setQ(0.85); - m_downsampleFilters[i].setGain(0); - m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); + m_downsampleFilters[i].setFrequency(21500); + m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; } @@ -88,15 +86,21 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { return( false ); } + m_eqControls.m_inProgress = true; double outSum = 0.0; - const float d = dryLevel(); - const float w = wetLevel(); const float outGain = m_eqControls.m_outGainModel.value(); const int sampleRate = Engine::mixer()->processingSampleRate() * 2; - sample_t dryS[2]; - sampleFrame m_inPeak; + sampleFrame m_inPeak = { 0, 0 }; + + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_inFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_inFftBands.clear(); + } - analyze( buf, frames, &m_eqControls.m_inFftBands) ; //TODO UPSAMPLE upsample( buf, frames ); @@ -214,7 +218,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) } } - sampleFrame outPeak; + sampleFrame outPeak = { 0, 0 }; gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; @@ -229,14 +233,20 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) for( fpp_t f = 0; f < frames; ++f ) { - buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); - buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); - analyze( buf, frames, &m_eqControls.m_outFftBands) ; - setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_outFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_outFftBands.clear(); + } + setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + m_eqControls.m_inProgress = false; return isRunning(); } @@ -259,8 +269,8 @@ void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFra { peak[0][1] = fabs( buf[f][0] ); } - } + } } sampleFrame m_lastUpFrame; @@ -288,54 +298,60 @@ void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) m_lastUpFrame[1] = buf[f][1]; } } +} void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) { for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { - buf[f][0] = m_upBuf[f2][0]; - buf[f][1] = m_upBuf[f2][1]; + buf[f][0] = m_upBuf[f2+1][0]; + buf[f][1] = m_upBuf[f2+1][1]; } } -void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, FftBands* fft) +void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, EqAnalyser* fft) { - const int FFT_BUFFER_SIZE = 2048; - fpp_t f = 0; - if( frames > FFT_BUFFER_SIZE ) + if(m_eqControls.m_analyzeModel.value() ) { + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + fft->m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + fft->m_buffer[fft->m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++fft->m_framesFilledUp; + } + + if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) + { + return; + } + + fft->m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = fft->m_sr / 2; + + fftwf_execute( fft->m_fftPlan ); + absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); + fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); fft->m_framesFilledUp = 0; - f = frames - FFT_BUFFER_SIZE; + } - // meger channels - for( ; f < frames; ++f ) - { - fft->m_buffer[fft->m_framesFilledUp] = - ( buf[f][0] + buf[f][1] ) * 0.5; - ++fft->m_framesFilledUp; - } - - if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) - { - return; - } - - fft->m_sr = Engine::mixer()->processingSampleRate(); - const int LOWEST_FREQ = 0; - const int HIGHEST_FREQ = fft->m_sr / 2; - - fftwf_execute( fft->m_fftPlan ); - absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); - - compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, - MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); - fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); - fft->m_framesFilledUp = 0; } -float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) + +float EqEffect::peakBand(float minF, float maxF, EqAnalyser *fft, int sr) { float peak = -60; float * b = fft->m_bands; @@ -351,7 +367,7 @@ float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) return (peak+100)/100; } -void EqEffect::setBandPeaks(FftBands *fft, int samplerate ) +void EqEffect::setBandPeaks(EqAnalyser *fft, int samplerate ) { m_eqControls.m_lowShelfPeakR = m_eqControls.m_lowShelfPeakL = peakBand( 0, @@ -399,4 +415,4 @@ Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) return new EqEffect( parent , static_cast( data ) ); } -}} +} diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 0ea793351..1fc96c077 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -66,7 +66,7 @@ private: EqLp12Filter m_lp24; EqLp12Filter m_lp480; EqLp12Filter m_lp481; - EqLp12Filter* m_downsampleFilters; + EqLinkwitzRiley* m_downsampleFilters; int m_dFilterCount; sampleFrame* m_upBuf; fpp_t m_upBufFrames; @@ -74,15 +74,15 @@ private: // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); - void analyze( sampleFrame *buf, const fpp_t frames, FftBands* fft ); - float peakBand(float minF, float maxF,FftBands*, int); + void analyze( sampleFrame *buf, const fpp_t frames, EqAnalyser* fft ); + float peakBand(float minF, float maxF,EqAnalyser*, int); inline float bandToFreq ( int index , int sampleRate ) { return index * sampleRate / (MAX_BANDS * 2); } - void setBandPeaks( FftBands *fft , int); + void setBandPeaks( EqAnalyser *fft , int); // float m_bands[MAX_BANDS]; // float m_energy; diff --git a/plugins/eq/eqfader.h b/plugins/eq/eqfader.h index 65c7a55a0..1057b60c0 100644 --- a/plugins/eq/eqfader.h +++ b/plugins/eq/eqfader.h @@ -47,14 +47,6 @@ public: m_lPeak = lPeak; m_rPeak = rPeak; connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); - m_text = 0; - QList tfs = Engine::mainWindow()->findChildren(); - m_text = tfs.last(); - if(m_text) - { - printf("found text float\n"); - } - connect(model , SIGNAL (dataChanged()) , this, SLOT (updateText () )); m_model = model; } @@ -95,16 +87,12 @@ private slots: update(); } - void updateText() - { - m_text->setText(QString()+m_model->value()); - } + private: float* m_lPeak; float* m_rPeak; - TextFloat* m_text; FloatModel* m_model; }; diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h index 69ee49c8b..e6924ca4c 100644 --- a/plugins/eq/eqfilter.h +++ b/plugins/eq/eqfilter.h @@ -38,74 +38,74 @@ class EqFilter : public StereoBiQuad { public: - EqFilter() - { + EqFilter() + { - } + } - virtual inline void setSampleRate( int sampleRate ) - { - if( sampleRate != m_sampleRate ) - { - m_sampleRate = sampleRate; - calcCoefficents(); - } - } + virtual inline void setSampleRate( int sampleRate ) + { + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + calcCoefficents(); + } + } - virtual inline void setFrequency( float freq ){ - if ( freq != m_freq ) - { - m_freq = freq; - calcCoefficents(); - } - } + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + calcCoefficents(); + } + } - virtual void setQ( float res ) - { - if ( res != m_res ) - { - m_res = res; - calcCoefficents(); - } - } + virtual void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } - virtual void setGain( float gain ) - { - if ( gain != m_gain ) - { - m_gain = gain; - calcCoefficents(); - } - } + virtual void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } - /// - /// \brief processBuffer - /// \param buf Audio Buffer - /// \param frames Count of sampleFrames in Audio Buffer - /// - virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) - { - for ( fpp_t f = 0 ; f < frames ; ++f) - { - buf[f][0] = update( buf[f][0] , 0); - buf[f][1] = update( buf[f][1] , 1); - } - } + /// + /// \brief processBuffer + /// \param buf Audio Buffer + /// \param frames Count of sampleFrames in Audio Buffer + /// + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } protected: - /// - /// \brief calcCoefficents - /// Override this in child classes to provide the coefficents, based on - /// Freq, Res and Gain - virtual void calcCoefficents(){ - setCoeffs( 0, 0, 0, 0, 0 ); + /// + /// \brief calcCoefficents + /// Override this in child classes to provide the coefficents, based on + /// Freq, Res and Gain + virtual void calcCoefficents(){ + setCoeffs( 0, 0, 0, 0, 0 ); - } + } - float m_sampleRate; - float m_freq; - float m_res; - float m_gain; + float m_sampleRate; + float m_freq; + float m_res; + float m_gain; }; @@ -118,38 +118,38 @@ protected: class EqHp12Filter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float alpha = s / ( 2 * m_res ); + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 + c ) * 0.5; - b1 = ( -( 1 + c ) ); - b2 = ( 1 + c ) * 0.5; - a0 = 1 + alpha; - a1 = ( -2 * c ); - a2 = 1 - alpha; + //calc coefficents + b0 = ( 1 + c ) * 0.5; + b1 = ( -( 1 + c ) ); + b2 = ( 1 + c ) * 0.5; + a0 = 1 + alpha; + a1 = ( -2 * c ); + a2 = 1 - alpha; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); + setCoeffs( a1, a2, b0, b1, b2 ); - } + } }; @@ -161,36 +161,36 @@ public : class EqLp12Filter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float alpha = s / ( 2 * m_res ); + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 - c ) * 0.5; - b1 = 1 - c; - b2 = ( 1 - c ) * 0.5; - a0 = 1 + alpha; - a1 = -2 * c; - a2 = 1 - alpha; + //calc coefficents + b0 = ( 1 - c ) * 0.5; + b1 = 1 - c; + b2 = ( 1 - c ) * 0.5; + a0 = 1 + alpha; + a1 = -2 * c; + a2 = 1 - alpha; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } + setCoeffs( a1, a2, b0, b1, b2 ); + } }; @@ -205,108 +205,156 @@ class EqPeakFilter : public EqFilter public: - virtual void calcCoefficents() - { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025); - float alpha = s / ( 2 * m_res ); + virtual void calcCoefficents() + { + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = 1 + alpha*A; - b1 = -2*c; - b2 = 1 - alpha*A; - a0 = 1 + alpha/A; - a1 = -2*c; - a2 = 1 - alpha/A; + //calc coefficents + b0 = 1 + alpha*A; + b1 = -2*c; + b2 = 1 - alpha*A; + a0 = 1 + alpha/A; + a1 = -2*c; + a2 = 1 - alpha/A; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; - a0 = 1; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } + setCoeffs( a1, a2, b0, b1, b2 ); + } }; class EqLowShelfFilter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025); -// float alpha = s / ( 2 * m_res ); - float beta = sqrt( A ) / m_res; + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + // float alpha = s / ( 2 * m_res ); + float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); - b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; - b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; - a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); - a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + //calc coefficents + b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; + b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); + a2 = ( A + 1 ) + ( A - 1) * c - beta * s; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); + setCoeffs( a1, a2, b0, b1, b2 ); - } + } }; class EqHighShelfFilter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025 ); - float beta = sqrt( A ) / m_res; + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025 ); + float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); + a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + +class EqLinkwitzRiley : public StereoLinkwitzRiley +{ +public: + EqLinkwitzRiley() : + StereoLinkwitzRiley( 44100) + { + } + + virtual inline void setSR( int sampleRate ) + { + if( sampleRate != m_sr ) + { + m_sr = sampleRate; + setLowpass(m_freq); + setSampleRate( sampleRate ); + } + } + + + + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + setLowpass(m_freq); + } + } + + + + + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } +protected: + + float m_freq; + int m_sr; - //calc coefficents - b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); - b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); - b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; - a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); - a2 = ( A + 1) - ( A - 1 ) * c - beta * s; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; - a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } }; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index f1d53fa2e..059555108 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -31,19 +31,22 @@ #include "QMouseEvent" EqParameterWidget::EqParameterWidget( QWidget *parent ) : - QWidget( parent ), - m_bands ( 0 ), - m_selectedBand ( 0 ) + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) { - m_bands = new EqBand[8]; - resize( 250, 116 ); - connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - float totalLength = log10( 21000 ); - m_pixelsPerUnitWidth = width( ) / totalLength ; + m_bands = new EqBand[8]; + resize( 250, 116 ); + // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(200); + float totalLength = log10( 21000 ); + m_pixelsPerUnitWidth = width( ) / totalLength ; float totalHeight = 80; - m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); - m_scale = 1.5; - m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); + m_scale = 1.5; + m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); } @@ -78,39 +81,39 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) //draw 0dB line painter.drawLine(0, gainToYPixel( 0 ) , width(), gainToYPixel( 0 ) ); - for( int i = 0 ; i < bandCount() ; i++ ) - { + for( int i = 0 ; i < bandCount() ; i++ ) + { - m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - float x = freqToXPixel( m_bands[i].freq->value() ); - float y = height() * 0.5; - float gain = 1; - if( m_bands[i].gain ) - { - gain = m_bands[i].gain->value(); - } + float x = freqToXPixel( m_bands[i].freq->value() ); + float y = height() * 0.5; + float gain = 1; + if( m_bands[i].gain ) + { + gain = m_bands[i].gain->value(); + } y = gainToYPixel( gain ); - float bw = m_bands[i].freq->value() / m_bands[i].res->value(); - m_bands[i].x = x; m_bands[i].y = y; - painter.drawPoint( x, y ); + float bw = m_bands[i].freq->value() / m_bands[i].res->value(); + m_bands[i].x = x; m_bands[i].y = y; + painter.drawPoint( x, y ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); - if(i == 0 || i == bandCount() - 1 ){ - painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); - } - else - { - painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); - } - } - //Draw color band - int sectionLength = width() / bandCount(); - for( int i = 0; i < bandCount(); i++) - { - m_bands[i].color.setAlpha( 255 ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); - } + if(i == 0 || i == bandCount() - 1 ){ + painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + } + else + { + painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); + } + } + //Draw color band + int sectionLength = width() / bandCount(); + for( int i = 0; i < bandCount(); i++) + { + m_bands[i].color.setAlpha( 255 ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + } } @@ -118,11 +121,11 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) void EqParameterWidget::mousePressEvent( QMouseEvent *event ) { - m_oldX = event->x(); m_oldY = event->y(); - m_selectedBand = selectNearestHandle( event->x(), event->y() ); - m_mouseAction = none; - if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; - if ( event->button() == Qt::RightButton ) m_mouseAction = res; + m_oldX = event->x(); m_oldY = event->y(); + m_selectedBand = selectNearestHandle( event->x(), event->y() ); + m_mouseAction = none; + if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; + if ( event->button() == Qt::RightButton ) m_mouseAction = res; } @@ -130,8 +133,8 @@ void EqParameterWidget::mousePressEvent( QMouseEvent *event ) void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) { - m_selectedBand = 0; - m_mouseAction = none; + m_selectedBand = 0; + m_mouseAction = none; } @@ -139,26 +142,26 @@ void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) { - int deltaX = event->x() - m_oldX; - int deltaR = event->y() - m_oldY; - m_oldX = event->x(); m_oldY = event->y(); - if(m_selectedBand && m_selectedBand->active->value() ) - { - switch ( m_mouseAction ) { - case none : - break; - case drag: - if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); - if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); - break; - case res: - if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); - if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); - break; - default: - break; - } - } + int deltaX = event->x() - m_oldX; + int deltaR = event->y() - m_oldY; + m_oldX = event->x(); m_oldY = event->y(); + if(m_selectedBand && m_selectedBand->active->value() ) + { + switch ( m_mouseAction ) { + case none : + break; + case drag: + if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); + if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); + break; + case res: + if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); + if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); + break; + default: + break; + } + } } @@ -166,11 +169,11 @@ void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) { - EqBand* selected = selectNearestHandle( event->x() , event->y() ); - if( selected ) - { - selected->active->setValue( selected->active->value() ? 0 : 1 ); - } + EqBand* selected = selectNearestHandle( event->x() , event->y() ); + if( selected ) + { + selected->active->setValue( selected->active->value() ? 0 : 1 ); + } } @@ -178,39 +181,39 @@ void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) { - EqBand* selectedModel = 0; - float* distanceToHandles = new float[bandCount()]; - //calc distance to each handle - for( int i = 0 ; i < bandCount() ; i++) - { - int xOffset = m_bands[i].x - x; - int yOffset = m_bands[i].y - y; - distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); - } - //select band - int shortestBand = 0; - for (int i = 1 ; i < bandCount() ; i++ ) - { - if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ - shortestBand = i; - } - } - if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) - { - selectedModel = &m_bands[shortestBand]; - } - delete distanceToHandles; - return selectedModel; + EqBand* selectedModel = 0; + float* distanceToHandles = new float[bandCount()]; + //calc distance to each handle + for( int i = 0 ; i < bandCount() ; i++) + { + int xOffset = m_bands[i].x - x; + int yOffset = m_bands[i].y - y; + distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + } + //select band + int shortestBand = 0; + for (int i = 1 ; i < bandCount() ; i++ ) + { + if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ + shortestBand = i; + } + } + if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) + { + selectedModel = &m_bands[shortestBand]; + } + delete distanceToHandles; + return selectedModel; } EqBand::EqBand() : - gain ( 0 ), - res ( 0 ), - freq ( 0 ), - color ( QColor( 255, 255, 255 ) ), - name ( QString( "" ) ) + gain ( 0 ), + res ( 0 ), + freq ( 0 ), + color ( QColor( 255, 255, 255 ) ), + name ( QString( "" ) ) { } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 47490afa3..3054f0b95 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -1,3 +1,26 @@ + +/* eqspectrumview.h - defination of EqSpectrumView class. +* +* Copyright (c) 2014 David French +* +* This file is part of LMMS - http://lmms.io +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program (see COPYING); if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301 USA. +* +*/ #ifndef EQSPECTRUMVIEW_H #define EQSPECTRUMVIEW_H @@ -5,11 +28,12 @@ //#include "eqeffect.h" #include "qwidget.h" #include "fft_helpers.h" +#include "Engine.h" -const int MAX_BANDS = 512; +const int MAX_BANDS = 2048; -class FftBands +class EqAnalyser { public: @@ -24,15 +48,76 @@ public: float m_energy; int m_sr; - FftBands() : - m_framesFilledUp( 0 ), - m_energy( 0 ) - { - memset( m_buffer, 0, sizeof( m_buffer ) ); + EqAnalyser() + { + m_inProgress=false; + clear(); + } + + + + + bool getInProgress() + { + return m_inProgress; + } + + + + void clear() + { + m_framesFilledUp = 0; + m_energy = 0; + memset( m_buffer, 0, sizeof( m_buffer ) ); m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); + } + + + + void analyze(sampleFrame *buf, const fpp_t frames ) + { + m_inProgress=true; + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + m_buffer[m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++m_framesFilledUp; + } + + if( m_framesFilledUp < FFT_BUFFER_SIZE ) + { + m_inProgress = false; + return; + } + + m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = m_sr / 2; + + fftwf_execute( m_fftPlan ); + absspec( m_specBuf, m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2))); + m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE ); + m_framesFilledUp = 0; + m_inProgress = false; + } +private: + bool m_inProgress; }; @@ -40,12 +125,15 @@ class EqSpectrumView : public QWidget { public: - explicit EqSpectrumView( FftBands * b, QWidget * _parent = 0) : + explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0) : QWidget( _parent ), m_sa( b ) { setFixedSize( 250, 116 ); - connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(2000); setAttribute( Qt::WA_TranslucentBackground, true ); m_skipBands = MAX_BANDS * 0.5; float totalLength = log10( 21000); @@ -59,47 +147,37 @@ public: { } QColor color; - FftBands *m_sa; - int m_lastY; + EqAnalyser *m_sa; + QPainterPath pp; virtual void paintEvent( QPaintEvent* event ) { const int fh = height(); - const int LOWER_Y = -96; // dB - int m_lastY = height(); + const int LOWER_Y = -60; // dB QPainter p( this ); p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - //draw grid lines - int y33 = (int)( fh * 2.0 / 3.0 * (20*(log10( 0.33 ) ) - LOWER_Y ) / (-LOWER_Y ) ); - p.drawLine(0, y33, width(), y33); - const float e = m_sa->m_energy; if( e <= 0 ) { //dont draw anything return; } + if(m_sa->getInProgress() ){ + p.fillPath( pp ,QBrush( color ) ); + return; + } + pp = QPainterPath(); float * b = m_sa->m_bands; int h; - bool linX = true; - if( linX ) + pp.moveTo( 0,height() ); + for( int x = 0; x < MAX_BANDS; ++x, ++b ) { - for( int x = 0; x < MAX_BANDS; ++x, ++b ) - { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); - if( h < 0 ) h = 0; else if( h >= fh ) continue; - p.drawLine(freqToXPixel(bandToFreq(x -1 ) ),m_lastY, freqToXPixel(bandToFreq(x ) ), fh-h ); - m_lastY = fh-h; - } - } - else - { - for( int x = 0; x < 31; ++x, ++b ) - { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); - if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3; - p.drawPoint(x * 8, fh-h ); - } + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; + pp.lineTo( freqToXPixel(bandToFreq(x ) ), fh-h ); } + pp.lineTo(width(), height() ); + pp.closeSubpath(); + p.fillPath( pp ,QBrush( color ) ); } From a8924a34dd08b75d6020f5aea78242531120455e Mon Sep 17 00:00:00 2001 From: tresf Date: Sat, 13 Dec 2014 11:27:14 -0500 Subject: [PATCH 06/52] Check major/minor version before setting theme directory --- src/core/config_mgr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/config_mgr.cpp b/src/core/config_mgr.cpp index 411de79d5..93f286345 100644 --- a/src/core/config_mgr.cpp +++ b/src/core/config_mgr.cpp @@ -289,7 +289,13 @@ void configManager::loadConfigFile() node = node.nextSibling(); } - if( value( "paths", "artwork" ) != "" ) + // don't use dated theme folders as they break the UI (i.e. 0.4 != 1.0, etc) + bool use_artwork_path = + root.attribute( "version" ).startsWith( + QString::number( LMMS_VERSION_MAJOR ) + "." + + QString::number( LMMS_VERSION_MINOR ) ); + + if( use_artwork_path && value( "paths", "artwork" ) != "" ) { m_artworkDir = value( "paths", "artwork" ); if( !QDir( m_artworkDir ).exists() ) From a182a3e8cc9c512906aee38f1b8e067f31447198 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sat, 13 Dec 2014 12:11:31 -0500 Subject: [PATCH 07/52] Fix scroll bar gap Closes #1437 --- src/tracks/InstrumentTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index bc9728e36..20b737444 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -86,7 +86,7 @@ const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; -const int PIANO_HEIGHT = 84; +const int PIANO_HEIGHT = 82; const int INSTRUMENT_WINDOW_CACHE_SIZE = 8; From e6ae2be65a5f3cd55d055be52ad0348ec3586604 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sat, 13 Dec 2014 12:19:20 -0500 Subject: [PATCH 08/52] Bump DualFilter high cutoff to 20k Closes #1395. --- plugins/DualFilter/DualFilterControls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index 344a34ca0..df295c39f 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -39,7 +39,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled1Model( true, this, tr( "Filter 1 enabled" ) ), m_filter1Model( this, tr( "Filter 1 type" ) ), - m_cut1Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), + m_cut1Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), m_res1Model( 0.5, basicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 1" ) ), m_gain1Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 1" ) ), @@ -47,7 +47,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled2Model( true, this, tr( "Filter 2 enabled" ) ), m_filter2Model( this, tr( "Filter 2 type" ) ), - m_cut2Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), + m_cut2Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), m_res2Model( 0.5, basicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { From 77fd245c48753618f1bf2e3bd96675e31e2c45b1 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 20:35:50 +0000 Subject: [PATCH 09/52] EQ updates to UI, Initial Ui complete --- plugins/eq/artwork.png | Bin 50584 -> 27381 bytes plugins/eq/eqcontrolsdialog.cpp | 48 +++++++++++++++++++------------ plugins/eq/eqeffect.cpp | 4 +-- plugins/eq/eqparameterwidget.cpp | 2 +- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png index 8a4a0418b87ed04c2670276d2cd4b4e5c506439a..73b3473b4f8cd1f390ece29ff24c4754775ea2d0 100644 GIT binary patch literal 27381 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
A`poYU)s$P)mrMz-@#cT z&XIJgW%b4W5;!pRx#Z$HQ*R(z*&;*i;eLS_(Uf-og1 zD0|09mGgJ?KEG!baHzlTUt?n%Rb;c*R7S6C?Ny+`W}?-*PYHXMEs$0ErC^EiP7Bj`YpNn zI9X9{8fa`BG@rW(Tnz*15{ZT?Bi|HDGk%uW#;KTeB9(}X-0h6>XwgpLne{yH_ZNtl zPTq1to=>LKId%^V=K$XuZ@eE=Z}h z5;VzDtp8DH{Zlg^^=h^*+vwYs8U*~hQkEg1yaf1ZRk1eTvNw`;$h?1{Q`hd@P&TB< zH~}rK{!!k&^I{XHNyA|v)uWV$v;Tlv4~x6TsKCVTk>xnMP4?Q+i*9_>rh&;~cab6R*a>Y0tbyt#9$MH0_CzU_ydtH)B ziw27v*`{vkQx@le=>N_C6rXxQiDFzPUUb$EfqR$q%~&9!jANlk>CM``1Y^v7K?w6^{@n1_ zdd!YownB#vvsS}@9P-cXQ z1r6$yea{b4+#L&l9wIy%nD9CM$b3m0ropBRMDLyb6B#Eb_OTXdmKYZ+*Y@jK(UM2? zsYdM;#81yLE6vd!77@P+Wk7|*5Y?ckS$_SQ$2xLQ)1VpH-ynO{`^xH@o_+b4tq z@yOaPLW)dozkJ*U2XOCpJN)>;_-z)wpGl}X&Y~l{CU|q$3M|ZUw4tBxwWiY)Q2ZtD zJ)U}~vG#byrTNmkyy3-UDe#t~Vp0@%*qklWC-K64IIVhkt?auX^BbXB>QDAbOQ(ZE zH9eQOAUr?M>Erb%tWSMPLagMy{(V?2vsi4{N$>+PUBNR0@ph_%J(b~vh40VkrM?{Z z4T}kH841!}=Oumn6iWOrNoL=8=|-tn#fBC~@iQJNEWV1=cQjS{JGQj3D}XnNIb#Ch(9cbD>wJ{yV9T+bXN*j6r@R6uDn6kD3D^ ze8RFvfFc?t?)rm6%lz#>VCjaMl5HZVv9hvu6n=g54q?;|qPOHvcGIhqp8@-EkOHS# zeseyzNMO7}LNQ!up+k}zZK_7-5V_wa)F>6|!{H=7h*E>zeS663%r47kSD6CUz^i;P zx_a*_56m6if5$};>)8ScrHQ;KGKE@yv96Z2PbCVWS>n1+d9O2gxZO83SsNM19Cw&Z zO9r2(_|Ella)6^Tu}~^RllK02#c8gw6$Gm5$;ViRp1SdPge80hx4_~S=k2p~dy)6- zv*QNshRts1RapUlUP4{#>2in0(C_xb>;W(~fP`Igx^vhzZX7GK z3!7aZXf+PAaR*7UW#5`#V`@m*1WJvR^AJn~!k z<;`*(h}gaOjg*N#YRT0sZx+O0#$N93A88X)y^4>*l+djuuRppQY1Zh-y7Efc{Iy}z z7yXi-nYJ9aWX&1No&?P*%Ip%UKOwy~DrCw2v!PPXU~X9rn~Nk9ExNE)6%r(YR%U-8 zU8UkqHUKn+sm8B-yW0OoGD@HeJLK1e4#w{~zwn8i`I)*amNrF7X>jaE_HR%GdUt71 z^bfnT{fqPJmzX-5p%jD2^Q;!rJfr%!VdXWYR&m4<@mVq$*9kv0?m1yl(Lle6B)+3h>HELMebZLP{aj}F_#3?ZUgi*&+;f=2VTtc$lgdxJ`F%9g z@;}dLi>5TtVn*|)?`xV3Z|hbvLW$}QIJ#T5HedIMtF-KvjoIV$#J?r8bbhSqQ8;`| zRTQ_e+8sxmiLAKWv2$38^_`NT9OnPj=%LuKeJ9bvNS;h-^J>?Pbeun zz4pUZ*T1D@b3<1ZYqJA0`uLq3;xDLm$cQP zIvVW+l_QpR1lMmOT~k8RW7j4Xj>Q~W4DKJM4ZQkz^=n+ZLWE5I|17}kenPQ1L9n7; zN~PEK=YU0~$Fq6(Qi562K2K@Na>7M&(Tt~cc7>o%b6Hucp70h}`+f0l3PaBb3Ko*3 zlANp~_)16bMn~{ZP8BP!bl+*WPj-x-re$q+XA+lN^QBUh9*r&AzYP`>Isrn1v55Mu z;U)cobrxvbufHSt7T-Yv?`laTIyfqZkqnOoK@$@>%PuyKhinski!!yCkr#9M=I`F1 zIAX)j2U-=|Kmr%0l$B^6!_yc2zZSY(erPXl2x?t!!~PcTf=68(HHzqWd+X8uvF$n7 zDta2AXd>Rk?FB>Kpdgz2BuL~8>Zh}+1cnfD;QOoZ6vZ5@n z*W??sBiCbOzg-QyBm*lZK|iL3{AC@B=3ak|jlf>sArKO5gL$DrWtO@EOs+^>5$VdKHJAr!m=(UBUwf<#$a1d-(VTDwT9G0- zVFT@_YbHKt48;jA>6lon_y~mEQ3tz4R?BAAaO>uExiY9(qD zEsE@Wk=Gk)Kw7@iDr;cx9$Fm_TaL~6@4Hdxz%OvAy zm3>AF!w$_AMs+l{-#-;wUs(tbn8@>uuB!L&@oSAv7P=|iOMBuaNX1T@^#!!^MR2-$ z6)b*`S9aEz18dE&Fe?eUmZGRC*nWHvRb#HskJCkFd;Dl2puPDOn5L35HQsizcp(?E zig^h8Bzj4%8eil_SzToHsB=W&=)}N5B=++#ymqW|5{Cz_lH$f=tI8hyCU&=Vq_mBx zfv7oZ@o{?cx>x^88Gk2}uQZbG7qciT(a$TPO|o*vQ>jcj;Qxs_&naDRINJ8%ST z4?l`F?l!$5G0HRVJ>5=4#TOVTS;z&vt&e`Yvz0sDZJasrEF3JCQ%%7yKbq;HhAjBy zS2u{cr|>6RX;JEV%~EK>!~}(NcYysJ9%Ny%j$x4-He$r z5N|%NPjn=Lr52R$UoP9n$TR?|Z`ex>S+yogBdEYF+uv#oQld>YsVtt3-Lzc%E?Nd1 z2bo#NC)UJ?(_+Acj$w_s!xIh1X%63CdF^VrYiqBXS;BE6Iw`c$~KZI^N$`p<&%=I z>wdKs8m|q=zbt9MO5fVJ38T_JI$1}$&$*6_3vY@9cJMgARAYJ=A6L`-8Ttxw z19FHr@&%xSRuEV1*9i2<5s+_@&t<*#BZrv`GfW>$m(L}%jr`v9A+FgR3}7b{;eX|0!7F7WaHR9={n$_q zshU!}lnaG9*5Petl}iT$Pqd?oy5ktRtkJ~oNfia}LYpQT#&H)fexl(I*<ipYbSJ>Vxr@u0v}zBD#ua0jR%O%bheL=O!5$ z5)`couU64zV;8-_p`SS_2@n62zcZFd%Zcv4@^;+SR@xhSfo5C~80#q%AEizA z2$(ffEKA7jUG!TQTX2YfD3H&c(MYRNP|cQBFSMRK2Sy^0>HLP+j?d`3` zk@-#Zf7v`W!uzUHVx|=K%iV&qavfc}faQ+TuEOGqg>D>zc z_ZTtv_2+^Y-p#6O`B67ko0VTZ-qj7y*nAXFtXDgXq~&T>@ARtt1=RA~OKEqpqb%tA z?3Ly#gG&1q<)umDJlM>acJvEpgMY`A0^fXnX)A0NV{RHKOmUP-XTm}%^nvTAFo4Z!6B^<) zTX0bk?C|T0P|2KCgWH+$BYDy{!4tLdEEFT_=M&DURBrf6hQJ0`s@W?Ql!tmxLFt*+YU&!U)%BKR$FFy zn@bKU@bK}C8uh!@*Nq6gtFZ>m+Y1&=rk!ySXT7wGwQgEHJv~o*DQzVT=BRH&*RTsz zu_f7LKx`%ru`X-IQhThS^gA<=^2%%y&EDe%R5V@3OBi-`c7WoU*RzL0^Y-}pLDSJ%=8*bP?X&uflYI9Z&zUiUVk&j%)E1G@h9VAuwI6k(mrjy=ojryIIt-mka+?lms z`Dy5_ke^z*+y16GFffA$2&{P#6%_?2CUW97lF4*9J36Yoz1`7Vwpcy4Xf|tea8UXd zvE~S;I~@=YwiJx08Jo^1qg{(A@T9b%rEs(jBz&xkJhBh9WjW#~mC5+7A6fkammR$NPGu z7m4X=!toUA7p6hIr2U)dg9%WtGX8ERUEJw+244>w^>}{*$If^MHSW_ZKXFk}b(JpF zuU|{T_siz2{>Fg9ew-Y~>gg?ZC=&QtJ<|rQ{CYc#(sX^idR5xtPByOvW5eK^mK3;b z?9ot$nm7vfk}I$0bU#0Mb-g^Vig`WNGQmha+9JS#bW?WU0WGZMmd8t^Q^Xb420%-P z1micq>TA4qiJ@B>*qKf?U@V6wkz}9j9 zG7Lp{wtCs@XC)6N=(s(hA3Lu+kwL~&~bDQ zfH>8B<<;me(lIP8a43<>)y8{fCC7Q@lWG{r5|SJ0VJk+``0t` z9%ttpLhUO-?xX5xU?qHr-u~BwgmaMZ8XAGkLHM>HC#&iW3+51ZD#I{9>Q7uyk4Y;$cd1@aC6K$3GU zutfW^SZf7Kdz)da^OP^zQux;|3C|1Kz3@yhT$d+v*gKx-BGG0b|YLQiM zu92uX@r-KqyPC?7tZ*Eg``0aTG}?LhPFIVH(>!ch8QBJ7ur9&JdM}?e1GnilCP+G7<# zt5Uag-cn2d%axYm;z+>ODcANsNX#8GfXJa;GUIn&;utQ#XCSkbZs>5xK-M0TT7n=_Y|*QV36 zGm?e3&QGNew6>y%1m$d6^U4QZ>o*ytZLaKS|Jy*aszCvCEL5AV-(MY`&g`}$M;W?v ziCmsa?{vh;?ujya(EEEva=-TlnjuANYg$><&!z~?+SBn5?47-XcW=y?>+y5)(+I1E z*+?mQ`oFA9!Y2O|7q<$yP|teC3(bCZoKEPliKM1MkcVD9?D|*_I;f+8r9XFwnIhDC zA*8Z}-hcSMoV`HOMo(eFv1JjE(Cr1Z6ou>sC0UhC;8ow@oGgf=;EUY?mS4&qVp-)t z8Q1xNmctTJ>Ft{Kd@k=T7LaEx8|EK+IlB|+);JwZ3Q+Tv%l{p8_MR8Pi4P^~$EKhX z^(PJv>9P7cV=$LrEk~7Q4ePp>E;T}ttSyU4QJOW2G;!VmJy2O~r#z@3x9x?({Hk~Q zecENvrv8F+n1=5XA6(efmC~;1=TE!CS!jd%p{ZWXPBocja7Gz+hC9p&+KB>uL7N@; z=Ow^h}>H=PTig2ilz2n&Kxzl?^W|&U#Wt_tUaiAT4sxumC{i0U%A<6{mZW~H) z4MV^_e4XbWE+NTj2yc+|baF{a_4alZj1(G)Sa;5Yv!lOk&RfvY(o42MPG93xIRxBX zWdu;h8GGO(*8ts)+?6OAg_os+CJwCWA z?yh2^{@wlegP$7OnyI>7ooyjG$BjGwi7!Lz%zV*x9$<*yoUad}E?BZ{#}%;A$0%D0 z{=0Ab=kiEWVK1jNs>zag-SIpR2B71MhG)H55Gnzb-Fo9mG*$p^_r0>IC4-67&2PH> z=>X-V=IB|of#(ECEYa^ji|%GQZ~}OlTiQ|7(c9=s=nXo8Y5oy*_&l^;u5wWKdIYzKr5_m(_Rz0(mw99(J=-@6EmvrD3BA8_j`U_uCEQl3sRp;I0Ps z@1{czMO>0Gi=+W~1P`#6rm|-JWjsA0rM>bY+!j*)Q8uG8r_!G9d$S5#dGiEl#%w~Q zTW1O{dq}M9-Vocrv&Kxh*0<@}E&>_Xt8rly6aV$e(+zUzzqp?6jq2IZ9DqTT7R zx(ekp)Ipy1`Tb=AsuD4u>xtRz@d+)fg3q>|2GxlmnV1U6tmN*F!7h1JuqV5G=XYXw zE}3CF`re3~sM=eU(pATpj0g-eUVtq&3VE6u49hudk9!X@$j8g*0=!K%?cYa%w0iA0 za%&yNm;NQf_}h{{4w7+IXp?qeE|Qc7j`e6I9$Co8`CR1P3gzK~j<8lUS&{8Xr$5b> zUxIFaj>`x3EP?E_URn-qtRD_fqQ+dQM;ZzT0%rBU%tS&D)cEIq`AvqrdzKS06MiLQ zu4t4^gbB}-LrKZ@LP~!ZxUp>0yyi`oebOR~#G$R$h(+MRUEW^{S%Lp<62j_^ZnD@V zG|=?Y2(mF3o^vLaR^QhSG-SypiTP|hCw>FU!^Xo;{kz`TDS2T^h6n3)x28?_Vgkog zQp;#KaIToK1zX%sN3HX>v}fsRb*`=&Zf3gy5&%n$A88#cTA`L@D5o-5+A)Z!>I>FXvFJ&aQ6KbulNsvMS`EUd=`vx% zQO`b~pNm?@AF`Q#K7GLSK(3+*n)Ep8mx@SxaMg13?qt}PjG4p+1h|A%bO^JO(0SP` za>jd2d;Z$1K;dXtxC+Izso-*k0Zhx5YA-u9G2$v5weVppHfh$IdbSR=;4377K$o9! z>3pBazBluOmv=m>+L|XKc@vzv9Ix}n9AAR!ZC3*3zWP$HdK?0E%CyzW=bq}SmIi-J z2i7~co}e=~JbU|4jvEk2bsJmgt;=Wr@UsD zaz6YpEl0Y&-8t}84iLu$m^|#S1Bs}t2X_j-pJ#0kMu8K`%Do-6dP`nS9&1a>O}OIu zllH)sgWRLAEv4#81Vt`2(<@*WZ9;S z@q>asS5mEscKy%h-*Lr0S33+D-_WPC=vL;}LFK7`P$IAoo1=2bqt%UTu+X$oey@Iy z6jb$xHOy7Zf|va&4CgBJOJT5Us18S>vTyD6hd3@;HMHY0T`U(NDdmY&aHC~X;neEa z&_u1~p;utkMyw(ikugHT{}{S<&M>gt``8NV`-fzDUVGdx&{7+%2`BwfKUN77e)dN~ zKH6UnJwpbEbqf|Ztr7hu6_$y7-x(oCF$ zTd{1hsV7eNn>v>lEBgD_X};hv?cF>DrxZ#-QEGGo!+Jax@pqeo;ndU6EGVM-+OR|V z874a?{F@2WFZwn5yT5%?G}N0*1`f;Qvo~xNSWGz|cFDLEy25!$| z89$pxGmb|gwTAz^_^psJs&%=2yrchVps0iUqpP=)xuv#f1S_BnwI@v%*k+kTlfCyo zYIqV#;yD>a3zWslLYSb(D)c77dA2{D^s@jLJFXFzE$cwJuz?@G@-(MUmjxL zXtR!3!+8W?hrcuBwK6?)gPY18ey?vkyaE8kZv(|K>upv7Y#)1Ve`G!mWd4ZqK7yP= zY45}A?#{RI{_b3WhD|@{lp}!3*}Mg7ZEfW&pT$x9UVArQ?}K@LbG@FIXc*Ar@M@;y zz@`P`aPX#HeNuu7X9_!y3Y>EPeaJGlgN2XK0Qr<0x*iv@IaXGnkE74fgyZ|Gz05Nue zFvZ&j!DBlqoPXT(^RE6tlVtlCprNk;>2{**aH?H>Ng1@ zF~JP+e&#d^fquZ-WTMCj8&V5bo9H{glB9qWGH*@yJ&Ri?RPY;Nhzw1zg}j0w<5Dwz zGMZACBA7CiJ49xqd~KcXRquVVj9(L0*Lga=60-THNff@Q50@h`!?sM-2|OSi zb0>xih(D#SP>P>fW%{Mo+jP2m-3r6XTw728fVQ*LHbS5MK3~GNUC(E>$M*Jz=F>*; zTdK!r_>0)`)2M4#qvI~@`2h}ZS&cghaOwI|ZWu&CXy~>cl)t-}{DkKvy2nq26|$AA zjUGnVT(7{RjB3u;rGhmXbIC3DBuk|vm7+p~+MUI;ny3-u`-zWXiT_ZtpSV61U2(l$ z|0ft=n@OP=x zJ&W+<0_m?qLrF?1Cof+@ad;;(;W(>JN4b-(`0$0S()w7!pfE6*rj;{kg7Q zQz$;NabW!Gtn%eEy|*cpxXP9WlpOz>m>(a;YBR6O164ovi3Npd?F7A4I6B8{arOpn zO!O}O5yRJFoBYIek^9V`DuLDNeDwt%R7bgH4TPt;6Hk_ zKy5BP&aNp{@pR4cSP_^cb)c<{yrNei zv3TktxQKF?N@uYfvA4r+ztYR}$A9X<@0=*ib0D#;y_)E)eT2DP$pKDBo68&sd1hTOj8 z%w6|>2Ijp-M+JD?`wu5X%PYA17UBHTQedipEiyyL+b9tn1Q}vJHj6)LAmX(gSr8*i{95qw!S{~q zU#zOgE3>5%)EYO{GFE4Oj>JD~6o8JGo1eazT&=eZX`-H_qpQxO*E^on$7)(yM0Ukg z>^E;&-}a*jmJCqi6_0dLGRc~I?n$HDNVKdpef+I0(@enSU1g<@QZDs%=s+>8P{8HLwb{t{tOUH31Z4 ze{)UzqqwN<9huHP$5%~@!g7g*rd1-_!RUc%OeY5NtIl3Y-+|>=vrW`4h4!{HV>hDW zBHK3dqrsx0=14I#r9YcBaGGAcL5S`ji|enl0`HVuxKXmH|1Y5_Ue-D&wVk2h{nFyGL5?;3?z)EA zd=B%~Y6W|H`*3!D1&_b)F?iQ~_ru+H-wlWe?|tuk;rjLK@XmAZguOF+aO1{JI6ORr zcb+>3_ug|aT)1!%-hAs#*x%cShaY)2T)A=u&YV31)vOv^tQNaLKv=D7xOM9mtX3e|S<6H?U& zN#Z2`(R*HqLTx{~`X^yT#Gp;^#2EO}?==oc1JnZ{Gj{C|37vL=wWfaPih%f%8dU%m{_KKm>K z-5Tb5^ELoqL0wmXYYehP_$Jr6@U_7ZrMpu4Y*jTtQl-ep2m&lus|I*qd+jy2=bn4O zRSxdE|30|;?z_7Qb^rbM2ZaqGnLOtMCO7Kbx($j@!Flr2iflf)Ia#a&$;)U+cYb3aEyz;737fEg6 zt^);kno+{j!o=f~dk}b-if`kKB|?_W0d~A@6Wj5{5r>C|aOu(|c;=aBg6dgBgt^ar zLV(q34a?;U>Uup;NDBpVSeTnGFoXsS78J#S1hQCgaUq08N%5_>-h#7d&%%7MSitSu zx0}+tPD6ndJ+#}ht^}{G$iQ{j>cy+?3|xA4ajnr&L$Mv43lL=Fhv|zdr)nJp05-WY5Hsv$MlhrrlMhx8tt#O_C+$}ni_gx-DEGc z0d@axxmd!bifuE*32=)dO(nIw+JfCh(oIjN)Lha+#c$C0GJ2^=)56!TgVUO1@nKpmhIhUo%ay)J5yyNh9@ z2V0MCP*u&3)MMQYdd)_n5c6j<} zfHvE~RSwRy!Myq29?Srs7E-z-*~d`xR?0IBX@^cwL+U7@Y&{(I0Szy0I6SwoQv_EH zjgeXU5AO!ik40nAGdQhAH?o*VMIiPAzRk9SxMNJ8-KB=jM;^kb0gD z)-?|Eb2|WU0M36m68XvI+v@%nc`+duiV9JQ$7VsRx@)YV*PnI$iS`_1$1m5U&cAK- zQa3()Rnem@FwjL2c}G7^Dh%`>&|xTi-km?^9#KwzSir&IAuJY$ zorkTj_MyHxva?eAkd14|29?7>^}3}a0ac}r{lK5JVV z{&(6og{SvO-zRnUrSF$I2TSMT7k4eMQ#zj1$dCe~|DAs)?F~q?b$Dk2J|_I@F;7am zcHZTg{_YYIiS!!jwZ+E1RC23z*iL~`#TMyhr@yd-Ma$_v_0&^?`!So(J4a}7*wPWp zMYn@rtyjGm7bw+f4gz5>CtQ=(bzH*&>3Pur;Cj98fj8D8EmbaO=^Bx$WRoVr{@y-Z zyLJr#U;w@j0G_$}3_#`Jl~-Qrw<$F?(a+@)>U9siUBbNgmgr=qGM7xfNOUBH=+!Xg zBouI_D>Z+;`#vR%7X$&wcT1pv19g3J&uI4rx?Q5go$z;_E*5Bx(#koOE*2bP(0htZ zatU1N_mT4syL8LITeaZD`8#a4-c;k6DpApM0|J2EBGqYBhq;Bb*=*2%E%-hkcOb{* z-wos$&8liZrbnSZc4C&he@hkk`E5iWGTt6s6DQ zZ|_?zE#g4bz=&v-FPK;^mvHIQC3yPjrw4d6o6o@)Uo^^OhmD8Zf-YV7W^QHzN-ghq zkb@g>cC@CsG%p!_>mQ9e&<+;s$eRPk5@7hxc@dtyy}gchxNzY@n3B?ZzExGh3opC? zHP*0NG}SHZ^_qyb_t#dDVhb@>p{>oX+5zAP4o^4fiFK57xw@?_nQ*O^Yv*0i@4%tD zspw&oV>!AdRXh_vQL1)wR&-Kp*%-mrTk0()13v|>?%X)okmGvbt$1}71J{ZG3sNtW z=cp1O_FzKqD-J7L{DANXCg@s5-rE^7*=TZf3;Wuc}$l zA)@ovTR^>X<;uX*1q7Jy&6`_W11uNIPMK`EY=C#QT8Vui`A#QR=|&d;3+BaiAL!4@ zHJ#k&8qi88ueCJR1SVptKe4U6H$Pza_V(b~o7Vsl;o`-MBrUD6C!c&0o_O*JCisme zo?6?^TYqW`mgPIF=VlS$*u*R?%F`WdUGzOti>cVL={7y}eywMqbS|^mO!RD|&+)&- zsE^oBiMmK#x8*sc=pX6)Q_s-ytS23NNYTrL984T5Q;b9(AJhKgj) zJWnKwjPDkLV^k5~mAD|OO@_n+S$WhLhp7$PH{W~{001stya+-kniFHG`=kNvCY_n5 z1mkUdu9$7%vn+B`e8*0%jr>c+g_}Uyyqgz3uU7(H54_g7C;@5iVkPY~w0H~vQ76ld z-bso-k}{A67%5L%3LyA^8wKB80nwPjYzaihGDIx*Y}W^W&^wcvZ<;)N4ExwO{M{P%5944X`-96%_RcVC^MQDcs0JoZ`bX0Z*c45gj6 zF9U`e;b3vtdAgov@Sv-Cp&Un2z!}SmNdc^cXD2p8Ib+ez?h zWA8Z%aFv2b1&EK1N*GJ|Y@5b73KKcq!$p@a%01nf(F_eu^jva)Buvzj31aiB$+Chj zn`Ay_`;qIBaqd!{s>hD8R&WfQVq`>F!Zpy&U4Z)DHz}_#^%>}2g90UM{vW|n3;|)NGPQ!5W%6|`~ z|Hzh-Ybmf}QC}#&RYu{Ml6>TFs~MEX2C|XixzXd2zd}-bh*hQe4kE~oDSNX3wX1|{ zmhYMNy6koF`+Z$Xa$J6l3}Zds-=`^F1_U>t==^4qvUfZXY)DSHJiaZ-}z2>$GLN`M;mQY z(HNpp8WKv%n4v0DJ&F&NskDj*J_kq%D+rPUp;jGh9|XRB9|EzK?(nFzR~LXATos^X zFAgrr9n$XYPHes?DV5P`f5*FyfrWKCW5-E7!-k^Z_Zo|w&M zuv{+T;P9XkzG=m@IOu9sj~W-7lTJs|(zN&RL;#H|_T7p*ksRrt#$zC<@=8VPV;pD9 z<&Sy80r<}6@Q%Cgg1g>v7tE>(7R%MZONCAn4g(3FCB2XD)_e|mRaI$^H(LIt z)NjY=2DIby1;#JHtO-FoQ<$eP$1h% z3gi3sKTgz_9c-_i&=Q7qt95SZ4Tm123;AJ@asZ+WK`PfOzQuQ;K5y{-oWpItYu2Xq z4C#)(H4x=|2Msldwm=BX!|(1EHF|GZs~^sGRsQ`r1W?yD^K|_B1nMgkiHAX-49epo z@t!-mRNr_q@TOh+H`1}fRgo?;D9jlK^h(@?8_m%j6YQ0O$g!%@Sl)B2;+FpRnl%5V zg&n3mUQ6)I8bzfi7IXDA3W7!@>$+})+Xt$16clykd2v@g0A(#`#osfOoQwf)_&!ws zKA<(&fOcX#asf3ti1SHUv8G8*;4=eIK|*_TA#gWPe+s^bbZ^yrk@er(bQP|;|5hmm z?zIXMy#N5~Y9?P_+Zw+44%XR%^dY7|=G<^0pkVD=+5kxX;)c)8wfY+{po0i)5d_}& zmYI3yo%RIV@RLOfnZmgnP-sI>6+w^^5O<0&0O~-{z70n2m@GAh9hnFZn%_u%Klp3NyD>kZ}ryKC%e z2GTL5ZBOOEH_RkA-^q>(-LY|x7_q<48YXCk4g7tSen@2{#41GjXJdK2F^Nr{g#`dm z&8lvkbilh>t-6F{+PbG})k9K_IH?^79tY`17^ao9o+?r`dm*!+;kl|_+D}jcqsm+T z1RM@@vFWM6XGub1LtYAp+!bHq6MUC@~~s*0Tyx}PLJqy zir`AKOP*HL0jj~fk=obrAP~Ai)(6BxkaFZONS$M24r#k6Hvl+JvmlWB(f+sAmZ|$} z)qAOQ(e73CDnhS>Jv=LR*Y~_+iX-YQ@+pT+D z*MoB40BCtLPHnLlJX`;~hdVjCdyqAxgY&3++08j3!g8^I8#ivi&6_u2wO$YU8S$SK zcscK)b)oR@)!y%j#NL@RXPR~%d~2ujIF5jBmbv0?HM3dSYA4%^NZp*QFk1}PWG#l!8S z6NBtRM^UH!K|pP=-eB+fOXufLTj<>-LOlwLF{2Ayxb+|%C9rQ#)cLaPoCLYi*!^=% zx>O9v`evG4M-SSBwCjyR?j>E5ow)Q#U@3Wx$OGG~V+GY}(0JNdg(wfiY8)|-aapPj zeRc~XHtpTtc>8U*cI_H0SF3IX;chtPV5Fa#$O{Kvf1^0?&gOI2-`{IGaL(sJ)rzaC zZXgSn#I^@L5Yx6jOuL$Wo-Z0MLOF!%X8&Eog}}DJ`!+2`?@ySagB>gLjJbE{l3_W6 zXBa&IZ7JkPK5)dt2RTS2gX*9`@bDKz(*h%}GQ&cX@CiAV=PYoAn!t+Mp3imLP(^Vf~I;1VrxN5aBRS( z5-icu%_JH?atf(50oPaPcHT;M7tLPKx_Ef=9Wwv`=9e#Dj$U|9+J!ZjR|58b^Vxp` zFTeZ>oY_Cq1G*0m)O8na4pvv-N$QyJGC95AL?=~AH53AIau6&=HAv^$6$B)FWtl4J zC!iFZ(rQB>6XruuGpz^8p9IRnVMtZ5i`G~Pq_hbKLjsDZc#s!`o z3AQof8(TD8xBdCB#vt>f12xj?%;7NG1fA!K+kl}1fXXp?597-$?Gz5c`cq?A!N-n^ z63)`zat@L*uBl=y-=x=r?xJlKbm9DYxO(+!GX7-wozkAE&%}CB()apZ|3>L#n;Km; zzU7<01-|&jFT$&@z6#6L5?s~RGy4m#Yobs;1d@*DW1gnu^-(%v;31+!7jV8f?E&ge zhKsKgi5InnCP>IfWI3W5jLW&Z7{k%%%D{b5XM#3Fk$^}q3{nqy(CKKGxvvO;1Hn+J zE|zfd;sto->1RgGmzY=0ONLX&icux>7yv_-cqAlnp zd8mH-%A2%@0XmrAeDx0@aAj2&i6~jdN$d4_@2$$gR-ZutN^mUP9k4Lq5JOw9(p4<~ z&^ci{F5Jv^p$|O6j#|M$w7@~tRm~w=d^{-od}i8`y{ohn4nxf^M+Vyj~E}Q|pG)?J}^|D>yt{z{QIfyRPG#c)H$0r^PaJqW_{w8XyFj zV06;;LRulU+5zu!xrA?c?0v!UKJjaxfX5$u3?6#up}t|?gkG+6QxFlJdg`gBb^FUN zH}Baed2}riPE*Z-SUC0pFTRy^EI%@!76!p!O3GvRyQ9%=;$5V{&8aj5Pj5rTdk^P4 z2Im_74m#gVJPj+tCUH{@hN+PxeQ|UY!rZ{iXY@;5YV-Nq#xV?EGSJ<9dz zLW87XnI;In8~P@Y6p(q>UmD!yCD8#T7O1{S_k?94xX_>N90u-HI$5O1U$=8Za=dq9 z9noF#0m0t;4!v{7VxD2vh$Dq88nCVERZlOhmaV6IvFk#dax}ys`XGxWj1SX})FDpQ z;dgn8h$P0yC)2#pwf3CgNv6g!v5YK?CtHrU3c9DdwY8w z=RrH7`icyiM`!Vr0d$3Z-#z%nmtN{tOy?Xp(yY)AObom~3bx2@xU44S+LarFY9|hC zWYBke>&lM`(MOO$*FjiuZxZ}BWmPJ* zoC|gnEXLLb4quR+;Q%6ORvoZ9R#oRK#4ywl3)Rs`H(_$XiasB@Wv)OK6OwXxJB^qv~)Gr3eBE?~TDh)dOinK}=9iwV`M77Lu zN@S2$N4V=QznRp!ko)QbqczsBs@Jewu3)w5LHGQH^FizPJ{p?{QDYy>9kL+I5hPd; zc(nm-j(v?0C_7g_hg)!-(`J$Y0KfK$Pl%`dYya%m-~%7{06g~iV*mi~`OkeG{`}9s z29G`V7+iVuQMl`_yE^{$|NNz2h9{nU0^a@5yG`J$*^^H`33Xk=%dflwXJ}qu>-jeA zG-y6uc%{P>5QTaGIn8Y7mAO6wAG|A-kAOnP0SeE@_0>bX`V1s+5&+6sxP@s1uf?x* zzBphaP^#5T4-kR4SO&=lCJXd&@(*JS%YEd52YK(MUW zK!+j751=J_G@vDfIyk>OlywLff>^LGbOk~%MYg!8!d8(WU0q|hP)R^l*9hynhLvw< zxoXnJE?l_4KsS?GAWoK?t{is4h|#6d4&h`WsHzI8Sp}7=prRrU007_ho!ue;F2w#m1BSsi&TT3+K-_TAYivb@O@+HDXZgAZIx_ z*j!XUCjLC!#%u&`PL5s40G-IjWy$+4_se6!Hg&!V0xsfrVEo`DMbC}Ag|zZkr@Ex- zLm_ZW0LOt)p@mEav_`102E@7@rx@@=NP-Equlxk&P5Gx>xgmu@1ZpU)!h>lJ(!nb4 zSz;``Oyf@3_!<-v2ouBk6j3646r^>s`@OFFJl*AL35&x;6m(O&>s-wlE|*J#ZT#<^ z4WK`<{k#9?7v#AITz^7$-+ebc{P4qY<;s=Lv%PfbB3!wA84wZv>%aZC;nSb_44m2D zhsPg(9M6C6mo)A?=`)+r>0ysVCtteDE&-0KkO{ z7vR}v-wz-B;0NJf|EqruKlRf;4gbqO_y+(0@L&9`zXku||M?g2pZ>^?z`Ni5?v84p z+L=?eD1Y+FCjlJ5tFL}741~fAyw0?nxo~!LUN4GZh*Crrgut8PNh9jCf?R;%qS%8h z(_u(W#7mOt7S#~USb8#)dPjIUiFQGryc|C7Y15(;<~mpG;ZQj zxjR9(40)G{1biv=;OGQ#VWg&R^Q%)64^ z*$jU5SAG>PUAhF1Jp2gUfB*gP&wlk+;eGG>26*(!6?pK$2jP`hUxk12@qY~e_D}yE ztk-LJWy^5|vqru3;$&3I13OPwcK~(pN zC&7V(S6+Fg8?Y53IJeh;YmJ>|YnbX1mJp|D0quP;o=6s!MpNmk-rououghtB{(xV_ zC^iM1Tr}F*nDrnn4{& zr{K5kO(1Q#Ty=T6?>YY-cJn#VAefQn)_+yX51MhkO9(?dYICt(GT)KD>zT?9mhWEYieQ@jcZMggHcfxxf zc@L~sD|qaE?}I=8`kw;;zyl9F0Q-CUY7hHO?1?9y004lOUwH+bbFjC+*WR2NR800WjWxi}fc|aMlBAv+ok$IK%4A3u z+oqaJR=^9cmw0Nv)D{Dtx2Y!U;_TSpGe5l6unxRfM1%@mkX6ullvS9aLo#tXkF%1h z;85H)bX!`2-Ic4_@!OoJS2YW}m(XA&afO-MSsz3rw1KMv!Y8cjX5&FBrFX;c_kus2 z_QI+!j#w-fCeV%X*eNem>zKnmUfxAVYW5(#kG|gNI(+r{uXNw&`G*-v+48@8^Lcds zKzn<8T{_vVTesi~U;G06$(R2G-v9ph!*kC)2j|Y6OZITHtQtJB_{U%VWBA&uUu%@i z&g{dC^s1`c00&|{Z23dG^y09ht!vf8ItL>Uk`T8Mm8uKcg}6&)($0kg?7(#)t1#gf zX%9AxY{8_SXQBuN2kTCNTWJ(%2&M#qH@?lL|1I3#j{UjQc2k}6!#ZyB zXM>1cff5KEK?c=jbWBU{CG%-OO$4RwE*PULCVF%J{P~7v+`v*86ML_w?;F!BPxY4! zs0L3r@IndhweIil!~XvM;7k4ApS}7lJo4}(@W2BPbfCNqd-91V!8r%7zWOSF1E^>7 zCdqo;@Iv*vCP~gik1&i*_sP&cQv#V>miL7F);qF-S>WRlk#y&jKoQe_7_%8J0+0%|em7B7E@+@hx8y;G-wuFjrv^9B`KV^IE=o^xPn+**&YgDH==-qs{B5_r zuUyxfC`k1hMtj?)(XQoXtMGU%_m_?Z9vG~us{4B*)ZJ46{(ky-LBG4_u&W@C8liD| z{P^+?Q1^XK)jkF1DyXpusx22wxNzaZDA2W3sA=uUwBT8czqL{jRLOB3YqepmJNCA> zw+9bB^bp*C|NSC}Pfj~L{&+(>y!Ns9@r|PL(sfXRWky-VRcvscAg?=fvx|pDx@3&5D!zg zy2h9k?8qrrvl+~1vj9+Pl5*x8@z?|Kb%PGiz9iny8Pu9UVkjZh>`oyR+5rsC#~;v- zZU6oBda=fCr`x2-_wxYQSnC_kI&!S4m5Qu8PuGw6x&_~=nvn&SjXAPd)>y-(OP5A_ zx~WWr6!`Mb#4@rp?>2dk&;RM4p(YwUW<|+;Z4;TbMv$C$>4m}Pv|~y7!ICJ3l((mS zhjtFyb*FxAa?deCgEh6-I)!SGe%JmcX`X7WvFX-Z>Um}PC!M!5P$ANHNT01*pybkl zmUE=LN$vP4zA<)f)<9G28K=(cxSLf5a*WnYidKl>9U{Vo|YzqyO@mDbKn6L4s5)u3K!GXz?|+j{og9a18e6w1xQLwWlt)| zjvfE0_tEF4IcqU*SLzD~01efkJxt9t*9Non9_zzQB_fMAhiu3A>skV{ww<8w;rArv zD6e^xd?(!5A-1;~}Ln zB+Y>sAX@Ldw0&0sRy`k^*C`jf{58 zF^g)BK5Q(l)7on1qKGDzlf^?@QOEMcsR-

qxWw53E{N(<}UdctETZi=&tfo^gY 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

-2_<$enGCzv1xla*m2E@iko4x{Bmb4^O$PUES#{!0e1aB zFHzugw=FTp@jw?cZ1x`Wi;odK z<#8;M$EIH_@^05(TP)1>L~tl%Y<=^J`tjw?+x>ra271#pn;5!xSH9tD&y49j_1lB# zx^(Z=Vk`Og62<2tGNTC67H%2>PiQ;w=?n+Zf4VtTtn z_`4n~+^I&2{37Q!L&**~;KvO#c(KZ%2QARpTrL~LbOUvyGfl9f$DESqIl0f%Ohj$- zyy4Z=GDwntzt0xPYBi3I`!Jwc);1k5XeE1hkZN@+3TE%b)(2U7XLwJ`k_lQwI=dpP@N{ZWVpFgjmHC@=sW@IrNCsDJ63T^Y3S32}H=Q3D`O=!yQ-} zO#sIOK-!%;5vPo5GE;GKm1lXC)_R4bRnIHSU!wksna^Io<{1A1 z?o}Y;J8)rw9tyNhc#&Jks%06x^>OjAQX?%LR;+O>w-E+qQy6_Ebp0KlcEZV1Yi^q; z27O#Voar|42s!wsGT@P;m0!z~2&-kE7`-_wy4=Y|8x8Pl4gHTmn4|vE_I6H%gK)ys z4ZW_0C4a}T9f`ha{}yl0c@VR2%Y0krYMkq*3W1s0$1Cj3FT&Fb*X|6(9bxG#;(W^{ z_!Vi0hn+n;HGw)UN_xha6^_y&Y#=tsoTfo@Kd-+uG`U}kN<-s0eIHB4=jT@+GnR=l zn|RZ41%GeQI47RsudafDpgXM3J3r%3YJF-9Up%65sa;Nisz-)qZ2gKoxw(86+r!?g zL{YCg;+0sk#vtWlF49EAoOKV=~6{RK`$2{cPH^H>v9vWX?`c> z?+>Ymc3GCuHB3WHA7G}13k1_1umz1?J!}kPI(O{&SVoYeqHYZEG$=0G4VyZr{wj^N z_T{S$q?wSR{t4b-!*jPVS+yRvFK;$+UQp4yUz@iG(l|>``ZpH&7QO7e_WD+md9I%+ zGIm<0?$vU>_m1RSGPff8gKr-T@9oFz(J4Zo2($xce$~+9uO!hZb+LP_W{?kWV;!vD!+LQTM=%#YP-a5P!~@H(w~;DE8iF%M8A$uUFoB0kgYR9mAGMB zk4o6IP?iH8M*GFjNK}8C8@(2uarPL(@W~ISM>a$_h_CDBYy_h-cEeLTD-Tpr z%8OhiN@se}E?ZQyRc9jEuW*PPjM`1HuNdbz4@%GCuk4HAGUHRo|@HIm>?26 z$iMxLR~ZLL?$YqcoZ6bDSf*IbOmEuB)xTycgBPY}Zu!nfe_E|$C$t)du@;z%y)Rz) zWiOrIPEG+n_Mg;%Yp18FR%>EQ1D#EZjE}Eu?zP&c6Z6IoS^$QrnKFW$V3k33-Q7xt>5aPen4p4Np1DCbURB@J^%(x zJ0H|p8P^Fy!#=07<|qbdS|G7)AGVHg5HuEC5G&-7ecKB}U?llKY3+wRE8MkUEgSg_ zNp8xzN_y$`587+dxFh=vzvyDujH$s-YPGI$n0Y2Hj*rNwP#)s5p0uCCqW}iURq6w? zznR$~_e@ksQ^xy?)wR~WpzUF}ycm{0%|{XcNdx=KlDc7l4jcd{_OTgM=1kOdK7WPm z8QQ|ew>HjI_@46qdww#z5>W83^PYcA)7ikr-qB2D&xC*fM54>zg?I0om(p6Qnvg)= zmfjwyMxvX|qp!O*9P>v^k7;!N2OE}XIJQ2V44M{W$BAv=3nV7mRGVc}j#t%r0AIeWJ=4`l z-1uorM5S?TJe%E95s5%&#}|B^goA9UvZ8)$tXOmK=^8;~U0PFlTlNdYlX%bC=Y`+> z{e4gW;%K{ZCn?~xti|MFt@6U`UPQWF`a4AUM%|s^9M@8fe^1w2Z zw3f>Ih}vAReQohS+6y);ZxR;qJ;q@YPHm3dv!Hw&;lu+f%Ysk*^uIs34jOC^hvr3% zics&5Tmning~2tP*jg&R#K}SMLS~L|`62ke1gnHi# zr$Us6V_la=|67W7zO<0__vsRz=f}Uai*~uhfb(}}S|{JnT1x*7B>Ut4WhtN6EV*Z0 zYA!{zoEPkcW&JJLBk-O*nXCZjwk^#YG^IvGX_70Fhn)$9KTc}d0vJl5HtmQ(cm%13 zqNjv3z4@3T8K3L>Wmd^_nxg)sJs7QsbArGaIwe}AG6fO}K7+NJufFz+ko0&nUTplO zE#ijcT+5_aS={m3T(FMt$>D6aqWv(Oo5s>G*i*I@5e^HYq8n6y&J`pqF0YZ1j zM$4^ZVO)s)?ge!V*}asQ%H-FlxRl)uAr9uPwLR%t6@5_^Bp*xRyH8A>;2mq6-mT`z z{iZR24g}Q-KvqYuOIv$LZrtH3D?_j2i+dUjX1G=wJU6Q4POM}Uzm!EvQB=nHMUjvQ zKj@ju`2&{=*8NgVra78-%k#Rc!V1an{uR7ay12|bAu*@Tv0WZyoqjk8%Q~Ic5ian( z;5|>+`=xxscTX%glMX%_m--*l#u3rhqY@KOmu%E?z^)9t&k@Bqat(j<#?J!mHF(;m zrA)rEI9-0kR+OWCIzG)l1m$~&S9>5v_*tBzuIEdj`z{tCvaa&#Oa<8ifKWHq)fVAl z13bMV#2ooM0xxj222FGg<^!ViV;;cHc{_tRAX%OSxqMYQ9J5qAG z2A&h8nA<3(-1kyEqrYdQA3Ovx>PPliQBS3M_3P%hS8Gd!L{o7K>#P6pt9y*4Lt7N0 z82$Pk?Ep0Q@IUGBoym27XTLygWBS`i2?!2A(~4|e&)FC4VF#-{>%FG~kx8oQm>7~NZRW(J zv9t=L*K37Y>3n`FFA+2ptzxdvG`FT1!Kh`eA}29)Pn5UrNX_P-M2(>j_9iyfVvv7* zE9ZF9W9o&!Ue#T%yq?5DKIB%tjMebMhyS=|G?8R^tMMm9_9}7{Ol)jf!)DEkAnw}% zaY^zfqo~%-FAT`Y4XpIfQ^Ne^-foKWpS$l2mk~;3Og)b}wvJJw2_*8RC(FOp6}Z0l_nzwqD@Ikni|uhxTU;nl|N$gdX_hmRGE^=7x0? z;h6Tojx1$?Jm4V2-F1|JJI5)(Wtq*bc$^K8027WY(Q`$5DbO--vOt8!MAQqwnw z5mg_bF=f{rkLUU+A3VPJ%Q6&jba`>|f48AX%sM&CQfR*9YWWpo*#G7s^t+Y9@HDv6 zp3c0`QLpzAL^9yjdCqaJeS@gC_feI}iQ8DNjMU;1%pNBfLa=lF&!K6TWU$LJSP=RI zH^}lp3SILW$#y7%7eX=SA4-x|DD%1IkX40;ZJlMX0bM4)c?ao5AZlbJGv4Xh>G!=4 zj324pD`g@L-&$c|l}!0bIAZv?#C2~>wBIQTH;l=d!|2gtW4b2~pW9(;;Gh91Z%yPlDeanucCTD?=y`?;I&y zkXY$Ef7T=Cx6on(ErBh&CZ=!nCyb|&s6HjmvkyI^?@V3te^=;M89y_5iXdK>*kB`q zG=JeDe{IUEvY2Y_RS>4H4mx7!MaK86R!LB2TT9YNFCa`ts9t1`8%h30mhsg2Xh7Qz zD*2NNWoJsk8~lYB_2RnJ0wV*{FfG`n%(#{9Dx{eEcV8FMEyu7oJuL*}VX5zCqmr4@ zMc8El<&X|2@?EAJ(pe{)D+AGpjjcCJc>%jmduCh+bb|$j^*S5fiQ&1W0{2f9_gi<0 zq>@TX$6n$whXe9uvZ-5RL}(_&PtYOrS>3DSPi~YSn;^kyc*xFpc%+2QvUgFgcNDEW zQZo?}?B8wD#Ut)619>=lDi26}>>h0hvL0lb*Krh_>MtDsw(1mn=r9jos*nKnA5L4Z zxai-%>bHYWg~#QZ_U{vYNpAxF1TS4}J{C{b4_N-+pJUh3<$0?M+WVsAINDb2_}F_1 z)w0sqbCrB~RrP@t)2h~HyH0V&2 zPl;JqIKBjs2O<}oEzL=aB zX*t?xF+4g`J^3eoseLkfxgOfck*lkEdrUyjdukM*TVUMo;Hb8ex;6hKXvR3khW)c{ zaRE+E?)0rbZ=UR;!`#l)Q@*Kpvw0UOJSz&(&H`P;Z%lg{2fKTdKxY>}p73$h?SjV! zF1C^yFI+M7|KU10bt0H=t;p@a)-bB%6YuL7+MJNkU&ilKGQAc53R?cG z^j!YQyL6x0I$BCl-gjS$@jIVLsyta|_5!dM26xYf618?U#$9&(3+hnu-RO~!3mOH1m-GGUVOsOu< zf#GGvuPD^ill)!EtjxAFF5Z?LO`CqG90&kn*2T=5ZHu!D5~k6Ee~cF1ouFNw@LnXh zlx%SBmOiVOeTo17UVvP7q~2Y+brmQ>@S4H8j9$SkWh8Hg9_F>@2WO|^pf`DYe|X!i zCViz|+qBL4zuf+2x*@C*fpp1VG8O`;PONUa=8!?xWPx~0^6jTW%5o1qWg0C5Ik?;k zc)8`8H|@zN(`21Hv8}Sk!P+3tBS*Qad-y{UDHV$r|E)TJZAPi%_VWln_7X#+p1Tac zk#*veULz==k@Ll~>eic?p}&I6bU1D}wBgB)?ILyzQ}AF_sLypfoGtuUh!I*O{80Xo zYmf%Ub|(+_QCpqhR{bTcA+d;jyB#k2Fw^00kveTh#A{88X4{gZx+kRM`JZBO$9VfW z<%zTWmE6*nCJtdrKcPYRj=$-)dr<;sW{e&d+-2i=k%cmUT^^*g7&(0b|i9EOA9*62wjXMm?bDEvJ#uxCD za7=a=_B7oqj2w%6-zkv@ zhPd|j24S+Y%JnNQ##|Qv+4_pb^eP7H)S&|KB03~-X7rL?WCO@&CgfB1G;5Aw*0S52 zQuG`gCbBhT1$DuQ`_#f4VqY`e2N6j(+z(UFPG_OavmPcMg2I}e4FK_(-Y=Bkmbh6w zn$9BzYC}a>(x@r0=K!s5dRLn*=q1>XyFbnseRth7&p8mM0`uguDfTMk-jAnW!rlJm zT;S_e29MaT=?bY`B}25G4c%sBlB5RRMTDQM2X>Ydg}lEr(vcP|vJcUo)ei;7ty@SF zZOdYyrWnV*Xn<3V$2>NzFLC&gp2Wmn>s$H8W$NKCPQ}^<(FL?nf)bc^RK3^9AG4joRNs$|Zd5cC zH0KMfYfyY)kbJv4-oIa@4_n*RXQ!E#el%da)ewEsd+UclV0MwG*T&D&w?CTjMb6_F zq_ON83!g!^r?l%6+4-pEW40A5!IR*+IL;ZOtXN=QqdSTKpwNNFlSwdYBHOb ztc5Xl6PIZHPjP+TJWc5MmFzV3GdM-wnOewr8J$rEASN0{x`{3nL!DqMpQh4Lf)N^+ zDT_BUw2nOYo26=5i?MwNH4`TEF1rgo7U@}0X?d`_0vBLiXs>i+3yTSVQyaUrn zO*Jq?w%5xzyx|-84*!DrsttqA{hLlEk=+_mk3Z(KiT{d%&iu%Iu{LbkwZL=fK{=Ro znWI?ZineiH>gJ(MhzV8Mnj@S^C-L^1a5Wpn>$r}uD(+*D0J3~-Q;`Wr*|9rRI_}W_ z(ecBr_v?ez(+N=apyE$`J+ki(Me<`!UNJt2!5z_DkJT3LePsk*pPJsZBd9Y4cCn9( z+$Yr@L|Lt8UbIt?+=oG@st4`X?a=bc#{52lGu$Y`e4~!O_A1-o+*YHsRfOtXEMK?8^lKs{BD%i)U3M|ueNST?(9w}MYb0AGD205} z*>)pz-gQj4-^zKZJ}2djNI)KpOjTI-HCZZ}iT`VOeHaQ$SL10AFy{I!vy>id`gFqd zKo^r}EA$n!Xu&iGukj{vKVXTlqEglAc=!|ceRg9$jgGN!TM^EXMuN-heIRxQ-}>{l za&FpEF0h!l+Hs2ypZtAm+77#^NKaJ7Z0o3D1L3dnwe|}S!nbuKXFt|E39U}ZN&IS z0>CWo+6$05f-?ymy)CbnqFwI3_AO+kVM%e_&mz*htvdq>rsJ@JC|@RD?kdYSA64y~ z>MX_l`=vZ)m=h#%68*mx7TU8neEQRl}3{u7xyj-e?BOM4ypEBZQ5YLijm2Z1^-8 zVsHoM{ff5VuMPWHJZE3Xad{o#G%j#_vMn`PO@05b_cydqUY*B+h$zA?`oN zTOW4wlOdh1{?%WbnY!oNZrFBUS^i6rpKCTN*M@HS_Qb*&b(JH2E=m;E{gQ)9@rpCe_r;(NET* z_4+~;GL`7KQs}5oJxce-?7$~x$7RdaU)V2*6Z)n9)8)qHVF2pL{BnH$-%iZsg+4`} zJBEJuEn75}J~A&gQ%e5|AuGKX(AGSh9=WCB0lKdDRsNMDFokX%YfVXeV!t;6YZ^&U zqs@xU+yYsgT0SQaBlR5;V<)ZAZPnL1>a)3>&^#$F&la4_uNSS*hwqwotsXnEXwH{X z8_Hx!0sK)_?>P=zBU^J{8+qtuZ`XTkXz=|Lw%!H!DzH8b(^YDtyB1;5t30;L_ZtOoZJlIYc3t{Y zTJV=pz)uleT@v!B#bL1V{f3b)$6FftxU1k2j^xX<5w;q?2h~KI)R%4?b&9L zmuy%sow3h{?eMJ7yH=LVO)sfrD6#xcP_Z&vffc(_Ba(hkzDS08Q=N+XNR?3B6MlOz z*O^*K6xyOs*J}(RdgTs={WR^ixUJb&;JQ=(6w?@w`*5zVG{f`As*TT9iZ8~nb{|5P zcNo?;-Pi9|YYTV?BCVv6+Y`{JlxpGnEab+3LxmM~BJqvcdRAFt*byr%Zg;R&|FHIn zSb9%!2ck5G%roqeK1j~p9RBZ7nI`se`k@Mt0!>Q_A#qF|8ofPBM({dh2cj?-S z0O7#gs94FNc*y{$5O46D?v;i%VhQ1Gl5o?&6R$#625fcIu71!nV-Tq%4u zUNfGFtljOgEuGN-TcW3gYXJ(>Y{zN@@fb<=1 zN{t$J=`3tr$HEtRiMsH_-VM(ed|jq3M->5%Gz0R&*bH| zq#%)wVK)~&`eD}(fX!TBFz#Lx@|e|bVj}LDC1S{g&~Q)l*v`UqguHxu_ zxS8^(@w3gV;+3rXQ~G_@KsK&$xr@lFp*^I7+@sVEALJM)Qnk)_+9Z2s1`d1V$D-b2 z14L!rPSU`QT!OxvGp>ojbw*WZO8^d6;F)b^zAs-%Nb-nYStZMBRC!Ri+(JH`-}i)0 z(92?{PiNVY8xKii-ii5j&Bs_gS;5O8L0L2fV$qy`Lcz0LS+>iwG}DAiolw9Dy$LBP z2FHEdM$vO5QwhA4_p0gQRBdM=@<1dEpj; zgrc-9i-*IwDgJb+%R*G@#7BIQymQrcitb&svFR+_I}IGB-p|WUFbb2GjxX$*O{J5w zCYD3Q!^CN96VuUg0qVKnYbkj~jdtR_wR`IHr*Uq_(+W^Is>G6Qu!BP0eSwCx&|h=9 z%ZmIi0>&g0#^pZfglf1XjhY!Xpm@~wlXb53@I%W512NmlyoJ}y2H`zqc4|I?l4<`I zpd(+(?9LQag9JqvXu(Tt4>B%00`_=6?UGh~b3NI&Ze%_dl0+bbTZ>3Y1l3}Ru1g?7 zSTkFxqF23qt;?hb9n6O@#qxhF^)2@*;*kBg+rwfVxO&3b_GmPKhl2@2uud(MstHpd z)92|FOL1%owfT)(zkB>S(dE!;m2KqIM!6(t}|~5;1W)a&i61F)p@^7(8#K;~Ka!pc++gG<}4*_Gq$9|Q@S z(Hn7?wnz`R8?P~dDH~Do+?3j)z~$HLYMF$Ih~Hn86K&3nJ?>2MaWLA!# za-#kj+P1g6vsaYp=6#$3$&U;wW^UZp*#=teDw5e$27F&WGD9BC^bq+E>o%)%O4H<9F`%T2p6)6{xu;h^0ucI0(uDVl>Y4N@89W)#cj3 z$NoIL^;LGlQSiUp$P!zWsrZal`N8ZUI|Hccz(hmzjLh_!7%0{FOUl)J&|vWEOOCvQ zMT|1dE<^9pUnJt-8}D!X^{4u31w03j*?!^fd06xs74EH?mYjc#n$q-;-!PmQy0j?L zDvZR+iFQ}SOu_$=tGh85zcat<0KViZZLFhi7%g}mr@a}gpQ3jS8?1*t{_6hKb{LH%&Gi+dQVOS7}GnnBdkxWC^1!Sv)67+Yf0qb==A~{Am-C*3kBYS8zMK&QtynmBYzN0gnHSO$(pPV6( z_{(;+QPM&m0$83M$$>YR!*3&%zy#pUC4+7BQ=bd1?#_*w*bK(0P51N6YeU1ZwMWEj zec12_9&54#HTY-br?*Hg%O3Xn`N_$O#!Ra=0VZ8qFd zYgd5xt4npomK`IJA7DcMm6x7n?za#N7LC^E!)KYYr7y!+VT4cLn3kL~ii#XUO-%&t zZ*JL}q@>1$;kZZoF8p(snhRcYcMKxoV8}!9^618VH{*&TENWEd z_BRQ$>>H;9R+%FT8=~_+_fr&BB00H@uLagJpS2j(k5|)le)bt`_9wHWPT-uB#8al2 zRk;6_$EUtT*LL`>cyvzu3!CTYa~-#3oNTU!^z& zr3R$}?e$0}p7p5YcvZ5{e?45pY&#VPy{a?jf}4T|^8ckHME|H$h`@xghN{)8*qUj9 z{^FSmGvHsQKX^_$6QYHUIZQ6qIf|9%?C~50rdje=+XdsSMeZD4$0%Nv7?c2O<;1;+ ztWh)TESCGpapO;>hGM!-L1IU-?(Ts6s7NCA+85T}v0d;N&rwv|@>e!N-GPX*;CNzE zmEFZ5kX?0$di#``h;+le8s2XNTUr)xM?Mbbu`0}1WQ#yBKJfj|{AJ#El+qwT-WfjF z6v9#VVvjp-tGQ;$4YRDam3^U7B1z7re_d8n;OvEC$LxTavuuw{{#lxu)>c03(%V4W zK#DXZecGXQvM{i+3pxHHX-S&1&l7>sR6cn7A$g>HpotB<3h~@nV|WhqY=Ph<{7SQ9 zqnxBO#&W$nuJp~Mqx&-1gOK>YCvFGBs%ox4&oFKXG3kWtIrqUm0es8TU8@v4=R|b{Ld6+&#IGo0S9;V3{ zS^G43FlQ9}x<#RMR842nS}!U(bz0h{MtW4L28b!|rvt^S-XwdeyY70%z>YL_(sLK( zYgcTnCRhKJTVvmb9-4tp){6rgA5W#{{8<4!erhxw#$(e$y_C2}3UDnwe9e?sc*Vk=0b$+qWx z%{rV+W_x{Z`rHDH`_JZ+&8Ja^-}7Rq-$`rG)>sSTFZJr#xN&?Ej30DS{1!mt5mIp= zQ<~RbZDQ>U7w2|jvW-e&0C#h$w5)q7@=>h41<&^z`Ub|u$A$E)=k923)wn;m}; z4Bxx=)gQ1?8>3J9J_#Bi%2#qwk~`gJuqNjX9vGK3A|-4fV8B<4(X(6D!R~G{`1AP^ zU9Xro;LHICdoOgxlns+zEzj;#fA#^f+Ug-Umt?>9`Qgw*6{*m++dI}&rsD#Jg^gX> zQE^vxu01H<-0J4v>Zul(QrT%D;Wj;Wt*h5qMjbeqj7pw}Bt@+E1K2)paj;N+#`~5F zmmGr@y! zR3PZ`SILU3z5NhUI|_Ltx*FGP8BLNJ)$N*|NQs7nn4Z&mh~2GF?ppuKE_wHKhx0qX zwQ6wYQvC*nB3OwWoS{ITwnZD8L1+lqP8u33xt9Rw2TMgh*W;Sd4J}7!u9~xs%TlR2 zYAKrJCom{;=qo~6hrRCOZ?FM39z0cs-&mvm=eZI8B6V+UtTYNyFgj3CLbMymjk<2G z!FFT$eCnIW#wj{5BEPA|N>V8D_aUqOT-yp(@nPL#BwRSkKc--*VqsN5{v~fR-9G27 zL5D6>!s9Z^@yW+4oY{3jwEYnujm|v78f;8Z{nhCxSW=w8wL@&`nJouV`(KuApT8O3 ze?FF@Iry?gGZ|~YGs%hsA5(E0Pn^|Kw#QBF?HG!C4%XC>{Kkru7ILIHA6bl|50%FM zgTf>a;E7c}77}|8*n1jru%~d(oN^+d(HWt#xF(Z_^s0O!EmkF@qn#(;D2zMcf@1y9 zbj2CQeZB+8X)=nPU&c%oNZ&&W(T2PIM2A2+jT3glBF!}%1(r5%QMGgOPlZ3yz=Nz# z6zSXG@xKwHFu?mLwRWv5WcVg)*pQ=P^HaT1`RsQO z)JhWa`5QhRlCcW^*sdp1d)nVc-XRgIV*H{7vNdnV(JgTjj{AH^Y3`Me!#k2dR59@a z{6E#@0Tf?z_-mXxPhm8?M;J6Djg9W;=M*U z8~wmHLyefk?|Nz{koot}1gJ1&S?9NHhuT@nPCz$SQ1JI@{2@-tDJ)KI@Y5T`h4kGc z?71IQkxZiFvRwnuTL(GSB9ON~mPC*;NKzAhbuTv=W&z}Y{H{%lcLhb}8yR?!dv)5P z)fopV+&4QD2AAUxbZz>P%gI8j{j=64@2fnOBv)b$gKS97b$GnY!^xX45`wbISAv){ zo^RRYxO}m;=}d`dNC%dmt#us1YiUeV{cHSJ*;d}^5G4y3Dd1?zj*K#`kad{U{_u(! z;180@XRyE1ie7B@TH?t`p{`ZVvvW2L#u<+icGtoEnny_(+E-;;4hICnG*3(7uPR9% zDxh)oXszsftsMcDgkQiLyQi;AH=lQ^B!zpL=yE1qMdncLAPu-@_fx=E1*{WMI~g^? z2cYkC;G;~s&pmezZj2C}9fkNxXQH0p=9o@@jheaM2Nq=;@mH6%_2q0sDsj9n#A;%T zxPZu5fi(12rp6!_hIVniZVkE1FYH*|vsa6J8gLS07&u$($DO2HGmbrlWU3$q_jgUG zY*pGXd@4J;s~6~_c7mzqwCc{>b6~YfTmgRMzme3W1iQJ|^19+{DNRukIaxJ_zwloT zC5=Lc+m%kd!I|7C?@INSuvIKmrSWF!ZIX5G`JVulL3LD~5LJzES|j^!S`Wt0>hH4I z{?LCKXwsTR#fq>dhu%bjQ6M8yUyv#D<1b1O`8bz3R(zmP+}Vy<-0^6jk5 z-J?3)C*@c@^#WGLB$h_SdOexjI$Ij<7HejnQ&4@tapZCMBda2d5yn-&*=S zdJ=l$4|>#E1Q!x4;%}{E{Y3-gh!*}mJWf&L=Xb736~dVY_~=kG`v=oZZaboAX1z_vrK&<-aHdC5<-P zCARE=Bp<(ijw>`l&Ln&54AUJ-h&LnNpHO%RL#!HwnolU?K1$#@cmr)k?RJa34iG(< zpE^<6WYWJrNRUcep*HZl6*VI$7^XGdtn1L7Jo`@ys zrA~Qz&J=tytw!OXSgTpi&W9`LAjFf~ytmFleqy{P`G$#@OS?mn1L8Dpxk+*U5QTkW zGJGEUn~@;5%j7fsLP+1^O22~uUTcvom9()`U_&bQpQk!%s82bc`|~HJps;whXNGIi zugl6RE@4?Ry`c_dJim<~*;Vc{-J+#%q$-XR*orWFLwRyIDny<0?2IL-^3BNMwIQH{ zj`jD`fXbZ6LT`^SdPp$YntyZHB5Ch>=7+RV$Gx9G?TI=|i@NR)JHz)~sT6jSj;Ri@ zT0q*LV*I~2W-KP83(vr!x8ruJ!P(A$H;kYI9R#iDq=r<@s7w+iq@9HDcXvR0NB`cW zG{g$~(_rQdY7ZA_8-;5*O+k*u4>mld=KNaesuj`YAXnsRedi#2xoX88W))(+`0PBF z2l+$tK)0Hsf$fn&((WdQ0qjVuu84N@NVaTkyNZTmhwDCX*k~WbMU2VMiTZTLrN!x^ z)OreZ*q_bknBIfA56_A&N#eD!vkQNS3Q5~^FL|LU7T=}IH=RCQ!27+K@N3G+;$>4; zXyW=qmsHS7rY*vPM*Q^YXmG#mW0JO=woi^7xwmD#xVgi~2e2)zDbSmWItwW!T zGTcfv8yuPiIc#&3K=kQ%S;wjm9m_X;la)kuFgKTo#9p<;Nw)Vz@Ss33LgECWg0W08 z?iKhzXk5qB#&-jmn<3Os(N_4mPpak#OvJskG zrj%MjZPBB{|Ng%h;5dKQqj&Tmp0Z-Yz}|XtgUzlVgk9O@`2IiTYr5E~EWAF!Y>7ic z^JUaHSdFwa8Xs^E&j_ZdBlvpa#00(_dyBOD0^e7=CKm zzeJsp>euwL3TEMrSo83@GWo{@jP9sM`)z4Usf{m7G=wLX05<1xR`BYXr@``7ylcQHv#Us>Xig_DtNR|M4xI* z^?U4p4bj1QQS2Rqhr6L|Ih$Tj_n+dvMILq6C`|78HOx#4JjL3=%LtQ*#S-zQoYCo8 z>OW2|0M&#_Y;|j@drv9+aJ{axeF%smwcZ&Qaw9B$I}2MdLjR9uJh+hhv=|3|84&8; zIF{g0yeB~8u{MNwuq{xheo!ayxa`@lE&k10(NrE3%W- z^{TDvUIIsgELTc__W>=`ySiAVapt;~6}H}qz!A{phH zXCK9_tsESyM%n7sy&T^7%ga)Z<)F*59JL;nvYh;FHKXsRM~`<}=k-*|ETTDL&el=!;OTWe1?fRe3Nt>2jp`bb>Hn-d=6(30Q47%y2iJlap? zJ)WlN4WR3Z8UeSpFi|O&Zh?vng!TLNd(egJhao{b1L*R*SujU2bnkv_K{GP!L00_< zP4Lx|xHCM89Y2k1;BqcHl%;Q0$F%kLB|CCO0x>M*$4s^s3lvf9Pd%P_sc=V4>CJPAGo$&p^)+Ubix|&| z5h00OTdarh?AeEW9S~Es10Im6f;@%kyd*& zN3yNHJ-WShZZaA9+PIvvn-T($Xpv+ZKde@prs*}{T3)j}R=%deT$Zk_@6l9!q;h9@ zA0CB*?q6mCdKO#;%xf~dmnRD5T9(&a9ML_lO?J!z*KSdH=yvJ#p z%5*OTp5GM`(FJcy>hv4NBL4(YebjTTJuI5K$5Ao+F*O$Gs@D>7;0=- zEF-GJS{Mflu&1tP3Y^@U>{L45!keBY&D*Fb1nXDd&QCrOL*mSjK{4* zC0j9j8C`gmMXB=8vzB!v{c1HzkyHyRwb99JdU99|S0d?HxwvKAUs`!gA9ZG7Mh_&r z;3BPno?w0?+!uS{d9U5V#$78ak%_atbg+!5>aFh&yVSH&Y^=O{srVJb59@0o0MtwvgleGwyyvh*#?@aBn?lQOfDs8^k=-e5CXu(IyZ zqE%Law(1a3EI-@V*Rx=I-lNRM6_rYP=U%yOKN-(FL(N*ilL3d}`Lo;#nbH z#<5om&1A_UbvfQ|mfT zfuvt8)hn-C+xquN(vlI9LGP={aQ9**wKOCWGhmKs^Ozr$t(KDOb^H{)^NcZvywE|X zmuhdP#0}x*8&dmL|ySsC}dnS=DqZB8dMkcAx zn;Y413Ipl`C6_XIOC z;vqGly)5K6yaKWTjilX^4%m;_DJSYD1A|Ut|S^7>_(71jXP$rti8J+U`oZ>l3mB zU@TzO@0;iOq>ZBlZ|f%lTdfeMYuj1fdTUYDdf2T^F&IG4TBTcT?6(MRwL0FJoP^=2 z$5iDd8JaAkw3SysO?PFwTg=X49`(NNgS9MzH8*QB>$BoA=)QX37b(rL?_of_hSiI} zoJpu;?hC8txW3*sDNcLTD=2&3vIWBPJeOCmUY!{R2$&hFJTd}UD@!k~h*@B1w3@+4 z9HQ5IfNWNr=90>6(Ya{mT`^#JM;NWtX+3h^T0CNcNvDG zB`J={RFAUr(QgL)QQwZph^WmWs$r0_jlWo(7sf-^f1i6SWaOc&-x%FRwD7B>|HyVYEP%eQ%GlNPV5n(y!i^ zL`rd}>zM`EUXq#|s8gFq)k|i;YlEsa;86nu&ls_+;o4YH4YjnTz{jm7${~-;+ORAO zI3r;^K%4I#)q{R#g;K27P!>7s@v)vD)Af+~P6jkl2^s5^*CarDIX(ORK4qL}fiT8H z*Ppf9?aua81|+?vG?_9phT%nq0CT=6E%%zmg)Abk^ObD9r#)zykr@HG24E|R#1lY3 ziF*24WT~9C6e9{IN|I=4+nE9MW=-qWGhf;~f~~x-sHDSMe9`7t)17K7kM_BG>X-7! z(fUCkj@T|F^~{{x+7dPLa5LU6D@v20og+muOTXO$BUTm7`s{31Liz6~l$vRG+9o9A zQ~9lP-34@%?1d~kFWKdAIF#3~Uz=1q$~H&kXO@>8dFxCzx^-PTGapgeuOx<{;jfYO#)4ii?{;hU;nt*IEx=<8)C>y%4TH+qu&1NW zP~5e2yw&v#b%xX6UUOep&wjVtnd?~JE6ZS)<#G%tM_tz`!=0wg5D`*mVw&>Yff-`IwVz=9c zjbM=BQnPND62O|u2R5t`iPNC>o_Xj|CM7Bh^BSyp;EC$NBcP9B=hB+A&Ka=-@_W5L z>t3yUbZ)5Bt(AJYzrR1TL?hbW+BCdlyiKErC_zziMJ*#HLP@AZZdW(C^C_S>kABlM|)VSv`Sy}H$97YZ=K@5SWUY`~~%4c&p98SQM zDPQwK7S=t?c4CX@ZFWDj&X;KlgZ3Vz5n#kK*<+)tNeUq!br{ zDx$Smk3DNVScZW%B$yVmXhW_AbgQcn1L)cUW~oUud09~Kyu?ggD~d7dIiVL!#_MOw zhpy$VXOeK(&;--K0hht<-?vUVGQU=smk2FKq zUpf~+Gh-GQdbuH6mVsR>^l-_rO^@TKxxc*0L}3zHdkWoT`l#!<7{$@iUd&`{BM;VN z6|8ges*@t)XJtHV>ggFHZHuxsdE6X?>?fpPk?)O)gZBcA`E$5BG z3Kl@__xt0zW1735aAYRmAY$`8x;^?{2H0CzPZXMAGV3Ab&Mwl7au!27XS|n|O~nIQ zrbI47i?V>;t86V3OSjt3CcgOr)H{NC&F0>e;Ah+;uM6L$2a0KC*GlVsyM%ZB)TMJ7 zZ)>gjoTj@n-LIg&q~g1b0pfXg5r}xIYc&)x+7q*S0a+%GY0lz>=Q1V7Rs)<`qo8IF z)%Sbe@Ar__UK^o!a;ljaI!cDi04GCLdL?h3pp6!#(cZU}NN3%rjPYP4yVL?Y1LWRf zOW?U#x|R{pXaxvM4l&zM;#L6!)%G$S0zHr2Yoye>)R0Yf%YgY7UGrLWH!M0i`o86J(_%;=kT5|lzT>5PaivbpqeQw@e&%mvLwCnZ?&;RUI8mJi>!R< zO`-RAR#tCXld<+9ddQ{hH}aS>G-ni}%G|4{Jnq(jzDLuaYkTNh&G~B#7GymzWJ6`l z_ie$Pv7S46UZ~MZX3evJjtqkc(6iz+()ndQdHs98-IwAU?g zl>LA8Yx+BCCfpjw(BCUdpQ@ZR%cg>QcOv}Tw2|UGpd9&H6>;~+y3TP3sF|k|l%B|#17SOXQ ze3t<88hv;#iL~c^t_5_D0rcvt9~=u_p)spQ<}rYo=Z`ctyhJ&jtcO63k&TsW;L=Q! z=iNulB@)+(m_?THVc6YScROos3UN)(z%oGegtThmS0I;N-qzR9@$#D140k5kp?5qm zs}N^-1gX!O-VpAWqT4;HG8>I$~TKI%bkZVX_@bs2S@)U#s+~RwLNcVt?bIKdn*I z1fti*R6BR2;fN?uE4e7roxi3mY%eh1@?2Wh8Pi$o@eQy6F;nlH@rE@6X#r;h?5J9X z0W9yB$a%0RcXZhP^SZc@2RX$w;<5BdR&k3gqjW1X}J)1CVUo)$#&SUu_Lo+S85p6sx?>V-q8` zF?2AD_sab_tI59G?VxubCB?0IR5kkVOfoUUuFXs*vKElZcu+lLVJ$-fsl91Hx9ZT5 zkPcb|*=uO^5WG03m8H%CoM|1G2Mzb7k{xIjuE~_?S(}NMP=Yp+YsYjE-NyDOzgEWk zR-!RmBtO-}myZ;>h>yGADxy`3JeHil(fnt%zL7o!Q_hRd(aLm>*rKcsJM!X@k)=VM zF$Pv3M_zg)k6WWM#~3;h(4SE5RvDMqz$DwA)N9UyI!m>P%tYVc5@JBdBhZYpb!&45 zB;|xaDOw1qZ6TUOvi|(=Sbmf7%B|08p-HnfxQGR*HF&lJB!(@sp1sAsA6 zOGv-=$a0q;T?kfcnx-<{KROLUKACL=jI`WYDn9b&Eu%5&wB}LKvN=l@v^~Zmn-ph# z7TOcNy~4ZSURf-&KcV}d4e`uY$pI2Wi2(ioZ|`ciTh)1{P3XDj_x|3CI%}O4KR<%0 z5&9U}hLFZyYwxsa5(wCqG?Hex{%)FQC=1fc+zygMuh}>)xyc?Iu_Z`@8u*&;CKZox zxhtt1!>Wkg<<^ZH;FWN$)(7hOOzl0rHM`N4A*~M`ncF=o&U>N@tyR}|Yg>cUWQo3a z+7-Eb^0X8J_d1^l(!)K+MV_eg8C5y7B5jN3y=)CbNP4`mIYlNta@9?54Uz@XFbNTX%?9>mz!q^-C;h9Z34N_XoX zOL=2j2Y{d(NP8c3UeJ3}<5)?l0_(Go*USiCtmlT%2*Xk>vnu4Jh`Z$Xxr=7$UMbhE+RDVRb$jWlfB3Bt(H9GoGhqKGIq#$ z(`eCS(50d~tyM-^qY~<4U0Y3QO3U@Oqzjgif2zcsI6JX9J#pG ziAfhg%13Aw@>b=&M_|RP3GQ)rJo&^P@MAB7DjLU1<98PP?rlLk8(O~GXIYWGH5YrI zOAX|HdY+%m>louMzm0PI@3|UQ$Cn^mF~sMivDct#VMt0!>vnNb)~5RNwh&3rxgqH? zPH|8|HR!z5jI{;nYYsZH9(%0q*~+8T=6%cDJSAxbdAS(@gSao5z=tim|008l~{ zq?@AUIT6GG&FP|xl>GocS}V z!&~cNAd;6B7kte>Z-lPFikc%~-B53RK<@~)Mu6l_A|+lF;T6XHE-h|%8)uM2-Q{~d z9uKpUTH2s3M_X&XtU)(=+ygB|WUa$m5t)`fuNDbIH3)is4}>XBasug?Q3!`yptWAT zrBv3k;Adx(?JzACX~NRt-$E}U1UQ{gs(Yv z+-K7IhFPl?M&W`pDsCkYmGfTcIcpm|6{C?hX{+7MlCk6r{gujUJ#>;;s7He;Utr8N zb*ooK@1GcrpMhvtQk?b1ynb-K*{DIc#9TDPrpe5rNLH_XMY{J0lbhr)w9wm9=)5)$ zj%uk(EOgen)$p?;t*$$RUK3qgx!;mrwS*NU+E=>BIn^}Duu=+EP5zJO^UHrF^{WNT z&>N!}bna&@DnK?%)a$d_5^D^8da>a^+wyvCXf=7^PmJ7=`RnCuI3rRoo5*sM?olhV zT9=Tn5|2?Y9SO-{K&rlb+}qZ6X2dWf(W+)Jxn6RqlY|TStUh$<9w3soR?5N>yV3%0 zQ4ieol`ECfJ81uSDOZTen@CvAiD8fMRX z?v__atB+r4%m*B=uZNmz-V17v0^}~ODb8ruv`Ts$a1I$j9g3dqqSp0Cf&(d#L8DfU zvp_aByL+H)Z7=AB*tMny5VSx$@AA9nuC5RYaTY18w<1hwA@?GGsqy4% zA~(vm#Ob7KpRMUuTi+9Pq^xV8v2$^+t%-zR8L4~j>s7|wBBl5=qmM8rq7H0tI%sOn zV`&aMf=31;wP_>vVw~i!_%_YdZKJ^0H;Oh+&$tLw?$P>((0E8pz!Do>#=^wlj8D zhsx4=ZEfBp1gF)T zrnX6V@{LGAhBP5iuk$Vj{EXMO7>`dk-c}Utsc`rFt%-55H0lod*|H`_dL7b_?lDps zDb%XBxf-JUzSi}N^vipywozn3x?WbGL|X$93fCB8nCJOGuZuwV`QCVP=2m*8X1|#& zp%H{wrUoLVEIr(0(V}Y0FmoL8sD!GOW{@11nw!zHEeFX#H6Z{Yyu1(Hf~c-2lsnVN zd{Tto!;xx>&qLMv(BNS&XF3YemO+x&xjf?e5oCi zrbCux3E$t}P2wyy2u4e*9sPaQ+RTs(t5v2-edrQDdij2YScVgJ$=(@yuHL9z>YGQo zm}?h;hx*mKQNWH#j3En1r?9-#IKR3OZW!^l`-^tYAm~SZrMq9uNHZ8{tC4r!az$I* z8bE1Ek#%;KQ*y@oMQLb}TsBu5ff#i3`&dCJg2ZT${mdCNE9$n4yN6gtIxaQn9I;G( zj-m`T3&k7JOKlPw8HGnD{Vcew8Llpj`dP8M=lp7mO2)WvRPXs)OWRt@dD4v^HRu%f zMICFY0-HN_4`LZlt`{+w(t7oz5YgbHMFLe~}$2%dsh6ZWLso}7eoDb{VJ*^O^j&WR#d5j^%XRi;exP)HA zme!6QQGZXk_miUc(%@Kg-_y(A(s|Njj-ZmGo(Nt0e$u{F(vs3PHtApaIaGQTS#KGJ zMy>g^paa_1wXKL^z#luVh03ZNK zL_t)!+%UHtFAXSFK&z=TN2_bmXxV~;q{(X?vt@(tNlb&}&m^_Av}PYm%FJ4ViL)Z| z*65r@1?bxFum!%G@kR`*Cudo!WzgmO2(Wvtp+KTT&-~MfL@d~rb4oR1Zta&|xm1mU z)q`vxh^FL7AOV_Y@O#~7Z~aou-mw#B(Ts#d|H>(tanF5!e;;7;(wLXL>t&?Z z#+v+e1Jw%MxRy4_(ALn;B0}_c-RQ08zs4|g*W^m6N6&eC(gp)!=jVPI$!QU~^qLw+ zqWYSrTn^&XGw{OBnt_xeRW6du*9oP&)^fjIbOD_QTuT*qF4A|`kW^0uFR86*h=A0N zh}1+`NU-Jf(E11jW~bM97omHLACL{wD%_w^Ih}Y=K3ADU(k4mjY1+YHH?Ys9i&zE|=b|XDw?~L$W-%U(2Deio`4g zNJ=Y|#nw{2fORiDTp#H9R~L5@2YL2vW=MrQg?h9+IL(D46Wy91KR-WPwV@QDbJg|J zG=+cu`KJo1L9gE(4ZKuHD5^|tfMq)bZso7e_>LMYq4xYI4KGm+TI6}xYwGC)@zN)A z(U??2qg7w`S{EpXymV{}RiJao`Ycp<1`#%qgvGQqE#>@c1avgYTFsHQ+6t{meJ9Rh z6+t0IZ(2be2Y*YI>IgDmK^SGE&!qx5UGytA$#;m_ZDJW;%Ni#x@d~KjQ;hh@k-0?_ zWoai^5c?f__`A1vI*qG!yqwbDs?}SKy5-0tvelY_l#G*R&`Soi7UU4!bC3w%L)R-g zbJG3x8g$7)oGrs68D*qMjcSvlUZ+>KuzcU+@lcuKI8-2eIV}rV%d>dia~G&tc+;~7 zU4ScFub&5r=@qRbu;?cmbFZ41rKx}z_;+${k@>FCkJcg{y;5L^aVn!fu0I=X0V`k6 zT{9_v?9E3@fdf*nfC$29Xn_=wYc`F()>Amh*^e0G-k_H%XywKBRG@odbE^msg+TLr z=Xu_-#UV; zlVkQ8^(^hFr7ykcWX*l`2*YIMOD?jO3g75!y;a+iswaXdBWVK8xYsy6T*?wbWUcq2 z2ok++vo!&v-fB-jx~J`|wXKWuy2`c<+Qz}bE4_{QJHYELdgvB(XDu`ofw^*{9z%C> z5uzANvGAqet0QvdRpjET`deDwkn{7j9HY=SlH*1C&aFOpZGX1%>WHmC(jnUM5K$k* z8MgL8a>Mq4V>whM^J3w`RmbDipKYs(LXOqLC)m`qmigID?bX z2yc|!5}{k)71^`Vz)d?YGVi&fHm^Z1uQ6-T5lGD9=FL$tXRr}*7FyUQp#>6}pz+FU zaQoR2+q)K99j}X@#~rEWyd&H?3^x{8qgvhXGsn-FE7YKudnU&g)~iSp&6!JU)~a$x zB6e?-j6lxZHImpoQ3+E7ZAALeQAf|4ekosn=tI|vSn?a_Yf89U;$hF^Fcz0PSLI&j znNavXzh<82@b&d|*n})pphrQMH~0AJ{5i7d*h!sPU_)L1DoSbFlfT_d0QBib9brDC z^N8z?L5t$o>kdCBJ}#}g~*(8Ye4F9=|QUTj%x_f(Uy!bS|#sYiyB#%)KZKxuW|3oX-!1I z3_WkksHdF|=TK0T#Iu*9ZEKpNhbZV-4i0P3y{%VT%UantNsVer0qbpf(W2~SS;Bw+ z{Rh8KVv*}*)bAesA%qRZi=QSv>6?EGAGQ{;_k+S!x_F_a_sfJ+2g9c_q+Co00=zK>k zV;OWQaz?6KDYr8RuW-5Y+)FMMwQ1)<+d`zuc1;epr%~^@fn8rvKjO##-}u<(qiI)zFVVHWHV^xz;_du3a_J>s5gEVZq%_g`7VJIFl%J?ExT4>5!os`}AAp1egjNuRr#ROas) z@IFygkNn@+D8(3l2bfz^XA{eGT}zn(5ttL~Jc)XNC@mH)3${?@vEPDJa<0yX6HU8; zdtTC6mkeZSY%GJWk<)lk4{9hWgpKmg(|J`ZQib;QYvesrkYh#fbI9Ied>Z3t#Z$(k zLWtA!Or3^UJd%GCG3a4+5EKZm22Mp_f6jyP6e-khcA7T3r@z@#P}f2$ixg_m^EqsQ z#b$z#w{YSIy$rEcz=FW}cWIwP;MiJDb*oL)>x3gMtRCmd!$r}A(n<9pZ2S({|8+T} zB4UpRySqEz7(!T{k9j$jfp`AA?U&yV*{{DC4#%Q~{wiW6PjRuQkPQ7yB#(|>1Ei|| ziU7egzvo{gzH>L%$86-|?r&-R)9>F3r(m7&(`w+gO~a~smS6?ihDJS4?fuftX)TDe zh;cMDfcyRurJ=R)dlaBF2S}tT3?amno^v3@ZSJ6L8xm7iEqsm?xlo}8#M3Q5t4F&D z0$0=Dr8Y3Du(PF0#Co4pkXn+DXii002%BqKsC2Q1d9L=}q!1XGoHyQ_FA(Wl(z-9M zkGbe>_n!4{X+83`Pp$ty|5kbozlJ~N51;b-i>1~7-Llgvo|Mx5;LES^hwEh&x@43J zmm}(uI4Mw-H)LP1+ScgL`FC2JhN#`afslfbpy;JF-{*2JKPS`E!(=zQV)8CAgsF4` zNu-uS4AZ1D%V{cYs3?pE-+A+dJq5^ z?zOz~wZ`0AOX8V}NME-V-CNEjihOBNy5+ng*W*rtE-^AW^U}ggQRn)eMJpg%BXZ7w z?V$SAP;ZZhGS?D*LRb>-p&0ARqIB&wr(0`Uti4|f5G6hImZ-Zo5{aDCL7ZiW^@L+_ z!WKD%^z;~~^}?+%`z~7Eg8O)gfVsZ#^1WYQUtykSwx3msm^e8(5AS@BmNg3%&?s`b zO;DobEeU?k?&0nY#k~^7_NI64J`g%fDRSU`FI!Bx`9>pbE!A{}19PXrZqx~Tj}`$+ zhB=dFaTVfAT39EbG*mw+ge5GZ{<;KB=hkCS{@e;Qv|KqdcRjgw>1PR6_s&Pn!L|DP zEj6~9Jz(~pBsVd0?tCB3CpDtrSmS7fZbhlmMU>u)MEJe(M_#Yg+Tc(Yx_218qTegN ze-l^u1x1%FRo+q+FE#QUgo(hjXq)R<-|rJTK>F+$J zyj9uOI+Qm4lBi2EqFjW$EKB%VenMJwRyOIqUcPaZqg*q-Na19yF}EmiwZB`=kQ5Lj z#k(z)^gGC1s{ky^=_RAcd5_a$Vs*(mgUS^qaFnhbwJW!o)XKf~jL@Zt&A~?8WJB`t zmJ>4Cu>9WsCmVIkA=V7K#940fTKx6-Wf>FC{VMyxx&4M^T?-+kWeMqLQG1&;Zu1@} zmDK@JQ-v>a<)ix_Qh4ld=J)&jF5k+({TOBE=A{#<4{i+DM z^?G`uxw5d(GJ;$Lo6$>de__ungs@~*I-^=$GQtw|t5wh^37u%y5QV9|5~nElx)rQ> zs9vu&<{=10aflHX3)I#g%9NJyv&1kxU%b7C@~m?kfK(`lwAy$UTQBhxq$(3FOpzjT zl5T5ji^E`_SfqJXbr1wUyJuBT&C{D{YY6e|`J5kvE(+s_Kt*%0Cu8hVlb}a7u zmb~3`KakqisKc$z-@lYUZ_z`SkRikXat1<+w6}ZsSXd`OdX&}zqFN};ACJ%p5>U3N zp&@~)_cOfCfH&|%*8H4hOu3svjiV>ukW$(R6s9m8peF7j)IiDB4e)qoJtFnm2)(9w zsHVSqMC_k2kREl5vC)eBy=c?BM&xHi^B%|@b=I~0IZiQ5L1$0XB$TBz01_NnQl?(z zw6lzPPbbeqrQKr8=`m+3lA~Uhr76DsoYK}NAx-8|6TQq0Ez1(VzrS5VF_sZ+^@3_P zIxBDFfz3Ty-6MzYgLpq7hmPb4(IQyM;q>&G-zAUUio$7zhTGiHA~J;b6nX8Y(3b4; zl)j(VDyI?4)WYA0F+?Ngn#i2H4rtMz7Tjsa>BUw$YsD%hJkBvjsa3D>DN$h2QA#R3 z96UwY<}~Q~6;Xjz&5oiERzKqy*GE+EZp=LdMpjozi#W_*pCgu0Z!zSQniMzafmf4h z2oTUJcP#>~Nl$vC?Aj`M_}YSL11W0n9bj%%?zEbn8g$KLXDeVs3~>r-ge`eOT%S2d z6>CYM)=s&zv`H&V2H!CRI5SB%eyz^`|3G&MtM?MTR+b}S33|Fn7KrMyMS zN|K=I8kZ23m{uh=UxbY8MUt?IRnd*mxLQb)bss#H@0uY;;CP8+#VVldxp&XBT3s^U z)}ZWeo5ruQIYMZnbySh9k5qkcuGYX6GC ztx;R^Zv4F?cZnS=*Sl!lEl8_nEARZfJBJ)e3%%|VW1AEpsOK1BNHK(IdTja{7Mv_W z>{4V;iE+wvlMKBjl5(|^F16yJjCv@|ZSR2Mpte>PpK(laoN+~RYEz713A&@8eFkU9 zY2h4qzF%Ko5M3=QAK9|WO5Pa#Jg!C0LN-G9<-C6vW8Ty7pB1sbYqYI7m!4~0wz1N+ zE-4>!sjs^i#9d+3rD(0x2uE|ZRIA%_+!&u|X(4tE73YhO(HFR zH4@B7upw3aYm}}v-62(wd#_F8c%Ts0#7#&AIR%}}t(B%+1!-ASXKg~>ig2#?U5Y-? zh(-EL1hV&dZ>S!*xOjrB5Q(MVRtl(;{;&Qbxaf-ha!rn<^{b5mbR*r;L^& zuszoemcIlPlkwG6uC(VJ~+N@P&$T_J^S+|5QeY^&|f zBMXWAUOFJ0>JP^sOwWhgynA@x;->5{>eBu5dc5Uz(&iZwsiQ?MHAPOA(M0Evbv->% zIeI>l&=FDu>n$?XSRxSzQT)U;L>^u1bfj`f^mgs%`<=yC15uSeR}ICML8zRNM6E$bofmG?qHvhT5+sRPSWdvpqm^|3&C;UT^hWEwuQ9TCy%9*S zxlywwwD($QfJL4Fpa*tJR|SdnLuFn&|vY<9Ve4ksL;DU2-gq9M^*d*CQ#2-t+1m2JP0- z0vDP-q;uIKRN2sz7{ekQr&pU8cMjaSa*;Uq-OCgk(yyMtG$5ji)=ER`b?#d30bOHT zAG&n$%LR_W=i31vX{)Z}aw1xx8Es1|k3o&P2GvUjkL$hH76=MeBF4L>RnB9az3-Fy zUA@IO-t(*}2Gb_9R!uG4XXKbPcmGbt9C1vjbDCR?=$b8Ia*j7al2jdRhR69N6~#%0 zk4tN~9NjpnsUtKn@U)PX=>;n1)%T1klHfi=j? zBBZyflf4XeS{___FLgrlCiQqc4pF+62h?tgmN&l(yIa+g8tk7lyd0JY^obN9YKDwB`^X z`7bRx(5?egl=w=8zcU;GPeEx5+VqLgJDPIi)`^yL)*S#I8ulobNo&iBRz1etLT*tJ z78Ef?B~`6dY>!C2TZC?HWFY45FODMq3@vNECe3#!ibE9E>MTaTNWrri)zB&C2s{7d z`|2mLB|<93L!p_w309~%wT_#UwO|pa#ZtAi*za=vgw=oh32?BrS!->hoP&xG&%Aq4 z;aTfUiq17-&VE)}Ucw9Kd0{brV` z)XS69xY0|Mo!vqWQB`!(U{;C8ySx#c>XWp2Q=?12Lw?=Y*O!DKi!zpF*(RW;D6aJ;_Lu}# zK@I!b&Jnug)M?Iz$2ss&FiPM1FBo&lh+6DeRK!TS#?(+t(fKDs{T7v~w`~+lEv2xe zv~$K$+Sl@)d2`v)J+_?zd%DG|GuaEEmXe71j zS}97F`rkEz3$4nX_P=uziy{#c)L1Lfa)-zqiB!*+kS+Oeq-BM&`1SgpEn4TVfP*c> zOj}P{L8*jHc~~evWoXV3Lj8&qY95POZ8sc&;Srdft~;*hp3=dh)U!1j3zkq2;*?m? z{VR?7!Juo=Gq+F?!7pc_kZQP|b zB$0b?uhAU9K`eu>D-ZN8F;bewby-4)Q%I8*UB?h(dH;*pp4|sF4ZM5G)*Bb35M^aV{j3=MEzQ}k4ciS zmh`IRd}sz;D~Lm$yGz?zuZSLry=Y{?>pka;TLkKbdEfu@L{xQZB z;uJziQ6{ih`{Ywd#bXG|5_U9JG34XRC_&bJM`_KdosiZ$f`ark$}I@ooR$>Av^+1u zdTq)JIfduV$ZcpFy=x3%ic^TI?sBzl6&?9)BrnI%KWrLgY^TlHdjMQmJtMSgpel_5m zQQu9g8r%WzR_}w9}|tk%&hs=qcfR zsZSlr-S>j!JY45zY<{Yvur3}XLhx`>w`_`*Aw%v@Z2_Q%A{NmVu#C8cGNIQ+N-=S8 zxVDz9_2DUg{cF*;H%iy$D?%^LRk)`>BkA?`NUSM&%@ZpBWt`gu;aljQ9fEW?Vu<8B z{4(3@kN_HGhpt2YQ_&(FkFGf?v)OYKv($smfRJCkNqP$7Jam&a4q8pC zm?sLC98ybPpaoet1EILW5&fhA6=|)8E^d3`K7YQ;TNe=fp1^Bi)(MwSrwwwyxo+|Y zd;X^wLWcM4SxAm2=lZSs1&hnQHqF(78u}pjl%4^4&TWSy9Bh3t&A3UCFbcbK&soqP z!adI!Roa7o$U`Y@3yA*tD~M%!c##L{*VKPvdMCUkyn=3cXUn8|w5BzUaU{C;%r6w? zmmJz*j9=@_`sOd?(bzxk_PnUqpZuP~??zBou7@>;y~}@BuQ5zH;_%T*A3yiA6&PJH z*INww4szF+;=E!f)XU*|x49}>8d1}Vds=BrThTO;c`K5Ul-jI~O=2~&Hor3`%bf^L zu7)o?cx zwTklNqsEjgknW}X(3?zK#!*OFzUFGv~-yzBt_eRwCD#hY(4LxL}WxAU^@S$lv6JU#Oeq^@7*x+=fUmV z-a-iH!=op;MW&~*lC|ZS{uFXU#QHnPCxq#(H}MEqBKVU00g*6L_t)gFoij~mlJKY1@w_HzdztHyn7WvfLl-{8zt~}Aw2z#pBdlg@4kh^7+ zHEwweDbOe_kv{TTO3OR*xxZ6Hjzk9B?;aD`TaZG?_S=^`jp~`z2W<5V(ukS+e41v+7nJtRdp3BapZO}v*51g; zosBsvYlzA*dZ{>jd+XGwYtgl&IOwTNuQ_LLmHb(4n5cx!=n1#yJ+i4DdyH3dhL2Bg>$wDT_anmjqr1nr|qNk&XM~ z@i;&k*86z6vnM1uXvgc&s2%mTM(%rRw0)J%rfWNWKXh^L`t9u^$E9_zh#ju9|Gw&{ zUC*v(Z=U_tT-n&6diPai@T`ZQzs#$?&~a&of1uj-k4qeFzb0+nQ0XuCW9DQ9UHOTV zr`V{icRas;ITE@^GfUr zUr#8TxMWJ%xi!~kMB6?)EO1|b;iGuz!(V2E6;6K8cggZtK(6n?!1A?=b*+EQyk*;F zu(qr4OnOxI{ov0jqNihR?Vrt`FTe5CZj*UIM-nu%3;P(?E_ohx{?oA~PkB`>E=~&n zx}h(!uluP;#KiE`VkTa%9(4YBCpXu7cGtF*S5Np=bs3)TICJ{*!cB)KEq*rD_Nu_@ zFm;jKnbNUxrEapGys6ILD{SUfep$1I&F%T=O*yW|F8#|Wl&^^P4P1OKe17|Ks}&2L zJG@@=;`;@Wt144ITsPLy?>!&Se2%o3SSJEYw%76HFFVdQ&MBb@00Z!JYybcN diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index f6424788a..0633a8cf5 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,7 +51,7 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 255, 80 ); + m_inSpec->color = QColor( 255, 0, 255, 150 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); m_outSpec->color = QColor(00, 255, 255, 80); @@ -92,45 +92,57 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : for( int i = 0; i < m_parameterWidget->bandCount() ; i++) { m_resKnob = new Knob( knobBright_26, this ); - m_resKnob->move(cw * i + ko , 205 ); + if(i ==0 || i == 7) + { + m_resKnob->move(cw * i + ko , 190 ); + } else + { + m_resKnob->move(cw * i + ko , 205 ); + } m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); m_resKnob->setHintText( tr( "Resonance:") , ""); m_freqKnob = new Knob( knobBright_26, this ); - m_freqKnob->move(cw * i + ko, 235 ); + if( i == 0 || i == 7 ) + { + m_freqKnob->move( cw * i + ko, 222 ); + } else + { + m_freqKnob->move(cw * i + ko, 235 ); + } m_freqKnob->setVolumeKnob( false ); m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); m_freqKnob->setHintText( tr( "Frequency:" ), "Hz" ); - m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); + m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this , "" , LedCheckBox::Green ); m_activeBox->move( cw * i + fo + 3, 260 ); m_activeBox->setModel( m_parameterWidget->getBandModels( i )->active ); } //hp filter type - m_hp12Box = new LedCheckBox( tr( "12dB" ), this ); - m_hp12Box->move( cw*0 + ko, 175 ); + m_hp12Box = new LedCheckBox( tr( "12dB" ), this , "" , LedCheckBox::Green ); + m_hp12Box->move( cw*0 + ko, 170 ); m_hp12Box->setModel( &controls->m_hp12Model ); - m_hp24Box = new LedCheckBox( tr( "24dB" ), this ); - m_hp24Box->move( cw*0 + ko, 155 ); + m_hp24Box = new LedCheckBox( tr( "24dB" ), this , "" , LedCheckBox::Green ); + m_hp24Box->move( cw*0 + ko, 150 ); m_hp24Box->setModel( &controls->m_hp24Model ); - m_hp48Box = new LedCheckBox( tr( "48dB" ), this ); - m_hp48Box->move( cw*0 + ko, 135 ); + m_hp48Box = new LedCheckBox( tr( "48dB" ), this , "" , LedCheckBox::Green ); + m_hp48Box->move( cw*0 + ko, 130 ); m_hp48Box->setModel( &controls->m_hp48Model ); //LP filter type - m_lp12Box = new LedCheckBox( tr( "12dB"), this ); - m_lp12Box->move( cw*7 + ko -5 , 175 ); + m_lp12Box = new LedCheckBox( tr( "12dB"), this , "" , LedCheckBox::Green ); + m_lp12Box->move( cw*7 + ko -5 , 170 ); m_lp12Box->setModel( &controls->m_lp12Model ); - m_lp24Box = new LedCheckBox( tr( "24dB"), this ); - m_lp24Box->move( cw*7 + ko - 5, 155 ); + m_lp24Box = new LedCheckBox( tr( "24dB"), this , "" , LedCheckBox::Green ); + m_lp24Box->move( cw*7 + ko - 5, 150 ); m_lp24Box->setModel( &controls->m_lp24Model ); - m_lp48Box = new LedCheckBox( tr( "48dB"), this ); - m_lp48Box->move( cw*7 + ko - 5, 135 ); + m_lp48Box = new LedCheckBox( tr( "48dB"), this , "" , LedCheckBox::Green ); + m_lp48Box->move( cw*7 + ko - 5, 130 ); m_lp48Box->setModel( &controls->m_lp48Model ); automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); @@ -146,8 +158,8 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : hpBtnGrp->setModel( &m_controls->m_hpTypeModel,false); //Analize Box - m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); - m_analyzeBox->move( cw*1 + ko + 5, 10 ); + m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this , "" , LedCheckBox::Green ); + m_analyzeBox->move( cw*1 + ko + 5, 15 ); m_analyzeBox->setModel( &controls->m_analyzeModel ); } diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 9c52d688b..992aab69b 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -59,11 +59,11 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 20; + m_dFilterCount = 6; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(21500); + m_downsampleFilters[i].setFrequency(20000); m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 059555108..6a3ae7aa1 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -40,7 +40,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(200); + timer->start(100); float totalLength = log10( 21000 ); m_pixelsPerUnitWidth = width( ) / totalLength ; float totalHeight = 80; From f64ca3ff454641381121fb080e6c325473e70ef9 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 21:54:50 +0000 Subject: [PATCH 10/52] EQ Clean up of code --- plugins/eq/eqcontrols.cpp | 19 +------ plugins/eq/eqcontrolsdialog.cpp | 6 +- plugins/eq/eqcontrolsdialog.h | 4 +- plugins/eq/eqeffect.cpp | 94 ++++++++------------------------ plugins/eq/eqeffect.h | 4 +- plugins/eq/eqfilter.h | 23 ++++++++ plugins/eq/eqparameterwidget.cpp | 17 +++--- plugins/eq/eqspectrumview.h | 34 ++++++------ 8 files changed, 76 insertions(+), 125 deletions(-) diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index cb0300382..82cb11191 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -23,7 +23,6 @@ */ #include - #include "eqcontrols.h" #include "eqeffect.h" @@ -72,8 +71,8 @@ EqControls::EqControls( EqEffect *effect ) : m_hp24Model( false, this , tr( "HP 24" ) ), m_hp48Model( false, this , tr( "HP 48" ) ), m_analyzeModel( true, this , tr( "Analyze enable" ) ), - m_lpTypeModel(0,0,2,this, tr( "low pass type") ) , - m_hpTypeModel(0,0,2,this, tr( "high pass type") ) + m_lpTypeModel( 0,0,2,this, tr( "low pass type") ) , + m_hpTypeModel( 0,0,2,this, tr( "high pass type") ) { m_hpFeqModel.setScaleLogarithmic( true ); m_lowShelfFreqModel.setScaleLogarithmic( true ); @@ -83,7 +82,6 @@ EqControls::EqControls( EqEffect *effect ) : m_para4FreqModel.setScaleLogarithmic( true ); m_highShelfFreqModel.setScaleLogarithmic( true ); m_lpFreqModel.setScaleLogarithmic( true ); - m_para1GainModel.setScaleLogarithmic( true ); m_inPeakL = 0; m_inPeakR = 0; @@ -94,7 +92,6 @@ EqControls::EqControls( EqEffect *effect ) : m_para4PeakL = 0; m_para4PeakR = 0; m_highShelfPeakL = 0; m_highShelfPeakR = 0; m_inProgress = false; - } @@ -110,7 +107,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para3GainModel.loadSettings( _this, "Peak3gain" ); m_para4GainModel.loadSettings( _this, "Peak4gain" ); m_highShelfGainModel.loadSettings( _this , "HighShelfgain"); - m_hpResModel.loadSettings( _this ,"HPres"); m_lowShelfResModel.loadSettings( _this, "LowShelfres" ); m_para1ResModel.loadSettings( _this ,"Peak1res" ); @@ -119,7 +115,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4ResModel.loadSettings( _this ,"Peak4res" ); m_highShelfResModel.loadSettings( _this, "HighShelfres" ); m_lpResModel.loadSettings( _this, "LPres"); - m_hpFeqModel.loadSettings( _this, "HPfreq" ); m_lowShelfFreqModel.loadSettings( _this, "LowShelffreq" ); m_para1FreqModel.loadSettings( _this, "Peak1freq" ); @@ -128,7 +123,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4FreqModel.loadSettings( _this, "Peak4freq" ); m_highShelfFreqModel.loadSettings( _this, "Highshelffreq" ); m_lpFreqModel.loadSettings( _this, "LPfreq" ); - m_hpActiveModel.loadSettings( _this, "HPactive" ); m_lowShelfActiveModel.loadSettings( _this, "Lowshelfactive" ); m_para1ActiveModel.loadSettings( _this, "Peak1active"); @@ -137,16 +131,13 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4ActiveModel.loadSettings( _this, "Peak4active"); m_highShelfActiveModel.loadSettings( _this, "Highshelfactive" ); m_lpActiveModel.loadSettings( _this, "LPactive" ); - m_lp12Model.loadSettings( _this , "LP12" ); m_lp24Model.loadSettings( _this , "LP24" ); m_lp48Model.loadSettings( _this , "LP48" ); - m_hp12Model.loadSettings( _this , "HP12" ); m_hp24Model.loadSettings( _this , "HP24" ); m_hp48Model.loadSettings( _this , "HP48" ); m_analyzeModel.loadSettings( _this, "Analyzeenable"); - m_lpTypeModel.loadSettings( _this, "LP" ); m_hpTypeModel.loadSettings( _this, "HP" ); } @@ -165,7 +156,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para3GainModel.saveSettings( doc, parent, "Peak3gain" ); m_para4GainModel.saveSettings( doc, parent, "Peak4gain" ); m_highShelfGainModel.saveSettings( doc, parent, "HighShelfgain"); - m_hpResModel.saveSettings( doc, parent ,"HPres"); m_lowShelfResModel.saveSettings( doc, parent, "LowShelfres" ); m_para1ResModel.saveSettings( doc, parent,"Peak1res" ); @@ -174,7 +164,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4ResModel.saveSettings( doc, parent,"Peak4res" ); m_highShelfResModel.saveSettings( doc, parent, "HighShelfres" ); m_lpResModel.saveSettings( doc, parent, "LPres"); - m_hpFeqModel.saveSettings( doc, parent, "HPfreq" ); m_lowShelfFreqModel.saveSettings( doc, parent, "LowShelffreq" ); m_para1FreqModel.saveSettings( doc, parent, "Peak1freq" ); @@ -183,7 +172,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4FreqModel.saveSettings( doc, parent, "Peak4freq" ); m_highShelfFreqModel.saveSettings( doc, parent, "Highshelffreq" ); m_lpFreqModel.saveSettings( doc, parent, "LPfreq" ); - m_hpActiveModel.saveSettings( doc, parent, "HPactive" ); m_lowShelfActiveModel.saveSettings( doc, parent, "Lowshelfactive" ); m_para1ActiveModel.saveSettings( doc, parent, "Peak1active" ); @@ -192,16 +180,13 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4ActiveModel.saveSettings( doc, parent, "Peak4active" ); m_highShelfActiveModel.saveSettings( doc, parent, "Highshelfactive" ); m_lpActiveModel.saveSettings( doc, parent, "LPactive" ); - m_lp12Model.saveSettings( doc, parent, "LP12" ); m_lp24Model.saveSettings( doc, parent, "LP24" ); m_lp48Model.saveSettings( doc, parent, "LP48" ); - m_hp12Model.saveSettings( doc, parent, "HP12" ); m_hp24Model.saveSettings( doc, parent, "HP24" ); m_hp48Model.saveSettings( doc, parent, "HP48" ); m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); - m_lpTypeModel.saveSettings( doc, parent, "LP" ); m_hpTypeModel.saveSettings( doc, parent, "HP" ); } diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 0633a8cf5..f38f8636d 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -38,7 +38,7 @@ -EqControlsDialog::EqControlsDialog(EqControls *controls) : +EqControlsDialog::EqControlsDialog( EqControls *controls ) : EffectControlDialog( controls ) { @@ -94,10 +94,10 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_resKnob = new Knob( knobBright_26, this ); if(i ==0 || i == 7) { - m_resKnob->move(cw * i + ko , 190 ); + m_resKnob->move( cw * i + ko , 190 ); } else { - m_resKnob->move(cw * i + ko , 205 ); + m_resKnob->move( cw * i + ko , 205 ); } m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index fd8327dce..bda074f4a 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -33,7 +33,7 @@ #include "MainWindow.h" #include "qpushbutton.h" #include "eqspectrumview.h" -#include "qlist.h" + class EqControls; @@ -66,9 +66,7 @@ private: LedCheckBox* m_hp12Box; LedCheckBox* m_hp24Box; LedCheckBox* m_hp48Box; - LedCheckBox* m_analyzeBox; - EqParameterWidget* m_parameterWidget; EqSpectrumView* m_inSpec; EqSpectrumView* m_outSpec; diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 992aab69b..19f990479 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -30,12 +30,6 @@ #include "Engine.h" #include "MainWindow.h" -//TODO -//re write to store data from each filter(models,name, storeage name ) in a class, stored in array -//then just loop ever array for each section - - - extern "C" { @@ -52,7 +46,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = NULL } ; - +} EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : @@ -74,7 +68,6 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K EqEffect::~EqEffect() { - } @@ -100,10 +93,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { m_eqControls.m_inFftBands.clear(); } - - //TODO UPSAMPLE upsample( buf, frames ); - gain(m_upBuf , m_upBufFrames, m_eqControls.m_inGainModel.value(), &m_inPeak ); m_eqControls.m_inPeakL = m_eqControls.m_inPeakL < m_inPeak[0] ? m_inPeak[0] : m_eqControls.m_inPeakL; m_eqControls.m_inPeakR = m_eqControls.m_inPeakR < m_inPeak[1] ? m_inPeak[0] : m_eqControls.m_inPeakR; @@ -222,21 +212,16 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; - - //TODO lp filter before downsample for( int i =0; i < m_dFilterCount; i++) { m_downsampleFilters[i].processBuffer(m_upBuf , m_upBufFrames ); } - downSample( buf, frames ); - for( fpp_t f = 0; f < frames; ++f ) { outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); - if(m_eqControls.m_analyzeModel.value() ) { m_eqControls.m_outFftBands.analyze( buf, frames ); @@ -245,7 +230,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { m_eqControls.m_outFftBands.clear(); } - setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + setBandPeaks( &m_eqControls.m_outFftBands , ( int )( sampleRate * 0.5 ) ); m_eqControls.m_inProgress = false; return isRunning(); } @@ -253,7 +238,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) -void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak) +void EqEffect::gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ) { peak[0][0] = 0.0f; peak[0][1] = 0.0f; for( fpp_t f = 0; f < frames; ++f ) @@ -273,11 +258,15 @@ void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFra } } + + + + sampleFrame m_lastUpFrame; -void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) +void EqEffect::upsample( sampleFrame *buf, const fpp_t frames ) { - if(m_upBufFrames != frames * 2 ) + if( m_upBufFrames != frames * 2 ) { if( m_upBuf ) { @@ -286,21 +275,19 @@ void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) m_upBuf = new sampleFrame[frames * 2]; m_upBufFrames = frames * 2; } - for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { - m_upBuf[f2][0] = linearInterpolate(m_lastUpFrame[0],buf[f][0], 0.5); - m_upBuf[f2][1] = linearInterpolate(m_lastUpFrame[1],buf[f][1], 0.5); + m_upBuf[f2][0] = linearInterpolate( m_lastUpFrame[0],buf[f][0], 0.5 ); + m_upBuf[f2][1] = linearInterpolate( m_lastUpFrame[1],buf[f][1], 0.5 ); m_upBuf[f2+1][0] = buf[f][0]; m_upBuf[f2+1][1] = buf[f][1]; - m_lastUpFrame[0] = buf[f][0]; m_lastUpFrame[1] = buf[f][1]; } } -} -void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) + +void EqEffect::downSample( sampleFrame *buf, const fpp_t frames ) { for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { @@ -309,49 +296,10 @@ void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) } } -void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, EqAnalyser* fft) -{ - if(m_eqControls.m_analyzeModel.value() ) - { - const int FFT_BUFFER_SIZE = 2048; - fpp_t f = 0; - if( frames > FFT_BUFFER_SIZE ) - { - fft->m_framesFilledUp = 0; - f = frames - FFT_BUFFER_SIZE; - } - // meger channels - for( ; f < frames; ++f ) - { - fft->m_buffer[fft->m_framesFilledUp] = - ( buf[f][0] + buf[f][1] ) * 0.5; - ++fft->m_framesFilledUp; - } - - if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) - { - return; - } - - fft->m_sr = Engine::mixer()->processingSampleRate(); - const int LOWEST_FREQ = 0; - const int HIGHEST_FREQ = fft->m_sr / 2; - - fftwf_execute( fft->m_fftPlan ); - absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); - - compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, - MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); - fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); - fft->m_framesFilledUp = 0; - - } -} -float EqEffect::peakBand(float minF, float maxF, EqAnalyser *fft, int sr) + +float EqEffect::peakBand( float minF, float maxF, EqAnalyser *fft, int sr ) { float peak = -60; float * b = fft->m_bands; @@ -377,25 +325,29 @@ void EqEffect::setBandPeaks(EqAnalyser *fft, int samplerate ) peakBand( m_eqControls.m_para1FreqModel.value() - (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), m_eqControls.m_para1FreqModel.value() - + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = peakBand( m_eqControls.m_para2FreqModel.value() - (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), m_eqControls.m_para2FreqModel.value() - + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = peakBand( m_eqControls.m_para3FreqModel.value() - (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), m_eqControls.m_para3FreqModel.value() - + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = peakBand( m_eqControls.m_para4FreqModel.value() - (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), m_eqControls.m_para4FreqModel.value() - + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = peakBand( m_eqControls.m_highShelfFreqModel.value(), diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 1fc96c077..a2e660cc2 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -71,7 +71,6 @@ private: sampleFrame* m_upBuf; fpp_t m_upBufFrames; - // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); void analyze( sampleFrame *buf, const fpp_t frames, EqAnalyser* fft ); @@ -83,8 +82,7 @@ private: } void setBandPeaks( EqAnalyser *fft , int); - // float m_bands[MAX_BANDS]; - // float m_energy; + }; diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h index e6924ca4c..d704ae517 100644 --- a/plugins/eq/eqfilter.h +++ b/plugins/eq/eqfilter.h @@ -43,6 +43,9 @@ public: } + + + virtual inline void setSampleRate( int sampleRate ) { if( sampleRate != m_sampleRate ) @@ -52,6 +55,9 @@ public: } } + + + virtual inline void setFrequency( float freq ){ if ( freq != m_freq ) { @@ -60,6 +66,9 @@ public: } } + + + virtual void setQ( float res ) { if ( res != m_res ) @@ -69,6 +78,9 @@ public: } } + + + virtual void setGain( float gain ) { if ( gain != m_gain ) @@ -78,6 +90,9 @@ public: } } + + + /// /// \brief processBuffer /// \param buf Audio Buffer @@ -153,6 +168,8 @@ public : }; + + /// /// \brief The EqLp12Filter class. /// A 2 pole low pass filter @@ -236,6 +253,9 @@ public: } }; + + + class EqLowShelfFilter : public EqFilter { public : @@ -309,6 +329,9 @@ public : } }; + + + class EqLinkwitzRiley : public StereoLinkwitzRiley { public: diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 6a3ae7aa1..f52b3a36b 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -26,7 +26,6 @@ #include "QPainter" #include "qwidget.h" #include "lmms_math.h" - #include "MainWindow.h" #include "QMouseEvent" @@ -47,7 +46,6 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); - } @@ -55,7 +53,6 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : EqParameterWidget::~EqParameterWidget() { - } @@ -83,8 +80,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { - - m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + m_bands[i].color.setAlpha( m_bands[i].active->value() ? activeAplha() : inactiveAlpha() ); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; @@ -98,7 +94,8 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); - if(i == 0 || i == bandCount() - 1 ){ + if( i == 0 || i == bandCount() - 1 ) + { painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } else @@ -112,7 +109,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) { m_bands[i].color.setAlpha( 255 ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + painter.drawLine( sectionLength * i , 1, sectionLength * (i+1) , 1 ); } } @@ -184,15 +181,15 @@ EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) EqBand* selectedModel = 0; float* distanceToHandles = new float[bandCount()]; //calc distance to each handle - for( int i = 0 ; i < bandCount() ; i++) + for( int i = 0 ; i < bandCount() ; i++ ) { int xOffset = m_bands[i].x - x; int yOffset = m_bands[i].y - y; - distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + distanceToHandles[i] = fabs( sqrt( ( xOffset * xOffset ) + ( yOffset * yOffset ) ) ); } //select band int shortestBand = 0; - for (int i = 1 ; i < bandCount() ; i++ ) + for ( int i = 1 ; i < bandCount() ; i++ ) { if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ shortestBand = i; diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 3054f0b95..d2aec61f0 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -1,4 +1,3 @@ - /* eqspectrumview.h - defination of EqSpectrumView class. * * Copyright (c) 2014 David French @@ -38,12 +37,10 @@ class EqAnalyser public: fftwf_plan m_fftPlan; - fftwf_complex * m_specBuf; float m_absSpecBuf[FFT_BUFFER_SIZE+1]; float m_buffer[FFT_BUFFER_SIZE*2]; int m_framesFilledUp; - float m_bands[MAX_BANDS]; float m_energy; int m_sr; @@ -77,7 +74,7 @@ public: - void analyze(sampleFrame *buf, const fpp_t frames ) + void analyze( sampleFrame *buf, const fpp_t frames ) { m_inProgress=true; const int FFT_BUFFER_SIZE = 2048; @@ -110,8 +107,8 @@ public: compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2))); + ( int )( LOWEST_FREQ * ( FFT_BUFFER_SIZE + 1 ) / ( float )( m_sr / 2 ) ), + ( int )( HIGHEST_FREQ * ( FFT_BUFFER_SIZE + 1) / ( float )( m_sr / 2 ) ) ); m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE ); m_framesFilledUp = 0; m_inProgress = false; @@ -125,12 +122,11 @@ class EqSpectrumView : public QWidget { public: - explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0) : + explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0 ): QWidget( _parent ), m_sa( b ) { setFixedSize( 250, 116 ); - // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(2000); @@ -140,12 +136,18 @@ public: m_pixelsPerUnitWidth = width( ) / totalLength ; m_scale = 1.5; color = QColor( 255, 255, 255, 255 ); - } + + + virtual ~EqSpectrumView() { } + + + + QColor color; EqAnalyser *m_sa; QPainterPath pp; @@ -171,13 +173,13 @@ public: pp.moveTo( 0,height() ); for( int x = 0; x < MAX_BANDS; ++x, ++b ) { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + h = (int)( fh * 2.0 / 3.0 * ( 20 * ( log10 ( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); if( h < 0 ) h = 0; else if( h >= fh ) continue; - pp.lineTo( freqToXPixel(bandToFreq(x ) ), fh-h ); + pp.lineTo( freqToXPixel(bandToFreq( x ) ), fh-h ); } - pp.lineTo(width(), height() ); + pp.lineTo( width(), height() ); pp.closeSubpath(); - p.fillPath( pp ,QBrush( color ) ); + p.fillPath( pp, QBrush( color ) ); } @@ -190,7 +192,7 @@ public: inline float bandToFreq ( int index ) { - return index * m_sa->m_sr / (MAX_BANDS * 2); + return index * m_sa->m_sr / (MAX_BANDS * 2 ); } @@ -202,10 +204,6 @@ private: float m_pixelsPerUnitWidth; float m_scale; int m_skipBands; - - - - } ; From 7536584386d468fda8066dd0bd1c3b692bcd5f70 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 22:03:15 +0000 Subject: [PATCH 11/52] EQ remove unused files --- plugins/eq/eqfader.cpp | 0 plugins/eq/eqfilter.cpp | 5 ----- plugins/eq/eqhighshelffileter.h | 1 - plugins/eq/eqhp12.h | 0 plugins/eq/eqlowshelffilter.h | 3 --- plugins/eq/eqlp12.h | 0 plugins/eq/eqpeekfilter.cpp | 3 --- plugins/eq/eqpeekfilter.h | 3 --- 8 files changed, 15 deletions(-) delete mode 100644 plugins/eq/eqfader.cpp delete mode 100644 plugins/eq/eqfilter.cpp delete mode 100644 plugins/eq/eqhighshelffileter.h delete mode 100644 plugins/eq/eqhp12.h delete mode 100644 plugins/eq/eqlowshelffilter.h delete mode 100644 plugins/eq/eqlp12.h delete mode 100644 plugins/eq/eqpeekfilter.cpp delete mode 100644 plugins/eq/eqpeekfilter.h diff --git a/plugins/eq/eqfader.cpp b/plugins/eq/eqfader.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqfilter.cpp b/plugins/eq/eqfilter.cpp deleted file mode 100644 index 7407377b9..000000000 --- a/plugins/eq/eqfilter.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "eqfilter.h" - - - - diff --git a/plugins/eq/eqhighshelffileter.h b/plugins/eq/eqhighshelffileter.h deleted file mode 100644 index 8b1378917..000000000 --- a/plugins/eq/eqhighshelffileter.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plugins/eq/eqhp12.h b/plugins/eq/eqhp12.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqlowshelffilter.h b/plugins/eq/eqlowshelffilter.h deleted file mode 100644 index b28b04f64..000000000 --- a/plugins/eq/eqlowshelffilter.h +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/eq/eqlp12.h b/plugins/eq/eqlp12.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqpeekfilter.cpp b/plugins/eq/eqpeekfilter.cpp deleted file mode 100644 index 1cc60cf4c..000000000 --- a/plugins/eq/eqpeekfilter.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "eqpeekfilter.h" - - diff --git a/plugins/eq/eqpeekfilter.h b/plugins/eq/eqpeekfilter.h deleted file mode 100644 index b28b04f64..000000000 --- a/plugins/eq/eqpeekfilter.h +++ /dev/null @@ -1,3 +0,0 @@ - - - From c5c9b59167da7bb90229c587d729d41fccdaf926 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 14 Dec 2014 00:09:30 +0000 Subject: [PATCH 12/52] EQ changed makefile to allow windows build on travis --- plugins/eq/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index b537ab632..0a5515fc4 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -2,4 +2,4 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") From b595d432d9f4c96bf31dde389317d4a100412325 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 14 Dec 2014 21:13:20 +0000 Subject: [PATCH 13/52] EQ Fixed so builds on win, Changed display colors, made res lines thinner --- plugins/eq/CMakeLists.txt | 3 ++- plugins/eq/eqcontrolsdialog.cpp | 4 ++-- plugins/eq/eqparameterwidget.cpp | 2 +- plugins/eq/eqparameterwidget.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index 0a5515fc4..9b471d5b6 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -2,4 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h ../../src/gui/widgets/Fader.cpp +MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index f38f8636d..1071b87bb 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,10 +51,10 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 255, 150 ); + m_inSpec->color = QColor( 255, 0, 0, 150 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(00, 255, 255, 80); + m_outSpec->color = QColor(0, 255, 0, 80); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index f52b3a36b..784806558 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -93,7 +93,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index 766efad60..6848ba938 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -87,7 +87,7 @@ public: const int inactiveAlpha() { - return 100; + return 50; } From 0789bae53a7a58bb61cb18dc917f5723f376cba5 Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 15 Dec 2014 10:41:43 +0200 Subject: [PATCH 14/52] Crossover EQ initial commit, also fix bugs in LR4 filter and Fader --- include/BasicFilters.h | 81 ++++--- include/Fader.h | 2 +- plugins/CMakeLists.txt | 1 + plugins/CrossoverEQ/CMakeLists.txt | 3 + plugins/CrossoverEQ/CrossoverEQ.cpp | 219 ++++++++++++++++++ plugins/CrossoverEQ/CrossoverEQ.h | 77 ++++++ .../CrossoverEQ/CrossoverEQControlDialog.cpp | 115 +++++++++ .../CrossoverEQ/CrossoverEQControlDialog.h | 50 ++++ plugins/CrossoverEQ/CrossoverEQControls.cpp | 116 ++++++++++ plugins/CrossoverEQ/CrossoverEQControls.h | 86 +++++++ plugins/CrossoverEQ/artwork.png | Bin 0 -> 52627 bytes plugins/CrossoverEQ/fader_bg.png | Bin 0 -> 234 bytes plugins/CrossoverEQ/fader_empty.png | Bin 0 -> 198 bytes plugins/CrossoverEQ/fader_knob2.png | Bin 0 -> 783 bytes src/gui/widgets/Fader.cpp | 15 +- 15 files changed, 728 insertions(+), 37 deletions(-) create mode 100644 plugins/CrossoverEQ/CMakeLists.txt create mode 100644 plugins/CrossoverEQ/CrossoverEQ.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQ.h create mode 100644 plugins/CrossoverEQ/CrossoverEQControlDialog.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQControlDialog.h create mode 100644 plugins/CrossoverEQ/CrossoverEQControls.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQControls.h create mode 100644 plugins/CrossoverEQ/artwork.png create mode 100644 plugins/CrossoverEQ/fader_bg.png create mode 100644 plugins/CrossoverEQ/fader_empty.png create mode 100644 plugins/CrossoverEQ/fader_knob2.png diff --git a/include/BasicFilters.h b/include/BasicFilters.h index e09d40c42..eda8d7b14 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,6 +64,7 @@ public: for( int i = 0; i < CHANNELS; ++i ) { m_z1[i] = m_z2[i] = m_z3[i] = m_z4[i] = 0.0f; + m_y1[i] = m_y2[i] = m_y3[i] = m_y4[i] = 0.0f; } } @@ -75,69 +76,89 @@ public: inline void setCoeffs( float freq ) { // wc - const float wc = F_2PI * freq / m_sampleRate; - const float wc2 = wc * wc; - const float wc3 = wc2 * wc; + const double wc = D_2PI * freq; + const double wc2 = wc * wc; + const double wc3 = wc2 * wc; m_wc4 = wc2 * wc2; // k - const float k = wc / tan( wc * 0.5 ); - const float k2 = k * k; - const float k3 = k2 * k; + const double k = wc / tan( D_PI * freq / m_sampleRate ); + const double k2 = k * k; + const double k3 = k2 * k; m_k4 = k2 * k2; // a static const double sqrt2 = sqrt( 2.0 ); - const float sq_tmp1 = sqrt2 * wc3 * k; - const float sq_tmp2 = sqrt2 * wc * k3; - m_a = 1.0f / ( 4.0f * wc2 * k2 + 2.0f * sq_tmp1 + m_k4 + 2.0f * sq_tmp2 + m_wc4 ); + const double sq_tmp1 = sqrt2 * wc3 * k; + const double sq_tmp2 = sqrt2 * wc * k3; + + m_a = 1.0 / ( 4.0 * wc2 * k2 + 2.0 * sq_tmp1 + m_k4 + 2.0 * sq_tmp2 + m_wc4 ); // b - m_b1 = ( 4.0f * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; - m_b2 = ( 6.0f * m_wc4 - 8.0f * wc2 * k2 + 6.0f * m_k4 ) * m_a; - m_b3 = ( 4.0f * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; - m_b4 = ( m_k4 - 2.0f * sq_tmp1 + m_wc4 - 2.0f * sq_tmp2 + 4.0f * wc2 * k2 ) * m_a; + m_b1 = ( 4.0 * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; + m_b2 = ( 6.0 * m_wc4 - 8.0 * wc2 * k2 + 6.0 * m_k4 ) * m_a; + m_b3 = ( 4.0 * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; + m_b4 = ( m_k4 - 2.0 * sq_tmp1 + m_wc4 - 2.0 * sq_tmp2 + 4.0 * wc2 * k2 ) * m_a; } inline void setLowpass( float freq ) { setCoeffs( freq ); m_a0 = m_wc4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = 4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline void setHighpass( float freq ) { setCoeffs( freq ); m_a0 = m_k4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = -4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline float update( float in, ch_cnt_t ch ) { - const float a0in = m_a0 * in; - const float a1in = m_a1 * in; - const float out = m_z1[ch] + a0in; + const double y = m_a0 * in + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ) - + ( m_y1[ch] * m_b1 ) - ( m_y2[ch] * m_b2 ) - + ( m_y3[ch] * m_b3 ) - ( m_y4[ch] * m_b4 ); + + m_z4[ch] = m_z3[ch]; + m_z3[ch] = m_z2[ch]; + m_z2[ch] = m_z1[ch]; + m_z1[ch] = in; - m_z1[ch] = a1in + m_z2[ch] - ( m_b1 * out ); - m_z2[ch] = ( m_a2 * in ) + m_z3[ch] - ( m_b2 * out ); - m_z3[ch] = a1in + m_z4[ch] - ( m_b3 * out ); - m_z4[ch] = a0in - ( m_b4 * out ); + m_y4[ch] = m_y3[ch]; + m_y3[ch] = m_y2[ch]; + m_y2[ch] = m_y1[ch]; + m_y1[ch] = y; - return out; + return y; + +// for some reason converting to direct form 2 doesn't seem to work for this filter +/* const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - + ( m_z3[ch] * m_b3 ) - ( m_z4[ch] * m_b4 ); + + m_z4[ch] = m_z3[ch]; + m_z3[ch] = m_z2[ch]; + m_z2[ch] = m_z1[ch]; + m_z1[ch] = x; + + return ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 );*/ } private: float m_sampleRate; - float m_wc4; - float m_k4; - float m_a, m_a0, m_a1, m_a2; - float m_b1, m_b2, m_b3, m_b4; + double m_wc4; + double m_k4; + double m_a, m_a0, m_a1, m_a2; + double m_b1, m_b2, m_b3, m_b4; - typedef float frame[CHANNELS]; + typedef double frame[CHANNELS]; frame m_z1, m_z2, m_z3, m_z4; + frame m_y1, m_y2, m_y3, m_y4; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley; diff --git a/include/Fader.h b/include/Fader.h index 96c3ae732..9e8f034ea 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -103,7 +103,7 @@ private: float fRange = m_model->maxValue() - m_model->minValue(); float realVal = m_model->value() - m_model->minValue(); - return height() - ( ( height() - ( *s_knob ).height() ) * ( realVal / fRange ) ); + return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } FloatModel * m_model; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4df760ecd..cf7a5a94e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -6,6 +6,7 @@ ADD_SUBDIRECTORY(Bitcrush) ADD_SUBDIRECTORY(carlabase) ADD_SUBDIRECTORY(carlapatchbay) ADD_SUBDIRECTORY(carlarack) +ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) diff --git a/plugins/CrossoverEQ/CMakeLists.txt b/plugins/CrossoverEQ/CMakeLists.txt new file mode 100644 index 000000000..fbc8407d9 --- /dev/null +++ b/plugins/CrossoverEQ/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(crossovereq CrossoverEQ.cpp CrossoverEQControls.cpp CrossoverEQControlDialog.cpp MOCFILES CrossoverEQControls.h CrossoverEQControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp new file mode 100644 index 000000000..a50b6381f --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -0,0 +1,219 @@ +/* + * CrossoverEQ.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQ.h" +#include "lmms_math.h" +#include "embed.cpp" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Crossover Equalizer", + QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +}; + +} + + +CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : + Effect( &crossovereq_plugin_descriptor, parent, key ), + m_controls( this ), + m_sampleRate( Engine::mixer()->processingSampleRate() ), + m_lp1( m_sampleRate ), + m_lp2( m_sampleRate ), + m_lp3( m_sampleRate ), + m_hp2( m_sampleRate ), + m_hp3( m_sampleRate ), + m_hp4( m_sampleRate ), + m_needsUpdate( true ) +{ + m_tmp1 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_tmp2 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_work = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); +} + +CrossoverEQEffect::~CrossoverEQEffect() +{ + MM_FREE( m_tmp1 ); + MM_FREE( m_tmp2 ); + MM_FREE( m_work ); +} + +void CrossoverEQEffect::sampleRateChanged() +{ + m_sampleRate = Engine::mixer()->processingSampleRate(); + m_lp1.setSampleRate( m_sampleRate ); + m_lp2.setSampleRate( m_sampleRate ); + m_lp3.setSampleRate( m_sampleRate ); + m_hp2.setSampleRate( m_sampleRate ); + m_hp3.setSampleRate( m_sampleRate ); + m_hp4.setSampleRate( m_sampleRate ); + m_needsUpdate = true; +} + + +bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + + // filters update + if( m_needsUpdate || m_controls.m_xover12.isValueChanged() ) + { + m_lp1.setLowpass( m_controls.m_xover12.value() ); + m_lp1.clearHistory(); + m_hp2.setHighpass( m_controls.m_xover12.value() ); + m_hp2.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover23.isValueChanged() ) + { + m_lp2.setLowpass( m_controls.m_xover23.value() ); + m_lp2.clearHistory(); + m_hp3.setHighpass( m_controls.m_xover23.value() ); + m_hp3.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover34.isValueChanged() ) + { + m_lp3.setLowpass( m_controls.m_xover34.value() ); + m_lp3.clearHistory(); + m_hp4.setHighpass( m_controls.m_xover34.value() ); + m_hp4.clearHistory(); + } + + // gain values update + if( m_needsUpdate || m_controls.m_gain1.isValueChanged() ) + { + m_gain1 = dbvToAmp( m_controls.m_gain1.value() ); + } + if( m_needsUpdate || m_controls.m_gain2.isValueChanged() ) + { + m_gain2 = dbvToAmp( m_controls.m_gain2.value() ); + } + if( m_needsUpdate || m_controls.m_gain3.isValueChanged() ) + { + m_gain3 = dbvToAmp( m_controls.m_gain3.value() ); + } + if( m_needsUpdate || m_controls.m_gain4.isValueChanged() ) + { + m_gain4 = dbvToAmp( m_controls.m_gain4.value() ); + } + + // mute values update + const bool mute1 = m_controls.m_mute1.value(); + const bool mute2 = m_controls.m_mute2.value(); + const bool mute3 = m_controls.m_mute3.value(); + const bool mute4 = m_controls.m_mute4.value(); + + m_needsUpdate = false; + + memset( m_work, 0, sizeof( sampleFrame ) * frames ); + + // run temp bands + for( int f = 0; f < frames; ++f ) + { + m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 ); + m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 ); + m_tmp2[f][0] = m_hp3.update( buf[f][0], 0 ); + m_tmp2[f][1] = m_hp3.update( buf[f][1], 1 ); + } + + // run band 1 + if( ! mute1 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1; + m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1; + } + } + + // run band 2 + if( ! mute2 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2; + m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2; + } + } + + // run band 3 + if( ! mute3 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3; + m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3; + } + } + + // run band 4 + if( ! mute4 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4; + m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4; + } + } + + const float d = dryLevel(); + const float w = wetLevel(); + double outSum = 0.0; + for( int f = 0; f < frames; ++f ) + { + buf[f][0] = d * buf[f][0] + w * m_work[f][0]; + buf[f][1] = d * buf[f][1] + w * m_work[f][1]; + outSum = buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; + } + + checkGate( outSum ); + + return isRunning(); +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new CrossoverEQEffect( parent, static_cast( data ) ); +} + +} diff --git a/plugins/CrossoverEQ/CrossoverEQ.h b/plugins/CrossoverEQ/CrossoverEQ.h new file mode 100644 index 000000000..36b3a6bc5 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.h @@ -0,0 +1,77 @@ +/* + * CrossoverEQ.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_H +#define CROSSOVEREQ_H + +#include "Effect.h" +#include "CrossoverEQControls.h" +#include "ValueBuffer.h" +#include "lmms_math.h" +#include "BasicFilters.h" + +class CrossoverEQEffect : public Effect +{ +public: + CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); + virtual ~CrossoverEQEffect(); + virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ); + + virtual EffectControls* controls() + { + return &m_controls; + } + +private: + CrossoverEQControls m_controls; + + void sampleRateChanged(); + + float m_sampleRate; + + float m_gain1; + float m_gain2; + float m_gain3; + float m_gain4; + + StereoLinkwitzRiley m_lp1; + StereoLinkwitzRiley m_lp2; + StereoLinkwitzRiley m_lp3; + + StereoLinkwitzRiley m_hp2; + StereoLinkwitzRiley m_hp3; + StereoLinkwitzRiley m_hp4; + + sampleFrame * m_tmp1; + sampleFrame * m_tmp2; + sampleFrame * m_work; + + bool m_needsUpdate; + + friend class CrossoverEQControls; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp new file mode 100644 index 000000000..8a9eecf4d --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -0,0 +1,115 @@ +/* + * CrossoverEQControlDialog.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include "CrossoverEQControlDialog.h" +#include "CrossoverEQControls.h" +#include "embed.h" +#include "ToolTip.h" +#include "LedCheckbox.h" +#include "Knob.h" +#include "Fader.h" + +CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * controls ) : + EffectControlDialog( controls ) +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 167, 188 ); + + // knobs + Knob * xover12 = new Knob( knobBright_26, this ); + xover12->move( 29, 15 ); + xover12->setModel( & controls->m_xover12 ); + xover12->setLabel( "1/2" ); + xover12->setHintText( tr( "Band 1/2 Crossover:" ), " Hz" ); + + Knob * xover23 = new Knob( knobBright_26, this ); + xover23->move( 69, 15 ); + xover23->setModel( & controls->m_xover23 ); + xover23->setLabel( "2/3" ); + xover23->setHintText( tr( "Band 2/3 Crossover:" ), " Hz" ); + + Knob * xover34 = new Knob( knobBright_26, this ); + xover34->move( 109, 15 ); + xover34->setModel( & controls->m_xover34 ); + xover34->setLabel( "3/4" ); + xover34->setHintText( tr( "Band 3/4 Crossover:" ), " Hz" ); + + m_fader_bg = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_bg" ) ); + m_fader_empty = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_empty" ) ); + m_fader_knob = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_knob2" ) ); + + // faders + Fader * gain1 = new Fader( &controls->m_gain1, "Band 1 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain1->move( 7, 56 ); + gain1->setDisplayConversion( false ); + gain1->setHintText( tr( "Band 1 Gain:" ), " dBV" ); + + Fader * gain2 = new Fader( &controls->m_gain2, "Band 2 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain2->move( 47, 56 ); + gain2->setDisplayConversion( false ); + gain2->setHintText( tr( "Band 2 Gain:" ), " dBV" ); + + Fader * gain3 = new Fader( &controls->m_gain3, "Band 3 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain3->move( 87, 56 ); + gain3->setDisplayConversion( false ); + gain3->setHintText( tr( "Band 3 Gain:" ), " dBV" ); + + Fader * gain4 = new Fader( &controls->m_gain4, "Band 4 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain4->move( 127, 56 ); + gain4->setDisplayConversion( false ); + gain4->setHintText( tr( "Band 4 Gain:" ), " dBV" ); + + // leds + LedCheckBox * mute1 = new LedCheckBox( "M", this, tr( "Band 1 Mute" ), LedCheckBox::Red ); + mute1->move( 11, 158 ); + mute1->setModel( & controls->m_mute1 ); + ToolTip::add( mute1, tr( "Mute Band 1" ) ); + + LedCheckBox * mute2 = new LedCheckBox( "M", this, tr( "Band 2 Mute" ), LedCheckBox::Red ); + mute2->move( 51, 158 ); + mute2->setModel( & controls->m_mute2 ); + ToolTip::add( mute2, tr( "Mute Band 2" ) ); + + LedCheckBox * mute3 = new LedCheckBox( "M", this, tr( "Band 3 Mute" ), LedCheckBox::Red ); + mute3->move( 91, 158 ); + mute3->setModel( & controls->m_mute3 ); + ToolTip::add( mute3, tr( "Mute Band 3" ) ); + + LedCheckBox * mute4 = new LedCheckBox( "M", this, tr( "Band 4 Mute" ), LedCheckBox::Red ); + mute4->move( 131, 158 ); + mute4->setModel( & controls->m_mute4 ); + ToolTip::add( mute4, tr( "Mute Band 4" ) ); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.h b/plugins/CrossoverEQ/CrossoverEQControlDialog.h new file mode 100644 index 000000000..08c678886 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.h @@ -0,0 +1,50 @@ +/* + * CrossoverEQControlDialog.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROL_DIALOG_H +#define CROSSOVEREQ_CONTROL_DIALOG_H + +#include +#include "EffectControlDialog.h" + +class CrossoverEQControls; + +class CrossoverEQControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + CrossoverEQControlDialog( CrossoverEQControls * controls ); + virtual ~CrossoverEQControlDialog() + { + } + +private: + QPixmap m_fader_bg; + QPixmap m_fader_empty; + QPixmap m_fader_knob; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControls.cpp b/plugins/CrossoverEQ/CrossoverEQControls.cpp new file mode 100644 index 000000000..9c58eabff --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.cpp @@ -0,0 +1,116 @@ +/* + * CrossoverEQControls.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQControls.h" +#include "CrossoverEQ.h" + +CrossoverEQControls::CrossoverEQControls( CrossoverEQEffect * eff ) : + EffectControls( eff ), + m_effect( eff ), + m_xover12( 125.f, 50.f, 10000.f, 1.0f, this, "Band 1/2 Crossover" ), + m_xover23( 1250.f, 50.f, 20000.f, 1.0f, this, "Band 2/3 Crossover" ), + m_xover34( 5000.f, 50.f, 20000.f, 1.0f, this, "Band 3/4 Crossover" ), + m_gain1( 0.f, -60.f, 30.f, 0.1f, this, "Band 1 Gain" ), + m_gain2( 0.f, -60.f, 30.f, 0.1f, this, "Band 2 Gain" ), + m_gain3( 0.f, -60.f, 30.f, 0.1f, this, "Band 3 Gain" ), + m_gain4( 0.f, -60.f, 30.f, 0.1f, this, "Band 4 Gain" ), + m_mute1( false, this, "Mute Band 1" ), + m_mute2( false, this, "Mute Band 2" ), + m_mute3( false, this, "Mute Band 3" ), + m_mute4( false, this, "Mute Band 4" ) +{ + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); + connect( &m_xover12, SIGNAL( dataChanged() ), this, SLOT( xover12Changed() ) ); + connect( &m_xover23, SIGNAL( dataChanged() ), this, SLOT( xover23Changed() ) ); + connect( &m_xover34, SIGNAL( dataChanged() ), this, SLOT( xover34Changed() ) ); + + m_xover12.setScaleLogarithmic( true ); + m_xover23.setScaleLogarithmic( true ); + m_xover34.setScaleLogarithmic( true ); +} + +void CrossoverEQControls::saveSettings( QDomDocument & doc, QDomElement & elem ) +{ + m_xover12.saveSettings( doc, elem, "xover12" ); + m_xover23.saveSettings( doc, elem, "xover23" ); + m_xover34.saveSettings( doc, elem, "xover34" ); + + m_gain1.saveSettings( doc, elem, "gain1" ); + m_gain2.saveSettings( doc, elem, "gain2" ); + m_gain3.saveSettings( doc, elem, "gain3" ); + m_gain4.saveSettings( doc, elem, "gain4" ); + + m_mute1.saveSettings( doc, elem, "mute1" ); + m_mute2.saveSettings( doc, elem, "mute2" ); + m_mute3.saveSettings( doc, elem, "mute3" ); + m_mute4.saveSettings( doc, elem, "mute4" ); +} + +void CrossoverEQControls::loadSettings( const QDomElement & elem ) +{ + m_xover12.loadSettings( elem, "xover12" ); + m_xover23.loadSettings( elem, "xover23" ); + m_xover34.loadSettings( elem, "xover34" ); + + m_gain1.loadSettings( elem, "gain1" ); + m_gain2.loadSettings( elem, "gain2" ); + m_gain3.loadSettings( elem, "gain3" ); + m_gain4.loadSettings( elem, "gain4" ); + + m_mute1.loadSettings( elem, "mute1" ); + m_mute2.loadSettings( elem, "mute2" ); + m_mute3.loadSettings( elem, "mute3" ); + m_mute4.loadSettings( elem, "mute4" ); + + m_effect->m_needsUpdate = true; +} + +void CrossoverEQControls::xover12Changed() +{ + float v = m_xover12.value(); + if( m_xover23.value() < v ) { m_xover23.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover23Changed() +{ + float v = m_xover23.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover34Changed() +{ + float v = m_xover34.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover23.value() > v ) { m_xover23.setValue( v ); } +} + + +void CrossoverEQControls::sampleRateChanged() +{ + m_effect->sampleRateChanged(); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControls.h b/plugins/CrossoverEQ/CrossoverEQControls.h new file mode 100644 index 000000000..18e87baee --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.h @@ -0,0 +1,86 @@ +/* + * CrossoverEQControls.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROLS_H +#define CROSSOVEREQ_CONTROLS_H + +#include "EffectControls.h" +#include "CrossoverEQControlDialog.h" + +class CrossoverEQEffect; + +class CrossoverEQControls : public EffectControls +{ + Q_OBJECT +public: + CrossoverEQControls( CrossoverEQEffect * eff ); + virtual ~CrossoverEQControls() {} + + virtual void saveSettings( QDomDocument & doc, QDomElement & elem ); + virtual void loadSettings( const QDomElement & elem ); + inline virtual QString nodeName() const + { + return( "crossoevereqcontrols" ); + } + + virtual int controlCount() + { + return( 11 ); + } + + virtual EffectControlDialog * createView() + { + return( new CrossoverEQControlDialog( this ) ); + } + +private slots: + void xover12Changed(); + void xover23Changed(); + void xover34Changed(); + void sampleRateChanged(); + +private: + CrossoverEQEffect * m_effect; + + FloatModel m_xover12; + FloatModel m_xover23; + FloatModel m_xover34; + + FloatModel m_gain1; + FloatModel m_gain2; + FloatModel m_gain3; + FloatModel m_gain4; + + BoolModel m_mute1; + BoolModel m_mute2; + BoolModel m_mute3; + BoolModel m_mute4; + + friend class CrossoverEQControlDialog; + friend class CrossoverEQEffect; +}; + +#endif diff --git a/plugins/CrossoverEQ/artwork.png b/plugins/CrossoverEQ/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..5510d2b3c3edc757483f2b21a7c3eb957ab2e448 GIT binary patch literal 52627 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 literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_bg.png b/plugins/CrossoverEQ/fader_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..abe28110594016ba54bfbedbbbf0e4083256641b GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^3P7B~!3HFsZ#L=!Qk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{T>gWn53Y_73rryA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zq7+XT$B>MBZ*Lj$GAQu49&B6iD1XOc(=dUHjT7z&Ux=~Z?)7V@iuK8NXJ+^wex+v^ m7cU6ZMI>18JG{p0*Iw>mJ7(n&=9lw8+B{wTT-G@yGywn+8#>?s literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_empty.png b/plugins/CrossoverEQ/fader_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..4a95f05aa4ccd260e734b6b792da3853129b606a GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^3P7B~!2~2TE-t+Zq&N#aB8wRqxP?KOkzv*x37{Z* ziKnkC`#l~$UM+)$rAksjA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA_q?w$B>MBZ_gSEGB9wkD40I{?w`yh&d+th^VOv*hK&vi0vs$%xTqh}I+ceQcii4m R@Cj%ZgQu&X%Q~loCID9JGCcqQ literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_knob2.png b/plugins/CrossoverEQ/fader_knob2.png new file mode 100644 index 0000000000000000000000000000000000000000..252b485ee8784472c5629cb49f36e94b43e31a04 GIT binary patch literal 783 zcmV+q1MvKbP)E=Z010qNS#tmY3ljhU3ljkVnw%H_000McNliru-V6>6DjHiJ>&yTE02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00MVOL_t(Y$HkR9P8>lHhQI2W zft7Z6tPtT!BwT?rZ~;r)0cWIKfE*uyjV#$InGaxDTmXrP4bTQASh0(jp{pG9bkAeg zscEUzkC~~zs{X%f;Dd+6&15pU9t;L&d7dAL2qJ*6xm@DCQWQl34XXgnIgVh|pci#qRRf@!_MR(? z#e$ukozwAne3ApW%pH_v*?@{5&TY&>d-S6>Rn%-9*lXupmK9G=PbF}aM`NSW2Op6v|uXN(*(ylSlrUCWU7p6PT7u$==`RmH=@1Ev}PGlz)7)sZ)Jg!{hH z2FOtLNxYjWFjlJ--Uo5#bvWdC&hzs#W`;ONCLs%DStebCSGAReT9HAO^*-Er?-6m# z=kvsUaM0M=Dw2&nny9LZ;c(cCw&A|5@^}WrEV+;>!_3IC3{_=!cQ=({(1%i0Ro4Lo z?(Xi^fThKKBSJ}$`kL>K5Ia>6MA@OhD~fS^eB7o0_V@Se`VT_f$N^nS3p%&%x1Hnz$%4wd3pJ5G#Z^2MNx`Ktztn{dlU#rqda4C zOSY(y*1h*0Rb@7t{kXloodEY8V#nk032+2#|A!yp{d7A0_0fN0{sIMGiQ&E)@hSiS N002ovPDHLkV1oZ)VzdAN literal 0 HcmV?d00001 diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 1af7bb040..0f0bef69d 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -170,7 +170,7 @@ void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) { int dy = m_moveStartPoint - mouseEvent->globalY(); - float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *s_knob ).height() ); + float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *m_knob ).height() ); model()->setValue( m_startValue + delta ); @@ -186,7 +186,7 @@ void Fader::mousePressEvent( QMouseEvent* mouseEvent ) if( mouseEvent->button() == Qt::LeftButton && ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) { - if( mouseEvent->y() >= knobPosY() - ( *s_knob ).height() && mouseEvent->y() < knobPosY() ) + if( mouseEvent->y() >= knobPosY() - ( *m_knob ).height() && mouseEvent->y() < knobPosY() ) { updateTextFloat(); s_textFloat->show(); @@ -346,13 +346,16 @@ void Fader::paintEvent( QPaintEvent * ev) // background painter.drawPixmap( ev->rect(), *m_back, ev->rect() ); - // peak leds //float fRange = abs( m_fMaxPeak ) + abs( m_fMinPeak ); + int height = m_back->height(); + int width = m_back->width() / 2; + int center = m_back->width() - width; + int peak_L = calculateDisplayPeak( m_fPeakValue_L - m_fMinPeak ); int persistentPeak_L = qMax( 3, calculateDisplayPeak( m_persistentPeak_L - m_fMinPeak ) ); - painter.drawPixmap( QRect( 0, peak_L, 11, 116 - peak_L ), *m_leds, QRect( 0, peak_L, 11, 116 - peak_L ) ); + painter.drawPixmap( QRect( 0, peak_L, width, height - peak_L ), *m_leds, QRect( 0, peak_L, width, height - peak_L ) ); if( m_persistentPeak_L > 0.05 ) { @@ -363,7 +366,7 @@ void Fader::paintEvent( QPaintEvent * ev) int peak_R = calculateDisplayPeak( m_fPeakValue_R - m_fMinPeak ); int persistentPeak_R = qMax( 3, calculateDisplayPeak( m_persistentPeak_R - m_fMinPeak ) ); - painter.drawPixmap( QRect( 11, peak_R, 11, 116 - peak_R ), *m_leds, QRect( 11, peak_R, 11, 116 - peak_R ) ); + painter.drawPixmap( QRect( center, peak_R, width, height - peak_R ), *m_leds, QRect( center, peak_R, width, height - peak_R ) ); if( m_persistentPeak_R > 0.05 ) { @@ -373,7 +376,7 @@ void Fader::paintEvent( QPaintEvent * ev) } // knob - painter.drawPixmap( 0, knobPosY() - ( *m_knob ).height(), *s_knob ); + painter.drawPixmap( 0, knobPosY() - m_knob->height(), *m_knob ); } From 47e6f3b614995adf71685e89bc3e12a87cd87b8c Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 12:38:51 +0000 Subject: [PATCH 15/52] EQ Change colors used, to be more accessable. --- plugins/eq/artwork.png | Bin 27381 -> 27905 bytes plugins/eq/eqcontrolsdialog.cpp | 27 +++++++++++++-------------- plugins/eq/eqparameterwidget.cpp | 17 ++++++----------- plugins/eq/eqparameterwidget.h | 2 +- plugins/eq/eqspectrumview.h | 1 + 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png index 73b3473b4f8cd1f390ece29ff24c4754775ea2d0..33fa4960defbf65b02db84e0b812bec717e4e0c3 100644 GIT binary patch literal 27905 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%0A?-KV-lr7 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 Hu0mjf8ekH` literal 27381 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
A`poYU)s$P)mrMz-@#cT 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

qxWw53E{N(<}UdctETZi=&tfo^gY z$>^TC$zow$kSwcbjBnz{;Qt-tKF18W+)25~*7&#su*O5jz>#l7$7Ekns?E7icawa01k zIccDq3dUMeO(lmii)z@0r^`=1rv_SXcDdQP#ekIadofDDvasZ&?BwL+j^U22+wrj; z-Ejn6OHfL>ww(K50p>9dixh~Rn$Ii-ra2ws^?BBk|2ZG?(5Kv{Soz0j_16w=bWQ** z#Sy1cqEeo8YA~eEKWD<9^y($;{!E$Q$~i!(FoJr$67Q#Z!P*W(t9pyY0*Xz)L$~Pf zO1sthak>}_0CcUAJECEC+)fNIV<}=9FsAa2B`<1|^tGHcv|Rm;J8hD45R`yYnpk}I z!vU9rGN%P9qSp{BM48HqE;K`W9>wT{+@cB-)v%2lJSH`)ZzHms0cFih$hk)`k5((9 z$Q9tQ<`|ylNi`OnEEx_OXs0q7a(UdChihu|SZt#mpH@*Dqf7Z*OlCDqH4&}+BfpTN z%tMR?**cm_inFftBands, this); - m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 0, 150 ); + m_inSpec->move( 51, 2 ); + m_inSpec->color = QColor( 238, 154, 120, 80 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); - m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(0, 255, 0, 80); + m_outSpec->move( 51, 2 ); + m_outSpec->color = QColor(145, 205, 22, 80); m_parameterWidget = new EqParameterWidget( this ); - m_parameterWidget->move( 50, 5 ); + m_parameterWidget->move( 51, 2 ); - setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); - setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); - setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255, 173, 115), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); - setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255, 255, 0), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); - setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(0, 255, 0), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); - setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(0, 186, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); - setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(222, 0, 222 ), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); - setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(156, 156, 156 ), tr( "LP" ) ,0,0); + setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(255 ,255, 255), tr( "HP" ) ,0,0); + setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255 ,255, 255), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); + setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255 ,255, 255), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); + setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255 ,255, 255), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); + setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(255 ,255, 255), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); + setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(255 ,255, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); + setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(255 ,255, 255), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); + setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(255 ,255, 255), tr( "LP" ) ,0,0); int cw = width()/8; //the chanel width in pixels int ko = ( cw * 0.5 ) - ((new Knob( knobBright_26, 0 ))->width() * 0.5 ); diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 784806558..735c7b72f 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -81,7 +81,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { m_bands[i].color.setAlpha( m_bands[i].active->value() ? activeAplha() : inactiveAlpha() ); - painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; float gain = 1; @@ -92,25 +92,20 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) y = gainToYPixel( gain ); float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; - painter.drawPoint( x, y ); + const int radius = 7; + painter.drawEllipse( x - radius , y - radius, radius * 2 ,radius * 2 ); + QString msg = QString ("%1").arg (QString::number (i + 1)); + painter.drawText(x - (radius * 0.5), y + (radius * 0.85 ), msg ); painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { - painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + painter.drawLine(x , y, x, y - (m_bands[i].res->value() * 4 ) ); } else { painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); } } - //Draw color band - int sectionLength = width() / bandCount(); - for( int i = 0; i < bandCount(); i++) - { - m_bands[i].color.setAlpha( 255 ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine( sectionLength * i , 1, sectionLength * (i+1) , 1 ); - } } diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index 6848ba938..c47ea259d 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -87,7 +87,7 @@ public: const int inactiveAlpha() { - return 50; + return 100; } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index d2aec61f0..34bf09444 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -180,6 +180,7 @@ public: pp.lineTo( width(), height() ); pp.closeSubpath(); p.fillPath( pp, QBrush( color ) ); + p.drawPath( pp ); } From f357bc7291a3e5e40dcb35a947d9c78b0b84de07 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 15:54:05 +0000 Subject: [PATCH 16/52] Proposed fix for #1411 Crash on LB302 preset preview . --- plugins/lb302/lb302.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..dfc54e005 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -780,10 +780,12 @@ void lb302Synth::processNote( NotePlayHandle * _n ) void lb302Synth::play( sampleFrame * _working_buffer ) { + m_notesMutex.lock(); while( ! m_notes.isEmpty() ) { processNote( m_notes.takeFirst() ); }; + m_notesMutex.unlock(); const fpp_t frames = engine::mixer()->framesPerPeriod(); From f27ea7bc2b97b58b0165e03df7c28c10ab2296c9 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 21:40:58 +0000 Subject: [PATCH 17/52] Proposed fix for #1432 LB302 preset preview audio cut-off --- plugins/lb302/lb302.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..e3e2628c6 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -789,7 +789,7 @@ void lb302Synth::play( sampleFrame * _working_buffer ) process( _working_buffer, frames ); instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); - release_frame = 0; +// release_frame = 0; //removed for issue # 1432 } From 6fb8df486f5f33605665243430d368efc8f8b32f Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 14:25:56 +0000 Subject: [PATCH 18/52] EQ changed file and directory names to CamelCase --- plugins/CMakeLists.txt | 2 +- plugins/{eq => Eq}/CMakeLists.txt | 4 ++-- plugins/{eq/eqcontrols.cpp => Eq/EqControls.cpp} | 4 ++-- plugins/{eq/eqcontrols.h => Eq/EqControls.h} | 2 +- .../EqControlsDialog.cpp} | 8 ++++---- .../eqcontrolsdialog.h => Eq/EqControlsDialog.h} | 4 ++-- plugins/{eq/eqeffect.cpp => Eq/EqEffect.cpp} | 6 +++--- plugins/{eq/eqeffect.h => Eq/EqEffect.h} | 4 ++-- plugins/{eq/eqfader.h => Eq/EqFader.h} | 0 plugins/{eq/eqfilter.h => Eq/EqFilter.h} | 0 .../EqParameterWidget.cpp} | 6 +++--- .../eqparameterwidget.h => Eq/EqParameterWidget.h} | 0 .../{eq/eqspectrumview.h => Eq/EqSpectrumView.h} | 0 plugins/{eq => Eq}/artwork.png | Bin plugins/{eq => Eq}/logo.png | Bin 15 files changed, 20 insertions(+), 20 deletions(-) rename plugins/{eq => Eq}/CMakeLists.txt (55%) rename plugins/{eq/eqcontrols.cpp => Eq/EqControls.cpp} (99%) rename plugins/{eq/eqcontrols.h => Eq/EqControls.h} (99%) rename plugins/{eq/eqcontrolsdialog.cpp => Eq/EqControlsDialog.cpp} (98%) rename plugins/{eq/eqcontrolsdialog.h => Eq/EqControlsDialog.h} (97%) rename plugins/{eq/eqeffect.cpp => Eq/EqEffect.cpp} (99%) rename plugins/{eq/eqeffect.h => Eq/EqEffect.h} (98%) rename plugins/{eq/eqfader.h => Eq/EqFader.h} (100%) rename plugins/{eq/eqfilter.h => Eq/EqFilter.h} (100%) rename plugins/{eq/eqparameterwidget.cpp => Eq/EqParameterWidget.cpp} (97%) rename plugins/{eq/eqparameterwidget.h => Eq/EqParameterWidget.h} (100%) rename plugins/{eq/eqspectrumview.h => Eq/EqSpectrumView.h} (100%) rename plugins/{eq => Eq}/artwork.png (100%) rename plugins/{eq => Eq}/logo.png (100%) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7807ff544..63c80538e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,7 +10,7 @@ ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) -ADD_SUBDIRECTORY(eq) +ADD_SUBDIRECTORY(Eq) ADD_SUBDIRECTORY(flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) diff --git a/plugins/eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt similarity index 55% rename from plugins/eq/CMakeLists.txt rename to plugins/Eq/CMakeLists.txt index 9b471d5b6..294005af0 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/Eq/CMakeLists.txt @@ -2,5 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h ../../src/gui/widgets/Fader.cpp -MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h ../../src/gui/widgets/Fader.cpp +MOCFILES EqControls.h EqParameterWidget.h EqFader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrols.cpp b/plugins/Eq/EqControls.cpp similarity index 99% rename from plugins/eq/eqcontrols.cpp rename to plugins/Eq/EqControls.cpp index 82cb11191..62d71cabc 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/Eq/EqControls.cpp @@ -23,8 +23,8 @@ */ #include -#include "eqcontrols.h" -#include "eqeffect.h" +#include "EqControls.h" +#include "EqEffect.h" diff --git a/plugins/eq/eqcontrols.h b/plugins/Eq/EqControls.h similarity index 99% rename from plugins/eq/eqcontrols.h rename to plugins/Eq/EqControls.h index f3646cce6..eec745b6f 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/Eq/EqControls.h @@ -26,7 +26,7 @@ #define EQCONTROLS_H #include "EffectControls.h" -#include "eqcontrolsdialog.h" +#include "EqControlsDialog.h" #include "Knob.h" class EqEffect; diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/Eq/EqControlsDialog.cpp similarity index 98% rename from plugins/eq/eqcontrolsdialog.cpp rename to plugins/Eq/EqControlsDialog.cpp index 57ba248de..950ed1272 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/Eq/EqControlsDialog.cpp @@ -23,17 +23,17 @@ */ -#include "eqcontrolsdialog.h" -#include "eqcontrols.h" +#include "EqControlsDialog.h" +#include "EqControls.h" #include "embed.h" #include "Fader.h" -#include "eqfader.h" +#include "EqFader.h" #include "Engine.h" #include "AutomatableButton.h" #include "QWidget" #include "MainWindow.h" #include "LedCheckbox.h" -//#include "eqspectrumview.h" + diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/Eq/EqControlsDialog.h similarity index 97% rename from plugins/eq/eqcontrolsdialog.h rename to plugins/Eq/EqControlsDialog.h index bda074f4a..d220d51ad 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/Eq/EqControlsDialog.h @@ -29,10 +29,10 @@ #include "Fader.h" #include "Knob.h" #include "LedCheckbox.h" -#include "eqparameterwidget.h" +#include "EqParameterWidget.h" #include "MainWindow.h" #include "qpushbutton.h" -#include "eqspectrumview.h" +#include "EqSpectrumView.h" class EqControls; diff --git a/plugins/eq/eqeffect.cpp b/plugins/Eq/EqEffect.cpp similarity index 99% rename from plugins/eq/eqeffect.cpp rename to plugins/Eq/EqEffect.cpp index 19f990479..75b7c8bb1 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -22,7 +22,7 @@ * */ -#include "eqeffect.h" +#include "EqEffect.h" #include "embed.cpp" #include "lmms_math.h" #include "BasicFilters.h" @@ -53,11 +53,11 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 6; + m_dFilterCount = 4; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(20000); + m_downsampleFilters[i].setFrequency(21000); m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; diff --git a/plugins/eq/eqeffect.h b/plugins/Eq/EqEffect.h similarity index 98% rename from plugins/eq/eqeffect.h rename to plugins/Eq/EqEffect.h index a2e660cc2..9a4cd9411 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/Eq/EqEffect.h @@ -26,10 +26,10 @@ #define EQEFFECT_H #include "Effect.h" -#include "eqcontrols.h" +#include "EqControls.h" #include "lmms_math.h" #include "BasicFilters.h" -#include "eqfilter.h" +#include "EqFilter.h" diff --git a/plugins/eq/eqfader.h b/plugins/Eq/EqFader.h similarity index 100% rename from plugins/eq/eqfader.h rename to plugins/Eq/EqFader.h diff --git a/plugins/eq/eqfilter.h b/plugins/Eq/EqFilter.h similarity index 100% rename from plugins/eq/eqfilter.h rename to plugins/Eq/EqFilter.h diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/Eq/EqParameterWidget.cpp similarity index 97% rename from plugins/eq/eqparameterwidget.cpp rename to plugins/Eq/EqParameterWidget.cpp index 735c7b72f..cafe5ac23 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -22,7 +22,7 @@ * */ -#include "eqparameterwidget.h" +#include "EqParameterWidget.h" #include "QPainter" #include "qwidget.h" #include "lmms_math.h" @@ -94,8 +94,8 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) m_bands[i].x = x; m_bands[i].y = y; const int radius = 7; painter.drawEllipse( x - radius , y - radius, radius * 2 ,radius * 2 ); - QString msg = QString ("%1").arg (QString::number (i + 1)); - painter.drawText(x - (radius * 0.5), y + (radius * 0.85 ), msg ); + QString msg = QString ( "%1" ).arg ( QString::number (i + 1) ); + painter.drawText(x - ( radius * 0.5 ), y + ( radius * 0.85 ), msg ); painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { diff --git a/plugins/eq/eqparameterwidget.h b/plugins/Eq/EqParameterWidget.h similarity index 100% rename from plugins/eq/eqparameterwidget.h rename to plugins/Eq/EqParameterWidget.h diff --git a/plugins/eq/eqspectrumview.h b/plugins/Eq/EqSpectrumView.h similarity index 100% rename from plugins/eq/eqspectrumview.h rename to plugins/Eq/EqSpectrumView.h diff --git a/plugins/eq/artwork.png b/plugins/Eq/artwork.png similarity index 100% rename from plugins/eq/artwork.png rename to plugins/Eq/artwork.png diff --git a/plugins/eq/logo.png b/plugins/Eq/logo.png similarity index 100% rename from plugins/eq/logo.png rename to plugins/Eq/logo.png From 8d3637e754f734235c2163860d6a482da6c5f96f Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 15:54:05 +0000 Subject: [PATCH 19/52] Proposed fix for #1411 Crash on LB302 preset preview . --- plugins/lb302/lb302.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..dfc54e005 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -780,10 +780,12 @@ void lb302Synth::processNote( NotePlayHandle * _n ) void lb302Synth::play( sampleFrame * _working_buffer ) { + m_notesMutex.lock(); while( ! m_notes.isEmpty() ) { processNote( m_notes.takeFirst() ); }; + m_notesMutex.unlock(); const fpp_t frames = engine::mixer()->framesPerPeriod(); From 8b2ce06da8c255b0ef8075717182f3aab3cee9a7 Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 16:41:08 +0000 Subject: [PATCH 20/52] Proposed fix for 1450 Mem leak in sample-track --- src/core/SampleBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 2db7e26fa..6941c6da9 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -942,6 +942,7 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr, _p.drawPolyline( l, nb_frames / fpp ); _p.drawPolyline( r, nb_frames / fpp ); delete[] l; + delete[] r; } From 91063ab7d2eeaee29545fafdc989f666ab23642d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 16 Dec 2014 19:40:02 +0000 Subject: [PATCH 21/52] Update Carla plugin to latest API --- plugins/carlabase/carla.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index ad3d92019..db14178a2 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -251,16 +251,16 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco switch (opcode) { - case HOST_OPCODE_NULL: + case NATIVE_HOST_OPCODE_NULL: break; - case HOST_OPCODE_UPDATE_PARAMETER: - case HOST_OPCODE_UPDATE_MIDI_PROGRAM: - case HOST_OPCODE_RELOAD_PARAMETERS: - case HOST_OPCODE_RELOAD_MIDI_PROGRAMS: - case HOST_OPCODE_RELOAD_ALL: + case NATIVE_HOST_OPCODE_UPDATE_PARAMETER: + case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM: + case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS: + case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS: + case NATIVE_HOST_OPCODE_RELOAD_ALL: // nothing break; - case HOST_OPCODE_UI_UNAVAILABLE: + case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: handleUiClosed(); break; } @@ -459,7 +459,7 @@ PluginView* CarlaInstrument::instantiateView(QWidget* parent) void CarlaInstrument::sampleRateChanged() { - fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); + fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); } // ------------------------------------------------------------------- From f2ab783db9443f3cba62867db29aa9fdcacf2a09 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 16 Dec 2014 19:40:31 +0000 Subject: [PATCH 22/52] Fix build when using old linux systems --- src/gui/PianoRoll.cpp | 5 +++++ src/gui/plugin_browser.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/gui/PianoRoll.cpp b/src/gui/PianoRoll.cpp index 2b454b381..266a1415a 100644 --- a/src/gui/PianoRoll.cpp +++ b/src/gui/PianoRoll.cpp @@ -70,6 +70,11 @@ #include "text_float.h" +#if QT_VERSION < 0x040800 +#define MiddleButton MidButton +#endif + + typedef AutomationPattern::timeMap timeMap; diff --git a/src/gui/plugin_browser.cpp b/src/gui/plugin_browser.cpp index 9827ce2e7..95ec8b42a 100644 --- a/src/gui/plugin_browser.cpp +++ b/src/gui/plugin_browser.cpp @@ -28,6 +28,8 @@ #include #include +#include // for std::sort + #include "plugin_browser.h" #include "embed.h" #include "debug.h" From f65ec076034f8ce6b96e39832655242e96f9404b Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Tue, 16 Dec 2014 15:32:20 -0500 Subject: [PATCH 23/52] Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fbbd979a..e30f417b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "1") SET(VERSION_MINOR "0") -SET(VERSION_PATCH "99") +SET(VERSION_PATCH "100") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) From da6fd6ef5cd72d98a29cd83cac9d0b2dcb69433a Mon Sep 17 00:00:00 2001 From: Vesa V Date: Tue, 16 Dec 2014 23:00:14 +0200 Subject: [PATCH 24/52] Update opl2instrument.cpp --- plugins/opl2/opl2instrument.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index bb64c901b..88166ab39 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -497,7 +497,7 @@ void opl2instrument::loadPatch(unsigned char inst[14]) { void opl2instrument::tuneEqual(int center, float Hz) { float tmp; for(int n=0; n<128; ++n) { - tmp = Hz*pow( 2, ( n - center ) / 12.0 + pitchbend / 1200.0 ); + tmp = Hz*pow( 2.0, ( n - center ) * ( 1.0 / 12.0 ) + pitchbend * ( 1.0 / 1200.0 ) ); fnums[n] = Hz2fnum( tmp ); } } @@ -505,7 +505,7 @@ void opl2instrument::tuneEqual(int center, float Hz) { // Find suitable F number in lowest possible block int opl2instrument::Hz2fnum(float Hz) { for(int block=0; block<8; ++block) { - unsigned int fnum = Hz * pow(2, 20-block) / 49716; + unsigned int fnum = Hz * pow( 2.0, 20.0 - (double)block ) * ( 1.0 / 49716.0 ); if(fnum<1023) { return fnum + (block << 10); } From 347b5a121dc369757a32dbdd46e0bc6ea48985f8 Mon Sep 17 00:00:00 2001 From: Vesa V Date: Tue, 16 Dec 2014 23:02:00 +0200 Subject: [PATCH 25/52] Update papu_instrument.cpp --- plugins/papu/papu_instrument.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 0b45eacad..8a3d387c9 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -360,11 +360,11 @@ void papuInstrument::playNote( NotePlayHandle * _n, //PRNG Frequency = (1048576 Hz / (ratio + 1)) / 2 ^ (shiftclockfreq + 1) char sopt=0; char ropt=1; - float fopt = 524288.0 / ( ropt * pow( 2, sopt+1 ) ); + float fopt = 524288.0 / ( ropt * pow( 2.0, sopt + 1.0 ) ); float f; for ( char s=0; s<16; s++ ) for ( char r=0; r<8; r++ ) { - f = 524288.0 / ( r * pow( 2, s+1 ) ); + f = 524288.0 / ( r * pow( 2.0, s + 1.0 ) ); if( fabs( freq-fopt ) > fabs( freq-f ) ) { fopt = f; ropt = r; From d21f0a7114b8afccf2acb2f726b2e1df790b8ee5 Mon Sep 17 00:00:00 2001 From: Daniel Winzen Date: Mon, 15 Dec 2014 21:08:16 +0100 Subject: [PATCH 26/52] Remove RackView widget before deleting the ChannelView Fixes the following two errors I spotted using valgrind: When deleting a channel; ==936== Invalid read of size 8 ==936== at 0x56FA1D: FxMixerView::deleteChannel(int) (FxMixerView.cpp:374) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== by 0x5216BF1: QAction::triggered(bool) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) ==936== by 0x52185C2: QAction::activate(QAction::ActionEvent) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) ==936== Address 0x14d51b90 is 32 bytes inside a block of size 40 free'd ==936== at 0x4C2C2E0: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==936== by 0x56F9ED: FxMixerView::deleteChannel(int) (FxMixerView.cpp:370) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== by 0x5216BF1: QAction::triggered(bool) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) When loading a new project after adding some channels: ==936== Invalid read of size 8 ==936== at 0x570785: FxMixerView::refreshDisplay() (FxMixerView.cpp:202) ==936== by 0x4B590E: Song::clearProject() (Song.cpp:740) ==936== by 0x4B7885: Song::createNewProject() (Song.cpp:817) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== Address 0x56a12ab0 is 32 bytes inside a block of size 40 free'd ==936== at 0x4C2C2E0: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==936== by 0x57075B: FxMixerView::refreshDisplay() (FxMixerView.cpp:201) ==936== by 0x4B590E: Song::clearProject() (Song.cpp:740) ==936== by 0x4B7885: Song::createNewProject() (Song.cpp:817) --- src/gui/FxMixerView.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 8948e8474..f750bbc76 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -194,11 +194,12 @@ void FxMixerView::refreshDisplay() for( int i = 1; iremoveWidget(m_fxChannelViews[i]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); delete m_fxChannelViews[i]->m_fader; delete m_fxChannelViews[i]->m_muteBtn; delete m_fxChannelViews[i]->m_fxLine; + delete m_fxChannelViews[i]->m_rackView; delete m_fxChannelViews[i]; - m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); } m_channelAreaWidget->adjustSize(); @@ -343,15 +344,14 @@ void FxMixerView::deleteChannel(int index) // delete the view chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); delete m_fxChannelViews[index]->m_fader; delete m_fxChannelViews[index]->m_muteBtn; delete m_fxChannelViews[index]->m_fxLine; + delete m_fxChannelViews[index]->m_rackView; delete m_fxChannelViews[index]; m_channelAreaWidget->adjustSize(); - // delete the fx rack - m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); - // make sure every channel knows what index it is for(int i=0; i Date: Tue, 16 Dec 2014 23:49:39 +0200 Subject: [PATCH 27/52] LR filter -> direct form 2 --- include/BasicFilters.h | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/include/BasicFilters.h b/include/BasicFilters.h index eda8d7b14..e6dfbcbe3 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,7 +64,6 @@ public: for( int i = 0; i < CHANNELS; ++i ) { m_z1[i] = m_z2[i] = m_z3[i] = m_z4[i] = 0.0f; - m_y1[i] = m_y2[i] = m_y3[i] = m_y4[i] = 0.0f; } } @@ -119,34 +118,16 @@ public: inline float update( float in, ch_cnt_t ch ) { - const double y = m_a0 * in + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + - ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ) - - ( m_y1[ch] * m_b1 ) - ( m_y2[ch] * m_b2 ) - - ( m_y3[ch] * m_b3 ) - ( m_y4[ch] * m_b4 ); - - m_z4[ch] = m_z3[ch]; - m_z3[ch] = m_z2[ch]; - m_z2[ch] = m_z1[ch]; - m_z1[ch] = in; - - m_y4[ch] = m_y3[ch]; - m_y3[ch] = m_y2[ch]; - m_y2[ch] = m_y1[ch]; - m_y1[ch] = y; - - return y; - -// for some reason converting to direct form 2 doesn't seem to work for this filter -/* const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - + const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - ( m_z3[ch] * m_b3 ) - ( m_z4[ch] * m_b4 ); - + const double y = ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ); m_z4[ch] = m_z3[ch]; m_z3[ch] = m_z2[ch]; m_z2[ch] = m_z1[ch]; m_z1[ch] = x; - return ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + - ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 );*/ + return y; } private: @@ -158,7 +139,6 @@ private: typedef double frame[CHANNELS]; frame m_z1, m_z2, m_z3, m_z4; - frame m_y1, m_y2, m_y3, m_y4; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley; From 2a448e92429884da3d7c15ae030f24cf40baa7f4 Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 23:02:45 +0000 Subject: [PATCH 28/52] EQ cleaning up memory management --- plugins/Eq/EqEffect.cpp | 4 ++++ plugins/Eq/EqParameterWidget.cpp | 4 ++++ plugins/Eq/EqSpectrumView.h | 10 +++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 75b7c8bb1..f344ed519 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -68,6 +68,10 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K EqEffect::~EqEffect() { + if(m_upBuf) + { + delete m_upBuf; + } } diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index cafe5ac23..0cbcd1bfb 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -53,6 +53,10 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : EqParameterWidget::~EqParameterWidget() { + if(m_bands) + { + delete m_bands; + } } diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index 34bf09444..d86899eea 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -49,10 +49,16 @@ public: EqAnalyser() { m_inProgress=false; + m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); + m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); clear(); } - + virtual ~EqAnalyser() + { + fftwf_destroy_plan( m_fftPlan ); + fftwf_free( m_specBuf ); + } bool getInProgress() @@ -67,8 +73,6 @@ public: m_framesFilledUp = 0; m_energy = 0; memset( m_buffer, 0, sizeof( m_buffer ) ); - m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); - m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); } From 43ad2d52bd69044c512563ccdcee9a8fe766c313 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 01:32:10 +0000 Subject: [PATCH 29/52] added EXPORT to Fader to allow use in plugins in windows builds --- include/Fader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Fader.h b/include/Fader.h index 9e8f034ea..3e808802a 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -57,7 +57,7 @@ class TextFloat; -class Fader : public QWidget, public FloatModelView +class EXPORT Fader : public QWidget, public FloatModelView { Q_OBJECT public: From bac22e96072fe91cefa43222e23569d9edbf4d36 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 02:57:49 +0000 Subject: [PATCH 30/52] EQ tidy up, --- plugins/Eq/CMakeLists.txt | 4 +- plugins/Eq/EqControls.cpp | 2 + plugins/Eq/EqEffect.cpp | 3 +- plugins/Eq/EqFader.h | 4 +- plugins/Eq/EqFilter.h | 4 +- plugins/Eq/EqParameterWidget.cpp | 8 +- plugins/Eq/EqParameterWidget.h | 129 ++++++++++++++++--------------- plugins/Eq/EqSpectrumView.h | 7 +- 8 files changed, 89 insertions(+), 72 deletions(-) diff --git a/plugins/Eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt index 294005af0..3cd4b8885 100644 --- a/plugins/Eq/CMakeLists.txt +++ b/plugins/Eq/CMakeLists.txt @@ -2,5 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h ../../src/gui/widgets/Fader.cpp -MOCFILES EqControls.h EqParameterWidget.h EqFader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h +MOCFILES EqControls.h EqParameterWidget.h EqFader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp index 62d71cabc..19b63fc4a 100644 --- a/plugins/Eq/EqControls.cpp +++ b/plugins/Eq/EqControls.cpp @@ -85,6 +85,8 @@ EqControls::EqControls( EqEffect *effect ) : m_para1GainModel.setScaleLogarithmic( true ); m_inPeakL = 0; m_inPeakR = 0; + m_outPeakL = 0; + m_outPeakR = 0; m_lowShelfPeakL = 0; m_lowShelfPeakR = 0; m_para1PeakL = 0; m_para1PeakR = 0; m_para2PeakL = 0; m_para2PeakR = 0; diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index f344ed519..ee78f459d 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -51,7 +51,8 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : Effect( &eq_plugin_descriptor, parent, key ), - m_eqControls( this ) + m_eqControls( this ), + m_upBufFrames( 0 ) { m_dFilterCount = 4; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h index 1057b60c0..0e1a3cc2d 100644 --- a/plugins/Eq/EqFader.h +++ b/plugins/Eq/EqFader.h @@ -32,7 +32,7 @@ -class EqFader : public Fader +class EXPORT EqFader : public Fader { public: @@ -48,6 +48,8 @@ public: m_rPeak = rPeak; connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); m_model = model; + setPeak_L( 0 ); + setPeak_R( 0 ); } diff --git a/plugins/Eq/EqFilter.h b/plugins/Eq/EqFilter.h index d704ae517..b703edc95 100644 --- a/plugins/Eq/EqFilter.h +++ b/plugins/Eq/EqFilter.h @@ -336,7 +336,9 @@ class EqLinkwitzRiley : public StereoLinkwitzRiley { public: EqLinkwitzRiley() : - StereoLinkwitzRiley( 44100) + StereoLinkwitzRiley( 44100), + m_freq(0 ), + m_sr( 1 ) { } diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index 0cbcd1bfb..4d3cb6e9a 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -46,6 +46,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + } @@ -56,6 +57,7 @@ EqParameterWidget::~EqParameterWidget() if(m_bands) { delete m_bands; + m_bands = 0; } } @@ -210,6 +212,10 @@ EqBand::EqBand() : res ( 0 ), freq ( 0 ), color ( QColor( 255, 255, 255 ) ), - name ( QString( "" ) ) + x( 0 ), + y( 0 ), + name ( QString( "" ) ), + peakL( 0 ), + peakR( 0 ) { } diff --git a/plugins/Eq/EqParameterWidget.h b/plugins/Eq/EqParameterWidget.h index c47ea259d..6cc7f1a89 100644 --- a/plugins/Eq/EqParameterWidget.h +++ b/plugins/Eq/EqParameterWidget.h @@ -32,18 +32,19 @@ class EqBand { public : - EqBand(); - FloatModel* gain; - FloatModel* res; - FloatModel* freq; - BoolModel* active; - QColor color; - int x; - int y; - QString name; + EqBand(); + FloatModel* gain; + FloatModel* res; + FloatModel* freq; + BoolModel* active; + QColor color; + int x; + int y; + QString name; float* peakL; float* peakR; + }; @@ -52,51 +53,51 @@ class EqParameterWidget : public QWidget { public: - explicit EqParameterWidget( QWidget *parent = 0 ); - ~EqParameterWidget(); - const int bandCount() - { - return 8; - } + explicit EqParameterWidget( QWidget *parent = 0 ); + ~EqParameterWidget(); + const int bandCount() + { + return 8; + } - const int maxDistanceFromHandle() - { - return 20; - } + const int maxDistanceFromHandle() + { + return 20; + } - EqBand* getBandModels( int i ) - { - return &m_bands[i]; - } + EqBand* getBandModels( int i ) + { + return &m_bands[i]; + } - const int activeAplha() - { - return 200; - } + const int activeAplha() + { + return 200; + } - const int inactiveAlpha() - { + const int inactiveAlpha() + { return 100; - } + } - const float resPixelMultiplyer() - { - return 100; - } + const float resPixelMultiplyer() + { + return 100; + } signals: @@ -104,55 +105,55 @@ signals: public slots: protected: - virtual void paintEvent ( QPaintEvent * event ); - virtual void mousePressEvent(QMouseEvent * event ); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - virtual void mouseDoubleClickEvent(QMouseEvent * event); + virtual void paintEvent ( QPaintEvent * event ); + virtual void mousePressEvent(QMouseEvent * event ); + virtual void mouseReleaseEvent(QMouseEvent * event); + virtual void mouseMoveEvent(QMouseEvent * event); + virtual void mouseDoubleClickEvent(QMouseEvent * event); private: - EqBand* m_bands; - float m_pixelsPerUnitWidth; - float m_pixelsPerUnitHeight; - float m_pixelsPerOctave; - float m_scale; - EqBand* m_selectedBand; + EqBand *m_bands; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; - EqBand* selectNearestHandle( const int x, const int y ); + EqBand* selectNearestHandle( const int x, const int y ); - enum MouseAction { none, drag, res } m_mouseAction; - int m_oldX, m_oldY; + enum MouseAction { none, drag, res } m_mouseAction; + int m_oldX, m_oldY; int *m_xGridBands; - inline int freqToXPixel( float freq ) - { - return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); - } + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } - inline float xPixelToFreq( int x ) - { - return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); - } + inline float xPixelToFreq( int x ) + { + return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); + } - inline int gainToYPixel( float gain ) - { - return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); - } + inline int gainToYPixel( float gain ) + { + return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); + } - inline float yPixelToGain( int y ) - { - return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; - } + inline float yPixelToGain( int y ) + { + return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; + } }; diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index d86899eea..71c64f5a4 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -46,7 +46,10 @@ public: int m_sr; - EqAnalyser() + EqAnalyser() : + m_framesFilledUp ( 0 ), + m_energy ( 0 ), + m_sr ( 1 ) { m_inProgress=false; m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); @@ -73,7 +76,7 @@ public: m_framesFilledUp = 0; m_energy = 0; memset( m_buffer, 0, sizeof( m_buffer ) ); - + memset( m_bands, 0, sizeof( m_bands ) ); } From 01c8cbcde09a9727e841b58ab9bdf22c37f7e745 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 12:28:03 +0000 Subject: [PATCH 31/52] EQ remove EXPORT from EqFader --- plugins/Eq/EqFader.h | 2 +- plugins/Eq/EqParameterWidget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h index 0e1a3cc2d..9ef7f80c3 100644 --- a/plugins/Eq/EqFader.h +++ b/plugins/Eq/EqFader.h @@ -32,7 +32,7 @@ -class EXPORT EqFader : public Fader +class EqFader : public Fader { public: diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index 4d3cb6e9a..31c74a7f0 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -56,7 +56,7 @@ EqParameterWidget::~EqParameterWidget() { if(m_bands) { - delete m_bands; + delete[] m_bands; m_bands = 0; } } From fae0c8c74e65b363d2d5e954fed5d6948fc72343 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 12:44:22 +0000 Subject: [PATCH 32/52] EQ --- include/Fader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Fader.h b/include/Fader.h index 9e8f034ea..3e808802a 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -57,7 +57,7 @@ class TextFloat; -class Fader : public QWidget, public FloatModelView +class EXPORT Fader : public QWidget, public FloatModelView { Q_OBJECT public: From 85da25fe2a0be1093198bdc35018a628828bee1c Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 17 Dec 2014 12:59:36 -0500 Subject: [PATCH 33/52] better naming consistency with windows builds i.e. `LMMS 1.1.0.dmg` will now be `lmms-1.1.0-mac10.7.dmg` --- data/scripts/create_apple_dmg.sh.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/scripts/create_apple_dmg.sh.in b/data/scripts/create_apple_dmg.sh.in index eb77b75bb..508b455da 100644 --- a/data/scripts/create_apple_dmg.sh.in +++ b/data/scripts/create_apple_dmg.sh.in @@ -16,10 +16,11 @@ DMG_BACKGROUND_IMG="dmg_branding.png" cp "@CMAKE_SOURCE_DIR@/data/${DMG_BACKGROUND_IMG}" . # you should not need to change these +OS_VER=`sw_vers -productVersion|cut -d"." -f1-2` APP_LOWERCASE=$(echo $APP_NAME|tr '[:upper:]' '[:lower:]') APP_EXE="${APP_NAME}.app/Contents/MacOS/${APP_LOWERCASE}" -VOL_NAME="${APP_NAME} ${VERSION}" # volume name will be "SuperCoolApp 1.0.0" +VOL_NAME="${APP_LOWERCASE}-${VERSION}-mac${OS_VER}" # volume name will be "SuperCoolApp 1.0.0" DMG_TMP="${VOL_NAME}-temp.dmg" DMG_FINAL="${VOL_NAME}.dmg" # final DMG name will be "SuperCoolApp 1.0.0.dmg" STAGING_DIR="./Install" # we copy all our stuff into this dir From 448b783e8bfac1e1d9537e984ea41f59eeb55ca3 Mon Sep 17 00:00:00 2001 From: tresf Date: Wed, 17 Dec 2014 13:43:14 -0500 Subject: [PATCH 34/52] Fix compilation on Xcode 4.6 --- plugins/LadspaEffect/swh/imp_1199.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/LadspaEffect/swh/imp_1199.c b/plugins/LadspaEffect/swh/imp_1199.c index ec1b35028..05a59c565 100644 --- a/plugins/LadspaEffect/swh/imp_1199.c +++ b/plugins/LadspaEffect/swh/imp_1199.c @@ -74,7 +74,11 @@ static fftw_real *real_in, *real_out, *comp_in, *comp_out; unsigned int fft_length[IMPULSES]; +#ifdef __clang__ +void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) +#elif inline void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) +#endif { fftw_real impulse_time[MAX_FFT_LENGTH]; #ifdef FFTW3 From 3c5d940561258e747024643f5ebe13a06235777a Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 17 Dec 2014 14:20:58 -0500 Subject: [PATCH 35/52] Fix ifdef typo leftover from clang fix --- plugins/LadspaEffect/swh/imp_1199.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/LadspaEffect/swh/imp_1199.c b/plugins/LadspaEffect/swh/imp_1199.c index 05a59c565..a5c7d3876 100644 --- a/plugins/LadspaEffect/swh/imp_1199.c +++ b/plugins/LadspaEffect/swh/imp_1199.c @@ -76,7 +76,7 @@ unsigned int fft_length[IMPULSES]; #ifdef __clang__ void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) -#elif +#else inline void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) #endif { From 0891c5358243f0346636561f7195b2b42e2ec835 Mon Sep 17 00:00:00 2001 From: Daniel Winzen Date: Wed, 17 Dec 2014 21:01:26 +0100 Subject: [PATCH 36/52] More memory fixes This fixes memory leaks in ZASF and Controller. It also sets an uninitalised variable in audio_file_processor. --- plugins/audio_file_processor/audio_file_processor.cpp | 1 + plugins/zynaddsubfx/LocalZynAddSubFx.cpp | 1 + src/core/Controller.cpp | 3 +++ 3 files changed, 5 insertions(+) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index fcdb6931d..5b5f1d4e1 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -740,6 +740,7 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ m_to( m_sampleBuffer.frames() ), m_last_from( 0 ), m_last_to( 0 ), + m_last_amp( 0 ), m_startKnob( 0 ), m_endKnob( 0 ), m_loopKnob( 0 ), diff --git a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp index ac126c380..7bb7e7640 100644 --- a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp @@ -89,6 +89,7 @@ LocalZynAddSubFx::LocalZynAddSubFx() : LocalZynAddSubFx::~LocalZynAddSubFx() { delete m_master; + delete m_ioEngine; if( --s_instanceCount == 0 ) { diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index 6d0fc1a6f..671f3f3a2 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -173,8 +173,11 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent ) if( dummy ) c = dummy; else + { c = new Controller( DummyController, NULL, QString() ); + dummy = c; + } break; case Controller::LfoController: From f7741f184f83e6b9e2f081d39efffb2c499962f6 Mon Sep 17 00:00:00 2001 From: Daniel Winzen Date: Thu, 18 Dec 2014 20:58:12 +0100 Subject: [PATCH 37/52] German translation update --- data/locale/de.qm | Bin 299164 -> 302180 bytes data/locale/de.ts | 159 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/data/locale/de.qm b/data/locale/de.qm index 5834e25392f6b4df48a22b2d42b5d2dc502dfa72..353543c22e1b3aec37ab3d10b9842529b2afd96b 100644 GIT binary patch delta 18140 zcmb`v2Ur!!y7&85RrksPRLmkODyWzfW|W|U2m%I-Bn2fX!8GbJVHOosj2KZ71CCbV&e{84z4n>C&w2Jf_qq4`W}e~K-D`Dq^;`AEsx?bi6nnI@n5D(e$kK(c zmdra-sr>$aEB4-LOVn~O5mhFBBMGcZboVq^9p`hwMntbBf{nofunE!YWYC@H-5}6| zc+4iSE%AZ&U^_4X^aSUCUf_GshxnioL>A#e^wN$@j1wR$`OS^ ze6uPN^=v_;g9JfVH`zva%mJM9VHlFWhl$7MDim#gBZ}TZ%)-VJ#pDq`|BGm_2Shmz zYzOWo`fVyPjhgtjNxN}XC`lk?UQ&AzWI*X*} z-*7#Sq{%VFtKsu0z9eou0auZ*;C?G7Pa-L8B8i|tlGcpC5F{& zmfDka?I%$Wp-@!Iw(-_*8}Cmb>3TL%(sD4L=uRV&p4TPTKZc~&6@t-#Ck}#1G90wg z{ya$^F$rfUXBZ_%OP#JHSHMU#@*&wViNxuvB-bbbLBx^VFqN3uf#k;hh?gx!a+7OB zU(bU#h`!e$*&EX#Ayy$PQIh2LjYzb5OLB*v#H)NH*$@4?K3AdSfcAouiGR-~xhqDI z7Ly!ZmMl^gBgq3T#7e%gv3ygKr{Kou1cjpYY?7x<#GKqu@^no0xSu4?SVp490g}@k zAkf<+FKkble_`S^Pw@xd6*h^J}VUO+)3V^Z6OgdhU7gk zT;;VS?@c91UIgurBGJZ=fZ+J0a{3H#<{9BsW*fZQ%?mW z!4ss;$9)0yNiz@{-lYR+cf}B!HjVUU?vkW^LniC_%t};zl;s&w>@+Gd%F0GmuI@|X z_eW8MkFcm`lWn~Ai>i#SM~q8g1JI9Ljs+5ry-zMbpkSX`Q;qq~BsM!y4a`lkc?Q+k zEujNLsfKkb1mr0c_PwZPP0ZE2CR7uW6nQJDrey}c7&w}0E;tHqp_+JOv85N)_Q9K^ zx1>5Mf0F8Vq=tn#L?`A^qyB#oHAtk!6|+dR|3QsYR+7*#avuO~eN&a(2SH2U3?TP= z(98p4soCjB63-*31tdtXUQ^4S`21}ywS=VTb$zn5vL3v1r8ZW+&!jd#FjqA#$jkVX z#G_B-wIGy4-*V(V>L~FA@#HiA2obxjkaej{J~>yQ`mW^j7$&1@MQT57AhdNCwa-L{ zhE}HbJGbI`FtsoEMWRI!wf}P@2|o`TgT`8@{g>Z~4f#eLnja?mFpxULZNm*yZR{IN z9jhN9@p?6NtT`UWvn6$`vzUZ)I(2N(2H!hD9d}KI_LZ}7)e8XbzH&fz1W?ZYUb+Yw@Q$|)2ruTsFelO(P! zq@d9rP+va~TJSZEg2uunS$=p^(714-A5$nO7tV2sCk4wm|9OXkYriM@_>zJ{(V-_@ zDLD5MKEFW0h1;NJ)&Iev6#VCK*pEC4350*Dzmh`w%_C7=Z{v&m6fzJVI$@bYR-zMi z4Tqh2vPhwYXM-ntic`+@t09Z@K_FLgJjk_i4r-KW7F_3)z5I#&Jnm}@;C z5wMy<2Vv}cytOg1Ifbr+0Xg}ILieV_vY9CKNCNRmYZOZS6!jF4Oj-){95@YjWHa?r zfz1o4cVjK_avQ1lw{;|`%2FSzHqDQxK2bwR^d>M7Y(o9USY{AEJOT$ZNDMkk5!3r1 z&{;|m@BK-5U#DoDOssDkiitiz?AK0;Il7gY`*(%>;9m-bWjiI7TS5H6F%W^!!%E;5 za1@9@=;1X=vKlbe7)q9`2I~MNV~CkrqGXF6mPo&ohPH&wxBo)J=FB5D$$^GtU<^IW z(x}mkh%dcOqf3Mk&sa-iepG?zpV7E=%}ErbQEKz@#4AM5tiA~UI*+1x=N6LKpFs=k z=fW-wqXmmb!ntmy#XhOT^;N+u#LY*+$HYyJWLfO3VD@Pv}!^jiB|_`_0UrwzZ;7J85{fTcfT4-+!L_BXX7)5N!M=*uh!mf0v$y(U> zPjvK!D{-&Abi7G1k}B<`GtXj3RE?+eJE8xhkI=;{uxx%m6>{&5bZsq6wUh_KR7;=f zR+~T)y<+KB=Xh{C-Dhbe9{SOXZumUP;!1CS^C7lqI=vkQW~R{F(O`xrz4z=!>|`JM zvNQuRY%-%#9npbtOf&TxNe=c*8(u)dKh?$%H>R%+Q|o+;8GaTczM&s87K1u&$YsWM ztw_}F%8J*)oRSVPhs`yi6}=S-3wy#!r^FLqyq=YfNygZjS%p`HM5S_AWv4PE$|kbP zT`mxdKE&#DO(Xt7Rw#^9S^Z6EL^r3fCU%u!K^w3p3B^b(S(DcW|oHK%dxTFQ`RXYm_*N&tn>2j#3wXWC{ZUCP&bPhEn~s*PhyeNSl2f{Nh+Sr zdX(5itX?e^+FU|AMK*>dvwro4L;q)$XZ;?%C$^v`3$xDh65cG#dcOD@i+OjMxW5mJ zE3=RIx`k|r5u)7qgAHq1m!$H)vk@7Xbp8|ANHk38E!l6bcEq-hVHQ0kTB9i&yA38h z?F$GKKD#cPcy|m*b$!^(gjU2o7)!rwNg;7=3dSeQ1I??E$2Ya^!Zv0FUvwWD`kJ!aa?CS{=Ni~g}nsy{H z=Mm={D-fSK-^R2oF2WjMGX3J(sjzU>A9MS)S4isGl2Ep4~ZwM_?VFxyED!B_$_$Doiq95(p8DiUdksAc|AI&)3ayB{8K3UzY(+{l?eLwXz0ZHy@mK zny-Jc6O{wO^J~^d=C+J)_bo+Y1cAW6T8zi6LIbjp#xc`=4q{3QPV6eeA4CH}E! zE8Oc|g+i6gzZ^Y^+QTWqPTnL@YPCYCHxnWWLuaqIv2%ZgQeaylvVIbG+ay#yvWd+K z6skXVA*Q?}ib=(xO~ZtP#WNjI?G;fn4S~V*08yrD5iw0WQ6Z%Ri90() z!Yff#-wfr01EOxd)yQb-in(eMIM|7{?Vo4si8tQ0KbanbPo928i-iKZPNpd|28 zG;>AF=iFShXo1Q5=WNk>EFoX3XplA#ULyOETBBpv9A}1#? zC{`lgd5(xJ>xlBsOA#CWjwt@ULRS2;h&@&w+ObU}yv6lFWfU^|h9V{BILcWrV)(*B z1R}%5@MX}dNkL-xesE3)F)BNhc$Vdhav(-D6Qe8V5Km|&M$bo8<(pZI-VT0`7h}ew zDzoIY81o$JHrrKrN@&AS+SRZ^Wj&vK3NjXs**^9_$LV-B; zLY-0(d>;e43T=Q_%#55O|RLK+;#yfDqkg44-52i_7q9c%NYYDeM>?7P!Gv`1!I#` zMzSjo8$D!~RIEo1f=rE6BDg*AJ5MEty_mgE%Snpb&U`9W$e4H;ZbG5qk<@G)3Z;vONX=&V zLE$r5YTgVk;>R(mMGKikyNWh;sUvw9U?*B#le|4z5k1S2I&PGSg{MotXKs*4uPt?Q zE+WxCTk13`8TCz9sndA`sr?-#MLm-~N&y}367#5`Q0QVMi}ix=4wC|h)F!D+fz)MO zZTRISlA@<66{K$4?vN@{T5)h&zK3qu-KKeae_sm zL}^k)MMPBNk4usIP&lhv(txpzh|g*+MISgvEXBbh#iafQ)A>poI1)V$Zy*g)Api;g zD8+t4w0g`SC4T8hWQdc7jfp~ysD(6a{83_SRSNlsCJKdlxsCo_3MJ<<(r|QC6!nxw zEJ3i^=ADfJLi$aYLE^$HY1A1F!u$@)qzUe4N#sA4Cceo+)O$stFbucRcak(&4-43* zr8L>clc@iAY07XInh%wvsWpC*RC=g1En^L_V*N5wOVzdve<)4cm;yuTWaHQ@Y5Lm5 zM6)I;N+kW=QG3jU!3Cl@@f^g9OaU#>2}L@_G9eiXD5TWzi#v z9*9x3|UVitd^9U_77q_%UUJ@p4 zAC8D^1FgP+_ys^E1n^}9(stxMIY%)8-aD3c^_#(N2(q-#~k?K8RfQQHm^|hD2VfWo?d&5kQ$6ImzHmjq z^5uGsijzbZlia|r8Q!qG++dRvv5#X>E02OV?H0$`54M(D zZ^j(?D$8CYHxX@pBYWrdK?%xaWB7KteNzl++%~!6yw!;KViZcf4$A>iKZyk&lLIsN zz(-b*0~f=vS{gl*1AoFy6qb{_-1LW!`XG1JPD2V9BZnruCbnUT9J=l(g5f)IuXeSG zru33~dBSj3pCyNRVUcJ`O*!ml4b-fH<*-L~#7+;9!+uqQy?YB*Cbq7D93F_sZXF6p zfhf?d%a{A_fB`>IT^=wX4Ep~vO*!B%zsS)XhNZ$RIoiDe+)JVf)8KI0&tgy$!D_(_b>xgdFT0$fT&o}B6dU*vbl#$b1O(jcf~ z`F-*fUzFtT3|7dpFUwQ%kUV!EA{po{?95!;rl!C$H|mj`)Wpg~EJaq2wGauh~74MC25C?Q~QHLYvzd zd0oz(jm0D9lk!GO>pWt+_Q`qq$ymo2Am{CXPa8T^{yi_Ac)ert*27sui;BzJ$~q7$ z6)W%DRTVMm6C3AiSX_p@=g6NVR{bUK+YhbSitkOSY7PkV(BPnjWGPKQEdTNcP?Qv8YPtdNmm5u5*7zUmIcqPi?!ZPAEG zzf~bCUD8Ik1vb{rR>+6?Dip(gY$w@^!grjfq6YB>4^O46k4( zzr}(z`?|#ht6Kja@*alQCag;IBS z6|MCr9vG!k^*M_?=7P%b69y!@t4;xhV z7WW~k@f=m72}P)7e^WL74Hj{8fU4O5bm+)wRkK%VM877fT71NVJMCAsbiD~XU{ome zid40hdJ?OBLgn!;4Enz;6$j|qvSX^Yy-~GWt_ClH?N#lL*1&8}QYbq0urYX#jjzL1 zKC7_6-}Q;gw_GGq;#HNedl@*VLn^+O%+xeTvS>WX6?|5nIJl};;brMdEgHa4_w(w6&->&LGwx# zTO4jUP_K$>`-XUAMOESeM-rw9s-#A{NZ8L*jr|aZMz4%A&G-ft|Ux zUNx?|C5L#TUNtUH5IdQzN)0H5+3u{6)w*S4oliD4uBw`3&G?3dsV1kpAy}TJno?yR z7H;ROW_V*#B_34Gh`vN(-A+|{#!FNKTdC6Lg45TkGS1#0DQK>0`FyBvxe}`7xwYXl zI;xgitOpV9s+GC$ZiZDLdZvp}WxGE`aQt1By&Zabql_wNCgOvxKWyxqsmj&EAn6~e zHm0{C?is1tv=Q+_SA!~VKb+Lh(W>8@z?;r=w=vCIwQ~zhb8_%M_?K$WSUjimBbDX2 z%UG;kT~{4Hj9k@jgX+Yq$tWwTRVPazvx|09o%Fo{!_h=_(GD@-@X4x6Y9vxUAF6KD zpNJw`bJZ^+e_8kWK6Oh?^SP4w<4)UG1aFhFmQpdRG)u? z#3m=Ge$@R!%;0TN{ajFon5nm#W38I;xoUX`EZ&!aYIWjS;w^WnH3M*dtU#?h3N`z) znp*b>!RPiXYJ+uwM5?d0laPSTovF67mYzHg*%)w9UDgQ!M*cZ<*&bOWxqMZZJKPq< zy83Es9Ko9IQOM6({;(ZfKdi2nh;aGn7gL2~tb<@!p zV%K*N@2MkxYcmtDozmd9CC_Y#fu5P{UAWW!5?eP}= zd!?1>-~n~pKJf3W7+4u>t8Q0)8X}$p>JIz&6W!zLj(Lp`H*`|_4}>o$RzsoGb%8oy zFXD-vE7gJ4_Et>*)5*eC9Z`3&Hn>^_(co%tb;!C7ME;i)^1#z-YXHL3p6Z?(;hf@J z)xEmIutbznhn2Cq(FN)-YlEvNgXrMu6Y6kld)ZnL?PYgVN0i-1;$4zD;sTbIvSQTH z-jL+48|vteT@n93U8;`p@P`_`QOEp_5NTwRI@a4nb@zfjQT(^3k=4J+v;O=T462whWc3j+X$yK6f%QBeXREb#A?y%W06ydYK~K1 zZS94iGDLlIIHKIw`)qvESA9Px7_s6v_0uP)Q(SjYKhGKulboWEmD-|yk#+&Q5ODh8_95x+qFZyE_3!?-bDBi%BQh@Y!b1?h+$MQGITa9?31jZWB+ z_;y~STmL&rJ@;$$_Yn3kxTvw{??E4zBx>y5L$A+&(%3h{jgOr*#dpGx>~E?mz87Ba z)GowUG9gPNO|Ltc10SL_eV+6o$xx&T|CB`HmzyS{wTyIGrHS%Frn6?U zCMp+(XHYd86WVJAdLqM#ir2(7oJRcn6;1q*D_Hr>)C>)Ji2_X-%_!e_D8yueABd$^ z)r^foBz5yI&A8&(kmyIvxC$Sznt$6*Gj0JsxaFalAR}XGm9Cl4{0i0`YiLrPQFUL@ zQ!{DVK9cCFX7c5qM6LWaQ&wRGH~gYzYETv8D<)~Cw{M3W07tGm_lN9 zf6cauMI`$5(`+Bxkwnd{njKC15j*sUX2-Gv7G%Fknq6g~uf{r>f)VSnx9+4uUe#Hl zc$TawIR1qw!%tK2xDSbzy)=6QFejqsX!dQ8h_OAI1L>Hwkia1<8&bK`nnOE};`4ig1@xT4&2@Em18`t!q;mUaXw9#O;;?{LEYU{%8G=>2;@apAwTZ9d3MG#p z+Ceu^puOp#jcxh@L%KjaxYlFh>4Dn8t1$$xMrxD2g7E@vwZj}?%4^wKlmi|yLOXnJ zI!x~+?eHI~QQ0Waj#?ECTYptMc6uw~8+$91ynMCe+>x>ks;ixtx)O1Jls5J2KBC%9 zwNvIeAl&Yzo$_KCiR)vuGuy(XceUpQ?11?~OvCy9sTY9E_!L$ph6Y*<(Oc!(ph>KWQM`meB{_S$#95MR_b zYd`n0>i@cr$^rjSq)^!PwXw6KLdl~*`xRdl-%_>TH{Bu%&(;12fhBYstYc^Gk*sdk z@kfx%4p*I2Wh-LFRGst%iOHD=oi-UU#VPSGp>l zjw1d~U9GFK6l1eKR9Ce;B9!M-bX6~HL;C$gSN&cI5^L7zYKCPYvgxFA3m8oNTC}cC z8k~!pudZnqq}9Pp=Uy=t6%wIyZ}STKFsJL9^+oKsKSk$p;2^QAncyMtl&-Da5M)|f z5W(s)ADvfq?6>7lw(GnnjUb--Qs;ZR7tysiUBIzI6buV>UHaT5zOtS!C=2IH`{;sq z^&_g3qzk@gM_dlob#D$I5N6PIw=NkKIqQ1YH4)!&UDqcgirBcZy1s#BNL)Uvi)?{6 zFXf_8JPp@H?LsNHrK7IDr34;4MCkel&qL&6ryJm0mAFr`E-ri%i46|ABo`Bw4}a=X zN+S-Ka7H(5Ec}1xVBN5^j#$^|tsB0h2IfLB-K0ycVVLZ6Gjy<&FAgbW`u@5Z)hnT- z6ROMb9!R2elrGa#hl1u^g}h6=&SJeFs%%y+h#J3i3#LJBB6{l(c3Zd-JFcETXs?5*2V0B6J2>Gn-uMXd8T zh5S$@-M;q+G3!5o2uNbq>h>Rm|K8e8xBvcI7?M}I{eL{bwt*1cf&GwRlMA{-JMg(f zqVCu@7i>vO(4Bk?2|Z}3D_pw?zQw33%+JFM?9`o_E0fq_anPMQQb>~13*G6C82cM_ zb(cJ^z&%FjF5CGd!g;5=JYy?FSE#!_9d9c9bvHMGF(Yh@o36Vx4h_3+*4-U>7CSW# z>K^t_CH|+I?tOk1@vld8@2~AcTArf&FxH9Kg&5t(i3syI|DhKr3*i5&Y}4z;UqS)l zh~9)%e45@&Z-4ha32Lh^RuWaN!fbuHzAzw5kLt_6#!$_Ar!S9n4l(fxc>k<)s_R`n9ih$-|Z@ruTJ)+!)`Uckg;1KDXOr1-jYAL<@MPyE~h5BaL z|6t)hTlC(84-ng~*8A_kqWa!8`p$7h*pYQiA5^^~Zrq{|3XVqr@l4;-?g|>%uJ3j0 z3O-LzDCnZT_X7kfZ8ZA6`}+~^GesY92x=MK)W+Cm`Y10>eC=#~98xmj(apwy0ahaZ zA1?H9=V2O`G}9*;gOL;-*QcCphD|aX^i$3|5sxdWpL!n_aE4w#-5Dmc%oF|ebd*%) zx6;or!!Fdy($Bg2lO#4yKR-Q(#ICveg>I+^^jM=`vc-rZbX)zh){tnwW%_I^%us}E zW6EOv+IN;h)MkF^bJF(_YgbOc!Gbqlwpzcb>mo$KC-hslA=vz*8dw=j12LQb_z1$X zJ?Nv~HVd0$>_YVg6MAB$r;UEklrGp65UW3QuRd0K&)695sz0n*NZfQsf21kO^yQQE zN7@(Q#V_iQSrFSXA3y!E{HBQ6j_c2mBa!bI{h3b)Mk^fFUu^i8_%DP0QYJ(jTU~!C z4`cZAFa53hu;pKR=x?=vTCLisP-?qW|8ygK$DW(|q9BB3?$h-zdO(uPS}SDUt_pdz z&H7K)g^n#3^_EYsYLd8kQ2&#~V1+i-pgVk&SaDy4;%OCwQS+F@(hP$=ayUuvZYVXc zHqrdb3R&9<3We^WjXt9dzPKM_KGuV8Sk07+z3Gt}t5oH*-is5wJNw7kATUcHJ!@mghYtMUnX!V-nd ze!syjGLppNGX}T6ULxjW2Df)bB*Nnjb<4vLRd}LM>NDCH|sK8)E4C6AK6iPea&Ltc-B2Awq>YQu$5BI%PUE-xD8jh5~NPPU=aMEro zYEvZ*C#Sqa`d`6N_%)6As6fN1$3evZ`e8UrH<7JRHeBkFg3K@5a7DS^!Ekpz-q7W$ z;Scv|Bz!;H*yXn2k2{D0CZ!r4J}XONMis-eJ*!BZ`)qj8+hRiWy~^Ha0JA9JkzhzRx&uv_H1I6f;f=!LMqx zON>*4pk&K$8K=*>3?CM4oIT?x3A?olC9l`UgdKU4Nf!BxMc?dsX4QZTdm88!*&_B zhu$F8_@QyvAMr#!H;j8cClD_--gxLI;v%n>#*^F8&Iyz8eBdHf^)fpfFWk6GEUB~c z%2aovfJF*<*J8$N?;Bt_af$JUYb)Xn-Wl(UBIxf+qD7Cv~d>%R#kztJS)rK)7297j-U6hJNwP_|+@;-?d^G)jPM6%H5jVAT+ z0AiQNf|rTgCz&)K5g@JYVA3IpmfXIW^s`qJ%RFz=XTKvpeTPZ^4Ciynq<;l2dSWtL znqgIMtI58EwP8O~@i9=h$@fjAx?q>{@OGwB{TWFj-c&k%4DsO>Q|X)-)W{x~$~rnC z1)RU!dO)o05`{wF+eV*HCdZQ}Q7n%zIo|k5l+wynsXQi4-KwTaSSH}-GfmDd;5VuLrC zTAj4Oa?h)3YV{lqIZQBlDmUIU`5b+Wec2C80eK5i-)w9Oe9($$eN$5x&2SXm$rNIx zp_!?hm8N7e8dWu|@U4L5fE@~@ z-p5UwT8}01{Ie;q#S{|e;-+1$u#m@I*?1}6v}emfsBv}E{w|mU*LRtY_j5<7+F)Z~ zdDDpnXG!wzY_goQy9@PmGhOixBJpdK>1H=fvb&c|H_wlR@2YIN{X|PFzKQ96F}&H4 zs-{1RpCx|EY~z*7riU{FNIW}ddSqQ(tJ===_&p-S=c`Ol3lI}m^)~&P0jD(dmFbxP zle(IUioxU$4Ko#$0uxVIOfRga^3Y<_+n#rcS1d5Se|wUs!UWToiHN@AHKwmvOqW7$ zn7K|y)Zg7~7}XP7?AqWj;=wb`O$xg~UmuuT zJ9fa*QERiuXQYNX9w1V~obl$i-H{sR90rjZu9wX1J`6y}dCTm1u@$yMMVLMBFeF;H z&0fXvpz1Bm9q!yEv9YGPW6VC(KTFv-f2g_Br~(+QuRqLz)(5Qj0`Y+LlgwSL4_JR3 z!~=3Q=8$TrbDY^|?tT$*PmA{EP-6v>D%CLe9FEm|wU@c)hV>+=PMLc@L|&9z(cDMt zg^KV~b3Y>_y4cGcW^F&W1Bmu>r zm)3wO4IN@*RA=+DzQ?g&p^AB3#wJXj66SSt!NlLp8=LrIt6P+Ln-*H2-DBQ%9bWGE zpXQy_9I?yU+k7ay1F>-x%}4z95v}kwA2Zp*3!X4vtPL-_Ws~`8PG}1}_>lRAFhNvb z%r{RUy(oXr{KwjdB<7VjKRmV-yL_ISAFUcgLVwNttZWCUWuW&h1IqVOxWAPS>68ceYiMM>mDL%~EENgru|auknW z^qHr zi2ra`$;^XCS;fk-(s%23iYrI|)o0I`u-JqF)}Cj~c~QCk-|woky=!nxT-5(|6IHXm zZfQDU;FAP}-u6g3f|8G%g{W}Lcd3O%+_p~;o_e_b48{yTze^f;B zgdrPt4B5cW7PEI~8x|iK8xt3k92M!*1rsQ9$U)|`1&s;VQ!r|cy{=I>-2x@bL8KBhW!-W2GoJ%R1;p7sZ;G_sI zDP{2Bgv8{3KdKG?mnZ#mdWE4kNq9&Yg`*Gu{ZW><5z4cYl!=s(k~}!&KRl}qb;NkI zBVXm6{(0Y( zkNF#u5bxw0^B+1;8;&3d|5~SOXSgEkRQT^&6a;}MDH;^_udmrTD9FjB^WdoXf2d2) zh@|8wtm|9tkmY|WN}Io*^6%8-|I2fotk3iyl6IVdVQ8BQ?C hseeMEQ`LIb_O!`i;s0V`+s$d}?L#%NBk_RJU?*@I*cm(kb^*VE zuEa<9f!)A|L>AGJs4$C2bSC<*Eto)bED1bBbo>kWo#@0?BB?0`XbScL^S}TMP!^no zb6;>8@lUJ3c{pDU&d2BLz&P*^xEX8(?jjNV72J#S8Dt^p5V1xXxNsU5j)B*S2UY`r z5VOZa$Zd#*9#$wW;4S5z#DltnCy5F^5UEwfXg8=P#_EGwVtg`)7bHIuM+t+BZ|67G|+`e*H0m909n8}Ykp24 zpMa$pdWLv#35CM>F;Ota{?nW&B%Qb=2h%a3Bc`wg*cqHeG%J#rlu3O5d19WcNYum9 z+^7g%1zkzFVk-ZeN}?NPuJ8{DH}Jn7Bs`zs^YP#Za1Dvokgc~vNgMz_^d<2HOY-3w zNdxAQ=-Ef1MfCR|X?Qc@RnL$#atbk*n+kdLIwS?pB3?}iB%I2Z<{K!7U_a zU;r!Uz9A`Q0jB;SNn2+SA5)-Eg4{@{H%a_En548&60KuwbW0>D;{j2>BP1P&!1~X> zZ#^J-atYjo4_YZ?Wrr!`MY}-!ARjk8QWL@onY}3ytvN^1?LR~VghEkGZ{yXsHs*aN z=}rn})fddb?+ZzK(}-9=6_P&q;P=I##fK#JSh*mnK9Kaa7SU2ilC}GZGzUm7Srd|M zvT^Vyk}G3r>g#Q6{EOr&lS!QaNpjuN#B0bTH;W*q&mp@JdQ>E4_~tNtXr4a5?}klX`Load%c(k&&~Clo@68}^0a_`ygHu0WD~ z1CmEsh?UxEqy1Zw7pGvHuOu&703i(}ISNugY9GnbNhJFGLvl*PczIw zAPMKaB%j3%Cl!%u<_x0WUrBBBg+2HJ4uCyqMrt$8c@e314u^2J14n?fEu_xC4|}{M z&8T-oz1>KAIE2{3(WEc`fW+StWU`(|aw-+}8Y;7ZN-Jzg6&t-Hp8td@e}!p1Utr_4 zK&m;rG1TiY2y6M5k;A!O#7A}|hu=`;&qY*sbuAJ*T&XUERqU|rp}I#Us98O#YZVU9 zK!rkgn(Ebq^rj7>dYDR)cAn}*<9l9Bs9r)AIFjn&jm6Gza_WXRiK#*jRbC|38AZ)< zwi9K!Q}dC~^`?`kMb$(SodT%El+7eq5IF}yecu+4^Ju88O=*?Ld6t{V-~asiHS8shySYA5aG?KlY#wzkNvj-b1cN*nomDq-Q@8DTB=&ewx91O`{}I%E$td`r@T$~39#1ssHg!L=4<8(% z?nnNTXyHxWU(JNENwu-Z3+i6Hm)N*;a%*#j=u=g48@nIhe{W;|BjjG^EQuH8$-Uk@ zSkt!T-f$g>%HPS|r2`3XCvra=Ni_bdjnN6@u{j+2eOyoBPLh8SDrap6hiHd(xpYY*C zKiX2CG zZ!dVrCY#B37|eOKi8emrO8k?CYn;H1PdjmP$<@ziNh4C zhvCtTrKxRU+l?z|+KN@g<_(}}aahB)b17{0TH^7YXm)8|;!BM*=XXsK*(M6#)`mpJ zK8k2liMahwS~heAQO_`1bpM~-J^JpvU zOd@U|ZF@R}`0RMvc^}1DmUrsA?W+G zx^z7chOEaQg}jq1-A-)`pAZN(1$WWi4!tZSe6Q2pp5w6QmFN+RA#vvvz3qoD_&=ae zv$_#mJ%&DofpLB5(`;~AZ7S;0kJ#C-RJ}+XAqE03&)evv|$AOjkw;u5cXKei0gV|3R z2fepr6+%L>cFkDj_c=u6wzC@5%9AK{ht=qPjac9`*0663@fV{N3e|hoWOoeF?Y69? zT@7MmU$K_sOOjX>!<=VlkjU%CoEM)#WOSdkv8)|OR0{`rI-$$N%@vuaN z;>&5~-6#=m*@yYae=xJ{S>KO;NYZ36%Yf3mi8Xl4{Mtwu$lFH00yfP00E_3bVb6+) zt#o1jRw*|>VE)$g_sX{{QORN^|D5eK1jugGR% zT#EU`W;xms`?oB!=rN53oDVObnYay5=+FF?>ainE+u5Wcjk>{?3)64(2&yfsyb z%6wvZyJ7pU9AkO!%8=xCl-(Ivmx#|~`K@4(YFX^qeYp&Y;buuxOR>`90Sw&&ph_<$X%GuCG7Edgyojf?D=>s!Hr$)Wg9F}sRrz&^?^0( z*sIQ+h=#zpL^WzjHV zxue;S3nr4PHQ?0BokWy7=esHsU--+$#S$0(O-ZEJ;@U`9wwkkfiPSujJO=a1rwwq^ zZoGP00}^GP@me+giNCE54hQFg6TyezB(m^#?YKiGOzX|r3MJEW?r4vxU$BPPKfRyG zH(4Pc7t5V4-6S?<0B;rt6a1(;cRra%VpJG!t*=6?RYl%rGV;L^Wq3CWgssy;?q>ab zTp8~D5Hqk+@ZRI!V#coXp$(4{#oge;Yvx&qf6wG&ijb@Itjxz%dw>}5KRzj{Hl#I$ zhi=az9^=NRXJwMOXV2%%#8O=t%IEEICb4fXUu0jK_!7YvO?*Za)W^n6Fb$Ar&`)SBjNH7Pqosc1>d&9kwo|jzAX-%@6Wfb1m`X1 z+g5`Mn(!TO50O|A!ZYeQ5o_zi4|tR#FXpckW=z7& zPT?mV_mW7g#m^QZ3n~}I&!2iiY^pD}Ty}0pl2dh_+X8wWvytbvTTWt5sErZCuN~b@ zyyXOby;~|W*T)K(=?uTV9nwAaxI$I}*NeZOxG$lIbSe*!_}1sq1hd68{i)>?%|PQiw%05vs?B z5i{-=C8bg@!It%+OqWuhYTPXaNIl=#bogfBF6-m+tiNOP6h%$DH!TCB8i#$b0oeo6Rs)^BKB;p>9 zVoZf9sNcL3V}d^ujkb~5MT#-!DnVu{i1D9rJ-C!YW_&HCY(Gy@_wHi)nj8ci55)8& zXp`lzn0^9W@~;R>@q_*+47DDRn6y#MuCX1Zl4LP^HOeL5&WhOwz~Y)>&b;Nw*Tcn} zH&COcIdZy(B2rv~(@GIb&O+GE6^W?cwNUM_7t2Fs;!k`;?Ad;BN)JUs&Ev#3 z99Af`>Ms&zd?p@UTWn}#IZXUhRk7jxWD;wqisUMo!muKd>;%rr70KV=s2EN+gk-Blgr>f!Hrp9I6EAo>NvF=?J^B>byAi629cZ7aOnL z630Hm$+=G!XB?{&e*{__YvAB0E;NQv2(St0A(C zuJf=I*Tp5v1+3{=k?YNI-AP>e_bG{_i{hFGV#Xmk;#%5#5{Dm(8!cWVeHx{ZciJIt zry|jLw-!XA^X{Rr+*v)C*isVtDlqDp$oDu&B6O=L$c4#l?jZ_pEkGK+Ox%kfMSM*s zao^ONL~N*dR)8GP&Q3gE1%vZPC0=cSa5c>rh4-!JL&Tdki6qtNC%(Ug;h`7eZx8~P z0j`qVDS>#|SV>XG=n#vf=;LA!N#9lwKYBtk=V3|4SR}hrFv+83C4_@TxRpy$^E70XDjYNV9ZtBraW$!Y*qNzR#8BJLjShp_3MT zOl(H1;}Yuu3HgDIUEWBG^ssn-pQOdpVNgEak|OK=A<4K}S`xRFn7K?`UO6YrlmuzX zt|<`yJR4{Ck)l#j@Q!v?$ZHl#F*{KnUOH1+k!-b3#Zv67a1zBqQi5A15~^x8p7c@3 zm(^A%_CA)9f@h)}*+L8pE4Thg&Yofb$b z229w8Invg{e~2c_()NivNchD{X)&MRm5ag8BzDyY5jN}^3KoOb&wqef;4kpIl(vkM z*j*2VDDEBts=yVX20RODkvQ%Ch65d(SY0VCAqPe9HVTDnzKx#tHa=b`rM-rC9ydlI zD;Y1P{j{E!QOHWVNIPd9K?b-|+L>_`{F*{agM6+4I*-Ip?g8$rge**M~o zbfA=_6Aa*a>A-YEWT6A41Iu9WihQL5UjvB+*-4of;nP~>NSWW9iOr6dj_q?G{(hx& zJPS^*?>XssPA;)BcGB^@mBeeUm2&Qzh_<9j7hN|KeaVt8r@SNH__vhXqym&jqmWk* zk**{maC)>rx@vJa2^UaSx@~DrBD%kHhZ@5t)dZV>qon&?P^;cNL%P2p4NkEtc$dUj zo%Aq106b@MqRc4imv?8-Rr)pbDFUAX(ytLXKi*KL zzPk~vPmtL!M*Kk)kc0kU3DhAHK;)+P@5=He%*e~JvNjYRVe<*uun~#W^wJ82eSO*3 z0r5wcJ*G`UtI zxP+rFa_#hYL66gENPAO=p z+&m#S@__3pt|&Kdj=<@QWxm|ht~K8Dn%s1EHDaIaQR5Cok=r1*ybBhOmRsIO5$$`D z+-k>ZRQZbJ_Wwdgz8;ZXXYM9S-zs-aA3~ycsEvav$=zFFNrNiL?yHhX*j-U5^?E9M z2mT?}YnI$AJ`;BBj@)Y<463C`qTK6G7u2Ax%6+vq&cOTZf7MR|BHL~h9j z5QUaxPkH1)1W;#s$U#9!q+S*&wD4C$bF-YT1CnewjUwZ%qUMjXC$Wo zD^KmHC$V>hJhlHSV)I?V4CsHm!SeKMtj&eP^6c?&Aw#>%5gp-sx;ohCnItb94K*x3 zSzhch7}fmR3fZPH^5S$P!To2*(U;~Sx9uUvbj=`ftFF9!S{0Hijh0tz+)ez%6dNzK zmgA$5UUap{YY0ws?ln0v#}YuIsG^)y3R3%PpuDjnoM*42@}}Od2!6N7n?5~xOMaNCoyX+MQ@n^8ZjvoZ!W4T=zU2%9)F)dYj~M*ax9!tJ-opU@a&U)|&{ zvmc{Ic2oX(J`yo~0QyKoi;3Q|OO1Pqax#o^SF*!n2vKULZBwtl3Y&fxzmsFL`gb+U$tg1R4G3+_D zs-`PiN0G->b*5$!FJpOXJ1F*1In+H!yh^^R-t(Fy4w_VrW1;t-{8f$D4I!!C9#!-C zg+$t)sur_g0e4JMwGP4q9dDv){XT~1R~wbfSKQdGpQ^260rY;dLaEmRReNb5v3j#r z9Y6aMTQ?HKgRM(dbsCJKn`PZA99$>1-ci*#t1hJ6L80h+!bYzeHa@?t>b7MxQQsFT z59ji5&kib2XD4EHqf}l=&ZyR3QuWlHM0+q+)$6q*@$B`gUe+6~9|huu>(f-dyF%|D z4g-gS$H9s4{|}OJFbNm9s<-ut*1rMqKpPsU`m7p2{ML0@-GQtU7DuyF9)u;r}DQ4R~4)Ltq-zc9EbeSH;G?L$$7|Dt0BfXt63THyG8KbxrbwuEvlUxOnj@lp+^4W z>!z~gH(7wJ{DbPQ^#SBgss}4ErDI;I-udLB2^OIG6dHmWRe9B?OYKN%+(h;5MJP$# z%&PCRFjMpQseU&qMtk9>>Q6#L*!YraZfzC+byLd|VZgptRjViE5_j&U)&$}F%y_jf z3)*vkom%$|G31^f7PZ0Jx1g75I|+%?($8u;Yl*0hgN^R3)D@~BQ24jGy25}&lB!ft zS3HB5@7GJUH6CEiCo1IG-E6$>sQzaXqTbU@)phjNak6Z5&r>^?ZlZ26Q|k$14gtrSQ|gu*%cCqf zL)~gN*0#1v-NrtPMD9{`n@55~i!y4LeSMI4VL*Ao}1+wVUG%lKT~ z@e|x};xI54+@$U_1n%0B_yh+vh$YojcdoMpA=VVN+ldp%3G) zu2Awgr1n0B_+Wo)bua6HNh3fEn3SUKZ5=S_1&HxB)=~Rzb0hM)p^$ryP+Q{(CW_Sq zcfr?$JWvnnkHQHXdP(hH-s%?rsQs-2ZVU!7;KuFh;no2+7J?XXQ$2M+h2tb%y;28U zL(3=riaNL}rt(*eI@le%GVYgDhjjFU_H0#$>_wzA%|Sh;s|h=XKB>n#;oLu7J?$h+ z_ulpD8T#7j{CHZ4!bvSpb)?mc1r1WKcmg4b^;gK+4OFkzBKGtAsg94eZWkD;UONgx zST{qx_V6mysB_d-cT8(@)hA2BPOR^xKG(1v60I2ZxhD4z6~`%Lnp5g?gP*|PS5}`J z5lK`pTz#v(E9#<0)dkZn@cV_eaA0MY`q6eD#C+-MmoHFXxS6AVlQ<8p-th`q>5=NU zG1s8SZPcF@yAoUQpZe=!2w6r)^|vP2R#GZl{VnbZ(conYnWdik`wjS#jos8gf?gB9 z6Q};II!!EejQY=xpDPK>_0gfBVy{tu z#t*X&Yjnbn#79?+ZpU7dydP@x4-vkv+y_D(6Z&ZEilEn*Drrizw*D?(Q|b`x$I-W% zQpe!T?s#d+=dUHHLOD&q#_lc(rSe-f4aVU6^Dk(c1|wex_EX4A%{0woGcZ%NG;IpdmKgCs z)2-ne)XJSS-KL{-6zgfx^r`|AyUtP5N5F)3Hf#E{fDm;Zt?A=4hxm;@8ef(Jw;QIA z)qibc)AE{rx%fQ1sit3XDlxfGGw42KpzywC$P2h@uG0+vHW{JaW=%kQ8E*WjCeRfL zOVV3SU>Xe1h-e#wExR?Nx*${WS82vJTY_k|jb_}$JYs`-Xr}gghaDLfO_;|jG)g7# z3$gG5&D=mlNVhj?!b_!KO4BsqmA}C4GEI1b_4-%Md>M&H%kP@`ZSr7Zmun(wJ;MuL z)htYc_I&lwEV}uJsGXN)@fI{zEkj0WBKy=NzV?kKs(WWRh1)g`SfYt;1a~_@(nMdu zK!>hqV*g!;@<9#F%G_~?-8yUHV<0QDnrY&Zps-E96$;HM8@r8EDA^ZlHcGK1ww}~% zZMTWonwH>ZFiEp5G6owbDpu6&+ItvMX|GBDK7!cre>Hoq$Y`3q2B(mSyrkK`ppe9X zbD9Hl-C-fiYYw&=M(p?u&A}vOI)V9`!xf;W+Gm<0Gj?E)+Z=_wTBbtr;I-z+`C_8w zOmpP<5Ln97noMuVLf{I`@tqPjPW+juIT;IS?Ke?#8eKf9cu8~mP!@hLTp_PLMw7Gs z3*75F8wdZR$@zK%-40jH#eh~YNG8q28-r2n%C^xXUvsr8W@y-N&Gnyw#G|@v9&JIO z(r3Nq(e3ZVKOWaS>G_UC`*xaVV-f%J4>vV0o@bD#bYAmn04lQOKWctQ!zWBQq?Oiy zr=qlmT`(Y_`?RHvRzsDft#ln7+HU`8Yb9%m>UYsPwvyqz!nJi@KSSf?g4U_|IAYa* zX`9wTZh7LSwnZci&X=~@Rtxb!|BX~AG)=XwEVv+*%hWo%qpjC zsk~HQ+rbxaGGL*$Tf`cajQVQbLIq5gAB;X&<$WGun6_u5cbAH2af?X)Ve;SN~}`Op&D=__Ml za&Ks-|4t_Uw3;?-%W&BEDDB*+cEq=yQ7E+@tqpfZ>J{`{yC7mS!uUXoHsZ%|BBxf` z#Vg7X@BUu9v=eN&=M8Pl+87ci?rLK%K`TmWwW~WdCdnM7UHuv+`Fw3{!biB_?orxx z+aXjR^R?@aVzCRY6vt_$> zPi!=7wzEPu=cRVf4orE+2<_pj6|nX%wTHV($OlGik50=adevTg$^&cbb3=Q&U^DSo zIX1qYrpM)lV|DCWe@g=imD%7*^; z>a2Zkx<|~hw~dXSYoAZ7LabJI?MM9&;@_reKmSD_QD=(w`yl9fa*RU$-dmy2X4%+% zfkLT8f%XT!CqDep{@Q()Xn30Tw=XQ-Keu!&w*+#`5jy@1GqTr9C)L~s{oim&Clw>H z$g$UHLz@uaa#p9?hwOFGW1U?O+^~d;uGB1NV#Z`$%?((J6pOBQB?KU^s_AOq*pF=a zx312^(j+!r)YbD(B)Z3S4ZJ51znZIS7z5u?mvpUqBgyp)(m7X+fUwupId^zZtWgJD z>!G>u|A+4CI-Wd5Z0$JkG`LgO$!;Q82qG9==b&?~gQEGPO1iEKXJE>FbRL%m!9CZ| zd7sN6{-u$w_mBs~*Tw7lB;tJaS)I?}VMNs@>wIq85vNbO{%v652PNzJTRS)p*XRZ} zG7;a?PB$dZ5=d-LbKM9RylJWX3dOzKy1>IIwYFHG8(A7R92KG)>9dO1)GS?4t=hyp z8+2ob?AHnC+QaZn z)kW)I2@9twWSWY)XiJ^yD800)t=QxGjOQld$E@NY@KwZKTXh*# z*leNh^g(=HLZ>?y?f^B-v*@y)V~TF?)#aq_hRr{%%gIQ`gH+L7Tq&bc(OP%$Yz{WX z7o6-A7lx(EC69squYrn2wH{N*oLmNka)ZGoo zxX%CT9?Z-o%ATrwIx+&SsW@FxMj~p$mb$v4+s9GUy{r2&w;Hj_O!svG;`g)}dXasE zsOEmXZr%+NJ6`KeXrj}i_xchKicmn@rY~6*#i;Xs`ietgC)Uo?SNfoXw;Z6ag!Y42 z;-atO=8VqGaeWP^M8p&M`dYe4c!3i7dbg^;PR!Fc&=(O|)+OkhDsg{?zGdV}Z10(^ zcfO8{D8!;~jr|9_vzxx_gp~(hq)u0K|Eme&~r|#Qm!215QI7{Sy^hMBqk!perZ7@vD9; zQZ3Q?n2m0iZJZdQAA1!Ra8(2SWTOxKe3*Vpc5CcO*{NTAwHon}Vfx5Nuyhd<^-;B8 z8OyHIN5!H7vMgC2ZH7IlpQvAP3*&zMs$U(uoy7jb`ZW!Z-ut%Guis;Y{=eL&PikL; z27|0mLBoUs{?AF?`qa-k(279)_SoaZ+F#Z0wBU``576)KyB3{|+4_C^5kwY5gEhb+ z5W;+S5C}td_pE;ZGHgkaXX-N-_a?efU4Qx^vf$oLZ5+5ve@0_jgRNpy^k-Y4d|o0> zf3`cSSCd-l&mmT0U6}q{2L9GyLYV$CRUz`2qrdzO!DY#(`s>Y}BZ{4*zY&iq4P2_f zk&dM+8m7P71Sb2_3H@CcXw!yL3Z<6E^e=b8Hyr${FYJRzt3?O>+X0xtbw&EO796ln z50neMMmPO8YXf5UBK^1b^-#Beq5s1|pq`Ztx-(hC>`0-wmtZhzo!^*i@gQdpqI1(3g47L85fu8d` zL+zvt;?H^*99|(Z`ZC|(SQ#_1R&A);eW|@!DEUvmw~fI42MRLztoY{WYjq zzA&^rgFxbzi@{~;M@)4IL)*4Ph?g8|Xq$~qjg_g~5g%%@KabtTT9SuZ;m; z8ocX_B60JDq3@qkB(V_&|41|}xYiJ$f^1ZYjN8!kA4}QqhJfp6EA;7V2zb4n_}J45 zC3PLch)WpG*GnNUpQ2Fko`#T+VZ^RIG>o(I)-6M53S>RZ-4L2)BysnSA#5HbAn2$e zTI+~X^l8J=OVIw`%?vT$A-cCq8e(_n5*ZE|*7|Zd+tx1$8hStD~j!H-x|)AdqBK&CBxbBScx}v4B2-3 z&@jGe$X@&zrKanKoF6g7ryMj~eBOun^8iCG6(IReHQX351)1DJL!R~evwsZ_R^tt8 zxELNgFCpPN#Ks<-43F=_>(9$JJbhh(L`0CZFWJ?OgFmJ=phzzW7lHu>LK=i&+4S$2akQik$N|R3$ z&zx+O>tRMN1{qa;xg_59GaC0*A=Z7YvBWkcWa`$&Qj>OI50A=N&XVGbDX(jEXx#w~ zlMJK72)wb=5u?*Zh+ghbVBJ|6+&)O5U=GH9!!Vv`WbAk15mAjSqu&eM_{LA;AYKNdUB+nf4_`&1TCvf;2)m4X zE-(&9<|oeYFb157Lu9rIEFjWFDP#@S+t|EVp_ra$7a9O>;2rrCx!)OiWX~;W^al(>D#Gbb?PFM=QXkwhO9DM0$48z}V zP%kfISb-CX_gjtOiJxJoUfJ04f-!s}&Rb44E|~3wF5YxwWFIKi+K$GkWjEofDjSza zXQ4oAR4BEvGp^Z(PESatab26&MCbl7Zpg1fJjb%um}rgm(;gdB7ogAiHwJ79J~8en zc0zVuMxo@KXxuY$2@GT%Em}0sZ_gTAn6RR2z_~jF8nqWNqcpOo;d}C&p`Pj)) z!Fc));-3zA#_R(a=TxZiYOl4}4VhuQmj8g*1j(2e=?rJ=LH7vaX=dl~Z` z+YxUVWqc$Gp|1hPM^Si_(aVjGtZy{l(fIV>W#V>QjV~I)3?&9Blv@2XzVVAh1Q=s{ zzjF?WfJeq3Ya@sm+M8I}N9YXcP3jbQ`!B;y>hs>juG)h)QEWHrO`5L=gf>4m>5vRd zHOrgy%hB74+hfwFe1;RPY|_8R`Lf3*{d;ird6U`F8d*SjQ;E_T_{k|#x!%|`9Qxf< zZX`qe^VDQNZVvWwJ~!EK4?%6~iK#*r*vB|ug{*xqg+e-GCG@}JU{jUsY!u0}O;z&$ zKpj7us#k)LG^%Z?j%q&7{%xw|0w-y{Z2Bi>H&H^2smYcv#G}`nn$-^^8r96ydP_IV zP(xGet>Cx|rZ%$+uy^mh$;Gh~HVFrq+GXPbRuq`py}>x9*QPGY@A6IEvMkSuHI6rV zr>{X>vW2PFlXgT~-An zT5aiG7ZxkZw8^6~lHbY-rQR=1yW7vj)Q&Z!<4-1qW`^moBP`%aUmGu1G-d8NMcnIz z>HIKfl$0mg=swSMAt9HfHseiK>>fb98kq9B_CXZz(Nxe6vUIb%so?5NFy3_Ug%(Y^ zHzv!Yk~6SBp}_RGR4(zeQ*6A_+Vpg(H}aA$rf1gPR_)HF=S7J0p4gjS9`Pcn!bsDr zI5?XLzNXg#9Fu7(EQzHYf8JDB4jkiTdaGDMX8JVn0r85xO+}xwi7L-G6)!+69W>7L z1FdVRXR*c1b?{z4j++f(1F^$A%WPbM224Pq*{*pE(W-9dvOVx%b6cD3Qz9Vs9n2LU z{Xt;W(p-b(p|QHmTq`h!SWjWDGapORBi`)rW;gMsj%J6?*b=j8o7u7Hf5g3On_K4e zgIdlsx3A(xq-k&N_#FvevLzn}$na7sm^<}HhLX%!e71| zX>8t57Z$V6Z5xMVn3IN{$6B91YTg#N8^SZwylo{o`muReOAqWQt6<)*h4hOW=KXiz z#m;UpANr>X3bH%Rr&HXB&Aw?q+v7MABDU E0KZqp8~^|S diff --git a/data/locale/de.ts b/data/locale/de.ts index a7d84072f..167686614 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -654,6 +654,63 @@ Wenn Sie daran interessiert sind LMMS in eine andere Sprache zu übersetzen oder Plugin entfe&rnen + + DelayControls + + Delay Samples + Samples verzögern + + + Feedback + Rückkopplung + + + Lfo Frequency + LFO-Frequenz + + + Lfo Amount + LFO-Stärke + + + + DelayControlsDialog + + Delay + Verzögerung + + + Delay Time + Verzögerungszeit + + + Regen + + + + Feedback Amount + Rückkopplungsstärke + + + Rate + Rate + + + Lfo + LFO + + + Lfo Amt + LFO-Stärke + + + + DetuningHelper + + Note detuning + Noten-Verstimmung + + DualFilterControlDialog @@ -780,6 +837,13 @@ Wenn Sie daran interessiert sind LMMS in eine andere Sprache zu übersetzen oder Vokalformant-Filter + + DummyEffect + + NOT FOUND + NICHT GEFUNDEN + + Effect @@ -3294,7 +3358,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Bandlimited Ramp wave - + Bandbegrenzte Sägezahnwelle Bandlimited Square wave @@ -3330,7 +3394,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Digital Ramp wave - + Digitale Sägezahnwelle Digital Square wave @@ -3350,7 +3414,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Ramp wave - + Sägezahnwelle Square wave @@ -4085,6 +4149,21 @@ Grund: »%2« LMMS Plugin %1 hat keinen Plugin-Deskriptor namens %2! + + PluginBrowser + + Instrument plugins + Instrument-Plugins + + + Instrument browser + Instrument-Browser + + + Drag an instrument into either the Song-Editor, the Beat+Bassline Editor or into an existing instrument track. + Ziehen Sie ein Instrument entweder in den Song-Editor, den Beat+Bassline-Editor oder in eine existierende Instrumentspur. + + ProjectRenderer @@ -6990,10 +7069,6 @@ Doppelklicken auf eines der Plugins zeigt Informaitonen über die Ports an. pluginBrowser - - Instrument plugins - Instrument-Plugins - VST-host for using VST(i)-plugins within LMMS VST-Host zum Benutzen von VST(i)-Plugins innerhalb von LMMS @@ -7056,14 +7131,6 @@ This chip was used in the Commodore 64 computer. Emulation des MOS6581 und MOS8580 SID Chips. Dieser Chip wurde in Commodore 64 Computern genutzt. - - Instrument browser - Instrument-Browser - - - Drag an instrument into either the Song-Editor, the Beat+Bassline Editor or into an existing instrument track. - Ziehen Sie ein Instrument entweder in den Song-Editor, den Beat+Bassline-Editor oder in eine existierende Instrumentspur. - Player for SoundFont files Wiedergabe von SoundFont-Dateien @@ -7148,6 +7215,10 @@ Dieser Chip wurde in Commodore 64 Computern genutzt. Carla Patchbay Instrument Carla Patchbay Instrument + + A native delay plugin + Ein natives Verzögerung-Plugin + projectNotes @@ -7476,6 +7547,57 @@ Latenz: %2 ms Wiedergabe-Courser im AudioFileProcessor anzeigen + + setupWidget + + OSS (Open Sound System) + OSS (Open Sound System) + + + SDL (Simple DirectMedia Layer) + SDL (Simple DirectMedia Layer) + + + ALSA-Sequencer (Advanced Linux Sound Architecture) + ALSA-Sequencer (Advanced Linux Sound Architecture) + + + JACK (JACK Audio Connection Kit) + JACK (JACK Audio Connection Kit) + + + ALSA Raw-MIDI (Advanced Linux Sound Architecture) + ALSA Raw-MIDI (Advanced Linux Sound Architecture) + + + PulseAudio (bad latency!) + PulseAudio (Schlechte Latenz!) + + + Dummy (no sound output) + Dummy (Keine Soundausgabe) + + + Dummy (no MIDI support) + Dummy (Keine MIDI-Unterstützung) + + + WinMM MIDI + WinMM MIDI + + + OSS Raw-MIDI (Open Sound System) + OSS Raw-MIDI (Open Sound System) + + + ALSA (Advanced Linux Sound Architecture) + ALSA (Advanced Linux Sound Architecture) + + + PortAudio + PortAudio + + sf2Instrument @@ -7902,6 +8024,13 @@ Latenz: %2 ms Rechts-nach-rechts + + tabWidget + + Settings for %1 + Einstellungen für %1 + + timeLine From c0415ce3b3b41a03ec9184ccfd1064786cf72442 Mon Sep 17 00:00:00 2001 From: Vesa Date: Thu, 18 Dec 2014 22:36:39 +0200 Subject: [PATCH 38/52] Changes to interpolation (explicitly use fma more), some new DSP building blocks New file: Delay.h - contains some simple delay effects for use in DSP - perhaps for designing reverbs or similar. All are in double precision because why not. --- include/Delay.h | 363 ++++++++++++++++++++++++++++++++++++++++ include/interpolation.h | 12 +- 2 files changed, 369 insertions(+), 6 deletions(-) create mode 100644 include/Delay.h diff --git a/include/Delay.h b/include/Delay.h new file mode 100644 index 000000000..1c51f1ba8 --- /dev/null +++ b/include/Delay.h @@ -0,0 +1,363 @@ +/* + * Delay.h - Delay effect objects to use as building blocks in DSP + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef DELAY_H +#define DELAY_H + +#include "lmms_basics.h" +#include "lmms_math.h" +#include "interpolation.h" +#include "MemoryManager.h" + +// brief usage + +// Classes: + +// CombFeedback: a feedback comb filter - basically a simple delay line, makes a comb shape in the freq response +// CombFeedfwd: a feed-forward comb filter - an "inverted" comb filter, can be combined with CombFeedback to create a net allpass if negative gain is used +// CombFeedbackDualtap: same as CombFeedback but takes two delay values +// AllpassDelay: an allpass delay - combines feedback and feed-forward - has flat frequency response + +// all classes are templated with channel count, any arbitrary channel count can be used for each fx + +// Methods (for all classes): + +// setDelay sets delay amount in frames. It's up to you to make this samplerate-agnostic. +// Fractions are allowed - linear interpolation is used to deal with them +// CombFeedbackDualTap is a special case: it requires 2 delay times + +// setMaxDelay (re)sets the maximum allowed delay, in frames +// NOTE: for performance reasons, there's no bounds checking at setDelay, so make sure you set maxDelay >= delay! + +// clearHistory clears the delay buffer + +// setGain sets the feedback/feed-forward gain, in linear amplitude, negative values are allowed +// 1.0 is full feedback/feed-forward, -1.0 is full negative feedback/feed-forward + +// update runs the fx for one frame - takes as arguments input and number of channel to run, returns output + +template +class CombFeedback +{ +public: + typedef double frame[CHANNELS]; + + CombFeedback( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedback() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ); + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in + m_gain * y; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + + +template +class CombFeedfwd +{ + typedef double frame[CHANNELS]; + + CombFeedfwd( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedfwd() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * m_gain; + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + + +template +class CombFeedbackDualtap +{ + typedef double frame[CHANNELS]; + + CombFeedbackDualtap( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedbackDualtap() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelays( double delay1, double delay2 ) + { + m_delay1 = static_cast( ceil( delay1 ) ); + m_fraction1 = 1.0 - ( delay1 - floor( delay1 ) ); + + m_delay2 = static_cast( ceil( delay2 ) ); + m_fraction2 = 2.0 - ( delay2 - floor( delay2 ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos1 = m_position - m_delay1; + if( readPos1 < 0 ) { readPos1 += m_size; } + + int readPos2 = m_position - m_delay2; + if( readPos2 < 0 ) { readPos2 += m_size; } + + const double y = linearInterpolate( m_buffer[readPos1][ch], m_buffer[( readPos1 + 1 ) % m_size][ch], m_fraction1 ) + + linearInterpolate( m_buffer[readPos2][ch], m_buffer[( readPos2 + 1 ) % m_size][ch], m_fraction2 ); + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in + m_gain * y; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay1; + int m_delay2; + double m_fraction1; + double m_fraction2; +}; + + +template +class AllpassDelay +{ +public: + typedef double frame[CHANNELS]; + + AllpassDelay( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~AllpassDelay() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * -m_gain; + const double x = in + m_gain * y; + + ++m_position %= m_size; + + m_buffer[m_position][ch] = x; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + +// convenience typedefs for stereo effects +typedef CombFeedback<2> StereoCombFeedback; +typedef CombFeedfwd<2> StereoCombFeedfwd; +typedef CombFeedbackDualtap<2> StereoCombFeedbackDualtap; +typedef AllpassDelay<2> StereoAllpassDelay; + +#endif diff --git a/include/interpolation.h b/include/interpolation.h index cbe274d42..693897587 100644 --- a/include/interpolation.h +++ b/include/interpolation.h @@ -71,9 +71,9 @@ inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x ) float frcu = frsq*v0; float t1 = v3 + 3*v1; - return( v1 + 0.5f * frcu + x * ( v2 - frcu * ( 1.0f/6.0f ) - - t1 * ( 1.0f/6.0f ) - v0 / 3.0f ) + frsq * x * ( t1 * - ( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * ( 0.5f * v2 - v1 ) ); + return( v1 + fastFmaf( 0.5f, frcu, x ) * ( v2 - frcu * ( 1.0f/6.0f ) - + fastFmaf( t1, ( 1.0f/6.0f ), -v0 ) * ( 1.0f/3.0f ) ) + frsq * x * ( t1 * + ( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * fastFmaf( 0.5f, v2, -v1 ) ); } @@ -102,7 +102,7 @@ inline float optimalInterpolate( float v0, float v1, float x ) const float c2 = even * -0.004541102062639801; const float c3 = odd * -1.57015627178718420; - return ( ( c3*z + c2 ) * z + c1 ) * z + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 ); } @@ -119,7 +119,7 @@ inline float optimal4pInterpolate( float v0, float v1, float v2, float v3, float const float c2 = even1 * -0.246185007019907091 + even2 * 0.24614027139700284; const float c3 = odd1 * -0.36030925263849456 + odd2 * 0.10174985775982505; - return ( ( c3*z + c2 ) * z + c1 ) * z + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 ); } @@ -130,7 +130,7 @@ inline float lagrangeInterpolate( float v0, float v1, float v2, float v3, float const float c1 = v2 - v0 * ( 1.0f / 3.0f ) - v1 * 0.5f - v3 * ( 1.0f / 6.0f ); const float c2 = 0.5f * (v0 + v2) - v1; const float c3 = ( 1.0f/6.0f ) * ( v3 - v0 ) + 0.5f * ( v1 - v2 ); - return ( ( c3*x + c2 ) * x + c1 ) * x + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, x, c2 ), x, c1 ), x, c0 ); } From 5f5d405552962d1af10b24f8767a0a74ec6b3f4f Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 20 Dec 2014 22:33:29 +0000 Subject: [PATCH 39/52] Render from start of track. --- src/core/Song.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index c9ac56452..2dd9c549b 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -619,6 +619,7 @@ void Song::stop() void Song::startExport() { stop(); + m_playPos[Mode_PlaySong].setTicks( 0 ); playSong(); From 0d44dc6ac59f67f5e64c62092abfb7b8fe61643f Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 03:38:11 +0000 Subject: [PATCH 40/52] Proposed fix for 856 Recursive VST Effect Enhancement Request --- plugins/VstEffect/VstSubPluginFeatures.cpp | 33 ++++++++++++++++++---- plugins/VstEffect/VstSubPluginFeatures.h | 7 ++++- plugins/vst_base/VstPlugin.cpp | 7 ++--- src/core/RemotePlugin.cpp | 2 +- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index 8e0c623c3..2a0e625dc 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -31,6 +31,7 @@ #include "ConfigManager.h" + VstSubPluginFeatures::VstSubPluginFeatures( Plugin::PluginTypes _type ) : SubPluginFeatures( _type ) { @@ -52,16 +53,38 @@ void VstSubPluginFeatures::fillDescriptionWidget( QWidget * _parent, void VstSubPluginFeatures::listSubPluginKeys( const Plugin::Descriptor * _desc, KeyList & _kl ) const { - QStringList dlls = QDir( ConfigManager::inst()->vstDir() ). - entryList( QStringList() << "*.dll", - QDir::Files, QDir::Name ); + QStringList *dlls = new QStringList(); + const QString path = QString(""); + addPluginsFromDir(dlls, path ); // TODO: eval m_type - for( QStringList::ConstIterator it = dlls.begin(); - it != dlls.end(); ++it ) + for( QStringList::ConstIterator it = dlls->begin(); + it != dlls->end(); ++it ) { EffectKey::AttributeMap am; am["file"] = *it; _kl.push_back( Key( _desc, QFileInfo( *it ).baseName(), am ) ); } + delete dlls; +} + +void VstSubPluginFeatures::addPluginsFromDir(QStringList* filenames, QString path) const +{ + QStringList dirs = QDir ( ConfigManager::inst()->vstDir() + path ). + entryList( QStringList() << "*" , + QDir::Dirs, QDir::Name ); + for(int i = 0; i < dirs.size(); i++) + { + if(dirs.at( i )[0] != '.' ) + { + addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) ); + } + } + QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ). + entryList( QStringList() << "*.dll", + QDir::Files, QDir::Name ); + for(int i = 0; i < dlls.size(); i++) + { + filenames->append(path + QDir::separator() + dlls.at( i )); + } } diff --git a/plugins/VstEffect/VstSubPluginFeatures.h b/plugins/VstEffect/VstSubPluginFeatures.h index 51fdc05cd..db2adcfab 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.h +++ b/plugins/VstEffect/VstSubPluginFeatures.h @@ -40,8 +40,13 @@ public: virtual void listSubPluginKeys( const Plugin::Descriptor * _desc, KeyList & _kl ) const; - +private: + void addPluginsFromDir(QStringList* filenames, QString path) const; } ; + + + + #endif diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 0cc604d63..1cc745769 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -172,10 +172,9 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) QString p = m_plugin; - if( QFileInfo( p ).dir().isRelative() ) - { - p = ConfigManager::inst()->vstDir() + QDir::separator() + p; - } + p.remove(0,1); + p = ConfigManager::inst()->vstDir() + p; + sendMessage( message( IdVstLoadPlugin ).addString( QSTR_TO_STDSTR( p ) ) ); diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index c30f0c9e3..84555fb3c 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -131,7 +131,7 @@ bool RemotePlugin::init( const QString &pluginExecutable, m_failed = false; } QString exec = ConfigManager::inst()->pluginDir() + - QDir::separator() + pluginExecutable; + pluginExecutable; QStringList args; // swap in and out for bidirectional communication From f54540dea74c5968d6cc34675136e7d5b4b75f70 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 10:51:11 +0000 Subject: [PATCH 41/52] 856 now loads absolute and relative paths correctly --- plugins/VstEffect/VstSubPluginFeatures.cpp | 4 +++- plugins/vst_base/VstPlugin.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index 2a0e625dc..64db352b2 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -84,7 +84,9 @@ void VstSubPluginFeatures::addPluginsFromDir(QStringList* filenames, QString pat QDir::Files, QDir::Name ); for(int i = 0; i < dlls.size(); i++) { - filenames->append(path + QDir::separator() + dlls.at( i )); + QString fName = path + QDir::separator() + dlls.at( i ); + fName.remove( 0, 1 ); + filenames->append(fName); } } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 1cc745769..a283bb9c7 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -172,8 +172,10 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) QString p = m_plugin; - p.remove(0,1); - p = ConfigManager::inst()->vstDir() + p; + if( QFileInfo( p ).dir().isRelative() ) + { + p = ConfigManager::inst()->vstDir() + p; + } sendMessage( message( IdVstLoadPlugin ).addString( QSTR_TO_STDSTR( p ) ) ); From cc74273f60ddc2a2a813411ba922a96ae140fa99 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 14:19:48 +0000 Subject: [PATCH 42/52] Proposed fix for 1049 VST knobs won't remember settings --- plugins/vst_base/VstPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 0cc604d63..4a71437f4 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -420,7 +420,7 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) { ( *it ).section( ':', 0, 0 ).toInt(), "", - ( *it ).section( ':', 1, 1 ).toFloat() + ( *it ).section( ':', 2, -1 ).toFloat() } ; m.addInt( item.index ); m.addString( item.shortLabel ); From 188f3714d7176e80fd0032d213171aa42fe5d0e8 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 16:10:43 +0000 Subject: [PATCH 43/52] Proposed fix 1080 Panning Sample tracks --- include/SampleTrack.h | 3 +++ src/tracks/SampleTrack.cpp | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 440b10e7e..c8b17b148 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -148,9 +148,11 @@ public: private: FloatModel m_volumeModel; + FloatModel m_panningModel; AudioPort m_audioPort; + friend class SampleTrackView; } ; @@ -181,6 +183,7 @@ private: EffectRackView * m_effectRack; QWidget * m_effWindow; Knob * m_volumeKnob; + Knob * m_panningKnob; } ; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 49d59f66c..3333e4ae4 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -47,6 +47,7 @@ #include "EffectRackView.h" #include "TrackLabelButton.h" #include "ConfigManager.h" +#include "panning_constants.h" SampleTCO::SampleTCO( Track * _track ) : @@ -405,9 +406,12 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : Track( Track::SampleTrack, tc ), m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 1.0, this, tr( "Volume" ) ), - m_audioPort( tr( "Sample track" ), true, &m_volumeModel, NULL ) + m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, + this, tr( "Panning" ) ), + m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel ) { setName( tr( "Sample track" ) ); + m_panningModel.setCenterValue( DefaultPanning ); } @@ -550,6 +554,14 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); + m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + tr( "Panning" ) ); + m_panningKnob->setModel( &_t->m_panningModel ); + m_panningKnob->setHintText( tr( "Panning:" ), "%" ); + m_panningKnob->move( DEFAULT_SETTINGS_WIDGET_WIDTH-24, 2 ); + m_panningKnob->setLabel( tr( "PAN" ) ); + m_panningKnob->show(); + m_effectRack = new EffectRackView( _t->audioPort()->effects() ); m_effectRack->setFixedSize( 240, 242 ); From a4359ec5845b37ade34914f297295989a0716eda Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 23:36:42 +0000 Subject: [PATCH 44/52] 1080 save and load pan settings --- src/tracks/SampleTrack.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 3333e4ae4..f55100698 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -496,6 +496,7 @@ void SampleTrack::saveTrackSpecificSettings( QDomDocument & _doc, _this.setAttribute( "icon", tlb->pixmapFile() ); #endif m_volumeModel.saveSettings( _doc, _this, "vol" ); + m_panningModel.saveSettings( _doc, _this, "pan" ); } @@ -517,6 +518,7 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) node = node.nextSibling(); } m_volumeModel.loadSettings( _this, "vol" ); + m_panningModel.loadSettings( _this, "pan" ); } From 5172acb1e3bccc061224ba9459691ce4136ab946 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 21 Dec 2014 14:19:48 +0000 Subject: [PATCH 45/52] Proposed fix for 1049 VST knobs won't remember settings --- plugins/vst_base/VstPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 4e3f0d1bc..8e7632ca4 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -415,7 +415,7 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) { ( *it ).section( ':', 0, 0 ).toInt(), "", - ( *it ).section( ':', 1, 1 ).toFloat() + ( *it ).section( ':', 2, -1 ).toFloat() } ; m.addInt( item.index ); m.addString( item.shortLabel ); From 5fc1b36f6ffc0bfc3c4a4af209c99986d62bf365 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 22 Dec 2014 05:17:53 +0000 Subject: [PATCH 46/52] 856 reformat --- plugins/VstEffect/VstSubPluginFeatures.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index 64db352b2..a54e4089f 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -67,7 +67,7 @@ void VstSubPluginFeatures::listSubPluginKeys( const Plugin::Descriptor * _desc, delete dlls; } -void VstSubPluginFeatures::addPluginsFromDir(QStringList* filenames, QString path) const +void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString path ) const { QStringList dirs = QDir ( ConfigManager::inst()->vstDir() + path ). entryList( QStringList() << "*" , @@ -76,17 +76,17 @@ void VstSubPluginFeatures::addPluginsFromDir(QStringList* filenames, QString pat { if(dirs.at( i )[0] != '.' ) { - addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) ); + addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) ); } } QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ). entryList( QStringList() << "*.dll", QDir::Files, QDir::Name ); - for(int i = 0; i < dlls.size(); i++) + for( int i = 0; i < dlls.size(); i++ ) { QString fName = path + QDir::separator() + dlls.at( i ); fName.remove( 0, 1 ); - filenames->append(fName); + filenames->append( fName ); } } From dd053b99d29ec734e346352991e715908699a73c Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 22 Dec 2014 00:31:50 -0500 Subject: [PATCH 47/52] Fix whitespace --- plugins/VstEffect/VstSubPluginFeatures.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index a54e4089f..3f218c284 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -72,9 +72,9 @@ void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString pa QStringList dirs = QDir ( ConfigManager::inst()->vstDir() + path ). entryList( QStringList() << "*" , QDir::Dirs, QDir::Name ); - for(int i = 0; i < dirs.size(); i++) + for( int i = 0; i < dirs.size(); i++ ) { - if(dirs.at( i )[0] != '.' ) + if( dirs.at( i )[0] != '.' ) { addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) ); } From 13b1220d1689f8d37168ee72248465308361d9a5 Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 22 Dec 2014 14:45:17 +0200 Subject: [PATCH 48/52] Delay.h fix --- include/Delay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Delay.h b/include/Delay.h index 1c51f1ba8..9010232b9 100644 --- a/include/Delay.h +++ b/include/Delay.h @@ -245,7 +245,7 @@ class CombFeedbackDualtap m_fraction1 = 1.0 - ( delay1 - floor( delay1 ) ); m_delay2 = static_cast( ceil( delay2 ) ); - m_fraction2 = 2.0 - ( delay2 - floor( delay2 ) ); + m_fraction2 = 1.0 - ( delay2 - floor( delay2 ) ); } inline void setGain( double gain ) From b982fa4b4c5df049eea4f3098824a421b456b97e Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 22 Dec 2014 22:22:51 +0000 Subject: [PATCH 49/52] Flanger correct interpolate calculation --- plugins/flanger/monodelay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/flanger/monodelay.cpp b/plugins/flanger/monodelay.cpp index 4d1fdaa4c..74e884642 100644 --- a/plugins/flanger/monodelay.cpp +++ b/plugins/flanger/monodelay.cpp @@ -60,7 +60,7 @@ void MonoDelay::tick( sample_t* sample ) { readIndex += m_maxLength; } - float fract = fraction( m_length ); + float fract = 1.0f - fraction( m_length ); if(readIndex != m_maxLength-1 ) { *sample = linearInterpolate(m_buffer[readIndex] , From 7f47fc955d20f6f5ca6131f24182baf1eb7bdfb7 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 22 Dec 2014 22:25:55 +0000 Subject: [PATCH 50/52] Flanger reformat, convert spaces to tabs --- plugins/flanger/flangercontrols.cpp | 44 +++---- plugins/flanger/flangercontrols.h | 58 ++++----- plugins/flanger/flangercontrolsdialog.h | 8 +- plugins/flanger/flangereffect.cpp | 152 ++++++++++++------------ plugins/flanger/flangereffect.h | 26 ++-- plugins/flanger/monodelay.cpp | 66 +++++----- plugins/flanger/monodelay.h | 42 +++---- plugins/flanger/noise.cpp | 4 +- plugins/flanger/noise.h | 6 +- plugins/flanger/quadraturelfo.cpp | 8 +- plugins/flanger/quadraturelfo.h | 58 ++++----- 11 files changed, 236 insertions(+), 236 deletions(-) diff --git a/plugins/flanger/flangercontrols.cpp b/plugins/flanger/flangercontrols.cpp index 55d7f215c..52e2d0a87 100644 --- a/plugins/flanger/flangercontrols.cpp +++ b/plugins/flanger/flangercontrols.cpp @@ -32,17 +32,17 @@ FlangerControls::FlangerControls( FlangerEffect *effect ) : - EffectControls ( effect ), - m_effect ( effect ), - m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay Samples" ) ) , - m_lfoFrequencyModel( 0.25, 0.01, 5, 0.0001, 20000.0 ,this, tr( "Lfo Frequency" ) ), - m_lfoAmountModel( 0.0, 0.0, 0.0025 , 0.0001 , this , tr( "Seconds" ) ), - m_feedbackModel( 0.0 , 0.0 , 1.0 , 0.0001, this, tr( "Regen" ) ), - m_whiteNoiseAmountModel( 0.0 , 0.0 , 0.05 , 0.0001, this, tr( "Noise" ) ), - m_invertFeedbackModel ( false , this, tr( "Invert" ) ) + EffectControls ( effect ), + m_effect ( effect ), + m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay Samples" ) ) , + m_lfoFrequencyModel( 0.25, 0.01, 5, 0.0001, 20000.0 ,this, tr( "Lfo Frequency" ) ), + m_lfoAmountModel( 0.0, 0.0, 0.0025 , 0.0001 , this , tr( "Seconds" ) ), + m_feedbackModel( 0.0 , 0.0 , 1.0 , 0.0001, this, tr( "Regen" ) ), + m_whiteNoiseAmountModel( 0.0 , 0.0 , 0.05 , 0.0001, this, tr( "Noise" ) ), + m_invertFeedbackModel ( false , this, tr( "Invert" ) ) { - connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changedSampleRate() ) ); + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changedSampleRate() ) ); } @@ -50,12 +50,12 @@ FlangerControls::FlangerControls( FlangerEffect *effect ) : void FlangerControls::loadSettings( const QDomElement &_this ) { - m_delayTimeModel.loadSettings( _this, "DelayTimeSamples" ); - m_lfoFrequencyModel.loadSettings( _this, "LfoFrequency" ); - m_lfoAmountModel.loadSettings( _this, "LfoAmount" ); - m_feedbackModel.loadSettings( _this, "Feedback" ); - m_whiteNoiseAmountModel.loadSettings( _this, "WhiteNoise" ); - m_invertFeedbackModel.loadSettings( _this, "Invert" ); + m_delayTimeModel.loadSettings( _this, "DelayTimeSamples" ); + m_lfoFrequencyModel.loadSettings( _this, "LfoFrequency" ); + m_lfoAmountModel.loadSettings( _this, "LfoAmount" ); + m_feedbackModel.loadSettings( _this, "Feedback" ); + m_whiteNoiseAmountModel.loadSettings( _this, "WhiteNoise" ); + m_invertFeedbackModel.loadSettings( _this, "Invert" ); } @@ -64,12 +64,12 @@ void FlangerControls::loadSettings( const QDomElement &_this ) void FlangerControls::saveSettings( QDomDocument &doc, QDomElement &parent ) { - m_delayTimeModel.saveSettings( doc , parent, "DelayTimeSamples" ); - m_lfoFrequencyModel.saveSettings( doc, parent , "LfoFrequency" ); - m_lfoAmountModel.saveSettings( doc, parent , "LfoAmount" ); - m_feedbackModel.saveSettings( doc, parent, "Feedback" ) ; - m_whiteNoiseAmountModel.saveSettings( doc, parent , "WhiteNoise" ) ; - m_invertFeedbackModel.saveSettings( doc, parent, "Invert" ); + m_delayTimeModel.saveSettings( doc , parent, "DelayTimeSamples" ); + m_lfoFrequencyModel.saveSettings( doc, parent , "LfoFrequency" ); + m_lfoAmountModel.saveSettings( doc, parent , "LfoAmount" ); + m_feedbackModel.saveSettings( doc, parent, "Feedback" ) ; + m_whiteNoiseAmountModel.saveSettings( doc, parent , "WhiteNoise" ) ; + m_invertFeedbackModel.saveSettings( doc, parent, "Invert" ); } @@ -77,7 +77,7 @@ void FlangerControls::saveSettings( QDomDocument &doc, QDomElement &parent ) void FlangerControls::changedSampleRate() { - m_effect->changeSampleRate(); + m_effect->changeSampleRate(); } diff --git a/plugins/flanger/flangercontrols.h b/plugins/flanger/flangercontrols.h index bbd52dbee..bd0f43742 100644 --- a/plugins/flanger/flangercontrols.h +++ b/plugins/flanger/flangercontrols.h @@ -34,41 +34,41 @@ class FlangerEffect; class FlangerControls : public EffectControls { - Q_OBJECT + Q_OBJECT public: - FlangerControls( FlangerEffect* effect ); - virtual ~FlangerControls() - { - } - virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); - virtual void loadSettings ( const QDomElement &_this ); - inline virtual QString nodeName() const - { - return "Flanger"; - } - virtual int controlCount() - { - return 5; - } - virtual EffectControlDialog* createView() - { - return new FlangerControlsDialog( this ); - } + FlangerControls( FlangerEffect* effect ); + virtual ~FlangerControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Flanger"; + } + virtual int controlCount() + { + return 5; + } + virtual EffectControlDialog* createView() + { + return new FlangerControlsDialog( this ); + } private slots: - void changedSampleRate(); + void changedSampleRate(); private: - FlangerEffect* m_effect; - FloatModel m_delayTimeModel; - TempoSyncKnobModel m_lfoFrequencyModel; - FloatModel m_lfoAmountModel; - FloatModel m_feedbackModel; - FloatModel m_whiteNoiseAmountModel; - BoolModel m_invertFeedbackModel; + FlangerEffect* m_effect; + FloatModel m_delayTimeModel; + TempoSyncKnobModel m_lfoFrequencyModel; + FloatModel m_lfoAmountModel; + FloatModel m_feedbackModel; + FloatModel m_whiteNoiseAmountModel; + BoolModel m_invertFeedbackModel; - friend class FlangerControlsDialog; - friend class FlangerEffect; + friend class FlangerControlsDialog; + friend class FlangerEffect; }; diff --git a/plugins/flanger/flangercontrolsdialog.h b/plugins/flanger/flangercontrolsdialog.h index bdfa90d45..1fef65a3e 100644 --- a/plugins/flanger/flangercontrolsdialog.h +++ b/plugins/flanger/flangercontrolsdialog.h @@ -32,10 +32,10 @@ class FlangerControls; class FlangerControlsDialog : public EffectControlDialog { public: - FlangerControlsDialog( FlangerControls* controls ); - virtual ~FlangerControlsDialog() - { - } + FlangerControlsDialog( FlangerControls* controls ); + virtual ~FlangerControlsDialog() + { + } }; #endif // FLANGERCONTROLSDIALOG_H diff --git a/plugins/flanger/flangereffect.cpp b/plugins/flanger/flangereffect.cpp index a309e38c9..fcaadfca7 100644 --- a/plugins/flanger/flangereffect.cpp +++ b/plugins/flanger/flangereffect.cpp @@ -31,28 +31,28 @@ extern "C" Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = { - STRINGIFY( PLUGIN_NAME ), - "Flanger", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), - "Dave French ", - 0x0100, - Plugin::Effect, - new PluginPixmapLoader( "logo" ), - NULL, - NULL + STRINGIFY( PLUGIN_NAME ), + "Flanger", + QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), + "Dave French ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL } ; FlangerEffect::FlangerEffect( Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key ) : - Effect( &flanger_plugin_descriptor, parent, key ), - m_flangerControls( this ) + Effect( &flanger_plugin_descriptor, parent, key ), + m_flangerControls( this ) { - m_lfo = new QuadratureLfo( Engine::mixer()->processingSampleRate() ); - m_lDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); - m_rDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); - m_noise = new Noise; + m_lfo = new QuadratureLfo( Engine::mixer()->processingSampleRate() ); + m_lDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); + m_rDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); + m_noise = new Noise; } @@ -60,22 +60,22 @@ FlangerEffect::FlangerEffect( Model *parent, const Plugin::Descriptor::SubPlugin FlangerEffect::~FlangerEffect() { - if(m_lDelay ) - { - delete m_lDelay; - } - if( m_rDelay ) - { - delete m_rDelay; - } - if(m_lfo ) - { - delete m_lfo; - } - if(m_noise) - { - delete m_noise; - } + if(m_lDelay ) + { + delete m_lDelay; + } + if( m_rDelay ) + { + delete m_rDelay; + } + if(m_lfo ) + { + delete m_lfo; + } + if(m_noise) + { + delete m_noise; + } } @@ -83,48 +83,48 @@ FlangerEffect::~FlangerEffect() bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) { - if( !isEnabled() || !isRunning () ) - { - return( false ); - } - double outSum = 0.0; - const float d = dryLevel(); - const float w = wetLevel(); - const float length = m_flangerControls.m_delayTimeModel.value() * Engine::mixer()->processingSampleRate(); - const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); - float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::mixer()->processingSampleRate(); - bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); - m_lfo->setFrequency( m_flangerControls.m_lfoFrequencyModel.value() ); - m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); - m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); - sample_t dryS[2]; - float leftLfo; - float rightLfo; - for( fpp_t f = 0; f < frames; ++f ) - { - buf[f][0] += m_noise->tick() * noise; - buf[f][1] += m_noise->tick() * noise; - dryS[0] = buf[f][0]; - dryS[1] = buf[f][1]; - m_lfo->tick(&leftLfo, &rightLfo); - m_lDelay->setLength( ( float )length + ( amplitude * leftLfo ) ); - m_rDelay->setLength( ( float )length+ ( amplitude * rightLfo ) ); - if(invertFeedback) - { - m_lDelay->tick( &buf[f][1] ); - m_rDelay->tick(&buf[f][0] ); - } else - { - m_lDelay->tick( &buf[f][0] ); - m_rDelay->tick( &buf[f][1] ); - } + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + double outSum = 0.0; + const float d = dryLevel(); + const float w = wetLevel(); + const float length = m_flangerControls.m_delayTimeModel.value() * Engine::mixer()->processingSampleRate(); + const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); + float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::mixer()->processingSampleRate(); + bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); + m_lfo->setFrequency( m_flangerControls.m_lfoFrequencyModel.value() ); + m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); + m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); + sample_t dryS[2]; + float leftLfo; + float rightLfo; + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] += m_noise->tick() * noise; + buf[f][1] += m_noise->tick() * noise; + dryS[0] = buf[f][0]; + dryS[1] = buf[f][1]; + m_lfo->tick(&leftLfo, &rightLfo); + m_lDelay->setLength( ( float )length + ( amplitude * leftLfo ) ); + m_rDelay->setLength( ( float )length+ ( amplitude * rightLfo ) ); + if(invertFeedback) + { + m_lDelay->tick( &buf[f][1] ); + m_rDelay->tick(&buf[f][0] ); + } else + { + m_lDelay->tick( &buf[f][0] ); + m_rDelay->tick( &buf[f][1] ); + } - buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); - buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); - outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; - } - checkGate( outSum / frames ); - return isRunning(); + buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); + buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + checkGate( outSum / frames ); + return isRunning(); } @@ -132,9 +132,9 @@ bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) void FlangerEffect::changeSampleRate() { - m_lfo->setSampleRate( Engine::mixer()->processingSampleRate() ); - m_lDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); - m_rDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); + m_lfo->setSampleRate( Engine::mixer()->processingSampleRate() ); + m_lDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); + m_rDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); } @@ -145,7 +145,7 @@ extern "C" //needed for getting plugin out of shared lib Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) { - return new FlangerEffect( parent , static_cast( data ) ); + return new FlangerEffect( parent , static_cast( data ) ); } }} diff --git a/plugins/flanger/flangereffect.h b/plugins/flanger/flangereffect.h index d476a056a..04f308249 100644 --- a/plugins/flanger/flangereffect.h +++ b/plugins/flanger/flangereffect.h @@ -36,21 +36,21 @@ class FlangerEffect : public Effect { public: - FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); - virtual ~FlangerEffect(); - virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); - virtual EffectControls* controls() - { - return &m_flangerControls; - } - void changeSampleRate(); + FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~FlangerEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_flangerControls; + } + void changeSampleRate(); private: - FlangerControls m_flangerControls; - MonoDelay* m_lDelay; - MonoDelay* m_rDelay; - QuadratureLfo* m_lfo; - Noise* m_noise; + FlangerControls m_flangerControls; + MonoDelay* m_lDelay; + MonoDelay* m_rDelay; + QuadratureLfo* m_lfo; + Noise* m_noise; }; diff --git a/plugins/flanger/monodelay.cpp b/plugins/flanger/monodelay.cpp index 74e884642..ebb5b322d 100644 --- a/plugins/flanger/monodelay.cpp +++ b/plugins/flanger/monodelay.cpp @@ -28,14 +28,14 @@ MonoDelay::MonoDelay( int maxTime , int sampleRate ) { - m_buffer = 0; - m_maxTime = maxTime; - m_maxLength = maxTime * sampleRate; - m_length = m_maxLength; + m_buffer = 0; + m_maxTime = maxTime; + m_maxLength = maxTime * sampleRate; + m_length = m_maxLength; - m_index = 0; - m_feedback = 0.0f; - setSampleRate( sampleRate ); + m_index = 0; + m_feedback = 0.0f; + setSampleRate( sampleRate ); } @@ -43,10 +43,10 @@ MonoDelay::MonoDelay( int maxTime , int sampleRate ) MonoDelay::~MonoDelay() { - if( m_buffer ) - { - delete m_buffer; - } + if( m_buffer ) + { + delete m_buffer; + } } @@ -54,24 +54,24 @@ MonoDelay::~MonoDelay() void MonoDelay::tick( sample_t* sample ) { - m_buffer[m_index] = *sample; - int readIndex = m_index - ( int )m_length; - if(readIndex < 0) - { - readIndex += m_maxLength; - } + m_buffer[m_index] = *sample; + int readIndex = m_index - ( int )m_length; + if(readIndex < 0) + { + readIndex += m_maxLength; + } float fract = 1.0f - fraction( m_length ); - if(readIndex != m_maxLength-1 ) - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[readIndex+1], fract ); - } else - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[0], fract ); - } - m_buffer[m_index] += *sample * m_feedback; - m_index = ( m_index +1 ) % m_maxLength; + if(readIndex != m_maxLength-1 ) + { + *sample = linearInterpolate(m_buffer[readIndex] , + m_buffer[readIndex+1], fract ); + } else + { + *sample = linearInterpolate(m_buffer[readIndex] , + m_buffer[0], fract ); + } + m_buffer[m_index] += *sample * m_feedback; + m_index = ( m_index +1 ) % m_maxLength; } @@ -79,11 +79,11 @@ void MonoDelay::tick( sample_t* sample ) void MonoDelay::setSampleRate( int sampleRate ) { - if( m_buffer ) - { - delete m_buffer; - } + if( m_buffer ) + { + delete m_buffer; + } - m_buffer = new sample_t[( int )( sampleRate * m_maxTime )]; + m_buffer = new sample_t[( int )( sampleRate * m_maxTime )]; } diff --git a/plugins/flanger/monodelay.h b/plugins/flanger/monodelay.h index 8c544fd6c..866c5e697 100644 --- a/plugins/flanger/monodelay.h +++ b/plugins/flanger/monodelay.h @@ -30,31 +30,31 @@ class MonoDelay { public: - MonoDelay( int maxTime , int sampleRate ); - ~MonoDelay(); - inline void setLength( float length ) - { - if( length <= m_maxLength && length >= 0 ) - { - m_length = length; - } - } + MonoDelay( int maxTime , int sampleRate ); + ~MonoDelay(); + inline void setLength( float length ) + { + if( length <= m_maxLength && length >= 0 ) + { + m_length = length; + } + } - inline void setFeedback( float feedback ) - { - m_feedback = feedback; - } + inline void setFeedback( float feedback ) + { + m_feedback = feedback; + } - void tick( sample_t* sample ); - void setSampleRate( int sampleRate ); + void tick( sample_t* sample ); + void setSampleRate( int sampleRate ); private: - sample_t* m_buffer; - int m_maxLength; - float m_length; - int m_index; - float m_feedback; - float m_maxTime; + sample_t* m_buffer; + int m_maxLength; + float m_length; + int m_index; + float m_feedback; + float m_maxTime; }; #endif // MONODELAY_H diff --git a/plugins/flanger/noise.cpp b/plugins/flanger/noise.cpp index bd0d84b98..2c4606381 100644 --- a/plugins/flanger/noise.cpp +++ b/plugins/flanger/noise.cpp @@ -27,7 +27,7 @@ Noise::Noise() { - inv_randmax = 1.0/FAST_RAND_MAX; /* for range of 0 - 1.0 */ + inv_randmax = 1.0/FAST_RAND_MAX; /* for range of 0 - 1.0 */ } @@ -35,5 +35,5 @@ Noise::Noise() float Noise::tick() { - return (float) ((2.0 * fast_rand() * inv_randmax) - 1.0); + return (float) ((2.0 * fast_rand() * inv_randmax) - 1.0); } diff --git a/plugins/flanger/noise.h b/plugins/flanger/noise.h index 008700ed7..ea0a854bd 100644 --- a/plugins/flanger/noise.h +++ b/plugins/flanger/noise.h @@ -28,10 +28,10 @@ class Noise { public: - Noise(); - float tick(); + Noise(); + float tick(); private: - double inv_randmax; + double inv_randmax; }; #endif // NOISE_H diff --git a/plugins/flanger/quadraturelfo.cpp b/plugins/flanger/quadraturelfo.cpp index 1e4d7d0f1..49e8299ad 100644 --- a/plugins/flanger/quadraturelfo.cpp +++ b/plugins/flanger/quadraturelfo.cpp @@ -26,13 +26,13 @@ QuadratureLfo::QuadratureLfo( int sampleRate ) { - setSampleRate(sampleRate); + setSampleRate(sampleRate); } void QuadratureLfo::tick( float *s, float *c ) { - *s = sinf( m_phase ); - *c = cosf( m_phase ); - m_phase += m_increment; + *s = sinf( m_phase ); + *c = cosf( m_phase ); + m_phase += m_increment; } diff --git a/plugins/flanger/quadraturelfo.h b/plugins/flanger/quadraturelfo.h index f884f691c..04f2e62df 100644 --- a/plugins/flanger/quadraturelfo.h +++ b/plugins/flanger/quadraturelfo.h @@ -30,44 +30,44 @@ class QuadratureLfo { public: - QuadratureLfo( int sampleRate ); - ~QuadratureLfo() - { - } + QuadratureLfo( int sampleRate ); + ~QuadratureLfo() + { + } - inline void setFrequency( double frequency ) - { - if( frequency < 0 || frequency > ( m_samplerate / 2.0 ) || frequency == m_frequency ) - { - return; - } - m_frequency = frequency; - m_increment = m_frequency * m_twoPiOverSr; + inline void setFrequency( double frequency ) + { + if( frequency < 0 || frequency > ( m_samplerate / 2.0 ) || frequency == m_frequency ) + { + return; + } + m_frequency = frequency; + m_increment = m_frequency * m_twoPiOverSr; - if( m_phase >= F_2PI ) - { - m_phase -= F_2PI; - } - } + if( m_phase >= F_2PI ) + { + m_phase -= F_2PI; + } + } - inline void setSampleRate ( int samplerate ) - { - m_samplerate = samplerate; - m_twoPiOverSr = F_2PI / samplerate; - m_increment = m_frequency * m_twoPiOverSr; - } + inline void setSampleRate ( int samplerate ) + { + m_samplerate = samplerate; + m_twoPiOverSr = F_2PI / samplerate; + m_increment = m_frequency * m_twoPiOverSr; + } - void tick( float *s, float *c ); + void tick( float *s, float *c ); private: - double m_frequency; - double m_phase; - double m_increment; - double m_twoPiOverSr; - int m_samplerate; + double m_frequency; + double m_phase; + double m_increment; + double m_twoPiOverSr; + int m_samplerate; }; From eca59c30eb9df6a000f3297ba29d66926cf16c7c Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 22 Dec 2014 22:52:47 +0000 Subject: [PATCH 51/52] Flanger change Filenames to CamelCase --- plugins/CMakeLists.txt | 2 +- plugins/Flanger/CMakeLists.txt | 3 +++ .../FlangerControls.cpp} | 4 ++-- .../flangercontrols.h => Flanger/FlangerControls.h} | 2 +- .../FlangerControlsDialog.cpp} | 4 ++-- .../FlangerControlsDialog.h} | 0 .../flangereffect.cpp => Flanger/FlangerEffect.cpp} | 2 +- .../flangereffect.h => Flanger/FlangerEffect.h} | 8 ++++---- .../monodelay.cpp => Flanger/MonoDelay.cpp} | 2 +- .../{flanger/monodelay.h => Flanger/MonoDelay.h} | 0 plugins/{flanger/noise.cpp => Flanger/Noise.cpp} | 2 +- plugins/{flanger/noise.h => Flanger/Noise.h} | 0 .../quadraturelfo.cpp => Flanger/QuadratureLfo.cpp} | 2 +- .../quadraturelfo.h => Flanger/QuadratureLfo.h} | 0 plugins/{flanger => Flanger}/artwork.png | Bin plugins/{flanger => Flanger}/logo.png | Bin plugins/flanger/CMakeLists.txt | 3 --- 17 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 plugins/Flanger/CMakeLists.txt rename plugins/{flanger/flangercontrols.cpp => Flanger/FlangerControls.cpp} (97%) rename plugins/{flanger/flangercontrols.h => Flanger/FlangerControls.h} (98%) rename plugins/{flanger/flangercontrolsdialog.cpp => Flanger/FlangerControlsDialog.cpp} (97%) rename plugins/{flanger/flangercontrolsdialog.h => Flanger/FlangerControlsDialog.h} (100%) rename plugins/{flanger/flangereffect.cpp => Flanger/FlangerEffect.cpp} (99%) rename plugins/{flanger/flangereffect.h => Flanger/FlangerEffect.h} (93%) rename plugins/{flanger/monodelay.cpp => Flanger/MonoDelay.cpp} (98%) rename plugins/{flanger/monodelay.h => Flanger/MonoDelay.h} (100%) rename plugins/{flanger/noise.cpp => Flanger/Noise.cpp} (98%) rename plugins/{flanger/noise.h => Flanger/Noise.h} (100%) rename plugins/{flanger/quadraturelfo.cpp => Flanger/QuadratureLfo.cpp} (97%) rename plugins/{flanger/quadraturelfo.h => Flanger/QuadratureLfo.h} (100%) rename plugins/{flanger => Flanger}/artwork.png (100%) rename plugins/{flanger => Flanger}/logo.png (100%) delete mode 100644 plugins/flanger/CMakeLists.txt diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 63c80538e..d7b4cf11e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -11,7 +11,7 @@ ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) ADD_SUBDIRECTORY(Eq) -ADD_SUBDIRECTORY(flanger) +ADD_SUBDIRECTORY(Flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) ADD_SUBDIRECTORY(kicker) diff --git a/plugins/Flanger/CMakeLists.txt b/plugins/Flanger/CMakeLists.txt new file mode 100644 index 000000000..c3febd094 --- /dev/null +++ b/plugins/Flanger/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(flanger FlangerEffect.cpp FlangerControls.cpp FlangerControlsDialog.cpp Noise.cpp QuadratureLfo.cpp MonoDelay.cpp MOCFILES FlangerControls.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/flanger/flangercontrols.cpp b/plugins/Flanger/FlangerControls.cpp similarity index 97% rename from plugins/flanger/flangercontrols.cpp rename to plugins/Flanger/FlangerControls.cpp index 52e2d0a87..34d2b8f52 100644 --- a/plugins/flanger/flangercontrols.cpp +++ b/plugins/Flanger/FlangerControls.cpp @@ -24,8 +24,8 @@ #include -#include "flangercontrols.h" -#include "flangereffect.h" +#include "FlangerControls.h" +#include "FlangerEffect.h" #include "Engine.h" #include "Song.h" diff --git a/plugins/flanger/flangercontrols.h b/plugins/Flanger/FlangerControls.h similarity index 98% rename from plugins/flanger/flangercontrols.h rename to plugins/Flanger/FlangerControls.h index bd0f43742..29f5a5aaf 100644 --- a/plugins/flanger/flangercontrols.h +++ b/plugins/Flanger/FlangerControls.h @@ -27,7 +27,7 @@ #include "EffectControls.h" #include "Knob.h" -#include "flangercontrolsdialog.h" +#include "FlangerControlsDialog.h" class FlangerEffect; diff --git a/plugins/flanger/flangercontrolsdialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp similarity index 97% rename from plugins/flanger/flangercontrolsdialog.cpp rename to plugins/Flanger/FlangerControlsDialog.cpp index 8b3c61df4..26b223b29 100644 --- a/plugins/flanger/flangercontrolsdialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -22,8 +22,8 @@ * */ -#include "flangercontrolsdialog.h" -#include "flangercontrols.h" +#include "FlangerControlsDialog.h" +#include "FlangerControls.h" #include "embed.h" #include "LedCheckbox.h" #include "TempoSyncKnob.h" diff --git a/plugins/flanger/flangercontrolsdialog.h b/plugins/Flanger/FlangerControlsDialog.h similarity index 100% rename from plugins/flanger/flangercontrolsdialog.h rename to plugins/Flanger/FlangerControlsDialog.h diff --git a/plugins/flanger/flangereffect.cpp b/plugins/Flanger/FlangerEffect.cpp similarity index 99% rename from plugins/flanger/flangereffect.cpp rename to plugins/Flanger/FlangerEffect.cpp index fcaadfca7..4a029dad8 100644 --- a/plugins/flanger/flangereffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -22,7 +22,7 @@ * */ -#include "flangereffect.h" +#include "FlangerEffect.h" #include "Engine.h" #include "embed.cpp" diff --git a/plugins/flanger/flangereffect.h b/plugins/Flanger/FlangerEffect.h similarity index 93% rename from plugins/flanger/flangereffect.h rename to plugins/Flanger/FlangerEffect.h index 04f308249..ac6125623 100644 --- a/plugins/flanger/flangereffect.h +++ b/plugins/Flanger/FlangerEffect.h @@ -27,10 +27,10 @@ #define FLANGEREFFECT_H #include "Effect.h" -#include "flangercontrols.h" -#include "quadraturelfo.h" -#include "monodelay.h" -#include "noise.h" +#include "FlangerControls.h" +#include "QuadratureLfo.h" +#include "MonoDelay.h" +#include "Noise.h" class FlangerEffect : public Effect diff --git a/plugins/flanger/monodelay.cpp b/plugins/Flanger/MonoDelay.cpp similarity index 98% rename from plugins/flanger/monodelay.cpp rename to plugins/Flanger/MonoDelay.cpp index ebb5b322d..9cb473721 100644 --- a/plugins/flanger/monodelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -22,7 +22,7 @@ * */ -#include "monodelay.h" +#include "MonoDelay.h" #include "interpolation.h" #include "lmms_math.h" diff --git a/plugins/flanger/monodelay.h b/plugins/Flanger/MonoDelay.h similarity index 100% rename from plugins/flanger/monodelay.h rename to plugins/Flanger/MonoDelay.h diff --git a/plugins/flanger/noise.cpp b/plugins/Flanger/Noise.cpp similarity index 98% rename from plugins/flanger/noise.cpp rename to plugins/Flanger/Noise.cpp index 2c4606381..4d4c06e0a 100644 --- a/plugins/flanger/noise.cpp +++ b/plugins/Flanger/Noise.cpp @@ -22,7 +22,7 @@ * */ -#include "noise.h" +#include "Noise.h" #include "lmms_math.h" Noise::Noise() diff --git a/plugins/flanger/noise.h b/plugins/Flanger/Noise.h similarity index 100% rename from plugins/flanger/noise.h rename to plugins/Flanger/Noise.h diff --git a/plugins/flanger/quadraturelfo.cpp b/plugins/Flanger/QuadratureLfo.cpp similarity index 97% rename from plugins/flanger/quadraturelfo.cpp rename to plugins/Flanger/QuadratureLfo.cpp index 49e8299ad..bc31dc326 100644 --- a/plugins/flanger/quadraturelfo.cpp +++ b/plugins/Flanger/QuadratureLfo.cpp @@ -22,7 +22,7 @@ * */ -#include "quadraturelfo.h" +#include "QuadratureLfo.h" QuadratureLfo::QuadratureLfo( int sampleRate ) { diff --git a/plugins/flanger/quadraturelfo.h b/plugins/Flanger/QuadratureLfo.h similarity index 100% rename from plugins/flanger/quadraturelfo.h rename to plugins/Flanger/QuadratureLfo.h diff --git a/plugins/flanger/artwork.png b/plugins/Flanger/artwork.png similarity index 100% rename from plugins/flanger/artwork.png rename to plugins/Flanger/artwork.png diff --git a/plugins/flanger/logo.png b/plugins/Flanger/logo.png similarity index 100% rename from plugins/flanger/logo.png rename to plugins/Flanger/logo.png diff --git a/plugins/flanger/CMakeLists.txt b/plugins/flanger/CMakeLists.txt deleted file mode 100644 index bb3579e73..000000000 --- a/plugins/flanger/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -INCLUDE(BuildPlugin) - -BUILD_PLUGIN(flanger flangereffect.cpp flangercontrols.cpp flangercontrolsdialog.cpp noise.cpp quadraturelfo.cpp monodelay.cpp MOCFILES flangercontrols.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") From dd27dc688a5c4c9b19a924a04b5307cf6358526e Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 23 Dec 2014 09:33:13 +0000 Subject: [PATCH 52/52] updated as per 1474 --- plugins/Flanger/MonoDelay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Flanger/MonoDelay.cpp b/plugins/Flanger/MonoDelay.cpp index 9cb473721..9afc3ee17 100644 --- a/plugins/Flanger/MonoDelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -55,7 +55,7 @@ MonoDelay::~MonoDelay() void MonoDelay::tick( sample_t* sample ) { m_buffer[m_index] = *sample; - int readIndex = m_index - ( int )m_length; + int readIndex = m_index - ( int )m_length - 1; if(readIndex < 0) { readIndex += m_maxLength;