view plugin/FeatureExtractionPlugin.h @ 61:749b5521e082

* adjust for latency if the plugin reports it
author Chris Cannam
date Mon, 27 Mar 2006 16:15:19 +0000
parents 0a34d529f8e0
children
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    Sonic Visualiser
    An audio file viewer and annotation editor.
    Centre for Digital Music, Queen Mary, University of London.
    This file copyright 2006 Chris Cannam.
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#ifndef _FEATURE_EXTRACTION_PLUGIN_H_
#define _FEATURE_EXTRACTION_PLUGIN_H_

#include "PluginInstance.h"

#include <string>
#include <vector>
#include <map>

#include "base/RealTime.h"

/**
 * FeatureExtractionPlugin is a base class for plugin instance classes
 * that provide feature extraction from audio or related data.
 *
 * In most cases, the input will be audio and the output will be a
 * stream of derived data at a lower sampling resolution than the
 * input.
 *
 * Note that this class inherits several abstract methods from
 * PluginInstance, that must be implemented by the subclass.
 */

/**
 * Plugin Lifecycle
 * ================
 *
 * Feature extraction plugins are managed differently from real-time
 * plugins.  The main difference is that the parameters for a feature
 * extraction plugin are configured before the plugin is used, and do
 * not change during use.
 *
 * 1. Host constructs the plugin, passing it the input sample rate.
 * The plugin may do basic initialisation, but should not do anything
 * computationally expensive at this point.
 *
 * 2. Host may query the plugin's available outputs.
 *
 * 3. Host queries programs and parameter descriptors, and may set
 * some or all of them.  Parameters that are not explicitly set should
 * take their default values as specified in the parameter descriptor.
 * When a program is set, the parameter values may change and the host
 * will re-query them to check.
 *
 * 4. Host queries the preferred step size, block size, number of
 * channels, and the number of values per feature for the plugin's
 * outputs.  These may all vary depending on the parameter values.
 *
 * 5. Plugin is properly initialised with a call to initialise.  This
 * fixes the step size, block size, and number of channels, as well as
 * all of the parameter and program settings.  If the values passed in
 * to initialise do not match the plugin's advertised preferred values
 * from step 4, the plugin may refuse to initialise and return false
 * (although if possible it should accept the new values).
 *
 * 6. Host will repeatedly call the process method to pass in blocks
 * of input data.  This method may return features extracted from that
 * data (if the plugin is causal).
 *
 * 7. Host will call getRemainingFeatures exactly once, after all the
 * input data has been processed.  This may return any non-causal or
 * leftover features.
 *
 * 8. At any point after initialise was called, the host may
 * optionally call the reset method and restart processing.  (This
 * does not mean it can change the parameters, which are fixed from
 * initialise until destruction.)
 *
 * A plugin does not need to handle the case where setParameter or
 * selectProgram is called after initialise has been called.  It's the
 * host's responsibility not to do that.
 */

class FeatureExtractionPlugin : public PluginInstance
{
public:
    /**
     * Initialise a plugin to prepare it for use with the given number
     * of input channels, step size (window increment, in sample
     * frames) and block size (window size, in sample frames).
     *
     * The input sample rate should have been already specified at
     * construction time.
     * 
     * Return true for successful initialisation, false if the number
     * of input channels, step size and/or block size cannot be
     * supported.
     */
    virtual bool initialise(size_t inputChannels,
			    size_t stepSize,
			    size_t blockSize) = 0;

    /**
     * Reset the plugin after use, to prepare it for another clean
     * run.  Not called for the first initialisation (i.e. initialise
     * must also do a reset).
     */
    virtual void reset() = 0;

    /**
     * Get the preferred step size (window increment -- the distance
     * in sample frames between the start frames of consecutive blocks
     * passed to the process() function) for the plugin.  This should
     * be called before initialise().
     */
    virtual size_t getPreferredStepSize() const = 0;

    /**
     * Get the preferred block size (window size -- the number of
     * sample frames passed in each block to the process() function).
     * This should be called before initialise().
     */
    virtual size_t getPreferredBlockSize() const { return getPreferredStepSize(); }

    /**
     * Get the minimum supported number of input channels.
     */
    virtual size_t getMinChannelCount() const { return 1; }

    /**
     * Get the maximum supported number of input channels.
     */
    virtual size_t getMaxChannelCount() const { return 1; }

    struct OutputDescriptor
    {
	/**
	 * The name of the output, in computer-usable form.  Should be
	 * reasonably short and without whitespace or punctuation.
	 */
	std::string name;

