#include "audiographer/general/analyser.h"
#include "pbd/fastlog.h"
+#include "pbd/compose.h"
+#include "ardour/debug.h"
using namespace AudioGrapher;
+const float Analyser::fft_range_db (120); // dB
+
Analyser::Analyser (float sample_rate, unsigned int channels, framecnt_t bufsize, framecnt_t n_samples)
: _ebur128_plugin (0)
, _dbtp_plugin (0)
, _n_samples (n_samples)
, _pos (0)
{
+ DEBUG_TRACE (PBD::DEBUG::ExportAnalysis, string_compose ("Analyser r:%1 c:%2 f:%3 d:%4\n", sample_rate, channels, bufsize, n_samples));
+
assert (bufsize % channels == 0);
- //printf ("NEW ANALYSER %p r:%.1f c:%d f:%ld l%ld\n", this, sample_rate, channels, bufsize, n_samples);
+ assert (bufsize > 1);
+
if (channels > 0 && channels <= 2) {
using namespace Vamp::HostExt;
PluginLoader* loader (PluginLoader::getInstance ());
assert (_ebur128_plugin);
_ebur128_plugin->reset ();
if (!_ebur128_plugin->initialise (channels, _bufsize, _bufsize)) {
- printf ("FAILED TO INITIALIZE EBUR128\n");
delete _ebur128_plugin;
_ebur128_plugin = 0;
}
assert (_dbtp_plugin[c]);
_dbtp_plugin[c]->reset ();
if (!_dbtp_plugin[c]->initialise (1, _bufsize, _bufsize)) {
- printf ("FAILED TO INITIALIZE DBTP %d\n", c);
delete _dbtp_plugin[c];
_dbtp_plugin[c] = 0;
}
_bufs[1] = (float*) malloc (sizeof (float) * _bufsize);
const size_t peaks = sizeof (_result.peaks) / sizeof (ARDOUR::PeakData::PeakDatum) / 4;
- _spp = ceil ((_n_samples + 1.f) / (float) peaks);
+ _spp = ceil ((_n_samples + 2.f) / (float) peaks);
const size_t swh = sizeof (_result.spectrum) / sizeof (float);
const size_t height = sizeof (_result.spectrum[0]) / sizeof (float);
const size_t width = swh / height;
- _fpp = ceil ((_n_samples + 1.f) / (float) width);
+ _fpp = ceil ((_n_samples + 2.f) / (float) width);
_fft_data_size = _bufsize / 2;
_fft_freq_per_bin = sample_rate / _fft_data_size / 2.f;
const float nyquist = (sample_rate * .5);
#if 0 // linear
-#define YPOS(FREQ) ceil (height * (1.0 - FREQ / nyquist))
+#define YPOS(FREQ) rint (height * (1.0 - FREQ / nyquist))
#else
-#define YPOS(FREQ) ceil (height * (1 - logf (1.f + .1f * _fft_data_size * FREQ / nyquist) / logf (1.f + .1f * _fft_data_size)))
+#define YPOS(FREQ) rint (height * (1 - logf (1.f + .1f * _fft_data_size * FREQ / nyquist) / logf (1.f + .1f * _fft_data_size)))
#endif
_result.freq[0] = YPOS (50);
}
void
-Analyser::process (ProcessContext<float> const & c)
+Analyser::process (ProcessContext<float> const & ctx)
{
- framecnt_t n_samples = c.frames () / c.channels ();
- assert (c.frames () % c.channels () == 0);
+ const framecnt_t n_samples = ctx.frames () / ctx.channels ();
+ assert (ctx.channels () == _channels);
+ assert (ctx.frames () % ctx.channels () == 0);
assert (n_samples <= _bufsize);
- //printf ("PROC %p @%ld F: %ld, S: %ld C:%d\n", this, _pos, c.frames (), n_samples, c.channels ());
- float const * d = c.data ();
+ //printf ("PROC %p @%ld F: %ld, S: %ld C:%d\n", this, _pos, ctx.frames (), n_samples, ctx.channels ());
+
+ // allow 1 sample slack for resampling
+ if (_pos + n_samples > _n_samples + 1) {
+ _pos += n_samples;
+ ListedSource<float>::output (ctx);
+ return;
+ }
+
+ float const * d = ctx.data ();
framecnt_t s;
const unsigned cmask = _result.n_channels - 1; // [0, 1]
for (s = 0; s < n_samples; ++s) {
_fft_data_in[s] = 0;
- const framecnt_t pk = (_pos + s) / _spp;
+ const framecnt_t pbin = (_pos + s) / _spp;
for (unsigned int c = 0; c < _channels; ++c) {
const float v = *d;
if (fabsf(v) > _result.peak) { _result.peak = fabsf(v); }
_bufs[c][s] = v;
const unsigned int cc = c & cmask;
- if (_result.peaks[cc][pk].min > v) { _result.peaks[cc][pk].min = *d; }
- if (_result.peaks[cc][pk].max < v) { _result.peaks[cc][pk].max = *d; }
+ if (_result.peaks[cc][pbin].min > v) { _result.peaks[cc][pbin].min = *d; }
+ if (_result.peaks[cc][pbin].max < v) { _result.peaks[cc][pbin].max = *d; }
_fft_data_in[s] += v * _hann_window[s] / (float) _channels;
++d;
}
_ebur128_plugin->process (_bufs, Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate));
}
- float const * const data = c.data ();
+ float const * const data = ctx.data ();
for (unsigned int c = 0; c < _channels; ++c) {
if (!_dbtp_plugin[c]) { continue; }
for (s = 0; s < n_samples; ++s) {
const framecnt_t x0 = _pos / _fpp;
framecnt_t x1 = (_pos + n_samples) / _fpp;
if (x0 == x1) x1 = x0 + 1;
- const float range = 80; // dB
- for (uint32_t i = 1; i < _fft_data_size - 1; ++i) {
+ for (uint32_t i = 0; i < _fft_data_size - 1; ++i) {
const float level = fft_power_at_bin (i, i);
- if (level < -range) continue;
- const float pk = level > 0.0 ? 1.0 : (range + level) / range;
+ if (level < -fft_range_db) continue;
+ const float pk = level > 0.0 ? 1.0 : (fft_range_db + level) / fft_range_db;
#if 0 // linear
- const uint32_t y0 = height - ceil (i * (float) height / _fft_data_size);
- uint32_t y1= height - ceil (i * (float) height / _fft_data_size);
+ const uint32_t y0 = floor (i * (float) height / _fft_data_size);
+ uint32_t y1 = ceil ((i + 1.0) * (float) height / _fft_data_size);
#else // logscale
- const uint32_t y0 = height - ceilf (height * logf (1.f + .1f * i) / logf (1.f + .1f * _fft_data_size));
- uint32_t y1 = height - ceilf (height * logf (1.f + .1f * (i + 1.f)) / logf (1.f + .1f * _fft_data_size));
+ const uint32_t y0 = floor (height * logf (1.f + .1f * i) / logf (1.f + .1f * _fft_data_size));
+ uint32_t y1 = ceilf (height * logf (1.f + .1f * (i + 1.f)) / logf (1.f + .1f * _fft_data_size));
#endif
- if (y0 == y1 && y0 > 0) y1 = y0 - 1;
+ assert (y0 < height);
+ assert (y1 > 0 && y1 <= height);
+ if (y0 == y1) y1 = y0 + 1;
for (int x = x0; x < x1; ++x) {
- for (uint32_t y = y0; y > y1; --y) {
- if (_result.spectrum[x][y] < pk) { _result.spectrum[x][y] = pk; }
+ for (uint32_t y = y0; y < y1 && y < height; ++y) {
+ uint32_t yy = height - 1 - y;
+ if (_result.spectrum[x][yy] < pk) { _result.spectrum[x][yy] = pk; }
}
}
}
_pos += n_samples;
/* pass audio audio through */
- ListedSource<float>::output (c);
+ ListedSource<float>::output (ctx);
}
ARDOUR::ExportAnalysisPtr
Analyser::result ()
{
- //printf ("PROCESSED %ld / %ld samples\n", _pos, _n_samples);
- if (_pos == 0) {
+ DEBUG_TRACE (PBD::DEBUG::ExportAnalysis, string_compose ("Processed %1 / %2 samples\n", _pos, _n_samples));
+ if (_pos == 0 || ::llabs (_pos -_n_samples) > 1) {
+ printf("NO ANAL\n");
return ARDOUR::ExportAnalysisPtr ();
}
+
if (_ebur128_plugin) {
Vamp::Plugin::FeatureSet features = _ebur128_plugin->getRemainingFeatures ();
if (!features.empty () && features.size () == 3) {
}
}
+ const unsigned cmask = _result.n_channels - 1; // [0, 1]
for (unsigned int c = 0; c < _channels; ++c) {
if (!_dbtp_plugin[c]) { continue; }
Vamp::Plugin::FeatureSet features = _dbtp_plugin[c]->getRemainingFeatures ();
- if (!features.empty () && features.size () == 1) {
+ if (!features.empty () && features.size () == 2) {
_result.have_dbtp = true;
float p = features[0][0].values[0];
if (p > _result.truepeak) { _result.truepeak = p; }
+
+ for (std::vector<float>::const_iterator i = features[1][0].values.begin();
+ i != features[1][0].values.end(); ++i) {
+ const framecnt_t pk = (*i) / _spp;
+ const unsigned int cc = c & cmask;
+ _result.truepeakpos[cc].insert (pk);
+ }
}
}