1d4ffc1cd7f0601c0de4ce573af9c02473b9d9dc
[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, const std::string& name, boost::shared_ptr<AutomationControl> gc, bool control_midi_also)
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_control (gc)
48         , _gain_automation_buffer(0)
49         , _midi_amp (control_midi_also)
50 {
51         set_display_name (name);
52         add_control (_gain_control);
53 }
54
55 bool
56 Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out)
57 {
58         out = in;
59         return true;
60 }
61
62 bool
63 Amp::configure_io (ChanCount in, ChanCount out)
64 {
65         if (out != in) { // always 1:1
66                 return false;
67         }
68
69         return Processor::configure_io (in, out);
70 }
71
72 void
73 Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool)
74 {
75         if (!_active && !_pending_active) {
76                 return;
77         }
78
79         if (_apply_gain) {
80
81                 if (_apply_gain_automation) {
82
83                         gain_t* gab = _gain_automation_buffer;
84                         assert (gab);
85
86                         if (_midi_amp) {
87                                 for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
88                                         MidiBuffer& mb (*i);
89                                         for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
90                                                 Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
91                                                 if (ev.is_note_on()) {
92                                                         assert(ev.time() >= 0 && ev.time() < nframes);
93                                                         ev.scale_velocity (fabsf (gab[ev.time()]));
94                                                 }
95                                         }
96                                 }
97                         }
98
99
100                         const double a = 156.825 / _session.nominal_frame_rate(); // 25 Hz LPF; see Amp::apply_gain for details
101                         double lpf = _current_gain;
102
103                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
104                                 Sample* const sp = i->data();
105                                 lpf = _current_gain;
106                                 for (pframes_t nx = 0; nx < nframes; ++nx) {
107                                         sp[nx] *= lpf;
108                                         lpf += a * (gab[nx] - lpf);
109                                 }
110                         }
111
112                         if (fabs (lpf) < GAIN_COEFF_TINY) {
113                                 _current_gain = GAIN_COEFF_ZERO;
114                         } else {
115                                 _current_gain = lpf;
116                         }
117
118                 } else { /* manual (scalar) gain */
119
120                         gain_t const dg = _gain_control->user_double();
121
122                         if (_current_gain != dg) {
123
124                                 _current_gain = Amp::apply_gain (bufs, _session.nominal_frame_rate(), nframes, _current_gain, dg, _midi_amp);
125
126                         } else if (_current_gain != GAIN_COEFF_UNITY) {
127
128                                 /* gain has not changed, but its non-unity */
129
130                                 if (_midi_amp) {
131                                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
132                                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
133
134                                                 MidiBuffer& mb (*i);
135
136                                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
137                                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
138                                                         if (ev.is_note_on()) {
139                                                                 ev.scale_velocity (fabsf (_current_gain));
140                                                         }
141                                                 }
142                                         }
143                                 }
144
145                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
146                                         apply_gain_to_buffer (i->data(), nframes, _current_gain);
147                                 }
148                         }
149                 }
150         }
151
152         _active = _pending_active;
153 }
154
155 gain_t
156 Amp::apply_gain (BufferSet& bufs, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target, bool midi_amp)
157 {
158         /** Apply a (potentially) declicked gain to the buffers of @a bufs */
159         gain_t rv = target;
160
161         if (nframes == 0 || bufs.count().n_total() == 0) {
162                 return initial;
163         }
164
165         // if we don't need to declick, defer to apply_simple_gain
166         if (initial == target) {
167                 apply_simple_gain (bufs, nframes, target);
168                 return target;
169         }
170
171         /* MIDI Gain */
172         if (midi_amp) {
173                 /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
174                 for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
175
176                         gain_t  delta;
177                         if (target < initial) {
178                                 /* fade out: remove more and more of delta from initial */
179                                 delta = -(initial - target);
180                         } else {
181                                 /* fade in: add more and more of delta from initial */
182                                 delta = target - initial;
183                         }
184
185                         MidiBuffer& mb (*i);
186
187                         for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
188                                 Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
189
190                                 if (ev.is_note_on()) {
191                                         const gain_t scale = delta * (ev.time()/(double) nframes);
192                                         ev.scale_velocity (fabsf (initial+scale));
193                                 }
194                         }
195                 }
196         }
197
198         /* Audio Gain */
199
200         /* Low pass filter coefficient: 1.0 - e^(-2.0 * Ï€ * f / 48000) f in Hz.
201          * for f << SR,  approx a ~= 6.2 * f / SR;
202          */
203         const double a = 156.825 / sample_rate; // 25 Hz LPF
204
205         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
206                 Sample* const buffer = i->data();
207                 double lpf = initial;
208
209                 for (pframes_t nx = 0; nx < nframes; ++nx) {
210                         buffer[nx] *= lpf;
211                         lpf += a * (target - lpf);
212                 }
213                 if (i == bufs.audio_begin()) {
214                         rv = lpf;
215                 }
216         }
217         if (fabsf (rv - target) < GAIN_COEFF_TINY) return target;
218         if (fabsf (rv) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
219         return rv;
220 }
221
222 void
223 Amp::declick (BufferSet& bufs, framecnt_t nframes, int dir)
224 {
225         if (nframes == 0 || bufs.count().n_total() == 0) {
226                 return;
227         }
228
229         const framecnt_t declick = std::min ((framecnt_t) 512, nframes);
230         const double     fractional_shift = 1.0 / declick ;
231         gain_t           delta, initial;
232
233         if (dir < 0) {
234                 /* fade out: remove more and more of delta from initial */
235                 delta = -1.0;
236                 initial = GAIN_COEFF_UNITY;
237         } else {
238                 /* fade in: add more and more of delta from initial */
239                 delta = 1.0;
240                 initial = GAIN_COEFF_ZERO;
241         }
242
243         /* Audio Gain */
244         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
245                 Sample* const buffer = i->data();
246
247                 double fractional_pos = 0.0;
248
249                 for (pframes_t nx = 0; nx < declick; ++nx) {
250                         buffer[nx] *= initial + (delta * fractional_pos);
251                         fractional_pos += fractional_shift;
252                 }
253
254                 /* now ensure the rest of the buffer has the target value applied, if necessary. */
255                 if (declick != nframes) {
256                         if (dir < 0) {
257                                 memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
258                         }
259                 }
260         }
261 }
262
263
264 gain_t
265 Amp::apply_gain (AudioBuffer& buf, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target)
266 {
267         /* Apply a (potentially) declicked gain to the contents of @a buf
268          * -- used by MonitorProcessor::run()
269          */
270
271         if (nframes == 0) {
272                 return initial;
273         }
274
275         // if we don't need to declick, defer to apply_simple_gain
276         if (initial == target) {
277                 apply_simple_gain (buf, nframes, target);
278                 return target;
279         }
280
281         Sample* const buffer = buf.data();
282         const double a = 156.825 / sample_rate; // 25 Hz LPF, see [other] Amp::apply_gain() above for details
283
284         double lpf = initial;
285         for (pframes_t nx = 0; nx < nframes; ++nx) {
286                 buffer[nx] *= lpf;
287                 lpf += a * (target - lpf);
288         }
289
290         if (fabs (lpf - target) < GAIN_COEFF_TINY) return target;
291         if (fabs (lpf) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
292         return lpf;
293 }
294
295 void
296 Amp::apply_simple_gain (BufferSet& bufs, framecnt_t nframes, gain_t target, bool midi_amp)
297 {
298         if (fabsf (target) < GAIN_COEFF_SMALL) {
299
300                 if (midi_amp) {
301                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
302                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
303                                 MidiBuffer& mb (*i);
304
305                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
306                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
307                                         if (ev.is_note_on()) {
308                                                 ev.set_velocity (0);
309                                         }
310                                 }
311                         }
312                 }
313
314                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
315                         memset (i->data(), 0, sizeof (Sample) * nframes);
316                 }
317
318         } else if (target != GAIN_COEFF_UNITY) {
319
320                 if (midi_amp) {
321                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
322                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
323                                 MidiBuffer& mb (*i);
324
325                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
326                                         Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
327                                         if (ev.is_note_on()) {
328                                                 ev.scale_velocity (fabsf (target));
329                                         }
330                                 }
331                         }
332                 }
333
334                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
335                         apply_gain_to_buffer (i->data(), nframes, target);
336                 }
337         }
338 }
339
340 void
341 Amp::apply_simple_gain (AudioBuffer& buf, framecnt_t nframes, gain_t target)
342 {
343         if (fabsf (target) < GAIN_COEFF_SMALL) {
344                 memset (buf.data(), 0, sizeof (Sample) * nframes);
345         } else if (target != GAIN_COEFF_UNITY) {
346                 apply_gain_to_buffer (buf.data(), nframes, target);
347         }
348 }
349
350 void
351 Amp::inc_gain (gain_t factor, void *src)
352 {
353         float desired_gain = _gain_control->user_double();
354
355         if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
356                 // really?! what's the idea here?
357                 set_gain (0.000001f + (0.000001f * factor), src);
358         } else {
359                 set_gain (desired_gain + (desired_gain * factor), src);
360         }
361 }
362
363 void
364 Amp::set_gain (gain_t val, void *)
365 {
366         _gain_control->set_value (val, Controllable::NoGroup);
367 }
368
369 XMLNode&
370 Amp::state (bool full_state)
371 {
372         XMLNode& node (Processor::state (full_state));
373         node.add_property("type", _gain_control->parameter().type() == GainAutomation ? "amp" : "trim");
374         node.add_child_nocopy (_gain_control->get_state());
375
376         return node;
377 }
378
379 int
380 Amp::set_state (const XMLNode& node, int version)
381 {
382         XMLNode* gain_node;
383
384         Processor::set_state (node, version);
385
386         if ((gain_node = node.child (Controllable::xml_node_name.c_str())) != 0) {
387                 _gain_control->set_state (*gain_node, version);
388         }
389
390         return 0;
391 }
392
393 Amp::GainControl::GainControl (Session& session, const Evoral::Parameter &param, boost::shared_ptr<AutomationList> al)
394         : AutomationControl (session, param, ParameterDescriptor(param),
395                              al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)),
396                              param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) {
397
398         alist()->reset_default (1.0);
399
400         lower_db = accurate_coefficient_to_dB (_desc.lower);
401         range_db = accurate_coefficient_to_dB (_desc.upper) - lower_db;
402 }
403
404 void
405 Amp::GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition /* group_override */)
406 {
407         if (writable()) {
408                 set_value_unchecked (val);
409         }
410 }
411
412 void
413 Amp::GainControl::set_value_unchecked (double val)
414 {
415         AutomationControl::set_value (std::max (std::min (val, (double)_desc.upper), (double)_desc.lower), Controllable::NoGroup);
416         _session.set_dirty ();
417 }
418
419 double
420 Amp::GainControl::internal_to_interface (double v) const
421 {
422         if (_desc.type == GainAutomation) {
423                 return gain_to_slider_position (v);
424         } else {
425                 return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
426         }
427 }
428
429 double
430 Amp::GainControl::interface_to_internal (double v) const
431 {
432         if (_desc.type == GainAutomation) {
433                 return slider_position_to_gain (v);
434         } else {
435                 return dB_to_coefficient (lower_db + v * range_db);
436         }
437 }
438
439 double
440 Amp::GainControl::internal_to_user (double v) const
441 {
442         return accurate_coefficient_to_dB (v);
443 }
444
445 double
446 Amp::GainControl::user_to_internal (double u) const
447 {
448         return dB_to_coefficient (u);
449 }
450
451 std::string
452 Amp::GainControl::get_user_string () const
453 {
454         char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
455         return std::string(theBuf);
456 }
457
458 /** Write gain automation for this cycle into the buffer previously passed in to
459  *  set_gain_automation_buffer (if we are in automation playback mode and the
460  *  transport is rolling).
461  */
462 void
463 Amp::setup_gain_automation (framepos_t start_frame, framepos_t end_frame, framecnt_t nframes)
464 {
465         Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
466
467         if (am.locked()
468             && (_session.transport_rolling() || _session.bounce_processing())
469             && _gain_control->automation_playback())
470         {
471                 assert (_gain_automation_buffer);
472                 _apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
473                         start_frame, end_frame, _gain_automation_buffer, nframes);
474                 if (start_frame != _current_automation_frame) {
475                         _current_gain = _gain_automation_buffer[0];
476                 }
477                 _current_automation_frame = end_frame;
478         } else {
479                 _apply_gain_automation = false;
480                 _current_automation_frame = INT64_MAX;
481         }
482 }
483
484 bool
485 Amp::visible() const
486 {
487         return true;
488 }
489
490 std::string
491 Amp::value_as_string (boost::shared_ptr<AutomationControl> ac) const
492 {
493         if (ac == _gain_control) {
494                 char buffer[32];
495                 snprintf (buffer, sizeof (buffer), _("%.2fdB"), ac->internal_to_user (ac->get_value ()));
496                 return buffer;
497         }
498
499         return Automatable::value_as_string (ac);
500 }
501
502 /** Sets up the buffer that setup_gain_automation and ::run will use for
503  *  gain automationc curves.  Must be called before setup_gain_automation,
504  *  and must be called with process lock held.
505  */
506
507 void
508 Amp::set_gain_automation_buffer (gain_t* g)
509 {
510         _gain_automation_buffer = g;
511 }