From 1595c2728e4652d75f1ffc512cd0e227bbb68f74 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 10 Dec 2014 08:44:25 +0000 Subject: [PATCH] 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 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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 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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