Integrate the leveler into decode.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 11 Mar 2015 23:36:19 +0000 (00:36 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 11 Mar 2015 23:36:19 +0000 (00:36 +0100)
Makefile
decode.cpp
level.cpp
level.h [new file with mode: 0644]

index 44e2a5c..fe8c404 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CXXFLAGS=--std=gnu++0x -O2 -fno-math-errno -g -Wall
 LDLIBS=-lavcodec -lavformat -lavutil -lswresample
 
-all: synth decode sync level cleaner
+all: synth decode sync cleaner
 
 %.o: %.cpp
        $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
@@ -11,7 +11,7 @@ OBJS=decode.o synth.o synth_main.o interpolate.o sync.o level.o
 DEPS=$(OBJS:.o=.d)
 -include $(DEPS)
 
-decode: interpolate.o audioreader.o decode.o
+decode: interpolate.o audioreader.o decode.o level.o
        $(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
 
 synth: synth.o synth_main.o
@@ -20,11 +20,8 @@ synth: synth.o synth_main.o
 sync: interpolate.o sync.o
        $(CXX) -o $@ $^ $(LDFLAGS)
 
-level: level.o
-       $(CXX) -o $@ $^ $(LDFLAGS)
-
 cleaner: cleaner.o
        $(CXX) -o $@ $^ $(LDFLAGS)
 
 clean:
-       $(RM) synth decode sync level cleaner $(OBJS) $(DEPS)
+       $(RM) synth decode sync cleaner $(OBJS) $(DEPS)
index 7def49d..32e2cc3 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "audioreader.h"
 #include "interpolate.h"
+#include "level.h"
 #include "tap.h"
 
 #define BUFSIZE 4096
@@ -29,6 +30,8 @@ static float crop_start = 0.0f, crop_end = HUGE_VAL;
 static float filter_coeff[NUM_FILTER_COEFF] = { 1.0f };  // The rest is filled with 0.
 static bool output_filtered = false;
 static bool quiet = false;
+static bool do_auto_level = false;
+static bool output_leveled = false;
 
 // between [x,x+1]
 double find_zerocrossing(const std::vector<float> &pcm, int x)
@@ -151,6 +154,7 @@ void output_tap(const std::vector<pulse>& pulses, double calibration_factor)
 }
 
 static struct option long_options[] = {
+       {"auto-level",       0,                 0, 'a' },
        {"no-calibrate",     0,                 0, 's' },
        {"plot-cycles",      0,                 0, 'p' },
        {"hysteresis-limit", required_argument, 0, 'l' },
@@ -166,6 +170,8 @@ void help()
 {
        fprintf(stderr, "decode [OPTIONS] AUDIO-FILE > TAP-FILE\n");
        fprintf(stderr, "\n");
+       fprintf(stderr, "  -a, --auto-level             automatically adjust amplitude levels throughout the file\n");
+       fprintf(stderr, "  -A, --output-leveled         output leveled waveform to leveled.raw\n");
        fprintf(stderr, "  -s, --no-calibrate           do not try to calibrate on sync pulse length\n");
        fprintf(stderr, "  -p, --plot-cycles            output debugging info to cycles.plot\n");
        fprintf(stderr, "  -l, --hysteresis-limit VAL   change amplitude threshold for ignoring pulses (0..32768)\n");
@@ -181,11 +187,19 @@ void parse_options(int argc, char **argv)
 {
        for ( ;; ) {
                int option_index = 0;
-               int c = getopt_long(argc, argv, "spl:f:Fc:qh", long_options, &option_index);
+               int c = getopt_long(argc, argv, "aAspl:f:Fc:qh", long_options, &option_index);
                if (c == -1)
                        break;
 
                switch (c) {
+               case 'a':
+                       do_auto_level = true;
+                       break;
+
+               case 'A':
+                       output_leveled = true;
+                       break;
+
                case 's':
                        do_calibrate = false;
                        break;
@@ -348,6 +362,15 @@ int main(int argc, char **argv)
                pcm = do_filter(pcm, filter_coeff);
        }
 
+       if (do_auto_level) {
+               pcm = level_samples(pcm, sample_rate);
+               if (output_leveled) {
+                       FILE *fp = fopen("leveled.raw", "wb");
+                       fwrite(pcm.data(), pcm.size() * sizeof(pcm[0]), 1, fp);
+                       fclose(fp);
+               }
+       }
+
 #if 0
        for (int i = 0; i < LEN; ++i) {
                in[i] += rand() % 10000;
index 3637799..fea5926 100644 (file)
--- a/level.cpp
+++ b/level.cpp
@@ -6,9 +6,6 @@
 #include <vector>
 #include <algorithm>
 
-#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.
 // 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;
 
@@ -77,46 +59,38 @@ static float filter_update(float in)
        return out;
 }
 
-int main(int argc, char **argv)
+std::vector<float> level_samples(const std::vector<float> &pcm, int sample_rate)
 {
-       std::vector<short> 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<float> filtered_samples, refiltered_samples;
+       std::vector<float> filtered_samples, refiltered_samples, leveled_samples;
        filtered_samples.resize(pcm.size());
        refiltered_samples.resize(pcm.size());
+       leveled_samples.resize(pcm.size());
 
-       filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
+       filter_init(M_PI * LPFILTER_FREQ / sample_rate);
        for (unsigned i = 0; i < pcm.size(); ++i) {
                filtered_samples[i] = filter_update(fabs(pcm[i]));
        }
-       filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
+       filter_init(M_PI * LPFILTER_FREQ / sample_rate);
        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);
+               filter_init(M_PI * LPFILTER_FREQ / sample_rate);
                for (unsigned i = 0; i < pcm.size(); ++i) {
                        filtered_samples[i] = filter_update(refiltered_samples[i]);
                }
-               filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
+               filter_init(M_PI * LPFILTER_FREQ / sample_rate);
                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);
+               float f = DAMPENING_FACTOR * std::max<float>(refiltered_samples[i], MIN_LEVEL);
+               leveled_samples[i] = pcm[i] / f;
        }
+
+       return leveled_samples;
 }
diff --git a/level.h b/level.h
new file mode 100644 (file)
index 0000000..3f830d7
--- /dev/null
+++ b/level.h
@@ -0,0 +1,8 @@
+#ifndef _LEVEL_H
+#define _LEVEL_H 1
+
+#include <vector>
+
+std::vector<float> level_samples(const std::vector<float> &pcm, int sample_rate);
+
+#endif  // !defined(_LEVEL_H)