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 $<
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
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)
#include "audioreader.h"
#include "interpolate.h"
+#include "level.h"
#include "tap.h"
#define BUFSIZE 4096
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)
}
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' },
{
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");
{
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;
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;
#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;
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;
}
--- /dev/null
+#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)