Chris@25: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@25: Chris@25: /* Chris@25: Vamp Chris@25: Chris@25: An API for audio analysis and feature extraction plugins. Chris@25: Chris@25: Centre for Digital Music, Queen Mary, University of London. Chris@25: Copyright 2006 Chris Cannam. Chris@25: Chris@25: Permission is hereby granted, free of charge, to any person Chris@25: obtaining a copy of this software and associated documentation Chris@25: files (the "Software"), to deal in the Software without Chris@25: restriction, including without limitation the rights to use, copy, Chris@25: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@25: of the Software, and to permit persons to whom the Software is Chris@25: furnished to do so, subject to the following conditions: Chris@25: Chris@25: The above copyright notice and this permission notice shall be Chris@25: included in all copies or substantial portions of the Software. Chris@25: Chris@25: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@25: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@25: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@25: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@25: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@25: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@25: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@25: Chris@25: Except as contained in this notice, the names of the Centre for Chris@25: Digital Music; Queen Mary, University of London; and Chris Cannam Chris@25: shall not be used in advertising or otherwise to promote the sale, Chris@25: use or other dealings in this Software without prior written Chris@25: authorization. Chris@25: */ Chris@25: Chris@25: #ifndef _VAMP_SDK_PLUGIN_H_ Chris@25: #define _VAMP_SDK_PLUGIN_H_ Chris@25: Chris@25: #include Chris@25: #include Chris@25: #include Chris@25: Chris@25: #include "PluginBase.h" Chris@25: #include "RealTime.h" Chris@25: Chris@25: #include "plugguard.h" Chris@25: _VAMP_SDK_PLUGSPACE_BEGIN(Plugin.h) Chris@25: Chris@25: namespace Vamp { Chris@25: Chris@25: /** Chris@25: * \class Plugin Plugin.h Chris@25: * Chris@25: * Vamp::Plugin is a base class for plugin instance classes Chris@25: * that provide feature extraction from audio or related data. Chris@25: * Chris@25: * In most cases, the input will be audio and the output will be a Chris@25: * stream of derived data at a lower sampling resolution than the Chris@25: * input. Chris@25: * Chris@25: * Note that this class inherits several abstract methods from Chris@25: * PluginBase. These must be implemented by the subclass. Chris@25: * Chris@25: * Chris@25: * PLUGIN LIFECYCLE Chris@25: * Chris@25: * Feature extraction plugins are managed differently from real-time Chris@25: * plugins (such as VST effects). The main difference is that the Chris@25: * parameters for a feature extraction plugin are configured before Chris@25: * the plugin is used, and do not change during use. Chris@25: * Chris@25: * 1. Host constructs the plugin, passing it the input sample rate. Chris@25: * The plugin may do basic initialisation, but should not do anything Chris@25: * computationally expensive at this point. You must make sure your Chris@25: * plugin is cheap to construct, otherwise you'll seriously affect the Chris@25: * startup performance of almost all hosts. If you have serious Chris@25: * initialisation to do, the proper place is in initialise() (step 5). Chris@25: * Chris@25: * 2. Host may query the plugin's available outputs. Chris@25: * Chris@25: * 3. Host queries programs and parameter descriptors, and may set Chris@25: * some or all of them. Parameters that are not explicitly set should Chris@25: * take their default values as specified in the parameter descriptor. Chris@25: * When a program is set, the parameter values may change and the host Chris@25: * will re-query them to check. Chris@25: * Chris@25: * 4. Host queries the preferred step size, block size and number of Chris@25: * channels. These may all vary depending on the parameter values. Chris@25: * (Note however that you cannot make the number of distinct outputs Chris@25: * dependent on parameter values.) Chris@25: * Chris@25: * 5. Plugin is properly initialised with a call to initialise. This Chris@25: * fixes the step size, block size, and number of channels, as well as Chris@25: * all of the parameter and program settings. If the values passed in Chris@25: * to initialise do not match the plugin's advertised preferred values Chris@25: * from step 4, the plugin may refuse to initialise and return false Chris@25: * (although if possible it should accept the new values). Any Chris@25: * computationally expensive setup code should take place here. Chris@25: * Chris@25: * 6. Host finally checks the number of values, resolution, extents Chris@25: * etc per output (which may vary depending on the number of channels, Chris@25: * step size and block size as well as the parameter values). Chris@25: * Chris@25: * 7. Host will repeatedly call the process method to pass in blocks Chris@25: * of input data. This method may return features extracted from that Chris@25: * data (if the plugin is causal). Chris@25: * Chris@25: * 8. Host will call getRemainingFeatures exactly once, after all the Chris@25: * input data has been processed. This may return any non-causal or Chris@25: * leftover features. Chris@25: * Chris@25: * 9. At any point after initialise was called, the host may Chris@25: * optionally call the reset method and restart processing. (This Chris@25: * does not mean it can change the parameters, which are fixed from Chris@25: * initialise until destruction.) Chris@25: * Chris@25: * A plugin does not need to handle the case where setParameter or Chris@25: * selectProgram is called after initialise has been called. It's the Chris@25: * host's responsibility not to do that. Similarly, the plugin may Chris@25: * safely assume that initialise is called no more than once. Chris@25: */ Chris@25: Chris@25: class Plugin : public PluginBase Chris@25: { Chris@25: public: Chris@25: virtual ~Plugin() { } Chris@25: Chris@25: /** Chris@25: * Initialise a plugin to prepare it for use with the given number Chris@25: * of input channels, step size (window increment, in sample Chris@25: * frames) and block size (window size, in sample frames). Chris@25: * Chris@25: * The input sample rate should have been already specified at Chris@25: * construction time. Chris@25: * Chris@25: * Return true for successful initialisation, false if the number Chris@25: * of input channels, step size and/or block size cannot be Chris@25: * supported. Chris@25: */ Chris@25: virtual bool initialise(size_t inputChannels, Chris@25: size_t stepSize, Chris@25: size_t blockSize) = 0; Chris@25: Chris@25: /** Chris@25: * Reset the plugin after use, to prepare it for another clean Chris@25: * run. Not called for the first initialisation (i.e. initialise Chris@25: * must also do a reset). Chris@25: */ Chris@25: virtual void reset() = 0; Chris@25: Chris@25: enum InputDomain { TimeDomain, FrequencyDomain }; Chris@25: Chris@25: /** Chris@25: * Get the plugin's required input domain. Chris@25: * Chris@25: * If this is TimeDomain, the samples provided to the process() Chris@25: * function (below) will be in the time domain, as for a Chris@25: * traditional audio processing plugin. Chris@25: * Chris@25: * If this is FrequencyDomain, the host will carry out a windowed Chris@25: * FFT of size equal to the negotiated block size on the data Chris@25: * before passing the frequency bin data in to process(). The Chris@25: * input data for the FFT will be rotated so as to place the Chris@25: * origin in the centre of the block. Chris@25: * The plugin does not get to choose the window type -- the host Chris@25: * will either let the user do so, or will use a Hanning window. Chris@25: */ Chris@25: virtual InputDomain getInputDomain() const = 0; Chris@25: Chris@25: /** Chris@25: * Get the preferred block size (window size -- the number of Chris@25: * sample frames passed in each block to the process() function). Chris@25: * This should be called before initialise(). Chris@25: * Chris@25: * A plugin that can handle any block size may return 0. The Chris@25: * final block size will be set in the initialise() call. Chris@25: */ Chris@25: virtual size_t getPreferredBlockSize() const { return 0; } Chris@25: Chris@25: /** Chris@25: * Get the preferred step size (window increment -- the distance Chris@25: * in sample frames between the start frames of consecutive blocks Chris@25: * passed to the process() function) for the plugin. This should Chris@25: * be called before initialise(). Chris@25: * Chris@25: * A plugin may return 0 if it has no particular interest in the Chris@25: * step size. In this case, the host should make the step size Chris@25: * equal to the block size if the plugin is accepting input in the Chris@25: * time domain. If the plugin is accepting input in the frequency Chris@25: * domain, the host may use any step size. The final step size Chris@25: * will be set in the initialise() call. Chris@25: */ Chris@25: virtual size_t getPreferredStepSize() const { return 0; } Chris@25: Chris@25: /** Chris@25: * Get the minimum supported number of input channels. Chris@25: */ Chris@25: virtual size_t getMinChannelCount() const { return 1; } Chris@25: Chris@25: /** Chris@25: * Get the maximum supported number of input channels. Chris@25: */ Chris@25: virtual size_t getMaxChannelCount() const { return 1; } Chris@25: Chris@25: struct OutputDescriptor Chris@25: { Chris@25: /** Chris@25: * The name of the output, in computer-usable form. Should be Chris@25: * reasonably short and without whitespace or punctuation, using Chris@25: * the characters [a-zA-Z0-9_-] only. Chris@25: * Example: "zero_crossing_count" Chris@25: */ Chris@25: std::string identifier; Chris@25: Chris@25: /** Chris@25: * The human-readable name of the output. Chris@25: * Example: "Zero Crossing Counts" Chris@25: */ Chris@25: std::string name; Chris@25: Chris@25: /** Chris@25: * A human-readable short text describing the output. May be Chris@25: * empty if the name has said it all already. Chris@25: * Example: "The number of zero crossing points per processing block" Chris@25: */ Chris@25: std::string description; Chris@25: Chris@25: /** Chris@25: * The unit of the output, in human-readable form. Chris@25: */ Chris@25: std::string unit; Chris@25: Chris@25: /** Chris@25: * True if the output has the same number of values per sample Chris@25: * for every output sample. Outputs for which this is false Chris@25: * are unlikely to be very useful in a general-purpose host. Chris@25: */ Chris@25: bool hasFixedBinCount; Chris@25: Chris@25: /** Chris@25: * The number of values per result of the output. Undefined Chris@25: * if hasFixedBinCount is false. If this is zero, the output Chris@25: * is point data (i.e. only the time of each output is of Chris@25: * interest, the value list will be empty). Chris@25: */ Chris@25: size_t binCount; Chris@25: Chris@25: /** Chris@25: * The (human-readable) names of each of the bins, if Chris@25: * appropriate. This is always optional. Chris@25: */ Chris@25: std::vector binNames; Chris@25: Chris@25: /** Chris@25: * True if the results in each output bin fall within a fixed Chris@25: * numeric range (minimum and maximum values). Undefined if Chris@25: * binCount is zero. Chris@25: */ Chris@25: bool hasKnownExtents; Chris@25: Chris@25: /** Chris@25: * Minimum value of the results in the output. Undefined if Chris@25: * hasKnownExtents is false or binCount is zero. Chris@25: */ Chris@25: float minValue; Chris@25: Chris@25: /** Chris@25: * Maximum value of the results in the output. Undefined if Chris@25: * hasKnownExtents is false or binCount is zero. Chris@25: */ Chris@25: float maxValue; Chris@25: Chris@25: /** Chris@25: * True if the output values are quantized to a particular Chris@25: * resolution. Undefined if binCount is zero. Chris@25: */ Chris@25: bool isQuantized; Chris@25: Chris@25: /** Chris@25: * Quantization resolution of the output values (e.g. 1.0 if Chris@25: * they are all integers). Undefined if isQuantized is false Chris@25: * or binCount is zero. Chris@25: */ Chris@25: float quantizeStep; Chris@25: Chris@25: enum SampleType { Chris@25: Chris@25: /// Results from each process() align with that call's block start Chris@25: OneSamplePerStep, Chris@25: Chris@25: /// Results are evenly spaced in time (sampleRate specified below) Chris@25: FixedSampleRate, Chris@25: Chris@25: /// Results are unevenly spaced and have individual timestamps Chris@25: VariableSampleRate Chris@25: }; Chris@25: Chris@25: /** Chris@25: * Positioning in time of the output results. Chris@25: */ Chris@25: SampleType sampleType; Chris@25: Chris@25: /** Chris@25: * Sample rate of the output results, as samples per second. Chris@25: * Undefined if sampleType is OneSamplePerStep. Chris@25: * Chris@25: * If sampleType is VariableSampleRate and this value is Chris@25: * non-zero, then it may be used to calculate a resolution for Chris@25: * the output (i.e. the "duration" of each sample, in time, Chris@25: * will be 1/sampleRate seconds). It's recommended to set Chris@25: * this to zero if that behaviour is not desired. Chris@25: */ Chris@25: float sampleRate; Chris@25: Chris@25: /** Chris@25: * True if the returned results for this output are known to Chris@25: * have a duration field. Chris@25: */ Chris@25: bool hasDuration; Chris@25: Chris@25: OutputDescriptor() : // defaults for mandatory non-class-type members Chris@25: hasFixedBinCount(false), hasKnownExtents(false), isQuantized(false), Chris@25: sampleType(OneSamplePerStep), sampleRate(0), hasDuration(false) { } Chris@25: }; Chris@25: Chris@25: typedef std::vector OutputList; Chris@25: Chris@25: /** Chris@25: * Get the outputs of this plugin. An output's index in this list Chris@25: * is used as its numeric index when looking it up in the Chris@25: * FeatureSet returned from the process() call. Chris@25: */ Chris@25: virtual OutputList getOutputDescriptors() const = 0; Chris@25: Chris@25: struct Feature Chris@25: { Chris@25: /** Chris@25: * True if an output feature has its own timestamp. This is Chris@25: * mandatory if the output has VariableSampleRate, optional if Chris@25: * the output has FixedSampleRate, and unused if the output Chris@25: * has OneSamplePerStep. Chris@25: */ Chris@25: bool hasTimestamp; Chris@25: Chris@25: /** Chris@25: * Timestamp of the output feature. This is mandatory if the Chris@25: * output has VariableSampleRate or if the output has Chris@25: * FixedSampleRate and hasTimestamp is true, and unused Chris@25: * otherwise. Chris@25: */ Chris@25: RealTime timestamp; Chris@25: Chris@25: /** Chris@25: * True if an output feature has a specified duration. This Chris@25: * is optional if the output has VariableSampleRate or Chris@25: * FixedSampleRate, and and unused if the output has Chris@25: * OneSamplePerStep. Chris@25: */ Chris@25: bool hasDuration; Chris@25: Chris@25: /** Chris@25: * Duration of the output feature. This is mandatory if the Chris@25: * output has VariableSampleRate or FixedSampleRate and Chris@25: * hasDuration is true, and unused otherwise. Chris@25: */ Chris@25: RealTime duration; Chris@25: Chris@25: /** Chris@25: * Results for a single sample of this feature. If the output Chris@25: * hasFixedBinCount, there must be the same number of values Chris@25: * as the output's binCount count. Chris@25: */ Chris@25: std::vector values; Chris@25: Chris@25: /** Chris@25: * Label for the sample of this feature. Chris@25: */ Chris@25: std::string label; Chris@25: Chris@25: Feature() : // defaults for mandatory non-class-type members Chris@25: hasTimestamp(false), hasDuration(false) { } Chris@25: }; Chris@25: Chris@25: typedef std::vector FeatureList; Chris@25: Chris@25: typedef std::map FeatureSet; // key is output no Chris@25: Chris@25: /** Chris@25: * Process a single block of input data. Chris@25: * Chris@25: * If the plugin's inputDomain is TimeDomain, inputBuffers will Chris@25: * point to one array of floats per input channel, and each of Chris@25: * these arrays will contain blockSize consecutive audio samples Chris@25: * (the host will zero-pad as necessary). The timestamp in this Chris@25: * case will be the real time in seconds of the start of the Chris@25: * supplied block of samples. Chris@25: * Chris@25: * If the plugin's inputDomain is FrequencyDomain, inputBuffers Chris@25: * will point to one array of floats per input channel, and each Chris@25: * of these arrays will contain blockSize/2+1 consecutive pairs of Chris@25: * real and imaginary component floats corresponding to bins Chris@25: * 0..(blockSize/2) of the FFT output. That is, bin 0 (the first Chris@25: * pair of floats) contains the DC output, up to bin blockSize/2 Chris@25: * which contains the Nyquist-frequency output. There will Chris@25: * therefore be blockSize+2 floats per channel in total. The Chris@25: * timestamp will be the real time in seconds of the centre of the Chris@25: * FFT input window (i.e. the very first block passed to process Chris@25: * might contain the FFT of half a block of zero samples and the Chris@25: * first half-block of the actual data, with a timestamp of zero). Chris@25: * Chris@25: * Return any features that have become available after this Chris@25: * process call. (These do not necessarily have to fall within Chris@25: * the process block, except for OneSamplePerStep outputs.) Chris@25: */ Chris@25: virtual FeatureSet process(const float *const *inputBuffers, Chris@25: RealTime timestamp) = 0; Chris@25: Chris@25: /** Chris@25: * After all blocks have been processed, calculate and return any Chris@25: * remaining features derived from the complete input. Chris@25: */ Chris@25: virtual FeatureSet getRemainingFeatures() = 0; Chris@25: Chris@25: /** Chris@25: * Used to distinguish between Vamp::Plugin and other potential Chris@25: * sibling subclasses of PluginBase. Do not reimplement this Chris@25: * function in your subclass. Chris@25: */ Chris@25: virtual std::string getType() const { return "Feature Extraction Plugin"; } Chris@25: Chris@25: protected: Chris@25: Plugin(float inputSampleRate) : Chris@25: m_inputSampleRate(inputSampleRate) { } Chris@25: Chris@25: float m_inputSampleRate; Chris@25: }; Chris@25: Chris@25: } Chris@25: Chris@25: _VAMP_SDK_PLUGSPACE_END(Plugin.h) Chris@25: Chris@25: #endif Chris@25: Chris@25: Chris@25: