1 |
// qsamplerChannelFxForm.cpp |
2 |
// |
3 |
/**************************************************************************** |
4 |
Copyright (C) 2010-2012, rncbc aka Rui Nuno Capela. All rights reserved. |
5 |
Copyright (C) 2008, Christian Schoenebeck |
6 |
|
7 |
This program is free software; you can redistribute it and/or |
8 |
modify it under the terms of the GNU General Public License |
9 |
as published by the Free Software Foundation; either version 2 |
10 |
of the License, or (at your option) any later version. |
11 |
|
12 |
This program is distributed in the hope that it will be useful, |
13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
GNU General Public License for more details. |
16 |
|
17 |
You should have received a copy of the GNU General Public License |
18 |
along with this program; if not, write to the Free Software |
19 |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
20 |
|
21 |
*****************************************************************************/ |
22 |
|
23 |
#include "qsamplerAbout.h" |
24 |
#include "qsamplerChannelFxForm.h" |
25 |
#include "qsamplerFxSendsModel.h" |
26 |
|
27 |
// let's not reinvent the wheel for audio routing |
28 |
#include "qsamplerChannel.h" |
29 |
|
30 |
#include <math.h> |
31 |
|
32 |
#include <QAbstractButton> |
33 |
#include <QLineEdit> |
34 |
#include <QHeaderView> |
35 |
#include <QMap> |
36 |
|
37 |
namespace { // private namespace |
38 |
|
39 |
static const char* _midiControllerName(int iMidiCtrl) { |
40 |
switch (iMidiCtrl) { |
41 |
case 0: return "Bank select MSB"; |
42 |
case 1: return "Modulation MSB"; |
43 |
case 2: return "Breath Controller"; |
44 |
case 4: return "Foot Controller MSB"; |
45 |
case 5: return "Portamento Time MSB"; |
46 |
case 6: return "(N)RPN Data Byte"; |
47 |
case 7: return "Main Volume"; |
48 |
case 8: return "Balance"; |
49 |
case 10: return "Panorama"; |
50 |
case 11: return "Expression"; |
51 |
case 12: return "Effect Control 1"; |
52 |
case 13: return "Effect Control 2"; |
53 |
case 16: return "General Purpose Controller 1"; |
54 |
case 17: return "General Purpose Controller 2"; |
55 |
case 18: return "General Purpose Controller 3"; |
56 |
case 19: return "General Purpose Controller 4"; |
57 |
case 32: return "Bank select LSB"; |
58 |
case 63: return "LSB for Controllers 0?31"; |
59 |
case 64: return "Hold 1"; |
60 |
case 65: return "Portamento"; |
61 |
case 66: return "Sostenuto"; |
62 |
case 67: return "Soft Pedal"; |
63 |
case 68: return "Legato Footswitch"; |
64 |
case 69: return "Hold 2"; |
65 |
case 70: return "Sound Controller 1 (Sound Variation)"; |
66 |
case 71: return "Sound Controller 2 (Harmonic Content)"; |
67 |
case 72: return "Sound Controller 3 (Release Time)"; |
68 |
case 73: return "Sound Controller 4 (Attack Time)"; |
69 |
case 74: return "Sound Controller 5 (Brightness)"; |
70 |
case 75: return "Sound Controller 6"; |
71 |
case 76: return "Sound Controller 7"; |
72 |
case 77: return "Sound Controller 8"; |
73 |
case 78: return "Sound Controller 9"; |
74 |
case 79: return "Sound Controller 10"; |
75 |
case 80: return "General Purpose 5"; |
76 |
case 81: return "General Purpose 6"; |
77 |
case 82: return "General Purpose 7"; |
78 |
case 83: return "General Purpose 8"; |
79 |
case 84: return "Portamento Control"; |
80 |
case 91: return "Effects 1 Depth"; |
81 |
case 92: return "Effects 2 Depth"; |
82 |
case 93: return "Effects 3 Depth"; |
83 |
case 94: return "Effects 4 Depth"; |
84 |
case 95: return "Effects 5 Depth"; |
85 |
case 96: return "Data Increment (N)RPN"; |
86 |
case 97: return "Data Decrement (N)RPN"; |
87 |
case 98: return "NRPN LSB"; |
88 |
case 99: return "NRPN MSB"; |
89 |
case 100: return "RPN LSB"; |
90 |
case 101: return "RPN MSB"; |
91 |
case 120: return "All Sounds Off"; |
92 |
case 121: return "Controller Reset"; |
93 |
case 122: return "Local Control on/off"; |
94 |
case 123: return "All Notes Off"; |
95 |
case 124: return "Omni Off"; |
96 |
case 125: return "Omni On"; |
97 |
case 126: return "Mono On / Poly Off"; |
98 |
case 127: return "Poly On / Mono Off"; |
99 |
default: return ""; |
100 |
} |
101 |
} |
102 |
|
103 |
} // private namespace |
104 |
|
105 |
|
106 |
namespace QSampler { |
107 |
|
108 |
ChannelFxForm::ChannelFxForm ( |
109 |
Channel* pSamplerChannel, QWidget* pParent, Qt::WindowFlags wflags ) |
110 |
: QDialog(pParent, wflags) |
111 |
{ |
112 |
m_ui.setupUi(this); |
113 |
|
114 |
m_pSamplerChannel = pSamplerChannel; |
115 |
|
116 |
m_pAudioDevice = NULL; |
117 |
|
118 |
FxSendsModel* pModel = |
119 |
new FxSendsModel(m_pSamplerChannel->channelID(), m_ui.SendsListView); |
120 |
m_ui.SendsListView->setModel(pModel); |
121 |
#if QT_VERSION >= 0x040300 |
122 |
m_ui.SendsListView->setSelectionRectVisible(true); |
123 |
#endif |
124 |
|
125 |
const int iRowHeight = m_ui.audioRoutingTable->fontMetrics().height() + 4; |
126 |
m_ui.audioRoutingTable->verticalHeader()->setDefaultSectionSize(iRowHeight); |
127 |
m_ui.audioRoutingTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); |
128 |
ChannelRoutingModel* pRoutingModel = |
129 |
new ChannelRoutingModel(m_ui.audioRoutingTable); |
130 |
m_ui.audioRoutingTable->setModel(pRoutingModel); |
131 |
ChannelRoutingDelegate* pRoutingDelegate = |
132 |
new ChannelRoutingDelegate(m_ui.audioRoutingTable); |
133 |
m_ui.audioRoutingTable->setItemDelegate(pRoutingDelegate); |
134 |
#if QT_VERSION >= 0x050000 |
135 |
m_ui.audioRoutingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); |
136 |
#else |
137 |
m_ui.audioRoutingTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); |
138 |
#endif |
139 |
// m_ui.audioRoutingTable->verticalHeader()->hide(); |
140 |
|
141 |
QAbstractButton* pApplyButton = |
142 |
m_ui.buttonBox->button(QDialogButtonBox::Apply); |
143 |
pApplyButton->setEnabled(false); |
144 |
pApplyButton->setIcon(QIcon(":/images/formEdit.png")); |
145 |
|
146 |
QAbstractButton* pCancelButton = |
147 |
m_ui.buttonBox->button(QDialogButtonBox::Cancel); |
148 |
pCancelButton->setIcon(QIcon(":/images/formRemove.png")); |
149 |
|
150 |
QAbstractButton* pOkButton = |
151 |
m_ui.buttonBox->button(QDialogButtonBox::Ok); |
152 |
pOkButton->setIcon(QIcon(":/images/formAccept.png")); |
153 |
|
154 |
QAbstractButton* pResetButton = |
155 |
m_ui.buttonBox->button(QDialogButtonBox::Reset); |
156 |
pResetButton->setEnabled(false); |
157 |
pResetButton->setToolTip("Revert all changes."); |
158 |
|
159 |
m_ui.destroyPushButton->setEnabled(false); |
160 |
|
161 |
m_ui.mainParametersGroupBox->setEnabled(false); |
162 |
m_ui.audioRoutingGroupBox->setEnabled(false); |
163 |
|
164 |
for (int i = 0; i < 128; ++i) { |
165 |
m_ui.depthCtrlComboBox->addItem( |
166 |
QString("[") + QString::number(i) + "] " + _midiControllerName(i) |
167 |
); |
168 |
} |
169 |
|
170 |
connect( |
171 |
m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), |
172 |
this, SLOT(onButtonClicked(QAbstractButton*)) |
173 |
); |
174 |
connect( |
175 |
m_ui.createPushButton, SIGNAL(clicked()), |
176 |
this, SLOT(onCreateFxSend()) |
177 |
); |
178 |
connect( |
179 |
m_ui.destroyPushButton, SIGNAL(clicked()), |
180 |
this, SLOT(onDestroyFxSend()) |
181 |
); |
182 |
connect( |
183 |
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
184 |
pApplyButton, SLOT(setEnabled(bool)) |
185 |
); |
186 |
connect( |
187 |
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
188 |
pResetButton, SLOT(setEnabled(bool)) |
189 |
); |
190 |
connect( |
191 |
m_ui.SendsListView, SIGNAL(clicked(const QModelIndex&)), |
192 |
this, SLOT(onFxSendSelection(const QModelIndex&)) |
193 |
); |
194 |
connect( |
195 |
m_ui.depthCtrlComboBox, SIGNAL(activated(int)), |
196 |
this, SLOT(onDepthCtrlChanged(int)) |
197 |
); |
198 |
connect( |
199 |
m_ui.depthSpinBox, SIGNAL(valueChanged(int)), |
200 |
this, SLOT(onCurrentSendDepthChanged(int)) |
201 |
); |
202 |
connect( |
203 |
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
204 |
this, SLOT(updateTableCellRenderers(const QModelIndex&, const QModelIndex&)) |
205 |
); |
206 |
connect( |
207 |
pRoutingModel, SIGNAL(modelReset()), |
208 |
this, SLOT(updateTableCellRenderers()) |
209 |
); |
210 |
connect( |
211 |
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
212 |
this, SLOT(onRoutingTableChanged()) |
213 |
); |
214 |
} |
215 |
|
216 |
ChannelFxForm::~ChannelFxForm() { |
217 |
if (m_pAudioDevice) delete m_pAudioDevice; |
218 |
} |
219 |
|
220 |
void ChannelFxForm::onButtonClicked(QAbstractButton* button) { |
221 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
222 |
switch (m_ui.buttonBox->buttonRole(button)) { |
223 |
case QDialogButtonBox::AcceptRole: |
224 |
case QDialogButtonBox::ApplyRole: |
225 |
pModel->applyToSampler(); |
226 |
// force a refresh of the parameter control elements |
227 |
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
228 |
break; |
229 |
case QDialogButtonBox::ResetRole: |
230 |
pModel->cleanRefresh(); |
231 |
// force a refresh of the parameter control elements |
232 |
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
233 |
break; |
234 |
default: // to avoid gcc warnings |
235 |
break; |
236 |
} |
237 |
} |
238 |
|
239 |
void ChannelFxForm::onFxSendSelection(const QModelIndex& index) { |
240 |
const bool bValid = index.isValid(); |
241 |
m_ui.destroyPushButton->setEnabled(bValid); |
242 |
m_ui.mainParametersGroupBox->setEnabled(bValid); |
243 |
m_ui.audioRoutingGroupBox->setEnabled(bValid); |
244 |
|
245 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
246 |
FxSend* pFxSend = pModel->fxSend(index); |
247 |
|
248 |
// clear routing model |
249 |
ChannelRoutingModel* pRoutingModel = |
250 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
251 |
pRoutingModel->refresh(NULL, ChannelRoutingMap()); |
252 |
pRoutingModel->routingMap().clear(); // Reset routing change map. |
253 |
if (m_pAudioDevice) { |
254 |
delete m_pAudioDevice; |
255 |
m_pAudioDevice = NULL; |
256 |
} |
257 |
|
258 |
if (!pFxSend) return; |
259 |
|
260 |
// update routing model |
261 |
if (m_pSamplerChannel->audioDevice() >= 0) { |
262 |
m_pAudioDevice = |
263 |
new Device(Device::Audio, m_pSamplerChannel->audioDevice()); |
264 |
pRoutingModel->refresh(m_pAudioDevice, pFxSend->audioRouting()); |
265 |
} |
266 |
|
267 |
m_ui.depthCtrlComboBox->setCurrentIndex(pFxSend->sendDepthMidiCtrl()); |
268 |
m_ui.depthSpinBox->setValue( |
269 |
int(::round(pFxSend->currentDepth() * 100.0)) |
270 |
); |
271 |
} |
272 |
|
273 |
void ChannelFxForm::onCreateFxSend() { |
274 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
275 |
pModel->addFxSend(); |
276 |
} |
277 |
|
278 |
void ChannelFxForm::onDestroyFxSend() { |
279 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
280 |
pModel->removeFxSend(m_ui.SendsListView->currentIndex()); |
281 |
} |
282 |
|
283 |
void ChannelFxForm::onDepthCtrlChanged(int iMidiCtrl) { |
284 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
285 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
286 |
FxSend* pFxSend = pModel->fxSend(index); |
287 |
if (!pFxSend) return; |
288 |
|
289 |
pFxSend->setSendDepthMidiCtrl(iMidiCtrl); |
290 |
pModel->onExternalModifiication(index); |
291 |
} |
292 |
|
293 |
void ChannelFxForm::onCurrentSendDepthChanged(int depthPercent) { |
294 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
295 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
296 |
FxSend* pFxSend = pModel->fxSend(index); |
297 |
if (!pFxSend) return; |
298 |
|
299 |
if (depthPercent == int( ::round(pFxSend->currentDepth() * 100.0) )) |
300 |
return; // nothing changed actually |
301 |
|
302 |
pFxSend->setCurrentDepth(double(depthPercent) / 100.0); |
303 |
pModel->onExternalModifiication(index); |
304 |
} |
305 |
|
306 |
void ChannelFxForm::onRoutingTableChanged() { |
307 |
ChannelRoutingModel* pRoutingModel = |
308 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
309 |
if (pRoutingModel->routingMap().size() <= 0) |
310 |
return; // no changes |
311 |
|
312 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
313 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
314 |
FxSend* pFxSend = pModel->fxSend(index); |
315 |
if (!pFxSend) { |
316 |
pRoutingModel->routingMap().clear(); // reset routing change map |
317 |
return; |
318 |
} |
319 |
|
320 |
ChannelRoutingMap routingMap = pRoutingModel->routingMap(); |
321 |
for ( |
322 |
ChannelRoutingMap::iterator iter = routingMap.begin(); |
323 |
iter != routingMap.end(); ++iter |
324 |
) pFxSend->setAudioChannel(iter.key(), iter.value()); |
325 |
|
326 |
pRoutingModel->routingMap().clear(); // reset routing change map |
327 |
|
328 |
pModel->onExternalModifiication(index); |
329 |
} |
330 |
|
331 |
void ChannelFxForm::updateTableCellRenderers() { |
332 |
ChannelRoutingModel* pRoutingModel = |
333 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
334 |
const int rows = pRoutingModel->rowCount(); |
335 |
const int cols = pRoutingModel->columnCount(); |
336 |
updateTableCellRenderers( |
337 |
pRoutingModel->index(0, 0), |
338 |
pRoutingModel->index(rows - 1, cols - 1) |
339 |
); |
340 |
} |
341 |
|
342 |
void ChannelFxForm::updateTableCellRenderers ( |
343 |
const QModelIndex& topLeft, const QModelIndex& bottomRight ) |
344 |
{ |
345 |
ChannelRoutingModel* pRoutingModel = |
346 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
347 |
for (int r = topLeft.row(); r <= bottomRight.row(); r++) { |
348 |
for (int c = topLeft.column(); c <= bottomRight.column(); c++) { |
349 |
const QModelIndex index = pRoutingModel->index(r, c); |
350 |
m_ui.audioRoutingTable->openPersistentEditor(index); |
351 |
} |
352 |
} |
353 |
} |
354 |
|
355 |
} // namespace QSampler |
356 |
|
357 |
// end of qsamplerChannelFxForm.cpp |