changeset 443:381ec750eeee

* Add beginnings of transform-search-by-text function
author Chris Cannam
date Mon, 22 Sep 2008 15:44:03 +0000
parents 04b7fd31e1c6
children 14521503f196
files transform/TransformDescription.h transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 3 files changed, 204 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/transform/TransformDescription.h	Fri Sep 19 12:55:35 2008 +0000
+++ b/transform/TransformDescription.h	Mon Sep 22 15:44:03 2008 +0000
@@ -49,10 +49,12 @@
     TransformDescription(QString _type, QString _category,
                          TransformId _identifier, QString _name,
                          QString _friendlyName, QString _description,
+                         QString _longDescription,
                          QString _maker, QString _units, bool _configurable) :
         type(_type), category(_category),
         identifier(_identifier), name(_name),
         friendlyName(_friendlyName), description(_description),
+        longDescription(_longDescription),
         maker(_maker), units(_units), configurable(_configurable) { }
 
     QString type; // e.g. feature extraction plugin
@@ -61,6 +63,7 @@
     QString name; // plugin's name if 1 output, else "name: output"
     QString friendlyName; // short text for layer name
     QString description; // sentence describing transform
+    QString longDescription; // description "using" plugin name "by" maker
     QString maker;
     QString units;
     bool configurable;
--- a/transform/TransformFactory.cpp	Fri Sep 19 12:55:35 2008 +0000
+++ b/transform/TransformFactory.cpp	Mon Sep 22 15:44:03 2008 +0000
@@ -30,6 +30,9 @@
 #include <QRegExp>
 #include <QTextStream>
 
+using std::cerr;
+using std::endl;
+
 TransformFactory *
 TransformFactory::m_instance = new TransformFactory;
 
@@ -51,14 +54,14 @@
     std::set<TransformDescription> dset;
     for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
 	 i != m_transforms.end(); ++i) {
-//        std::cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << std::endl;
+//        cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << endl;
 	dset.insert(i->second);
     }
 
     TransformList list;
     for (std::set<TransformDescription>::const_iterator i = dset.begin();
 	 i != dset.end(); ++i) {
-//        std::cerr << "inserting transform into list: id = " << i->identifier.toStdString() << std::endl;
+//        cerr << "inserting transform into list: id = " << i->identifier.toStdString() << endl;
 	list.push_back(*i);
     }
 
@@ -227,7 +230,7 @@
 	    FeatureExtractionPluginFactory::instanceFor(pluginId);
 
 	if (!factory) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
+	    cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << endl;
 	    continue;
 	}
 
@@ -235,7 +238,7 @@
 	    factory->instantiatePlugin(pluginId, 44100);
 
 	if (!plugin) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
+	    cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << endl;
 	    continue;
 	}
 		
@@ -257,21 +260,23 @@
             QString maker = plugin->getMaker().c_str();
             if (maker == "") maker = tr("<unknown maker>");
 
-            if (description == "") {
+            QString longDescription = description;
+
+            if (longDescription == "") {
                 if (outputs.size() == 1) {
-                    description = tr("Extract features using \"%1\" plugin (from %2)")
+                    longDescription = tr("Extract features using \"%1\" plugin (from %2)")
                         .arg(pluginName).arg(maker);
                 } else {
-                    description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
+                    longDescription = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
                         .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
                 }
             } else {
                 if (outputs.size() == 1) {
-                    description = tr("%1 using \"%2\" plugin (from %3)")
-                        .arg(description).arg(pluginName).arg(maker);
+                    longDescription = tr("%1 using \"%2\" plugin (from %3)")
+                        .arg(longDescription).arg(pluginName).arg(maker);
                 } else {
-                    description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
-                        .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
+                    longDescription = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
+                        .arg(longDescription).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
                 }
             }                    
 
@@ -288,7 +293,7 @@
             bool configurable = (!plugin->getPrograms().empty() ||
                                  !plugin->getParameterDescriptors().empty());
 
-//            std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << " friendly name: " << friendlyName.toStdString() << std::endl;
+//            cerr << "Feature extraction plugin transform: " << transformId.toStdString() << " friendly name: " << friendlyName.toStdString() << endl;
 
 	    transforms[transformId] = 
                 TransformDescription(tr("Analysis"),
@@ -297,6 +302,7 @@
                                      userName,
                                      friendlyName,
                                      description,
+                                     longDescription,
                                      maker,
                                      units,
                                      configurable);
@@ -322,7 +328,7 @@
             RealTimePluginFactory::instanceFor(pluginId);
 
 	if (!factory) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
+	    cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << endl;
 	    continue;
 	}
 
