patches_dialog.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 = NULL;
  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. #if QT_VERSION >= 0x050000
  67. pHeader->setSectionsMovable(false);
  68. #else
  69. pHeader->setMovable(false);
  70. #endif
  71. pHeader->setStretchLastSection(true);
  72. m_progListView->resizeColumnToContents(0); // Prog.
  73. //pHeader->resizeSection(1, 200); // Name.
  74. // Initial sort order...
  75. m_bankListView->sortItems(0, Qt::AscendingOrder);
  76. m_progListView->sortItems(0, Qt::AscendingOrder);
  77. // UI connections...
  78. QObject::connect(m_bankListView,
  79. SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
  80. SLOT(bankChanged()));
  81. QObject::connect(m_progListView,
  82. SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
  83. SLOT(progChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
  84. QObject::connect(m_progListView,
  85. SIGNAL(itemActivated(QTreeWidgetItem*,int)),
  86. SLOT(accept()));
  87. QObject::connect(m_okButton,
  88. SIGNAL(clicked()),
  89. SLOT(accept()));
  90. QObject::connect(m_cancelButton,
  91. SIGNAL(clicked()),
  92. SLOT(reject()));
  93. }
  94. // Destructor.
  95. patchesDialog::~patchesDialog()
  96. {
  97. }
  98. // Dialog setup loader.
  99. void patchesDialog::setup ( fluid_synth_t * pSynth, int iChan,
  100. const QString & _chanName,
  101. LcdSpinBoxModel * _bankModel,
  102. LcdSpinBoxModel * _progModel,
  103. QLabel * _patchLabel )
  104. {
  105. // We'll going to changes the whole thing...
  106. m_dirty = 0;
  107. m_bankModel = _bankModel;
  108. m_progModel = _progModel;
  109. m_patchLabel = _patchLabel;
  110. // Set the proper caption...
  111. setWindowTitle( _chanName + " - Soundfont patches" );
  112. // set m_pSynth to NULL so we don't trigger any progChanged events
  113. m_pSynth = NULL;
  114. // Load bank list from actual synth stack...
  115. m_bankListView->setSortingEnabled(false);
  116. m_bankListView->clear();
  117. // now it should be safe to set internal stuff
  118. m_pSynth = pSynth;
  119. m_iChan = iChan;
  120. QTreeWidgetItem *pBankItem = NULL;
  121. // For all soundfonts (in reversed stack order) fill the available banks...
  122. int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
  123. for (int i = 0; i < cSoundFonts; i++) {
  124. fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
  125. if (pSoundFont) {
  126. #ifdef CONFIG_FLUID_BANK_OFFSET
  127. int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
  128. #endif
  129. fluid_sfont_iteration_start(pSoundFont);
  130. #if FLUIDSYNTH_VERSION_MAJOR < 2
  131. fluid_preset_t preset;
  132. fluid_preset_t *pCurPreset = &preset;
  133. #else
  134. fluid_preset_t *pCurPreset;
  135. #endif
  136. while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
  137. int iBank = fluid_preset_get_banknum(pCurPreset);
  138. #ifdef CONFIG_FLUID_BANK_OFFSET
  139. iBank += iBankOffset;
  140. #endif
  141. if (!findBankItem(iBank)) {
  142. pBankItem = new patchItem(m_bankListView, pBankItem);
  143. if (pBankItem)
  144. pBankItem->setText(0, QString::number(iBank));
  145. }
  146. }
  147. }
  148. }
  149. m_bankListView->setSortingEnabled(true);
  150. // Set the selected bank.
  151. m_iBank = 0;
  152. fluid_preset_t *pPreset = ::fluid_synth_get_channel_preset(m_pSynth, m_iChan);
  153. if (pPreset) {
  154. m_iBank = fluid_preset_get_banknum(pPreset);
  155. #ifdef CONFIG_FLUID_BANK_OFFSET
  156. m_iBank += ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(fluid_preset_get_sfont(sfont)));
  157. #endif
  158. }
  159. pBankItem = findBankItem(m_iBank);
  160. m_bankListView->setCurrentItem(pBankItem);
  161. m_bankListView->scrollToItem(pBankItem);
  162. bankChanged();
  163. // Set the selected program.
  164. if (pPreset)
  165. m_iProg = fluid_preset_get_num(pPreset);
  166. QTreeWidgetItem *pProgItem = findProgItem(m_iProg);
  167. m_progListView->setCurrentItem(pProgItem);
  168. m_progListView->scrollToItem(pProgItem);
  169. // Done with setup...
  170. //m_iDirtySetup--;
  171. }
  172. // Stabilize current state form.
  173. void patchesDialog::stabilizeForm()
  174. {
  175. m_okButton->setEnabled(validateForm());
  176. }
  177. // Validate form fields.
  178. bool patchesDialog::validateForm()
  179. {
  180. bool bValid = true;
  181. bValid = bValid && (m_bankListView->currentItem() != NULL);
  182. bValid = bValid && (m_progListView->currentItem() != NULL);
  183. return bValid;
  184. }
  185. // Realize a bank-program selection preset.
  186. void patchesDialog::setBankProg ( int iBank, int iProg )
  187. {
  188. if (m_pSynth == NULL)
  189. return;
  190. // just select the synth's program preset...
  191. ::fluid_synth_bank_select(m_pSynth, m_iChan, iBank);
  192. ::fluid_synth_program_change(m_pSynth, m_iChan, iProg);
  193. // Maybe this is needed to stabilize things around.
  194. ::fluid_synth_program_reset(m_pSynth);
  195. }
  196. // Validate form fields and accept it valid.
  197. void patchesDialog::accept()
  198. {
  199. if (validateForm()) {
  200. // Unload from current selected dialog items.
  201. int iBank = (m_bankListView->currentItem())->text(0).toInt();
  202. int iProg = (m_progListView->currentItem())->text(0).toInt();
  203. // And set it right away...
  204. setBankProg(iBank, iProg);
  205. if (m_dirty > 0) {
  206. m_bankModel->setValue( iBank );
  207. m_progModel->setValue( iProg );
  208. m_patchLabel->setText( m_progListView->
  209. currentItem()->text( 1 ) );
  210. }
  211. // Do remember preview state...
  212. // if (m_pOptions)
  213. // m_pOptions->bPresetPreview = m_ui.PreviewCheckBox->isChecked();
  214. // We got it.
  215. QDialog::accept();
  216. }
  217. }
  218. // Reject settings (Cancel button slot).
  219. void patchesDialog::reject (void)
  220. {
  221. // Reset selection to initial selection, if applicable...
  222. if (m_dirty > 0)
  223. setBankProg(m_bankModel->value(), m_progModel->value());
  224. // Done (hopefully nothing).
  225. QDialog::reject();
  226. }
  227. // Find the bank item of given bank number id.
  228. QTreeWidgetItem *patchesDialog::findBankItem ( int iBank )
  229. {
  230. QList<QTreeWidgetItem *> banks
  231. = m_bankListView->findItems(
  232. QString::number(iBank), Qt::MatchExactly, 0);
  233. QListIterator<QTreeWidgetItem *> iter(banks);
  234. if (iter.hasNext())
  235. return iter.next();
  236. else
  237. return NULL;
  238. }
  239. // Find the program item of given program number id.
  240. QTreeWidgetItem *patchesDialog::findProgItem ( int iProg )
  241. {
  242. QList<QTreeWidgetItem *> progs
  243. = m_progListView->findItems(
  244. QString::number(iProg), Qt::MatchExactly, 0);
  245. QListIterator<QTreeWidgetItem *> iter(progs);
  246. if (iter.hasNext())
  247. return iter.next();
  248. else
  249. return NULL;
  250. }
  251. // Bank change slot.
  252. void patchesDialog::bankChanged (void)
  253. {
  254. if (m_pSynth == NULL)
  255. return;
  256. QTreeWidgetItem *pBankItem = m_bankListView->currentItem();
  257. if (pBankItem == NULL)
  258. return;
  259. int iBankSelected = pBankItem->text(0).toInt();
  260. // Clear up the program listview.
  261. m_progListView->setSortingEnabled(false);
  262. m_progListView->clear();
  263. QTreeWidgetItem *pProgItem = NULL;
  264. // For all soundfonts (in reversed stack order) fill the available programs...
  265. int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
  266. for (int i = 0; i < cSoundFonts && !pProgItem; i++) {
  267. fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
  268. if (pSoundFont) {
  269. #ifdef CONFIG_FLUID_BANK_OFFSET
  270. int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
  271. #endif
  272. fluid_sfont_iteration_start(pSoundFont);
  273. #if FLUIDSYNTH_VERSION_MAJOR < 2
  274. fluid_preset_t preset;
  275. fluid_preset_t *pCurPreset = &preset;
  276. #else
  277. fluid_preset_t *pCurPreset;
  278. #endif
  279. while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
  280. int iBank = fluid_preset_get_banknum(pCurPreset);
  281. #ifdef CONFIG_FLUID_BANK_OFFSET
  282. iBank += iBankOffset;
  283. #endif
  284. int iProg = fluid_preset_get_num(pCurPreset);
  285. if (iBank == iBankSelected && !findProgItem(iProg)) {
  286. pProgItem = new patchItem(m_progListView, pProgItem);
  287. if (pProgItem) {
  288. pProgItem->setText(0, QString::number(iProg));
  289. pProgItem->setText(1, fluid_preset_get_name(pCurPreset));
  290. //pProgItem->setText(2, QString::number(fluid_sfont_get_id(pSoundFont)));
  291. //pProgItem->setText(3, QFileInfo(
  292. // fluid_sfont_get_name(pSoundFont).baseName());
  293. }
  294. }
  295. }
  296. }
  297. }
  298. m_progListView->setSortingEnabled(true);
  299. // Stabilize the form.
  300. stabilizeForm();
  301. }
  302. // Program change slot.
  303. void patchesDialog::progChanged (QTreeWidgetItem * _curr, QTreeWidgetItem * _prev)
  304. {
  305. if (m_pSynth == NULL || _curr == NULL)
  306. return;
  307. // Which preview state...
  308. if( validateForm() ) {
  309. // Set current selection.
  310. int iBank = (m_bankListView->currentItem())->text(0).toInt();
  311. int iProg = _curr->text(0).toInt();
  312. // And set it right away...
  313. setBankProg(iBank, iProg);
  314. // Now we're dirty nuff.
  315. m_dirty++;
  316. }
  317. // Stabilize the form.
  318. stabilizeForm();
  319. }