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