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