mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 07:45:00 +01:00
Re-work plugin-analysis (fix crashes)
Plugin-analysis uses a fixed number of samples, which may be larger than the session's block-size. This caused problems for some VST plugins that use audioMasterCallback to query the session's block-size. Plugin FFT analysis now processes in chunks of the session's block-size to avoid this issue. This also allows to increase accuracy for all plugin standards (FFT size was increased to 8192).
This commit is contained in:
parent
61623f730c
commit
06854e1315
2 changed files with 94 additions and 94 deletions
|
|
@ -1,22 +1,22 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2008 Paul Davis
|
* Copyright (C) 2018 Robin Gareus <robin@gareus.org>
|
||||||
Author: Sampo Savolainen
|
* Copyright (C) 2008 Paul Davis
|
||||||
|
* Original Author: Sampo Savolainen
|
||||||
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
|
* This program is free software; you can redistribute it and/or
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
* modify it under the terms of the GNU General Public License
|
||||||
(at your option) any later version.
|
* 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
|
* This program is distributed in the hope that it will be useful,
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
GNU General Public License for more details.
|
* 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
|
* You should have received a copy of the GNU General Public License
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
@ -25,13 +25,13 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#ifdef COMPILER_MSVC
|
#ifdef COMPILER_MSVC
|
||||||
#include <float.h>
|
# include <float.h>
|
||||||
/* isinf() & isnan() are C99 standards, which older MSVC doesn't provide */
|
/* isinf() & isnan() are C99 standards, which older MSVC doesn't provide */
|
||||||
#define ISINF(val) !((bool)_finite((double)val))
|
# define ISINF(val) !((bool)_finite((double)val))
|
||||||
#define ISNAN(val) (bool)_isnan((double)val)
|
# define ISNAN(val) (bool)_isnan((double)val)
|
||||||
#else
|
#else
|
||||||
#define ISINF(val) std::isinf((val))
|
# define ISINF(val) std::isinf((val))
|
||||||
#define ISNAN(val) std::isnan((val))
|
# define ISNAN(val) std::isnan((val))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
|
|
@ -57,6 +57,7 @@ PluginEqGui::PluginEqGui(boost::shared_ptr<ARDOUR::PluginInsert> pluginInsert)
|
||||||
: _min_dB(-12.0)
|
: _min_dB(-12.0)
|
||||||
, _max_dB(+12.0)
|
, _max_dB(+12.0)
|
||||||
, _step_dB(3.0)
|
, _step_dB(3.0)
|
||||||
|
, _block_size(0)
|
||||||
, _buffer_size(0)
|
, _buffer_size(0)
|
||||||
, _signal_buffer_size(0)
|
, _signal_buffer_size(0)
|
||||||
, _impulse_fft(0)
|
, _impulse_fft(0)
|
||||||
|
|
@ -170,13 +171,14 @@ void
|
||||||
PluginEqGui::start_listening ()
|
PluginEqGui::start_listening ()
|
||||||
{
|
{
|
||||||
if (!_plugin) {
|
if (!_plugin) {
|
||||||
_plugin = _plugin_insert->get_impulse_analysis_plugin();
|
_plugin = _plugin_insert->get_impulse_analysis_plugin ();
|
||||||
}
|
}
|
||||||
|
|
||||||
_plugin->activate();
|
_plugin->activate ();
|
||||||
set_buffer_size(4096, 16384);
|
set_buffer_size (8192, 16384);
|
||||||
_plugin->set_block_size (_buffer_size);
|
_block_size = 0; // re-initialize the plugin next time.
|
||||||
// Connect the realtime signal collection callback
|
|
||||||
|
/* Connect the realtime signal collection callback */
|
||||||
_plugin_insert->AnalysisDataGathered.connect (analysis_connection, invalidator (*this), boost::bind (&PluginEqGui::signal_collect_callback, this, _1, _2), gui_context());
|
_plugin_insert->AnalysisDataGathered.connect (analysis_connection, invalidator (*this), boost::bind (&PluginEqGui::signal_collect_callback, this, _1, _2), gui_context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +292,7 @@ PluginEqGui::set_buffer_size(uint32_t size, uint32_t signal_size)
|
||||||
_buffer_size = size;
|
_buffer_size = size;
|
||||||
_signal_buffer_size = signal_size;
|
_signal_buffer_size = signal_size;
|
||||||
|
|
||||||
// allocate separate in+out buffers, VST cannot process in-place
|
/* allocate separate in+out buffers, VST cannot process in-place */
|
||||||
ARDOUR::ChanCount acount (_plugin->get_info()->n_inputs + _plugin->get_info()->n_outputs);
|
ARDOUR::ChanCount acount (_plugin->get_info()->n_inputs + _plugin->get_info()->n_outputs);
|
||||||
ARDOUR::ChanCount ccount = ARDOUR::ChanCount::max (_plugin->get_info()->n_inputs, _plugin->get_info()->n_outputs);
|
ARDOUR::ChanCount ccount = ARDOUR::ChanCount::max (_plugin->get_info()->n_inputs, _plugin->get_info()->n_outputs);
|
||||||
|
|
||||||
|
|
@ -363,91 +365,91 @@ PluginEqGui::run_impulse_analysis()
|
||||||
uint32_t inputs = _plugin->get_info()->n_inputs.n_audio();
|
uint32_t inputs = _plugin->get_info()->n_inputs.n_audio();
|
||||||
uint32_t outputs = _plugin->get_info()->n_outputs.n_audio();
|
uint32_t outputs = _plugin->get_info()->n_outputs.n_audio();
|
||||||
|
|
||||||
// Create the impulse, can't use silence() because consecutive calls won't work
|
/* Create the impulse, can't use silence() because consecutive calls won't work */
|
||||||
for (uint32_t i = 0; i < inputs; ++i) {
|
for (uint32_t i = 0; i < inputs; ++i) {
|
||||||
ARDOUR::AudioBuffer& buf = _bufferset.get_audio(i);
|
ARDOUR::AudioBuffer& buf = _bufferset.get_audio(i);
|
||||||
ARDOUR::Sample* d = buf.data();
|
ARDOUR::Sample* d = buf.data();
|
||||||
memset(d, 0, sizeof(ARDOUR::Sample)*_buffer_size);
|
memset (d, 0, sizeof(ARDOUR::Sample) * _buffer_size);
|
||||||
*d = 1.0;
|
*d = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARDOUR::ChanMapping in_map(_plugin->get_info()->n_inputs);
|
/* Silence collect buffers to copy data to */
|
||||||
ARDOUR::ChanMapping out_map(_plugin->get_info()->n_outputs);
|
|
||||||
// map output buffers after input buffers (no inplace for VST)
|
|
||||||
out_map.offset_to (DataType::AUDIO, inputs);
|
|
||||||
|
|
||||||
_plugin->connect_and_run(_bufferset, 0, _buffer_size, 1.0, in_map, out_map, _buffer_size, 0);
|
|
||||||
samplecnt_t f = _plugin->signal_latency ();
|
|
||||||
// Adding user_latency() could be interesting
|
|
||||||
|
|
||||||
// Gather all output, taking latency into account.
|
|
||||||
_impulse_fft->reset();
|
|
||||||
|
|
||||||
// Silence collect buffers to copy data to, can't use silence() because consecutive calls won't work
|
|
||||||
for (uint32_t i = 0; i < outputs; ++i) {
|
for (uint32_t i = 0; i < outputs; ++i) {
|
||||||
ARDOUR::AudioBuffer &buf = _collect_bufferset.get_audio(i);
|
ARDOUR::AudioBuffer &buf = _collect_bufferset.get_audio(i);
|
||||||
ARDOUR::Sample *d = buf.data();
|
ARDOUR::Sample *d = buf.data();
|
||||||
memset(d, 0, sizeof(ARDOUR::Sample)*_buffer_size);
|
memset(d, 0, sizeof(ARDOUR::Sample) * _buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f == 0) {
|
/* create default linear I/O maps */
|
||||||
//std::cerr << "0: no latency, copying full buffer, trivial.." << std::endl;
|
ARDOUR::ChanMapping in_map (_plugin->get_info()->n_inputs);
|
||||||
for (uint32_t i = 0; i < outputs; ++i) {
|
ARDOUR::ChanMapping out_map (_plugin->get_info()->n_outputs);
|
||||||
memcpy(_collect_bufferset.get_audio(i).data(),
|
/* map output buffers after input buffers (no inplace for VST) */
|
||||||
_bufferset.get_audio(inputs + i).data(), _buffer_size * sizeof(float));
|
out_map.offset_to (DataType::AUDIO, inputs);
|
||||||
|
|
||||||
|
/* run at most at session's block size chunks.
|
||||||
|
*
|
||||||
|
* This is important since VSTs may call audioMasterGetBlockSize
|
||||||
|
* or access various other /real/ session paramaters using the
|
||||||
|
* audioMasterCallback
|
||||||
|
*/
|
||||||
|
samplecnt_t block_size = ARDOUR_UI::instance()->the_session()->get_block_size();
|
||||||
|
if (_block_size != block_size) {
|
||||||
|
_block_size = block_size;
|
||||||
|
_plugin->set_block_size (block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
samplepos_t sample_pos = 0;
|
||||||
|
samplecnt_t latency = _plugin->signal_latency ();
|
||||||
|
samplecnt_t samples_remain = _buffer_size + latency;
|
||||||
|
|
||||||
|
_impulse_fft->reset();
|
||||||
|
|
||||||
|
while (samples_remain > 0) {
|
||||||
|
|
||||||
|
samplecnt_t n_samples = std::min (samples_remain, block_size);
|
||||||
|
_plugin->connect_and_run(_bufferset, sample_pos, sample_pos + n_samples, 1.0, in_map, out_map, n_samples, 0);
|
||||||
|
samples_remain -= n_samples;
|
||||||
|
|
||||||
|
/* zero input buffers */
|
||||||
|
if (sample_pos == 0 && samples_remain > 0) {
|
||||||
|
for (uint32_t i = 0; i < inputs; ++i) {
|
||||||
|
_bufferset.get_audio(i).data()[0] = 0.f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
//int C = 0;
|
|
||||||
//std::cerr << (++C) << ": latency is " << f << " samples, doing split processing.." << std::endl;
|
|
||||||
samplecnt_t target_offset = 0;
|
|
||||||
samplecnt_t samples_left = _buffer_size; // refaktoroi
|
|
||||||
do {
|
|
||||||
if (f >= _buffer_size) {
|
|
||||||
//std::cerr << (++C) << ": f (=" << f << ") is larger than buffer_size, still trying to reach the actual output" << std::endl;
|
|
||||||
// there is no data in this buffer regarding to the input!
|
|
||||||
f -= _buffer_size;
|
|
||||||
} else {
|
|
||||||
// this buffer contains either the first, last or a whole bu the output of the impulse
|
|
||||||
// first part: offset is 0, so we copy to the start of _collect_bufferset
|
|
||||||
// we start at output offset "f"
|
|
||||||
// .. and copy "buffer size" - "f" - "offset" samples
|
|
||||||
|
|
||||||
samplecnt_t length = _buffer_size - f - target_offset;
|
#ifndef NDEBUG
|
||||||
|
if (samples_remain > 0) {
|
||||||
//std::cerr << (++C) << ": copying " << length << " samples to _collect_bufferset.get_audio(i)+" << target_offset << " from bufferset at offset " << f << std::endl;
|
for (uint32_t i = 0; i < inputs; ++i) {
|
||||||
for (uint32_t i = 0; i < outputs; ++i) {
|
pframes_t unused;
|
||||||
memcpy(_collect_bufferset.get_audio(i).data(target_offset),
|
assert (_bufferset.get_audio(i).check_silence (block_size, unused));
|
||||||
_bufferset.get_audio(inputs + i).data() + f,
|
|
||||||
length * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
target_offset += length;
|
|
||||||
samples_left -= length;
|
|
||||||
f = 0;
|
|
||||||
}
|
}
|
||||||
if (samples_left > 0) {
|
}
|
||||||
// Silence the buffers
|
#endif
|
||||||
for (uint32_t i = 0; i < inputs; ++i) {
|
|
||||||
ARDOUR::AudioBuffer &buf = _bufferset.get_audio(i);
|
|
||||||
ARDOUR::Sample *d = buf.data();
|
|
||||||
memset(d, 0, sizeof(ARDOUR::Sample)*_buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
_plugin->connect_and_run (_bufferset, target_offset, target_offset + _buffer_size, 1.0, in_map, out_map, _buffer_size, 0);
|
if (sample_pos + n_samples > latency) {
|
||||||
|
samplecnt_t dst_off = sample_pos >= latency ? sample_pos - latency : 0;
|
||||||
|
samplecnt_t src_off = sample_pos >= latency ? 0 : latency - sample_pos;
|
||||||
|
samplecnt_t n_copy = std::min (n_samples, sample_pos + n_samples - latency);
|
||||||
|
|
||||||
|
assert (dst_off + n_copy <= _buffer_size);
|
||||||
|
assert (src_off + n_copy <= _block_size);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < outputs; ++i) {
|
||||||
|
memcpy(
|
||||||
|
&(_collect_bufferset.get_audio(i).data()[dst_off]),
|
||||||
|
&(_bufferset.get_audio(inputs + i).data()[src_off]),
|
||||||
|
n_copy * sizeof(float));
|
||||||
}
|
}
|
||||||
} while ( samples_left > 0);
|
}
|
||||||
|
|
||||||
|
sample_pos += n_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < outputs; ++i) {
|
for (uint32_t i = 0; i < outputs; ++i) {
|
||||||
_impulse_fft->analyze(_collect_bufferset.get_audio(i).data());
|
_impulse_fft->analyze (_collect_bufferset.get_audio(i).data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize the output
|
|
||||||
_impulse_fft->calculate();
|
_impulse_fft->calculate();
|
||||||
|
|
||||||
// This signals calls expose_analysis_area()
|
|
||||||
_analysis_area->queue_draw();
|
_analysis_area->queue_draw();
|
||||||
|
|
||||||
ARDOUR_UI::instance()->drop_process_buffers ();
|
ARDOUR_UI::instance()->drop_process_buffers ();
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __ardour_plugin_eq_gui_h
|
#ifndef __ardour_plugin_eq_gui_h
|
||||||
|
|
@ -99,6 +98,7 @@ private:
|
||||||
float _log_coeff;
|
float _log_coeff;
|
||||||
float _log_max;
|
float _log_max;
|
||||||
|
|
||||||
|
ARDOUR::samplecnt_t _block_size;
|
||||||
ARDOUR::samplecnt_t _buffer_size;
|
ARDOUR::samplecnt_t _buffer_size;
|
||||||
ARDOUR::samplecnt_t _signal_buffer_size;
|
ARDOUR::samplecnt_t _signal_buffer_size;
|
||||||
|
|
||||||
|
|
@ -106,7 +106,6 @@ private:
|
||||||
ARDOUR::BufferSet _bufferset;
|
ARDOUR::BufferSet _bufferset;
|
||||||
ARDOUR::BufferSet _collect_bufferset;
|
ARDOUR::BufferSet _collect_bufferset;
|
||||||
|
|
||||||
|
|
||||||
// dimensions
|
// dimensions
|
||||||
float _analysis_width;
|
float _analysis_width;
|
||||||
float _analysis_height;
|
float _analysis_height;
|
||||||
|
|
@ -155,4 +154,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue