1 /*
2  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2011-2015 David Robillard <d@drobilla.net>
4  * Copyright (C) 2011-2017 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (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 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  */
21 
22 #include <gtkmm/stock.h>
23 #include <gtkmm/table.h>
24 
25 #include <boost/algorithm/string.hpp>
26 
27 #include "gtkmm2ext/utils.h"
28 
29 #include "midi++/midnam_patch.h"
30 
31 #include "ardour/beats_samples_converter.h"
32 #include "ardour/instrument_info.h"
33 
34 #include "patch_change_dialog.h"
35 #include "gui_thread.h"
36 
37 #include "pbd/i18n.h"
38 
39 using namespace std;
40 using namespace Gtk;
41 using namespace Gtkmm2ext;
42 
43 /** @param tc If non-0, a time converter for this patch change.  If 0, time control will be desensitized */
PatchChangeDialog(const ARDOUR::BeatsSamplesConverter * tc,ARDOUR::Session * session,Evoral::PatchChange<Temporal::Beats> const & patch,ARDOUR::InstrumentInfo & info,const Gtk::BuiltinStockID & ok,bool allow_delete,bool modal)44 PatchChangeDialog::PatchChangeDialog (
45 	const ARDOUR::BeatsSamplesConverter*        tc,
46 	ARDOUR::Session*                            session,
47 	Evoral::PatchChange<Temporal::Beats> const& patch,
48 	ARDOUR::InstrumentInfo&                     info,
49 	const Gtk::BuiltinStockID&                  ok,
50 	bool                                        allow_delete,
51 	bool                                        modal)
52 	: ArdourDialog (_("Patch Change"), modal)
53 	, _time_converter (tc)
54 	, _info (info)
55 	, _time (X_("patchchangetime"), true, "", true, false)
56 	, _channel (*manage (new Adjustment (1, 1, 16, 1, 4)))
57 	, _program (*manage (new Adjustment (1, 1, 128, 1, 16)))
58 	, _bank_msb (*manage (new Adjustment (0, 0, 127, 1, 16)))
59 	, _bank_lsb (*manage (new Adjustment (0, 0, 127, 1, 16)))
60 	, _ignore_signals (false)
61 	, _keep_open (!modal)
62 {
63 	Table* t = manage (new Table (4, 2));
64 	Label* l;
65 	t->set_spacings (6);
66 	int r = 0;
67 
68 	if (_time_converter) {
69 
70 		l = manage (left_aligned_label (_("Time")));
71 		t->attach (*l, 0, 1, r, r + 1);
72 		t->attach (_time, 1, 2, r, r + 1);
73 		++r;
74 
75 		_time.set_session (session);
76 		_time.set_mode (AudioClock::BBT);
77 		_time.set (_time_converter->to (patch.time ()), true);
78 	}
79 
80 	l = manage (left_aligned_label (_("Patch Bank")));
81 	t->attach (*l, 0, 1, r, r + 1);
82 	t->attach (_bank_combo, 1, 2, r, r + 1);
83 	++r;
84 
85 	_bank_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_combo_changed));
86 
87 	l = manage (left_aligned_label (_("Patch")));
88 	t->attach (*l, 0, 1, r, r + 1);
89 	t->attach (_patch_combo, 1, 2, r, r + 1);
90 	++r;
91 
92 	_patch_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::patch_combo_changed));
93 
94 	l = manage (left_aligned_label (_("Channel")));
95 	t->attach (*l, 0, 1, r, r + 1);
96 	t->attach (_channel, 1, 2, r, r + 1);
97 	++r;
98 
99 	_channel.set_value (patch.channel() + 1);
100 	_channel.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::channel_changed));
101 
102 	l = manage (left_aligned_label (_("Program")));
103 	t->attach (*l, 0, 1, r, r + 1);
104 	t->attach (_program, 1, 2, r, r + 1);
105 	++r;
106 
107 	_program.set_value (patch.program () + 1);
108 	_program.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::program_changed));
109 
110 	l = manage (left_aligned_label (_("Bank MSB")));
111 	t->attach (*l, 0, 1, r, r + 1);
112 	t->attach (_bank_msb, 1, 2, r, r + 1);
113 	++r;
114 
115 	l = manage (left_aligned_label (_("Bank LSB")));
116 	t->attach (*l, 0, 1, r, r + 1);
117 	t->attach (_bank_lsb, 1, 2, r, r + 1);
118 	++r;
119 
120 	assert (patch.bank() != UINT16_MAX);
121 
122 	_bank_msb.set_value ((patch.bank() >> 7));
123 	_bank_msb.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed));
124 	_bank_lsb.set_value ((patch.bank() & 127));
125 	_bank_lsb.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed));
126 
127 	get_vbox()->add (*t);
128 
129 	if (modal) {
130 		add_button (Stock::CANCEL, RESPONSE_CANCEL);
131 	}
132 	add_button (ok, RESPONSE_ACCEPT);
133 	if (allow_delete) {
134 		add_button (Gtk::StockID(GTK_STOCK_DELETE), RESPONSE_REJECT);
135 	}
136 	set_default_response (RESPONSE_ACCEPT);
137 
138 	fill_bank_combo ();
139 	set_active_bank_combo ();
140 	bank_combo_changed ();
141 
142 	_info.Changed.connect (_info_changed_connection, invalidator (*this),
143 			       boost::bind (&PatchChangeDialog::instrument_info_changed, this), gui_context());
144 
145 	show_all ();
146 }
147 
148 void
on_response(int response_id)149 PatchChangeDialog::on_response (int response_id)
150 {
151 	if (_keep_open) {
152 		Gtk::Dialog::on_response (response_id);
153 	} else {
154 		ArdourDialog::on_response (response_id);
155 	}
156 }
157 
158 int
get_14bit_bank() const159 PatchChangeDialog::get_14bit_bank () const
160 {
161 	return (_bank_msb.get_value_as_int() << 7) + _bank_lsb.get_value_as_int();
162 }
163 
164 void
instrument_info_changed()165 PatchChangeDialog::instrument_info_changed ()
166 {
167 	_bank_combo.clear ();
168 	_patch_combo.clear ();
169 	fill_bank_combo ();
170 	fill_patch_combo ();
171 }
172 
173 Evoral::PatchChange<Temporal::Beats>
patch() const174 PatchChangeDialog::patch () const
175 {
176 	Temporal::Beats t = Temporal::Beats();
177 
178 	if (_time_converter) {
179 		t = _time_converter->from (_time.current_time ());
180 	}
181 
182 	return Evoral::PatchChange<Temporal::Beats> (
183 		t,
184 		_channel.get_value_as_int() - 1,
185 		_program.get_value_as_int() - 1,
186 		get_14bit_bank ()
187 		);
188 }
189 
190 /** Fill the bank_combo according to the current _channel */
191 void
fill_bank_combo()192 PatchChangeDialog::fill_bank_combo ()
193 {
194 	_bank_combo.clear ();
195 
196 	boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
197 
198 	if (!cns) {
199 		return;
200 	}
201 
202 	for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
203 		string n = (*i)->name ();
204 		boost::replace_all (n, "_", " ");
205 		_bank_combo.append_text (n);
206 	}
207 }
208 
209 /** Set the active value of the bank_combo, and _current_patch_bank, from the contents of _bank */
210 void
set_active_bank_combo()211 PatchChangeDialog::set_active_bank_combo ()
212 {
213 	_current_patch_bank.reset ();
214 
215 	boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
216 
217 	if (!cns) {
218 		return;
219 	}
220 
221 	for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
222 
223 		string n = (*i)->name ();
224 		boost::replace_all (n, "_", " ");
225 
226 		if ((*i)->number() == get_14bit_bank()) {
227 			_current_patch_bank = *i;
228 			_ignore_signals = true;
229 			_bank_combo.set_active_text (n);
230 			_ignore_signals = false;
231 			return;
232 		}
233 	}
234 
235 	_ignore_signals = true;
236 	_bank_combo.set_active (-1);
237 	_ignore_signals = false;
238 }
239 
240 /** Update _current_patch_bank and reflect the current value of
241  *  bank_combo in the rest of the dialog.
242  */
243 void
bank_combo_changed()244 PatchChangeDialog::bank_combo_changed ()
245 {
246 	if (_ignore_signals) {
247 		return;
248 	}
249 
250 	_current_patch_bank.reset ();
251 
252 	boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel.get_value_as_int() - 1);
253 
254 	if (!cns) {
255 		return;
256 	}
257 
258 	for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
259 		string n = (*i)->name ();
260 		boost::replace_all (n, "_", " ");
261 		if (n == _bank_combo.get_active_text()) {
262 			_current_patch_bank = *i;
263 		}
264 	}
265 
266 	if (_current_patch_bank == 0) {
267 		return;
268 	}
269 
270 	/* Reflect */
271 
272 	fill_patch_combo ();
273 	set_active_patch_combo ();
274 
275 	if (_current_patch_bank->number() != UINT16_MAX) {
276 		_ignore_signals = true;
277 		_bank_msb.set_value (_current_patch_bank->number() >> 7);
278 		_bank_lsb.set_value (_current_patch_bank->number() & 127);
279 		_ignore_signals = false;
280 	}
281 }
282 
283 /** Fill the contents of the patch combo */
284 void
fill_patch_combo()285 PatchChangeDialog::fill_patch_combo ()
286 {
287 	_patch_combo.clear ();
288 
289 	if (_current_patch_bank == 0) {
290 		return;
291 	}
292 
293 	const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
294 	for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
295 		string n = (*j)->name ();
296 		boost::replace_all (n, "_", " ");
297 		_patch_combo.append_text (n);
298 	}
299 }
300 
301 /** Set the active value of the patch combo from the value of the _program entry */
302 void
set_active_patch_combo()303 PatchChangeDialog::set_active_patch_combo ()
304 {
305 	if (_ignore_signals) {
306 		return;
307 	}
308 
309 	if (_current_patch_bank == 0) {
310 		_ignore_signals = true;
311 		_patch_combo.set_active (-1);
312 		_ignore_signals = false;
313 		return;
314 	}
315 
316 	const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
317 	for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
318 		string n = (*j)->name ();
319 		boost::replace_all (n, "_", " ");
320 
321 		MIDI::Name::PatchPrimaryKey const & key = (*j)->patch_primary_key ();
322 		if (key.program() == _program.get_value() - 1) {
323 			_ignore_signals = true;
324 			_patch_combo.set_active_text (n);
325 			_ignore_signals = false;
326 			return;
327 		}
328 	}
329 
330 	_ignore_signals = true;
331 	_patch_combo.set_active (-1);
332 	_ignore_signals = false;
333 }
334 
335 /** Set _program from the current state of _patch_combo */
336 void
patch_combo_changed()337 PatchChangeDialog::patch_combo_changed ()
338 {
339 	if (_ignore_signals || _current_patch_bank == 0) {
340 		return;
341 	}
342 
343 	const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
344 
345 	for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
346 		string n = (*j)->name ();
347 		boost::replace_all (n, "_", " ");
348 
349 		if (n == _patch_combo.get_active_text ()) {
350 			_ignore_signals = true;
351 			_program.set_value ((*j)->program_number() + 1);
352 			_bank_msb.set_value ((*j)->bank_number() >> 7);
353 			_bank_lsb.set_value ((*j)->bank_number() & 127);
354 			_ignore_signals = false;
355 			break;
356 		}
357 	}
358 }
359 
360 void
channel_changed()361 PatchChangeDialog::channel_changed ()
362 {
363 	fill_bank_combo ();
364 	set_active_bank_combo ();
365 	fill_patch_combo ();
366 	set_active_patch_combo ();
367 }
368 
369 void
program_changed()370 PatchChangeDialog::program_changed ()
371 {
372 	if (_ignore_signals) {
373 		return;
374 	}
375 
376 	set_active_patch_combo ();
377 }
378 
379 void
bank_changed()380 PatchChangeDialog::bank_changed ()
381 {
382 	if (_ignore_signals) {
383 		return;
384 	}
385 
386 	set_active_bank_combo ();
387 	fill_patch_combo ();
388 	set_active_patch_combo ();
389 }
390 
391