Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@52: Sonic Visualiser Chris@52: An audio file viewer and annotation editor. Chris@52: Centre for Digital Music, Queen Mary, University of London. Chris@52: This file copyright 2006 Chris Cannam. Chris@0: Chris@52: This program is free software; you can redistribute it and/or Chris@52: modify it under the terms of the GNU General Public License as Chris@52: published by the Free Software Foundation; either version 2 of the Chris@52: License, or (at your option) any later version. See the file Chris@52: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #include "TransformFactory.h" Chris@0: Chris@0: #include "FeatureExtractionPluginTransform.h" Chris@60: #include "RealTimePluginTransform.h" Chris@0: Chris@0: #include "plugin/FeatureExtractionPluginFactory.h" Chris@60: #include "plugin/RealTimePluginFactory.h" Chris@66: #include "plugin/PluginXml.h" Chris@0: Chris@56: #include "widgets/PluginParameterDialog.h" Chris@56: Chris@64: #include "model/DenseTimeValueModel.h" Chris@64: Chris@0: #include Chris@63: #include Chris@63: Chris@63: #include Chris@0: Chris@0: TransformFactory * Chris@0: TransformFactory::m_instance = new TransformFactory; Chris@0: Chris@0: TransformFactory * Chris@0: TransformFactory::instance() Chris@0: { Chris@0: return m_instance; Chris@0: } Chris@0: Chris@0: TransformFactory::~TransformFactory() Chris@0: { Chris@0: } Chris@0: Chris@0: TransformFactory::TransformList Chris@0: TransformFactory::getAllTransforms() Chris@0: { Chris@16: if (m_transforms.empty()) populateTransforms(); Chris@16: Chris@0: TransformList list; Chris@56: for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); Chris@16: i != m_transforms.end(); ++i) { Chris@56: list.push_back(i->second); Chris@16: } Chris@0: Chris@16: return list; Chris@16: } Chris@16: Chris@63: std::vector Chris@63: TransformFactory::getAllTransformTypes() Chris@63: { Chris@63: if (m_transforms.empty()) populateTransforms(); Chris@63: Chris@63: std::set types; Chris@63: for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); Chris@63: i != m_transforms.end(); ++i) { Chris@63: types.insert(i->second.type); Chris@63: } Chris@63: Chris@63: std::vector rv; Chris@63: for (std::set::iterator i = types.begin(); i != types.end(); ++i) { Chris@63: rv.push_back(*i); Chris@63: } Chris@63: Chris@63: return rv; Chris@63: } Chris@63: Chris@16: void Chris@16: TransformFactory::populateTransforms() Chris@16: { Chris@60: TransformDescriptionMap transforms; Chris@60: Chris@60: populateFeatureExtractionPlugins(transforms); Chris@60: populateRealTimePlugins(transforms); Chris@60: Chris@60: // disambiguate plugins with similar descriptions Chris@60: Chris@60: std::map descriptions; Chris@60: Chris@60: for (TransformDescriptionMap::iterator i = transforms.begin(); Chris@60: i != transforms.end(); ++i) { Chris@60: Chris@60: TransformDesc desc = i->second; Chris@60: Chris@60: ++descriptions[desc.description]; Chris@60: ++descriptions[QString("%1 [%2]").arg(desc.description).arg(desc.maker)]; Chris@60: } Chris@60: Chris@60: std::map counts; Chris@60: m_transforms.clear(); Chris@60: Chris@60: for (TransformDescriptionMap::iterator i = transforms.begin(); Chris@60: i != transforms.end(); ++i) { Chris@60: Chris@60: TransformDesc desc = i->second; Chris@60: QString name = desc.name; Chris@60: QString description = desc.description; Chris@60: QString maker = desc.maker; Chris@60: Chris@60: if (descriptions[description] > 1) { Chris@60: description = QString("%1 [%2]").arg(description).arg(maker); Chris@60: if (descriptions[description] > 1) { Chris@60: description = QString("%1 <%2>") Chris@60: .arg(description).arg(++counts[description]); Chris@60: } Chris@60: } Chris@60: Chris@60: desc.description = description; Chris@60: m_transforms[name] = desc; Chris@60: } Chris@60: } Chris@60: Chris@60: void Chris@60: TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) Chris@60: { Chris@60: std::vector plugs = Chris@0: FeatureExtractionPluginFactory::getAllPluginIdentifiers(); Chris@0: Chris@60: for (size_t i = 0; i < plugs.size(); ++i) { Chris@47: Chris@60: QString pluginId = plugs[i]; Chris@0: Chris@0: FeatureExtractionPluginFactory *factory = Chris@0: FeatureExtractionPluginFactory::instanceFor(pluginId); Chris@0: Chris@20: if (!factory) { Chris@20: std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; Chris@20: continue; Chris@20: } Chris@0: Chris@66: Vamp::Plugin *plugin = Chris@20: factory->instantiatePlugin(pluginId, 48000); Chris@0: Chris@20: if (!plugin) { Chris@20: std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; Chris@20: continue; Chris@20: } Chris@20: Chris@20: QString pluginDescription = plugin->getDescription().c_str(); Chris@66: Vamp::Plugin::OutputList outputs = Chris@20: plugin->getOutputDescriptors(); Chris@0: Chris@20: for (size_t j = 0; j < outputs.size(); ++j) { Chris@0: Chris@20: QString transformName = QString("%1:%2") Chris@20: .arg(pluginId).arg(outputs[j].name.c_str()); Chris@20: Chris@20: QString userDescription; Chris@60: QString friendlyName; Chris@63: QString units = outputs[j].unit.c_str(); Chris@20: Chris@20: if (outputs.size() == 1) { Chris@20: userDescription = pluginDescription; Chris@60: friendlyName = pluginDescription; Chris@20: } else { Chris@20: userDescription = QString("%1: %2") Chris@20: .arg(pluginDescription) Chris@20: .arg(outputs[j].description.c_str()); Chris@60: friendlyName = outputs[j].description.c_str(); Chris@0: } Chris@20: Chris@56: bool configurable = (!plugin->getPrograms().empty() || Chris@56: !plugin->getParameterDescriptors().empty()); Chris@56: Chris@60: transforms[transformName] = Chris@63: TransformDesc(tr("Analysis Plugin"), Chris@63: transformName, Chris@56: userDescription, Chris@60: friendlyName, Chris@60: plugin->getMaker().c_str(), Chris@63: units, Chris@56: configurable); Chris@0: } Chris@0: } Chris@60: } Chris@47: Chris@60: void Chris@60: TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms) Chris@60: { Chris@60: std::vector plugs = Chris@60: RealTimePluginFactory::getAllPluginIdentifiers(); Chris@47: Chris@63: QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); Chris@63: Chris@60: for (size_t i = 0; i < plugs.size(); ++i) { Chris@60: Chris@60: QString pluginId = plugs[i]; Chris@47: Chris@60: RealTimePluginFactory *factory = Chris@60: RealTimePluginFactory::instanceFor(pluginId); Chris@47: Chris@60: if (!factory) { Chris@60: std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; Chris@60: continue; Chris@47: } Chris@47: Chris@60: const RealTimePluginDescriptor *descriptor = Chris@60: factory->getPluginDescriptor(pluginId); Chris@60: Chris@60: if (!descriptor) { Chris@60: std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl; Chris@60: continue; Chris@60: } Chris@60: Chris@60: if (descriptor->controlOutputPortCount == 0 || Chris@60: descriptor->audioInputPortCount == 0) continue; Chris@60: Chris@60: std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl; Chris@60: Chris@60: QString pluginDescription = descriptor->name.c_str(); Chris@60: Chris@60: for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { Chris@60: Chris@60: QString transformName = QString("%1:%2").arg(pluginId).arg(j); Chris@60: QString userDescription; Chris@63: QString units; Chris@60: Chris@60: if (j < descriptor->controlOutputPortNames.size() && Chris@60: descriptor->controlOutputPortNames[j] != "") { Chris@63: Chris@63: QString portName = descriptor->controlOutputPortNames[j].c_str(); Chris@63: Chris@60: userDescription = tr("%1: %2") Chris@60: .arg(pluginDescription) Chris@63: .arg(portName); Chris@63: Chris@63: if (unitRE.indexIn(portName) >= 0) { Chris@63: units = unitRE.cap(1); Chris@63: } Chris@63: Chris@60: } else if (descriptor->controlOutputPortCount > 1) { Chris@63: Chris@60: userDescription = tr("%1: Output %2") Chris@60: .arg(pluginDescription) Chris@60: .arg(j + 1); Chris@63: Chris@60: } else { Chris@63: Chris@60: userDescription = pluginDescription; Chris@60: } Chris@60: Chris@63: Chris@60: bool configurable = (descriptor->parameterCount > 0); Chris@60: Chris@60: transforms[transformName] = Chris@63: TransformDesc(tr("Real-Time Plugin"), Chris@63: transformName, Chris@60: userDescription, Chris@60: userDescription, Chris@60: descriptor->maker.c_str(), Chris@63: units, Chris@60: configurable); Chris@60: } Chris@60: } Chris@16: } Chris@16: Chris@16: QString Chris@16: TransformFactory::getTransformDescription(TransformName name) Chris@16: { Chris@16: if (m_transforms.find(name) != m_transforms.end()) { Chris@56: return m_transforms[name].description; Chris@16: } else return ""; Chris@0: } Chris@0: Chris@18: QString Chris@18: TransformFactory::getTransformFriendlyName(TransformName name) Chris@18: { Chris@60: if (m_transforms.find(name) != m_transforms.end()) { Chris@60: return m_transforms[name].friendlyName; Chris@60: } else return ""; Chris@18: } Chris@18: Chris@63: QString Chris@63: TransformFactory::getTransformUnits(TransformName name) Chris@63: { Chris@63: if (m_transforms.find(name) != m_transforms.end()) { Chris@63: return m_transforms[name].units; Chris@63: } else return ""; Chris@63: } Chris@63: Chris@56: bool Chris@57: TransformFactory::isTransformConfigurable(TransformName name) Chris@57: { Chris@57: if (m_transforms.find(name) != m_transforms.end()) { Chris@57: return m_transforms[name].configurable; Chris@57: } else return false; Chris@57: } Chris@57: Chris@57: bool Chris@64: TransformFactory::getTransformChannelRange(TransformName name, Chris@64: int &min, int &max) Chris@64: { Chris@64: QString id = name.section(':', 0, 2); Chris@64: Chris@64: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@64: Chris@66: Vamp::Plugin *plugin = Chris@64: FeatureExtractionPluginFactory::instanceFor(id)-> Chris@64: instantiatePlugin(id, 48000); Chris@64: if (!plugin) return false; Chris@64: Chris@64: min = plugin->getMinChannelCount(); Chris@64: max = plugin->getMaxChannelCount(); Chris@64: delete plugin; Chris@64: Chris@64: return true; Chris@64: Chris@64: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@64: Chris@64: const RealTimePluginDescriptor *descriptor = Chris@64: RealTimePluginFactory::instanceFor(id)-> Chris@64: getPluginDescriptor(id); Chris@64: if (!descriptor) return false; Chris@64: Chris@64: min = descriptor->audioInputPortCount; Chris@64: max = descriptor->audioInputPortCount; Chris@64: Chris@64: return true; Chris@64: } Chris@64: Chris@64: return false; Chris@64: } Chris@64: Chris@64: bool Chris@79: TransformFactory::getChannelRange(TransformName name, Vamp::PluginBase *plugin, Chris@79: int &minChannels, int &maxChannels) Chris@79: { Chris@79: Vamp::Plugin *vp = 0; Chris@79: if ((vp = dynamic_cast(plugin))) { Chris@79: minChannels = vp->getMinChannelCount(); Chris@79: maxChannels = vp->getMaxChannelCount(); Chris@79: return true; Chris@79: } else { Chris@79: return getTransformChannelRange(name, minChannels, maxChannels); Chris@79: } Chris@79: } Chris@79: Chris@79: bool Chris@56: TransformFactory::getConfigurationForTransform(TransformName name, Chris@56: Model *inputModel, Chris@64: int &channel, Chris@56: QString &configurationXml) Chris@0: { Chris@56: QString id = name.section(':', 0, 2); Chris@56: QString output = name.section(':', 3); Chris@56: Chris@56: bool ok = false; Chris@56: configurationXml = m_lastConfigurations[name]; Chris@56: Chris@56: std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; Chris@56: Chris@66: Vamp::PluginBase *plugin = 0; Chris@60: Chris@56: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@60: Chris@60: plugin = FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin Chris@56: (id, inputModel->getSampleRate()); Chris@60: Chris@60: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@60: Chris@60: plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin Chris@60: (id, 0, 0, inputModel->getSampleRate(), 1024, 1); Chris@60: } Chris@60: Chris@60: if (plugin) { Chris@60: if (configurationXml != "") { Chris@66: PluginXml(plugin).setParametersFromXml(configurationXml); Chris@56: } Chris@64: Chris@64: int sourceChannels = 1; Chris@64: if (dynamic_cast(inputModel)) { Chris@64: sourceChannels = dynamic_cast(inputModel) Chris@64: ->getChannelCount(); Chris@64: } Chris@64: Chris@64: int minChannels = 1, maxChannels = sourceChannels; Chris@79: getChannelRange(name, plugin, minChannels, maxChannels); Chris@64: Chris@64: int targetChannels = sourceChannels; Chris@64: if (sourceChannels < minChannels) targetChannels = minChannels; Chris@64: if (sourceChannels > maxChannels) targetChannels = maxChannels; Chris@64: Chris@64: int defaultChannel = channel; Chris@64: Chris@64: PluginParameterDialog *dialog = new PluginParameterDialog(plugin, Chris@64: sourceChannels, Chris@64: targetChannels, Chris@64: defaultChannel); Chris@60: if (dialog->exec() == QDialog::Accepted) { Chris@60: ok = true; Chris@60: } Chris@66: configurationXml = PluginXml(plugin).toXmlString(); Chris@64: channel = dialog->getChannel(); Chris@60: delete dialog; Chris@60: delete plugin; Chris@56: } Chris@56: Chris@56: if (ok) m_lastConfigurations[name] = configurationXml; Chris@56: Chris@56: return ok; Chris@0: } Chris@0: Chris@0: Transform * Chris@0: TransformFactory::createTransform(TransformName name, Model *inputModel, Chris@64: int channel, QString configurationXml, bool start) Chris@0: { Chris@0: Transform *transform = 0; Chris@0: Chris@64: //!!! use channel Chris@64: Chris@56: QString id = name.section(':', 0, 2); Chris@56: QString output = name.section(':', 3); Chris@56: Chris@56: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@56: transform = new FeatureExtractionPluginTransform(inputModel, Chris@56: id, Chris@64: channel, Chris@56: configurationXml, Chris@56: output); Chris@60: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@60: transform = new RealTimePluginTransform(inputModel, Chris@60: id, Chris@64: channel, Chris@60: configurationXml, Chris@63: getTransformUnits(name), Chris@60: output.toInt()); Chris@0: } else { Chris@77: std::cerr << "TransformFactory::createTransform: Unknown transform \"" Chris@77: << name.toStdString() << "\"" << std::endl; Chris@77: return transform; Chris@0: } Chris@0: Chris@0: if (start && transform) transform->start(); Chris@16: transform->setObjectName(name); Chris@0: return transform; Chris@0: } Chris@0: Chris@0: Model * Chris@56: TransformFactory::transform(TransformName name, Model *inputModel, Chris@64: int channel, QString configurationXml) Chris@0: { Chris@64: Transform *t = createTransform(name, inputModel, channel, Chris@64: configurationXml, false); Chris@0: Chris@0: if (!t) return 0; Chris@0: Chris@0: connect(t, SIGNAL(finished()), this, SLOT(transformFinished())); Chris@0: Chris@0: t->start(); Chris@0: return t->detachOutputModel(); Chris@0: } Chris@0: Chris@0: void Chris@0: TransformFactory::transformFinished() Chris@0: { Chris@0: QObject *s = sender(); Chris@0: Transform *transform = dynamic_cast(s); Chris@0: Chris@0: if (!transform) { Chris@0: std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: transform->wait(); // unnecessary but reassuring Chris@0: delete transform; Chris@0: } Chris@0: