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