/[svn]/gigedit/trunk/src/gigedit/midirules.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/midirules.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2507 - (show annotations) (download)
Sun Jan 12 19:37:55 2014 UTC (10 years, 2 months ago) by persson
File size: 18976 byte(s)
* added dialog for editing the CtrlTrigger and Legato midi rules

1 /* -*- c++ -*-
2 * Copyright (C) 2013-2014 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 #include "midirules.h"
21
22 #include <gtkmm/stock.h>
23
24 #include "global.h"
25
26 MidiRules::MidiRules() :
27 label(_("Midi rule:")),
28 unknown(_("unknown")),
29 quit_button(Gtk::Stock::CLOSE)
30 {
31 set_title(_("Midi Rules"));
32 set_border_width(6);
33
34 add(vbox);
35
36 hbox.set_border_width(6);
37 hbox.set_spacing(6);
38 hbox.pack_start(label, Gtk::PACK_SHRINK);
39 hbox.pack_start(combo, Gtk::PACK_SHRINK);
40 const char* choices[] = { _("none"), _("Controller trigger"),
41 _("Legato"), 0 };
42 for (int i = 0 ; choices[i] ; i++) {
43 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
44 combo.append_text(choices[i]);
45 #else
46 combo.append(choices[i]);
47 #endif
48 }
49 combo.signal_changed().connect(
50 sigc::mem_fun(*this, &MidiRules::combo_changed));
51 vbox.pack_start(hbox, Gtk::PACK_SHRINK);
52
53 box.set_border_width(6);
54 vbox.pack_start(box);
55
56 button_box.set_border_width(6);
57 button_box.set_layout(Gtk::BUTTONBOX_END);
58 button_box.pack_start(quit_button);
59 quit_button.set_can_default();
60 quit_button.grab_focus();
61 quit_button.signal_clicked().connect(
62 sigc::mem_fun(*this, &MidiRules::hide));
63 vbox.pack_start(button_box, Gtk::PACK_SHRINK);
64
65 legato.signal_changed().connect(sig_changed.make_slot());
66 ctrl_trigger.signal_changed().connect(sig_changed.make_slot());
67
68 show_all_children();
69 }
70
71 void MidiRules::combo_changed() {
72 if (update_model) return;
73
74 int rowno = combo.get_active_row_number();
75
76 if (remove_unknown_from_combo()) {
77 rowno--;
78 }
79
80 gig::MidiRule* rule = m->GetMidiRule(0);
81 switch (rowno) {
82 case NONE:
83 if (rule) {
84 m->DeleteMidiRule(0);
85 set_instrument(m);
86 }
87 break;
88 case CTRL_TRIGGER:
89 if (!dynamic_cast<gig::MidiRuleCtrlTrigger*>(rule)) {
90 m->AddMidiRuleCtrlTrigger();
91 set_instrument(m);
92 }
93 break;
94 case LEGATO:
95 if (!dynamic_cast<gig::MidiRuleLegato*>(rule)) {
96 m->AddMidiRuleLegato();
97 set_instrument(m);
98 }
99 break;
100 }
101 sig_changed();
102 }
103
104 void MidiRules::set_instrument(gig::Instrument* instrument) {
105 m = instrument;
106 update_model++;
107
108 std::vector<Widget*> children = box.get_children();
109 if (children.size() == 1) {
110 box.remove(*children[0]);
111 }
112
113 gig::MidiRule* rule = instrument->GetMidiRule(0);
114 int active = -1;
115 if (!rule) {
116 active = NONE;
117 } else if (gig::MidiRuleLegato* r =
118 dynamic_cast<gig::MidiRuleLegato*>(rule)) {
119 active = LEGATO;
120 box.add(legato);
121 legato.set_rule(r);
122 } else if (gig::MidiRuleCtrlTrigger* r =
123 dynamic_cast<gig::MidiRuleCtrlTrigger*>(rule)) {
124 active = CTRL_TRIGGER;
125 box.add(ctrl_trigger);
126 ctrl_trigger.set_rule(r);
127 } else {
128 if (combo.get_model()->children().size() == NUMBER_OF_RULES) {
129 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
130 combo.prepend_text(unknown);
131 #else
132 combo.prepend(unknown);
133 #endif
134 }
135 combo.set_active(0);
136 }
137 if (active != -1) {
138 remove_unknown_from_combo();
139 combo.set_active(active);
140 }
141 show_all_children();
142 update_model--;
143 }
144
145 bool MidiRules::remove_unknown_from_combo() {
146 if (combo.get_model()->children().size() == NUMBER_OF_RULES + 1) {
147 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
148 combo.remove_text(unknown);
149 #else
150 combo.remove_text(0);
151 #endif
152 return true;
153 }
154 return false;
155 }
156
157
158 MidiRuleCtrlTrigger::MidiRuleCtrlTrigger() :
159 table(2, 1),
160 eControllerNumber(_("Controller"))
161 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
162 , add_button(Gtk::Stock::ADD),
163 remove_button(Gtk::Stock::REMOVE)
164 #endif
165 {
166 connect(eControllerNumber, &gig::MidiRuleCtrlTrigger::ControllerNumber);
167
168 set_spacing(6);
169
170 scrolled_window.set_size_request(-1, 120);
171
172 list_store = Gtk::ListStore::create(columns);
173 tree_view.set_model(list_store);
174 list_store->signal_row_changed().connect(
175 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::row_changed));
176 list_store->signal_row_inserted().connect(
177 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::row_inserted));
178 list_store->signal_row_deleted().connect(
179 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::row_deleted));
180
181 append_num_column(_("Trigger point"), columns.trigger_point);
182 tree_view.append_column_editable(_("Descending"), columns.descending);
183 append_num_column(_("Vel sensitivity"), columns.vel_sensitivity, 1, 100);
184 append_note_column(_("Key"), columns.key);
185 tree_view.append_column_editable(_("Note off"), columns.note_off);
186 tree_view.append_column_editable(_("Switch"), columns.switch_logic);
187
188 int cols_count = append_num_column(_("Velocity"), columns.velocity);
189 Gtk::TreeViewColumn* col = tree_view.get_column(cols_count - 1);
190 col->add_attribute(*col->get_first_cell(), "visible", columns.switch_logic);
191 cols_count = tree_view.append_column_editable(_("Override pedal"),
192 columns.override_pedal);
193 col = tree_view.get_column(cols_count - 1);
194 col->add_attribute(*col->get_first_cell(), "visible", columns.note_off);
195
196 tree_view.get_selection()->signal_changed().connect(
197 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::sel_changed));
198
199 scrolled_window.add(tree_view);
200 scrolled_window.set_shadow_type(Gtk::SHADOW_IN);
201 scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
202
203 table.set_col_spacings(5);
204 table.add(eControllerNumber);
205 pack_start(table, Gtk::PACK_SHRINK);
206
207 vbox.add(scrolled_window);
208
209 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
210 vbox.set_spacing(6);
211 toolbar.set_layout(Gtk::BUTTONBOX_START);
212 toolbar.set_homogeneous();
213 toolbar.set_spacing(6);
214 #else
215 toolbar.set_toolbar_style(Gtk::TOOLBAR_ICONS);
216 toolbar.set_show_arrow(false);
217 toolbar.set_icon_size(Gtk::IconSize(1));
218 toolbar.get_style_context()->add_class("inline-toolbar");
219
220 add_button.set_icon_name("list-add-symbolic");
221 remove_button.set_icon_name("list-remove-symbolic");
222 #endif
223
224 add_button.signal_clicked().connect(
225 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::add_row));
226 toolbar.add(add_button);
227
228 remove_button.signal_clicked().connect(
229 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::remove_row));
230 toolbar.add(remove_button);
231
232 vbox.pack_start(toolbar, Gtk::PACK_SHRINK);
233 add(vbox);
234 }
235
236 int MidiRuleCtrlTrigger::append_num_column(
237 const char* title,
238 const Gtk::TreeModelColumn<int>& column,
239 int lower, int upper) {
240 Gtk::CellRendererSpin* renderer = Gtk::manage(new Gtk::CellRendererSpin());
241 renderer->property_editable() = true;
242 renderer->signal_editing_started().connect(
243 sigc::bind(
244 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::num_editing_started),
245 renderer));
246 renderer->signal_edited().connect(
247 sigc::bind(
248 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::num_edited),
249 column));
250 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
251 renderer->property_adjustment() = new Gtk::Adjustment(lower, lower, upper);
252 #else
253 renderer->property_adjustment() =
254 Gtk::Adjustment::create(lower, lower, upper);
255 #endif
256
257 int cols_count = tree_view.append_column(title, *renderer);
258 Gtk::TreeViewColumn* col = tree_view.get_column(cols_count - 1);
259 col->add_attribute(*renderer, "text", column);
260 col->set_min_width(92);
261 return cols_count;
262 }
263
264 int MidiRuleCtrlTrigger::append_note_column(
265 const char* title,
266 const Gtk::TreeModelColumn<Glib::ustring>& column) {
267 Gtk::CellRendererSpin* renderer = Gtk::manage(new Gtk::CellRendererSpin());
268 renderer->property_editable() = true;
269 renderer->signal_editing_started().connect(
270 sigc::bind(
271 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::note_editing_started),
272 renderer));
273 renderer->signal_edited().connect(
274 sigc::bind(
275 sigc::mem_fun(*this, &MidiRuleCtrlTrigger::note_edited),
276 column));
277 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
278 renderer->property_adjustment() = new Gtk::Adjustment(0, 0, 127);
279 #else
280 renderer->property_adjustment() = Gtk::Adjustment::create(0, 0, 127);
281 #endif
282
283 int cols_count = tree_view.append_column(title, *renderer);
284 Gtk::TreeViewColumn* col = tree_view.get_column(cols_count - 1);
285 col->add_attribute(*renderer, "text", column);
286 col->set_min_width(98);
287 return cols_count;
288 }
289
290 void MidiRuleCtrlTrigger::set_rule(gig::MidiRuleCtrlTrigger* r) {
291 update_model++;
292 update(r);
293 list_store->clear();
294 for (int i = 0 ; i < r->Triggers ; i++) {
295 Gtk::TreeModel::Row row = *list_store->append();
296 row[columns.trigger_point] = r->pTriggers[i].TriggerPoint;
297 row[columns.descending] = r->pTriggers[i].Descending;
298 row[columns.vel_sensitivity] = r->pTriggers[i].VelSensitivity;
299 row[columns.key] = note_str(r->pTriggers[i].Key);
300 row[columns.note_off] = r->pTriggers[i].NoteOff;
301 row[columns.switch_logic] = r->pTriggers[i].Velocity != 255;
302 if (r->pTriggers[i].Velocity != 255) {
303 row[columns.velocity] = r->pTriggers[i].Velocity;
304 }
305 row[columns.override_pedal] = r->pTriggers[i].OverridePedal;
306 }
307 sel_changed();
308 add_button.set_sensitive();
309 if (r->Triggers == 32) add_button.set_sensitive(false);
310 update_model--;
311 }
312
313 void MidiRuleCtrlTrigger::num_editing_started(
314 Gtk::CellEditable* editable,
315 const Glib::ustring& path,
316 Gtk::CellRendererSpin* renderer) {
317 int lower = renderer->property_adjustment().get_value()->get_lower();
318 int upper = renderer->property_adjustment().get_value()->get_upper();
319 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
320 renderer->property_adjustment() = new Gtk::Adjustment(lower, lower, upper);
321 #else
322 renderer->property_adjustment() =
323 Gtk::Adjustment::create(lower, lower, upper);
324 #endif
325 Gtk::SpinButton* spin_button = dynamic_cast<Gtk::SpinButton*>(editable);
326 if (spin_button) {
327 spin_button->set_numeric();
328 }
329 }
330
331 void MidiRuleCtrlTrigger::note_editing_started(
332 Gtk::CellEditable* editable,
333 const Glib::ustring& path,
334 Gtk::CellRendererSpin* renderer) {
335 int value = note_value(renderer->property_text());
336 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
337 renderer->property_adjustment() = new Gtk::Adjustment(0, 0, 127);
338 #else
339 renderer->property_adjustment() = Gtk::Adjustment::create(0, 0, 127);
340 #endif
341 Gtk::SpinButton* spin_button = dynamic_cast<Gtk::SpinButton*>(editable);
342 if (spin_button) {
343 spin_button->get_adjustment()->set_value(value);
344 spin_button_show_notes(*spin_button);
345 }
346 }
347
348 void MidiRuleCtrlTrigger::num_edited(
349 const Glib::ustring& path,
350 const Glib::ustring& new_text,
351 const Gtk::TreeModelColumn<int>& column) {
352 Gtk::TreeModel::Row row = *list_store->get_iter(path);
353
354 Gtk::TreeModel::Path cpath;
355 Gtk::TreeViewColumn* col;
356 tree_view.get_cursor(cpath, col);
357
358 int lower = 0;
359 int upper = 127;
360 const Gtk::CellRenderer* r = col->get_first_cell();
361 if (const Gtk::CellRendererSpin* renderer =
362 dynamic_cast<const Gtk::CellRendererSpin*>(r)) {
363 lower = renderer->property_adjustment().get_value()->get_lower();
364 upper = renderer->property_adjustment().get_value()->get_upper();
365 }
366
367 row[column] = std::max(lower, std::min(atoi(new_text.c_str()), upper));
368 }
369
370 void MidiRuleCtrlTrigger::note_edited(
371 const Glib::ustring& path,
372 const Glib::ustring& new_text,
373 const Gtk::TreeModelColumn<Glib::ustring>& column) {
374 Gtk::TreeModel::Row row = *list_store->get_iter(path);
375
376 row[column] = note_str(note_value(new_text));
377 }
378
379 void MidiRuleCtrlTrigger::sel_changed() {
380 Gtk::TreeModel::iterator it = tree_view.get_selection()->get_selected();
381 remove_button.set_sensitive();
382 if (!it) remove_button.set_sensitive(false);
383 }
384
385 void MidiRuleCtrlTrigger::add_row() {
386 Gtk::TreeModel::Path path;
387 Gtk::TreeViewColumn* col;
388 tree_view.get_cursor(path, col);
389 if (!path.empty()) tree_view.set_cursor(path);
390
391 Gtk::TreeModel::iterator it = list_store->append();
392 Gtk::TreeModel::Row row = *it;
393
394 update_model++;
395 row[columns.trigger_point] = 64;
396 row[columns.descending] = false;
397 row[columns.vel_sensitivity] = 50;
398 row[columns.key] = note_str(60);
399 row[columns.note_off] = false;
400 row[columns.switch_logic] = false;
401 row[columns.override_pedal] = false;
402 update_model--;
403
404 tree_view.get_selection()->select(row);
405 path = list_store->get_path(it);
406 tree_view.scroll_to_row(path);
407 tree_view.set_cursor(path);
408 }
409
410 void MidiRuleCtrlTrigger::remove_row() {
411 Gtk::TreeModel::Path cpath;
412 Gtk::TreeViewColumn* col;
413 tree_view.get_cursor(cpath, col);
414 if (!cpath.empty()) tree_view.set_cursor(cpath);
415
416 Gtk::TreeModel::iterator it = tree_view.get_selection()->get_selected();
417 if (it) {
418 Gtk::TreePath path = list_store->get_path(it);
419 list_store->erase(it);
420
421 it = tree_view.get_selection()->get_selected();
422 if (!it) {
423 int i = path[0];
424 int n = list_store->children().size();
425 if (n) {
426 if (i >= n) i = n - 1;
427 path[0] = i;
428 tree_view.get_selection()->select(path);
429 }
430 }
431 }
432 }
433
434 void MidiRuleCtrlTrigger::row_changed(const Gtk::TreeModel::Path& path,
435 const Gtk::TreeModel::iterator& iter) {
436 if (update_model) return;
437
438 Gtk::TreeModel::Row row = *iter;
439 int i = path[0];
440
441 if (m->pTriggers[i].Velocity == 255 && row[columns.switch_logic]) {
442 update_model++;
443 row[columns.velocity] = 100;
444 update_model--;
445 }
446
447 int key = note_value(row[columns.key]);
448
449 if (m->pTriggers[i].TriggerPoint != row[columns.trigger_point] ||
450 m->pTriggers[i].Descending != row[columns.descending] ||
451 m->pTriggers[i].VelSensitivity != row[columns.vel_sensitivity] ||
452 m->pTriggers[i].Key != key ||
453 m->pTriggers[i].NoteOff != row[columns.note_off] ||
454 (m->pTriggers[i].Velocity != 255) != row[columns.switch_logic] ||
455 m->pTriggers[i].Velocity != row[columns.velocity] ||
456 m->pTriggers[i].OverridePedal != row[columns.override_pedal])
457 {
458 m->pTriggers[i].TriggerPoint = row[columns.trigger_point];
459 m->pTriggers[i].Descending = row[columns.descending];
460 m->pTriggers[i].VelSensitivity = row[columns.vel_sensitivity];
461 m->pTriggers[i].Key = key;
462 m->pTriggers[i].NoteOff = row[columns.note_off];
463 m->pTriggers[i].Velocity =
464 row[columns.switch_logic] ? row[columns.velocity] : 255;
465 m->pTriggers[i].OverridePedal = row[columns.override_pedal];
466 sig_changed();
467 }
468 }
469
470 void MidiRuleCtrlTrigger::row_inserted(const Gtk::TreeModel::Path& path,
471 const Gtk::TreeModel::iterator& iter) {
472 if (update_model) return;
473 Gtk::TreeModel::Row row = *iter;
474 int i = m->Triggers++;
475 m->pTriggers[i].TriggerPoint = 64;
476 m->pTriggers[i].Descending = false;
477 m->pTriggers[i].VelSensitivity = 50;
478 m->pTriggers[i].Key = 60;
479 m->pTriggers[i].NoteOff = false;
480 m->pTriggers[i].Velocity = 255;
481 m->pTriggers[i].OverridePedal = false;
482 add_button.set_sensitive();
483 if (m->Triggers == 32) add_button.set_sensitive(false);
484 sig_changed();
485 }
486
487 void MidiRuleCtrlTrigger::row_deleted(const Gtk::TreeModel::Path& path) {
488 if (update_model) return;
489 for (int i = path[0] + 1 ; i < m->Triggers ; i++) {
490 m->pTriggers[i - 1] = m->pTriggers[i];
491 }
492 m->Triggers--;
493 add_button.set_sensitive();
494 sig_changed();
495 }
496
497
498
499 MidiRuleLegato::MidiRuleLegato() :
500 Table(2, 1),
501 eBypassUseController(_("Bypass use controller")),
502 eBypassKey(_("Bypass key")),
503 eBypassController(_("Bypass controller")),
504 eThresholdTime(_("Threshold time"), 10, 500),
505 eReleaseTime(_("Release time"), 10, 500),
506 eKeyRangeLow(_("Key range low")),
507 eKeyRangeHigh(_("Key range high")),
508 eReleaseTriggerKey(_("Release trigger key")),
509 eAltSustain1Key(_("Alt sustain 1 key")),
510 eAltSustain2Key(_("Alt sustain 2 key"))
511 {
512 connect(eBypassUseController, &gig::MidiRuleLegato::BypassUseController);
513 connect(eBypassKey, &gig::MidiRuleLegato::BypassKey);
514 connect(eBypassController, &gig::MidiRuleLegato::BypassController);
515 connect(eThresholdTime, &gig::MidiRuleLegato::ThresholdTime);
516 connect(eReleaseTime, &gig::MidiRuleLegato::ReleaseTime);
517 connect(eKeyRangeLow, eKeyRangeHigh, &gig::MidiRuleLegato::KeyRange);
518 connect(eReleaseTriggerKey, &gig::MidiRuleLegato::ReleaseTriggerKey);
519 connect(eAltSustain1Key, &gig::MidiRuleLegato::AltSustain1Key);
520 connect(eAltSustain2Key, &gig::MidiRuleLegato::AltSustain2Key);
521
522 set_col_spacings(5);
523
524 add(eBypassUseController);
525 add(eBypassKey);
526 add(eBypassController);
527 add(eThresholdTime);
528 add(eReleaseTime);
529 add(eKeyRangeLow);
530 add(eKeyRangeHigh);
531 add(eReleaseTriggerKey);
532 add(eAltSustain1Key);
533 add(eAltSustain2Key);
534
535 eBypassUseController.signal_value_changed().connect(
536 sigc::mem_fun(*this, &MidiRuleLegato::BypassUseController_toggled));
537 }
538
539 void MidiRuleLegato::BypassUseController_toggled() {
540 bool useController = eBypassUseController.get_value();
541 eBypassKey.set_sensitive(!useController);
542 eBypassController.set_sensitive(useController);
543 }
544
545 void MidiRuleLegato::set_rule(gig::MidiRuleLegato* r) {
546 update_model++;
547 update(r);
548 BypassUseController_toggled();
549 update_model--;
550 }

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC