1 /***************************************************************************
2 ** **
3 ** Polyphone, a soundfont editor **
4 ** Copyright (C) 2013-2020 Davy Triponney **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Davy Triponney **
21 ** Website/Contact: https://www.polyphone-soundfonts.com **
22 ** Date: 01.01.2013 **
23 ***************************************************************************/
24
25 #include "toolmixturecreation_gui.h"
26 #include "ui_toolmixturecreation_gui.h"
27 #include "contextmanager.h"
28 #include <QMessageBox>
29
ToolMixtureCreation_gui(QWidget * parent)30 ToolMixtureCreation_gui::ToolMixtureCreation_gui(QWidget *parent) :
31 AbstractToolGui(parent),
32 ui(new Ui::ToolMixtureCreation_gui)
33 {
34 ui->setupUi(this);
35
36 // Icons
37 ui->pushAddDiv->setIcon(ContextManager::theme()->getColoredSvg(":/icons/document-new.svg", QSize(16, 16), ThemeManager::BUTTON_TEXT));
38 ui->pushRemoveDiv->setIcon(ContextManager::theme()->getColoredSvg(":/icons/minus.svg", QSize(16, 16), ThemeManager::BUTTON_TEXT));
39 ui->pushAddRank->setIcon(ContextManager::theme()->getColoredSvg(":/icons/document-new.svg", QSize(16, 16), ThemeManager::BUTTON_TEXT));
40 ui->pushRemoveRank->setIcon(ContextManager::theme()->getColoredSvg(":/icons/minus.svg", QSize(16, 16), ThemeManager::BUTTON_TEXT));
41 }
42
~ToolMixtureCreation_gui()43 ToolMixtureCreation_gui::~ToolMixtureCreation_gui()
44 {
45 delete ui;
46 }
47
updateInterface(AbstractToolParameters * parameters,IdList ids)48 void ToolMixtureCreation_gui::updateInterface(AbstractToolParameters * parameters, IdList ids)
49 {
50 Q_UNUSED(ids)
51 ToolMixtureCreation_parameters * params = dynamic_cast<ToolMixtureCreation_parameters *>(parameters);
52
53 // Recall values
54 _divisions = params->getDivisions();
55 ui->lineNom->setText(params->getInstrumentName());
56 ui->comboFreq->setCurrentIndex(params->getDensityType());
57 ui->checkBouclage->setChecked(params->getLoopSample());
58 ui->checkStereo->setChecked(params->getStereoSample());
59
60 // Select octaves
61 this->on_comboType1_currentIndexChanged(0);
62
63 // Display divisions
64 this->dispDiv();
65 }
66
saveParameters(AbstractToolParameters * parameters)67 void ToolMixtureCreation_gui::saveParameters(AbstractToolParameters * parameters)
68 {
69 // Save values
70 ToolMixtureCreation_parameters * params = dynamic_cast<ToolMixtureCreation_parameters *>(parameters);
71 params->setDivisions(_divisions);
72 params->setInstrumentName(ui->lineNom->text());
73 params->setDensityType(ui->comboFreq->currentIndex());
74 params->setLoopSample(ui->checkBouclage->isChecked());
75 params->setStereoSample(ui->checkStereo->isChecked());
76 }
77
78 // Display
79
dispDiv()80 void ToolMixtureCreation_gui::dispDiv()
81 {
82 // Clear the list
83 ui->listDivisions->blockSignals(true);
84 ui->listDivisions->clear();
85 ui->listRangs->clear();
86
87 // Fill the list
88 int valMin, valMax;
89 foreach (DivisionInfo di, _divisions)
90 {
91 // Add an element
92 valMin = qMin(di.getMinKey(), di.getMaxKey());
93 valMax = qMax(di.getMinKey(), di.getMaxKey());
94 ui->listDivisions->addItem(ContextManager::keyName()->getKeyName(valMin) + " - " +
95 ContextManager::keyName()->getKeyName(valMax));
96 }
97 ui->listDivisions->blockSignals(false);
98 }
99
dispRang(int numDiv)100 void ToolMixtureCreation_gui::dispRang(int numDiv)
101 {
102 // Effacement liste rangs
103 ui->listRangs->blockSignals(true);
104 ui->listRangs->clear();
105 if (ui->listDivisions->selectedItems().length() == 1)
106 {
107 // Numéro de la division
108 if (numDiv == -1)
109 numDiv = this->getNumDiv();
110 if (numDiv < 0) return;
111 DivisionInfo di = _divisions.at(numDiv);
112 foreach (RankInfo ri, di.getRanks())
113 ui->listRangs->addItem(getText(ri.getOvertoneType(), ri.getOctave()));
114 }
115 ui->listRangs->blockSignals(false);
116 }
117
getText(int overtoneType,int octave)118 QString ToolMixtureCreation_gui::getText(int overtoneType, int octave)
119 {
120 QString text;
121 switch (overtoneType)
122 {
123 case 0: // octaves
124 switch (octave)
125 {
126 case 0: text = "64'" ; break;
127 case 1: text = "32'" ; break;
128 case 2: text = "16'" ; break;
129 case 3: text = "8'" ; break;
130 case 4: text = "4'" ; break;
131 case 5: text = "2'" ; break;
132 case 6: text = "1'" ; break;
133 case 7: text = "1/2'" ; break;
134 case 8: text = "1/4'" ; break;
135 case 9: text = "1/8'" ; break;
136 case 10: text = "1/16'"; break;
137 }
138 break;
139 case 1: // quintes
140 switch (octave)
141 {
142 case 0: text = "21' 1/3"; break;
143 case 1: text = "10' 2/3"; break;
144 case 2: text = "5' 1/3" ; break;
145 case 3: text = "2' 2/3" ; break;
146 case 4: text = "1' 1/3" ; break;
147 case 5: text = "2/3'" ; break;
148 case 6: text = "1/3'" ; break;
149 case 7: text = "1/6'" ; break;
150 case 8: text = "1/12'" ; break;
151 }
152 break;
153 case 2: // tierces
154 switch (octave)
155 {
156 case 0: text = "12' 4/5"; break;
157 case 1: text = "6' 2/5" ; break;
158 case 2: text = "3' 1/5" ; break;
159 case 3: text = "1' 3/5" ; break;
160 case 4: text = "4/5'" ; break;
161 case 5: text = "2/5'" ; break;
162 case 6: text = "1/5'" ; break;
163 case 7: text = "1/10'" ; break;
164 }
165 break;
166 case 3: // septièmes
167 switch (octave)
168 {
169 case 0: text = "9' 1/7"; break;
170 case 1: text = "4' 4/7"; break;
171 case 2: text = "2' 2/7"; break;
172 case 3: text = "1' 1/7"; break;
173 case 4: text = "4/7'" ; break;
174 case 5: text = "2/7'" ; break;
175 case 6: text = "1/7'" ; break;
176 case 7: text = "1/14'" ; break;
177 }
178 break;
179 case 4: // neuvièmes
180 switch (octave)
181 {
182 case 0: text = "7' 1/9"; break;
183 case 1: text = "3' 5/9"; break;
184 case 2: text = "1' 7/9"; break;
185 case 3: text = "8/9'" ; break;
186 case 4: text = "4/9'" ; break;
187 case 5: text = "2/9'" ; break;
188 case 6: text = "1/9'" ; break;
189 }
190 break;
191 case 5: // onzièmes
192 switch (octave)
193 {
194 case 0: text = "5' 9/11" ; break;
195 case 1: text = "2' 10/11"; break;
196 case 2: text = "1' 5/11" ; break;
197 case 3: text = "8/11'" ; break;
198 case 4: text = "4/11'" ; break;
199 case 5: text = "2/11'" ; break;
200 case 6: text = "1/11'" ; break;
201 }
202 break;
203 case 6: // treizièmes
204 switch (octave)
205 {
206 case 0: text = "4' 12/13"; break;
207 case 1: text = "2' 6/13"; break;
208 case 2: text = "1' 3/13"; break;
209 case 3: text = "8/13'" ; break;
210 case 4: text = "4/13'" ; break;
211 case 5: text = "2/13'" ; break;
212 case 6: text = "1/13'" ; break;
213 }
214 break;
215 case 7: // quinzièmes
216 switch (octave)
217 {
218 case 0: text = "4' 4/15"; break;
219 case 1: text = "2' 2/15"; break;
220 case 2: text = "1' 1/15"; break;
221 case 3: text = "8/15'" ; break;
222 case 4: text = "4/15'" ; break;
223 case 5: text = "2/15'" ; break;
224 case 6: text = "1/15'" ; break;
225 }
226 break;
227 case 8: // dix-septièmes
228 switch (octave)
229 {
230 case 0: text = "3' 13/17"; break;
231 case 1: text = "1' 15/17"; break;
232 case 2: text = "16/17'" ; break;
233 case 3: text = "8/17'" ; break;
234 case 4: text = "4/17'" ; break;
235 case 5: text = "2/17'" ; break;
236 }
237 break;
238 case 9: // dix-neuvièmes
239 switch (octave)
240 {
241 case 0: text = "3' 7/19" ; break;
242 case 1: text = "1' 13/19"; break;
243 case 2: text = "16/19'" ; break;
244 case 3: text = "8/19'" ; break;
245 case 4: text = "4/19'" ; break;
246 case 5: text = "2/19'" ; break;
247 }
248 break;
249 case 10: // vingt-et-unièmes
250 switch (octave)
251 {
252 case 0: text = "3' 1/21" ; break;
253 case 1: text = "1' 11/21"; break;
254 case 2: text = "16/21'" ; break;
255 case 3: text = "8/21'" ; break;
256 case 4: text = "4/21'" ; break;
257 case 5: text = "2/21'" ; break;
258 }
259 break;
260 case 11: // vingt-troisièmes
261 switch (octave)
262 {
263 case 0: text = "2' 18/23"; break;
264 case 1: text = "1' 9/23" ; break;
265 case 2: text = "16/23'" ; break;
266 case 3: text = "8/23'" ; break;
267 case 4: text = "4/23'" ; break;
268 case 5: text = "2/23'" ; break;
269 }
270 break;
271 case 12: // vingt-cinquièmes
272 switch (octave)
273 {
274 case 0: text = "2' 14/25"; break;
275 case 1: text = "1' 7/25" ; break;
276 case 2: text = "16/25'" ; break;
277 case 3: text = "8/25'" ; break;
278 case 4: text = "4/25'" ; break;
279 case 5: text = "2/25'" ; break;
280 }
281 break;
282 case 13: // vingt-septièmes
283 switch (octave)
284 {
285 case 0: text = "2' 10/27"; break;
286 case 1: text = "1' 5/27" ; break;
287 case 2: text = "16/27'" ; break;
288 case 3: text = "8/27'" ; break;
289 case 4: text = "4/27'" ; break;
290 case 5: text = "2/27'" ; break;
291 }
292 break;
293 }
294 return text;
295 }
296
dispType2(int index)297 void ToolMixtureCreation_gui::dispType2(int index)
298 {
299 // Reset du combobox 2
300 ui->comboType2->blockSignals(true);
301 ui->comboType2->clear();
302
303 // Nombre d'éléments en fonction du type de mutation
304 int nbItem = 0;
305 if (index == 0)
306 nbItem = 11;
307 else if (index == 1)
308 nbItem = 9;
309 else if (index < 4)
310 nbItem = 8;
311 else if (index < 8)
312 nbItem = 7;
313 else
314 nbItem = 6;
315
316 for (int i = 0; i < nbItem; i++)
317 ui->comboType2->addItem(getText(index, i));
318
319 ui->comboType2->blockSignals(false);
320 }
321
getNumDiv()322 int ToolMixtureCreation_gui::getNumDiv()
323 {
324 if (ui->listDivisions->selectedItems().length() != 1)
325 return -1;
326 int numDiv = ui->listDivisions->row(ui->listDivisions->selectedItems().first());
327 if (_divisions.length() <= numDiv)
328 return -1;
329 return numDiv;
330 }
331
getNumRang()332 int ToolMixtureCreation_gui::getNumRang()
333 {
334 if (ui->listRangs->selectedItems().length() != 1)
335 return -1;
336 int numRang = ui->listRangs->row(ui->listRangs->selectedItems().first());
337 int numDiv = this->getNumDiv();
338 if (numDiv < 0)
339 return -1;
340 if (_divisions[numDiv].getRanks().count() <= numRang)
341 return -1;
342 return numRang;
343 }
344
345 // Edit divisions
346
on_listDivisions_itemSelectionChanged()347 void ToolMixtureCreation_gui::on_listDivisions_itemSelectionChanged()
348 {
349 int currentRow = ui->listDivisions->currentRow();
350
351 // Modification des spinboxes
352 DivisionInfo di = _divisions[currentRow];
353 ui->spinDivStart->blockSignals(true);
354 ui->spinDivEnd->blockSignals(true);
355 ui->spinDivStart->setValue(di.getMinKey());
356 ui->spinDivEnd->setValue(di.getMaxKey());
357 ui->spinDivStart->blockSignals(false);
358 ui->spinDivEnd->blockSignals(false);
359
360 // Affichage rangs
361 this->dispRang(currentRow);
362 }
363
on_pushAddDiv_pressed()364 void ToolMixtureCreation_gui::on_pushAddDiv_pressed()
365 {
366 // Ajdd a division
367 _divisions << DivisionInfo(ui->spinDivStart->value(), ui->spinDivEnd->value());
368
369 // Recopie des rangs précédents
370 int numDiv = this->getNumDiv();
371 if (numDiv >= 0)
372 foreach (RankInfo ri, _divisions[numDiv].getRanks())
373 _divisions[_divisions.size() - 1].addRank(ri.getOvertoneType(), ri.getOctave());
374
375 // Affichage
376 this->dispDiv();
377
378 // Sélection de l'élément nouvellement créé
379 ui->listDivisions->setCurrentRow(_divisions.length()-1);
380 }
381
on_pushRemoveDiv_pressed()382 void ToolMixtureCreation_gui::on_pushRemoveDiv_pressed()
383 {
384 int numDiv = this->getNumDiv();
385 if (numDiv < 0) return;
386 _divisions.removeAt(numDiv);
387
388 // Affichage
389 this->dispDiv();
390
391 // Selection
392 if (numDiv >= ui->listDivisions->count())
393 numDiv--;
394 if (numDiv >= 0)
395 ui->listDivisions->setCurrentRow(numDiv);
396 else
397 this->dispRang();
398 }
399
on_spinDivStart_valueChanged(int arg1)400 void ToolMixtureCreation_gui::on_spinDivStart_valueChanged(int arg1)
401 {
402 int numDiv = this->getNumDiv();
403 if (numDiv < 0)
404 return;
405 _divisions[numDiv].setMinKey(arg1);
406
407 // Affichage
408 this->dispDiv();
409
410 // Resélection de l'élément
411 ui->listDivisions->blockSignals(true);
412 ui->listDivisions->setCurrentRow(numDiv);
413 ui->listDivisions->blockSignals(false);
414 }
415
on_spinDivEnd_valueChanged(int arg1)416 void ToolMixtureCreation_gui::on_spinDivEnd_valueChanged(int arg1)
417 {
418 int numDiv = this->getNumDiv();
419 if (numDiv < 0) return;
420 _divisions[numDiv].setMaxKey(arg1);
421
422 // Affichage
423 this->dispDiv();
424
425 // Resélection de l'élément
426 ui->listDivisions->blockSignals(true);
427 ui->listDivisions->setCurrentRow(numDiv);
428 ui->listDivisions->blockSignals(false);
429 }
430
431 // Edit ranks
432
on_listRangs_itemSelectionChanged()433 void ToolMixtureCreation_gui::on_listRangs_itemSelectionChanged()
434 {
435 int currentRow = ui->listRangs->currentRow();
436
437 // Récupération numéro de division et rang
438 int numDiv = this->getNumDiv();
439 if (numDiv < 0) return;
440 if (currentRow >= _divisions[numDiv].getRanks().count())
441 return;
442
443 // Update comboboxes
444 ui->comboType1->blockSignals(true);
445 ui->comboType2->blockSignals(true);
446 ui->comboType1->setCurrentIndex(_divisions[numDiv].getRanks()[currentRow].getOvertoneType());
447 this->dispType2(_divisions[numDiv].getRanks()[currentRow].getOvertoneType());
448 ui->comboType2->blockSignals(true);
449 ui->comboType2->setCurrentIndex(_divisions[numDiv].getRanks()[currentRow].getOctave());
450 ui->comboType1->blockSignals(false);
451 ui->comboType2->blockSignals(false);
452 }
453
on_pushAddRank_clicked()454 void ToolMixtureCreation_gui::on_pushAddRank_clicked()
455 {
456 // Récupération numéro de division
457 int numDiv = this->getNumDiv();
458 if (numDiv < 0) return;
459
460 // Ajout d'un rang
461 int type1 = ui->comboType1->currentIndex();
462 int type2 = ui->comboType2->currentIndex();
463 _divisions[numDiv].addRank(type1, type2);
464
465 // Affichage
466 this->dispRang();
467
468 // Sélection de l'élément nouvellement créé
469 ui->listRangs->setCurrentRow(_divisions[numDiv].getRanks().count() - 1);
470 }
471
on_pushRemoveRank_clicked()472 void ToolMixtureCreation_gui::on_pushRemoveRank_clicked()
473 {
474 // Récupération numéro de division et rang
475 int numDiv = this->getNumDiv();
476 if (numDiv < 0) return;
477 int numRang = this->getNumRang();
478 if (numRang < 0) return;
479
480 // Remove a rank
481 _divisions[numDiv].removeRank(numRang);
482
483 // Affichage
484 this->dispRang();
485
486 // Selection
487 if (numRang >= ui->listRangs->count())
488 numRang--;
489 if (numRang >= 0)
490 ui->listRangs->setCurrentRow(numRang);
491 }
492
on_comboType1_currentIndexChanged(int index)493 void ToolMixtureCreation_gui::on_comboType1_currentIndexChanged(int index)
494 {
495 // Modification combobox type2
496 this->dispType2(index);
497
498 // Sélection du 4ème élément (fondamentale 8')
499 ui->comboType2->setCurrentIndex(3);
500 }
501
on_comboType2_currentIndexChanged(int index)502 void ToolMixtureCreation_gui::on_comboType2_currentIndexChanged(int index)
503 {
504 // Récupération numéro de division
505 int numDiv = this->getNumDiv();
506 if (numDiv < 0) return;
507
508 // Récupération numéro de rang
509 int numRang = this->getNumRang();
510 if (numRang < 0) return;
511
512 // Modification liste paramètres
513 _divisions[numDiv].getRanks()[numRang].setOvertoneType(ui->comboType1->currentIndex());
514 _divisions[numDiv].getRanks()[numRang].setOctave(index);
515
516 // Affichage
517 this->dispRang();
518
519 // Resélection de l'élément
520 ui->listRangs->blockSignals(true);
521 ui->listRangs->setCurrentRow(numRang);
522 ui->listRangs->blockSignals(false);
523 }
524
on_pushCancel_clicked()525 void ToolMixtureCreation_gui::on_pushCancel_clicked()
526 {
527 emit(this->canceled());
528 }
529
on_pushOk_clicked()530 void ToolMixtureCreation_gui::on_pushOk_clicked()
531 {
532 // Check that the name is specified
533 if (ui->lineNom->text().isEmpty())
534 {
535 QMessageBox::warning(this, tr("Warning"), tr("The instrument name must be provided."));
536 return;
537 }
538
539 // Check that at least one division is not empty
540 bool ok = false;
541 for (int i = 0; i < _divisions.length(); i++)
542 {
543 if (!_divisions[i].getRanks().empty())
544 {
545 ok = true;
546 break;
547 }
548 }
549 if (!ok)
550 {
551 QMessageBox::warning(this, tr("Warning"), tr("At least one rank must be specified."));
552 return;
553 }
554
555 emit(this->validated());
556 }
557