annotate widgets/PluginParameterBox.cpp @ 1204:d421df27e184 3.0-integration

Further PropertyBox layout overhaul: avoid crash (/ assertion failure) when property type changes from e.g. colour to colourmap, by replacing the existing widget within the layout rather than trying to repopulate it
author Chris Cannam
date Tue, 20 Dec 2016 10:49:24 +0000
parents 4a578a360011
children 01778052e663
rev   line source
Chris@60 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@60 2
Chris@60 3 /*
Chris@60 4 Sonic Visualiser
Chris@60 5 An audio file viewer and annotation editor.
Chris@60 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@60 8
Chris@60 9 This program is free software; you can redistribute it and/or
Chris@60 10 modify it under the terms of the GNU General Public License as
Chris@60 11 published by the Free Software Foundation; either version 2 of the
Chris@60 12 License, or (at your option) any later version. See the file
Chris@60 13 COPYING included with this distribution for more information.
Chris@60 14 */
Chris@60 15
Chris@60 16 #include "PluginParameterBox.h"
Chris@60 17
Chris@60 18 #include "AudioDial.h"
Chris@60 19
Chris@71 20 #include "plugin/PluginXml.h"
Chris@342 21 #include "plugin/RealTimePluginInstance.h" // for PortHint stuff
Chris@71 22
Chris@167 23 #include "base/RangeMapper.h"
Chris@167 24
Chris@60 25 #include <QDoubleSpinBox>
Chris@60 26 #include <QGridLayout>
Chris@63 27 #include <QComboBox>
Chris@63 28 #include <QCheckBox>
Chris@60 29 #include <QLayout>
Chris@60 30 #include <QLabel>
Chris@60 31
Chris@60 32 #include <iostream>
Chris@60 33 #include <string>
Chris@60 34
Chris@78 35 #include <cmath>
Chris@78 36
Chris@71 37 PluginParameterBox::PluginParameterBox(Vamp::PluginBase *plugin, QWidget *parent) :
Chris@62 38 QFrame(parent),
Chris@293 39 m_plugin(plugin),
Chris@293 40 m_programCombo(0)
Chris@60 41 {
Chris@60 42 m_layout = new QGridLayout;
Chris@60 43 setLayout(m_layout);
Chris@60 44 populate();
Chris@60 45 }
Chris@60 46
Chris@60 47 PluginParameterBox::~PluginParameterBox()
Chris@60 48 {
Chris@60 49 }
Chris@60 50
Chris@60 51 void
Chris@60 52 PluginParameterBox::populate()
Chris@60 53 {
Chris@71 54 Vamp::PluginBase::ParameterList params = m_plugin->getParameterDescriptors();
Chris@293 55 m_programs = m_plugin->getPrograms();
Chris@60 56
Chris@60 57 m_params.clear();
Chris@60 58
Chris@293 59 if (params.empty() && m_programs.empty()) {
Chris@62 60 m_layout->addWidget
Chris@62 61 (new QLabel(tr("This plugin has no adjustable parameters.")),
Chris@62 62 0, 0);
Chris@62 63 }
Chris@62 64
Chris@63 65 int offset = 0;
Chris@63 66
Chris@293 67 if (!m_programs.empty()) {
Chris@63 68
Chris@63 69 std::string currentProgram = m_plugin->getCurrentProgram();
Chris@63 70
Chris@293 71 m_programCombo = new QComboBox;
Chris@293 72 m_programCombo->setMaxVisibleItems
Chris@908 73 (int(m_programs.size() < 25 ? m_programs.size() : 20));
Chris@63 74
Chris@908 75 for (int i = 0; in_range_for(m_programs, i); ++i) {
Chris@293 76 m_programCombo->addItem(m_programs[i].c_str());
Chris@293 77 if (m_programs[i] == currentProgram) {
Chris@908 78 m_programCombo->setCurrentIndex(int(i));
Chris@63 79 }
Chris@63 80 }
Chris@63 81
Chris@63 82 m_layout->addWidget(new QLabel(tr("Program")), 0, 0);
Chris@293 83 m_layout->addWidget(m_programCombo, 0, 1, 1, 2);
Chris@63 84
Chris@293 85 connect(m_programCombo, SIGNAL(currentIndexChanged(const QString &)),
Chris@63 86 this, SLOT(programComboChanged(const QString &)));
Chris@63 87
Chris@63 88 offset = 1;
Chris@63 89 }
Chris@63 90
Chris@908 91 for (int i = 0; in_range_for(params, i); ++i) {
Chris@60 92
Chris@207 93 QString identifier = params[i].identifier.c_str();
Chris@60 94 QString name = params[i].name.c_str();
Chris@60 95 QString unit = params[i].unit.c_str();
Chris@60 96
Chris@60 97 float min = params[i].minValue;
Chris@60 98 float max = params[i].maxValue;
Chris@60 99 float deft = params[i].defaultValue;
Chris@207 100 float value = m_plugin->getParameter(params[i].identifier);
Chris@60 101
Chris@342 102 int hint = PortHint::NoHint;
Chris@342 103 RealTimePluginInstance *rtpi = dynamic_cast<RealTimePluginInstance *>
Chris@342 104 (m_plugin);
Chris@342 105 if (rtpi) {
Chris@342 106 hint = rtpi->getParameterDisplayHint(i);
Chris@342 107 }
Chris@342 108
Chris@60 109 float qtz = 0.0;
Chris@60 110 if (params[i].isQuantized) qtz = params[i].quantizeStep;
Chris@60 111
Chris@682 112 // cerr << "PluginParameterBox: hint = " << hint << ", min = " << min << ", max = "
Chris@682 113 // << max << ", qtz = " << qtz << endl;
Chris@342 114
Chris@74 115 std::vector<std::string> valueNames = params[i].valueNames;
Chris@74 116
Chris@60 117 // construct an integer range
Chris@60 118
Chris@60 119 int imin = 0, imax = 100;
Chris@60 120
Chris@342 121 if (!(hint & PortHint::Logarithmic)) {
Chris@342 122 if (qtz > 0.0) {
Chris@908 123 imax = int(lrintf((max - min) / qtz));
Chris@342 124 } else {
Chris@908 125 qtz = (max - min) / 100.f;
Chris@342 126 }
Chris@60 127 }
Chris@60 128
Chris@60 129 //!!! would be nice to ensure the default value corresponds to
Chris@60 130 // an integer!
Chris@60 131
Chris@207 132 QLabel *label = new QLabel(name);
Chris@208 133 if (params[i].description != "") {
Chris@828 134 label->setToolTip(QString("<qt>%1</qt>")
Chris@828 135 .arg(params[i].description.c_str())
Chris@828 136 .replace("\n", "<br>"));
Chris@208 137 }
Chris@63 138 m_layout->addWidget(label, i + offset, 0);
Chris@60 139
Chris@60 140 ParamRec rec;
Chris@60 141 rec.param = params[i];
Chris@63 142 rec.dial = 0;
Chris@63 143 rec.spin = 0;
Chris@63 144 rec.check = 0;
Chris@74 145 rec.combo = 0;
Chris@63 146
Chris@74 147 if (params[i].isQuantized && !valueNames.empty()) {
Chris@74 148
Chris@74 149 QComboBox *combobox = new QComboBox;
Chris@207 150 combobox->setObjectName(identifier);
Chris@74 151 for (unsigned int j = 0; j < valueNames.size(); ++j) {
Chris@74 152 combobox->addItem(valueNames[j].c_str());
Chris@249 153 if ((unsigned int)(lrintf(fabsf((value - min) / qtz))) == j) {
Chris@74 154 combobox->setCurrentIndex(j);
Chris@74 155 }
Chris@74 156 }
Chris@74 157 connect(combobox, SIGNAL(activated(int)),
Chris@74 158 this, SLOT(dialChanged(int)));
Chris@74 159 m_layout->addWidget(combobox, i + offset, 1, 1, 2);
Chris@74 160 rec.combo = combobox;
Chris@74 161
Chris@74 162 } else if (min == 0.0 && max == 1.0 && qtz == 1.0) {
Chris@63 163
Chris@63 164 QCheckBox *checkbox = new QCheckBox;
Chris@207 165 checkbox->setObjectName(identifier);
Chris@293 166 checkbox->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked);
Chris@63 167 connect(checkbox, SIGNAL(stateChanged(int)),
Chris@63 168 this, SLOT(checkBoxChanged(int)));
Chris@63 169 m_layout->addWidget(checkbox, i + offset, 2);
Chris@63 170 rec.check = checkbox;
Chris@63 171
Chris@63 172 } else {
Chris@63 173
Chris@63 174 AudioDial *dial = new AudioDial;
Chris@207 175 dial->setObjectName(name);
Chris@63 176 dial->setMinimum(imin);
Chris@63 177 dial->setMaximum(imax);
Chris@63 178 dial->setPageStep(1);
Chris@63 179 dial->setNotchesVisible((imax - imin) <= 12);
Chris@342 180 //!!! dial->setDefaultValue(lrintf((deft - min) / qtz));
Chris@342 181 // dial->setValue(lrintf((value - min) / qtz));
Chris@63 182 dial->setFixedWidth(32);
Chris@63 183 dial->setFixedHeight(32);
Chris@342 184 RangeMapper *rm = 0;
Chris@342 185 if (hint & PortHint::Logarithmic) {
Chris@342 186 rm = new LogRangeMapper(imin, imax, min, max, unit);
Chris@342 187 } else {
Chris@342 188 rm = new LinearRangeMapper(imin, imax, min, max, unit);
Chris@342 189 }
Chris@342 190 dial->setRangeMapper(rm);
Chris@342 191 dial->setDefaultValue(rm->getPositionForValue(deft));
Chris@342 192 dial->setValue(rm->getPositionForValue(value));
Chris@168 193 dial->setShowToolTip(true);
Chris@63 194 connect(dial, SIGNAL(valueChanged(int)),
Chris@63 195 this, SLOT(dialChanged(int)));
Chris@63 196 m_layout->addWidget(dial, i + offset, 1);
Chris@63 197
Chris@63 198 QDoubleSpinBox *spinbox = new QDoubleSpinBox;
Chris@207 199 spinbox->setObjectName(identifier);
Chris@63 200 spinbox->setMinimum(min);
Chris@63 201 spinbox->setMaximum(max);
Chris@63 202 spinbox->setSuffix(QString(" %1").arg(unit));
Chris@342 203 if (qtz != 0) spinbox->setSingleStep(qtz);
Chris@63 204 spinbox->setValue(value);
Chris@103 205 spinbox->setDecimals(4);
Chris@63 206 connect(spinbox, SIGNAL(valueChanged(double)),
Chris@63 207 this, SLOT(spinBoxChanged(double)));
Chris@63 208 m_layout->addWidget(spinbox, i + offset, 2);
Chris@63 209 rec.dial = dial;
Chris@63 210 rec.spin = spinbox;
Chris@63 211 }
Chris@63 212
Chris@207 213 m_params[identifier] = rec;
Chris@207 214 m_nameMap[name] = identifier;
Chris@60 215 }
Chris@60 216 }
Chris@60 217
Chris@60 218 void
Chris@60 219 PluginParameterBox::dialChanged(int ival)
Chris@60 220 {
Chris@60 221 QObject *obj = sender();
Chris@207 222 QString identifier = obj->objectName();
Chris@60 223
Chris@207 224 if (m_params.find(identifier) == m_params.end() &&
Chris@207 225 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 226 identifier = m_nameMap[identifier];
Chris@167 227 }
Chris@167 228
Chris@207 229 if (m_params.find(identifier) == m_params.end()) {
Chris@682 230 cerr << "WARNING: PluginParameterBox::dialChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@60 231 return;
Chris@60 232 }
Chris@60 233
Chris@207 234 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@60 235
Chris@60 236 float min = params.minValue;
Chris@60 237 float max = params.maxValue;
Chris@60 238
Chris@168 239 float newValue;
Chris@168 240
Chris@60 241 float qtz = 0.0;
Chris@60 242 if (params.isQuantized) qtz = params.quantizeStep;
Chris@168 243
Chris@168 244 AudioDial *ad = dynamic_cast<AudioDial *>(obj);
Chris@60 245
Chris@168 246 if (ad && ad->rangeMapper()) {
Chris@168 247
Chris@908 248 newValue = float(ad->mappedValue());
Chris@168 249 if (newValue < min) newValue = min;
Chris@168 250 if (newValue > max) newValue = max;
Chris@168 251 if (qtz != 0.0) {
Chris@908 252 ival = int(lrintf((newValue - min) / qtz));
Chris@908 253 newValue = min + float(ival) * qtz;
Chris@168 254 }
Chris@168 255
Chris@168 256 } else {
Chris@908 257 if (qtz == 0.f) {
Chris@908 258 qtz = (max - min) / 100.f;
Chris@168 259 }
Chris@908 260 newValue = min + float(ival) * qtz;
Chris@60 261 }
Chris@60 262
Chris@587 263 // SVDEBUG << "PluginParameterBox::dialChanged: newValue = " << newValue << endl;
Chris@342 264
Chris@207 265 QDoubleSpinBox *spin = m_params[identifier].spin;
Chris@63 266 if (spin) {
Chris@63 267 spin->blockSignals(true);
Chris@63 268 spin->setValue(newValue);
Chris@63 269 spin->blockSignals(false);
Chris@63 270 }
Chris@60 271
Chris@587 272 // SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << newValue << endl;
Chris@530 273
Chris@207 274 m_plugin->setParameter(identifier.toStdString(), newValue);
Chris@64 275
Chris@293 276 updateProgramCombo();
Chris@293 277
Chris@71 278 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@60 279 }
Chris@60 280
Chris@60 281 void
Chris@63 282 PluginParameterBox::checkBoxChanged(int state)
Chris@63 283 {
Chris@63 284 QObject *obj = sender();
Chris@207 285 QString identifier = obj->objectName();
Chris@63 286
Chris@207 287 if (m_params.find(identifier) == m_params.end() &&
Chris@207 288 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 289 identifier = m_nameMap[identifier];
Chris@167 290 }
Chris@167 291
Chris@207 292 if (m_params.find(identifier) == m_params.end()) {
Chris@682 293 cerr << "WARNING: PluginParameterBox::checkBoxChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@63 294 return;
Chris@63 295 }
Chris@63 296
Chris@207 297 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@63 298
Chris@207 299 if (state) m_plugin->setParameter(identifier.toStdString(), 1.0);
Chris@207 300 else m_plugin->setParameter(identifier.toStdString(), 0.0);
Chris@64 301
Chris@293 302 updateProgramCombo();
Chris@293 303
Chris@71 304 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@63 305 }
Chris@63 306
Chris@63 307 void
Chris@60 308 PluginParameterBox::spinBoxChanged(double value)
Chris@60 309 {
Chris@60 310 QObject *obj = sender();
Chris@207 311 QString identifier = obj->objectName();
Chris@60 312
Chris@207 313 if (m_params.find(identifier) == m_params.end() &&
Chris@207 314 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 315 identifier = m_nameMap[identifier];
Chris@167 316 }
Chris@167 317
Chris@207 318 if (m_params.find(identifier) == m_params.end()) {
Chris@682 319 cerr << "WARNING: PluginParameterBox::spinBoxChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@60 320 return;
Chris@60 321 }
Chris@60 322
Chris@207 323 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@60 324
Chris@60 325 float min = params.minValue;
Chris@60 326 float max = params.maxValue;
Chris@60 327
Chris@60 328 float qtz = 0.0;
Chris@60 329 if (params.isQuantized) qtz = params.quantizeStep;
Chris@60 330
Chris@60 331 if (qtz > 0.0) {
Chris@908 332 int step = int(lrintf(float(value - min) / qtz));
Chris@908 333 value = min + float(step) * qtz;
Chris@60 334 }
Chris@60 335
Chris@807 336 // int imax = 100;
Chris@60 337
Chris@60 338 if (qtz > 0.0) {
Chris@807 339 // imax = lrintf((max - min) / qtz);
Chris@60 340 } else {
Chris@908 341 qtz = (max - min) / 100.f;
Chris@60 342 }
Chris@60 343
Chris@908 344 int ival = int(lrintf(float(value - min) / qtz));
Chris@60 345
Chris@207 346 AudioDial *dial = m_params[identifier].dial;
Chris@63 347 if (dial) {
Chris@63 348 dial->blockSignals(true);
Chris@342 349 if (dial->rangeMapper()) {
Chris@342 350 dial->setMappedValue(value);
Chris@342 351 } else {
Chris@342 352 dial->setValue(ival);
Chris@342 353 }
Chris@63 354 dial->blockSignals(false);
Chris@63 355 }
Chris@60 356
Chris@587 357 SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << value << endl;
Chris@530 358
Chris@908 359 m_plugin->setParameter(identifier.toStdString(), float(value));
Chris@64 360
Chris@293 361 updateProgramCombo();
Chris@293 362
Chris@71 363 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@60 364 }
Chris@60 365
Chris@63 366 void
Chris@63 367 PluginParameterBox::programComboChanged(const QString &newProgram)
Chris@63 368 {
Chris@63 369 m_plugin->selectProgram(newProgram.toStdString());
Chris@60 370
Chris@63 371 for (std::map<QString, ParamRec>::iterator i = m_params.begin();
Chris@63 372 i != m_params.end(); ++i) {
Chris@63 373
Chris@71 374 Vamp::PluginBase::ParameterDescriptor &param = i->second.param;
Chris@207 375 float value = m_plugin->getParameter(param.identifier);
Chris@63 376
Chris@63 377 if (i->second.spin) {
Chris@63 378 i->second.spin->blockSignals(true);
Chris@63 379 i->second.spin->setValue(value);
Chris@63 380 i->second.spin->blockSignals(false);
Chris@63 381 }
Chris@63 382
Chris@63 383 if (i->second.dial) {
Chris@63 384
Chris@63 385 float min = param.minValue;
Chris@63 386 float max = param.maxValue;
Chris@63 387
Chris@63 388 float qtz = 0.0;
Chris@63 389 if (param.isQuantized) qtz = param.quantizeStep;
Chris@63 390
Chris@63 391 if (qtz == 0.0) {
Chris@908 392 qtz = (max - min) / 100.f;
Chris@63 393 }
Chris@63 394
Chris@63 395 i->second.dial->blockSignals(true);
Chris@908 396 i->second.dial->setValue(int(lrintf(float(value - min) / qtz)));
Chris@63 397 i->second.dial->blockSignals(false);
Chris@63 398 }
Chris@74 399
Chris@74 400 if (i->second.combo) {
Chris@74 401 i->second.combo->blockSignals(true);
Chris@908 402 i->second.combo->setCurrentIndex(int(lrintf(value)));
Chris@74 403 i->second.combo->blockSignals(false);
Chris@74 404 }
Chris@293 405
Chris@293 406 if (i->second.check) {
Chris@293 407 i->second.check->blockSignals(true);
Chris@293 408 i->second.check->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked);
Chris@293 409 i->second.check->blockSignals(false);
Chris@293 410 }
Chris@63 411 }
Chris@64 412
Chris@71 413 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@63 414 }
Chris@63 415
Chris@293 416 void
Chris@293 417 PluginParameterBox::updateProgramCombo()
Chris@293 418 {
Chris@293 419 if (!m_programCombo || m_programs.empty()) return;
Chris@293 420
Chris@293 421 std::string currentProgram = m_plugin->getCurrentProgram();
Chris@293 422
Chris@908 423 for (int i = 0; in_range_for(m_programs, i); ++i) {
Chris@293 424 if (m_programs[i] == currentProgram) {
Chris@293 425 m_programCombo->setCurrentIndex(i);
Chris@293 426 }
Chris@293 427 }
Chris@293 428 }
Chris@293 429
Chris@293 430