svcore  1.9
TransformFactory.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam and QMUL.
00008    
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "TransformFactory.h"
00017 
00018 #include "plugin/FeatureExtractionPluginFactory.h"
00019 #include "plugin/RealTimePluginFactory.h"
00020 #include "plugin/RealTimePluginInstance.h"
00021 #include "plugin/PluginXml.h"
00022 
00023 #include <vamp-hostsdk/Plugin.h>
00024 #include <vamp-hostsdk/PluginHostAdapter.h>
00025 #include <vamp-hostsdk/PluginWrapper.h>
00026 
00027 #include "rdf/PluginRDFIndexer.h"
00028 #include "rdf/PluginRDFDescription.h"
00029 
00030 #include "base/XmlExportable.h"
00031 
00032 #include <iostream>
00033 #include <set>
00034 
00035 #include <QRegExp>
00036 #include <QTextStream>
00037 
00038 #include "base/Thread.h"
00039 
00040 //#define DEBUG_TRANSFORM_FACTORY 1
00041 
00042 TransformFactory *
00043 TransformFactory::m_instance = new TransformFactory;
00044 
00045 TransformFactory *
00046 TransformFactory::getInstance()
00047 {
00048     return m_instance;
00049 }
00050 
00051 void
00052 TransformFactory::deleteInstance()
00053 {
00054     SVDEBUG << "TransformFactory::deleteInstance called" << endl;
00055     delete m_instance;
00056     m_instance = 0;
00057 }
00058 
00059 TransformFactory::TransformFactory() :
00060     m_transformsPopulated(false),
00061     m_uninstalledTransformsPopulated(false),
00062     m_thread(0),
00063     m_exiting(false),
00064     m_populatingSlowly(false)
00065 {
00066 }
00067 
00068 TransformFactory::~TransformFactory()
00069 {
00070     m_exiting = true;
00071     if (m_thread) {
00072 #ifdef DEBUG_TRANSFORM_FACTORY
00073         SVDEBUG << "TransformFactory::~TransformFactory: waiting on thread" << endl;
00074 #endif
00075         m_thread->wait();
00076         delete m_thread;
00077 #ifdef DEBUG_TRANSFORM_FACTORY
00078         SVDEBUG << "TransformFactory::~TransformFactory: waited and done" << endl;
00079 #endif
00080     }
00081 }
00082 
00083 void
00084 TransformFactory::startPopulationThread()
00085 {
00086     m_uninstalledTransformsMutex.lock();
00087 
00088     if (m_thread) {
00089         m_uninstalledTransformsMutex.unlock();
00090         return;
00091     }
00092     m_thread = new UninstalledTransformsPopulateThread(this);
00093 
00094     m_uninstalledTransformsMutex.unlock();
00095 
00096     m_thread->start();
00097 }
00098 
00099 void
00100 TransformFactory::UninstalledTransformsPopulateThread::run()
00101 {
00102     m_factory->m_populatingSlowly = true;
00103     sleep(1);
00104     m_factory->populateUninstalledTransforms();
00105 }
00106 
00107 TransformList
00108 TransformFactory::getAllTransformDescriptions()
00109 {
00110     populateTransforms();
00111 
00112     std::set<TransformDescription> dset;
00113     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
00114          i != m_transforms.end(); ++i) {
00115 #ifdef DEBUG_TRANSFORM_FACTORY
00116         cerr << "inserting transform into set: id = " << i->second.identifier << endl;
00117 #endif
00118         dset.insert(i->second);
00119     }
00120 
00121     TransformList list;
00122     for (std::set<TransformDescription>::const_iterator i = dset.begin();
00123          i != dset.end(); ++i) {
00124 #ifdef DEBUG_TRANSFORM_FACTORY
00125         cerr << "inserting transform into list: id = " << i->identifier << endl;
00126 #endif
00127         list.push_back(*i);
00128     }
00129 
00130     return list;
00131 }
00132 
00133 TransformDescription
00134 TransformFactory::getTransformDescription(TransformId id)
00135 {
00136     populateTransforms();
00137 
00138     if (m_transforms.find(id) == m_transforms.end()) {
00139         return TransformDescription();
00140     }
00141 
00142     return m_transforms[id];
00143 }
00144 
00145 bool
00146 TransformFactory::haveInstalledTransforms()
00147 {
00148     populateTransforms();
00149     return !m_transforms.empty();
00150 }
00151 
00152 TransformList
00153 TransformFactory::getUninstalledTransformDescriptions()
00154 {
00155     m_populatingSlowly = false;
00156     populateUninstalledTransforms();
00157     
00158     std::set<TransformDescription> dset;
00159     for (TransformDescriptionMap::const_iterator i = m_uninstalledTransforms.begin();
00160          i != m_uninstalledTransforms.end(); ++i) {
00161 #ifdef DEBUG_TRANSFORM_FACTORY
00162         cerr << "inserting transform into set: id = " << i->second.identifier << endl;
00163 #endif
00164         dset.insert(i->second);
00165     }
00166 
00167     TransformList list;
00168     for (std::set<TransformDescription>::const_iterator i = dset.begin();
00169          i != dset.end(); ++i) {
00170 #ifdef DEBUG_TRANSFORM_FACTORY
00171         cerr << "inserting transform into uninstalled list: id = " << i->identifier << endl;
00172 #endif
00173         list.push_back(*i);
00174     }
00175 
00176     return list;
00177 }
00178 
00179 TransformDescription
00180 TransformFactory::getUninstalledTransformDescription(TransformId id)
00181 {
00182     m_populatingSlowly = false;
00183     populateUninstalledTransforms();
00184 
00185     if (m_uninstalledTransforms.find(id) == m_uninstalledTransforms.end()) {
00186         return TransformDescription();
00187     }
00188 
00189     return m_uninstalledTransforms[id];
00190 }
00191 
00192 bool
00193 TransformFactory::haveUninstalledTransforms(bool waitForCheckToComplete)
00194 {
00195     if (waitForCheckToComplete) {
00196         populateUninstalledTransforms();
00197     } else {
00198         if (!m_uninstalledTransformsMutex.tryLock()) {
00199             return false;
00200         }
00201         if (!m_uninstalledTransformsPopulated) {
00202             m_uninstalledTransformsMutex.unlock();
00203             return false;
00204         }
00205         m_uninstalledTransformsMutex.unlock();
00206     }
00207 
00208     return !m_uninstalledTransforms.empty();
00209 }
00210 
00211 TransformFactory::TransformInstallStatus
00212 TransformFactory::getTransformInstallStatus(TransformId id)
00213 {
00214     populateTransforms();
00215 
00216     if (m_transforms.find(id) != m_transforms.end()) {
00217         return TransformInstalled;
00218     }
00219     
00220     if (!m_uninstalledTransformsMutex.tryLock()) {
00221         // uninstalled transforms are being populated; this may take some time,
00222         // and they aren't critical
00223         return TransformUnknown;
00224     }
00225 
00226     if (!m_uninstalledTransformsPopulated) {
00227         m_uninstalledTransformsMutex.unlock();
00228         m_populatingSlowly = false;
00229         populateUninstalledTransforms();
00230         m_uninstalledTransformsMutex.lock();
00231     }
00232 
00233     if (m_uninstalledTransforms.find(id) != m_uninstalledTransforms.end()) {
00234         m_uninstalledTransformsMutex.unlock();
00235         return TransformNotInstalled;
00236     }
00237 
00238     m_uninstalledTransformsMutex.unlock();
00239     return TransformUnknown;
00240 }
00241     
00242 
00243 std::vector<TransformDescription::Type>
00244 TransformFactory::getAllTransformTypes()
00245 {
00246     populateTransforms();
00247 
00248     std::set<TransformDescription::Type> types;
00249     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
00250          i != m_transforms.end(); ++i) {
00251         types.insert(i->second.type);
00252     }
00253 
00254     std::vector<TransformDescription::Type> rv;
00255     for (std::set<TransformDescription::Type>::iterator i = types.begin(); i != types.end(); ++i) {
00256         rv.push_back(*i);
00257     }
00258 
00259     return rv;
00260 }
00261 
00262 std::vector<QString>
00263 TransformFactory::getTransformCategories(TransformDescription::Type transformType)
00264 {
00265     populateTransforms();
00266 
00267     std::set<QString> categories;
00268     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
00269          i != m_transforms.end(); ++i) {
00270         if (i->second.type == transformType) {
00271             categories.insert(i->second.category);
00272         }
00273     }
00274 
00275     bool haveEmpty = false;
00276     
00277     std::vector<QString> rv;
00278     for (std::set<QString>::iterator i = categories.begin(); 
00279          i != categories.end(); ++i) {
00280         if (*i != "") rv.push_back(*i);
00281         else haveEmpty = true;
00282     }
00283 
00284     if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
00285 
00286     return rv;
00287 }
00288 
00289 std::vector<QString>
00290 TransformFactory::getTransformMakers(TransformDescription::Type transformType)
00291 {
00292     populateTransforms();
00293 
00294     std::set<QString> makers;
00295     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
00296          i != m_transforms.end(); ++i) {
00297         if (i->second.type == transformType) {
00298             makers.insert(i->second.maker);
00299         }
00300     }
00301 
00302     bool haveEmpty = false;
00303     
00304     std::vector<QString> rv;
00305     for (std::set<QString>::iterator i = makers.begin(); 
00306          i != makers.end(); ++i) {
00307         if (*i != "") rv.push_back(*i);
00308         else haveEmpty = true;
00309     }
00310 
00311     if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
00312 
00313     return rv;
00314 }
00315 
00316 QString
00317 TransformFactory::getTransformTypeName(TransformDescription::Type type) const
00318 {
00319     switch (type) {
00320     case TransformDescription::Analysis: return tr("Analysis");
00321     case TransformDescription::Effects: return tr("Effects");
00322     case TransformDescription::EffectsData: return tr("Effects Data");
00323     case TransformDescription::Generator: return tr("Generator");
00324     case TransformDescription::UnknownType: return tr("Other");
00325     }
00326     return tr("Other");
00327 }
00328 
00329 void
00330 TransformFactory::populateTransforms()
00331 {
00332     MutexLocker locker(&m_transformsMutex,
00333                        "TransformFactory::populateTransforms");
00334     if (m_transformsPopulated) {
00335         return;
00336     }
00337 
00338     TransformDescriptionMap transforms;
00339 
00340     populateFeatureExtractionPlugins(transforms);
00341     if (m_exiting) return;
00342     populateRealTimePlugins(transforms);
00343     if (m_exiting) return;
00344 
00345     // disambiguate plugins with similar names
00346 
00347     std::map<QString, int> names;
00348     std::map<QString, QString> pluginSources;
00349     std::map<QString, QString> pluginMakers;
00350 
00351     for (TransformDescriptionMap::iterator i = transforms.begin();
00352          i != transforms.end(); ++i) {
00353 
00354         TransformDescription desc = i->second;
00355 
00356         QString td = desc.name;
00357         QString tn = td.section(": ", 0, 0);
00358         QString pn = desc.identifier.section(":", 1, 1);
00359 
00360         if (pluginSources.find(tn) != pluginSources.end()) {
00361             if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
00362                 ++names[tn];
00363             }
00364         } else {
00365             ++names[tn];
00366             pluginSources[tn] = pn;
00367             pluginMakers[tn] = desc.maker;
00368         }
00369     }
00370 
00371     std::map<QString, int> counts;
00372     m_transforms.clear();
00373 
00374     for (TransformDescriptionMap::iterator i = transforms.begin();
00375          i != transforms.end(); ++i) {
00376 
00377         TransformDescription desc = i->second;
00378         QString identifier = desc.identifier;
00379         QString maker = desc.maker;
00380 
00381         QString td = desc.name;
00382         QString tn = td.section(": ", 0, 0);
00383         QString to = td.section(": ", 1);
00384 
00385         if (names[tn] > 1) {
00386             maker.replace(QRegExp(tr(" [\\(<].*$")), "");
00387             tn = QString("%1 [%2]").arg(tn).arg(maker);
00388         }
00389 
00390         if (to != "") {
00391             desc.name = QString("%1: %2").arg(tn).arg(to);
00392         } else {
00393             desc.name = tn;
00394         }
00395 
00396         m_transforms[identifier] = desc;
00397     }       
00398 
00399     m_transformsPopulated = true;
00400 }
00401 
00402 void
00403 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
00404 {
00405     std::vector<QString> plugs =
00406         FeatureExtractionPluginFactory::getAllPluginIdentifiers();
00407     if (m_exiting) return;
00408 
00409     for (int i = 0; i < (int)plugs.size(); ++i) {
00410 
00411         QString pluginId = plugs[i];
00412 
00413         FeatureExtractionPluginFactory *factory =
00414             FeatureExtractionPluginFactory::instanceFor(pluginId);
00415 
00416         if (!factory) {
00417             cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId << endl;
00418             continue;
00419         }
00420 
00421         Vamp::Plugin *plugin = 
00422             factory->instantiatePlugin(pluginId, 44100);
00423 
00424         if (!plugin) {
00425             cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId << endl;
00426             continue;
00427         }
00428                 
00429         QString pluginName = plugin->getName().c_str();
00430         QString category = factory->getPluginCategory(pluginId);
00431 
00432         Vamp::Plugin::OutputList outputs =
00433             plugin->getOutputDescriptors();
00434 
00435         for (int j = 0; j < (int)outputs.size(); ++j) {
00436 
00437             QString transformId = QString("%1:%2")
00438                     .arg(pluginId).arg(outputs[j].identifier.c_str());
00439 
00440             QString userName;
00441             QString friendlyName;
00442             QString units = outputs[j].unit.c_str();
00443             QString description = plugin->getDescription().c_str();
00444             QString maker = plugin->getMaker().c_str();
00445             if (maker == "") maker = tr("<unknown maker>");
00446 
00447             QString longDescription = description;
00448 
00449             if (longDescription == "") {
00450                 if (outputs.size() == 1) {
00451                     longDescription = tr("Extract features using \"%1\" plugin (from %2)")
00452                         .arg(pluginName).arg(maker);
00453                 } else {
00454                     longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
00455                         .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
00456                 }
00457             } else {
00458                 if (outputs.size() == 1) {
00459                     longDescription = tr("%1 using \"%2\" plugin (from %3)")
00460                         .arg(longDescription).arg(pluginName).arg(maker);
00461                 } else {
00462                     longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
00463                         .arg(longDescription).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
00464                 }
00465             }                    
00466 
00467             if (outputs.size() == 1) {
00468                 userName = pluginName;
00469                 friendlyName = pluginName;
00470             } else {
00471                 userName = QString("%1: %2")
00472                     .arg(pluginName)
00473                     .arg(outputs[j].name.c_str());
00474                 friendlyName = outputs[j].name.c_str();
00475             }
00476 
00477             bool configurable = (!plugin->getPrograms().empty() ||
00478                                  !plugin->getParameterDescriptors().empty());
00479 
00480 #ifdef DEBUG_TRANSFORM_FACTORY
00481             cerr << "Feature extraction plugin transform: " << transformId << " friendly name: " << friendlyName << endl;
00482 #endif
00483 
00484             transforms[transformId] = 
00485                 TransformDescription(TransformDescription::Analysis,
00486                                      category,
00487                                      transformId,
00488                                      userName,
00489                                      friendlyName,
00490                                      description,
00491                                      longDescription,
00492                                      maker,
00493                                      units,
00494                                      configurable);
00495         }
00496 
00497         delete plugin;
00498     }
00499 }
00500 
00501 void
00502 TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
00503 {
00504     std::vector<QString> plugs =
00505         RealTimePluginFactory::getAllPluginIdentifiers();
00506     if (m_exiting) return;
00507 
00508     static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
00509 
00510     for (int i = 0; i < (int)plugs.size(); ++i) {
00511         
00512         QString pluginId = plugs[i];
00513 
00514         RealTimePluginFactory *factory =
00515             RealTimePluginFactory::instanceFor(pluginId);
00516 
00517         if (!factory) {
00518             cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId << endl;
00519             continue;
00520         }
00521 
00522         const RealTimePluginDescriptor *descriptor =
00523             factory->getPluginDescriptor(pluginId);
00524 
00525         if (!descriptor) {
00526             cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId << endl;
00527             continue;
00528         }
00529         
00531 //            descriptor->audioInputPortCount == 0) continue;
00532 
00533 //        cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << endl;
00534         
00535         QString pluginName = descriptor->name.c_str();
00536         QString category = factory->getPluginCategory(pluginId);
00537         bool configurable = (descriptor->parameterCount > 0);
00538         QString maker = descriptor->maker.c_str();
00539         if (maker == "") maker = tr("<unknown maker>");
00540 
00541         if (descriptor->audioInputPortCount > 0) {
00542 
00543             for (int j = 0; j < (int)descriptor->controlOutputPortCount; ++j) {
00544 
00545                 QString transformId = QString("%1:%2").arg(pluginId).arg(j);
00546                 QString userName;
00547                 QString units;
00548                 QString portName;
00549 
00550                 if (j < (int)descriptor->controlOutputPortNames.size() &&
00551                     descriptor->controlOutputPortNames[j] != "") {
00552 
00553                     portName = descriptor->controlOutputPortNames[j].c_str();
00554 
00555                     userName = tr("%1: %2")
00556                         .arg(pluginName)
00557                         .arg(portName);
00558 
00559                     if (unitRE.indexIn(portName) >= 0) {
00560                         units = unitRE.cap(1);
00561                     }
00562 
00563                 } else if (descriptor->controlOutputPortCount > 1) {
00564 
00565                     userName = tr("%1: Output %2")
00566                         .arg(pluginName)
00567                         .arg(j + 1);
00568 
00569                 } else {
00570 
00571                     userName = pluginName;
00572                 }
00573 
00574                 QString description;
00575 
00576                 if (portName != "") {
00577                     description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
00578                         .arg(portName)
00579                         .arg(pluginName)
00580                         .arg(maker);
00581                 } else {
00582                     description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
00583                         .arg(j + 1)
00584                         .arg(pluginName)
00585                         .arg(maker);
00586                 }
00587 
00588                 transforms[transformId] = 
00589                     TransformDescription(TransformDescription::EffectsData,
00590                                          category,
00591                                          transformId,
00592                                          userName,
00593                                          userName,
00594                                          "",
00595                                          description,
00596                                          maker,
00597                                          units,
00598                                          configurable);
00599             }
00600         }
00601 
00602         if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
00603 
00604             if (descriptor->audioOutputPortCount > 0) {
00605 
00606                 QString transformId = QString("%1:A").arg(pluginId);
00607                 TransformDescription::Type type = TransformDescription::Effects;
00608 
00609                 QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
00610                     .arg(pluginName)
00611                     .arg(maker);
00612 
00613                 if (descriptor->audioInputPortCount == 0) {
00614                     type = TransformDescription::Generator;
00615                     QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
00616                         .arg(pluginName)
00617                         .arg(maker);
00618                 }
00619 
00620                 transforms[transformId] =
00621                     TransformDescription(type,
00622                                          category,
00623                                          transformId,
00624                                          pluginName,
00625                                          pluginName,
00626                                          "",
00627                                          description,
00628                                          maker,
00629                                          "",
00630                                          configurable);
00631             }
00632         }
00633     }
00634 }
00635 
00636 void
00637 TransformFactory::populateUninstalledTransforms()
00638 {
00639     if (m_exiting) return;
00640 
00641     populateTransforms();
00642     if (m_exiting) return;
00643 
00644     MutexLocker locker(&m_uninstalledTransformsMutex,
00645                        "TransformFactory::populateUninstalledTransforms");
00646     if (m_uninstalledTransformsPopulated) return;
00647 
00648     PluginRDFIndexer::getInstance()->indexConfiguredURLs();
00649     if (m_exiting) return;
00650 
00652 
00653     QStringList ids = PluginRDFIndexer::getInstance()->getIndexedPluginIds();
00654     
00655     for (QStringList::const_iterator i = ids.begin(); i != ids.end(); ++i) {
00656         
00657         PluginRDFDescription desc(*i);
00658 
00659         QString name = desc.getPluginName();
00660 #ifdef DEBUG_TRANSFORM_FACTORY
00661         if (name == "") {
00662             cerr << "TransformFactory::populateUninstalledTransforms: "
00663                  << "No name available for plugin " << *i
00664                  << ", skipping" << endl;
00665             continue;
00666         }
00667 #endif
00668 
00669         QString description = desc.getPluginDescription();
00670         QString maker = desc.getPluginMaker();
00671         QString infoUrl = desc.getPluginInfoURL();
00672 
00673         QStringList oids = desc.getOutputIds();
00674 
00675         for (QStringList::const_iterator j = oids.begin(); j != oids.end(); ++j) {
00676 
00677             TransformId tid = Transform::getIdentifierForPluginOutput(*i, *j);
00678             
00679             if (m_transforms.find(tid) != m_transforms.end()) {
00680 #ifdef DEBUG_TRANSFORM_FACTORY
00681                 cerr << "TransformFactory::populateUninstalledTransforms: "
00682                           << tid << " is installed; adding info url if appropriate, skipping rest" << endl;
00683 #endif
00684                 if (infoUrl != "") {
00685                     if (m_transforms[tid].infoUrl == "") {
00686                         m_transforms[tid].infoUrl = infoUrl;
00687                     }
00688                 }
00689                 continue;
00690             }
00691 
00692 #ifdef DEBUG_TRANSFORM_FACTORY
00693             cerr << "TransformFactory::populateUninstalledTransforms: "
00694                       << "adding " << tid << endl;
00695 #endif
00696 
00697             QString oname = desc.getOutputName(*j);
00698             if (oname == "") oname = *j;
00699             
00700             TransformDescription td;
00701             td.type = TransformDescription::Analysis;
00702             td.category = "";
00703             td.identifier = tid;
00704 
00705             if (oids.size() == 1) {
00706                 td.name = name;
00707             } else if (name != "") {
00708                 td.name = tr("%1: %2").arg(name).arg(oname);
00709             }
00710 
00711             QString longDescription = description;
00713             if (longDescription == "") {
00714                 if (oids.size() == 1) {
00715                     longDescription = tr("Extract features using \"%1\" plugin (from %2)")
00716                         .arg(name).arg(maker);
00717                 } else {
00718                     longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
00719                         .arg(oname).arg(name).arg(maker);
00720                 }
00721             } else {
00722                 if (oids.size() == 1) {
00723                     longDescription = tr("%1 using \"%2\" plugin (from %3)")
00724                         .arg(longDescription).arg(name).arg(maker);
00725                 } else {
00726                     longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
00727                         .arg(longDescription).arg(oname).arg(name).arg(maker);
00728                 }
00729             }                    
00730 
00731             td.friendlyName = name; 
00732             td.description = description;
00733             td.longDescription = longDescription;
00734             td.maker = maker;
00735             td.infoUrl = infoUrl;
00736             td.units = "";
00737             td.configurable = false;
00738 
00739             m_uninstalledTransforms[tid] = td;
00740         }
00741 
00742         if (m_exiting) return;
00743     }
00744 
00745     m_uninstalledTransformsPopulated = true;
00746 
00747 #ifdef DEBUG_TRANSFORM_FACTORY
00748     cerr << "populateUninstalledTransforms exiting" << endl;
00749 #endif
00750 }
00751 
00752 Transform
00753 TransformFactory::getDefaultTransformFor(TransformId id, int rate)
00754 {
00755     Transform t;
00756     t.setIdentifier(id);
00757     if (rate != 0) t.setSampleRate(rate);
00758 
00759     Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate);
00760 
00761     if (plugin) {
00762         t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion()));
00763         setParametersFromPlugin(t, plugin);
00764         makeContextConsistentWithPlugin(t, plugin);
00765         delete plugin;
00766     }
00767 
00768     return t;
00769 }
00770 
00771 Vamp::PluginBase *
00772 TransformFactory::instantiatePluginFor(const Transform &transform)
00773 {
00774     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
00775         (transform.getIdentifier(), transform.getSampleRate());
00776 
00777     if (plugin) {
00778         setPluginParameters(transform, plugin);
00779     }
00780 
00781     return plugin;
00782 }
00783 
00784 Vamp::PluginBase *
00785 TransformFactory::instantiateDefaultPluginFor(TransformId identifier, int rate)
00786 {
00787     Transform t;
00788     t.setIdentifier(identifier);
00789     if (rate == 0) rate = 44100;
00790     QString pluginId = t.getPluginIdentifier();
00791 
00792     Vamp::PluginBase *plugin = 0;
00793 
00794     if (t.getType() == Transform::FeatureExtraction) {
00795 
00796         FeatureExtractionPluginFactory *factory = 
00797             FeatureExtractionPluginFactory::instanceFor(pluginId);
00798 
00799         if (factory) {
00800             plugin = factory->instantiatePlugin(pluginId, rate);
00801         }
00802 
00803     } else {
00804 
00805         RealTimePluginFactory *factory = 
00806             RealTimePluginFactory::instanceFor(pluginId);
00807 
00808         if (factory) {
00809             plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1);
00810         }
00811     }
00812 
00813     return plugin;
00814 }
00815 
00816 Vamp::Plugin *
00817 TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin)
00818 {
00819     Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin);
00820     if (!vp) {
00821 //        cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << endl;
00822         vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); 
00823 }
00824     if (!vp) {
00825 //        cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << endl;
00826         vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); 
00827     }
00828     if (!vp) {
00829 //        cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << endl;
00830     }
00831     return vp;
00832 }
00833 
00834 bool
00835 TransformFactory::haveTransform(TransformId identifier)
00836 {
00837     populateTransforms();
00838     return (m_transforms.find(identifier) != m_transforms.end());
00839 }
00840 
00841 QString
00842 TransformFactory::getTransformName(TransformId identifier)
00843 {
00844     if (m_transforms.find(identifier) != m_transforms.end()) {
00845         return m_transforms[identifier].name;
00846     } else return "";
00847 }
00848 
00849 QString
00850 TransformFactory::getTransformFriendlyName(TransformId identifier)
00851 {
00852     if (m_transforms.find(identifier) != m_transforms.end()) {
00853         return m_transforms[identifier].friendlyName;
00854     } else return "";
00855 }
00856 
00857 QString
00858 TransformFactory::getTransformUnits(TransformId identifier)
00859 {
00860     if (m_transforms.find(identifier) != m_transforms.end()) {
00861         return m_transforms[identifier].units;
00862     } else return "";
00863 }
00864 
00865 QString
00866 TransformFactory::getTransformInfoUrl(TransformId identifier)
00867 {
00868     if (m_transforms.find(identifier) != m_transforms.end()) {
00869         return m_transforms[identifier].infoUrl;
00870     } else return "";
00871 }
00872 
00873 Vamp::Plugin::InputDomain
00874 TransformFactory::getTransformInputDomain(TransformId identifier)
00875 {
00876     Transform transform;
00877     transform.setIdentifier(identifier);
00878 
00879     if (transform.getType() != Transform::FeatureExtraction) {
00880         return Vamp::Plugin::TimeDomain;
00881     }
00882 
00883     Vamp::Plugin *plugin =
00884         downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0));
00885 
00886     if (plugin) {
00887         Vamp::Plugin::InputDomain d = plugin->getInputDomain();
00888         delete plugin;
00889         return d;
00890     }
00891 
00892     return Vamp::Plugin::TimeDomain;
00893 }
00894 
00895 bool
00896 TransformFactory::isTransformConfigurable(TransformId identifier)
00897 {
00898     if (m_transforms.find(identifier) != m_transforms.end()) {
00899         return m_transforms[identifier].configurable;
00900     } else return false;
00901 }
00902 
00903 bool
00904 TransformFactory::getTransformChannelRange(TransformId identifier,
00905                                            int &min, int &max)
00906 {
00907     QString id = identifier.section(':', 0, 2);
00908 
00909     if (FeatureExtractionPluginFactory::instanceFor(id)) {
00910 
00911         Vamp::Plugin *plugin = 
00912             FeatureExtractionPluginFactory::instanceFor(id)->
00913             instantiatePlugin(id, 44100);
00914         if (!plugin) return false;
00915 
00916         min = plugin->getMinChannelCount();
00917         max = plugin->getMaxChannelCount();
00918         delete plugin;
00919 
00920         return true;
00921 
00922     } else if (RealTimePluginFactory::instanceFor(id)) {
00923 
00924         // don't need to instantiate
00925 
00926         const RealTimePluginDescriptor *descriptor = 
00927             RealTimePluginFactory::instanceFor(id)->
00928             getPluginDescriptor(id);
00929         if (!descriptor) return false;
00930 
00931         min = descriptor->audioInputPortCount;
00932         max = descriptor->audioInputPortCount;
00933 
00934         return true;
00935     }
00936 
00937     return false;
00938 }
00939 
00940 void
00941 TransformFactory::setParametersFromPlugin(Transform &transform,
00942                                           Vamp::PluginBase *plugin)
00943 {
00944     Transform::ParameterMap pmap;
00945 
00947 
00949 
00950     Vamp::PluginBase::ParameterList parameters =
00951         plugin->getParameterDescriptors();
00952 
00953     for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
00954          i != parameters.end(); ++i) {
00955         pmap[i->identifier.c_str()] = plugin->getParameter(i->identifier);
00956 //        cerr << "TransformFactory::setParametersFromPlugin: parameter "
00957 //                  << i->identifier << " -> value " <<
00958 //            pmap[i->identifier.c_str()] << endl;
00959     }
00960 
00961     transform.setParameters(pmap);
00962 
00963     if (plugin->getPrograms().empty()) {
00964         transform.setProgram("");
00965     } else {
00966         transform.setProgram(plugin->getCurrentProgram().c_str());
00967     }
00968 
00969     RealTimePluginInstance *rtpi =
00970         dynamic_cast<RealTimePluginInstance *>(plugin);
00971 
00972     Transform::ConfigurationMap cmap;
00973 
00974     if (rtpi) {
00975 
00976         RealTimePluginInstance::ConfigurationPairMap configurePairs =
00977             rtpi->getConfigurePairs();
00978 
00979         for (RealTimePluginInstance::ConfigurationPairMap::const_iterator i
00980                  = configurePairs.begin(); i != configurePairs.end(); ++i) {
00981             cmap[i->first.c_str()] = i->second.c_str();
00982         }
00983     }
00984 
00985     transform.setConfiguration(cmap);
00986 }
00987 
00988 void
00989 TransformFactory::setPluginParameters(const Transform &transform,
00990                                       Vamp::PluginBase *plugin)
00991 {
00993 
00995 
00996     RealTimePluginInstance *rtpi =
00997         dynamic_cast<RealTimePluginInstance *>(plugin);
00998 
00999     if (rtpi) {
01000         const Transform::ConfigurationMap &cmap = transform.getConfiguration();
01001         for (Transform::ConfigurationMap::const_iterator i = cmap.begin();
01002              i != cmap.end(); ++i) {
01003             rtpi->configure(i->first.toStdString(), i->second.toStdString());
01004         }
01005     }
01006 
01007     if (transform.getProgram() != "") {
01008         plugin->selectProgram(transform.getProgram().toStdString());
01009     }
01010 
01011     const Transform::ParameterMap &pmap = transform.getParameters();
01012 
01013     Vamp::PluginBase::ParameterList parameters =
01014         plugin->getParameterDescriptors();
01015 
01016     for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
01017          i != parameters.end(); ++i) {
01018         QString key = i->identifier.c_str();
01019         Transform::ParameterMap::const_iterator pmi = pmap.find(key);
01020         if (pmi != pmap.end()) {
01021             plugin->setParameter(i->identifier, pmi->second);
01022         }
01023     }
01024 }
01025 
01026 void
01027 TransformFactory::makeContextConsistentWithPlugin(Transform &transform,
01028                                                   Vamp::PluginBase *plugin)
01029 {
01030     const Vamp::Plugin *vp = downcastVampPlugin(plugin);
01031 
01032     if (!vp) {
01033         // time domain input for real-time effects plugin
01034         if (!transform.getBlockSize()) {
01035             if (!transform.getStepSize()) transform.setStepSize(1024);
01036             transform.setBlockSize(transform.getStepSize());
01037         } else {
01038             transform.setStepSize(transform.getBlockSize());
01039         }
01040     } else {
01041         Vamp::Plugin::InputDomain domain = vp->getInputDomain();
01042         if (!transform.getStepSize()) {
01043             transform.setStepSize(vp->getPreferredStepSize());
01044         }
01045         if (!transform.getBlockSize()) {
01046             transform.setBlockSize(vp->getPreferredBlockSize());
01047         }
01048         if (!transform.getBlockSize()) {
01049             transform.setBlockSize(1024);
01050         }
01051         if (!transform.getStepSize()) {
01052             if (domain == Vamp::Plugin::FrequencyDomain) {
01053 //                cerr << "frequency domain, step = " << blockSize/2 << endl;
01054                 transform.setStepSize(transform.getBlockSize()/2);
01055             } else {
01056 //                cerr << "time domain, step = " << blockSize/2 << endl;
01057                 transform.setStepSize(transform.getBlockSize());
01058             }
01059         }
01060     }
01061 }
01062 
01063 QString
01064 TransformFactory::getPluginConfigurationXml(const Transform &t)
01065 {
01066     QString xml;
01067 
01068     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
01069         (t.getIdentifier(), 0);
01070     if (!plugin) {
01071         cerr << "TransformFactory::getPluginConfigurationXml: "
01072                   << "Unable to instantiate plugin for transform \""
01073                   << t.getIdentifier() << "\"" << endl;
01074         return xml;
01075     }
01076 
01077     setPluginParameters(t, plugin);
01078 
01079     QTextStream out(&xml);
01080     PluginXml(plugin).toXml(out);
01081     delete plugin;
01082 
01083     return xml;
01084 }
01085 
01086 void
01087 TransformFactory::setParametersFromPluginConfigurationXml(Transform &t,
01088                                                           QString xml)
01089 {
01090     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
01091         (t.getIdentifier(), 0);
01092     if (!plugin) {
01093         cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
01094                   << "Unable to instantiate plugin for transform \""
01095                   << t.getIdentifier() << "\"" << endl;
01096         return;
01097     }
01098 
01099     PluginXml(plugin).setParametersFromXml(xml);
01100     setParametersFromPlugin(t, plugin);
01101     delete plugin;
01102 }
01103 
01104 TransformFactory::SearchResults
01105 TransformFactory::search(QString keyword)
01106 {
01107     QStringList keywords;
01108     keywords << keyword;
01109     return search(keywords);
01110 }
01111 
01112 TransformFactory::SearchResults
01113 TransformFactory::search(QStringList keywords)
01114 {
01115     populateTransforms();
01116 
01117     if (keywords.size() > 1) {
01118         // Additional score for all keywords in a row
01119         keywords.push_back(keywords.join(" "));
01120     }
01121 
01122     SearchResults results;
01123     TextMatcher matcher;
01124 
01125     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
01126          i != m_transforms.end(); ++i) {
01127 
01128         TextMatcher::Match match;
01129 
01130         match.key = i->first;
01131         
01132         matcher.test(match, keywords,
01133                      getTransformTypeName(i->second.type),
01134                      tr("Plugin type"), 5);
01135 
01136         matcher.test(match, keywords, i->second.category, tr("Category"), 20);
01137         matcher.test(match, keywords, i->second.identifier, tr("System Identifier"), 6);
01138         matcher.test(match, keywords, i->second.name, tr("Name"), 30);
01139         matcher.test(match, keywords, i->second.description, tr("Description"), 20);
01140         matcher.test(match, keywords, i->second.maker, tr("Maker"), 10);
01141         matcher.test(match, keywords, i->second.units, tr("Units"), 10);
01142 
01143         if (match.score > 0) results[i->first] = match;
01144     }
01145 
01146     if (!m_uninstalledTransformsMutex.tryLock()) {
01147         // uninstalled transforms are being populated; this may take some time,
01148         // and they aren't critical, but we will speed them up if necessary
01149         cerr << "TransformFactory::search: Uninstalled transforms mutex is held, skipping" << endl;
01150         m_populatingSlowly = false;
01151         return results;
01152     }
01153 
01154     if (!m_uninstalledTransformsPopulated) {
01155         cerr << "WARNING: TransformFactory::search: Uninstalled transforms are not populated yet" << endl
01156                   << "and are not being populated either -- was the thread not started correctly?" << endl;
01157         m_uninstalledTransformsMutex.unlock();
01158         return results;
01159     }
01160 
01161     m_uninstalledTransformsMutex.unlock();
01162 
01163     for (TransformDescriptionMap::const_iterator i = m_uninstalledTransforms.begin();
01164          i != m_uninstalledTransforms.end(); ++i) {
01165 
01166         TextMatcher::Match match;
01167 
01168         match.key = i->first;
01169         
01170         matcher.test(match, keywords,
01171                      getTransformTypeName(i->second.type),
01172                      tr("Plugin type"), 2);
01173 
01174         matcher.test(match, keywords, i->second.category, tr("Category"), 10);
01175         matcher.test(match, keywords, i->second.identifier, tr("System Identifier"), 3);
01176         matcher.test(match, keywords, i->second.name, tr("Name"), 15);
01177         matcher.test(match, keywords, i->second.description, tr("Description"), 10);
01178         matcher.test(match, keywords, i->second.maker, tr("Maker"), 5);
01179         matcher.test(match, keywords, i->second.units, tr("Units"), 5);
01180 
01181         if (match.score > 0) results[i->first] = match;
01182     }
01183 
01184     return results;
01185 }
01186