| Chris@66 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@66 | 2 | 
| Chris@66 | 3 /* | 
| Chris@66 | 4     Sonic Visualiser | 
| Chris@66 | 5     An audio file viewer and annotation editor. | 
| Chris@66 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@66 | 7     This file copyright 2006 Chris Cannam. | 
| Chris@66 | 8 | 
| Chris@66 | 9     This program is free software; you can redistribute it and/or | 
| Chris@66 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@66 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@66 | 12     License, or (at your option) any later version.  See the file | 
| Chris@66 | 13     COPYING included with this distribution for more information. | 
| Chris@66 | 14 */ | 
| Chris@66 | 15 | 
| Chris@66 | 16 #include "PluginXml.h" | 
| Chris@66 | 17 | 
| Chris@66 | 18 #include <QRegExp> | 
| Chris@66 | 19 #include <QXmlAttributes> | 
| Chris@66 | 20 | 
| Chris@66 | 21 #include <QDomDocument> | 
| Chris@66 | 22 #include <QDomElement> | 
| Chris@66 | 23 #include <QDomNamedNodeMap> | 
| Chris@66 | 24 #include <QDomAttr> | 
| Chris@66 | 25 | 
| Chris@314 | 26 #include <QTextStream> | 
| Chris@314 | 27 | 
| Chris@475 | 28 #include <vamp-hostsdk/PluginBase.h> | 
| Chris@75 | 29 #include "RealTimePluginInstance.h" | 
| Chris@66 | 30 | 
| Chris@66 | 31 #include <iostream> | 
| Chris@66 | 32 | 
| Chris@66 | 33 PluginXml::PluginXml(Vamp::PluginBase *plugin) : | 
| Chris@66 | 34     m_plugin(plugin) | 
| Chris@66 | 35 { | 
| Chris@66 | 36 } | 
| Chris@66 | 37 | 
| Chris@66 | 38 PluginXml::~PluginXml() { } | 
| Chris@66 | 39 | 
| Chris@66 | 40 QString | 
| Chris@81 | 41 PluginXml::encodeConfigurationChars(QString text) | 
| Chris@81 | 42 { | 
| Chris@81 | 43     QString rv(text); | 
| Chris@81 | 44     rv.replace(";", "[[SEMICOLON]]"); | 
| Chris@81 | 45     rv.replace("=", "[[EQUALS]]"); | 
| Chris@81 | 46     return rv; | 
| Chris@81 | 47 } | 
| Chris@81 | 48 | 
| Chris@81 | 49 QString | 
| Chris@81 | 50 PluginXml::decodeConfigurationChars(QString text) | 
| Chris@81 | 51 { | 
| Chris@81 | 52     QString rv(text); | 
| Chris@81 | 53     rv.replace("[[SEMICOLON]]", ";"); | 
| Chris@81 | 54     rv.replace("[[EQUALS]]", "="); | 
| Chris@81 | 55     return rv; | 
| Chris@81 | 56 } | 
| Chris@81 | 57 | 
| Chris@314 | 58 void | 
| Chris@314 | 59 PluginXml::toXml(QTextStream &stream, | 
| Chris@314 | 60                  QString indent, QString extraAttributes) const | 
| Chris@66 | 61 { | 
| Chris@314 | 62     stream << indent; | 
| Chris@66 | 63 | 
| Chris@314 | 64     stream << QString("<plugin identifier=\"%1\" name=\"%2\" description=\"%3\" maker=\"%4\" version=\"%5\" copyright=\"%6\" %7 ") | 
| Chris@239 | 65         .arg(encodeEntities(QString(m_plugin->getIdentifier().c_str()))) | 
| Chris@66 | 66         .arg(encodeEntities(QString(m_plugin->getName().c_str()))) | 
| Chris@66 | 67         .arg(encodeEntities(QString(m_plugin->getDescription().c_str()))) | 
| Chris@66 | 68         .arg(encodeEntities(QString(m_plugin->getMaker().c_str()))) | 
| Chris@66 | 69         .arg(m_plugin->getPluginVersion()) | 
| Chris@66 | 70         .arg(encodeEntities(QString(m_plugin->getCopyright().c_str()))) | 
| Chris@66 | 71         .arg(extraAttributes); | 
| Chris@66 | 72 | 
| Chris@66 | 73     if (!m_plugin->getPrograms().empty()) { | 
| Chris@314 | 74         stream << QString("program=\"%1\" ") | 
| Chris@66 | 75             .arg(encodeEntities(m_plugin->getCurrentProgram().c_str())); | 
| Chris@66 | 76     } | 
| Chris@66 | 77 | 
| Chris@66 | 78     Vamp::PluginBase::ParameterList parameters = | 
| Chris@66 | 79         m_plugin->getParameterDescriptors(); | 
| Chris@66 | 80 | 
| Chris@66 | 81     for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); | 
| Chris@66 | 82          i != parameters.end(); ++i) { | 
| Chris@185 | 83 | 
| Chris@690 | 84 //        SVDEBUG << "PluginXml::toXml: parameter name \"" | 
| Chris@185 | 85 //                  << i->name.c_str() << "\" has value " | 
| Chris@687 | 86 //                  << m_plugin->getParameter(i->name) << endl; | 
| Chris@185 | 87 | 
| Chris@314 | 88         stream << QString("param-%1=\"%2\" ") | 
| Chris@239 | 89             .arg(stripInvalidParameterNameCharacters(QString(i->identifier.c_str()))) | 
| Chris@239 | 90             .arg(m_plugin->getParameter(i->identifier)); | 
| Chris@66 | 91     } | 
| Chris@66 | 92 | 
| Chris@75 | 93     RealTimePluginInstance *rtpi = | 
| Chris@75 | 94         dynamic_cast<RealTimePluginInstance *>(m_plugin); | 
| Chris@75 | 95     if (rtpi) { | 
| Chris@75 | 96         std::map<std::string, std::string> configurePairs = | 
| Chris@75 | 97             rtpi->getConfigurePairs(); | 
| Chris@75 | 98         QString config; | 
| Chris@75 | 99         for (std::map<std::string, std::string>::iterator i = configurePairs.begin(); | 
| Chris@75 | 100              i != configurePairs.end(); ++i) { | 
| Chris@75 | 101             QString key = i->first.c_str(); | 
| Chris@75 | 102             QString value = i->second.c_str(); | 
| Chris@81 | 103             key = encodeConfigurationChars(key); | 
| Chris@81 | 104             value = encodeConfigurationChars(value); | 
| Chris@75 | 105             if (config != "") config += ";"; | 
| Chris@75 | 106             config += QString("%1=%2").arg(key).arg(value); | 
| Chris@75 | 107         } | 
| Chris@75 | 108         if (config != "") { | 
| Chris@314 | 109             stream << QString("configuration=\"%1\" ") | 
| Chris@75 | 110                 .arg(encodeEntities(config)); | 
| Chris@75 | 111         } | 
| Chris@75 | 112     } | 
| Chris@75 | 113 | 
| Chris@314 | 114     stream << "/>\n"; | 
| Chris@66 | 115 } | 
| Chris@66 | 116 | 
| Chris@66 | 117 #define CHECK_ATTRIBUTE(ATTRIBUTE, ACCESSOR) \ | 
| Chris@66 | 118     QString ATTRIBUTE = attrs.value(#ATTRIBUTE); \ | 
| Chris@66 | 119     if (ATTRIBUTE != "" && ATTRIBUTE != ACCESSOR().c_str()) { \ | 
| Chris@843 | 120         cerr << "WARNING: PluginXml::setParameters: Plugin " \ | 
| Chris@66 | 121                   << #ATTRIBUTE << " does not match (attributes have \"" \ | 
| Chris@686 | 122                   << ATTRIBUTE << "\", my " \ | 
| Chris@843 | 123                   << #ATTRIBUTE << " is \"" << ACCESSOR() << "\")" << endl; \ | 
| Chris@66 | 124     } | 
| Chris@66 | 125 | 
| Chris@66 | 126 void | 
| Chris@66 | 127 PluginXml::setParameters(const QXmlAttributes &attrs) | 
| Chris@66 | 128 { | 
| Chris@239 | 129     CHECK_ATTRIBUTE(identifier, m_plugin->getIdentifier); | 
| Chris@66 | 130     CHECK_ATTRIBUTE(name, m_plugin->getName); | 
| Chris@66 | 131     CHECK_ATTRIBUTE(description, m_plugin->getDescription); | 
| Chris@66 | 132     CHECK_ATTRIBUTE(maker, m_plugin->getMaker); | 
| Chris@66 | 133     CHECK_ATTRIBUTE(copyright, m_plugin->getCopyright); | 
| Chris@66 | 134 | 
| Chris@66 | 135     bool ok; | 
| Chris@66 | 136     int version = attrs.value("version").trimmed().toInt(&ok); | 
| Chris@66 | 137     if (ok && version != m_plugin->getPluginVersion()) { | 
| Chris@843 | 138         cerr << "WARNING: PluginXml::setParameters: Plugin version does not match (attributes have " << version << ", my version is " << m_plugin->getPluginVersion() << ")" << endl; | 
| Chris@66 | 139     } | 
| Chris@66 | 140 | 
| Chris@75 | 141     RealTimePluginInstance *rtpi = | 
| Chris@75 | 142         dynamic_cast<RealTimePluginInstance *>(m_plugin); | 
| Chris@75 | 143     if (rtpi) { | 
| Chris@75 | 144         QString config = attrs.value("configuration"); | 
| Chris@75 | 145         if (config != "") { | 
| Chris@75 | 146             QStringList configList = config.split(";"); | 
| Chris@75 | 147             for (QStringList::iterator i = configList.begin(); | 
| Chris@75 | 148                  i != configList.end(); ++i) { | 
| Chris@75 | 149                 QStringList kv = i->split("="); | 
| Chris@75 | 150                 if (kv.count() < 2) { | 
| Chris@844 | 151                     cerr << "WARNING: PluginXml::setParameters: Malformed configure pair string: \"" << *i << "\"" << endl; | 
| Chris@75 | 152                     continue; | 
| Chris@75 | 153                 } | 
| Chris@75 | 154                 QString key(kv[0]), value(kv[1]); | 
| Chris@81 | 155                 key = decodeConfigurationChars(key); | 
| Chris@81 | 156                 value = decodeConfigurationChars(value); | 
| Chris@75 | 157                 rtpi->configure(key.toStdString(), value.toStdString()); | 
| Chris@75 | 158             } | 
| Chris@75 | 159         } | 
| Chris@75 | 160     } | 
| Chris@75 | 161 | 
| Chris@66 | 162     if (!m_plugin->getPrograms().empty()) { | 
| Chris@66 | 163         m_plugin->selectProgram(attrs.value("program").toStdString()); | 
| Chris@66 | 164     } | 
| Chris@66 | 165 | 
| Chris@66 | 166     Vamp::PluginBase::ParameterList parameters = | 
| Chris@66 | 167         m_plugin->getParameterDescriptors(); | 
| Chris@66 | 168 | 
| Chris@66 | 169     for (Vamp::PluginBase::ParameterList::const_iterator i = | 
| Chris@66 | 170              parameters.begin(); i != parameters.end(); ++i) { | 
| Chris@66 | 171 | 
| Chris@239 | 172         QString pname = QString("param-%1") | 
| Chris@66 | 173             .arg(stripInvalidParameterNameCharacters | 
| Chris@239 | 174                  (QString(i->identifier.c_str()))); | 
| Chris@66 | 175 | 
| Chris@239 | 176         if (attrs.value(pname) == "") { | 
| Chris@690 | 177 //            SVDEBUG << "PluginXml::setParameters: no parameter \"" << i->name << "\" (attribute \"" << name << "\")" << endl; | 
| Chris@66 | 178             continue; | 
| Chris@66 | 179         } | 
| Chris@66 | 180 | 
| Chris@66 | 181         bool ok; | 
| Chris@239 | 182         float value = attrs.value(pname).trimmed().toFloat(&ok); | 
| Chris@66 | 183         if (ok) { | 
| Chris@690 | 184 //            SVDEBUG << "PluginXml::setParameters: setting parameter \"" | 
| Chris@687 | 185 //                      << i->identifier << "\" to value " << value << endl; | 
| Chris@239 | 186             m_plugin->setParameter(i->identifier, value); | 
| Chris@66 | 187         } else { | 
| Chris@843 | 188             cerr << "WARNING: PluginXml::setParameters: Invalid value \"" << attrs.value(pname) << "\" for parameter \"" << i->identifier << "\" (attribute \"" << pname << "\")" << endl; | 
| Chris@66 | 189         } | 
| Chris@66 | 190     } | 
| Chris@66 | 191 } | 
| Chris@66 | 192 | 
| Chris@66 | 193 void | 
| Chris@66 | 194 PluginXml::setParametersFromXml(QString xml) | 
| Chris@66 | 195 { | 
| Chris@66 | 196     QDomDocument doc; | 
| Chris@66 | 197 | 
| Chris@66 | 198     QString error; | 
| Chris@66 | 199     int errorLine; | 
| Chris@66 | 200     int errorColumn; | 
| Chris@66 | 201 | 
| Chris@690 | 202 //    SVDEBUG << "PluginXml::setParametersFromXml: XML is \"" | 
| Chris@845 | 203 //              << xml << "\"" << endl; | 
| Chris@185 | 204 | 
| Chris@66 | 205     if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { | 
| Chris@843 | 206         cerr << "PluginXml::setParametersFromXml: Error in parsing XML: " << error << " at line " << errorLine << ", column " << errorColumn << endl; | 
| Chris@843 | 207         cerr << "Input follows:" << endl; | 
| Chris@843 | 208         cerr << xml << endl; | 
| Chris@843 | 209         cerr << "Input ends." << endl; | 
| Chris@66 | 210         return; | 
| Chris@66 | 211     } | 
| Chris@66 | 212 | 
| Chris@66 | 213     QDomElement pluginElt = doc.firstChildElement("plugin"); | 
| Chris@66 | 214     QDomNamedNodeMap attrNodes = pluginElt.attributes(); | 
| Chris@66 | 215     QXmlAttributes attrs; | 
| Chris@66 | 216 | 
| Chris@929 | 217     for (int i = 0; i < attrNodes.length(); ++i) { | 
| Chris@66 | 218         QDomAttr attr = attrNodes.item(i).toAttr(); | 
| Chris@66 | 219         if (attr.isNull()) continue; | 
| Chris@690 | 220 //        SVDEBUG << "PluginXml::setParametersFromXml: Adding attribute \"" << attr.name()//                  << "\" with value \"" << attr.value() << "\"" << endl; | 
| Chris@66 | 221         attrs.append(attr.name(), "", "", attr.value()); | 
| Chris@66 | 222     } | 
| Chris@66 | 223 | 
| Chris@66 | 224     setParameters(attrs); | 
| Chris@66 | 225 } | 
| Chris@163 | 226 | 
| Chris@66 | 227 QString | 
| Chris@66 | 228 PluginXml::stripInvalidParameterNameCharacters(QString s) const | 
| Chris@66 | 229 { | 
| Chris@66 | 230     s.replace(QRegExp("[^a-zA-Z0-9_]*"), ""); | 
| Chris@66 | 231     return s; | 
| Chris@66 | 232 } | 
| Chris@66 | 233 |