mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-03 12:19:33 +01:00
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)
This commit is contained in:
parent
5fdd6ca21e
commit
d26ad5573c
3 changed files with 257 additions and 228 deletions
|
|
@ -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<Sample> _buf;
|
||||
boost::shared_array<Sample> _pending_buf;
|
||||
bool _pending_flush;
|
||||
|
||||
typedef std::vector<boost::shared_array<Sample> > AudioDlyBuf;
|
||||
typedef std::vector<boost::shared_array<MidiBuffer> > MidiDlyBuf;
|
||||
|
||||
AudioDlyBuf _buf;
|
||||
boost::shared_ptr<MidiBuffer> _midi_buf;
|
||||
bool _pending_flush;
|
||||
|
||||
#ifndef NDEBUG
|
||||
Glib::Threads::Mutex _set_delay_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/* Audio buffers */
|
||||
if (_buf.size () == bufs.count ().n_audio () && _buf.size () > 0) {
|
||||
|
||||
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);
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 ];
|
||||
}
|
||||
ro = (ro + 1) % (_pending_bsiz + 1);
|
||||
roold = (roold + 1) % (_bsiz + 1);
|
||||
}
|
||||
}
|
||||
_woff = (_woff - delay_diff) % _bsiz;
|
||||
|
||||
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;
|
||||
/* 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 + fade_len) % rbs;
|
||||
_woff = (_woff + fade_len) % 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);
|
||||
|
||||
if (pending_flush) {
|
||||
DEBUG_TRACE (DEBUG::LatencyCompensation,
|
||||
string_compose ("Flush buffer: %1\n", name()));
|
||||
memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample));
|
||||
}
|
||||
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 ();
|
||||
|
||||
// adjust read pointer
|
||||
_roff += _delay - pending_delay;
|
||||
|
||||
if (_roff < 0) {
|
||||
_roff -= rbs * floor(_roff / (float)rbs);
|
||||
}
|
||||
_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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
_roff = (_roff + nsamples) % rbs;
|
||||
_woff = (_woff + nsamples) % rbs;
|
||||
}
|
||||
|
||||
if (_midi_buf.get()) {
|
||||
/* set new delay */
|
||||
_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
|
||||
/* 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;
|
||||
}
|
||||
} else {
|
||||
/* set new delay for MIDI only */
|
||||
_delay = pending_delay;
|
||||
}
|
||||
|
||||
MidiBuffer* dly = _midi_buf.get();
|
||||
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& 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<MidiBuffer::TimeType> 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<MidiBuffer::TimeType> 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<Sample> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue