Move logarithmic property into Evoral, add rangesteps
[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         , min_unbound(0)
48         , max_unbound(0)
49         , enumeration(false)
50 {
51         ScalePoints sp;
52
53         switch((AutomationType)parameter.type()) {
54         case GainAutomation:
55                 upper  = Config->get_max_gain();
56                 normal = 1.0f;
57                 break;
58         case BusSendLevel:
59                 upper = Config->get_max_gain ();
60                 normal = 1.0f;
61                 break;
62         case BusSendEnable:
63                 normal = 1.0f;
64                 toggled = true;
65                 break;
66         case TrimAutomation:
67                 upper  = 10; // +20dB
68                 lower  = .1; // -20dB
69                 normal = 1.0f;
70                 break;
71         case PanAzimuthAutomation:
72                 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
73                 upper  = 1.0f;
74                 break;
75         case PanWidthAutomation:
76                 lower  = -1.0;
77                 upper  = 1.0;
78                 normal = 0.0f;
79                 break;
80         case RecEnableAutomation:
81         case RecSafeAutomation:
82                 lower  = 0.0;
83                 upper  = 1.0;
84                 toggled = true;
85                 break;
86         case PluginAutomation:
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                 break;
107         case MidiPitchBenderAutomation:
108                 lower  = 0.0;
109                 normal = 8192.0;
110                 upper  = 16383.0;
111                 break;
112         case PhaseAutomation:
113                 toggled = true;
114                 break;
115         case MonitoringAutomation:
116                 enumeration = true;
117                 integer_step = true;
118                 lower = MonitorAuto;
119                 upper = MonitorDisk; /* XXX bump when we add MonitorCue */
120                 break;
121         case SoloIsolateAutomation:
122                 toggled = true;
123                 break;
124         case SoloSafeAutomation:
125                 toggled = true;
126                 break;
127         default:
128                 break;
129         }
130
131         update_steps();
132 }
133
134 ParameterDescriptor::ParameterDescriptor()
135         : Evoral::ParameterDescriptor()
136         , key((uint32_t)-1)
137         , datatype(Variant::NOTHING)
138         , type(NullAutomation)
139         , unit(NONE)
140         , step(0)
141         , smallstep(0)
142         , largestep(0)
143         , integer_step(false)
144         , sr_dependent(false)
145         , min_unbound(0)
146         , max_unbound(0)
147         , enumeration(false)
148 {}
149
150 void
151 ParameterDescriptor::update_steps()
152 {
153         if (unit == ParameterDescriptor::MIDI_NOTE) {
154                 step      = smallstep = 1;  // semitone
155                 largestep = 12;             // octave
156         } else if (type == GainAutomation || type == TrimAutomation) {
157                 /* dB_coeff_step gives a step normalized for [0, max_gain].  This is
158                    like "slider position", so we convert from "slider position" to gain
159                    to have the correct unit here. */
160                 largestep = position_to_gain (dB_coeff_step(upper));
161                 step      = position_to_gain (largestep / 10.0);
162                 smallstep = step;
163         } else {
164                 /* note that LV2Plugin::get_parameter_descriptor ()
165                  * overrides this is lv2:rangeStep is set for a port.
166                  */
167                 const float delta = upper - lower;
168
169                 /* 30 happens to be the total number of steps for a fader with default
170                    max gain of 2.0 (6 dB), so we use 30 here too for consistency. */
171                 step      = smallstep = (delta / 300.0f);
172                 largestep = (delta / 30.0f);
173
174                 if (logarithmic) {
175                         /* Steps are linear, but we map them with pow like values (in
176                            internal_to_interface).  Thus, they are applied exponentially,
177                            which means too few steps.  So, divide to get roughly the
178                            desired number of steps (30).  This is not mathematically
179                            precise but seems to be about right for the controls I tried.
180                            If you're reading this, you've probably found a case where that
181                            isn't true, and somebody needs to sit down with a piece of paper
182                            and actually do the math. */
183                         smallstep = smallstep / logf(30.0f);
184                         step      = step      / logf(30.0f);
185                         largestep = largestep / logf(30.0f);
186                 } else if (integer_step) {
187                         smallstep = 1.0;
188                         step      = std::max(1.f, rintf (step));
189                         largestep = std::max(1.f, rintf (largestep));
190                 }
191         }
192 }
193
194 std::string
195 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
196 {
197         char buf[16];
198         if (b > 127) {
199                 snprintf(buf, sizeof(buf), "%d", b);
200                 return buf;
201         }
202
203         static const char* en_notes[] = {
204                 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
205         };
206
207         static const char* notes[] = {
208                 S_("Note|C"),
209                 S_("Note|C#"),
210                 S_("Note|D"),
211                 S_("Note|D#"),
212                 S_("Note|E"),
213                 S_("Note|F"),
214                 S_("Note|F#"),
215                 S_("Note|G"),
216                 S_("Note|G#"),
217                 S_("Note|A"),
218                 S_("Note|A#"),
219                 S_("Note|B")
220         };
221
222         /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
223         const int octave = b / 12 - 1;
224         const size_t p = b % 12;
225         snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
226         return buf;
227 }
228
229 std::string
230 ParameterDescriptor::normalize_note_name(const std::string& name)
231 {
232         // Remove whitespaces and convert to lower case for a more resilient parser
233         return boost::to_lower_copy(boost::erase_all_copy(name, " "));
234 };
235
236 ParameterDescriptor::NameNumMap
237 ParameterDescriptor::build_midi_name2num()
238 {
239         NameNumMap name2num;
240         for (uint8_t num = 0; num < 128; num++) {
241                 name2num[normalize_note_name(midi_note_name(num))] = num;
242         }
243         return name2num;
244 }
245
246 uint8_t
247 ParameterDescriptor::midi_note_num (const std::string& name)
248 {
249         static NameNumMap name2num = build_midi_name2num();
250
251         uint8_t num = -1;                       // -1 (or 255) is returned in case of failure
252
253         NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
254         if (it != name2num.end())
255                 num = it->second;
256
257         return num;
258 }
259
260 } // namespace ARDOUR