pcm/Normalizer: expose the class, convert functions to methods

This commit is contained in:
Max Kellermann 2023-03-13 12:12:47 +01:00
parent 48c8c7daf5
commit e6fedcbd10
4 changed files with 56 additions and 89 deletions

View File

@ -12,20 +12,15 @@
#include <string.h>
class NormalizeFilter final : public Filter {
Compressor *const compressor;
PcmNormalizer normalizer;
PcmBuffer buffer;
public:
explicit NormalizeFilter(const AudioFormat &audio_format)
:Filter(audio_format), compressor(Compressor_new()) {
:Filter(audio_format) {
}
~NormalizeFilter() override {
Compressor_delete(compressor);
}
NormalizeFilter(const NormalizeFilter &) = delete;
NormalizeFilter &operator=(const NormalizeFilter &) = delete;
@ -59,7 +54,7 @@ NormalizeFilter::FilterPCM(std::span<const std::byte> src)
auto *dest = (int16_t *)buffer.Get(src.size());
memcpy(dest, src.data(), src.size());
Compressor_Process_int16(compressor, dest, src.size() / 2);
normalizer.ProcessS16(dest, src.size() / 2);
return { (const std::byte *)dest, src.size() };
}

View File

@ -4,68 +4,17 @@
#include "Normalizer.hxx"
struct Compressor {
///! Target level (on a scale of 0-32767)
static constexpr int target = 16384;
//! The maximum amount to amplify by
static constexpr int maxgain = 32;
//! How much inertia ramping has
static constexpr int smooth = 8;
//! History of the peak values
int *const peaks;
//! History of the gain values
int *const gain;
//! History of clip amounts
int *const clipped;
unsigned int pos = 0;
const unsigned int bufsz;
Compressor(unsigned history) noexcept
:peaks(new int[history]{}),
gain(new int[history]{}),
clipped(new int[history]{}),
bufsz(history)
{
}
~Compressor() noexcept {
delete[] peaks;
delete[] gain;
delete[] clipped;
}
};
struct Compressor *
Compressor_new(unsigned int history) noexcept
{
return new Compressor(history);
}
void
Compressor_delete(struct Compressor *obj) noexcept
{
delete obj;
}
void
Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
unsigned int count) noexcept
PcmNormalizer::ProcessS16(int16_t *audio, unsigned int count) noexcept
{
int16_t *ap;
unsigned int i;
int *peaks = obj->peaks;
int curGain = obj->gain[obj->pos];
int curGain = gain[pos];
int newGain;
int peakVal = 1;
int peakPos = 0;
int slot = (obj->pos + 1) % obj->bufsz;
int *clipped = obj->clipped + slot;
int slot = (pos + 1) % bufsz;
int *clipped_ = clipped + slot;
unsigned int ramp = count;
int delta;
@ -84,7 +33,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
peaks[slot] = peakVal;
for (i = 0; i < obj->bufsz; i++)
for (i = 0; i < bufsz; i++)
{
if (peaks[i] > peakVal)
{
@ -94,15 +43,14 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
}
//! Determine target gain
newGain = (1 << 10)*obj->target/peakVal;
newGain = (1 << 10)*target/peakVal;
//! Adjust the gain with inertia from the previous gain value
newGain = (curGain*((1 << obj->smooth) - 1) + newGain)
>> obj->smooth;
newGain = (curGain*((1 << smooth) - 1) + newGain) >> smooth;
//! Make sure it's no more than the maximum gain value
if (newGain > (obj->maxgain << 10))
newGain = obj->maxgain << 10;
if (newGain > (maxgain << 10))
newGain = maxgain << 10;
//! Make sure it's no less than 1:1
if (newGain < (1 << 10))
@ -117,7 +65,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
}
//! Record the new gain
obj->gain[slot] = newGain;
gain[slot] = newGain;
if (!ramp)
ramp = 1;
@ -126,7 +74,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
delta = (newGain - curGain) / (int)ramp;
ap = audio;
*clipped = 0;
*clipped_ = 0;
for (i = 0; i < count; i++)
{
int sample;
@ -135,11 +83,11 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
sample = *ap*curGain >> 10;
if (sample < -32768)
{
*clipped += -32768 - sample;
*clipped_ += -32768 - sample;
sample = -32768;
} else if (sample > 32767)
{
*clipped += sample - 32767;
*clipped_ += sample - 32767;
sample = 32767;
}
*ap++ = sample;
@ -151,6 +99,6 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
curGain = newGain;
}
obj->pos = slot;
pos = slot;
}

View File

@ -6,19 +6,46 @@
#include <cstdint>
struct Compressor;
class PcmNormalizer {
///! Target level (on a scale of 0-32767)
static constexpr int target = 16384;
//! Create a new compressor (use history value of 0 for default)
struct Compressor *
Compressor_new(unsigned int history = 400) noexcept;
//! The maximum amount to amplify by
static constexpr int maxgain = 32;
//! Delete a compressor
void
Compressor_delete(struct Compressor *) noexcept;
//! How much inertia ramping has
static constexpr int smooth = 8;
//! Process 16-bit signed data
void
Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count) noexcept;
//! History of the peak values
int *const peaks;
//! History of the gain values
int *const gain;
//! History of clip amounts
int *const clipped;
unsigned int pos = 0;
const unsigned int bufsz;
public:
PcmNormalizer(unsigned history=400) noexcept
:peaks(new int[history]{}),
gain(new int[history]{}),
clipped(new int[history]{}),
bufsz(history)
{
}
~PcmNormalizer() noexcept {
delete[] peaks;
delete[] gain;
delete[] clipped;
}
//! Process 16-bit signed data
void ProcessS16(int16_t *data, unsigned int count) noexcept;
};
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed

View File

@ -22,7 +22,6 @@
int main(int argc, char **argv)
try {
struct Compressor *compressor;
static char buffer[4096];
ssize_t nbytes;
@ -35,16 +34,14 @@ try {
if (argc > 1)
audio_format = ParseAudioFormat(argv[1], false);
compressor = Compressor_new();
PcmNormalizer normalizer;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
Compressor_Process_int16(compressor,
(int16_t *)buffer, nbytes / 2);
normalizer.ProcessS16((int16_t *)buffer, nbytes / 2);
[[maybe_unused]] ssize_t ignored = write(1, buffer, nbytes);
}
Compressor_delete(compressor);
return EXIT_SUCCESS;
} catch (...) {
PrintException(std::current_exception());