Chris@60: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@60: Chris@60: /* Chris@60: Sonic Visualiser Chris@60: An audio file viewer and annotation editor. Chris@60: Centre for Digital Music, Queen Mary, University of London. Chris@182: This file copyright 2006 Chris Cannam and QMUL. Chris@60: Chris@60: This program is free software; you can redistribute it and/or Chris@60: modify it under the terms of the GNU General Public License as Chris@60: published by the Free Software Foundation; either version 2 of the Chris@60: License, or (at your option) any later version. See the file Chris@60: COPYING included with this distribution for more information. Chris@60: */ Chris@60: Chris@60: #include "PluginParameterBox.h" Chris@60: Chris@60: #include "AudioDial.h" Chris@60: Chris@71: #include "plugin/PluginXml.h" Chris@342: #include "plugin/RealTimePluginInstance.h" // for PortHint stuff Chris@71: Chris@167: #include "base/RangeMapper.h" Chris@167: Chris@60: #include Chris@60: #include Chris@63: #include Chris@63: #include Chris@60: #include Chris@60: #include Chris@60: Chris@60: #include Chris@60: #include Chris@60: Chris@78: #include Chris@78: Chris@71: PluginParameterBox::PluginParameterBox(Vamp::PluginBase *plugin, QWidget *parent) : Chris@62: QFrame(parent), Chris@293: m_plugin(plugin), Chris@1408: m_programCombo(nullptr) Chris@60: { Chris@60: m_layout = new QGridLayout; Chris@60: setLayout(m_layout); Chris@60: populate(); Chris@60: } Chris@60: Chris@60: PluginParameterBox::~PluginParameterBox() Chris@60: { Chris@60: } Chris@60: Chris@60: void Chris@60: PluginParameterBox::populate() Chris@60: { Chris@71: Vamp::PluginBase::ParameterList params = m_plugin->getParameterDescriptors(); Chris@293: m_programs = m_plugin->getPrograms(); Chris@60: Chris@60: m_params.clear(); Chris@60: Chris@293: if (params.empty() && m_programs.empty()) { Chris@62: m_layout->addWidget Chris@62: (new QLabel(tr("This plugin has no adjustable parameters.")), Chris@62: 0, 0); Chris@62: } Chris@62: Chris@63: int offset = 0; Chris@63: Chris@293: if (!m_programs.empty()) { Chris@63: Chris@63: std::string currentProgram = m_plugin->getCurrentProgram(); Chris@63: Chris@293: m_programCombo = new QComboBox; Chris@293: m_programCombo->setMaxVisibleItems Chris@908: (int(m_programs.size() < 25 ? m_programs.size() : 20)); Chris@63: Chris@908: for (int i = 0; in_range_for(m_programs, i); ++i) { Chris@293: m_programCombo->addItem(m_programs[i].c_str()); Chris@293: if (m_programs[i] == currentProgram) { Chris@908: m_programCombo->setCurrentIndex(int(i)); Chris@63: } Chris@63: } Chris@63: Chris@63: m_layout->addWidget(new QLabel(tr("Program")), 0, 0); Chris@293: m_layout->addWidget(m_programCombo, 0, 1, 1, 2); Chris@63: Chris@293: connect(m_programCombo, SIGNAL(currentIndexChanged(const QString &)), Chris@63: this, SLOT(programComboChanged(const QString &))); Chris@63: Chris@63: offset = 1; Chris@63: } Chris@63: Chris@908: for (int i = 0; in_range_for(params, i); ++i) { Chris@60: Chris@207: QString identifier = params[i].identifier.c_str(); Chris@60: QString name = params[i].name.c_str(); Chris@60: QString unit = params[i].unit.c_str(); Chris@60: Chris@60: float min = params[i].minValue; Chris@60: float max = params[i].maxValue; Chris@60: float deft = params[i].defaultValue; Chris@207: float value = m_plugin->getParameter(params[i].identifier); Chris@60: Chris@342: int hint = PortHint::NoHint; Chris@342: RealTimePluginInstance *rtpi = dynamic_cast Chris@342: (m_plugin); Chris@342: if (rtpi) { Chris@342: hint = rtpi->getParameterDisplayHint(i); Chris@342: } Chris@342: Chris@60: float qtz = 0.0; Chris@60: if (params[i].isQuantized) qtz = params[i].quantizeStep; Chris@60: Chris@682: // cerr << "PluginParameterBox: hint = " << hint << ", min = " << min << ", max = " Chris@682: // << max << ", qtz = " << qtz << endl; Chris@342: Chris@74: std::vector valueNames = params[i].valueNames; Chris@74: Chris@60: // construct an integer range Chris@60: Chris@60: int imin = 0, imax = 100; Chris@60: Chris@342: if (!(hint & PortHint::Logarithmic)) { Chris@342: if (qtz > 0.0) { Chris@908: imax = int(lrintf((max - min) / qtz)); Chris@1348: if (imax <= imin) { Chris@1348: imax = 100; Chris@1348: qtz = (max - min) / 100.f; Chris@1348: } Chris@342: } else { Chris@908: qtz = (max - min) / 100.f; Chris@342: } Chris@60: } Chris@60: Chris@60: //!!! would be nice to ensure the default value corresponds to Chris@60: // an integer! Chris@60: Chris@207: QLabel *label = new QLabel(name); Chris@208: if (params[i].description != "") { Chris@828: label->setToolTip(QString("%1") Chris@828: .arg(params[i].description.c_str()) Chris@828: .replace("\n", "
")); Chris@208: } Chris@63: m_layout->addWidget(label, i + offset, 0); Chris@60: Chris@60: ParamRec rec; Chris@60: rec.param = params[i]; Chris@1408: rec.dial = nullptr; Chris@1408: rec.spin = nullptr; Chris@1408: rec.check = nullptr; Chris@1408: rec.combo = nullptr; Chris@63: Chris@74: if (params[i].isQuantized && !valueNames.empty()) { Chris@74: Chris@74: QComboBox *combobox = new QComboBox; Chris@207: combobox->setObjectName(identifier); Chris@74: for (unsigned int j = 0; j < valueNames.size(); ++j) { Chris@74: combobox->addItem(valueNames[j].c_str()); Chris@249: if ((unsigned int)(lrintf(fabsf((value - min) / qtz))) == j) { Chris@74: combobox->setCurrentIndex(j); Chris@74: } Chris@74: } Chris@74: connect(combobox, SIGNAL(activated(int)), Chris@74: this, SLOT(dialChanged(int))); Chris@74: m_layout->addWidget(combobox, i + offset, 1, 1, 2); Chris@74: rec.combo = combobox; Chris@74: Chris@74: } else if (min == 0.0 && max == 1.0 && qtz == 1.0) { Chris@63: Chris@63: QCheckBox *checkbox = new QCheckBox; Chris@207: checkbox->setObjectName(identifier); Chris@293: checkbox->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked); Chris@63: connect(checkbox, SIGNAL(stateChanged(int)), Chris@63: this, SLOT(checkBoxChanged(int))); Chris@63: m_layout->addWidget(checkbox, i + offset, 2); Chris@63: rec.check = checkbox; Chris@63: Chris@63: } else { Chris@63: Chris@63: AudioDial *dial = new AudioDial; Chris@207: dial->setObjectName(name); Chris@63: dial->setMinimum(imin); Chris@63: dial->setMaximum(imax); Chris@63: dial->setPageStep(1); Chris@63: dial->setNotchesVisible((imax - imin) <= 12); Chris@342: //!!! dial->setDefaultValue(lrintf((deft - min) / qtz)); Chris@342: // dial->setValue(lrintf((value - min) / qtz)); Chris@63: dial->setFixedWidth(32); Chris@63: dial->setFixedHeight(32); Chris@1348: if (max == min || imax == imin) { Chris@1348: SVCERR << "WARNING: for parameter \"" << name Chris@1348: << "\" of plugin \"" << m_plugin->getName() Chris@1348: << "\": invalid range " << min << " -> " << max Chris@1348: << " with quantize step " << qtz << endl; Chris@342: } else { Chris@1408: RangeMapper *rm = nullptr; Chris@1348: if (hint & PortHint::Logarithmic) { Chris@1348: rm = new LogRangeMapper(imin, imax, min, max, unit); Chris@1348: } else { Chris@1348: rm = new LinearRangeMapper(imin, imax, min, max, unit); Chris@1348: } Chris@1348: dial->setRangeMapper(rm); Chris@1348: dial->setDefaultValue(rm->getPositionForValue(deft)); Chris@1348: dial->setValue(rm->getPositionForValue(value)); Chris@342: } Chris@168: dial->setShowToolTip(true); Chris@63: connect(dial, SIGNAL(valueChanged(int)), Chris@63: this, SLOT(dialChanged(int))); Chris@63: m_layout->addWidget(dial, i + offset, 1); Chris@63: Chris@63: QDoubleSpinBox *spinbox = new QDoubleSpinBox; Chris@207: spinbox->setObjectName(identifier); Chris@63: spinbox->setMinimum(min); Chris@63: spinbox->setMaximum(max); Chris@63: spinbox->setSuffix(QString(" %1").arg(unit)); Chris@342: if (qtz != 0) spinbox->setSingleStep(qtz); Chris@63: spinbox->setValue(value); Chris@103: spinbox->setDecimals(4); Chris@63: connect(spinbox, SIGNAL(valueChanged(double)), Chris@63: this, SLOT(spinBoxChanged(double))); Chris@63: m_layout->addWidget(spinbox, i + offset, 2); Chris@63: rec.dial = dial; Chris@63: rec.spin = spinbox; Chris@63: } Chris@63: Chris@207: m_params[identifier] = rec; Chris@207: m_nameMap[name] = identifier; Chris@60: } Chris@60: } Chris@60: Chris@60: void Chris@60: PluginParameterBox::dialChanged(int ival) Chris@60: { Chris@60: QObject *obj = sender(); Chris@207: QString identifier = obj->objectName(); Chris@60: Chris@207: if (m_params.find(identifier) == m_params.end() && Chris@207: m_nameMap.find(identifier) != m_nameMap.end()) { Chris@207: identifier = m_nameMap[identifier]; Chris@167: } Chris@167: Chris@207: if (m_params.find(identifier) == m_params.end()) { Chris@682: cerr << "WARNING: PluginParameterBox::dialChanged: Unknown parameter \"" << identifier << "\"" << endl; Chris@60: return; Chris@60: } Chris@60: Chris@207: Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param; Chris@60: Chris@60: float min = params.minValue; Chris@60: float max = params.maxValue; Chris@60: Chris@168: float newValue; Chris@168: Chris@60: float qtz = 0.0; Chris@60: if (params.isQuantized) qtz = params.quantizeStep; Chris@168: Chris@168: AudioDial *ad = dynamic_cast(obj); Chris@60: Chris@168: if (ad && ad->rangeMapper()) { Chris@168: Chris@908: newValue = float(ad->mappedValue()); Chris@168: if (newValue < min) newValue = min; Chris@168: if (newValue > max) newValue = max; Chris@168: if (qtz != 0.0) { Chris@908: ival = int(lrintf((newValue - min) / qtz)); Chris@908: newValue = min + float(ival) * qtz; Chris@168: } Chris@168: Chris@168: } else { Chris@908: if (qtz == 0.f) { Chris@908: qtz = (max - min) / 100.f; Chris@168: } Chris@908: newValue = min + float(ival) * qtz; Chris@60: } Chris@60: Chris@587: // SVDEBUG << "PluginParameterBox::dialChanged: newValue = " << newValue << endl; Chris@342: Chris@207: QDoubleSpinBox *spin = m_params[identifier].spin; Chris@63: if (spin) { Chris@63: spin->blockSignals(true); Chris@63: spin->setValue(newValue); Chris@63: spin->blockSignals(false); Chris@63: } Chris@60: Chris@587: // SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << newValue << endl; Chris@530: Chris@207: m_plugin->setParameter(identifier.toStdString(), newValue); Chris@64: Chris@293: updateProgramCombo(); Chris@293: Chris@71: emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString()); Chris@60: } Chris@60: Chris@60: void Chris@63: PluginParameterBox::checkBoxChanged(int state) Chris@63: { Chris@63: QObject *obj = sender(); Chris@207: QString identifier = obj->objectName(); Chris@63: Chris@207: if (m_params.find(identifier) == m_params.end() && Chris@207: m_nameMap.find(identifier) != m_nameMap.end()) { Chris@207: identifier = m_nameMap[identifier]; Chris@167: } Chris@167: Chris@207: if (m_params.find(identifier) == m_params.end()) { Chris@682: cerr << "WARNING: PluginParameterBox::checkBoxChanged: Unknown parameter \"" << identifier << "\"" << endl; Chris@63: return; Chris@63: } Chris@63: Chris@207: Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param; Chris@63: Chris@207: if (state) m_plugin->setParameter(identifier.toStdString(), 1.0); Chris@207: else m_plugin->setParameter(identifier.toStdString(), 0.0); Chris@64: Chris@293: updateProgramCombo(); Chris@293: Chris@71: emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString()); Chris@63: } Chris@63: Chris@63: void Chris@60: PluginParameterBox::spinBoxChanged(double value) Chris@60: { Chris@60: QObject *obj = sender(); Chris@207: QString identifier = obj->objectName(); Chris@60: Chris@207: if (m_params.find(identifier) == m_params.end() && Chris@207: m_nameMap.find(identifier) != m_nameMap.end()) { Chris@207: identifier = m_nameMap[identifier]; Chris@167: } Chris@167: Chris@207: if (m_params.find(identifier) == m_params.end()) { Chris@682: cerr << "WARNING: PluginParameterBox::spinBoxChanged: Unknown parameter \"" << identifier << "\"" << endl; Chris@60: return; Chris@60: } Chris@60: Chris@207: Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param; Chris@60: Chris@60: float min = params.minValue; Chris@60: float max = params.maxValue; Chris@60: Chris@60: float qtz = 0.0; Chris@60: if (params.isQuantized) qtz = params.quantizeStep; Chris@60: Chris@60: if (qtz > 0.0) { Chris@908: int step = int(lrintf(float(value - min) / qtz)); Chris@908: value = min + float(step) * qtz; Chris@60: } Chris@60: Chris@807: // int imax = 100; Chris@60: Chris@60: if (qtz > 0.0) { Chris@807: // imax = lrintf((max - min) / qtz); Chris@60: } else { Chris@908: qtz = (max - min) / 100.f; Chris@60: } Chris@60: Chris@908: int ival = int(lrintf(float(value - min) / qtz)); Chris@60: Chris@207: AudioDial *dial = m_params[identifier].dial; Chris@63: if (dial) { Chris@63: dial->blockSignals(true); Chris@342: if (dial->rangeMapper()) { Chris@342: dial->setMappedValue(value); Chris@342: } else { Chris@342: dial->setValue(ival); Chris@342: } Chris@63: dial->blockSignals(false); Chris@63: } Chris@60: Chris@587: SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << value << endl; Chris@530: Chris@908: m_plugin->setParameter(identifier.toStdString(), float(value)); Chris@64: Chris@293: updateProgramCombo(); Chris@293: Chris@71: emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString()); Chris@60: } Chris@60: Chris@63: void Chris@63: PluginParameterBox::programComboChanged(const QString &newProgram) Chris@63: { Chris@63: m_plugin->selectProgram(newProgram.toStdString()); Chris@60: Chris@63: for (std::map::iterator i = m_params.begin(); Chris@63: i != m_params.end(); ++i) { Chris@63: Chris@71: Vamp::PluginBase::ParameterDescriptor ¶m = i->second.param; Chris@207: float value = m_plugin->getParameter(param.identifier); Chris@63: Chris@63: if (i->second.spin) { Chris@63: i->second.spin->blockSignals(true); Chris@63: i->second.spin->setValue(value); Chris@63: i->second.spin->blockSignals(false); Chris@63: } Chris@63: Chris@63: if (i->second.dial) { Chris@63: Chris@63: float min = param.minValue; Chris@63: float max = param.maxValue; Chris@63: Chris@63: float qtz = 0.0; Chris@63: if (param.isQuantized) qtz = param.quantizeStep; Chris@63: Chris@63: if (qtz == 0.0) { Chris@908: qtz = (max - min) / 100.f; Chris@63: } Chris@63: Chris@63: i->second.dial->blockSignals(true); Chris@908: i->second.dial->setValue(int(lrintf(float(value - min) / qtz))); Chris@63: i->second.dial->blockSignals(false); Chris@63: } Chris@74: Chris@74: if (i->second.combo) { Chris@74: i->second.combo->blockSignals(true); Chris@908: i->second.combo->setCurrentIndex(int(lrintf(value))); Chris@74: i->second.combo->blockSignals(false); Chris@74: } Chris@293: Chris@293: if (i->second.check) { Chris@293: i->second.check->blockSignals(true); Chris@293: i->second.check->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked); Chris@293: i->second.check->blockSignals(false); Chris@293: } Chris@63: } Chris@64: Chris@71: emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString()); Chris@63: } Chris@63: Chris@293: void Chris@293: PluginParameterBox::updateProgramCombo() Chris@293: { Chris@293: if (!m_programCombo || m_programs.empty()) return; Chris@293: Chris@293: std::string currentProgram = m_plugin->getCurrentProgram(); Chris@293: Chris@908: for (int i = 0; in_range_for(m_programs, i); ++i) { Chris@293: if (m_programs[i] == currentProgram) { Chris@293: m_programCombo->setCurrentIndex(i); Chris@293: } Chris@293: } Chris@293: } Chris@293: Chris@293: