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