From d87002617e459ec84ad177c86f24cdb8d3f8d796 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 30 Mar 2021 01:22:50 +0200 Subject: [PATCH] Implement zero-latency convolver --- libs/ardour/ardour/convolver.h | 10 ++-- libs/ardour/convolver.cc | 98 ++++++++++++++++++++++++++++++---- libs/ardour/luabindings.cc | 6 ++- share/scripts/__convolv.lua | 19 +++++-- 4 files changed, 113 insertions(+), 20 deletions(-) diff --git a/libs/ardour/ardour/convolver.h b/libs/ardour/ardour/convolver.h index 2aca6d6917..7a335f8287 100644 --- a/libs/ardour/ardour/convolver.h +++ b/libs/ardour/ardour/convolver.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Robin Gareus + * Copyright (C) 2018-2021 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 @@ -61,6 +61,7 @@ protected: uint32_t _max_size; uint32_t _offset; bool _configured; + bool _threaded; private: class ImpData : public Readable @@ -164,8 +165,11 @@ public: Convolver (Session&, std::string const&, IRChannelConfig irc = Mono, IRSettings irs = IRSettings ()); - void run_mono (float*, uint32_t); - void run_stereo (float* L, float* R, uint32_t); + void run_mono_buffered (float*, uint32_t); + void run_stereo_buffered (float* L, float* R, uint32_t); + + void run_mono_no_latency (float*, uint32_t); + void run_stereo_no_latency (float* L, float* R, uint32_t); private: std::vector > _readables; diff --git a/libs/ardour/convolver.cc b/libs/ardour/convolver.cc index 4212c8635e..2f94950836 100644 --- a/libs/ardour/convolver.cc +++ b/libs/ardour/convolver.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Robin Gareus + * Copyright (C) 2018-2021 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 @@ -45,6 +45,7 @@ Convolution::Convolution (Session& session, uint32_t n_in, uint32_t n_out) , _max_size (0) , _offset (0) , _configured (false) + , _threaded (false) , _n_inputs (n_in) , _n_outputs (n_out) { @@ -86,20 +87,26 @@ Convolution::restart () _convproc.cleanup (); _convproc.set_options (0); + uint32_t n_part; + + if (_threaded) { + _n_samples = 64; + n_part = Convproc::MAXPART; + } else { + _n_samples = _session.get_block_size (); + uint32_t power_of_two; + for (power_of_two = 1; 1U << power_of_two < _n_samples; ++power_of_two) ; + _n_samples = 1 << power_of_two; + n_part = std::min ((uint32_t)Convproc::MAXPART, _n_samples); + } + _offset = 0; _max_size = 0; - _n_samples = _session.get_block_size (); for (std::vector::const_iterator i = _impdata.begin (); i != _impdata.end (); ++i) { _max_size = std::max (_max_size, (uint32_t)i->readable_length ()); } - uint32_t power_of_two; - for (power_of_two = 1; 1U << power_of_two < _n_samples; ++power_of_two) ; - _n_samples = 1 << power_of_two; - - int n_part = std::min ((uint32_t)Convproc::MAXPART, 4 * _n_samples); - int rv = _convproc.configure ( /*in*/ _n_inputs, /*out*/ _n_outputs, @@ -226,6 +233,8 @@ Convolver::Convolver ( , _irc (irc) , _ir_settings (irs) { + _threaded = true; + std::vector > readables = Readable::load (_session, path); if (readables.empty ()) { @@ -305,7 +314,7 @@ Convolver::Convolver ( } void -Convolver::run_mono (float* buf, uint32_t n_samples) +Convolver::run_mono_buffered (float* buf, uint32_t n_samples) { assert (_convproc.state () == Convproc::ST_PROC); assert (_irc == Mono); @@ -334,7 +343,7 @@ Convolver::run_mono (float* buf, uint32_t n_samples) } void -Convolver::run_stereo (float* left, float* right, uint32_t n_samples) +Convolver::run_stereo_buffered (float* left, float* right, uint32_t n_samples) { assert (_convproc.state () == Convproc::ST_PROC); assert (_irc != Mono); @@ -362,3 +371,72 @@ Convolver::run_stereo (float* left, float* right, uint32_t n_samples) } } } + +void +Convolver::run_mono_no_latency (float* buf, uint32_t n_samples) +{ + assert (_convproc.state () == Convproc::ST_PROC); + assert (_irc == Mono); + + uint32_t done = 0; + uint32_t remain = n_samples; + + while (remain > 0) { + uint32_t ns = std::min (remain, _n_samples - _offset); + + float* const in = _convproc.inpdata (/*channel*/ 0); + float* const out = _convproc.outdata (/*channel*/ 0); + + memcpy (&in[_offset], &buf[done], sizeof (float) * ns); + + if (_offset + ns == _n_samples) { + _convproc.process (); + memcpy (&buf[done], &out[_offset], sizeof (float) * ns); + _offset = 0; + } else { + assert (remain == ns); + _convproc.tailonly (_offset + ns); + memcpy (&buf[done], &out[_offset], sizeof (float) * ns); + _offset += ns; + } + done += ns; + remain -= ns; + } +} + +void +Convolver::run_stereo_no_latency (float* left, float* right, uint32_t n_samples) +{ + assert (_convproc.state () == Convproc::ST_PROC); + assert (_irc != Mono); + + uint32_t done = 0; + uint32_t remain = n_samples; + + float* const outL = _convproc.outdata (0); + float* const outR = _convproc.outdata (1); + + while (remain > 0) { + uint32_t ns = std::min (remain, _n_samples - _offset); + + memcpy (&_convproc.inpdata (0)[_offset], &left[done], sizeof (float) * ns); + if (_irc >= Stereo) { + memcpy (&_convproc.inpdata (1)[_offset], &right[done], sizeof (float) * ns); + } + + if (_offset + ns == _n_samples) { + _convproc.process (); + memcpy (&left[done], &outL[_offset], sizeof (float) * ns); + memcpy (&right[done], &outR[_offset], sizeof (float) * ns); + _offset = 0; + } else { + assert (remain == ns); + _convproc.tailonly (_offset + ns); + memcpy (&left[done], &outL[_offset], sizeof (float) * ns); + memcpy (&right[done], &outR[_offset], sizeof (float) * ns); + _offset += ns; + } + done += ns; + remain -= ns; + } +} diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index e0c4fbb0e5..6c708c4d27 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -2724,8 +2724,10 @@ LuaBindings::common (lua_State* L) .deriveClass ("Convolver") .addConstructor () - .addFunction ("run_mono", &ARDOUR::DSP::Convolver::run_mono) - .addFunction ("run_stereo", &ARDOUR::DSP::Convolver::run_stereo) + .addFunction ("run_mono_buffered", &ARDOUR::DSP::Convolver::run_mono_buffered) + .addFunction ("run_stereo_buffered", &ARDOUR::DSP::Convolver::run_stereo_buffered) + .addFunction ("run_mono_no_latency", &ARDOUR::DSP::Convolver::run_mono_no_latency) + .addFunction ("run_stereo_no_latency", &ARDOUR::DSP::Convolver::run_stereo_no_latency) .endClass () /* DSP enums */ diff --git a/share/scripts/__convolv.lua b/share/scripts/__convolv.lua index b84f0d3db9..ec1885f78e 100644 --- a/share/scripts/__convolv.lua +++ b/share/scripts/__convolv.lua @@ -8,8 +8,9 @@ function dsp_ioconfig () return } end -local conv, mode, ir_file +local conv, mode, ir_file, buffered +buffered = false ir_file = "/tmp/reverbs/St Nicolaes Church.wav" ir_file = "/tmp/reverbs/Large Wide Echo Hall.wav" @@ -33,7 +34,7 @@ function dsp_configure (ins, outs) end function dsp_latency () - if conv then + if conv and buffered then return conv:latency() else return 0 @@ -51,9 +52,17 @@ function dsp_run (ins, outs, n_samples) end end - if #outs == 1 then - conv:run_mono (outs[1], n_samples) + if buffered then + if #outs == 1 then + conv:run_mono_buffered (outs[1], n_samples) + else + conv:run_stereo_buffered (outs[1], outs[2], n_samples) + end else - conv:run_stereo (outs[1], outs[2], n_samples) + if #outs == 1 then + conv:run_mono_no_latency (outs[1], n_samples) + else + conv:run_stereo_no_latency (outs[1], outs[2], n_samples) + end end end