annotate widgets/PluginParameterBox.cpp @ 1581:11660e0c896f audio-source-refactor

Use shared_ptr for plugin instances throughout; also descriptors are copyable
author Chris Cannam
date Fri, 20 Mar 2020 16:31:23 +0000
parents c8a6fd3f9dff
children 19da7fa80c3c
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@1581 37 PluginParameterBox::PluginParameterBox(std::shared_ptr<Vamp::PluginBase> plugin,
Chris@1581 38 QWidget *parent) :
Chris@62 39 QFrame(parent),
Chris@293 40 m_plugin(plugin),
Chris@1408 41 m_programCombo(nullptr)
Chris@60 42 {
Chris@60 43 m_layout = new QGridLayout;
Chris@60 44 setLayout(m_layout);
Chris@60 45 populate();
Chris@60 46 }
Chris@60 47
Chris@60 48 PluginParameterBox::~PluginParameterBox()
Chris@60 49 {
Chris@60 50 }
Chris@60 51
Chris@60 52 void
Chris@60 53 PluginParameterBox::populate()
Chris@60 54 {
Chris@71 55 Vamp::PluginBase::ParameterList params = m_plugin->getParameterDescriptors();
Chris@293 56 m_programs = m_plugin->getPrograms();
Chris@60 57
Chris@60 58 m_params.clear();
Chris@60 59
Chris@293 60 if (params.empty() && m_programs.empty()) {
Chris@62 61 m_layout->addWidget
Chris@62 62 (new QLabel(tr("This plugin has no adjustable parameters.")),
Chris@62 63 0, 0);
Chris@62 64 }
Chris@62 65
Chris@63 66 int offset = 0;
Chris@63 67
Chris@293 68 if (!m_programs.empty()) {
Chris@63 69
Chris@63 70 std::string currentProgram = m_plugin->getCurrentProgram();
Chris@63 71
Chris@293 72 m_programCombo = new QComboBox;
Chris@293 73 m_programCombo->setMaxVisibleItems
Chris@908 74 (int(m_programs.size() < 25 ? m_programs.size() : 20));
Chris@63 75
Chris@908 76 for (int i = 0; in_range_for(m_programs, i); ++i) {
Chris@293 77 m_programCombo->addItem(m_programs[i].c_str());
Chris@293 78 if (m_programs[i] == currentProgram) {
Chris@908 79 m_programCombo->setCurrentIndex(int(i));
Chris@63 80 }
Chris@63 81 }
Chris@63 82
Chris@63 83 m_layout->addWidget(new QLabel(tr("Program")), 0, 0);
Chris@293 84 m_layout->addWidget(m_programCombo, 0, 1, 1, 2);
Chris@63 85
Chris@293 86 connect(m_programCombo, SIGNAL(currentIndexChanged(const QString &)),
Chris@63 87 this, SLOT(programComboChanged(const QString &)));
Chris@63 88
Chris@63 89 offset = 1;
Chris@63 90 }
Chris@63 91
Chris@908 92 for (int i = 0; in_range_for(params, i); ++i) {
Chris@60 93
Chris@207 94 QString identifier = params[i].identifier.c_str();
Chris@60 95 QString name = params[i].name.c_str();
Chris@60 96 QString unit = params[i].unit.c_str();
Chris@60 97
Chris@60 98 float min = params[i].minValue;
Chris@60 99 float max = params[i].maxValue;
Chris@60 100 float deft = params[i].defaultValue;
Chris@207 101 float value = m_plugin->getParameter(params[i].identifier);
Chris@60 102
Chris@342 103 int hint = PortHint::NoHint;
Chris@1581 104 auto rtpi = std::dynamic_pointer_cast<RealTimePluginInstance>
Chris@342 105 (m_plugin);
Chris@342 106 if (rtpi) {
Chris@342 107 hint = rtpi->getParameterDisplayHint(i);
Chris@342 108 }
Chris@342 109
Chris@60 110 float qtz = 0.0;
Chris@60 111 if (params[i].isQuantized) qtz = params[i].quantizeStep;
Chris@60 112
Chris@682 113 // cerr << "PluginParameterBox: hint = " << hint << ", min = " << min << ", max = "
Chris@682 114 // << max << ", qtz = " << qtz << endl;
Chris@342 115
Chris@74 116 std::vector<std::string> valueNames = params[i].valueNames;
Chris@74 117
Chris@60 118 // construct an integer range
Chris@60 119
Chris@60 120 int imin = 0, imax = 100;
Chris@60 121
Chris@342 122 if (!(hint & PortHint::Logarithmic)) {
Chris@342 123 if (qtz > 0.0) {
Chris@908 124 imax = int(lrintf((max - min) / qtz));
Chris@1348 125 if (imax <= imin) {
Chris@1348 126 imax = 100;
Chris@1348 127 qtz = (max - min) / 100.f;
Chris@1348 128 }
Chris@342 129 } else {
Chris@908 130 qtz = (max - min) / 100.f;
Chris@342 131 }
Chris@60 132 }
Chris@60 133
Chris@60 134 //!!! would be nice to ensure the default value corresponds to
Chris@60 135 // an integer!
Chris@60 136
Chris@207 137 QLabel *label = new QLabel(name);
Chris@208 138 if (params[i].description != "") {
Chris@828 139 label->setToolTip(QString("<qt>%1</qt>")
Chris@828 140 .arg(params[i].description.c_str())
Chris@828 141 .replace("\n", "<br>"));
Chris@208 142 }
Chris@63 143 m_layout->addWidget(label, i + offset, 0);
Chris@60 144
Chris@60 145 ParamRec rec;
Chris@60 146 rec.param = params[i];
Chris@1408 147 rec.dial = nullptr;
Chris@1408 148 rec.spin = nullptr;
Chris@1408 149 rec.check = nullptr;
Chris@1408 150 rec.combo = nullptr;
Chris@63 151
Chris@74 152 if (params[i].isQuantized && !valueNames.empty()) {
Chris@74 153
Chris@74 154 QComboBox *combobox = new QComboBox;
Chris@207 155 combobox->setObjectName(identifier);
Chris@74 156 for (unsigned int j = 0; j < valueNames.size(); ++j) {
Chris@74 157 combobox->addItem(valueNames[j].c_str());
Chris@249 158 if ((unsigned int)(lrintf(fabsf((value - min) / qtz))) == j) {
Chris@74 159 combobox->setCurrentIndex(j);
Chris@74 160 }
Chris@74 161 }
Chris@74 162 connect(combobox, SIGNAL(activated(int)),
Chris@74 163 this, SLOT(dialChanged(int)));
Chris@74 164 m_layout->addWidget(combobox, i + offset, 1, 1, 2);
Chris@74 165 rec.combo = combobox;
Chris@74 166
Chris@74 167 } else if (min == 0.0 && max == 1.0 && qtz == 1.0) {
Chris@63 168
Chris@63 169 QCheckBox *checkbox = new QCheckBox;
Chris@207 170 checkbox->setObjectName(identifier);
Chris@293 171 checkbox->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked);
Chris@63 172 connect(checkbox, SIGNAL(stateChanged(int)),
Chris@63 173 this, SLOT(checkBoxChanged(int)));
Chris@63 174 m_layout->addWidget(checkbox, i + offset, 2);
Chris@63 175 rec.check = checkbox;
Chris@63 176
Chris@63 177 } else {
Chris@63 178
Chris@63 179 AudioDial *dial = new AudioDial;
Chris@207 180 dial->setObjectName(name);
Chris@63 181 dial->setMinimum(imin);
Chris@63 182 dial->setMaximum(imax);
Chris@63 183 dial->setPageStep(1);
Chris@63 184 dial->setNotchesVisible((imax - imin) <= 12);
Chris@342 185 //!!! dial->setDefaultValue(lrintf((deft - min) / qtz));
Chris@342 186 // dial->setValue(lrintf((value - min) / qtz));
Chris@63 187 dial->setFixedWidth(32);
Chris@63 188 dial->setFixedHeight(32);
Chris@1348 189 if (max == min || imax == imin) {
Chris@1348 190 SVCERR << "WARNING: for parameter \"" << name
Chris@1348 191 << "\" of plugin \"" << m_plugin->getName()
Chris@1348 192 << "\": invalid range " << min << " -> " << max
Chris@1348 193 << " with quantize step " << qtz << endl;
Chris@342 194 } else {
Chris@1408 195 RangeMapper *rm = nullptr;
Chris@1348 196 if (hint & PortHint::Logarithmic) {
Chris@1348 197 rm = new LogRangeMapper(imin, imax, min, max, unit);
Chris@1348 198 } else {
Chris@1348 199 rm = new LinearRangeMapper(imin, imax, min, max, unit);
Chris@1348 200 }
Chris@1348 201 dial->setRangeMapper(rm);
Chris@1348 202 dial->setDefaultValue(rm->getPositionForValue(deft));
Chris@1348 203 dial->setValue(rm->getPositionForValue(value));
Chris@342 204 }
Chris@168 205 dial->setShowToolTip(true);
Chris@63 206 connect(dial, SIGNAL(valueChanged(int)),
Chris@63 207 this, SLOT(dialChanged(int)));
Chris@63 208 m_layout->addWidget(dial, i + offset, 1);
Chris@63 209
Chris@63 210 QDoubleSpinBox *spinbox = new QDoubleSpinBox;
Chris@207 211 spinbox->setObjectName(identifier);
Chris@63 212 spinbox->setMinimum(min);
Chris@63 213 spinbox->setMaximum(max);
Chris@63 214 spinbox->setSuffix(QString(" %1").arg(unit));
Chris@342 215 if (qtz != 0) spinbox->setSingleStep(qtz);
Chris@63 216 spinbox->setValue(value);
Chris@103 217 spinbox->setDecimals(4);
Chris@63 218 connect(spinbox, SIGNAL(valueChanged(double)),
Chris@63 219 this, SLOT(spinBoxChanged(double)));
Chris@63 220 m_layout->addWidget(spinbox, i + offset, 2);
Chris@63 221 rec.dial = dial;
Chris@63 222 rec.spin = spinbox;
Chris@63 223 }
Chris@63 224
Chris@207 225 m_params[identifier] = rec;
Chris@207 226 m_nameMap[name] = identifier;
Chris@60 227 }
Chris@60 228 }
Chris@60 229
Chris@60 230 void
Chris@60 231 PluginParameterBox::dialChanged(int ival)
Chris@60 232 {
Chris@60 233 QObject *obj = sender();
Chris@207 234 QString identifier = obj->objectName();
Chris@60 235
Chris@207 236 if (m_params.find(identifier) == m_params.end() &&
Chris@207 237 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 238 identifier = m_nameMap[identifier];
Chris@167 239 }
Chris@167 240
Chris@207 241 if (m_params.find(identifier) == m_params.end()) {
Chris@682 242 cerr << "WARNING: PluginParameterBox::dialChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@60 243 return;
Chris@60 244 }
Chris@60 245
Chris@207 246 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@60 247
Chris@60 248 float min = params.minValue;
Chris@60 249 float max = params.maxValue;
Chris@60 250
Chris@168 251 float newValue;
Chris@168 252
Chris@60 253 float qtz = 0.0;
Chris@60 254 if (params.isQuantized) qtz = params.quantizeStep;
Chris@168 255
Chris@168 256 AudioDial *ad = dynamic_cast<AudioDial *>(obj);
Chris@60 257
Chris@168 258 if (ad && ad->rangeMapper()) {
Chris@168 259
Chris@908 260 newValue = float(ad->mappedValue());
Chris@168 261 if (newValue < min) newValue = min;
Chris@168 262 if (newValue > max) newValue = max;
Chris@168 263 if (qtz != 0.0) {
Chris@908 264 ival = int(lrintf((newValue - min) / qtz));
Chris@908 265 newValue = min + float(ival) * qtz;
Chris@168 266 }
Chris@168 267
Chris@168 268 } else {
Chris@908 269 if (qtz == 0.f) {
Chris@908 270 qtz = (max - min) / 100.f;
Chris@168 271 }
Chris@908 272 newValue = min + float(ival) * qtz;
Chris@60 273 }
Chris@60 274
Chris@587 275 // SVDEBUG << "PluginParameterBox::dialChanged: newValue = " << newValue << endl;
Chris@342 276
Chris@207 277 QDoubleSpinBox *spin = m_params[identifier].spin;
Chris@63 278 if (spin) {
Chris@63 279 spin->blockSignals(true);
Chris@63 280 spin->setValue(newValue);
Chris@63 281 spin->blockSignals(false);
Chris@63 282 }
Chris@60 283
Chris@587 284 // SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << newValue << endl;
Chris@530 285
Chris@207 286 m_plugin->setParameter(identifier.toStdString(), newValue);
Chris@64 287
Chris@293 288 updateProgramCombo();
Chris@293 289
Chris@71 290 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@60 291 }
Chris@60 292
Chris@60 293 void
Chris@63 294 PluginParameterBox::checkBoxChanged(int state)
Chris@63 295 {
Chris@63 296 QObject *obj = sender();
Chris@207 297 QString identifier = obj->objectName();
Chris@63 298
Chris@207 299 if (m_params.find(identifier) == m_params.end() &&
Chris@207 300 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 301 identifier = m_nameMap[identifier];
Chris@167 302 }
Chris@167 303
Chris@207 304 if (m_params.find(identifier) == m_params.end()) {
Chris@682 305 cerr << "WARNING: PluginParameterBox::checkBoxChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@63 306 return;
Chris@63 307 }
Chris@63 308
Chris@207 309 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@63 310
Chris@207 311 if (state) m_plugin->setParameter(identifier.toStdString(), 1.0);
Chris@207 312 else m_plugin->setParameter(identifier.toStdString(), 0.0);
Chris@64 313
Chris@293 314 updateProgramCombo();
Chris@293 315
Chris@71 316 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@63 317 }
Chris@63 318
Chris@63 319 void
Chris@60 320 PluginParameterBox::spinBoxChanged(double value)
Chris@60 321 {
Chris@60 322 QObject *obj = sender();
Chris@207 323 QString identifier = obj->objectName();
Chris@60 324
Chris@207 325 if (m_params.find(identifier) == m_params.end() &&
Chris@207 326 m_nameMap.find(identifier) != m_nameMap.end()) {
Chris@207 327 identifier = m_nameMap[identifier];
Chris@167 328 }
Chris@167 329
Chris@207 330 if (m_params.find(identifier) == m_params.end()) {
Chris@682 331 cerr << "WARNING: PluginParameterBox::spinBoxChanged: Unknown parameter \"" << identifier << "\"" << endl;
Chris@60 332 return;
Chris@60 333 }
Chris@60 334
Chris@207 335 Vamp::PluginBase::ParameterDescriptor params = m_params[identifier].param;
Chris@60 336
Chris@60 337 float min = params.minValue;
Chris@60 338 float max = params.maxValue;
Chris@60 339
Chris@60 340 float qtz = 0.0;
Chris@60 341 if (params.isQuantized) qtz = params.quantizeStep;
Chris@60 342
Chris@60 343 if (qtz > 0.0) {
Chris@908 344 int step = int(lrintf(float(value - min) / qtz));
Chris@908 345 value = min + float(step) * qtz;
Chris@60 346 }
Chris@60 347
Chris@807 348 // int imax = 100;
Chris@60 349
Chris@60 350 if (qtz > 0.0) {
Chris@807 351 // imax = lrintf((max - min) / qtz);
Chris@60 352 } else {
Chris@908 353 qtz = (max - min) / 100.f;
Chris@60 354 }
Chris@60 355
Chris@908 356 int ival = int(lrintf(float(value - min) / qtz));
Chris@60 357
Chris@207 358 AudioDial *dial = m_params[identifier].dial;
Chris@63 359 if (dial) {
Chris@63 360 dial->blockSignals(true);
Chris@342 361 if (dial->rangeMapper()) {
Chris@342 362 dial->setMappedValue(value);
Chris@342 363 } else {
Chris@342 364 dial->setValue(ival);
Chris@342 365 }
Chris@63 366 dial->blockSignals(false);
Chris@63 367 }
Chris@60 368
Chris@587 369 SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << value << endl;
Chris@530 370
Chris@908 371 m_plugin->setParameter(identifier.toStdString(), float(value));
Chris@64 372
Chris@293 373 updateProgramCombo();
Chris@293 374
Chris@71 375 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@60 376 }
Chris@60 377
Chris@63 378 void
Chris@63 379 PluginParameterBox::programComboChanged(const QString &newProgram)
Chris@63 380 {
Chris@63 381 m_plugin->selectProgram(newProgram.toStdString());
Chris@60 382
Chris@63 383 for (std::map<QString, ParamRec>::iterator i = m_params.begin();
Chris@63 384 i != m_params.end(); ++i) {
Chris@63 385
Chris@71 386 Vamp::PluginBase::ParameterDescriptor &param = i->second.param;
Chris@207 387 float value = m_plugin->getParameter(param.identifier);
Chris@63 388
Chris@63 389 if (i->second.spin) {
Chris@63 390 i->second.spin->blockSignals(true);
Chris@63 391 i->second.spin->setValue(value);
Chris@63 392 i->second.spin->blockSignals(false);
Chris@63 393 }
Chris@63 394
Chris@63 395 if (i->second.dial) {
Chris@63 396
Chris@63 397 float min = param.minValue;
Chris@63 398 float max = param.maxValue;
Chris@63 399
Chris@63 400 float qtz = 0.0;
Chris@63 401 if (param.isQuantized) qtz = param.quantizeStep;
Chris@63 402
Chris@63 403 if (qtz == 0.0) {
Chris@908 404 qtz = (max - min) / 100.f;
Chris@63 405 }
Chris@63 406
Chris@63 407 i->second.dial->blockSignals(true);
Chris@908 408 i->second.dial->setValue(int(lrintf(float(value - min) / qtz)));
Chris@63 409 i->second.dial->blockSignals(false);
Chris@63 410 }
Chris@74 411
Chris@74 412 if (i->second.combo) {
Chris@74 413 i->second.combo->blockSignals(true);
Chris@908 414 i->second.combo->setCurrentIndex(int(lrintf(value)));
Chris@74 415 i->second.combo->blockSignals(false);
Chris@74 416 }
Chris@293 417
Chris@293 418 if (i->second.check) {
Chris@293 419 i->second.check->blockSignals(true);
Chris@293 420 i->second.check->setCheckState(value < 0.5 ? Qt::Unchecked : Qt::Checked);
Chris@293 421 i->second.check->blockSignals(false);
Chris@293 422 }
Chris@63 423 }
Chris@64 424
Chris@71 425 emit pluginConfigurationChanged(PluginXml(m_plugin).toXmlString());
Chris@63 426 }
Chris@63 427
Chris@293 428 void
Chris@293 429 PluginParameterBox::updateProgramCombo()
Chris@293 430 {
Chris@293 431 if (!m_programCombo || m_programs.empty()) return;
Chris@293 432
Chris@293 433 std::string currentProgram = m_plugin->getCurrentProgram();
Chris@293 434
Chris@908 435 for (int i = 0; in_range_for(m_programs, i); ++i) {
Chris@293 436 if (m_programs[i] == currentProgram) {
Chris@293 437 m_programCombo->setCurrentIndex(i);
Chris@293 438 }
Chris@293 439 }
Chris@293 440 }
Chris@293 441
Chris@293 442