1 /*-
2 * Copyright (c) 2017-2019 Hans Petter Selasky. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include "midipp_gridlayout.h"
27 #include "midipp_buttonmap.h"
28 #include "midipp_spinbox.h"
29 #include "midipp_chansel.h"
30 #include "midipp_checkbox.h"
31 #include "midipp_groupbox.h"
32 #include "midipp_mainwindow.h"
33 #include "midipp_instrument.h"
34 #include "midipp_devsel.h"
35
MppInstrumentTab(MppMainWindow * _mw)36 MppInstrumentTab :: MppInstrumentTab(MppMainWindow *_mw)
37 {
38 mw = _mw;
39
40 gl = new MppGridLayout();
41
42 but_instr_program = new QPushButton(tr(":: Program one ::\nchannel"));
43 but_instr_program_all = new QPushButton(tr(":: Program all ::\nchannels"));
44 but_instr_reset = new QPushButton(tr("Reset"));
45 but_instr_rem = new QPushButton(tr("Delete muted"));
46 but_instr_mute_all = new QPushButton(tr("Mute all"));
47 but_instr_unmute_all = new QPushButton(tr("Unmute all"));
48
49 spn_instr_curr_dev = new MppDevSel(-1, MPP_DEV_ALL);
50
51 spn_instr_curr_chan = new MppChanSel(mw, 0, 0);
52 connect(spn_instr_curr_chan, SIGNAL(valueChanged(int)), this, SLOT(handle_instr_channel_changed(int)));
53
54 spn_instr_curr_bank = new QSpinBox();
55 spn_instr_curr_bank->setRange(0, 16383);
56 spn_instr_curr_bank->setValue(0);
57
58 spn_instr_curr_prog = new QSpinBox();
59 spn_instr_curr_prog->setRange(0, 127);
60 spn_instr_curr_prog->setValue(0);
61
62 gb_instr_select = new MppGroupBox(tr("Synth and Recording Instrument Selector"));
63 gb_instr_select->addWidget(new QLabel(tr("Device")), 0, 0, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
64 gb_instr_select->addWidget(new QLabel(tr("Channel")), 0, 1, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
65 gb_instr_select->addWidget(new QLabel(tr("Bank")), 0, 2, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
66 gb_instr_select->addWidget(new QLabel(tr("Program")), 0, 3, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
67 gb_instr_select->addWidget(spn_instr_curr_dev, 1, 0, 1, 1);
68 gb_instr_select->addWidget(spn_instr_curr_chan, 1, 1, 1, 1);
69 gb_instr_select->addWidget(spn_instr_curr_bank, 1, 2, 1, 1);
70 gb_instr_select->addWidget(spn_instr_curr_prog, 1, 3, 1, 1);
71 gb_instr_select->addWidget(but_instr_program, 1, 4, 1, 1);
72 gb_instr_select->addWidget(but_instr_program_all, 1, 5, 1, 1);
73
74 gb_instr_table = new MppGroupBox(tr("Synth and Recording Instrument Table"));
75
76 gb_instr_table->addWidget(new QLabel(tr("Bank")), 0, 1, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
77 gb_instr_table->addWidget(new QLabel(tr("Program")), 0, 2, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
78 gb_instr_table->addWidget(new QLabel(tr("Mute")), 0, 3, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
79
80 gb_instr_table->addWidget(new QLabel(tr("Bank")), 0, 5, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
81 gb_instr_table->addWidget(new QLabel(tr("Program")), 0, 6, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
82 gb_instr_table->addWidget(new QLabel(tr("Mute")), 0, 7, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
83
84 for (unsigned n = 0; n != 16; n++) {
85 int y_off = (n & 8) ? 4 : 0;
86
87 spn_instr_bank[n] = new QSpinBox();
88 spn_instr_bank[n]->setRange(0, 16383);
89 connect(spn_instr_bank[n], SIGNAL(valueChanged(int)), this, SLOT(handle_instr_changed(int)));
90
91 spn_instr_prog[n] = new QSpinBox();
92 spn_instr_prog[n]->setRange(0, 127);
93 connect(spn_instr_prog[n], SIGNAL(valueChanged(int)), this, SLOT(handle_instr_changed(int)));
94
95 cbx_instr_mute[n] = new MppCheckBox(n);
96 connect(cbx_instr_mute[n], SIGNAL(stateChanged(int,int)), this, SLOT(handle_instr_changed(int)));
97
98 gb_instr_table->addWidget(new QLabel(MppChanName(n)), (n & 7) + 1, 0 + y_off, 1, 1, Qt::AlignVCenter|Qt::AlignRight);
99 gb_instr_table->addWidget(spn_instr_bank[n], (n & 7) + 1, 1 + y_off, 1, 1, Qt::AlignVCenter|Qt::AlignRight);
100 gb_instr_table->addWidget(spn_instr_prog[n], (n & 7) + 1, 2 + y_off, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
101 gb_instr_table->addWidget(cbx_instr_mute[n], (n & 7) + 1, 3 + y_off, 1, 1, Qt::AlignVCenter|Qt::AlignHCenter);
102 }
103
104 gl->addWidget(gb_instr_select, 0, 0, 1, 8);
105 gl->addWidget(gb_instr_table, 1, 0, 1, 8);
106
107 gl->setRowStretch(3, 1);
108 gl->setColumnStretch(8, 1);
109
110 gl->addWidget(but_instr_mute_all, 4, 0, 1, 2);
111 gl->addWidget(but_instr_unmute_all, 4, 2, 1, 2);
112 gl->addWidget(but_instr_rem, 4, 4, 1, 2);
113 gl->addWidget(but_instr_reset, 4, 6, 1, 2);
114
115 connect(but_instr_rem, SIGNAL(released()), this, SLOT(handle_instr_rem()));
116 connect(but_instr_program, SIGNAL(released()), this, SLOT(handle_instr_program()));
117 connect(but_instr_program_all, SIGNAL(released()), this, SLOT(handle_instr_program_all()));
118 connect(but_instr_reset, SIGNAL(released()), this, SLOT(handle_instr_reset()));
119 connect(but_instr_mute_all, SIGNAL(released()), this, SLOT(handle_instr_mute_all()));
120 connect(but_instr_unmute_all, SIGNAL(released()), this, SLOT(handle_instr_unmute_all()));
121 }
122
~MppInstrumentTab()123 MppInstrumentTab :: ~MppInstrumentTab()
124 {
125 mw->atomic_lock();
126 mw->tab_instrument = 0;
127 mw->atomic_unlock();
128 }
129
130 void
handle_instr_channel_changed(int chan)131 MppInstrumentTab :: handle_instr_channel_changed(int chan)
132 {
133 MppInstrumentTab * const ni = 0;
134
135 if (this == ni)
136 return;
137
138 int temp[2];
139
140 temp[0] = spn_instr_bank[chan]->value();
141 temp[1] = spn_instr_prog[chan]->value();
142
143 spn_instr_curr_bank->setValue(temp[0]);
144 spn_instr_curr_prog->setValue(temp[1]);
145
146 MPP_BLOCKED(spn_instr_curr_chan,setValue(chan));
147 }
148
149 void
handle_instr_program()150 MppInstrumentTab :: handle_instr_program()
151 {
152 int chan = spn_instr_curr_chan->value();
153 int bank = spn_instr_curr_bank->value();
154 int prog = spn_instr_curr_prog->value();
155
156 MPP_BLOCKED(spn_instr_bank[chan],setValue(bank));
157 MPP_BLOCKED(spn_instr_prog[chan],setValue(prog));
158 MPP_BLOCKED(cbx_instr_mute[chan],setChecked(0));
159
160 mw->atomic_lock();
161 mw->instr[chan].updated |= 1;
162 mw->atomic_unlock();
163
164 handle_instr_changed(0);
165 }
166
167 void
handle_instr_program_all()168 MppInstrumentTab :: handle_instr_program_all()
169 {
170
171 mw->atomic_lock();
172 for (unsigned int x = 0; x != 16; x++)
173 mw->instr[x].updated |= 1;
174 mw->atomic_unlock();
175
176 handle_instr_changed(0);
177 }
178
179 void
handle_instr_changed(int dummy)180 MppInstrumentTab :: handle_instr_changed(int dummy)
181 {
182 MppInstrumentTab * const ni = 0;
183
184 if (this == ni)
185 return;
186
187 struct mid_data *d = &mw->mid_data;
188 int temp[3];
189 int dev;
190 uint8_t curr_chan;
191 uint8_t x;
192 uint8_t y;
193 uint8_t z;
194 uint8_t update_curr;
195 uint8_t trig;
196
197 curr_chan = spn_instr_curr_chan->value();
198
199 for (x = 0; x != 16; x++) {
200
201 mw->atomic_lock();
202
203 temp[0] = spn_instr_bank[x]->value();
204 temp[1] = spn_instr_prog[x]->value();
205 temp[2] = cbx_instr_mute[x]->isChecked();
206
207 update_curr = 0;
208
209 if (mw->instr[x].bank != temp[0]) {
210 if (mw->instr[x].updated & 2) {
211 temp[0] = mw->instr[x].bank;
212 } else {
213 mw->instr[x].bank = temp[0];
214 mw->instr[x].updated |= 1;
215 }
216 update_curr = 1;
217 }
218 if (mw->instr[x].prog != temp[1]) {
219 if (mw->instr[x].updated & 2) {
220 temp[1] = mw->instr[x].prog;
221 } else {
222 mw->instr[x].prog = temp[1];
223 mw->instr[x].updated |= 1;
224 }
225 update_curr = 1;
226 }
227 if (mw->instr[x].muted != temp[2]) {
228 if (mw->instr[x].updated & 2) {
229 temp[2] = mw->instr[x].muted;
230 } else {
231 mw->instr[x].muted = temp[2];
232 mw->instr[x].updated |= 1;
233 }
234 update_curr = 1;
235 }
236 mw->atomic_unlock();
237
238 if (update_curr) {
239 MPP_BLOCKED(spn_instr_bank[x],setValue(temp[0]));
240 MPP_BLOCKED(spn_instr_prog[x],setValue(temp[1]));
241 MPP_BLOCKED(cbx_instr_mute[x],setChecked(temp[2]));
242
243 mw->atomic_lock();
244 update_curr = (curr_chan == x);
245 mw->atomic_unlock();
246 }
247
248 if (update_curr) {
249 MPP_BLOCKED(spn_instr_curr_chan,setValue(x));
250 MPP_BLOCKED(spn_instr_curr_bank,setValue(temp[0]));
251 MPP_BLOCKED(spn_instr_curr_prog,setValue(temp[1]));
252 }
253 }
254
255 /* Get device number */
256
257 dev = spn_instr_curr_dev->value();
258
259 /* Do the real programming */
260
261 mw->atomic_lock();
262 trig = mw->midiTriggered;
263 mw->midiTriggered = 1;
264
265 for (z = x = 0; x != 16; x++) {
266 if (mw->instr[x].updated == 0)
267 continue;
268
269 mw->instr[x].updated = 0;
270 for (y = 0; y != MPP_MAGIC_DEVNO; y++) {
271 if (dev != -1 && dev != y)
272 continue;
273 if (mw->check_play(0, x, 0, y) == 0)
274 continue;
275 mid_delay(d, z);
276 mid_set_bank_program(d, x,
277 mw->instr[x].bank,
278 mw->instr[x].prog);
279
280 /* put some delay between the commands */
281 z++;
282 }
283 }
284
285 mw->midiTriggered = trig;
286 mw->atomic_unlock();
287 }
288
289 void
handle_instr_reset()290 MppInstrumentTab :: handle_instr_reset()
291 {
292 MppInstrumentTab * const ni = 0;
293
294 if (this == ni)
295 return;
296
297 for (uint8_t x = 0; x != 16; x++) {
298 MPP_BLOCKED(spn_instr_bank[x],setValue(0));
299 MPP_BLOCKED(spn_instr_prog[x],setValue(0));
300 MPP_BLOCKED(cbx_instr_mute[x],setChecked(0));
301
302 mw->atomic_lock();
303 mw->instr[x].updated = 1;
304 mw->atomic_unlock();
305 }
306
307 MPP_BLOCKED(spn_instr_curr_chan,setValue(0));
308 MPP_BLOCKED(spn_instr_curr_bank,setValue(0));
309 MPP_BLOCKED(spn_instr_curr_prog,setValue(0));
310
311 handle_instr_changed(0);
312 }
313
314 void
handle_instr_mute_all()315 MppInstrumentTab :: handle_instr_mute_all()
316 {
317
318 for (uint8_t n = 0; n != 16; n++)
319 MPP_BLOCKED(cbx_instr_mute[n],setChecked(1));
320
321 handle_instr_changed(0);
322 }
323
324 void
handle_instr_unmute_all()325 MppInstrumentTab :: handle_instr_unmute_all()
326 {
327
328 for (uint8_t n = 0; n != 16; n++)
329 MPP_BLOCKED(cbx_instr_mute[n],setChecked(0));
330
331 handle_instr_changed(0);
332 }
333
334 void
handle_instr_rem()335 MppInstrumentTab :: handle_instr_rem()
336 {
337 struct umidi20_event *event;
338 struct umidi20_event *event_next;
339
340 handle_instr_changed(0);
341
342 mw->atomic_lock();
343
344 for (unsigned int x = 0; x != MPP_MAX_TRACKS; x++) {
345 if (mw->track[x] == 0)
346 continue;
347
348 UMIDI20_QUEUE_FOREACH_SAFE(event, &mw->track[x]->queue, event_next) {
349 if (umidi20_event_get_what(event) & UMIDI20_WHAT_CHANNEL) {
350 uint8_t chan = umidi20_event_get_channel(event) & 0xF;
351 if (mw->instr[chan].muted) {
352 UMIDI20_IF_REMOVE(&mw->track[x]->queue, event);
353 umidi20_event_free(event);
354 }
355 }
356 }
357 }
358
359 mw->atomic_unlock();
360 }
361