annotate Example VamPy plugins/test/PyMFCC_time.py @ 120:a38d318c85a9 tip

MSVC fixes
author Chris Cannam
date Wed, 18 Dec 2019 16:51:20 +0000
parents 27bab3a16c9a
children
rev   line source
fazekasgy@37 1 '''PyMFCC_time.py - This example Vampy plugin demonstrates
fazekasgy@37 2 how to return sprectrogram-like features.
fazekasgy@37 3
fazekasgy@37 4 This plugin has time domain input and is using
fazekasgy@37 5 the numpy array interface. Flag: vf_ARRAY
fazekasgy@37 6
fazekasgy@37 7 Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 8 Copyright 2006 Gyorgy Fazekas, QMUL.
fazekasgy@37 9 (See Vamp API for licence information.)
fazekasgy@37 10
fazekasgy@37 11 Constants for Mel frequency conversion and filter
fazekasgy@37 12 centre calculation are taken from the GNU GPL licenced
fazekasgy@37 13 Freespeech library. Copyright (C) 1999 Jean-Marc Valin
fazekasgy@37 14 '''
fazekasgy@37 15
fazekasgy@37 16 import sys,numpy
fazekasgy@37 17 from numpy import log,exp,floor,sum
fazekasgy@37 18 from numpy import *
fazekasgy@37 19 from numpy.fft import *
fazekasgy@37 20 import vampy
fazekasgy@37 21 from vampy import *
fazekasgy@37 22
fazekasgy@37 23
fazekasgy@37 24 class melScaling(object):
fazekasgy@37 25
fazekasgy@37 26 def __init__(self,sampleRate,inputSize,numBands,minHz = 0,maxHz = None):
fazekasgy@37 27 '''Initialise frequency warping and DCT matrix.
fazekasgy@37 28 Parameters:
fazekasgy@37 29 sampleRate: audio sample rate
fazekasgy@37 30 inputSize: length of magnitude spectrum (half of FFT size assumed)
fazekasgy@37 31 numBands: number of mel Bands (MFCCs)
fazekasgy@37 32 minHz: lower bound of warping (default = DC)
fazekasgy@37 33 maxHz: higher bound of warping (default = Nyquist frequency)
fazekasgy@37 34 '''
fazekasgy@37 35 self.sampleRate = sampleRate
fazekasgy@37 36 self.NqHz = sampleRate / 2.0
fazekasgy@37 37 self.minHz = minHz
fazekasgy@37 38 if maxHz is None : maxHz = self.NqHz
fazekasgy@37 39 self.maxHz = maxHz
fazekasgy@37 40 self.inputSize = inputSize
fazekasgy@37 41 self.numBands = numBands
fazekasgy@37 42 self.valid = False
fazekasgy@37 43 self.updated = False
fazekasgy@37 44
fazekasgy@37 45
fazekasgy@37 46 def update(self):
fazekasgy@37 47 # make sure this will run only once if called from a vamp process
fazekasgy@37 48
fazekasgy@37 49 if self.updated: return self.valid
fazekasgy@37 50 self.updated = True
fazekasgy@37 51 self.valid = False
fazekasgy@37 52 print 'Updating parameters and recalculating filters: '
fazekasgy@37 53 print 'Nyquist: ',self.NqHz
fazekasgy@37 54
fazekasgy@37 55 if self.maxHz > self.NqHz :
fazekasgy@37 56 raise Exception('Maximum frequency must be smaller than the Nyquist frequency')
fazekasgy@37 57
fazekasgy@37 58 self.maxMel = 1000*log(1+self.maxHz/700.0)/log(1+1000.0/700.0)
fazekasgy@37 59 self.minMel = 1000*log(1+self.minHz/700.0)/log(1+1000.0/700.0)
fazekasgy@37 60 print 'minHz:%s\nmaxHz:%s\nminMel:%s\nmaxMel:%s\n' %(self.minHz,self.maxHz,self.minMel,self.maxMel)
fazekasgy@37 61 self.filterMatrix = self.getFilterMatrix(self.inputSize,self.numBands)
fazekasgy@37 62 self.DCTMatrix = self.getDCTMatrix(self.numBands)
fazekasgy@37 63 self.filterIter = self.filterMatrix.__iter__()
fazekasgy@37 64 self.valid = True
fazekasgy@37 65 return self.valid
fazekasgy@37 66
fazekasgy@37 67 # try :
fazekasgy@37 68 # self.maxMel = 1000*log(1+self.maxHz/700.0)/log(1+1000.0/700.0)
fazekasgy@37 69 # self.minMel = 1000*log(1+self.minHz/700.0)/log(1+1000.0/700.0)
fazekasgy@37 70 # self.filterMatrix = self.getFilterMatrix(self.inputSize,self.numBands)
fazekasgy@37 71 # self.DCTMatrix = self.getDCTMatrix(self.numBands)
fazekasgy@37 72 # self.filterIter = self.filterMatrix.__iter__()
fazekasgy@37 73 # self.valid = True
fazekasgy@37 74 # return True
fazekasgy@37 75 # except :
fazekasgy@37 76 # print "Invalid parameter setting encountered in MelScaling class."
fazekasgy@37 77 # return False
fazekasgy@37 78 # return True
fazekasgy@37 79
fazekasgy@37 80 def getFilterCentres(self,inputSize,numBands):
fazekasgy@37 81 '''Calculate Mel filter centres around FFT bins.
fazekasgy@37 82 This function calculates two extra bands at the edges for
fazekasgy@37 83 finding the starting and end point of the first and last
fazekasgy@37 84 actual filters.'''
fazekasgy@37 85 centresMel = numpy.array(xrange(numBands+2)) * (self.maxMel-self.minMel)/(numBands+1) + self.minMel
fazekasgy@37 86 centresBin = numpy.floor(0.5 + 700.0*inputSize*(exp(centresMel*log(1+1000.0/700.0)/1000.0)-1)/self.NqHz)
fazekasgy@37 87 return numpy.array(centresBin,int)
fazekasgy@37 88
fazekasgy@37 89 def getFilterMatrix(self,inputSize,numBands):
fazekasgy@37 90 '''Compose the Mel scaling matrix.'''
fazekasgy@37 91 filterMatrix = numpy.zeros((numBands,inputSize))
fazekasgy@37 92 self.filterCentres = self.getFilterCentres(inputSize,numBands)
fazekasgy@37 93 for i in xrange(numBands) :
fazekasgy@37 94 start,centre,end = self.filterCentres[i:i+3]
fazekasgy@37 95 self.setFilter(filterMatrix[i],start,centre,end)
fazekasgy@37 96 return filterMatrix.transpose()
fazekasgy@37 97
fazekasgy@37 98 def setFilter(self,filt,filterStart,filterCentre,filterEnd):
fazekasgy@37 99 '''Calculate a single Mel filter.'''
fazekasgy@37 100 k1 = numpy.float32(filterCentre-filterStart)
fazekasgy@37 101 k2 = numpy.float32(filterEnd-filterCentre)
fazekasgy@37 102 up = (numpy.array(xrange(filterStart,filterCentre))-filterStart)/k1
fazekasgy@37 103 dn = (filterEnd-numpy.array(xrange(filterCentre,filterEnd)))/k2
fazekasgy@37 104 filt[filterStart:filterCentre] = up
fazekasgy@37 105 filt[filterCentre:filterEnd] = dn
fazekasgy@37 106
fazekasgy@37 107 def warpSpectrum(self,magnitudeSpectrum):
fazekasgy@37 108 '''Compute the Mel scaled spectrum.'''
fazekasgy@37 109 return numpy.dot(magnitudeSpectrum,self.filterMatrix)
fazekasgy@37 110
fazekasgy@37 111 def getDCTMatrix(self,size):
fazekasgy@37 112 '''Calculate the square DCT transform matrix. Results are
fazekasgy@37 113 equivalent to Matlab dctmtx(n) but with 64 bit precision.'''
fazekasgy@37 114 DCTmx = numpy.array(xrange(size),numpy.float64).repeat(size).reshape(size,size)
fazekasgy@37 115 DCTmxT = numpy.pi * (DCTmx.transpose()+0.5) / size
fazekasgy@37 116 DCTmxT = (1.0/sqrt( size / 2.0)) * cos(DCTmx * DCTmxT)
fazekasgy@37 117 DCTmxT[0] = DCTmxT[0] * (sqrt(2.0)/2.0)
fazekasgy@37 118 return DCTmxT
fazekasgy@37 119
fazekasgy@37 120 def dct(self,data_matrix):
fazekasgy@37 121 '''Compute DCT of input matrix.'''
fazekasgy@37 122 return numpy.dot(self.DCTMatrix,data_matrix)
fazekasgy@37 123
fazekasgy@37 124 def getMFCCs(self,warpedSpectrum,cn=True):
fazekasgy@37 125 '''Compute MFCC coefficients from Mel warped magnitude spectrum.'''
fazekasgy@37 126 mfccs=self.dct(numpy.log(warpedSpectrum))
fazekasgy@37 127 if cn is False : mfccs[0] = 0.0
fazekasgy@37 128 return mfccs
fazekasgy@37 129
fazekasgy@37 130
fazekasgy@37 131 class PyMFCC_time(melScaling):
fazekasgy@37 132
fazekasgy@37 133 def __init__(self,inputSampleRate):
fazekasgy@37 134
fazekasgy@37 135 # flags for setting some Vampy options
fazekasgy@37 136 self.vampy_flags = vf_DEBUG | vf_ARRAY | vf_REALTIME
fazekasgy@37 137
fazekasgy@37 138 self.m_inputSampleRate = int(inputSampleRate)
fazekasgy@37 139 self.m_stepSize = 512
fazekasgy@37 140 self.m_blockSize = 2048
fazekasgy@37 141 self.m_channels = 1
fazekasgy@37 142 self.numBands = 40
fazekasgy@37 143 self.cnull = 1
fazekasgy@37 144 self.two_ch = False
fazekasgy@37 145 melScaling.__init__(self,int(self.m_inputSampleRate),self.m_blockSize/2,self.numBands)
fazekasgy@37 146
fazekasgy@37 147 def initialise(self,channels,stepSize,blockSize):
fazekasgy@37 148 self.m_channels = channels
fazekasgy@37 149 self.m_stepSize = stepSize
fazekasgy@37 150 self.m_blockSize = blockSize
fazekasgy@37 151 self.window = numpy.hamming(blockSize)
fazekasgy@37 152 melScaling.__init__(self,int(self.m_inputSampleRate),self.m_blockSize/2,self.numBands)
fazekasgy@37 153 return True
fazekasgy@37 154
fazekasgy@37 155 def getMaker(self):
fazekasgy@37 156 return 'Vampy Test Plugins'
fazekasgy@37 157
fazekasgy@37 158 def getCopyright(self):
fazekasgy@37 159 return 'Plugin By George Fazekas'
fazekasgy@37 160
fazekasgy@37 161 def getName(self):
fazekasgy@37 162 return 'Vampy TimeDomain MFCC Plugin'
fazekasgy@37 163
fazekasgy@37 164 def getIdentifier(self):
fazekasgy@37 165 return 'vampy-mfcc-test-timedomain'
fazekasgy@37 166
fazekasgy@37 167 def getDescription(self):
fazekasgy@37 168 return 'A simple MFCC plugin. (TimeDomain)'
fazekasgy@37 169
fazekasgy@37 170 def getMaxChannelCount(self):
fazekasgy@37 171 return 2
fazekasgy@37 172
fazekasgy@37 173 def getInputDomain(self):
fazekasgy@37 174 return TimeDomain
fazekasgy@37 175
fazekasgy@37 176 def getPreferredBlockSize(self):
fazekasgy@37 177 return 2048
fazekasgy@37 178
fazekasgy@37 179 def getPreferredStepSize(self):
fazekasgy@37 180 return 512
fazekasgy@37 181
fazekasgy@37 182 def getOutputDescriptors(self):
fazekasgy@37 183
fazekasgy@37 184 Generic = OutputDescriptor()
fazekasgy@37 185 Generic.hasFixedBinCount=True
fazekasgy@37 186 Generic.binCount=int(self.numBands)-self.cnull
fazekasgy@37 187 Generic.hasKnownExtents=False
fazekasgy@37 188 Generic.isQuantized=True
fazekasgy@37 189 Generic.sampleType = OneSamplePerStep
fazekasgy@37 190
fazekasgy@37 191 # note the inheritance of attributes (use is optional)
fazekasgy@37 192 MFCC = OutputDescriptor(Generic)
fazekasgy@37 193 MFCC.identifier = 'mfccs'
fazekasgy@37 194 MFCC.name = 'MFCCs'
fazekasgy@37 195 MFCC.description = 'MFCC Coefficients'
fazekasgy@37 196 MFCC.binNames=map(lambda x: 'C '+str(x),range(self.cnull,int(self.numBands)))
fazekasgy@37 197 MFCC.unit = None
fazekasgy@37 198 if self.two_ch and self.m_channels == 2 :
fazekasgy@37 199 MFCC.binCount = self.m_channels * (int(self.numBands)-self.cnull)
fazekasgy@37 200 else :
fazekasgy@37 201 MFCC.binCount = self.numBands-self.cnull
fazekasgy@37 202
fazekasgy@37 203 warpedSpectrum = OutputDescriptor(Generic)
fazekasgy@37 204 warpedSpectrum.identifier='warped-fft'
fazekasgy@37 205 warpedSpectrum.name='Mel Scaled Spectrum'
fazekasgy@37 206 warpedSpectrum.description='Mel Scaled Magnitide Spectrum'
fazekasgy@37 207 warpedSpectrum.unit='Mel'
fazekasgy@37 208 if self.two_ch and self.m_channels == 2 :
fazekasgy@37 209 warpedSpectrum.binCount = self.m_channels * int(self.numBands)
fazekasgy@37 210 else :
fazekasgy@37 211 warpedSpectrum.binCount = self.numBands
fazekasgy@37 212
fazekasgy@37 213 melFilter = OutputDescriptor(Generic)
fazekasgy@37 214 melFilter.identifier = 'mel-filter-matrix'
fazekasgy@37 215 melFilter.sampleType='FixedSampleRate'
fazekasgy@37 216 melFilter.sampleRate=self.m_inputSampleRate/self.m_stepSize
fazekasgy@37 217 melFilter.name='Mel Filter Matrix'
fazekasgy@37 218 melFilter.description='Returns the created filter matrix in getRemainingFeatures.'
fazekasgy@37 219 melFilter.unit = None
fazekasgy@37 220
fazekasgy@37 221 return OutputList(MFCC,warpedSpectrum,melFilter)
fazekasgy@37 222
fazekasgy@37 223
fazekasgy@37 224 def getParameterDescriptors(self):
fazekasgy@37 225
fazekasgy@37 226 melbands = ParameterDescriptor()
fazekasgy@37 227 melbands.identifier='melbands'
fazekasgy@37 228 melbands.name='Number of bands (coefficients)'
fazekasgy@37 229 melbands.description='Set the number of coefficients.'
fazekasgy@37 230 melbands.unit = ''
fazekasgy@37 231 melbands.minValue = 2
fazekasgy@37 232 melbands.maxValue = 128
fazekasgy@37 233 melbands.defaultValue = 40
fazekasgy@37 234 melbands.isQuantized = True
fazekasgy@37 235 melbands.quantizeStep = 1
fazekasgy@37 236
fazekasgy@37 237 cnull = ParameterDescriptor()
fazekasgy@37 238 cnull.identifier='cnull'
fazekasgy@37 239 cnull.name='Return C0'
fazekasgy@37 240 cnull.description='Select if the DC coefficient is required.'
fazekasgy@37 241 cnull.unit = None
fazekasgy@37 242 cnull.minValue = 0
fazekasgy@37 243 cnull.maxValue = 1
fazekasgy@37 244 cnull.defaultValue = 0
fazekasgy@37 245 cnull.isQuantized = True
fazekasgy@37 246 cnull.quantizeStep = 1
fazekasgy@37 247
fazekasgy@37 248 two_ch = ParameterDescriptor(cnull)
fazekasgy@37 249 two_ch.identifier='two_ch'
fazekasgy@37 250 two_ch.name='Process channels separately'
fazekasgy@37 251 two_ch.description='Process two channel files separately.'
fazekasgy@37 252 two_ch.defaultValue = False
fazekasgy@37 253
fazekasgy@37 254 minHz = ParameterDescriptor()
fazekasgy@37 255 minHz.identifier='minHz'
fazekasgy@37 256 minHz.name='minimum frequency'
fazekasgy@37 257 minHz.description='Set the lower frequency bound.'
fazekasgy@37 258 minHz.unit='Hz'
fazekasgy@37 259 minHz.minValue = 0
fazekasgy@37 260 minHz.maxValue = 24000
fazekasgy@37 261 minHz.defaultValue = 0
fazekasgy@37 262 minHz.isQuantized = True
fazekasgy@37 263 minHz.quantizeStep = 1.0
fazekasgy@37 264
fazekasgy@37 265 maxHz = ParameterDescriptor()
fazekasgy@37 266 maxHz.identifier='maxHz'
fazekasgy@37 267 maxHz.description='Set the upper frequency bound.'
fazekasgy@37 268 maxHz.name='maximum frequency'
fazekasgy@37 269 maxHz.unit='Hz'
fazekasgy@37 270 maxHz.minValue = 100
fazekasgy@37 271 maxHz.maxValue = 24000
fazekasgy@37 272 maxHz.defaultValue = 11025
fazekasgy@37 273 maxHz.isQuantized = True
fazekasgy@37 274 maxHz.quantizeStep = 100
fazekasgy@37 275
fazekasgy@37 276 return ParameterList(melbands,minHz,maxHz,cnull,two_ch)
fazekasgy@37 277
fazekasgy@37 278
fazekasgy@37 279 def setParameter(self,paramid,newval):
fazekasgy@37 280 self.valid = False
fazekasgy@37 281 if paramid == 'minHz' :
fazekasgy@37 282 if newval < self.maxHz and newval < self.NqHz :
fazekasgy@37 283 self.minHz = float(newval)
fazekasgy@37 284 print 'minHz: ', self.minHz
fazekasgy@37 285 if paramid == 'maxHz' :
fazekasgy@37 286 print 'trying to set maxHz to: ',newval
fazekasgy@37 287 if newval < self.NqHz and newval > self.minHz+1000 :
fazekasgy@37 288 self.maxHz = float(newval)
fazekasgy@37 289 else :
fazekasgy@37 290 self.maxHz = self.NqHz
fazekasgy@37 291 print 'set to: ',self.maxHz
fazekasgy@37 292 if paramid == 'cnull' :
fazekasgy@37 293 self.cnull = int(not int(newval))
fazekasgy@37 294 if paramid == 'melbands' :
fazekasgy@37 295 self.numBands = int(newval)
fazekasgy@37 296 if paramid == 'two_ch' :
fazekasgy@37 297 self.two_ch = bool(newval)
fazekasgy@37 298
fazekasgy@37 299 return
fazekasgy@37 300
fazekasgy@37 301 def getParameter(self,paramid):
fazekasgy@37 302 if paramid == 'minHz' :
fazekasgy@37 303 return float(self.minHz)
fazekasgy@37 304 if paramid == 'maxHz' :
fazekasgy@37 305 return float(self.maxHz)
fazekasgy@37 306 if paramid == 'cnull' :
fazekasgy@37 307 return float(not int(self.cnull))
fazekasgy@37 308 if paramid == 'melbands' :
fazekasgy@37 309 return float(self.numBands)
fazekasgy@37 310 if paramid == 'two_ch' :
fazekasgy@37 311 return float(self.two_ch)
fazekasgy@37 312 else:
fazekasgy@37 313 return 0.0
fazekasgy@37 314
fazekasgy@37 315 # set numpy process using the 'use_numpy_interface' flag
fazekasgy@37 316 def process(self,inputbuffers,timestamp):
fazekasgy@37 317
fazekasgy@37 318 if self.m_channels == 2 and self.two_ch :
fazekasgy@37 319 return self.process2ch(inputbuffers,timestamp)
fazekasgy@37 320
fazekasgy@37 321 # calculate the filter and DCT matrices, check
fazekasgy@37 322 # if they are computable given a set of parameters
fazekasgy@37 323 # (we only do this once, when the process is called first)
fazekasgy@37 324 if not self.update() : return None
fazekasgy@37 325
fazekasgy@37 326 fftsize = self.m_blockSize
fazekasgy@37 327
fazekasgy@37 328 if self.m_channels > 1 :
fazekasgy@37 329 audioSamples = (inputbuffers[0]+inputbuffers[1])/2
fazekasgy@37 330 else :
fazekasgy@37 331 audioSamples = inputbuffers[0]
fazekasgy@37 332
fazekasgy@37 333 #complexSpectrum = frombuffer(membuffer[0],complex64,-1,8)
fazekasgy@37 334 complexSpectrum = fft(self.window*audioSamples,fftsize)
fazekasgy@37 335 magnitudeSpectrum = abs(complexSpectrum)[0:fftsize/2] / (fftsize/2)
fazekasgy@37 336
fazekasgy@37 337 # do the computation
fazekasgy@37 338 melSpectrum = self.warpSpectrum(magnitudeSpectrum)
fazekasgy@37 339 melCepstrum = self.getMFCCs(melSpectrum,cn=True)
fazekasgy@37 340
fazekasgy@37 341 # output feature set (the builtin dict type can also be used)
fazekasgy@37 342 outputs = FeatureSet()
fazekasgy@37 343 outputs[0] = Feature(melCepstrum[self.cnull:])
fazekasgy@37 344 outputs[1] = Feature(melSpectrum)
fazekasgy@37 345
fazekasgy@37 346 return outputs
fazekasgy@37 347
fazekasgy@37 348
fazekasgy@37 349 # process two channel files (stack the returned arrays)
fazekasgy@37 350 def process2ch(self,inputbuffers,timestamp):
fazekasgy@37 351 if not self.update() : return None
fazekasgy@37 352
fazekasgy@37 353 fftsize = self.m_blockSize
fazekasgy@37 354
fazekasgy@37 355 audioSamples0 = inputbuffers[0]
fazekasgy@37 356 audioSamples1 = inputbuffers[1]
fazekasgy@37 357
fazekasgy@37 358 complexSpectrum0 = fft(self.window*audioSamples0,fftsize)
fazekasgy@37 359 complexSpectrum1 = fft(self.window*audioSamples1,fftsize)
fazekasgy@37 360
fazekasgy@37 361 magnitudeSpectrum0 = abs(complexSpectrum0)[0:fftsize/2] / (fftsize/2)
fazekasgy@37 362 magnitudeSpectrum1 = abs(complexSpectrum1)[0:fftsize/2] / (fftsize/2)
fazekasgy@37 363
fazekasgy@37 364 # do the computation
fazekasgy@37 365 melSpectrum0 = self.warpSpectrum(magnitudeSpectrum0)
fazekasgy@37 366 melCepstrum0 = self.getMFCCs(melSpectrum0,cn=True)
fazekasgy@37 367 melSpectrum1 = self.warpSpectrum(magnitudeSpectrum1)
fazekasgy@37 368 melCepstrum1 = self.getMFCCs(melSpectrum1,cn=True)
fazekasgy@37 369
fazekasgy@37 370 outputs = FeatureSet()
fazekasgy@37 371 outputs[0] = Feature(hstack((melCepstrum1[self.cnull:],melCepstrum0[self.cnull:])))
fazekasgy@37 372 outputs[1] = Feature(hstack((melSpectrum1,melSpectrum0)))
fazekasgy@37 373
fazekasgy@37 374 return outputs
fazekasgy@37 375
fazekasgy@37 376
fazekasgy@37 377 def getRemainingFeatures(self):
fazekasgy@37 378 if not self.update() : return []
fazekasgy@37 379 frameSampleStart = 0
fazekasgy@37 380
fazekasgy@37 381 output_featureSet = FeatureSet()
fazekasgy@37 382
fazekasgy@37 383 # the filter is the third output (index starts from zero)
fazekasgy@37 384 output_featureSet[2] = flist = FeatureList()
fazekasgy@37 385
fazekasgy@37 386 while True:
fazekasgy@37 387 f = Feature()
fazekasgy@37 388 f.hasTimestamp = True
fazekasgy@37 389 f.timestamp = frame2RealTime(frameSampleStart,self.m_inputSampleRate)
fazekasgy@37 390 try :
fazekasgy@37 391 f.values = self.filterIter.next()
fazekasgy@37 392 except StopIteration :
fazekasgy@37 393 break
fazekasgy@37 394 flist.append(f)
fazekasgy@37 395 frameSampleStart += self.m_stepSize
fazekasgy@37 396
fazekasgy@37 397 return output_featureSet
fazekasgy@37 398