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