Delayline: report if set_delay() actually changed the delay
[ardour.git] / libs / ardour / delayline.cc
1 /*
2     Copyright (C) 2006, 2013 Paul Davis
3     Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify it
6     under the terms of the GNU General Public License as published by the Free
7     Software Foundation; either version 2 of the License, or (at your option)
8     any later version.
9
10     This program is distributed in the hope that it will be useful, but WITHOUT
11     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13     for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <assert.h>
21 #include <cmath>
22
23 #include "pbd/compose.h"
24
25 #include "ardour/debug.h"
26 #include "ardour/audio_buffer.h"
27 #include "ardour/midi_buffer.h"
28 #include "ardour/buffer_set.h"
29 #include "ardour/delayline.h"
30
31 using namespace std;
32 using namespace PBD;
33 using namespace ARDOUR;
34
35 DelayLine::DelayLine (Session& s, const std::string& name)
36     : Processor (s, string_compose ("latcomp-%1-%2", name, this))
37                 , _delay(0)
38                 , _pending_delay(0)
39                 , _bsiz(0)
40                 , _pending_bsiz(0)
41                 , _roff(0)
42                 , _woff(0)
43                 , _pending_flush(false)
44 {
45 }
46
47 DelayLine::~DelayLine ()
48 {
49 }
50
51 bool
52 DelayLine::set_name (const string& name)
53 {
54         return Processor::set_name (string_compose ("latcomp-%1-%2", name, this));
55 }
56
57 #define FADE_LEN (16)
58 void
59 DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /* end_sample */, double /* speed */, pframes_t nsamples, bool)
60 {
61         const uint32_t chn = _configured_output.n_audio();
62         pframes_t p0 = 0;
63         uint32_t c;
64
65         const sampleoffset_t pending_delay = _pending_delay;
66         const sampleoffset_t delay_diff = _delay - pending_delay;
67         const bool pending_flush = _pending_flush;
68         _pending_flush = false;
69
70         /* run() and set_delay() may be called in parallel by
71          * different threads.
72          * if a larger buffer is needed, it is allocated in
73          * set_delay(), here it is just swap'ed in place
74          */
75         if (_pending_bsiz) {
76                 assert(_pending_bsiz >= _bsiz);
77
78                 const size_t boff = _pending_bsiz - _bsiz;
79                 if (_bsiz > 0) {
80                         /* write offset is retained. copy existing data to new buffer */
81                         sampleoffset_t wl = _bsiz - _woff;
82                         memcpy(_pending_buf.get(), _buf.get(), sizeof(Sample) * _woff * chn);
83                         memcpy(_pending_buf.get() + (_pending_bsiz - wl) * chn, _buf.get() + _woff * chn, sizeof(Sample) * wl * chn);
84
85                         /* new buffer is all zero by default, fade into the existing data copied above */
86                         sampleoffset_t wo = _pending_bsiz - wl;
87                         for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
88                                 const gain_t gain = (gain_t)pos / (gain_t)FADE_LEN;
89                                 for (c = 0; c < _configured_output.n_audio(); ++c) {
90                                         _pending_buf.get()[ wo * chn + c ] *= gain;
91                                         wo = (wo + 1) % (_pending_bsiz + 1);
92                                 }
93                         }
94
95                         /* read-pointer will be moved and may up anywhere..
96                          * copy current data for smooth fade-out below
97                          */
98                         sampleoffset_t roold = _roff;
99                         sampleoffset_t ro = _roff;
100                         if (ro > _woff) {
101                                 ro += boff;
102                         }
103                         ro += delay_diff;
104                         if (ro < 0) {
105                                 ro -= (_pending_bsiz + 1) * floor(ro / (float)(_pending_bsiz + 1));
106                         }
107                         ro = ro % (_pending_bsiz + 1);
108                         for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
109                                 for (c = 0; c < _configured_output.n_audio(); ++c) {
110                                         _pending_buf.get()[ ro * chn + c ] = _buf.get()[ roold * chn + c ];
111                                 }
112                                 ro = (ro + 1) % (_pending_bsiz + 1);
113                                 roold = (roold + 1) % (_bsiz + 1);
114                         }
115                 }
116
117                 if (_roff > _woff) {
118                         _roff += boff;
119                 }
120
121                 // use shared_array::swap() ??
122                 _buf = _pending_buf;
123                 _bsiz = _pending_bsiz;
124                 _pending_bsiz = 0;
125                 _pending_buf.reset();
126         }
127
128         /* there may be no buffer when delay == 0.
129          * we also need to check audio-channels in case all audio-channels
130          * were removed in which case no new buffer was allocated. */
131         Sample *buf = _buf.get();
132         if (buf && _configured_output.n_audio() > 0) {
133
134                 assert (_bsiz >= pending_delay);
135                 const samplecnt_t rbs = _bsiz + 1;
136
137                 if (pending_delay != _delay || pending_flush) {
138                         const pframes_t fade_len = (nsamples >= FADE_LEN) ? FADE_LEN : nsamples / 2;
139
140                         DEBUG_TRACE (DEBUG::LatencyCompensation,
141                                         string_compose ("Old %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
142                                                 name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
143
144                         // fade out at old position
145                         c = 0;
146                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) {
147                                 Sample * const data = i->data();
148                                 sampleoffset_t roff = _roff;
149                                 sampleoffset_t woff = _woff;
150                                 for (pframes_t pos = 0; pos < fade_len; ++pos) {
151                                         const gain_t gain = (gain_t)(fade_len - pos) / (gain_t)fade_len;
152                                         buf[ woff * chn + c ] = data[ pos ];
153                                         data[ pos ] = buf[ roff * chn + c ] * gain;
154                                         roff = (roff + 1) % rbs;
155                                         woff = (woff + 1) % rbs;
156                                 }
157                         }
158                         _roff = (_roff + fade_len) % rbs;
159                         _woff = (_woff + fade_len) % rbs;
160
161                         if (pending_flush) {
162                                 DEBUG_TRACE (DEBUG::LatencyCompensation,
163                                                 string_compose ("Flush buffer: %1\n", name()));
164                                 memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample));
165                         }
166
167                         // adjust read pointer
168                         _roff += _delay - pending_delay;
169
170                         if (_roff < 0) {
171                                 _roff -= rbs * floor(_roff / (float)rbs);
172                         }
173                         _roff = _roff % rbs;
174
175                         // fade in at new position
176                         c = 0;
177                         for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) {
178                                 Sample * const data = i->data();
179                                 sampleoffset_t roff = _roff;
180                                 sampleoffset_t woff = _woff;
181                                 for (pframes_t pos = fade_len; pos < 2 * fade_len; ++pos) {
182                                         const gain_t gain = (gain_t)(pos - fade_len) / (gain_t)fade_len;
183                                         buf[ woff * chn + c ] = data[ pos ];
184                                         data[ pos ] = buf[ roff * chn + c ] * gain;
185                                         roff = (roff + 1) % rbs;
186                                         woff = (woff + 1) % rbs;
187                                 }
188                         }
189                         _roff = (_roff + fade_len) % rbs;
190                         _woff = (_woff + fade_len) % rbs;
191                         p0  = 2 * fade_len;
192
193                         _delay = pending_delay;
194
195                         DEBUG_TRACE (DEBUG::LatencyCompensation,
196                                         string_compose ("New %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
197                                                 name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
198                 }
199
200                 assert(_delay == ((_woff - _roff + rbs) % rbs));
201
202                 c = 0;
203                 for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) {
204                         Sample * const data = i->data();
205                         sampleoffset_t roff = _roff;
206                         sampleoffset_t woff = _woff;
207                         for (pframes_t pos = p0; pos < nsamples; ++pos) {
208                                 buf[ woff * chn + c ] = data[ pos ];
209                                 data[ pos ] = buf[ roff * chn + c ];
210                                 roff = (roff + 1) % rbs;
211                                 woff = (woff + 1) % rbs;
212                         }
213                 }
214                 _roff = (_roff + nsamples) % rbs;
215                 _woff = (_woff + nsamples) % rbs;
216         }
217
218         if (_midi_buf.get()) {
219                 _delay = pending_delay;
220
221                 for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
222                         if (i != bufs.midi_begin()) { break; } // XXX only one buffer for now
223
224                         MidiBuffer* dly = _midi_buf.get();
225                         MidiBuffer& mb (*i);
226                         if (pending_flush) {
227                                 dly->silence(nsamples);
228                         }
229
230                         // If the delay time changes, iterate over all events in the dly-buffer
231                         // and adjust the time in-place. <= 0 becomes 0.
232                         //
233                         // iterate over all events in dly-buffer and subtract one cycle
234                         // (nsamples) from the timestamp, bringing them closer to de-queue.
235                         for (MidiBuffer::iterator m = dly->begin(); m != dly->end(); ++m) {
236                                 MidiBuffer::TimeType *t = m.timeptr();
237                                 if (*t > nsamples + delay_diff) {
238                                         *t -= nsamples + delay_diff;
239                                 } else {
240                                         *t = 0;
241                                 }
242                         }
243
244                         if (_delay != 0) {
245                                 // delay events in current-buffer, in place.
246                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
247                                         MidiBuffer::TimeType *t = m.timeptr();
248                                         *t += _delay;
249                                 }
250                         }
251
252                         // move events from dly-buffer into current-buffer until nsamples
253                         // and remove them from the dly-buffer
254                         for (MidiBuffer::iterator m = dly->begin(); m != dly->end();) {
255                                 const Evoral::Event<MidiBuffer::TimeType> ev (*m, false);
256                                 if (ev.time() >= nsamples) {
257                                         break;
258                                 }
259                                 mb.insert_event(ev);
260                                 m = dly->erase(m);
261                         }
262
263                         /* For now, this is only relevant if there is there's a positive delay.
264                          * In the future this could also be used to delay 'too early' events
265                          * (ie '_global_port_buffer_offset + _port_buffer_offset' - midi_port.cc)
266                          */
267                         if (_delay != 0) {
268                                 // move events after nsamples from current-buffer into dly-buffer
269                                 // and trim current-buffer after nsamples
270                                 for (MidiBuffer::iterator m = mb.begin(); m != mb.end();) {
271                                         const Evoral::Event<MidiBuffer::TimeType> ev (*m, false);
272                                         if (ev.time() < nsamples) {
273                                                 ++m;
274                                                 continue;
275                                         }
276                                         dly->insert_event(ev);
277                                         m = mb.erase(m);
278                                 }
279                         }
280                 }
281         }
282
283         _delay = pending_delay;
284 }
285
286 bool
287 DelayLine::set_delay(samplecnt_t signal_delay)
288 {
289         if (signal_delay < 0) {
290                 signal_delay = 0;
291                 cerr << "WARNING: latency compensation is not possible.\n";
292         }
293
294         if (signal_delay == _pending_delay) {
295                 DEBUG_TRACE (DEBUG::LatencyCompensation,
296                                 string_compose ("%1 set_delay - no change: %2 samples for %3 channels\n",
297                                         name(), signal_delay, _configured_output.n_audio()));
298                 return false;
299         }
300
301         DEBUG_TRACE (DEBUG::LatencyCompensation,
302                         string_compose ("%1 set_delay to %2 samples for %3 channels\n",
303                                 name(), signal_delay, _configured_output.n_audio()));
304
305         if (signal_delay <= _bsiz) {
306                 _pending_delay = signal_delay;
307                 return true;
308         }
309
310         if (_pending_bsiz) {
311                 if (_pending_bsiz < signal_delay) {
312                         cerr << "LatComp: buffer resize in progress. "<< name() << "pending: "<< _pending_bsiz <<" want: " << signal_delay <<"\n"; // XXX
313                 } else {
314                         _pending_delay = signal_delay;
315                 }
316                 return true;
317         }
318
319         allocate_pending_buffers (signal_delay);
320
321         _pending_delay = signal_delay;
322
323         DEBUG_TRACE (DEBUG::LatencyCompensation,
324                         string_compose ("allocated buffer for %1 of size %2\n",
325                                 name(), signal_delay));
326
327         return true;
328 }
329
330 bool
331 DelayLine::can_support_io_configuration (const ChanCount& in, ChanCount& out)
332 {
333         out = in;
334         return true;
335 }
336
337 void
338 DelayLine::allocate_pending_buffers (samplecnt_t signal_delay)
339 {
340         assert (signal_delay >= 0);
341         const samplecnt_t rbs = signal_delay + 1;
342
343         if (_configured_output.n_audio() > 0 ) {
344                 _pending_buf.reset(new Sample[_configured_output.n_audio() * rbs]);
345                 memset(_pending_buf.get(), 0, _configured_output.n_audio() * rbs * sizeof (Sample));
346                 _pending_bsiz = signal_delay;
347         } else {
348                 _pending_buf.reset();
349                 _pending_bsiz = 0;
350         }
351 }
352
353 bool
354 DelayLine::configure_io (ChanCount in, ChanCount out)
355 {
356         if (out != in) { // always 1:1
357                 return false;
358         }
359
360         if (_configured_output != out) {
361                 // run() won't be called concurrently, so it's
362                 // save for replace existing _pending_buf.
363                 //
364                 // configure_io is either called with process-lock held
365                 // from route's configure_io() or by use_target() from the c'tor.
366                 allocate_pending_buffers (_pending_delay);
367         }
368
369         DEBUG_TRACE (DEBUG::LatencyCompensation,
370                         string_compose ("configure IO: %1 Ain: %2 Aout: %3 Min: %4 Mout: %5\n",
371                                 name(), in.n_audio(), out.n_audio(), in.n_midi(), out.n_midi()));
372
373         // TODO support multiple midi buffers
374         if (in.n_midi() > 0 && !_midi_buf) {
375                 _midi_buf.reset(new MidiBuffer(16384));
376         }
377
378         return Processor::configure_io (in, out);
379 }
380
381 void
382 DelayLine::flush()
383 {
384         _pending_flush = true;
385 }
386
387 XMLNode&
388 DelayLine::state (bool full_state)
389 {
390         XMLNode& node (Processor::state (full_state));
391         node.set_property("type", "delay");
392         return node;
393 }