Centralize Parameter scaling
[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 <boost/algorithm/string.hpp>
21
22 #include "pbd/control_math.h"
23
24 #include "ardour/amp.h"
25 #include "ardour/dB.h"
26 #include "ardour/parameter_descriptor.h"
27 #include "ardour/rc_configuration.h"
28 #include "ardour/types.h"
29 #include "ardour/utils.h"
30
31 #include "pbd/i18n.h"
32
33 namespace ARDOUR {
34
35 ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
36         : Evoral::ParameterDescriptor()
37         , key((uint32_t)-1)
38         , datatype(Variant::NOTHING)
39         , type((AutomationType)parameter.type())
40         , unit(NONE)
41         , step(0)
42         , smallstep(0)
43         , largestep(0)
44         , integer_step(parameter.type() >= MidiCCAutomation &&
45                        parameter.type() <= MidiChannelPressureAutomation)
46         , sr_dependent(false)
47         , enumeration(false)
48 {
49         ScalePoints sp;
50
51         /* Note: defaults in Evoral::ParameterDescriptor */
52
53         switch((AutomationType)parameter.type()) {
54         case GainAutomation:
55         case BusSendLevel:
56                 upper  = Config->get_max_gain();
57                 normal = 1.0f;
58                 break;
59         case BusSendEnable:
60                 normal = 1.0f;
61                 toggled = true;
62                 break;
63         case TrimAutomation:
64                 upper  = 10; // +20dB
65                 lower  = .1; // -20dB
66                 normal = 1.0f;
67                 logarithmic = true;
68                 break;
69         case PanAzimuthAutomation:
70                 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
71                 upper  = 1.0f;
72                 break;
73         case PanWidthAutomation:
74                 lower  = -1.0;
75                 upper  = 1.0;
76                 normal = 0.0f;
77                 break;
78         case RecEnableAutomation:
79         case RecSafeAutomation:
80                 lower  = 0.0;
81                 upper  = 1.0;
82                 toggled = true;
83                 break;
84         case FadeInAutomation:
85         case FadeOutAutomation:
86         case EnvelopeAutomation:
87                 upper  = 2.0f;
88                 normal = 1.0f;
89                 break;
90         case SoloAutomation:
91         case MuteAutomation:
92                 upper  = 1.0f;
93                 normal = 0.0f;
94                 toggled = true;
95                 break;
96         case MidiCCAutomation:
97         case MidiPgmChangeAutomation:
98         case MidiChannelPressureAutomation:
99         case MidiNotePressureAutomation:
100                 lower  = 0.0;
101                 normal = 0.0;
102                 upper  = 127.0;
103                 break;
104         case MidiPitchBenderAutomation:
105                 lower  = 0.0;
106                 normal = 8192.0;
107                 upper  = 16383.0;
108                 break;
109         case PhaseAutomation:
110                 toggled = true;
111                 break;
112         case MonitoringAutomation:
113                 enumeration = true;
114                 integer_step = true;
115                 lower = MonitorAuto;
116                 upper = MonitorDisk; /* XXX bump when we add MonitorCue */
117                 break;
118         case SoloIsolateAutomation:
119         case SoloSafeAutomation:
120                 toggled = true;
121                 break;
122         default:
123                 break;
124         }
125
126         update_steps();
127 }
128
129 ParameterDescriptor::ParameterDescriptor()
130         : Evoral::ParameterDescriptor()
131         , key((uint32_t)-1)
132         , datatype(Variant::NOTHING)
133         , type(NullAutomation)
134         , unit(NONE)
135         , step(0)
136         , smallstep(0)
137         , largestep(0)
138         , integer_step(false)
139         , sr_dependent(false)
140         , enumeration(false)
141 {}
142
143 void
144 ParameterDescriptor::update_steps()
145 {
146         if (unit == ParameterDescriptor::MIDI_NOTE) {
147                 step      = smallstep = 1;  // semitone
148                 largestep = 12;             // octave
149         } else if (type == GainAutomation || type == TrimAutomation) {
150                 /* dB_coeff_step gives a step normalized for [0, max_gain].  This is
151                    like "slider position", so we convert from "slider position" to gain
152                    to have the correct unit here. */
153                 largestep = position_to_gain (dB_coeff_step(upper));
154                 step      = position_to_gain (largestep / 10.0);
155                 smallstep = step;
156         } else {
157                 /* note that LV2Plugin::get_parameter_descriptor ()
158                  * overrides this is lv2:rangeStep is set for a port.
159                  */
160                 const float delta = upper - lower;
161
162                 /* 30 happens to be the total number of steps for a fader with default
163                    max gain of 2.0 (6 dB), so we use 30 here too for consistency. */
164                 step      = smallstep = (delta / 300.0f);
165                 largestep = (delta / 30.0f);
166
167                 if (logarithmic) {
168                         /* Steps are linear, but we map them with pow like values (in
169                            internal_to_interface).  Thus, they are applied exponentially,
170                            which means too few steps.  So, divide to get roughly the
171                            desired number of steps (30).  This is not mathematically
172                            precise but seems to be about right for the controls I tried.
173                            If you're reading this, you've probably found a case where that
174                            isn't true, and somebody needs to sit down with a piece of paper
175                            and actually do the math. */
176                         smallstep = smallstep / logf(30.0f);
177                         step      = step      / logf(30.0f);
178                         largestep = largestep / logf(30.0f);
179                 } else if (integer_step) {
180                         smallstep = 1.0;
181                         step      = std::max(1.f, rintf (step));
182                         largestep = std::max(1.f, rintf (largestep));
183                 }
184         }
185 }
186
187 std::string
188 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
189 {
190         char buf[16];
191         if (b > 127) {
192                 snprintf(buf, sizeof(buf), "%d", b);
193                 return buf;
194         }
195
196         static const char* en_notes[] = {
197                 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
198         };
199
200         static const char* notes[] = {
201                 S_("Note|C"),
202                 S_("Note|C#"),
203                 S_("Note|D"),
204                 S_("Note|D#"),
205                 S_("Note|E"),
206                 S_("Note|F"),
207                 S_("Note|F#"),
208                 S_("Note|G"),
209                 S_("Note|G#"),
210                 S_("Note|A"),
211                 S_("Note|A#"),
212                 S_("Note|B")
213         };
214
215         /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
216         const int octave = b / 12 - 1;
217         const size_t p = b % 12;
218         snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
219         return buf;
220 }
221
222 std::string
223 ParameterDescriptor::normalize_note_name(const std::string& name)
224 {
225         // Remove whitespaces and convert to lower case for a more resilient parser
226         return boost::to_lower_copy(boost::erase_all_copy(name, " "));
227 };
228
229 ParameterDescriptor::NameNumMap
230 ParameterDescriptor::build_midi_name2num()
231 {
232         NameNumMap name2num;
233         for (uint8_t num = 0; num < 128; num++) {
234                 name2num[normalize_note_name(midi_note_name(num))] = num;
235         }
236         return name2num;
237 }
238
239 uint8_t
240 ParameterDescriptor::midi_note_num (const std::string& name)
241 {
242         static NameNumMap name2num = build_midi_name2num();
243
244         uint8_t num = -1;                       // -1 (or 255) is returned in case of failure
245
246         NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
247         if (it != name2num.end())
248                 num = it->second;
249
250         return num;
251 }
252
253 float
254 ParameterDescriptor::to_interface (float val) const
255 {
256         val = std::min (upper, std::max (lower, val));
257         switch(type) {
258                 case GainAutomation:
259                 case BusSendLevel:
260                 case EnvelopeAutomation:
261                         val = gain_to_slider_position_with_max (val, upper);
262                         break;
263                 case TrimAutomation:
264                         {
265                                 const float lower_db = accurate_coefficient_to_dB (lower);
266                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
267                                 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
268                         }
269                         break;
270                 case PanAzimuthAutomation:
271                 case PanElevationAutomation:
272                         val = 1.f - val;
273                         break;
274                 case PanWidthAutomation:
275                         val = .5f + val * .5f;
276                         break;
277                 default:
278                         if (logarithmic) {
279                                 if (rangesteps > 1) {
280                                         val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
281                                 } else {
282                                         val = logscale_to_position (val, lower, upper);
283                                 }
284                         } else if (toggled) {
285                                 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
286                         } else if (integer_step) {
287                                 /* evenly-divide steps. lower,upper inclusive
288                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
289                                  * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
290                                  *       0             1             2             3             4
291                                  *      0.1           0.3           0.5           0.7           0.9
292                                  */
293                                 val = (val + .5f - lower) / (1.f + upper - lower);
294                         } else {
295                                 val = (val - lower) / (upper - lower);
296                         }
297                         break;
298         }
299         val = std::max (0.f, std::min (1.f, val));
300         return val;
301 }
302
303 float
304 ParameterDescriptor::from_interface (float val) const
305 {
306         val = std::max (0.f, std::min (1.f, val));
307
308         switch(type) {
309                 case GainAutomation:
310                 case EnvelopeAutomation:
311                 case BusSendLevel:
312                         val = slider_position_to_gain_with_max (val, upper);
313                         break;
314                 case TrimAutomation:
315                         {
316                                 const float lower_db = accurate_coefficient_to_dB (lower);
317                                 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
318                                 val = dB_to_coefficient (lower_db + val * range_db);
319                         }
320                         break;
321                 case PanAzimuthAutomation:
322                 case PanElevationAutomation:
323                          val = 1.f - val;
324                         break;
325                 case PanWidthAutomation:
326                         val = 2.f * val - 1.f;
327                         break;
328                 default:
329                         if (logarithmic) {
330                                 assert (!toggled && !integer_step); // update_steps() should prevent that.
331                                 if (rangesteps > 1) {
332                                         val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
333                                 } else {
334                                         val = position_to_logscale (val, lower, upper);
335                                 }
336                         } else if (toggled) {
337                                 val = val > 0 ? upper : lower;
338                         } else if (integer_step) {
339                                 /* upper and lower are inclusive. use evenly-divided steps
340                                  * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
341                                  * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
342                                  */
343                                 val =  round (lower + val * (1.f + upper - lower) - .5f);
344                         } else if (rangesteps > 1) {
345                                 /* similar to above, but for float controls */
346                                 val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
347                                 val = val * (upper - lower) + lower;
348                         } else {
349                                 val = val * (upper - lower) + lower;
350                         }
351                         break;
352         }
353         val = std::min (upper, std::max (lower, val));
354         return val;
355 }
356
357 } // namespace ARDOUR