@@ -330,14 +336,14 @@
             factory->getPluginDescriptor(pluginId);
 
         if (!descriptor) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
+	    cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << endl;
 	    continue;
 	}
 	
 //!!!        if (descriptor->controlOutputPortCount == 0 ||
 //            descriptor->audioInputPortCount == 0) continue;
 
-//        std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl;
+//        std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << endl;
 	
 	QString pluginName = descriptor->name.c_str();
         QString category = factory->getPluginCategory(pluginId);
@@ -398,6 +404,7 @@
                                          transformId,
                                          userName,
                                          userName,
+                                         "",
                                          description,
                                          maker,
                                          units,
@@ -429,6 +436,7 @@
                                          transformId,
                                          pluginName,
                                          pluginName,
+                                         "",
                                          description,
                                          maker,
                                          "",
@@ -506,15 +514,15 @@
 {
     Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin);
     if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl;
+//        cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << endl;
         vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); //!!! why?
 }
     if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl;
+//        cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << endl;
         vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why?
     }
     if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl;
+//        cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << endl;
     }
     return vp;
 }
@@ -727,10 +735,10 @@
         }
         if (!transform.getStepSize()) {
             if (domain == Vamp::Plugin::FrequencyDomain) {
-//                std::cerr << "frequency domain, step = " << blockSize/2 << std::endl;
+//                cerr << "frequency domain, step = " << blockSize/2 << endl;
                 transform.setStepSize(transform.getBlockSize()/2);
             } else {
-//                std::cerr << "time domain, step = " << blockSize/2 << std::endl;
+//                cerr << "time domain, step = " << blockSize/2 << endl;
                 transform.setStepSize(transform.getBlockSize());
             }
         }
@@ -745,9 +753,9 @@
     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
         (t.getIdentifier(), 0);
     if (!plugin) {
-        std::cerr << "TransformFactory::getPluginConfigurationXml: "
+        cerr << "TransformFactory::getPluginConfigurationXml: "
                   << "Unable to instantiate plugin for transform \""
-                  << t.getIdentifier().toStdString() << "\"" << std::endl;
+                  << t.getIdentifier().toStdString() << "\"" << endl;
         return xml;
     }
 
@@ -767,9 +775,9 @@
     Vamp::PluginBase *plugin = instantiateDefaultPluginFor
         (t.getIdentifier(), 0);
     if (!plugin) {
-        std::cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
+        cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
                   << "Unable to instantiate plugin for transform \""
-                  << t.getIdentifier().toStdString() << "\"" << std::endl;
+                  << t.getIdentifier().toStdString() << "\"" << endl;
         return;
     }
 
@@ -777,4 +785,137 @@
     setParametersFromPlugin(t, plugin);
     delete plugin;
 }
+/*
+TransformFactory::SearchResults
+TransformFactory::search(QStringList keywords)
+{
+    SearchResults results;
+    SearchResults partial;
+    for (int i = 0; i < keywords.size(); ++i) {
+        partial = search(keywords[i]);
+        for (SearchResults::const_iterator j = partial.begin();
+             j != partial.end(); ++j) {
+            if (results.find(j->first) == results.end()) {
+                results[j->first] = j->second;
+            } else {
+                results[j->first].score += j->second.score;
+                results[j->first].fragments << j->second.fragments;
+            }
+        }
+    }
+    return results;
+}
+*/
 
