Getting started with VamPy

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_templ...

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_templ...

Sonic Annotator:
http://www.omras2.org/SonicAnnotator/

Sonic Visualiser (Optional)
http://www.sonicvisualiser.com/