ardour
delayline.cc
Go to the documentation of this file.
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 ("latency-compensation-%1", name))
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 
48 {
49 }
50 
51 #define FADE_LEN (16)
52 void
53 DelayLine::run (BufferSet& bufs, framepos_t /* start_frame */, framepos_t /* end_frame */, pframes_t nsamples, bool)
54 {
55  const uint32_t chn = _configured_output.n_audio();
56  pframes_t p0 = 0;
57  uint32_t c;
58 
59  const frameoffset_t pending_delay = _pending_delay;
60  const frameoffset_t delay_diff = _delay - pending_delay;
61  const bool pending_flush = _pending_flush;
62  _pending_flush = false;
63 
64  /* run() and set_delay() may be called in parallel by
65  * different threads.
66  * if a larger buffer is needed, it is allocated in
67  * set_delay(), here it is just swap'ed in place
68  */
69  if (_pending_bsiz) {
70  assert(_pending_bsiz >= _bsiz);
71 
72  const size_t boff = _pending_bsiz - _bsiz;
73  if (_bsiz > 0) {
74  /* write offset is retained. copy existing data to new buffer */
75  frameoffset_t wl = _bsiz - _woff;
76  memcpy(_pending_buf.get(), _buf.get(), sizeof(Sample) * _woff * chn);
77  memcpy(_pending_buf.get() + (_pending_bsiz - wl) * chn, _buf.get() + _woff * chn, sizeof(Sample) * wl * chn);
78 
79  /* new buffer is all zero by default, fade into the existing data copied above */
80  frameoffset_t wo = _pending_bsiz - wl;
81  for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
82  const gain_t gain = (gain_t)pos / (gain_t)FADE_LEN;
83  for (c = 0; c < _configured_input.n_audio(); ++c) {
84  _pending_buf.get()[ wo * chn + c ] *= gain;
85  wo = (wo + 1) % (_pending_bsiz + 1);
86  }
87  }
88 
89  /* read-pointer will be moved and may up anywhere..
90  * copy current data for smooth fade-out below
91  */
92  frameoffset_t roold = _roff;
93  frameoffset_t ro = _roff;
94  if (ro > _woff) {
95  ro += boff;
96  }
97  ro += delay_diff;
98  if (ro < 0) {
99  ro -= (_pending_bsiz +1) * floor(ro / (float)(_pending_bsiz +1));
100  }
101  ro = ro % (_pending_bsiz + 1);
102  for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
103  for (c = 0; c < _configured_input.n_audio(); ++c) {
104  _pending_buf.get()[ ro * chn + c ] = _buf.get()[ roold * chn + c ];
105  ro = (ro + 1) % (_pending_bsiz + 1);
106  roold = (roold + 1) % (_bsiz + 1);
107  }
108  }
109  }
110 
111  if (_roff > _woff) {
112  _roff += boff;
113  }
114 
115  _buf = _pending_buf;
116  _bsiz = _pending_bsiz;
117  _pending_bsiz = 0;
119  }
120 
121  /* there may be no buffer when delay == 0.
122  * we also need to check audio-channels in case all audio-channels
123  * were removed in which case no new buffer was allocated. */
124  Sample *buf = _buf.get();
125  if (buf && _configured_output.n_audio() > 0) {
126 
127  assert (_bsiz >= pending_delay);
128  const framecnt_t rbs = _bsiz + 1;
129 
130  if (pending_delay != _delay || pending_flush) {
131  const pframes_t fade_len = (nsamples >= FADE_LEN) ? FADE_LEN : nsamples / 2;
132 
134  string_compose ("Old %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
135  name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
136 
137  // fade out at old position
138  c = 0;
139  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
140  Sample * const data = i->data();
141  for (pframes_t pos = 0; pos < fade_len; ++pos) {
142  const gain_t gain = (gain_t)(fade_len - pos) / (gain_t)fade_len;
143  buf[ _woff * chn + c ] = data[ pos ];
144  data[ pos ] = buf[ _roff * chn + c ] * gain;
145  _roff = (_roff + 1) % rbs;
146  _woff = (_woff + 1) % rbs;
147  }
148  }
149 
150  if (pending_flush) {
152  string_compose ("Flush buffer: %1\n", name()));
153  memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample));
154  }
155 
156  // adjust read pointer
157  _roff += _delay - pending_delay;
158 
159  if (_roff < 0) {
160  _roff -= rbs * floor(_roff / (float)rbs);
161  }
162  _roff = _roff % rbs;
163 
164  // fade in at new position
165  c = 0;
166  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
167  Sample * const data = i->data();
168  for (pframes_t pos = fade_len; pos < 2 * fade_len; ++pos) {
169  const gain_t gain = (gain_t)(pos - fade_len) / (gain_t)fade_len;
170  buf[ _woff * chn + c ] = data[ pos ];
171  data[ pos ] = buf[ _roff * chn + c ] * gain;
172  _roff = (_roff + 1) % rbs;
173  _woff = (_woff + 1) % rbs;
174  }
175  }
176  p0 = 2 * fade_len;
177 
178  _delay = pending_delay;
179 
181  string_compose ("New %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
182  name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
183  }
184 
185  assert(_delay == ((_woff - _roff + rbs) % rbs));
186 
187  c = 0;
188  for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
189  Sample * const data = i->data();
190  for (pframes_t pos = p0; pos < nsamples; ++pos) {
191  buf[ _woff * chn + c ] = data[ pos ];
192  data[ pos ] = buf[ _roff * chn + c ];
193  _roff = (_roff + 1) % rbs;
194  _woff = (_woff + 1) % rbs;
195  }
196  }
197  }
198 
199  if (_midi_buf.get()) {
200  _delay = pending_delay;
201 
202  for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
203  if (i != bufs.midi_begin()) { break; } // XXX only one buffer for now
204 
205  MidiBuffer* dly = _midi_buf.get();
206  MidiBuffer& mb (*i);
207  if (pending_flush) {
208  dly->silence(nsamples);
209  }
210 
211  // If the delay time changes, iterate over all events in the dly-buffer
212  // and adjust the time in-place. <= 0 becomes 0.
213  //
214  // iterate over all events in dly-buffer and subtract one cycle
215  // (nsamples) from the timestamp, bringing them closer to de-queue.
216  for (MidiBuffer::iterator m = dly->begin(); m != dly->end(); ++m) {
217  MidiBuffer::TimeType *t = m.timeptr();
218  if (*t > nsamples + delay_diff) {
219  *t -= nsamples + delay_diff;
220  } else {
221  *t = 0;
222  }
223  }
224 
225  if (_delay != 0) {
226  // delay events in current-buffer, in place.
227  for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
228  MidiBuffer::TimeType *t = m.timeptr();
229  *t += _delay;
230  }
231  }
232 
233  // move events from dly-buffer into current-buffer until nsamples
234  // and remove them from the dly-buffer
235  for (MidiBuffer::iterator m = dly->begin(); m != dly->end();) {
236  const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
237  if (ev.time() >= nsamples) {
238  break;
239  }
240  mb.insert_event(ev);
241  m = dly->erase(m);
242  }
243 
244  /* For now, this is only relevant if there is there's a positive delay.
245  * In the future this could also be used to delay 'too early' events
246  * (ie '_global_port_buffer_offset + _port_buffer_offset' - midi_port.cc)
247  */
248  if (_delay != 0) {
249  // move events after nsamples from current-buffer into dly-buffer
250  // and trim current-buffer after nsamples
251  for (MidiBuffer::iterator m = mb.begin(); m != mb.end();) {
252  const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
253  if (ev.time() < nsamples) {
254  ++m;
255  continue;
256  }
257  dly->insert_event(ev);
258  m = mb.erase(m);
259  }
260  }
261  }
262  }
263 
264  _delay = pending_delay;
265 }
266 
267 void
269 {
270  if (signal_delay < 0) {
271  signal_delay = 0;
272  cerr << "WARNING: latency compensation is not possible.\n";
273  }
274 
275  const framecnt_t rbs = signal_delay + 1;
276 
278  string_compose ("%1 set_delay to %2 samples for %3 channels\n",
279  name(), signal_delay, _configured_output.n_audio()));
280 
281  if (signal_delay <= _bsiz) {
282  _pending_delay = signal_delay;
283  return;
284  }
285 
286  if (_pending_bsiz) {
287  if (_pending_bsiz < signal_delay) {
288  cerr << "LatComp: buffer resize in progress. "<< name() << "pending: "<< _pending_bsiz <<" want: " << signal_delay <<"\n"; // XXX
289  } else {
290  _pending_delay = signal_delay;
291  }
292  return;
293  }
294 
295  if (_configured_output.n_audio() > 0 ) {
297  memset(_pending_buf.get(), 0, _configured_output.n_audio() * rbs * sizeof (Sample));
298  _pending_bsiz = signal_delay;
299  } else {
301  _pending_bsiz = 0;
302  }
303 
304  _pending_delay = signal_delay;
305 
307  string_compose ("allocated buffer for %1 of size %2\n",
308  name(), signal_delay));
309 }
310 
311 bool
313 {
314  out = in;
315  return true;
316 }
317 
318 bool
320 {
321  if (out != in) { // always 1:1
322  return false;
323  }
324 
325  // TODO realloc buffers if channel count changes..
326  // TODO support multiple midi buffers
327 
329  string_compose ("configure IO: %1 Ain: %2 Aout: %3 Min: %4 Mout: %5\n",
330  name(), in.n_audio(), out.n_audio(), in.n_midi(), out.n_midi()));
331 
332  if (in.n_midi() > 0 && !_midi_buf) {
333  _midi_buf.reset(new MidiBuffer(16384));
334  }
335 
336  return Processor::configure_io (in, out);
337 }
338 
339 void
341 {
342  _pending_flush = true;
343 }
344 
345 XMLNode&
346 DelayLine::state (bool full_state)
347 {
348  XMLNode& node (Processor::state (full_state));
349  node.add_property("type", "delay");
350  return node;
351 }
framecnt_t _pending_delay
Definition: delayline.h:58
boost::shared_ptr< Sample > _buf
Definition: delayline.h:61
boost::shared_ptr< Sample > _pending_buf
Definition: delayline.h:62
void silence(framecnt_t nframes, framecnt_t offset=0)
Definition: midi_buffer.cc:290
uint32_t pframes_t
Definition: types.h:61
uint32_t n_audio() const
Definition: chan_count.h:63
XMLNode & state(bool full)
Definition: delayline.cc:346
#define FADE_LEN
Definition: delayline.cc:51
Definition: Beats.hpp:239
LIBARDOUR_API PBD::PropertyDescriptor< bool > gain
Definition: route_group.cc:44
float gain_t
Definition: types.h:58
framepos_t TimeType
Definition: midi_buffer.h:38
uint32_t n_midi() const
Definition: chan_count.h:66
boost::shared_ptr< MidiBuffer > _midi_buf
Definition: delayline.h:63
ChanCount _configured_output
Definition: processor.h:132
void set_delay(framecnt_t signal_delay)
Definition: delayline.cc:268
int64_t framecnt_t
Definition: types.h:76
midi_iterator midi_begin()
Definition: buffer_set.h:177
framecnt_t _bsiz
Definition: delayline.h:59
float Sample
Definition: types.h:54
audio_iterator audio_begin()
Definition: buffer_set.h:173
Definition: amp.h:29
iterator erase(const iterator &i)
Definition: midi_buffer.h:133
Time time() const
Definition: Event.hpp:132
virtual bool configure_io(ChanCount in, ChanCount out)
Definition: processor.cc:246
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int64_t framepos_t
Definition: types.h:66
void run(BufferSet &, framepos_t, framepos_t, pframes_t, bool)
Definition: delayline.cc:53
audio_iterator audio_end()
Definition: buffer_set.h:174
int64_t frameoffset_t
Definition: types.h:71
T * get() const
Definition: shared_ptr.hpp:268
framecnt_t _delay
Definition: delayline.h:58
bool can_support_io_configuration(const ChanCount &in, ChanCount &out)
Definition: delayline.cc:312
XMLProperty * add_property(const char *name, const std::string &value)
const char * name
framecnt_t _pending_bsiz
Definition: delayline.h:59
midi_iterator midi_end()
Definition: buffer_set.h:178
Definition: xml++.h:95
std::string name() const
bool configure_io(ChanCount in, ChanCount out)
Definition: delayline.cc:319
Definition: debug.h:30
virtual XMLNode & state(bool full)
Definition: processor.cc:109
LIBARDOUR_API uint64_t LatencyCompensation
Definition: debug.cc:33
frameoffset_t _woff
Definition: delayline.h:60
bool insert_event(const Evoral::MIDIEvent< TimeType > &event)
Definition: midi_buffer.cc:203
iterator begin()
Definition: midi_buffer.h:127
frameoffset_t _roff
Definition: delayline.h:60
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
ChanCount _configured_input
Definition: processor.h:131