Maria@4
|
1 # -*- coding: utf-8 -*-
|
Maria@4
|
2 """
|
Maria@4
|
3 Created on Mon Aug 17 13:37:00 2015
|
Maria@4
|
4
|
Maria@4
|
5 @author: mariapanteli
|
Maria@4
|
6 """
|
Maria@4
|
7
|
Maria@4
|
8 import librosa
|
Maria@4
|
9 import scipy.signal
|
Maria@4
|
10 # import matplotlib.pyplot as plt
|
Maria@4
|
11 import numpy
|
Maria@4
|
12
|
Maria@4
|
13 class OPMellin:
|
Maria@4
|
14 def __init__(self, win2sec=8):
|
Maria@4
|
15 self.y = None
|
Maria@4
|
16 self.sr = None
|
Maria@4
|
17 self.melspec = None
|
Maria@4
|
18 self.melsr = None
|
Maria@4
|
19 self.op = None
|
Maria@4
|
20 self.opmellin = None
|
Maria@4
|
21 self.win2sec = win2sec
|
Maria@4
|
22
|
Maria@4
|
23 def load_audiofile(self, filename='test.wav', sr=None, segment=True):
|
Maria@4
|
24 self.y, self.sr = librosa.load(filename, sr=sr)
|
Maria@4
|
25 #self.y, self.sr = librosa.load(filename) # default sr=22050
|
Maria@4
|
26 if segment:
|
Maria@4
|
27 tracklength = self.y.shape[0]/float(self.sr)
|
Maria@4
|
28 startSample = 0
|
Maria@4
|
29 endSample = None
|
Maria@4
|
30 if tracklength > 90:
|
Maria@4
|
31 startPointSec = (tracklength/2.)-20
|
Maria@4
|
32 startSample = round(startPointSec*self.sr)
|
Maria@4
|
33 endSample = startSample+45*self.sr
|
Maria@4
|
34 self.y = self.y[startSample:endSample]
|
Maria@4
|
35
|
Maria@4
|
36
|
Maria@4
|
37 # def mel_spectrogram(self, y=None, sr=None, filename='1_21.wav'):
|
Maria@4
|
38 def mel_spectrogram(self, y=None, sr=None):
|
Maria@4
|
39 if self.y is None:
|
Maria@4
|
40 self.y = y
|
Maria@4
|
41 if self.sr is None:
|
Maria@4
|
42 self.sr = sr
|
Maria@4
|
43 win1 = int(round(0.04*self.sr))
|
Maria@4
|
44 hop1 = int(round(win1/8.))
|
Maria@4
|
45 nfft1 = int(2**numpy.ceil(numpy.log2(win1)))
|
Maria@4
|
46 nmels = 40
|
Maria@4
|
47 # y, sr = librosa.load(filename, sr=fs)
|
Maria@4
|
48 # melspec = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=nfft, win_length=win1, window=scipy.signal.hamming, hop_length=hop1, n_mels=nmels)
|
Maria@4
|
49 # melspec = librosa.feature.melspectrogram(y=self.y, sr=self.sr, n_fft=nfft, hop_length=hop1, n_mels=nmels)
|
Maria@4
|
50 D = numpy.abs(librosa.stft(self.y, n_fft=nfft1, hop_length=hop1, win_length=win1, window=scipy.signal.hamming))**2
|
Maria@4
|
51 #melspec = librosa.feature.melspectrogram(S=D, sr=self.sr, n_mels=nmels)
|
Maria@4
|
52 melspec = librosa.feature.melspectrogram(S=D, sr=self.sr, n_mels=nmels, fmax=8000)
|
Maria@4
|
53 melsr = self.sr/float(hop1)
|
Maria@4
|
54 # librosa.display.specshow(melspec)
|
Maria@4
|
55 # return melspec, melsr
|
Maria@4
|
56 self.melspec = melspec
|
Maria@4
|
57 self.melsr = melsr
|
Maria@4
|
58
|
Maria@4
|
59 # post process spectrogram
|
Maria@4
|
60 def post_process_spec(self, melspec=None, log=True, medianfilt=False, sqrt=False, smooth=False, diff=True, subtractmean=True, halfwave=True, maxnormal=True):
|
Maria@4
|
61 if self.melspec is None:
|
Maria@4
|
62 self.melspec = melspec
|
Maria@4
|
63 if log:
|
Maria@4
|
64 self.melspec = librosa.logamplitude(self.melspec)
|
Maria@4
|
65 if medianfilt:
|
Maria@4
|
66 ks = int(0.1 * self.melsr) # 100ms kernel size
|
Maria@4
|
67 if ks % 2 == 0: # ks must be odd
|
Maria@4
|
68 ks += 1
|
Maria@4
|
69 nmels = self.melspec.shape[0]
|
Maria@4
|
70 for i in range(nmels):
|
Maria@4
|
71 self.melspec[i,:] = scipy.signal.medfilt(self.melspec[i,:],kernel_size = ks)
|
Maria@4
|
72 if sqrt:
|
Maria@4
|
73 self.melspec = self.melspec**.5
|
Maria@4
|
74 if smooth:
|
Maria@4
|
75 step = 50.0
|
Maria@4
|
76 nmels = self.melspec.shape[0]
|
Maria@4
|
77 mm = self.melspec
|
Maria@4
|
78 for i in range(nmels):
|
Maria@4
|
79 mm[i,:] = numpy.convolve(mm[i,:], numpy.ones(step)/step, mode='same')
|
Maria@4
|
80 self.melspec = mm
|
Maria@4
|
81 if diff:
|
Maria@4
|
82 self.melspec = numpy.concatenate((self.melspec,self.melspec[:,-1,None]),axis=1) # append one frame before diff to keep number of frames the same
|
Maria@4
|
83 self.melspec = numpy.diff(self.melspec, n=1, axis=1)
|
Maria@4
|
84 if subtractmean:
|
Maria@4
|
85 mean = self.melspec.mean(axis=1)
|
Maria@4
|
86 mean.shape = (mean.shape[0], 1)
|
Maria@4
|
87 self.melspec = self.melspec - mean
|
Maria@4
|
88 if halfwave:
|
Maria@4
|
89 self.melspec[numpy.where(self.melspec < 0)] = 0
|
Maria@4
|
90 if maxnormal:
|
Maria@4
|
91 if self.melspec.max() != 0: # avoid division by 0
|
Maria@4
|
92 self.melspec = self.melspec/self.melspec.max()
|
Maria@4
|
93 # return melspec
|
Maria@4
|
94
|
Maria@4
|
95
|
Maria@4
|
96 def onset_patterns(self, melspec=None, melsr=None, fft=True, logfilter=False, center=True, bpmrange=True):
|
Maria@4
|
97 if self.melspec is None:
|
Maria@4
|
98 self.melspec = melspec
|
Maria@4
|
99 if self.melsr is None:
|
Maria@4
|
100 self.melsr = melsr
|
Maria@4
|
101 win2 = int(round(self.win2sec*self.melsr))
|
Maria@4
|
102 hop2 = int(round(0.5*self.melsr))
|
Maria@4
|
103 nmels, nmelframes = self.melspec.shape
|
Maria@4
|
104 if fft: # periodicity via fft
|
Maria@4
|
105 #nfft2 = int(2**numpy.ceil(numpy.log2(win2)))
|
Maria@4
|
106 nfft2 = 2048 # nfft2 does not depend on win2??
|
Maria@4
|
107 melspectemp = self.melspec
|
Maria@4
|
108 if ((nfft2 > win2) and (center is False)):
|
Maria@4
|
109 # pad the signal by nfft2-win2 so that frame decomposition (n_frames)
|
Maria@4
|
110 # is still based on win2 and not nfft2
|
Maria@4
|
111 melspectemp = numpy.concatenate([numpy.zeros((nmels,int((nfft2 - win2) // 2))),self.melspec, numpy.zeros((nmels,int((nfft2 - win2) // 2)))],axis=1)
|
Maria@4
|
112 if melspectemp.shape[1]<nfft2:
|
Maria@4
|
113 # pad with zeros to have at least one 8-sec window
|
Maria@4
|
114 nzeros = nfft2 - melspectemp.shape[1]
|
Maria@4
|
115 melspectemp = numpy.concatenate([numpy.zeros((nmels,int(numpy.ceil(nzeros / 2.)))),melspectemp, numpy.zeros((nmels,int(numpy.ceil(nzeros / 2.))))],axis=1)
|
Maria@4
|
116 #melspectemp = numpy.concatenate([melspectemp, numpy.zeros((nmels,nzeros))],axis=1)
|
Maria@4
|
117 # nframes = int(round(numpy.ceil(round(nmelframes/hop2))))
|
Maria@4
|
118 ff0 = numpy.abs(librosa.stft(y=melspectemp[0, :], win_length=win2, hop_length=hop2, n_fft=nfft2, window=scipy.signal.hamming, center=center))
|
Maria@4
|
119 nframes = ff0.shape[1]
|
Maria@4
|
120 # nmags, nframes = ff0.shape
|
Maria@4
|
121 if logfilter: # log filterbank
|
Maria@4
|
122 nbins = 25
|
Maria@4
|
123 binsperoct = 5
|
Maria@4
|
124 fft2 = numpy.zeros((nmels, nbins, nframes))
|
Maria@4
|
125 logfb = librosa.filters.logfrequency(self.melsr, nfft2, n_bins=nbins, bins_per_octave=binsperoct, fmin=0.5)
|
Maria@4
|
126 # logfb = librosa.filters.logfrequency(self.melsr, nfft2, n_bins=25, bins_per_octave=5, fmin=0.5)
|
Maria@4
|
127 for i in range(nmels):
|
Maria@4
|
128 fftmags = numpy.abs(librosa.stft(y=melspectemp[i, :], win_length=win2, hop_length=hop2, n_fft=nfft2, window=scipy.signal.hamming, center=center))
|
Maria@4
|
129 fftmags = numpy.dot(logfb, fftmags) # log
|
Maria@4
|
130 fft2[i, :, :] = fftmags
|
Maria@4
|
131 else:
|
Maria@4
|
132 # nmags = min(round(nfft2/2), 200)
|
Maria@4
|
133 if bpmrange:
|
Maria@4
|
134 # freqresinbpm = float(self.melsr)/float(nfft2)*60.
|
Maria@4
|
135 freqresinbpm = float(self.melsr)/float(nfft2/2.)*60.
|
Maria@4
|
136 minmag = int(numpy.floor(30./freqresinbpm)) # min tempo 30bpm
|
Maria@4
|
137 maxmag = int(numpy.ceil(960./freqresinbpm)) # max tempo 960 bpm
|
Maria@4
|
138 magsinds = range(minmag, maxmag)
|
Maria@4
|
139 else:
|
Maria@4
|
140 magsinds = range(0, 200)
|
Maria@4
|
141 nmags = len(magsinds)
|
Maria@4
|
142 fft2 = numpy.zeros((nmels, nmags, nframes))
|
Maria@4
|
143 for i in range(nmels):
|
Maria@4
|
144 fftmags = numpy.abs(librosa.stft(y=melspectemp[i, :], win_length=win2, hop_length=hop2, n_fft=nfft2, window=scipy.signal.hamming, center=center))
|
Maria@4
|
145 fftmags = fftmags[magsinds, :]
|
Maria@4
|
146 fft2[i, :, :] = fftmags
|
Maria@4
|
147 op = fft2
|
Maria@4
|
148 else: # periodicity via autocorrelation
|
Maria@4
|
149 nmags = min(win2, 1000)
|
Maria@4
|
150 if nmelframes < win2:
|
Maria@4
|
151 nframes = 1
|
Maria@4
|
152 else:
|
Maria@4
|
153 nframes = int(nmelframes/float(win2))
|
Maria@4
|
154 autocor = numpy.zeros((nmels, nmags, nframes))
|
Maria@4
|
155 for i in range(nmels):
|
Maria@4
|
156 ac = numpy.zeros((nmags, nframes))
|
Maria@4
|
157 for j in range(nframes):
|
Maria@4
|
158 ac[:, j] = librosa.autocorrelate(self.melspec[i, (j*win2):min((j+1)*win2, nmelframes)], max_size=nmags)
|
Maria@4
|
159 autocor[i, :, :] = ac
|
Maria@4
|
160 op = autocor
|
Maria@4
|
161 self.op = op
|
Maria@4
|
162
|
Maria@4
|
163 def post_process_op(self, medianfiltOP=False):
|
Maria@4
|
164 if medianfiltOP:
|
Maria@4
|
165 hop2 = int(round(0.5*self.melsr))
|
Maria@4
|
166 ssr = self.melsr/float(hop2)
|
Maria@4
|
167 ks = int(0.5 * ssr) # 100ms kernel size
|
Maria@4
|
168 if ks % 2 == 0: # ks must be odd
|
Maria@4
|
169 ks += 1
|
Maria@4
|
170 nmels, nmags, nframes = self.op.shape
|
Maria@4
|
171 for i in range(nmels):
|
Maria@4
|
172 for j in range(nframes):
|
Maria@4
|
173 #self.op[i,:,j] = scipy.signal.medfilt(self.op[i,:,j],kernel_size = ks)
|
Maria@4
|
174 self.op[i,:,j] = numpy.convolve(self.op[i,:,j], numpy.ones(ks)/ks, mode='same')
|
Maria@4
|
175
|
Maria@4
|
176 def mellin_transform(self, op=None):
|
Maria@4
|
177 if self.op is None:
|
Maria@4
|
178 self.op = op
|
Maria@4
|
179 nmels, nmags, nframes = self.op.shape
|
Maria@4
|
180 #nmagsout = min(200, nmags)
|
Maria@4
|
181 nmagsout = 200
|
Maria@4
|
182 u_max = numpy.log(nmags)
|
Maria@4
|
183 delta_c = numpy.pi / u_max
|
Maria@4
|
184 c_max = nmagsout
|
Maria@4
|
185 c = numpy.arange(delta_c, c_max, delta_c)
|
Maria@4
|
186 k = range(1, nmags)
|
Maria@4
|
187 exponent = 0.5 - c*1j
|
Maria@4
|
188
|
Maria@4
|
189 normMat = 1./(exponent * numpy.sqrt(2*numpy.pi))
|
Maria@4
|
190 normMat.shape = (normMat.shape[0], 1)
|
Maria@4
|
191 normMat = numpy.repeat(normMat.T, nmels, axis=0)
|
Maria@4
|
192 kernelMat = numpy.asarray([numpy.power(ki, exponent) for ki in k])
|
Maria@4
|
193 opmellin = numpy.zeros((nmels, nmagsout, nframes))
|
Maria@4
|
194 for i in range(nframes):
|
Maria@4
|
195 self.op[:, -1, i] = 0
|
Maria@4
|
196 deltaMat = - numpy.diff(self.op[:, :, i])
|
Maria@4
|
197 mellin = numpy.abs(numpy.dot(deltaMat, kernelMat) * normMat)
|
Maria@4
|
198 opmellin[:, :, i] = mellin[:, :nmagsout]
|
Maria@4
|
199 self.opmellin = opmellin
|
Maria@4
|
200
|
Maria@4
|
201
|
Maria@4
|
202 def post_process_opmellin(self, opmellin=None, aveFreq=False, normFrame=True):
|
Maria@4
|
203 if self.opmellin is None:
|
Maria@4
|
204 self.opmellin = opmellin
|
Maria@4
|
205 if aveFreq:
|
Maria@4
|
206 self.opmellin = numpy.mean(self.opmellin, axis=0)
|
Maria@4
|
207 else: # just reshape
|
Maria@4
|
208 nmels, nmags, nframes = self.opmellin.shape
|
Maria@4
|
209 self.opmellin = self.opmellin.reshape((nmels*nmags, nframes))
|
Maria@4
|
210 if normFrame:
|
Maria@4
|
211 min_opmellin = numpy.amin(self.opmellin, axis=0, keepdims=True)
|
Maria@4
|
212 max_opmellin = numpy.amax(self.opmellin, axis=0, keepdims=True)
|
Maria@4
|
213 denom = max_opmellin - min_opmellin
|
Maria@4
|
214 denom[denom==0] = 1 # avoid division by 0 if frame is all 0s-silent
|
Maria@4
|
215 self.opmellin = (self.opmellin - min_opmellin) / denom
|
Maria@4
|
216
|
Maria@4
|
217
|
Maria@4
|
218 def get_opmellin(self, filename='test.wav', logfilter=False, center=True, medianfiltspec=False, medianfiltOP=False):
|
Maria@4
|
219 self.load_audiofile(filename=filename)
|
Maria@4
|
220 self.mel_spectrogram()
|
Maria@4
|
221 #self.post_process_spec(medianfilt=medianfiltspec)
|
Maria@4
|
222 self.post_process_spec(log=False, sqrt=True, medianfilt=medianfiltspec) # sqrt seems to work better
|
Maria@4
|
223 self.onset_patterns(logfilter=logfilter, center=center)
|
Maria@4
|
224 self.post_process_op(medianfiltOP=medianfiltOP)
|
Maria@4
|
225 self.mellin_transform()
|
Maria@4
|
226 self.post_process_opmellin()
|
Maria@4
|
227 return self.opmellin
|
Maria@4
|
228
|
Maria@4
|
229 def get_opmellin_from_melspec(self, melspec=[], melsr=[]):
|
Maria@4
|
230 self.melspec = melspec
|
Maria@4
|
231 self.melsr = melsr
|
Maria@4
|
232 self.post_process_spec(log=False, sqrt=True, medianfilt=True) # sqrt seems to work better
|
Maria@4
|
233 self.onset_patterns(logfilter=False, center=False)
|
Maria@4
|
234 self.post_process_op(medianfiltOP=True)
|
Maria@4
|
235 self.mellin_transform()
|
Maria@4
|
236 self.post_process_opmellin()
|
Maria@4
|
237 return self.opmellin
|
Maria@4
|
238
|
Maria@4
|
239 if __name__ == '__main__':
|
Maria@4
|
240 op = OPMellin()
|
Maria@4
|
241 op.get_opmellin()
|