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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2541 - (show annotations) (download)
Wed Apr 23 16:49:05 2014 UTC (4 years, 7 months ago) by schoenebeck
File size: 16536 byte(s)
* Mark all recently added new leverage controllers (which are an
  unofficial gig sound format extension, only understood by
  LinuxSampler so far, not by GSt) with a "[EXT]" tag and show
  the user a warning dialog in case he tries to use one of those
  new controller types (warning can be switched off from menu).

1 /*
2 * Copyright (C) 2006-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, or (at
7 * 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 program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include <cstring>
21
22 #include "paramedit.h"
23
24 #include "global.h"
25 #include "compat.h"
26 #include "Settings.h"
27
28 #include <gtkmm/messagedialog.h>
29
30 std::string gig_encoding("CP1252");
31
32 Glib::ustring gig_to_utf8(const gig::String& gig_string) {
33 return Glib::convert_with_fallback(gig_string, "UTF-8", gig_encoding, "?");
34 }
35
36 gig::String gig_from_utf8(const Glib::ustring& utf8_string) {
37 return Glib::convert_with_fallback(utf8_string, gig_encoding, "UTF-8", "?");
38 }
39
40
41 namespace {
42 struct CCText {
43 const char* const txt;
44 bool isExtension; ///< True if this is a controller only supported by LinuxSampler, but not supperted by Gigasampler/GigaStudio.
45 };
46 static const CCText controlChangeTexts[] = {
47 // 3 special ones (not being CCs)
48 { _("none") }, { _("channelaftertouch") }, { _("velocity") },
49 {0}, // bank select MSB (hard coded in sampler, so discouraged to be used here, even though considerable)
50 { _("modwheel") }, // "Modulation Wheel or Lever",
51 { _("breath") }, // "Breath Controller",
52 { _("undefined"), true },
53 { _("foot") }, // "Foot Controller",
54 { _("portamentotime") }, // "Portamento Time",
55 { _("data entry MSB"), true },
56 { _("volume"), true },
57 { _("balance"), true },
58 { _("undefined"), true },
59 { _("pan"), true },
60 { _("expression"), true },
61 { _("effect1") }, // "Effect Control 1",
62 { _("effect2") }, // "Effect Control 2",
63 { _("undefined"), true },
64 { _("undefined"), true },
65 { _("genpurpose1") }, // "General Purpose Controller 1",
66 { _("genpurpose2") }, // "General Purpose Controller 2",
67 { _("genpurpose3") }, // "General Purpose Controller 3",
68 { _("genpurpose4") }, // "General Purpose Controller 4",
69 { _("undefined"), true },
70 { _("undefined"), true },
71 { _("undefined"), true },
72 { _("undefined"), true },
73 { _("undefined"), true },
74 { _("undefined"), true },
75 { _("undefined"), true },
76 { _("undefined"), true },
77 { _("undefined"), true },
78 { _("undefined"), true },
79 { _("undefined"), true },
80 { _("undefined"), true },
81
82 // LSB variant of the various controllers above
83 // (so discouraged to be used here for now)
84 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
85 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},
86 {0}, {0}, {0}, {0},
87
88 { _("sustainpedal") }, // "Damper Pedal on/off (Sustain)",
89 { _("portamento") }, // "Portamento On/Off",
90 { _("sostenuto") }, // "Sustenuto On/Off",
91 { _("softpedal") }, // "Soft Pedal On/Off",
92 { _("legato"), true },
93 { _("hold2"), true },
94 { _("soundvariation"), true },
95 { _("timbre"), true },
96 { _("releasetime"), true },
97 { _("attacktime"), true },
98 { _("brightness"), true },
99 { _("decaytime"), true },
100 { _("vibratorate"), true },
101 { _("vibratodepth"), true },
102 { _("vibratodelay"), true },
103 { _("undefined"), true },
104 { _("genpurpose5") }, // "General Purpose Controller 5",
105 { _("genpurpose6") }, // "General Purpose Controller 6",
106 { _("genpurpose7") }, // "General Purpose Controller 7",
107 { _("genpurpose8") }, // "General Purpose Controller 8",
108 { _("portamentoctrl"), true },
109 { _("undefined"), true },
110 { _("undefined"), true },
111 { _("undefined"), true },
112 {0}, // high resolution velocity prefix (so discouraged to be used here)
113 { _("undefined"), true },
114 { _("undefined"), true },
115 { _("effect1depth") }, // "Effects 1 Depth",
116 { _("effect2depth") }, // "Effects 2 Depth",
117 { _("effect3depth") }, // "Effects 3 Depth",
118 { _("effect4depth") }, // "Effects 4 Depth",
119 { _("effect5depth") }, // "Effects 5 Depth"
120 { _("dataincrement"), true },
121 { _("datadecrement"), true },
122 {0}, // NRPN LSB (so discouraged to be used here)
123 {0}, // NRPN MSB (so discouraged to be used here)
124 {0}, // RPN LSB (so discouraged to be used here)
125 {0}, // RPN MSB (so discouraged to be used here)
126 { _("undefined"), true },
127 { _("undefined"), true },
128 { _("undefined"), true },
129 { _("undefined"), true },
130 { _("undefined"), true },
131 { _("undefined"), true },
132 { _("undefined"), true },
133 { _("undefined"), true },
134 { _("undefined"), true },
135 { _("undefined"), true },
136 { _("undefined"), true },
137 { _("undefined"), true },
138 { _("undefined"), true },
139 { _("undefined"), true },
140 { _("undefined"), true },
141 { _("undefined"), true },
142 { _("undefined"), true },
143 { _("undefined"), true } // CC 119
144 // (all other ones that follow [CC 120- CC 127] are hard coded channel
145 // mode messages, so those are discouraged to be used here)
146 };
147 }
148
149 #define controlChangeTextsSize (sizeof(controlChangeTexts) / sizeof(CCText))
150
151 LabelWidget::LabelWidget(const char* labelText, Gtk::Widget& widget) :
152 label(Glib::ustring(labelText) + ":"),
153 widget(widget)
154 {
155 label.set_alignment(Gtk::ALIGN_START);
156 }
157
158 void LabelWidget::set_sensitive(bool sensitive)
159 {
160 label.set_sensitive(sensitive);
161 widget.set_sensitive(sensitive);
162 }
163
164 NumEntry::NumEntry(const char* labelText, double lower, double upper,
165 int decimals) :
166 LabelWidget(labelText, box),
167 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
168 adjust(lower, lower, upper, 1, 10),
169 #else
170 adjust(Gtk::Adjustment::create(lower, lower, upper, 1, 10)),
171 #endif
172 scale(adjust),
173 spinbutton(adjust)
174 {
175 scale.set_size_request(70);
176 spinbutton.set_digits(decimals);
177 spinbutton.set_value(0);
178 spinbutton.set_numeric();
179 scale.set_draw_value(false);
180 box.pack_start(spinbutton, Gtk::PACK_SHRINK);
181 box.add(scale);
182 }
183
184 NumEntryGain::NumEntryGain(const char* labelText,
185 double lower, double upper,
186 int decimals, double coeff) :
187 NumEntry(labelText, lower, upper, decimals),
188 value(0),
189 coeff(coeff),
190 connected(true)
191 {
192 spinbutton.signal_value_changed().connect(
193 sigc::mem_fun(*this, &NumEntryGain::value_changed));
194 }
195
196 void NumEntryGain::value_changed()
197 {
198 if (!connected) return;
199
200 const double f = pow(10, spinbutton.get_digits());
201 int new_value = round_to_int(spinbutton.get_value() * f);
202 if (new_value != round_to_int(value / coeff * f)) {
203 value = round_to_int(new_value / f * coeff);
204 sig_changed();
205 }
206 }
207
208 void NumEntryGain::set_value(int32_t value)
209 {
210 if (value != this->value) {
211 this->value = value;
212
213 connected = false;
214 bool plus6 = value < 0;
215 spinbutton.set_value(plus6 ? 0 : value / coeff);
216 set_sensitive(!plus6);
217 connected = true;
218
219 sig_changed();
220 }
221 }
222
223
224 BoolEntryPlus6::BoolEntryPlus6(const char* labelText, NumEntryGain& eGain, int32_t plus6value) :
225 LabelWidget(labelText, checkbutton),
226 checkbutton(labelText),
227 eGain(eGain),
228 plus6value(plus6value)
229 {
230 checkbutton.signal_toggled().connect(
231 sigc::mem_fun(*this, &BoolEntryPlus6::value_changed));
232 }
233
234 void BoolEntryPlus6::value_changed()
235 {
236 if (checkbutton.get_active()) eGain.set_value(plus6value);
237 else if (eGain.get_value() < 0) eGain.set_value(0);
238 }
239
240 int32_t BoolEntryPlus6::get_value() const
241 {
242 return eGain.get_value();
243 }
244
245 void BoolEntryPlus6::set_value(int32_t value)
246 {
247 checkbutton.set_active(value < 0);
248 }
249
250 NumEntryPermille::NumEntryPermille(const char* labelText,
251 double lower, double upper, int decimals) :
252 NumEntry(labelText, lower, upper, decimals),
253 value(0)
254 {
255 spinbutton.signal_value_changed().connect(
256 sigc::mem_fun(*this, &NumEntryPermille::value_changed));
257 }
258
259 void NumEntryPermille::value_changed()
260 {
261 uint16_t new_value = uint16_t(spinbutton.get_value() * 10 + 0.5);
262 if (new_value != value) {
263 value = uint16_t(spinbutton.get_value() * 10 + 0.5);
264 sig_changed();
265 }
266 }
267
268 void NumEntryPermille::set_value(uint16_t value)
269 {
270 if (value != this->value) {
271 spinbutton.set_value(value / 10.0);
272 }
273 }
274
275
276 NoteEntry::NoteEntry(const char* labelText) :
277 NumEntryTemp<uint8_t>(labelText)
278 {
279 spin_button_show_notes(spinbutton);
280 }
281
282 namespace {
283 const char* notes[] = {
284 _("C"), _("C#"), _("D"), _("D#"), _("E"), _("F"),_("F#"),
285 _("G"), _("G#"), _("A"), _("A#"), _("B")
286 };
287
288 int note_value(const Glib::ustring& note, double* value)
289 {
290 const char* str = note.c_str();
291
292 int i;
293 for (i = 11 ; i >= 0 ; i--) {
294 if (strncasecmp(str, notes[i], strlen(notes[i])) == 0) break;
295 }
296 if (i >= 0) {
297 char* endptr;
298 long x = strtol(str + strlen(notes[i]), &endptr, 10);
299 if (endptr != str + strlen(notes[i])) {
300 *value = std::max(0L, std::min(i + (x + 1) * 12, 127L));
301 return true;
302 }
303 } else {
304 char* endptr;
305 long x = strtol(str, &endptr, 10);
306 if (endptr != str) {
307 *value = std::max(0L, std::min(x, 127L));
308 return true;
309 }
310 }
311 return Gtk::INPUT_ERROR;
312 }
313 }
314
315 int note_value(const Glib::ustring& note)
316 {
317 double value = 0;
318 note_value(note, &value);
319 return value;
320 }
321
322 Glib::ustring note_str(int note)
323 {
324 char buf[10];
325 sprintf(buf, "%s%d", notes[note % 12], note / 12 - 1);
326 return buf;
327 }
328
329 namespace {
330 // Convert the Entry text to a number
331 int on_input(double* new_value, Gtk::SpinButton* spinbutton) {
332 return note_value(spinbutton->get_text(), new_value);
333 }
334
335 // Convert the Adjustment position to text
336 bool on_output(Gtk::SpinButton* spinbutton) {
337 spinbutton->set_text(
338 note_str(spinbutton->get_adjustment()->get_value() + 0.5));
339 return true;
340 }
341 }
342
343 // Make a SpinButton show notes instead of numbers
344 void spin_button_show_notes(Gtk::SpinButton& spin_button)
345 {
346 spin_button.set_numeric(false);
347 spin_button.set_width_chars(4);
348 spin_button.signal_input().connect(
349 sigc::bind(sigc::ptr_fun(&on_input), &spin_button));
350 spin_button.signal_output().connect(
351 sigc::bind(sigc::ptr_fun(&on_output), &spin_button));
352 }
353
354 ChoiceEntryLeverageCtrl::ChoiceEntryLeverageCtrl(const char* labelText) :
355 LabelWidget(labelText, align),
356 align(0, 0, 0, 0)
357 {
358 for (int i = 0 ; i < controlChangeTextsSize ; i++) {
359 if (controlChangeTexts[i].txt) {
360 const int cc = i - 3;
361 Glib::ustring s = (i < 3)
362 ? controlChangeTexts[i].txt
363 : Glib::ustring::compose("CC%1: %2%3", cc, controlChangeTexts[i].txt, controlChangeTexts[i].isExtension ? " [EXT]" : "");
364 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
365 combobox.append_text(s);
366 #else
367 combobox.append(s);
368 #endif
369 }
370 }
371 combobox.signal_changed().connect(
372 sigc::mem_fun(*this, &ChoiceEntryLeverageCtrl::value_changed));
373 align.add(combobox);
374 value.type = gig::leverage_ctrl_t::type_none;
375 value.controller_number = 0;
376 }
377
378 void ChoiceEntryLeverageCtrl::value_changed()
379 {
380 int rowno = combobox.get_active_row_number();
381 switch (rowno)
382 {
383 case -1:
384 break;
385 case 0:
386 value.type = gig::leverage_ctrl_t::type_none;
387 break;
388 case 1:
389 value.type = gig::leverage_ctrl_t::type_channelaftertouch;
390 break;
391 case 2:
392 value.type = gig::leverage_ctrl_t::type_velocity;
393 break;
394 default:
395 value.type = gig::leverage_ctrl_t::type_controlchange;
396 int x = 3;
397 for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
398 if (controlChangeTexts[cc + 3].txt) {
399 if (rowno == x) {
400 value.controller_number = cc;
401 if (controlChangeTexts[cc + 3].isExtension &&
402 Settings::singleton()->warnUserOnExtensions)
403 {
404 Glib::ustring txt = _("<b>Format Extension</b>\n\nAll controllers marked with \"<b>[EXT]</b>\" are an extension to the original gig sound format. They will only work with LinuxSampler, but they will <b>not work</b> with Gigasampler/GigaStudio!\n\n(You may disable this warning in the <i>Settings</i> menu.)");
405 Gtk::MessageDialog msg(
406 txt, true, Gtk::MESSAGE_WARNING
407 );
408 msg.run();
409 }
410 break;
411 }
412 x++;
413 }
414 }
415 break;
416 }
417 if (rowno >= 0) sig_changed();
418 }
419
420 void ChoiceEntryLeverageCtrl::set_value(gig::leverage_ctrl_t value)
421 {
422 int comboIndex;
423 switch (value.type)
424 {
425 case gig::leverage_ctrl_t::type_none:
426 comboIndex = 0;
427 break;
428 case gig::leverage_ctrl_t::type_channelaftertouch:
429 comboIndex = 1;
430 break;
431 case gig::leverage_ctrl_t::type_velocity:
432 comboIndex = 2;
433 break;
434 case gig::leverage_ctrl_t::type_controlchange: {
435 comboIndex = -1;
436 int x = 3;
437 for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
438 if (controlChangeTexts[cc + 3].txt) {
439 if (value.controller_number == cc) {
440 comboIndex = x;
441 break;
442 }
443 x++;
444 }
445 }
446 break;
447 }
448 default:
449 comboIndex = -1;
450 break;
451 }
452 combobox.set_active(comboIndex);
453 }
454
455
456 BoolEntry::BoolEntry(const char* labelText) :
457 LabelWidget(labelText, checkbutton),
458 checkbutton(labelText)
459 {
460 checkbutton.signal_toggled().connect(sig_changed.make_slot());
461 }
462
463
464 StringEntry::StringEntry(const char* labelText) :
465 LabelWidget(labelText, entry)
466 {
467 entry.signal_changed().connect(sig_changed.make_slot());
468 }
469
470 gig::String StringEntry::get_value() const
471 {
472 return gig_from_utf8(entry.get_text());
473 }
474
475 void StringEntry::set_value(const gig::String& value) {
476 entry.set_text(gig_to_utf8(value));
477 }
478
479
480 StringEntryMultiLine::StringEntryMultiLine(const char* labelText) :
481 LabelWidget(labelText, frame)
482 {
483 text_buffer = text_view.get_buffer();
484 frame.set_shadow_type(Gtk::SHADOW_IN);
485 frame.add(text_view);
486 text_buffer->signal_changed().connect(sig_changed.make_slot());
487 }
488
489 gig::String StringEntryMultiLine::get_value() const
490 {
491 Glib::ustring value = text_buffer->get_text();
492 for (int i = 0 ; (i = value.find("\x0a", i)) >= 0 ; i += 2)
493 value.replace(i, 1, "\x0d\x0a");
494 return gig_from_utf8(value);
495 }
496
497 void StringEntryMultiLine::set_value(const gig::String& value)
498 {
499 Glib::ustring text = gig_to_utf8(value);
500 for (int i = 0 ; (i = text.find("\x0d\x0a", i, 2)) >= 0 ; i++)
501 text.replace(i, 2, "\x0a");
502 text_buffer->set_text(text);
503 }
504
505
506 Table::Table(int x, int y) : Gtk::Table(x, y), rowno(0) { }
507
508 void Table::add(BoolEntry& boolentry)
509 {
510 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
511 Gtk::FILL, Gtk::SHRINK);
512 rowno++;
513 }
514
515 void Table::add(BoolEntryPlus6& boolentry)
516 {
517 attach(boolentry.widget, 0, 2, rowno, rowno + 1,
518 Gtk::FILL, Gtk::SHRINK);
519 rowno++;
520 }
521
522 void Table::add(LabelWidget& prop)
523 {
524 attach(prop.label, 1, 2, rowno, rowno + 1,
525 Gtk::FILL, Gtk::SHRINK);
526 attach(prop.widget, 2, 3, rowno, rowno + 1,
527 Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK);
528 rowno++;
529 }

  ViewVC Help
Powered by ViewVC