# HG changeset patch # User Chris Cannam # Date 1194369300 0 # Node ID 6e9dcf09b7fe8a41fcdbebaa352621d31a9206dd # Parent 3179d8b29336e575ab1790df408574deb366d8e6 * Another incremental update (this class not yet used) diff -r 3179d8b29336 -r 6e9dcf09b7fe plugin/plugin.pro --- a/plugin/plugin.pro Tue Nov 06 17:08:11 2007 +0000 +++ b/plugin/plugin.pro Tue Nov 06 17:15:00 2007 +0000 @@ -39,6 +39,8 @@ transform/PluginTransformer.h \ transform/RealTimePluginTransformer.h \ transform/Transform.h \ + transform/TransformDescription.h \ + transform/TransformFactory.h \ transform/Transformer.h \ transform/TransformerFactory.h SOURCES += DSSIPluginFactory.cpp \ @@ -56,5 +58,6 @@ transform/PluginTransformer.cpp \ transform/RealTimePluginTransformer.cpp \ transform/Transform.cpp \ + transform/TransformFactory.cpp \ transform/Transformer.cpp \ transform/TransformerFactory.cpp diff -r 3179d8b29336 -r 6e9dcf09b7fe plugin/transform/TransformFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/TransformFactory.cpp Tue Nov 06 17:15:00 2007 +0000 @@ -0,0 +1,494 @@ +/* -*- 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/PluginXml.h" + +#include "vamp-sdk/PluginHostAdapter.h" + +#include +#include + +#include + +TransformFactory * +TransformFactory::m_instance = new TransformFactory; + +TransformFactory * +TransformFactory::getInstance() +{ + return m_instance; +} + +TransformFactory::~TransformFactory() +{ +} + +TransformList +TransformFactory::getAllTransforms() +{ + if (m_transforms.empty()) populateTransforms(); + + std::set dset; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + dset.insert(i->second); + } + + TransformList list; + for (std::set::const_iterator i = dset.begin(); + i != dset.end(); ++i) { + list.push_back(*i); + } + + return list; +} + +std::vector +TransformFactory::getAllTransformTypes() +{ + if (m_transforms.empty()) populateTransforms(); + + std::set types; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + types.insert(i->second.type); + } + + std::vector rv; + for (std::set::iterator i = types.begin(); i != types.end(); ++i) { + rv.push_back(*i); + } + + return rv; +} + +std::vector +TransformFactory::getTransformCategories(QString transformType) +{ + if (m_transforms.empty()) populateTransforms(); + + std::set categories; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + if (i->second.type == transformType) { + categories.insert(i->second.category); + } + } + + bool haveEmpty = false; + + std::vector rv; + for (std::set::iterator i = categories.begin(); + i != categories.end(); ++i) { + if (*i != "") rv.push_back(*i); + else haveEmpty = true; + } + + if (haveEmpty) rv.push_back(""); // make sure empty category sorts last + + return rv; +} + +std::vector +TransformFactory::getTransformMakers(QString transformType) +{ + if (m_transforms.empty()) populateTransforms(); + + std::set makers; + for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + if (i->second.type == transformType) { + makers.insert(i->second.maker); + } + } + + bool haveEmpty = false; + + std::vector rv; + for (std::set::iterator i = makers.begin(); + i != makers.end(); ++i) { + if (*i != "") rv.push_back(*i); + else haveEmpty = true; + } + + if (haveEmpty) rv.push_back(""); // make sure empty category sorts last + + return rv; +} + +void +TransformFactory::populateTransforms() +{ + TransformDescriptionMap transforms; + + populateFeatureExtractionPlugins(transforms); + populateRealTimePlugins(transforms); + + // disambiguate plugins with similar names + + std::map names; + std::map pluginSources; + std::map pluginMakers; + + for (TransformDescriptionMap::iterator i = transforms.begin(); + i != transforms.end(); ++i) { + + 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, 48000); + + if (!plugin) { + std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; + continue; + } + + QString pluginName = plugin->getName().c_str(); + QString category = factory->getPluginCategory(pluginId); + + Vamp::Plugin::OutputList outputs = + plugin->getOutputDescriptors(); + + for (size_t j = 0; j < outputs.size(); ++j) { + + QString transformId = QString("%1:%2") + .arg(pluginId).arg(outputs[j].identifier.c_str()); + + QString userName; + QString friendlyName; + QString units = outputs[j].unit.c_str(); + QString description = plugin->getDescription().c_str(); + QString maker = plugin->getMaker().c_str(); + if (maker == "") maker = tr(""); + + if (description == "") { + if (outputs.size() == 1) { + description = tr("Extract features using \"%1\" plugin (from %2)") + .arg(pluginName).arg(maker); + } else { + description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)") + .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); + } + } else { + if (outputs.size() == 1) { + description = tr("%1 using \"%2\" plugin (from %3)") + .arg(description).arg(pluginName).arg(maker); + } else { + description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)") + .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); + } + } + + if (outputs.size() == 1) { + userName = pluginName; + friendlyName = pluginName; + } else { + userName = QString("%1: %2") + .arg(pluginName) + .arg(outputs[j].name.c_str()); + friendlyName = outputs[j].name.c_str(); + } + + bool configurable = (!plugin->getPrograms().empty() || + !plugin->getParameterDescriptors().empty()); + +// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl; + + transforms[transformId] = + 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); + } + } + } +} + +bool +TransformFactory::haveTransform(TransformId identifier) +{ + return (m_transforms.find(identifier) != m_transforms.end()); +} + +QString +TransformFactory::getTransformName(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].name; + } else return ""; +} + +QString +TransformFactory::getTransformFriendlyName(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].friendlyName; + } else return ""; +} + +QString +TransformFactory::getTransformUnits(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].units; + } else return ""; +} + +bool +TransformFactory::isTransformConfigurable(TransformId identifier) +{ + if (m_transforms.find(identifier) != m_transforms.end()) { + return m_transforms[identifier].configurable; + } else return false; +} + +bool +TransformFactory::getTransformChannelRange(TransformId identifier, + int &min, int &max) +{ + QString id = identifier.section(':', 0, 2); + + if (FeatureExtractionPluginFactory::instanceFor(id)) { + + Vamp::Plugin *plugin = + FeatureExtractionPluginFactory::instanceFor(id)-> + instantiatePlugin(id, 48000); + if (!plugin) return false; + + min = plugin->getMinChannelCount(); + max = plugin->getMaxChannelCount(); + delete plugin; + + return true; + + } else if (RealTimePluginFactory::instanceFor(id)) { + + const RealTimePluginDescriptor *descriptor = + RealTimePluginFactory::instanceFor(id)-> + getPluginDescriptor(id); + if (!descriptor) return false; + + min = descriptor->audioInputPortCount; + max = descriptor->audioInputPortCount; + + return true; + } + + return false; +} diff -r 3179d8b29336 -r 6e9dcf09b7fe plugin/transform/TransformFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/transform/TransformFactory.h Tue Nov 06 17:15:00 2007 +0000 @@ -0,0 +1,88 @@ +/* -*- 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 + +namespace Vamp { class PluginBase; } + +class TransformFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~TransformFactory(); + + static TransformFactory *getInstance(); + + TransformList getAllTransforms(); + + 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); + + /** + * Full name of a transform, suitable for putting on a menu. + */ + QString getTransformName(TransformId identifier); + + /** + * Brief but friendly name of a transform, suitable for use + * as the name of the output layer. + */ + QString getTransformFriendlyName(TransformId identifier); + + QString getTransformUnits(TransformId identifier); + + /** + * Return true if the transform has any configurable parameters, + * i.e. if getConfigurationForTransform can ever return a non-trivial + * (not equivalent to empty) configuration string. + */ + bool isTransformConfigurable(TransformId identifier); + + /** + * If the transform has a prescribed number or range of channel + * inputs, return true and set minChannels and maxChannels to the + * minimum and maximum number of channel inputs the transform can + * accept. Return false if it doesn't care. + */ + bool getTransformChannelRange(TransformId identifier, + int &minChannels, int &maxChannels); + +protected: + typedef std::map TransformDescriptionMap; + TransformDescriptionMap m_transforms; + + void populateTransforms(); + void populateFeatureExtractionPlugins(TransformDescriptionMap &); + void populateRealTimePlugins(TransformDescriptionMap &); + + static TransformFactory *m_instance; +}; + + +#endif