1 |
schoenebeck |
1667 |
// qsamplerChannelFxForm.cpp |
2 |
|
|
// |
3 |
|
|
/**************************************************************************** |
4 |
capela |
2459 |
Copyright (C) 2010-2013, rncbc aka Rui Nuno Capela. All rights reserved. |
5 |
schoenebeck |
1667 |
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 |
capela |
2459 |
You should have received a copy of the GNU General Public License along |
18 |
|
|
with this program; if not, write to the Free Software Foundation, Inc., |
19 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20 |
schoenebeck |
1667 |
|
21 |
|
|
*****************************************************************************/ |
22 |
|
|
|
23 |
|
|
#include "qsamplerAbout.h" |
24 |
|
|
#include "qsamplerChannelFxForm.h" |
25 |
|
|
#include "qsamplerFxSendsModel.h" |
26 |
|
|
|
27 |
schoenebeck |
1668 |
// let's not reinvent the wheel for audio routing |
28 |
|
|
#include "qsamplerChannel.h" |
29 |
|
|
|
30 |
|
|
#include <math.h> |
31 |
|
|
|
32 |
schoenebeck |
1667 |
#include <QAbstractButton> |
33 |
schoenebeck |
1668 |
#include <QLineEdit> |
34 |
|
|
#include <QHeaderView> |
35 |
|
|
#include <QMap> |
36 |
schoenebeck |
1667 |
|
37 |
schoenebeck |
1668 |
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 |
schoenebeck |
1667 |
namespace QSampler { |
107 |
|
|
|
108 |
|
|
ChannelFxForm::ChannelFxForm ( |
109 |
schoenebeck |
1668 |
Channel* pSamplerChannel, QWidget* pParent, Qt::WindowFlags wflags ) |
110 |
schoenebeck |
1667 |
: QDialog(pParent, wflags) |
111 |
|
|
{ |
112 |
|
|
m_ui.setupUi(this); |
113 |
|
|
|
114 |
schoenebeck |
1668 |
m_pSamplerChannel = pSamplerChannel; |
115 |
schoenebeck |
1667 |
|
116 |
schoenebeck |
1668 |
m_pAudioDevice = NULL; |
117 |
|
|
|
118 |
schoenebeck |
1667 |
FxSendsModel* pModel = |
119 |
schoenebeck |
1668 |
new FxSendsModel(m_pSamplerChannel->channelID(), m_ui.SendsListView); |
120 |
schoenebeck |
1667 |
m_ui.SendsListView->setModel(pModel); |
121 |
capela |
1706 |
m_ui.SendsListView->setSelectionRectVisible(true); |
122 |
schoenebeck |
1667 |
|
123 |
schoenebeck |
1668 |
const int iRowHeight = m_ui.audioRoutingTable->fontMetrics().height() + 4; |
124 |
|
|
m_ui.audioRoutingTable->verticalHeader()->setDefaultSectionSize(iRowHeight); |
125 |
|
|
m_ui.audioRoutingTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); |
126 |
|
|
ChannelRoutingModel* pRoutingModel = |
127 |
|
|
new ChannelRoutingModel(m_ui.audioRoutingTable); |
128 |
|
|
m_ui.audioRoutingTable->setModel(pRoutingModel); |
129 |
|
|
ChannelRoutingDelegate* pRoutingDelegate = |
130 |
|
|
new ChannelRoutingDelegate(m_ui.audioRoutingTable); |
131 |
|
|
m_ui.audioRoutingTable->setItemDelegate(pRoutingDelegate); |
132 |
capela |
2388 |
#if QT_VERSION >= 0x050000 |
133 |
|
|
m_ui.audioRoutingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); |
134 |
|
|
#else |
135 |
schoenebeck |
1668 |
m_ui.audioRoutingTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); |
136 |
capela |
2387 |
#endif |
137 |
schoenebeck |
1668 |
// m_ui.audioRoutingTable->verticalHeader()->hide(); |
138 |
|
|
|
139 |
schoenebeck |
1667 |
QAbstractButton* pApplyButton = |
140 |
|
|
m_ui.buttonBox->button(QDialogButtonBox::Apply); |
141 |
|
|
pApplyButton->setEnabled(false); |
142 |
capela |
2074 |
pApplyButton->setIcon(QIcon(":/images/formEdit.png")); |
143 |
schoenebeck |
1667 |
|
144 |
|
|
QAbstractButton* pCancelButton = |
145 |
|
|
m_ui.buttonBox->button(QDialogButtonBox::Cancel); |
146 |
capela |
2074 |
pCancelButton->setIcon(QIcon(":/images/formRemove.png")); |
147 |
schoenebeck |
1667 |
|
148 |
|
|
QAbstractButton* pOkButton = |
149 |
|
|
m_ui.buttonBox->button(QDialogButtonBox::Ok); |
150 |
capela |
2074 |
pOkButton->setIcon(QIcon(":/images/formAccept.png")); |
151 |
schoenebeck |
1667 |
|
152 |
|
|
QAbstractButton* pResetButton = |
153 |
|
|
m_ui.buttonBox->button(QDialogButtonBox::Reset); |
154 |
|
|
pResetButton->setEnabled(false); |
155 |
schoenebeck |
1668 |
pResetButton->setToolTip("Revert all changes."); |
156 |
schoenebeck |
1667 |
|
157 |
|
|
m_ui.destroyPushButton->setEnabled(false); |
158 |
|
|
|
159 |
schoenebeck |
1668 |
m_ui.mainParametersGroupBox->setEnabled(false); |
160 |
|
|
m_ui.audioRoutingGroupBox->setEnabled(false); |
161 |
|
|
|
162 |
|
|
for (int i = 0; i < 128; ++i) { |
163 |
|
|
m_ui.depthCtrlComboBox->addItem( |
164 |
|
|
QString("[") + QString::number(i) + "] " + _midiControllerName(i) |
165 |
|
|
); |
166 |
|
|
} |
167 |
|
|
|
168 |
schoenebeck |
1667 |
connect( |
169 |
|
|
m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), |
170 |
|
|
this, SLOT(onButtonClicked(QAbstractButton*)) |
171 |
|
|
); |
172 |
|
|
connect( |
173 |
|
|
m_ui.createPushButton, SIGNAL(clicked()), |
174 |
|
|
this, SLOT(onCreateFxSend()) |
175 |
|
|
); |
176 |
|
|
connect( |
177 |
|
|
m_ui.destroyPushButton, SIGNAL(clicked()), |
178 |
|
|
this, SLOT(onDestroyFxSend()) |
179 |
|
|
); |
180 |
|
|
connect( |
181 |
|
|
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
182 |
|
|
pApplyButton, SLOT(setEnabled(bool)) |
183 |
|
|
); |
184 |
|
|
connect( |
185 |
|
|
pModel, SIGNAL(fxSendsDirtyChanged(bool)), |
186 |
|
|
pResetButton, SLOT(setEnabled(bool)) |
187 |
|
|
); |
188 |
|
|
connect( |
189 |
|
|
m_ui.SendsListView, SIGNAL(clicked(const QModelIndex&)), |
190 |
|
|
this, SLOT(onFxSendSelection(const QModelIndex&)) |
191 |
|
|
); |
192 |
schoenebeck |
1668 |
connect( |
193 |
|
|
m_ui.depthCtrlComboBox, SIGNAL(activated(int)), |
194 |
|
|
this, SLOT(onDepthCtrlChanged(int)) |
195 |
|
|
); |
196 |
|
|
connect( |
197 |
|
|
m_ui.depthSpinBox, SIGNAL(valueChanged(int)), |
198 |
|
|
this, SLOT(onCurrentSendDepthChanged(int)) |
199 |
|
|
); |
200 |
|
|
connect( |
201 |
|
|
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
202 |
|
|
this, SLOT(updateTableCellRenderers(const QModelIndex&, const QModelIndex&)) |
203 |
|
|
); |
204 |
|
|
connect( |
205 |
|
|
pRoutingModel, SIGNAL(modelReset()), |
206 |
|
|
this, SLOT(updateTableCellRenderers()) |
207 |
|
|
); |
208 |
|
|
connect( |
209 |
|
|
pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |
210 |
|
|
this, SLOT(onRoutingTableChanged()) |
211 |
|
|
); |
212 |
schoenebeck |
1667 |
} |
213 |
|
|
|
214 |
|
|
ChannelFxForm::~ChannelFxForm() { |
215 |
schoenebeck |
1668 |
if (m_pAudioDevice) delete m_pAudioDevice; |
216 |
schoenebeck |
1667 |
} |
217 |
|
|
|
218 |
|
|
void ChannelFxForm::onButtonClicked(QAbstractButton* button) { |
219 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
220 |
|
|
switch (m_ui.buttonBox->buttonRole(button)) { |
221 |
|
|
case QDialogButtonBox::AcceptRole: |
222 |
|
|
case QDialogButtonBox::ApplyRole: |
223 |
|
|
pModel->applyToSampler(); |
224 |
schoenebeck |
1668 |
// force a refresh of the parameter control elements |
225 |
|
|
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
226 |
schoenebeck |
1667 |
break; |
227 |
|
|
case QDialogButtonBox::ResetRole: |
228 |
|
|
pModel->cleanRefresh(); |
229 |
schoenebeck |
1668 |
// force a refresh of the parameter control elements |
230 |
|
|
onFxSendSelection(m_ui.SendsListView->currentIndex()); |
231 |
schoenebeck |
1667 |
break; |
232 |
|
|
default: // to avoid gcc warnings |
233 |
|
|
break; |
234 |
|
|
} |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
void ChannelFxForm::onFxSendSelection(const QModelIndex& index) { |
238 |
schoenebeck |
1668 |
const bool bValid = index.isValid(); |
239 |
|
|
m_ui.destroyPushButton->setEnabled(bValid); |
240 |
|
|
m_ui.mainParametersGroupBox->setEnabled(bValid); |
241 |
|
|
m_ui.audioRoutingGroupBox->setEnabled(bValid); |
242 |
|
|
|
243 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
244 |
|
|
FxSend* pFxSend = pModel->fxSend(index); |
245 |
|
|
|
246 |
|
|
// clear routing model |
247 |
|
|
ChannelRoutingModel* pRoutingModel = |
248 |
|
|
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
249 |
|
|
pRoutingModel->refresh(NULL, ChannelRoutingMap()); |
250 |
|
|
pRoutingModel->routingMap().clear(); // Reset routing change map. |
251 |
|
|
if (m_pAudioDevice) { |
252 |
|
|
delete m_pAudioDevice; |
253 |
|
|
m_pAudioDevice = NULL; |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
if (!pFxSend) return; |
257 |
|
|
|
258 |
|
|
// update routing model |
259 |
|
|
if (m_pSamplerChannel->audioDevice() >= 0) { |
260 |
|
|
m_pAudioDevice = |
261 |
|
|
new Device(Device::Audio, m_pSamplerChannel->audioDevice()); |
262 |
|
|
pRoutingModel->refresh(m_pAudioDevice, pFxSend->audioRouting()); |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
m_ui.depthCtrlComboBox->setCurrentIndex(pFxSend->sendDepthMidiCtrl()); |
266 |
|
|
m_ui.depthSpinBox->setValue( |
267 |
capela |
1671 |
int(::round(pFxSend->currentDepth() * 100.0)) |
268 |
schoenebeck |
1668 |
); |
269 |
schoenebeck |
1667 |
} |
270 |
|
|
|
271 |
|
|
void ChannelFxForm::onCreateFxSend() { |
272 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
273 |
|
|
pModel->addFxSend(); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
void ChannelFxForm::onDestroyFxSend() { |
277 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
278 |
|
|
pModel->removeFxSend(m_ui.SendsListView->currentIndex()); |
279 |
|
|
} |
280 |
|
|
|
281 |
schoenebeck |
1668 |
void ChannelFxForm::onDepthCtrlChanged(int iMidiCtrl) { |
282 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
283 |
|
|
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
284 |
|
|
FxSend* pFxSend = pModel->fxSend(index); |
285 |
|
|
if (!pFxSend) return; |
286 |
|
|
|
287 |
|
|
pFxSend->setSendDepthMidiCtrl(iMidiCtrl); |
288 |
|
|
pModel->onExternalModifiication(index); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
void ChannelFxForm::onCurrentSendDepthChanged(int depthPercent) { |
292 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
293 |
|
|
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
294 |
|
|
FxSend* pFxSend = pModel->fxSend(index); |
295 |
|
|
if (!pFxSend) return; |
296 |
|
|
|
297 |
|
|
if (depthPercent == int( ::round(pFxSend->currentDepth() * 100.0) )) |
298 |
|
|
return; // nothing changed actually |
299 |
|
|
|
300 |
|
|
pFxSend->setCurrentDepth(double(depthPercent) / 100.0); |
301 |
|
|
pModel->onExternalModifiication(index); |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
void ChannelFxForm::onRoutingTableChanged() { |
305 |
|
|
ChannelRoutingModel* pRoutingModel = |
306 |
|
|
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
307 |
|
|
if (pRoutingModel->routingMap().size() <= 0) |
308 |
|
|
return; // no changes |
309 |
|
|
|
310 |
|
|
FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); |
311 |
|
|
const QModelIndex index = m_ui.SendsListView->currentIndex(); |
312 |
|
|
FxSend* pFxSend = pModel->fxSend(index); |
313 |
|
|
if (!pFxSend) { |
314 |
|
|
pRoutingModel->routingMap().clear(); // reset routing change map |
315 |
|
|
return; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
ChannelRoutingMap routingMap = pRoutingModel->routingMap(); |
319 |
|
|
for ( |
320 |
|
|
ChannelRoutingMap::iterator iter = routingMap.begin(); |
321 |
|
|
iter != routingMap.end(); ++iter |
322 |
|
|
) pFxSend->setAudioChannel(iter.key(), iter.value()); |
323 |
|
|
|
324 |
|
|
pRoutingModel->routingMap().clear(); // reset routing change map |
325 |
|
|
|
326 |
|
|
pModel->onExternalModifiication(index); |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
void ChannelFxForm::updateTableCellRenderers() { |
330 |
|
|
ChannelRoutingModel* pRoutingModel = |
331 |
|
|
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
332 |
|
|
const int rows = pRoutingModel->rowCount(); |
333 |
|
|
const int cols = pRoutingModel->columnCount(); |
334 |
|
|
updateTableCellRenderers( |
335 |
|
|
pRoutingModel->index(0, 0), |
336 |
|
|
pRoutingModel->index(rows - 1, cols - 1) |
337 |
|
|
); |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
void ChannelFxForm::updateTableCellRenderers ( |
341 |
|
|
const QModelIndex& topLeft, const QModelIndex& bottomRight ) |
342 |
|
|
{ |
343 |
|
|
ChannelRoutingModel* pRoutingModel = |
344 |
|
|
(ChannelRoutingModel*) m_ui.audioRoutingTable->model(); |
345 |
|
|
for (int r = topLeft.row(); r <= bottomRight.row(); r++) { |
346 |
|
|
for (int c = topLeft.column(); c <= bottomRight.column(); c++) { |
347 |
|
|
const QModelIndex index = pRoutingModel->index(r, c); |
348 |
|
|
m_ui.audioRoutingTable->openPersistentEditor(index); |
349 |
|
|
} |
350 |
|
|
} |
351 |
|
|
} |
352 |
|
|
|
353 |
schoenebeck |
1667 |
} // namespace QSampler |
354 |
|
|
|
355 |
|
|
// end of qsamplerChannelFxForm.cpp |