annotate Example VamPy plugins/PySpectralFeatures.py @ 53:7e59caea821b

* Make a better job of preloading Python, especially when it's in a framework. Go for the Python file in the frameworks directory in preference to any libpythonX.Y.dylib. Particularly, don't try to preload any library without an absolute path until we've exhausted all our framework possibilities (so as to avoid picking up an ancient system library).
author cannam
date Fri, 09 Oct 2009 13:48:25 +0000
parents 8b2eddf686da
children 93eddad99f3c
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
fazekasgy@37 7 2) Spectral Creast 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:
fazekasgy@37 25 self.vampy_flags = vf_DEBUG | 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
fazekasgy@47 43 self.prevMag = zeros((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.'
fazekasgy@37 57
fazekasgy@37 58 def getMaxChannelCount(self):
fazekasgy@37 59 return 1
fazekasgy@37 60
fazekasgy@37 61 def getInputDomain(self):
fazekasgy@37 62 return FrequencyDomain
fazekasgy@37 63
fazekasgy@37 64 def getOutputDescriptors(self):
fazekasgy@37 65
fazekasgy@37 66 #Generic values are the same for all
fazekasgy@37 67 Generic = OutputDescriptor()
fazekasgy@37 68 Generic.hasFixedBinCount=True
fazekasgy@37 69 Generic.binCount=1
fazekasgy@37 70 Generic.hasKnownExtents=False
fazekasgy@37 71 Generic.isQuantized=False
fazekasgy@37 72 Generic.sampleType = OneSamplePerStep
fazekasgy@37 73 Generic.unit = 'Hz'
fazekasgy@37 74
fazekasgy@37 75 #Spectral centroid etc...
fazekasgy@37 76 SC = OutputDescriptor(Generic)
fazekasgy@37 77 SC.identifier = 'vampy-sc'
fazekasgy@37 78 SC.name = 'Spectral Centroid'
fazekasgy@37 79 SC.description ='Spectral Centroid (Brightness)'
fazekasgy@37 80
fazekasgy@37 81 SCF = OutputDescriptor(Generic)
fazekasgy@37 82 SCF.identifier = 'vampy-scf'
fazekasgy@37 83 SCF.name = 'Spectral Crest Factor'
fazekasgy@37 84 SCF.description = 'Spectral Crest (Tonality)'
fazekasgy@37 85 SCF.unit = 'v'
fazekasgy@37 86
fazekasgy@37 87 BW = OutputDescriptor(Generic)
fazekasgy@37 88 BW.identifier = 'vampy-bw'
fazekasgy@37 89 BW.name = 'Band Width'
fazekasgy@37 90 BW.description = 'Spectral Band Width'
fazekasgy@37 91
fazekasgy@37 92 SD = OutputDescriptor(Generic)
fazekasgy@37 93 SD.identifier = 'vampy-sd'
fazekasgy@37 94 SD.name = 'Spectral Difference'
fazekasgy@37 95 SD.description = 'Eucledian distance of successive magnitude spectra.'
fazekasgy@37 96
fazekasgy@37 97 #return a tuple, list or OutputList(SC,SCF,BW)
fazekasgy@37 98 return OutputList(SC,SCF,BW,SD)
fazekasgy@37 99
fazekasgy@37 100 def getParameterDescriptors(self):
fazekasgy@37 101
fazekasgy@37 102 threshold = ParameterDescriptor()
fazekasgy@37 103 threshold.identifier='threshold'
fazekasgy@37 104 threshold.name='Noise threshold'
fazekasgy@37 105 threshold.description='Noise threshold'
fazekasgy@37 106 threshold.unit='v'
fazekasgy@37 107 threshold.minValue=0
fazekasgy@37 108 threshold.maxValue=1
fazekasgy@37 109 threshold.defaultValue=0.05
fazekasgy@37 110 threshold.isQuantized=False
fazekasgy@37 111
fazekasgy@37 112 return ParameterList(threshold)
fazekasgy@37 113
fazekasgy@37 114 def setParameter(self,paramid,newval):
fazekasgy@37 115 if paramid == 'threshold' :
fazekasgy@37 116 self.threshold = newval
fazekasgy@37 117 return
fazekasgy@37 118
fazekasgy@37 119 def getParameter(self,paramid):
fazekasgy@37 120 if paramid == 'threshold' :
fazekasgy@37 121 return self.threshold
fazekasgy@37 122 else:
fazekasgy@37 123 return 0.0
fazekasgy@37 124
fazekasgy@37 125
fazekasgy@37 126 # using the numpy memory buffer interface:
fazekasgy@37 127 # flag : vf_BUFFER (or implement processN)
fazekasgy@37 128 # NOTE: Vampy can now pass numpy arrays directly using
fazekasgy@37 129 # the flag vf_ARRAY (see MFCC plugin for example)
fazekasgy@37 130 def process(self,membuffer,timestamp):
fazekasgy@37 131
fazekasgy@37 132 fftsize = self.m_blockSize
fazekasgy@37 133 sampleRate = self.m_inputSampleRate
fazekasgy@37 134
fazekasgy@37 135 #for time domain plugins use the following line:
fazekasgy@37 136 #audioSamples = frombuffer(membuffer[0],float32)
fazekasgy@37 137
fazekasgy@37 138 #for frequency domain plugins use:
fazekasgy@37 139 complexSpectrum = frombuffer(membuffer[0],complex64,-1,8)
fazekasgy@37 140
fazekasgy@37 141 # meaning of the parameters above:
fazekasgy@37 142 # complex64 : data type of the created numpy array
fazekasgy@37 143 # -1 : convert the whole buffer
fazekasgy@37 144 # 8 : skip the DC component (2*32bit / 8bit = 8byte)
fazekasgy@37 145
fazekasgy@37 146 magnitudeSpectrum = abs(complexSpectrum) / (fftsize*0.5)
fazekasgy@37 147 #phaseSpectrum = angle(complexSpectrum)
fazekasgy@37 148
fazekasgy@37 149 freq = array(range(1,len(complexSpectrum)+1)) \
fazekasgy@37 150 * sampleRate / fftsize
fazekasgy@37 151
fazekasgy@37 152 # return features in a FeatureSet()
fazekasgy@37 153 output_featureSet = FeatureSet()
fazekasgy@37 154
fazekasgy@37 155 tpower = sum(magnitudeSpectrum)
fazekasgy@37 156
fazekasgy@37 157 if tpower > self.threshold :
fazekasgy@37 158 centroid = sum(freq * magnitudeSpectrum) / tpower
fazekasgy@37 159 crest = max(magnitudeSpectrum) / tpower
fazekasgy@37 160 bw = sum( abs(freq - centroid) * magnitudeSpectrum ) / tpower
fazekasgy@37 161 normMag = magnitudeSpectrum / tpower
fazekasgy@37 162 sd = sqrt(sum(power((normMag - self.prevMag),2)))
fazekasgy@37 163 self.prevMag = normMag
fazekasgy@37 164 else :
fazekasgy@37 165 centroid = 0.0
fazekasgy@37 166 crest = 0.0
fazekasgy@37 167 bw = 0.0
fazekasgy@37 168 sd = 0.0
fazekasgy@37 169
fazekasgy@37 170 # Any value resulting from the process can be returned.
fazekasgy@37 171 # It is no longer necessary to wrap single values into lists
fazekasgy@37 172 # and convert numpy.floats to python floats,
fazekasgy@37 173 # however a FeatureList() (or python list) can be returned
fazekasgy@37 174 # if more than one feature is calculated per frame.
fazekasgy@37 175 # The feature values can be e.g. int, float, list or array.
fazekasgy@37 176
fazekasgy@37 177 output_featureSet[0] = Feature(centroid)
fazekasgy@37 178 output_featureSet[1] = Feature(crest)
fazekasgy@37 179 output_featureSet[2] = Feature(bw)
fazekasgy@37 180 output_featureSet[3] = Feature(sd)
fazekasgy@37 181
fazekasgy@37 182 return output_featureSet