Chris@330: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@330: 
Chris@330: /*
Chris@330:     Sonic Visualiser
Chris@330:     An audio file viewer and annotation editor.
Chris@330:     Centre for Digital Music, Queen Mary, University of London.
Chris@330:     This file copyright 2006 Chris Cannam and QMUL.
Chris@330:    
Chris@330:     This program is free software; you can redistribute it and/or
Chris@330:     modify it under the terms of the GNU General Public License as
Chris@330:     published by the Free Software Foundation; either version 2 of the
Chris@330:     License, or (at your option) any later version.  See the file
Chris@330:     COPYING included with this distribution for more information.
Chris@330: */
Chris@330: 
Chris@330: #include "TransformFactory.h"
Chris@330: 
Chris@330: #include "plugin/FeatureExtractionPluginFactory.h"
Chris@330: #include "plugin/RealTimePluginFactory.h"
Chris@332: #include "plugin/RealTimePluginInstance.h"
Chris@330: #include "plugin/PluginXml.h"
Chris@330: 
Chris@475: #include <vamp-hostsdk/Plugin.h>
Chris@475: #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@475: #include <vamp-hostsdk/PluginWrapper.h>
Chris@330: 
Chris@457: #include "rdf/PluginRDFIndexer.h"
Chris@457: #include "rdf/PluginRDFDescription.h"
Chris@457: 
Chris@446: #include "base/XmlExportable.h"
Chris@446: 
Chris@330: #include <iostream>
Chris@330: #include <set>
Chris@330: 
Chris@330: #include <QRegExp>
Chris@350: #include <QTextStream>
Chris@330: 
Chris@460: #include "base/Thread.h"
Chris@460: 
Chris@810: //#define DEBUG_TRANSFORM_FACTORY 1
Chris@810: 
Chris@330: TransformFactory *
Chris@330: TransformFactory::m_instance = new TransformFactory;
Chris@330: 
Chris@330: TransformFactory *
Chris@330: TransformFactory::getInstance()
Chris@330: {
Chris@330:     return m_instance;
Chris@330: }
Chris@330: 
Chris@574: void
Chris@574: TransformFactory::deleteInstance()
Chris@574: {
Chris@690:     SVDEBUG << "TransformFactory::deleteInstance called" << endl;
Chris@574:     delete m_instance;
Chris@574:     m_instance = 0;
Chris@574: }
Chris@574: 
Chris@457: TransformFactory::TransformFactory() :
Chris@457:     m_transformsPopulated(false),
Chris@477:     m_uninstalledTransformsPopulated(false),
Chris@574:     m_thread(0),
Chris@976:     m_exiting(false),
Chris@976:     m_populatingSlowly(false)
Chris@457: {
Chris@457: }
Chris@457: 
Chris@330: TransformFactory::~TransformFactory()
Chris@330: {
Chris@574:     m_exiting = true;
Chris@574:     if (m_thread) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@690:         SVDEBUG << "TransformFactory::~TransformFactory: waiting on thread" << endl;
Chris@600: #endif
Chris@574:         m_thread->wait();
Chris@574:         delete m_thread;
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@690:         SVDEBUG << "TransformFactory::~TransformFactory: waited and done" << endl;
Chris@600: #endif
Chris@574:     }
Chris@330: }
Chris@330: 
Chris@477: void
Chris@477: TransformFactory::startPopulationThread()
Chris@477: {
Chris@482:     m_uninstalledTransformsMutex.lock();
Chris@477: 
Chris@482:     if (m_thread) {
Chris@482:         m_uninstalledTransformsMutex.unlock();
Chris@482:         return;
Chris@482:     }
Chris@482:     m_thread = new UninstalledTransformsPopulateThread(this);
Chris@477: 
Chris@482:     m_uninstalledTransformsMutex.unlock();
Chris@482: 
Chris@477:     m_thread->start();
Chris@477: }
Chris@477: 
Chris@481: void
Chris@481: TransformFactory::UninstalledTransformsPopulateThread::run()
Chris@481: {
Chris@481:     m_factory->m_populatingSlowly = true;
Chris@481:     sleep(1);
Chris@481:     m_factory->populateUninstalledTransforms();
Chris@481: }
Chris@481: 
Chris@330: TransformList
Chris@350: TransformFactory::getAllTransformDescriptions()
Chris@330: {
Chris@460:     populateTransforms();
Chris@330: 
Chris@330:     std::set<TransformDescription> dset;
Chris@330:     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330: 	 i != m_transforms.end(); ++i) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:         cerr << "inserting transform into set: id = " << i->second.identifier << endl;
Chris@600: #endif
Chris@330: 	dset.insert(i->second);
Chris@330:     }
Chris@330: 
Chris@330:     TransformList list;
Chris@330:     for (std::set<TransformDescription>::const_iterator i = dset.begin();
Chris@330: 	 i != dset.end(); ++i) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:         cerr << "inserting transform into list: id = " << i->identifier << endl;
Chris@600: #endif
Chris@330: 	list.push_back(*i);
Chris@330:     }
Chris@330: 
Chris@330:     return list;
Chris@330: }
Chris@330: 
Chris@350: TransformDescription
Chris@350: TransformFactory::getTransformDescription(TransformId id)
Chris@350: {
Chris@460:     populateTransforms();
Chris@350: 
Chris@350:     if (m_transforms.find(id) == m_transforms.end()) {
Chris@350:         return TransformDescription();
Chris@350:     }
Chris@350: 
Chris@350:     return m_transforms[id];
Chris@350: }
Chris@350: 
Chris@485: bool
Chris@485: TransformFactory::haveInstalledTransforms()
Chris@485: {
Chris@485:     populateTransforms();
Chris@485:     return !m_transforms.empty();
Chris@485: }
Chris@485: 
Chris@457: TransformList
Chris@457: TransformFactory::getUninstalledTransformDescriptions()
Chris@457: {
Chris@479:     m_populatingSlowly = false;
Chris@460:     populateUninstalledTransforms();
Chris@457:     
Chris@457:     std::set<TransformDescription> dset;
Chris@457:     for (TransformDescriptionMap::const_iterator i = m_uninstalledTransforms.begin();
Chris@457: 	 i != m_uninstalledTransforms.end(); ++i) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:         cerr << "inserting transform into set: id = " << i->second.identifier << endl;
Chris@600: #endif
Chris@457: 	dset.insert(i->second);
Chris@457:     }
Chris@457: 
Chris@457:     TransformList list;
Chris@457:     for (std::set<TransformDescription>::const_iterator i = dset.begin();
Chris@457: 	 i != dset.end(); ++i) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:         cerr << "inserting transform into uninstalled list: id = " << i->identifier << endl;
Chris@600: #endif
Chris@457: 	list.push_back(*i);
Chris@457:     }
Chris@457: 
Chris@457:     return list;
Chris@457: }
Chris@457: 
Chris@457: TransformDescription
Chris@457: TransformFactory::getUninstalledTransformDescription(TransformId id)
Chris@457: {
Chris@479:     m_populatingSlowly = false;
Chris@460:     populateUninstalledTransforms();
Chris@457: 
Chris@457:     if (m_uninstalledTransforms.find(id) == m_uninstalledTransforms.end()) {
Chris@457:         return TransformDescription();
Chris@457:     }
Chris@457: 
Chris@457:     return m_uninstalledTransforms[id];
Chris@457: }
Chris@457: 
Chris@485: bool
Chris@485: TransformFactory::haveUninstalledTransforms(bool waitForCheckToComplete)
Chris@485: {
Chris@485:     if (waitForCheckToComplete) {
Chris@485:         populateUninstalledTransforms();
Chris@485:     } else {
Chris@485:         if (!m_uninstalledTransformsMutex.tryLock()) {
Chris@485:             return false;
Chris@485:         }
Chris@485:         if (!m_uninstalledTransformsPopulated) {
Chris@485:             m_uninstalledTransformsMutex.unlock();
Chris@485:             return false;
Chris@485:         }
Chris@485:         m_uninstalledTransformsMutex.unlock();
Chris@485:     }
Chris@485: 
Chris@485:     return !m_uninstalledTransforms.empty();
Chris@485: }
Chris@485: 
Chris@457: TransformFactory::TransformInstallStatus
Chris@457: TransformFactory::getTransformInstallStatus(TransformId id)
Chris@457: {
Chris@460:     populateTransforms();
Chris@457: 
Chris@457:     if (m_transforms.find(id) != m_transforms.end()) {
Chris@457:         return TransformInstalled;
Chris@457:     }
Chris@473:     
Chris@473:     if (!m_uninstalledTransformsMutex.tryLock()) {
Chris@473:         // uninstalled transforms are being populated; this may take some time,
Chris@473:         // and they aren't critical
Chris@473:         return TransformUnknown;
Chris@473:     }
Chris@473: 
Chris@473:     if (!m_uninstalledTransformsPopulated) {
Chris@473:         m_uninstalledTransformsMutex.unlock();
Chris@479:         m_populatingSlowly = false;
Chris@473:         populateUninstalledTransforms();
Chris@473:         m_uninstalledTransformsMutex.lock();
Chris@473:     }
Chris@473: 
Chris@457:     if (m_uninstalledTransforms.find(id) != m_uninstalledTransforms.end()) {
Chris@482:         m_uninstalledTransformsMutex.unlock();
Chris@457:         return TransformNotInstalled;
Chris@457:     }
Chris@473: 
Chris@473:     m_uninstalledTransformsMutex.unlock();
Chris@457:     return TransformUnknown;
Chris@457: }
Chris@457:     
Chris@457: 
Chris@487: std::vector<TransformDescription::Type>
Chris@330: TransformFactory::getAllTransformTypes()
Chris@330: {
Chris@460:     populateTransforms();
Chris@330: 
Chris@487:     std::set<TransformDescription::Type> types;
Chris@330:     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330: 	 i != m_transforms.end(); ++i) {
Chris@330:         types.insert(i->second.type);
Chris@330:     }
Chris@330: 
Chris@487:     std::vector<TransformDescription::Type> rv;
Chris@487:     for (std::set<TransformDescription::Type>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@330:         rv.push_back(*i);
Chris@330:     }
Chris@330: 
Chris@330:     return rv;
Chris@330: }
Chris@330: 
Chris@330: std::vector<QString>
Chris@487: TransformFactory::getTransformCategories(TransformDescription::Type transformType)
Chris@330: {
Chris@460:     populateTransforms();
Chris@330: 
Chris@330:     std::set<QString> categories;
Chris@330:     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330:          i != m_transforms.end(); ++i) {
Chris@330:         if (i->second.type == transformType) {
Chris@330:             categories.insert(i->second.category);
Chris@330:         }
Chris@330:     }
Chris@330: 
Chris@330:     bool haveEmpty = false;
Chris@330:     
Chris@330:     std::vector<QString> rv;
Chris@330:     for (std::set<QString>::iterator i = categories.begin(); 
Chris@330:          i != categories.end(); ++i) {
Chris@330:         if (*i != "") rv.push_back(*i);
Chris@330:         else haveEmpty = true;
Chris@330:     }
Chris@330: 
Chris@330:     if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330: 
Chris@330:     return rv;
Chris@330: }
Chris@330: 
Chris@330: std::vector<QString>
Chris@487: TransformFactory::getTransformMakers(TransformDescription::Type transformType)
Chris@330: {
Chris@460:     populateTransforms();
Chris@330: 
Chris@330:     std::set<QString> makers;
Chris@330:     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@330:          i != m_transforms.end(); ++i) {
Chris@330:         if (i->second.type == transformType) {
Chris@330:             makers.insert(i->second.maker);
Chris@330:         }
Chris@330:     }
Chris@330: 
Chris@330:     bool haveEmpty = false;
Chris@330:     
Chris@330:     std::vector<QString> rv;
Chris@330:     for (std::set<QString>::iterator i = makers.begin(); 
Chris@330:          i != makers.end(); ++i) {
Chris@330:         if (*i != "") rv.push_back(*i);
Chris@330:         else haveEmpty = true;
Chris@330:     }
Chris@330: 
Chris@330:     if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
Chris@330: 
Chris@330:     return rv;
Chris@330: }
Chris@330: 
Chris@487: QString
Chris@487: TransformFactory::getTransformTypeName(TransformDescription::Type type) const
Chris@487: {
Chris@487:     switch (type) {
Chris@487:     case TransformDescription::Analysis: return tr("Analysis");
Chris@487:     case TransformDescription::Effects: return tr("Effects");
Chris@487:     case TransformDescription::EffectsData: return tr("Effects Data");
Chris@487:     case TransformDescription::Generator: return tr("Generator");
Chris@487:     case TransformDescription::UnknownType: return tr("Other");
Chris@487:     }
Chris@489:     return tr("Other");
Chris@487: }
Chris@487: 
Chris@330: void
Chris@330: TransformFactory::populateTransforms()
Chris@330: {
Chris@460:     MutexLocker locker(&m_transformsMutex,
Chris@460:                        "TransformFactory::populateTransforms");
Chris@460:     if (m_transformsPopulated) {
Chris@460:         return;
Chris@460:     }
Chris@460: 
Chris@330:     TransformDescriptionMap transforms;
Chris@330: 
Chris@330:     populateFeatureExtractionPlugins(transforms);
Chris@576:     if (m_exiting) return;
Chris@330:     populateRealTimePlugins(transforms);
Chris@576:     if (m_exiting) return;
Chris@330: 
Chris@330:     // disambiguate plugins with similar names
Chris@330: 
Chris@330:     std::map<QString, int> names;
Chris@330:     std::map<QString, QString> pluginSources;
Chris@330:     std::map<QString, QString> pluginMakers;
Chris@330: 
Chris@330:     for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@330:          i != transforms.end(); ++i) {
Chris@330: 
Chris@330:         TransformDescription desc = i->second;
Chris@330: 
Chris@330:         QString td = desc.name;
Chris@330:         QString tn = td.section(": ", 0, 0);
Chris@330:         QString pn = desc.identifier.section(":", 1, 1);
Chris@330: 
Chris@330:         if (pluginSources.find(tn) != pluginSources.end()) {
Chris@330:             if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
Chris@330:                 ++names[tn];
Chris@330:             }
Chris@330:         } else {
Chris@330:             ++names[tn];
Chris@330:             pluginSources[tn] = pn;
Chris@330:             pluginMakers[tn] = desc.maker;
Chris@330:         }
Chris@330:     }
Chris@330: 
Chris@330:     std::map<QString, int> counts;
Chris@330:     m_transforms.clear();
Chris@330: 
Chris@330:     for (TransformDescriptionMap::iterator i = transforms.begin();
Chris@330:          i != transforms.end(); ++i) {
Chris@330: 
Chris@330:         TransformDescription desc = i->second;
Chris@330: 	QString identifier = desc.identifier;
Chris@330:         QString maker = desc.maker;
Chris@330: 
Chris@330:         QString td = desc.name;
Chris@330:         QString tn = td.section(": ", 0, 0);
Chris@330:         QString to = td.section(": ", 1);
Chris@330: 
Chris@330: 	if (names[tn] > 1) {
Chris@330:             maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@330: 	    tn = QString("%1 [%2]").arg(tn).arg(maker);
Chris@330: 	}
Chris@330: 
Chris@330:         if (to != "") {
Chris@330:             desc.name = QString("%1: %2").arg(tn).arg(to);
Chris@330:         } else {
Chris@330:             desc.name = tn;
Chris@330:         }
Chris@330: 
Chris@330: 	m_transforms[identifier] = desc;
Chris@330:     }	    
Chris@457: 
Chris@457:     m_transformsPopulated = true;
Chris@330: }
Chris@330: 
Chris@330: void
Chris@330: TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
Chris@330: {
Chris@330:     std::vector<QString> plugs =
Chris@330: 	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
Chris@576:     if (m_exiting) return;
Chris@330: 
Chris@930:     for (int i = 0; i < (int)plugs.size(); ++i) {
Chris@330: 
Chris@330: 	QString pluginId = plugs[i];
Chris@330: 
Chris@330: 	FeatureExtractionPluginFactory *factory =
Chris@330: 	    FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@330: 
Chris@330: 	if (!factory) {
Chris@845: 	    cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId << endl;
Chris@330: 	    continue;
Chris@330: 	}
Chris@330: 
Chris@330: 	Vamp::Plugin *plugin = 
Chris@350: 	    factory->instantiatePlugin(pluginId, 44100);
Chris@330: 
Chris@330: 	if (!plugin) {
Chris@845: 	    cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId << endl;
Chris@330: 	    continue;
Chris@330: 	}
Chris@330: 		
Chris@330: 	QString pluginName = plugin->getName().c_str();
Chris@330:         QString category = factory->getPluginCategory(pluginId);
Chris@330: 
Chris@330: 	Vamp::Plugin::OutputList outputs =
Chris@330: 	    plugin->getOutputDescriptors();
Chris@330: 
Chris@930: 	for (int j = 0; j < (int)outputs.size(); ++j) {
Chris@330: 
Chris@330: 	    QString transformId = QString("%1:%2")
Chris@330: 		    .arg(pluginId).arg(outputs[j].identifier.c_str());
Chris@330: 
Chris@330: 	    QString userName;
Chris@330:             QString friendlyName;
Chris@330:             QString units = outputs[j].unit.c_str();
Chris@330:             QString description = plugin->getDescription().c_str();
Chris@330:             QString maker = plugin->getMaker().c_str();
Chris@330:             if (maker == "") maker = tr("<unknown maker>");
Chris@330: 
Chris@443:             QString longDescription = description;
Chris@443: 
Chris@443:             if (longDescription == "") {
Chris@330:                 if (outputs.size() == 1) {
Chris@443:                     longDescription = tr("Extract features using \"%1\" plugin (from %2)")
Chris@330:                         .arg(pluginName).arg(maker);
Chris@330:                 } else {
Chris@443:                     longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
Chris@330:                         .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330:                 }
Chris@330:             } else {
Chris@330:                 if (outputs.size() == 1) {
Chris@443:                     longDescription = tr("%1 using \"%2\" plugin (from %3)")
Chris@443:                         .arg(longDescription).arg(pluginName).arg(maker);
Chris@330:                 } else {
Chris@443:                     longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
Chris@443:                         .arg(longDescription).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
Chris@330:                 }
Chris@330:             }                    
Chris@330: 
Chris@330: 	    if (outputs.size() == 1) {
Chris@330: 		userName = pluginName;
Chris@330:                 friendlyName = pluginName;
Chris@330: 	    } else {
Chris@330: 		userName = QString("%1: %2")
Chris@330: 		    .arg(pluginName)
Chris@330: 		    .arg(outputs[j].name.c_str());
Chris@330:                 friendlyName = outputs[j].name.c_str();
Chris@330: 	    }
Chris@330: 
Chris@330:             bool configurable = (!plugin->getPrograms().empty() ||
Chris@330:                                  !plugin->getParameterDescriptors().empty());
Chris@330: 
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@686:             cerr << "Feature extraction plugin transform: " << transformId << " friendly name: " << friendlyName << endl;
Chris@600: #endif
Chris@330: 
Chris@330: 	    transforms[transformId] = 
Chris@487:                 TransformDescription(TransformDescription::Analysis,
Chris@332:                                      category,
Chris@332:                                      transformId,
Chris@332:                                      userName,
Chris@332:                                      friendlyName,
Chris@332:                                      description,
Chris@443:                                      longDescription,
Chris@332:                                      maker,
Chris@332:                                      units,
Chris@332:                                      configurable);
Chris@330: 	}
Chris@330: 
Chris@330:         delete plugin;
Chris@330:     }
Chris@330: }
Chris@330: 
Chris@330: void
Chris@330: TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
Chris@330: {
Chris@330:     std::vector<QString> plugs =
Chris@330: 	RealTimePluginFactory::getAllPluginIdentifiers();
Chris@576:     if (m_exiting) return;
Chris@330: 
Chris@330:     static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
Chris@330: 
Chris@930:     for (int i = 0; i < (int)plugs.size(); ++i) {
Chris@330:         
Chris@330: 	QString pluginId = plugs[i];
Chris@330: 
Chris@330:         RealTimePluginFactory *factory =
Chris@330:             RealTimePluginFactory::instanceFor(pluginId);
Chris@330: 
Chris@330: 	if (!factory) {
Chris@845: 	    cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId << endl;
Chris@330: 	    continue;
Chris@330: 	}
Chris@330: 
Chris@330:         const RealTimePluginDescriptor *descriptor =
Chris@330:             factory->getPluginDescriptor(pluginId);
Chris@330: 
Chris@330:         if (!descriptor) {
Chris@845: 	    cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId << endl;
Chris@330: 	    continue;
Chris@330: 	}
Chris@330: 	
Chris@330: //!!!        if (descriptor->controlOutputPortCount == 0 ||
Chris@330: //            descriptor->audioInputPortCount == 0) continue;
Chris@330: 
Chris@843: //        cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << endl;
Chris@330: 	
Chris@330: 	QString pluginName = descriptor->name.c_str();
Chris@330:         QString category = factory->getPluginCategory(pluginId);
Chris@330:         bool configurable = (descriptor->parameterCount > 0);
Chris@330:         QString maker = descriptor->maker.c_str();
Chris@330:         if (maker == "") maker = tr("<unknown maker>");
Chris@330: 
Chris@330:         if (descriptor->audioInputPortCount > 0) {
Chris@330: 
Chris@930:             for (int j = 0; j < (int)descriptor->controlOutputPortCount; ++j) {
Chris@330: 
Chris@330:                 QString transformId = QString("%1:%2").arg(pluginId).arg(j);
Chris@330:                 QString userName;
Chris@330:                 QString units;
Chris@330:                 QString portName;
Chris@330: 
Chris@930:                 if (j < (int)descriptor->controlOutputPortNames.size() &&
Chris@330:                     descriptor->controlOutputPortNames[j] != "") {
Chris@330: 
Chris@330:                     portName = descriptor->controlOutputPortNames[j].c_str();
Chris@330: 
Chris@330:                     userName = tr("%1: %2")
Chris@330:                         .arg(pluginName)
Chris@330:                         .arg(portName);
Chris@330: 
Chris@330:                     if (unitRE.indexIn(portName) >= 0) {
Chris@330:                         units = unitRE.cap(1);
Chris@330:                     }
Chris@330: 
Chris@330:                 } else if (descriptor->controlOutputPortCount > 1) {
Chris@330: 
Chris@330:                     userName = tr("%1: Output %2")
Chris@330:                         .arg(pluginName)
Chris@330:                         .arg(j + 1);
Chris@330: 
Chris@330:                 } else {
Chris@330: 
Chris@330:                     userName = pluginName;
Chris@330:                 }
Chris@330: 
Chris@330:                 QString description;
Chris@330: 
Chris@330:                 if (portName != "") {
Chris@330:                     description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
Chris@330:                         .arg(portName)
Chris@330:                         .arg(pluginName)
Chris@330:                         .arg(maker);
Chris@330:                 } else {
Chris@330:                     description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
Chris@330:                         .arg(j + 1)
Chris@330:                         .arg(pluginName)
Chris@330:                         .arg(maker);
Chris@330:                 }
Chris@330: 
Chris@330:                 transforms[transformId] = 
Chris@487:                     TransformDescription(TransformDescription::EffectsData,
Chris@332:                                          category,
Chris@332:                                          transformId,
Chris@332:                                          userName,
Chris@332:                                          userName,
Chris@443:                                          "",
Chris@332:                                          description,
Chris@332:                                          maker,
Chris@332:                                          units,
Chris@332:                                          configurable);
Chris@330:             }
Chris@330:         }
Chris@330: 
Chris@330:         if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
Chris@330: 
Chris@330:             if (descriptor->audioOutputPortCount > 0) {
Chris@330: 
Chris@330:                 QString transformId = QString("%1:A").arg(pluginId);
Chris@487:                 TransformDescription::Type type = TransformDescription::Effects;
Chris@330: 
Chris@330:                 QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
Chris@330:                     .arg(pluginName)
Chris@330:                     .arg(maker);
Chris@330: 
Chris@330:                 if (descriptor->audioInputPortCount == 0) {
Chris@487:                     type = TransformDescription::Generator;
Chris@330:                     QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
Chris@330:                         .arg(pluginName)
Chris@330:                         .arg(maker);
Chris@330:                 }
Chris@330: 
Chris@330:                 transforms[transformId] =
Chris@330:                     TransformDescription(type,
Chris@332:                                          category,
Chris@332:                                          transformId,
Chris@332:                                          pluginName,
Chris@332:                                          pluginName,
Chris@443:                                          "",
Chris@332:                                          description,
Chris@332:                                          maker,
Chris@332:                                          "",
Chris@332:                                          configurable);
Chris@330:             }
Chris@330:         }
Chris@330:     }
Chris@330: }
Chris@330: 
Chris@457: void
Chris@457: TransformFactory::populateUninstalledTransforms()
Chris@457: {
Chris@576:     if (m_exiting) return;
Chris@576: 
Chris@460:     populateTransforms();
Chris@576:     if (m_exiting) return;
Chris@460: 
Chris@460:     MutexLocker locker(&m_uninstalledTransformsMutex,
Chris@460:                        "TransformFactory::populateUninstalledTransforms");
Chris@460:     if (m_uninstalledTransformsPopulated) return;
Chris@460: 
Chris@461:     PluginRDFIndexer::getInstance()->indexConfiguredURLs();
Chris@576:     if (m_exiting) return;
Chris@457: 
Chris@457:     //!!! This will be amazingly slow
Chris@457: 
Chris@457:     QStringList ids = PluginRDFIndexer::getInstance()->getIndexedPluginIds();
Chris@457:     
Chris@457:     for (QStringList::const_iterator i = ids.begin(); i != ids.end(); ++i) {
Chris@457:         
Chris@457:         PluginRDFDescription desc(*i);
Chris@457: 
Chris@457:         QString name = desc.getPluginName();
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@600:         if (name == "") {
Chris@810:             cerr << "TransformFactory::populateUninstalledTransforms: "
Chris@810:                  << "No name available for plugin " << *i
Chris@810:                  << ", skipping" << endl;
Chris@600:             continue;
Chris@600:         }
Chris@600: #endif
Chris@457: 
Chris@457:         QString description = desc.getPluginDescription();
Chris@457:         QString maker = desc.getPluginMaker();
Chris@462:         QString infoUrl = desc.getPluginInfoURL();
Chris@457: 
Chris@457:         QStringList oids = desc.getOutputIds();
Chris@457: 
Chris@457:         for (QStringList::const_iterator j = oids.begin(); j != oids.end(); ++j) {
Chris@457: 
Chris@457:             TransformId tid = Transform::getIdentifierForPluginOutput(*i, *j);
Chris@457:             
Chris@457:             if (m_transforms.find(tid) != m_transforms.end()) {
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:                 cerr << "TransformFactory::populateUninstalledTransforms: "
Chris@687:                           << tid << " is installed; adding info url if appropriate, skipping rest" << endl;
Chris@600: #endif
Chris@468:                 if (infoUrl != "") {
Chris@468:                     if (m_transforms[tid].infoUrl == "") {
Chris@468:                         m_transforms[tid].infoUrl = infoUrl;
Chris@468:                     }
Chris@468:                 }
Chris@457:                 continue;
Chris@457:             }
Chris@457: 
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@810:             cerr << "TransformFactory::populateUninstalledTransforms: "
Chris@687:                       << "adding " << tid << endl;
Chris@600: #endif
Chris@457: 
Chris@457:             QString oname = desc.getOutputName(*j);
Chris@457:             if (oname == "") oname = *j;
Chris@457:             
Chris@457:             TransformDescription td;
Chris@487:             td.type = TransformDescription::Analysis;
Chris@457:             td.category = "";
Chris@457:             td.identifier = tid;
Chris@457: 
Chris@457:             if (oids.size() == 1) {
Chris@457:                 td.name = name;
Chris@457:             } else if (name != "") {
Chris@457:                 td.name = tr("%1: %2").arg(name).arg(oname);
Chris@457:             }
Chris@457: 
Chris@462:             QString longDescription = description;
Chris@462:             //!!! basically duplicated from above
Chris@462:             if (longDescription == "") {
Chris@462:                 if (oids.size() == 1) {
Chris@462:                     longDescription = tr("Extract features using \"%1\" plugin (from %2)")
Chris@462:                         .arg(name).arg(maker);
Chris@462:                 } else {
Chris@462:                     longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
Chris@462:                         .arg(oname).arg(name).arg(maker);
Chris@462:                 }
Chris@462:             } else {
Chris@462:                 if (oids.size() == 1) {
Chris@462:                     longDescription = tr("%1 using \"%2\" plugin (from %3)")
Chris@462:                         .arg(longDescription).arg(name).arg(maker);
Chris@462:                 } else {
Chris@462:                     longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
Chris@462:                         .arg(longDescription).arg(oname).arg(name).arg(maker);
Chris@462:                 }
Chris@462:             }                    
Chris@462: 
Chris@457:             td.friendlyName = name; //!!!???
Chris@457:             td.description = description;
Chris@462:             td.longDescription = longDescription;
Chris@457:             td.maker = maker;
Chris@462:             td.infoUrl = infoUrl;
Chris@457:             td.units = "";
Chris@457:             td.configurable = false;
Chris@457: 
Chris@457:             m_uninstalledTransforms[tid] = td;
Chris@457:         }
Chris@574: 
Chris@576:         if (m_exiting) return;
Chris@457:     }
Chris@457: 
Chris@457:     m_uninstalledTransformsPopulated = true;
Chris@460: 
Chris@600: #ifdef DEBUG_TRANSFORM_FACTORY
Chris@843:     cerr << "populateUninstalledTransforms exiting" << endl;
Chris@600: #endif
Chris@457: }
Chris@350: 
Chris@350: Transform
Chris@930: TransformFactory::getDefaultTransformFor(TransformId id, int rate)
Chris@350: {
Chris@350:     Transform t;
Chris@350:     t.setIdentifier(id);
Chris@350:     if (rate != 0) t.setSampleRate(rate);
Chris@350: 
Chris@351:     Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate);
Chris@350: 
Chris@350:     if (plugin) {
Chris@366:         t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion()));
Chris@350:         setParametersFromPlugin(t, plugin);
Chris@350:         makeContextConsistentWithPlugin(t, plugin);
Chris@350:         delete plugin;
Chris@350:     }
Chris@350: 
Chris@350:     return t;
Chris@350: }
Chris@350: 
Chris@350: Vamp::PluginBase *
Chris@351: TransformFactory::instantiatePluginFor(const Transform &transform)
Chris@351: {
Chris@351:     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351:         (transform.getIdentifier(), transform.getSampleRate());
Chris@508: 
Chris@351:     if (plugin) {
Chris@351:         setPluginParameters(transform, plugin);
Chris@351:     }
Chris@508: 
Chris@351:     return plugin;
Chris@351: }
Chris@351: 
Chris@351: Vamp::PluginBase *
Chris@930: TransformFactory::instantiateDefaultPluginFor(TransformId identifier, int rate)
Chris@350: {
Chris@350:     Transform t;
Chris@350:     t.setIdentifier(identifier);
Chris@350:     if (rate == 0) rate = 44100;
Chris@350:     QString pluginId = t.getPluginIdentifier();
Chris@350: 
Chris@350:     Vamp::PluginBase *plugin = 0;
Chris@350: 
Chris@350:     if (t.getType() == Transform::FeatureExtraction) {
Chris@350: 
Chris@350:         FeatureExtractionPluginFactory *factory = 
Chris@350:             FeatureExtractionPluginFactory::instanceFor(pluginId);
Chris@350: 
Chris@439:         if (factory) {
Chris@439:             plugin = factory->instantiatePlugin(pluginId, rate);
Chris@439:         }
Chris@350: 
Chris@350:     } else {
Chris@350: 
Chris@350:         RealTimePluginFactory *factory = 
Chris@350:             RealTimePluginFactory::instanceFor(pluginId);
Chris@439: 
Chris@439:         if (factory) {
Chris@439:             plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1);
Chris@439:         }
Chris@350:     }
Chris@350: 
Chris@350:     return plugin;
Chris@350: }
Chris@350: 
Chris@350: Vamp::Plugin *
Chris@350: TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin)
Chris@350: {
Chris@350:     Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin);
Chris@350:     if (!vp) {
Chris@443: //        cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << endl;
Chris@350:         vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); //!!! why?
Chris@350: }
Chris@350:     if (!vp) {
Chris@443: //        cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << endl;
Chris@350:         vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why?
Chris@350:     }
Chris@350:     if (!vp) {
Chris@443: //        cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << endl;
Chris@350:     }
Chris@350:     return vp;
Chris@350: }
Chris@350: 
Chris@330: bool
Chris@330: TransformFactory::haveTransform(TransformId identifier)
Chris@330: {
Chris@460:     populateTransforms();
Chris@330:     return (m_transforms.find(identifier) != m_transforms.end());
Chris@330: }
Chris@330: 
Chris@330: QString
Chris@330: TransformFactory::getTransformName(TransformId identifier)
Chris@330: {
Chris@330:     if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330: 	return m_transforms[identifier].name;
Chris@330:     } else return "";
Chris@330: }
Chris@330: 
Chris@330: QString
Chris@330: TransformFactory::getTransformFriendlyName(TransformId identifier)
Chris@330: {
Chris@330:     if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330: 	return m_transforms[identifier].friendlyName;
Chris@330:     } else return "";
Chris@330: }
Chris@330: 
Chris@330: QString
Chris@330: TransformFactory::getTransformUnits(TransformId identifier)
Chris@330: {
Chris@330:     if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330: 	return m_transforms[identifier].units;
Chris@330:     } else return "";
Chris@330: }
Chris@330: 
Chris@472: QString
Chris@472: TransformFactory::getTransformInfoUrl(TransformId identifier)
Chris@472: {
Chris@472:     if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@472: 	return m_transforms[identifier].infoUrl;
Chris@472:     } else return "";
Chris@472: }
Chris@472: 
Chris@350: Vamp::Plugin::InputDomain
Chris@350: TransformFactory::getTransformInputDomain(TransformId identifier)
Chris@350: {
Chris@350:     Transform transform;
Chris@350:     transform.setIdentifier(identifier);
Chris@350: 
Chris@350:     if (transform.getType() != Transform::FeatureExtraction) {
Chris@350:         return Vamp::Plugin::TimeDomain;
Chris@350:     }
Chris@350: 
Chris@350:     Vamp::Plugin *plugin =
Chris@351:         downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0));
Chris@350: 
Chris@350:     if (plugin) {
Chris@350:         Vamp::Plugin::InputDomain d = plugin->getInputDomain();
Chris@350:         delete plugin;
Chris@350:         return d;
Chris@350:     }
Chris@350: 
Chris@350:     return Vamp::Plugin::TimeDomain;
Chris@350: }
Chris@350: 
Chris@330: bool
Chris@330: TransformFactory::isTransformConfigurable(TransformId identifier)
Chris@330: {
Chris@330:     if (m_transforms.find(identifier) != m_transforms.end()) {
Chris@330: 	return m_transforms[identifier].configurable;
Chris@330:     } else return false;
Chris@330: }
Chris@330: 
Chris@330: bool
Chris@330: TransformFactory::getTransformChannelRange(TransformId identifier,
Chris@330:                                            int &min, int &max)
Chris@330: {
Chris@330:     QString id = identifier.section(':', 0, 2);
Chris@330: 
Chris@330:     if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@330: 
Chris@330:         Vamp::Plugin *plugin = 
Chris@330:             FeatureExtractionPluginFactory::instanceFor(id)->
Chris@350:             instantiatePlugin(id, 44100);
Chris@330:         if (!plugin) return false;
Chris@330: 
Chris@330:         min = plugin->getMinChannelCount();
Chris@330:         max = plugin->getMaxChannelCount();
Chris@330:         delete plugin;
Chris@330: 
Chris@330:         return true;
Chris@330: 
Chris@330:     } else if (RealTimePluginFactory::instanceFor(id)) {
Chris@330: 
Chris@350:         // don't need to instantiate
Chris@350: 
Chris@330:         const RealTimePluginDescriptor *descriptor = 
Chris@330:             RealTimePluginFactory::instanceFor(id)->
Chris@330:             getPluginDescriptor(id);
Chris@330:         if (!descriptor) return false;
Chris@330: 
Chris@330:         min = descriptor->audioInputPortCount;
Chris@330:         max = descriptor->audioInputPortCount;
Chris@330: 
Chris@330:         return true;
Chris@330:     }
Chris@330: 
Chris@330:     return false;
Chris@330: }
Chris@332: 
Chris@332: void
Chris@332: TransformFactory::setParametersFromPlugin(Transform &transform,
Chris@332:                                           Vamp::PluginBase *plugin)
Chris@332: {
Chris@332:     Transform::ParameterMap pmap;
Chris@332: 
Chris@350:     //!!! record plugin & API version
Chris@350: 
Chris@350:     //!!! check that this is the right plugin!
Chris@350: 
Chris@332:     Vamp::PluginBase::ParameterList parameters =
Chris@332:         plugin->getParameterDescriptors();
Chris@332: 
Chris@332:     for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
Chris@332:          i != parameters.end(); ++i) {
Chris@332:         pmap[i->identifier.c_str()] = plugin->getParameter(i->identifier);
Chris@810: //        cerr << "TransformFactory::setParametersFromPlugin: parameter "
Chris@583: //                  << i->identifier << " -> value " <<
Chris@687: //            pmap[i->identifier.c_str()] << endl;
Chris@332:     }
Chris@332: 
Chris@332:     transform.setParameters(pmap);
Chris@332: 
Chris@332:     if (plugin->getPrograms().empty()) {
Chris@332:         transform.setProgram("");
Chris@332:     } else {
Chris@332:         transform.setProgram(plugin->getCurrentProgram().c_str());
Chris@332:     }
Chris@332: 
Chris@332:     RealTimePluginInstance *rtpi =
Chris@332:         dynamic_cast<RealTimePluginInstance *>(plugin);
Chris@332: 
Chris@332:     Transform::ConfigurationMap cmap;
Chris@332: 
Chris@332:     if (rtpi) {
Chris@332: 
Chris@332:         RealTimePluginInstance::ConfigurationPairMap configurePairs =
Chris@332:             rtpi->getConfigurePairs();
Chris@332: 
Chris@332:         for (RealTimePluginInstance::ConfigurationPairMap::const_iterator i
Chris@332:                  = configurePairs.begin(); i != configurePairs.end(); ++i) {
Chris@332:             cmap[i->first.c_str()] = i->second.c_str();
Chris@332:         }
Chris@332:     }
Chris@332: 
Chris@332:     transform.setConfiguration(cmap);
Chris@332: }
Chris@332: 
Chris@332: void
Chris@350: TransformFactory::setPluginParameters(const Transform &transform,
Chris@350:                                       Vamp::PluginBase *plugin)
Chris@350: {
Chris@350:     //!!! check plugin & API version (see e.g. PluginXml::setParameters)
Chris@350: 
Chris@350:     //!!! check that this is the right plugin!
Chris@350: 
Chris@350:     RealTimePluginInstance *rtpi =
Chris@350:         dynamic_cast<RealTimePluginInstance *>(plugin);
Chris@350: 
Chris@350:     if (rtpi) {
Chris@350:         const Transform::ConfigurationMap &cmap = transform.getConfiguration();
Chris@350:         for (Transform::ConfigurationMap::const_iterator i = cmap.begin();
Chris@350:              i != cmap.end(); ++i) {
Chris@350:             rtpi->configure(i->first.toStdString(), i->second.toStdString());
Chris@350:         }
Chris@350:     }
Chris@350: 
Chris@350:     if (transform.getProgram() != "") {
Chris@350:         plugin->selectProgram(transform.getProgram().toStdString());
Chris@350:     }
Chris@350: 
Chris@350:     const Transform::ParameterMap &pmap = transform.getParameters();
Chris@350: 
Chris@350:     Vamp::PluginBase::ParameterList parameters =
Chris@350:         plugin->getParameterDescriptors();
Chris@350: 
Chris@350:     for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
Chris@350:          i != parameters.end(); ++i) {
Chris@350:         QString key = i->identifier.c_str();
Chris@350:         Transform::ParameterMap::const_iterator pmi = pmap.find(key);
Chris@350:         if (pmi != pmap.end()) {
Chris@350:             plugin->setParameter(i->identifier, pmi->second);
Chris@350:         }
Chris@350:     }
Chris@350: }
Chris@350: 
Chris@350: void
Chris@332: TransformFactory::makeContextConsistentWithPlugin(Transform &transform,
Chris@332:                                                   Vamp::PluginBase *plugin)
Chris@332: {
Chris@350:     const Vamp::Plugin *vp = downcastVampPlugin(plugin);
Chris@332: 
Chris@332:     if (!vp) {
Chris@332:         // time domain input for real-time effects plugin
Chris@332:         if (!transform.getBlockSize()) {
Chris@332:             if (!transform.getStepSize()) transform.setStepSize(1024);
Chris@332:             transform.setBlockSize(transform.getStepSize());
Chris@332:         } else {
Chris@332:             transform.setStepSize(transform.getBlockSize());
Chris@332:         }
Chris@332:     } else {
Chris@332:         Vamp::Plugin::InputDomain domain = vp->getInputDomain();
Chris@332:         if (!transform.getStepSize()) {
Chris@332:             transform.setStepSize(vp->getPreferredStepSize());
Chris@332:         }
Chris@332:         if (!transform.getBlockSize()) {
Chris@332:             transform.setBlockSize(vp->getPreferredBlockSize());
Chris@332:         }
Chris@332:         if (!transform.getBlockSize()) {
Chris@332:             transform.setBlockSize(1024);
Chris@332:         }
Chris@332:         if (!transform.getStepSize()) {
Chris@332:             if (domain == Vamp::Plugin::FrequencyDomain) {
Chris@443: //                cerr << "frequency domain, step = " << blockSize/2 << endl;
Chris@332:                 transform.setStepSize(transform.getBlockSize()/2);
Chris@332:             } else {
Chris@443: //                cerr << "time domain, step = " << blockSize/2 << endl;
Chris@332:                 transform.setStepSize(transform.getBlockSize());
Chris@332:             }
Chris@332:         }
Chris@332:     }
Chris@332: }
Chris@332: 
Chris@350: QString
Chris@350: TransformFactory::getPluginConfigurationXml(const Transform &t)
Chris@332: {
Chris@350:     QString xml;
Chris@350: 
Chris@351:     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351:         (t.getIdentifier(), 0);
Chris@350:     if (!plugin) {
Chris@810:         cerr << "TransformFactory::getPluginConfigurationXml: "
Chris@350:                   << "Unable to instantiate plugin for transform \""
Chris@686:                   << t.getIdentifier() << "\"" << endl;
Chris@350:         return xml;
Chris@332:     }
Chris@332: 
Chris@351:     setPluginParameters(t, plugin);
Chris@351: 
Chris@350:     QTextStream out(&xml);
Chris@350:     PluginXml(plugin).toXml(out);
Chris@350:     delete plugin;
Chris@332: 
Chris@350:     return xml;
Chris@350: }
Chris@332: 
Chris@350: void
Chris@350: TransformFactory::setParametersFromPluginConfigurationXml(Transform &t,
Chris@350:                                                           QString xml)
Chris@350: {
Chris@351:     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
Chris@351:         (t.getIdentifier(), 0);
Chris@350:     if (!plugin) {
Chris@810:         cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
Chris@350:                   << "Unable to instantiate plugin for transform \""
Chris@686:                   << t.getIdentifier() << "\"" << endl;
Chris@350:         return;
Chris@332:     }
Chris@332: 
Chris@350:     PluginXml(plugin).setParametersFromXml(xml);
Chris@350:     setParametersFromPlugin(t, plugin);
Chris@350:     delete plugin;
Chris@332: }
Chris@332: 
Chris@443: TransformFactory::SearchResults
Chris@443: TransformFactory::search(QString keyword)
Chris@443: {
Chris@443:     QStringList keywords;
Chris@443:     keywords << keyword;
Chris@443:     return search(keywords);
Chris@443: }
Chris@443: 
Chris@443: TransformFactory::SearchResults
Chris@443: TransformFactory::search(QStringList keywords)
Chris@443: {
Chris@460:     populateTransforms();
Chris@443: 
Chris@447:     if (keywords.size() > 1) {
Chris@447:         // Additional score for all keywords in a row
Chris@447:         keywords.push_back(keywords.join(" "));
Chris@447:     }
Chris@447: 
Chris@443:     SearchResults results;
Chris@457:     TextMatcher matcher;
Chris@443: 
Chris@443:     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
Chris@443:          i != m_transforms.end(); ++i) {
Chris@443: 
Chris@457:         TextMatcher::Match match;
Chris@443: 
Chris@457:         match.key = i->first;
Chris@443:         
Chris@487:         matcher.test(match, keywords,
Chris@487:                      getTransformTypeName(i->second.type),
Chris@487:                      tr("Plugin type"), 5);
Chris@487: 
Chris@457:         matcher.test(match, keywords, i->second.category, tr("Category"), 20);
Chris@457:         matcher.test(match, keywords, i->second.identifier, tr("System Identifier"), 6);
Chris@457:         matcher.test(match, keywords, i->second.name, tr("Name"), 30);
Chris@457:         matcher.test(match, keywords, i->second.description, tr("Description"), 20);
Chris@457:         matcher.test(match, keywords, i->second.maker, tr("Maker"), 10);
Chris@457:         matcher.test(match, keywords, i->second.units, tr("Units"), 10);
Chris@457: 
Chris@457:         if (match.score > 0) results[i->first] = match;
Chris@457:     }
Chris@457: 
Chris@460:     if (!m_uninstalledTransformsMutex.tryLock()) {
Chris@460:         // uninstalled transforms are being populated; this may take some time,
Chris@484:         // and they aren't critical, but we will speed them up if necessary
Chris@810:         cerr << "TransformFactory::search: Uninstalled transforms mutex is held, skipping" << endl;
Chris@484:         m_populatingSlowly = false;
Chris@460:         return results;
Chris@460:     }
Chris@460: 
Chris@460:     if (!m_uninstalledTransformsPopulated) {
Chris@843:         cerr << "WARNING: TransformFactory::search: Uninstalled transforms are not populated yet" << endl
Chris@460:                   << "and are not being populated either -- was the thread not started correctly?" << endl;
Chris@460:         m_uninstalledTransformsMutex.unlock();
Chris@460:         return results;
Chris@460:     }
Chris@460: 
Chris@460:     m_uninstalledTransformsMutex.unlock();
Chris@457: 
Chris@457:     for (TransformDescriptionMap::const_iterator i = m_uninstalledTransforms.begin();
Chris@457:          i != m_uninstalledTransforms.end(); ++i) {
Chris@457: 
Chris@457:         TextMatcher::Match match;
Chris@457: 
Chris@457:         match.key = i->first;
Chris@457:         
Chris@487:         matcher.test(match, keywords,
Chris@487:                      getTransformTypeName(i->second.type),
Chris@487:                      tr("Plugin type"), 2);
Chris@487: 
Chris@457:         matcher.test(match, keywords, i->second.category, tr("Category"), 10);
Chris@457:         matcher.test(match, keywords, i->second.identifier, tr("System Identifier"), 3);
Chris@457:         matcher.test(match, keywords, i->second.name, tr("Name"), 15);
Chris@457:         matcher.test(match, keywords, i->second.description, tr("Description"), 10);
Chris@457:         matcher.test(match, keywords, i->second.maker, tr("Maker"), 5);
Chris@457:         matcher.test(match, keywords, i->second.units, tr("Units"), 5);
Chris@443: 
Chris@443:         if (match.score > 0) results[i->first] = match;
Chris@443:     }
Chris@443: 
Chris@443:     return results;
Chris@443: }
Chris@443: