2 * Copyright (C) 2014 David Robillard <d@drobilla.net>
3 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
4 * Copyright (C) 2016 Paul Davis <paul@linuxaudiosystems.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <boost/algorithm/string.hpp>
24 #include "pbd/control_math.h"
26 #include "ardour/amp.h"
27 #include "ardour/dB.h"
28 #include "ardour/parameter_descriptor.h"
29 #include "ardour/rc_configuration.h"
30 #include "ardour/types.h"
31 #include "ardour/utils.h"
37 ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
38 : Evoral::ParameterDescriptor()
40 , datatype(Variant::NOTHING)
41 , type((AutomationType)parameter.type())
46 , integer_step(parameter.type() >= MidiCCAutomation &&
47 parameter.type() <= MidiChannelPressureAutomation)
53 /* Note: defaults in Evoral::ParameterDescriptor */
55 switch((AutomationType)parameter.type()) {
58 upper = Config->get_max_gain();
72 case PanAzimuthAutomation:
73 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
76 case PanWidthAutomation:
81 case RecEnableAutomation:
82 case RecSafeAutomation:
87 case FadeInAutomation:
88 case FadeOutAutomation:
89 case EnvelopeAutomation:
99 case MidiCCAutomation:
100 case MidiPgmChangeAutomation:
101 case MidiChannelPressureAutomation:
102 case MidiNotePressureAutomation:
108 case MidiPitchBenderAutomation:
114 case PhaseAutomation:
116 scale_points = boost::shared_ptr<ScalePoints>(new ScalePoints());
117 scale_points->insert (std::make_pair (_("Normal"), 0));
118 scale_points->insert (std::make_pair (_("Invert"), 1));
120 case MonitoringAutomation:
124 upper = MonitorDisk; /* XXX bump when we add MonitorCue */
125 scale_points = boost::shared_ptr<ScalePoints>(new ScalePoints());
126 scale_points->insert (std::make_pair (_("Auto"), MonitorAuto));
127 scale_points->insert (std::make_pair (_("Input"), MonitorInput));
128 scale_points->insert (std::make_pair (_("Disk"), MonitorDisk));
130 case SoloIsolateAutomation:
131 case SoloSafeAutomation:
141 ParameterDescriptor::ParameterDescriptor()
142 : Evoral::ParameterDescriptor()
144 , datatype(Variant::NOTHING)
145 , type(NullAutomation)
150 , integer_step(false)
151 , sr_dependent(false)
156 ParameterDescriptor::update_steps()
159 if (toggled || enumeration) {
162 if (logarithmic && sr_dependent && upper > lower && lower == 0) {
163 /* work-around for plugins with a log-scale control 0..SR; log (0) is not defined */
164 lower = upper / 1000.f;
166 if (logarithmic && (upper <= lower || lower * upper <= 0)) {
167 /* log-scale params need upper > lower and both values need the same sign */
170 if (rangesteps < 2) {
174 /* enums need scale-points.
175 * The GUI is more restrictive, a dropdown is displayed
176 * IIF scale_points.size() == (1 + upper - lower)
178 if (!scale_points || scale_points->empty ()) {
183 if (lower >= upper) {
184 integer_step = false;
188 /* upper == lower does not make any sense */
189 if (lower == upper) {
190 upper = lower + 0.01; // add some arbitrary value
195 if (unit == ParameterDescriptor::MIDI_NOTE) {
196 step = smallstep = 1; // semitone
197 largestep = 12; // octave
198 } else if (type == GainAutomation || type == TrimAutomation) {
199 /* dB_coeff_step gives a step normalized for [0, max_gain]. This is
200 like "slider position", so we convert from "slider position" to gain
201 to have the correct unit here. */
202 largestep = position_to_gain (dB_coeff_step(upper));
203 step = position_to_gain (largestep / 10.0);
205 } else if (logarithmic) {
206 /* ignore logscale rangesteps. {small|large}steps are used with the spinbox.
207 * gtk-spinbox shows the internal (not interface) value and up/down
208 * arrows linearly increase.
209 * The AutomationController uses internal_to_interface():
210 * ui-step [0..1] -> log (1 + largestep / lower) / log (upper / lower)
211 * so we use a step that's a multiple of "lower" for the interface step:
212 * log (1 + x) / log (upper / lower)
214 smallstep = step = lower / 11;
215 largestep = lower / 3;
216 /* NOTE: the actual value does use rangesteps via
217 * logscale_to_position_with_steps(), position_to_logscale_with_steps()
218 * when it is converted.
220 } else if (rangesteps > 1) {
221 const float delta = upper - lower;
223 smallstep = step = 1.0;
224 largestep = std::max(1.f, rintf (delta / (rangesteps - 1.f)));
226 step = smallstep = delta / (rangesteps - 1.f);
227 largestep = std::min ((delta / 4.0f), 10.f * smallstep);
230 const float delta = upper - lower;
231 /* 30 steps between min/max (300 for fine-grained) */
233 smallstep = step = 1.0;
234 largestep = std::max(1.f, rintf (delta / 30.f));
236 step = smallstep = (delta / 300.0f);
237 largestep = (delta / 30.0f);
243 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
247 snprintf(buf, sizeof(buf), "%d", b);
251 static const char* en_notes[] = {
252 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
255 static const char* notes[] = {
270 /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
271 const int octave = b / 12 - 1;
272 const size_t p = b % 12;
273 snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
278 ParameterDescriptor::normalize_note_name(const std::string& name)
280 // Remove whitespaces and convert to lower case for a more resilient parser
281 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
284 ParameterDescriptor::NameNumMap
285 ParameterDescriptor::build_midi_name2num()
288 for (uint8_t num = 0; num < 128; num++) {
289 name2num[normalize_note_name(midi_note_name(num))] = num;
295 ParameterDescriptor::midi_note_num (const std::string& name)
297 static NameNumMap name2num = build_midi_name2num();
299 uint8_t num = -1; // -1 (or 255) is returned in case of failure
301 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
302 if (it != name2num.end())
309 ParameterDescriptor::to_interface (float val) const
311 val = std::min (upper, std::max (lower, val));
315 case EnvelopeAutomation:
316 val = gain_to_slider_position_with_max (val, upper);
320 const float lower_db = accurate_coefficient_to_dB (lower);
321 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
322 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
325 case PanAzimuthAutomation:
326 case PanElevationAutomation:
329 case PanWidthAutomation:
330 val = .5f + val * .5f;
334 if (rangesteps > 1) {
335 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
337 val = logscale_to_position (val, lower, upper);
339 } else if (toggled) {
340 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
341 } else if (integer_step) {
342 /* evenly-divide steps. lower,upper inclusive
343 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
344 * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
346 * 0.1 0.3 0.5 0.7 0.9
348 val = (val + .5f - lower) / (1.f + upper - lower);
350 val = (val - lower) / (upper - lower);
354 val = std::max (0.f, std::min (1.f, val));
359 ParameterDescriptor::from_interface (float val) const
361 val = std::max (0.f, std::min (1.f, val));
365 case EnvelopeAutomation:
367 val = slider_position_to_gain_with_max (val, upper);
371 const float lower_db = accurate_coefficient_to_dB (lower);
372 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
373 val = dB_to_coefficient (lower_db + val * range_db);
376 case PanAzimuthAutomation:
377 case PanElevationAutomation:
380 case PanWidthAutomation:
381 val = 2.f * val - 1.f;
385 assert (!toggled && !integer_step); // update_steps() should prevent that.
386 if (rangesteps > 1) {
387 val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
389 val = position_to_logscale (val, lower, upper);
391 } else if (toggled) {
392 val = val > 0 ? upper : lower;
393 } else if (integer_step) {
394 /* upper and lower are inclusive. use evenly-divided steps
395 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
396 * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
398 val = floor (lower + val * (1.f + upper - lower));
399 } else if (rangesteps > 1) {
400 /* similar to above, but for float controls */
401 val = round (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
402 val = val * (upper - lower) + lower;
404 val = val * (upper - lower) + lower;
408 val = std::min (upper, std::max (lower, val));
413 ParameterDescriptor::is_linear () const
420 case EnvelopeAutomation:
430 ParameterDescriptor::compute_delta (float from, float to) const
442 ParameterDescriptor::apply_delta (float val, float delta) const
452 ParameterDescriptor::step_enum (float val, bool prev) const
457 assert (scale_points && !scale_points->empty ());
458 float rv = scale_points->begin()->second;
459 float delta = fabsf (val - rv);
460 std::vector<float> avail;
462 for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
465 if (fabsf (val - s) < delta) {
467 delta = fabsf (val - s);
470 /* ScalePoints map is sorted by text string */
471 std::sort (avail.begin (), avail.end ());
472 std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
473 assert (it != avail.end());
476 if (it == avail.begin()) {
481 if (++it == avail.end()) {
488 } // namespace ARDOUR