patches_dialog.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * patches_dialog.cpp - display sf2 patches
  3. *
  4. * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
  5. *
  6. * This file is part of LMMS - https://lmms.io
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2 of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with this program (see COPYING); if not, write to the
  20. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. * Boston, MA 02110-1301 USA.
  22. *
  23. */
  24. #include "patches_dialog.h"
  25. #include <QHeaderView>
  26. //#include <QFileInfo>
  27. // Custom list-view item (as for numerical sort purposes...)
  28. class patchItem : public QTreeWidgetItem
  29. {
  30. public:
  31. // Constructor.
  32. patchItem( QTreeWidget *pListView,
  33. QTreeWidgetItem *pItemAfter )
  34. : QTreeWidgetItem( pListView, pItemAfter ) {}
  35. // Sort/compare overriden method.
  36. bool operator< ( const QTreeWidgetItem& other ) const
  37. {
  38. int iColumn = QTreeWidgetItem::treeWidget()->sortColumn();
  39. const QString& s1 = text( iColumn );
  40. const QString& s2 = other.text( iColumn );
  41. if( iColumn == 0 || iColumn == 2 )
  42. {
  43. return( s1.toInt() < s2.toInt() );
  44. }
  45. else
  46. {
  47. return( s1 < s2 );
  48. }
  49. }
  50. };
  51. // Constructor.
  52. patchesDialog::patchesDialog( QWidget *pParent, Qt::WindowFlags wflags )
  53. : QDialog( pParent, wflags )
  54. {
  55. // Setup UI struct...
  56. setupUi( this );
  57. m_pSynth = nullptr;
  58. m_iChan = 0;
  59. m_iBank = 0;
  60. m_iProg = 0;
  61. // Soundfonts list view...
  62. QHeaderView *pHeader = m_progListView->header();
  63. // pHeader->setResizeMode(QHeaderView::Custom);
  64. pHeader->setDefaultAlignment(Qt::AlignLeft);
  65. // pHeader->setDefaultSectionSize(200);
  66. pHeader->setSectionsMovable(false);
  67. pHeader->setStretchLastSection(true);
  68. m_progListView->resizeColumnToContents(0); // Prog.
  69. //pHeader->resizeSection(1, 200); // Name.
  70. // Initial sort order...
  71. m_bankListView->sortItems(0, Qt::AscendingOrder);
  72. m_progListView->sortItems(0, Qt::AscendingOrder);
  73. // UI connections...
  74. QObject::connect(m_bankListView,
  75. SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
  76. SLOT(bankChanged()));
  77. QObject::connect(m_progListView,
  78. SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
  79. SLOT(progChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
  80. QObject::connect(m_progListView,
  81. SIGNAL(itemActivated(QTreeWidgetItem*,int)),
  82. SLOT(accept()));
  83. QObject::connect(m_okButton,
  84. SIGNAL(clicked()),
  85. SLOT(accept()));
  86. QObject::connect(m_cancelButton,
  87. SIGNAL(clicked()),
  88. SLOT(reject()));
  89. }
  90. // Destructor.
  91. patchesDialog::~patchesDialog()
  92. {
  93. }
  94. // Dialog setup loader.
  95. void patchesDialog::setup ( fluid_synth_t * pSynth, int iChan,
  96. const QString & _chanName,
  97. LcdSpinBoxModel * _bankModel,
  98. LcdSpinBoxModel * _progModel,
  99. QLabel * _patchLabel )
  100. {
  101. // We'll going to changes the whole thing...
  102. m_dirty = 0;
  103. m_bankModel = _bankModel;
  104. m_progModel = _progModel;
  105. m_patchLabel = _patchLabel;
  106. // Set the proper caption...
  107. setWindowTitle( _chanName + " - Soundfont patches" );
  108. // set m_pSynth to NULL so we don't trigger any progChanged events
  109. m_pSynth = nullptr;
  110. // Load bank list from actual synth stack...
  111. m_bankListView->setSortingEnabled(false);
  112. m_bankListView->clear();
  113. // now it should be safe to set internal stuff
  114. m_pSynth = pSynth;
  115. m_iChan = iChan;
  116. QTreeWidgetItem *pBankItem = nullptr;
  117. // For all soundfonts (in reversed stack order) fill the available banks...
  118. int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
  119. for (int i = 0; i < cSoundFonts; i++) {
  120. fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
  121. if (pSoundFont) {
  122. #ifdef CONFIG_FLUID_BANK_OFFSET
  123. int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
  124. #endif
  125. fluid_sfont_iteration_start(pSoundFont);
  126. #if FLUIDSYNTH_VERSION_MAJOR < 2
  127. fluid_preset_t preset;
  128. fluid_preset_t *pCurPreset = &preset;
  129. #else
  130. fluid_preset_t *pCurPreset;
  131. #endif
  132. while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
  133. int iBank = fluid_preset_get_banknum(pCurPreset);
  134. #ifdef CONFIG_FLUID_BANK_OFFSET
  135. iBank += iBankOffset;
  136. #endif
  137. if (!findBankItem(iBank)) {
  138. pBankItem = new patchItem(m_bankListView, pBankItem);
  139. if (pBankItem)
  140. pBankItem->setText(0, QString::number(iBank));
  141. }
  142. }
  143. }
  144. }
  145. m_bankListView->setSortingEnabled(true);
  146. // Set the selected bank.
  147. m_iBank = 0;
  148. fluid_preset_t *pPreset = ::fluid_synth_get_channel_preset(m_pSynth, m_iChan);
  149. if (pPreset) {
  150. m_iBank = fluid_preset_get_banknum(pPreset);
  151. #ifdef CONFIG_FLUID_BANK_OFFSET
  152. m_iBank += ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(fluid_preset_get_sfont(sfont)));
  153. #endif
  154. }
  155. pBankItem = findBankItem(m_iBank);
  156. m_bankListView->setCurrentItem(pBankItem);
  157. m_bankListView->scrollToItem(pBankItem);
  158. bankChanged();
  159. // Set the selected program.
  160. if (pPreset)
  161. m_iProg = fluid_preset_get_num(pPreset);
  162. QTreeWidgetItem *pProgItem = findProgItem(m_iProg);
  163. m_progListView->setCurrentItem(pProgItem);
  164. m_progListView->scrollToItem(pProgItem);
  165. // Done with setup...
  166. //m_iDirtySetup--;
  167. }
  168. // Stabilize current state form.
  169. void patchesDialog::stabilizeForm()
  170. {
  171. m_okButton->setEnabled(validateForm());
  172. }
  173. // Validate form fields.
  174. bool patchesDialog::validateForm()
  175. {
  176. bool bValid = true;
  177. bValid = bValid && (m_bankListView->currentItem() != nullptr);
  178. bValid = bValid && (m_progListView->currentItem() != nullptr);
  179. return bValid;
  180. }
  181. // Realize a bank-program selection preset.
  182. void patchesDialog::setBankProg ( int iBank, int iProg )
  183. {
  184. if (m_pSynth == nullptr)
  185. return;
  186. // just select the synth's program preset...
  187. ::fluid_synth_bank_select(m_pSynth, m_iChan, iBank);
  188. ::fluid_synth_program_change(m_pSynth, m_iChan, iProg);
  189. // Maybe this is needed to stabilize things around.
  190. ::fluid_synth_program_reset(m_pSynth);
  191. }
  192. // Validate form fields and accept it valid.
  193. void patchesDialog::accept()
  194. {
  195. if (validateForm()) {
  196. // Unload from current selected dialog items.
  197. int iBank = (m_bankListView->currentItem())->text(0).toInt();
  198. int iProg = (m_progListView->currentItem())->text(0).toInt();
  199. // And set it right away...
  200. setBankProg(iBank, iProg);
  201. if (m_dirty > 0) {
  202. m_bankModel->setValue( iBank );
  203. m_progModel->setValue( iProg );
  204. m_patchLabel->setText( m_progListView->
  205. currentItem()->text( 1 ) );
  206. }
  207. // Do remember preview state...
  208. // if (m_pOptions)
  209. // m_pOptions->bPresetPreview = m_ui.PreviewCheckBox->isChecked();
  210. // We got it.
  211. QDialog::accept();
  212. }
  213. }
  214. // Reject settings (Cancel button slot).
  215. void patchesDialog::reject (void)
  216. {
  217. // Reset selection to initial selection, if applicable...
  218. if (m_dirty > 0)
  219. setBankProg(m_bankModel->value(), m_progModel->value());
  220. // Done (hopefully nothing).
  221. QDialog::reject();
  222. }
  223. // Find the bank item of given bank number id.
  224. QTreeWidgetItem *patchesDialog::findBankItem ( int iBank )
  225. {
  226. QList<QTreeWidgetItem *> banks
  227. = m_bankListView->findItems(
  228. QString::number(iBank), Qt::MatchExactly, 0);
  229. QListIterator<QTreeWidgetItem *> iter(banks);
  230. if (iter.hasNext())
  231. return iter.next();
  232. else
  233. return nullptr;
  234. }
  235. // Find the program item of given program number id.
  236. QTreeWidgetItem *patchesDialog::findProgItem ( int iProg )
  237. {
  238. QList<QTreeWidgetItem *> progs
  239. = m_progListView->findItems(
  240. QString::number(iProg), Qt::MatchExactly, 0);
  241. QListIterator<QTreeWidgetItem *> iter(progs);
  242. if (iter.hasNext())
  243. return iter.next();
  244. else
  245. return nullptr;
  246. }
  247. // Bank change slot.
  248. void patchesDialog::bankChanged (void)
  249. {
  250. if (m_pSynth == nullptr)
  251. return;
  252. QTreeWidgetItem *pBankItem = m_bankListView->currentItem();
  253. if (pBankItem == nullptr)
  254. return;
  255. int iBankSelected = pBankItem->text(0).toInt();
  256. // Clear up the program listview.
  257. m_progListView->setSortingEnabled(false);
  258. m_progListView->clear();
  259. QTreeWidgetItem *pProgItem = nullptr;
  260. // For all soundfonts (in reversed stack order) fill the available programs...
  261. int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
  262. for (int i = 0; i < cSoundFonts && !pProgItem; i++) {
  263. fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
  264. if (pSoundFont) {
  265. #ifdef CONFIG_FLUID_BANK_OFFSET
  266. int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
  267. #endif
  268. fluid_sfont_iteration_start(pSoundFont);
  269. #if FLUIDSYNTH_VERSION_MAJOR < 2
  270. fluid_preset_t preset;
  271. fluid_preset_t *pCurPreset = &preset;
  272. #else
  273. fluid_preset_t *pCurPreset;
  274. #endif
  275. while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
  276. int iBank = fluid_preset_get_banknum(pCurPreset);
  277. #ifdef CONFIG_FLUID_BANK_OFFSET
  278. iBank += iBankOffset;
  279. #endif
  280. int iProg = fluid_preset_get_num(pCurPreset);
  281. if (iBank == iBankSelected && !findProgItem(iProg)) {
  282. pProgItem = new patchItem(m_progListView, pProgItem);
  283. if (pProgItem) {
  284. pProgItem->setText(0, QString::number(iProg));
  285. pProgItem->setText(1, fluid_preset_get_name(pCurPreset));
  286. //pProgItem->setText(2, QString::number(fluid_sfont_get_id(pSoundFont)));
  287. //pProgItem->setText(3, QFileInfo(
  288. // fluid_sfont_get_name(pSoundFont).baseName());
  289. }
  290. }
  291. }
  292. }
  293. }
  294. m_progListView->setSortingEnabled(true);
  295. // Stabilize the form.
  296. stabilizeForm();
  297. }
  298. // Program change slot.
  299. void patchesDialog::progChanged (QTreeWidgetItem * _curr, QTreeWidgetItem * _prev)
  300. {
  301. if (m_pSynth == nullptr || _curr == nullptr)
  302. return;
  303. // Which preview state...
  304. if( validateForm() ) {
  305. // Set current selection.
  306. int iBank = (m_bankListView->currentItem())->text(0).toInt();
  307. int iProg = _curr->text(0).toInt();
  308. // And set it right away...
  309. setBankProg(iBank, iProg);
  310. // Now we're dirty nuff.
  311. m_dirty++;
  312. }
  313. // Stabilize the form.
  314. stabilizeForm();
  315. }