mi@2
|
1 import os, sys
|
mi@2
|
2 import numpy as np
|
mi@2
|
3 import scipy as sp
|
mi@2
|
4 from scipy.ndimage.filters import *
|
mi@2
|
5 from vampy import *
|
mi@2
|
6 from copy import deepcopy
|
mi@2
|
7
|
mi@2
|
8 class HPspectrogram:
|
mi@2
|
9
|
mi@2
|
10 def __init__(self, inputSampleRate):
|
mi@2
|
11
|
mi@2
|
12 # flags:
|
mi@2
|
13 self.vampy_flags = vf_DEBUG | vf_ARRAY | vf_REALTIME
|
mi@2
|
14 self.inputSampleRate = inputSampleRate
|
mi@2
|
15 self.stepSize = 1024
|
mi@2
|
16 self.blockSize = 2048
|
mi@2
|
17 self.channels = 1
|
mi@2
|
18 self.harmonic_medwin = 8
|
mi@2
|
19 self.percussive_medwin = 8
|
mi@2
|
20 self.harmonic_thresh = 0.5
|
mi@2
|
21 self.percussive_thresh = 0.5
|
mi@2
|
22 self.whitening_on = False
|
mi@2
|
23 self.whitenRelaxCoeff = 0.9997
|
mi@2
|
24 self.whitenFloor = 0.01
|
mi@2
|
25 self.magPeaks = None
|
mi@2
|
26
|
mi@2
|
27 def initialise(self, channels, stepSize, blockSize):
|
mi@2
|
28 self.channels = channels
|
mi@2
|
29 self.stepSize = stepSize
|
mi@2
|
30 self.blockSize = blockSize
|
mi@2
|
31 self.maglist = []
|
mi@2
|
32 self.timestamp = []
|
mi@2
|
33 return True
|
mi@2
|
34
|
mi@2
|
35 def reset(self):
|
mi@2
|
36 # reset any initial conditions
|
mi@2
|
37 self.maglist = []
|
mi@2
|
38 self.timestamp = []
|
mi@2
|
39 return None
|
mi@2
|
40
|
mi@2
|
41 def getMaker(self):
|
mi@2
|
42 return 'Mi Tian'
|
mi@2
|
43
|
mi@2
|
44 def getName(self):
|
mi@2
|
45 return 'Harmonic percussive spectrogram'
|
mi@2
|
46
|
mi@2
|
47 def getIdentifier(self):
|
mi@2
|
48 return 'hps'
|
mi@2
|
49
|
mi@2
|
50 def getDescription(self):
|
mi@2
|
51 return 'Harmonic and percussive emphasised spectrograms.'
|
mi@2
|
52
|
mi@2
|
53 def getCopyright(self):
|
mi@2
|
54 return ''
|
mi@2
|
55
|
mi@2
|
56 def getMaxChannelCount(self):
|
mi@2
|
57 return 1
|
mi@2
|
58
|
mi@2
|
59 def getInputDomain(self):
|
mi@2
|
60 return FrequencyDomain
|
mi@2
|
61
|
mi@2
|
62 def getOutputDescriptors(self):
|
mi@2
|
63
|
mi@2
|
64 #Generic values are the same for all
|
mi@2
|
65 Generic = OutputDescriptor()
|
mi@2
|
66 Generic.hasFixedBinCount=False
|
mi@2
|
67 Generic.hasKnownExtents=False
|
mi@2
|
68 Generic.isQuantized=False
|
mi@2
|
69 Generic.sampleType = OneSamplePerStep
|
mi@2
|
70 Generic.unit = 'Hz'
|
mi@2
|
71
|
mi@2
|
72 HSPEC = OutputDescriptor(Generic)
|
mi@2
|
73 HSPEC.identifier = 'gt'
|
mi@2
|
74 HSPEC.name = 'Harmonic emphasised spectrogram'
|
mi@2
|
75 HSPEC.description = 'Harmonic emphasised spectrogram'
|
mi@2
|
76 HSPEC.binCount = self.nBands
|
mi@2
|
77
|
mi@2
|
78 PSPEC = OutputDescriptor(Generic)
|
mi@2
|
79 PSPEC.identifier = 'gt'
|
mi@2
|
80 PSPEC.name = 'Percussive emphasised spectrogram'
|
mi@2
|
81 PSPEC.description = 'Percussive emphasised spectrogram'
|
mi@2
|
82
|
mi@2
|
83 return OutputList(HSPEC, PSPEC)
|
mi@2
|
84
|
mi@2
|
85 def getParameterDescriptors(self):
|
mi@2
|
86
|
mi@2
|
87 harmonic_thresh = ParameterDescriptor()
|
mi@2
|
88 harmonic_thresh.identifier='percussive_thresh'
|
mi@2
|
89 harmonic_thresh.name='percussive_thresh'
|
mi@2
|
90 harmonic_thresh.description='Binarisation threshold for harmonic emphasised magnitude spectrogram'
|
mi@2
|
91 harmonic_thresh.unit='v'
|
mi@2
|
92 harmonic_thresh.minValue=0
|
mi@2
|
93 harmonic_thresh.maxValue=1
|
mi@2
|
94 harmonic_thresh.defaultValue=0.5
|
mi@2
|
95 harmonic_thresh.isQuantized=False
|
mi@2
|
96
|
mi@2
|
97 percussive_thresh = ParameterDescriptor(harmonic_thresh)
|
mi@2
|
98 percussive_thresh.identifier='percussive_thresh'
|
mi@2
|
99 percussive_thresh.name='percussive_thresh'
|
mi@2
|
100 percussive_thresh.description='Binarisation threshold for percussive emphasised magnitude spectrogram'
|
mi@2
|
101
|
mi@2
|
102 harmonic_medwin = ParameterDescriptor()
|
mi@2
|
103 harmonic_medwin.identifier = 'harmonic_medwin'
|
mi@2
|
104 harmonic_medwin.description = 'Median window length for harmonic part.'
|
mi@2
|
105 harmonic_medwin.name = 'Median window length for harmonic part.'
|
mi@2
|
106 harmonic_medwin.minValue = 1
|
mi@2
|
107 harmonic_medwin.defaultValue = 8
|
mi@2
|
108 harmonic_medwin.maxValue = 15
|
mi@2
|
109 harmonic_medwin.isQuantized = True
|
mi@2
|
110
|
mi@2
|
111 percussive_medwin = ParameterDescriptor(harmonic_medwin)
|
mi@2
|
112 percussive_medwin.identifier = 'percussive_medwin'
|
mi@2
|
113 percussive_medwin.description = 'Median window length for percussive part.'
|
mi@2
|
114 percussive_medwin.name = 'Median window length for percussive part.'
|
mi@2
|
115
|
mi@2
|
116 boolDescriptor = ParameterDescriptor()
|
mi@2
|
117 boolDescriptor.isQuantized = True
|
mi@2
|
118 boolDescriptor.minValue= 0
|
mi@2
|
119 boolDescriptor.maxValue= 1
|
mi@2
|
120 boolDescriptor.quantizeStep = 1
|
mi@2
|
121
|
mi@2
|
122 whitening = ParameterDescriptor(boolDescriptor)
|
mi@2
|
123 whitening.identifier='whitening'
|
mi@2
|
124 whitening.name='Adaptive whitening'
|
mi@2
|
125 whitening.description='Turn adaptive whitening on or off'
|
mi@2
|
126 whitening.defaultValue = False
|
mi@2
|
127
|
mi@2
|
128 return ParameterList(harmonic_thresh, percussive_thresh, harmonic_medwin, percussive_medwin, whitening)
|
mi@2
|
129
|
mi@2
|
130 def setParameter(self, paramid, newval):
|
mi@2
|
131 if paramid == 'percussive_thresh' :
|
mi@2
|
132 self.percussive_thresh = newval
|
mi@2
|
133 if paramid == 'harmonic_medwin' :
|
mi@2
|
134 self.harmonic_medwin = newval
|
mi@2
|
135 if paramid == 'harmonic_medwin' :
|
mi@2
|
136 self.harmonic_medwin = newval
|
mi@2
|
137 if paramid == 'percussive_medwin' :
|
mi@2
|
138 self.percussive_medwin = newval
|
mi@2
|
139 if paramid == 'whitening' :
|
mi@2
|
140 self.whitening_on = newval == 1.0
|
mi@2
|
141
|
mi@2
|
142 return None
|
mi@2
|
143
|
mi@2
|
144 def getParameter(self, paramid):
|
mi@2
|
145 if paramid == 'percussive_thresh' :
|
mi@2
|
146 return self.percussive_thresh
|
mi@2
|
147 if paramid == 'harmonic_medwin' :
|
mi@2
|
148 return self.harmonic_medwin
|
mi@2
|
149 if paramid == 'percussive_medwin' :
|
mi@2
|
150 return self.percussive_medwin
|
mi@2
|
151 if paramid == 'harmonic_medwin' :
|
mi@2
|
152 return self.harmonic_medwin
|
mi@2
|
153 if paramid == 'whitening' :
|
mi@2
|
154 if self.whitening_on :
|
mi@2
|
155 return 1.0
|
mi@2
|
156 else :
|
mi@2
|
157 return 0.0
|
mi@2
|
158 else:
|
mi@2
|
159 return 0.0
|
mi@2
|
160
|
mi@2
|
161 def whiten(self, magnitudeSpectrogram):
|
mi@2
|
162 '''This function reproduces adaptive whitening as described in Dan Stowell's paper.'''
|
mi@2
|
163
|
mi@2
|
164 half_length = self.blockSize * 0.5 + 1.0
|
mi@2
|
165 nFrames = magnitudeSpectrogram.shape[0]
|
mi@2
|
166 whitened_ms = np.zeros_like(magnitudeSpectrogram)
|
mi@2
|
167
|
mi@2
|
168 if self.magPeaks is None :
|
mi@2
|
169 self.magPeaks = np.zeros(half_length, dtype = float32)
|
mi@2
|
170
|
mi@2
|
171 for i in xrange(nFrames):
|
mi@2
|
172 m = magnitudeSpectrogram[i, :]
|
mi@2
|
173 idx = m < self.magPeaks
|
mi@2
|
174 m[idx] += (self.magPeaks[idx] - m[idx]) * self.whitenRelaxCoeff
|
mi@2
|
175 m[m < self.whitenFloor] = self.whitenFloor
|
mi@2
|
176 self.magPeaks = m
|
mi@2
|
177
|
mi@2
|
178 whitened_ms[i, :] = magnitudeSpectrogram[i, :] / m
|
mi@2
|
179
|
mi@2
|
180 return whitened_ms
|
mi@2
|
181
|
mi@2
|
182 def process(self, inputbuffers, timestamp):
|
mi@2
|
183
|
mi@2
|
184 output_featureSet = FeatureSet()
|
mi@2
|
185 self.timestamp.append(timestamp)
|
mi@2
|
186 complexSpectrum = inputbuffers[0]
|
mi@2
|
187 magnitudeSpectrum = abs(complexSpectrum)
|
mi@2
|
188 self.maglist.append(magnitudeSpectrum)
|
mi@2
|
189
|
mi@2
|
190 return output_featureSet
|
mi@2
|
191
|
mi@2
|
192 def getRemainingFeatures(self):
|
mi@2
|
193
|
mi@2
|
194 output_featureSet = FeatureSet()
|
mi@2
|
195
|
mi@2
|
196 nFrames = len(self.maglist)
|
mi@2
|
197 magnitudeSpectrogram = np.array(self.maglist)
|
mi@2
|
198
|
mi@2
|
199 if self.whitening_on:
|
mi@2
|
200 magnitudeSpectrogram = self.whiten(magnitudeSpectrogram)
|
mi@2
|
201 else:
|
mi@2
|
202 magnitudeSpectrogram = (magnitudeSpectrogram - np.min(magnitudeSpectrogram)) / (np.max(magnitudeSpectrogram) - np.min(magnitudeSpectrogram))
|
mi@2
|
203
|
mi@2
|
204 harmonic_ma = median_filter(magnitudeSpectrogram, size=(self.harmonic_medwin, 1))
|
mi@2
|
205 percussive_ma = median_filter(magnitudeSpectrogram, size=(1, self.percussive_medwin))
|
mi@2
|
206
|
mi@2
|
207 harmonicSpectrogram = magnitudeSpectrogram * (harmonic_ma>=percussive_ma).astype(float)
|
mi@2
|
208 percussiveSpectrogram = magnitudeSpectrogram * (harmonic_ma<percussive_ma).astype(float)
|
mi@2
|
209
|
mi@2
|
210 output_featureSet[0] = flist0 = FeatureList()
|
mi@2
|
211 output_featureSet[1] = flist1 = FeatureList()
|
mi@2
|
212 for frame in xrange(nFrames):
|
mi@2
|
213 f = Feature(timestamp = self.timestamp[frame], values = ma_harmonicSpectrogram[frame, :])
|
mi@2
|
214 flist0.append(f)
|
mi@2
|
215 f = Feature(timestamp = self.timestamp[frame], values = ma_percussiveSpectrogram[frame, :])
|
mi@2
|
216 flist1.append(f)
|
mi@2
|
217
|
mi@2
|
218 return output_featureSet |