annotate Example VamPy plugins/PySpectralFeatures.py @ 92:a6718f9fe942

If a module appears to redefine one of our own types, refuse to load it. Also clear out the class dict for all refused modules now, so that we don't get stale names on the next scan due to not having cleared the module on unload
author Chris Cannam
date Mon, 14 Jan 2019 16:19:44 +0000
parents f5b8646494d2
children
rev   line source
fazekasgy@37 1 '''PySpectralFeatures.py - Example plugin demonstrates
fazekasgy@37 2 how to calculate and return simple low-level spectral
fazekasgy@37 3 descriptors (curves) using Numpy and the buffer interface.
fazekasgy@37 4
fazekasgy@37 5 Outputs:
fazekasgy@37 6 1) Spectral Centroid
cannam@59 7 2) Spectral Crest Factor
fazekasgy@37 8 3) Spectral Band-width
fazekasgy@37 9 4) Spectral Difference (first order)
fazekasgy@37 10
fazekasgy@37 11 Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 12 Copyright (C) 2009 Gyorgy Fazekas, QMUL. (See Vamp sources
fazekasgy@37 13 for licence information.)
fazekasgy@37 14
fazekasgy@37 15 '''
fazekasgy@37 16
fazekasgy@37 17 from numpy import *
fazekasgy@37 18 from vampy import *
fazekasgy@37 19
fazekasgy@37 20 class PySpectralFeatures:
fazekasgy@37 21
fazekasgy@37 22 def __init__(self,inputSampleRate):
fazekasgy@37 23
fazekasgy@37 24 # flags:
Chris@67 25 self.vampy_flags = vf_BUFFER | vf_REALTIME
fazekasgy@37 26
fazekasgy@37 27 self.m_inputSampleRate = inputSampleRate
fazekasgy@37 28 self.m_stepSize = 0
fazekasgy@37 29 self.m_blockSize = 0
fazekasgy@37 30 self.m_channels = 0
fazekasgy@37 31 self.threshold = 0.05
fazekasgy@37 32 return None
fazekasgy@37 33
fazekasgy@37 34 def initialise(self,channels,stepSize,blockSize):
fazekasgy@37 35 self.m_channels = channels
fazekasgy@37 36 self.m_stepSize = stepSize
fazekasgy@37 37 self.m_blockSize = blockSize
fazekasgy@47 38 self.prevMag = zeros((blockSize/2))
fazekasgy@37 39 return True
fazekasgy@37 40
fazekasgy@37 41 def reset(self):
fazekasgy@37 42 # reset any initial conditions
Chris@68 43 self.prevMag = zeros((self.m_blockSize/2))
fazekasgy@37 44 return None
fazekasgy@37 45
fazekasgy@37 46 def getMaker(self):
fazekasgy@37 47 return 'Vampy Example Plugins'
fazekasgy@37 48
fazekasgy@37 49 def getName(self):
fazekasgy@37 50 return 'Vampy Spectral Features'
fazekasgy@37 51
fazekasgy@37 52 def getIdentifier(self):
fazekasgy@37 53 return 'vampy-sf3'
fazekasgy@37 54
fazekasgy@37 55 def getDescription(self):
fazekasgy@37 56 return 'A collection of low-level spectral descriptors.'
Chris@69 57
Chris@69 58 def getCopyright(self):
Chris@69 59 return 'Plugin By George Fazekas. Freely redistributable example plugin (BSD license)'
Chris@69 60
fazekasgy@37 61 def getMaxChannelCount(self):
fazekasgy@37 62 return 1
fazekasgy@37 63
fazekasgy@37 64 def getInputDomain(self):
fazekasgy@37 65 return FrequencyDomain
fazekasgy@37 66
fazekasgy@37 67 def getOutputDescriptors(self):
fazekasgy@37 68
fazekasgy@37 69 #Generic values are the same for all
fazekasgy@37 70 Generic = OutputDescriptor()
fazekasgy@37 71 Generic.hasFixedBinCount=True
fazekasgy@37 72 Generic.binCount=1
fazekasgy@37 73 Generic.hasKnownExtents=False
fazekasgy@37 74 Generic.isQuantized=False
fazekasgy@37 75 Generic.sampleType = OneSamplePerStep
fazekasgy@37 76 Generic.unit = 'Hz'
fazekasgy@37 77
fazekasgy@37 78 #Spectral centroid etc...
fazekasgy@37 79 SC = OutputDescriptor(Generic)
fazekasgy@37 80 SC.identifier = 'vampy-sc'
fazekasgy@37 81 SC.name = 'Spectral Centroid'
fazekasgy@37 82 SC.description ='Spectral Centroid (Brightness)'
fazekasgy@37 83
fazekasgy@37 84 SCF = OutputDescriptor(Generic)
fazekasgy@37 85 SCF.identifier = 'vampy-scf'
fazekasgy@37 86 SCF.name = 'Spectral Crest Factor'
fazekasgy@37 87 SCF.description = 'Spectral Crest (Tonality)'
fazekasgy@37 88 SCF.unit = 'v'
fazekasgy@37 89
fazekasgy@37 90 BW = OutputDescriptor(Generic)
fazekasgy@37 91 BW.identifier = 'vampy-bw'
fazekasgy@37 92 BW.name = 'Band Width'
fazekasgy@37 93 BW.description = 'Spectral Band Width'
fazekasgy@37 94
fazekasgy@37 95 SD = OutputDescriptor(Generic)
fazekasgy@37 96 SD.identifier = 'vampy-sd'
fazekasgy@37 97 SD.name = 'Spectral Difference'
fazekasgy@37 98 SD.description = 'Eucledian distance of successive magnitude spectra.'
fazekasgy@37 99
fazekasgy@37 100 #return a tuple, list or OutputList(SC,SCF,BW)
fazekasgy@37 101 return OutputList(SC,SCF,BW,SD)
fazekasgy@37 102
fazekasgy@37 103 def getParameterDescriptors(self):
fazekasgy@37 104
fazekasgy@37 105 threshold = ParameterDescriptor()
fazekasgy@37 106 threshold.identifier='threshold'
fazekasgy@37 107 threshold.name='Noise threshold'
Chris@68 108 threshold.description='Magnitude below which a process block will be disregarded and zeroes returned'
fazekasgy@37 109 threshold.unit='v'
fazekasgy@37 110 threshold.minValue=0
fazekasgy@37 111 threshold.maxValue=1
fazekasgy@37 112 threshold.defaultValue=0.05
fazekasgy@37 113 threshold.isQuantized=False
fazekasgy@37 114
fazekasgy@37 115 return ParameterList(threshold)
fazekasgy@37 116
fazekasgy@37 117 def setParameter(self,paramid,newval):
fazekasgy@37 118 if paramid == 'threshold' :
fazekasgy@37 119 self.threshold = newval
fazekasgy@37 120 return
fazekasgy@37 121
fazekasgy@37 122 def getParameter(self,paramid):
fazekasgy@37 123 if paramid == 'threshold' :
fazekasgy@37 124 return self.threshold
fazekasgy@37 125 else:
fazekasgy@37 126 return 0.0
fazekasgy@37 127
fazekasgy@37 128
fazekasgy@37 129 # using the numpy memory buffer interface:
fazekasgy@37 130 # flag : vf_BUFFER (or implement processN)
fazekasgy@37 131 # NOTE: Vampy can now pass numpy arrays directly using
fazekasgy@37 132 # the flag vf_ARRAY (see MFCC plugin for example)
fazekasgy@37 133 def process(self,membuffer,timestamp):
fazekasgy@37 134
fazekasgy@37 135 fftsize = self.m_blockSize
fazekasgy@37 136 sampleRate = self.m_inputSampleRate
fazekasgy@37 137
fazekasgy@37 138 #for time domain plugins use the following line:
fazekasgy@37 139 #audioSamples = frombuffer(membuffer[0],float32)
fazekasgy@37 140
fazekasgy@37 141 #for frequency domain plugins use:
fazekasgy@37 142 complexSpectrum = frombuffer(membuffer[0],complex64,-1,8)
fazekasgy@37 143
fazekasgy@37 144 # meaning of the parameters above:
fazekasgy@37 145 # complex64 : data type of the created numpy array
fazekasgy@37 146 # -1 : convert the whole buffer
fazekasgy@37 147 # 8 : skip the DC component (2*32bit / 8bit = 8byte)
fazekasgy@37 148
fazekasgy@37 149 magnitudeSpectrum = abs(complexSpectrum) / (fftsize*0.5)
fazekasgy@37 150 #phaseSpectrum = angle(complexSpectrum)
fazekasgy@37 151
fazekasgy@37 152 freq = array(range(1,len(complexSpectrum)+1)) \
fazekasgy@37 153 * sampleRate / fftsize
fazekasgy@37 154
fazekasgy@37 155 # return features in a FeatureSet()
fazekasgy@37 156 output_featureSet = FeatureSet()
fazekasgy@37 157
fazekasgy@37 158 tpower = sum(magnitudeSpectrum)
fazekasgy@37 159
fazekasgy@37 160 if tpower > self.threshold :
fazekasgy@37 161 centroid = sum(freq * magnitudeSpectrum) / tpower
fazekasgy@37 162 crest = max(magnitudeSpectrum) / tpower
fazekasgy@37 163 bw = sum( abs(freq - centroid) * magnitudeSpectrum ) / tpower
fazekasgy@37 164 normMag = magnitudeSpectrum / tpower
fazekasgy@37 165 sd = sqrt(sum(power((normMag - self.prevMag),2)))
fazekasgy@37 166 self.prevMag = normMag
fazekasgy@37 167 else :
fazekasgy@37 168 centroid = 0.0
fazekasgy@37 169 crest = 0.0
fazekasgy@37 170 bw = 0.0
fazekasgy@37 171 sd = 0.0
fazekasgy@37 172
fazekasgy@37 173 # Any value resulting from the process can be returned.
fazekasgy@37 174 # It is no longer necessary to wrap single values into lists
fazekasgy@37 175 # and convert numpy.floats to python floats,
fazekasgy@37 176 # however a FeatureList() (or python list) can be returned
fazekasgy@37 177 # if more than one feature is calculated per frame.
fazekasgy@37 178 # The feature values can be e.g. int, float, list or array.
fazekasgy@37 179
fazekasgy@37 180 output_featureSet[0] = Feature(centroid)
fazekasgy@37 181 output_featureSet[1] = Feature(crest)
fazekasgy@37 182 output_featureSet[2] = Feature(bw)
fazekasgy@37 183 output_featureSet[3] = Feature(sd)
fazekasgy@37 184
fazekasgy@37 185 return output_featureSet