+TransformFactory::SearchResults
+TransformFactory::search(QString keyword)
+{
+    QStringList keywords;
+    keywords << keyword;
+    return search(keywords);
+}
+
+TransformFactory::SearchResults
+TransformFactory::search(QStringList keywords)
+{
+    if (m_transforms.empty()) populateTransforms();
+
+    SearchResults results;
+
+    for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
+         i != m_transforms.end(); ++i) {
+
+        Match match;
+
+        match.transform = i->first;
+        
+        searchTest(match, keywords, i->second.type, tr("Plugin type"), 10);
+        searchTest(match, keywords, i->second.category, tr("Category"), 20);
+        searchTest(match, keywords, i->second.identifier, tr("System Identifier"), 5);
+        searchTest(match, keywords, i->second.name, tr("Name"), 30);
+        searchTest(match, keywords, i->second.description, tr("Description"), 20);
+        searchTest(match, keywords, i->second.maker, tr("Maker"), 10);
+        searchTest(match, keywords, i->second.units, tr("Units"), 10);
+
+        if (match.score > 0) results[i->first] = match;
+    }
+
+    return results;
+}
+
+void
+TransformFactory::searchTest(Match &match, QStringList keywords, QString text,
+                             QString textType, int score)
+{
+/*
+    if (text.toLower() == keyword.toLower()) {
+        match.score += score * 1.5;
+        match.fragments << tr("%1: <b>%2</b>").arg(textType).arg(text);
+        return;
+    }
+*/
+    int len = text.length();
+    int prevEnd = 0;
+    QString fragment;
+
+    while (1) {
+
+        bool first = (prevEnd == 0);
+        
+        int idx = -1;
+        QString keyword;
+
+        for (int ki = 0; ki < keywords.size(); ++ki) {
+            int midx = text.indexOf(keywords[ki], prevEnd, Qt::CaseInsensitive);
+            if (midx >= 0 && midx < len) {
+                if (midx < idx || idx == -1) {
+                    idx = midx;
+                    keyword = keywords[ki];
+                }
+            }
+        }
+
+        if (idx < 0 || idx >= len) break;
+
+        int klen = keyword.length();
+
+        if (first) {
+            match.score += score;
+        } else {
+            match.score += score / 4;
+        }
+
+        int start = idx;
+        int end = start + klen;
+
+        if (start == 0) match.score += 1;
+        if (end == len) match.score += 1;
+
+        if (start > prevEnd + 14) {
+//            cerr << "start = " << start << ", prevEnd = " <<prevEnd << ", length = " << len << ", text = " << text.toStdString() << endl;
+            QString s = text.right((len - start) + 10);
+//            cerr << "s = " << s.toStdString() << endl;
+            s = s.left(10) + "<b>" + s.left(klen + 10).right(klen) + "</b>";
+//            cerr << "s = " << s.toStdString() << endl;
+            fragment += tr("...%1").arg(s);
+//            cerr << "fragment = " << fragment.toStdString() << endl;
+        } else {
+            QString s = text.right(len - prevEnd);
+            s = s.left(start - prevEnd) + "<b>" + s.left(end - prevEnd).right(klen) + "</b>";
+            fragment += s;
+        }
+
+        prevEnd = end;
+    }
+
+    if (prevEnd > 0 && prevEnd < len) {
+        int n = len - prevEnd;
+        fragment += text.right(n).left(n < 8 ? n : 8);
+    }
+
+    if (fragment != "") {
+        fragment = tr("%1: %2").arg(textType).arg(fragment);
+        match.fragments << fragment;
+    }
+}
+
--- a/transform/TransformFactory.h	Fri Sep 19 12:55:35 2008 +0000
+++ b/transform/TransformFactory.h	Mon Sep 22 15:44:03 2008 +0000
@@ -21,6 +21,7 @@
 #include <vamp-sdk/Plugin.h>
 
 #include <QObject>
+#include <QStringList>
 
 #include <map>
 #include <set>
@@ -41,6 +42,38 @@
     std::vector<QString> getTransformCategories(QString transformType);
     std::vector<QString> getTransformMakers(QString transformType);
 
+    struct Match
+    {
+        TransformId transform;
+        int score;
+        QStringList fragments;
+
+        Match() : score(0) { }
+        Match(const Match &m) : transform(m.transform),
+                                score(m.score), fragments(m.fragments) { }
+
+        bool operator<(const Match &m) {
+            if (score != m.score) {
+                return score < m.score;
+            } else if (transform != m.transform) {
+                return transform < m.transform;
+            } else if (fragments.size() != m.fragments.size()) {
+                return fragments.size() < m.fragments.size();
+            } else {
+                for (int i = 0; i < fragments.size(); ++i) {
+                    if (fragments[i] != m.fragments[i]) {
+                        return fragments[i] < m.fragments[i];
+                    }
+                }
+            }
+            return false;
+        }
+    };
+
+    typedef std::map<TransformId, Match> SearchResults;
+    SearchResults search(QString keyword);
+    SearchResults search(QStringList keywords);
+    
     /**
      * Return true if the given transform is known.
      */
@@ -165,6 +198,9 @@
     void populateFeatureExtractionPlugins(TransformDescriptionMap &);
     void populateRealTimePlugins(TransformDescriptionMap &);
 
+    void searchTest(Match &match, QStringList keywords, QString text,
+                    QString textType, int score);
+
     Vamp::PluginBase *instantiateDefaultPluginFor(TransformId id, size_t rate);
 
     static TransformFactory *m_instance;