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