changeset 12:1879a2997ebf

Add resampler command-line program
author Chris Cannam
date Fri, 18 Oct 2013 12:18:10 +0100
parents 381823e25b8a
children 1b95c3a52b45
files garage-resampler/Makefile garage-resampler/resample.cpp
diffstat 2 files changed, 205 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- 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)
--- /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 <iostream>
+#include <sndfile.h>
+#include <time.h>
+#include <string>
+#include <cstdlib>
+#include <cstring>
+#include <cmath>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+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 <rate> <infile.wav> <outfile.wav>" << 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<Resampler *> 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;
+}
+
+
+
+    
+	
+