annotate Example VamPy plugins/PySpectralFeatures.py @ 24:7d28bed0864e

* Rearrange Python plugin construction. Formerly, the PyPluginAdapter has retained a single plugin instance pointer for each plugin found, and its createPlugin method has simply returned a new PyPlugin object wrapping the same instance pointer. This has a couple of negative consequences: - Because construction of the actual Python instance occurred before the wrapper was constructed, it was not possible to pass arguments (i.e. the sample rate) from the wrapper constructor to the Python plugin instance constructor -- they had to be passed later, to initialise, disadvantaging those plugins that would like to use the sample rate for parameter & step/block size calculations etc - Because there was only a single Python plugin instance, it was not possible to run more than one instance at once with any isolation This rework instead stores the Python class pointer (rather than instance pointer) in the PyPluginAdapter, and each PyPlugin wrapper instance creates its own Python plugin instance. What could possibly go wrong?
author cannam
date Mon, 17 Aug 2009 15:22:06 +0000 (2009-08-17)
parents d2d36e7d2276
children ba3686eb697c
rev   line source
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]