debug instrumentation for locate time
[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 (rangesteps > 1) {
204                 const float delta = upper - lower;
205                 if (logarithmic) {
206                         smallstep = step = (powf (delta, 1.f  / (float)rangesteps) - 1.f) * lower;
207                         largestep = (powf (delta, std::max (0.5f, 10.f / (float)rangesteps)) - 1.f) * lower;
208                 } else if (integer_step) {
209                         smallstep = step = 1.0;
210                         largestep = std::max(1.f, rintf (delta / (rangesteps - 1)));
211                 } else {
212                         step = smallstep = delta / (rangesteps - 1);
213                         largestep = std::min ((delta / 4.0f), 10.f * smallstep); // XXX
214                 }
215         } else {
216                 const float delta = upper - lower;
217                 /* 30 steps between min/max (300 for fine-grained) */
218                 if (logarithmic) {
219                         smallstep = step = (powf (delta, 1.f / 300.f) - 1.f) * lower;
220                         largestep = (powf (delta, 1.f / 30.f) - 1.f) * lower;
221                 } else if (integer_step) {
222                         smallstep = step = 1.0;
223                         largestep = std::max(1.f, rintf (delta / 30.f));
224                 } else {
225                         step      = smallstep = (delta / 300.0f);
226                         largestep = (delta / 30.0f);
227                 }
228         }
229 }
230
231 std::string
232 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
233 {
234         char buf[16];
235         if (b > 127) {
236                 snprintf(buf, sizeof(buf), "%d", b);
237                 return buf;
238         }
239
240         static const char* en_notes[] = {
241                 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
242         };
243
244         static const char* notes[] = {
245                 S_("Note|C"),
246                 S_("Note|C#"),
247                 S_("Note|D"),
248                 S_("Note|D#"),
249                 S_("Note|E"),
250                 S_("Note|F"),
251                 S_("Note|F#"),
252                 S_("Note|G"),
253                 S_("Note|G#"),
254                 S_("Note|A"),
255                 S_("Note|A#"),
256                 S_("Note|B")
257         };
258
259         /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
260         const int octave = b / 12 - 1;
261         const size_t p = b % 12;
262         snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
263         return buf;
264 }
265
266 std::string
267 ParameterDescriptor::normalize_note_name(const std::string& name)
268 {
269         // Remove whitespaces and convert to lower case for a more resilient parser
270         return boost::to_lower_copy(boost::erase_all_copy(name, " "));
271 };
272
273 ParameterDescriptor::NameNumMap
274 ParameterDescriptor::build_midi_name2num()
275 {
276         NameNumMap name2num;
277         for (uint8_t num = 0; num < 128; num++) {
278                 name2num[normalize_note_name(midi_note_name(num))] = num;
279         }
280         return name2num;
281 }
282
283 uint8_t
284 ParameterDescriptor::midi_note_num (const std::string& name)
285 {
286         static NameNumMap name2num = build_midi_name2num();
287
288         uint8_t num = -1;                       // -1 (or 255) is returned in case of failure
289
290         NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
291         if (it != name2num.end())
292                 num = it->second;
293
294         return num;
295 }
296
297 float
298 ParameterDescriptor::to_interface (float val) const
299 {
300         val = std::min (upper, std::max (lower, val));
301         switch(type) {
302                 case GainAutomation:
303                 case BusSendLevel:
304                 case EnvelopeAutomation:
305                         val = gain_to_slider_position_with_max (val, upper);
306                         break;
307                 case TrimAutomation:
308                         {
309                                 const float lower_db = accurate_coefficient_to_dB (lower);
310                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
311                                 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
312                         }
313                         break;
314                 case PanAzimuthAutomation:
315                 case PanElevationAutomation:
316                         val = val;
317                         break;
318                 case PanWidthAutomation:
319                         val = .5f + val * .5f;
320                         break;
321                 default:
322                         if (logarithmic) {
323                                 if (rangesteps > 1) {
324                                         val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
325                                 } else {
326                                         val = logscale_to_position (val, lower, upper);
327                                 }
328                         } else if (toggled) {
329                                 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
330                         } else if (integer_step) {
331                                 /* evenly-divide steps. lower,upper inclusive
332                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
333                                  * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
334                                  *       0             1             2             3             4
335                                  *      0.1           0.3           0.5           0.7           0.9
336                                  */
337                                 val = (val + .5f - lower) / (1.f + upper - lower);
338                         } else {
339                                 val = (val - lower) / (upper - lower);
340                         }
341                         break;
342         }
343         val = std::max (0.f, std::min (1.f, val));
344         return val;
345 }
346
347 float
348 ParameterDescriptor::from_interface (float val) const
349 {
350         val = std::max (0.f, std::min (1.f, val));
351
352         switch(type) {
353                 case GainAutomation:
354                 case EnvelopeAutomation:
355                 case BusSendLevel:
356                         val = slider_position_to_gain_with_max (val, upper);
357                         break;
358                 case TrimAutomation:
359                         {
360                                 const float lower_db = accurate_coefficient_to_dB (lower);
361                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
362                                 val = dB_to_coefficient (lower_db + val * range_db);
363                         }
364                         break;
365                 case PanAzimuthAutomation:
366                 case PanElevationAutomation:
367                          val = val;
368                         break;
369                 case PanWidthAutomation:
370                         val = 2.f * val - 1.f;
371                         break;
372                 default:
373                         if (logarithmic) {
374                                 assert (!toggled && !integer_step); // update_steps() should prevent that.
375                                 if (rangesteps > 1) {
376                                         val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
377                                 } else {
378                                         val = position_to_logscale (val, lower, upper);
379                                 }
380                         } else if (toggled) {
381                                 val = val > 0 ? upper : lower;
382                         } else if (integer_step) {
383                                 /* upper and lower are inclusive. use evenly-divided steps
384                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
385                                  * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
386                                  */
387                                 val =  round (lower + val * (1.f + upper - lower) - .5f);
388                         } else if (rangesteps > 1) {
389                                 /* similar to above, but for float controls */
390                                 val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
391                                 val = val * (upper - lower) + lower;
392                         } else {
393                                 val = val * (upper - lower) + lower;
394                         }
395                         break;
396         }
397         val = std::min (upper, std::max (lower, val));
398         return val;
399 }
400
401 bool
402 ParameterDescriptor::is_linear () const
403 {
404         if (logarithmic) {
405                 return false;
406         }
407         switch(type) {
408                 case GainAutomation:
409                 case EnvelopeAutomation:
410                 case BusSendLevel:
411                         return false;
412                 default:
413                         break;
414         }
415         return true;
416 }
417
418 float
419 ParameterDescriptor::compute_delta (float from, float to) const
420 {
421         if (is_linear ()) {
422                 return to - from;
423         }
424         if (from == 0) {
425                 return 0;
426         }
427         return to / from;
428 }
429
430 float
431 ParameterDescriptor::apply_delta (float val, float delta) const
432 {
433         if (is_linear ()) {
434                 return val + delta;
435         } else {
436                 return val * delta;
437         }
438 }
439
440 float
441 ParameterDescriptor::step_enum (float val, bool prev) const
442 {
443         if (!enumeration) {
444                 return val;
445         }
446         assert (scale_points && !scale_points->empty ());
447         float rv = scale_points->begin()->second;
448         float delta = fabsf (val - rv);
449         std::vector<float> avail;
450
451         for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
452                 float s = i->second;
453                 avail.push_back (s);
454                 if (fabsf (val - s) < delta) {
455                         rv = s;
456                         delta = fabsf (val - s);
457                 }
458         }
459         /* ScalePoints map is sorted by text string */
460         std::sort (avail.begin (), avail.end ());
461         std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
462         assert (it != avail.end());
463
464         if (prev) {
465                 if (it == avail.begin()) {
466                         return rv;
467                 }
468                 return *(--it);
469         } else {
470                 if (++it == avail.end()) {
471                         return rv;
472                 }
473                 return *(it);
474         }
475 }
476
477 } // namespace ARDOUR