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 (logarithmic) {
204 /* ignore logscale rangesteps. {small|large}steps are used with the spinbox.
205 * gtk-spinbox shows the internal (not interface) value and up/down
206 * arrows linearly increase.
207 * The AutomationController uses internal_to_interface():
208 * ui-step [0..1] -> log (1 + largestep / lower) / log (upper / lower)
209 * so we use a step that's a multiple of "lower" for the interface step:
210 * log (1 + x) / log (upper / lower)
212 smallstep = step = lower / 11;
213 largestep = lower / 3;
214 /* NOTE: the actual value does use rangesteps via
215 * logscale_to_position_with_steps(), position_to_logscale_with_steps()
216 * when it is converted.
218 } else if (rangesteps > 1) {
219 const float delta = upper - lower;
221 smallstep = step = 1.0;
222 largestep = std::max(1.f, rintf (delta / (rangesteps - 1.f)));
224 step = smallstep = delta / (rangesteps - 1.f);
225 largestep = std::min ((delta / 4.0f), 10.f * smallstep);
228 const float delta = upper - lower;
229 /* 30 steps between min/max (300 for fine-grained) */
231 smallstep = step = 1.0;
232 largestep = std::max(1.f, rintf (delta / 30.f));
234 step = smallstep = (delta / 300.0f);
235 largestep = (delta / 30.0f);
241 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
245 snprintf(buf, sizeof(buf), "%d", b);
249 static const char* en_notes[] = {
250 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
253 static const char* notes[] = {
268 /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
269 const int octave = b / 12 - 1;
270 const size_t p = b % 12;
271 snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
276 ParameterDescriptor::normalize_note_name(const std::string& name)
278 // Remove whitespaces and convert to lower case for a more resilient parser
279 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
282 ParameterDescriptor::NameNumMap
283 ParameterDescriptor::build_midi_name2num()
286 for (uint8_t num = 0; num < 128; num++) {
287 name2num[normalize_note_name(midi_note_name(num))] = num;
293 ParameterDescriptor::midi_note_num (const std::string& name)
295 static NameNumMap name2num = build_midi_name2num();
297 uint8_t num = -1; // -1 (or 255) is returned in case of failure
299 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
300 if (it != name2num.end())
307 ParameterDescriptor::to_interface (float val) const
309 val = std::min (upper, std::max (lower, val));
313 case EnvelopeAutomation:
314 val = gain_to_slider_position_with_max (val, upper);
318 const float lower_db = accurate_coefficient_to_dB (lower);
319 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
320 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
323 case PanAzimuthAutomation:
324 case PanElevationAutomation:
327 case PanWidthAutomation:
328 val = .5f + val * .5f;
332 if (rangesteps > 1) {
333 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
335 val = logscale_to_position (val, lower, upper);
337 } else if (toggled) {
338 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
339 } else if (integer_step) {
340 /* evenly-divide steps. lower,upper inclusive
341 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
342 * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
344 * 0.1 0.3 0.5 0.7 0.9
346 val = (val + .5f - lower) / (1.f + upper - lower);
348 val = (val - lower) / (upper - lower);
352 val = std::max (0.f, std::min (1.f, val));
357 ParameterDescriptor::from_interface (float val) const
359 val = std::max (0.f, std::min (1.f, val));
363 case EnvelopeAutomation:
365 val = slider_position_to_gain_with_max (val, upper);
369 const float lower_db = accurate_coefficient_to_dB (lower);
370 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
371 val = dB_to_coefficient (lower_db + val * range_db);
374 case PanAzimuthAutomation:
375 case PanElevationAutomation:
378 case PanWidthAutomation:
379 val = 2.f * val - 1.f;
383 assert (!toggled && !integer_step); // update_steps() should prevent that.
384 if (rangesteps > 1) {
385 val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
387 val = position_to_logscale (val, lower, upper);
389 } else if (toggled) {
390 val = val > 0 ? upper : lower;
391 } else if (integer_step) {
392 /* upper and lower are inclusive. use evenly-divided steps
393 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
394 * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
396 val = floor (lower + val * (1.f + upper - lower));
397 } else if (rangesteps > 1) {
398 /* similar to above, but for float controls */
399 val = round (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
400 val = val * (upper - lower) + lower;
402 val = val * (upper - lower) + lower;
406 val = std::min (upper, std::max (lower, val));
411 ParameterDescriptor::is_linear () const
418 case EnvelopeAutomation:
428 ParameterDescriptor::compute_delta (float from, float to) const
440 ParameterDescriptor::apply_delta (float val, float delta) const
450 ParameterDescriptor::step_enum (float val, bool prev) const
455 assert (scale_points && !scale_points->empty ());
456 float rv = scale_points->begin()->second;
457 float delta = fabsf (val - rv);
458 std::vector<float> avail;
460 for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) {
463 if (fabsf (val - s) < delta) {
465 delta = fabsf (val - s);
468 /* ScalePoints map is sorted by text string */
469 std::sort (avail.begin (), avail.end ());
470 std::vector<float>::const_iterator it = std::find (avail.begin (), avail.end (), rv);
471 assert (it != avail.end());
474 if (it == avail.begin()) {
479 if (++it == avail.end()) {
486 } // namespace ARDOUR