The great audio processing overhaul.
[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 <cstring>
20 #include <cmath>
21 #include <algorithm>
22 #include "ardour/amp.h"
23 #include "ardour/audio_buffer.h"
24 #include "ardour/buffer_set.h"
25 #include "ardour/configuration.h"
26 #include "ardour/io.h"
27 #include "ardour/session.h"
28
29 namespace ARDOUR {
30
31 Amp::Amp(Session& s, IO& io)
32         : Processor(s, "Amp")
33         , _io(io)
34         , _mute(false)
35         , _apply_gain(true)
36         , _apply_gain_automation(false)
37         , _current_gain(1.0)
38         , _desired_gain(1.0)
39 {
40 }
41
42 bool
43 Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out) const
44 {
45         out = in;
46         return true;
47 }
48
49 bool
50 Amp::configure_io (ChanCount in, ChanCount out)
51 {
52         if (out != in) { // always 1:1
53                 return false;
54         }
55         
56         return Processor::configure_io (in, out);
57 }
58
59 void
60 Amp::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes)
61 {
62         gain_t* gab = _session.gain_automation_buffer();
63
64         if (_mute && !bufs.is_silent()) {
65                 Amp::apply_gain (bufs, nframes, _current_mute_gain, _desired_mute_gain, false);
66                 if (_desired_mute_gain == 0.0f) {
67                         bufs.is_silent(true);
68                 }
69         }
70
71         if (_apply_gain) {
72                 
73                 if (_apply_gain_automation) {
74                         
75                         if (_io.phase_invert()) {
76                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
77                                         Sample* const sp = i->data();
78                                         for (nframes_t nx = 0; nx < nframes; ++nx) {
79                                                 sp[nx] *= -gab[nx];
80                                         }
81                                 }
82                         } else {
83                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
84                                         Sample* const sp = i->data();
85                                         for (nframes_t nx = 0; nx < nframes; ++nx) {
86                                                 sp[nx] *= gab[nx];
87                                         }
88                                 }
89                         }
90                         
91                 } else { /* manual (scalar) gain */
92                         
93                         if (_current_gain != _desired_gain) {
94                                 
95                                 Amp::apply_gain (bufs, nframes, _current_gain, _desired_gain, _io.phase_invert());
96                                 _current_gain = _desired_gain;
97                                 
98                         } else if (_current_gain != 0.0f && (_io.phase_invert() || _current_gain != 1.0f)) {
99                                 
100                                 /* no need to interpolate current gain value,
101                                    but its non-unity, so apply it. if the gain
102                                    is zero, do nothing because we'll ship silence
103                                    below.
104                                 */
105
106                                 gain_t this_gain;
107                                 
108                                 if (_io.phase_invert()) {
109                                         this_gain = -_current_gain;
110                                 } else {
111                                         this_gain = _current_gain;
112                                 }
113                                 
114                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
115                                         Sample* const sp = i->data();
116                                         apply_gain_to_buffer(sp, nframes, this_gain);
117                                 }
118
119                         } else if (_current_gain == 0.0f) {
120                                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
121                                         i->clear();
122                                 }
123                         }
124                 }
125         }
126 }
127
128 /** Apply a declicked gain to the audio buffers of @a bufs */
129 void
130 Amp::apply_gain (BufferSet& bufs, nframes_t nframes,
131                 gain_t initial, gain_t target, bool invert_polarity)
132 {
133         if (nframes == 0) {
134                 return;
135         }
136
137         if (bufs.count().n_audio() == 0) {
138                 return;
139         }
140
141         // if we don't need to declick, defer to apply_simple_gain
142         if (initial == target) {
143                 if (target == 0.0) {
144                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
145                                 memset (i->data(), 0, sizeof (Sample) * nframes);
146                         }
147                 } else if (target != 1.0) {
148                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
149                                 apply_gain_to_buffer (i->data(), nframes, target);
150                         }
151                 }
152                 return;
153         }
154
155         const nframes_t declick = std::min ((nframes_t)128, nframes);
156         gain_t         delta;
157         double         fractional_shift = -1.0/declick;
158         double         fractional_pos;
159         gain_t         polscale = invert_polarity ? -1.0f : 1.0f;
160
161         if (target < initial) {
162                 /* fade out: remove more and more of delta from initial */
163                 delta = -(initial - target);
164         } else {
165                 /* fade in: add more and more of delta from initial */
166                 delta = target - initial;
167         }
168
169         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
170                 Sample* const buffer = i->data();
171
172                 fractional_pos = 1.0;
173
174                 for (nframes_t nx = 0; nx < declick; ++nx) {
175                         buffer[nx] *= polscale * (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
176                         fractional_pos += fractional_shift;
177                 }
178                 
179                 /* now ensure the rest of the buffer has the target value applied, if necessary. */
180                 
181                 if (declick != nframes) {
182
183                         if (invert_polarity) {
184                                 target = -target;
185                         }
186
187                         if (target == 0.0) {
188                                 memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
189                         } else if (target != 1.0) {
190                                 apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
191                         }
192                 }
193         }
194 }
195
196 void
197 Amp::apply_simple_gain (BufferSet& bufs, nframes_t nframes, gain_t target)
198 {
199 }
200
201 XMLNode&
202 Amp::state (bool full_state)
203 {
204         return get_state();
205 }
206
207 XMLNode&
208 Amp::get_state()
209 {
210         XMLNode* node = new XMLNode(state_node_name);
211         node->add_property("type", "amp");
212         return *node;
213 }
214
215 } // namespace ARDOUR