ardour
amp.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify it
5  under the terms of the GNU General Public License as published by the Free
6  Software Foundation; either version 2 of the License, or (at your option)
7  any later version.
8 
9  This program is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  for more details.
13 
14  You should have received a copy of the GNU General Public License along
15  with this program; if not, write to the Free Software Foundation, Inc.,
16  675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #include <iostream>
20 #include <cstring>
21 #include <cmath>
22 #include <algorithm>
23 
24 #include "evoral/Curve.hpp"
25 
26 #include "ardour/amp.h"
27 #include "ardour/audio_buffer.h"
28 #include "ardour/buffer_set.h"
29 #include "ardour/midi_buffer.h"
31 #include "ardour/session.h"
32 
33 #include "i18n.h"
34 
35 using namespace ARDOUR;
36 using namespace PBD;
37 
38 // used for low-pass filter denormal protection
39 #define GAIN_COEFF_TINY (1e-10) // -200dB
40 
41 Amp::Amp (Session& s, std::string type)
42  : Processor(s, "Amp")
43  , _apply_gain(true)
44  , _apply_gain_automation(false)
45  , _current_gain(GAIN_COEFF_UNITY)
46  , _current_automation_frame (INT64_MAX)
47  , _gain_automation_buffer(0)
48  , _type (type)
49  , _midi_amp (type != "trim")
50 {
53  _gain_control = boost::shared_ptr<GainControl> (new GainControl ((_type == "trim") ? X_("trimcontrol") : X_("gaincontrol"), s, this, p, gl));
55 
57 }
58 
59 std::string
61 {
62  return _("Fader");
63 }
64 
65 bool
67 {
68  out = in;
69  return true;
70 }
71 
72 bool
74 {
75  if (out != in) { // always 1:1
76  return false;
77  }
78 
79  return Processor::configure_io (in, out);
80 }
81 
82 void
83 Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool)
84 {
85  if (!_active && !_pending_active) {
86  return;
87  }
88 
89  if (_apply_gain) {
90 
92 
94  assert (gab);
95 
96  if (_midi_amp) {
97  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
98  MidiBuffer& mb (*i);
99  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
101  if (ev.is_note_on()) {
102  assert(ev.time() >= 0 && ev.time() < nframes);
103  ev.scale_velocity (fabsf (gab[ev.time()]));
104  }
105  }
106  }
107  }
108 
109 
110  const double a = 156.825 / _session.nominal_frame_rate(); // 25 Hz LPF; see Amp::apply_gain for details
111  double lpf = _current_gain;
112 
113  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
114  Sample* const sp = i->data();
115  lpf = _current_gain;
116  for (pframes_t nx = 0; nx < nframes; ++nx) {
117  sp[nx] *= lpf;
118  lpf += a * (gab[nx] - lpf);
119  }
120  }
121 
122  if (fabs (lpf) < GAIN_COEFF_TINY) {
124  } else {
125  _current_gain = lpf;
126  }
127 
128  } else { /* manual (scalar) gain */
129 
130  gain_t const dg = _gain_control->user_double();
131 
132  if (_current_gain != dg) {
133 
135 
136  } else if (_current_gain != GAIN_COEFF_UNITY) {
137 
138  /* gain has not changed, but its non-unity */
139 
140  if (_midi_amp) {
141  /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
142  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
143 
144  MidiBuffer& mb (*i);
145 
146  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
148  if (ev.is_note_on()) {
149  ev.scale_velocity (fabsf (_current_gain));
150  }
151  }
152  }
153  }
154 
155  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
156  apply_gain_to_buffer (i->data(), nframes, _current_gain);
157  }
158  }
159  }
160  }
161 
163 }
164 
165 gain_t
166 Amp::apply_gain (BufferSet& bufs, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target, bool midi_amp)
167 {
169  gain_t rv = target;
170 
171  if (nframes == 0 || bufs.count().n_total() == 0) {
172  return initial;
173  }
174 
175  // if we don't need to declick, defer to apply_simple_gain
176  if (initial == target) {
177  apply_simple_gain (bufs, nframes, target);
178  return target;
179  }
180 
181  /* MIDI Gain */
182  if (midi_amp) {
183  /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
184  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
185 
186  gain_t delta;
187  if (target < initial) {
188  /* fade out: remove more and more of delta from initial */
189  delta = -(initial - target);
190  } else {
191  /* fade in: add more and more of delta from initial */
192  delta = target - initial;
193  }
194 
195  MidiBuffer& mb (*i);
196 
197  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
199 
200  if (ev.is_note_on()) {
201  const gain_t scale = delta * (ev.time()/(double) nframes);
202  ev.scale_velocity (fabsf (initial+scale));
203  }
204  }
205  }
206  }
207 
208  /* Audio Gain */
209 
210  /* Low pass filter coefficient: 1.0 - e^(-2.0 * π * f / 48000) f in Hz.
211  * for f << SR, approx a ~= 6.2 * f / SR;
212  */
213  const double a = 156.825 / sample_rate; // 25 Hz LPF
214 
215  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
216  Sample* const buffer = i->data();
217  double lpf = initial;
218 
219  for (pframes_t nx = 0; nx < nframes; ++nx) {
220  buffer[nx] *= lpf;
221  lpf += a * (target - lpf);
222  }
223  if (i == bufs.audio_begin()) {
224  rv = lpf;
225  }
226  }
227  if (fabsf (rv - target) < GAIN_COEFF_TINY) return target;
228  if (fabsf (rv) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
229  return rv;
230 }
231 
232 void
233 Amp::declick (BufferSet& bufs, framecnt_t nframes, int dir)
234 {
235  if (nframes == 0 || bufs.count().n_total() == 0) {
236  return;
237  }
238 
239  const framecnt_t declick = std::min ((framecnt_t) 512, nframes);
240  const double fractional_shift = 1.0 / declick ;
241  gain_t delta, initial;
242 
243  if (dir < 0) {
244  /* fade out: remove more and more of delta from initial */
245  delta = -1.0;
246  initial = GAIN_COEFF_UNITY;
247  } else {
248  /* fade in: add more and more of delta from initial */
249  delta = 1.0;
250  initial = GAIN_COEFF_ZERO;
251  }
252 
253  /* Audio Gain */
254  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
255  Sample* const buffer = i->data();
256 
257  double fractional_pos = 0.0;
258 
259  for (pframes_t nx = 0; nx < declick; ++nx) {
260  buffer[nx] *= initial + (delta * fractional_pos);
261  fractional_pos += fractional_shift;
262  }
263 
264  /* now ensure the rest of the buffer has the target value applied, if necessary. */
265  if (declick != nframes) {
266  if (dir < 0) {
267  memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
268  }
269  }
270  }
271 }
272 
273 
274 gain_t
275 Amp::apply_gain (AudioBuffer& buf, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target)
276 {
277  /* Apply a (potentially) declicked gain to the contents of @a buf
278  * -- used by MonitorProcessor::run()
279  */
280 
281  if (nframes == 0) {
282  return initial;
283  }
284 
285  // if we don't need to declick, defer to apply_simple_gain
286  if (initial == target) {
287  apply_simple_gain (buf, nframes, target);
288  return target;
289  }
290 
291  Sample* const buffer = buf.data();
292  const double a = 156.825 / sample_rate; // 25 Hz LPF, see [other] Amp::apply_gain() above for details
293 
294  double lpf = initial;
295  for (pframes_t nx = 0; nx < nframes; ++nx) {
296  buffer[nx] *= lpf;
297  lpf += a * (target - lpf);
298  }
299 
300  if (fabs (lpf - target) < GAIN_COEFF_TINY) return target;
301  if (fabs (lpf) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
302  return lpf;
303 }
304 
305 void
306 Amp::apply_simple_gain (BufferSet& bufs, framecnt_t nframes, gain_t target, bool midi_amp)
307 {
308  if (fabsf (target) < GAIN_COEFF_SMALL) {
309 
310  if (midi_amp) {
311  /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
312  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
313  MidiBuffer& mb (*i);
314 
315  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
317  if (ev.is_note_on()) {
318  ev.set_velocity (0);
319  }
320  }
321  }
322  }
323 
324  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
325  memset (i->data(), 0, sizeof (Sample) * nframes);
326  }
327 
328  } else if (target != GAIN_COEFF_UNITY) {
329 
330  if (midi_amp) {
331  /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
332  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
333  MidiBuffer& mb (*i);
334 
335  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
337  if (ev.is_note_on()) {
338  ev.scale_velocity (fabsf (target));
339  }
340  }
341  }
342  }
343 
344  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
345  apply_gain_to_buffer (i->data(), nframes, target);
346  }
347  }
348 }
349 
350 void
352 {
353  if (fabsf (target) < GAIN_COEFF_SMALL) {
354  memset (buf.data(), 0, sizeof (Sample) * nframes);
355  } else if (target != GAIN_COEFF_UNITY) {
356  apply_gain_to_buffer (buf.data(), nframes, target);
357  }
358 }
359 
360 void
361 Amp::inc_gain (gain_t factor, void *src)
362 {
363  float desired_gain = _gain_control->user_double();
364 
365  if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
366  // really?! what's the idea here?
367  set_gain (0.000001f + (0.000001f * factor), src);
368  } else {
369  set_gain (desired_gain + (desired_gain * factor), src);
370  }
371 }
372 
373 void
374 Amp::set_gain (gain_t val, void *)
375 {
376  _gain_control->set_value (val);
377 }
378 
379 XMLNode&
380 Amp::state (bool full_state)
381 {
382  XMLNode& node (Processor::state (full_state));
383  node.add_property("type", _type);
384  node.add_child_nocopy (_gain_control->get_state());
385 
386  return node;
387 }
388 
389 int
390 Amp::set_state (const XMLNode& node, int version)
391 {
392  XMLNode* gain_node;
393 
394  Processor::set_state (node, version);
395 
396  if ((gain_node = node.child (Controllable::xml_node_name.c_str())) != 0) {
397  _gain_control->set_state (*gain_node, version);
398  }
399 
400  return 0;
401 }
402 
403 void
405 {
406  AutomationControl::set_value (std::max (std::min (val, (double)_desc.upper), (double)_desc.lower));
407  _amp->session().set_dirty ();
408 }
409 
410 double
412 {
413  if (_desc.type == GainAutomation) {
414  return gain_to_slider_position (v);
415  } else {
416  return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
417  }
418 }
419 
420 double
422 {
423  if (_desc.type == GainAutomation) {
424  return slider_position_to_gain (v);
425  } else {
426  return dB_to_coefficient (lower_db + v * range_db);
427  }
428 }
429 
430 double
432 {
433  return accurate_coefficient_to_dB (v);
434 }
435 
436 double
438 {
439  return dB_to_coefficient (u);
440 }
441 
442 std::string
444 {
445  char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
446  return std::string(theBuf);
447 }
448 
453 void
455 {
456  Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
457 
458  if (am.locked()
460  && _gain_control->automation_playback())
461  {
462  assert (_gain_automation_buffer);
463  _apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
464  start_frame, end_frame, _gain_automation_buffer, nframes);
465  if (start_frame != _current_automation_frame) {
467  }
468  _current_automation_frame = end_frame;
469  } else {
470  _apply_gain_automation = false;
471  _current_automation_frame = INT64_MAX;
472  }
473 }
474 
475 bool
477 {
478  return true;
479 }
480 
481 std::string
483 {
484  if (ac == _gain_control) {
485  char buffer[32];
486  snprintf (buffer, sizeof (buffer), _("%.2fdB"), ac->internal_to_user (ac->get_value ()));
487  return buffer;
488  }
489 
490  return Automatable::value_as_string (ac);
491 }
492 
498 void
500 {
502 }
bool transport_rolling() const
Definition: session.h:592
double internal_to_interface(double) const
Definition: amp.cc:411
bool _apply_gain
Definition: amp.h:117
framecnt_t nominal_frame_rate() const
Definition: session.h:367
ARDOUR::Session & _session
static double gain_to_slider_position(ARDOUR::gain_t g)
Definition: utils.h:83
float lower
Minimum value (in Hz, for frequencies)
bool _apply_gain_automation
Definition: amp.h:118
boost::shared_ptr< GainControl > _gain_control
Definition: amp.h:122
Session & session() const
bool is_note_on() const
Definition: MIDIEvent.hpp:68
int set_state(const XMLNode &, int version)
Definition: amp.cc:390
gain_t * _gain_automation_buffer
Definition: amp.h:125
bool apply_gain() const
Definition: amp.h:50
#define GAIN_COEFF_TINY
Definition: amp.cc:39
void set_gain(gain_t g, void *src)
Definition: amp.cc:374
uint32_t pframes_t
Definition: types.h:61
tuple f
Definition: signals.py:35
const ParameterDescriptor _desc
Amp(Session &s, std::string type="amp")
Definition: amp.cc:41
double user_to_internal(double) const
Definition: amp.cc:437
float gain_t
Definition: types.h:58
bool configure_io(ChanCount in, ChanCount out)
Definition: amp.cc:73
void run(BufferSet &bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool)
Definition: amp.cc:83
int set_state(const XMLNode &, int version)
Definition: processor.cc:173
#define GAIN_COEFF_ZERO
Definition: dB.h:26
virtual void add_control(boost::shared_ptr< Evoral::Control >)
Definition: automatable.cc:138
static float accurate_coefficient_to_dB(float coeff)
Definition: dB.h:38
uint32_t n_total() const
Definition: chan_count.h:69
#define _(Text)
Definition: i18n.h:11
XMLNode & state(bool full)
Definition: amp.cc:380
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
midi_iterator midi_begin()
Definition: buffer_set.h:177
Glib::Threads::Mutex & control_lock() const
Definition: ControlSet.hpp:70
float Sample
Definition: types.h:54
virtual std::string value_as_string(boost::shared_ptr< AutomationControl >) const
Definition: automatable.cc:494
float upper
Maximum value (in Hz, for frequencies)
#define GAIN_COEFF_SMALL
Definition: dB.h:27
void inc_gain(gain_t delta, void *src)
Definition: amp.cc:361
audio_iterator audio_begin()
Definition: buffer_set.h:173
Definition: amp.h:29
void set_gain_automation_buffer(gain_t *)
Definition: amp.cc:499
Time time() const
Definition: Event.hpp:132
static void apply_simple_gain(BufferSet &bufs, framecnt_t nframes, gain_t target, bool midi_amp=true)
Definition: amp.cc:306
virtual bool configure_io(ChanCount in, ChanCount out)
Definition: processor.cc:246
double internal_to_user(double) const
Definition: amp.cc:431
int64_t framepos_t
Definition: types.h:66
void scale_velocity(float factor)
Definition: MIDIEvent.hpp:80
audio_iterator audio_end()
Definition: buffer_set.h:174
framepos_t _current_automation_frame
Definition: amp.h:120
static void declick(BufferSet &bufs, framecnt_t nframes, int dir)
Definition: amp.cc:233
static const std::string xml_node_name
Definition: controllable.h:116
float _current_gain
Definition: amp.h:119
std::string get_user_string() const
Definition: amp.cc:443
XMLProperty * add_property(const char *name, const std::string &value)
void set_value(double val)
Definition: amp.cc:404
void setup_gain_automation(framepos_t start_frame, framepos_t end_frame, framecnt_t nframes)
Definition: amp.cc:454
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
std::string value_as_string(boost::shared_ptr< AutomationControl >) const
Definition: amp.cc:482
midi_iterator midi_end()
Definition: buffer_set.h:178
Definition: xml++.h:95
const ChanCount & count() const
Definition: buffer_set.h:87
bool can_support_io_configuration(const ChanCount &in, ChanCount &out)
Definition: amp.cc:66
Definition: debug.h:30
bool bounce_processing() const
Definition: session.h:872
virtual XMLNode & state(bool full)
Definition: processor.cc:109
static float dB_to_coefficient(float dB)
Definition: dB.h:30
XMLNode * child(const char *) const
Definition: xml++.cc:309
double interface_to_internal(double) const
Definition: amp.cc:421
iterator begin()
Definition: midi_buffer.h:127
bool _midi_amp
Definition: amp.h:127
const Sample * data(framecnt_t offset=0) const
Definition: audio_buffer.h:187
bool visible() const
Definition: amp.cc:476
static LilvNode * get_value(LilvWorld *world, const LilvNode *subject, const LilvNode *predicate)
Definition: lv2_plugin.cc:971
#define GAIN_COEFF_UNITY
Definition: dB.h:28
LIBARDOUR_API apply_gain_to_buffer_t apply_gain_to_buffer
Definition: globals.cc:131
std::string _type
Definition: amp.h:126
virtual double internal_to_user(double i) const
Definition: controllable.h:73
std::string display_name() const
Definition: amp.cc:60
static ARDOUR::gain_t slider_position_to_gain(double pos)
Definition: utils.h:106
void set_velocity(uint8_t value)
Definition: MIDIEvent.hpp:79