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()
147 if (toggled || enumeration) {
150 if (logarithmic && (upper <= lower || lower * upper <= 0)) {
153 if (rangesteps < 2) {
157 if (unit == ParameterDescriptor::MIDI_NOTE) {
158 step = smallstep = 1; // semitone
159 largestep = 12; // octave
160 } else if (type == GainAutomation || type == TrimAutomation) {
161 /* dB_coeff_step gives a step normalized for [0, max_gain]. This is
162 like "slider position", so we convert from "slider position" to gain
163 to have the correct unit here. */
164 largestep = position_to_gain (dB_coeff_step(upper));
165 step = position_to_gain (largestep / 10.0);
167 } else if (rangesteps > 1) {
168 const float delta = upper - lower;
170 step = smallstep = (delta / (rangesteps - 1)); // XXX
171 largestep = std::min ((delta / 5.0f), 10.f * smallstep); // XXX
174 smallstep = smallstep / logf (rangesteps); // XXX
175 step = step / logf (rangesteps);
176 largestep = largestep / logf (rangesteps);
177 } else if (integer_step) {
179 step = std::max(1.f, rintf (rangesteps));
180 largestep = std::max(1.f, rintf (largestep));
183 const float delta = upper - lower;
185 /* 30 happens to be the total number of steps for a fader with default
186 max gain of 2.0 (6 dB), so we use 30 here too for consistency. */
187 step = smallstep = (delta / 300.0f);
188 largestep = (delta / 30.0f);
191 /* Steps are linear, but we map them with pow like values (in
192 internal_to_interface). Thus, they are applied exponentially,
193 which means too few steps. So, divide to get roughly the
194 desired number of steps (30). This is not mathematically
195 precise but seems to be about right for the controls I tried.
196 If you're reading this, you've probably found a case where that
197 isn't true, and somebody needs to sit down with a piece of paper
198 and actually do the math. */
199 smallstep = smallstep / logf(30.0f);
200 step = step / logf(30.0f);
201 largestep = largestep / logf(30.0f);
202 } else if (integer_step) {
204 step = std::max(1.f, rintf (step));
205 largestep = std::max(1.f, rintf (largestep));
211 ParameterDescriptor::midi_note_name (const uint8_t b, bool translate)
215 snprintf(buf, sizeof(buf), "%d", b);
219 static const char* en_notes[] = {
220 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
223 static const char* notes[] = {
238 /* MIDI note 0 is in octave -1 (in scientific pitch notation) */
239 const int octave = b / 12 - 1;
240 const size_t p = b % 12;
241 snprintf (buf, sizeof (buf), "%s%d", translate ? notes[p] : en_notes[p], octave);
246 ParameterDescriptor::normalize_note_name(const std::string& name)
248 // Remove whitespaces and convert to lower case for a more resilient parser
249 return boost::to_lower_copy(boost::erase_all_copy(name, " "));
252 ParameterDescriptor::NameNumMap
253 ParameterDescriptor::build_midi_name2num()
256 for (uint8_t num = 0; num < 128; num++) {
257 name2num[normalize_note_name(midi_note_name(num))] = num;
263 ParameterDescriptor::midi_note_num (const std::string& name)
265 static NameNumMap name2num = build_midi_name2num();
267 uint8_t num = -1; // -1 (or 255) is returned in case of failure
269 NameNumMap::const_iterator it = name2num.find(normalize_note_name(name));
270 if (it != name2num.end())
277 ParameterDescriptor::to_interface (float val) const
279 val = std::min (upper, std::max (lower, val));
283 case EnvelopeAutomation:
284 val = gain_to_slider_position_with_max (val, upper);
288 const float lower_db = accurate_coefficient_to_dB (lower);
289 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
290 val = (accurate_coefficient_to_dB (val) - lower_db) / range_db;
293 case PanAzimuthAutomation:
294 case PanElevationAutomation:
297 case PanWidthAutomation:
298 val = .5f + val * .5f;
302 if (rangesteps > 1) {
303 val = logscale_to_position_with_steps (val, lower, upper, rangesteps);
305 val = logscale_to_position (val, lower, upper);
307 } else if (toggled) {
308 return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f;
309 } else if (integer_step) {
310 /* evenly-divide steps. lower,upper inclusive
311 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
312 * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0]
314 * 0.1 0.3 0.5 0.7 0.9
316 val = (val + .5f - lower) / (1.f + upper - lower);
318 val = (val - lower) / (upper - lower);
322 val = std::max (0.f, std::min (1.f, val));
327 ParameterDescriptor::from_interface (float val) const
329 val = std::max (0.f, std::min (1.f, val));
333 case EnvelopeAutomation:
335 val = slider_position_to_gain_with_max (val, upper);
339 const float lower_db = accurate_coefficient_to_dB (lower);
340 const float range_db = accurate_coefficient_to_dB (upper) - lower_db;
341 val = dB_to_coefficient (lower_db + val * range_db);
344 case PanAzimuthAutomation:
345 case PanElevationAutomation:
348 case PanWidthAutomation:
349 val = 2.f * val - 1.f;
353 assert (!toggled && !integer_step); // update_steps() should prevent that.
354 if (rangesteps > 1) {
355 val = position_to_logscale_with_steps (val, lower, upper, rangesteps);
357 val = position_to_logscale (val, lower, upper);
359 } else if (toggled) {
360 val = val > 0 ? upper : lower;
361 } else if (integer_step) {
362 /* upper and lower are inclusive. use evenly-divided steps
363 * e.g. 5 integers 0,1,2,3,4 are mapped to a fader
364 * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0]
366 val = round (lower + val * (1.f + upper - lower) - .5f);
367 } else if (rangesteps > 1) {
368 /* similar to above, but for float controls */
369 val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
370 val = val * (upper - lower) + lower;
372 val = val * (upper - lower) + lower;
376 val = std::min (upper, std::max (lower, val));
381 ParameterDescriptor::is_linear () const
388 case EnvelopeAutomation:
398 ParameterDescriptor::compute_delta (float from, float to) const
410 ParameterDescriptor::apply_delta (float val, float delta) const
419 } // namespace ARDOUR