From 6f609e6b664d5a17f6a5a60e438c46318957975a Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Thu, 25 Jun 2020 09:08:08 -0400 Subject: [PATCH] Chroma and tonnetz updates (#1183) * fixed #1170 and #1176 * fixed docstring for chroma_cqt * updated chroma notebook to reflect new defaults * added safety check and test for chroma_cqt --- docs/examples/plot_chroma.py | 43 ++++-------------- librosa/feature/spectral.py | 21 +++++++-- .../test_display/test_tonnetz.png | Bin 15853 -> 16142 bytes tests/test_features.py | 8 +++- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/docs/examples/plot_chroma.py b/docs/examples/plot_chroma.py index 2f0d872059..521d0a50de 100644 --- a/docs/examples/plot_chroma.py +++ b/docs/examples/plot_chroma.py @@ -16,12 +16,11 @@ # Beyond the default parameter settings of librosa's chroma functions, we apply the following # enhancements: # -# 1. Over-sampling the frequency axis to reduce sensitivity to tuning deviations -# 2. Harmonic-percussive-residual source separation to eliminate transients. -# 3. Nearest-neighbor smoothing to eliminate passing tones and sparse noise. This is inspired by the +# 1. Harmonic-percussive-residual source separation to eliminate transients. +# 2. Nearest-neighbor smoothing to eliminate passing tones and sparse noise. This is inspired by the # recurrence-based smoothing technique of # `Cho and Bello, 2011 `_. -# 4. Local median filtering to suppress remaining discontinuities. +# 3. Local median filtering to suppress remaining discontinuities. # Code source: Brian McFee # License: ISC @@ -65,44 +64,22 @@ plt.tight_layout() -########################################################### -# We can correct for minor tuning deviations by using 3 CQT -# bins per semi-tone, instead of one -chroma_os = librosa.feature.chroma_cqt(y=y, sr=sr, bins_per_octave=12*3) - - -plt.figure(figsize=(12, 4)) - -plt.subplot(2, 1, 1) -librosa.display.specshow(chroma_orig[idx], y_axis='chroma') -plt.colorbar() -plt.ylabel('Original') - - -plt.subplot(2, 1, 2) -librosa.display.specshow(chroma_os[idx], y_axis='chroma', x_axis='time') -plt.colorbar() -plt.ylabel('3x-over') -plt.tight_layout() - - ######################################################## -# That cleaned up some rough edges, but we can do better -# by isolating the harmonic component. +# We can do better by isolating the harmonic component of the audio signal # We'll use a large margin for separating harmonics from percussives y_harm = librosa.effects.harmonic(y=y, margin=8) -chroma_os_harm = librosa.feature.chroma_cqt(y=y_harm, sr=sr, bins_per_octave=12*3) +chroma_harm = librosa.feature.chroma_cqt(y=y_harm, sr=sr) plt.figure(figsize=(12, 4)) plt.subplot(2, 1, 1) -librosa.display.specshow(chroma_os[idx], y_axis='chroma') +librosa.display.specshow(chroma_orig[idx], y_axis='chroma') plt.colorbar() plt.ylabel('3x-over') plt.subplot(2, 1, 2) -librosa.display.specshow(chroma_os_harm[idx], y_axis='chroma', x_axis='time') +librosa.display.specshow(chroma_harm[idx], y_axis='chroma', x_axis='time') plt.colorbar() plt.ylabel('Harmonic') plt.tight_layout() @@ -112,8 +89,8 @@ # There's still some noise in there though. # We can clean it up using non-local filtering. # This effectively removes any sparse additive noise from the features. -chroma_filter = np.minimum(chroma_os_harm, - librosa.decompose.nn_filter(chroma_os_harm, +chroma_filter = np.minimum(chroma_harm, + librosa.decompose.nn_filter(chroma_harm, aggregate=np.median, metric='cosine')) @@ -121,7 +98,7 @@ plt.figure(figsize=(12, 4)) plt.subplot(2, 1, 1) -librosa.display.specshow(chroma_os_harm[idx], y_axis='chroma') +librosa.display.specshow(chroma_harm[idx], y_axis='chroma') plt.colorbar() plt.ylabel('Harmonic') diff --git a/librosa/feature/spectral.py b/librosa/feature/spectral.py index 04a2934949..d9dbacf7b1 100644 --- a/librosa/feature/spectral.py +++ b/librosa/feature/spectral.py @@ -1206,7 +1206,7 @@ def chroma_stft(y=None, sr=22050, S=None, norm=np.inf, n_fft=2048, def chroma_cqt(y=None, sr=22050, C=None, hop_length=512, fmin=None, norm=np.inf, threshold=0.0, tuning=None, n_chroma=12, - n_octaves=7, window=None, bins_per_octave=None, cqt_mode='full'): + n_octaves=7, window=None, bins_per_octave=36, cqt_mode='full'): r'''Constant-Q chromagram Parameters @@ -1246,9 +1246,13 @@ def chroma_cqt(y=None, sr=22050, C=None, hop_length=512, fmin=None, window : None or np.ndarray Optional window parameter to `filters.cq_to_chroma` - bins_per_octave : int > 0 + bins_per_octave : int > 0, optional Number of bins per octave in the CQT. - Default: matches `n_chroma` + Must be an integer multiple of `n_chroma`. + Default: 36 (3 bins per semitone) + + If `None`, it will match `n_chroma`. + cqt_mode : ['full', 'hybrid'] Constant-Q transform mode @@ -1294,6 +1298,9 @@ def chroma_cqt(y=None, sr=22050, C=None, hop_length=512, fmin=None, if bins_per_octave is None: bins_per_octave = n_chroma + elif np.remainder(bins_per_octave, n_chroma) != 0: + raise ParameterError('bins_per_octave={} must be an integer ' + 'multiple of n_chroma={}'.format(bins_per_octave, n_chroma)) # Build the CQT if we don't have one already if C is None: @@ -1468,7 +1475,7 @@ def chroma_cens(y=None, sr=22050, C=None, hop_length=512, fmin=None, return util.normalize(cens, norm=norm, axis=0) -def tonnetz(y=None, sr=22050, chroma=None): +def tonnetz(y=None, sr=22050, chroma=None, **kwargs): '''Computes the tonal centroid features (tonnetz), following the method of [1]_. @@ -1490,6 +1497,10 @@ def tonnetz(y=None, sr=22050, chroma=None): If `None`, a cqt chromagram is performed. + kwargs + Additional keyword arguments to `chroma_cqt`, if `chroma` is not + pre-computed. + Returns ------- tonnetz : np.ndarray [shape(6, t)] @@ -1547,7 +1558,7 @@ def tonnetz(y=None, sr=22050, chroma=None): 'passed as an argument.') if chroma is None: - chroma = chroma_cqt(y=y, sr=sr) + chroma = chroma_cqt(y=y, sr=sr, **kwargs) # Generate Transformation matrix dim_map = np.linspace(0, 12, num=chroma.shape[0], endpoint=False) diff --git a/tests/baseline_images/test_display/test_tonnetz.png b/tests/baseline_images/test_display/test_tonnetz.png index 1c1732e0181fed86db2bbed93d03702b2c341147..e324da225efdf35b9acd67b15be53feab40c940e 100644 GIT binary patch literal 16142 zcmeHucTiJ#-*41sWqnjwmR$=N@PSoCL_~UbR|KW5pddA_kq!yH1W4RcS`r9SLJ1+<-*MkIcW!y#nfu54%v@(2O>)lp_3!snzURS@ z_Erb~F8_BJ40iC!WeZ0bY|n8RZ1;^X_kwS-qkahi|NJfdhbz~<1b^bcyqyR>?+dx? z8V-ZW`~>~o^}@WwAAD&Lap^{c6WS*t`lm2&Sny8~AwlSfApcv(qrAhy{n04xv**uh zojL9o5fNgfsrm1}pGAlHY8EaVjlp2YVOK1^zZR1_&y0QKDU4MW&z^0$!b=~#{QAz- zuabh-dWVxvb{u-RXYboxK`-r++rC!t`1SN|X`h6BF<$4B_q%wVx(~a4M)Cbk+efOp zr!2mro-!ERNPDN0*aE}j8;IlW*!r!F)y4}yQ2WOn@xd`gDxhQYqEgzbXC{;qNdeE*y51sLqk>o1SP zU>AP++gC8yALn*|34{H31SNO-dd5tVI{nh?BZ_@K@R&%a z1PBM?O!aXskH-lZ+DNH3 z%qT-vR_;Fg9gpWXMzF^A94w)@T6eY#20iCa53}4mT&?6e!oG>nV@EIZ<>3#suG2Oi zVECcvs0bgS9}1@*S;D?~@RHHVq07j^EQO@r%gFM_L=|OTuQjT*N>|=L-%+hMO{uVU z0;8;ax`?c4I`omO)L2!3aItY{jI|6OC+XJhs7>@)kF8syT8bvTbhG{0D~u1ro>Li% zXG(;_;oXcdWFxD~u3wnR_Hv2j^Y!p}R)?rupg5W-I$f(sFQ-EimCq1bPUfwf~b{k?jje4BolK}7B34-2*WGLVxdt0 z*L3}cw*KB_hJ}|(N9*EjXaDdrC*cQOrd4-iLO+2oZi#-LFBng&AiZkk-76>Q(zW0( zineUA4_>)ttKv*n9Qv8=`0XojF4nm5afioxi2T+x#z+Z2u@Y{&F0YKsU*VikzGc%n zMUEG2q^mIL>b(<@sZEUf3GF({-W7AkN~B;zFRXIk^7^>Ap$n~oW2Yp}J&Z7GvZ4vI z%wJ4Tm>y?4VOxaOM*5n~8;;`9dP)C$q{3o8?0ib|b`^13#^98+;c8;$kJb+7o$8*e zHVgz%Lh&=G?>uWGbo8z6KDV>GsdY%sL*?+PdkS2ibGdQ5k5tThwd;0}R(9R~!I$4#MpbA2QOrPe z^B%Z69(k*DU%WqEn6Tv|On1bbXN&HQFpnfw4ZdqsEx0`1#rCtxY5b4|cGJl;I;I3| zJNrjq0_Nj4jjH!7*W1agecP@ru|F6o6{fA;I-&dw{Rv+#==$OpYr$_W-skXC@rK&; zYquKOSX{S3w^Si%FfQ`c`mfn>-w#*kd%)C!zr7$2=VFd#Wv)eU|Jnl1gEst0ilg9) zn$m^rBCQfD^4|LkEicN@`gfjlH~;ub_NrP*Le@1qP3+3|@o~GJ4|h>-$QvgIRS9%|B^1Lz>cSbJj?twaOQQeHv7Q z5qnr^QQYmu$0B$3*2nZsz4T%qVY{O|$4S3fYpn4^aAosZ<{r1*m%WK*ZXH<6&xpk< z-7c0iLrwJCmPGk0nk4Ys*&r*j*Wum?Jw4%K#?YQT?qL~~$D}l|A^Vz*>GVZk3w{i5 z;qrD=2EEp3;@DOzQ>^jvX49_c&3P~y_*hWTS}Sgt?``>`pZaIt`eW+4^z;uGC2kKA z=N4ns>ursSb1dnhJ(^nQvS;2Bw!;LUZ0GzEqzfjpI&R@qm<6f_ii2GO{BsAxsqH<~ z7pTU%dW=1LB2AF7iTHX3*XSJloIe+R@VIb7Q*)*A+ki$+4i5f)q)+pgWw5;UD$0}` zZoK^wdunbMweKy}e#{H@cITEb*z7Eo6Tc5c>i_P^aI|7iSIas}p~z9qy2$a2CnX}M zGpE<*Sj>l?ubxi3C75U&T`eiV8qM?hL`)zb?_+uR~M8gZ4e^IGae1+Br|BqNs&lY&5dS8y7XjetLmX9 zM$Yd{G=|sjQ{R8+Ok#!?MorMXVh`bnI%K`%%i-}pBYt^eBIv$UwZDR1P~v?htU;go zj+h;5N-wrc3bB*cmRW)H?#`fc zIx9)7BHoSG_}DXE&7Zs6urZm$naM$)eSw6*MuZ!$UsKi7tV3C`vu`TuS@IVzUR-)i zi2p9dT(ZHY^nm$6&h%u-%7(sEE3vR>zwYAs?QCwiy={S6Ldp68fX?I>?@Z;!2^y=S z7RDO3*Ty-`D}8Z{?*4pJGMdSFNl?`BKXeDkI^V`_uRrllJ#Jknr~vF{c#cKfP)YFL&1v5T;=EwS4<>V?Mo-0v%cER3hA!G24JMb}9lGuz+!z&DrKL5fP)-6fr=?Q0L# z(R68h5obPJiBNiS((a)O!Q{N4X|WZcDV^hB;Oz|`?FjRy}{htIlK-MZA>KbsfLS`@<{dv~Y5(Wh?q`sv6o^=S9ES9K~jsZ{KS z{VDYt1;6kxgjZV90Zx&UCRH4EEUfDvSI=&-%)vrwR^E$#R%DaWQFfg;sleW_JaFJZ z`xl2j-V4$JcCPr_qr4Yj^ZN8=f4k0%M)c(9YtHoMF#h^6{%?V*Ml+en$xLL5f3yM& zb`BLI_0NmSGXt6wZn{tVSsKb~eeF3)#+zzaQ_$VK@_e#jx*U~G=LR{4mDG&}x#DU( z-trs^^Laih`c9{9gl^6**>{opc?dn_lR>q;^+Om@hexsRr;75k1Zn&z0KzRbzCE4G z-4_1y9(e`kxC8RSzVyv(6}W4QKka@s#*^JsPSUe?gFn~D|^UP z#lNMfZ#cZMG|tUAI-+=cZazP^j#-fM6PC?y)N*45^aMFHW*PVAJ#j=lPDiKGLIY8= zVswS0!)%Dio!Z6Y73bP*E zDJ9WT*RykzLqAord0NH4nGjJDH{j{KwiIRo3tClc#U5KDq%w1~!f#RM3k2;p_eVc22E6(zAv|Xr(V!k#Ghn}h&&viu2qfH$uvauxnrdww= z`907udU_GKbQ^Enlt)+f!kv>+_`4qq_ zPyXfEkmuRFf#s1cN^NP5S1*tEshG?nMdaHw2BXm)s06#(F2c{^Uz3cfeU;1 z1zF%E5us8$TM>kaqS?wY)S2mm8cTKm-i88g!;WMtDNqq4zwd{l^v@}- zuB~-=Ml*-LKCi4S*(}&G#BO#YEYWCm_o|T3=xp5*DEQ_@I?vbin`xap_tpNxXOkZBmoqA%reE@CxoT1;)_= zy`Q{a{qn^U%|`PL2&-y9Pxv!xepu-hX57^`?^{_}D0Axtx7|Cq^r=mzb91&}$GFmW zjO>54;#~wl6sDk=RiW_BQQutIXm&!h-onpPiiu&3#}Fb~MqQIf;XtBW#i79A@(1In zCJl&ayLg0NlGWG)`}6D-*k<%>0S5b*8{ zO~RX`!%&x%4kO}2C*oInzlekqwIbriMl?WVea4H;4Z>k}q@Uj@v*pKxc980J+%vKa z!+m;k4Yie(?G2|>L8o|S#L3+7z6V@-$>M7MF4#>MmA^85C^M4rW}%*~sLzVwGt#-v({za&Ng56)3M=R zyqKdG9DuY^I}uX7a(QjP5G1OMK~vG~B)cT~F6U zC<5pe1K~JD%-qvCE_YObAvMq3GnjhcR_GCMn@-7bXGMZ^H{Sr-{<=iN;Dn_*bCmq8 zdVZ3*(E?d7s+KQzp)QPWr{BUcIqVVgJ+zGTqcup7X_IRM866j8Ouqqn|E33ab9%H% zNwfLW562Q#2eiOqHq2xPPTo^MB|4U_y0=NeVDXRk9XGz&munab?rvYF?rh(nViRn$ zb<|Q!mb)4jTel{`cI}%np8MmjvhuAr55El^`RV>8D=Vvbu(wez<1pC!C~$%d*C)a` zAm1l+ZaSCW7+7A3oy|M^Ip32#z^SrQO3*PAmO21XSV%CFS1pKNmKN>@OG;rH-I)&} zn6BLzf>38vR4-_UnIskIygIIn`jYb#3QTa`9>!+%&SULhv4q`_xWMG z1VnSMX>@GA_Y{XobKU5~bKN+QO;;yJYW0^V}yxIRw+H=_*8|pkT-xVX8r_ zKTv`+ioL~)Zo4p6BIxsRcHr5s92=;q_Eu=Q=-*SpXQ>m}r~A8jVb7=Ia`<#TODUQO zKEg~{xvcOmx5`I8qmB6_y!-jeuy3K94;4LrdW}%bm6d(0uobxG(7E(jUqUFC zOso56NlM}V3f!+cK3z6)W_mVCDc_p>b#=uLb(oJQ_hi@lo4*_1)P3J|xo~>6>s{V`Uh!egsC^v$Dck-owoYm7U7N3<5E)(u<_Y?-|l zK8p#~#b@no5@o!FHt3>Q`9{ymYc$DqVkv2ewv?QBMA5!;s#n*4%ah4HS^bk|HcMN} zbKPl+{)H)jowkbfE!H|Dt5x-TdcpZ+OJ@f~(e>=oRC_<~@AO%L+y=DvS)zaLkTI=F zG5>u5ukVU0)3jP`D92GLO+$fh#CKVj{trK9WhxS*uSscHUZeEIQ4gW9J$1YP|^i z*$+YtrWcT|e(joS(ez=ULt3Id-SvtOO3M^2Nyby%y#k+83e|;1`v$NN{@EvOqOrQ)Y~V8RI2&erupg@Ljg-k2(hx+ z)@VNjQ0D8|*rmM1_}OT;MY2ErnOhnc2D=#-)eT#&)5I<%g-$)T>S&WOKZe*5F*ubw zqDl^w+3EJi8ju2FE*c=*Vl~HxzWLUr{n@3YI@i_#KZ^?IB;CN__TYNvA)E9w57bEw z&kn|cwQIV^6fuNQ=JEhx9jGE%{qZLiX-Bako!KI0)oKu zTUB3r*-!u^+AJ-1Yocq!|2uqP+=2!K|N6r4;+@s?^?LEd=;%?oleUQhfgrdTJ<$-F zGY#_a?o$d4JeUr>VGnJVp6Oe~RbrLLYW^52uAPp*6_^X0DFQXV0%z8GLI4%hhh zrBwI>T3qiji#2B0;8K`fyjq(xM$|SV}4WlQC&$ERgh| zP1;xoYg+BAw@{0hi|*?K1i6@PzEr-kE!Y3BFF-qIt$TLISNx`+2ZeNLG6PLQ!jqtJ zHh)91PF3PFDhXGfoP6`xO7SQ}zH<}SMn~gS^J8vJgtQ%8s;x}W01GPp_C$sUja0V_ z_Qyh0_u?H7+_rEaNDm2MsRn%!=xrm-u`7Bjnw}ILfFd4m$3vRV69==S5Dpy}7`Rc7 z@&sBO`9yqkFp!zFq6HKGK||P*ECOgSuiU(+`bi#!R~ovrz1|75nq&8mgp7tpaqW4RJkA$bWuP&(Tn&3^!Y_7{DQJ_>vCFUJ6@l41OO*5fQO>$ z_rPGyQXI289$+&q-Bl7F$9}r2)DD07$>R#C)5(@zw$RH8hhDkLbrqR-)>G0McyJWD9$cXt4VJA_pfX z?8pL{XW8Wqh%twCK(`HNbLHqI4(P14k1{xed@!vBOYL$H5enu3Og&?tQKs~`)YZ=OZ}w@z&%z{=sNt7JF4*ns&P6!al>xIV1Z13MkErw z^IyGs6~n>nPbO>k`B^1FLzR!-b-N?^Xz1M2`(T5=OH)UJ9l)N+OCga+2I5a`9H3A6 z|J$WgMa2P21G{d z-u?SPmdo>OF_0M3GoWITBwA_iAr$tD^&o0pO-eCfBB0s=mVJJZQ1fl+%`XS)H~x1D{c{v z*nH;#(Ejb)-$?|ElC!9$g*qrUGiIcBfzBYoVkMoWoXW?FxDBf`8%_>e>;vS#?%7@+ z?N*7fvJ-3?`oCYBmq`2_)uJ0!Gak2>^At5i;#+UAKX19m*}r=m0$ZD{}cJuUA``_{W(c6K}(dF)W=?nQI)%bxH znJ%7a5uQH8#7Cgwc>d<63%SNW7U@#o@gA6aPE@n@+`m^ND!hLzYlv?a@8{-%Qb5}| zY*C6>PlleTD^yb^g5WCZL?t_6cEm9+_a_}aWiE8w5iK2qf4Gzc>j&V|g(=C;xAO)w zFotu5PSu!|8J+$PJU3oC$XfXRW|?~0aFuaz{}tC3bc-=@Yl~*ExjInlWuD1Pu`aXX z`y==jO|tZpK*y?$J1#WHHv3^rOC3)Ch@m(K;0G6ftPqOIQfaM9%o=54iCFx$TrqZf zx}5c?vXnHNvf!@Sk~O5j4%jA`Vnp;%Hvh${QvAH}){Amfko|hDj{s91Y)!9s7F=ey zv$eQ5orxxMPp5%`cPSIwL{4vnj9*kS(!l5ho|%})P%Q~mCZ`jRQ zkDG;2$@mBG(v(C$9lGw&b+3XUz7_4Ej3h*!<8FyT0=4hB^0OwDJR-q9UX551WZPMP zzWT(_MJ`1>_)2%jkJ-L~c$N-Ks(2xT*B7yqRm~kLCrOuZSaX*-VHer04-Lvgg9UGc5G zSWOwV9naUR65hxQv6ZJLbQbYr*#o$4Uf)MM*F4-oM@4_Qszx?`!CY_HWIWcC*3&Zc zV%;_%HmGeXLet&JdL92@noPKe*NHOjZk&B|nZfhZj;<8gtRIN#0Wp)|6&IWv+HrJX zd|aVmYhv~mkOfA3@&Q)XN(YImqY#``5ViEFM_Ym6!2yU)DO_=9KhOx~jXHpc2VAyi zsXM0t26$dmR&VMK!P>Ddh~0@*(j(zU99 z74jJ*Hp_GftzJUu8{NEQ2_e?d*ZWl40V*C?jsf`tq`)G(&)H=d+|J%&tyhbHU^g-o z3VD1{8XTiLI`a6adeDnzLWeN=0@QkXC;*=0STRSblYkR*Vpe+fA*i(^h5IC>g=RHJ z)q`NyYy<91PEKw=-1_Oajr+Kb`+j;kS1X(gor!Pk^qJqR%=Qx?v(U)MXpDIhL}g=n zhSDn$05@<5)#sonYR;8ja1|AmDvXe~(Z924-ue3po5^vuB&x|BbdL2K)D0+1L! z28>0nW@cOThf=Z~Kosm~Gw;Vl^8A33iSkIR$i6$_=vL)ov58TsS2L z0+)~rK+J-lEC4UxbI#t$4SBt9X{BJtL|GZtyws7Y8|a)HJCg;0X{F6Bt6R%cZUuCun?`rg2e!Whr0Ivhi_fqd)nV zTC>qz4(8htJq70GK7hZNnwk!<*-C(fJ^(a?%BR?Lv>XEZ`3h0fK%6?BiP+Vx(V1P1j5t({}-;a_{(7k z^8(ZdEZ{5yz(SE=E}`}_JfmEJkG%0l6DteBy+UA-L({v_7u~vIToL^rRO%*gSnT*= z>c$&`Pe0NP=Kzq$)`VK01K;kmGFp_DZtn6oP3kS5wG+UfaGj|YTca4V*z9{Uls*W zvuJC}9V?ihPu?eY(jq!~U5~DhnrP@pcd*_Q_XLO39GD9=FcxDhg2 zS?!M=T&9(4fh&jYA}@_BV7|dh+OEl zLb7daj7@f{cQ`M#o*_A1`T7`WY$~l8jA02 zWVhZuV_NL^wY==Hhi=aL%E6T?G_BnCRD^RzJy44s-DWbD9+yiPh!T!-x5jyINBRBQ zCyRbQ|MYizROti;rD~Cl9v)mB@k;Q|BE%aE-7RElrKec)4a!q#6l?#?OK2CP`%jD< zO~J9_Y1HbFEctSdm2hsuC5wo3V55iW;;CwF#2UXKlwQ8cl(P|v$>gr6xxx^-erI4JsWNnBgNy~g!!>+D5Lz`PztYkQ{D3~zl-s#}wd!i!H zG1lAJ;b^>RFgnCDxkzN0?KRdgWCG7a4i`F^rw(ma>Cmz;<-@d0?h!9`^%yP0s6NMN zy_;8mvV=0`*D}mbr&`$`u|<{-`n5!-LBL z4lHXC^*k6jdk=PcJILvqYZWAG{yR85gUF5bu|bK0`-X(1iNGXB{>l|e>>oAR!{gVl zY>i*Fr>hsm=cV@|^i>-9D$2KXqV4pv{gvu1Fev%5lJNJQS7!f6a>w%?;U>Uupyr)**LXL~_#=1-h`pTyR!Vu>@#3YLI3V;2ELKT3r#CSXj4W6H zkqlMfpcgPdBtPxcrLwe0864!szdB=;L zUXIeU(co962e>l3yx&`DyI8puv&xeV9Zc(47?oZ*S*>dh7m34{yXWck{}8&n=MCl6 z4x;-h2=B;PIwf6bj~b-Q)DDyfp^jb_c3tMxr(%Mn50(hm?BDSm``golEovU8Sq-L_ zY=(`W4YpkwG^W&G$YW1H?J-bvocagT?G6l{ye*{kmMT`1AZxvK;PgIgZKAI|R-IU) zk3hSk8C^5wEUE|%@^bQETuW|WZ-{Pi6U~-$%YcTxChw22X!p8H?ct?7#k(@gBc5l4 zb-qkDZiTm%QhmrN-9FE^;apn|=7q|T+N0vF*L}%(EkB>v zwid5KG}j@YC#nWrX4p^TUFf?vfgoF5?b;t4ALz7IQ{CFh@^2~h-we};w!=R+s*~3) z@)?tK4T1`C;S}MvkZNZ)p7}nDv|y=Zd}&6}`NAOS+lx8qGvo zRboV3;Uwr#K!$NtV?Nn3rndxD>KF9#QV`VCbVL>}ESlnwT2{fS9!!f1`hnslABQ#Q zO*M8Y!Wfy2r!`3{yY;{FW0T9V-n;?V?aH(^+Uh_Ap*)pdtvQQ&t5R6OiS$e0h4vo> z)^@aj)rfT(TW2tyb5&GI*=`TVq(LW(_OTQl67GD6pL3<#lN2Vow6s&K zbrksBw|S{RRH_;Zo-D)F%@8Cs$ zg>5-nXk~i%_KX){s$>=+V?j2PnL;3vr@Ss6V1%!CPb>>9B|;lFU+^Buowe*o9c{ll z8uiz9<<+~%%izV~HUjLAfm0nnXq#qKA^-WD-@o4Gx-A7vD;?`1TILQ`L@%goG6k8g zzH9+?(`C&CCv<_N9w+1HW;3r&V2a(k^kJ?*XWra@Y*Yt@|*w zPWFPqu+ipjVn1>Ho1G)Qj-tHn4!fMOkpZc@uw^q>VCjKMBbCs>t5%nyqoef_0DFf@ z>^GOELpMI&?|l8+e$LbTiVs1B{Q`+!Y`#M`;1x=>e~y*c~U{2STj)J^zd8tNdSbBQc_YjmNTdmfBBsN*n~`{xpaVX@XeH!3cyGqZypfFrBa84 z)5^+m>LPKZE-O5aE;Q@h2n4NJi0f&x6h7Z_Nyq+^uiZH04w*(Ist>yLF>be^gXDTJ?B zwfil0kQ=RCvNfp-K4Y7fwc5nMA;IT&#rVnO{KUjpm~1rXa#^4-BL03rso=%)g@d-% z+bt|xMMK0_y#?i+JmDxLwsw% z5U0n~T(_Maj85jLj8a}A(c~MqYA%Oeq+I2b>I5TKhPtTk_tA$#!!!&8a^bhc=R;hMy$zUESqeE0h z@b5Mp$@k&kcvo-3D@w9u0-2LHs+>pN6O#Qatma3bcM+Gs3;g$V?ykB z2`e|pSYwfmarP+B=?bha<)PCdhw6UB^chZ(X-&7X?tPsH;Bm3Pg4flOE?p2xQErToHIxH|oRh z{ybrrnCM5klAs&Tcr|5BK|~GFS}3$GwkBPVQr`?5x|1b<yJw|BXQXSUamba`5 z7+crb3ifUK3!$v^B&;cJe53zxpFC$H#YstHvNGPsB`#(EF4zaFidrCSF}ynL+;aP8y9*`~ zjgCpzvKIquMhA85s32Hg7+i`eA9h=aYI3V&FueNgz6Of%h2(DFow zA10pJZIE(G<=6Jx=Xb~zSOt$wcT4Y|S~l+Xys|gqCkw`*<85tsmmE)j`76vK;`!`% zJ7YW^e)#Uq-tK3p>7T4#{A3kzNafe<+Q0rH2(6FyV8Ea9%17t!?)W^cxUz14mA5*~ zT6XD*8mQ(I1;NksvO9dhfd2feXK^bGc0cwp4E*`i61D{fyQq2}d{QKR2E5<%qY4am z#_zkIV6eY*xBdu&U6I@J9Sn91`~MIBzs^kY@Z#3<1C?&_iLzSBe!a!#5C{aRZ42yX zrdTX?#Cx902_N>;j$ED$&!~3z`HW>MB~g}oPS&k&@iYt;p}0%tWPA!{bAqkuh5Pk> z(&66X%Zc&vIA%A>xTw1lZq>IOu2a=8%A6hgoL8NmFV}H#EUkt)-k8;K z@G26C#9?nyXFv61xuv~qa8p+xF=ju}$^C3UQJ@M!(6`~jr<>Is?|*in>9@>ehf_u;NBQaegAL_F2Exh(lZC z{*AYF;$*5Vj%P7XjVc`8u8MteQZ6q1O(_vyG<9%W8eVm=EW(C@r}rRm+={O8DBkaq z@co_m;nV!y#)O%y0|Pj{UY@)=!fHPUS92L}OJbEW6JlM03boyEHSYBCR6hMWJ)OAS zgnFQMES6!1#Rju0b-|Qj>VSUn@Z+AavOYVqiM#`D|5NuehhFZZH(opS zWH#PhK@1#RZ%mB8BeQYmuPpP?phLdfO8FOqP6g3ZYlHR+dSA5OviNw@YW{hr=0ktH%vFPkE~qqbskD@kT%ly)u0%VekEs%@cBRhapq z^(YxCWr^@;CtXVa8appc|2cPg_h^jO zaO1VirZ*imv7GmhYHTz#5cYPL2`#bJ6~nZ9Ng7yPpRysF>8Isun-@i=9Bp}gd%LD{ zA!|D1uT60)gZ9=l4i$x7Qu>TRdYh*PMWBeC{xo5%o@0f?`WIdC7})rs*sw&Fh?7a@ zEWEIbvDt4XY;U5(5c=ijLebonG$nXgH8)7a2RaL3#3;^z10XdF&|ez|q(E{#DD0l4p!n-d;lEI(xLyyx%cq zAMNB1PhTFqU=5o)k2tB%Q~FxvUa{ok`S!&CeMo+?^Y`~B_%2-IJ$s^6O>3URdbmGq zJxk*CH0Bjv3jL!;aA-xa37DPrA3MpE8$%1*DGV#_D(*}a@+Ze zBYzi#EVS>GnrUfFXf9lPd&s}G@xjSQg#(W-amUj5{dU%(8w>Q#sHhcZ)*UlTF7}G{ zcY&OX2)Rd((;dh+V24gLG%q5Gqsb9Uv1eJ=*l`A{6ocEi17{KtU*~h@BDTdD+)DF= z`R%8xGEvgf*p_V4xcp{1vQ{)@&$$v+Jvuw9fFEV{ZyXK$>!)Y+^L|2hn&)5VMGUuy zv+!V*4t4rYq2iaG?AiDM{PoQvAJ6Di5UaWFm=9YDBcw_ucs_o~Cp)j2kn$8wzI$r# zfOMC?Q^-dvmMXW(F!pm!-rrCC-NIt#%uWrSzZ^W9T4+?8T7;H&nH6}#jz4Wp2d7%(XKI&xlTbl{lmgAdGY`}5T( z{I;`mo5s6-x%n4kSjxmMCQmO*RZGi%k4mYVX*gG8Wx-N#8fmP)t7H;Ay&KcIcZRi0 z>QsKY?f?CJBMgPTn@Bb}+*b9e>SX#!r>YOE4j-Llw|q>%$|%g|{EZTXtzBo!x8nYi;nT+%8$oB-@zreRxkwLfzOS`HSUBFc@EH>v@Zq z)dFQ~+%DNxRFiOYmc?qzko>(J9`97`Ln*$T?L3fg7R}##%X@L;9d{(8iy~-}#6rnC z+%sRibD&JKVW}lPevO-g$D$4Dy)hQl6>No9i$3f@3XVPvRRJ8`qW| z!xe~3badka6)r~FpGaGqwSw38vgNRref|W;YM;v^QLGAl1b4iSKOE3@3@9D`#jT%| zUBidGxr1IQ6a;!DJ3hWn#HUZFDzAUqi;-LnU7yOZ^7Zvqxchz!vj4gRMT0oSCG^@DGzDH$U(%2w9<6B+?zraB#K*MOabr^tq~{t5MhsaCD~!;~FxW!YcJu~@ zDp{M8^i~C~OkXx}$~L$NJ2WlNTx(@j>L3^m;GrkJ(D7}3uwCh30Id>B;xwAbXgKeu zxrSU8^UBonV@^XWFi}P$AwK@GGTJcNXgZTND$%21j(iK^M*=3>PIO0LHNcQZJB@`6 zis&tyh_?KAseF`W_KA%NmHP|-f)LyX zW6fM*#+&j7y}0XL7;~_UPP){dv@xD5bUS_k1`{7!=-pDddnPYB_@v`ol{}1i-4GmG zaE*(SfSDmR$TR)`oABqd8x;v%G1EHuFxEh(0m7YRy%l!9_UAMFzrdn3++6zL1@2MX zw=*YG$JgfHK3qOfZg!_N>5#TpYf^Xy(7OeXZ2!Shx3rc73-5ce#}ehO5|u??_TJ`% zU{qRJF)A%J5VoIh%&_xbYWU^86sLZc>8i>{M&9^T9v5EF(_QaAHq$(x znMzY%@OK%=AU@7=izHR|yz3p_ZcLqKv*W_8s%2CUEu4=v+&_1a;KK^K($T|zKCe~e zWx$#$PSIjY1C!KEGLwIsW5hLQ|HITiJ5lqzM8vTt#mi(!^;nAd|CNnYERn6$t`o2Mz3Vai^|j;)LNV zp4@KlQhwCf)$V(_hk2YcI&PA&1i|lDk%7CDFVj;LM>2spzI>i~r!ef1Ao|9^WAt4s zQ)kB9S*3;eCbJ&nvaIbh?D?DZ`V^$Jw03RQ^yI}`Iej|?f~=r9xxVbuRv&AUH!<~q zO;K6a#v4Pl1MO&XT`W@cO8jylvZDL^_CxgC&N4${c^NyO=uug$?H*IxcOp*PEU;2> z)=XCscdFS|!TGtrmMbx6bS}r;8krNWdyP$bS877nAK#!vSKO#mPHaC|Aw?MS7wdh4SzlnR5B76yF>j~f)6FA6@Zzdp5Q3Bow3~k|EsV&`HIC~3xwsx5 z@1NdT@N3X2;=OhCYv6@A$#WwNrj( z-#^k+g19u9Y_b@CvJFT9$Ew?(_u>q)I}GPv7l7B9X3^n|Kn5tMRCgMy7u)2@k;!Cj zzn;QOy?`4akp0y#4m_ZieGqC(LGv1SVq!ad?KkUBAPo<7p=G*ES1u(f!yCQVAMKGJ zfBRu?;x4U*Q+tgjpAOcC1)OxPJ7vy&V*4@Sfcp3&`QT&tC=j%xe^im?cBi0qWi(w6 zGDVB10vt}uL;Q#GrV14Kv-N}goiO}`A(Mw`??Pr;L5`PYO2mTi!Q2l%iQQ0l#MLjg z>{2~E4yE8Yvdc{H(L2M9|45(%4C?C}-p1hALtB15ys@z{{y|xMcLXhDuU9Ec z)~HuY?Z*q0MYA@<09`qd7d<<&^pLCyV(U01abh?Cr%`Yld{=#k3g#|Q78I`(GnuCK z4#eTZ<9s?IB_(E3)nrp-)jgHrp_H4Oi+sOj`^W1JZU=}X_pD~yz>EpGa9|tA&Q#@- zo4=)dBj<`TS|N2oCn8c%4HJsU`O=ccX??pA8!V*OEIz*Q%{wG5?Xfv7J29T9m3-W; zc!8S*o)tp%{(dNPacLM44M`6oQXHB#`VGjU_7^%6we;Xb-~3p<#`jNg7+&i^c%zo_ zt2`}}iG>xf#i5zSW%Dhth!AuXGnU&c^wIe509~lP--hkvXVJkDW~iwthMV)PjLKDmQXv`kaS{M|+Nu z&R#HEj->FQkyybf{!aI+y6LQdWm|Y2dSforJT^)Js1$CIR_yWM25#%8ss+6d+hMRx z?R{s$o6DW5#~;HtZ;eFGpen)|b=swaBYIb&weN8V*MLhm`q2PGna! zSRc*Xm6LQj4ZSkvzlYr?kD4o<;nzG?iaIvWi(2LJC~vNpjaQNu6ZYAf;zzx=z|PQr zREhrD@8B`I_%jT4)1E6O4S3c+nqhrQ?4e6-v)>xln`jguAE{!tL}MlGD}sd;EObn|fxT$OV0`7(Hi(4H+m^#I zL6UUQ4fVOKh6cS{tBSjO-lT=*N+~pHAX3VT#S86^mif~!ksp#%Hzt`hb_NkcFm)rQ zb(MF~%N#Z}EuVQ;G)ZO`Y$j@aT@j2te&-)6{4LuHrvhj)l~!iCy~P>5b@L0sx#O+8 zBJ)Vy>m-}t(i@qi-mdPZSRv@UX~w&XAe^fG!Hz1KFV$rj1Yh#LvYSXblxj% zX6Dx{LI1-0EcBIexa1YtcIM68+;zX)g1y!OO%+<$qWE|u78^-2)DnlEt%q~!)kf*^ z?iU5k(_wQPdh!(^zJyaSHKnJEb10kqETWY8v5J*(PVaiq$kPq~yV3LX;VUfHKMxo$ zyeIOe*dLhY+6aX5`3Vl!qo^6a{+>9)zIZEXcfWHAKFIpb)Te@8bc&hj=yq(2YmIkw z*0wi_bAwnP7KL3|EH)1PeeBS)qUfq?nM8RfIqQxVQyrgEZcSq=;)g-%y?n(|9PyIj zQ3T4nl$j{#yi#;`;Z_qZ7AbrX86Asp<_s~5m8L7>5PUvlrbg6*r}@rB ze(imc>^66ihFB9Ojd%68gp>@QXD9zoiU>;V8hhCy%z5C$G98rm%Lzsl((*hqT4z5p zT7=0jbqy4BgY6jRg^h<1DTn-Xl!Kr*GR>Oau~%j%9E z;^hogploUboj`h4ezIfV(T{}}o{!I$;gX#zWNgiN_BkL=XakCosUPG#654wiai{6I znr*WG_2O-?`?*7E+dh?v$F;G9)PcZ zw)O1HF+#Elv{sPsNFVrEOI3e@Ge|N=gUP%B$A$=`Yc!h>WVjE=0F#B@rpkQa@mynh z{nFCh2U0OAN>Q^f*E&&+`vi<=L=G^C(%dh!ip3_0*s<>Jt(e)f%Kk&*a;8&0+w5n_|TTi1FN7D0CfHejvr_*pEDaMK5j4enuJ^}eo#abXSSYCiT zaNlo}f|QYrgDdjfNX$w{bVD$>F&B1k{(S&@N~dNjD=QD`_*`0DU0rw^1E9T0AUtfo zFVi^8-@+m%a{2|hZHg9J8%p?@r@g-@y^#Z)!(F(lrb40vK~Zp{j8ffYPG1BSs+;!1 zCGagnqe_vJsjux%AOT6Ru=w~TO?{%07K9Q8{m$v96oTU%5bjmdAMW;z&P$LYAVr#?`HDkq0-At zKE^y59Q)dy|G6D#A8C9Fdh4}xaFUOZ@X*O$zXWZv>5gpL;xkYgtN8Az*=wM?_rf=q zGT|6Bc$8`Y6!rewhcVv=NM&hzS+-|t7mRwtVDDdRzkzRg$EqZa)0)J*cpQ0ixeZBk zL#4S&!(dA{lICIen!lI*2)2ReSWBEq1An#GmI`+F;G;L7Bx+1-|(KUPFR5gly6V?k0I2B41!3Xo+VeoB7;E0a**Ifp6ovZ{Ua9p&~6^ zl8=r8dR!I4l51;JIAif1xPXD5{r0DD$qGD$J3%F$GyxMl4z1U1Ql_V}I()KE0MZ*y zc`cTIirFZ^$RXDuK4xpaetY+xJ^wy@+cVJQL$uE_h7HDkX;XtgwAkke(R?;ttJIqxG2=3VR?=UJ#4U={GCcjB$MgRTzL=C zyaWB}VL%uYE!|-23%+pqe9?k0uup&gA(aeGn2jm-qrSuUfKlx&(7eB&44@)g6Fi(j ztfff#rPQ816lmcCnCO=i%OB#l>vVx#4Hr+qTUWZ^%;|Y(4!!I#TbDu9qHZ@|dH`hs zFR;@S_sHvi#2PRTN=w5xx>Gz`9`4|xFalIi<E6&DTjLX(6d|ujcT@zc+Xo9)H?%a94(W`8(I=giGSfYL zxYKARCll}I9C9CE_lGo8DMC@KtQ1zqJM)J ziO37=N(~8C(WG|t2z14k*MN;f`vW5+V&p@n+n)?o`&yw;C%dh>m+*V5ZLJjUCuv3#g~IUJ?`lxX`!sg3TX zB=Lxh7D+a)GtgpL38PAal{D%hX!`w@<-f2lQOoSBTb4uj7*k6ykBf{edCc2Gl24*# zKHUcG%UCp84T>a*llLw2cRjalr*{P#A4A~iUyUtjLFd^InpcOdwZf*^9r8dliYrQS zwTSFYBGvYiWZNH7?Jq{|B|VplI*L%<{(N$`kh zm^w&T7Da-)!~g5-E(+ zD`mPn=2kWrcg(rcaGVQ-N^7scx^0Z`+YIyB_ca|r-DA6V?7N0_bDN9WySKKpKU=>D zkDQeDJ85e(s$_NfGEeFB%RFL0M@`VxiteZ+BKCs7g?UwLhRw8^&(}CRXWD96Z}VJW z?X~(BH+v-<$-u@t%9k?BoZfI1g%zE2525{hzV;ub{AT~GeUH_Xr^FZE;N`S{c@t;! zY*-cJ9m@)bP2}c`QF{t!fJ`V#ZSAUmGB@0K>h4{DUeh{7s+>y`_Z9}2lWa>pMclPI zsl$gK)%R}dT)FIpkA8isv#PJeEzZ}qK(%8`WL6PNz}I6Ki}8INt-$POMB?5eHgQj( zc%)8`K;)HZ5A+X31yN;!C$=3eT$%UC*w}a{yQ{0IWmOLyI7}-lZpLh4xHNoAS>1!0?|#x!C-DX@i){#Ac&3w zfj~T_EPgq91lro&ja(~Gws`;g6iDqL25~qkkVJ#@BltHWQlqU3f-J-Kps@8V1}>L* zh)#5xuCzx0G-Vm%1{!bfliQ#GF=H-x2Suid8Mq`eaPaLf8$zLrm|h!1XK_FjKBxzU zddUV(A_qwoI~${-(IV&;nqP~*yDbq3Qj(LCX&xXR4yr>#12r}(M#7Ph|F;+fA}vU+ zdGXI}&5b&ZS>@2>P>wSo%P0}{SuNm+cBWDV{jya3FLJK^5c7l1}Dl)Df$kJ{!bRQq-%f7okM4v`ud zqUW`gie##3Gvixo;6<5v40Lx(JGg~ihs;D!`EC%(t1WqwM*@y`ku%;TB1&YwDIH(9 z(8*M^+RLrZ7oCJO^xCvR(;?=1Cu-R3Fqqe)N%OX6f=TS!wRT_geMCl7K;yzE)s!Tl z5%lUwqzR5h%$Z0~ue{?G2L_o9sW70)ea9PCI_0?pfJP1S!0Ttw^JPF;5aOMXsp1l3 z-6MW!O;Psvcct-32q-F4jrCk#p3-Q;2$Rv_t`=^h6=!zf>z_2r>S^t0%`zS+n_Zu7Jmkh-{2O=`=^73YrYvjFvdV2iYQP% zJt!xa&=tEF!ex0#j2U7+kqFtjzb_vQ%fG+5IRj5Qq~r6k4mkn1*f7XNuX(VKK;hxG*N)7`)@-#!K}z~4=-T-ugj+p5Adz%yLf zQm~lvRn>jn?@976Sl;)vJNUoP zzFrj^D7P%YQzhaFAnfklc9LmJ*GPmYv-Yi)hnc#5*C6W%m2XQ+@)CM&;b!2ODnq?h z*}?U5d)TPu$lV2>w$h@Qb3K+R8y@2rn7X%jS>>|+I>5p`vE&jn=#M%HT zmI7F%qO`Q~+WYO}FCQxl7zd|h-V**- z74rrv3(#3|zqx3~Ct&oQ(Tnr*2|ylM*xGjOZ8HGt@dN+A;^cg(<6B|xf8@`>sz@CG zEJK?wfO zRNRqGckEW)zU8(-)PGdXQ?*FI)I%f!(0v84mhgASS|D>;KqR6{gzv_Oo3JPa6Ei-? zCcj=+m0m#D2KzdH);Cv1*_c%`I6}rW273~Ib*Fw)Y@L46ZwFWx-=#1&rwzEk zZL~%+R`Ng!U25Jgcz+1$L}&&U_A8Ln`$0$rq06)X&2`9CQb431gxud>LBOWY_G2bN z?I(g_zJA~&c+c#g+}c^?Q#}-jl5Q9WHJR%V~hOe{N1LUbnImGSN zdD28>7iK2$@rlt$QcpRtJcIHnBW5iM=Wlt#u_QI!*DunlV4&)91?YT~cLEF>@gc;| zgV5X{TAoBrvYV4b(H{gR!9q|qeuugUitEOfN3LNhA#$IhR`^${tG`y(DtKKgf+K7f zJ<{giBwwH|=ID;&6BEL8A9;($_3r4Zt5!Ocml3PX5GbEg<{RwGbdgLKUZ*Dtdyej-jmy-92oio>;*Bi{R@k8kiX2`Z(k6lw@WLdbQEzBxzWZyEc1(-Ll+qS&gR9cidwID<_f)+MD96KZ@P_@mb1d|DTm&! z5YU!{LBEpE&PDYZ_k^7optUlwP4p5{Ogx#F>eD#;_2a1fuQr!N~7Z!dEwqRI)!z$oIHy$=&gPop}afaM9td| z&5$d#rAZaD-c@V6=RDs&UqSPGo!S|2(ZRU2)^e;-qdiwK?j5o0*>tlf98p>nqbFuU~{`7T2-&7S!% zd9^$x{vbTFb>XHSn;6mfN1CorYhRD{4?DXE56-jp6S48HWjfW>S2=%TyC&J#38~L? zv$JzU*A#>wn7*NXyJ#h+5TX0?gDAw(IDsNXSn&F4CjGH;Q$*5u2tGo&#x#Ey)$k^- zszJ17*VTGF!)+fxrjnm^eA*A<_qu2Q^BPo!z6T)-WFpH=PQGM4pY8^l){>n2!)4zZ z2#0BnGLR^7PFDm)!k{-ml~}^JOllZ{z&QcvPB5e_-qM!71t{IEpInw4Fg%B=6*r*T zwv7q9W(`2pZV0a3w1^c$kSOegOEwLG4x8BJvGzOEksVYHdfqIx^6xJk-KS=g1QCWR@3vP> zO--XyK|BvN>e*v zpxm+M&Q+k@LH`r7U7OmlC`Qz8di{G`tipOpG1xO8XLRO<@Q09{L%!54Wf@a#N|^Z} z!>Ou(VaI-kEy#78C>qHVV!=?|J6*%Yv#0O^5_qL=xvopR(6!7n2d#{)5SGf{=qmyG zGu2Azi8`*K%FTNlD9Y|oA^ly?s!Rl|b8%0F5b6U2u4esz!>|0iVPG1gcKoOGb#oLB zg9$Y`j|6}Hx3Sl;e`edszx~?a4O{cRqMosF!Buub5c&DCH=$ey;oGf%Bt1yhjwB5q z9GMfVcSH?4=&;LPc$+viaqC&T!$OIhBT1<=`e|{HYH&57Cxb}JrmyhV*a0rJ%A|Xd zj78(wr3P|P`(%yieQ|@EX;Xz4--)-hWn_F z27n&Xoc$NSnp&wb9j0$Z>Z;aYZ&M2bdC6mB%aOo>fx2^`r~z6NWHw1i^OPQt9_J4 zziyw&trFpz{R8p~HUh>O->m8leTgPQ+3@Kijwe)3OTK!+3rA|KATTZ(i|3n<`!(gd ztnksP)6e%f%@f&#P+a3%y^bUMNG>U<-hMfup@rP~Bi7X5@ZtdX1|JIEqoTw}^YE)1 z7ijg>r!0lL1isw*bGJPH=L1A-#-o&BAD0Vp{&#W>_pyEBI4#<>`FfEBcVAVA-atf;*lf*(X{@J=csj#+nY zboz`9T{%9GH~opJ9O}t%Ck@bNh+ouH{+Yiy3U7XP#2R#gNlSz?81YPa28ibcHwdHO zFLX5W2{?)n__uU#5U5?V-vBSzZ3~Os1+~jSeBhgPUeJsAe%H5OS1rqI_Irq~+n4o2 zExx@rCK_mNqQ|!q&B1=CiVF;F-SQXp36MtJp)w2?5E+vj70w4B)&eXikz@*35n*%B zCrzlO=Tg48nPk1s&K3*G4De*Qcx|mkw(+Nu+;C_4GElYcZUZIA&mW)c^8(%C5VnV? z70L*N<`e7R-8S&o&z6=>^&WB=?jkVPn><_Uw6*)p%t7ulnxTST=#_N-F4%i{6cgkv z;bId!%V{H5G<`XH_<#C3rzUjl2!KpTsHS6UYumWVh~{8Vwn5$8NKoLQf&$INB8ijC zU@&qQSqR+P?MoQy7+(B0{Bs5ml|(P#LGCW6dZyLLw+kC}gzWA9t;cEcT~N6{M-4P7 zr^GIJX@g?xS}(|tnWmBVzLBC11X>5G7XY1M)yRMnW^ajY3aBQS0&}NDffAQfI@xCp zkSw?lNU{+$660IDG(DK4Q5!h)Gej)_V>>e4m9LE9`(n~@sMT&OtB>jz*6Svc6FWOQ z>;C@NuHozYFj!44@R%Uc)z>Z7v)obSskRKObnpMet4#oBKG+LN4d2AXZ&GHfSNd2y z=pkP(yFqb7jUE8?0#8yK)W$3g+L+65jr9;>eE)i!xdl+Af9Q|%zWp8!fJ{&+E1mI( zkB{%r@?8(umIjj6yXPa0Pn;QxcLpq7eb)k>NK(L^fR}vx7);m08nm}7Ln+>#s!k~x zHmSBp`0k!!C;Rvdb_aLTwNNGDFX^x3Drf~|cate>RrOVuf>Z;LHH|I1(rdoFMK7LI z@b#2%mcuKCv-$n!2~y$3$o}qQ$%*!&fy%~ouibhiJ2qZJk#skx!0ENwHBueZXTUj{ ztB~l`ka~S5y(YPObC6(f1)7vR$OYBj$1XHEMsxe!=wBJA}H%=>p8Q43bNsJf?hByFOwMU@3(;J`@u3$ieJcjp?3|l zo90osND+naE#zh#*r*@KT`V0t5yyK}-xKvme_n%GctHK*GGN()v#w<7oS2gf?I#ik z=&5|tOMP=TaX5dtBNP|Z*%PzWGtgHVUdS?PtmYkbx^;S?w9s6m6GgsS_d{HCg<;AK zc|Ur5yz`hdYJcnOnKALgp^GA=IN_bQcM-CjF~z(saY41H;>Hv1baq_ytJLeeSb>L- zkq-Fc_}8dHRyAJ7t*f7Y0~o#3%9$%KlgAbGHjR3ntZ6SpPW6+*F4d=0wItDv{;Z5d zy8G?UmP&ZElOT)huiG>CVjr)sWZS+T?MKEvhQapEGq1o zbGfUjxkdX?N`6pUD2}i1g8N)n>+9F#B{ye&%$$_i^-4Upf=*BNh_RHVq6@s;FP)&^ z4FefCD`PY_%@Rp&xqr;u_C#uB&_H1BV^ncOBcY|bNzTu806ZKHb$r=3FY@T&9-%(N zbj$7iPN1l4VX>*~AqtM(wFUOKUWFt6Faz}45=lwqYXgD$*|;V-Z>XvAqf{~ctA|Lz^VCZXfJz;$8we-jT> z4_=qq0t@^OTyF43K=0p51Nd`O6(rdAmS?~r*!$o>c_^^xLmg88umg9b-}pIItFV6t R3<7rHoQ*~0?>GPTKL96*zs~>w diff --git a/tests/test_features.py b/tests/test_features.py index 995308ff13..11ec8d19e3 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -428,9 +428,15 @@ def test_tonnetz_audio(y_ex): assert tonnetz.shape[0] == 6 +@pytest.mark.xfail(raises=librosa.ParameterError) +def test_chroma_cqt_badcombo(y_ex): + y, sr = y_ex + librosa.feature.chroma_cqt(y=y, sr=sr, n_chroma=24, bins_per_octave=36) + + def test_tonnetz_cqt(y_ex): y, sr = y_ex - chroma_cqt = librosa.feature.chroma_cqt(y=y, sr=sr, n_chroma=24) + chroma_cqt = librosa.feature.chroma_cqt(y=y, sr=sr, n_chroma=36) tonnetz = librosa.feature.tonnetz(chroma=chroma_cqt, sr=sr) assert tonnetz.shape[1] == chroma_cqt.shape[1] assert tonnetz.shape[0] == 6