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