Chris@320: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@320: 
Chris@320: /*
Chris@320:     Sonic Visualiser
Chris@320:     An audio file viewer and annotation editor.
Chris@320:     Centre for Digital Music, Queen Mary, University of London.
Chris@328:     This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@320:    
Chris@320:     This program is free software; you can redistribute it and/or
Chris@320:     modify it under the terms of the GNU General Public License as
Chris@320:     published by the Free Software Foundation; either version 2 of the
Chris@320:     License, or (at your option) any later version.  See the file
Chris@320:     COPYING included with this distribution for more information.
Chris@320: */
Chris@320: 
Chris@320: #include "Transform.h"
Chris@320: 
Chris@328: #include "plugin/PluginIdentifier.h"
Chris@328: 
Chris@332: #include "plugin/FeatureExtractionPluginFactory.h"
Chris@1138: #include "plugin/RealTimePluginFactory.h"
Chris@332: 
Chris@350: #include <QXmlAttributes>
Chris@350: 
Chris@350: #include <QDomDocument>
Chris@350: #include <QDomElement>
Chris@350: #include <QDomNamedNodeMap>
Chris@350: #include <QDomAttr>
Chris@350: 
Chris@350: #include <QTextStream>
Chris@350: 
Chris@350: #include <iostream>
Chris@350: 
Chris@328: Transform::Transform() :
Chris@508:     m_summaryType(NoSummary),
Chris@328:     m_stepSize(0),
Chris@328:     m_blockSize(0),
Chris@328:     m_windowType(HanningWindow),
Chris@328:     m_sampleRate(0)
Chris@320: {
Chris@320: }
Chris@320: 
Chris@350: Transform::Transform(QString xml) :
Chris@508:     m_summaryType(NoSummary),
Chris@350:     m_stepSize(0),
Chris@350:     m_blockSize(0),
Chris@350:     m_windowType(HanningWindow),
Chris@350:     m_sampleRate(0)
Chris@350: {
Chris@350:     QDomDocument doc;
Chris@350:     
Chris@350:     QString error;
Chris@350:     int errorLine;
Chris@350:     int errorColumn;
Chris@350: 
Chris@350:     if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) {
Chris@1163:         m_errorString = QString("%1 at line %2, column %3")
Chris@1163:             .arg(error).arg(errorLine).arg(errorColumn);
Chris@350:         return;
Chris@350:     }
Chris@350:     
Chris@350:     QDomElement transformElt = doc.firstChildElement("transform");
Chris@350:     QDomNamedNodeMap attrNodes = transformElt.attributes();
Chris@350:     QXmlAttributes attrs;
Chris@350: 
Chris@930:     for (int i = 0; i < attrNodes.length(); ++i) {
Chris@350:         QDomAttr attr = attrNodes.item(i).toAttr();
Chris@350:         if (!attr.isNull()) attrs.append(attr.name(), "", "", attr.value());
Chris@350:     }
Chris@350: 
Chris@350:     setFromXmlAttributes(attrs);
Chris@350: 
Chris@350:     for (QDomElement paramElt = transformElt.firstChildElement("parameter");
Chris@350:          !paramElt.isNull();
Chris@350:          paramElt = paramElt.nextSiblingElement("parameter")) {
Chris@350: 
Chris@350:         QDomNamedNodeMap paramAttrs = paramElt.attributes();
Chris@350: 
Chris@350:         QDomAttr nameAttr = paramAttrs.namedItem("name").toAttr();
Chris@350:         if (nameAttr.isNull() || nameAttr.value() == "") continue;
Chris@350:         
Chris@350:         QDomAttr valueAttr = paramAttrs.namedItem("value").toAttr();
Chris@350:         if (valueAttr.isNull() || valueAttr.value() == "") continue;
Chris@350: 
Chris@350:         setParameter(nameAttr.value(), valueAttr.value().toFloat());
Chris@350:     }
Chris@350: 
Chris@350:     for (QDomElement configElt = transformElt.firstChildElement("configuration");
Chris@350:          !configElt.isNull();
Chris@350:          configElt = configElt.nextSiblingElement("configuration")) {
Chris@350: 
Chris@350:         QDomNamedNodeMap configAttrs = configElt.attributes();
Chris@350: 
Chris@350:         QDomAttr nameAttr = configAttrs.namedItem("name").toAttr();
Chris@350:         if (nameAttr.isNull() || nameAttr.value() == "") continue;
Chris@350:         
Chris@350:         QDomAttr valueAttr = configAttrs.namedItem("value").toAttr();
Chris@350:         if (valueAttr.isNull() || valueAttr.value() == "") continue;
Chris@350: 
Chris@350:         setConfigurationValue(nameAttr.value(), valueAttr.value());
Chris@350:     }
Chris@350: }
Chris@350: 
Chris@320: Transform::~Transform()
Chris@320: {
Chris@320: }
Chris@320: 
Chris@350: bool
Chris@400: Transform::operator==(const Transform &t) const
Chris@350: {
Chris@583:     bool identical =  
Chris@350:         m_id == t.m_id &&
Chris@350:         m_parameters == t.m_parameters &&
Chris@350:         m_configuration == t.m_configuration &&
Chris@350:         m_program == t.m_program &&
Chris@508:         m_summaryType == t.m_summaryType &&
Chris@350:         m_stepSize == t.m_stepSize &&
Chris@350:         m_blockSize == t.m_blockSize &&
Chris@350:         m_windowType == t.m_windowType &&
Chris@350:         m_startTime == t.m_startTime &&
Chris@350:         m_duration == t.m_duration &&
Chris@350:         m_sampleRate == t.m_sampleRate;
Chris@583: /*
Chris@690:     SVDEBUG << "Transform::operator==: identical = " << identical << endl;
Chris@843:     cerr << "A = " << endl;
Chris@843:     cerr << toXmlString() << endl;
Chris@843:     cerr << "B = " << endl;
Chris@843:     cerr << t.toXmlString() << endl;
Chris@583: */
Chris@583:     return identical;
Chris@350: }
Chris@350: 
Chris@400: bool
Chris@400: Transform::operator<(const Transform &t) const
Chris@400: {
Chris@400:     if (m_id != t.m_id) {
Chris@400:         return m_id < t.m_id;
Chris@400:     }
Chris@400:     if (m_parameters != t.m_parameters) {
Chris@400:         return mapLessThan<QString, float>(m_parameters, t.m_parameters);
Chris@400:     }
Chris@400:     if (m_configuration != t.m_configuration) {
Chris@400:         return mapLessThan<QString, QString>(m_configuration, t.m_configuration);
Chris@400:     }
Chris@400:     if (m_program != t.m_program) {
Chris@400:         return m_program < t.m_program;
Chris@400:     }
Chris@508:     if (m_summaryType != t.m_summaryType) {
Chris@508:         return int(m_summaryType) < int(t.m_summaryType);
Chris@508:     }
Chris@400:     if (m_stepSize != t.m_stepSize) {
Chris@400:         return m_stepSize < t.m_stepSize;
Chris@400:     }
Chris@400:     if (m_blockSize != t.m_blockSize) {
Chris@400:         return m_blockSize < t.m_blockSize;
Chris@400:     }
Chris@400:     if (m_windowType != t.m_windowType) {
Chris@400:         return m_windowType < t.m_windowType;
Chris@400:     }
Chris@400:     if (m_startTime != t.m_startTime) {
Chris@400:         return m_startTime < t.m_startTime;
Chris@400:     }
Chris@400:     if (m_duration != t.m_duration) {
Chris@400:         return m_duration < t.m_duration;
Chris@400:     }
Chris@400:     if (m_sampleRate != t.m_sampleRate) {
Chris@400:         return m_sampleRate < t.m_sampleRate;
Chris@400:     }
Chris@400:     return false;
Chris@400: }
Chris@400: 
Chris@350: void
Chris@350: Transform::setIdentifier(TransformId id)
Chris@350: {
Chris@350:     m_id = id;
Chris@350: }
Chris@350: 
Chris@350: TransformId
Chris@350: Transform::getIdentifier() const
Chris@350: {
Chris@350:     return m_id;
Chris@350: }
Chris@350: 
Chris@328: QString
Chris@328: Transform::createIdentifier(QString type, QString soName, QString label,
Chris@328:                             QString output)
Chris@328: {
Chris@328:     QString pluginId = PluginIdentifier::createIdentifier(type, soName, label);
Chris@328:     return pluginId + ":" + output;
Chris@328: }
Chris@328: 
Chris@328: void
Chris@328: Transform::parseIdentifier(QString identifier,
Chris@328:                            QString &type, QString &soName,
Chris@328:                            QString &label, QString &output)
Chris@328: {
Chris@328:     output = identifier.section(':', 3);
Chris@328:     PluginIdentifier::parseIdentifier(identifier.section(':', 0, 2),
Chris@328:                                       type, soName, label);
Chris@328: }
Chris@328: 
Chris@328: Transform::Type
Chris@328: Transform::getType() const
Chris@328: {
Chris@1225:     if (RealTimePluginFactory::instanceFor(getPluginIdentifier())) {
Chris@1138:         return RealTimeEffect;
Chris@332:     } else {
Chris@1225:         return FeatureExtraction;
Chris@332:     }
Chris@328: }
Chris@328: 
Chris@328: QString
Chris@328: Transform::getPluginIdentifier() const
Chris@328: {
Chris@328:     return m_id.section(':', 0, 2);
Chris@328: }
Chris@328: 
Chris@328: QString
Chris@328: Transform::getOutput() const
Chris@328: {
Chris@328:     return m_id.section(':', 3);
Chris@328: }
Chris@328: 
Chris@353: void
Chris@353: Transform::setPluginIdentifier(QString pluginIdentifier)
Chris@353: {
Chris@353:     m_id = pluginIdentifier + ':' + getOutput();
Chris@353: }
Chris@353: 
Chris@353: void
Chris@353: Transform::setOutput(QString output)
Chris@353: {
Chris@353:     m_id = getPluginIdentifier() + ':' + output;
Chris@353: }
Chris@353: 
Chris@353: TransformId
Chris@353: Transform::getIdentifierForPluginOutput(QString pluginIdentifier,
Chris@353:                                         QString output)
Chris@353: {
Chris@353:     return pluginIdentifier + ':' + output;
Chris@353: }
Chris@353: 
Chris@350: const Transform::ParameterMap &
Chris@350: Transform::getParameters() const
Chris@350: {
Chris@350:     return m_parameters;
Chris@350: }
Chris@350: 
Chris@328: void
Chris@350: Transform::setParameters(const ParameterMap &pm)
Chris@328: {
Chris@350:     m_parameters = pm;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setParameter(QString name, float value)
Chris@350: {
Chris@690: //    SVDEBUG << "Transform::setParameter(" << name//              << ") -> " << value << endl;
Chris@350:     m_parameters[name] = value;
Chris@350: }
Chris@350: 
Chris@350: const Transform::ConfigurationMap &
Chris@350: Transform::getConfiguration() const
Chris@350: {
Chris@350:     return m_configuration;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setConfiguration(const ConfigurationMap &cm)
Chris@350: {
Chris@350:     m_configuration = cm;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setConfigurationValue(QString name, QString value)
Chris@350: {
Chris@690:     SVDEBUG << "Transform::setConfigurationValue(" << name              << ") -> " << value << endl;
Chris@350:     m_configuration[name] = value;
Chris@350: }
Chris@350: 
Chris@350: QString
Chris@366: Transform::getPluginVersion() const
Chris@366: {
Chris@366:     return m_pluginVersion;
Chris@366: }
Chris@366: 
Chris@366: void
Chris@366: Transform::setPluginVersion(QString version)
Chris@366: {
Chris@366:     m_pluginVersion = version;
Chris@366: }
Chris@366: 
Chris@366: QString
Chris@350: Transform::getProgram() const
Chris@350: {
Chris@350:     return m_program;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setProgram(QString program)
Chris@350: {
Chris@350:     m_program = program;
Chris@350: }
Chris@350: 
Chris@508: Transform::SummaryType
Chris@508: Transform::getSummaryType() const
Chris@508: {
Chris@508:     return m_summaryType;
Chris@508: }
Chris@508: 
Chris@508: void
Chris@508: Transform::setSummaryType(SummaryType type)
Chris@508: {
Chris@508:     m_summaryType = type;
Chris@508: }
Chris@328:     
Chris@930: int
Chris@350: Transform::getStepSize() const
Chris@350: {
Chris@350:     return m_stepSize;
Chris@328: }
Chris@350: 
Chris@350: void
Chris@930: Transform::setStepSize(int s)
Chris@350: {
Chris@350:     m_stepSize = s;
Chris@350: }
Chris@350:     
Chris@930: int
Chris@350: Transform::getBlockSize() const
Chris@350: {
Chris@350:     return m_blockSize;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@930: Transform::setBlockSize(int s)
Chris@350: {
Chris@350:     m_blockSize = s;
Chris@350: }
Chris@350: 
Chris@350: WindowType
Chris@350: Transform::getWindowType() const
Chris@350: {
Chris@350:     return m_windowType;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setWindowType(WindowType type)
Chris@350: {
Chris@350:     m_windowType = type;
Chris@350: }
Chris@350: 
Chris@350: RealTime
Chris@350: Transform::getStartTime() const
Chris@350: {
Chris@350:     return m_startTime;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setStartTime(RealTime t)
Chris@350: {
Chris@350:     m_startTime = t;
Chris@350: }
Chris@350: 
Chris@350: RealTime
Chris@350: Transform::getDuration() const
Chris@350: {
Chris@350:     return m_duration;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::setDuration(RealTime d)
Chris@350: {
Chris@350:     m_duration = d;
Chris@350: }
Chris@350:     
Chris@1047: sv_samplerate_t
Chris@350: Transform::getSampleRate() const
Chris@350: {
Chris@350:     return m_sampleRate;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@1047: Transform::setSampleRate(sv_samplerate_t rate)
Chris@350: {
Chris@350:     m_sampleRate = rate;
Chris@350: }
Chris@350: 
Chris@350: void
Chris@350: Transform::toXml(QTextStream &out, QString indent, QString extraAttributes) const
Chris@350: {
Chris@350:     out << indent;
Chris@350: 
Chris@350:     bool haveContent = true;
Chris@350:     if (m_parameters.empty() && m_configuration.empty()) haveContent = false;
Chris@350: 
Chris@396:     out << QString("<transform\n    id=\"%1\"\n    pluginVersion=\"%2\"\n    program=\"%3\"\n    stepSize=\"%4\"\n    blockSize=\"%5\"\n    windowType=\"%6\"\n    startTime=\"%7\"\n    duration=\"%8\"\n    sampleRate=\"%9\"")
Chris@350:         .arg(encodeEntities(m_id))
Chris@366:         .arg(encodeEntities(m_pluginVersion))
Chris@350:         .arg(encodeEntities(m_program))
Chris@350:         .arg(m_stepSize)
Chris@350:         .arg(m_blockSize)
Chris@350:         .arg(encodeEntities(Window<float>::getNameForType(m_windowType).c_str()))
Chris@350:         .arg(encodeEntities(m_startTime.toString().c_str()))
Chris@350:         .arg(encodeEntities(m_duration.toString().c_str()))
Chris@366:         .arg(m_sampleRate);
Chris@366: 
Chris@508:     if (m_summaryType != NoSummary) {
Chris@508:         out << QString("\n    summaryType=\"%1\"").arg(summaryTypeToString(m_summaryType));
Chris@508:     }
Chris@508: 
Chris@366:     if (extraAttributes != "") {
Chris@366:         out << " " << extraAttributes;
Chris@366:     }
Chris@350: 
Chris@350:     if (haveContent) {
Chris@350: 
Chris@350:         out << ">\n";
Chris@350: 
Chris@350:         for (ParameterMap::const_iterator i = m_parameters.begin();
Chris@350:              i != m_parameters.end(); ++i) {
Chris@350:             out << indent << "  "
Chris@350:                 << QString("<parameter name=\"%1\" value=\"%2\"/>\n")
Chris@350:                 .arg(encodeEntities(i->first))
Chris@350:                 .arg(i->second);
Chris@350:         }
Chris@350:         
Chris@350:         for (ConfigurationMap::const_iterator i = m_configuration.begin();
Chris@350:              i != m_configuration.end(); ++i) {
Chris@350:             out << indent << "  "
Chris@350:                 << QString("<configuration name=\"%1\" value=\"%2\"/>\n")
Chris@350:                 .arg(encodeEntities(i->first))
Chris@350:                 .arg(encodeEntities(i->second));
Chris@350:         }
Chris@350: 
Chris@350:         out << indent << "</transform>\n";
Chris@350: 
Chris@350:     } else {
Chris@350: 
Chris@350:         out << "/>\n";
Chris@350:     }
Chris@350: }
Chris@350: 
Chris@508: Transform::SummaryType
Chris@508: Transform::stringToSummaryType(QString str)
Chris@508: {
Chris@508:     str = str.toLower();
Chris@508:     if (str == "minimum" || str == "min") return Minimum;
Chris@508:     if (str == "maximum" || str == "max") return Maximum;
Chris@508:     if (str == "mean") return Mean;
Chris@508:     if (str == "median") return Median;
Chris@508:     if (str == "mode") return Mode;
Chris@508:     if (str == "sum") return Sum;
Chris@508:     if (str == "variance") return Variance;
Chris@508:     if (str == "standard-deviation" || str == "standardDeviation" ||
Chris@508:         str == "standard deviation" || str == "sd") return StandardDeviation;
Chris@508:     if (str == "count") return Count;
Chris@508:     if (str == "") return NoSummary;
Chris@690:     SVDEBUG << "Transform::stringToSummaryType: unknown summary type \""
Chris@687:               << str << "\"" << endl;
Chris@508:     return NoSummary;
Chris@508: }
Chris@508: 
Chris@508: QString
Chris@508: Transform::summaryTypeToString(SummaryType type)
Chris@508: {
Chris@508:     switch (type) {
Chris@508:     case Minimum: return "min";
Chris@508:     case Maximum: return "max";
Chris@508:     case Mean: return "mean";
Chris@508:     case Median: return "median";
Chris@508:     case Mode: return "mode";
Chris@508:     case Sum: return "sum";
Chris@508:     case Variance: return "variance";
Chris@508:     case StandardDeviation: return "sd";
Chris@508:     case Count: return "count";
Chris@508:     case NoSummary: return "";
Chris@508:     default:
Chris@690:         SVDEBUG << "Transform::summaryTypeToString: unexpected summary type "
Chris@687:                   << int(type) << endl;
Chris@508:         return "";
Chris@508:     }
Chris@508: }
Chris@508: 
Chris@350: void
Chris@350: Transform::setFromXmlAttributes(const QXmlAttributes &attrs)
Chris@350: {
Chris@350:     if (attrs.value("id") != "") {
Chris@350:         setIdentifier(attrs.value("id"));
Chris@350:     }
Chris@350: 
Chris@366:     if (attrs.value("pluginVersion") != "") {
Chris@366:         setPluginVersion(attrs.value("pluginVersion"));
Chris@366:     }
Chris@366: 
Chris@350:     if (attrs.value("program") != "") {
Chris@350:         setProgram(attrs.value("program"));
Chris@350:     }
Chris@350: 
Chris@350:     if (attrs.value("stepSize") != "") {
Chris@350:         setStepSize(attrs.value("stepSize").toInt());
Chris@350:     }
Chris@350: 
Chris@350:     if (attrs.value("blockSize") != "") {
Chris@350:         setBlockSize(attrs.value("blockSize").toInt());
Chris@350:     }
Chris@350: 
Chris@350:     if (attrs.value("windowType") != "") {
Chris@350:         setWindowType(Window<float>::getTypeForName
Chris@350:                       (attrs.value("windowType").toStdString()));
Chris@350:     }
Chris@350: 
Chris@350:     if (attrs.value("startTime") != "") {
Chris@350:         setStartTime(RealTime::fromString(attrs.value("startTime").toStdString()));
Chris@350:     }
Chris@350: 
Chris@350:     if (attrs.value("duration") != "") {
Chris@988:         setDuration(RealTime::fromString(attrs.value("duration").toStdString()));
Chris@350:     }
Chris@350:     
Chris@350:     if (attrs.value("sampleRate") != "") {
Chris@350:         setSampleRate(attrs.value("sampleRate").toFloat());
Chris@350:     }
Chris@508: 
Chris@508:     if (attrs.value("summaryType") != "") {
Chris@508:         setSummaryType(stringToSummaryType(attrs.value("summaryType")));
Chris@508:     }
Chris@350: }
Chris@350: