From 8d7d0c656008d93b3bf5368863e42720e3753999 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 6 May 2013 01:24:55 +0200 Subject: [PATCH] Add a simple leveller/compressor. --- Makefile | 9 ++-- level.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 level.cpp diff --git a/Makefile b/Makefile index ae22aa8..a5c7b66 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CXXFLAGS=-O2 -ffast-math -g -Wall -all: synth decode sync +all: synth decode sync level %.o: %.cpp $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $< -OBJS=decode.o synth.o synth_main.o interpolate.o sync.o +OBJS=decode.o synth.o synth_main.o interpolate.o sync.o level.o DEPS=$(OBJS:.o=.d) -include $(DEPS) @@ -19,5 +19,8 @@ synth: synth.o synth_main.o sync: interpolate.o sync.o $(CXX) -o $@ $^ $(LDFLAGS) +level: level.o + $(CXX) -o $@ $^ $(LDFLAGS) + clean: - $(RM) synth decode sync $(OBJS) $(DEPS) + $(RM) synth decode sync level $(OBJS) $(DEPS) diff --git a/level.cpp b/level.cpp new file mode 100644 index 0000000..e14704a --- /dev/null +++ b/level.cpp @@ -0,0 +1,125 @@ +// A small program to try to even out the audio levels within a file +// (essentially a compressor with infinite lookahead). + +#include +#include +#include +#include + +#define BUFSIZE 4096 +#define WAVE_FREQ 44100.0 + +// The frequency to filter on, in Hertz. Larger values makes the +// compressor react faster, but if it is too large, you'll +// ruin the waveforms themselves. +#define LPFILTER_FREQ 50.0 + +// The minimum estimated sound level at any given point. +// If you decrease this, you'll be able to amplify really silent signals +// by more, but you'll also increase the level of silent (ie. noise-only) segments, +// possibly caused misdetected pulses in these segments. +#define MIN_LEVEL 0.05 + +// A final scalar to get the audio within approximately the right range. +// Increase to _lower_ overall volume. +#define DAMPENING_FACTOR 5.0 + +// 6dB/oct per round. +#define FILTER_DEPTH 4 + +struct stereo_sample { + short left, right; +}; + +inline short clip(int x) +{ + if (x < -32768) { + return x; + } else if (x > 32767) { + return 32767; + } else { + return short(x); + } +} + +static float a1, a2, b0, b1, b2; +static float d0, d1; + +static void filter_init(float cutoff_radians) +{ + float resonance = 1.0f / sqrt(2.0f); + float sn, cs; + sincosf(cutoff_radians, &sn, &cs); + + float alpha = float(sn / (2 * resonance)); + + // coefficients for lowpass filter + float a0 = 1 + alpha; + b0 = (1 - cs) * 0.5f; + b1 = 1 - cs; + b2 = b0; + a1 = -2 * cs; + a2 = 1 - alpha; + + float invA0 = 1.0f / a0; + b0 *= invA0; + b1 *= invA0; + b2 *= invA0; + a1 *= invA0; + a2 *= invA0; + + // reset filter delays + d0 = d1 = 0.0f; +} + +static float filter_update(float in) +{ + float out = b0*in + d0; + d0 = b1 * in - a1 * out + d1; + d1 = b2 * in - a2 * out; + return out; +} + +int main(int argc, char **argv) +{ + std::vector pcm; + + while (!feof(stdin)) { + short buf[BUFSIZE]; + ssize_t ret = fread(buf, sizeof(short), BUFSIZE, stdin); + if (ret >= 0) { + pcm.insert(pcm.end(), buf, buf + ret); + } + } + + // filter forwards, then backwards (perfect phase filtering) + std::vector filtered_samples, refiltered_samples; + filtered_samples.resize(pcm.size()); + refiltered_samples.resize(pcm.size()); + + filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ); + for (unsigned i = 0; i < pcm.size(); ++i) { + filtered_samples[i] = filter_update(fabs(pcm[i])); + } + filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ); + for (unsigned i = pcm.size(); i --> 0; ) { + refiltered_samples[i] = filter_update(filtered_samples[i]); + } + + for (int i = 1; i < FILTER_DEPTH; ++i) { + filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ); + for (unsigned i = 0; i < pcm.size(); ++i) { + filtered_samples[i] = filter_update(refiltered_samples[i]); + } + filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ); + for (unsigned i = pcm.size(); i --> 0; ) { + refiltered_samples[i] = filter_update(filtered_samples[i]); + } + } + + for (unsigned i = 0; i < pcm.size(); ++i) { + float f = DAMPENING_FACTOR * std::max(refiltered_samples[i] * (1.0 / 32768.0), MIN_LEVEL); + short s = clip(lrintf(pcm[i] / f)); + fwrite(&s, sizeof(s), 1, stdout); + } +} -- 2.39.2