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.
21 #include <boost/algorithm/string.hpp>
23 #include "pbd/control_math.h"
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"
36 ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
37 : Evoral::ParameterDescriptor()
39 , datatype(Variant::NOTHING)
40 , type((AutomationType)parameter.type())
45 , integer_step(parameter.type() >= MidiCCAutomation &&
46 parameter.type() <= MidiChannelPressureAutomation)
52 /* Note: defaults in Evoral::ParameterDescriptor */
54 switch((AutomationType)parameter.type()) {
57 upper = Config->get_max_gain();
70 case PanAzimuthAutomation:
71 normal = 0.5f; // there really is no _normal but this works for stereo, sort of
74 case PanWidthAutomation:
79 case RecEnableAutomation:
80 case RecSafeAutomation:
85 case FadeInAutomation:
86 case FadeOutAutomation:
87 case EnvelopeAutomation:
97 case MidiCCAutomation:
98 case MidiPgmChangeAutomation:
99 case MidiChannelPressureAutomation:
100 case MidiNotePressureAutomation:
106 case MidiPitchBenderAutomation:
112 case PhaseAutomation:
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));
118 case MonitoringAutomation:
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));
128 case SoloIsolateAutomation:
129 case SoloSafeAutomation:
139 ParameterDescriptor::ParameterDescriptor()
140 : Evoral::ParameterDescriptor()
142 , datatype(Variant::NOTHING)
143 , type(NullAutomation)
148 , integer_step(false)
149 , sr_dependent(false)
154 ParameterDescriptor::update_steps()
157 if (toggled || enumeration) {
160 if (logarithmic && (upper <= lower || lower * upper <= 0)) {
163 if (rangesteps < 2) {
167 if (!scale_points || scale_points->empty ()) {
172 if (lower >= upper) {
173 integer_step = false;
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);
187 } else if (rangesteps > 1) {
188 const float delta = upper - lower;
190 step = smallstep = (delta / (rangesteps - 1)); // XXX
191 largestep = std::min ((delta / 5.0f), 10.f * smallstep); // XXX
194 smallstep = smallstep / logf (rangesteps); // XXX
195 step = step / logf (rangesteps);
196 largestep = largestep / logf (rangesteps);
197 } else if (integer_step) {
199 step = std::max(1.f, rintf (rangesteps));
200 largestep = std::max(1.f, rintf (largestep));
203 const float delta = upper - lower;
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);
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) {
224 step = std::max(1.f, rintf (step));
225 largestep = std::max(1.f, rintf (largestep));
231 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
235 snprintf(buf, sizeof(buf), "%d", b);
239 static const char* en_notes[] = {
240 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
243 static const char* notes[] = {
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);
266 ParameterDescriptor::normalize_note_name(const std::string& name)
268 // Remove whitespaces and convert to lower case for a more resilient parser
269 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
272 ParameterDescriptor::NameNumMap
273 ParameterDescriptor::build_midi_name2num()
276 for (uint8_t num = 0; num < 128; num++) {
277 name2num[normalize_note_name(midi_note_name(num))] = num;
283 ParameterDescriptor::midi_note_num (const std::string& name)
285 static NameNumMap name2num = build_midi_name2num();
287 uint8_t num = -1; // -1 (or 255) is returned in case of failure
289 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
290 if (it != name2num.end())
297 ParameterDescriptor::to_interface (float val) const
299 val = std::min (upper, std::max (lower, val));
303 case EnvelopeAutomation:
304 val = gain_to_slider_position_with_max (val, upper);
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;
313 case PanAzimuthAutomation:
314 case PanElevationAutomation:
317 case PanWidthAutomation:
318 val = .5f + val * .5f;
322 if (rangesteps > 1) {
323 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
325 val = logscale_to_position (val, lower, upper);
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]
334 * 0.1 0.3 0.5 0.7 0.9
336 val = (val + .5f - lower) / (1.f + upper - lower);
338 val = (val - lower) / (upper - lower);
342 val = std::max (0.f, std::min (1.f, val));
347 ParameterDescriptor::from_interface (float val) const
349 val = std::max (0.f, std::min (1.f, val));
353 case EnvelopeAutomation:
355 val = slider_position_to_gain_with_max (val, upper);
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);
364 case PanAzimuthAutomation:
365 case PanElevationAutomation:
368 case PanWidthAutomation:
369 val = 2.f * val - 1.f;
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);
377 val = position_to_logscale (val, lower, upper);
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]
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;
392 val = val * (upper - lower) + lower;
396 val = std::min (upper, std::max (lower, val));
401 ParameterDescriptor::is_linear () const
408 case EnvelopeAutomation:
418 ParameterDescriptor::compute_delta (float from, float to) const
430 ParameterDescriptor::apply_delta (float val, float delta) const
440 ParameterDescriptor::step_enum (float val, bool prev) const
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;
450 for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
453 if (fabsf (val - s) < delta) {
455 delta = fabsf (val - s);
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());
464 if (it == avail.begin()) {
469 if (++it == avail.end()) {
476 } // namespace ARDOUR