"LGPL-2.1" doesn't exist, and the old copyright header did not specify the "or later" clause, so I must assume it's 2.1 only.
103 lines
2.5 KiB
C++
103 lines
2.5 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-only
|
|
// Copyright The Music Player Daemon Project
|
|
// Based on AudioCompress (c)2007 busybee (http://beesbuzz.biz/
|
|
|
|
#include "Normalizer.hxx"
|
|
#include "Clamp.hxx"
|
|
#include "SampleFormat.hxx"
|
|
#include "Traits.hxx"
|
|
#include "util/Compiler.h"
|
|
|
|
#include <algorithm> // for std::fill_n()
|
|
|
|
void
|
|
PcmNormalizer::Reset() noexcept
|
|
{
|
|
prev_gain = 0;
|
|
pos = 0;
|
|
std::fill_n(peaks, bufsz, 0);
|
|
}
|
|
|
|
void
|
|
PcmNormalizer::ProcessS16(int16_t *gcc_restrict dest,
|
|
const std::span<const int16_t> src) noexcept
|
|
{
|
|
constexpr SampleFormat format = SampleFormat::S16;
|
|
using Traits = SampleTraits<format>;
|
|
using long_type = Traits::long_type;
|
|
|
|
constexpr unsigned SHIFT = 10;
|
|
|
|
int peakVal = 1;
|
|
std::size_t peakPos = 0;
|
|
for (std::size_t i = 0; i < src.size(); i++) {
|
|
int val = src[i];
|
|
if (val < 0)
|
|
val = -val;
|
|
if (val > peakVal)
|
|
{
|
|
peakVal = val;
|
|
peakPos = i;
|
|
}
|
|
}
|
|
|
|
pos = (pos + 1) % bufsz;
|
|
peaks[pos] = peakVal;
|
|
|
|
for (std::size_t i = 0; i < bufsz; i++) {
|
|
if (peaks[i] > peakVal)
|
|
{
|
|
peakVal = peaks[i];
|
|
peakPos = 0;
|
|
}
|
|
}
|
|
|
|
//! Determine target gain
|
|
long_type newGain = (1 << SHIFT)*target/peakVal;
|
|
|
|
//! Adjust the gain with inertia from the previous gain value
|
|
long_type curGain = prev_gain;
|
|
newGain = (curGain*((1 << smooth) - 1) + newGain) >> smooth;
|
|
|
|
//! Make sure it's no more than the maximum gain value
|
|
if (newGain > (maxgain << SHIFT))
|
|
newGain = maxgain << SHIFT;
|
|
|
|
//! Make sure it's no less than 1:1
|
|
if (newGain < (1 << SHIFT))
|
|
newGain = 1 << SHIFT;
|
|
|
|
//! Make sure the adjusted gain won't cause clipping
|
|
std::size_t ramp = src.size();
|
|
if ((peakVal*newGain >> SHIFT) > Traits::MAX)
|
|
{
|
|
newGain = (Traits::MAX << SHIFT)/peakVal;
|
|
//! Truncate the ramp time
|
|
ramp = peakPos;
|
|
}
|
|
|
|
//! Record the new gain
|
|
prev_gain = newGain;
|
|
|
|
if (!ramp)
|
|
ramp = 1;
|
|
if (!curGain)
|
|
curGain = 1 << SHIFT;
|
|
const long_type delta = (newGain - curGain) / (long_type)ramp;
|
|
|
|
for (const auto sample : src.first(ramp)) {
|
|
//! Amplify the sample
|
|
*dest++ = PcmClamp<format>(sample * curGain >> SHIFT);
|
|
|
|
//! Adjust the gain
|
|
curGain += delta;
|
|
}
|
|
|
|
curGain = newGain;
|
|
|
|
for (const auto sample : src.subspan(ramp)) {
|
|
//! Amplify the sample
|
|
*dest++ = PcmClamp<format>(sample * curGain >> SHIFT);
|
|
}
|
|
}
|