Implement zero-latency convolver

This commit is contained in:
Robin Gareus 2021-03-30 01:22:50 +02:00
parent cce1a67e75
commit d87002617e
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
4 changed files with 113 additions and 20 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2020 Robin Gareus <robin@gareus.org>
* Copyright (C) 2018-2021 Robin Gareus <robin@gareus.org>
*
* 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<boost::shared_ptr<Readable> > _readables;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2020 Robin Gareus <robin@gareus.org>
* Copyright (C) 2018-2021 Robin Gareus <robin@gareus.org>
*
* 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<ImpData>::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<boost::shared_ptr<Readable> > 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;
}
}

View File

@ -2724,8 +2724,10 @@ LuaBindings::common (lua_State* L)
.deriveClass <DSP::Convolver, DSP::Convolution> ("Convolver")
.addConstructor <void (*) (Session&, std::string const&, DSP::Convolver::IRChannelConfig, DSP::Convolver::IRSettings)> ()
.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 */

View File

@ -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