fazekasgy@52
|
1 '''PySpectralCentroid.py - Example plugin demonstrates
|
fazekasgy@52
|
2 how to write a C style plugin using VamPy in pure Python.
|
fazekasgy@52
|
3 This plugin also introduces the use of the builtin vampy
|
fazekasgy@52
|
4 extension module.
|
fazekasgy@52
|
5
|
fazekasgy@52
|
6 The plugin has frequency domain input and is using the
|
Chris@69
|
7 legacy interface: the FFT output is passed as a list
|
fazekasgy@52
|
8 of complex numbers.
|
fazekasgy@52
|
9
|
fazekasgy@52
|
10 Outputs:
|
fazekasgy@52
|
11 1) Spectral centroid
|
fazekasgy@52
|
12
|
fazekasgy@52
|
13 Note: This is not the adviced way of writing Vampy plugins now,
|
fazekasgy@52
|
14 since the interfaces provided for Numpy are at least 5 times
|
fazekasgy@52
|
15 faster. However, this is still a nice and easy to understand
|
fazekasgy@52
|
16 example, which also shows how can one write a reasonable
|
fazekasgy@52
|
17 plugin without having Numpy installed.
|
fazekasgy@52
|
18
|
fazekasgy@52
|
19 Warning: Earlier versions of this plugin are now obsolete.
|
fazekasgy@52
|
20 (They were using the legacy interface of Vampy 1 which
|
fazekasgy@52
|
21 did not distinquish between time and frequency domain inputs.)
|
fazekasgy@52
|
22
|
fazekasgy@52
|
23 Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@52
|
24 Copyright (C) 2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@52
|
25 for licence information.)
|
fazekasgy@52
|
26
|
fazekasgy@52
|
27 '''
|
fazekasgy@52
|
28
|
fazekasgy@52
|
29 # import the names we use from vampy
|
fazekasgy@52
|
30 from vampy import Feature,FeatureSet,ParameterDescriptor
|
fazekasgy@52
|
31 from vampy import OutputDescriptor,FrequencyDomain,OneSamplePerStep
|
fazekasgy@52
|
32
|
fazekasgy@52
|
33 from math import sqrt
|
fazekasgy@52
|
34
|
fazekasgy@52
|
35 class PySpectralCentroid:
|
fazekasgy@52
|
36
|
fazekasgy@52
|
37 def __init__(self,inputSampleRate):
|
fazekasgy@52
|
38 self.m_stepSize = 0
|
fazekasgy@52
|
39 self.m_blockSize = 0
|
fazekasgy@52
|
40 self.m_channels = 0
|
fazekasgy@52
|
41 self.previousSample = 0.0
|
fazekasgy@52
|
42 self.m_inputSampleRate = inputSampleRate
|
Chris@68
|
43 self.threshold = 0.05
|
fazekasgy@52
|
44
|
fazekasgy@52
|
45 def initialise(self,channels,stepSize,blockSize):
|
fazekasgy@52
|
46 self.m_channels = channels
|
fazekasgy@52
|
47 self.m_stepSize = stepSize
|
fazekasgy@52
|
48 self.m_blockSize = blockSize
|
fazekasgy@52
|
49 return True
|
fazekasgy@52
|
50
|
fazekasgy@52
|
51 def getMaker(self):
|
fazekasgy@52
|
52 return 'Vampy Example Plugins'
|
fazekasgy@52
|
53
|
fazekasgy@52
|
54 def getName(self):
|
fazekasgy@52
|
55 return 'Spectral Centroid (using legacy process interface)'
|
fazekasgy@52
|
56
|
fazekasgy@52
|
57 def getIdentifier(self):
|
fazekasgy@52
|
58 return 'vampy-sc3'
|
Chris@69
|
59
|
Chris@69
|
60 def getDescription(self):
|
Chris@69
|
61 return 'Calculate the linear frequency centroid of the short-time Fourier spectrum'
|
Chris@69
|
62
|
Chris@69
|
63 def getCopyright(self):
|
Chris@69
|
64 return 'Plugin By George Fazekas. Freely redistributable example plugin (BSD license)'
|
Chris@69
|
65
|
fazekasgy@52
|
66 def getMaxChannelCount(self):
|
fazekasgy@52
|
67 return 1
|
fazekasgy@52
|
68
|
fazekasgy@52
|
69 def getInputDomain(self):
|
fazekasgy@52
|
70 return FrequencyDomain
|
fazekasgy@52
|
71
|
fazekasgy@52
|
72 def getOutputDescriptors(self):
|
fazekasgy@52
|
73
|
fazekasgy@52
|
74 cod = OutputDescriptor()
|
fazekasgy@52
|
75 cod.identifier='vampy-sc3'
|
fazekasgy@52
|
76 cod.name='Spectral Centroid'
|
fazekasgy@52
|
77 cod.description='Spectral Centroid (Brightness)'
|
fazekasgy@52
|
78 cod.unit=''
|
fazekasgy@52
|
79 cod.hasFixedBinCount=True
|
fazekasgy@52
|
80 cod.binCount=1
|
fazekasgy@52
|
81 cod.hasKnownExtents=False
|
fazekasgy@52
|
82 cod.isQuantized=True
|
fazekasgy@52
|
83 cod.quantizeStep=1.0
|
fazekasgy@52
|
84 cod.sampleType=OneSamplePerStep
|
fazekasgy@52
|
85 return cod
|
fazekasgy@52
|
86
|
fazekasgy@52
|
87 def getParameterDescriptors(self):
|
fazekasgy@52
|
88 thd = ParameterDescriptor()
|
fazekasgy@52
|
89 thd.identifier='threshold'
|
fazekasgy@52
|
90 thd.name='Noise threshold'
|
Chris@68
|
91 thd.description='Magnitude below which a process block will be disregarded and zero returned'
|
fazekasgy@52
|
92 thd.unit='v'
|
fazekasgy@52
|
93 thd.minValue=0.0
|
fazekasgy@52
|
94 thd.maxValue=0.5
|
fazekasgy@52
|
95 thd.defaultValue=0.05
|
fazekasgy@52
|
96 thd.isQuantized=False
|
fazekasgy@52
|
97 return thd
|
fazekasgy@52
|
98
|
fazekasgy@52
|
99 def setParameter(self,paramid,newval):
|
fazekasgy@52
|
100 if paramid == 'threshold' :
|
fazekasgy@52
|
101 self.threshold = newval
|
fazekasgy@52
|
102 return
|
fazekasgy@52
|
103
|
fazekasgy@52
|
104 def getParameter(self,paramid):
|
fazekasgy@52
|
105 if paramid == 'threshold' :
|
fazekasgy@52
|
106 return self.threshold
|
fazekasgy@52
|
107 else:
|
fazekasgy@52
|
108 return 0.0
|
fazekasgy@52
|
109
|
fazekasgy@52
|
110 def process(self,inputbuffers,timestamp):
|
fazekasgy@52
|
111
|
fazekasgy@52
|
112 # this is a 1 channel frequency domain plugin, therefore
|
fazekasgy@52
|
113 # inputbuffers contain (block size / 2) + 1 complex numbers
|
fazekasgy@52
|
114 # corresponding to the FFT output from DC to Nyquist inclusive
|
fazekasgy@52
|
115
|
fazekasgy@52
|
116 cplxArray = inputbuffers[0][:-1]
|
fazekasgy@52
|
117
|
fazekasgy@52
|
118 prev = self.previousSample
|
fazekasgy@52
|
119 numLin = 0.0
|
fazekasgy@52
|
120 denom = 0.0
|
fazekasgy@52
|
121 centroid = 0.0
|
fazekasgy@52
|
122
|
fazekasgy@52
|
123 output = FeatureSet()
|
fazekasgy@52
|
124
|
fazekasgy@52
|
125 pw = 0
|
fazekasgy@52
|
126 for i in xrange(1,len(cplxArray)) :
|
fazekasgy@52
|
127 pw = pw + abs(cplxArray[i])
|
fazekasgy@52
|
128
|
fazekasgy@52
|
129 if pw > self.threshold :
|
fazekasgy@52
|
130 for i in range(1,(len(cplxArray))) :
|
fazekasgy@52
|
131
|
fazekasgy@52
|
132 re = cplxArray[i].real
|
fazekasgy@52
|
133 im = cplxArray[i].imag
|
fazekasgy@52
|
134 freq = i * self.m_inputSampleRate / self.m_blockSize
|
fazekasgy@52
|
135 power = sqrt (re*re + im*im) / (self.m_blockSize/2)
|
fazekasgy@52
|
136 denom = denom + power
|
fazekasgy@52
|
137 numLin = numLin + freq * power
|
fazekasgy@52
|
138
|
fazekasgy@52
|
139 if denom != 0 :
|
fazekasgy@52
|
140 centroid = numLin / denom
|
fazekasgy@52
|
141
|
fazekasgy@52
|
142 else :
|
fazekasgy@52
|
143 centroid = 0.0
|
fazekasgy@52
|
144
|
fazekasgy@52
|
145 output[0] = Feature()
|
fazekasgy@52
|
146 output[0].values = centroid
|
fazekasgy@52
|
147 output[0].label = str(centroid)
|
fazekasgy@52
|
148
|
fazekasgy@52
|
149 return output
|