From 726fa1365b0bf8dc3417e4207d18ccac694617df Mon Sep 17 00:00:00 2001 From: Maxime LE COZ Date: Fri, 20 Dec 2013 10:41:07 +0100 Subject: [PATCH] IRIT music detector and divergence segmentation added --- timeside/analyzer/__init__.py | 15 ++-- timeside/analyzer/irit_diverg.py | 52 ++++++------ timeside/analyzer/irit_music_.py | 116 ++++++++++++++++++++++++++ timeside/analyzer/irit_music_SLN.py | 117 ++++++++++++++++++++++++++ timeside/analyzer/irit_music_SLN.pyc | Bin 0 -> 3859 bytes timeside/analyzer/irit_music_SNB.py | 118 +++++++++++++++++++++++++++ timeside/analyzer/irit_music_SNB.pyc | Bin 0 -> 3935 bytes 7 files changed, 383 insertions(+), 35 deletions(-) create mode 100644 timeside/analyzer/irit_music_.py create mode 100644 timeside/analyzer/irit_music_SLN.py create mode 100644 timeside/analyzer/irit_music_SLN.pyc create mode 100644 timeside/analyzer/irit_music_SNB.py create mode 100644 timeside/analyzer/irit_music_SNB.pyc diff --git a/timeside/analyzer/__init__.py b/timeside/analyzer/__init__.py index e7a0150..6c8a2fc 100644 --- a/timeside/analyzer/__init__.py +++ b/timeside/analyzer/__init__.py @@ -2,18 +2,19 @@ from level import * from dc import * -from aubio_temporal import * -from aubio_pitch import * -from aubio_mfcc import * -from aubio_melenergy import * -from aubio_specdesc import * +#~ from aubio_temporal import * +#~ from aubio_pitch import * +#~ from aubio_mfcc import * +#~ from aubio_melenergy import * +#~ from aubio_specdesc import * from yaafe import * # TF : add Yaafe analyzer from spectrogram import Spectrogram from waveform import Waveform from vamp_plugin import VampSimpleHost from irit_speech_entropy import IRITSpeechEntropy from irit_speech_4hz import IRITSpeech4Hz -from irit_monopoly import IRITMonopoly from irit_diverg import IRITDiverg - +from irit_music_SLN import IRITMusicSLN +from irit_music_SNB import IRITMusicSNB +#~ from irit_monopoly import IRITMonopoly from odf import OnsetDetectionFunction diff --git a/timeside/analyzer/irit_diverg.py b/timeside/analyzer/irit_diverg.py index c52ff22..e15d2fe 100644 --- a/timeside/analyzer/irit_diverg.py +++ b/timeside/analyzer/irit_diverg.py @@ -237,7 +237,7 @@ def calculDistance(modeleLong,modeleCourt): return (2*modeleCourt.erreur_residuelle*modeleLong.erreur_residuelle/modeleLong.variance_erreur_residuelle-(1.0+QV)*modeleLong.erreur_residuelle**2/modeleLong.variance_erreur_residuelle+QV-1.0)/(2.0*QV) -def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True,seuil_vois=None,withTrace = False): +def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True): ''' Fonction principale de segmentation. @@ -249,9 +249,6 @@ def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True,se - lamb (float) : valeur de lambda pour la détection de chute de Wn. Par défaut = 40.0 - biais (float) : valeur du bias à appliquer (en négatif). Par défaut = -0.2 - with_backward (Bool) : Interrupteur du calcul ou non en backward. Par défaut = True - - seuil_vois (float) : Si fixé, défini les valeurs de lambda et du biais en fonction du voisement ou non du buffer initial du model court terme. - (voisement_yin > seuil_vois ==> Non voisé). Par défaut = None - - withTrace (Bool) : Enregistre ou non la trace de tous les calculs pour un affichage à postériori. Par défaut = False ''' # Initialisation @@ -306,10 +303,6 @@ def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True,se # Recherche de nouveau maximum if Wn > maxi[0] : maxi = (Wn,t) - - if withTrace : - dynaWn += [Wn] - tWn += [t] # Recherche de rupture par chute superieure à lambda if (maxi[0] - Wn) > lamb : @@ -325,30 +318,25 @@ def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True,se # Si une rupture à été detecté avec un modèle stable (Wn à croit) if t_rupt > -1 : - m = 'forward' + m = 1 if with_backward : bdata = data[t_rupt:rupt_last:-1] if len(bdata) > 0 : - front = segment(bdata,fe,ordre,float(Lmin)/fe,lamb,biais,with_backward=False,seuil_vois=seuil_vois,withTrace=withTrace) + front = segment(bdata,fe,ordre,float(Lmin)/fe,lamb,biais,with_backward=False) t_bs = [ t_rupt-tr for tr,_ in front] if len(t_bs) > 0 : t_rupt = t_bs[-1] - m ='backward' + m =-1 # Sinon on crée un segment de longueur minimale else : t_rupt = rupt_last+Lmin - m = 'instable' + m = 0 - if withTrace : - evnt['selected'] = t_rupt - evnt['comment'] = m - trace+=[evnt] - # Mise à jour des frontières t = t_rupt rupt_last = t_rupt @@ -363,7 +351,10 @@ def segment(data,fe,ordre=2,Lmin=0.02,lamb=40.0,biais=-0.2,with_backward=True,se class IRITDiverg(Analyzer): implements(IAnalyzer) ''' + + ''' + def __init__(self, blocksize=1024, stepsize=None) : super(IRITDiverg, self).__init__(); self.parents.append(Waveform()) @@ -372,11 +363,8 @@ class IRITDiverg(Analyzer): @interfacedoc def setup(self, channels=None, samplerate=None,blocksize=None, totalframes=None): - super(IRITDiverg, self).setup(channels, - samplerate, - blocksize, - totalframes) - self.parents.append(Waveform()) + super(IRITDiverg, self).setup(channels,samplerate,blocksize,totalframes) + @staticmethod @interfacedoc def id(): @@ -402,10 +390,18 @@ class IRITDiverg(Analyzer): return frames, eod def post_process(self): - data = self.process_pipe.results['waveform_analyzer'].data + data = list(self.process_pipe.results['waveform_analyzer'].data) frontieres = segment(data,self.samplerate(),self.ordre) - f = open('front.lab','w') - for t,m in frontieres : - f.write('%f\t%s\n'%(float(t)/self.samplerate(),m)); - f.close() - print frontieres + + + segs = self.new_result(data_mode='label', time_mode='event') + segs.id_metadata.id += '.' + 'segments' + segs.id_metadata.name += ' ' + 'Segments' + + label = {0: 'Instable', 1: 'Forward', -1: 'Backward'} + segs.label_metadata.label = label + + segs.data_object.label = [s[1] for s in frontieres] + segs.data_object.time = [(float(s[0]) / self.samplerate()) for s in frontieres] + self.process_pipe.results.add(segs) + return diff --git a/timeside/analyzer/irit_music_.py b/timeside/analyzer/irit_music_.py new file mode 100644 index 0000000..0c20b16 --- /dev/null +++ b/timeside/analyzer/irit_music_.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Maxime Le Coz + +# This file is part of TimeSide. + +# TimeSide 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. + +# TimeSide 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 TimeSide. If not, see . + +# Author: Maxime Le Coz + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.analyzer.utils import melFilterBank, computeModulation +from timeside.analyzer.utils import segmentFromValues +from timeside.analyzer import IRITDiverg +from timeside.api import IAnalyzer +from numpy import logical_and,array, hamming, dot, mean, float, arange, nonzero +from numpy.fft import rfft +from scipy.signal import firwin, lfilter +from pylab import plot,show +class IRITMusicLDN(Analyzer): + implements(IAnalyzer) + + def __init__(self, blocksize=1024, stepsize=None) : + super(IRITMusicLDN, self).__init__(); + self.parents.append(IRITDiverg()) + self.wLen = 1.0 + self.wStep = 0.1 + self.threshold = 20 + + @staticmethod + @interfacedoc + def id(): + return "irit_music_sln" + + @staticmethod + @interfacedoc + def name(): + return "IRIT Music Detector - Segment Length" + + @staticmethod + @interfacedoc + def unit(): + return "" + + def __str__(self): + return "Music confidence indexes" + + def process(self, frames, eod=False): + + return frames,eod + + + def post_process(self): + ''' + + ''' + + segList = self.process_pipe.results['irit_diverg.segments'].time + w = self.wLen/ 2; + end = segList[-1] + tLine = arange(0,end,self.wStep) + + segLen = array([0]*len(tLine)) + + for i,t in enumerate(tLine): + idx = nonzero(logical_and(segList>(t-w) ,segList<(t+w)))[0] + segLen[i]= len(idx) + + + plot(tLine,segLen) + show() + # Confidence Index + conf = array(segLen - self.threshold) / self.threshold + conf[conf > 1] = 1 + + segLenRes = self.new_result(data_mode='value', time_mode='framewise') + segLenRes.id_metadata.id += '.' + 'energy_confidence' + segLenRes.id_metadata.name += ' ' + 'Energy Confidence' + + segLenRes.data_object.value = segLen + + self.process_pipe.results.add(segLenRes) + + # Segment + convert = {False: 0, True: 1} + label = {0: 'nonMusic', 1: 'Music'} + + segList = segmentFromValues(segLen > self.threshold) + # Hint : Median filtering could imrove smoothness of the result + # from scipy.signal import medfilt + # segList = segmentFromValues(medfilt(modEnergyValue > self.threshold, 31)) + + segs = self.new_result(data_mode='label', time_mode='segment') + segs.id_metadata.id += '.' + 'segments' + segs.id_metadata.name += ' ' + 'Segments' + + segs.label_metadata.label = label + + segs.data_object.label = [convert[s[2]] for s in segList] + segs.data_object.time = [tLine[s[0]] for s in segList] + segs.data_object.duration = [tLine[s[1]]-tLine[s[0]] for s in segList] + + self.process_pipe.results.add(segs) + return diff --git a/timeside/analyzer/irit_music_SLN.py b/timeside/analyzer/irit_music_SLN.py new file mode 100644 index 0000000..3a7a638 --- /dev/null +++ b/timeside/analyzer/irit_music_SLN.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Maxime Le Coz + +# This file is part of TimeSide. + +# TimeSide 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. + +# TimeSide 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 TimeSide. If not, see . + +# Author: Maxime Le Coz + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.analyzer.utils import melFilterBank, computeModulation +from timeside.analyzer.utils import segmentFromValues +from timeside.analyzer import IRITDiverg +from timeside.api import IAnalyzer +from numpy import logical_and,array, hamming, dot, mean, float, arange, nonzero +from numpy.fft import rfft +from scipy.signal import firwin, lfilter +from pylab import plot,show + +class IRITMusicSLN(Analyzer): + implements(IAnalyzer) + + def __init__(self, blocksize=1024, stepsize=None) : + super(IRITMusicSLN, self).__init__(); + self.parents.append(IRITDiverg()) + self.wLen = 1.0 + self.wStep = 0.1 + self.threshold = 20 + + @staticmethod + @interfacedoc + def id(): + return "irit_music_sln" + + @staticmethod + @interfacedoc + def name(): + return "IRIT Music Detector - Segment Length" + + @staticmethod + @interfacedoc + def unit(): + return "" + + def __str__(self): + return "Music confidence indexes" + + def process(self, frames, eod=False): + + return frames,eod + + + def post_process(self): + ''' + + ''' + + segList = self.process_pipe.results['irit_diverg.segments'].time + w = self.wLen/ 2; + end = segList[-1] + tLine = arange(0,end,self.wStep) + + segLen = array([0]*len(tLine)) + + for i,t in enumerate(tLine): + idx = nonzero(logical_and(segList>(t-w) ,segList<(t+w)))[0] + segLen[i]= len(idx) + + + plot(tLine,segLen) + show() + # Confidence Index + conf = array(segLen - self.threshold) / self.threshold + conf[conf > 1] = 1 + + segLenRes = self.new_result(data_mode='value', time_mode='framewise') + segLenRes.id_metadata.id += '.' + 'energy_confidence' + segLenRes.id_metadata.name += ' ' + 'Energy Confidence' + + segLenRes.data_object.value = segLen + + self.process_pipe.results.add(segLenRes) + + # Segment + convert = {False: 0, True: 1} + label = {0: 'nonMusic', 1: 'Music'} + + segList = segmentFromValues(segLen > self.threshold) + # Hint : Median filtering could imrove smoothness of the result + # from scipy.signal import medfilt + # segList = segmentFromValues(medfilt(modEnergyValue > self.threshold, 31)) + + segs = self.new_result(data_mode='label', time_mode='segment') + segs.id_metadata.id += '.' + 'segments' + segs.id_metadata.name += ' ' + 'Segments' + + segs.label_metadata.label = label + + segs.data_object.label = [convert[s[2]] for s in segList] + segs.data_object.time = [tLine[s[0]] for s in segList] + segs.data_object.duration = [tLine[s[1]]-tLine[s[0]] for s in segList] + + self.process_pipe.results.add(segs) + return diff --git a/timeside/analyzer/irit_music_SLN.pyc b/timeside/analyzer/irit_music_SLN.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e54b8d0c04f778c4d5ad553d63bcb423b41b2c55 GIT binary patch literal 3859 zcmbVP>uwvz6+XMXNTNkusS6dmVcP^DT0|*Y)CB^!PHo440Y@lSL4z)uF4o*3wensV z?l7_fv48BBXrH5h`Xqgg0{vUG{k}7#s5AkphUAWB&YYR^?YXP=k9O;$_Fsa5%s(~! z{RmI>PYeluo@64qC!r^~FQG3UslH@2$?Fo<-MS`OL-MACP03dzTyfO8QdcEhb?b&? zYm&DlY`Jw)vOAKmOStaVE0VP(-;i)a@=XahCEt>8OY&_AwW9!Xrm*N%ofHcO|?l`8^5mfqF+wU51_{>+(kA zwaD+n?@7{@)BXk&kp3H=zK@x0ahZ=Zlbga;cAd+@n#nL8n4}!^Ju?)a13dFfDxW4P37wrYUBH&PP*u{K2#4#M%ww+JacW zL2OCV;4*~C*ClDn8ys&-vZBNd2~PV|Xet0dpoSLDW1{;QgT>F2E6Dcc+^4g6Se)17 zye?27){0qKq9$=-I|AUEPF5LDr+r*6CnF5_{QcqR&wt|QuMg7>)`~!_QBB8Y66}Gd zP8CIIk=iIyRgdF|`k>lsJT^rVaH{6Kc#bVv{o>S`vE>`tvx$KXvZT*9Qm)EmL*09k zm4la6`jt^B6^?Mx2cPIr{g6k}+*D~|K8WY8{Xsebn4G4JV9Vq2WpDt#ckonnRPlE4 zUBh?46+aA*$9O8fJ`8%Qj&fo(%9giPS+NwpoZS|NWvHgfk12>#sr@ei6+zQ6d+M3J zC&rqAEhoMAd#5fEd(h~}o-M&FXSasMmoDPm`~-h*2@K9Qx4}}L&i*fgAH zR8aJxD}<3RLm!?(z$Z>z4O5k_iE223PAg4krcAygpz)*}n5z0EZgznD0R?X2AzlS^ z251a*{7@=9=_34R8s~_9#QEL~v5uXZbZ0e&SW=fOjrw!sg__KeAdnF1GV%l=d{t+r z=-`<(kaYuUc`7`-Fu5QX~MG&Bmt?I3-y!EOf zS50~Fg^|xt4R9?8$X6aR@N3u}Fvx)yY0Dwmb+$KKV|Kh+;|~*vdcd6x=1FA81i5Ps z=lvM~wd8zL>gQXM?8z_uXzQA?Ey+G9+bRNVI}+_K^>*GNCuDl5PuO-(?n9Q{m;19j zLK?)?LE9vR`?Gbi2TQPkgrjq%J6u2jT-%DPEm!SpTnBSpa|&=B&T*|P+o42VK?;UC zpM2ht*@j#}Rk+lbZubwqeDODrw*mftvq7B-(y{$(!RBAz*3;V@TLZVz0P_=vy!l;! ze)G}S71LJ5BptYWm!8NFXtjFtPsQS(NjK5@e?0FJDrgNG>{3!pB&FwSVozclM|qhT zEe2n*lW@fVx!loN8Z>||(#oK&i5zQhVhS`Em(eo*b+1>kIr&)kdXKMjgO&|637Ruh z)N+=^FHEKdYmU~{7?)KmUCdK#byDq6{x?J`x%UmJ6|rCIKv#=wUG?9>p0*qFz>dc0 z*fldKgVPMX8?!NMT5yC(zzP)bP@u(<874yw<-!!x+)QF?0%n6?mBTIvnj^T2LEFN@ zTtx0}?Z46_%8iX_f#CaO`~ex+nW<6A7w4!j!H-C!usCsT+S53L9f?|Sch zom$&l@sGSM|Ipj z5BM_rq9NpWn7WwP{ub zbiII9be_iOz815%%C(iW)N*T_I{nc<5gus$=!_aW0hJxl(F2|d2;GGjT~s|vPaNZa zh+vB(vQ>k0e0f}@BQ%!*VFWwe;{D@GIRA}^V>|pEHLhOq!1=+is5eci``(tvhu;r% HzVH7B8easj literal 0 HcmV?d00001 diff --git a/timeside/analyzer/irit_music_SNB.py b/timeside/analyzer/irit_music_SNB.py new file mode 100644 index 0000000..15dce01 --- /dev/null +++ b/timeside/analyzer/irit_music_SNB.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013 Maxime Le Coz + +# This file is part of TimeSide. + +# TimeSide 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. + +# TimeSide 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 TimeSide. If not, see . + +# Author: Maxime Le Coz + +from timeside.core import implements, interfacedoc +from timeside.analyzer.core import Analyzer +from timeside.analyzer.utils import melFilterBank, computeModulation +from timeside.analyzer.utils import segmentFromValues +from timeside.analyzer import IRITDiverg +from timeside.api import IAnalyzer +from numpy import logical_and,array, hamming, dot, mean, float, arange, nonzero +from numpy.fft import rfft +from scipy.signal import firwin, lfilter +from pylab import plot,show + +class IRITMusicSNB(Analyzer): + implements(IAnalyzer) + + def __init__(self, blocksize=1024, stepsize=None) : + super(IRITMusicSNB, self).__init__(); + self.parents.append(IRITDiverg()) + self.wLen = 1.0 + self.wStep = 0.1 + self.threshold = 20 + + @staticmethod + @interfacedoc + def id(): + return "irit_music_snb" + + @staticmethod + @interfacedoc + def name(): + return "IRIT Music Detector - Segment Number" + + @staticmethod + @interfacedoc + def unit(): + return "" + + def __str__(self): + return "Music confidence indexes" + + def process(self, frames, eod=False): + + return frames,eod + + + def post_process(self): + ''' + + ''' + + segList = self.process_pipe.results['irit_diverg.segments'].time + w = self.wLen/ 2; + end = segList[-1] + tLine = arange(0,end,self.wStep) + + segLen = array([0]*len(tLine)) + + for i,t in enumerate(tLine): + idx = nonzero(logical_and(segList>(t-w) ,segList<(t+w)))[0] + l = [tLine[t1]-tLine[t2] for t1,t2 in zip()] + segLen[i]= len(idx) + + + plot(tLine,segLen) + show() + # Confidence Index + conf = array(segLen - self.threshold) / self.threshold + conf[conf > 1] = 1 + + segLenRes = self.new_result(data_mode='value', time_mode='framewise') + segLenRes.id_metadata.id += '.' + 'energy_confidence' + segLenRes.id_metadata.name += ' ' + 'Energy Confidence' + + segLenRes.data_object.value = segLen + + self.process_pipe.results.add(segLenRes) + + # Segment + convert = {False: 0, True: 1} + label = {0: 'nonMusic', 1: 'Music'} + + segList = segmentFromValues(segLen > self.threshold) + # Hint : Median filtering could imrove smoothness of the result + # from scipy.signal import medfilt + # segList = segmentFromValues(medfilt(modEnergyValue > self.threshold, 31)) + + segs = self.new_result(data_mode='label', time_mode='segment') + segs.id_metadata.id += '.' + 'segments' + segs.id_metadata.name += ' ' + 'Segments' + + segs.label_metadata.label = label + + segs.data_object.label = [convert[s[2]] for s in segList] + segs.data_object.time = [tLine[s[0]] for s in segList] + segs.data_object.duration = [tLine[s[1]]-tLine[s[0]] for s in segList] + + self.process_pipe.results.add(segs) + return diff --git a/timeside/analyzer/irit_music_SNB.pyc b/timeside/analyzer/irit_music_SNB.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49f493593e64a46baac9a6cc028cc1466d0f8327 GIT binary patch literal 3935 zcmbVPZEqXL5uQ7~iL@wD5-EwYov=xY5N)HhDe3}&+oZN)AgIF+C!<9Vbq^=rl0Nxf znA>G!8DziM{R;)!U(g@WFa1{q`n5p&%_>y+H0bd&r|3pXffQ@Ta|7Ny(dZ&TVKzeDK``8$;ElD|vo9{GEecFFHj z+9SVbcWzL!Pw4^q2NrHpa!6^P{63{eyZPII^ zS46+_-=e5Zr^78MApQ@Yp^KSmF__L0m8#5Wv&mp)R51z1D$2)0hlw^Y{3;8R%a^LK z+uFdSDtQzqpnVW#&&>`n<2;?sjrt;w=1FMcJhR(*NvkPycvR%+Z^C4*v|VEMpL?G_ zIf=hg#nj@OjJsmTyv;2PNj{CoVG@K{WNLuJq6jZd1G8sgn#S4Gu#uzOm@0l#6=p&< zN%GK`x@-uusgfO8p5ZFF<;Ud3WMb^im~s*q7tjKqolNA4c8w`#Ny$M!%P&M0=#{fFEqw4bT(3r|PO8Dq$EI@l3(qQ%m21f63R%5| zY*AEW(1*!4DXPtFc!$0u*kUz{2>GkhcStWdB)5)JuALZwwQk+riV zA3xXeOC?-1wlFXR9m$~I39ihr{C`Ud`8sK~Kd|Mnhio(KYMiXixy_lb#|t6*bL zgkm3BAdFm^xbPGfcx2gCFct2Ss3rw;swJJNJo=6T&5C@iwEhK7wgS0hX1I%&MtuCVo{YA67}cE3l&--K_DSiY3dL{cv0m{ z(Z##0(ThKEh+EXj(+m1Nj;_Esbk|xw?=dBKIWExBB3FkNoh!KxO1Vl5BHY0c%+ApSSW zpiYp%KtlGT6`6Z=+CWaTWHpen0m>a>tkcz&+h12qM-&rv5gdK?TMn33YfwI#ONpfD zNVWEuIUBgxf`|6{;Z7v2-#j zLpyRAth0rz4K%khpUK+b;Z-J)B7>GfGK{cV&yw&=B~so>X>CQaIamu&CS*O*9Gu5* zuom>-4X&DHztVv~tK6=Kcd#bSNtp(MSv<3?4~phIL8r&Ln=7Dqgo(%X%Dc^jlvd71 z2`VlFDx0UO2#xZ#d3^(eq^6hg%-drOo5d4`Y_CjR1eS+%ZE+N&%7knQ?|V%64hC5f z>kbII8%~snFd#7+s85*A&jFJQg+Qx?9WHCmWWL37NQ%w*F&;qZa|}p zHJ(VtvSV4xwz9|ob@Qsit@te0CMZq#fH&XgBWin2+i5vHx9|3y51eDCTWLED_sHqE zht96E>u$Pxm9Eoud$kq(zVp8GQ+Ll94cUmIvLN8njoyo`7H~Ms6M!Cb@c24%)lr@) z>4LpYQn|kjt_w)$hQMev}FWEYjl@>tm(|DEx8kH@0h;1j!WyvdGZ{2%c;fUMfb z@$B+g$5S+_9*g0zn@gsdT_Pl|#UZ!W-%;b@75A+l{F3!133uP=IQ;VGW0~)}{{_q` B5aR#< literal 0 HcmV?d00001 -- 2.39.5