fazekasgy@37: '''PySpectralFeatures.py - Example plugin demonstrates fazekasgy@37: how to calculate and return simple low-level spectral fazekasgy@37: descriptors (curves) using Numpy and the buffer interface. fazekasgy@37: fazekasgy@37: Outputs: fazekasgy@37: 1) Spectral Centroid cannam@59: 2) Spectral Crest Factor fazekasgy@37: 3) Spectral Band-width fazekasgy@37: 4) Spectral Difference (first order) fazekasgy@37: fazekasgy@37: Centre for Digital Music, Queen Mary University of London. fazekasgy@37: Copyright (C) 2009 Gyorgy Fazekas, QMUL. (See Vamp sources fazekasgy@37: for licence information.) fazekasgy@37: fazekasgy@37: ''' fazekasgy@37: fazekasgy@37: from numpy import * fazekasgy@37: from vampy import * fazekasgy@37: fazekasgy@37: class PySpectralFeatures: fazekasgy@37: fazekasgy@37: def __init__(self,inputSampleRate): fazekasgy@37: fazekasgy@37: # flags: Chris@67: self.vampy_flags = vf_BUFFER | vf_REALTIME fazekasgy@37: fazekasgy@37: self.m_inputSampleRate = inputSampleRate fazekasgy@37: self.m_stepSize = 0 fazekasgy@37: self.m_blockSize = 0 fazekasgy@37: self.m_channels = 0 fazekasgy@37: self.threshold = 0.05 fazekasgy@37: return None fazekasgy@37: fazekasgy@37: def initialise(self,channels,stepSize,blockSize): fazekasgy@37: self.m_channels = channels fazekasgy@37: self.m_stepSize = stepSize fazekasgy@37: self.m_blockSize = blockSize fazekasgy@47: self.prevMag = zeros((blockSize/2)) fazekasgy@37: return True fazekasgy@37: fazekasgy@37: def reset(self): fazekasgy@37: # reset any initial conditions Chris@68: self.prevMag = zeros((self.m_blockSize/2)) fazekasgy@37: return None fazekasgy@37: fazekasgy@37: def getMaker(self): fazekasgy@37: return 'Vampy Example Plugins' fazekasgy@37: fazekasgy@37: def getName(self): fazekasgy@37: return 'Vampy Spectral Features' fazekasgy@37: fazekasgy@37: def getIdentifier(self): fazekasgy@37: return 'vampy-sf3' fazekasgy@37: fazekasgy@37: def getDescription(self): fazekasgy@37: return 'A collection of low-level spectral descriptors.' Chris@69: Chris@69: def getCopyright(self): Chris@69: return 'Plugin By George Fazekas. Freely redistributable example plugin (BSD license)' Chris@69: fazekasgy@37: def getMaxChannelCount(self): fazekasgy@37: return 1 fazekasgy@37: fazekasgy@37: def getInputDomain(self): fazekasgy@37: return FrequencyDomain fazekasgy@37: fazekasgy@37: def getOutputDescriptors(self): fazekasgy@37: fazekasgy@37: #Generic values are the same for all fazekasgy@37: Generic = OutputDescriptor() fazekasgy@37: Generic.hasFixedBinCount=True fazekasgy@37: Generic.binCount=1 fazekasgy@37: Generic.hasKnownExtents=False fazekasgy@37: Generic.isQuantized=False fazekasgy@37: Generic.sampleType = OneSamplePerStep fazekasgy@37: Generic.unit = 'Hz' fazekasgy@37: fazekasgy@37: #Spectral centroid etc... fazekasgy@37: SC = OutputDescriptor(Generic) fazekasgy@37: SC.identifier = 'vampy-sc' fazekasgy@37: SC.name = 'Spectral Centroid' fazekasgy@37: SC.description ='Spectral Centroid (Brightness)' fazekasgy@37: fazekasgy@37: SCF = OutputDescriptor(Generic) fazekasgy@37: SCF.identifier = 'vampy-scf' fazekasgy@37: SCF.name = 'Spectral Crest Factor' fazekasgy@37: SCF.description = 'Spectral Crest (Tonality)' fazekasgy@37: SCF.unit = 'v' fazekasgy@37: fazekasgy@37: BW = OutputDescriptor(Generic) fazekasgy@37: BW.identifier = 'vampy-bw' fazekasgy@37: BW.name = 'Band Width' fazekasgy@37: BW.description = 'Spectral Band Width' fazekasgy@37: fazekasgy@37: SD = OutputDescriptor(Generic) fazekasgy@37: SD.identifier = 'vampy-sd' fazekasgy@37: SD.name = 'Spectral Difference' fazekasgy@37: SD.description = 'Eucledian distance of successive magnitude spectra.' fazekasgy@37: fazekasgy@37: #return a tuple, list or OutputList(SC,SCF,BW) fazekasgy@37: return OutputList(SC,SCF,BW,SD) fazekasgy@37: fazekasgy@37: def getParameterDescriptors(self): fazekasgy@37: fazekasgy@37: threshold = ParameterDescriptor() fazekasgy@37: threshold.identifier='threshold' fazekasgy@37: threshold.name='Noise threshold' Chris@68: threshold.description='Magnitude below which a process block will be disregarded and zeroes returned' fazekasgy@37: threshold.unit='v' fazekasgy@37: threshold.minValue=0 fazekasgy@37: threshold.maxValue=1 fazekasgy@37: threshold.defaultValue=0.05 fazekasgy@37: threshold.isQuantized=False fazekasgy@37: fazekasgy@37: return ParameterList(threshold) fazekasgy@37: fazekasgy@37: def setParameter(self,paramid,newval): fazekasgy@37: if paramid == 'threshold' : fazekasgy@37: self.threshold = newval fazekasgy@37: return fazekasgy@37: fazekasgy@37: def getParameter(self,paramid): fazekasgy@37: if paramid == 'threshold' : fazekasgy@37: return self.threshold fazekasgy@37: else: fazekasgy@37: return 0.0 fazekasgy@37: fazekasgy@37: fazekasgy@37: # using the numpy memory buffer interface: fazekasgy@37: # flag : vf_BUFFER (or implement processN) fazekasgy@37: # NOTE: Vampy can now pass numpy arrays directly using fazekasgy@37: # the flag vf_ARRAY (see MFCC plugin for example) fazekasgy@37: def process(self,membuffer,timestamp): fazekasgy@37: fazekasgy@37: fftsize = self.m_blockSize fazekasgy@37: sampleRate = self.m_inputSampleRate fazekasgy@37: fazekasgy@37: #for time domain plugins use the following line: fazekasgy@37: #audioSamples = frombuffer(membuffer[0],float32) fazekasgy@37: fazekasgy@37: #for frequency domain plugins use: fazekasgy@37: complexSpectrum = frombuffer(membuffer[0],complex64,-1,8) fazekasgy@37: fazekasgy@37: # meaning of the parameters above: fazekasgy@37: # complex64 : data type of the created numpy array fazekasgy@37: # -1 : convert the whole buffer fazekasgy@37: # 8 : skip the DC component (2*32bit / 8bit = 8byte) fazekasgy@37: fazekasgy@37: magnitudeSpectrum = abs(complexSpectrum) / (fftsize*0.5) fazekasgy@37: #phaseSpectrum = angle(complexSpectrum) fazekasgy@37: fazekasgy@37: freq = array(range(1,len(complexSpectrum)+1)) \ fazekasgy@37: * sampleRate / fftsize fazekasgy@37: fazekasgy@37: # return features in a FeatureSet() fazekasgy@37: output_featureSet = FeatureSet() fazekasgy@37: fazekasgy@37: tpower = sum(magnitudeSpectrum) fazekasgy@37: fazekasgy@37: if tpower > self.threshold : fazekasgy@37: centroid = sum(freq * magnitudeSpectrum) / tpower fazekasgy@37: crest = max(magnitudeSpectrum) / tpower fazekasgy@37: bw = sum( abs(freq - centroid) * magnitudeSpectrum ) / tpower fazekasgy@37: normMag = magnitudeSpectrum / tpower fazekasgy@37: sd = sqrt(sum(power((normMag - self.prevMag),2))) fazekasgy@37: self.prevMag = normMag fazekasgy@37: else : fazekasgy@37: centroid = 0.0 fazekasgy@37: crest = 0.0 fazekasgy@37: bw = 0.0 fazekasgy@37: sd = 0.0 fazekasgy@37: fazekasgy@37: # Any value resulting from the process can be returned. fazekasgy@37: # It is no longer necessary to wrap single values into lists fazekasgy@37: # and convert numpy.floats to python floats, fazekasgy@37: # however a FeatureList() (or python list) can be returned fazekasgy@37: # if more than one feature is calculated per frame. fazekasgy@37: # The feature values can be e.g. int, float, list or array. fazekasgy@37: fazekasgy@37: output_featureSet[0] = Feature(centroid) fazekasgy@37: output_featureSet[1] = Feature(crest) fazekasgy@37: output_featureSet[2] = Feature(bw) fazekasgy@37: output_featureSet[3] = Feature(sd) fazekasgy@37: fazekasgy@37: return output_featureSet