This is a simple tutorial on writing Vamp plugins in Python using VamPy, and using the command line host Sonic Annotator.
1. Getting Started
Download and install all required software for your platform. These are listed at the bottom of this page.
1a. Unzip the Sonic Annotator package and place it in a directory of your choice.
1b. Unzip the VamPy package and place it in one of the Vamp plugin install locations:
File extension System plugin folder Linux: vampy.so /usr/local/lib/vamp OS/X: vampy.dylib /Library/Audio/Plug-Ins/Vamp Windows: vampy.dll C:\Program Files\Vamp Plugins Solaris: vampy.so /usr/local/lib/vamp
More info: http://www.vamp-plugins.org/download.html#install
1.c Place the example plugin to the same directory.
Alternatively you can put vampy plugins somewhere else and export the environment variable VAMPY_EXTPATH e.g.:
export VAMPY_PATH="/your/vampy/plugin/directory"
2. Testing Sonic Annotator and the available plugins
List the available plugins using the command:
sonic-annotator -l
you should be able to see the line in the results:
vamp:vampy:vampy-unique-ID:vampy-unique-outputID
This is a unique identifier of the only output of the example plugin. You should replace 'vampy-unique-ID' and 'vampy-unique-outputID' with an ID of your choice in the plugin code.
3. Writing your first Vamp plugin
3a. Grab the file VamPyTemplatePlug.py which provides a simple skeleton for a Vamp plugin in Python. Using your favorite text editor, open the file and replace the string in the following functions with an ID of your choice:
def getIdentifier(self): return 'vampy-unique-ID'
def getOutputDescriptors(self): Generic.identifier = 'vampy-unique-outputID'
If you run Sonic Annotator again, you should be able
to see the newly created plugin IDs in the output.
Change your name etc...
3b. The example plugin returns the energy computed from the magnitude spectrum for each processing block. This computation is performed in the process() function.
def process(self,inputbuffers,timestamp): length = self.m_blockSize * 0.5 + 1 sampleRate = self.m_inputSampleRate complexSpectrum = inputbuffers[0] magnitudeSpectrum = abs(complexSpectrum) / length tpower = sum(magnitudeSpectrum)
Modify this function using an algorithm of your choice, or use the example provided at the bottom of this page.
4. Writing a simple onset detector
We will create a very simple onset detector using
High Frequency Content.
4a. Create a weighting function in process()
def process(self,inputbuffers,timestamp):
length = self.m_blockSize * 0.5 + 1
sampleRate = self.m_inputSampleRate
#weighting function
w = array(xrange(length)) / length
4b. Detection function and peak picking:
def process(self,inputbuffers,timestamp): [...] complexSpectrum = inputbuffers[0] magnitudeSpectrum = abs(complexSpectrum) / length weightedSpectrum = magnitudeSpectrum * w tpower = sum(weightedSpectrum) peak = False greater = False if tpower > self.prev : greater = True if tpower > self.threshold : if self.wasGreater and not greater : peak = True
4c. Return the onset times and store previous values:
def process(self,inputbuffers,timestamp): [...] # return features in a FeatureSet() output_featureSet = FeatureSet() if peak : output_featureSet[0] = Feature() output_featureSet[0].timestamp = self.prevTime output_featureSet[0].hasTimestamp = True # store previous values for the next process self.prev = tpower self.wasGreater = greater self.prevTime = timestamp return output_featureSet
4d. Do some house-keeping:
in the __init__() function add:
self.prev = 0.0 self.wasGreater = False self.prevTime = 0
in getOutputDescriptors() modify:
Generic.hasFixedBinCount=True Generic.binCount=0 Generic.sampleType = VariableSampleRate
That's all!
5. Using Sonic Annotator to produce results in RDF
Grab an audio file and type at the command line:
$ sonic-annotator -d vamp:vampy:yourID:yourOutputID audio_file.wav -w rdf --rdf-stdout > onsets.n3
where yourID and yourOutputID are the IDs you used for your plugin.
You may also use Sonic Annotator to produce simple comma separated values (CSV) using the csv writer option.
6. Loading the results in Sonic Visualiser
Open the audio file.
File->Import Audio File
Load the onset detection results:
File->Import Annotation Layer
choose RDF, then onsets.n3
Overlay the results for the audio:
Layer->Add Existing Layer->Instants
7. Full example process() function
If completing the process() function in the example above was hard to follow, here's the full implementation:
def process(self,inputbuffers,timestamp): # this is where features are computed length = self.m_blockSize * 0.5 + 1 sampleRate = self.m_inputSampleRate #weight w = array(xrange(length)) / length complexSpectrum = inputbuffers[0] magnitudeSpectrum = abs(complexSpectrum) / length weightedSpectrum = w * magnitudeSpectrum tpower = sum(weightedSpectrum) peak = False greater = False if tpower > self.prev : greater = True if tpower > self.threshold : if self.wasGreater and not greater : peak = True # return features in a FeatureSet() output_featureSet = FeatureSet() if peak : output_featureSet[0] = Feature() output_featureSet[0].timestamp = self.prevTime output_featureSet[0].hasTimestamp = True self.wasGreater = greater self.prevTime = timestamp self.prev=tpower return output_featureSet
You may also download this tutorial from here:
http://github.com/kurtjx/MatWoD/blob/master/examples/scripts/vampy_temp…
Requirements:
Python 2.5 or 2.6 with NumPy 1.1 or greater.
http://www.python.org/
http://www.numpy.org/
Vampy:
http://www.vamp-plugins.org/vampy.html
VamPyTemplatePlug.py :
http://github.com/kurtjx/MatWoD/blob/master/examples/scripts/vampy_temp…
Sonic Annotator:
http://www.omras2.org/SonicAnnotator/
Sonic Visualiser (Optional)
http://www.sonicvisualiser.com/