# HG changeset patch # User Chris Cannam # Date 1382095090 -3600 # Node ID 1879a2997ebf2e0a2be5959798cc1d1014ef5562 # Parent 381823e25b8a2f764100907cc721d2deeaf00fcb Add resampler command-line program diff -r 381823e25b8a -r 1879a2997ebf garage-resampler/Makefile --- a/garage-resampler/Makefile Fri Oct 18 11:11:41 2013 +0100 +++ b/garage-resampler/Makefile Fri Oct 18 12:18:10 2013 +0100 @@ -2,23 +2,31 @@ DEFINES := -DUSE_PTHREADS CFLAGS := -I../.. $(CFLAGS) $(DEFINES) -CXXFLAGS := -I../.. -Wall -g $(CXXFLAGS) $(DEFINES) -#CXXFLAGS := -I../.. -Wall -O3 -ffast-math -ftree-vectorize -ftree-vectorizer-verbose=3 $(CXXFLAGS) $(DEFINES) -LDFLAGS := $(LDFLAGS) -lboost_unit_test_framework -LIBS := ../../qm-dsp/libqm-dsp.a +#CXXFLAGS := -I../.. -Wall -g $(CXXFLAGS) $(DEFINES) +CXXFLAGS := -I../.. -Wall -O3 -ffast-math -ftree-vectorize $(CXXFLAGS) $(DEFINES) + +LIBS := ../../qm-dsp/libqm-dsp.a -lpthread +PROGRAM_LIBS := -lsndfile +TEST_LIBS := -lboost_unit_test_framework + +LDFLAGS := $(LDFLAGS) TESTS := test-resampler +PROGRAM := resample #VG := valgrind -all: $(TESTS) +all: $(TESTS) $(PROGRAM) @for t in $(TESTS); do echo "Running $$t"; $(VG) ./"$$t" || exit 1; done -test-resampler: TestResampler.o Resampler.o $(LIBS) - $(CXX) -o $@ $^ $(LDFLAGS) +test-resampler: TestResampler.o Resampler.o + $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) $(TEST_LIBS) -TestResampler.o: Resampler.h Resampler.cpp $(LIBS) +resample: Resampler.o resample.o + $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) $(PROGRAM_LIBS) + +TestResampler.o: Resampler.h Resampler.cpp clean: rm -f *.o $(TESTS) diff -r 381823e25b8a -r 1879a2997ebf garage-resampler/resample.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/garage-resampler/resample.cpp Fri Oct 18 12:18:10 2013 +0100 @@ -0,0 +1,189 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "Resampler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +int main(int argc, char **argv) +{ + int targetRate = 0; + bool version = false; + bool help = false; + int c = 0; + + while (1) { + int optionIndex = 0; + + static struct option longOpts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "to", 1, 0, 't' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, + "t:hV", + longOpts, &optionIndex); + if (c == -1) break; + + switch (c) { + case 'h': help = true; break; + case 'V': version = true; break; + case 't': targetRate = atoi(optarg); break; + default: help = true; break; + } + } + + if (version) { + cerr << "resample v0.1" << endl; //!!! + return 0; + } + + cerr << "help = " << help << ", targetRate = " << targetRate << ", optionIndex = " << optind << ", argc = " << argc << endl; + + if (help || targetRate == 0 || optind + 2 != argc) { + cerr << endl; + cerr << "usage: " << argv[0] << " --to " << endl; + cerr << endl; + return 2; + } + + timeval tv; + (void)gettimeofday(&tv, 0); + + char *fileName = strdup(argv[optind++]); + char *fileNameOut = strdup(argv[optind++]); + + SNDFILE *sndfile; + SNDFILE *sndfileOut; + SF_INFO sfinfo; + SF_INFO sfinfoOut; + memset(&sfinfo, 0, sizeof(SF_INFO)); + + sndfile = sf_open(fileName, SFM_READ, &sfinfo); + if (!sndfile) { + cerr << "ERROR: Failed to open input file \"" << fileName << "\": " + << sf_strerror(sndfile) << endl; + return 1; + } + + int sourceRate = sfinfo.samplerate; + + sfinfoOut.channels = sfinfo.channels; + sfinfoOut.format = sfinfo.format; + sfinfoOut.frames = int(ceil(double(sfinfo.frames) * targetRate) / + sourceRate); + sfinfoOut.samplerate = targetRate; + sfinfoOut.sections = sfinfo.sections; + sfinfoOut.seekable = sfinfo.seekable; + + sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut); + if (!sndfileOut) { + cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: " + << sf_strerror(sndfileOut) << endl; + return 1; + } + + int ibs = 1024; + int obs = int(ceil((double(ibs) * targetRate) / sourceRate)); + + int channels = sfinfo.channels; + float *ibuf = new float[channels * ibs]; + float *obuf = new float[channels * obs]; + + double **prebuf = new double*[channels]; + double **postbuf = new double*[channels]; + vector resamplers; // one per channel + + for (int c = 0; c < channels; ++c) { + prebuf[c] = new double[ibs]; + postbuf[c] = new double[obs]; + resamplers.push_back(new Resampler(sourceRate, targetRate)); + } + + int latency = resamplers[0]->getLatency(); + int n = 0; + + while (true) { + + int count = sf_readf_float(sndfile, ibuf, ibs); + if (count <= 0) break; + + int rcount = 0; + int ocount = 0; + + for (int c = 0; c < channels; ++c) { + + for (int i = 0; i < count; ++i) { + prebuf[c][i] = ibuf[i * channels + c]; + } + + rcount = resamplers[c]->process(prebuf[c], postbuf[c], count); + + if (n + rcount > latency) { + int off = 0; + if (latency > n) { + off = latency - n; + } + for (int i = off; i < rcount; ++i) { + obuf[(i - off) * channels + c] = postbuf[c][i]; + } + ocount = rcount - off; + } + } + + if (ocount > 0) { + sf_writef_float(sndfileOut, obuf, ocount); + } + + n += rcount; + } + + //!!! todo: tail (latency) + + sf_close(sndfile); + sf_close(sndfileOut); + + for (int c = 0; c < channels; ++c) { + delete[] prebuf[c]; + delete[] postbuf[c]; + delete resamplers[c]; + } + + delete[] prebuf; + delete[] postbuf; + delete[] ibuf; + delete[] obuf; + + timeval etv; + (void)gettimeofday(&etv, 0); + + etv.tv_sec -= tv.tv_sec; + if (etv.tv_usec < tv.tv_usec) { + etv.tv_usec += 1000000; + etv.tv_sec -= 1; + } + etv.tv_usec -= tv.tv_usec; + + double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0); + cerr << "elapsed time: " << sec << " sec, in frames/sec: " << sfinfo.frames/sec << ", out frames/sec: " << n/sec << endl; + + return 0; +} + + + + + +