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> #include <string.h>
class NormalizeFilter final : public Filter { class NormalizeFilter final : public Filter {
Compressor *const compressor; PcmNormalizer normalizer;
PcmBuffer buffer; PcmBuffer buffer;
public: public:
explicit NormalizeFilter(const AudioFormat &audio_format) 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(const NormalizeFilter &) = delete;
NormalizeFilter &operator=(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()); auto *dest = (int16_t *)buffer.Get(src.size());
memcpy(dest, src.data(), 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() }; return { (const std::byte *)dest, src.size() };
} }

View File

@ -4,68 +4,17 @@
#include "Normalizer.hxx" #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 void
Compressor_delete(struct Compressor *obj) noexcept PcmNormalizer::ProcessS16(int16_t *audio, unsigned int count) noexcept
{
delete obj;
}
void
Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
unsigned int count) noexcept
{ {
int16_t *ap; int16_t *ap;
unsigned int i; unsigned int i;
int *peaks = obj->peaks; int curGain = gain[pos];
int curGain = obj->gain[obj->pos];
int newGain; int newGain;
int peakVal = 1; int peakVal = 1;
int peakPos = 0; int peakPos = 0;
int slot = (obj->pos + 1) % obj->bufsz; int slot = (pos + 1) % bufsz;
int *clipped = obj->clipped + slot; int *clipped_ = clipped + slot;
unsigned int ramp = count; unsigned int ramp = count;
int delta; int delta;
@ -84,7 +33,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
peaks[slot] = peakVal; peaks[slot] = peakVal;
for (i = 0; i < obj->bufsz; i++) for (i = 0; i < bufsz; i++)
{ {
if (peaks[i] > peakVal) if (peaks[i] > peakVal)
{ {
@ -94,15 +43,14 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
} }
//! Determine target gain //! Determine target gain
newGain = (1 << 10)*obj->target/peakVal; newGain = (1 << 10)*target/peakVal;
//! Adjust the gain with inertia from the previous gain value //! Adjust the gain with inertia from the previous gain value
newGain = (curGain*((1 << obj->smooth) - 1) + newGain) newGain = (curGain*((1 << smooth) - 1) + newGain) >> smooth;
>> obj->smooth;
//! Make sure it's no more than the maximum gain value //! Make sure it's no more than the maximum gain value
if (newGain > (obj->maxgain << 10)) if (newGain > (maxgain << 10))
newGain = obj->maxgain << 10; newGain = maxgain << 10;
//! Make sure it's no less than 1:1 //! Make sure it's no less than 1:1
if (newGain < (1 << 10)) if (newGain < (1 << 10))
@ -117,7 +65,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
} }
//! Record the new gain //! Record the new gain
obj->gain[slot] = newGain; gain[slot] = newGain;
if (!ramp) if (!ramp)
ramp = 1; ramp = 1;
@ -126,7 +74,7 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
delta = (newGain - curGain) / (int)ramp; delta = (newGain - curGain) / (int)ramp;
ap = audio; ap = audio;
*clipped = 0; *clipped_ = 0;
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
int sample; int sample;
@ -135,11 +83,11 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
sample = *ap*curGain >> 10; sample = *ap*curGain >> 10;
if (sample < -32768) if (sample < -32768)
{ {
*clipped += -32768 - sample; *clipped_ += -32768 - sample;
sample = -32768; sample = -32768;
} else if (sample > 32767) } else if (sample > 32767)
{ {
*clipped += sample - 32767; *clipped_ += sample - 32767;
sample = 32767; sample = 32767;
} }
*ap++ = sample; *ap++ = sample;
@ -151,6 +99,6 @@ Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
curGain = newGain; curGain = newGain;
} }
obj->pos = slot; pos = slot;
} }

View File

@ -6,19 +6,46 @@
#include <cstdint> #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) //! The maximum amount to amplify by
struct Compressor * static constexpr int maxgain = 32;
Compressor_new(unsigned int history = 400) noexcept;
//! Delete a compressor //! How much inertia ramping has
void static constexpr int smooth = 8;
Compressor_delete(struct Compressor *) noexcept;
//! Process 16-bit signed data //! History of the peak values
void int *const peaks;
Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count) noexcept;
//! 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 //! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed

View File

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