~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/ardour/amp.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/session.h"
 
31
 
 
32
#include "i18n.h"
 
33
 
 
34
using namespace ARDOUR;
 
35
using namespace PBD;
 
36
using std::min;
 
37
 
 
38
/* gain range of -inf to +6dB, default 0dB */
 
39
const float Amp::max_gain_coefficient = 1.99526231f;
 
40
 
 
41
Amp::Amp (Session& s)
 
42
        : Processor(s, "Amp")
 
43
        , _apply_gain(true)
 
44
        , _apply_gain_automation(false)
 
45
        , _current_gain(1.0)
 
46
        , _gain_automation_buffer(0)
 
47
{
 
48
        Evoral::Parameter p (GainAutomation);
 
49
        p.set_range (0, max_gain_coefficient, 1, false);
 
50
        boost::shared_ptr<AutomationList> gl (new AutomationList (p));
 
51
        _gain_control = boost::shared_ptr<GainControl> (new GainControl (X_("gaincontrol"), s, this, p, gl));
 
52
        _gain_control->set_flags (Controllable::GainLike);
 
53
 
 
54
        add_control(_gain_control);
 
55
}
 
56
 
 
57
std::string
 
58
Amp::display_name() const
 
59
{
 
60
        return _("Fader");
 
61
}
 
62
 
 
63
bool
 
64
Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out)
 
65
{
 
66
        out = in;
 
67
        return true;
 
68
}
 
69
 
 
70
bool
 
71
Amp::configure_io (ChanCount in, ChanCount out)
 
72
{
 
73
        if (out != in) { // always 1:1
 
74
                return false;
 
75
        }
 
76
 
 
77
        return Processor::configure_io (in, out);
 
78
}
 
79
 
 
80
void
 
81
Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, 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
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
95
                                Sample* const sp = i->data();
 
96
                                for (pframes_t nx = 0; nx < nframes; ++nx) {
 
97
                                        sp[nx] *= gab[nx];
 
98
                                }
 
99
                        }
 
100
 
 
101
                        _current_gain = gab[nframes-1];
 
102
 
 
103
                } else { /* manual (scalar) gain */
 
104
 
 
105
                        gain_t const dg = _gain_control->user_double();
 
106
 
 
107
                        if (_current_gain != dg) {
 
108
 
 
109
                                Amp::apply_gain (bufs, nframes, _current_gain, dg);
 
110
                                _current_gain = dg;
 
111
 
 
112
                        } else if (_current_gain != 1.0f) {
 
113
 
 
114
                                /* gain has not changed, but its non-unity
 
115
                                */
 
116
 
 
117
                                for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
 
118
 
 
119
                                        MidiBuffer& mb (*i);
 
120
                                        
 
121
                                        for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
 
122
                                                Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
 
123
                                                if (ev.is_note_on()) {
 
124
                                                        ev.scale_velocity (_current_gain);
 
125
                                                }
 
126
                                        }
 
127
                                }
 
128
 
 
129
                                for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
130
                                        apply_gain_to_buffer (i->data(), nframes, _current_gain);
 
131
                                }
 
132
                        }
 
133
                }
 
134
        }
 
135
 
 
136
        _active = _pending_active;
 
137
}
 
138
 
 
139
void
 
140
Amp::apply_gain (BufferSet& bufs, framecnt_t nframes, gain_t initial, gain_t target)
 
141
{
 
142
        /** Apply a (potentially) declicked gain to the buffers of @a bufs
 
143
         */
 
144
 
 
145
        if (nframes == 0 || bufs.count().n_total() == 0) {
 
146
                return;
 
147
        }
 
148
 
 
149
        // if we don't need to declick, defer to apply_simple_gain
 
150
        if (initial == target) {
 
151
                apply_simple_gain (bufs, nframes, target);
 
152
                return;
 
153
        }
 
154
 
 
155
        const framecnt_t declick = std::min ((framecnt_t) 128, nframes);
 
156
        gain_t         delta;
 
157
        double         fractional_shift = -1.0/declick;
 
158
        double         fractional_pos;
 
159
 
 
160
        if (target < initial) {
 
161
                /* fade out: remove more and more of delta from initial */
 
162
                delta = -(initial - target);
 
163
        } else {
 
164
                /* fade in: add more and more of delta from initial */
 
165
                delta = target - initial;
 
166
        }
 
167
 
 
168
        /* MIDI Gain */
 
169
 
 
170
        for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
 
171
 
 
172
                MidiBuffer& mb (*i);
 
173
 
 
174
                for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
 
175
                        Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
 
176
 
 
177
                        if (ev.is_note_on()) {
 
178
                                const gain_t scale = delta * (ev.time()/(double) nframes);
 
179
                                ev.scale_velocity (initial+scale);
 
180
                        }
 
181
                }
 
182
        }
 
183
 
 
184
        /* Audio Gain */
 
185
 
 
186
        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
187
                Sample* const buffer = i->data();
 
188
 
 
189
                fractional_pos = 1.0;
 
