fazekasgy@9
|
1 '''PySpectralFeatures.py - Example plugin demonstrates'''
|
fazekasgy@9
|
2 '''how to use the NumPy array interface and write Matlab style code.'''
|
fazekasgy@9
|
3
|
fazekasgy@9
|
4 from numpy import *
|
fazekasgy@9
|
5
|
fazekasgy@9
|
6 class PySpectralFeatures:
|
fazekasgy@9
|
7
|
fazekasgy@9
|
8 def __init__(self):
|
fazekasgy@9
|
9 self.m_imputSampleRate = 0.0
|
fazekasgy@9
|
10 self.m_stepSize = 0
|
fazekasgy@9
|
11 self.m_blockSize = 0
|
fazekasgy@9
|
12 self.m_channels = 0
|
fazekasgy@9
|
13 self.threshold = 0.00
|
fazekasgy@9
|
14 self.r = 2.0
|
fazekasgy@9
|
15
|
fazekasgy@9
|
16 def initialise(self,channels,stepSize,blockSize,inputSampleRate):
|
fazekasgy@9
|
17 self.m_channels = channels
|
fazekasgy@9
|
18 self.m_stepSize = stepSize
|
fazekasgy@9
|
19 self.m_blockSize = blockSize
|
fazekasgy@9
|
20 self.m_inputSampleRate = inputSampleRate
|
fazekasgy@9
|
21 #self.prevMag = ones((blockSize/2)-1) / ((blockSize/2)-1)
|
fazekasgy@9
|
22 self.prevMag = zeros((blockSize/2)-1)
|
fazekasgy@9
|
23 self.prevMag[0] = 1
|
fazekasgy@9
|
24
|
fazekasgy@9
|
25 return True
|
fazekasgy@9
|
26
|
fazekasgy@9
|
27 def getMaker(self):
|
fazekasgy@9
|
28 return 'VamPy Example Plugins'
|
fazekasgy@9
|
29
|
fazekasgy@9
|
30 def getName(self):
|
fazekasgy@9
|
31 return 'VamPy Spectral Features'
|
fazekasgy@9
|
32
|
fazekasgy@9
|
33 def getIdentifier(self):
|
fazekasgy@9
|
34 return 'vampy-sf2'
|
fazekasgy@9
|
35
|
fazekasgy@9
|
36 def getDescription(self):
|
fazekasgy@9
|
37 return 'A collection of low-level spectral descriptors.'
|
fazekasgy@9
|
38
|
fazekasgy@9
|
39 def getMaxChannelCount(self):
|
fazekasgy@9
|
40 return 1
|
fazekasgy@9
|
41
|
fazekasgy@9
|
42 def getInputDomain(self):
|
fazekasgy@9
|
43 return 'FrequencyDomain'
|
fazekasgy@9
|
44
|
fazekasgy@9
|
45 def getOutputDescriptors(self):
|
fazekasgy@9
|
46
|
fazekasgy@9
|
47 #descriptors are python dictionaries
|
fazekasgy@9
|
48 #Generic values are the same for all
|
fazekasgy@9
|
49 Generic={
|
fazekasgy@9
|
50 'hasFixedBinCount':True,
|
fazekasgy@9
|
51 'binCount':1,
|
fazekasgy@9
|
52 'hasKnownExtents':False,
|
fazekasgy@9
|
53 'isQuantized':False,
|
fazekasgy@9
|
54 'sampleType':'OneSamplePerStep'
|
fazekasgy@9
|
55 }
|
fazekasgy@9
|
56
|
fazekasgy@9
|
57 #Spectral centroid etc...
|
fazekasgy@9
|
58 SC=Generic.copy()
|
fazekasgy@9
|
59 SC.update({
|
fazekasgy@9
|
60 'identifier':'vampy-sc',
|
fazekasgy@9
|
61 'name':'Spectral Centroid',
|
fazekasgy@9
|
62 'description':'Spectral Centroid (Brightness)',
|
fazekasgy@9
|
63 'unit':'Hz'
|
fazekasgy@9
|
64 })
|
fazekasgy@9
|
65
|
fazekasgy@9
|
66 SCF=Generic.copy()
|
fazekasgy@9
|
67 SCF.update({
|
fazekasgy@9
|
68 'identifier':'vampy-scf',
|
fazekasgy@9
|
69 'name':'Spectral Crest Factor',
|
fazekasgy@9
|
70 'description':'Spectral Crest (Tonality)',
|
fazekasgy@9
|
71 'unit':'v'
|
fazekasgy@9
|
72 })
|
fazekasgy@9
|
73
|
fazekasgy@9
|
74 BW=Generic.copy()
|
fazekasgy@9
|
75 BW.update({
|
fazekasgy@9
|
76 'identifier':'vampy-bw',
|
fazekasgy@9
|
77 'name':'Band Width',
|
fazekasgy@9
|
78 'description':'Spectral Band Width',
|
fazekasgy@9
|
79 'unit':'Hz',
|
fazekasgy@9
|
80 })
|
fazekasgy@9
|
81
|
fazekasgy@9
|
82 SE=Generic.copy()
|
fazekasgy@9
|
83 SE.update({
|
fazekasgy@9
|
84 'identifier':'vampy-se',
|
fazekasgy@9
|
85 'name':'Shannon Entropy',
|
fazekasgy@9
|
86 'description':'Shannon Entropy',
|
fazekasgy@9
|
87 'unit':'',
|
fazekasgy@9
|
88 })
|
fazekasgy@9
|
89
|
fazekasgy@9
|
90 RE=Generic.copy()
|
fazekasgy@9
|
91 RE.update({
|
fazekasgy@9
|
92 'identifier':'vampy-re',
|
fazekasgy@9
|
93 'name':'Renyi Entropy',
|
fazekasgy@9
|
94 'description':'Renyi Entropy',
|
fazekasgy@9
|
95 'unit':'',
|
fazekasgy@9
|
96 })
|
fazekasgy@9
|
97
|
fazekasgy@9
|
98 KL=Generic.copy()
|
fazekasgy@9
|
99 KL.update({
|
fazekasgy@9
|
100 'identifier':'vampy-kl',
|
fazekasgy@9
|
101 'name':'Kullback Leibler divergence',
|
fazekasgy@9
|
102 'description':'KL divergence between successive spectra',
|
fazekasgy@9
|
103 'unit':'',
|
fazekasgy@9
|
104 })
|
fazekasgy@9
|
105
|
fazekasgy@9
|
106 #return a list of dictionaries
|
fazekasgy@9
|
107 return [SC,SCF,BW,SE,RE,KL]
|
fazekasgy@9
|
108
|
fazekasgy@9
|
109 def getParameterDescriptors(self):
|
fazekasgy@9
|
110 threshold={
|
fazekasgy@9
|
111 'identifier':'threshold',
|
fazekasgy@9
|
112 'name':'Noise threshold: ',
|
fazekasgy@9
|
113 'description':'',
|
fazekasgy@9
|
114 'unit':'v',
|
fazekasgy@9
|
115 'minValue':0.0,
|
fazekasgy@9
|
116 'maxValue':0.5,
|
fazekasgy@9
|
117 'defaultValue':0.05,
|
fazekasgy@9
|
118 'isQuantized':False
|
fazekasgy@9
|
119 }
|
fazekasgy@9
|
120
|
fazekasgy@9
|
121 renyicoeff={
|
fazekasgy@9
|
122 'identifier':'r',
|
fazekasgy@9
|
123 'name':'Renyi entropy coeff: ',
|
fazekasgy@9
|
124 'description':'',
|
fazekasgy@9
|
125 'unit':'',
|
fazekasgy@9
|
126 'minValue':0.0,
|
fazekasgy@9
|
127 'maxValue':10.0,
|
fazekasgy@9
|
128 'defaultValue':2,
|
fazekasgy@9
|
129 'isQuantized':False
|
fazekasgy@9
|
130 }
|
fazekasgy@9
|
131
|
fazekasgy@9
|
132 return [threshold,renyicoeff]
|
fazekasgy@9
|
133
|
fazekasgy@9
|
134 def setParameter(self,paramid,newval):
|
fazekasgy@9
|
135 if paramid == 'threshold' :
|
fazekasgy@9
|
136 self.threshold = newval
|
fazekasgy@9
|
137 if paramid == 'r' :
|
fazekasgy@9
|
138 self.r == newval
|
fazekasgy@9
|
139 return
|
fazekasgy@9
|
140
|
fazekasgy@9
|
141 def getParameter(self,paramid):
|
fazekasgy@9
|
142 if paramid == 'threshold' :
|
fazekasgy@9
|
143 return self.threshold
|
fazekasgy@9
|
144 if paramid == 'r':
|
fazekasgy@9
|
145 return float(self.r)
|
fazekasgy@9
|
146 else:
|
fazekasgy@9
|
147 return 0.0
|
fazekasgy@9
|
148
|
fazekasgy@9
|
149 def processN(self,membuffer,samplecount):
|
fazekasgy@9
|
150 fftsize = self.m_blockSize
|
fazekasgy@9
|
151 sampleRate = self.m_inputSampleRate
|
fazekasgy@9
|
152
|
fazekasgy@9
|
153 #for time domain plugins use the following line:
|
fazekasgy@9
|
154 #audioSamples = frombuffer(membuffer[0],float32)
|
fazekasgy@9
|
155 #-1: do till the end, skip DC 2*32bit / 8bit = 8byte
|
fazekasgy@9
|
156 complexSpectrum = frombuffer(membuffer[0],complex64,-1,8)
|
fazekasgy@9
|
157 magnitudeSpectrum = abs(complexSpectrum) / (fftsize/2)
|
fazekasgy@9
|
158 tpower = sum(magnitudeSpectrum)
|
fazekasgy@9
|
159 #phaseSpectrum = angle(complexSpectrum)
|
fazekasgy@9
|
160
|
fazekasgy@9
|
161 freq = array(range(1,len(complexSpectrum)+1)) \
|
fazekasgy@9
|
162 * sampleRate / fftsize
|
fazekasgy@9
|
163
|
fazekasgy@9
|
164 centroid = 0.0
|
fazekasgy@9
|
165 crest = 0.0
|
fazekasgy@9
|
166 bw = 0.0
|
fazekasgy@9
|
167 shannon = 0.0
|
fazekasgy@9
|
168 renyi = 0.0
|
fazekasgy@9
|
169 r = self.r
|
fazekasgy@9
|
170 KLdiv = 0.0
|
fazekasgy@9
|
171 flatness = 0.0
|
fazekasgy@9
|
172 exp=1.0 / (fftsize/2)
|
fazekasgy@9
|
173 #print exp
|
fazekasgy@9
|
174
|
fazekasgy@9
|
175 #declare outputs
|
fazekasgy@9
|
176 output0=[]
|
fazekasgy@9
|
177 output1=[]
|
fazekasgy@9
|
178 output2=[]
|
fazekasgy@9
|
179 output3=[]
|
fazekasgy@9
|
180 output4=[]
|
fazekasgy@9
|
181 output5=[]
|
fazekasgy@9
|
182
|
fazekasgy@9
|
183 if tpower > self.threshold :
|
fazekasgy@9
|
184
|
fazekasgy@9
|
185 centroid = sum(freq * magnitudeSpectrum) / tpower
|
fazekasgy@9
|
186 crest = max(magnitudeSpectrum) / tpower
|
fazekasgy@9
|
187 bw = sum( abs(freq - centroid) * magnitudeSpectrum ) / tpower
|
fazekasgy@9
|
188 #flatness = prod(abs(complexSpectrum))
|
fazekasgy@9
|
189 #print flatness
|
fazekasgy@9
|
190 normMag = magnitudeSpectrum / tpower #make it sum to 1
|
fazekasgy@9
|
191 shannon = - sum( normMag * log2(normMag) )
|
fazekasgy@9
|
192 renyi = (1/1-r) * log10( sum( power(normMag,r)))
|
fazekasgy@9
|
193 KLdiv = sum( normMag * log2(normMag / self.prevMag) )
|
fazekasgy@9
|
194 self.prevMag = normMag
|
fazekasgy@9
|
195
|
fazekasgy@9
|
196 output0.append({
|
fazekasgy@9
|
197 'hasTimestamp':False,
|
fazekasgy@9
|
198 'values':[float(centroid)],
|
fazekasgy@9
|
199 #'label':str(centroid)
|
fazekasgy@9
|
200 })
|
fazekasgy@9
|
201
|
fazekasgy@9
|
202 output1.append({
|
fazekasgy@9
|
203 'hasTimestamp':False,
|
fazekasgy@9
|
204 'values':[float(crest)],
|
fazekasgy@9
|
205 #'label':str(crest)
|
fazekasgy@9
|
206 })
|
fazekasgy@9
|
207
|
fazekasgy@9
|
208 output2.append({
|
fazekasgy@9
|
209 'hasTimestamp':False,
|
fazekasgy@9
|
210 'values':[float(bw)],
|
fazekasgy@9
|
211 #'label':str(bw)
|
fazekasgy@9
|
212 })
|
fazekasgy@9
|
213
|
fazekasgy@9
|
214 output3.append({
|
fazekasgy@9
|
215 'hasTimestamp':False,
|
fazekasgy@9
|
216 'values':[float(shannon)],
|
fazekasgy@9
|
217 #'label':str(shannon)
|
fazekasgy@9
|
218 })
|
fazekasgy@9
|
219
|
fazekasgy@9
|
220 output4.append({
|
fazekasgy@9
|
221 'hasTimestamp':False,
|
fazekasgy@9
|
222 'values':[float(renyi)],
|
fazekasgy@9
|
223 #'label':str(renyi)
|
fazekasgy@9
|
224 })
|
fazekasgy@9
|
225
|
fazekasgy@9
|
226 output5.append({
|
fazekasgy@9
|
227 'hasTimestamp':False,
|
fazekasgy@9
|
228 'values':[float(KLdiv)], #strictly must be a list
|
fazekasgy@9
|
229 #'label':str(renyi)
|
fazekasgy@9
|
230 })
|
fazekasgy@9
|
231
|
fazekasgy@9
|
232 #return a LIST of list of dictionaries
|
fazekasgy@9
|
233 return [output0,output1,output2,output3,output4,output5]
|