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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2541 - (hide annotations) (download)
Wed Apr 23 16:49:05 2014 UTC (9 years, 11 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 schoenebeck 1225 /*
2 persson 2507 * Copyright (C) 2006-2014 Andreas Persson
3 schoenebeck 1225 *
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 persson 1714 #include <cstring>
21    
22 schoenebeck 1225 #include "paramedit.h"
23    
24 persson 1831 #include "global.h"
25 persson 2169 #include "compat.h"
26 schoenebeck 2541 #include "Settings.h"
27 persson 1831
28 schoenebeck 2541 #include <gtkmm/messagedialog.h>
29    
30 persson 2446 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 schoenebeck 1225 namespace {
42 schoenebeck 2541 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 schoenebeck 2538
82     // LSB variant of the various controllers above
83     // (so discouraged to be used here for now)
84 schoenebeck 2541 {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 schoenebeck 2538
88 schoenebeck 2541 { _("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 schoenebeck 2538 // (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 schoenebeck 1225 };
147     }
148    
149 schoenebeck 2541 #define controlChangeTextsSize (sizeof(controlChangeTexts) / sizeof(CCText))
150 schoenebeck 2538
151 schoenebeck 1225 LabelWidget::LabelWidget(const char* labelText, Gtk::Widget& widget) :
152     label(Glib::ustring(labelText) + ":"),
153     widget(widget)
154     {
155 persson 2169 label.set_alignment(Gtk::ALIGN_START);
156 schoenebeck 1225 }
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 persson 2151 LabelWidget(labelText, box),
167 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
168 schoenebeck 1225 adjust(lower, lower, upper, 1, 10),
169 persson 2169 #else
170     adjust(Gtk::Adjustment::create(lower, lower, upper, 1, 10)),
171     #endif
172 schoenebeck 1225 scale(adjust),
173 persson 2151 spinbutton(adjust)
174 schoenebeck 1225 {
175 persson 2423 scale.set_size_request(70);
176 schoenebeck 1225 spinbutton.set_digits(decimals);
177 persson 1460 spinbutton.set_value(0);
178 persson 2507 spinbutton.set_numeric();
179 schoenebeck 1225 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 persson 2151 value(0),
189 persson 1460 coeff(coeff),
190     connected(true)
191 schoenebeck 1225 {
192     spinbutton.signal_value_changed().connect(
193     sigc::mem_fun(*this, &NumEntryGain::value_changed));
194     }
195    
196     void NumEntryGain::value_changed()
197     {
198 persson 1460 if (!connected) return;
199    
200 schoenebeck 1359 const double f = pow(10, spinbutton.get_digits());
201     int new_value = round_to_int(spinbutton.get_value() * f);
202 persson 1460 if (new_value != round_to_int(value / coeff * f)) {
203     value = round_to_int(new_value / f * coeff);
204     sig_changed();
205 schoenebeck 1225 }
206     }
207    
208 persson 1460 void NumEntryGain::set_value(int32_t value)
209 schoenebeck 1225 {
210 persson 1460 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 schoenebeck 1225 }
222    
223    
224     BoolEntryPlus6::BoolEntryPlus6(const char* labelText, NumEntryGain& eGain, int32_t plus6value) :
225     LabelWidget(labelText, checkbutton),
226 persson 1262 checkbutton(labelText),
227 schoenebeck 1225 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 persson 1460 if (checkbutton.get_active()) eGain.set_value(plus6value);
237     else if (eGain.get_value() < 0) eGain.set_value(0);
238 schoenebeck 1225 }
239    
240 persson 1460 int32_t BoolEntryPlus6::get_value() const
241 schoenebeck 1225 {
242 persson 1460 return eGain.get_value();
243 schoenebeck 1225 }
244    
245 persson 1460 void BoolEntryPlus6::set_value(int32_t value)
246     {
247     checkbutton.set_active(value < 0);
248     }
249    
250 schoenebeck 1225 NumEntryPermille::NumEntryPermille(const char* labelText,
251     double lower, double upper, int decimals) :
252 persson 1460 NumEntry(labelText, lower, upper, decimals),
253     value(0)
254 schoenebeck 1225 {
255     spinbutton.signal_value_changed().connect(
256     sigc::mem_fun(*this, &NumEntryPermille::value_changed));
257     }
258    
259     void NumEntryPermille::value_changed()
260     {
261 persson 1460 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 schoenebeck 1225 }
266     }
267    
268 persson 1460 void NumEntryPermille::set_value(uint16_t value)
269 schoenebeck 1225 {
270 persson 1460 if (value != this->value) {
271     spinbutton.set_value(value / 10.0);
272     }
273 schoenebeck 1225 }
274    
275    
276     NoteEntry::NoteEntry(const char* labelText) :
277     NumEntryTemp<uint8_t>(labelText)
278     {
279 persson 2507 spin_button_show_notes(spinbutton);
280 schoenebeck 1225 }
281    
282 persson 2507 namespace {
283     const char* notes[] = {
284     _("C"), _("C#"), _("D"), _("D#"), _("E"), _("F"),_("F#"),
285     _("G"), _("G#"), _("A"), _("A#"), _("B")
286     };
287 schoenebeck 1225
288 persson 2507 int note_value(const Glib::ustring& note, double* value)
289     {
290     const char* str = note.c_str();
291 schoenebeck 1225
292 persson 2507 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 schoenebeck 1225 {
317 persson 2507 double value = 0;
318     note_value(note, &value);
319     return value;
320     }
321 schoenebeck 1225
322 persson 2507 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 schoenebeck 1225 }
334 persson 2507
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 schoenebeck 1225 }
341     }
342    
343 persson 2507 // Make a SpinButton show notes instead of numbers
344     void spin_button_show_notes(Gtk::SpinButton& spin_button)
345 schoenebeck 1225 {
346 persson 2507 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 schoenebeck 1225 }
353    
354     ChoiceEntryLeverageCtrl::ChoiceEntryLeverageCtrl(const char* labelText) :
355 persson 2151 LabelWidget(labelText, align),
356     align(0, 0, 0, 0)
357 schoenebeck 1225 {
358 schoenebeck 2538 for (int i = 0 ; i < controlChangeTextsSize ; i++) {
359 schoenebeck 2541 if (controlChangeTexts[i].txt) {
360 schoenebeck 2538 const int cc = i - 3;
361     Glib::ustring s = (i < 3)
362 schoenebeck 2541 ? controlChangeTexts[i].txt
363     : Glib::ustring::compose("CC%1: %2%3", cc, controlChangeTexts[i].txt, controlChangeTexts[i].isExtension ? " [EXT]" : "");
364 persson 2507 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 24) || GTKMM_MAJOR_VERSION < 2
365 schoenebeck 2538 combobox.append_text(s);
366 persson 2169 #else
367 schoenebeck 2538 combobox.append(s);
368 persson 2169 #endif
369 schoenebeck 1225 }
370     }
371     combobox.signal_changed().connect(
372     sigc::mem_fun(*this, &ChoiceEntryLeverageCtrl::value_changed));
373     align.add(combobox);
374 persson 1460 value.type = gig::leverage_ctrl_t::type_none;
375     value.controller_number = 0;
376 schoenebeck 1225 }
377    
378     void ChoiceEntryLeverageCtrl::value_changed()
379     {
380 persson 1460 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 schoenebeck 2538 for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
398 schoenebeck 2541 if (controlChangeTexts[cc + 3].txt) {
399 persson 1460 if (rowno == x) {
400     value.controller_number = cc;
401 schoenebeck 2541 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 persson 1460 break;
411 schoenebeck 1225 }
412 persson 1460 x++;
413 schoenebeck 1225 }
414     }
415 persson 1460 break;
416 schoenebeck 1225 }
417 persson 1460 if (rowno >= 0) sig_changed();
418 schoenebeck 1225 }
419    
420 persson 1460 void ChoiceEntryLeverageCtrl::set_value(gig::leverage_ctrl_t value)
421 schoenebeck 1225 {
422 schoenebeck 2538 int comboIndex;
423 persson 1460 switch (value.type)
424 schoenebeck 1225 {
425     case gig::leverage_ctrl_t::type_none:
426 schoenebeck 2538 comboIndex = 0;
427 schoenebeck 1225 break;
428     case gig::leverage_ctrl_t::type_channelaftertouch:
429 schoenebeck 2538 comboIndex = 1;
430 schoenebeck 1225 break;
431     case gig::leverage_ctrl_t::type_velocity:
432 schoenebeck 2538 comboIndex = 2;
433 schoenebeck 1225 break;
434 schoenebeck 2538 case gig::leverage_ctrl_t::type_controlchange: {
435     comboIndex = -1;
436     int x = 3;
437     for (uint cc = 0 ; cc < controlChangeTextsSize - 3 ; cc++) {
438 schoenebeck 2541 if (controlChangeTexts[cc + 3].txt) {
439 persson 1460 if (value.controller_number == cc) {
440 schoenebeck 2538 comboIndex = x;
441 schoenebeck 1225 break;
442     }
443 schoenebeck 2538 x++;
444 schoenebeck 1225 }
445     }
446     break;
447 schoenebeck 2538 }
448 schoenebeck 1225 default:
449 schoenebeck 2538 comboIndex = -1;
450 schoenebeck 1225 break;
451     }
452 schoenebeck 2538 combobox.set_active(comboIndex);
453 schoenebeck 1225 }
454    
455    
456     BoolEntry::BoolEntry(const char* labelText) :
457     LabelWidget(labelText, checkbutton),
458 persson 1460 checkbutton(labelText)
459 schoenebeck 1225 {
460 persson 1460 checkbutton.signal_toggled().connect(sig_changed.make_slot());
461 schoenebeck 1225 }
462    
463    
464     StringEntry::StringEntry(const char* labelText) :
465     LabelWidget(labelText, entry)
466     {
467 persson 1582 entry.signal_changed().connect(sig_changed.make_slot());
468 schoenebeck 1225 }
469    
470 persson 2446 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 persson 1582 StringEntryMultiLine::StringEntryMultiLine(const char* labelText) :
481     LabelWidget(labelText, frame)
482 schoenebeck 1225 {
483 persson 1582 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 schoenebeck 1225 }
488    
489 persson 1582 gig::String StringEntryMultiLine::get_value() const
490 schoenebeck 1225 {
491 persson 1582 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 persson 2446 return gig_from_utf8(value);
495 schoenebeck 1225 }
496 persson 1582
497 persson 2446 void StringEntryMultiLine::set_value(const gig::String& value)
498 persson 1582 {
499 persson 2446 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 persson 1582 }
504 persson 2423
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