view qm-dsp-resample/resample.cpp @ 28:69ee50c19c0c tip

Add decimate-b
author Chris Cannam
date Tue, 22 Oct 2013 08:59:42 +0100
parents 20ed07459176
children
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

#include "qm-dsp/dsp/rateconversion/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;
    double snr = 100;
    double bandwidth = 0.02;
    int c = 0;

    while (1) {
        int optionIndex = 0;

        static struct option longOpts[] = {
            { "help",          0, 0, 'h' },
            { "version",       0, 0, 'V' },
            { "to",            1, 0, 't' },
	    { "snr",           1, 0, 's' },
	    { "bandwidth",     1, 0, 'b' },
            { 0, 0, 0, 0 }
        };

        c = getopt_long(argc, argv,
                        "t:hVs:b:",
                        longOpts, &optionIndex);
        if (c == -1) break;

        switch (c) {
        case 'h': help = true; break;
        case 'V': version = true; break;
        case 't': targetRate = atoi(optarg); break;
	case 's': snr = atof(optarg); break;
	case 'b': bandwidth = atof(optarg); break;
        default:  help = true; break;
        }
    }

    if (version) {
	cerr << "resample v0.1" << endl; //!!!
	return 0;
    }

    if (help || targetRate == 0 || optind + 2 != argc) {
	cerr << endl;
	cerr << "usage: " << argv[0] << " --to <rate> [--snr <s>] [--bandwidth <b>] <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 channels = sfinfo.channels;
    vector<Resampler *> resamplers; // one per channel

    cerr << "Resampling from " << sourceRate << " to " << targetRate
	 << " Hz [with snr " << snr << ", bandwidth " << bandwidth << "]"
	 << endl;

    for (int c = 0; c < channels; ++c) {
	resamplers.push_back
	    (new Resampler(sourceRate, targetRate, snr, bandwidth));
    }

    int outputLatency = resamplers[0]->getLatency();
    int inputLatency = int(ceil((double(outputLatency) * sourceRate) / 
				targetRate));
    
    int ibs = 1024;
    if (ibs < inputLatency) {
	ibs = inputLatency;
    }

    int obs = int(ceil((double(ibs) * targetRate) / sourceRate));

    float *ibuf = new float[channels * ibs];
    float *obuf = new float[channels * obs];
    
    double **prebuf = new double*[channels];
    double **postbuf = new double*[channels];

    for (int c = 0; c < channels; ++c) {
	prebuf[c] = new double[ibs];
	postbuf[c] = new double[obs];
    }
    
    int n = 0;
    int rcount = 0;
    int	ocount = 0;
    
    while (true) {

	int count = sf_readf_float(sndfile, ibuf, ibs);
	if (count <= 0) break;
	
	rcount = 0;
	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 > outputLatency) {
		int off = 0;
		if (outputLatency > n) {
		    off = outputLatency - 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;
    }

    // latency tail
    for (int c = 0; c < channels; ++c) {
	for (int i = 0; i < inputLatency; ++i) {
	    prebuf[c][i] = 0.0;
	}
	rcount = resamplers[c]->process(prebuf[c], postbuf[c], inputLatency);
	for (int i = 0; i < rcount; ++i) {
	    obuf[i * channels + c] = postbuf[c][i];
	}
    }
    sf_writef_float(sndfileOut, obuf, outputLatency);
    n += outputLatency;

    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 << sfinfo.frames << " frames in, " << n << " frames out"
	 << ", nominal ratio " << double(targetRate)/double(sourceRate)
	 << ", actual " << double(n)/double(sfinfo.frames)
	 << endl << "Elapsed time: " << sec << " sec, in frames/sec: "
	 << int(sfinfo.frames/sec) << ", out frames/sec: " << int(n/sec) << endl;

    return 0;
}