# HG changeset patch # User Chris Cannam # Date 1194440341 0 # Node ID f620ce48c95039051848d25516ee8532ffe12c79 # Parent 6e9dcf09b7fe8a41fcdbebaa352621d31a9206dd * Further naming change: Transformer -> ModelTransformer. The Transform class now describes a thing that can be done, and the ModelTransformer does it to a Model. diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/plugin.pro --- a/plugin/plugin.pro Tue Nov 06 17:15:00 2007 +0000 +++ b/plugin/plugin.pro Wed Nov 07 12:59:01 2007 +0000 @@ -35,14 +35,14 @@ api/alsa/seq_event.h \ api/alsa/seq_midi_event.h \ api/alsa/sound/asequencer.h \ - transform/FeatureExtractionPluginTransformer.h \ + transform/FeatureExtractionModelTransformer.h \ transform/PluginTransformer.h \ - transform/RealTimePluginTransformer.h \ + transform/RealTimeEffectModelTransformer.h \ transform/Transform.h \ transform/TransformDescription.h \ transform/TransformFactory.h \ - transform/Transformer.h \ - transform/TransformerFactory.h + transform/ModelTransformer.h \ + transform/ModelTransformerFactory.h SOURCES += DSSIPluginFactory.cpp \ DSSIPluginInstance.cpp \ FeatureExtractionPluginFactory.cpp \ @@ -54,10 +54,10 @@ RealTimePluginInstance.cpp \ api/dssi_alsa_compat.c \ plugins/SamplePlayer.cpp \ - transform/FeatureExtractionPluginTransformer.cpp \ + transform/FeatureExtractionModelTransformer.cpp \ transform/PluginTransformer.cpp \ - transform/RealTimePluginTransformer.cpp \ + transform/RealTimeEffectModelTransformer.cpp \ transform/Transform.cpp \ transform/TransformFactory.cpp \ - transform/Transformer.cpp \ - transform/TransformerFactory.cpp + transform/ModelTransformer.cpp \ + transform/ModelTransformerFactory.cpp diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/FeatureExtractionModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.cpp Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,553 @@ +/* -*- 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 and QMUL. + + 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. +*/ + +#include "FeatureExtractionModelTransformer.h" + +#include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/PluginXml.h" +#include "vamp-sdk/Plugin.h" + +#include "data/model/Model.h" +#include "base/Window.h" +#include "data/model/SparseOneDimensionalModel.h" +#include "data/model/SparseTimeValueModel.h" +#include "data/model/EditableDenseThreeDimensionalModel.h" +#include "data/model/DenseTimeValueModel.h" +#include "data/model/NoteModel.h" +#include "data/model/FFTModel.h" +#include "data/model/WaveFileModel.h" + +#include + +#include + +FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Model *inputModel, + QString pluginId, + const ExecutionContext &context, + QString configurationXml, + QString outputName) : + PluginTransformer(inputModel, context), + m_plugin(0), + m_descriptor(0), + m_outputFeatureNo(0) +{ +// std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + if (!factory) { + std::cerr << "FeatureExtractionModelTransformer: No factory available for plugin id \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); + + if (!m_plugin) { + std::cerr << "FeatureExtractionModelTransformer: Failed to instantiate plugin \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + if (configurationXml != "") { + PluginXml(m_plugin).setParametersFromXml(configurationXml); + } + + DenseTimeValueModel *input = getInput(); + if (!input) return; + + size_t channelCount = input->getChannelCount(); + if (m_plugin->getMaxChannelCount() < channelCount) { + channelCount = 1; + } + if (m_plugin->getMinChannelCount() > channelCount) { + std::cerr << "FeatureExtractionModelTransformer:: " + << "Can't provide enough channels to plugin (plugin min " + << m_plugin->getMinChannelCount() << ", max " + << m_plugin->getMaxChannelCount() << ", input model has " + << input->getChannelCount() << ")" << std::endl; + return; + } + + std::cerr << "Initialising feature extraction plugin with channels = " + << channelCount << ", step = " << m_context.stepSize + << ", block = " << m_context.blockSize << std::endl; + + if (!m_plugin->initialise(channelCount, + m_context.stepSize, + m_context.blockSize)) { + std::cerr << "FeatureExtractionModelTransformer: Plugin " + << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; + return; + } + + Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); + + if (outputs.empty()) { + std::cerr << "FeatureExtractionModelTransformer: Plugin \"" + << pluginId.toStdString() << "\" has no outputs" << std::endl; + return; + } + + for (size_t i = 0; i < outputs.size(); ++i) { + if (outputName == "" || outputs[i].identifier == outputName.toStdString()) { + m_outputFeatureNo = i; + m_descriptor = new Vamp::Plugin::OutputDescriptor + (outputs[i]); + break; + } + } + + if (!m_descriptor) { + std::cerr << "FeatureExtractionModelTransformer: Plugin \"" + << pluginId.toStdString() << "\" has no output named \"" + << outputName.toStdString() << "\"" << std::endl; + return; + } + +// std::cerr << "FeatureExtractionModelTransformer: output sample type " +// << m_descriptor->sampleType << std::endl; + + int binCount = 1; + float minValue = 0.0, maxValue = 0.0; + bool haveExtents = false; + + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + +// std::cerr << "FeatureExtractionModelTransformer: output bin count " +// << binCount << std::endl; + + if (binCount > 0 && m_descriptor->hasKnownExtents) { + minValue = m_descriptor->minValue; + maxValue = m_descriptor->maxValue; + haveExtents = true; + } + + size_t modelRate = m_input->getSampleRate(); + size_t modelResolution = 1; + + switch (m_descriptor->sampleType) { + + case Vamp::Plugin::OutputDescriptor::VariableSampleRate: + if (m_descriptor->sampleRate != 0.0) { + modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001); + } + break; + + case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: + modelResolution = m_context.stepSize; + break; + + case Vamp::Plugin::OutputDescriptor::FixedSampleRate: + modelRate = size_t(m_descriptor->sampleRate + 0.001); + break; + } + + if (binCount == 0) { + + m_output = new SparseOneDimensionalModel(modelRate, modelResolution, + false); + + } else if (binCount == 1) { + + SparseTimeValueModel *model; + if (haveExtents) { + model = new SparseTimeValueModel + (modelRate, modelResolution, minValue, maxValue, false); + } else { + model = new SparseTimeValueModel + (modelRate, modelResolution, false); + } + model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); + + m_output = model; + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + // We don't have a sparse 3D model, so interpret this as a + // note model. There's nothing to define which values to use + // as which parameters of the note -- for the moment let's + // treat the first as pitch, second as duration in frames, + // third (if present) as velocity. (Our note model doesn't + // yet store velocity.) + //!!! todo: ask the user! + + NoteModel *model; + if (haveExtents) { + model = new NoteModel + (modelRate, modelResolution, minValue, maxValue, false); + } else { + model = new NoteModel + (modelRate, modelResolution, false); + } + model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); + + m_output = model; + + } else { + + EditableDenseThreeDimensionalModel *model = + new EditableDenseThreeDimensionalModel + (modelRate, modelResolution, binCount, false); + + if (!m_descriptor->binNames.empty()) { + std::vector names; + for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) { + names.push_back(m_descriptor->binNames[i].c_str()); + } + model->setBinNames(names); + } + + m_output = model; + } +} + +FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() +{ + std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl; + delete m_plugin; + delete m_descriptor; +} + +DenseTimeValueModel * +FeatureExtractionModelTransformer::getInput() +{ + DenseTimeValueModel *dtvm = + dynamic_cast(getInputModel()); + if (!dtvm) { + std::cerr << "FeatureExtractionModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + } + return dtvm; +} + +void +FeatureExtractionModelTransformer::run() +{ + DenseTimeValueModel *input = getInput(); + if (!input) return; + + if (!m_output) return; + + while (!input->isReady()) { +/* + if (dynamic_cast(input)) { + std::cerr << "FeatureExtractionModelTransformer::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl; + sleep(2); + break; // no need to wait + } +*/ + std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl; + sleep(1); + } + + size_t sampleRate = m_input->getSampleRate(); + + size_t channelCount = input->getChannelCount(); + if (m_plugin->getMaxChannelCount() < channelCount) { + channelCount = 1; + } + + float **buffers = new float*[channelCount]; + for (size_t ch = 0; ch < channelCount; ++ch) { + buffers[ch] = new float[m_context.blockSize + 2]; + } + + bool frequencyDomain = (m_plugin->getInputDomain() == + Vamp::Plugin::FrequencyDomain); + std::vector fftModels; + + if (frequencyDomain) { + for (size_t ch = 0; ch < channelCount; ++ch) { + FFTModel *model = new FFTModel + (getInput(), + channelCount == 1 ? m_context.channel : ch, + m_context.windowType, + m_context.blockSize, + m_context.stepSize, + m_context.blockSize, + false); + if (!model->isOK()) { + QMessageBox::critical + (0, tr("FFT cache failed"), + tr("Failed to create the FFT model for this transform.\n" + "There may be insufficient memory or disc space to continue.")); + delete model; + setCompletion(100); + return; + } + model->resume(); + fftModels.push_back(model); + } + } + + long startFrame = m_input->getStartFrame(); + long endFrame = m_input->getEndFrame(); + + long contextStart = m_context.startFrame; + long contextDuration = m_context.duration; + + if (contextStart == 0 || contextStart < startFrame) { + contextStart = startFrame; + } + + if (contextDuration == 0) { + contextDuration = endFrame - contextStart; + } + if (contextStart + contextDuration > endFrame) { + contextDuration = endFrame - contextStart; + } + + long blockFrame = contextStart; + + long prevCompletion = 0; + + setCompletion(0); + + while (!m_abandoned) { + + if (frequencyDomain) { + if (blockFrame - int(m_context.blockSize)/2 > + contextStart + contextDuration) break; + } else { + if (blockFrame >= + contextStart + contextDuration) break; + } + +// std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " +// << blockFrame << ", endFrame " << endFrame << ", blockSize " +// << m_context.blockSize << std::endl; + + long completion = + (((blockFrame - contextStart) / m_context.stepSize) * 99) / + (contextDuration / m_context.stepSize); + + // channelCount is either m_input->channelCount or 1 + + for (size_t ch = 0; ch < channelCount; ++ch) { + if (frequencyDomain) { + int column = (blockFrame - startFrame) / m_context.stepSize; + for (size_t i = 0; i <= m_context.blockSize/2; ++i) { + fftModels[ch]->getValuesAt + (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); + } + } else { + getFrames(ch, channelCount, + blockFrame, m_context.blockSize, buffers[ch]); + } + } + + Vamp::Plugin::FeatureSet features = m_plugin->process + (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); + + for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { + Vamp::Plugin::Feature feature = + features[m_outputFeatureNo][fi]; + addFeature(blockFrame, feature); + } + + if (blockFrame == contextStart || completion > prevCompletion) { + setCompletion(completion); + prevCompletion = completion; + } + + blockFrame += m_context.stepSize; + } + + if (m_abandoned) return; + + Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); + + for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { + Vamp::Plugin::Feature feature = + features[m_outputFeatureNo][fi]; + addFeature(blockFrame, feature); + } + + if (frequencyDomain) { + for (size_t ch = 0; ch < channelCount; ++ch) { + delete fftModels[ch]; + } + } + + setCompletion(100); +} + +void +FeatureExtractionModelTransformer::getFrames(int channel, int channelCount, + long startFrame, long size, + float *buffer) +{ + long offset = 0; + + if (startFrame < 0) { + for (int i = 0; i < size && startFrame + i < 0; ++i) { + buffer[i] = 0.0f; + } + offset = -startFrame; + size -= offset; + if (size <= 0) return; + startFrame = 0; + } + + long got = getInput()->getData + ((channelCount == 1 ? m_context.channel : channel), + startFrame, size, buffer + offset); + + while (got < size) { + buffer[offset + got] = 0.0; + ++got; + } + + if (m_context.channel == -1 && channelCount == 1 && + getInput()->getChannelCount() > 1) { + // use mean instead of sum, as plugin input + int cc = getInput()->getChannelCount(); + for (long i = 0; i < size; ++i) { + buffer[i] /= cc; + } + } +} + +void +FeatureExtractionModelTransformer::addFeature(size_t blockFrame, + const Vamp::Plugin::Feature &feature) +{ + size_t inputRate = m_input->getSampleRate(); + +// std::cerr << "FeatureExtractionModelTransformer::addFeature(" +// << blockFrame << ")" << std::endl; + + int binCount = 1; + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + + size_t frame = blockFrame; + + if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + if (!feature.hasTimestamp) { + std::cerr + << "WARNING: FeatureExtractionModelTransformer::addFeature: " + << "Feature has variable sample rate but no timestamp!" + << std::endl; + return; + } else { + frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); + } + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::FixedSampleRate) { + + if (feature.hasTimestamp) { + //!!! warning: sampleRate may be non-integral + frame = Vamp::RealTime::realTime2Frame(feature.timestamp, + lrintf(m_descriptor->sampleRate)); + } else { + frame = m_output->getEndFrame(); + } + } + + if (binCount == 0) { + + SparseOneDimensionalModel *model = getOutput(); + if (!model) return; + model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); + + } else if (binCount == 1) { + + float value = 0.0; + if (feature.values.size() > 0) value = feature.values[0]; + + SparseTimeValueModel *model = getOutput(); + if (!model) return; + model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); +// std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + float pitch = 0.0; + if (feature.values.size() > 0) pitch = feature.values[0]; + + float duration = 1; + if (feature.values.size() > 1) duration = feature.values[1]; + + float velocity = 100; + if (feature.values.size() > 2) velocity = feature.values[2]; + + NoteModel *model = getOutput(); + if (!model) return; + + model->addPoint(NoteModel::Point(frame, pitch, + lrintf(duration), + feature.label.c_str())); + + } else { + + DenseThreeDimensionalModel::Column values = feature.values; + + EditableDenseThreeDimensionalModel *model = + getOutput(); + if (!model) return; + + model->setColumn(frame / model->getResolution(), values); + } +} + +void +FeatureExtractionModelTransformer::setCompletion(int completion) +{ + int binCount = 1; + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + +// std::cerr << "FeatureExtractionModelTransformer::setCompletion(" +// << completion << ")" << std::endl; + + if (binCount == 0) { + + SparseOneDimensionalModel *model = getOutput(); + if (!model) return; + model->setCompletion(completion); + + } else if (binCount == 1) { + + SparseTimeValueModel *model = getOutput(); + if (!model) return; + model->setCompletion(completion); + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + NoteModel *model = getOutput(); + if (!model) return; + model->setCompletion(completion); + + } else { + + EditableDenseThreeDimensionalModel *model = + getOutput(); + if (!model) return; + model->setCompletion(completion); + } +} + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/FeatureExtractionModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.h Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,62 @@ +/* -*- 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 and QMUL. + + 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_TRANSFORMER_H_ +#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ + +#include "PluginTransformer.h" + +class DenseTimeValueModel; + +class FeatureExtractionModelTransformer : public PluginTransformer +{ + Q_OBJECT + +public: + FeatureExtractionModelTransformer(Model *inputModel, + QString plugin, + const ExecutionContext &context, + QString configurationXml = "", + QString outputName = ""); + virtual ~FeatureExtractionModelTransformer(); + +protected: + virtual void run(); + + Vamp::Plugin *m_plugin; + Vamp::Plugin::OutputDescriptor *m_descriptor; + int m_outputFeatureNo; + + void addFeature(size_t blockFrame, + const Vamp::Plugin::Feature &feature); + + void setCompletion(int); + + void getFrames(int channel, int channelCount, + long startFrame, long size, float *buffer); + + // just casts + DenseTimeValueModel *getInput(); + template ModelClass *getOutput() { + ModelClass *mc = dynamic_cast(m_output); + if (!mc) { + std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; + } + return mc; + } +}; + +#endif + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/FeatureExtractionPluginTransformer.cpp --- a/plugin/transform/FeatureExtractionPluginTransformer.cpp Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,553 +0,0 @@ -/* -*- 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 and QMUL. - - 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. -*/ - -#include "FeatureExtractionPluginTransformer.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/PluginXml.h" -#include "vamp-sdk/Plugin.h" - -#include "data/model/Model.h" -#include "base/Window.h" -#include "data/model/SparseOneDimensionalModel.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/EditableDenseThreeDimensionalModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/NoteModel.h" -#include "data/model/FFTModel.h" -#include "data/model/WaveFileModel.h" - -#include - -#include - -FeatureExtractionPluginTransformer::FeatureExtractionPluginTransformer(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString outputName) : - PluginTransformer(inputModel, context), - m_plugin(0), - m_descriptor(0), - m_outputFeatureNo(0) -{ -// std::cerr << "FeatureExtractionPluginTransformer::FeatureExtractionPluginTransformer: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "FeatureExtractionPluginTransformer: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); - - if (!m_plugin) { - std::cerr << "FeatureExtractionPluginTransformer: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } - - DenseTimeValueModel *input = getInput(); - if (!input) return; - - size_t channelCount = input->getChannelCount(); - if (m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; - } - if (m_plugin->getMinChannelCount() > channelCount) { - std::cerr << "FeatureExtractionPluginTransformer:: " - << "Can't provide enough channels to plugin (plugin min " - << m_plugin->getMinChannelCount() << ", max " - << m_plugin->getMaxChannelCount() << ", input model has " - << input->getChannelCount() << ")" << std::endl; - return; - } - - std::cerr << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << m_context.stepSize - << ", block = " << m_context.blockSize << std::endl; - - if (!m_plugin->initialise(channelCount, - m_context.stepSize, - m_context.blockSize)) { - std::cerr << "FeatureExtractionPluginTransformer: Plugin " - << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; - return; - } - - Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); - - if (outputs.empty()) { - std::cerr << "FeatureExtractionPluginTransformer: Plugin \"" - << pluginId.toStdString() << "\" has no outputs" << std::endl; - return; - } - - for (size_t i = 0; i < outputs.size(); ++i) { - if (outputName == "" || outputs[i].identifier == outputName.toStdString()) { - m_outputFeatureNo = i; - m_descriptor = new Vamp::Plugin::OutputDescriptor - (outputs[i]); - break; - } - } - - if (!m_descriptor) { - std::cerr << "FeatureExtractionPluginTransformer: Plugin \"" - << pluginId.toStdString() << "\" has no output named \"" - << outputName.toStdString() << "\"" << std::endl; - return; - } - -// std::cerr << "FeatureExtractionPluginTransformer: output sample type " -// << m_descriptor->sampleType << std::endl; - - int binCount = 1; - float minValue = 0.0, maxValue = 0.0; - bool haveExtents = false; - - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionPluginTransformer: output bin count " -// << binCount << std::endl; - - if (binCount > 0 && m_descriptor->hasKnownExtents) { - minValue = m_descriptor->minValue; - maxValue = m_descriptor->maxValue; - haveExtents = true; - } - - size_t modelRate = m_input->getSampleRate(); - size_t modelResolution = 1; - - switch (m_descriptor->sampleType) { - - case Vamp::Plugin::OutputDescriptor::VariableSampleRate: - if (m_descriptor->sampleRate != 0.0) { - modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001); - } - break; - - case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_context.stepSize; - break; - - case Vamp::Plugin::OutputDescriptor::FixedSampleRate: - modelRate = size_t(m_descriptor->sampleRate + 0.001); - break; - } - - if (binCount == 0) { - - m_output = new SparseOneDimensionalModel(modelRate, modelResolution, - false); - - } else if (binCount == 1) { - - SparseTimeValueModel *model; - if (haveExtents) { - model = new SparseTimeValueModel - (modelRate, modelResolution, minValue, maxValue, false); - } else { - model = new SparseTimeValueModel - (modelRate, modelResolution, false); - } - model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); - - m_output = model; - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - // We don't have a sparse 3D model, so interpret this as a - // note model. There's nothing to define which values to use - // as which parameters of the note -- for the moment let's - // treat the first as pitch, second as duration in frames, - // third (if present) as velocity. (Our note model doesn't - // yet store velocity.) - //!!! todo: ask the user! - - NoteModel *model; - if (haveExtents) { - model = new NoteModel - (modelRate, modelResolution, minValue, maxValue, false); - } else { - model = new NoteModel - (modelRate, modelResolution, false); - } - model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); - - m_output = model; - - } else { - - EditableDenseThreeDimensionalModel *model = - new EditableDenseThreeDimensionalModel - (modelRate, modelResolution, binCount, false); - - if (!m_descriptor->binNames.empty()) { - std::vector names; - for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) { - names.push_back(m_descriptor->binNames[i].c_str()); - } - model->setBinNames(names); - } - - m_output = model; - } -} - -FeatureExtractionPluginTransformer::~FeatureExtractionPluginTransformer() -{ - std::cerr << "FeatureExtractionPluginTransformer::~FeatureExtractionPluginTransformer()" << std::endl; - delete m_plugin; - delete m_descriptor; -} - -DenseTimeValueModel * -FeatureExtractionPluginTransformer::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "FeatureExtractionPluginTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -FeatureExtractionPluginTransformer::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - if (!m_output) return; - - while (!input->isReady()) { -/* - if (dynamic_cast(input)) { - std::cerr << "FeatureExtractionPluginTransformer::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl; - sleep(2); - break; // no need to wait - } -*/ - std::cerr << "FeatureExtractionPluginTransformer::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - size_t sampleRate = m_input->getSampleRate(); - - size_t channelCount = input->getChannelCount(); - if (m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; - } - - float **buffers = new float*[channelCount]; - for (size_t ch = 0; ch < channelCount; ++ch) { - buffers[ch] = new float[m_context.blockSize + 2]; - } - - bool frequencyDomain = (m_plugin->getInputDomain() == - Vamp::Plugin::FrequencyDomain); - std::vector fftModels; - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - FFTModel *model = new FFTModel - (getInput(), - channelCount == 1 ? m_context.channel : ch, - m_context.windowType, - m_context.blockSize, - m_context.stepSize, - m_context.blockSize, - false); - if (!model->isOK()) { - QMessageBox::critical - (0, tr("FFT cache failed"), - tr("Failed to create the FFT model for this transform.\n" - "There may be insufficient memory or disc space to continue.")); - delete model; - setCompletion(100); - return; - } - model->resume(); - fftModels.push_back(model); - } - } - - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); - - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; - - if (contextStart == 0 || contextStart < startFrame) { - contextStart = startFrame; - } - - if (contextDuration == 0) { - contextDuration = endFrame - contextStart; - } - if (contextStart + contextDuration > endFrame) { - contextDuration = endFrame - contextStart; - } - - long blockFrame = contextStart; - - long prevCompletion = 0; - - setCompletion(0); - - while (!m_abandoned) { - - if (frequencyDomain) { - if (blockFrame - int(m_context.blockSize)/2 > - contextStart + contextDuration) break; - } else { - if (blockFrame >= - contextStart + contextDuration) break; - } - -// std::cerr << "FeatureExtractionPluginTransformer::run: blockFrame " -// << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << m_context.blockSize << std::endl; - - long completion = - (((blockFrame - contextStart) / m_context.stepSize) * 99) / - (contextDuration / m_context.stepSize); - - // channelCount is either m_input->channelCount or 1 - - for (size_t ch = 0; ch < channelCount; ++ch) { - if (frequencyDomain) { - int column = (blockFrame - startFrame) / m_context.stepSize; - for (size_t i = 0; i <= m_context.blockSize/2; ++i) { - fftModels[ch]->getValuesAt - (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); - } - } else { - getFrames(ch, channelCount, - blockFrame, m_context.blockSize, buffers[ch]); - } - } - - Vamp::Plugin::FeatureSet features = m_plugin->process - (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); - - for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { - Vamp::Plugin::Feature feature = - features[m_outputFeatureNo][fi]; - addFeature(blockFrame, feature); - } - - if (blockFrame == contextStart || completion > prevCompletion) { - setCompletion(completion); - prevCompletion = completion; - } - - blockFrame += m_context.stepSize; - } - - if (m_abandoned) return; - - Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - - for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { - Vamp::Plugin::Feature feature = - features[m_outputFeatureNo][fi]; - addFeature(blockFrame, feature); - } - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - delete fftModels[ch]; - } - } - - setCompletion(100); -} - -void -FeatureExtractionPluginTransformer::getFrames(int channel, int channelCount, - long startFrame, long size, - float *buffer) -{ - long offset = 0; - - if (startFrame < 0) { - for (int i = 0; i < size && startFrame + i < 0; ++i) { - buffer[i] = 0.0f; - } - offset = -startFrame; - size -= offset; - if (size <= 0) return; - startFrame = 0; - } - - long got = getInput()->getData - ((channelCount == 1 ? m_context.channel : channel), - startFrame, size, buffer + offset); - - while (got < size) { - buffer[offset + got] = 0.0; - ++got; - } - - if (m_context.channel == -1 && channelCount == 1 && - getInput()->getChannelCount() > 1) { - // use mean instead of sum, as plugin input - int cc = getInput()->getChannelCount(); - for (long i = 0; i < size; ++i) { - buffer[i] /= cc; - } - } -} - -void -FeatureExtractionPluginTransformer::addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature) -{ - size_t inputRate = m_input->getSampleRate(); - -// std::cerr << "FeatureExtractionPluginTransformer::addFeature(" -// << blockFrame << ")" << std::endl; - - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - - size_t frame = blockFrame; - - if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - if (!feature.hasTimestamp) { - std::cerr - << "WARNING: FeatureExtractionPluginTransformer::addFeature: " - << "Feature has variable sample rate but no timestamp!" - << std::endl; - return; - } else { - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); - } - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::FixedSampleRate) { - - if (feature.hasTimestamp) { - //!!! warning: sampleRate may be non-integral - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, - lrintf(m_descriptor->sampleRate)); - } else { - frame = m_output->getEndFrame(); - } - } - - if (binCount == 0) { - - SparseOneDimensionalModel *model = getOutput(); - if (!model) return; - model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); - - } else if (binCount == 1) { - - float value = 0.0; - if (feature.values.size() > 0) value = feature.values[0]; - - SparseTimeValueModel *model = getOutput(); - if (!model) return; - model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); -// std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - float pitch = 0.0; - if (feature.values.size() > 0) pitch = feature.values[0]; - - float duration = 1; - if (feature.values.size() > 1) duration = feature.values[1]; - - float velocity = 100; - if (feature.values.size() > 2) velocity = feature.values[2]; - - NoteModel *model = getOutput(); - if (!model) return; - - model->addPoint(NoteModel::Point(frame, pitch, - lrintf(duration), - feature.label.c_str())); - - } else { - - DenseThreeDimensionalModel::Column values = feature.values; - - EditableDenseThreeDimensionalModel *model = - getOutput(); - if (!model) return; - - model->setColumn(frame / model->getResolution(), values); - } -} - -void -FeatureExtractionPluginTransformer::setCompletion(int completion) -{ - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionPluginTransformer::setCompletion(" -// << completion << ")" << std::endl; - - if (binCount == 0) { - - SparseOneDimensionalModel *model = getOutput(); - if (!model) return; - model->setCompletion(completion); - - } else if (binCount == 1) { - - SparseTimeValueModel *model = getOutput(); - if (!model) return; - model->setCompletion(completion); - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - NoteModel *model = getOutput(); - if (!model) return; - model->setCompletion(completion); - - } else { - - EditableDenseThreeDimensionalModel *model = - getOutput(); - if (!model) return; - model->setCompletion(completion); - } -} - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/FeatureExtractionPluginTransformer.h --- a/plugin/transform/FeatureExtractionPluginTransformer.h Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* -*- 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 and QMUL. - - 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_TRANSFORMER_H_ -#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ - -#include "PluginTransformer.h" - -class DenseTimeValueModel; - -class FeatureExtractionPluginTransformer : public PluginTransformer -{ - Q_OBJECT - -public: - FeatureExtractionPluginTransformer(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString outputName = ""); - virtual ~FeatureExtractionPluginTransformer(); - -protected: - virtual void run(); - - Vamp::Plugin *m_plugin; - Vamp::Plugin::OutputDescriptor *m_descriptor; - int m_outputFeatureNo; - - void addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature); - - void setCompletion(int); - - void getFrames(int channel, int channelCount, - long startFrame, long size, float *buffer); - - // just casts - DenseTimeValueModel *getInput(); - template ModelClass *getOutput() { - ModelClass *mc = dynamic_cast(m_output); - if (!mc) { - std::cerr << "FeatureExtractionPluginTransformer::getOutput: Output model not conformable" << std::endl; - } - return mc; - } -}; - -#endif - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/ModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/ModelTransformer.cpp Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,32 @@ +/* -*- 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. +*/ + +#include "ModelTransformer.h" + +ModelTransformer::ModelTransformer(Model *m) : + m_input(m), + m_output(0), + m_detached(false), + m_abandoned(false) +{ +} + +ModelTransformer::~ModelTransformer() +{ + m_abandoned = true; + wait(); + if (!m_detached) delete m_output; +} + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/ModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/ModelTransformer.h Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,59 @@ +/* -*- 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 _TRANSFORMER_H_ +#define _TRANSFORMER_H_ + +#include "base/Thread.h" + +#include "data/model/Model.h" + +/** + * A ModelTransformer turns one data model into another. + * + * Typically in this application, a ModelTransformer might have a + * DenseTimeValueModel as its input (e.g. an audio waveform) and a + * SparseOneDimensionalModel (e.g. detected beats) as its output. + * + * The ModelTransformer typically runs in the background, as a + * separate thread populating the output model. The model is + * available to the user of the ModelTransformer immediately, but may + * be initially empty until the background thread has populated it. + */ + +class ModelTransformer : public Thread +{ +public: + virtual ~ModelTransformer(); + + // Just a hint to the processing thread that it should give up. + // Caller should still wait() and/or delete the transform before + // assuming its input and output models are no longer required. + void abandon() { m_abandoned = true; } + + Model *getInputModel() { return m_input; } + Model *getOutputModel() { return m_output; } + Model *detachOutputModel() { m_detached = true; return m_output; } + +protected: + ModelTransformer(Model *m); + + Model *m_input; // I don't own this + Model *m_output; // I own this, unless... + bool m_detached; // ... this is true. + bool m_abandoned; +}; + +#endif diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/ModelTransformerFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/ModelTransformerFactory.cpp Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,867 @@ +/* -*- 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 and QMUL. + + 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. +*/ + +#include "ModelTransformerFactory.h" + +#include "FeatureExtractionModelTransformer.h" +#include "RealTimeEffectModelTransformer.h" + +#include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/RealTimePluginFactory.h" +#include "plugin/PluginXml.h" + +#include "widgets/PluginParameterDialog.h" + +#include "data/model/DenseTimeValueModel.h" + +#include "vamp-sdk/PluginHostAdapter.h" + +#include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here + +#include +#include + +#include + +ModelTransformerFactory * +ModelTransformerFactory::m_instance = new ModelTransformerFactory; + +ModelTransformerFactory * +ModelTransformerFactory::getInstance() +{ + return m_instance; +} + +ModelTransformerFactory::~ModelTransformerFactory() +{ +} + +TransformList +ModelTransformerFactory::getAllTransforms() +{ + if (m_transforms.empty()) populateTransforms(); + + std::set dset; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + dset.insert(i->second); + } + + TransformList list; + for (std::set::const_iterator i = dset.begin(); + i != dset.end(); ++i) { + list.push_back(*i); + } + + return list; +} + +std::vector +ModelTransformerFactory::getAllTransformerTypes() +{ + if (m_transforms.empty()) populateTransforms(); + + std::set types; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + types.insert(i->second.type); + } + + std::vector rv; + for (std::set::iterator i = types.begin(); i != types.end(); ++i) { + rv.push_back(*i); + } + + return rv; +} + +std::vector +ModelTransformerFactory::getTransformerCategories(QString transformType) +{ + if (m_transforms.empty()) populateTransforms(); + + std::set categories; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + if (i->second.type == transformType) { + categories.insert(i->second.category); + } + } + + bool haveEmpty = false; + + std::vector rv; + for (std::set::iterator i = categories.begin(); + i != categories.end(); ++i) { + if (*i != "") rv.push_back(*i); + else haveEmpty = true; + } + + if (haveEmpty) rv.push_back(""); // make sure empty category sorts last + + return rv; +} + +std::vector +ModelTransformerFactory::getTransformerMakers(QString transformType) +{ + if (m_transforms.empty()) populateTransforms(); + + std::set makers; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + if (i->second.type == transformType) { + makers.insert(i->second.maker); + } + } + + bool haveEmpty = false; + + std::vector rv; + for (std::set::iterator i = makers.begin(); + i != makers.end(); ++i) { + if (*i != "") rv.push_back(*i); + else haveEmpty = true; + } + + if (haveEmpty) rv.push_back(""); // make sure empty category sorts last + + return rv; +} + +void +ModelTransformerFactory::populateTransforms() +{ + TransformDescriptionMap transforms; + + populateFeatureExtractionPlugins(transforms); + populateRealTimePlugins(transforms); + + // disambiguate plugins with similar names + + std::map names; + std::map pluginSources; + std::map pluginMakers; + + for (TransformDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + TransformDescription desc = i->second; + + QString td = desc.name; + QString tn = td.section(": ", 0, 0); + QString pn = desc.identifier.section(":", 1, 1); + + if (pluginSources.find(tn) != pluginSources.end()) { + if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) { + ++names[tn]; + } + } else { + ++names[tn]; + pluginSources[tn] = pn; + pluginMakers[tn] = desc.maker; + } + } + + std::map counts; + m_transforms.clear(); + + for (TransformDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + TransformDescription desc = i->second; + QString identifier = desc.identifier; + QString maker = desc.maker; + + QString td = desc.name; + QString tn = td.section(": ", 0, 0); + QString to = td.section(": ", 1); + + if (names[tn] > 1) { + maker.replace(QRegExp(tr(" [\\(<].*$")), ""); + tn = QString("%1 [%2]").arg(tn).arg(maker); + } + + if (to != "") { + desc.name = QString("%1: %2").arg(tn).arg(to); + } else { + desc.name = tn; + } + + m_transforms[identifier] = desc; + } +} + +void +ModelTransformerFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) +{ + std::vector plugs = + FeatureExtractionPluginFactory::getAllPluginIdentifiers(); + + for (size_t i = 0; i < plugs.size(); ++i) { + + QString pluginId = plugs[i]; + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + if (!factory) { + std::cerr << "WARNING: ModelTransformerFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; + continue; + } + + Vamp::Plugin *plugin = + factory->instantiatePlugin(pluginId, 48000); + + if (!plugin) { + std::cerr << "WARNING: ModelTransformerFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; + continue; + } + + QString pluginName = plugin->getName().c_str(); + QString category = factory->getPluginCategory(pluginId); + + Vamp::Plugin::OutputList outputs = + plugin->getOutputDescriptors(); + + for (size_t j = 0; j < outputs.size(); ++j) { + + QString transformId = QString("%1:%2") + .arg(pluginId).arg(outputs[j].identifier.c_str()); + + QString userName; + QString friendlyName; + QString units = outputs[j].unit.c_str(); + QString description = plugin->getDescription().c_str(); + QString maker = plugin->getMaker().c_str(); + if (maker == "") maker = tr(""); + + if (description == "") { + if (outputs.size() == 1) { + description = tr("Extract features using \"%1\" plugin (from %2)") + .arg(pluginName).arg(maker); + } else { + description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)") + .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); + } + } else { + if (outputs.size() == 1) { + description = tr("%1 using \"%2\" plugin (from %3)") + .arg(description).arg(pluginName).arg(maker); + } else { + description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)") + .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); + } + } + + if (outputs.size() == 1) { + userName = pluginName; + friendlyName = pluginName; + } else { + userName = QString("%1: %2") + .arg(pluginName) + .arg(outputs[j].name.c_str()); + friendlyName = outputs[j].name.c_str(); + } + + bool configurable = (!plugin->getPrograms().empty() || + !plugin->getParameterDescriptors().empty()); + +// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl; + + transforms[transformId] = + TransformDescription(tr("Analysis"), + category, + transformId, + userName, + friendlyName, + description, + maker, + units, + configurable); + } + + delete plugin; + } +} + +void +ModelTransformerFactory::populateRealTimePlugins(TransformDescriptionMap &transforms) +{ + std::vector plugs = + RealTimePluginFactory::getAllPluginIdentifiers(); + + static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); + + for (size_t i = 0; i < plugs.size(); ++i) { + + QString pluginId = plugs[i]; + + RealTimePluginFactory *factory = + RealTimePluginFactory::instanceFor(pluginId); + + if (!factory) { + std::cerr << "WARNING: ModelTransformerFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; + continue; + } + + const RealTimePluginDescriptor *descriptor = + factory->getPluginDescriptor(pluginId); + + if (!descriptor) { + std::cerr << "WARNING: ModelTransformerFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl; + continue; + } + +//!!! if (descriptor->controlOutputPortCount == 0 || +// descriptor->audioInputPortCount == 0) continue; + +// std::cout << "ModelTransformerFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl; + + QString pluginName = descriptor->name.c_str(); + QString category = factory->getPluginCategory(pluginId); + bool configurable = (descriptor->parameterCount > 0); + QString maker = descriptor->maker.c_str(); + if (maker == "") maker = tr(""); + + if (descriptor->audioInputPortCount > 0) { + + for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { + + QString transformId = QString("%1:%2").arg(pluginId).arg(j); + QString userName; + QString units; + QString portName; + + if (j < descriptor->controlOutputPortNames.size() && + descriptor->controlOutputPortNames[j] != "") { + + portName = descriptor->controlOutputPortNames[j].c_str(); + + userName = tr("%1: %2") + .arg(pluginName) + .arg(portName); + + if (unitRE.indexIn(portName) >= 0) { + units = unitRE.cap(1); + } + + } else if (descriptor->controlOutputPortCount > 1) { + + userName = tr("%1: Output %2") + .arg(pluginName) + .arg(j + 1); + + } else { + + userName = pluginName; + } + + QString description; + + if (portName != "") { + description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)") + .arg(portName) + .arg(pluginName) + .arg(maker); + } else { + description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)") + .arg(j + 1) + .arg(pluginName) + .arg(maker); + } + + transforms[transformId] = + TransformDescription(tr("Effects Data"), + category, + transformId, + userName, + userName, + description, + maker, + units, + configurable); + } + } + + if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { + + if (descriptor->audioOutputPortCount > 0) { + + QString transformId = QString("%1:A").arg(pluginId); + QString type = tr("Effects"); + + QString description = tr("Transformer audio signal with \"%1\" effect plugin (from %2)") + .arg(pluginName) + .arg(maker); + + if (descriptor->audioInputPortCount == 0) { + type = tr("Generators"); + QString description = tr("Generate audio signal using \"%1\" plugin (from %2)") + .arg(pluginName) + .arg(maker); + } + + transforms[transformId] = + TransformDescription(type, + category, + transformId, + pluginName, + pluginName, + description, + maker, + "", + configurable); + } + } + } +} + +bool +ModelTransformerFactory::haveTransformer(TransformId identifier) +{ + return (m_transforms.find(identifier) != m_transforms.end()); +} + +QString +ModelTransformerFactory::getTransformerName(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].name; + } else return ""; +} + +QString +ModelTransformerFactory::getTransformerFriendlyName(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].friendlyName; + } else return ""; +} + +QString +ModelTransformerFactory::getTransformerUnits(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].units; + } else return ""; +} + +bool +ModelTransformerFactory::isTransformerConfigurable(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].configurable; + } else return false; +} + +bool +ModelTransformerFactory::getTransformerChannelRange(TransformId identifier, + int &min, int &max) +{ + QString id = identifier.section(':', 0, 2); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + Vamp::Plugin *plugin = + FeatureExtractionPluginFactory::instanceFor(id)-> + instantiatePlugin(id, 48000); + if (!plugin) return false; + + min = plugin->getMinChannelCount(); + max = plugin->getMaxChannelCount(); + delete plugin; + + return true; + + } else if (RealTimePluginFactory::instanceFor(id)) { + + const RealTimePluginDescriptor *descriptor = + RealTimePluginFactory::instanceFor(id)-> + getPluginDescriptor(id); + if (!descriptor) return false; + + min = descriptor->audioInputPortCount; + max = descriptor->audioInputPortCount; + + return true; + } + + return false; +} + +bool +ModelTransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin, + int &minChannels, int &maxChannels) +{ + Vamp::Plugin *vp = 0; + if ((vp = dynamic_cast(plugin)) || + (vp = dynamic_cast(plugin))) { + minChannels = vp->getMinChannelCount(); + maxChannels = vp->getMaxChannelCount(); + return true; + } else { + return getTransformerChannelRange(identifier, minChannels, maxChannels); + } +} + +Model * +ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier, + const std::vector &candidateInputModels, + PluginTransformer::ExecutionContext &context, + QString &configurationXml, + AudioCallbackPlaySource *source, + size_t startFrame, + size_t duration) +{ + if (candidateInputModels.empty()) return 0; + + //!!! This will need revision -- we'll have to have a callback + //from the dialog for when the candidate input model is changed, + //as we'll need to reinitialise the channel settings in the dialog + Model *inputModel = candidateInputModels[0]; //!!! for now + QStringList candidateModelNames; + std::map modelMap; + for (size_t i = 0; i < candidateInputModels.size(); ++i) { + QString modelName = candidateInputModels[i]->objectName(); + QString origModelName = modelName; + int dupcount = 1; + while (modelMap.find(modelName) != modelMap.end()) { + modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount); + } + modelMap[modelName] = candidateInputModels[i]; + candidateModelNames.push_back(modelName); + } + + QString id = identifier.section(':', 0, 2); + QString output = identifier.section(':', 3); + QString outputLabel = ""; + QString outputDescription = ""; + + bool ok = false; + configurationXml = m_lastConfigurations[identifier]; + +// std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; + + Vamp::PluginBase *plugin = 0; + + bool frequency = false; + bool effect = false; + bool generator = false; + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + std::cerr << "getConfigurationForTransformer: instantiating Vamp plugin" << std::endl; + + Vamp::Plugin *vp = + FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin + (id, inputModel->getSampleRate()); + + if (vp) { + + plugin = vp; + frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain); + + std::vector od = + vp->getOutputDescriptors(); + if (od.size() > 1) { + for (size_t i = 0; i < od.size(); ++i) { + if (od[i].identifier == output.toStdString()) { + outputLabel = od[i].name.c_str(); + outputDescription = od[i].description.c_str(); + break; + } + } + } + } + + } else if (RealTimePluginFactory::instanceFor(id)) { + + RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); + const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); + + if (desc->audioInputPortCount > 0 && + desc->audioOutputPortCount > 0 && + !desc->isSynth) { + effect = true; + } + + if (desc->audioInputPortCount == 0) { + generator = true; + } + + if (output != "A") { + int outputNo = output.toInt(); + if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) { + outputLabel = desc->controlOutputPortNames[outputNo].c_str(); + } + } + + size_t sampleRate = inputModel->getSampleRate(); + size_t blockSize = 1024; + size_t channels = 1; + if (effect && source) { + sampleRate = source->getTargetSampleRate(); + blockSize = source->getTargetBlockSize(); + channels = source->getTargetChannelCount(); + } + + RealTimePluginInstance *rtp = factory->instantiatePlugin + (id, 0, 0, sampleRate, blockSize, channels); + + plugin = rtp; + + if (effect && source && rtp) { + source->setAuditioningPlugin(rtp); + } + } + + if (plugin) { + + context = PluginTransformer::ExecutionContext(context.channel, plugin); + + if (configurationXml != "") { + PluginXml(plugin).setParametersFromXml(configurationXml); + } + + int sourceChannels = 1; + if (dynamic_cast(inputModel)) { + sourceChannels = dynamic_cast(inputModel) + ->getChannelCount(); + } + + int minChannels = 1, maxChannels = sourceChannels; + getChannelRange(identifier, plugin, minChannels, maxChannels); + + int targetChannels = sourceChannels; + if (!effect) { + if (sourceChannels < minChannels) targetChannels = minChannels; + if (sourceChannels > maxChannels) targetChannels = maxChannels; + } + + int defaultChannel = context.channel; + + PluginParameterDialog *dialog = new PluginParameterDialog(plugin); + + if (candidateModelNames.size() > 1 && !generator) { + dialog->setCandidateInputModels(candidateModelNames); + } + + if (startFrame != 0 || duration != 0) { + dialog->setShowSelectionOnlyOption(true); + } + + if (targetChannels > 0) { + dialog->setChannelArrangement(sourceChannels, targetChannels, + defaultChannel); + } + + dialog->setOutputLabel(outputLabel, outputDescription); + + dialog->setShowProcessingOptions(true, frequency); + + if (dialog->exec() == QDialog::Accepted) { + ok = true; + } + + QString selectedInput = dialog->getInputModel(); + if (selectedInput != "") { + if (modelMap.find(selectedInput) != modelMap.end()) { + inputModel = modelMap[selectedInput]; + std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl; + } else { + std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl; + } + } else { + std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl; + } + + configurationXml = PluginXml(plugin).toXmlString(); + context.channel = dialog->getChannel(); + + if (startFrame != 0 || duration != 0) { + if (dialog->getSelectionOnly()) { + context.startFrame = startFrame; + context.duration = duration; + } + } + + dialog->getProcessingParameters(context.stepSize, + context.blockSize, + context.windowType); + + context.makeConsistentWithPlugin(plugin); + + delete dialog; + + if (effect && source) { + source->setAuditioningPlugin(0); // will delete our plugin + } else { + delete plugin; + } + } + + if (ok) m_lastConfigurations[identifier] = configurationXml; + + return ok ? inputModel : 0; +} + +PluginTransformer::ExecutionContext +ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier, + Model *inputModel) +{ + PluginTransformer::ExecutionContext context(-1); + + QString id = identifier.section(':', 0, 2); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + Vamp::Plugin *vp = + FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin + (id, inputModel ? inputModel->getSampleRate() : 48000); + + if (vp) { + context = PluginTransformer::ExecutionContext(-1, vp); + delete vp; + } + } + + return context; +} + +ModelTransformer * +ModelTransformerFactory::createTransformer(TransformId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml) +{ + ModelTransformer *transformer = 0; + + QString id = identifier.section(':', 0, 2); + QString output = identifier.section(':', 3); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + transformer = new FeatureExtractionModelTransformer(inputModel, + id, + context, + configurationXml, + output); + } else if (RealTimePluginFactory::instanceFor(id)) { + transformer = new RealTimeEffectModelTransformer(inputModel, + id, + context, + configurationXml, + getTransformerUnits(identifier), + output == "A" ? -1 : + output.toInt()); + } else { + std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \"" + << identifier.toStdString() << "\"" << std::endl; + return transformer; + } + + if (transformer) transformer->setObjectName(identifier); + return transformer; +} + +Model * +ModelTransformerFactory::transform(TransformId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml) +{ + ModelTransformer *t = createTransformer(identifier, inputModel, context, + configurationXml); + + if (!t) return 0; + + connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); + + m_runningTransformers.insert(t); + + t->start(); + Model *model = t->detachOutputModel(); + + if (model) { + QString imn = inputModel->objectName(); + QString trn = getTransformerFriendlyName(identifier); + if (imn != "") { + if (trn != "") { + model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); + } else { + model->setObjectName(imn); + } + } else if (trn != "") { + model->setObjectName(trn); + } + } else { + t->wait(); + } + + return model; +} + +void +ModelTransformerFactory::transformerFinished() +{ + QObject *s = sender(); + ModelTransformer *transformer = dynamic_cast(s); + + std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl; + + if (!transformer) { + std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl; + return; + } + + if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { + std::cerr << "WARNING: ModelTransformerFactory::transformerFinished(" + << transformer + << "): I have no record of this transformer running!" + << std::endl; + } + + m_runningTransformers.erase(transformer); + + transformer->wait(); // unnecessary but reassuring + delete transformer; +} + +void +ModelTransformerFactory::modelAboutToBeDeleted(Model *m) +{ + TransformerSet affected; + + for (TransformerSet::iterator i = m_runningTransformers.begin(); + i != m_runningTransformers.end(); ++i) { + + ModelTransformer *t = *i; + + if (t->getInputModel() == m || t->getOutputModel() == m) { + affected.insert(t); + } + } + + for (TransformerSet::iterator i = affected.begin(); + i != affected.end(); ++i) { + + ModelTransformer *t = *i; + + t->abandon(); + + t->wait(); // this should eventually call back on + // transformerFinished, which will remove from + // m_runningTransformers and delete. + } +} + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/ModelTransformerFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/ModelTransformerFactory.h Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,156 @@ +/* -*- 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 and QMUL. + + 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 _MODEL_TRANSFORMER_FACTORY_H_ +#define _MODEL_TRANSFORMER_FACTORY_H_ + +#include "Transform.h" +#include "TransformDescription.h" + +#include "ModelTransformer.h" + +#include "PluginTransformer.h" + +#include +#include + +namespace Vamp { class PluginBase; } + +class AudioCallbackPlaySource; + +//!!! split into TransformFactory (information about available +// transforms, create default Transform for each transform ID etc) and +// TransformerFactory (create Transformers to apply transforms) + +class ModelTransformerFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~ModelTransformerFactory(); + + static ModelTransformerFactory *getInstance(); + + TransformList getAllTransforms(); + + std::vector getAllTransformerTypes(); + + std::vector getTransformerCategories(QString transformType); + std::vector getTransformerMakers(QString transformType); + + /** + * Get a configuration XML string for the given transform (by + * asking the user, most likely). Returns the selected input + * model if the transform is acceptable, 0 if the operation should + * be cancelled. Audio callback play source may be used to + * audition effects plugins, if provided. + */ + Model *getConfigurationForTransformer(TransformId identifier, + const std::vector &candidateInputModels, + PluginTransformer::ExecutionContext &context, + QString &configurationXml, + AudioCallbackPlaySource *source = 0, + size_t startFrame = 0, + size_t duration = 0); + + /** + * Get the default execution context for the given transform + * and input model (if known). + */ + PluginTransformer::ExecutionContext getDefaultContextForTransformer(TransformId identifier, + Model *inputModel = 0); + + /** + * Return the output model resulting from applying the named + * transform to the given input model. The transform may still be + * working in the background when the model is returned; check the + * output model's isReady completion status for more details. + * + * If the transform is unknown or the input model is not an + * appropriate type for the given transform, or if some other + * problem occurs, return 0. + * + * The returned model is owned by the caller and must be deleted + * when no longer needed. + */ + Model *transform(TransformId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml = ""); + + /** + * Return true if the given transform is known. + */ + bool haveTransformer(TransformId identifier); + + /** + * Full name of a transform, suitable for putting on a menu. + */ + QString getTransformerName(TransformId identifier); + + /** + * Brief but friendly name of a transform, suitable for use + * as the name of the output layer. + */ + QString getTransformerFriendlyName(TransformId identifier); + + QString getTransformerUnits(TransformId identifier); + + /** + * Return true if the transform has any configurable parameters, + * i.e. if getConfigurationForTransformer can ever return a non-trivial + * (not equivalent to empty) configuration string. + */ + bool isTransformerConfigurable(TransformId identifier); + + /** + * If the transform has a prescribed number or range of channel + * inputs, return true and set minChannels and maxChannels to the + * minimum and maximum number of channel inputs the transform can + * accept. Return false if it doesn't care. + */ + bool getTransformerChannelRange(TransformId identifier, + int &minChannels, int &maxChannels); + +protected slots: + void transformerFinished(); + + void modelAboutToBeDeleted(Model *); + +protected: + ModelTransformer *createTransformer(TransformId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml); + + typedef std::map TransformerConfigurationMap; + TransformerConfigurationMap m_lastConfigurations; + + typedef std::map TransformDescriptionMap; + TransformDescriptionMap m_transforms; + + typedef std::set TransformerSet; + TransformerSet m_runningTransformers; + + void populateTransforms(); + void populateFeatureExtractionPlugins(TransformDescriptionMap &); + void populateRealTimePlugins(TransformDescriptionMap &); + + bool getChannelRange(TransformId identifier, + Vamp::PluginBase *plugin, int &min, int &max); + + static ModelTransformerFactory *m_instance; +}; + + +#endif diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/PluginTransformer.cpp --- a/plugin/transform/PluginTransformer.cpp Tue Nov 06 17:15:00 2007 +0000 +++ b/plugin/transform/PluginTransformer.cpp Wed Nov 07 12:59:01 2007 +0000 @@ -20,7 +20,7 @@ PluginTransformer::PluginTransformer(Model *inputModel, const ExecutionContext &context) : - Transformer(inputModel), + ModelTransformer(inputModel), m_context(context) { } diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/PluginTransformer.h --- a/plugin/transform/PluginTransformer.h Tue Nov 06 17:15:00 2007 +0000 +++ b/plugin/transform/PluginTransformer.h Wed Nov 07 12:59:01 2007 +0000 @@ -16,7 +16,7 @@ #ifndef _PLUGIN_TRANSFORMER_H_ #define _PLUGIN_TRANSFORMER_H_ -#include "Transformer.h" +#include "ModelTransformer.h" #include "base/Window.h" @@ -25,7 +25,7 @@ //!!! should this just move back up to Transformer? It is after all used //directly in all sorts of generic places, like Document -class PluginTransformer : public Transformer +class PluginTransformer : public ModelTransformer { public: class ExecutionContext { diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/RealTimeEffectModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.cpp Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,274 @@ +/* -*- 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 and QMUL. + + 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. +*/ + +#include "RealTimeEffectModelTransformer.h" + +#include "plugin/RealTimePluginFactory.h" +#include "plugin/RealTimePluginInstance.h" +#include "plugin/PluginXml.h" + +#include "data/model/Model.h" +#include "data/model/SparseTimeValueModel.h" +#include "data/model/DenseTimeValueModel.h" +#include "data/model/WritableWaveFileModel.h" +#include "data/model/WaveFileModel.h" + +#include + +RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Model *inputModel, + QString pluginId, + const ExecutionContext &context, + QString configurationXml, + QString units, + int output) : + PluginTransformer(inputModel, context), + m_pluginId(pluginId), + m_configurationXml(configurationXml), + m_units(units), + m_plugin(0), + m_outputNo(output) +{ + if (!m_context.blockSize) m_context.blockSize = 1024; + +// std::cerr << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl; + + RealTimePluginFactory *factory = + RealTimePluginFactory::instanceFor(pluginId); + + if (!factory) { + std::cerr << "RealTimeEffectModelTransformer: No factory available for plugin id \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + DenseTimeValueModel *input = getInput(); + if (!input) return; + + m_plugin = factory->instantiatePlugin(pluginId, 0, 0, + m_input->getSampleRate(), + m_context.blockSize, + input->getChannelCount()); + + if (!m_plugin) { + std::cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + if (configurationXml != "") { + PluginXml(m_plugin).setParametersFromXml(configurationXml); + } + + if (m_outputNo >= 0 && + m_outputNo >= int(m_plugin->getControlOutputCount())) { + std::cerr << "RealTimeEffectModelTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; + return; + } + + if (m_outputNo == -1) { + + size_t outputChannels = m_plugin->getAudioOutputCount(); + if (outputChannels > input->getChannelCount()) { + outputChannels = input->getChannelCount(); + } + + WritableWaveFileModel *model = new WritableWaveFileModel + (input->getSampleRate(), outputChannels); + + m_output = model; + + } else { + + SparseTimeValueModel *model = new SparseTimeValueModel + (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); + + if (units != "") model->setScaleUnits(units); + + m_output = model; + } +} + +RealTimeEffectModelTransformer::~RealTimeEffectModelTransformer() +{ + delete m_plugin; +} + +DenseTimeValueModel * +RealTimeEffectModelTransformer::getInput() +{ + DenseTimeValueModel *dtvm = + dynamic_cast(getInputModel()); + if (!dtvm) { + std::cerr << "RealTimeEffectModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + } + return dtvm; +} + +void +RealTimeEffectModelTransformer::run() +{ + DenseTimeValueModel *input = getInput(); + if (!input) return; + + while (!input->isReady()) { + if (dynamic_cast(input)) break; // no need to wait + std::cerr << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << std::endl; + sleep(1); + } + + SparseTimeValueModel *stvm = dynamic_cast(m_output); + WritableWaveFileModel *wwfm = dynamic_cast(m_output); + if (!stvm && !wwfm) return; + + if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; + + size_t sampleRate = input->getSampleRate(); + size_t channelCount = input->getChannelCount(); + if (!wwfm && m_context.channel != -1) channelCount = 1; + + long blockSize = m_plugin->getBufferSize(); + + float **inbufs = m_plugin->getAudioInputBuffers(); + + long startFrame = m_input->getStartFrame(); + long endFrame = m_input->getEndFrame(); + + long contextStart = m_context.startFrame; + long contextDuration = m_context.duration; + + if (contextStart == 0 || contextStart < startFrame) { + contextStart = startFrame; + } + + if (contextDuration == 0) { + contextDuration = endFrame - contextStart; + } + if (contextStart + contextDuration > endFrame) { + contextDuration = endFrame - contextStart; + } + + wwfm->setStartFrame(contextStart); + + long blockFrame = contextStart; + + long prevCompletion = 0; + + long latency = m_plugin->getLatency(); + + while (blockFrame < contextStart + contextDuration + latency && + !m_abandoned) { + + long completion = + (((blockFrame - contextStart) / blockSize) * 99) / + ((contextDuration) / blockSize); + + long got = 0; + + if (channelCount == 1) { + if (inbufs && inbufs[0]) { + got = input->getData + (m_context.channel, blockFrame, blockSize, inbufs[0]); + while (got < blockSize) { + inbufs[0][got++] = 0.0; + } + } + for (size_t ch = 1; ch < m_plugin->getAudioInputCount(); ++ch) { + for (long i = 0; i < blockSize; ++i) { + inbufs[ch][i] = inbufs[0][i]; + } + } + } else { + for (size_t ch = 0; ch < channelCount; ++ch) { + if (inbufs && inbufs[ch]) { + got = input->getData + (ch, blockFrame, blockSize, inbufs[ch]); + while (got < blockSize) { + inbufs[ch][got++] = 0.0; + } + } + } + for (size_t ch = channelCount; ch < m_plugin->getAudioInputCount(); ++ch) { + for (long i = 0; i < blockSize; ++i) { + inbufs[ch][i] = inbufs[ch % channelCount][i]; + } + } + } + +/* + std::cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< std::endl; + + for (size_t ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) { + std::cerr << "Input channel " << ch << std::endl; + for (size_t i = 0; i < 100; ++i) { + std::cerr << inbufs[ch][i] << " "; + if (isnan(inbufs[ch][i])) { + std::cerr << "\n\nWARNING: NaN in audio input" << std::endl; + } + } + } +*/ + + m_plugin->run(Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); + + if (stvm) { + + float value = m_plugin->getControlOutputValue(m_outputNo); + + long pointFrame = blockFrame; + if (pointFrame > latency) pointFrame -= latency; + else pointFrame = 0; + + stvm->addPoint(SparseTimeValueModel::Point + (pointFrame, value, "")); + + } else if (wwfm) { + + float **outbufs = m_plugin->getAudioOutputBuffers(); + + if (outbufs) { + + if (blockFrame >= latency) { + long writeSize = std::min + (blockSize, + contextStart + contextDuration + latency - blockFrame); + wwfm->addSamples(outbufs, writeSize); + } else if (blockFrame + blockSize >= latency) { + long offset = latency - blockFrame; + long count = blockSize - offset; + float **tmp = new float *[channelCount]; + for (size_t c = 0; c < channelCount; ++c) { + tmp[c] = outbufs[c] + offset; + } + wwfm->addSamples(tmp, count); + delete[] tmp; + } + } + } + + if (blockFrame == contextStart || completion > prevCompletion) { + if (stvm) stvm->setCompletion(completion); + if (wwfm) wwfm->setCompletion(completion); + prevCompletion = completion; + } + + blockFrame += blockSize; + } + + if (m_abandoned) return; + + if (stvm) stvm->setCompletion(100); + if (wwfm) wwfm->setCompletion(100); +} + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/RealTimeEffectModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.h Wed Nov 07 12:59:01 2007 +0000 @@ -0,0 +1,50 @@ +/* -*- 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 and QMUL. + + 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 _REAL_TIME_PLUGIN_TRANSFORMER_H_ +#define _REAL_TIME_PLUGIN_TRANSFORMER_H_ + +#include "PluginTransformer.h" +#include "plugin/RealTimePluginInstance.h" + +class DenseTimeValueModel; + +class RealTimeEffectModelTransformer : public PluginTransformer +{ +public: + RealTimeEffectModelTransformer(Model *inputModel, + QString plugin, + const ExecutionContext &context, + QString configurationXml = "", + QString units = "", + int output = -1); // -1 -> audio, 0+ -> data + virtual ~RealTimeEffectModelTransformer(); + +protected: + virtual void run(); + + QString m_pluginId; + QString m_configurationXml; + QString m_units; + + RealTimePluginInstance *m_plugin; + int m_outputNo; + + // just casts + DenseTimeValueModel *getInput(); +}; + +#endif + diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/RealTimePluginTransformer.cpp --- a/plugin/transform/RealTimePluginTransformer.cpp Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,274 +0,0 @@ -/* -*- 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 and QMUL. - - 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. -*/ - -#include "RealTimePluginTransformer.h" - -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - -#include "data/model/Model.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/WritableWaveFileModel.h" -#include "data/model/WaveFileModel.h" - -#include - -RealTimePluginTransformer::RealTimePluginTransformer(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString units, - int output) : - PluginTransformer(inputModel, context), - m_pluginId(pluginId), - m_configurationXml(configurationXml), - m_units(units), - m_plugin(0), - m_outputNo(output) -{ - if (!m_context.blockSize) m_context.blockSize = 1024; - -// std::cerr << "RealTimePluginTransformer::RealTimePluginTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "RealTimePluginTransformer: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - DenseTimeValueModel *input = getInput(); - if (!input) return; - - m_plugin = factory->instantiatePlugin(pluginId, 0, 0, - m_input->getSampleRate(), - m_context.blockSize, - input->getChannelCount()); - - if (!m_plugin) { - std::cerr << "RealTimePluginTransformer: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } - - if (m_outputNo >= 0 && - m_outputNo >= int(m_plugin->getControlOutputCount())) { - std::cerr << "RealTimePluginTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; - return; - } - - if (m_outputNo == -1) { - - size_t outputChannels = m_plugin->getAudioOutputCount(); - if (outputChannels > input->getChannelCount()) { - outputChannels = input->getChannelCount(); - } - - WritableWaveFileModel *model = new WritableWaveFileModel - (input->getSampleRate(), outputChannels); - - m_output = model; - - } else { - - SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); - - if (units != "") model->setScaleUnits(units); - - m_output = model; - } -} - -RealTimePluginTransformer::~RealTimePluginTransformer() -{ - delete m_plugin; -} - -DenseTimeValueModel * -RealTimePluginTransformer::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "RealTimePluginTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -RealTimePluginTransformer::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - while (!input->isReady()) { - if (dynamic_cast(input)) break; // no need to wait - std::cerr << "RealTimePluginTransformer::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - SparseTimeValueModel *stvm = dynamic_cast(m_output); - WritableWaveFileModel *wwfm = dynamic_cast(m_output); - if (!stvm && !wwfm) return; - - if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; - - size_t sampleRate = input->getSampleRate(); - size_t channelCount = input->getChannelCount(); - if (!wwfm && m_context.channel != -1) channelCount = 1; - - long blockSize = m_plugin->getBufferSize(); - - float **inbufs = m_plugin->getAudioInputBuffers(); - - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); - - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; - - if (contextStart == 0 || contextStart < startFrame) { - contextStart = startFrame; - } - - if (contextDuration == 0) { - contextDuration = endFrame - contextStart; - } - if (contextStart + contextDuration > endFrame) { - contextDuration = endFrame - contextStart; - } - - wwfm->setStartFrame(contextStart); - - long blockFrame = contextStart; - - long prevCompletion = 0; - - long latency = m_plugin->getLatency(); - - while (blockFrame < contextStart + contextDuration + latency && - !m_abandoned) { - - long completion = - (((blockFrame - contextStart) / blockSize) * 99) / - ((contextDuration) / blockSize); - - long got = 0; - - if (channelCount == 1) { - if (inbufs && inbufs[0]) { - got = input->getData - (m_context.channel, blockFrame, blockSize, inbufs[0]); - while (got < blockSize) { - inbufs[0][got++] = 0.0; - } - } - for (size_t ch = 1; ch < m_plugin->getAudioInputCount(); ++ch) { - for (long i = 0; i < blockSize; ++i) { - inbufs[ch][i] = inbufs[0][i]; - } - } - } else { - for (size_t ch = 0; ch < channelCount; ++ch) { - if (inbufs && inbufs[ch]) { - got = input->getData - (ch, blockFrame, blockSize, inbufs[ch]); - while (got < blockSize) { - inbufs[ch][got++] = 0.0; - } - } - } - for (size_t ch = channelCount; ch < m_plugin->getAudioInputCount(); ++ch) { - for (long i = 0; i < blockSize; ++i) { - inbufs[ch][i] = inbufs[ch % channelCount][i]; - } - } - } - -/* - std::cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< std::endl; - - for (size_t ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) { - std::cerr << "Input channel " << ch << std::endl; - for (size_t i = 0; i < 100; ++i) { - std::cerr << inbufs[ch][i] << " "; - if (isnan(inbufs[ch][i])) { - std::cerr << "\n\nWARNING: NaN in audio input" << std::endl; - } - } - } -*/ - - m_plugin->run(Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); - - if (stvm) { - - float value = m_plugin->getControlOutputValue(m_outputNo); - - long pointFrame = blockFrame; - if (pointFrame > latency) pointFrame -= latency; - else pointFrame = 0; - - stvm->addPoint(SparseTimeValueModel::Point - (pointFrame, value, "")); - - } else if (wwfm) { - - float **outbufs = m_plugin->getAudioOutputBuffers(); - - if (outbufs) { - - if (blockFrame >= latency) { - long writeSize = std::min - (blockSize, - contextStart + contextDuration + latency - blockFrame); - wwfm->addSamples(outbufs, writeSize); - } else if (blockFrame + blockSize >= latency) { - long offset = latency - blockFrame; - long count = blockSize - offset; - float **tmp = new float *[channelCount]; - for (size_t c = 0; c < channelCount; ++c) { - tmp[c] = outbufs[c] + offset; - } - wwfm->addSamples(tmp, count); - delete[] tmp; - } - } - } - - if (blockFrame == contextStart || completion > prevCompletion) { - if (stvm) stvm->setCompletion(completion); - if (wwfm) wwfm->setCompletion(completion); - prevCompletion = completion; - } - - blockFrame += blockSize; - } - - if (m_abandoned) return; - - if (stvm) stvm->setCompletion(100); - if (wwfm) wwfm->setCompletion(100); -} - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/RealTimePluginTransformer.h --- a/plugin/transform/RealTimePluginTransformer.h Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* -*- 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 and QMUL. - - 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 _REAL_TIME_PLUGIN_TRANSFORMER_H_ -#define _REAL_TIME_PLUGIN_TRANSFORMER_H_ - -#include "PluginTransformer.h" -#include "plugin/RealTimePluginInstance.h" - -class DenseTimeValueModel; - -class RealTimePluginTransformer : public PluginTransformer -{ -public: - RealTimePluginTransformer(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString units = "", - int output = -1); // -1 -> audio, 0+ -> data - virtual ~RealTimePluginTransformer(); - -protected: - virtual void run(); - - QString m_pluginId; - QString m_configurationXml; - QString m_units; - - RealTimePluginInstance *m_plugin; - int m_outputNo; - - // just casts - DenseTimeValueModel *getInput(); -}; - -#endif - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/Transformer.cpp --- a/plugin/transform/Transformer.cpp Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* -*- 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. -*/ - -#include "Transformer.h" - -Transformer::Transformer(Model *m) : - m_input(m), - m_output(0), - m_detached(false), - m_abandoned(false) -{ -} - -Transformer::~Transformer() -{ - m_abandoned = true; - wait(); - if (!m_detached) delete m_output; -} - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/Transformer.h --- a/plugin/transform/Transformer.h Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* -*- 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 _TRANSFORMER_H_ -#define _TRANSFORMER_H_ - -#include "base/Thread.h" - -#include "data/model/Model.h" - -typedef QString TransformerId; - -/** - * A Transformer turns one data model into another. - * - * Typically in this application, a Transformer might have a - * DenseTimeValueModel as its input (e.g. an audio waveform) and a - * SparseOneDimensionalModel (e.g. detected beats) as its output. - * - * The Transformer typically runs in the background, as a separate - * thread populating the output model. The model is available to the - * user of the Transformer immediately, but may be initially empty until - * the background thread has populated it. - */ - -class Transformer : public Thread -{ -public: - virtual ~Transformer(); - - // Just a hint to the processing thread that it should give up. - // Caller should still wait() and/or delete the transform before - // assuming its input and output models are no longer required. - void abandon() { m_abandoned = true; } - - Model *getInputModel() { return m_input; } - Model *getOutputModel() { return m_output; } - Model *detachOutputModel() { m_detached = true; return m_output; } - -protected: - Transformer(Model *m); - - Model *m_input; // I don't own this - Model *m_output; // I own this, unless... - bool m_detached; // ... this is true. - bool m_abandoned; -}; - -#endif diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/TransformerFactory.cpp --- a/plugin/transform/TransformerFactory.cpp Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,867 +0,0 @@ -/* -*- 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 and QMUL. - - 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. -*/ - -#include "TransformerFactory.h" - -#include "FeatureExtractionPluginTransformer.h" -#include "RealTimePluginTransformer.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/PluginXml.h" - -#include "widgets/PluginParameterDialog.h" - -#include "data/model/DenseTimeValueModel.h" - -#include "vamp-sdk/PluginHostAdapter.h" - -#include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here - -#include -#include - -#include - -TransformerFactory * -TransformerFactory::m_instance = new TransformerFactory; - -TransformerFactory * -TransformerFactory::getInstance() -{ - return m_instance; -} - -TransformerFactory::~TransformerFactory() -{ -} - -TransformList -TransformerFactory::getAllTransforms() -{ - if (m_transforms.empty()) populateTransforms(); - - std::set dset; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - dset.insert(i->second); - } - - TransformList list; - for (std::set::const_iterator i = dset.begin(); - i != dset.end(); ++i) { - list.push_back(*i); - } - - return list; -} - -std::vector -TransformerFactory::getAllTransformerTypes() -{ - if (m_transforms.empty()) populateTransforms(); - - std::set types; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - types.insert(i->second.type); - } - - std::vector rv; - for (std::set::iterator i = types.begin(); i != types.end(); ++i) { - rv.push_back(*i); - } - - return rv; -} - -std::vector -TransformerFactory::getTransformerCategories(QString transformType) -{ - if (m_transforms.empty()) populateTransforms(); - - std::set categories; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - if (i->second.type == transformType) { - categories.insert(i->second.category); - } - } - - bool haveEmpty = false; - - std::vector rv; - for (std::set::iterator i = categories.begin(); - i != categories.end(); ++i) { - if (*i != "") rv.push_back(*i); - else haveEmpty = true; - } - - if (haveEmpty) rv.push_back(""); // make sure empty category sorts last - - return rv; -} - -std::vector -TransformerFactory::getTransformerMakers(QString transformType) -{ - if (m_transforms.empty()) populateTransforms(); - - std::set makers; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - if (i->second.type == transformType) { - makers.insert(i->second.maker); - } - } - - bool haveEmpty = false; - - std::vector rv; - for (std::set::iterator i = makers.begin(); - i != makers.end(); ++i) { - if (*i != "") rv.push_back(*i); - else haveEmpty = true; - } - - if (haveEmpty) rv.push_back(""); // make sure empty category sorts last - - return rv; -} - -void -TransformerFactory::populateTransforms() -{ - TransformDescriptionMap transforms; - - populateFeatureExtractionPlugins(transforms); - populateRealTimePlugins(transforms); - - // disambiguate plugins with similar names - - std::map names; - std::map pluginSources; - std::map pluginMakers; - - for (TransformDescriptionMap::iterator i = transforms.begin(); - i != transforms.end(); ++i) { - - TransformDescription desc = i->second; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString pn = desc.identifier.section(":", 1, 1); - - if (pluginSources.find(tn) != pluginSources.end()) { - if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) { - ++names[tn]; - } - } else { - ++names[tn]; - pluginSources[tn] = pn; - pluginMakers[tn] = desc.maker; - } - } - - std::map counts; - m_transforms.clear(); - - for (TransformDescriptionMap::iterator i = transforms.begin(); - i != transforms.end(); ++i) { - - TransformDescription desc = i->second; - QString identifier = desc.identifier; - QString maker = desc.maker; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString to = td.section(": ", 1); - - if (names[tn] > 1) { - maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - tn = QString("%1 [%2]").arg(tn).arg(maker); - } - - if (to != "") { - desc.name = QString("%1: %2").arg(tn).arg(to); - } else { - desc.name = tn; - } - - m_transforms[identifier] = desc; - } -} - -void -TransformerFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) -{ - std::vector plugs = - FeatureExtractionPluginFactory::getAllPluginIdentifiers(); - - for (size_t i = 0; i < plugs.size(); ++i) { - - QString pluginId = plugs[i]; - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "WARNING: TransformerFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - Vamp::Plugin *plugin = - factory->instantiatePlugin(pluginId, 48000); - - if (!plugin) { - std::cerr << "WARNING: TransformerFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - QString pluginName = plugin->getName().c_str(); - QString category = factory->getPluginCategory(pluginId); - - Vamp::Plugin::OutputList outputs = - plugin->getOutputDescriptors(); - - for (size_t j = 0; j < outputs.size(); ++j) { - - QString transformId = QString("%1:%2") - .arg(pluginId).arg(outputs[j].identifier.c_str()); - - QString userName; - QString friendlyName; - QString units = outputs[j].unit.c_str(); - QString description = plugin->getDescription().c_str(); - QString maker = plugin->getMaker().c_str(); - if (maker == "") maker = tr(""); - - if (description == "") { - if (outputs.size() == 1) { - description = tr("Extract features using \"%1\" plugin (from %2)") - .arg(pluginName).arg(maker); - } else { - description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)") - .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); - } - } else { - if (outputs.size() == 1) { - description = tr("%1 using \"%2\" plugin (from %3)") - .arg(description).arg(pluginName).arg(maker); - } else { - description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)") - .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); - } - } - - if (outputs.size() == 1) { - userName = pluginName; - friendlyName = pluginName; - } else { - userName = QString("%1: %2") - .arg(pluginName) - .arg(outputs[j].name.c_str()); - friendlyName = outputs[j].name.c_str(); - } - - bool configurable = (!plugin->getPrograms().empty() || - !plugin->getParameterDescriptors().empty()); - -// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl; - - transforms[transformId] = - TransformDescription(tr("Analysis"), - category, - transformId, - userName, - friendlyName, - description, - maker, - units, - configurable); - } - - delete plugin; - } -} - -void -TransformerFactory::populateRealTimePlugins(TransformDescriptionMap &transforms) -{ - std::vector plugs = - RealTimePluginFactory::getAllPluginIdentifiers(); - - static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); - - for (size_t i = 0; i < plugs.size(); ++i) { - - QString pluginId = plugs[i]; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "WARNING: TransformerFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - const RealTimePluginDescriptor *descriptor = - factory->getPluginDescriptor(pluginId); - - if (!descriptor) { - std::cerr << "WARNING: TransformerFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - -//!!! if (descriptor->controlOutputPortCount == 0 || -// descriptor->audioInputPortCount == 0) continue; - -// std::cout << "TransformerFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl; - - QString pluginName = descriptor->name.c_str(); - QString category = factory->getPluginCategory(pluginId); - bool configurable = (descriptor->parameterCount > 0); - QString maker = descriptor->maker.c_str(); - if (maker == "") maker = tr(""); - - if (descriptor->audioInputPortCount > 0) { - - for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { - - QString transformId = QString("%1:%2").arg(pluginId).arg(j); - QString userName; - QString units; - QString portName; - - if (j < descriptor->controlOutputPortNames.size() && - descriptor->controlOutputPortNames[j] != "") { - - portName = descriptor->controlOutputPortNames[j].c_str(); - - userName = tr("%1: %2") - .arg(pluginName) - .arg(portName); - - if (unitRE.indexIn(portName) >= 0) { - units = unitRE.cap(1); - } - - } else if (descriptor->controlOutputPortCount > 1) { - - userName = tr("%1: Output %2") - .arg(pluginName) - .arg(j + 1); - - } else { - - userName = pluginName; - } - - QString description; - - if (portName != "") { - description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)") - .arg(portName) - .arg(pluginName) - .arg(maker); - } else { - description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)") - .arg(j + 1) - .arg(pluginName) - .arg(maker); - } - - transforms[transformId] = - TransformDescription(tr("Effects Data"), - category, - transformId, - userName, - userName, - description, - maker, - units, - configurable); - } - } - - if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { - - if (descriptor->audioOutputPortCount > 0) { - - QString transformId = QString("%1:A").arg(pluginId); - QString type = tr("Effects"); - - QString description = tr("Transformer audio signal with \"%1\" effect plugin (from %2)") - .arg(pluginName) - .arg(maker); - - if (descriptor->audioInputPortCount == 0) { - type = tr("Generators"); - QString description = tr("Generate audio signal using \"%1\" plugin (from %2)") - .arg(pluginName) - .arg(maker); - } - - transforms[transformId] = - TransformDescription(type, - category, - transformId, - pluginName, - pluginName, - description, - maker, - "", - configurable); - } - } - } -} - -bool -TransformerFactory::haveTransformer(TransformId identifier) -{ - return (m_transforms.find(identifier) != m_transforms.end()); -} - -QString -TransformerFactory::getTransformerName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].name; - } else return ""; -} - -QString -TransformerFactory::getTransformerFriendlyName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].friendlyName; - } else return ""; -} - -QString -TransformerFactory::getTransformerUnits(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].units; - } else return ""; -} - -bool -TransformerFactory::isTransformerConfigurable(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].configurable; - } else return false; -} - -bool -TransformerFactory::getTransformerChannelRange(TransformId identifier, - int &min, int &max) -{ - QString id = identifier.section(':', 0, 2); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *plugin = - FeatureExtractionPluginFactory::instanceFor(id)-> - instantiatePlugin(id, 48000); - if (!plugin) return false; - - min = plugin->getMinChannelCount(); - max = plugin->getMaxChannelCount(); - delete plugin; - - return true; - - } else if (RealTimePluginFactory::instanceFor(id)) { - - const RealTimePluginDescriptor *descriptor = - RealTimePluginFactory::instanceFor(id)-> - getPluginDescriptor(id); - if (!descriptor) return false; - - min = descriptor->audioInputPortCount; - max = descriptor->audioInputPortCount; - - return true; - } - - return false; -} - -bool -TransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin, - int &minChannels, int &maxChannels) -{ - Vamp::Plugin *vp = 0; - if ((vp = dynamic_cast(plugin)) || - (vp = dynamic_cast(plugin))) { - minChannels = vp->getMinChannelCount(); - maxChannels = vp->getMaxChannelCount(); - return true; - } else { - return getTransformerChannelRange(identifier, minChannels, maxChannels); - } -} - -Model * -TransformerFactory::getConfigurationForTransformer(TransformId identifier, - const std::vector &candidateInputModels, - PluginTransformer::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source, - size_t startFrame, - size_t duration) -{ - if (candidateInputModels.empty()) return 0; - - //!!! This will need revision -- we'll have to have a callback - //from the dialog for when the candidate input model is changed, - //as we'll need to reinitialise the channel settings in the dialog - Model *inputModel = candidateInputModels[0]; //!!! for now - QStringList candidateModelNames; - std::map modelMap; - for (size_t i = 0; i < candidateInputModels.size(); ++i) { - QString modelName = candidateInputModels[i]->objectName(); - QString origModelName = modelName; - int dupcount = 1; - while (modelMap.find(modelName) != modelMap.end()) { - modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount); - } - modelMap[modelName] = candidateInputModels[i]; - candidateModelNames.push_back(modelName); - } - - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); - QString outputLabel = ""; - QString outputDescription = ""; - - bool ok = false; - configurationXml = m_lastConfigurations[identifier]; - -// std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; - - Vamp::PluginBase *plugin = 0; - - bool frequency = false; - bool effect = false; - bool generator = false; - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - std::cerr << "getConfigurationForTransformer: instantiating Vamp plugin" << std::endl; - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, inputModel->getSampleRate()); - - if (vp) { - - plugin = vp; - frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain); - - std::vector od = - vp->getOutputDescriptors(); - if (od.size() > 1) { - for (size_t i = 0; i < od.size(); ++i) { - if (od[i].identifier == output.toStdString()) { - outputLabel = od[i].name.c_str(); - outputDescription = od[i].description.c_str(); - break; - } - } - } - } - - } else if (RealTimePluginFactory::instanceFor(id)) { - - RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); - const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); - - if (desc->audioInputPortCount > 0 && - desc->audioOutputPortCount > 0 && - !desc->isSynth) { - effect = true; - } - - if (desc->audioInputPortCount == 0) { - generator = true; - } - - if (output != "A") { - int outputNo = output.toInt(); - if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) { - outputLabel = desc->controlOutputPortNames[outputNo].c_str(); - } - } - - size_t sampleRate = inputModel->getSampleRate(); - size_t blockSize = 1024; - size_t channels = 1; - if (effect && source) { - sampleRate = source->getTargetSampleRate(); - blockSize = source->getTargetBlockSize(); - channels = source->getTargetChannelCount(); - } - - RealTimePluginInstance *rtp = factory->instantiatePlugin - (id, 0, 0, sampleRate, blockSize, channels); - - plugin = rtp; - - if (effect && source && rtp) { - source->setAuditioningPlugin(rtp); - } - } - - if (plugin) { - - context = PluginTransformer::ExecutionContext(context.channel, plugin); - - if (configurationXml != "") { - PluginXml(plugin).setParametersFromXml(configurationXml); - } - - int sourceChannels = 1; - if (dynamic_cast(inputModel)) { - sourceChannels = dynamic_cast(inputModel) - ->getChannelCount(); - } - - int minChannels = 1, maxChannels = sourceChannels; - getChannelRange(identifier, plugin, minChannels, maxChannels); - - int targetChannels = sourceChannels; - if (!effect) { - if (sourceChannels < minChannels) targetChannels = minChannels; - if (sourceChannels > maxChannels) targetChannels = maxChannels; - } - - int defaultChannel = context.channel; - - PluginParameterDialog *dialog = new PluginParameterDialog(plugin); - - if (candidateModelNames.size() > 1 && !generator) { - dialog->setCandidateInputModels(candidateModelNames); - } - - if (startFrame != 0 || duration != 0) { - dialog->setShowSelectionOnlyOption(true); - } - - if (targetChannels > 0) { - dialog->setChannelArrangement(sourceChannels, targetChannels, - defaultChannel); - } - - dialog->setOutputLabel(outputLabel, outputDescription); - - dialog->setShowProcessingOptions(true, frequency); - - if (dialog->exec() == QDialog::Accepted) { - ok = true; - } - - QString selectedInput = dialog->getInputModel(); - if (selectedInput != "") { - if (modelMap.find(selectedInput) != modelMap.end()) { - inputModel = modelMap[selectedInput]; - std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl; - } else { - std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl; - } - } else { - std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl; - } - - configurationXml = PluginXml(plugin).toXmlString(); - context.channel = dialog->getChannel(); - - if (startFrame != 0 || duration != 0) { - if (dialog->getSelectionOnly()) { - context.startFrame = startFrame; - context.duration = duration; - } - } - - dialog->getProcessingParameters(context.stepSize, - context.blockSize, - context.windowType); - - context.makeConsistentWithPlugin(plugin); - - delete dialog; - - if (effect && source) { - source->setAuditioningPlugin(0); // will delete our plugin - } else { - delete plugin; - } - } - - if (ok) m_lastConfigurations[identifier] = configurationXml; - - return ok ? inputModel : 0; -} - -PluginTransformer::ExecutionContext -TransformerFactory::getDefaultContextForTransformer(TransformId identifier, - Model *inputModel) -{ - PluginTransformer::ExecutionContext context(-1); - - QString id = identifier.section(':', 0, 2); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, inputModel ? inputModel->getSampleRate() : 48000); - - if (vp) { - context = PluginTransformer::ExecutionContext(-1, vp); - delete vp; - } - } - - return context; -} - -Transformer * -TransformerFactory::createTransformer(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml) -{ - Transformer *transform = 0; - - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - transform = new FeatureExtractionPluginTransformer(inputModel, - id, - context, - configurationXml, - output); - } else if (RealTimePluginFactory::instanceFor(id)) { - transform = new RealTimePluginTransformer(inputModel, - id, - context, - configurationXml, - getTransformerUnits(identifier), - output == "A" ? -1 : - output.toInt()); - } else { - std::cerr << "TransformerFactory::createTransformer: Unknown transform \"" - << identifier.toStdString() << "\"" << std::endl; - return transform; - } - - if (transform) transform->setObjectName(identifier); - return transform; -} - -Model * -TransformerFactory::transform(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml) -{ - Transformer *t = createTransformer(identifier, inputModel, context, - configurationXml); - - if (!t) return 0; - - connect(t, SIGNAL(finished()), this, SLOT(transformFinished())); - - m_runningTransformers.insert(t); - - t->start(); - Model *model = t->detachOutputModel(); - - if (model) { - QString imn = inputModel->objectName(); - QString trn = getTransformerFriendlyName(identifier); - if (imn != "") { - if (trn != "") { - model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); - } else { - model->setObjectName(imn); - } - } else if (trn != "") { - model->setObjectName(trn); - } - } else { - t->wait(); - } - - return model; -} - -void -TransformerFactory::transformFinished() -{ - QObject *s = sender(); - Transformer *transform = dynamic_cast(s); - - std::cerr << "TransformerFactory::transformFinished(" << transform << ")" << std::endl; - - if (!transform) { - std::cerr << "WARNING: TransformerFactory::transformFinished: sender is not a transform" << std::endl; - return; - } - - if (m_runningTransformers.find(transform) == m_runningTransformers.end()) { - std::cerr << "WARNING: TransformerFactory::transformFinished(" - << transform - << "): I have no record of this transform running!" - << std::endl; - } - - m_runningTransformers.erase(transform); - - transform->wait(); // unnecessary but reassuring - delete transform; -} - -void -TransformerFactory::modelAboutToBeDeleted(Model *m) -{ - TransformerSet affected; - - for (TransformerSet::iterator i = m_runningTransformers.begin(); - i != m_runningTransformers.end(); ++i) { - - Transformer *t = *i; - - if (t->getInputModel() == m || t->getOutputModel() == m) { - affected.insert(t); - } - } - - for (TransformerSet::iterator i = affected.begin(); - i != affected.end(); ++i) { - - Transformer *t = *i; - - t->abandon(); - - t->wait(); // this should eventually call back on - // transformFinished, which will remove from - // m_runningTransformers and delete. - } -} - diff -r 6e9dcf09b7fe -r f620ce48c950 plugin/transform/TransformerFactory.h --- a/plugin/transform/TransformerFactory.h Tue Nov 06 17:15:00 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -/* -*- 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 and QMUL. - - 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 _TRANSFORMER_FACTORY_H_ -#define _TRANSFORMER_FACTORY_H_ - -#include "TransformDescription.h" - -#include "Transformer.h" -#include "PluginTransformer.h" - -#include -#include - -namespace Vamp { class PluginBase; } - -class AudioCallbackPlaySource; - -//!!! split into TransformFactory (information about available -// transforms, create default Transform for each transform ID etc) and -// TransformerFactory (create Transformers to apply transforms) - -class TransformerFactory : public QObject -{ - Q_OBJECT - -public: - virtual ~TransformerFactory(); - - static TransformerFactory *getInstance(); - - TransformList getAllTransforms(); - - std::vector getAllTransformerTypes(); - - std::vector getTransformerCategories(QString transformType); - std::vector getTransformerMakers(QString transformType); - - /** - * Get a configuration XML string for the given transform (by - * asking the user, most likely). Returns the selected input - * model if the transform is acceptable, 0 if the operation should - * be cancelled. Audio callback play source may be used to - * audition effects plugins, if provided. - */ - Model *getConfigurationForTransformer(TransformId identifier, - const std::vector &candidateInputModels, - PluginTransformer::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source = 0, - size_t startFrame = 0, - size_t duration = 0); - - /** - * Get the default execution context for the given transform - * and input model (if known). - */ - PluginTransformer::ExecutionContext getDefaultContextForTransformer(TransformId identifier, - Model *inputModel = 0); - - /** - * Return the output model resulting from applying the named - * transform to the given input model. The transform may still be - * working in the background when the model is returned; check the - * output model's isReady completion status for more details. - * - * If the transform is unknown or the input model is not an - * appropriate type for the given transform, or if some other - * problem occurs, return 0. - * - * The returned model is owned by the caller and must be deleted - * when no longer needed. - */ - Model *transform(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml = ""); - - /** - * Return true if the given transform is known. - */ - bool haveTransformer(TransformId identifier); - - /** - * Full name of a transform, suitable for putting on a menu. - */ - QString getTransformerName(TransformId identifier); - - /** - * Brief but friendly name of a transform, suitable for use - * as the name of the output layer. - */ - QString getTransformerFriendlyName(TransformId identifier); - - QString getTransformerUnits(TransformId identifier); - - /** - * Return true if the transform has any configurable parameters, - * i.e. if getConfigurationForTransformer can ever return a non-trivial - * (not equivalent to empty) configuration string. - */ - bool isTransformerConfigurable(TransformId identifier); - - /** - * If the transform has a prescribed number or range of channel - * inputs, return true and set minChannels and maxChannels to the - * minimum and maximum number of channel inputs the transform can - * accept. Return false if it doesn't care. - */ - bool getTransformerChannelRange(TransformId identifier, - int &minChannels, int &maxChannels); - -protected slots: - void transformFinished(); - - void modelAboutToBeDeleted(Model *); - -protected: - Transformer *createTransformer(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml); - - struct TransformIdent - { - TransformId identifier; - QString configurationXml; - }; - - typedef std::map TransformerConfigurationMap; - TransformerConfigurationMap m_lastConfigurations; - - typedef std::map TransformDescriptionMap; - TransformDescriptionMap m_transforms; - - typedef std::set TransformerSet; - TransformerSet m_runningTransformers; - - void populateTransforms(); - void populateFeatureExtractionPlugins(TransformDescriptionMap &); - void populateRealTimePlugins(TransformDescriptionMap &); - - bool getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, int &min, int &max); - - static TransformerFactory *m_instance; -}; - - -#endif