Add API to step though parameter enumerations
[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 && (upper <= lower || lower * upper <= 0)) {
161                 logarithmic = false;
162         }
163         if (rangesteps < 2) {
164                 rangesteps = 0;
165         }
166         if (enumeration) {
167                 if (!scale_points || scale_points->empty ()) {
168                         enumeration = false;
169                 }
170         }
171         if (integer_step) {
172                 if (lower >= upper) {
173                         integer_step = false;
174                 }
175         }
176
177         if (unit == ParameterDescriptor::MIDI_NOTE) {
178                 step      = smallstep = 1;  // semitone
179                 largestep = 12;             // octave
180         } else if (type == GainAutomation || type == TrimAutomation) {
181                 /* dB_coeff_step gives a step normalized for [0, max_gain].  This is
182                    like "slider position", so we convert from "slider position" to gain
183                    to have the correct unit here. */
184                 largestep = position_to_gain (dB_coeff_step(upper));
185                 step      = position_to_gain (largestep / 10.0);
186                 smallstep = step;
187         } else if (rangesteps > 1) {
188                 const float delta = upper - lower;
189
190                 step = smallstep = (delta / (rangesteps - 1)); // XXX
191                 largestep = std::min ((delta / 5.0f), 10.f * smallstep); // XXX
192
193                 if (logarithmic) {
194                         smallstep = smallstep / logf (rangesteps); // XXX
195                         step      = step      / logf (rangesteps);
196                         largestep = largestep / logf (rangesteps);
197                 } else if (integer_step) {
198                         smallstep = 1.0;
199                         step      = std::max(1.f, rintf (rangesteps));
200                         largestep = std::max(1.f, rintf (largestep));
201                 }
202         } else {
203                 const float delta = upper - lower;
204
205                 /* 30 happens to be the total number of steps for a fader with default
206                    max gain of 2.0 (6 dB), so we use 30 here too for consistency. */
207                 step      = smallstep = (delta / 300.0f);
208                 largestep = (delta / 30.0f);
209
210                 if (logarithmic) {
211                         /* Steps are linear, but we map them with pow like values (in
212                            internal_to_interface).  Thus, they are applied exponentially,
213                            which means too few steps.  So, divide to get roughly the
214                            desired number of steps (30).  This is not mathematically
215                            precise but seems to be about right for the controls I tried.
216                            If you're reading this, you've probably found a case where that
217                            isn't true, and somebody needs to sit down with a piece of paper
218                            and actually do the math. */
219                         smallstep = smallstep / logf(30.0f);
220                         step      = step      / logf(30.0f);
221                         largestep = largestep / logf(30.0f);
222                 } else if (integer_step) {
223                         smallstep = 1.0;
224                         step      = std::max(1.f, rintf (step));
225                         largestep = std::max(1.f, rintf (largestep));
226                 }
227         }
228 }
229
230 std::string
231 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
232 {
233         char buf[16];
234         if (b > 127) {
235                 snprintf(buf, sizeof(buf), "%d", b);
236                 return buf;
237         }
238
239         static const char* en_notes[] = {
240                 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
241         };
242
243         static const char* notes[] = {
244                 S_("Note|C"),
245                 S_("Note|C#"),
246                 S_("Note|D"),
247                 S_("Note|D#"),
248                 S_("Note|E"),
249                 S_("Note|F"),
250                 S_("Note|F#"),
251                 S_("Note|G"),
252                 S_("Note|G#"),
253                 S_("Note|A"),
254                 S_("Note|A#"),
255                 S_("Note|B")
256         };
257
258         /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
259         const int octave = b / 12 - 1;
260         const size_t p = b % 12;
261         snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
262         return buf;
263 }
264
265 std::string
266 ParameterDescriptor::normalize_note_name(const std::string& name)
267 {
268         // Remove whitespaces and convert to lower case for a more resilient parser
269         return boost::to_lower_copy(boost::erase_all_copy(name, " "));
270 };
271
272 ParameterDescriptor::NameNumMap
273 ParameterDescriptor::build_midi_name2num()
274 {
275         NameNumMap name2num;
276         for (uint8_t num = 0; num < 128; num++) {
277                 name2num[normalize_note_name(midi_note_name(num))] = num;
278         }
279         return name2num;
280 }
281
282 uint8_t
283 ParameterDescriptor::midi_note_num (const std::string& name)
284 {
285         static NameNumMap name2num = build_midi_name2num();
286
287         uint8_t num = -1;                       // -1 (or 255) is returned in case of failure
288
289         NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
290         if (it != name2num.end())
291                 num = it->second;
292
293         return num;
294 }
295
296 float
297 ParameterDescriptor::to_interface (float val) const
298 {
299         val = std::min (upper, std::max (lower, val));
300         switch(type) {
301                 case GainAutomation:
302                 case BusSendLevel:
303                 case EnvelopeAutomation:
304                         val = gain_to_slider_position_with_max (val, upper);
305                         break;
306                 case TrimAutomation:
307                         {
308                                 const float lower_db = accurate_coefficient_to_dB (lower);
309                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
310                                 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
311                         }
312                         break;
313                 case PanAzimuthAutomation:
314                 case PanElevationAutomation:
315                         val = val;
316                         break;
317                 case PanWidthAutomation:
318                         val = .5f + val * .5f;
319                         break;
320                 default:
321                         if (logarithmic) {
322                                 if (rangesteps > 1) {
323                                         val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
324                                 } else {
325                                         val = logscale_to_position (val, lower, upper);
326                                 }
327                         } else if (toggled) {
328                                 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
329                         } else if (integer_step) {
330                                 /* evenly-divide steps. lower,upper inclusive
331                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
332                                  * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
333                                  *       0             1             2             3             4
334                                  *      0.1           0.3           0.5           0.7           0.9
335                                  */
336                                 val = (val + .5f - lower) / (1.f + upper - lower);
337                         } else {
338                                 val = (val - lower) / (upper - lower);
339                         }
340                         break;
341         }
342         val = std::max (0.f, std::min (1.f, val));
343         return val;
344 }
345
346 float
347 ParameterDescriptor::from_interface (float val) const
348 {
349         val = std::max (0.f, std::min (1.f, val));
350
351         switch(type) {
352                 case GainAutomation:
353                 case EnvelopeAutomation:
354                 case BusSendLevel:
355                         val = slider_position_to_gain_with_max (val, upper);
356                         break;
357                 case TrimAutomation:
358                         {
359                                 const float lower_db = accurate_coefficient_to_dB (lower);
360                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
361                                 val = dB_to_coefficient (lower_db + val * range_db);
362                         }
363                         break;
364                 case PanAzimuthAutomation:
365                 case PanElevationAutomation:
366                          val = val;
367                         break;
368                 case PanWidthAutomation:
369                         val = 2.f * val - 1.f;
370                         break;
371                 default:
372                         if (logarithmic) {
373                                 assert (!toggled && !integer_step); // update_steps() should prevent that.
374                                 if (rangesteps > 1) {
375                                         val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
376                                 } else {
377                                         val = position_to_logscale (val, lower, upper);
378                                 }
379                         } else if (toggled) {
380                                 val = val > 0 ? upper : lower;
381                         } else if (integer_step) {
382                                 /* upper and lower are inclusive. use evenly-divided steps
383                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
384                                  * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
385                                  */
386                                 val =  round (lower + val * (1.f + upper - lower) - .5f);
387                         } else if (rangesteps > 1) {
388                                 /* similar to above, but for float controls */
389                                 val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
390                                 val = val * (upper - lower) + lower;
391                         } else {
392                                 val = val * (upper - lower) + lower;
393                         }
394                         break;
395         }
396         val = std::min (upper, std::max (lower, val));
397         return val;
398 }
399
400 bool
401 ParameterDescriptor::is_linear () const
402 {
403         if (logarithmic) {
404                 return false;
405         }
406         switch(type) {
407                 case GainAutomation:
408                 case EnvelopeAutomation:
409                 case BusSendLevel:
410                         return false;
411                 default:
412                         break;
413         }
414         return true;
415 }
416
417 float
418 ParameterDescriptor::compute_delta (float from, float to) const
419 {
420         if (is_linear ()) {
421                 return to - from;
422         }
423         if (from == 0) {
424                 return 0;
425         }
426         return to / from;
427 }
428
429 float
430 ParameterDescriptor::apply_delta (float val, float delta) const
431 {
432         if (is_linear ()) {
433                 return val + delta;
434         } else {
435                 return val * delta;
436         }
437 }
438
439 float
440 ParameterDescriptor::step_enum (float val, bool prev) const
441 {
442         if (!enumeration) {
443                 return val;
444         }
445         assert (scale_points && !scale_points->empty ());
446         float rv = scale_points->begin()->second;
447         float delta = fabsf (val - rv);
448         std::vector<float> avail;
449
450         for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
451                 float s = i->second;
452                 avail.push_back (s);
453                 if (fabsf (val - s) < delta) {
454                         rv = s;
455                         delta = fabsf (val - s);
456                 }
457         }
458         /* ScalePoints map is sorted by text string */
459         std::sort (avail.begin (), avail.end ());
460         std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
461         assert (it != avail.end());
462
463         if (prev) {
464                 if (it == avail.begin()) {
465                         return rv;
466                 }
467                 return *(--it);
468         } else {
469                 if (++it == avail.end()) {
470                         return rv;
471                 }
472                 return *(it);
473         }
474 }
475
476 } // namespace ARDOUR