	/**
	 * The human-readable name of the output.
	 */
	std::string description;

	/**
	 * The unit of the output, in human-readable form.
	 */
	std::string unit;

	/**
	 * True if the output has the same number of values per result
	 * for every output result.  Outputs for which this is false
	 * are unlikely to be very useful in a general-purpose host.
	 */
	bool hasFixedValueCount;

	/**
	 * The number of values per result of the output.  Undefined
	 * if hasFixedValueCount is false.  If this is zero, the output
	 * is point data (i.e. only the time of each output is of
	 * interest, the value list will be empty).
	 *
	 * Note that this gives the number of values of a single
	 * output result, not of the output stream (which has one more
	 * dimension: time).
	 */
	size_t valueCount;

	/**
	 * The names of each of the values, if appropriate.  This is
	 * always optional.
	 */
	std::vector<std::string> valueNames;

	/**
	 * True if the results in the output have a fixed numeric
	 * range (minimum and maximum values).  Undefined if
	 * valueCount is zero.
	 */
	bool hasKnownExtents;

	/**
	 * Minimum value of the results in the output.  Undefined if
	 * hasKnownExtents is false or valueCount is zero.
	 */
	float minValue;

	/**
	 * Maximum value of the results in the output.  Undefined if
	 * hasKnownExtents is false or valueCount is zero.
	 */
	float maxValue;

	/**
	 * True if the output values are quantized to a particular
	 * resolution.  Undefined if valueCount is zero.
	 */
	bool isQuantized;

	/**
	 * Quantization resolution of the output values (e.g. 1.0 if
	 * they are all integers).  Undefined if isQuantized is false
	 * or valueCount is zero.
	 */
	float quantizeStep;

	enum SampleType {

	    /// Results from each process() align with that call's block start
	    OneSamplePerStep,

	    /// Results are evenly spaced in time (sampleRate specified below)
	    FixedSampleRate,

	    /// Results are unevenly spaced and have individual timestamps
	    VariableSampleRate
	};

	/**
	 * Positioning in time of the output results.
	 */
	SampleType sampleType;

	/**
	 * Sample rate of the output results.  Undefined if sampleType
	 * is OneSamplePerStep.
	 *
	 * If sampleType is VariableSampleRate and this value is
	 * non-zero, then it may be used to calculate a resolution for
	 * the output (i.e. the "duration" of each value, in time).
	 * It's recommended to set this to zero if that behaviour is
	 * not desired.
	 */
	float sampleRate;
    };

    typedef std::vector<OutputDescriptor> OutputList;

    /**
     * Get the outputs of this plugin.  An output's index in this list
     * is used as its numeric index when looking it up in the
     * FeatureSet returned from the process() call.
     */
    virtual OutputList getOutputDescriptors() const = 0;

    struct Feature
    {
	/**
	 * True if an output feature has its own timestamp.  This is
	 * mandatory if the output has VariableSampleRate, and is
	 * likely to be disregarded otherwise.
	 */
	bool hasTimestamp;

	/**
	 * Timestamp of the output feature.  This is mandatory if the
	 * output has VariableSampleRate, and is likely to be
	 * disregarded otherwise.  Undefined if hasTimestamp is false.
	 */
	RealTime timestamp;
	
	/**
	 * Results for a single sample of this feature.  If the output
	 * hasFixedValueCount, there must be the same number of values
	 * as the output's valueCount count.
	 */
	std::vector<float> values;

	/**
	 * Label for the sample of this feature.
	 */
	std::string label;
    };

    typedef std::vector<Feature> FeatureList;
    typedef std::map<int, FeatureList> FeatureSet; // key is output no

    /**
     * Process a single block of input data.  inputBuffers points to
     * one array of floats per input channel, and each of those arrays
     * contains the blockSize number of samples (the host will
     * zero-pad as necessary).  The timestamp is the real time in
     * seconds of the start of the supplied block of samples.
     *
     * Return any features that have become available after this
     * process call.  (These do not necessarily have to fall within
     * the process block, except for OneSamplePerStep outputs.)
     */
    virtual FeatureSet process(float **inputBuffers,
			       RealTime timestamp) = 0;

    /**
     * After all blocks have been processed, calculate and return any
     * remaining features derived from the complete input.
     */
    virtual FeatureSet getRemainingFeatures() = 0;

    virtual std::string getType() const { return "Feature Extraction Plugin"; }

protected:
    FeatureExtractionPlugin(float inputSampleRate) :
	m_inputSampleRate(inputSampleRate) { }

    float m_inputSampleRate;
};

#endif