Commit be5c3f82 authored by Stefan Westerfeld's avatar Stefan Westerfeld

Use different limiter algorithm, use volume envelope from block max.

Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parent 4bb23ea9
......@@ -14,76 +14,53 @@ Limiter::Limiter (int n_channels, int sample_rate) :
}
void
Limiter::set_attack (float attack_ms)
Limiter::set_block_size_ms (int ms)
{
look_ahead = sample_rate / 1000.0 * attack_ms;
look_ahead = max (look_ahead, 1u);
}
void
Limiter::set_release (float release_ms)
{
release_factor = exp (log (0.5) / (sample_rate / 1000.0 * release_ms));
release_factor = max (release_factor, 0.5f);
block_size = sample_rate * ms / 1000;
}
void
Limiter::set_ceiling (float new_ceiling)
{
ceiling = new_ceiling;
maximum = ceiling;
}
vector<float>
Limiter::process (const vector<float>& samples)
{
assert (look_ahead >= 1);
assert (release_factor > 0 && release_factor < 1);
assert (block_size >= 1);
const size_t n_frames = samples.size() / n_channels;
assert (n_frames * n_channels == samples.size()); // need all channels of each frame
buffer.insert (buffer.end(), samples.begin(), samples.end());
max_buffer.insert (max_buffer.end(), n_frames, ceiling);
for (size_t i = 0; i < max_buffer.size(); i++)
{
float channel_max = 0;
for (uint c = 0; c < n_channels; c++)
channel_max = max (channel_max, fabs (buffer[i * n_channels + c]));
if (channel_max > ceiling)
{
for (uint j = 0; j < look_ahead; j++)
{
if (int (i) - int (j) >= 0)
{
float alpha = float (j) / look_ahead;
max_buffer[i - j] = max (max_buffer[i - j], channel_max * (1 - alpha) + ceiling * alpha);
}
}
}
}
if (max_buffer.size() <= look_ahead)
/* need at least two complete blocks in buffer to produce output */
if (buffer.size() / n_channels < 2 * block_size)
return {};
const size_t todo = max_buffer.size() - look_ahead;
vector<float> out (todo * n_channels);
vector<float> out (block_size * n_channels);
for (size_t i = 0; i < todo; i++)
float block_max = ceiling;
float block_max2 = ceiling;
for (size_t x = 0; x < block_size * n_channels; x++)
{
block_max = max (block_max, fabs (buffer[x]));
block_max2 = max (block_max2, fabs (buffer[x + block_size * n_channels]));
}
float left_max = max (last_block_max, block_max);
float right_max = max (block_max, block_max2);
for (size_t i = 0; i < block_size; i++)
{
maximum = maximum * release_factor + max_buffer[i] * (1 - release_factor);
if (maximum < max_buffer[i])
maximum = max_buffer[i];
const float alpha = float (i) / block_size;
const float scale = ceiling / (left_max * (1 - alpha) + right_max * alpha);
const float scale = ceiling / maximum;
for (uint c = 0; c < n_channels; c++)
out[i * n_channels + c] = buffer[i * n_channels + c] * scale;
}
last_block_max = block_max;
buffer.erase (buffer.begin(), buffer.begin() + todo * n_channels);
max_buffer.erase (max_buffer.begin(), max_buffer.begin() + todo);
buffer.erase (buffer.begin(), buffer.begin() + block_size * n_channels);
return out;
}
......@@ -7,19 +7,16 @@
class Limiter
{
float ceiling = 1;
float maximum = 1;
float release_factor = 0;
uint look_ahead = 0;
uint n_channels = 0;
uint sample_rate = 0;
float last_block_max = 0;
uint block_size = 0;
uint n_channels = 0;
uint sample_rate = 0;
std::vector<float> max_buffer;
std::vector<float> buffer;
public:
Limiter (int n_channels, int sample_rate);
void set_release (float value_ms);
void set_attack (float value_ms);
void set_block_size_ms (int value_ms);
void set_ceiling (float ceiling);
std::vector<float> process (const std::vector<float>& samples);
......
......@@ -30,9 +30,7 @@ perf()
{
Limiter limiter (2, 44100);
limiter.set_attack (5);
limiter.set_release (50);
limiter.set_ceiling (1.0);
limiter.set_block_size_ms (1000);
vector<float> samples (2 * 1024);
......@@ -52,8 +50,7 @@ int
impulses()
{
Limiter limiter (2, 44100);
limiter.set_attack (5);
limiter.set_release (10);
limiter.set_block_size_ms (5);
limiter.set_ceiling (0.9);
vector<float> in_all, out_all;
......@@ -104,8 +101,7 @@ main (int argc, char **argv)
return 1;
}
Limiter limiter (in.n_channels(), in.sample_rate());
limiter.set_attack (5);
limiter.set_release (50);
limiter.set_block_size_ms (1000);
limiter.set_ceiling (0.9);
vector<float> in_samples;
do
......
......@@ -645,8 +645,7 @@ add_watermark (const string& infile, const string& outfile, const string& bits)
return 1;
Limiter limiter (n_channels, in_stream->sample_rate());
limiter.set_attack (Params::limiter_attack_ms);
limiter.set_release (Params::limiter_release_ms);
limiter.set_block_size_ms (Params::limiter_block_size_ms);
limiter.set_ceiling (Params::limiter_ceiling);
/* for signal to noise ratio */
......
......@@ -37,9 +37,8 @@ public:
static constexpr size_t frames_pad_start = 250; // padding at start, in case track starts with silence
static constexpr int mark_sample_rate = 44100; // watermark generation and detection sample rate
static constexpr double limiter_attack_ms = 5;
static constexpr double limiter_release_ms = 500;
static constexpr double limiter_ceiling = 1.0;
static constexpr double limiter_block_size_ms = 1000;
static constexpr double limiter_ceiling = 1.0;
static int test_cut; // for sync test
static bool test_no_sync;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment