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
|