From 681b65aa0b68c9e7a686de2d6ca0c18c61cb444b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 8 Apr 2016 18:07:45 +0200 Subject: Add a fixed (not de-clicked) multi-buffer audio/midi delayline. A ringbuffer intended to be used for plugin-thru/bypass latency compensation. --- libs/ardour/ardour/fixed_delay.h | 67 ++++++++++++++++++++ libs/ardour/fixed_delay.cc | 132 +++++++++++++++++++++++++++++++++++++++ libs/ardour/wscript | 1 + 3 files changed, 200 insertions(+) create mode 100644 libs/ardour/ardour/fixed_delay.h create mode 100644 libs/ardour/fixed_delay.cc (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/fixed_delay.h b/libs/ardour/ardour/fixed_delay.h new file mode 100644 index 0000000000..c98b40c315 --- /dev/null +++ b/libs/ardour/ardour/fixed_delay.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Robin Gareus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ardour_fixed_delay_h__ +#define __ardour_fixed_delay_h__ + +#include +#include "ardour/buffer.h" + +namespace ARDOUR { + +class ChanCount; + +class LIBARDOUR_API FixedDelay +{ +public: + FixedDelay (); + ~FixedDelay (); + + void configure (const ChanCount& count, framecnt_t); + void set (const ChanCount& count, framecnt_t); + + void delay (ARDOUR::DataType, uint32_t, Buffer&, const Buffer&, pframes_t, framecnt_t dst_offset = 0, framecnt_t src_offset = 0); + void flush() { _pending_flush = true; } + +private: + framecnt_t _max_delay; + framecnt_t _delay; + bool _pending_flush; + ChanCount _count; + + struct DelayBuffer { + public: + DelayBuffer () : buf (0), pos (0) {} + DelayBuffer (DataType dt, size_t capacity) + : buf (Buffer::create (dt, capacity)), pos (0) {} + ~DelayBuffer () { delete buf; } + Buffer * buf; + framepos_t pos; + }; + + typedef std::vector BufferVec; + // Vector of vectors, indexed by DataType + std::vector _buffers; + + void ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capacity); + void clear (); +}; + +} // namespace ARDOUR + +#endif // __ardour_fixed_delay_h__ diff --git a/libs/ardour/fixed_delay.cc b/libs/ardour/fixed_delay.cc new file mode 100644 index 0000000000..d59619cdf9 --- /dev/null +++ b/libs/ardour/fixed_delay.cc @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Robin Gareus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ardour/audio_buffer.h" +#include "ardour/buffer_set.h" +#include "ardour/fixed_delay.h" +#include "ardour/midi_buffer.h" + +using namespace ARDOUR; + +FixedDelay::FixedDelay () + : _max_delay (0) + , _delay (0) + , _pending_flush (false) +{ + for (size_t i = 0; i < DataType::num_types; ++i) { + _buffers.push_back (BufferVec ()); + } + _count.reset (); +} + +FixedDelay::~FixedDelay () +{ + clear (); +} + +void +FixedDelay::configure (const ChanCount& count, framecnt_t max_delay) +{ + if (max_delay <= _max_delay || count <= _count) { + return; + } + _max_delay = std::max (_max_delay, max_delay); + for (DataType::iterator i = DataType::begin (); i != DataType::end (); ++i) { + ensure_buffers (*i, count.get (*i), _max_delay + 1); + } +} + +void +FixedDelay::set (const ChanCount& count, framecnt_t delay) +{ + configure (count, delay); + _delay = delay; +} + +void +FixedDelay::ensure_buffers (DataType type, size_t num_buffers, size_t buffer_capacity) +{ + assert (type != DataType::NIL); + assert (type < _buffers.size ()); + if (num_buffers == 0) { + return; + } + BufferVec& bufs = _buffers[type]; + if (bufs.size () < num_buffers || (bufs.size () > 0 && bufs[0]->buf->capacity () < buffer_capacity)) { + for (BufferVec::iterator i = bufs.begin (); i != bufs.end (); ++i) { + delete (*i); + } + bufs.clear (); + for (size_t i = 0; i < num_buffers; ++i) { + bufs.push_back (new DelayBuffer (type, buffer_capacity)); + } + _count.set (type, num_buffers); + } +} + +void +FixedDelay::clear () +{ + for (std::vector::iterator i = _buffers.begin (); i != _buffers.end (); ++i) { + for (BufferVec::iterator j = (*i).begin (); j != (*i).end (); ++j) { + delete *j; + } + (*i).clear (); + } + _buffers.clear (); + _count.reset (); +} + +void +FixedDelay::delay ( + ARDOUR::DataType dt, uint32_t id, + Buffer& out, const Buffer& in, + pframes_t n_frames, + framecnt_t dst_offset, framecnt_t src_offset) +{ + if (_delay == 0) { + out.read_from (in, n_frames, dst_offset, src_offset); + return; + } + + assert (dt < _buffers.size ()); + assert (id < _buffers[dt].size ()); + DelayBuffer *db = _buffers[dt][id]; + + if (db->pos + n_frames > _max_delay) { + uint32_t w0 = _max_delay - db->pos; + uint32_t w1 = db->pos + n_frames - _max_delay; + db->buf->read_from (in, w0, db->pos, src_offset); + db->buf->read_from (in, w1, 0, src_offset + w0); + } else { + db->buf->read_from (in, n_frames, db->pos, src_offset); + } + + uint32_t rp = (db->pos + _max_delay - _delay) % _max_delay; + + if (rp + n_frames > _max_delay) { + uint32_t r0 = _max_delay - rp; + uint32_t r1 = rp + n_frames - _max_delay; + out.read_from (*db->buf, r0, dst_offset, rp); + out.read_from (*db->buf, r1, dst_offset + r0, 0); + } else { + out.read_from (*db->buf, n_frames, dst_offset, rp); + } + + db->pos = (db->pos + n_frames) % _max_delay; +} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index f38e62d3cc..80ec8e8e93 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -92,6 +92,7 @@ libardour_sources = [ 'filesystem_paths.cc', 'filter.cc', 'find_session.cc', + 'fixed_delay.cc', 'gain_control.cc', 'globals.cc', 'graph.cc', -- cgit v1.2.3