190
 
 
191
                for (pframes_t nx = 0; nx < declick; ++nx) {
 
192
                        buffer[nx] *= (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
 
193
                        fractional_pos += fractional_shift;
 
194
                }
 
195
 
 
196
                /* now ensure the rest of the buffer has the target value applied, if necessary. */
 
197
 
 
198
                if (declick != nframes) {
 
199
 
 
200
                        if (target == 0.0) {
 
201
                                memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
 
202
                        } else if (target != 1.0) {
 
203
                                apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
 
204
                        }
 
205
                }
 
206
        }
 
207
}
 
208
 
 
209
void
 
210
Amp::declick (BufferSet& bufs, framecnt_t nframes, int dir)
 
211
{
 
212
        /* Almost exactly like ::apply_gain() but skips MIDI buffers and has fixed initial+target
 
213
           values.
 
214
         */
 
215
 
 
216
        if (nframes == 0 || bufs.count().n_total() == 0) {
 
217
                return;
 
218
        }
 
219
 
 
220
        const framecnt_t declick = std::min ((framecnt_t) 128, nframes);
 
221
        gain_t         delta, initial, target;
 
222
        double         fractional_shift = -1.0/(declick-1);
 
223
        double         fractional_pos;
 
224
 
 
225
        if (dir < 0) {
 
226
                /* fade out: remove more and more of delta from initial */
 
227
                delta = -1.0;
 
228
                initial = 1.0;
 
229
                target = 0.0;
 
230
        } else {
 
231
                /* fade in: add more and more of delta from initial */
 
232
                delta = 1.0;
 
233
                initial = 0.0;
 
234
                target = 1.0;
 
235
        }
 
236
 
 
237
        /* Audio Gain */
 
238
 
 
239
        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
240
                Sample* const buffer = i->data();
 
241
 
 
242
                fractional_pos = 1.0;
 
243
 
 
244
                for (pframes_t nx = 0; nx < declick; ++nx) {
 
245
                        buffer[nx] *= (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
 
246
                        fractional_pos += fractional_shift;
 
247
                }
 
248
 
 
249
                /* now ensure the rest of the buffer has the target value applied, if necessary. */
 
250
 
 
251
                if (declick != nframes) {
 
252
 
 
253
                        if (target == 0.0) {
 
254
                                memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
 
255
                        } else if (target != 1.0) {
 
256
                                apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
 
257
                        }
 
258
                }
 
259
        }
 
260
}
 
261
 
 
262
void
 
263
Amp::apply_gain (AudioBuffer& buf, framecnt_t nframes, gain_t initial, gain_t target)
 
264
{
 
265
        /** Apply a (potentially) declicked gain to the contents of @a buf
 
266
         */
 
267
 
 
268
        if (nframes == 0) {
 
269
                return;
 
270
        }
 
271
 
 
272
        // if we don't need to declick, defer to apply_simple_gain
 
273
        if (initial == target) {
 
274
                apply_simple_gain (buf, nframes, target);
 
275
                return;
 
276
        }
 
277
 
 
278
        const framecnt_t declick = std::min ((framecnt_t) 128, nframes);
 
279
        gain_t         delta;
 
280
        double         fractional_shift = -1.0/declick;
 
281
        double         fractional_pos;
 
282
 
 
283
        if (target < initial) {
 
284
                /* fade out: remove more and more of delta from initial */
 
285
                delta = -(initial - target);
 
286
        } else {
 
287
                /* fade in: add more and more of delta from initial */
 
288
                delta = target - initial;
 
289
        }
 
290
 
 
291
 
 
292
        Sample* const buffer = buf.data();
 
293
 
 
294
        fractional_pos = 1.0;
 
295
 
 
296
        for (pframes_t nx = 0; nx < declick; ++nx) {
 
297
                buffer[nx] *= (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
 
298
                fractional_pos += fractional_shift;
 
299
        }
 
300
 
 
301
        /* now ensure the rest of the buffer has the target value applied, if necessary. */
 
302
 
 
303
        if (declick != nframes) {
 
304
 
 
305
                if (target == 0.0) {
 
306
                        memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
 
307
                } else if (target != 1.0) {
 
308
                        apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
 
309
                }
 
310
        }
 
311
}
 
312
 
 
313
void
 
314
Amp::apply_simple_gain (BufferSet& bufs, framecnt_t nframes, gain_t target)
 
315
{
 
316
        if (target == 0.0) {
 
317
 
 
318
                for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
 
319
                        MidiBuffer& mb (*i);
 
320
 
 
321
                        for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
 
322
                                Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
 
323
                                if (ev.is_note_on()) {
 
324
                                        ev.set_velocity (0);
 
325
                                }
 
326
                        }
 
327
                }
 
328
 
 
329
                for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
330
                        memset (i->data(), 0, sizeof (Sample) * nframes);
 
331
                }
 
332
 
 
333
        } else if (target != 1.0) {
 
334
 
 
335
                for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
 
336
                        MidiBuffer& mb (*i);
 
337
 
 
338
                        for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
 
339
                                Evoral::MIDIEvent<MidiBuffer::TimeType> ev = *m;
 
340
                                if (ev.is_note_on()) {
 
341
                                        ev.scale_velocity (target);
 
342
                                }
 
343
                        }
 
344
                }
 
345
 
 
346
                for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
 
347
                        apply_gain_to_buffer (i->data(), nframes, target);
 
348
                }
 
349
        }
 
350
}
 
351
 
 
352
void
 
353
Amp::apply_simple_gain (AudioBuffer& buf, framecnt_t nframes, gain_t target)
 
354
{
 
355
        if (target == 0.0) {
 
356
                memset (buf.data(), 0, sizeof (Sample) * nframes);
 
357
        } else if (target != 1.0) {
 
358
                apply_gain_to_buffer (buf.data(), nframes, target);
 
359
        }
 
360
}
 
361
 
 
362
void
 
363
Amp::inc_gain (gain_t factor, void *src)
 
364
{
 
365
        float desired_gain = _gain_control->user_double();
 
366
 
 
367
        if (desired_gain == 0.0f) {
 
368
                set_gain (0.000001f + (0.000001f * factor), src);
 
369
        } else {
 
370
                set_gain (desired_gain + (desired_gain * factor), src);
 
371
        }
 
372
}
 
373
 
 
374
void
 
375
Amp::set_gain (gain_t val, void *src)
 
376
{
 
377
        val = min (val, max_gain_coefficient);
 
378
 
 
379
        if (src != _gain_control.get()) {
 
380
                _gain_control->set_value (val);
 
381
                // bit twisty, this will come back and call us again
 
382
                // (this keeps control in sync with reality)
 
383
                return;
 
384
        }
 
385
 
 
386
        _gain_control->set_double (val);
 
387
        _session.set_dirty();
 
388
}
 
389
 
 
390
XMLNode&
 
391
Amp::state (bool full_state)
 
392
{
 
393
        XMLNode& node (Processor::state (full_state));
 
394
        node.add_property("type", "amp");
 
395
        node.add_child_nocopy (_gain_control->get_state());
 
396
 
 
397
        return node;
 
398
}
 
399
 
 
400
int
 
401
Amp::set_state (const XMLNode& node, int version)
 
402
{
 
403
        XMLNode* gain_node;
 
404
 
 
405
        Processor::set_state (node, version);
 
406
 
 
407
        if ((gain_node = node.child (Controllable::xml_node_name.c_str())) != 0) {
 
408
                _gain_control->set_state (*gain_node, version);
 
409
        }
 
410
 
 
411
        return 0;
 
412
}
 
413
 
 
414
void
 
415
Amp::GainControl::set_value (double val)
 
416
{
 
417
        if (val > max_gain_coefficient) {
 
418
                val = max_gain_coefficient;
 
419
        }
 
420
 
 
421
        _amp->set_gain (val, this);
 
422
 
 
423
        AutomationControl::set_value(val);
 
424
}
 
425
 
 
426
double
 
427
Amp::GainControl::internal_to_interface (double v) const
 
428
{
 
429
        return gain_to_slider_position (v);
 
430
}
 
431
 
 
432
double
 
433
Amp::GainControl::interface_to_internal (double v) const
 
434
{
 
435
        return slider_position_to_gain (v);
 
436
}
 
437
 
 
438
double
 
439
Amp::GainControl::internal_to_user (double v) const
 
440
{
 
441
        return accurate_coefficient_to_dB (v);
 
442
}
 
443
 
 
444
/** Write gain automation for this cycle into the buffer previously passed in to
 
445
 *  set_gain_automation_buffer (if we are in automation playback mode and the
 
446
 *  transport is rolling).
 
447
 */
 
448
void
 
449
Amp::setup_gain_automation (framepos_t start_frame, framepos_t end_frame, framecnt_t nframes)
 
450
{
 
451
        Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
 
452
 
 
453
        if (am.locked() && _session.transport_rolling() && _gain_control->automation_playback()) {
 
454
                assert (_gain_automation_buffer);
 
455
                _apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
 
456
                        start_frame, end_frame, _gain_automation_buffer, nframes);
 
457
        } else {
 
458
                _apply_gain_automation = false;
 
459
        }
 
460
}
 
461
 
 
462
bool
 
463
Amp::visible() const
 
464
{
 
465
        return true;
 
466
}
 
467
 
 
468
std::string
 
469
Amp::value_as_string (boost::shared_ptr<AutomationControl> ac) const
 
470
{
 
471
        if (ac == _gain_control) {
 
472
                char buffer[32];
 
473
                snprintf (buffer, sizeof (buffer), "%.2fdB", ac->internal_to_user (ac->get_value ()));
 
474
                return buffer;
 
475
        }
 
476
 
 
477
        return Automatable::value_as_string (ac);
 
478
}
 
479
 
 
480
/** Sets up the buffer that setup_gain_automation and ::run will use for
 
481
 *  gain automationc curves.  Must be called before setup_gain_automation,
 
482
 *  and must be called with process lock held.
 
483
 */
 
484
 
 
485
void
 
486
Amp::set_gain_automation_buffer (gain_t* g)
 
487
{
 
488
        _gain_automation_buffer = g;
 
489
}