Create sidechain ports when created from template (#0007680)
[ardour.git] / libs / ardour / parameter_descriptor.cc
1 /*
2     Copyright (C) 2014 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify it
6     under the terms of the GNU General Public License as published by the Free
7     Software Foundation; either version 2 of the License, or (at your option)
8     any later version.
9
10     This program is distributed in the hope that it will be useful, but WITHOUT
11     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13     for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <algorithm>
21 #include <boost/algorithm/string.hpp>
22
23 #include "pbd/control_math.h"
24
25 #include "ardour/amp.h"
26 #include "ardour/dB.h"
27 #include "ardour/parameter_descriptor.h"
28 #include "ardour/rc_configuration.h"
29 #include "ardour/types.h"
30 #include "ardour/utils.h"
31
32 #include "pbd/i18n.h"
33
34 namespace ARDOUR {
35
36 ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
37         : Evoral::ParameterDescriptor()
38         , key((uint32_t)-1)
39         , datatype(Variant::NOTHING)
40         , type((AutomationType)parameter.type())
41         , unit(NONE)
42         , step(0)
43         , smallstep(0)
44         , largestep(0)
45         , integer_step(parameter.type() >= MidiCCAutomation &&
46                        parameter.type() <= MidiChannelPressureAutomation)
47         , sr_dependent(false)
48         , enumeration(false)
49 {
50         ScalePoints sp;
51
52         /* Note: defaults in Evoral::ParameterDescriptor */
53
54         switch((AutomationType)parameter.type()) {
55         case GainAutomation:
56         case BusSendLevel:
57                 upper  = Config->get_max_gain();
58                 normal = 1.0f;
59                 break;
60         case BusSendEnable:
61                 normal = 1.0f;
62                 toggled = true;
63                 break;
64         case TrimAutomation:
65                 upper  = 10; // +20dB
66                 lower  = .1; // -20dB
67                 normal = 1.0f;
68                 logarithmic = true;
69                 break;
70         case PanAzimuthAutomation:
71                 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
72                 upper  = 1.0f;
73                 break;
74         case PanWidthAutomation:
75                 lower  = -1.0;
76                 upper  = 1.0;
77                 normal = 0.0f;
78                 break;
79         case RecEnableAutomation:
80         case RecSafeAutomation:
81                 lower  = 0.0;
82                 upper  = 1.0;
83                 toggled = true;
84                 break;
85         case FadeInAutomation:
86         case FadeOutAutomation:
87         case EnvelopeAutomation:
88                 upper  = 2.0f;
89                 normal = 1.0f;
90                 break;
91         case SoloAutomation:
92         case MuteAutomation:
93                 upper  = 1.0f;
94                 normal = 0.0f;
95                 toggled = true;
96                 break;
97         case MidiCCAutomation:
98         case MidiPgmChangeAutomation:
99         case MidiChannelPressureAutomation:
100         case MidiNotePressureAutomation:
101                 lower  = 0.0;
102                 normal = 0.0;
103                 upper  = 127.0;
104                 print_fmt = "%.0f";
105                 break;
106         case MidiPitchBenderAutomation:
107                 lower  = 0.0;
108                 normal = 8192.0;
109                 upper  = 16383.0;
110                 print_fmt = "%.0f";
111                 break;
112         case PhaseAutomation:
113                 toggled = true;
114                 scale_points = boost::shared_ptr<ScalePoints>(new ScalePoints());
115                 scale_points->insert (std::make_pair (_("Normal"), 0));
116                 scale_points->insert (std::make_pair (_("Invert"), 1));
117                 break;
118         case MonitoringAutomation:
119                 enumeration = true;
120                 integer_step = true;
121                 lower = MonitorAuto;
122                 upper = MonitorDisk; /* XXX bump when we add MonitorCue */
123                 scale_points = boost::shared_ptr<ScalePoints>(new ScalePoints());
124                 scale_points->insert (std::make_pair (_("Auto"), MonitorAuto));
125                 scale_points->insert (std::make_pair (_("Input"), MonitorInput));
126                 scale_points->insert (std::make_pair (_("Disk"), MonitorDisk));
127                 break;
128         case SoloIsolateAutomation:
129         case SoloSafeAutomation:
130                 toggled = true;
131                 break;
132         default:
133                 break;
134         }
135
136         update_steps();
137 }
138
139 ParameterDescriptor::ParameterDescriptor()
140         : Evoral::ParameterDescriptor()
141         , key((uint32_t)-1)
142         , datatype(Variant::NOTHING)
143         , type(NullAutomation)
144         , unit(NONE)
145         , step(0)
146         , smallstep(0)
147         , largestep(0)
148         , integer_step(false)
149         , sr_dependent(false)
150         , enumeration(false)
151 {}
152
153 void
154 ParameterDescriptor::update_steps()
155 {
156         /* sanitize flags */
157         if (toggled || enumeration) {
158                 logarithmic = false;
159         }
160         if (logarithmic && sr_dependent && upper > lower && lower == 0) {
161                 /* work-around for plugins with a log-scale control 0..SR; log (0) is not defined */
162                 lower = upper / 1000.f;
163         }
164         if (logarithmic && (upper <= lower || lower * upper <= 0)) {
165                 /* log-scale params need upper > lower and both values need the same sign */
166                 logarithmic = false;
167         }
168         if (rangesteps < 2) {
169                 rangesteps = 0;
170         }
171         if (enumeration) {
172                 /* enums need scale-points.
173                  * The GUI is more restrictive, a dropdown is displayed
174                  * IIF  scale_points.size() == (1 + upper - lower)
175                  */
176                 if (!scale_points || scale_points->empty ()) {
177                         enumeration = false;
178                 }
179         }
180         if (integer_step) {
181                 if (lower >= upper) {
182                         integer_step = false;
183                 }
184         }
185
186         /* upper == lower does not make any sense */
187         if (lower == upper) {
188                 upper = lower + 0.01; // add some arbitrary value
189         }
190
191         /* set steps */
192
193         if (unit == ParameterDescriptor::MIDI_NOTE) {
194                 step      = smallstep = 1;  // semitone
195                 largestep = 12;             // octave
196         } else if (type == GainAutomation || type == TrimAutomation) {
197                 /* dB_coeff_step gives a step normalized for [0, max_gain].  This is
198                    like "slider position", so we convert from "slider position" to gain
199                    to have the correct unit here. */
200                 largestep = position_to_gain (dB_coeff_step(upper));
201                 step      = position_to_gain (largestep / 10.0);
202                 smallstep = step;
203         } else if (logarithmic) {
204                 /* ignore logscale rangesteps. {small|large}steps are used with the spinbox.
205                  * gtk-spinbox shows the internal (not interface) value and up/down
206                  * arrows linearly increase.
207                  * The AutomationController uses internal_to_interface():
208                  *   ui-step [0..1] -> log (1 + largestep / lower) / log (upper / lower)
209                  * so we use a step that's a multiple of "lower" for the interface step:
210                  *   log (1 + x) / log (upper / lower)
211                  */
212                 smallstep = step = lower / 11;
213                 largestep = lower / 3;
214                 /* NOTE: the actual value does use rangesteps via
215                  * logscale_to_position_with_steps(), position_to_logscale_with_steps()
216                  * when it is converted.
217                  */
218         } else if (rangesteps > 1) {
219                 const float delta = upper - lower;
220                 if (integer_step) {
221                         smallstep = step = 1.0;
222                         largestep = std::max(1.f, rintf (delta / (rangesteps - 1.f)));
223                 } else {
224                         step = smallstep = delta / (rangesteps - 1.f);
225                         largestep = std::min ((delta / 4.0f), 10.f * smallstep);
226                 }
227         } else {
228                 const float delta = upper - lower;
229                 /* 30 steps between min/max (300 for fine-grained) */
230                 if (integer_step) {
231                         smallstep = step = 1.0;
232                         largestep = std::max(1.f, rintf (delta / 30.f));
233                 } else {
234                         step      = smallstep = (delta / 300.0f);
235                         largestep = (delta / 30.0f);
236                 }
237         }
238 }
239
240 std::string
241 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
242 {
243         char buf[16];
244         if (b > 127) {
245                 snprintf(buf, sizeof(buf), "%d", b);
246                 return buf;
247         }
248
249         static const char* en_notes[] = {
250                 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
251         };
252
253         static const char* notes[] = {
254                 S_("Note|C"),
255                 S_("Note|C#"),
256                 S_("Note|D"),
257                 S_("Note|D#"),
258                 S_("Note|E"),
259                 S_("Note|F"),
260                 S_("Note|F#"),
261                 S_("Note|G"),
262                 S_("Note|G#"),
263                 S_("Note|A"),
264                 S_("Note|A#"),
265                 S_("Note|B")
266         };
267
268         /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
269         const int octave = b / 12 - 1;
270         const size_t p = b % 12;
271         snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
272         return buf;
273 }
274
275 std::string
276 ParameterDescriptor::normalize_note_name(const std::string& name)
277 {
278         // Remove whitespaces and convert to lower case for a more resilient parser
279         return boost::to_lower_copy(boost::erase_all_copy(name, " "));
280 };
281
282 ParameterDescriptor::NameNumMap
283 ParameterDescriptor::build_midi_name2num()
284 {
285         NameNumMap name2num;
286         for (uint8_t num = 0; num < 128; num++) {
287                 name2num[normalize_note_name(midi_note_name(num))] = num;
288         }
289         return name2num;
290 }
291
292 uint8_t
293 ParameterDescriptor::midi_note_num (const std::string& name)
294 {
295         static NameNumMap name2num = build_midi_name2num();
296
297         uint8_t num = -1;                       // -1 (or 255) is returned in case of failure
298
299         NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
300         if (it != name2num.end())
301                 num = it->second;
302
303         return num;
304 }
305
306 float
307 ParameterDescriptor::to_interface (float val) const
308 {
309         val = std::min (upper, std::max (lower, val));
310         switch(type) {
311                 case GainAutomation:
312                 case BusSendLevel:
313                 case EnvelopeAutomation:
314                         val = gain_to_slider_position_with_max (val, upper);
315                         break;
316                 case TrimAutomation:
317                         {
318                                 const float lower_db = accurate_coefficient_to_dB (lower);
319                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
320                                 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
321                         }
322                         break;
323                 case PanAzimuthAutomation:
324                 case PanElevationAutomation:
325                         val = val;
326                         break;
327                 case PanWidthAutomation:
328                         val = .5f + val * .5f;
329                         break;
330                 default:
331                         if (logarithmic) {
332                                 if (rangesteps > 1) {
333                                         val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
334                                 } else {
335                                         val = logscale_to_position (val, lower, upper);
336                                 }
337                         } else if (toggled) {
338                                 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
339                         } else if (integer_step) {
340                                 /* evenly-divide steps. lower,upper inclusive
341                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
342                                  * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
343                                  *       0             1             2             3             4
344                                  *      0.1           0.3           0.5           0.7           0.9
345                                  */
346                                 val = (val + .5f - lower) / (1.f + upper - lower);
347                         } else {
348                                 val = (val - lower) / (upper - lower);
349                         }
350                         break;
351         }
352         val = std::max (0.f, std::min (1.f, val));
353         return val;
354 }
355
356 float
357 ParameterDescriptor::from_interface (float val) const
358 {
359         val = std::max (0.f, std::min (1.f, val));
360
361         switch(type) {
362                 case GainAutomation:
363                 case EnvelopeAutomation:
364                 case BusSendLevel:
365                         val = slider_position_to_gain_with_max (val, upper);
366                         break;
367                 case TrimAutomation:
368                         {
369                                 const float lower_db = accurate_coefficient_to_dB (lower);
370                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
371                                 val = dB_to_coefficient (lower_db + val * range_db);
372                         }
373                         break;
374                 case PanAzimuthAutomation:
375                 case PanElevationAutomation:
376                          val = val;
377                         break;
378                 case PanWidthAutomation:
379                         val = 2.f * val - 1.f;
380                         break;
381                 default:
382                         if (logarithmic) {
383                                 assert (!toggled && !integer_step); // update_steps() should prevent that.
384                                 if (rangesteps > 1) {
385                                         val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
386                                 } else {
387                                         val = position_to_logscale (val, lower, upper);
388                                 }
389                         } else if (toggled) {
390                                 val = val > 0 ? upper : lower;
391                         } else if (integer_step) {
392                                 /* upper and lower are inclusive. use evenly-divided steps
393                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
394                                  * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
395                                  */
396                                 val = floor (lower + val * (1.f + upper - lower));
397                         } else if (rangesteps > 1) {
398                                 /* similar to above, but for float controls */
399                                 val = round (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
400                                 val = val * (upper - lower) + lower;
401                         } else {
402                                 val = val * (upper - lower) + lower;
403                         }
404                         break;
405         }
406         val = std::min (upper, std::max (lower, val));
407         return val;
408 }
409
410 bool
411 ParameterDescriptor::is_linear () const
412 {
413         if (logarithmic) {
414                 return false;
415         }
416         switch(type) {
417                 case GainAutomation:
418                 case EnvelopeAutomation:
419                 case BusSendLevel:
420                         return false;
421                 default:
422                         break;
423         }
424         return true;
425 }
426
427 float
428 ParameterDescriptor::compute_delta (float from, float to) const
429 {
430         if (is_linear ()) {
431                 return to - from;
432         }
433         if (from == 0) {
434                 return 0;
435         }
436         return to / from;
437 }
438
439 float
440 ParameterDescriptor::apply_delta (float val, float delta) const
441 {
442         if (is_linear ()) {
443                 return val + delta;
444         } else {
445                 return val * delta;
446         }
447 }
448
449 float
450 ParameterDescriptor::step_enum (float val, bool prev) const
451 {
452         if (!enumeration) {
453                 return val;
454         }
455         assert (scale_points && !scale_points->empty ());
456         float rv = scale_points->begin()->second;
457         float delta = fabsf (val - rv);
458         std::vector<float> avail;
459
460         for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
461                 float s = i->second;
462                 avail.push_back (s);
463                 if (fabsf (val - s) < delta) {
464                         rv = s;
465                         delta = fabsf (val - s);
466                 }
467         }
468         /* ScalePoints map is sorted by text string */
469         std::sort (avail.begin (), avail.end ());
470         std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
471         assert (it != avail.end());
472
473         if (prev) {
474                 if (it == avail.begin()) {
475                         return rv;
476                 }
477                 return *(--it);
478         } else {
479                 if (++it == avail.end()) {
480                         return rv;
481                 }
482                 return *(it);
483         }
484 }
485
486 } // namespace ARDOUR