PBD::Signal<...>::connect() is already thread safe, so drop intermediate proxy/call_s...
[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/gain_control.h"
30 #include "ardour/midi_buffer.h"
31 #include "ardour/rc_configuration.h"
32 #include "ardour/session.h"
33
34 #include "pbd/i18n.h"
35
36 using namespace ARDOUR;
37 using namespace PBD;
38
39 // used for low-pass filter denormal protection
40 #define GAIN_COEFF_TINY (1e-10) // -200dB
41
42 Amp::Amp (Session& s, const std::string& name, boost::shared_ptr<GainControl> gc, bool control_midi_also)
43         : Processor(s, "Amp")
44         , _apply_gain(true)
45         , _apply_gain_automation(false)
46         , _current_gain(GAIN_COEFF_ZERO)
47         , _current_automation_frame (INT64_MAX)
48         , _gain_control (gc)
49         , _gain_automation_buffer(0)
50         , _midi_amp (control_midi_also)
51 {
52         set_display_name (name);
53         add_control (_gain_control);
54 }
55
56 bool
57 Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out)
58 {
59         out = in;
60         return true;
61 }
62
63 bool
64 Amp::configure_io (ChanCount in, ChanCount out)
65 {
66         if (out != in) { // always 1:1
67                 return false;
68         }
69
70         return Processor::configure_io (in, out);
71 }
72
73 static void
74 scale_midi_velocity(Evoral::Event<MidiBuffer::TimeType>& ev, float factor)
75 {
76         factor = std::max(factor, 0.0f);
77         ev.set_velocity(std::min(127L, lrintf(ev.velocity() * factor)));
78 }
79
80 void
81 Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, double /*speed*/, pframes_t nframes, bool)
82 {
83         if (!_active && !_pending_active) {
84                 return;
85         }
86
87         if (_apply_gain) {
88
89                 if (_apply_gain_automation) {
90
91                         gain_t* gab = _gain_automation_buffer;
92                         assert (gab);
93
94                         if (_midi_amp) {
95                                 for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
96                                         MidiBuffer& mb (*i);
97                                         for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
98                                                 Evoral::Event<MidiBuffer::TimeType> ev = *m;
99                                                 if (ev.is_note_on()) {
100                                                         assert(ev.time() >= 0 && ev.time() < nframes);
101                                                         scale_midi_velocity (ev, fabsf (gab[ev.time()]));
102                                                 }
103                                         }
104                                 }
105                         }
106
107
108                         const double a = 156.825 / _session.nominal_frame_rate(); // 25 Hz LPF; see Amp::apply_gain for details
109                         double lpf = _current_gain;
110
111                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
112                                 Sample* const sp = i->data();
113                                 lpf = _current_gain;
114                                 for (pframes_t nx = 0; nx < nframes; ++nx) {
115                                         sp[nx] *= lpf;
116                                         lpf += a * (gab[nx] - lpf);
117                                 }
118                         }
119
120                         if (fabs (lpf) < GAIN_COEFF_TINY) {
121                                 _current_gain = GAIN_COEFF_ZERO;
122                         } else {
123                                 _current_gain = lpf;
124                         }
125
126                 } else { /* manual (scalar) gain */
127
128                         gain_t const dg = _gain_control->get_value();
129
130                         if (_current_gain != dg) {
131
132                                 _current_gain = Amp::apply_gain (bufs, _session.nominal_frame_rate(), nframes, _current_gain, dg, _midi_amp);
133
134                         } else if (_current_gain != GAIN_COEFF_UNITY) {
135
136                                 /* gain has not changed, but its non-unity */
137
138                                 if (_midi_amp) {
139                                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
140                                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
141
142                                                 MidiBuffer& mb (*i);
143
144                                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
145                                                         Evoral::Event<MidiBuffer::TimeType> ev = *m;
146                                                         if (ev.is_note_on()) {
147                                                                 scale_midi_velocity (ev, fabsf (_current_gain));
148                                                         }
149                                                 }
150                                         }
151                                 }
152
153                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
154                                         apply_gain_to_buffer (i->data(), nframes, _current_gain);
155                                 }
156                         }
157                 }
158         }
159
160         _active = _pending_active;
161 }
162
163 gain_t
164 Amp::apply_gain (BufferSet& bufs, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target, bool midi_amp)
165 {
166         /** Apply a (potentially) declicked gain to the buffers of @a bufs */
167         gain_t rv = target;
168
169         if (nframes == 0 || bufs.count().n_total() == 0) {
170                 return initial;
171         }
172
173         // if we don't need to declick, defer to apply_simple_gain
174         if (initial == target) {
175                 apply_simple_gain (bufs, nframes, target);
176                 return target;
177         }
178
179         /* MIDI Gain */
180         if (midi_amp) {
181                 /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
182                 for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
183
184                         gain_t  delta;
185                         if (target < initial) {
186                                 /* fade out: remove more and more of delta from initial */
187                                 delta = -(initial - target);
188                         } else {
189                                 /* fade in: add more and more of delta from initial */
190                                 delta = target - initial;
191                         }
192
193                         MidiBuffer& mb (*i);
194
195                         for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
196                                 Evoral::Event<MidiBuffer::TimeType> ev = *m;
197
198                                 if (ev.is_note_on()) {
199                                         const gain_t scale = delta * (ev.time()/(double) nframes);
200                                         scale_midi_velocity (ev, fabsf (initial + scale));
201                                 }
202                         }
203                 }
204         }
205
206         /* Audio Gain */
207
208         /* Low pass filter coefficient: 1.0 - e^(-2.0 * Ï€ * f / 48000) f in Hz.
209          * for f << SR,  approx a ~= 6.2 * f / SR;
210          */
211         const double a = 156.825 / sample_rate; // 25 Hz LPF
212
213         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
214                 Sample* const buffer = i->data();
215                 double lpf = initial;
216
217                 for (pframes_t nx = 0; nx < nframes; ++nx) {
218                         buffer[nx] *= lpf;
219                         lpf += a * (target - lpf);
220                 }
221                 if (i == bufs.audio_begin()) {
222                         rv = lpf;
223                 }
224         }
225         if (fabsf (rv - target) < GAIN_COEFF_TINY) return target;
226         if (fabsf (rv) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
227         return rv;
228 }
229
230 void
231 Amp::declick (BufferSet& bufs, framecnt_t nframes, int dir)
232 {
233         if (nframes == 0 || bufs.count().n_total() == 0) {
234                 return;
235         }
236
237         const framecnt_t declick = std::min ((framecnt_t) 512, nframes);
238         const double     fractional_shift = 1.0 / declick ;
239         gain_t           delta, initial;
240
241         if (dir < 0) {
242                 /* fade out: remove more and more of delta from initial */
243                 delta = -1.0;
244                 initial = GAIN_COEFF_UNITY;
245         } else {
246                 /* fade in: add more and more of delta from initial */
247                 delta = 1.0;
248                 initial = GAIN_COEFF_ZERO;
249         }
250
251         /* Audio Gain */
252         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
253                 Sample* const buffer = i->data();
254
255                 double fractional_pos = 0.0;
256
257                 for (pframes_t nx = 0; nx < declick; ++nx) {
258                         buffer[nx] *= initial + (delta * fractional_pos);
259                         fractional_pos += fractional_shift;
260                 }
261
262                 /* now ensure the rest of the buffer has the target value applied, if necessary. */
263                 if (declick != nframes) {
264                         if (dir < 0) {
265                                 memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
266                         }
267                 }
268         }
269 }
270
271
272 gain_t
273 Amp::apply_gain (AudioBuffer& buf, framecnt_t sample_rate, framecnt_t nframes, gain_t initial, gain_t target)
274 {
275         /* Apply a (potentially) declicked gain to the contents of @a buf
276          * -- used by MonitorProcessor::run()
277          */
278
279         if (nframes == 0) {
280                 return initial;
281         }
282
283         // if we don't need to declick, defer to apply_simple_gain
284         if (initial == target) {
285                 apply_simple_gain (buf, nframes, target);
286                 return target;
287         }
288
289         Sample* const buffer = buf.data();
290         const double a = 156.825 / sample_rate; // 25 Hz LPF, see [other] Amp::apply_gain() above for details
291
292         double lpf = initial;
293         for (pframes_t nx = 0; nx < nframes; ++nx) {
294                 buffer[nx] *= lpf;
295                 lpf += a * (target - lpf);
296         }
297
298         if (fabs (lpf - target) < GAIN_COEFF_TINY) return target;
299         if (fabs (lpf) < GAIN_COEFF_TINY) return GAIN_COEFF_ZERO;
300         return lpf;
301 }
302
303 void
304 Amp::apply_simple_gain (BufferSet& bufs, framecnt_t nframes, gain_t target, bool midi_amp)
305 {
306         if (fabsf (target) < GAIN_COEFF_SMALL) {
307
308                 if (midi_amp) {
309                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
310                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
311                                 MidiBuffer& mb (*i);
312
313                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
314                                         Evoral::Event<MidiBuffer::TimeType> ev = *m;
315                                         if (ev.is_note_on()) {
316                                                 ev.set_velocity (0);
317                                         }
318                                 }
319                         }
320                 }
321
322                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
323                         memset (i->data(), 0, sizeof (Sample) * nframes);
324                 }
325
326         } else if (target != GAIN_COEFF_UNITY) {
327
328                 if (midi_amp) {
329                         /* don't Trim midi velocity -- only relevant for Midi on Audio tracks */
330                         for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
331                                 MidiBuffer& mb (*i);
332
333                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
334                                         Evoral::Event<MidiBuffer::TimeType> ev = *m;
335                                         if (ev.is_note_on()) {
336                                                 scale_midi_velocity(ev, fabsf (target));
337                                         }
338                                 }
339                         }
340                 }
341
342                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
343                         apply_gain_to_buffer (i->data(), nframes, target);
344                 }
345         }
346 }
347
348 void
349 Amp::apply_simple_gain (AudioBuffer& buf, framecnt_t nframes, gain_t target)
350 {
351         if (fabsf (target) < GAIN_COEFF_SMALL) {
352                 memset (buf.data(), 0, sizeof (Sample) * nframes);
353         } else if (target != GAIN_COEFF_UNITY) {
354                 apply_gain_to_buffer (buf.data(), nframes, target);
355         }
356 }
357
358 XMLNode&
359 Amp::state (bool full_state)
360 {
361         XMLNode& node (Processor::state (full_state));
362         node.add_property("type", _gain_control->parameter().type() == GainAutomation ? "amp" : "trim");
363         node.add_child_nocopy (_gain_control->get_state());
364
365         return node;
366 }
367
368 int
369 Amp::set_state (const XMLNode& node, int version)
370 {
371         XMLNode* gain_node;
372
373         Processor::set_state (node, version);
374
375         if ((gain_node = node.child (Controllable::xml_node_name.c_str())) != 0) {
376                 _gain_control->set_state (*gain_node, version);
377         }
378
379         return 0;
380 }
381
382 /** Write gain automation for this cycle into the buffer previously passed in to
383  *  set_gain_automation_buffer (if we are in automation playback mode and the
384  *  transport is rolling).
385  */
386 void
387 Amp::setup_gain_automation (framepos_t start_frame, framepos_t end_frame, framecnt_t nframes)
388 {
389         Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
390
391         if (am.locked()
392             && (_session.transport_rolling() || _session.bounce_processing())
393             && _gain_control->automation_playback())
394         {
395                 assert (_gain_automation_buffer);
396                 _apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
397                         start_frame, end_frame, _gain_automation_buffer, nframes);
398
399                 /* XXX the future requires a way to automate the control master
400                  * and merge its own automation vector/curve with this one. We
401                  * don't have a way to do that just yet, so for now, just get
402                  * the master's current gain and scale our own automation
403                  * vector/curve by this value.
404                  */
405
406                 if (_gain_control->slaved()) {
407                         const double master_gain = _gain_control->get_masters_value ();
408                         if (master_gain != 1.0) {
409                                 apply_gain_to_buffer (_gain_automation_buffer, nframes, master_gain);
410                         }
411                 }
412
413                 if (start_frame != _current_automation_frame && _session.bounce_processing ()) {
414                         _current_gain = _gain_automation_buffer[0];
415                 }
416                 _current_automation_frame = end_frame;
417         } else {
418                 _apply_gain_automation = false;
419                 _current_automation_frame = INT64_MAX;
420         }
421 }
422
423 bool
424 Amp::visible() const
425 {
426         return true;
427 }
428
429 std::string
430 Amp::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
431 {
432         if (ac == _gain_control) {
433                 char buffer[32];
434                 snprintf (buffer, sizeof (buffer), _("%.2fdB"), ac->internal_to_user (ac->get_value ()));
435                 return buffer;
436         }
437
438         return Automatable::value_as_string (ac);
439 }
440
441 /** Sets up the buffer that setup_gain_automation and ::run will use for
442  *  gain automationc curves.  Must be called before setup_gain_automation,
443  *  and must be called with process lock held.
444  */
445
446 void
447 Amp::set_gain_automation_buffer (gain_t* g)
448 {
449         _gain_automation_buffer = g;
450 }