fader: start muted, ramp up/interpolate to target
[ardour.git] / libs / ardour / amp.cc
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"
30 #include "ardour/rc_configuration.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_ZERO)
46         , _current_automation_frame (INT64_MAX)
47         , _gain_automation_buffer(0)
48         , _type (type)
49         , _midi_amp (type != "trim")
50 {
51         Evoral::Parameter p (_type == "trim" ? TrimAutomation : GainAutomation);
52         boost::shared_ptr<AutomationList> gl (new AutomationList (p));
53         _gain_control = boost::shared_ptr<GainControl> (new GainControl ((_type == "trim") ? X_("trimcontrol") : X_("gaincontrol"), s, this, p, gl));
54         _gain_control->set_flags (Controllable::GainLike);
55
56         add_control(_gain_control);
57 }
58
59 std::string
60 Amp::display_name() const
61 {
62         return _("Fader");
63 }
64
65 bool
66 Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out)
67 {
68         out = in;
69         return true;
70 }
71
72 bool
73 Amp::configure_io (ChanCount in, ChanCount out)
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
91                 if (_apply_gain_automation) {
92
93                         gain_t* gab = _gain_automation_buffer;
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) {
100                                                 Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *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) {
123                                 _current_gain = GAIN_COEFF_ZERO;
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
134                                 _current_gain = Amp::apply_gain (bufs, _session.nominal_frame_rate(), nframes, _current_gain, dg, _midi_amp);
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) {
147                                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *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
162         _active = _pending_active;
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 {
168         /** Apply a (potentially) declicked gain to the buffers of @a bufs */
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) {
198                                 Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *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) {
316                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *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) {
336                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *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
351 Amp::apply_simple_gain (AudioBuffer& buf, framecnt_t nframes, gain_t target)
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
404 Amp::GainControl::set_value (double val)
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
411 Amp::GainControl::internal_to_interface (double v) const
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
421 Amp::GainControl::interface_to_internal (double v) const
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
431 Amp::GainControl::internal_to_user (double v) const
432 {
433         return accurate_coefficient_to_dB (v);
434 }
435
436 double
437 Amp::GainControl::user_to_internal (double u) const
438 {
439         return dB_to_coefficient (u);
440 }
441
442 std::string
443 Amp::GainControl::get_user_string () const
444 {
445         char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
446         return std::string(theBuf);
447 }
448
449 /** Write gain automation for this cycle into the buffer previously passed in to
450  *  set_gain_automation_buffer (if we are in automation playback mode and the
451  *  transport is rolling).
452  */
453 void
454 Amp::setup_gain_automation (framepos_t start_frame, framepos_t end_frame, framecnt_t nframes)
455 {
456         Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
457
458         if (am.locked()
459             && (_session.transport_rolling() || _session.bounce_processing())
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) {
466                         _current_gain = _gain_automation_buffer[0];
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
476 Amp::visible() const
477 {
478         return true;
479 }
480
481 std::string
482 Amp::value_as_string (boost::shared_ptr<AutomationControl> ac) const
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
493 /** Sets up the buffer that setup_gain_automation and ::run will use for
494  *  gain automationc curves.  Must be called before setup_gain_automation,
495  *  and must be called with process lock held.
496  */
497
498 void
499 Amp::set_gain_automation_buffer (gain_t* g)
500 {
501         _gain_automation_buffer = g;
502 }