1 |
// qsamplerChannelFxForm.cpp |
2 |
// |
3 |
/**************************************************************************** |
4 |
Copyright (C) 2008, Christian Schoenebeck |
5 |
Copyright (C) 2010, rncbc aka Rui Nuno Capela. All rights reserved. |
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 |
m_ui.audioRoutingTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); |
135 |
// m_ui.audioRoutingTable->verticalHeader()->hide(); |
136 |
|
137 |
QAbstractButton* pApplyButton = |
138 |
m_ui.buttonBox->button(QDialogButtonBox::Apply); |
139 |
pApplyButton->setEnabled(false); |
140 |
pApplyButton->setIcon(QIcon(":/images/formEdit.png")); |
141 |
|
142 |
QAbstractButton* pCancelButton = |
143 |
m_ui.buttonBox->button(QDialogButtonBox::Cancel); |
144 |
pCancelButton->setIcon(QIcon(":/images/formRemove.png")); |
145 |
|
146 |
QAbstractButton* pOkButton = |
147 |
m_ui.buttonBox->button(QDialogButtonBox::Ok); |
148 |
pOkButton->setIcon(QIcon(":/images/formAccept.png")); |
149 |
|
150 |
QAbstractButton* pResetButton = |
151 |
m_ui.buttonBox->button(QDialogButtonBox::Reset); |
152 |
pResetButton->setEnabled(false); |
153 |
pResetButton->setToolTip("Revert all changes."); |
154 |
|
155 |
m_ui.destroyPushButton->setEnabled(false); |
156 |
|
157 |
m_ui.mainParametersGroupBox->setEnabled(false); |
158 |
m_ui.audioRoutingGroupBox->setEnabled(false); |
159 |
|
160 |
for (int i = 0; i < 128; ++i) { |
161 |
m_ui.depthCtrlComboBox->addItem( |
162 |
QString("[") + QString::number(i) + "] " + _midiControllerName(i) |
163 |
); |
164 |
} |
165 |
|
166 |
connect( |
167 |
m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), |
168 |
this, SLOT(onButtonClicked(QAbstractButton*)) |
169 |
); |
170 |
connect( |
171 |
m_ui.createPushButton, SIGNAL(clicked()), |
172 |
this, SLOT(onCreateFxSend()) |
173 |
); |
174 |
connect( |
175 |
m_ui.destroyPushButton, SIGNAL(clicked()), |
176 |
this, SLOT(onDestroyFxSend()) |
177 |
); |
178 |
connect( |
179 |
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
180 |
pApplyButton, SLOT(setEnabled(bool)) |
181 |
); |
182 |
connect( |
183 |
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
184 |
pResetButton, SLOT(setEnabled(bool)) |
185 |
); |
186 |
connect( |
187 |
m_ui.SendsListView, SIGNAL(clicked(const QModelIndex&)), |
188 |
this, SLOT(onFxSendSelection(const QModelIndex&)) |
189 |
); |
190 |
connect( |
191 |
m_ui.depthCtrlComboBox, SIGNAL(activated(int)), |
192 |
this, SLOT(onDepthCtrlChanged(int)) |
193 |
); |
194 |
connect( |
195 |
m_ui.depthSpinBox, SIGNAL(valueChanged(int)), |
196 |
this, SLOT(onCurrentSendDepthChanged(int)) |
197 |
); |
198 |
connect( |
199 |
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
200 |
this, SLOT(updateTableCellRenderers(const QModelIndex&, const QModelIndex&)) |
201 |
); |
202 |
connect( |
203 |
pRoutingModel, SIGNAL(modelReset()), |
204 |
this, SLOT(updateTableCellRenderers()) |
205 |
); |
206 |
connect( |
207 |
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
208 |
this, SLOT(onRoutingTableChanged()) |
209 |
); |
210 |
} |
211 |
|
212 |
ChannelFxForm::~ChannelFxForm() { |
213 |
if (m_pAudioDevice) delete m_pAudioDevice; |
214 |
} |
215 |
|
216 |
void ChannelFxForm::onButtonClicked(QAbstractButton* button) { |
217 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
218 |
switch (m_ui.buttonBox->buttonRole(button)) { |
219 |
case QDialogButtonBox::AcceptRole: |
220 |
case QDialogButtonBox::ApplyRole: |
221 |
pModel->applyToSampler(); |
222 |
// force a refresh of the parameter control elements |
223 |
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
224 |
break; |
225 |
case QDialogButtonBox::ResetRole: |
226 |
pModel->cleanRefresh(); |
227 |
// force a refresh of the parameter control elements |
228 |
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
229 |
break; |
230 |
default: // to avoid gcc warnings |
231 |
break; |
232 |
} |
233 |
} |
234 |
|
235 |
void ChannelFxForm::onFxSendSelection(const QModelIndex& index) { |
236 |
const bool bValid = index.isValid(); |
237 |
m_ui.destroyPushButton->setEnabled(bValid); |
238 |
m_ui.mainParametersGroupBox->setEnabled(bValid); |
239 |
m_ui.audioRoutingGroupBox->setEnabled(bValid); |
240 |
|
241 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
242 |
FxSend* pFxSend = pModel->fxSend(index); |
243 |
|
244 |
// clear routing model |
245 |
ChannelRoutingModel* pRoutingModel = |
246 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
247 |
pRoutingModel->refresh(NULL, ChannelRoutingMap()); |
248 |
pRoutingModel->routingMap().clear(); // Reset routing change map. |
249 |
if (m_pAudioDevice) { |
250 |
delete m_pAudioDevice; |
251 |
m_pAudioDevice = NULL; |
252 |
} |
253 |
|
254 |
if (!pFxSend) return; |
255 |
|
256 |
// update routing model |
257 |
if (m_pSamplerChannel->audioDevice() >= 0) { |
258 |
m_pAudioDevice = |
259 |
new Device(Device::Audio, m_pSamplerChannel->audioDevice()); |
260 |
pRoutingModel->refresh(m_pAudioDevice, pFxSend->audioRouting()); |
261 |
} |
262 |
|
263 |
m_ui.depthCtrlComboBox->setCurrentIndex(pFxSend->sendDepthMidiCtrl()); |
264 |
m_ui.depthSpinBox->setValue( |
265 |
int(::round(pFxSend->currentDepth() * 100.0)) |
266 |
); |
267 |
} |
268 |
|
269 |
void ChannelFxForm::onCreateFxSend() { |
270 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
271 |
pModel->addFxSend(); |
272 |
} |
273 |
|
274 |
void ChannelFxForm::onDestroyFxSend() { |
275 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
276 |
pModel->removeFxSend(m_ui.SendsListView->currentIndex()); |
277 |
} |
278 |
|
279 |
void ChannelFxForm::onDepthCtrlChanged(int iMidiCtrl) { |
280 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
281 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
282 |
FxSend* pFxSend = pModel->fxSend(index); |
283 |
if (!pFxSend) return; |
284 |
|
285 |
pFxSend->setSendDepthMidiCtrl(iMidiCtrl); |
286 |
pModel->onExternalModifiication(index); |
287 |
} |
288 |
|
289 |
void ChannelFxForm::onCurrentSendDepthChanged(int depthPercent) { |
290 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
291 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
292 |
FxSend* pFxSend = pModel->fxSend(index); |
293 |
if (!pFxSend) return; |
294 |
|
295 |
if (depthPercent == int( ::round(pFxSend->currentDepth() * 100.0) )) |
296 |
return; // nothing changed actually |
297 |
|
298 |
pFxSend->setCurrentDepth(double(depthPercent) / 100.0); |
299 |
pModel->onExternalModifiication(index); |
300 |
} |
301 |
|
302 |
void ChannelFxForm::onRoutingTableChanged() { |
303 |
ChannelRoutingModel* pRoutingModel = |
304 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
305 |
if (pRoutingModel->routingMap().size() <= 0) |
306 |
return; // no changes |
307 |
|
308 |
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
309 |
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
310 |
FxSend* pFxSend = pModel->fxSend(index); |
311 |
if (!pFxSend) { |
312 |
pRoutingModel->routingMap().clear(); // reset routing change map |
313 |
return; |
314 |
} |
315 |
|
316 |
ChannelRoutingMap routingMap = pRoutingModel->routingMap(); |
317 |
for ( |
318 |
ChannelRoutingMap::iterator iter = routingMap.begin(); |
319 |
iter != routingMap.end(); ++iter |
320 |
) pFxSend->setAudioChannel(iter.key(), iter.value()); |
321 |
|
322 |
pRoutingModel->routingMap().clear(); // reset routing change map |
323 |
|
324 |
pModel->onExternalModifiication(index); |
325 |
} |
326 |
|
327 |
void ChannelFxForm::updateTableCellRenderers() { |
328 |
ChannelRoutingModel* pRoutingModel = |
329 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
330 |
const int rows = pRoutingModel->rowCount(); |
331 |
const int cols = pRoutingModel->columnCount(); |
332 |
updateTableCellRenderers( |
333 |
pRoutingModel->index(0, 0), |
334 |
pRoutingModel->index(rows - 1, cols - 1) |
335 |
); |
336 |
} |
337 |
|
338 |
void ChannelFxForm::updateTableCellRenderers ( |
339 |
const QModelIndex& topLeft, const QModelIndex& bottomRight ) |
340 |
{ |
341 |
ChannelRoutingModel* pRoutingModel = |
342 |
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
343 |
for (int r = topLeft.row(); r <= bottomRight.row(); r++) { |
344 |
for (int c = topLeft.column(); c <= bottomRight.column(); c++) { |
345 |
const QModelIndex index = pRoutingModel->index(r, c); |
346 |
m_ui.audioRoutingTable->openPersistentEditor(index); |
347 |
} |
348 |
} |
349 |
} |
350 |
|
351 |
} // namespace QSampler |
352 |
|
353 |
// end of qsamplerChannelFxForm.cpp |