2 Copyright (C) 2014 Paul Davis
3 Author: David Robillard
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)
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
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.
20 #include <boost/algorithm/string.hpp>
22 #include "pbd/control_math.h"
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"
35 ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
36 : Evoral::ParameterDescriptor()
38 , datatype(Variant::NOTHING)
39 , type((AutomationType)parameter.type())
44 , integer_step(parameter.type() >= MidiCCAutomation &&
45 parameter.type() <= MidiChannelPressureAutomation)
51 /* Note: defaults in Evoral::ParameterDescriptor */
53 switch((AutomationType)parameter.type()) {
56 upper = Config->get_max_gain();
69 case PanAzimuthAutomation:
70 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
73 case PanWidthAutomation:
78 case RecEnableAutomation:
79 case RecSafeAutomation:
84 case FadeInAutomation:
85 case FadeOutAutomation:
86 case EnvelopeAutomation:
96 case MidiCCAutomation:
97 case MidiPgmChangeAutomation:
98 case MidiChannelPressureAutomation:
99 case MidiNotePressureAutomation:
104 case MidiPitchBenderAutomation:
109 case PhaseAutomation:
112 case MonitoringAutomation:
116 upper = MonitorDisk; /* XXX bump when we add MonitorCue */
118 case SoloIsolateAutomation:
119 case SoloSafeAutomation:
129 ParameterDescriptor::ParameterDescriptor()
130 : Evoral::ParameterDescriptor()
132 , datatype(Variant::NOTHING)
133 , type(NullAutomation)
138 , integer_step(false)
139 , sr_dependent(false)
144 ParameterDescriptor::update_steps()
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);
157 /* note that LV2Plugin::get_parameter_descriptor ()
158 * overrides this is lv2:rangeStep is set for a port.
160 const float delta = upper - lower;
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);
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) {
181 step = std::max(1.f, rintf (step));
182 largestep = std::max(1.f, rintf (largestep));
188 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
192 snprintf(buf, sizeof(buf), "%d", b);
196 static const char* en_notes[] = {
197 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
200 static const char* notes[] = {
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);
223 ParameterDescriptor::normalize_note_name(const std::string& name)
225 // Remove whitespaces and convert to lower case for a more resilient parser
226 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
229 ParameterDescriptor::NameNumMap
230 ParameterDescriptor::build_midi_name2num()
233 for (uint8_t num = 0; num < 128; num++) {
234 name2num[normalize_note_name(midi_note_name(num))] = num;
240 ParameterDescriptor::midi_note_num (const std::string& name)
242 static NameNumMap name2num = build_midi_name2num();
244 uint8_t num = -1; // -1 (or 255) is returned in case of failure
246 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
247 if (it != name2num.end())
254 ParameterDescriptor::to_interface (float val) const
256 val = std::min (upper, std::max (lower, val));
260 case EnvelopeAutomation:
261 val = gain_to_slider_position_with_max (val, upper);
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;
270 case PanAzimuthAutomation:
271 case PanElevationAutomation:
274 case PanWidthAutomation:
275 val = .5f + val * .5f;
279 if (rangesteps > 1) {
280 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
282 val = logscale_to_position (val, lower, upper);
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]
291 * 0.1 0.3 0.5 0.7 0.9
293 val = (val + .5f - lower) / (1.f + upper - lower);
295 val = (val - lower) / (upper - lower);
299 val = std::max (0.f, std::min (1.f, val));
304 ParameterDescriptor::from_interface (float val) const
306 val = std::max (0.f, std::min (1.f, val));
310 case EnvelopeAutomation:
312 val = slider_position_to_gain_with_max (val, upper);
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);
321 case PanAzimuthAutomation:
322 case PanElevationAutomation:
325 case PanWidthAutomation:
326 val = 2.f * val - 1.f;
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);
334 val = position_to_logscale (val, lower, upper);
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]
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;
349 val = val * (upper - lower) + lower;
353 val = std::min (upper, std::max (lower, val));
357 } // namespace ARDOUR