From d26ad5573ca47a851575d9fe3e11e9d5c4b7dd67 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 4 Nov 2017 05:20:14 +0100 Subject: Optimize Delaylines: block-process (not one sample at a time) This also tweaks fade behavior when the latency changes to prefer a x-fade when possible. This new variant does not support concurrent re-allocation and execution. Hence the auto-connect thread needs to take a lock before updating latencies (actually there's no need for an explicit update with built-in backends, so this case remains to be updated further) --- libs/ardour/ardour/delayline.h | 37 ++-- libs/ardour/delayline.cc | 445 +++++++++++++++++++++-------------------- libs/ardour/session.cc | 1 + 3 files changed, 256 insertions(+), 227 deletions(-) (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/delayline.h b/libs/ardour/ardour/delayline.h index c8b1fe12aa..58e2aaca96 100644 --- a/libs/ardour/ardour/delayline.h +++ b/libs/ardour/ardour/delayline.h @@ -40,19 +40,19 @@ public: DelayLine (Session& s, const std::string& name); ~DelayLine (); - bool display_to_user() const { return false; } + bool display_to_user () const { return false; } void run (BufferSet&, samplepos_t, samplepos_t, double, pframes_t, bool); - bool set_delay(samplecnt_t signal_delay); - samplecnt_t delay() { return _pending_delay; } + bool set_delay (samplecnt_t signal_delay); + samplecnt_t delay () { return _pending_delay; } bool configure_io (ChanCount in, ChanCount out); bool can_support_io_configuration (const ChanCount& in, ChanCount& out); - void flush(); - void realtime_handle_transport_stopped () { flush(); } - void realtime_locate () { flush(); } - void monitoring_changed() { flush(); } + void flush (); + void realtime_handle_transport_stopped () { flush (); } + void realtime_locate () { flush (); } + void monitoring_changed () { flush (); } bool set_name (const std::string& str); @@ -60,16 +60,27 @@ protected: XMLNode& state (); private: - void allocate_pending_buffers (samplecnt_t); + void allocate_pending_buffers (samplecnt_t, ChanCount const&); + + void write_to_rb (Sample* rb, Sample* src, samplecnt_t); // honor _woff, _bsiz. + void read_from_rb (Sample* rb, Sample* dst, samplecnt_t); // honor _roff, _bsiz friend class IO; - samplecnt_t _delay, _pending_delay; - samplecnt_t _bsiz, _pending_bsiz; + + samplecnt_t _bsiz; + samplecnt_t _delay, _pending_delay; sampleoffset_t _roff, _woff; - boost::shared_array _buf; - boost::shared_array _pending_buf; + bool _pending_flush; + + typedef std::vector > AudioDlyBuf; + typedef std::vector > MidiDlyBuf; + + AudioDlyBuf _buf; boost::shared_ptr _midi_buf; - bool _pending_flush; + +#ifndef NDEBUG + Glib::Threads::Mutex _set_delay_mutex; +#endif }; } // namespace ARDOUR diff --git a/libs/ardour/delayline.cc b/libs/ardour/delayline.cc index 19ef460fd0..d0a691c5ff 100644 --- a/libs/ardour/delayline.cc +++ b/libs/ardour/delayline.cc @@ -22,11 +22,12 @@ #include "pbd/compose.h" -#include "ardour/debug.h" #include "ardour/audio_buffer.h" -#include "ardour/midi_buffer.h" #include "ardour/buffer_set.h" +#include "ardour/debug.h" #include "ardour/delayline.h" +#include "ardour/midi_buffer.h" +#include "ardour/runtime_functions.h" using namespace std; using namespace PBD; @@ -34,13 +35,12 @@ using namespace ARDOUR; DelayLine::DelayLine (Session& s, const std::string& name) : Processor (s, string_compose ("latcomp-%1-%2", name, this)) - , _delay(0) - , _pending_delay(0) - , _bsiz(0) - , _pending_bsiz(0) - , _roff(0) - , _woff(0) - , _pending_flush(false) + , _bsiz (0) + , _delay (0) + , _pending_delay (0) + , _roff (0) + , _woff (0) + , _pending_flush (false) { } @@ -54,188 +54,158 @@ DelayLine::set_name (const string& name) return Processor::set_name (string_compose ("latcomp-%1-%2", name, this)); } -#define FADE_LEN (16) +#define FADE_LEN (128) + void -DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /* end_sample */, double /* speed */, pframes_t nsamples, bool) +DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /* end_sample */, double /* speed */, pframes_t n_samples, bool) { - const uint32_t chn = _configured_output.n_audio(); - pframes_t p0 = 0; - uint32_t c; +#ifndef NDEBUG + Glib::Threads::Mutex::Lock lm (_set_delay_mutex, Glib::Threads::TRY_LOCK); + assert (lm.locked ()); +#endif const sampleoffset_t pending_delay = _pending_delay; - const sampleoffset_t delay_diff = _delay - pending_delay; + sampleoffset_t delay_diff = _delay - pending_delay; const bool pending_flush = _pending_flush; _pending_flush = false; - /* run() and set_delay() may be called in parallel by - * different threads. - * if a larger buffer is needed, it is allocated in - * set_delay(), here it is just swap'ed in place - */ - if (_pending_bsiz) { - assert(_pending_bsiz >= _bsiz); - - const size_t boff = _pending_bsiz - _bsiz; - if (_bsiz > 0) { - /* write offset is retained. copy existing data to new buffer */ - sampleoffset_t wl = _bsiz - _woff; - memcpy(_pending_buf.get(), _buf.get(), sizeof(Sample) * _woff * chn); - memcpy(_pending_buf.get() + (_pending_bsiz - wl) * chn, _buf.get() + _woff * chn, sizeof(Sample) * wl * chn); - - /* new buffer is all zero by default, fade into the existing data copied above */ - sampleoffset_t wo = _pending_bsiz - wl; - for (pframes_t pos = 0; pos < FADE_LEN; ++pos) { - const gain_t gain = (gain_t)pos / (gain_t)FADE_LEN; - for (c = 0; c < _configured_output.n_audio(); ++c) { - _pending_buf.get()[ wo * chn + c ] *= gain; - wo = (wo + 1) % (_pending_bsiz + 1); + /* Audio buffers */ + if (_buf.size () == bufs.count ().n_audio () && _buf.size () > 0) { + + /* handle delay-changes first */ + if (delay_diff < 0) { + /* delay increases: fade out, insert silence, fade-in */ + const samplecnt_t fade_in_len = std::min (n_samples, (pframes_t)FADE_LEN); + samplecnt_t fade_out_len; + + if (_delay < FADE_LEN) { + /* if old delay was 0 or smaller than new-delay, add some data to fade. + * Add at most (FADE_LEN - _delay) samples, but no more than -delay_diff + */ + samplecnt_t add = std::min ((samplecnt_t)FADE_LEN - _delay, (samplecnt_t) -delay_diff); + fade_out_len = std::min (_delay + add, (samplecnt_t)FADE_LEN); + + if (add > 0) { + AudioDlyBuf::iterator bi = _buf.begin (); + for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i, ++bi) { + Sample* rb = (*bi).get (); + write_to_rb (rb, i->data (), add); + } + _woff = (_woff + add) % _bsiz; + delay_diff += add; } + } else { + fade_out_len = FADE_LEN; } - /* read-pointer will be moved and may up anywhere.. - * copy current data for smooth fade-out below - */ - sampleoffset_t roold = _roff; - sampleoffset_t ro = _roff; - if (ro > _woff) { - ro += boff; - } - ro += delay_diff; - if (ro < 0) { - ro -= (_pending_bsiz + 1) * floor(ro / (float)(_pending_bsiz + 1)); - } - ro = ro % (_pending_bsiz + 1); - for (pframes_t pos = 0; pos < FADE_LEN; ++pos) { - for (c = 0; c < _configured_output.n_audio(); ++c) { - _pending_buf.get()[ ro * chn + c ] = _buf.get()[ roold * chn + c ]; + /* fade-out, end of previously written data */ + for (AudioDlyBuf::iterator i = _buf.begin(); i != _buf.end (); ++i) { + Sample* rb = (*i).get (); + for (uint32_t s = 0; s < fade_out_len; ++s) { + sampleoffset_t off = (_woff + _bsiz - s) % _bsiz; + rb[off] *= s / (float) fade_out_len; } - ro = (ro + 1) % (_pending_bsiz + 1); - roold = (roold + 1) % (_bsiz + 1); - } - } - - if (_roff > _woff) { - _roff += boff; - } - - // use shared_array::swap() ?? - _buf = _pending_buf; - _bsiz = _pending_bsiz; - _pending_bsiz = 0; - _pending_buf.reset(); - } - - /* there may be no buffer when delay == 0. - * we also need to check audio-channels in case all audio-channels - * were removed in which case no new buffer was allocated. */ - Sample *buf = _buf.get(); - if (buf && _configured_output.n_audio() > 0) { - - assert (_bsiz >= pending_delay); - const samplecnt_t rbs = _bsiz + 1; - - if (pending_delay != _delay || pending_flush) { - const pframes_t fade_len = (nsamples >= FADE_LEN) ? FADE_LEN : nsamples / 2; - - DEBUG_TRACE (DEBUG::LatencyCompensation, - string_compose ("Old %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n", - name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff)); - - // fade out at old position - c = 0; - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) { - Sample * const data = i->data(); - sampleoffset_t roff = _roff; - sampleoffset_t woff = _woff; - for (pframes_t pos = 0; pos < fade_len; ++pos) { - const gain_t gain = (gain_t)(fade_len - pos) / (gain_t)fade_len; - buf[ woff * chn + c ] = data[ pos ]; - data[ pos ] = buf[ roff * chn + c ] * gain; - roff = (roff + 1) % rbs; - woff = (woff + 1) % rbs; + /* clear data in rb */ + // TODO optimize this using memset + for (uint32_t s = 0; s < -delay_diff; ++s) { + sampleoffset_t off = (_woff + _bsiz + s) % _bsiz; + rb[off] = 0.f; } } - _roff = (_roff + fade_len) % rbs; - _woff = (_woff + fade_len) % rbs; - if (pending_flush) { - DEBUG_TRACE (DEBUG::LatencyCompensation, - string_compose ("Flush buffer: %1\n", name())); - memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample)); - } - - // adjust read pointer - _roff += _delay - pending_delay; + _woff = (_woff - delay_diff) % _bsiz; - if (_roff < 0) { - _roff -= rbs * floor(_roff / (float)rbs); + /* fade-in, directly apply to input buffer */ + for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i) { + Sample* src = i->data (); + for (uint32_t s = 0; s < fade_in_len; ++s) { + src[s] *= s / (float) fade_in_len; + } } - _roff = _roff % rbs; - - // fade in at new position - c = 0; - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) { - Sample * const data = i->data(); - sampleoffset_t roff = _roff; - sampleoffset_t woff = _woff; - for (pframes_t pos = fade_len; pos < 2 * fade_len; ++pos) { - const gain_t gain = (gain_t)(pos - fade_len) / (gain_t)fade_len; - buf[ woff * chn + c ] = data[ pos ]; - data[ pos ] = buf[ roff * chn + c ] * gain; - roff = (roff + 1) % rbs; - woff = (woff + 1) % rbs; + } else if (delay_diff > 0) { + /* delay decreases: cross-fade, if possible */ + const samplecnt_t fade_out_len = std::min (_delay, (samplecnt_t)FADE_LEN); + const samplecnt_t fade_in_len = std::min (n_samples, (pframes_t)FADE_LEN); + const samplecnt_t xfade_len = std::min (fade_out_len, fade_in_len); + + AudioDlyBuf::iterator bi = _buf.begin (); + for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i, ++bi) { + Sample* rb = (*bi).get (); + Sample* src = i->data (); + + // TODO consider handling fade_out & fade_in separately + // if fade_out_len < fade_in_len. + for (uint32_t s = 0; s < xfade_len; ++s) { + sampleoffset_t off = (_roff + s) % _bsiz; + const gain_t g = s / (float) xfade_len; + src[s] *= g; + src[s] += (1.f - g) * rb[off]; } } - _roff = (_roff + fade_len) % rbs; - _woff = (_woff + fade_len) % rbs; - p0 = 2 * fade_len; - - _delay = pending_delay; - DEBUG_TRACE (DEBUG::LatencyCompensation, - string_compose ("New %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n", - name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff)); +#ifndef NDEBUG + sampleoffset_t check = (_roff + delay_diff) % _bsiz; +#endif + _roff = (_woff + _bsiz - pending_delay) % _bsiz; +#ifndef NDEBUG + assert (_roff == check); +#endif } - assert(_delay == ((_woff - _roff + rbs) % rbs)); - - c = 0; - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end() && c <= chn; ++i, ++c) { - Sample * const data = i->data(); - sampleoffset_t roff = _roff; - sampleoffset_t woff = _woff; - for (pframes_t pos = p0; pos < nsamples; ++pos) { - buf[ woff * chn + c ] = data[ pos ]; - data[ pos ] = buf[ roff * chn + c ]; - roff = (roff + 1) % rbs; - woff = (woff + 1) % rbs; + /* set new delay */ + _delay = pending_delay; + + /* delay audio buffers */ + assert (_delay == ((_woff - _roff + _bsiz) % _bsiz)); + AudioDlyBuf::iterator bi = _buf.begin (); + if (_delay == 0) { + /* do nothing */ + } else if (n_samples <= _delay) { + /* write all samples to rb, read all from rb */ + for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i, ++bi) { + Sample* rb = (*bi).get (); + write_to_rb (rb, i->data (), n_samples); + read_from_rb (rb, i->data (), n_samples); + } + _roff = (_roff + n_samples) % _bsiz; + _woff = (_woff + n_samples) % _bsiz; + } else { + /* only write _delay samples to ringbuffer, memmove buffer */ + samplecnt_t tail = n_samples - _delay; + for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i, ++bi) { + Sample* rb = (*bi).get (); + Sample* src = i->data (); + write_to_rb (rb, &src[tail], _delay); + memmove (&src[_delay], src, tail * sizeof(Sample)); + read_from_rb (rb, src, _delay); } + _roff = (_roff + _delay) % _bsiz; + _woff = (_woff + _delay) % _bsiz; } - _roff = (_roff + nsamples) % rbs; - _woff = (_woff + nsamples) % rbs; - } - - if (_midi_buf.get()) { + } else { + /* set new delay for MIDI only */ _delay = pending_delay; + } - for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) { - if (i != bufs.midi_begin()) { break; } // XXX only one buffer for now + if (_midi_buf.get ()) { + for (BufferSet::midi_iterator i = bufs.midi_begin (); i != bufs.midi_end (); ++i) { + if (i != bufs.midi_begin ()) { break; } // XXX only one buffer for now - MidiBuffer* dly = _midi_buf.get(); + MidiBuffer* dly = _midi_buf.get (); MidiBuffer& mb (*i); if (pending_flush) { - dly->silence(nsamples); + dly->silence (n_samples); } // If the delay time changes, iterate over all events in the dly-buffer // and adjust the time in-place. <= 0 becomes 0. // // iterate over all events in dly-buffer and subtract one cycle - // (nsamples) from the timestamp, bringing them closer to de-queue. - for (MidiBuffer::iterator m = dly->begin(); m != dly->end(); ++m) { - MidiBuffer::TimeType *t = m.timeptr(); - if (*t > nsamples + delay_diff) { - *t -= nsamples + delay_diff; + // (n_samples) from the timestamp, bringing them closer to de-queue. + for (MidiBuffer::iterator m = dly->begin (); m != dly->end (); ++m) { + MidiBuffer::TimeType *t = m.timeptr (); + if (*t > n_samples + delay_diff) { + *t -= n_samples + delay_diff; } else { *t = 0; } @@ -243,21 +213,21 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /* if (_delay != 0) { // delay events in current-buffer, in place. - for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) { - MidiBuffer::TimeType *t = m.timeptr(); + for (MidiBuffer::iterator m = mb.begin (); m != mb.end (); ++m) { + MidiBuffer::TimeType *t = m.timeptr (); *t += _delay; } } - // move events from dly-buffer into current-buffer until nsamples + // move events from dly-buffer into current-buffer until n_samples // and remove them from the dly-buffer - for (MidiBuffer::iterator m = dly->begin(); m != dly->end();) { + for (MidiBuffer::iterator m = dly->begin (); m != dly->end ();) { const Evoral::Event ev (*m, false); - if (ev.time() >= nsamples) { + if (ev.time () >= n_samples) { break; } - mb.insert_event(ev); - m = dly->erase(m); + mb.insert_event (ev); + m = dly->erase (m); } /* For now, this is only relevant if there is there's a positive delay. @@ -265,27 +235,30 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /* * (ie '_global_port_buffer_offset + _port_buffer_offset' - midi_port.cc) */ if (_delay != 0) { - // move events after nsamples from current-buffer into dly-buffer - // and trim current-buffer after nsamples - for (MidiBuffer::iterator m = mb.begin(); m != mb.end();) { + // move events after n_samples from current-buffer into dly-buffer + // and trim current-buffer after n_samples + for (MidiBuffer::iterator m = mb.begin (); m != mb.end ();) { const Evoral::Event ev (*m, false); - if (ev.time() < nsamples) { + if (ev.time () < n_samples) { ++m; continue; } - dly->insert_event(ev); - m = mb.erase(m); + dly->insert_event (ev); + m = mb.erase (m); } } } } - - _delay = pending_delay; } bool -DelayLine::set_delay(samplecnt_t signal_delay) +DelayLine::set_delay (samplecnt_t signal_delay) { +#ifndef NDEBUG + Glib::Threads::Mutex::Lock lm (_set_delay_mutex, Glib::Threads::TRY_LOCK); + assert (lm.locked ()); +#endif + if (signal_delay < 0) { signal_delay = 0; cerr << "WARNING: latency compensation is not possible.\n"; @@ -294,36 +267,19 @@ DelayLine::set_delay(samplecnt_t signal_delay) if (signal_delay == _pending_delay) { DEBUG_TRACE (DEBUG::LatencyCompensation, string_compose ("%1 set_delay - no change: %2 samples for %3 channels\n", - name(), signal_delay, _configured_output.n_audio())); + name (), signal_delay, _configured_output.n_audio ())); return false; } DEBUG_TRACE (DEBUG::LatencyCompensation, string_compose ("%1 set_delay to %2 samples for %3 channels\n", - name(), signal_delay, _configured_output.n_audio())); + name (), signal_delay, _configured_output.n_audio ())); - if (signal_delay <= _bsiz) { - _pending_delay = signal_delay; - return true; + if (signal_delay + 8192 + 1 > _bsiz) { + allocate_pending_buffers (signal_delay, _configured_output); } - if (_pending_bsiz) { - if (_pending_bsiz < signal_delay) { - cerr << "LatComp: buffer resize in progress. "<< name() << "pending: "<< _pending_bsiz <<" want: " << signal_delay <<"\n"; // XXX - } else { - _pending_delay = signal_delay; - } - return true; - } - - allocate_pending_buffers (signal_delay); - _pending_delay = signal_delay; - - DEBUG_TRACE (DEBUG::LatencyCompensation, - string_compose ("allocated buffer for %1 of size %2\n", - name(), signal_delay)); - return true; } @@ -335,51 +291,82 @@ DelayLine::can_support_io_configuration (const ChanCount& in, ChanCount& out) } void -DelayLine::allocate_pending_buffers (samplecnt_t signal_delay) +DelayLine::allocate_pending_buffers (samplecnt_t signal_delay, ChanCount const& cc) { assert (signal_delay >= 0); - const samplecnt_t rbs = signal_delay + 1; + samplecnt_t rbs = signal_delay + 8192 + 1; + rbs = std::max (_bsiz, rbs); - if (_configured_output.n_audio() > 0 ) { - _pending_buf.reset(new Sample[_configured_output.n_audio() * rbs]); - memset(_pending_buf.get(), 0, _configured_output.n_audio() * rbs * sizeof (Sample)); - _pending_bsiz = signal_delay; - } else { - _pending_buf.reset(); - _pending_bsiz = 0; + if (cc.n_audio () == _buf.size () && _bsiz == rbs) { + return; + } + + _buf.clear (); + if (cc.n_audio () == 0) { + return; + } + + AudioDlyBuf pending_buf; + for (uint32_t i = 0; i < cc.n_audio (); ++i) { + boost::shared_array b (new Sample[rbs]); + pending_buf.push_back (b); + memset (b.get (), 0, rbs * sizeof (Sample)); + } + + AudioDlyBuf::iterator bo = _buf.begin (); + AudioDlyBuf::iterator bn = pending_buf.begin (); + + for (; bo != _buf.end () && bn != pending_buf.end(); ++bo, ++bn) { + Sample* rbo = (*bo).get (); + Sample* rbn = (*bn).get (); + if (_roff < _woff) { + /* copy data between _roff .. _woff to new buffer */ + copy_vector (&rbn[_roff], &rbo[_roff], _woff - _roff); + } else { + /* copy data between _roff .. old_size to end of new buffer, increment _roff + * copy data from 0.._woff to beginning of new buffer + */ + sampleoffset_t offset = rbs - _bsiz; + copy_vector (&rbn[_roff + offset], &rbo[_roff], _bsiz - _roff); + copy_vector (rbn, rbo, _woff); + _roff += offset; + assert (_roff < rbs); + } } + _bsiz = rbs; + _buf.swap (pending_buf); } bool DelayLine::configure_io (ChanCount in, ChanCount out) { +#ifndef NDEBUG + Glib::Threads::Mutex::Lock lm (_set_delay_mutex, Glib::Threads::TRY_LOCK); + assert (lm.locked ()); +#endif + if (out != in) { // always 1:1 return false; } if (_configured_output != out) { - // run() won't be called concurrently, so it's - // save for replace existing _pending_buf. - // - // configure_io is either called with process-lock held - // from route's configure_io() or by use_target() from the c'tor. - allocate_pending_buffers (_pending_delay); + allocate_pending_buffers (_pending_delay, out); } DEBUG_TRACE (DEBUG::LatencyCompensation, string_compose ("configure IO: %1 Ain: %2 Aout: %3 Min: %4 Mout: %5\n", - name(), in.n_audio(), out.n_audio(), in.n_midi(), out.n_midi())); + name (), in.n_audio (), out.n_audio (), in.n_midi (), out.n_midi ())); // TODO support multiple midi buffers - if (in.n_midi() > 0 && !_midi_buf) { - _midi_buf.reset(new MidiBuffer(16384)); + if (in.n_midi () > 0 && !_midi_buf) { + _midi_buf.reset (new MidiBuffer (16384)); } return Processor::configure_io (in, out); } void -DelayLine::flush() +DelayLine::flush () { _pending_flush = true; } @@ -388,6 +375,36 @@ XMLNode& DelayLine::state () { XMLNode& node (Processor::state ()); - node.set_property("type", "delay"); + node.set_property ("type", "delay"); return node; } + +void +DelayLine::write_to_rb (Sample* rb, Sample* src, samplecnt_t n_samples) +{ + assert (n_samples < _bsiz); + if (_woff + n_samples < _bsiz) { + copy_vector (&rb[_woff], src, n_samples); + } else { + const samplecnt_t s0 = _bsiz - _woff; + const samplecnt_t s1 = n_samples - s0; + + copy_vector (&rb[_woff], src, s0); + copy_vector (rb, &src[s0], s1); + } +} + +void +DelayLine::read_from_rb (Sample* rb, Sample* dst, samplecnt_t n_samples) +{ + assert (n_samples < _bsiz); + if (_roff + n_samples < _bsiz) { + copy_vector (dst, &rb[_roff], n_samples); + } else { + const samplecnt_t s0 = _bsiz - _roff; + const samplecnt_t s1 = n_samples - s0; + + copy_vector (dst, &rb[_roff], s0); + copy_vector (&dst[s0], rb, s1); + } +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ef08eb0bf2..dabd4bc187 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -7353,6 +7353,7 @@ Session::auto_connect_thread_run () * modifies the capture-offset, which can be a problem. */ while (g_atomic_int_and (&_latency_recompute_pending, 0)) { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); update_latency_compensation (); } } -- cgit v1.2.3