# HG changeset patch # User Chris Cannam # Date 1205344937 0 # Node ID 370aa9714ef5dc0b037bb9c9b397d1d274072e7c # Parent 7aa1de571880ef557b864a6ad90f83f052e557a3 * Move plugin/transform to plain transform. This way transform can depend on model and GUI classes, but plugin doesn't have to. diff -r 7aa1de571880 -r 370aa9714ef5 plugin/plugin.pro --- a/plugin/plugin.pro Wed Mar 12 17:42:56 2008 +0000 +++ b/plugin/plugin.pro Wed Mar 12 18:02:17 2008 +0000 @@ -5,14 +5,15 @@ CONFIG += sv staticlib qt thread warn_on stl rtti exceptions QT += xml +QT -= gui TARGET = svplugin # Doesn't work with this library, which contains C99 as well as C++ PRECOMPILED_HEADER = -DEPENDPATH += . .. api plugins api/alsa api/alsa/sound transform -INCLUDEPATH += . .. api api/alsa plugins api/alsa/sound transform +DEPENDPATH += . .. api plugins api/alsa api/alsa/sound +INCLUDEPATH += . .. api api/alsa plugins api/alsa/sound OBJECTS_DIR = tmp_obj MOC_DIR = tmp_moc @@ -34,14 +35,7 @@ api/alsa/seq.h \ api/alsa/seq_event.h \ api/alsa/seq_midi_event.h \ - api/alsa/sound/asequencer.h \ - transform/FeatureExtractionModelTransformer.h \ - transform/RealTimeEffectModelTransformer.h \ - transform/Transform.h \ - transform/TransformDescription.h \ - transform/TransformFactory.h \ - transform/ModelTransformer.h \ - transform/ModelTransformerFactory.h + api/alsa/sound/asequencer.h SOURCES += DSSIPluginFactory.cpp \ DSSIPluginInstance.cpp \ FeatureExtractionPluginFactory.cpp \ @@ -52,10 +46,5 @@ RealTimePluginFactory.cpp \ RealTimePluginInstance.cpp \ api/dssi_alsa_compat.c \ - plugins/SamplePlayer.cpp \ - transform/FeatureExtractionModelTransformer.cpp \ - transform/RealTimeEffectModelTransformer.cpp \ - transform/Transform.cpp \ - transform/TransformFactory.cpp \ - transform/ModelTransformer.cpp \ - transform/ModelTransformerFactory.cpp + plugins/SamplePlayer.cpp + diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/FeatureExtractionModelTransformer.cpp --- a/plugin/transform/FeatureExtractionModelTransformer.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,643 +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 "FeatureExtractionModelTransformer.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/PluginXml.h" -#include "vamp-sdk/Plugin.h" - -#include "data/model/Model.h" -#include "base/Window.h" -#include "base/Exceptions.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 "TransformFactory.h" - -#include - -FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, - const Transform &transform) : - ModelTransformer(in, transform), - m_plugin(0), - m_descriptor(0), - m_outputFeatureNo(0) -{ -// std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl; - - QString pluginId = transform.getPluginIdentifier(); - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); - return; - } - - DenseTimeValueModel *input = getConformingInput(); - if (!input) { - m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); - return; - } - - m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate()); - if (!m_plugin) { - m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId); - return; - } - - TransformFactory::getInstance()->makeContextConsistentWithPlugin - (m_transform, m_plugin); - - TransformFactory::getInstance()->setPluginParameters - (m_transform, m_plugin); - - size_t channelCount = input->getChannelCount(); - if (m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; - } - if (m_plugin->getMinChannelCount() > channelCount) { - m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)") - .arg(pluginId) - .arg(m_plugin->getMinChannelCount()) - .arg(m_plugin->getMaxChannelCount()) - .arg(input->getChannelCount()); - return; - } - - std::cerr << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << m_transform.getStepSize() - << ", block = " << m_transform.getBlockSize() << std::endl; - - if (!m_plugin->initialise(channelCount, - m_transform.getStepSize(), - m_transform.getBlockSize())) { - - size_t pstep = m_transform.getStepSize(); - size_t pblock = m_transform.getBlockSize(); - - m_transform.setStepSize(0); - m_transform.setBlockSize(0); - TransformFactory::getInstance()->makeContextConsistentWithPlugin - (m_transform, m_plugin); - - if (m_transform.getStepSize() != pstep || - m_transform.getBlockSize() != pblock) { - - if (!m_plugin->initialise(channelCount, - m_transform.getStepSize(), - m_transform.getBlockSize())) { - - m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); - return; - - } else { - - m_message = tr("Feature extraction plugin \"%1\" rejected the given step and block sizes (%2 and %3); using plugin defaults (%4 and %5) instead") - .arg(pluginId) - .arg(pstep) - .arg(pblock) - .arg(m_transform.getStepSize()) - .arg(m_transform.getBlockSize()); - } - - } else { - - m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); - return; - } - } - - if (m_transform.getPluginVersion() != "") { - QString pv = QString("%1").arg(m_plugin->getPluginVersion()); - if (pv != m_transform.getPluginVersion()) { - QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3") - .arg(m_transform.getPluginVersion()) - .arg(pluginId) - .arg(pv); - if (m_message != "") { - m_message = QString("%1; %2").arg(vm).arg(m_message); - } else { - m_message = vm; - } - } - } - - Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); - - if (outputs.empty()) { - m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId); - return; - } - - for (size_t i = 0; i < outputs.size(); ++i) { - if (m_transform.getOutput() == "" || - outputs[i].identifier == m_transform.getOutput().toStdString()) { - m_outputFeatureNo = i; - m_descriptor = new Vamp::Plugin::OutputDescriptor - (outputs[i]); - break; - } - } - - if (!m_descriptor) { - m_message = tr("Plugin \"%1\" has no output named \"%2\"") - .arg(pluginId) - .arg(m_transform.getOutput()); - return; - } - -// std::cerr << "FeatureExtractionModelTransformer: output sample type " -// << m_descriptor->sampleType << std::endl; - - int binCount = 1; - float minValue = 0.0, maxValue = 0.0; - bool haveExtents = false; - - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionModelTransformer: output bin count " -// << binCount << std::endl; - - if (binCount > 0 && m_descriptor->hasKnownExtents) { - minValue = m_descriptor->minValue; - maxValue = m_descriptor->maxValue; - haveExtents = true; - } - - size_t modelRate = 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_transform.getStepSize(); - 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; - } - - if (m_output) m_output->setSourceModel(input); -} - -FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() -{ - std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl; - delete m_plugin; - delete m_descriptor; -} - -DenseTimeValueModel * -FeatureExtractionModelTransformer::getConformingInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -FeatureExtractionModelTransformer::run() -{ - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; - - if (!m_output) return; - - while (!input->isReady()) { -/* - if (dynamic_cast(input)) { - std::cerr << "FeatureExtractionModelTransformer::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl; - sleep(2); - break; // no need to wait - } -*/ - std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - size_t sampleRate = 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_transform.getBlockSize() + 2]; - } - - size_t stepSize = m_transform.getStepSize(); - size_t blockSize = m_transform.getBlockSize(); - - 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 - (getConformingInput(), - channelCount == 1 ? m_input.getChannel() : ch, - m_transform.getWindowType(), - blockSize, - stepSize, - blockSize, - false, - StorageAdviser::PrecisionCritical); - if (!model->isOK()) { - delete model; - setCompletion(100); - //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either - throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer"); - } - model->resume(); - fftModels.push_back(model); - } - } - - long startFrame = m_input.getModel()->getStartFrame(); - long endFrame = m_input.getModel()->getEndFrame(); - - RealTime contextStartRT = m_transform.getStartTime(); - RealTime contextDurationRT = m_transform.getDuration(); - - long contextStart = - RealTime::realTime2Frame(contextStartRT, sampleRate); - - long contextDuration = - RealTime::realTime2Frame(contextDurationRT, sampleRate); - - 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(blockSize)/2 > - contextStart + contextDuration) break; - } else { - if (blockFrame >= - contextStart + contextDuration) break; - } - -// std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " -// << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << blockSize << std::endl; - - long completion = - (((blockFrame - contextStart) / stepSize) * 99) / - (contextDuration / stepSize); - - // channelCount is either m_input.getModel()->channelCount or 1 - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - int column = (blockFrame - startFrame) / stepSize; - for (size_t i = 0; i <= blockSize/2; ++i) { - fftModels[ch]->getValuesAt - (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); - } - } - } else { - getFrames(channelCount, blockFrame, blockSize, buffers); - } - - 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 += stepSize; - } - - if (m_abandoned) return; - - Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - - for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { - Vamp::Plugin::Feature feature = - features[m_outputFeatureNo][fi]; - addFeature(blockFrame, feature); - } - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - delete fftModels[ch]; - } - } - - setCompletion(100); -} - -void -FeatureExtractionModelTransformer::getFrames(int channelCount, - long startFrame, long size, - float **buffers) -{ - long offset = 0; - - if (startFrame < 0) { - for (int c = 0; c < channelCount; ++c) { - for (int i = 0; i < size && startFrame + i < 0; ++i) { - buffers[c][i] = 0.0f; - } - } - offset = -startFrame; - size -= offset; - if (size <= 0) return; - startFrame = 0; - } - - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; - - long got = 0; - - if (channelCount == 1) { - - got = input->getData(m_input.getChannel(), startFrame, size, - buffers[0] + offset); - - if (m_input.getChannel() == -1 && input->getChannelCount() > 1) { - // use mean instead of sum, as plugin input - float cc = float(input->getChannelCount()); - for (long i = 0; i < size; ++i) { - buffers[0][i + offset] /= cc; - } - } - - } else { - - float **writebuf = buffers; - if (offset > 0) { - writebuf = new float *[channelCount]; - for (int i = 0; i < channelCount; ++i) { - writebuf[i] = buffers[i] + offset; - } - } - - got = input->getData(0, channelCount-1, startFrame, size, writebuf); - - if (writebuf != buffers) delete[] writebuf; - } - - while (got < size) { - for (int c = 0; c < channelCount; ++c) { - buffers[c][got + offset] = 0.0; - } - ++got; - } -} - -void -FeatureExtractionModelTransformer::addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature) -{ - size_t inputRate = m_input.getModel()->getSampleRate(); - -// std::cerr << "FeatureExtractionModelTransformer::addFeature(" -// << blockFrame << ")" << std::endl; - - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - - size_t frame = blockFrame; - - if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - if (!feature.hasTimestamp) { - std::cerr - << "WARNING: FeatureExtractionModelTransformer::addFeature: " - << "Feature has variable sample rate but no timestamp!" - << std::endl; - return; - } else { - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); - } - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::FixedSampleRate) { - - if (feature.hasTimestamp) { - //!!! warning: sampleRate may be non-integral - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, - lrintf(m_descriptor->sampleRate)); - } else { - frame = m_output->getEndFrame(); - } - } - - if (binCount == 0) { - - SparseOneDimensionalModel *model = - getConformingOutput(); - 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 = - getConformingOutput(); - 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]; - if (velocity < 0) velocity = 127; - if (velocity > 127) velocity = 127; - - NoteModel *model = getConformingOutput(); - if (!model) return; - - model->addPoint(NoteModel::Point(frame, pitch, - lrintf(duration), - velocity / 127.f, - feature.label.c_str())); - - } else { - - DenseThreeDimensionalModel::Column values = feature.values; - - EditableDenseThreeDimensionalModel *model = - getConformingOutput(); - if (!model) return; - - model->setColumn(frame / model->getResolution(), values); - } -} - -void -FeatureExtractionModelTransformer::setCompletion(int completion) -{ - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionModelTransformer::setCompletion(" -// << completion << ")" << std::endl; - - if (binCount == 0) { - - SparseOneDimensionalModel *model = - getConformingOutput(); - if (!model) return; - model->setCompletion(completion, true); //!!!m_context.updates); - - } else if (binCount == 1) { - - SparseTimeValueModel *model = - getConformingOutput(); - if (!model) return; - model->setCompletion(completion, true); //!!!m_context.updates); - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - NoteModel *model = - getConformingOutput(); - if (!model) return; - model->setCompletion(completion, true); //!!!m_context.updates); - - } else { - - EditableDenseThreeDimensionalModel *model = - getConformingOutput(); - if (!model) return; - model->setCompletion(completion, true); //!!!m_context.updates); - } -} - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/FeatureExtractionModelTransformer.h --- a/plugin/transform/FeatureExtractionModelTransformer.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ -#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ - -#include "ModelTransformer.h" - -#include - -#include - -#include - -class DenseTimeValueModel; - -class FeatureExtractionModelTransformer : public ModelTransformer -{ - Q_OBJECT - -public: - FeatureExtractionModelTransformer(Input input, - const Transform &transform); - virtual ~FeatureExtractionModelTransformer(); - -protected: - virtual void run(); - - Vamp::Plugin *m_plugin; - Vamp::Plugin::OutputDescriptor *m_descriptor; - int m_outputFeatureNo; - - void addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature); - - void setCompletion(int); - - void getFrames(int channelCount, long startFrame, long size, - float **buffer); - - // just casts - DenseTimeValueModel *getConformingInput(); - template ModelClass *getConformingOutput() { - ModelClass *mc = dynamic_cast(m_output); - if (!mc) { - std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; - } - return mc; - } -}; - -#endif - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/ModelTransformer.cpp --- a/plugin/transform/ModelTransformer.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "ModelTransformer.h" - -ModelTransformer::ModelTransformer(Input input, const Transform &transform) : - m_transform(transform), - m_input(input), - m_output(0), - m_detached(false), - m_abandoned(false) -{ -} - -ModelTransformer::~ModelTransformer() -{ - m_abandoned = true; - wait(); - if (!m_detached) delete m_output; -} - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/ModelTransformer.h --- a/plugin/transform/ModelTransformer.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _TRANSFORMER_H_ -#define _TRANSFORMER_H_ - -#include "base/Thread.h" - -#include "data/model/Model.h" - -#include "Transform.h" - -/** - * A ModelTransformer turns one data model into another. - * - * Typically in this application, a ModelTransformer might have a - * DenseTimeValueModel as its input (e.g. an audio waveform) and a - * SparseOneDimensionalModel (e.g. detected beats) as its output. - * - * The ModelTransformer typically runs in the background, as a - * separate thread populating the output model. The model is - * available to the user of the ModelTransformer immediately, but may - * be initially empty until the background thread has populated it. - */ - -class ModelTransformer : public Thread -{ -public: - virtual ~ModelTransformer(); - - class Input { - public: - Input(Model *m) : m_model(m), m_channel(-1) { } - Input(Model *m, int c) : m_model(m), m_channel(c) { } - - Model *getModel() const { return m_model; } - void setModel(Model *m) { m_model = m; } - - int getChannel() const { return m_channel; } - void setChannel(int c) { m_channel = c; } - - protected: - Model *m_model; - int m_channel; - }; - - /** - * Hint to the processing thread that it should give up, for - * example because the process is going to exit or we want to get - * rid of the input model. 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; } - - /** - * Return the input model for the transform. - */ - Model *getInputModel() { return m_input.getModel(); } - - /** - * Return the input channel spec for the transform. - */ - int getInputChannel() { return m_input.getChannel(); } - - /** - * Return the output model created by the transform. Returns a - * null model if the transform could not be initialised; an error - * message may be available via getMessage() in this situation. - */ - Model *getOutputModel() { return m_output; } - - /** - * Return the output model, also detaching it from the transformer - * so that it will not be deleted when the transformer is. The - * caller takes ownership of the model. - */ - Model *detachOutputModel() { m_detached = true; return m_output; } - - /** - * Return a warning or error message. If getOutputModel returned - * a null pointer, this should contain a fatal error message for - * the transformer; otherwise it may contain a warning to show to - * the user about e.g. suboptimal block size or whatever. - */ - QString getMessage() const { return m_message; } - -protected: - ModelTransformer(Input input, const Transform &transform); - - Transform m_transform; - Input m_input; // I don't own the model in this - Model *m_output; // I own this, unless... - bool m_detached; // ... this is true. - bool m_abandoned; - QString m_message; -}; - -#endif diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/ModelTransformerFactory.cpp --- a/plugin/transform/ModelTransformerFactory.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,450 +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 "ModelTransformerFactory.h" - -#include "FeatureExtractionModelTransformer.h" -#include "RealTimeEffectModelTransformer.h" - -#include "TransformFactory.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/PluginXml.h" - -#include "widgets/PluginParameterDialog.h" - -#include "data/model/DenseTimeValueModel.h" - -#include "vamp-sdk/PluginHostAdapter.h" - -#include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here - -#include -#include - -#include - -ModelTransformerFactory * -ModelTransformerFactory::m_instance = new ModelTransformerFactory; - -ModelTransformerFactory * -ModelTransformerFactory::getInstance() -{ - return m_instance; -} - -ModelTransformerFactory::~ModelTransformerFactory() -{ -} - -bool -ModelTransformerFactory::getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, - int &minChannels, int &maxChannels) -{ - Vamp::Plugin *vp = 0; - if ((vp = dynamic_cast(plugin)) || - (vp = dynamic_cast(plugin))) { - minChannels = vp->getMinChannelCount(); - maxChannels = vp->getMaxChannelCount(); - return true; - } else { - return TransformFactory::getInstance()-> - getTransformChannelRange(identifier, minChannels, maxChannels); - } -} - -ModelTransformer::Input -ModelTransformerFactory::getConfigurationForTransform(Transform &transform, - const std::vector &candidateInputModels, - Model *defaultInputModel, - AudioCallbackPlaySource *source, - size_t startFrame, - size_t duration) -{ - ModelTransformer::Input input(0); - - if (candidateInputModels.empty()) return input; - - //!!! 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]; - QStringList candidateModelNames; - QString defaultModelName; - 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); - if (candidateInputModels[i] == defaultInputModel) { - defaultModelName = modelName; - } - } - - QString id = transform.getPluginIdentifier(); - QString output = transform.getOutput(); - QString outputLabel = ""; - QString outputDescription = ""; - - bool ok = false; - QString configurationXml = m_lastConfigurations[transform.getIdentifier()]; - - 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) { - - // Ensure block size etc are valid - TransformFactory::getInstance()-> - makeContextConsistentWithPlugin(transform, plugin); - - // Prepare the plugin with any existing parameters already - // found in the transform - TransformFactory::getInstance()-> - setPluginParameters(transform, plugin); - - // For this interactive usage, we want to override those with - // whatever the user chose last time around - PluginXml(plugin).setParametersFromXml(configurationXml); - - int sourceChannels = 1; - if (dynamic_cast(inputModel)) { - sourceChannels = dynamic_cast(inputModel) - ->getChannelCount(); - } - - int minChannels = 1, maxChannels = sourceChannels; - getChannelRange(transform.getIdentifier(), plugin, - minChannels, maxChannels); - - int targetChannels = sourceChannels; - if (!effect) { - if (sourceChannels < minChannels) targetChannels = minChannels; - if (sourceChannels > maxChannels) targetChannels = maxChannels; - } - - int defaultChannel = -1; //!!! no longer saved! [was context.channel] - - PluginParameterDialog *dialog = new PluginParameterDialog(plugin); - - if (candidateModelNames.size() > 1 && !generator) { - dialog->setCandidateInputModels(candidateModelNames, - defaultModelName); - } - - 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; - } - - // Write parameters back to transform object - TransformFactory::getInstance()-> - setParametersFromPlugin(transform, plugin); - - input.setChannel(dialog->getChannel()); - - //!!! The dialog ought to be taking & returning transform - //objects and input objects and stuff rather than passing - //around all this misc stuff, but that's for tomorrow - //(whenever that may be) - - if (startFrame != 0 || duration != 0) { - if (dialog->getSelectionOnly()) { - transform.setStartTime(RealTime::frame2RealTime - (startFrame, inputModel->getSampleRate())); - transform.setDuration(RealTime::frame2RealTime - (duration, inputModel->getSampleRate())); - } - } - - size_t stepSize = 0, blockSize = 0; - WindowType windowType = HanningWindow; - - dialog->getProcessingParameters(stepSize, - blockSize, - windowType); - - transform.setStepSize(stepSize); - transform.setBlockSize(blockSize); - transform.setWindowType(windowType); - - TransformFactory::getInstance()-> - makeContextConsistentWithPlugin(transform, plugin); - - configurationXml = PluginXml(plugin).toXmlString(); - - delete dialog; - - if (effect && source) { - source->setAuditioningPlugin(0); // will delete our plugin - } else { - delete plugin; - } - } - - if (ok) { - m_lastConfigurations[transform.getIdentifier()] = configurationXml; - input.setModel(inputModel); - } - - return input; -} -/*!!! -PluginTransformer::ExecutionContext -ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier, - Model *inputModel) -{ - PluginTransformer::ExecutionContext context(-1); - - QString id = identifier.section(':', 0, 2); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, inputModel ? inputModel->getSampleRate() : 48000); - - if (vp) { - context = PluginTransformer::ExecutionContext(-1, vp); - delete vp; - } - } - - return context; -} -*/ -ModelTransformer * -ModelTransformerFactory::createTransformer(const Transform &transform, - const ModelTransformer::Input &input) -{ - ModelTransformer *transformer = 0; - - QString id = transform.getPluginIdentifier(); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - transformer = - new FeatureExtractionModelTransformer(input, transform); - - } else if (RealTimePluginFactory::instanceFor(id)) { - - transformer = - new RealTimeEffectModelTransformer(input, transform); - - } else { - std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \"" - << transform.getIdentifier().toStdString() << "\"" << std::endl; - return transformer; - } - - if (transformer) transformer->setObjectName(transform.getIdentifier()); - return transformer; -} - -Model * -ModelTransformerFactory::transform(const Transform &transform, - const ModelTransformer::Input &input, - QString &message) -{ - ModelTransformer *t = createTransformer(transform, input); - if (!t) return 0; - - connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); - - m_runningTransformers.insert(t); - - t->start(); - Model *model = t->detachOutputModel(); - - if (model) { - QString imn = input.getModel()->objectName(); - QString trn = - TransformFactory::getInstance()->getTransformFriendlyName - (transform.getIdentifier()); - 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(); - } - - message = t->getMessage(); - - return model; -} - -void -ModelTransformerFactory::transformerFinished() -{ - QObject *s = sender(); - ModelTransformer *transformer = dynamic_cast(s); - - std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl; - - if (!transformer) { - std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl; - return; - } - - if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { - std::cerr << "WARNING: ModelTransformerFactory::transformerFinished(" - << transformer - << "): I have no record of this transformer running!" - << std::endl; - } - - m_runningTransformers.erase(transformer); - - transformer->wait(); // unnecessary but reassuring - delete transformer; -} - -void -ModelTransformerFactory::modelAboutToBeDeleted(Model *m) -{ - TransformerSet affected; - - for (TransformerSet::iterator i = m_runningTransformers.begin(); - i != m_runningTransformers.end(); ++i) { - - ModelTransformer *t = *i; - - if (t->getInputModel() == m || t->getOutputModel() == m) { - affected.insert(t); - } - } - - for (TransformerSet::iterator i = affected.begin(); - i != affected.end(); ++i) { - - ModelTransformer *t = *i; - - t->abandon(); - - t->wait(); // this should eventually call back on - // transformerFinished, which will remove from - // m_runningTransformers and delete. - } -} - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/ModelTransformerFactory.h --- a/plugin/transform/ModelTransformerFactory.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +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 _MODEL_TRANSFORMER_FACTORY_H_ -#define _MODEL_TRANSFORMER_FACTORY_H_ - -#include "Transform.h" -#include "TransformDescription.h" - -#include "ModelTransformer.h" - -#include -#include - -namespace Vamp { class PluginBase; } - -class AudioCallbackPlaySource; - -class ModelTransformerFactory : public QObject -{ - Q_OBJECT - -public: - virtual ~ModelTransformerFactory(); - - static ModelTransformerFactory *getInstance(); - - /** - * Fill out the configuration for the given transform (by asking - * the user, most likely). Returns the selected input model and - * channel if the transform is acceptable, or an input with a null - * model if the operation should be cancelled. Audio callback - * play source may be used to audition effects plugins, if - * provided. - */ - ModelTransformer::Input - getConfigurationForTransform(Transform &transform, - const std::vector &candidateInputModels, - Model *defaultInputModel, - AudioCallbackPlaySource *source = 0, - size_t startFrame = 0, - size_t duration = 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. Set message if there is any error or - * warning to report. - * - * The returned model is owned by the caller and must be deleted - * when no longer needed. - */ - Model *transform(const Transform &transform, - const ModelTransformer::Input &input, - QString &message); - -protected slots: - void transformerFinished(); - - void modelAboutToBeDeleted(Model *); - -protected: - ModelTransformer *createTransformer(const Transform &transform, - const ModelTransformer::Input &input); - - typedef std::map TransformerConfigurationMap; - TransformerConfigurationMap m_lastConfigurations; - - typedef std::set TransformerSet; - TransformerSet m_runningTransformers; - - bool getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, int &min, int &max); - - static ModelTransformerFactory *m_instance; -}; - - -#endif diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/RealTimeEffectModelTransformer.cpp --- a/plugin/transform/RealTimeEffectModelTransformer.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,279 +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 "RealTimeEffectModelTransformer.h" - -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - -#include "data/model/Model.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/WritableWaveFileModel.h" -#include "data/model/WaveFileModel.h" - -#include "TransformFactory.h" - -#include - -RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in, - const Transform &transform) : - ModelTransformer(in, transform), - m_plugin(0) -{ - m_units = TransformFactory::getInstance()->getTransformUnits - (transform.getIdentifier()); - m_outputNo = - (transform.getOutput() == "A") ? -1 : transform.getOutput().toInt(); - - QString pluginId = transform.getPluginIdentifier(); - - if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024); - -// std::cerr << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "RealTimeEffectModelTransformer: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; - - m_plugin = factory->instantiatePlugin(pluginId, 0, 0, - input->getSampleRate(), - m_transform.getBlockSize(), - input->getChannelCount()); - - if (!m_plugin) { - std::cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin); - - if (m_outputNo >= 0 && - m_outputNo >= int(m_plugin->getControlOutputCount())) { - std::cerr << "RealTimeEffectModelTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; - return; - } - - if (m_outputNo == -1) { - - size_t outputChannels = m_plugin->getAudioOutputCount(); - if (outputChannels > input->getChannelCount()) { - outputChannels = input->getChannelCount(); - } - - WritableWaveFileModel *model = new WritableWaveFileModel - (input->getSampleRate(), outputChannels); - - m_output = model; - - } else { - - SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false); - - if (m_units != "") model->setScaleUnits(m_units); - - m_output = model; - } -} - -RealTimeEffectModelTransformer::~RealTimeEffectModelTransformer() -{ - delete m_plugin; -} - -DenseTimeValueModel * -RealTimeEffectModelTransformer::getConformingInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - std::cerr << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -RealTimeEffectModelTransformer::run() -{ - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; - - while (!input->isReady()) { - if (dynamic_cast(input)) break; // no need to wait - std::cerr << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - SparseTimeValueModel *stvm = dynamic_cast(m_output); - WritableWaveFileModel *wwfm = dynamic_cast(m_output); - if (!stvm && !wwfm) return; - - if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; - - size_t sampleRate = input->getSampleRate(); - size_t channelCount = input->getChannelCount(); - if (!wwfm && m_input.getChannel() != -1) channelCount = 1; - - long blockSize = m_plugin->getBufferSize(); - - float **inbufs = m_plugin->getAudioInputBuffers(); - - long startFrame = m_input.getModel()->getStartFrame(); - long endFrame = m_input.getModel()->getEndFrame(); - - RealTime contextStartRT = m_transform.getStartTime(); - RealTime contextDurationRT = m_transform.getDuration(); - - long contextStart = - RealTime::realTime2Frame(contextStartRT, sampleRate); - - long contextDuration = - RealTime::realTime2Frame(contextDurationRT, sampleRate); - - 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_input.getChannel(), 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 { - got = input->getData(0, channelCount - 1, - blockFrame, blockSize, - inbufs); - while (got < blockSize) { - for (size_t ch = 0; ch < channelCount; ++ch) { - inbufs[ch][got] = 0.0; - } - ++got; - } - 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 7aa1de571880 -r 370aa9714ef5 plugin/transform/RealTimeEffectModelTransformer.h --- a/plugin/transform/RealTimeEffectModelTransformer.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _REAL_TIME_PLUGIN_TRANSFORMER_H_ -#define _REAL_TIME_PLUGIN_TRANSFORMER_H_ - -#include "ModelTransformer.h" -#include "plugin/RealTimePluginInstance.h" - -class DenseTimeValueModel; - -class RealTimeEffectModelTransformer : public ModelTransformer -{ -public: - RealTimeEffectModelTransformer(Input input, - const Transform &transform); - virtual ~RealTimeEffectModelTransformer(); - -protected: - virtual void run(); - - QString m_units; - RealTimePluginInstance *m_plugin; - int m_outputNo; - - // just casts - DenseTimeValueModel *getConformingInput(); -}; - -#endif - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/Transform.cpp --- a/plugin/transform/Transform.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +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-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 - 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 "Transform.h" - -#include "plugin/PluginIdentifier.h" - -#include "plugin/FeatureExtractionPluginFactory.h" - -#include - -#include -#include -#include -#include - -#include - -#include - -Transform::Transform() : - m_stepSize(0), - m_blockSize(0), - m_windowType(HanningWindow), - m_sampleRate(0) -{ -} - -Transform::Transform(QString xml) : - m_stepSize(0), - m_blockSize(0), - m_windowType(HanningWindow), - m_sampleRate(0) -{ - QDomDocument doc; - - QString error; - int errorLine; - int errorColumn; - - if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { - std::cerr << "Transform::Transform: Error in parsing XML: " - << error.toStdString() << " at line " << errorLine - << ", column " << errorColumn << std::endl; - std::cerr << "Input follows:" << std::endl; - std::cerr << xml.toStdString() << std::endl; - std::cerr << "Input ends." << std::endl; - return; - } - - QDomElement transformElt = doc.firstChildElement("transform"); - QDomNamedNodeMap attrNodes = transformElt.attributes(); - QXmlAttributes attrs; - - for (unsigned int i = 0; i < attrNodes.length(); ++i) { - QDomAttr attr = attrNodes.item(i).toAttr(); - if (!attr.isNull()) attrs.append(attr.name(), "", "", attr.value()); - } - - setFromXmlAttributes(attrs); - - for (QDomElement paramElt = transformElt.firstChildElement("parameter"); - !paramElt.isNull(); - paramElt = paramElt.nextSiblingElement("parameter")) { - - QDomNamedNodeMap paramAttrs = paramElt.attributes(); - - QDomAttr nameAttr = paramAttrs.namedItem("name").toAttr(); - if (nameAttr.isNull() || nameAttr.value() == "") continue; - - QDomAttr valueAttr = paramAttrs.namedItem("value").toAttr(); - if (valueAttr.isNull() || valueAttr.value() == "") continue; - - setParameter(nameAttr.value(), valueAttr.value().toFloat()); - } - - for (QDomElement configElt = transformElt.firstChildElement("configuration"); - !configElt.isNull(); - configElt = configElt.nextSiblingElement("configuration")) { - - QDomNamedNodeMap configAttrs = configElt.attributes(); - - QDomAttr nameAttr = configAttrs.namedItem("name").toAttr(); - if (nameAttr.isNull() || nameAttr.value() == "") continue; - - QDomAttr valueAttr = configAttrs.namedItem("value").toAttr(); - if (valueAttr.isNull() || valueAttr.value() == "") continue; - - setConfigurationValue(nameAttr.value(), valueAttr.value()); - } -} - -Transform::~Transform() -{ -} - -bool -Transform::operator==(const Transform &t) -{ - return - m_id == t.m_id && - m_parameters == t.m_parameters && - m_configuration == t.m_configuration && - m_program == t.m_program && - m_stepSize == t.m_stepSize && - m_blockSize == t.m_blockSize && - m_windowType == t.m_windowType && - m_startTime == t.m_startTime && - m_duration == t.m_duration && - m_sampleRate == t.m_sampleRate; -} - -void -Transform::setIdentifier(TransformId id) -{ - m_id = id; -} - -TransformId -Transform::getIdentifier() const -{ - return m_id; -} - -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 -{ - if (FeatureExtractionPluginFactory::instanceFor(getPluginIdentifier())) { - return FeatureExtraction; - } else { - // We don't have an unknown/invalid return value, so always - // return this - return RealTimeEffect; - } -} - -QString -Transform::getPluginIdentifier() const -{ - return m_id.section(':', 0, 2); -} - -QString -Transform::getOutput() const -{ - return m_id.section(':', 3); -} - -void -Transform::setPluginIdentifier(QString pluginIdentifier) -{ - m_id = pluginIdentifier + ':' + getOutput(); -} - -void -Transform::setOutput(QString output) -{ - m_id = getPluginIdentifier() + ':' + output; -} - -TransformId -Transform::getIdentifierForPluginOutput(QString pluginIdentifier, - QString output) -{ - return pluginIdentifier + ':' + output; -} - -const Transform::ParameterMap & -Transform::getParameters() const -{ - return m_parameters; -} - -void -Transform::setParameters(const ParameterMap &pm) -{ - m_parameters = pm; -} - -void -Transform::setParameter(QString name, float value) -{ - std::cerr << "Transform::setParameter(" << name.toStdString() - << ") -> " << value << std::endl; - m_parameters[name] = value; -} - -const Transform::ConfigurationMap & -Transform::getConfiguration() const -{ - return m_configuration; -} - -void -Transform::setConfiguration(const ConfigurationMap &cm) -{ - m_configuration = cm; -} - -void -Transform::setConfigurationValue(QString name, QString value) -{ - std::cerr << "Transform::setConfigurationValue(" << name.toStdString() - << ") -> " << value.toStdString() << std::endl; - m_configuration[name] = value; -} - -QString -Transform::getPluginVersion() const -{ - return m_pluginVersion; -} - -void -Transform::setPluginVersion(QString version) -{ - m_pluginVersion = version; -} - -QString -Transform::getProgram() const -{ - return m_program; -} - -void -Transform::setProgram(QString program) -{ - m_program = program; -} - - -size_t -Transform::getStepSize() const -{ - return m_stepSize; -} - -void -Transform::setStepSize(size_t s) -{ - m_stepSize = s; -} - -size_t -Transform::getBlockSize() const -{ - return m_blockSize; -} - -void -Transform::setBlockSize(size_t s) -{ - m_blockSize = s; -} - -WindowType -Transform::getWindowType() const -{ - return m_windowType; -} - -void -Transform::setWindowType(WindowType type) -{ - m_windowType = type; -} - -RealTime -Transform::getStartTime() const -{ - return m_startTime; -} - -void -Transform::setStartTime(RealTime t) -{ - m_startTime = t; -} - -RealTime -Transform::getDuration() const -{ - return m_duration; -} - -void -Transform::setDuration(RealTime d) -{ - m_duration = d; -} - -float -Transform::getSampleRate() const -{ - return m_sampleRate; -} - -void -Transform::setSampleRate(float rate) -{ - m_sampleRate = rate; -} - -void -Transform::toXml(QTextStream &out, QString indent, QString extraAttributes) const -{ - out << indent; - - bool haveContent = true; - if (m_parameters.empty() && m_configuration.empty()) haveContent = false; - - out << QString("::getNameForType(m_windowType).c_str())) - .arg(encodeEntities(m_startTime.toString().c_str())) - .arg(encodeEntities(m_duration.toString().c_str())) - .arg(m_sampleRate); - - if (extraAttributes != "") { - out << " " << extraAttributes; - } - - if (haveContent) { - - out << ">\n"; - - for (ParameterMap::const_iterator i = m_parameters.begin(); - i != m_parameters.end(); ++i) { - out << indent << " " - << QString("\n") - .arg(encodeEntities(i->first)) - .arg(i->second); - } - - for (ConfigurationMap::const_iterator i = m_configuration.begin(); - i != m_configuration.end(); ++i) { - out << indent << " " - << QString("\n") - .arg(encodeEntities(i->first)) - .arg(encodeEntities(i->second)); - } - - out << indent << "\n"; - - } else { - - out << "/>\n"; - } -} - -void -Transform::setFromXmlAttributes(const QXmlAttributes &attrs) -{ - if (attrs.value("id") != "") { - setIdentifier(attrs.value("id")); - } - - if (attrs.value("pluginVersion") != "") { - setPluginVersion(attrs.value("pluginVersion")); - } - - if (attrs.value("program") != "") { - setProgram(attrs.value("program")); - } - - if (attrs.value("stepSize") != "") { - setStepSize(attrs.value("stepSize").toInt()); - } - - if (attrs.value("blockSize") != "") { - setBlockSize(attrs.value("blockSize").toInt()); - } - - if (attrs.value("windowType") != "") { - setWindowType(Window::getTypeForName - (attrs.value("windowType").toStdString())); - } - - if (attrs.value("startTime") != "") { - setStartTime(RealTime::fromString(attrs.value("startTime").toStdString())); - } - - if (attrs.value("duration") != "") { - setStartTime(RealTime::fromString(attrs.value("duration").toStdString())); - } - - if (attrs.value("sampleRate") != "") { - setSampleRate(attrs.value("sampleRate").toFloat()); - } -} - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/Transform.h --- a/plugin/transform/Transform.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +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-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 - 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/XmlExportable.h" -#include "base/Window.h" -#include "base/RealTime.h" - -#include - -typedef QString TransformId; - -class QXmlAttributes; - -namespace Vamp { - class PluginBase; -} - -class Transform : public XmlExportable -{ -public: - /** - * Construct a new Transform with default data and no identifier. - * The Transform object will be meaningless until some data and an - * identifier have been set on it. - * - * To construct a Transform for use with a particular transform - * identifier, use TransformFactory::getDefaultTransformFor. - */ - Transform(); - - /** - * Construct a Transform by parsing the given XML data string. - * This is the inverse of toXml. - */ - Transform(QString xml); - - virtual ~Transform(); - - /** - * Compare two Transforms. They only compare equal if every data - * element matches. - */ - bool operator==(const Transform &); - - void setIdentifier(TransformId id); - TransformId getIdentifier() const; - - enum Type { FeatureExtraction, RealTimeEffect }; - - Type getType() const; - QString getPluginIdentifier() const; - QString getOutput() const; - - void setPluginIdentifier(QString pluginIdentifier); - void setOutput(QString output); - - // Turn a plugin ID and output name into a transform ID. Note - // that our pluginIdentifier is the same thing as the Vamp SDK's - // PluginLoader::PluginKey. - static TransformId getIdentifierForPluginOutput(QString pluginIdentifier, - QString output = ""); - - typedef std::map ParameterMap; - - const ParameterMap &getParameters() const; - void setParameters(const ParameterMap &pm); - void setParameter(QString name, float value); - - typedef std::map ConfigurationMap; - - const ConfigurationMap &getConfiguration() const; - void setConfiguration(const ConfigurationMap &cm); - void setConfigurationValue(QString name, QString value); - - QString getPluginVersion() const; - void setPluginVersion(QString version); - - QString getProgram() const; - void setProgram(QString program); - - size_t getStepSize() const; - void setStepSize(size_t s); - - size_t getBlockSize() const; - void setBlockSize(size_t s); - - WindowType getWindowType() const; - void setWindowType(WindowType type); - - RealTime getStartTime() const; - void setStartTime(RealTime t); - - RealTime getDuration() const; // 0 -> all - void setDuration(RealTime d); - - float getSampleRate() const; // 0 -> as input - void setSampleRate(float rate); - - void toXml(QTextStream &stream, QString indent = "", - QString extraAttributes = "") const; - - /** - * Set the main transform data from the given XML attributes. - * This does not set the parameters or configuration, which are - * exported to separate XML elements rather than attributes of the - * transform element. - * - * Note that this only sets those attributes which are actually - * present in the argument. Any attributes not defined in the - * attribute will remain unchanged in the Transform. If your aim - * is to create a transform exactly matching the given attributes, - * ensure you start from an empty transform rather than one that - * has already been configured. - */ - void setFromXmlAttributes(const QXmlAttributes &); - -protected: - TransformId m_id; // pluginid:output, that is type:soname:label:output - - static QString createIdentifier - (QString type, QString soName, QString label, QString output); - - static void parseIdentifier - (QString identifier, - QString &type, QString &soName, QString &label, QString &output); - - ParameterMap m_parameters; - ConfigurationMap m_configuration; - QString m_pluginVersion; - QString m_program; - size_t m_stepSize; - size_t m_blockSize; - WindowType m_windowType; - RealTime m_startTime; - RealTime m_duration; - float m_sampleRate; -}; - -#endif - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/TransformDescription.h --- a/plugin/transform/TransformDescription.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +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-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 - 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_DESCRIPTION_H_ -#define _TRANSFORM_DESCRIPTION_H_ - -#include "Transform.h" - -#include - -#include - -/** - * Metadata associated with a transform. - * - * The transform ID is the same as that used in the Transform class. - * It 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 that creates these objects - * should 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. - * - * To obtain these objects, use - * TransformFactory::getAllTransformDescriptions and - * TransformFactory::getTransformDescription. - */ - -struct TransformDescription -{ - TransformDescription() { } - TransformDescription(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 TransformDescription &od) const { - return - (name < od.name) || - (name == od.name && identifier < od.identifier); - }; -}; - -typedef std::vector TransformList; - -#endif diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/TransformFactory.cpp --- a/plugin/transform/TransformFactory.cpp Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,776 +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 "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - -#include "vamp-sdk/Plugin.h" -#include "vamp-sdk/PluginHostAdapter.h" -#include "vamp-sdk/hostext/PluginWrapper.h" - -#include -#include - -#include -#include - -TransformFactory * -TransformFactory::m_instance = new TransformFactory; - -TransformFactory * -TransformFactory::getInstance() -{ - return m_instance; -} - -TransformFactory::~TransformFactory() -{ -} - -TransformList -TransformFactory::getAllTransformDescriptions() -{ - if (m_transforms.empty()) populateTransforms(); - - std::set dset; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { -// std::cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << std::endl; - dset.insert(i->second); - } - - TransformList list; - for (std::set::const_iterator i = dset.begin(); - i != dset.end(); ++i) { -// std::cerr << "inserting transform into list: id = " << i->identifier.toStdString() << std::endl; - list.push_back(*i); - } - - return list; -} - -TransformDescription -TransformFactory::getTransformDescription(TransformId id) -{ - if (m_transforms.empty()) populateTransforms(); - - if (m_transforms.find(id) == m_transforms.end()) { - return TransformDescription(); - } - - return m_transforms[id]; -} - -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) { - - TransformDescription desc = i->second; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString pn = desc.identifier.section(":", 1, 1); - - if (pluginSources.find(tn) != pluginSources.end()) { - if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) { - ++names[tn]; - } - } else { - ++names[tn]; - pluginSources[tn] = pn; - pluginMakers[tn] = desc.maker; - } - } - - std::map counts; - m_transforms.clear(); - - for (TransformDescriptionMap::iterator i = transforms.begin(); - i != transforms.end(); ++i) { - - TransformDescription desc = i->second; - QString identifier = desc.identifier; - QString maker = desc.maker; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString to = td.section(": ", 1); - - if (names[tn] > 1) { - maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - tn = QString("%1 [%2]").arg(tn).arg(maker); - } - - if (to != "") { - desc.name = QString("%1: %2").arg(tn).arg(to); - } else { - desc.name = tn; - } - - m_transforms[identifier] = desc; - } -} - -void -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, 44100); - - 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() << " friendly name: " << friendlyName.toStdString() << std::endl; - - transforms[transformId] = - TransformDescription(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] = - TransformDescription(tr("Effects Data"), - category, - transformId, - userName, - userName, - description, - maker, - units, - configurable); - } - } - - if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { - - if (descriptor->audioOutputPortCount > 0) { - - QString transformId = QString("%1:A").arg(pluginId); - QString type = tr("Effects"); - - QString description = tr("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] = - TransformDescription(type, - category, - transformId, - pluginName, - pluginName, - description, - maker, - "", - configurable); - } - } - } -} - - -Transform -TransformFactory::getDefaultTransformFor(TransformId id, size_t rate) -{ - Transform t; - t.setIdentifier(id); - if (rate != 0) t.setSampleRate(rate); - - Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate); - - if (plugin) { - t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion())); - setParametersFromPlugin(t, plugin); - makeContextConsistentWithPlugin(t, plugin); - delete plugin; - } - - return t; -} - -Vamp::PluginBase * -TransformFactory::instantiatePluginFor(const Transform &transform) -{ - Vamp::PluginBase *plugin = instantiateDefaultPluginFor - (transform.getIdentifier(), transform.getSampleRate()); - if (plugin) { - setPluginParameters(transform, plugin); - } - return plugin; -} - -Vamp::PluginBase * -TransformFactory::instantiateDefaultPluginFor(TransformId identifier, size_t rate) -{ - Transform t; - t.setIdentifier(identifier); - if (rate == 0) rate = 44100; - QString pluginId = t.getPluginIdentifier(); - - Vamp::PluginBase *plugin = 0; - - if (t.getType() == Transform::FeatureExtraction) { - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - plugin = factory->instantiatePlugin(pluginId, rate); - - } else { - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1); - } - - return plugin; -} - -Vamp::Plugin * -TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin) -{ - 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; - } - return vp; -} - -bool -TransformFactory::haveTransform(TransformId identifier) -{ - if (m_transforms.empty()) populateTransforms(); - 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 ""; -} - -Vamp::Plugin::InputDomain -TransformFactory::getTransformInputDomain(TransformId identifier) -{ - Transform transform; - transform.setIdentifier(identifier); - - if (transform.getType() != Transform::FeatureExtraction) { - return Vamp::Plugin::TimeDomain; - } - - Vamp::Plugin *plugin = - downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0)); - - if (plugin) { - Vamp::Plugin::InputDomain d = plugin->getInputDomain(); - delete plugin; - return d; - } - - return Vamp::Plugin::TimeDomain; -} - -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, 44100); - if (!plugin) return false; - - min = plugin->getMinChannelCount(); - max = plugin->getMaxChannelCount(); - delete plugin; - - return true; - - } else if (RealTimePluginFactory::instanceFor(id)) { - - // don't need to instantiate - - const RealTimePluginDescriptor *descriptor = - RealTimePluginFactory::instanceFor(id)-> - getPluginDescriptor(id); - if (!descriptor) return false; - - min = descriptor->audioInputPortCount; - max = descriptor->audioInputPortCount; - - return true; - } - - return false; -} - -void -TransformFactory::setParametersFromPlugin(Transform &transform, - Vamp::PluginBase *plugin) -{ - Transform::ParameterMap pmap; - - //!!! record plugin & API version - - //!!! check that this is the right plugin! - - Vamp::PluginBase::ParameterList parameters = - plugin->getParameterDescriptors(); - - for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); - i != parameters.end(); ++i) { - pmap[i->identifier.c_str()] = plugin->getParameter(i->identifier); - } - - transform.setParameters(pmap); - - if (plugin->getPrograms().empty()) { - transform.setProgram(""); - } else { - transform.setProgram(plugin->getCurrentProgram().c_str()); - } - - RealTimePluginInstance *rtpi = - dynamic_cast(plugin); - - Transform::ConfigurationMap cmap; - - if (rtpi) { - - RealTimePluginInstance::ConfigurationPairMap configurePairs = - rtpi->getConfigurePairs(); - - for (RealTimePluginInstance::ConfigurationPairMap::const_iterator i - = configurePairs.begin(); i != configurePairs.end(); ++i) { - cmap[i->first.c_str()] = i->second.c_str(); - } - } - - transform.setConfiguration(cmap); -} - -void -TransformFactory::setPluginParameters(const Transform &transform, - Vamp::PluginBase *plugin) -{ - //!!! check plugin & API version (see e.g. PluginXml::setParameters) - - //!!! check that this is the right plugin! - - RealTimePluginInstance *rtpi = - dynamic_cast(plugin); - - if (rtpi) { - const Transform::ConfigurationMap &cmap = transform.getConfiguration(); - for (Transform::ConfigurationMap::const_iterator i = cmap.begin(); - i != cmap.end(); ++i) { - rtpi->configure(i->first.toStdString(), i->second.toStdString()); - } - } - - if (transform.getProgram() != "") { - plugin->selectProgram(transform.getProgram().toStdString()); - } - - const Transform::ParameterMap &pmap = transform.getParameters(); - - Vamp::PluginBase::ParameterList parameters = - plugin->getParameterDescriptors(); - - for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); - i != parameters.end(); ++i) { - QString key = i->identifier.c_str(); - Transform::ParameterMap::const_iterator pmi = pmap.find(key); - if (pmi != pmap.end()) { - plugin->setParameter(i->identifier, pmi->second); - } - } -} - -void -TransformFactory::makeContextConsistentWithPlugin(Transform &transform, - Vamp::PluginBase *plugin) -{ - const Vamp::Plugin *vp = downcastVampPlugin(plugin); - - if (!vp) { - // time domain input for real-time effects plugin - if (!transform.getBlockSize()) { - if (!transform.getStepSize()) transform.setStepSize(1024); - transform.setBlockSize(transform.getStepSize()); - } else { - transform.setStepSize(transform.getBlockSize()); - } - } else { - Vamp::Plugin::InputDomain domain = vp->getInputDomain(); - if (!transform.getStepSize()) { - transform.setStepSize(vp->getPreferredStepSize()); - } - if (!transform.getBlockSize()) { - transform.setBlockSize(vp->getPreferredBlockSize()); - } - if (!transform.getBlockSize()) { - transform.setBlockSize(1024); - } - if (!transform.getStepSize()) { - if (domain == Vamp::Plugin::FrequencyDomain) { -// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; - transform.setStepSize(transform.getBlockSize()/2); - } else { -// std::cerr << "time domain, step = " << blockSize/2 << std::endl; - transform.setStepSize(transform.getBlockSize()); - } - } - } -} - -QString -TransformFactory::getPluginConfigurationXml(const Transform &t) -{ - QString xml; - - Vamp::PluginBase *plugin = instantiateDefaultPluginFor - (t.getIdentifier(), 0); - if (!plugin) { - std::cerr << "TransformFactory::getPluginConfigurationXml: " - << "Unable to instantiate plugin for transform \"" - << t.getIdentifier().toStdString() << "\"" << std::endl; - return xml; - } - - setPluginParameters(t, plugin); - - QTextStream out(&xml); - PluginXml(plugin).toXml(out); - delete plugin; - - return xml; -} - -void -TransformFactory::setParametersFromPluginConfigurationXml(Transform &t, - QString xml) -{ - Vamp::PluginBase *plugin = instantiateDefaultPluginFor - (t.getIdentifier(), 0); - if (!plugin) { - std::cerr << "TransformFactory::setParametersFromPluginConfigurationXml: " - << "Unable to instantiate plugin for transform \"" - << t.getIdentifier().toStdString() << "\"" << std::endl; - return; - } - - PluginXml(plugin).setParametersFromXml(xml); - setParametersFromPlugin(t, plugin); - delete plugin; -} - diff -r 7aa1de571880 -r 370aa9714ef5 plugin/transform/TransformFactory.h --- a/plugin/transform/TransformFactory.h Wed Mar 12 17:42:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +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-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 - 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 "TransformDescription.h" - -#include - -#include - -#include -#include - -class TransformFactory : public QObject -{ - Q_OBJECT - -public: - virtual ~TransformFactory(); - - static TransformFactory *getInstance(); - - TransformList getAllTransformDescriptions(); - TransformDescription getTransformDescription(TransformId id); - - std::vector getAllTransformTypes(); - std::vector getTransformCategories(QString transformType); - std::vector getTransformMakers(QString transformType); - - /** - * Return true if the given transform is known. - */ - bool haveTransform(TransformId identifier); - - /** - * A single transform ID can lead to many possible Transforms, - * with different parameters and execution context settings. - * Return the default one for the given transform. - */ - Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0); - - /** - * 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); - - Vamp::Plugin::InputDomain getTransformInputDomain(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); - - /** - * Load an appropriate plugin for the given transform and set the - * parameters, program and configuration strings on that plugin - * from the Transform object. - * - * Note that this requires that the transform has a meaningful - * sample rate set, as that is used as the rate for the plugin. A - * Transform can legitimately have rate set at zero (= "use the - * rate of the input source"), so the caller will need to test for - * this case. - * - * Returns the plugin thus loaded. This will be a - * Vamp::PluginBase, but not necessarily a Vamp::Plugin (only if - * the transform was a feature-extraction type -- call - * downcastVampPlugin if you only want Vamp::Plugins). Returns - * NULL if no suitable plugin was available. - * - * The returned plugin is owned by the caller, and should be - * deleted (using "delete") when no longer needed. - */ - Vamp::PluginBase *instantiatePluginFor(const Transform &transform); - - /** - * Convert a Vamp::PluginBase to a Vamp::Plugin, if it is one. - * Return NULL otherwise. This ill-fitting convenience function - * is really just a dynamic_cast wrapper. - */ - Vamp::Plugin *downcastVampPlugin(Vamp::PluginBase *); - - /** - * Set the plugin parameters, program and configuration strings on - * the given Transform object from the given plugin instance. - * Note that no check is made whether the plugin is actually the - * "correct" one for the transform. - */ - void setParametersFromPlugin(Transform &transform, Vamp::PluginBase *plugin); - - /** - * Set the parameters, program and configuration strings on the - * given plugin from the given Transform object. - */ - void setPluginParameters(const Transform &transform, Vamp::PluginBase *plugin); - - /** - * If the given Transform object has no processing step and block - * sizes set, set them to appropriate defaults for the given - * plugin. - */ - void makeContextConsistentWithPlugin(Transform &transform, Vamp::PluginBase *plugin); - - /** - * Retrieve a XML fragment that describes the - * plugin parameters, program and configuration data for the given - * transform. - * - * This function is provided for backward compatibility only. Use - * Transform::toXml where compatibility with PluginXml - * descriptions of transforms is not required. - */ - QString getPluginConfigurationXml(const Transform &transform); - - /** - * Set the plugin parameters, program and configuration strings on - * the given Transform object from the given XML - * fragment. - * - * This function is provided for backward compatibility only. Use - * Transform(QString) where compatibility with PluginXml - * descriptions of transforms is not required. - */ - void setParametersFromPluginConfigurationXml(Transform &transform, - QString xml); - -protected: - typedef std::map TransformDescriptionMap; - TransformDescriptionMap m_transforms; - - void populateTransforms(); - void populateFeatureExtractionPlugins(TransformDescriptionMap &); - void populateRealTimePlugins(TransformDescriptionMap &); - - Vamp::PluginBase *instantiateDefaultPluginFor(TransformId id, size_t rate); - - static TransformFactory *m_instance; -}; - - -#endif diff -r 7aa1de571880 -r 370aa9714ef5 transform/FeatureExtractionModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,643 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "FeatureExtractionModelTransformer.h" + +#include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/PluginXml.h" +#include "vamp-sdk/Plugin.h" + +#include "data/model/Model.h" +#include "base/Window.h" +#include "base/Exceptions.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 "TransformFactory.h" + +#include + +FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, + const Transform &transform) : + ModelTransformer(in, transform), + m_plugin(0), + m_descriptor(0), + m_outputFeatureNo(0) +{ +// std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl; + + QString pluginId = transform.getPluginIdentifier(); + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + if (!factory) { + m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); + return; + } + + DenseTimeValueModel *input = getConformingInput(); + if (!input) { + m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); + return; + } + + m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate()); + if (!m_plugin) { + m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId); + return; + } + + TransformFactory::getInstance()->makeContextConsistentWithPlugin + (m_transform, m_plugin); + + TransformFactory::getInstance()->setPluginParameters + (m_transform, m_plugin); + + size_t channelCount = input->getChannelCount(); + if (m_plugin->getMaxChannelCount() < channelCount) { + channelCount = 1; + } + if (m_plugin->getMinChannelCount() > channelCount) { + m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)") + .arg(pluginId) + .arg(m_plugin->getMinChannelCount()) + .arg(m_plugin->getMaxChannelCount()) + .arg(input->getChannelCount()); + return; + } + + std::cerr << "Initialising feature extraction plugin with channels = " + << channelCount << ", step = " << m_transform.getStepSize() + << ", block = " << m_transform.getBlockSize() << std::endl; + + if (!m_plugin->initialise(channelCount, + m_transform.getStepSize(), + m_transform.getBlockSize())) { + + size_t pstep = m_transform.getStepSize(); + size_t pblock = m_transform.getBlockSize(); + + m_transform.setStepSize(0); + m_transform.setBlockSize(0); + TransformFactory::getInstance()->makeContextConsistentWithPlugin + (m_transform, m_plugin); + + if (m_transform.getStepSize() != pstep || + m_transform.getBlockSize() != pblock) { + + if (!m_plugin->initialise(channelCount, + m_transform.getStepSize(), + m_transform.getBlockSize())) { + + m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + return; + + } else { + + m_message = tr("Feature extraction plugin \"%1\" rejected the given step and block sizes (%2 and %3); using plugin defaults (%4 and %5) instead") + .arg(pluginId) + .arg(pstep) + .arg(pblock) + .arg(m_transform.getStepSize()) + .arg(m_transform.getBlockSize()); + } + + } else { + + m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + return; + } + } + + if (m_transform.getPluginVersion() != "") { + QString pv = QString("%1").arg(m_plugin->getPluginVersion()); + if (pv != m_transform.getPluginVersion()) { + QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3") + .arg(m_transform.getPluginVersion()) + .arg(pluginId) + .arg(pv); + if (m_message != "") { + m_message = QString("%1; %2").arg(vm).arg(m_message); + } else { + m_message = vm; + } + } + } + + Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); + + if (outputs.empty()) { + m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId); + return; + } + + for (size_t i = 0; i < outputs.size(); ++i) { + if (m_transform.getOutput() == "" || + outputs[i].identifier == m_transform.getOutput().toStdString()) { + m_outputFeatureNo = i; + m_descriptor = new Vamp::Plugin::OutputDescriptor + (outputs[i]); + break; + } + } + + if (!m_descriptor) { + m_message = tr("Plugin \"%1\" has no output named \"%2\"") + .arg(pluginId) + .arg(m_transform.getOutput()); + return; + } + +// std::cerr << "FeatureExtractionModelTransformer: output sample type " +// << m_descriptor->sampleType << std::endl; + + int binCount = 1; + float minValue = 0.0, maxValue = 0.0; + bool haveExtents = false; + + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + +// std::cerr << "FeatureExtractionModelTransformer: output bin count " +// << binCount << std::endl; + + if (binCount > 0 && m_descriptor->hasKnownExtents) { + minValue = m_descriptor->minValue; + maxValue = m_descriptor->maxValue; + haveExtents = true; + } + + size_t modelRate = 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_transform.getStepSize(); + 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; + } + + if (m_output) m_output->setSourceModel(input); +} + +FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() +{ + std::cerr << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << std::endl; + delete m_plugin; + delete m_descriptor; +} + +DenseTimeValueModel * +FeatureExtractionModelTransformer::getConformingInput() +{ + DenseTimeValueModel *dtvm = + dynamic_cast(getInputModel()); + if (!dtvm) { + std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + } + return dtvm; +} + +void +FeatureExtractionModelTransformer::run() +{ + DenseTimeValueModel *input = getConformingInput(); + if (!input) return; + + if (!m_output) return; + + while (!input->isReady()) { +/* + if (dynamic_cast(input)) { + std::cerr << "FeatureExtractionModelTransformer::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl; + sleep(2); + break; // no need to wait + } +*/ + std::cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << std::endl; + sleep(1); + } + + size_t sampleRate = 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_transform.getBlockSize() + 2]; + } + + size_t stepSize = m_transform.getStepSize(); + size_t blockSize = m_transform.getBlockSize(); + + 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 + (getConformingInput(), + channelCount == 1 ? m_input.getChannel() : ch, + m_transform.getWindowType(), + blockSize, + stepSize, + blockSize, + false, + StorageAdviser::PrecisionCritical); + if (!model->isOK()) { + delete model; + setCompletion(100); + //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either + throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer"); + } + model->resume(); + fftModels.push_back(model); + } + } + + long startFrame = m_input.getModel()->getStartFrame(); + long endFrame = m_input.getModel()->getEndFrame(); + + RealTime contextStartRT = m_transform.getStartTime(); + RealTime contextDurationRT = m_transform.getDuration(); + + long contextStart = + RealTime::realTime2Frame(contextStartRT, sampleRate); + + long contextDuration = + RealTime::realTime2Frame(contextDurationRT, sampleRate); + + 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(blockSize)/2 > + contextStart + contextDuration) break; + } else { + if (blockFrame >= + contextStart + contextDuration) break; + } + +// std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " +// << blockFrame << ", endFrame " << endFrame << ", blockSize " +// << blockSize << std::endl; + + long completion = + (((blockFrame - contextStart) / stepSize) * 99) / + (contextDuration / stepSize); + + // channelCount is either m_input.getModel()->channelCount or 1 + + if (frequencyDomain) { + for (size_t ch = 0; ch < channelCount; ++ch) { + int column = (blockFrame - startFrame) / stepSize; + for (size_t i = 0; i <= blockSize/2; ++i) { + fftModels[ch]->getValuesAt + (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); + } + } + } else { + getFrames(channelCount, blockFrame, blockSize, buffers); + } + + 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 += stepSize; + } + + if (m_abandoned) return; + + Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); + + for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { + Vamp::Plugin::Feature feature = + features[m_outputFeatureNo][fi]; + addFeature(blockFrame, feature); + } + + if (frequencyDomain) { + for (size_t ch = 0; ch < channelCount; ++ch) { + delete fftModels[ch]; + } + } + + setCompletion(100); +} + +void +FeatureExtractionModelTransformer::getFrames(int channelCount, + long startFrame, long size, + float **buffers) +{ + long offset = 0; + + if (startFrame < 0) { + for (int c = 0; c < channelCount; ++c) { + for (int i = 0; i < size && startFrame + i < 0; ++i) { + buffers[c][i] = 0.0f; + } + } + offset = -startFrame; + size -= offset; + if (size <= 0) return; + startFrame = 0; + } + + DenseTimeValueModel *input = getConformingInput(); + if (!input) return; + + long got = 0; + + if (channelCount == 1) { + + got = input->getData(m_input.getChannel(), startFrame, size, + buffers[0] + offset); + + if (m_input.getChannel() == -1 && input->getChannelCount() > 1) { + // use mean instead of sum, as plugin input + float cc = float(input->getChannelCount()); + for (long i = 0; i < size; ++i) { + buffers[0][i + offset] /= cc; + } + } + + } else { + + float **writebuf = buffers; + if (offset > 0) { + writebuf = new float *[channelCount]; + for (int i = 0; i < channelCount; ++i) { + writebuf[i] = buffers[i] + offset; + } + } + + got = input->getData(0, channelCount-1, startFrame, size, writebuf); + + if (writebuf != buffers) delete[] writebuf; + } + + while (got < size) { + for (int c = 0; c < channelCount; ++c) { + buffers[c][got + offset] = 0.0; + } + ++got; + } +} + +void +FeatureExtractionModelTransformer::addFeature(size_t blockFrame, + const Vamp::Plugin::Feature &feature) +{ + size_t inputRate = m_input.getModel()->getSampleRate(); + +// std::cerr << "FeatureExtractionModelTransformer::addFeature(" +// << blockFrame << ")" << std::endl; + + int binCount = 1; + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + + size_t frame = blockFrame; + + if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + if (!feature.hasTimestamp) { + std::cerr + << "WARNING: FeatureExtractionModelTransformer::addFeature: " + << "Feature has variable sample rate but no timestamp!" + << std::endl; + return; + } else { + frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); + } + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::FixedSampleRate) { + + if (feature.hasTimestamp) { + //!!! warning: sampleRate may be non-integral + frame = Vamp::RealTime::realTime2Frame(feature.timestamp, + lrintf(m_descriptor->sampleRate)); + } else { + frame = m_output->getEndFrame(); + } + } + + if (binCount == 0) { + + SparseOneDimensionalModel *model = + getConformingOutput(); + 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 = + getConformingOutput(); + 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]; + if (velocity < 0) velocity = 127; + if (velocity > 127) velocity = 127; + + NoteModel *model = getConformingOutput(); + if (!model) return; + + model->addPoint(NoteModel::Point(frame, pitch, + lrintf(duration), + velocity / 127.f, + feature.label.c_str())); + + } else { + + DenseThreeDimensionalModel::Column values = feature.values; + + EditableDenseThreeDimensionalModel *model = + getConformingOutput(); + if (!model) return; + + model->setColumn(frame / model->getResolution(), values); + } +} + +void +FeatureExtractionModelTransformer::setCompletion(int completion) +{ + int binCount = 1; + if (m_descriptor->hasFixedBinCount) { + binCount = m_descriptor->binCount; + } + +// std::cerr << "FeatureExtractionModelTransformer::setCompletion(" +// << completion << ")" << std::endl; + + if (binCount == 0) { + + SparseOneDimensionalModel *model = + getConformingOutput(); + if (!model) return; + model->setCompletion(completion, true); //!!!m_context.updates); + + } else if (binCount == 1) { + + SparseTimeValueModel *model = + getConformingOutput(); + if (!model) return; + model->setCompletion(completion, true); //!!!m_context.updates); + + } else if (m_descriptor->sampleType == + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + + NoteModel *model = + getConformingOutput(); + if (!model) return; + model->setCompletion(completion, true); //!!!m_context.updates); + + } else { + + EditableDenseThreeDimensionalModel *model = + getConformingOutput(); + if (!model) return; + model->setCompletion(completion, true); //!!!m_context.updates); + } +} + diff -r 7aa1de571880 -r 370aa9714ef5 transform/FeatureExtractionModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/FeatureExtractionModelTransformer.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,65 @@ +/* -*- 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 "ModelTransformer.h" + +#include + +#include + +#include + +class DenseTimeValueModel; + +class FeatureExtractionModelTransformer : public ModelTransformer +{ + Q_OBJECT + +public: + FeatureExtractionModelTransformer(Input input, + const Transform &transform); + virtual ~FeatureExtractionModelTransformer(); + +protected: + virtual void run(); + + Vamp::Plugin *m_plugin; + Vamp::Plugin::OutputDescriptor *m_descriptor; + int m_outputFeatureNo; + + void addFeature(size_t blockFrame, + const Vamp::Plugin::Feature &feature); + + void setCompletion(int); + + void getFrames(int channelCount, long startFrame, long size, + float **buffer); + + // just casts + DenseTimeValueModel *getConformingInput(); + template ModelClass *getConformingOutput() { + ModelClass *mc = dynamic_cast(m_output); + if (!mc) { + std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; + } + return mc; + } +}; + +#endif + diff -r 7aa1de571880 -r 370aa9714ef5 transform/ModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/ModelTransformer.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,33 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ModelTransformer.h" + +ModelTransformer::ModelTransformer(Input input, const Transform &transform) : + m_transform(transform), + m_input(input), + m_output(0), + m_detached(false), + m_abandoned(false) +{ +} + +ModelTransformer::~ModelTransformer() +{ + m_abandoned = true; + wait(); + if (!m_detached) delete m_output; +} + diff -r 7aa1de571880 -r 370aa9714ef5 transform/ModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/ModelTransformer.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,111 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _TRANSFORMER_H_ +#define _TRANSFORMER_H_ + +#include "base/Thread.h" + +#include "data/model/Model.h" + +#include "Transform.h" + +/** + * A ModelTransformer turns one data model into another. + * + * Typically in this application, a ModelTransformer might have a + * DenseTimeValueModel as its input (e.g. an audio waveform) and a + * SparseOneDimensionalModel (e.g. detected beats) as its output. + * + * The ModelTransformer typically runs in the background, as a + * separate thread populating the output model. The model is + * available to the user of the ModelTransformer immediately, but may + * be initially empty until the background thread has populated it. + */ + +class ModelTransformer : public Thread +{ +public: + virtual ~ModelTransformer(); + + class Input { + public: + Input(Model *m) : m_model(m), m_channel(-1) { } + Input(Model *m, int c) : m_model(m), m_channel(c) { } + + Model *getModel() const { return m_model; } + void setModel(Model *m) { m_model = m; } + + int getChannel() const { return m_channel; } + void setChannel(int c) { m_channel = c; } + + protected: + Model *m_model; + int m_channel; + }; + + /** + * Hint to the processing thread that it should give up, for + * example because the process is going to exit or we want to get + * rid of the input model. 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; } + + /** + * Return the input model for the transform. + */ + Model *getInputModel() { return m_input.getModel(); } + + /** + * Return the input channel spec for the transform. + */ + int getInputChannel() { return m_input.getChannel(); } + + /** + * Return the output model created by the transform. Returns a + * null model if the transform could not be initialised; an error + * message may be available via getMessage() in this situation. + */ + Model *getOutputModel() { return m_output; } + + /** + * Return the output model, also detaching it from the transformer + * so that it will not be deleted when the transformer is. The + * caller takes ownership of the model. + */ + Model *detachOutputModel() { m_detached = true; return m_output; } + + /** + * Return a warning or error message. If getOutputModel returned + * a null pointer, this should contain a fatal error message for + * the transformer; otherwise it may contain a warning to show to + * the user about e.g. suboptimal block size or whatever. + */ + QString getMessage() const { return m_message; } + +protected: + ModelTransformer(Input input, const Transform &transform); + + Transform m_transform; + Input m_input; // I don't own the model in this + Model *m_output; // I own this, unless... + bool m_detached; // ... this is true. + bool m_abandoned; + QString m_message; +}; + +#endif diff -r 7aa1de571880 -r 370aa9714ef5 transform/ModelTransformerFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/ModelTransformerFactory.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,450 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ModelTransformerFactory.h" + +#include "FeatureExtractionModelTransformer.h" +#include "RealTimeEffectModelTransformer.h" + +#include "TransformFactory.h" + +#include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/RealTimePluginFactory.h" +#include "plugin/PluginXml.h" + +#include "widgets/PluginParameterDialog.h" + +#include "data/model/DenseTimeValueModel.h" + +#include "vamp-sdk/PluginHostAdapter.h" + +#include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here + +#include +#include + +#include + +ModelTransformerFactory * +ModelTransformerFactory::m_instance = new ModelTransformerFactory; + +ModelTransformerFactory * +ModelTransformerFactory::getInstance() +{ + return m_instance; +} + +ModelTransformerFactory::~ModelTransformerFactory() +{ +} + +bool +ModelTransformerFactory::getChannelRange(TransformId identifier, + Vamp::PluginBase *plugin, + int &minChannels, int &maxChannels) +{ + Vamp::Plugin *vp = 0; + if ((vp = dynamic_cast(plugin)) || + (vp = dynamic_cast(plugin))) { + minChannels = vp->getMinChannelCount(); + maxChannels = vp->getMaxChannelCount(); + return true; + } else { + return TransformFactory::getInstance()-> + getTransformChannelRange(identifier, minChannels, maxChannels); + } +} + +ModelTransformer::Input +ModelTransformerFactory::getConfigurationForTransform(Transform &transform, + const std::vector &candidateInputModels, + Model *defaultInputModel, + AudioCallbackPlaySource *source, + size_t startFrame, + size_t duration) +{ + ModelTransformer::Input input(0); + + if (candidateInputModels.empty()) return input; + + //!!! 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]; + QStringList candidateModelNames; + QString defaultModelName; + 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); + if (candidateInputModels[i] == defaultInputModel) { + defaultModelName = modelName; + } + } + + QString id = transform.getPluginIdentifier(); + QString output = transform.getOutput(); + QString outputLabel = ""; + QString outputDescription = ""; + + bool ok = false; + QString configurationXml = m_lastConfigurations[transform.getIdentifier()]; + + 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) { + + // Ensure block size etc are valid + TransformFactory::getInstance()-> + makeContextConsistentWithPlugin(transform, plugin); + + // Prepare the plugin with any existing parameters already + // found in the transform + TransformFactory::getInstance()-> + setPluginParameters(transform, plugin); + + // For this interactive usage, we want to override those with + // whatever the user chose last time around + PluginXml(plugin).setParametersFromXml(configurationXml); + + int sourceChannels = 1; + if (dynamic_cast(inputModel)) { + sourceChannels = dynamic_cast(inputModel) + ->getChannelCount(); + } + + int minChannels = 1, maxChannels = sourceChannels; + getChannelRange(transform.getIdentifier(), plugin, + minChannels, maxChannels); + + int targetChannels = sourceChannels; + if (!effect) { + if (sourceChannels < minChannels) targetChannels = minChannels; + if (sourceChannels > maxChannels) targetChannels = maxChannels; + } + + int defaultChannel = -1; //!!! no longer saved! [was context.channel] + + PluginParameterDialog *dialog = new PluginParameterDialog(plugin); + + if (candidateModelNames.size() > 1 && !generator) { + dialog->setCandidateInputModels(candidateModelNames, + defaultModelName); + } + + 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; + } + + // Write parameters back to transform object + TransformFactory::getInstance()-> + setParametersFromPlugin(transform, plugin); + + input.setChannel(dialog->getChannel()); + + //!!! The dialog ought to be taking & returning transform + //objects and input objects and stuff rather than passing + //around all this misc stuff, but that's for tomorrow + //(whenever that may be) + + if (startFrame != 0 || duration != 0) { + if (dialog->getSelectionOnly()) { + transform.setStartTime(RealTime::frame2RealTime + (startFrame, inputModel->getSampleRate())); + transform.setDuration(RealTime::frame2RealTime + (duration, inputModel->getSampleRate())); + } + } + + size_t stepSize = 0, blockSize = 0; + WindowType windowType = HanningWindow; + + dialog->getProcessingParameters(stepSize, + blockSize, + windowType); + + transform.setStepSize(stepSize); + transform.setBlockSize(blockSize); + transform.setWindowType(windowType); + + TransformFactory::getInstance()-> + makeContextConsistentWithPlugin(transform, plugin); + + configurationXml = PluginXml(plugin).toXmlString(); + + delete dialog; + + if (effect && source) { + source->setAuditioningPlugin(0); // will delete our plugin + } else { + delete plugin; + } + } + + if (ok) { + m_lastConfigurations[transform.getIdentifier()] = configurationXml; + input.setModel(inputModel); + } + + return input; +} +/*!!! +PluginTransformer::ExecutionContext +ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier, + Model *inputModel) +{ + PluginTransformer::ExecutionContext context(-1); + + QString id = identifier.section(':', 0, 2); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + Vamp::Plugin *vp = + FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin + (id, inputModel ? inputModel->getSampleRate() : 48000); + + if (vp) { + context = PluginTransformer::ExecutionContext(-1, vp); + delete vp; + } + } + + return context; +} +*/ +ModelTransformer * +ModelTransformerFactory::createTransformer(const Transform &transform, + const ModelTransformer::Input &input) +{ + ModelTransformer *transformer = 0; + + QString id = transform.getPluginIdentifier(); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + transformer = + new FeatureExtractionModelTransformer(input, transform); + + } else if (RealTimePluginFactory::instanceFor(id)) { + + transformer = + new RealTimeEffectModelTransformer(input, transform); + + } else { + std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \"" + << transform.getIdentifier().toStdString() << "\"" << std::endl; + return transformer; + } + + if (transformer) transformer->setObjectName(transform.getIdentifier()); + return transformer; +} + +Model * +ModelTransformerFactory::transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message) +{ + ModelTransformer *t = createTransformer(transform, input); + if (!t) return 0; + + connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); + + m_runningTransformers.insert(t); + + t->start(); + Model *model = t->detachOutputModel(); + + if (model) { + QString imn = input.getModel()->objectName(); + QString trn = + TransformFactory::getInstance()->getTransformFriendlyName + (transform.getIdentifier()); + 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(); + } + + message = t->getMessage(); + + return model; +} + +void +ModelTransformerFactory::transformerFinished() +{ + QObject *s = sender(); + ModelTransformer *transformer = dynamic_cast(s); + + std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::endl; + + if (!transformer) { + std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl; + return; + } + + if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { + std::cerr << "WARNING: ModelTransformerFactory::transformerFinished(" + << transformer + << "): I have no record of this transformer running!" + << std::endl; + } + + m_runningTransformers.erase(transformer); + + transformer->wait(); // unnecessary but reassuring + delete transformer; +} + +void +ModelTransformerFactory::modelAboutToBeDeleted(Model *m) +{ + TransformerSet affected; + + for (TransformerSet::iterator i = m_runningTransformers.begin(); + i != m_runningTransformers.end(); ++i) { + + ModelTransformer *t = *i; + + if (t->getInputModel() == m || t->getOutputModel() == m) { + affected.insert(t); + } + } + + for (TransformerSet::iterator i = affected.begin(); + i != affected.end(); ++i) { + + ModelTransformer *t = *i; + + t->abandon(); + + t->wait(); // this should eventually call back on + // transformerFinished, which will remove from + // m_runningTransformers and delete. + } +} + diff -r 7aa1de571880 -r 370aa9714ef5 transform/ModelTransformerFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/ModelTransformerFactory.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,96 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _MODEL_TRANSFORMER_FACTORY_H_ +#define _MODEL_TRANSFORMER_FACTORY_H_ + +#include "Transform.h" +#include "TransformDescription.h" + +#include "ModelTransformer.h" + +#include +#include + +namespace Vamp { class PluginBase; } + +class AudioCallbackPlaySource; + +class ModelTransformerFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~ModelTransformerFactory(); + + static ModelTransformerFactory *getInstance(); + + /** + * Fill out the configuration for the given transform (by asking + * the user, most likely). Returns the selected input model and + * channel if the transform is acceptable, or an input with a null + * model if the operation should be cancelled. Audio callback + * play source may be used to audition effects plugins, if + * provided. + */ + ModelTransformer::Input + getConfigurationForTransform(Transform &transform, + const std::vector &candidateInputModels, + Model *defaultInputModel, + AudioCallbackPlaySource *source = 0, + size_t startFrame = 0, + size_t duration = 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. Set message if there is any error or + * warning to report. + * + * The returned model is owned by the caller and must be deleted + * when no longer needed. + */ + Model *transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message); + +protected slots: + void transformerFinished(); + + void modelAboutToBeDeleted(Model *); + +protected: + ModelTransformer *createTransformer(const Transform &transform, + const ModelTransformer::Input &input); + + typedef std::map TransformerConfigurationMap; + TransformerConfigurationMap m_lastConfigurations; + + typedef std::set TransformerSet; + TransformerSet m_runningTransformers; + + bool getChannelRange(TransformId identifier, + Vamp::PluginBase *plugin, int &min, int &max); + + static ModelTransformerFactory *m_instance; +}; + + +#endif diff -r 7aa1de571880 -r 370aa9714ef5 transform/RealTimeEffectModelTransformer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/RealTimeEffectModelTransformer.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,279 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "RealTimeEffectModelTransformer.h" + +#include "plugin/RealTimePluginFactory.h" +#include "plugin/RealTimePluginInstance.h" +#include "plugin/PluginXml.h" + +#include "data/model/Model.h" +#include "data/model/SparseTimeValueModel.h" +#include "data/model/DenseTimeValueModel.h" +#include "data/model/WritableWaveFileModel.h" +#include "data/model/WaveFileModel.h" + +#include "TransformFactory.h" + +#include + +RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in, + const Transform &transform) : + ModelTransformer(in, transform), + m_plugin(0) +{ + m_units = TransformFactory::getInstance()->getTransformUnits + (transform.getIdentifier()); + m_outputNo = + (transform.getOutput() == "A") ? -1 : transform.getOutput().toInt(); + + QString pluginId = transform.getPluginIdentifier(); + + if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024); + +// std::cerr << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl; + + RealTimePluginFactory *factory = + RealTimePluginFactory::instanceFor(pluginId); + + if (!factory) { + std::cerr << "RealTimeEffectModelTransformer: No factory available for plugin id \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + DenseTimeValueModel *input = getConformingInput(); + if (!input) return; + + m_plugin = factory->instantiatePlugin(pluginId, 0, 0, + input->getSampleRate(), + m_transform.getBlockSize(), + input->getChannelCount()); + + if (!m_plugin) { + std::cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" + << pluginId.toStdString() << "\"" << std::endl; + return; + } + + TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin); + + if (m_outputNo >= 0 && + m_outputNo >= int(m_plugin->getControlOutputCount())) { + std::cerr << "RealTimeEffectModelTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; + return; + } + + if (m_outputNo == -1) { + + size_t outputChannels = m_plugin->getAudioOutputCount(); + if (outputChannels > input->getChannelCount()) { + outputChannels = input->getChannelCount(); + } + + WritableWaveFileModel *model = new WritableWaveFileModel + (input->getSampleRate(), outputChannels); + + m_output = model; + + } else { + + SparseTimeValueModel *model = new SparseTimeValueModel + (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false); + + if (m_units != "") model->setScaleUnits(m_units); + + m_output = model; + } +} + +RealTimeEffectModelTransformer::~RealTimeEffectModelTransformer() +{ + delete m_plugin; +} + +DenseTimeValueModel * +RealTimeEffectModelTransformer::getConformingInput() +{ + DenseTimeValueModel *dtvm = + dynamic_cast(getInputModel()); + if (!dtvm) { + std::cerr << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + } + return dtvm; +} + +void +RealTimeEffectModelTransformer::run() +{ + DenseTimeValueModel *input = getConformingInput(); + if (!input) return; + + while (!input->isReady()) { + if (dynamic_cast(input)) break; // no need to wait + std::cerr << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << std::endl; + sleep(1); + } + + SparseTimeValueModel *stvm = dynamic_cast(m_output); + WritableWaveFileModel *wwfm = dynamic_cast(m_output); + if (!stvm && !wwfm) return; + + if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; + + size_t sampleRate = input->getSampleRate(); + size_t channelCount = input->getChannelCount(); + if (!wwfm && m_input.getChannel() != -1) channelCount = 1; + + long blockSize = m_plugin->getBufferSize(); + + float **inbufs = m_plugin->getAudioInputBuffers(); + + long startFrame = m_input.getModel()->getStartFrame(); + long endFrame = m_input.getModel()->getEndFrame(); + + RealTime contextStartRT = m_transform.getStartTime(); + RealTime contextDurationRT = m_transform.getDuration(); + + long contextStart = + RealTime::realTime2Frame(contextStartRT, sampleRate); + + long contextDuration = + RealTime::realTime2Frame(contextDurationRT, sampleRate); + + 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_input.getChannel(), 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 { + got = input->getData(0, channelCount - 1, + blockFrame, blockSize, + inbufs); + while (got < blockSize) { + for (size_t ch = 0; ch < channelCount; ++ch) { + inbufs[ch][got] = 0.0; + } + ++got; + } + 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 7aa1de571880 -r 370aa9714ef5 transform/RealTimeEffectModelTransformer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/RealTimeEffectModelTransformer.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,43 @@ +/* -*- 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 "ModelTransformer.h" +#include "plugin/RealTimePluginInstance.h" + +class DenseTimeValueModel; + +class RealTimeEffectModelTransformer : public ModelTransformer +{ +public: + RealTimeEffectModelTransformer(Input input, + const Transform &transform); + virtual ~RealTimeEffectModelTransformer(); + +protected: + virtual void run(); + + QString m_units; + RealTimePluginInstance *m_plugin; + int m_outputNo; + + // just casts + DenseTimeValueModel *getConformingInput(); +}; + +#endif + diff -r 7aa1de571880 -r 370aa9714ef5 transform/Transform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/Transform.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,426 @@ +/* -*- 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-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 + 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 "Transform.h" + +#include "plugin/PluginIdentifier.h" + +#include "plugin/FeatureExtractionPluginFactory.h" + +#include + +#include +#include +#include +#include + +#include + +#include + +Transform::Transform() : + m_stepSize(0), + m_blockSize(0), + m_windowType(HanningWindow), + m_sampleRate(0) +{ +} + +Transform::Transform(QString xml) : + m_stepSize(0), + m_blockSize(0), + m_windowType(HanningWindow), + m_sampleRate(0) +{ + QDomDocument doc; + + QString error; + int errorLine; + int errorColumn; + + if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { + std::cerr << "Transform::Transform: Error in parsing XML: " + << error.toStdString() << " at line " << errorLine + << ", column " << errorColumn << std::endl; + std::cerr << "Input follows:" << std::endl; + std::cerr << xml.toStdString() << std::endl; + std::cerr << "Input ends." << std::endl; + return; + } + + QDomElement transformElt = doc.firstChildElement("transform"); + QDomNamedNodeMap attrNodes = transformElt.attributes(); + QXmlAttributes attrs; + + for (unsigned int i = 0; i < attrNodes.length(); ++i) { + QDomAttr attr = attrNodes.item(i).toAttr(); + if (!attr.isNull()) attrs.append(attr.name(), "", "", attr.value()); + } + + setFromXmlAttributes(attrs); + + for (QDomElement paramElt = transformElt.firstChildElement("parameter"); + !paramElt.isNull(); + paramElt = paramElt.nextSiblingElement("parameter")) { + + QDomNamedNodeMap paramAttrs = paramElt.attributes(); + + QDomAttr nameAttr = paramAttrs.namedItem("name").toAttr(); + if (nameAttr.isNull() || nameAttr.value() == "") continue; + + QDomAttr valueAttr = paramAttrs.namedItem("value").toAttr(); + if (valueAttr.isNull() || valueAttr.value() == "") continue; + + setParameter(nameAttr.value(), valueAttr.value().toFloat()); + } + + for (QDomElement configElt = transformElt.firstChildElement("configuration"); + !configElt.isNull(); + configElt = configElt.nextSiblingElement("configuration")) { + + QDomNamedNodeMap configAttrs = configElt.attributes(); + + QDomAttr nameAttr = configAttrs.namedItem("name").toAttr(); + if (nameAttr.isNull() || nameAttr.value() == "") continue; + + QDomAttr valueAttr = configAttrs.namedItem("value").toAttr(); + if (valueAttr.isNull() || valueAttr.value() == "") continue; + + setConfigurationValue(nameAttr.value(), valueAttr.value()); + } +} + +Transform::~Transform() +{ +} + +bool +Transform::operator==(const Transform &t) +{ + return + m_id == t.m_id && + m_parameters == t.m_parameters && + m_configuration == t.m_configuration && + m_program == t.m_program && + m_stepSize == t.m_stepSize && + m_blockSize == t.m_blockSize && + m_windowType == t.m_windowType && + m_startTime == t.m_startTime && + m_duration == t.m_duration && + m_sampleRate == t.m_sampleRate; +} + +void +Transform::setIdentifier(TransformId id) +{ + m_id = id; +} + +TransformId +Transform::getIdentifier() const +{ + return m_id; +} + +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 +{ + if (FeatureExtractionPluginFactory::instanceFor(getPluginIdentifier())) { + return FeatureExtraction; + } else { + // We don't have an unknown/invalid return value, so always + // return this + return RealTimeEffect; + } +} + +QString +Transform::getPluginIdentifier() const +{ + return m_id.section(':', 0, 2); +} + +QString +Transform::getOutput() const +{ + return m_id.section(':', 3); +} + +void +Transform::setPluginIdentifier(QString pluginIdentifier) +{ + m_id = pluginIdentifier + ':' + getOutput(); +} + +void +Transform::setOutput(QString output) +{ + m_id = getPluginIdentifier() + ':' + output; +} + +TransformId +Transform::getIdentifierForPluginOutput(QString pluginIdentifier, + QString output) +{ + return pluginIdentifier + ':' + output; +} + +const Transform::ParameterMap & +Transform::getParameters() const +{ + return m_parameters; +} + +void +Transform::setParameters(const ParameterMap &pm) +{ + m_parameters = pm; +} + +void +Transform::setParameter(QString name, float value) +{ + std::cerr << "Transform::setParameter(" << name.toStdString() + << ") -> " << value << std::endl; + m_parameters[name] = value; +} + +const Transform::ConfigurationMap & +Transform::getConfiguration() const +{ + return m_configuration; +} + +void +Transform::setConfiguration(const ConfigurationMap &cm) +{ + m_configuration = cm; +} + +void +Transform::setConfigurationValue(QString name, QString value) +{ + std::cerr << "Transform::setConfigurationValue(" << name.toStdString() + << ") -> " << value.toStdString() << std::endl; + m_configuration[name] = value; +} + +QString +Transform::getPluginVersion() const +{ + return m_pluginVersion; +} + +void +Transform::setPluginVersion(QString version) +{ + m_pluginVersion = version; +} + +QString +Transform::getProgram() const +{ + return m_program; +} + +void +Transform::setProgram(QString program) +{ + m_program = program; +} + + +size_t +Transform::getStepSize() const +{ + return m_stepSize; +} + +void +Transform::setStepSize(size_t s) +{ + m_stepSize = s; +} + +size_t +Transform::getBlockSize() const +{ + return m_blockSize; +} + +void +Transform::setBlockSize(size_t s) +{ + m_blockSize = s; +} + +WindowType +Transform::getWindowType() const +{ + return m_windowType; +} + +void +Transform::setWindowType(WindowType type) +{ + m_windowType = type; +} + +RealTime +Transform::getStartTime() const +{ + return m_startTime; +} + +void +Transform::setStartTime(RealTime t) +{ + m_startTime = t; +} + +RealTime +Transform::getDuration() const +{ + return m_duration; +} + +void +Transform::setDuration(RealTime d) +{ + m_duration = d; +} + +float +Transform::getSampleRate() const +{ + return m_sampleRate; +} + +void +Transform::setSampleRate(float rate) +{ + m_sampleRate = rate; +} + +void +Transform::toXml(QTextStream &out, QString indent, QString extraAttributes) const +{ + out << indent; + + bool haveContent = true; + if (m_parameters.empty() && m_configuration.empty()) haveContent = false; + + out << QString("::getNameForType(m_windowType).c_str())) + .arg(encodeEntities(m_startTime.toString().c_str())) + .arg(encodeEntities(m_duration.toString().c_str())) + .arg(m_sampleRate); + + if (extraAttributes != "") { + out << " " << extraAttributes; + } + + if (haveContent) { + + out << ">\n"; + + for (ParameterMap::const_iterator i = m_parameters.begin(); + i != m_parameters.end(); ++i) { + out << indent << " " + << QString("\n") + .arg(encodeEntities(i->first)) + .arg(i->second); + } + + for (ConfigurationMap::const_iterator i = m_configuration.begin(); + i != m_configuration.end(); ++i) { + out << indent << " " + << QString("\n") + .arg(encodeEntities(i->first)) + .arg(encodeEntities(i->second)); + } + + out << indent << "\n"; + + } else { + + out << "/>\n"; + } +} + +void +Transform::setFromXmlAttributes(const QXmlAttributes &attrs) +{ + if (attrs.value("id") != "") { + setIdentifier(attrs.value("id")); + } + + if (attrs.value("pluginVersion") != "") { + setPluginVersion(attrs.value("pluginVersion")); + } + + if (attrs.value("program") != "") { + setProgram(attrs.value("program")); + } + + if (attrs.value("stepSize") != "") { + setStepSize(attrs.value("stepSize").toInt()); + } + + if (attrs.value("blockSize") != "") { + setBlockSize(attrs.value("blockSize").toInt()); + } + + if (attrs.value("windowType") != "") { + setWindowType(Window::getTypeForName + (attrs.value("windowType").toStdString())); + } + + if (attrs.value("startTime") != "") { + setStartTime(RealTime::fromString(attrs.value("startTime").toStdString())); + } + + if (attrs.value("duration") != "") { + setStartTime(RealTime::fromString(attrs.value("duration").toStdString())); + } + + if (attrs.value("sampleRate") != "") { + setSampleRate(attrs.value("sampleRate").toFloat()); + } +} + diff -r 7aa1de571880 -r 370aa9714ef5 transform/Transform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/Transform.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,155 @@ +/* -*- 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-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 + 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/XmlExportable.h" +#include "base/Window.h" +#include "base/RealTime.h" + +#include + +typedef QString TransformId; + +class QXmlAttributes; + +namespace Vamp { + class PluginBase; +} + +class Transform : public XmlExportable +{ +public: + /** + * Construct a new Transform with default data and no identifier. + * The Transform object will be meaningless until some data and an + * identifier have been set on it. + * + * To construct a Transform for use with a particular transform + * identifier, use TransformFactory::getDefaultTransformFor. + */ + Transform(); + + /** + * Construct a Transform by parsing the given XML data string. + * This is the inverse of toXml. + */ + Transform(QString xml); + + virtual ~Transform(); + + /** + * Compare two Transforms. They only compare equal if every data + * element matches. + */ + bool operator==(const Transform &); + + void setIdentifier(TransformId id); + TransformId getIdentifier() const; + + enum Type { FeatureExtraction, RealTimeEffect }; + + Type getType() const; + QString getPluginIdentifier() const; + QString getOutput() const; + + void setPluginIdentifier(QString pluginIdentifier); + void setOutput(QString output); + + // Turn a plugin ID and output name into a transform ID. Note + // that our pluginIdentifier is the same thing as the Vamp SDK's + // PluginLoader::PluginKey. + static TransformId getIdentifierForPluginOutput(QString pluginIdentifier, + QString output = ""); + + typedef std::map ParameterMap; + + const ParameterMap &getParameters() const; + void setParameters(const ParameterMap &pm); + void setParameter(QString name, float value); + + typedef std::map ConfigurationMap; + + const ConfigurationMap &getConfiguration() const; + void setConfiguration(const ConfigurationMap &cm); + void setConfigurationValue(QString name, QString value); + + QString getPluginVersion() const; + void setPluginVersion(QString version); + + QString getProgram() const; + void setProgram(QString program); + + size_t getStepSize() const; + void setStepSize(size_t s); + + size_t getBlockSize() const; + void setBlockSize(size_t s); + + WindowType getWindowType() const; + void setWindowType(WindowType type); + + RealTime getStartTime() const; + void setStartTime(RealTime t); + + RealTime getDuration() const; // 0 -> all + void setDuration(RealTime d); + + float getSampleRate() const; // 0 -> as input + void setSampleRate(float rate); + + void toXml(QTextStream &stream, QString indent = "", + QString extraAttributes = "") const; + + /** + * Set the main transform data from the given XML attributes. + * This does not set the parameters or configuration, which are + * exported to separate XML elements rather than attributes of the + * transform element. + * + * Note that this only sets those attributes which are actually + * present in the argument. Any attributes not defined in the + * attribute will remain unchanged in the Transform. If your aim + * is to create a transform exactly matching the given attributes, + * ensure you start from an empty transform rather than one that + * has already been configured. + */ + void setFromXmlAttributes(const QXmlAttributes &); + +protected: + TransformId m_id; // pluginid:output, that is type:soname:label:output + + static QString createIdentifier + (QString type, QString soName, QString label, QString output); + + static void parseIdentifier + (QString identifier, + QString &type, QString &soName, QString &label, QString &output); + + ParameterMap m_parameters; + ConfigurationMap m_configuration; + QString m_pluginVersion; + QString m_program; + size_t m_stepSize; + size_t m_blockSize; + WindowType m_windowType; + RealTime m_startTime; + RealTime m_duration; + float m_sampleRate; +}; + +#endif + diff -r 7aa1de571880 -r 370aa9714ef5 transform/TransformDescription.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/TransformDescription.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,77 @@ +/* -*- 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-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 + 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_DESCRIPTION_H_ +#define _TRANSFORM_DESCRIPTION_H_ + +#include "Transform.h" + +#include + +#include + +/** + * Metadata associated with a transform. + * + * The transform ID is the same as that used in the Transform class. + * It 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 that creates these objects + * should 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. + * + * To obtain these objects, use + * TransformFactory::getAllTransformDescriptions and + * TransformFactory::getTransformDescription. + */ + +struct TransformDescription +{ + TransformDescription() { } + TransformDescription(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 TransformDescription &od) const { + return + (name < od.name) || + (name == od.name && identifier < od.identifier); + }; +}; + +typedef std::vector TransformList; + +#endif diff -r 7aa1de571880 -r 370aa9714ef5 transform/TransformFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/TransformFactory.cpp Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,776 @@ +/* -*- 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 "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/RealTimePluginFactory.h" +#include "plugin/RealTimePluginInstance.h" +#include "plugin/PluginXml.h" + +#include "vamp-sdk/Plugin.h" +#include "vamp-sdk/PluginHostAdapter.h" +#include "vamp-sdk/hostext/PluginWrapper.h" + +#include +#include + +#include +#include + +TransformFactory * +TransformFactory::m_instance = new TransformFactory; + +TransformFactory * +TransformFactory::getInstance() +{ + return m_instance; +} + +TransformFactory::~TransformFactory() +{ +} + +TransformList +TransformFactory::getAllTransformDescriptions() +{ + if (m_transforms.empty()) populateTransforms(); + + std::set dset; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { +// std::cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << std::endl; + dset.insert(i->second); + } + + TransformList list; + for (std::set::const_iterator i = dset.begin(); + i != dset.end(); ++i) { +// std::cerr << "inserting transform into list: id = " << i->identifier.toStdString() << std::endl; + list.push_back(*i); + } + + return list; +} + +TransformDescription +TransformFactory::getTransformDescription(TransformId id) +{ + if (m_transforms.empty()) populateTransforms(); + + if (m_transforms.find(id) == m_transforms.end()) { + return TransformDescription(); + } + + return m_transforms[id]; +} + +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) { + + TransformDescription desc = i->second; + + QString td = desc.name; + QString tn = td.section(": ", 0, 0); + QString pn = desc.identifier.section(":", 1, 1); + + if (pluginSources.find(tn) != pluginSources.end()) { + if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) { + ++names[tn]; + } + } else { + ++names[tn]; + pluginSources[tn] = pn; + pluginMakers[tn] = desc.maker; + } + } + + std::map counts; + m_transforms.clear(); + + for (TransformDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + TransformDescription desc = i->second; + QString identifier = desc.identifier; + QString maker = desc.maker; + + QString td = desc.name; + QString tn = td.section(": ", 0, 0); + QString to = td.section(": ", 1); + + if (names[tn] > 1) { + maker.replace(QRegExp(tr(" [\\(<].*$")), ""); + tn = QString("%1 [%2]").arg(tn).arg(maker); + } + + if (to != "") { + desc.name = QString("%1: %2").arg(tn).arg(to); + } else { + desc.name = tn; + } + + m_transforms[identifier] = desc; + } +} + +void +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, 44100); + + 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() << " friendly name: " << friendlyName.toStdString() << std::endl; + + transforms[transformId] = + TransformDescription(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] = + TransformDescription(tr("Effects Data"), + category, + transformId, + userName, + userName, + description, + maker, + units, + configurable); + } + } + + if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { + + if (descriptor->audioOutputPortCount > 0) { + + QString transformId = QString("%1:A").arg(pluginId); + QString type = tr("Effects"); + + QString description = tr("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] = + TransformDescription(type, + category, + transformId, + pluginName, + pluginName, + description, + maker, + "", + configurable); + } + } + } +} + + +Transform +TransformFactory::getDefaultTransformFor(TransformId id, size_t rate) +{ + Transform t; + t.setIdentifier(id); + if (rate != 0) t.setSampleRate(rate); + + Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate); + + if (plugin) { + t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion())); + setParametersFromPlugin(t, plugin); + makeContextConsistentWithPlugin(t, plugin); + delete plugin; + } + + return t; +} + +Vamp::PluginBase * +TransformFactory::instantiatePluginFor(const Transform &transform) +{ + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (transform.getIdentifier(), transform.getSampleRate()); + if (plugin) { + setPluginParameters(transform, plugin); + } + return plugin; +} + +Vamp::PluginBase * +TransformFactory::instantiateDefaultPluginFor(TransformId identifier, size_t rate) +{ + Transform t; + t.setIdentifier(identifier); + if (rate == 0) rate = 44100; + QString pluginId = t.getPluginIdentifier(); + + Vamp::PluginBase *plugin = 0; + + if (t.getType() == Transform::FeatureExtraction) { + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + plugin = factory->instantiatePlugin(pluginId, rate); + + } else { + + RealTimePluginFactory *factory = + RealTimePluginFactory::instanceFor(pluginId); + + plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1); + } + + return plugin; +} + +Vamp::Plugin * +TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin) +{ + 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; + } + return vp; +} + +bool +TransformFactory::haveTransform(TransformId identifier) +{ + if (m_transforms.empty()) populateTransforms(); + 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 ""; +} + +Vamp::Plugin::InputDomain +TransformFactory::getTransformInputDomain(TransformId identifier) +{ + Transform transform; + transform.setIdentifier(identifier); + + if (transform.getType() != Transform::FeatureExtraction) { + return Vamp::Plugin::TimeDomain; + } + + Vamp::Plugin *plugin = + downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0)); + + if (plugin) { + Vamp::Plugin::InputDomain d = plugin->getInputDomain(); + delete plugin; + return d; + } + + return Vamp::Plugin::TimeDomain; +} + +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, 44100); + if (!plugin) return false; + + min = plugin->getMinChannelCount(); + max = plugin->getMaxChannelCount(); + delete plugin; + + return true; + + } else if (RealTimePluginFactory::instanceFor(id)) { + + // don't need to instantiate + + const RealTimePluginDescriptor *descriptor = + RealTimePluginFactory::instanceFor(id)-> + getPluginDescriptor(id); + if (!descriptor) return false; + + min = descriptor->audioInputPortCount; + max = descriptor->audioInputPortCount; + + return true; + } + + return false; +} + +void +TransformFactory::setParametersFromPlugin(Transform &transform, + Vamp::PluginBase *plugin) +{ + Transform::ParameterMap pmap; + + //!!! record plugin & API version + + //!!! check that this is the right plugin! + + Vamp::PluginBase::ParameterList parameters = + plugin->getParameterDescriptors(); + + for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); + i != parameters.end(); ++i) { + pmap[i->identifier.c_str()] = plugin->getParameter(i->identifier); + } + + transform.setParameters(pmap); + + if (plugin->getPrograms().empty()) { + transform.setProgram(""); + } else { + transform.setProgram(plugin->getCurrentProgram().c_str()); + } + + RealTimePluginInstance *rtpi = + dynamic_cast(plugin); + + Transform::ConfigurationMap cmap; + + if (rtpi) { + + RealTimePluginInstance::ConfigurationPairMap configurePairs = + rtpi->getConfigurePairs(); + + for (RealTimePluginInstance::ConfigurationPairMap::const_iterator i + = configurePairs.begin(); i != configurePairs.end(); ++i) { + cmap[i->first.c_str()] = i->second.c_str(); + } + } + + transform.setConfiguration(cmap); +} + +void +TransformFactory::setPluginParameters(const Transform &transform, + Vamp::PluginBase *plugin) +{ + //!!! check plugin & API version (see e.g. PluginXml::setParameters) + + //!!! check that this is the right plugin! + + RealTimePluginInstance *rtpi = + dynamic_cast(plugin); + + if (rtpi) { + const Transform::ConfigurationMap &cmap = transform.getConfiguration(); + for (Transform::ConfigurationMap::const_iterator i = cmap.begin(); + i != cmap.end(); ++i) { + rtpi->configure(i->first.toStdString(), i->second.toStdString()); + } + } + + if (transform.getProgram() != "") { + plugin->selectProgram(transform.getProgram().toStdString()); + } + + const Transform::ParameterMap &pmap = transform.getParameters(); + + Vamp::PluginBase::ParameterList parameters = + plugin->getParameterDescriptors(); + + for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); + i != parameters.end(); ++i) { + QString key = i->identifier.c_str(); + Transform::ParameterMap::const_iterator pmi = pmap.find(key); + if (pmi != pmap.end()) { + plugin->setParameter(i->identifier, pmi->second); + } + } +} + +void +TransformFactory::makeContextConsistentWithPlugin(Transform &transform, + Vamp::PluginBase *plugin) +{ + const Vamp::Plugin *vp = downcastVampPlugin(plugin); + + if (!vp) { + // time domain input for real-time effects plugin + if (!transform.getBlockSize()) { + if (!transform.getStepSize()) transform.setStepSize(1024); + transform.setBlockSize(transform.getStepSize()); + } else { + transform.setStepSize(transform.getBlockSize()); + } + } else { + Vamp::Plugin::InputDomain domain = vp->getInputDomain(); + if (!transform.getStepSize()) { + transform.setStepSize(vp->getPreferredStepSize()); + } + if (!transform.getBlockSize()) { + transform.setBlockSize(vp->getPreferredBlockSize()); + } + if (!transform.getBlockSize()) { + transform.setBlockSize(1024); + } + if (!transform.getStepSize()) { + if (domain == Vamp::Plugin::FrequencyDomain) { +// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; + transform.setStepSize(transform.getBlockSize()/2); + } else { +// std::cerr << "time domain, step = " << blockSize/2 << std::endl; + transform.setStepSize(transform.getBlockSize()); + } + } + } +} + +QString +TransformFactory::getPluginConfigurationXml(const Transform &t) +{ + QString xml; + + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (t.getIdentifier(), 0); + if (!plugin) { + std::cerr << "TransformFactory::getPluginConfigurationXml: " + << "Unable to instantiate plugin for transform \"" + << t.getIdentifier().toStdString() << "\"" << std::endl; + return xml; + } + + setPluginParameters(t, plugin); + + QTextStream out(&xml); + PluginXml(plugin).toXml(out); + delete plugin; + + return xml; +} + +void +TransformFactory::setParametersFromPluginConfigurationXml(Transform &t, + QString xml) +{ + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (t.getIdentifier(), 0); + if (!plugin) { + std::cerr << "TransformFactory::setParametersFromPluginConfigurationXml: " + << "Unable to instantiate plugin for transform \"" + << t.getIdentifier().toStdString() << "\"" << std::endl; + return; + } + + PluginXml(plugin).setParametersFromXml(xml); + setParametersFromPlugin(t, plugin); + delete plugin; +} + diff -r 7aa1de571880 -r 370aa9714ef5 transform/TransformFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/transform/TransformFactory.h Wed Mar 12 18:02:17 2008 +0000 @@ -0,0 +1,174 @@ +/* -*- 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-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 + 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 "TransformDescription.h" + +#include + +#include + +#include +#include + +class TransformFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~TransformFactory(); + + static TransformFactory *getInstance(); + + TransformList getAllTransformDescriptions(); + TransformDescription getTransformDescription(TransformId id); + + std::vector getAllTransformTypes(); + std::vector getTransformCategories(QString transformType); + std::vector getTransformMakers(QString transformType); + + /** + * Return true if the given transform is known. + */ + bool haveTransform(TransformId identifier); + + /** + * A single transform ID can lead to many possible Transforms, + * with different parameters and execution context settings. + * Return the default one for the given transform. + */ + Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0); + + /** + * 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); + + Vamp::Plugin::InputDomain getTransformInputDomain(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); + + /** + * Load an appropriate plugin for the given transform and set the + * parameters, program and configuration strings on that plugin + * from the Transform object. + * + * Note that this requires that the transform has a meaningful + * sample rate set, as that is used as the rate for the plugin. A + * Transform can legitimately have rate set at zero (= "use the + * rate of the input source"), so the caller will need to test for + * this case. + * + * Returns the plugin thus loaded. This will be a + * Vamp::PluginBase, but not necessarily a Vamp::Plugin (only if + * the transform was a feature-extraction type -- call + * downcastVampPlugin if you only want Vamp::Plugins). Returns + * NULL if no suitable plugin was available. + * + * The returned plugin is owned by the caller, and should be + * deleted (using "delete") when no longer needed. + */ + Vamp::PluginBase *instantiatePluginFor(const Transform &transform); + + /** + * Convert a Vamp::PluginBase to a Vamp::Plugin, if it is one. + * Return NULL otherwise. This ill-fitting convenience function + * is really just a dynamic_cast wrapper. + */ + Vamp::Plugin *downcastVampPlugin(Vamp::PluginBase *); + + /** + * Set the plugin parameters, program and configuration strings on + * the given Transform object from the given plugin instance. + * Note that no check is made whether the plugin is actually the + * "correct" one for the transform. + */ + void setParametersFromPlugin(Transform &transform, Vamp::PluginBase *plugin); + + /** + * Set the parameters, program and configuration strings on the + * given plugin from the given Transform object. + */ + void setPluginParameters(const Transform &transform, Vamp::PluginBase *plugin); + + /** + * If the given Transform object has no processing step and block + * sizes set, set them to appropriate defaults for the given + * plugin. + */ + void makeContextConsistentWithPlugin(Transform &transform, Vamp::PluginBase *plugin); + + /** + * Retrieve a XML fragment that describes the + * plugin parameters, program and configuration data for the given + * transform. + * + * This function is provided for backward compatibility only. Use + * Transform::toXml where compatibility with PluginXml + * descriptions of transforms is not required. + */ + QString getPluginConfigurationXml(const Transform &transform); + + /** + * Set the plugin parameters, program and configuration strings on + * the given Transform object from the given XML + * fragment. + * + * This function is provided for backward compatibility only. Use + * Transform(QString) where compatibility with PluginXml + * descriptions of transforms is not required. + */ + void setParametersFromPluginConfigurationXml(Transform &transform, + QString xml); + +protected: + typedef std::map TransformDescriptionMap; + TransformDescriptionMap m_transforms; + + void populateTransforms(); + void populateFeatureExtractionPlugins(TransformDescriptionMap &); + void populateRealTimePlugins(TransformDescriptionMap &); + + Vamp::PluginBase *instantiateDefaultPluginFor(TransformId id, size_t rate); + + static TransformFactory *m_instance; +}; + + +#endif