1 //=============================================================================
2 //  Zerberus
3 //  Zample player
4 //
5 //  Copyright (C) 2013 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "zerberusgui.h"
14 
15 #include "mscore/preferences.h"
16 #include "mscore/extension.h"
17 #include "mscore/icons.h"
18 
19 //---------------------------------------------------------
20 //   SfzListDialog
21 //---------------------------------------------------------
22 
SfzListDialog(QWidget * parent)23 SfzListDialog::SfzListDialog(QWidget* parent)
24    : QDialog(parent)
25       {
26       setWindowTitle(tr("SFZ Files"));
27       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
28       list = new QListWidget;
29       list->setSelectionMode(QAbstractItemView::ExtendedSelection);
30 
31       okButton = new QPushButton;
32       cancelButton = new QPushButton;
33       okButton->setText(tr("Load"));
34       cancelButton->setText(tr("Cancel"));
35 
36       QVBoxLayout* layout = new QVBoxLayout;
37       buttonBox = new QDialogButtonBox;
38       layout->addWidget(list);
39       layout->addWidget(buttonBox);
40       buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
41       buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
42       setLayout(layout);
43       connect(okButton, SIGNAL(clicked()), SLOT(okClicked()));
44       connect(cancelButton, SIGNAL(clicked()), SLOT(cancelClicked()));
45       }
46 
47 //---------------------------------------------------------
48 //   add
49 //---------------------------------------------------------
50 
add(const QString & name,const QString & path)51 void SfzListDialog::add(const QString& name, const QString& path)
52       {
53       QListWidgetItem* item = new QListWidgetItem;
54       item->setText(name);
55       item->setData(Qt::UserRole, path);
56       list->addItem(item);
57       }
58 
59 //---------------------------------------------------------
60 //   okClicked
61 //---------------------------------------------------------
62 
okClicked()63 void SfzListDialog::okClicked()
64       {
65       for (auto item : list->selectedItems()) {
66             _namePaths.push_back({item->text(), item->data(Qt::UserRole).toString()});
67             }
68       accept();
69       }
70 
71 //---------------------------------------------------------
72 //   cancelClicked
73 //---------------------------------------------------------
74 
cancelClicked()75 void SfzListDialog::cancelClicked()
76       {
77       reject();
78       }
79 
80 //---------------------------------------------------------
81 //   gui
82 //---------------------------------------------------------
83 
gui()84 Ms::SynthesizerGui* Zerberus::gui()
85       {
86       if (_gui == 0)
87             _gui = new ZerberusGui(this);
88       return _gui;
89       }
90 
91 //---------------------------------------------------------
92 //   Zerberusgui
93 //---------------------------------------------------------
94 
ZerberusGui(Ms::Synthesizer * s)95 ZerberusGui::ZerberusGui(Ms::Synthesizer* s)
96    : SynthesizerGui(s)
97       {
98       setupUi(this);
99       connect(soundFontTop,    SIGNAL(clicked()), SLOT(soundFontTopClicked()));
100       connect(soundFontUp,     SIGNAL(clicked()), SLOT(soundFontUpClicked()));
101       connect(soundFontDown,   SIGNAL(clicked()), SLOT(soundFontDownClicked()));
102       connect(soundFontBottom, SIGNAL(clicked()), SLOT(soundFontBottomClicked()));
103       connect(soundFontAdd, SIGNAL(clicked()), SLOT(soundFontAddClicked()));
104       connect(soundFontDelete, SIGNAL(clicked()), SLOT(soundFontDeleteClicked()));
105       connect(&_futureWatcher, SIGNAL(finished()), this, SLOT(onSoundFontLoaded()));
106       _progressDialog = new QProgressDialog(tr("Loading…"), tr("Cancel"), 0, 100, 0, Qt::FramelessWindowHint);
107       _progressDialog->reset(); // required for Qt 5.5, see QTBUG-47042
108       connect(_progressDialog, SIGNAL(canceled()), this, SLOT(cancelLoadClicked()));
109       _progressTimer = new QTimer(this);
110       connect(_progressTimer, SIGNAL(timeout()), this, SLOT(updateProgress()));
111       connect(files, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons()));
112 
113       soundFontTop->setIcon(*Ms::icons[int(Ms::Icons::arrowsMoveToTop_ICON)]);
114       soundFontUp->setIcon(*Ms::icons[int(Ms::Icons::arrowUp_ICON)]);
115       soundFontDown->setIcon(*Ms::icons[int(Ms::Icons::arrowDown_ICON)]);
116       soundFontBottom->setIcon(*Ms::icons[int(Ms::Icons::arrowsMoveToBottom_ICON)]);
117 
118       updateButtons();
119       }
120 
121 //---------------------------------------------------------
122 //   moveSoundfontInTheList
123 //---------------------------------------------------------
124 
moveSoundfontInTheList(int currentIdx,int targetIdx)125 void ZerberusGui::moveSoundfontInTheList(int currentIdx, int targetIdx)
126       {
127       QStringList sfonts = zerberus()->soundFonts();
128       sfonts.move(currentIdx, targetIdx);
129       zerberus()->removeSoundFonts(zerberus()->soundFonts());
130 
131       loadSoundFontsAsync(sfonts);
132       files->setCurrentRow(targetIdx);
133       emit sfChanged();
134       }
135 
soundFontTopClicked()136 void ZerberusGui::soundFontTopClicked()
137       {
138       int row = files->currentRow();
139       if (row <= 0)
140             return;
141 
142       moveSoundfontInTheList(row, 0);
143       }
144 
soundFontBottomClicked()145 void ZerberusGui::soundFontBottomClicked()
146       {
147       int rows = files->count();
148       int row = files->currentRow();
149       if (row + 1 >= rows)
150             return;
151 
152       moveSoundfontInTheList(row, rows - 1);
153       }
154 
155 //---------------------------------------------------------
156 //   soundFontUpClicked
157 //---------------------------------------------------------
158 
soundFontUpClicked()159 void ZerberusGui::soundFontUpClicked()
160       {
161       int row = files->currentRow();
162       if (row <= 0)
163             return;
164 
165       moveSoundfontInTheList(row, row - 1);
166       }
167 
168 //---------------------------------------------------------
169 //   soundFontDownClicked
170 //---------------------------------------------------------
171 
soundFontDownClicked()172 void ZerberusGui::soundFontDownClicked()
173       {
174       int rows = files->count();
175       int row = files->currentRow();
176       if (row + 1 >= rows)
177             return;
178 
179       moveSoundfontInTheList(row, row + 1);
180       }
181 
182 //---------------------------------------------------------
183 //   loadSoundFontsAsync
184 //---------------------------------------------------------
185 
loadSoundFontsAsync(QStringList sfonts)186 void ZerberusGui::loadSoundFontsAsync(QStringList sfonts)
187       {
188       QFuture<bool> future = QtConcurrent::run(zerberus(), &Zerberus::loadSoundFonts, sfonts);
189       _futureWatcher.setFuture(future);
190       _progressTimer->start(1000);
191       _progressDialog->exec();
192 
193       synthesizerChanged();
194       }
195 
196 //---------------------------------------------------------
197 //   collectFiles
198 //---------------------------------------------------------
199 
collectFiles(QFileInfoList * l,const QString & path)200 static void collectFiles(QFileInfoList* l, const QString& path)
201       {
202 //printf("collect files <%s>\n", qPrintable(path));
203 
204       QDir dir(path);
205       foreach (const QFileInfo& s, dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) {
206             if (path == s.absoluteFilePath())
207                   return;
208 
209             if (s.isDir() && !s.isHidden())
210                   collectFiles(l, s.absoluteFilePath());
211             else {
212                   if (s.suffix().toLower() == "sfz")
213                         l->append(s);
214                   }
215             }
216       }
217 
218 //---------------------------------------------------------
219 //   sfzFiles
220 //---------------------------------------------------------
221 
sfzFiles()222 QFileInfoList Zerberus::sfzFiles()
223       {
224       QFileInfoList l;
225 
226       QStringList pl = Ms::preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS).split(";");
227       pl.prepend(QFileInfo(QString("%1%2").arg(Ms::mscoreGlobalShare).arg("sound")).absoluteFilePath());
228 
229       // append extensions directory
230       QStringList extensionsDir = Ms::Extension::getDirectoriesByType(Ms::Extension::sfzsDir);
231       pl.append(extensionsDir);
232 
233       foreach (const QString& s, pl) {
234             QString ss(s);
235             if (!s.isEmpty() && s[0] == '~')
236                   ss = QDir::homePath() + s.mid(1);
237             collectFiles(&l, ss);
238             }
239       return l;
240       }
241 
242 //---------------------------------------------------------
243 //   loadSfz
244 //---------------------------------------------------------
245 
loadSfz()246 void ZerberusGui::loadSfz() {
247 
248       if (_sfzToLoad.empty())
249             return;
250 
251      struct SfzNamePath item = _sfzToLoad.front();
252      QString sfName = item.name;
253      QString sfPath = item.path;
254      _sfzToLoad.pop_front();
255 
256      QStringList sl;
257      for (int i = 0; i < files->count(); ++i) {
258            QListWidgetItem* item1 = files->item(i);
259            sl.append(item1->text());
260            }
261 
262       if (sl.contains(sfName)) {
263             QMessageBox::warning(this,
264             tr("MuseScore"),
265             tr("SoundFont %1 already loaded").arg(sfPath));
266             }
267       else {
268             _loadedSfName = sfName;
269             _loadedSfPath = sfPath;
270             QFuture<bool> future = QtConcurrent::run(zerberus(), &Zerberus::addSoundFont, sfName);
271             _futureWatcher.setFuture(future);
272             _progressTimer->start(1000);
273             _progressDialog->exec();
274             }
275       }
276 
277 //---------------------------------------------------------
278 //   addClicked
279 //---------------------------------------------------------
280 
soundFontAddClicked()281 void ZerberusGui::soundFontAddClicked()
282       {
283       zerberus()->setLoadWasCanceled(false);
284 
285       QFileInfoList l = Zerberus::sfzFiles();
286 
287       SfzListDialog ld(this);
288       for (const QFileInfo& fi : l)
289             ld.add(fi.fileName(), fi.absoluteFilePath());
290       if (!ld.exec())
291             return;
292 
293       for (auto item : ld.getNamePaths()) {
294             _sfzToLoad.push_back(item);
295             }
296       loadSfz();
297       }
298 
299 //---------------------------------------------------------
300 //   cancelLoad
301 //---------------------------------------------------------
302 
cancelLoadClicked()303 void ZerberusGui::cancelLoadClicked()
304       {
305       zerberus()->setLoadWasCanceled(true);
306       }
307 
308 //---------------------------------------------------------
309 //   updateProgress
310 //---------------------------------------------------------
311 
updateProgress()312 void ZerberusGui::updateProgress()
313       {
314       _progressDialog->setValue(zerberus()->loadProgress());
315       }
316 
317 //---------------------------------------------------------
318 //   updateButtons
319 //---------------------------------------------------------
320 
updateButtons()321 void ZerberusGui::updateButtons()
322       {
323       int rows = zerberus()->soundFonts().count();
324       int row = files->currentRow();
325       soundFontTop->setEnabled(row > 0);
326       soundFontUp->setEnabled(row > 0);
327       soundFontDown->setEnabled((row != -1) && (row < (rows - 1)));
328       soundFontBottom->setEnabled((row != -1) && row < (rows - 1));
329       soundFontDelete->setEnabled(row != -1);
330       }
331 
332 //---------------------------------------------------------
333 //   onSoundFontLoaded
334 //---------------------------------------------------------
335 
onSoundFontLoaded()336 void ZerberusGui::onSoundFontLoaded()
337       {
338       bool loaded = _futureWatcher.result();
339       bool wasNotCanceled = !_progressDialog->wasCanceled();
340       _progressTimer->stop();
341       _progressDialog->reset();
342       if (loaded) {
343             QListWidgetItem* item = new QListWidgetItem;
344             item->setText(_loadedSfName);
345             item->setData(Qt::UserRole, _loadedSfPath);
346             //files->insertItem(0, item);
347             files->addItem(item);
348             emit valueChanged();
349             emit sfChanged();
350             }
351       else if (wasNotCanceled) {
352             QMessageBox::warning(this,
353             tr("MuseScore"),
354             tr("Cannot load SoundFont %1").arg(_loadedSfPath));
355             }
356       loadSfz();
357       }
358 
359 //---------------------------------------------------------
360 //   removeClicked
361 //---------------------------------------------------------
362 
soundFontDeleteClicked()363 void ZerberusGui::soundFontDeleteClicked()
364       {
365       int row = files->currentRow();
366       if (row >= 0) {
367             QString path(files->item(row)->data(Qt::UserRole).toString());
368             if (!zerberus()->removeSoundFont(path))
369                   qDebug("ZerberusGui::removeClicked: cannot remove sf %s", qPrintable(files->item(row)->text()));
370             delete files->takeItem(row);
371             emit valueChanged();
372             emit sfChanged();
373             updateButtons();
374             }
375       }
376 
377 //---------------------------------------------------------
378 //   synthesizerChanged
379 //---------------------------------------------------------
380 
synthesizerChanged()381 void ZerberusGui::synthesizerChanged()
382       {
383       files->clear();
384       QStringList sfonts = zerberus()->soundFonts();
385       for (QString path : sfonts) {
386             QListWidgetItem* item = new QListWidgetItem;
387             item->setText(QFileInfo(path).fileName());
388             item->setData(Qt::UserRole, path);
389             files->addItem(item);
390             }
391       }
392