1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Rosegarden 5 A MIDI and audio sequencer and musical notation editor. 6 Copyright 2000-2021 the Rosegarden development team. 7 8 Other copyrights also apply to some parts of this work. Please 9 see the AUTHORS file and individual file headers for details. 10 11 This program is free software; you can redistribute it and/or 12 modify it under the terms of the GNU General Public License as 13 published by the Free Software Foundation; either version 2 of the 14 License, or (at your option) any later version. See the file 15 COPYING included with this distribution for more information. 16 */ 17 18 #define RG_MODULE_STRING "[QuantizeParameters]" 19 20 #include "QuantizeParameters.h" 21 22 #include "misc/Debug.h" 23 #include "misc/Strings.h" // qStrToBool() etc... 24 #include "misc/ConfigGroups.h" // *ConfigGroup names 25 #include "base/Quantizer.h" 26 #include "base/BasicQuantizer.h" 27 #include "base/LegatoQuantizer.h" 28 #include "base/NotationQuantizer.h" 29 #include "gui/editors/notation/NotationStrings.h" 30 #include "gui/editors/notation/NotePixmapFactory.h" 31 #include "gui/widgets/LineEdit.h" 32 33 #include <QCheckBox> 34 #include <QComboBox> 35 #include <QLabel> 36 #include <QPixmap> 37 #include <QString> 38 #include <QVBoxLayout> 39 40 41 namespace Rosegarden 42 { 43 44 45 QuantizeParameters::QuantizeParameters(QWidget *parent, 46 QuantizerType defaultQuantizer, 47 bool showNotationOption) : 48 QFrame(parent), 49 m_standardQuantizations(BasicQuantizer::getStandardQuantizations()) 50 { 51 const bool inNotation = (defaultQuantizer == Notation); 52 53 m_settings.beginGroup( 54 inNotation ? NotationQuantizeConfigGroup : 55 GridQuantizeConfigGroup); 56 57 m_mainLayout = new QVBoxLayout; 58 m_mainLayout->setSpacing(5); 59 setContentsMargins(5, 5, 5, 5); 60 setLayout(m_mainLayout); 61 62 // Quantizer box 63 QGroupBox *quantizerBox = new QGroupBox(tr("Quantizer")); 64 QGridLayout *qbLayout = new QGridLayout; 65 quantizerBox->setLayout(qbLayout); 66 m_mainLayout->addWidget(quantizerBox); 67 68 qbLayout->addWidget(new QLabel(tr("Quantizer type:"), quantizerBox), 0, 0); 69 m_quantizerType = new QComboBox(quantizerBox); 70 m_quantizerType->addItem(tr("Grid quantizer")); 71 m_quantizerType->addItem(tr("Legato quantizer")); 72 m_quantizerType->addItem(tr("Heuristic notation quantizer")); 73 QuantizerType quantizerType = static_cast<QuantizerType>( 74 m_settings.value("quantizetype", defaultQuantizer).toInt()); 75 m_quantizerType->setCurrentIndex(quantizerType); 76 connect(m_quantizerType, SIGNAL(activated(int)), SLOT(slotTypeChanged(int))); 77 qbLayout->addWidget(m_quantizerType, 0, 1); 78 79 m_quantizeNotation = new QCheckBox( 80 tr("Quantize for notation only (leave performance unchanged)"), 81 quantizerBox); 82 m_quantizeNotation->setChecked(qStrToBool(m_settings.value( 83 "quantizenotationonly", inNotation))); 84 qbLayout->addWidget(m_quantizeNotation, 1, 0, 1, 2); 85 86 // ??? Always false. Only caller always sets this to false. 87 if (!showNotationOption) 88 m_quantizeNotation->hide(); 89 90 91 // Notation parameters box 92 m_notationBox = new QGroupBox( tr("Notation parameters")); 93 QGridLayout *nbLayout = new QGridLayout; 94 nbLayout->setSpacing(3); 95 m_notationBox->setLayout(nbLayout); 96 m_mainLayout->addWidget(m_notationBox); 97 98 // Base grid unit 99 nbLayout->addWidget(new QLabel(tr("Base grid unit:"), m_notationBox), 1, 0); 100 m_notationBaseGridUnit = new QComboBox(m_notationBox); 101 initBaseGridUnit("notationBaseGridUnit", m_notationBaseGridUnit); 102 nbLayout->addWidget(m_notationBaseGridUnit, 1, 1); 103 104 // Complexity 105 nbLayout->addWidget(new QLabel(tr("Complexity:"), m_notationBox), 0, 0); 106 m_complexity = new QComboBox(m_notationBox); 107 m_complexity->addItem(tr("Very high")); 108 m_complexity->addItem(tr("High")); 109 m_complexity->addItem(tr("Normal")); 110 m_complexity->addItem(tr("Low")); 111 m_complexity->addItem(tr("Very low")); 112 m_complexity->setCurrentIndex(m_settings.value( 113 "quantizesimplicity", 13).toInt() - 11); 114 nbLayout->addWidget(m_complexity, 0, 1); 115 116 // Tuplet level 117 nbLayout->addWidget(new QLabel(tr("Tuplet level:"), m_notationBox), 2, 0); 118 m_tupletLevel = new QComboBox(m_notationBox); 119 m_tupletLevel->addItem(tr("None")); 120 m_tupletLevel->addItem(tr("2-in-the-time-of-3")); 121 m_tupletLevel->addItem(tr("Triplet")); 122 m_tupletLevel->addItem(tr("Any")); 123 m_tupletLevel->setCurrentIndex(m_settings.value( 124 "quantizemaxtuplet", 3).toInt() - 1); 125 nbLayout->addWidget(m_tupletLevel, 2, 1); 126 127 // Permit counterpoint 128 m_permitCounterpoint = new QCheckBox(tr("Permit counterpoint"), m_notationBox); 129 m_permitCounterpoint->setChecked(qStrToBool(m_settings.value( 130 "quantizecounterpoint", "false" ))); 131 nbLayout->addWidget(m_permitCounterpoint, 3, 0, 1, 1); 132 133 134 // Grid parameters box 135 m_gridBox = new QGroupBox( tr("Grid parameters")); 136 QGridLayout *gbLayout = new QGridLayout; 137 gbLayout->setSpacing(3); 138 m_gridBox->setLayout(gbLayout); 139 m_mainLayout->addWidget(m_gridBox); 140 141 // Base grid unit 142 gbLayout->addWidget(new QLabel(tr("Base grid unit:"), m_gridBox), 0, 0); 143 m_gridBaseGridUnit = new QComboBox(m_gridBox); 144 initBaseGridUnit("gridBaseGridUnit", m_gridBaseGridUnit); 145 connect(m_gridBaseGridUnit, static_cast<void(QComboBox::*)(int)>( 146 &QComboBox::currentIndexChanged), 147 this, &QuantizeParameters::gridUnitChanged); 148 gbLayout->addWidget(m_gridBaseGridUnit, 0, 1); 149 150 // Arbitrary grid unit 151 m_arbitraryGridUnitLabel = new QLabel(tr("Arbitrary grid unit:"), m_gridBox);; 152 gbLayout->addWidget(m_arbitraryGridUnitLabel, 1, 0); 153 m_arbitraryGridUnit = new LineEdit(m_gridBox); 154 int arbitraryGridUnit = m_settings.value("arbitraryGridUnit", 1).toInt(); 155 m_arbitraryGridUnit->setText(QString::number(arbitraryGridUnit)); 156 gbLayout->addWidget(m_arbitraryGridUnit, 1, 1); 157 // Enable/Disable Arbitrary grid unit controls as appropriate. 158 gridUnitChanged(m_gridBaseGridUnit->currentIndex()); 159 160 // Swing 161 m_swingLabel = new QLabel(tr("Swing:"), m_gridBox); 162 gbLayout->addWidget(m_swingLabel, 2, 0); 163 m_swing = new QComboBox(m_gridBox); 164 165 int swing = m_settings.value("quantizeswing", 0).toInt(); 166 167 for (int i = -100; i <= 200; i += 10) { 168 m_swing->addItem(i == 0 ? tr("None") : QString("%1%").arg(i)); 169 170 // Found it? Select it. 171 if (i == swing) 172 m_swing->setCurrentIndex(m_swing->count() - 1); 173 } 174 175 gbLayout->addWidget(m_swing, 2, 1); 176 177 // Iterative amount 178 m_iterativeAmountLabel = new QLabel(tr("Iterative amount:"), m_gridBox); 179 gbLayout->addWidget(m_iterativeAmountLabel, 3, 0); 180 m_iterativeAmount = new QComboBox(m_gridBox); 181 182 int iterativeAmount = m_settings.value("quantizeiterate", 100).toInt(); 183 184 for (int i = 10; i <= 100; i += 10) { 185 m_iterativeAmount->addItem( 186 i == 100 ? tr("Full quantize") : QString("%1%").arg(i)); 187 188 // Found it? Select it. 189 if (i == iterativeAmount) 190 m_iterativeAmount->setCurrentIndex(m_iterativeAmount->count() - 1); 191 } 192 193 gbLayout->addWidget(m_iterativeAmount, 3, 1); 194 195 // Quantize durations 196 m_quantizeDurations = new QCheckBox( 197 tr("Quantize durations as well as start times"), m_gridBox); 198 m_quantizeDurations->setChecked(qStrToBool(m_settings.value( 199 "quantizedurations", "false"))); 200 gbLayout->addWidget(m_quantizeDurations, 4, 0, 1, 1); 201 202 203 // After quantization box 204 QGroupBox *afterQuantizationBox = 205 new QGroupBox(tr("After quantization"), this); 206 QGridLayout *pbLayout = new QGridLayout; 207 pbLayout->setSpacing(3); 208 afterQuantizationBox->setLayout(pbLayout); 209 m_mainLayout->addWidget(afterQuantizationBox); 210 211 // Re-beam 212 m_rebeam = new QCheckBox(tr("Re-beam"), afterQuantizationBox); 213 m_rebeam->setChecked(qStrToBool(m_settings.value( 214 "quantizerebeam", "true"))); 215 216 // Add articulations 217 m_addArticulations = new QCheckBox( 218 tr("Add articulations (staccato, tenuto, slurs)"), 219 afterQuantizationBox); 220 m_addArticulations->setChecked(qStrToBool(m_settings.value( 221 "quantizearticulate", "true"))); 222 223 // Tie notes at barlines 224 m_tieNotesAtBarlines = new QCheckBox( 225 tr("Tie notes at barlines etc"), afterQuantizationBox); 226 m_tieNotesAtBarlines->setChecked(qStrToBool(m_settings.value( 227 "quantizemakeviable", "false"))); 228 229 // Split-and-tie overlapping chords 230 m_splitAndTie = new QCheckBox( 231 tr("Split-and-tie overlapping chords"), afterQuantizationBox); 232 m_splitAndTie->setChecked(qStrToBool(m_settings.value( 233 "quantizedecounterpoint", "false"))); 234 235 pbLayout->addWidget(m_rebeam, 0, 0); 236 pbLayout->addWidget(m_addArticulations, 1, 0); 237 pbLayout->addWidget(m_tieNotesAtBarlines, 2, 0); 238 pbLayout->addWidget(m_splitAndTie, 3, 0); 239 240 // Show/Hide widgets as appropriate for the quantizer type. 241 slotTypeChanged(quantizerType); 242 243 } 244 245 void 246 QuantizeParameters::initBaseGridUnit(QString settingsKey, QComboBox *comboBox) 247 { 248 QPixmap noMap = NotePixmapFactory::makeToolbarPixmap("menu-no-note"); 249 250 timeT baseGridUnit = m_settings.value( 251 settingsKey, 252 static_cast<int>( 253 Note(Note::Demisemiquaver).getDuration())).toInt(); 254 255 bool found = false; 256 257 // For each standard quantization 258 for (unsigned int i = 0; i < m_standardQuantizations.size(); ++i) { 259 260 timeT time = m_standardQuantizations[i]; 261 timeT error = 0; 262 263 QPixmap pmap = NotePixmapFactory::makeNoteMenuPixmap(time, error); 264 QString label; 265 if (error == 0) 266 label = NotationStrings::makeNoteMenuLabel(time, false, error); 267 268 if (error == 0) { 269 comboBox->addItem(pmap, label); 270 } else { 271 // ??? We never end up in here since we are iterating through 272 // the standard quantizations. We can probably remove this. 273 comboBox->addItem(noMap, QString("%1").arg(time)); 274 } 275 276 // Found it? Select it. 277 if (m_standardQuantizations[i] == baseGridUnit) { 278 comboBox->setCurrentIndex(comboBox->count() - 1); 279 found = true; 280 } 281 } 282 283 comboBox->addItem(noMap, tr("Arbitrary grid unit")); 284 // Save the index for future reference. 285 m_arbitraryGridUnitIndex = comboBox->count() - 1; 286 287 // Nothing was found up to this point, go with arbitrary. 288 if (!found) 289 comboBox->setCurrentIndex(m_arbitraryGridUnitIndex); 290 } 291 292 void 293 QuantizeParameters::saveSettings() 294 { 295 m_settings.setValue("quantizetype", m_quantizerType->currentIndex()); 296 m_settings.setValue("gridBaseGridUnit", static_cast<unsigned long long>( 297 m_standardQuantizations[m_gridBaseGridUnit->currentIndex()])); 298 m_settings.setValue("arbitraryGridUnit", m_arbitraryGridUnit->text()); 299 m_settings.setValue("notationBaseGridUnit", static_cast<unsigned long long>( 300 m_standardQuantizations[m_notationBaseGridUnit->currentIndex()])); 301 m_settings.setValue("quantizeswing", m_swing->currentIndex() * 10 - 100); 302 m_settings.setValue("quantizeiterate", 303 m_iterativeAmount->currentIndex() * 10 + 10); 304 m_settings.setValue("quantizenotationonly", 305 m_quantizeNotation->isChecked()); 306 m_settings.setValue("quantizedurations", 307 m_quantizeDurations->isChecked()); 308 m_settings.setValue("quantizesimplicity", 309 m_complexity->currentIndex() + 11); 310 m_settings.setValue("quantizemaxtuplet", 311 m_tupletLevel->currentIndex() + 1); 312 m_settings.setValue("quantizecounterpoint", 313 m_permitCounterpoint->isChecked()); 314 m_settings.setValue("quantizerebeam", m_rebeam->isChecked()); 315 m_settings.setValue("quantizearticulate", m_addArticulations->isChecked()); 316 m_settings.setValue("quantizemakeviable", m_tieNotesAtBarlines->isChecked()); 317 m_settings.setValue("quantizedecounterpoint", m_splitAndTie->isChecked()); 318 } 319 320 timeT 321 QuantizeParameters::getGridUnit() const 322 { 323 timeT unit = 1; 324 325 // Arbitrary grid unit selected? 326 if (m_gridBaseGridUnit->currentIndex() == m_arbitraryGridUnitIndex) { 327 // Use the arbitrary grid unit field. 328 unit = m_arbitraryGridUnit->text().toInt(); 329 if (unit < 1) 330 unit = 1; 331 } else { 332 unit = m_standardQuantizations[ 333 m_gridBaseGridUnit->currentIndex()]; 334 } 335 336 return unit; 337 } 338 339 Quantizer * 340 QuantizeParameters::getQuantizer() 341 { 342 // ??? Similar to EventQuantizeCommand::makeQuantizer(). 343 // Can we pull out a common routine? Maybe a factory function 344 // in Quantizer. It would probably require a ton of parameters, 345 // though. 346 347 QuantizerType type = 348 static_cast<QuantizerType>(m_quantizerType->currentIndex()); 349 350 Quantizer *quantizer = nullptr; 351 352 switch (type) { 353 case Grid: 354 { 355 const timeT unit = getGridUnit(); 356 const int swingPercent = m_swing->currentIndex() * 10 - 100; 357 const int iteratePercent = 358 m_iterativeAmount->currentIndex() * 10 + 10; 359 360 if (m_quantizeNotation->isChecked()) { 361 quantizer = new BasicQuantizer( 362 Quantizer::RawEventData, // source 363 Quantizer::NotationPrefix, // target 364 unit, 365 m_quantizeDurations->isChecked(), // doDurations 366 swingPercent, 367 iteratePercent); 368 } else { // Quantize the events. 369 quantizer = new BasicQuantizer( 370 Quantizer::RawEventData, // source 371 Quantizer::RawEventData, // target 372 unit, 373 m_quantizeDurations->isChecked(), // doDurations 374 swingPercent, 375 iteratePercent); 376 } 377 378 break; 379 } 380 case Legato: 381 { 382 const timeT unit = getGridUnit(); 383 384 if (m_quantizeNotation->isChecked()) { 385 quantizer = new LegatoQuantizer( 386 Quantizer::RawEventData, // source 387 Quantizer::NotationPrefix, // target 388 unit); 389 } else { // Quantize the events. 390 quantizer = new LegatoQuantizer( 391 Quantizer::RawEventData, // source 392 Quantizer::RawEventData, // target 393 unit); 394 } 395 396 break; 397 } 398 case Notation: 399 { 400 401 NotationQuantizer *notationQuantizer = nullptr; 402 403 if (m_quantizeNotation->isChecked()) { 404 notationQuantizer = new NotationQuantizer(); 405 } else { 406 notationQuantizer = new NotationQuantizer( 407 Quantizer::RawEventData, // source 408 Quantizer::RawEventData); // target 409 } 410 411 notationQuantizer->setUnit(m_standardQuantizations[ 412 m_notationBaseGridUnit->currentIndex()]); 413 notationQuantizer->setSimplicityFactor( 414 m_complexity->currentIndex() + 11); 415 notationQuantizer->setMaxTuplet(m_tupletLevel->currentIndex() + 1); 416 notationQuantizer->setContrapuntal( 417 m_permitCounterpoint->isChecked()); 418 notationQuantizer->setArticulate(m_addArticulations->isChecked()); 419 420 // Cast up to baseclass type. 421 quantizer = static_cast<Quantizer *>(notationQuantizer); 422 423 break; 424 } 425 } 426 427 return quantizer; 428 } 429 430 void 431 QuantizeParameters::slotTypeChanged(int index) 432 { 433 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum)); 434 parentWidget()->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); 435 436 QuantizerType quantizerType = static_cast<QuantizerType>(index); 437 438 // Could do it this way too. Not sure which is easiest on the eyes. 439 //m_gridBox->setVisible(quantizerType != Notation); 440 //m_swingLabel->setVisible(quantizerType == Grid); 441 //m_swing->setVisible(quantizerType == Grid); 442 // ... 443 444 switch (quantizerType) { 445 case Grid: 446 m_gridBox->show(); 447 m_swingLabel->show(); 448 m_swing->show(); 449 m_iterativeAmountLabel->show(); 450 m_iterativeAmount->show(); 451 m_quantizeDurations->show(); 452 m_notationBox->hide(); 453 break; 454 case Legato: 455 m_gridBox->show(); 456 m_swingLabel->hide(); 457 m_swing->hide(); 458 m_iterativeAmountLabel->hide(); 459 m_iterativeAmount->hide(); 460 m_quantizeDurations->hide(); 461 m_notationBox->hide(); 462 break; 463 case Notation: 464 m_gridBox->hide(); 465 m_notationBox->show(); 466 break; 467 } 468 469 adjustSize(); 470 parentWidget()->adjustSize(); 471 } 472 473 void 474 QuantizeParameters::gridUnitChanged(int index) 475 { 476 // Enable/Disable Arbitrary grid unit widgets 477 bool arbitraryEnabled = (index == m_arbitraryGridUnitIndex); 478 m_arbitraryGridUnitLabel->setEnabled(arbitraryEnabled); 479 m_arbitraryGridUnit->setEnabled(arbitraryEnabled); 480 m_arbitraryGridUnit->setText(QString::number(getGridUnit())); 481 } 482 483 484 } 485