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 && sr_dependent && upper > lower && lower == 0) {
161 /* work-around for plugins with a log-scale control 0..SR; log (0) is not defined */
162 lower = upper / 1000.f;
164 if (logarithmic && (upper <= lower || lower * upper <= 0)) {
165 /* log-scale params need upper > lower and both values need the same sign */
168 if (rangesteps < 2) {
172 /* enums need scale-points.
173 * The GUI is more restrictive, a dropdown is displayed
174 * IIF scale_points.size() == (1 + upper - lower)
176 if (!scale_points || scale_points->empty ()) {
181 if (lower >= upper) {
182 integer_step = false;
186 /* upper == lower does not make any sense */
187 if (lower == upper) {
188 upper = lower + 0.01; // add some arbitrary value
193 if (unit == ParameterDescriptor::MIDI_NOTE) {
194 step = smallstep = 1; // semitone
195 largestep = 12; // octave
196 } else if (type == GainAutomation || type == TrimAutomation) {
197 /* dB_coeff_step gives a step normalized for [0, max_gain]. This is
198 like "slider position", so we convert from "slider position" to gain
199 to have the correct unit here. */
200 largestep = position_to_gain (dB_coeff_step(upper));
201 step = position_to_gain (largestep / 10.0);
203 } else if (rangesteps > 1) {
204 const float delta = upper - lower;
206 smallstep = step = (powf (delta, 1.f / (float)rangesteps) - 1.f) * lower;
207 largestep = (powf (delta, std::max (0.5f, 10.f / (float)rangesteps)) - 1.f) * lower;
208 } else if (integer_step) {
209 smallstep = step = 1.0;
210 largestep = std::max(1.f, rintf (delta / (rangesteps - 1)));
212 step = smallstep = delta / (rangesteps - 1);
213 largestep = std::min ((delta / 4.0f), 10.f * smallstep); // XXX
216 const float delta = upper - lower;
217 /* 30 steps between min/max (300 for fine-grained) */
219 smallstep = step = (powf (delta, 1.f / 300.f) - 1.f) * lower;
220 largestep = (powf (delta, 1.f / 30.f) - 1.f) * lower;
221 } else if (integer_step) {
222 smallstep = step = 1.0;
223 largestep = std::max(1.f, rintf (delta / 30.f));
225 step = smallstep = (delta / 300.0f);
226 largestep = (delta / 30.0f);
232 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
236 snprintf(buf, sizeof(buf), "%d", b);
240 static const char* en_notes[] = {
241 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
244 static const char* notes[] = {
259 /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
260 const int octave = b / 12 - 1;
261 const size_t p = b % 12;
262 snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
267 ParameterDescriptor::normalize_note_name(const std::string& name)
269 // Remove whitespaces and convert to lower case for a more resilient parser
270 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
273 ParameterDescriptor::NameNumMap
274 ParameterDescriptor::build_midi_name2num()
277 for (uint8_t num = 0; num < 128; num++) {
278 name2num[normalize_note_name(midi_note_name(num))] = num;
284 ParameterDescriptor::midi_note_num (const std::string& name)
286 static NameNumMap name2num = build_midi_name2num();
288 uint8_t num = -1; // -1 (or 255) is returned in case of failure
290 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
291 if (it != name2num.end())
298 ParameterDescriptor::to_interface (float val) const
300 val = std::min (upper, std::max (lower, val));
304 case EnvelopeAutomation:
305 val = gain_to_slider_position_with_max (val, upper);
309 const float lower_db = accurate_coefficient_to_dB (lower);
310 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
311 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
314 case PanAzimuthAutomation:
315 case PanElevationAutomation:
318 case PanWidthAutomation:
319 val = .5f + val * .5f;
323 if (rangesteps > 1) {
324 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
326 val = logscale_to_position (val, lower, upper);
328 } else if (toggled) {
329 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
330 } else if (integer_step) {
331 /* evenly-divide steps. lower,upper inclusive
332 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
333 * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
335 * 0.1 0.3 0.5 0.7 0.9
337 val = (val + .5f - lower) / (1.f + upper - lower);
339 val = (val - lower) / (upper - lower);
343 val = std::max (0.f, std::min (1.f, val));
348 ParameterDescriptor::from_interface (float val) const
350 val = std::max (0.f, std::min (1.f, val));
354 case EnvelopeAutomation:
356 val = slider_position_to_gain_with_max (val, upper);
360 const float lower_db = accurate_coefficient_to_dB (lower);
361 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
362 val = dB_to_coefficient (lower_db + val * range_db);
365 case PanAzimuthAutomation:
366 case PanElevationAutomation:
369 case PanWidthAutomation:
370 val = 2.f * val - 1.f;
374 assert (!toggled && !integer_step); // update_steps() should prevent that.
375 if (rangesteps > 1) {
376 val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
378 val = position_to_logscale (val, lower, upper);
380 } else if (toggled) {
381 val = val > 0 ? upper : lower;
382 } else if (integer_step) {
383 /* upper and lower are inclusive. use evenly-divided steps
384 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
385 * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
387 val = round (lower + val * (1.f + upper - lower) - .5f);
388 } else if (rangesteps > 1) {
389 /* similar to above, but for float controls */
390 val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
391 val = val * (upper - lower) + lower;
393 val = val * (upper - lower) + lower;
397 val = std::min (upper, std::max (lower, val));
402 ParameterDescriptor::is_linear () const
409 case EnvelopeAutomation:
419 ParameterDescriptor::compute_delta (float from, float to) const
431 ParameterDescriptor::apply_delta (float val, float delta) const
441 ParameterDescriptor::step_enum (float val, bool prev) const
446 assert (scale_points && !scale_points->empty ());
447 float rv = scale_points->begin()->second;
448 float delta = fabsf (val - rv);
449 std::vector<float> avail;
451 for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
454 if (fabsf (val - s) < delta) {
456 delta = fabsf (val - s);
459 /* ScalePoints map is sorted by text string */
460 std::sort (avail.begin (), avail.end ());
461 std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
462 assert (it != avail.end());
465 if (it == avail.begin()) {
470 if (++it == avail.end()) {
477 } // namespace ARDOUR