# HG changeset patch # User Chris Cannam # Date 1194276666 0 # Node ID 21bd032ae791b378066f0976fb4229fc4d8a639c # Parent 1d656dcda8efc3a66aa9d6fa593f75dd9ae3cae3 * Introduce new Transform class which contains data necessary to describe the context for a plugin -- the plugin's name and output, the step/block size etc (formerly spread across ExecutionContext and TransformFactory). Other code hasn't been updated to use this yet. * Rename existing Transform stuff to Transformers (because they run Transforms) I'm still not 100% sure about this change, don't rely on it. diff -r 1d656dcda8ef -r 21bd032ae791 plugin/plugin.pro --- a/plugin/plugin.pro Fri Nov 02 16:50:31 2007 +0000 +++ b/plugin/plugin.pro Mon Nov 05 15:31:06 2007 +0000 @@ -35,11 +35,12 @@ api/alsa/seq_event.h \ api/alsa/seq_midi_event.h \ api/alsa/sound/asequencer.h \ - transform/FeatureExtractionPluginTransform.h \ - transform/PluginTransform.h \ - transform/RealTimePluginTransform.h \ + transform/FeatureExtractionPluginTransformer.h \ + transform/PluginTransformer.h \ + transform/RealTimePluginTransformer.h \ transform/Transform.h \ - transform/TransformFactory.h + transform/Transformer.h \ + transform/TransformerFactory.h SOURCES += DSSIPluginFactory.cpp \ DSSIPluginInstance.cpp \ FeatureExtractionPluginFactory.cpp \ @@ -51,8 +52,9 @@ RealTimePluginInstance.cpp \ api/dssi_alsa_compat.c \ plugins/SamplePlayer.cpp \ - transform/FeatureExtractionPluginTransform.cpp \ - transform/PluginTransform.cpp \ - transform/RealTimePluginTransform.cpp \ + transform/FeatureExtractionPluginTransformer.cpp \ + transform/PluginTransformer.cpp \ + transform/RealTimePluginTransformer.cpp \ transform/Transform.cpp \ - transform/TransformFactory.cpp + transform/Transformer.cpp \ + transform/TransformerFactory.cpp diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/FeatureExtractionPluginTransform.cpp --- a/plugin/transform/FeatureExtractionPluginTransform.cpp Fri Nov 02 16:50:31 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 "FeatureExtractionPluginTransform.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 - -FeatureExtractionPluginTransform::FeatureExtractionPluginTransform(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString outputName) : - PluginTransform(inputModel, context), - m_plugin(0), - m_descriptor(0), - m_outputFeatureNo(0) -{ -// std::cerr << "FeatureExtractionPluginTransform::FeatureExtractionPluginTransform: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "FeatureExtractionPluginTransform: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); - - if (!m_plugin) { - std::cerr << "FeatureExtractionPluginTransform: 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 << "FeatureExtractionPluginTransform:: " - << "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 << "FeatureExtractionPluginTransform: Plugin " - << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; - return; - } - - Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); - - if (outputs.empty()) { - std::cerr << "FeatureExtractionPluginTransform: 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 << "FeatureExtractionPluginTransform: Plugin \"" - << pluginId.toStdString() << "\" has no output named \"" - << outputName.toStdString() << "\"" << std::endl; - return; - } - -// std::cerr << "FeatureExtractionPluginTransform: 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 << "FeatureExtractionPluginTransform: 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; - } -} - -FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform() -{ - std::cerr << "FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()" << std::endl; - delete m_plugin; - delete m_descriptor; -} - -DenseTimeValueModel * -FeatureExtractionPluginTransform::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "FeatureExtractionPluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -FeatureExtractionPluginTransform::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - if (!m_output) return; - - while (!input->isReady()) { -/* - if (dynamic_cast(input)) { - std::cerr << "FeatureExtractionPluginTransform::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 << "FeatureExtractionPluginTransform::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 << "FeatureExtractionPluginTransform::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 -FeatureExtractionPluginTransform::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 -FeatureExtractionPluginTransform::addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature) -{ - size_t inputRate = m_input->getSampleRate(); - -// std::cerr << "FeatureExtractionPluginTransform::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: FeatureExtractionPluginTransform::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 -FeatureExtractionPluginTransform::setCompletion(int completion) -{ - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionPluginTransform::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 1d656dcda8ef -r 21bd032ae791 plugin/transform/FeatureExtractionPluginTransform.h --- a/plugin/transform/FeatureExtractionPluginTransform.h Fri Nov 02 16:50:31 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_TRANSFORM_H_ -#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_ - -#include "PluginTransform.h" - -class DenseTimeValueModel; - -class FeatureExtractionPluginTransform : public PluginTransform -{ - Q_OBJECT - -public: - FeatureExtractionPluginTransform(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString outputName = ""); - virtual ~FeatureExtractionPluginTransform(); - -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 << "FeatureExtractionPluginTransform::getOutput: Output model not conformable" << std::endl; - } - return mc; - } -}; - -#endif - diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/FeatureExtractionPluginTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/FeatureExtractionPluginTransformer.cpp Mon Nov 05 15:31:06 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 "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 1d656dcda8ef -r 21bd032ae791 plugin/transform/FeatureExtractionPluginTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/FeatureExtractionPluginTransformer.h Mon Nov 05 15:31:06 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 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 1d656dcda8ef -r 21bd032ae791 plugin/transform/PluginTransform.cpp --- a/plugin/transform/PluginTransform.cpp Fri Nov 02 16:50:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +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 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 "PluginTransform.h" - -#include "vamp-sdk/PluginHostAdapter.h" -#include "vamp-sdk/hostext/PluginWrapper.h" - -PluginTransform::PluginTransform(Model *inputModel, - const ExecutionContext &context) : - Transform(inputModel), - m_context(context) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _bs) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(_bs ? _bs : 1024), - blockSize(_bs ? _bs : 1024), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _ss, - size_t _bs, WindowType _wt) : - channel(_c), - domain(Vamp::Plugin::FrequencyDomain), - stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)), - blockSize(_bs ? _bs : 1024), - windowType(_wt), - startFrame(0), - duration(0), - sampleRate(0) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, - const Vamp::PluginBase *_plugin) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(0), - blockSize(0), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0) -{ - makeConsistentWithPlugin(_plugin); -} - -bool -PluginTransform::ExecutionContext::operator==(const ExecutionContext &c) -{ - return (c.channel == channel && - c.domain == domain && - c.stepSize == stepSize && - c.blockSize == blockSize && - c.windowType == windowType && - c.startFrame == startFrame && - c.duration == duration && - c.sampleRate == sampleRate); -} - -void -PluginTransform::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin) -{ - const Vamp::Plugin *vp = dynamic_cast(_plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast(_plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast(_plugin); //!!! no, I mean really why? - } - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; - } - - if (!vp) { - domain = Vamp::Plugin::TimeDomain; - if (!stepSize) { - if (!blockSize) blockSize = 1024; - stepSize = blockSize; - } else { - if (!blockSize) blockSize = stepSize; - } - } else { - domain = vp->getInputDomain(); - if (!stepSize) stepSize = vp->getPreferredStepSize(); - if (!blockSize) blockSize = vp->getPreferredBlockSize(); - if (!blockSize) blockSize = 1024; - if (!stepSize) { - if (domain == Vamp::Plugin::FrequencyDomain) { -// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize/2; - } else { -// std::cerr << "time domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize; - } - } - } -} - - diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/PluginTransform.h --- a/plugin/transform/PluginTransform.h Fri Nov 02 16:50:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +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 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 _PLUGIN_TRANSFORM_H_ -#define _PLUGIN_TRANSFORM_H_ - -#include "Transform.h" - -#include "base/Window.h" - -#include "vamp-sdk/Plugin.h" - -//!!! should this just move back up to Transform? It is after all used -//directly in all sorts of generic places, like Document - -class PluginTransform : public Transform -{ -public: - class ExecutionContext { - public: - // Time domain: - ExecutionContext(int _c = -1, size_t _bs = 0); - - // Frequency domain: - ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt); - - // From plugin defaults: - ExecutionContext(int _c, const Vamp::PluginBase *_plugin); - - bool operator==(const ExecutionContext &); - - void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin); - - int channel; - Vamp::Plugin::InputDomain domain; - size_t stepSize; - size_t blockSize; - WindowType windowType; - size_t startFrame; - size_t duration; // 0 -> whole thing - float sampleRate; // 0 -> model's rate - }; - -protected: - PluginTransform(Model *inputModel, - const ExecutionContext &context); - - ExecutionContext m_context; -}; - -#endif diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/PluginTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/PluginTransformer.cpp Mon Nov 05 15:31:06 2007 +0000 @@ -0,0 +1,121 @@ +/* -*- 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 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 "PluginTransformer.h" + +#include "vamp-sdk/PluginHostAdapter.h" +#include "vamp-sdk/hostext/PluginWrapper.h" + +PluginTransformer::PluginTransformer(Model *inputModel, + const ExecutionContext &context) : + Transformer(inputModel), + m_context(context) +{ +} + +PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _bs) : + channel(_c), + domain(Vamp::Plugin::TimeDomain), + stepSize(_bs ? _bs : 1024), + blockSize(_bs ? _bs : 1024), + windowType(HanningWindow), + startFrame(0), + duration(0), + sampleRate(0) +{ +} + +PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _ss, + size_t _bs, WindowType _wt) : + channel(_c), + domain(Vamp::Plugin::FrequencyDomain), + stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)), + blockSize(_bs ? _bs : 1024), + windowType(_wt), + startFrame(0), + duration(0), + sampleRate(0) +{ +} + +PluginTransformer::ExecutionContext::ExecutionContext(int _c, + const Vamp::PluginBase *_plugin) : + channel(_c), + domain(Vamp::Plugin::TimeDomain), + stepSize(0), + blockSize(0), + windowType(HanningWindow), + startFrame(0), + duration(0), + sampleRate(0) +{ + makeConsistentWithPlugin(_plugin); +} + +bool +PluginTransformer::ExecutionContext::operator==(const ExecutionContext &c) +{ + return (c.channel == channel && + c.domain == domain && + c.stepSize == stepSize && + c.blockSize == blockSize && + c.windowType == windowType && + c.startFrame == startFrame && + c.duration == duration && + c.sampleRate == sampleRate); +} + +void +PluginTransformer::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin) +{ + const Vamp::Plugin *vp = dynamic_cast(_plugin); + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; + vp = dynamic_cast(_plugin); //!!! why? +} + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; + vp = dynamic_cast(_plugin); //!!! no, I mean really why? + } + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; + } + + if (!vp) { + domain = Vamp::Plugin::TimeDomain; + if (!stepSize) { + if (!blockSize) blockSize = 1024; + stepSize = blockSize; + } else { + if (!blockSize) blockSize = stepSize; + } + } else { + domain = vp->getInputDomain(); + if (!stepSize) stepSize = vp->getPreferredStepSize(); + if (!blockSize) blockSize = vp->getPreferredBlockSize(); + if (!blockSize) blockSize = 1024; + if (!stepSize) { + if (domain == Vamp::Plugin::FrequencyDomain) { +// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; + stepSize = blockSize/2; + } else { +// std::cerr << "time domain, step = " << blockSize/2 << std::endl; + stepSize = blockSize; + } + } + } +} + + diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/PluginTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/PluginTransformer.h Mon Nov 05 15:31:06 2007 +0000 @@ -0,0 +1,63 @@ +/* -*- 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 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 _PLUGIN_TRANSFORMER_H_ +#define _PLUGIN_TRANSFORMER_H_ + +#include "Transformer.h" + +#include "base/Window.h" + +#include "vamp-sdk/Plugin.h" + +//!!! 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 +{ +public: + class ExecutionContext { + public: + // Time domain: + ExecutionContext(int _c = -1, size_t _bs = 0); + + // Frequency domain: + ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt); + + // From plugin defaults: + ExecutionContext(int _c, const Vamp::PluginBase *_plugin); + + bool operator==(const ExecutionContext &); + + void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin); + + int channel; + Vamp::Plugin::InputDomain domain; + size_t stepSize; + size_t blockSize; + WindowType windowType; + size_t startFrame; + size_t duration; // 0 -> whole thing + float sampleRate; // 0 -> model's rate + }; + +protected: + PluginTransformer(Model *inputModel, + const ExecutionContext &context); + + ExecutionContext m_context; +}; + +#endif diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/RealTimePluginTransform.cpp --- a/plugin/transform/RealTimePluginTransform.cpp Fri Nov 02 16:50:31 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 "RealTimePluginTransform.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 - -RealTimePluginTransform::RealTimePluginTransform(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString units, - int output) : - PluginTransform(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 << "RealTimePluginTransform::RealTimePluginTransform: plugin " << pluginId.toStdString() << ", output " << output << std::endl; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "RealTimePluginTransform: 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 << "RealTimePluginTransform: 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 << "RealTimePluginTransform: 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; - } -} - -RealTimePluginTransform::~RealTimePluginTransform() -{ - delete m_plugin; -} - -DenseTimeValueModel * -RealTimePluginTransform::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "RealTimePluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -RealTimePluginTransform::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - while (!input->isReady()) { - if (dynamic_cast(input)) break; // no need to wait - std::cerr << "RealTimePluginTransform::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 1d656dcda8ef -r 21bd032ae791 plugin/transform/RealTimePluginTransform.h --- a/plugin/transform/RealTimePluginTransform.h Fri Nov 02 16:50:31 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_TRANSFORM_H_ -#define _REAL_TIME_PLUGIN_TRANSFORM_H_ - -#include "PluginTransform.h" -#include "plugin/RealTimePluginInstance.h" - -class DenseTimeValueModel; - -class RealTimePluginTransform : public PluginTransform -{ -public: - RealTimePluginTransform(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString units = "", - int output = -1); // -1 -> audio, 0+ -> data - virtual ~RealTimePluginTransform(); - -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 1d656dcda8ef -r 21bd032ae791 plugin/transform/RealTimePluginTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/RealTimePluginTransformer.cpp Mon Nov 05 15:31:06 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 "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 1d656dcda8ef -r 21bd032ae791 plugin/transform/RealTimePluginTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/RealTimePluginTransformer.h Mon Nov 05 15:31:06 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 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 1d656dcda8ef -r 21bd032ae791 plugin/transform/Transform.cpp --- a/plugin/transform/Transform.cpp Fri Nov 02 16:50:31 2007 +0000 +++ b/plugin/transform/Transform.cpp Mon Nov 05 15:31:06 2007 +0000 @@ -4,7 +4,7 @@ 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 file copyright 2006-2007 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 @@ -15,18 +15,61 @@ #include "Transform.h" -Transform::Transform(Model *m) : - m_input(m), - m_output(0), - m_detached(false), - m_abandoned(false) +#include "plugin/PluginIdentifier.h" + +Transform::Transform() : + m_stepSize(0), + m_blockSize(0), + m_windowType(HanningWindow), + m_sampleRate(0) { } Transform::~Transform() { - m_abandoned = true; - wait(); - if (!m_detached) delete m_output; } +QString +Transform::createIdentifier(QString type, QString soName, QString label, + QString output) +{ + QString pluginId = PluginIdentifier::createIdentifier(type, soName, label); + return pluginId + ":" + output; +} + +void +Transform::parseIdentifier(QString identifier, + QString &type, QString &soName, + QString &label, QString &output) +{ + output = identifier.section(':', 3); + PluginIdentifier::parseIdentifier(identifier.section(':', 0, 2), + type, soName, label); +} + +Transform::Type +Transform::getType() const +{ + QString type, soName, label, output; + parseIdentifier(m_id, type, soName, label, output); + if (type == "vamp") return FeatureExtraction; //!!! lousy + else return RealTimeEffect; +} + +QString +Transform::getPluginIdentifier() const +{ + return m_id.section(':', 0, 2); +} + +QString +Transform::getOutput() const +{ + return m_id.section(':', 3); +} + +void +Transform::toXml(QTextStream &stream, QString indent, QString extraAttributes) const +{ + +} diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/Transform.h --- a/plugin/transform/Transform.h Fri Nov 02 16:50:31 2007 +0000 +++ b/plugin/transform/Transform.h Mon Nov 05 15:31:06 2007 +0000 @@ -4,7 +4,7 @@ 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 file copyright 2006-2007 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 @@ -16,46 +16,93 @@ #ifndef _TRANSFORM_H_ #define _TRANSFORM_H_ -#include "base/Thread.h" +#include "base/XmlExportable.h" +#include "base/Window.h" -#include "data/model/Model.h" +#include + +#include typedef QString TransformId; -/** - * A Transform turns one data model into another. - * - * Typically in this application, a Transform might have a - * DenseTimeValueModel as its input (e.g. an audio waveform) and a - * SparseOneDimensionalModel (e.g. detected beats) as its output. - * - * The Transform typically runs in the background, as a separate - * thread populating the output model. The model is available to the - * user of the Transform immediately, but may be initially empty until - * the background thread has populated it. - */ +namespace Vamp { + class PluginBase; +} -class Transform : public Thread +class Transform : public XmlExportable { public: + Transform(); virtual ~Transform(); - // 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; } + void setIdentifier(TransformId id) { m_id = id; } + TransformId getIdentifier() const { return m_id; } + + void setPlugin(QString pluginIdentifier); + void setOutput(QString output); - Model *getInputModel() { return m_input; } - Model *getOutputModel() { return m_output; } - Model *detachOutputModel() { m_detached = true; return m_output; } + enum Type { FeatureExtraction, RealTimeEffect }; + + Type getType() const; + QString getPluginIdentifier() const; + QString getOutput() const; + + typedef std::map ParameterMap; + + ParameterMap getParameters() const { return m_parameters; } + void setParameters(const ParameterMap &pm) { m_parameters = pm; } + + typedef std::map ConfigurationMap; + + ConfigurationMap getConfiguration() const { return m_configuration; } + void setConfiguration(const ConfigurationMap &cm) { m_configuration = cm; } + + QString getProgram() const { return m_program; } + void setProgram(QString program) { m_program = program; } + + size_t getStepSize() const { return m_stepSize; } + void setStepSize(size_t s) { m_stepSize = s; } + + size_t getBlockSize() const { return m_blockSize; } + void setBlockSize(size_t s) { m_blockSize = s; } + + WindowType getWindowType() const { return m_windowType; } + void setWindowType(WindowType type) { m_windowType = type; } + + Vamp::RealTime getStartTime() const { return m_startTime; } + void setStartTime(Vamp::RealTime t) { m_startTime = t; } + + Vamp::RealTime getDuration() const { return m_duration; } // 0 -> all + void setDuration(Vamp::RealTime d) { m_duration = d; } + + float getSampleRate() const { return m_sampleRate; } // 0 -> as input + void setSampleRate(float rate) { m_sampleRate = rate; } + + void toXml(QTextStream &stream, QString indent = "", + QString extraAttributes = "") const; + + static Transform fromXmlString(QString xml); protected: - Transform(Model *m); + TransformId m_id; // pluginid:output, that is type:soname:label:output + + static QString createIdentifier + (QString type, QString soName, QString label, QString output); - Model *m_input; // I don't own this - Model *m_output; // I own this, unless... - bool m_detached; // ... this is true. - bool m_abandoned; + static void parseIdentifier + (QString identifier, + QString &type, QString &soName, QString &label, QString &output); + + ParameterMap m_parameters; + ConfigurationMap m_configuration; + QString m_program; + size_t m_stepSize; + size_t m_blockSize; + WindowType m_windowType; + Vamp::RealTime m_startTime; + Vamp::RealTime m_duration; + float m_sampleRate; }; #endif + diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/TransformFactory.cpp --- a/plugin/transform/TransformFactory.cpp Fri Nov 02 16:50:31 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 "TransformFactory.h" - -#include "FeatureExtractionPluginTransform.h" -#include "RealTimePluginTransform.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 - -TransformFactory * -TransformFactory::m_instance = new TransformFactory; - -TransformFactory * -TransformFactory::getInstance() -{ - return m_instance; -} - -TransformFactory::~TransformFactory() -{ -} - -TransformFactory::TransformList -TransformFactory::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 -TransformFactory::getAllTransformTypes() -{ - 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 -TransformFactory::getTransformCategories(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 -TransformFactory::getTransformMakers(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 -TransformFactory::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) { - - TransformDesc 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) { - - TransformDesc 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 -TransformFactory::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: TransformFactory::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: TransformFactory::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] = - TransformDesc(tr("Analysis"), - category, - transformId, - userName, - friendlyName, - description, - maker, - units, - configurable); - } - - delete plugin; - } -} - -void -TransformFactory::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: TransformFactory::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: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - -//!!! if (descriptor->controlOutputPortCount == 0 || -// descriptor->audioInputPortCount == 0) continue; - -// std::cout << "TransformFactory::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] = - TransformDesc(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("Transform 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] = - TransformDesc(type, - category, - transformId, - pluginName, - pluginName, - description, - maker, - "", - configurable); - } - } - } -} - -bool -TransformFactory::haveTransform(TransformId identifier) -{ - return (m_transforms.find(identifier) != m_transforms.end()); -} - -QString -TransformFactory::getTransformName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].name; - } else return ""; -} - -QString -TransformFactory::getTransformFriendlyName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].friendlyName; - } else return ""; -} - -QString -TransformFactory::getTransformUnits(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].units; - } else return ""; -} - -bool -TransformFactory::isTransformConfigurable(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].configurable; - } else return false; -} - -bool -TransformFactory::getTransformChannelRange(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 -TransformFactory::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 getTransformChannelRange(identifier, minChannels, maxChannels); - } -} - -Model * -TransformFactory::getConfigurationForTransform(TransformId identifier, - const std::vector &candidateInputModels, - PluginTransform::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 << "getConfigurationForTransform: 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 = PluginTransform::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; -} - -PluginTransform::ExecutionContext -TransformFactory::getDefaultContextForTransform(TransformId identifier, - Model *inputModel) -{ - PluginTransform::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 = PluginTransform::ExecutionContext(-1, vp); - delete vp; - } - } - - return context; -} - -Transform * -TransformFactory::createTransform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Transform *transform = 0; - - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - transform = new FeatureExtractionPluginTransform(inputModel, - id, - context, - configurationXml, - output); - } else if (RealTimePluginFactory::instanceFor(id)) { - transform = new RealTimePluginTransform(inputModel, - id, - context, - configurationXml, - getTransformUnits(identifier), - output == "A" ? -1 : - output.toInt()); - } else { - std::cerr << "TransformFactory::createTransform: Unknown transform \"" - << identifier.toStdString() << "\"" << std::endl; - return transform; - } - - if (transform) transform->setObjectName(identifier); - return transform; -} - -Model * -TransformFactory::transform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Transform *t = createTransform(identifier, inputModel, context, - configurationXml); - - if (!t) return 0; - - connect(t, SIGNAL(finished()), this, SLOT(transformFinished())); - - m_runningTransforms.insert(t); - - t->start(); - Model *model = t->detachOutputModel(); - - if (model) { - QString imn = inputModel->objectName(); - QString trn = getTransformFriendlyName(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 -TransformFactory::transformFinished() -{ - QObject *s = sender(); - Transform *transform = dynamic_cast(s); - - std::cerr << "TransformFactory::transformFinished(" << transform << ")" << std::endl; - - if (!transform) { - std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl; - return; - } - - if (m_runningTransforms.find(transform) == m_runningTransforms.end()) { - std::cerr << "WARNING: TransformFactory::transformFinished(" - << transform - << "): I have no record of this transform running!" - << std::endl; - } - - m_runningTransforms.erase(transform); - - transform->wait(); // unnecessary but reassuring - delete transform; -} - -void -TransformFactory::modelAboutToBeDeleted(Model *m) -{ - TransformSet affected; - - for (TransformSet::iterator i = m_runningTransforms.begin(); - i != m_runningTransforms.end(); ++i) { - - Transform *t = *i; - - if (t->getInputModel() == m || t->getOutputModel() == m) { - affected.insert(t); - } - } - - for (TransformSet::iterator i = affected.begin(); - i != affected.end(); ++i) { - - Transform *t = *i; - - t->abandon(); - - t->wait(); // this should eventually call back on - // transformFinished, which will remove from - // m_runningTransforms and delete. - } -} - diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/TransformFactory.h --- a/plugin/transform/TransformFactory.h Fri Nov 02 16:50:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +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 _TRANSFORM_FACTORY_H_ -#define _TRANSFORM_FACTORY_H_ - -#include "Transform.h" -#include "PluginTransform.h" - -#include -#include - -namespace Vamp { class PluginBase; } - -class AudioCallbackPlaySource; - -class TransformFactory : public QObject -{ - Q_OBJECT - -public: - virtual ~TransformFactory(); - - static TransformFactory *getInstance(); - - // The identifier is intended to be computer-referenceable, and - // unique within the application. The name is intended to be - // human readable. In principle it doesn't have to be unique, but - // the factory will add suffixes to ensure that it is, all the - // same (just to avoid user confusion). The friendly name is a - // shorter version of the name. The type is also intended to be - // user-readable, for use in menus. - - struct TransformDesc { - - TransformDesc() { } - TransformDesc(QString _type, QString _category, - TransformId _identifier, QString _name, - QString _friendlyName, QString _description, - QString _maker, QString _units, bool _configurable) : - type(_type), category(_category), - identifier(_identifier), name(_name), - friendlyName(_friendlyName), description(_description), - maker(_maker), units(_units), configurable(_configurable) { } - - QString type; // e.g. feature extraction plugin - QString category; // e.g. time > onsets - TransformId identifier; // e.g. vamp:vamp-aubio:aubioonset - QString name; // plugin's name if 1 output, else "name: output" - QString friendlyName; // short text for layer name - QString description; // sentence describing transform - QString maker; - QString units; - bool configurable; - - bool operator<(const TransformDesc &od) const { - return (name < od.name); - }; - }; - typedef std::vector TransformList; - - TransformList getAllTransforms(); - - std::vector getAllTransformTypes(); - - std::vector getTransformCategories(QString transformType); - std::vector getTransformMakers(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 *getConfigurationForTransform(TransformId identifier, - const std::vector &candidateInputModels, - PluginTransform::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). - */ - PluginTransform::ExecutionContext getDefaultContextForTransform(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 PluginTransform::ExecutionContext &context, - QString configurationXml = ""); - - /** - * Return true if the given transform is known. - */ - bool haveTransform(TransformId identifier); - - /** - * Full name of a transform, suitable for putting on a menu. - */ - QString getTransformName(TransformId identifier); - - /** - * Brief but friendly name of a transform, suitable for use - * as the name of the output layer. - */ - QString getTransformFriendlyName(TransformId identifier); - - QString getTransformUnits(TransformId identifier); - - /** - * Return true if the transform has any configurable parameters, - * i.e. if getConfigurationForTransform can ever return a non-trivial - * (not equivalent to empty) configuration string. - */ - bool isTransformConfigurable(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 getTransformChannelRange(TransformId identifier, - int &minChannels, int &maxChannels); - -protected slots: - void transformFinished(); - - void modelAboutToBeDeleted(Model *); - -protected: - Transform *createTransform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml); - - struct TransformIdent - { - TransformId identifier; - QString configurationXml; - }; - - typedef std::map TransformConfigurationMap; - TransformConfigurationMap m_lastConfigurations; - - typedef std::map TransformDescriptionMap; - TransformDescriptionMap m_transforms; - - typedef std::set TransformSet; - TransformSet m_runningTransforms; - - void populateTransforms(); - void populateFeatureExtractionPlugins(TransformDescriptionMap &); - void populateRealTimePlugins(TransformDescriptionMap &); - - bool getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, int &min, int &max); - - static TransformFactory *m_instance; -}; - - -#endif diff -r 1d656dcda8ef -r 21bd032ae791 plugin/transform/Transformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/Transformer.cpp Mon Nov 05 15:31:06 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 "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 1d656dcda8ef -r 21bd032ae791 plugin/transform/Transformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/Transformer.h Mon Nov 05 15:31:06 2007 +0000 @@ -0,0 +1,61 @@ +/* -*- 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 _TRANSFORM_H_ +#define _TRANSFORM_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 1d656dcda8ef -r 21bd032ae791 plugin/transform/TransformerFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/TransformerFactory.cpp Mon Nov 05 15:31:06 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 "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() +{ +} + +TransformerFactory::TransformerList +TransformerFactory::getAllTransformers() +{ + if (m_transforms.empty()) populateTransformers(); + + std::set dset; + for (TransformerDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + dset.insert(i->second); + } + + TransformerList 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()) populateTransformers(); + + std::set types; + for (TransformerDescriptionMap::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()) populateTransformers(); + + std::set categories; + for (TransformerDescriptionMap::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()) populateTransformers(); + + std::set makers; + for (TransformerDescriptionMap::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::populateTransformers() +{ + TransformerDescriptionMap transforms; + + populateFeatureExtractionPlugins(transforms); + populateRealTimePlugins(transforms); + + // disambiguate plugins with similar names + + std::map names; + std::map pluginSources; + std::map pluginMakers; + + for (TransformerDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + TransformerDesc 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 (TransformerDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + TransformerDesc 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(TransformerDescriptionMap &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::populateTransformers: 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::populateTransformers: 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] = + TransformerDesc(tr("Analysis"), + category, + transformId, + userName, + friendlyName, + description, + maker, + units, + configurable); + } + + delete plugin; + } +} + +void +TransformerFactory::populateRealTimePlugins(TransformerDescriptionMap &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::populateTransformers: 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::populateTransformers: 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] = + TransformerDesc(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] = + TransformerDesc(type, + category, + transformId, + pluginName, + pluginName, + description, + maker, + "", + configurable); + } + } + } +} + +bool +TransformerFactory::haveTransformer(TransformerId identifier) +{ + return (m_transforms.find(identifier) != m_transforms.end()); +} + +QString +TransformerFactory::getTransformerName(TransformerId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].name; + } else return ""; +} + +QString +TransformerFactory::getTransformerFriendlyName(TransformerId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].friendlyName; + } else return ""; +} + +QString +TransformerFactory::getTransformerUnits(TransformerId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].units; + } else return ""; +} + +bool +TransformerFactory::isTransformerConfigurable(TransformerId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].configurable; + } else return false; +} + +bool +TransformerFactory::getTransformerChannelRange(TransformerId 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(TransformerId 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(TransformerId 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(TransformerId 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(TransformerId 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(TransformerId 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 1d656dcda8ef -r 21bd032ae791 plugin/transform/TransformerFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/TransformerFactory.h Mon Nov 05 15:31:06 2007 +0000 @@ -0,0 +1,190 @@ +/* -*- 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 "Transformer.h" +#include "PluginTransformer.h" + +#include +#include + +namespace Vamp { class PluginBase; } + +class AudioCallbackPlaySource; + +class TransformerFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~TransformerFactory(); + + static TransformerFactory *getInstance(); + + // The identifier is intended to be computer-referenceable, and + // unique within the application. The name is intended to be + // human readable. In principle it doesn't have to be unique, but + // the factory will add suffixes to ensure that it is, all the + // same (just to avoid user confusion). The friendly name is a + // shorter version of the name. The type is also intended to be + // user-readable, for use in menus. + + struct TransformerDesc { + + TransformerDesc() { } + TransformerDesc(QString _type, QString _category, + TransformerId _identifier, QString _name, + QString _friendlyName, QString _description, + QString _maker, QString _units, bool _configurable) : + type(_type), category(_category), + identifier(_identifier), name(_name), + friendlyName(_friendlyName), description(_description), + maker(_maker), units(_units), configurable(_configurable) { } + + QString type; // e.g. feature extraction plugin + QString category; // e.g. time > onsets + TransformerId identifier; // e.g. vamp:vamp-aubio:aubioonset + QString name; // plugin's name if 1 output, else "name: output" + QString friendlyName; // short text for layer name + QString description; // sentence describing transform + QString maker; + QString units; + bool configurable; + + bool operator<(const TransformerDesc &od) const { + return (name < od.name); + }; + }; + typedef std::vector TransformerList; + + TransformerList getAllTransformers(); + + 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(TransformerId 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(TransformerId 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(TransformerId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml = ""); + + /** + * Return true if the given transform is known. + */ + bool haveTransformer(TransformerId identifier); + + /** + * Full name of a transform, suitable for putting on a menu. + */ + QString getTransformerName(TransformerId identifier); + + /** + * Brief but friendly name of a transform, suitable for use + * as the name of the output layer. + */ + QString getTransformerFriendlyName(TransformerId identifier); + + QString getTransformerUnits(TransformerId 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(TransformerId 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(TransformerId identifier, + int &minChannels, int &maxChannels); + +protected slots: + void transformFinished(); + + void modelAboutToBeDeleted(Model *); + +protected: + Transformer *createTransformer(TransformerId identifier, Model *inputModel, + const PluginTransformer::ExecutionContext &context, + QString configurationXml); + + struct TransformerIdent + { + TransformerId identifier; + QString configurationXml; + }; + + typedef std::map TransformerConfigurationMap; + TransformerConfigurationMap m_lastConfigurations; + + typedef std::map TransformerDescriptionMap; + TransformerDescriptionMap m_transforms; + + typedef std::set TransformerSet; + TransformerSet m_runningTransformers; + + void populateTransformers(); + void populateFeatureExtractionPlugins(TransformerDescriptionMap &); + void populateRealTimePlugins(TransformerDescriptionMap &); + + bool getChannelRange(TransformerId identifier, + Vamp::PluginBase *plugin, int &min, int &max); + + static TransformerFactory *m_instance; +}; + + +#endif