pcm/Normalizer: expose the class, convert functions to methods
This commit is contained in:
parent
48c8c7daf5
commit
e6fedcbd10
@ -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() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
Loading…
Reference in New Issue
Block a user