Mercurial > hg > silvet
diff constant-q-cpp/test/processfile.cpp @ 366:5d0a2ebb4d17
Bring dependent libraries in to repo
author | Chris Cannam |
---|---|
date | Fri, 24 Jun 2016 14:47:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/constant-q-cpp/test/processfile.cpp Fri Jun 24 14:47:45 2016 +0100 @@ -0,0 +1,295 @@ + +#include "ConstantQ.h" +#include "CQInverse.h" + +#include <sndfile.h> + +#include <iostream> + +using std::vector; +using std::cerr; +using std::endl; + +#include <cstring> + +#include <getopt.h> +#include <unistd.h> +#include <sys/time.h> +#include <cstdlib> + +int main(int argc, char **argv) +{ + double maxFreq = 0; + double minFreq = 0; + int bpo = 0; + bool help = false; + + int c; + + while (1) { + int optionIndex = 0; + + static struct option longOpts[] = { + { "help", 0, 0, 'h', }, + { "maxfreq", 1, 0, 'x', }, + { "minfreq", 1, 0, 'n', }, + { "bpo", 1, 0, 'b' }, + { 0, 0, 0, 0 }, + }; + + c = getopt_long(argc, argv, + "hx:n:b:", + longOpts, &optionIndex); + if (c == -1) break; + + switch (c) { + case 'h': help = true; break; + case 'x': maxFreq = atof(optarg); break; + case 'n': minFreq = atof(optarg); break; + case 'b': bpo = atoi(optarg); break; + default: help = true; break; + } + } + + if (help || (optind + 2 != argc && optind + 3 != argc)) { + cerr << endl; + cerr << "Usage: " << argv[0] << " [options] infile.wav outfile.wav [differencefile.wav]" << endl; + cerr << endl; + cerr << "Options:" << endl; + cerr << " -x<X>, --maxfreq <X> Maximum frequency (default = sample rate / 3)" << endl; + cerr << " -n<X>, --minfreq <X> Minimum frequency (default = 100, actual min may vary)" << endl; + cerr << " -b<X>, --bpo <X> Bins per octave (default = 60)" << endl; + cerr << " -h, --help Print this help" << endl; + cerr << endl; + cerr << "This rather useless program simply performs a forward Constant-Q transform with" << endl; + cerr << "the requested parameters, followed by its inverse, and writes the result to the" << endl; + cerr << "output file. If a diff file name is provided, the difference between input and" << endl; + cerr << "output signals is also written to that. All this accomplishes is to produce a" << endl; + cerr << "signal that approximates the input: it's intended for test purposes only." << endl; + cerr << endl; + cerr << "(Want to calculate and obtain a Constant-Q spectrogram? Use the CQVamp plugin" << endl; + cerr << "in a Vamp plugin host.)" << endl; + cerr << endl; + return 2; + } + + char *fileName = strdup(argv[optind++]); + char *fileNameOut = strdup(argv[optind++]); + char *diffFileName = (optind < argc ? strdup(argv[optind++]) : 0); + bool doDiff = (diffFileName != 0); + + SNDFILE *sndfile; + SNDFILE *sndfileOut; + SNDFILE *sndDiffFile = 0; + SF_INFO sfinfo; + SF_INFO sfinfoOut; + SF_INFO sfinfoDiff; + 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; + } + + sfinfoOut.channels = 1; + sfinfoOut.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + sfinfoOut.frames = sfinfo.frames; + sfinfoOut.samplerate = sfinfo.samplerate; + 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; + } + + if (doDiff) { + sfinfoDiff = sfinfoOut; + sndDiffFile = sf_open(diffFileName, SFM_WRITE, &sfinfoDiff); + if (!sndDiffFile) { + cerr << "ERROR: Failed to open diff output file \"" << diffFileName << "\" for writing: " + << sf_strerror(sndDiffFile) << endl; + return 1; + } + } + + int ibs = 1024; + int channels = sfinfo.channels; + float *fbuf = new float[channels * ibs]; + + if (maxFreq == 0.0) maxFreq = sfinfo.samplerate / 3; + if (minFreq == 0.0) minFreq = 100; + if (bpo == 0) bpo = 60; + + CQParameters params(sfinfo.samplerate, minFreq, maxFreq, bpo); + ConstantQ cq(params); + CQInverse cqi(params); + + cerr << "max freq = " << cq.getMaxFrequency() << ", min freq = " + << cq.getMinFrequency() << ", octaves = " << cq.getOctaves() << endl; + + cerr << "octave boundaries: "; + for (int i = 0; i < cq.getOctaves(); ++i) { + cerr << cq.getMaxFrequency() / pow(2, i) << " "; + } + cerr << endl; + + int inframe = 0; + int outframe = 0; + int latency = cq.getLatency() + cqi.getLatency(); + + vector<double> buffer; + + double maxdiff = 0.0; + int maxdiffidx = 0; + + cerr << "forward latency = " << cq.getLatency() << ", inverse latency = " + << cqi.getLatency() << ", total = " << latency << endl; + + timeval tv; + (void)gettimeofday(&tv, 0); + + while (inframe < sfinfo.frames) { + + int count = -1; + + if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) { + break; + } + + vector<double> cqin; + for (int i = 0; i < count; ++i) { + double v = fbuf[i * channels]; + if (channels > 1) { + for (int c = 1; c < channels; ++c) { + v += fbuf[i * channels + c]; + } + v /= channels; + } + cqin.push_back(v); + } + + if (doDiff) { + buffer.insert(buffer.end(), cqin.begin(), cqin.end()); + } + + vector<double> cqout = cqi.process(cq.process(cqin)); + + for (int i = 0; i < int(cqout.size()); ++i) { + if (cqout[i] > 1.0) cqout[i] = 1.0; + if (cqout[i] < -1.0) cqout[i] = -1.0; + } + + if (outframe >= latency) { + + sf_writef_double(sndfileOut, + cqout.data(), + cqout.size()); + + } else if (outframe + (int)cqout.size() >= latency) { + + int offset = latency - outframe; + sf_writef_double(sndfileOut, + cqout.data() + offset, + cqout.size() - offset); + } + + if (doDiff) { + for (int i = 0; i < (int)cqout.size(); ++i) { + if (outframe + i >= latency) { + int dframe = outframe + i - latency; + if (dframe >= (int)buffer.size()) cqout[i] = 0; + else cqout[i] -= buffer[dframe]; + if (fabs(cqout[i]) > maxdiff && + dframe > sfinfo.samplerate && // ignore first/last sec + dframe + sfinfo.samplerate < sfinfo.frames) { + maxdiff = fabs(cqout[i]); + maxdiffidx = dframe; + } + } + } + + if (outframe >= latency) { + + sf_writef_double(sndDiffFile, + cqout.data(), + cqout.size()); + + } else if (outframe + (int)cqout.size() >= latency) { + + int offset = latency - outframe; + sf_writef_double(sndDiffFile, + cqout.data() + offset, + cqout.size() - offset); + } + } + + inframe += count; + outframe += cqout.size(); + } + + vector<double> r = cqi.process(cq.getRemainingOutput()); + vector<double> r2 = cqi.getRemainingOutput(); + + r.insert(r.end(), r2.begin(), r2.end()); + + for (int i = 0; i < int(r.size()); ++i) { + if (r[i] > 1.0) r[i] = 1.0; + if (r[i] < -1.0) r[i] = -1.0; + } + + sf_writef_double(sndfileOut, r.data(), r.size()); + if (doDiff) { + for (int i = 0; i < (int)r.size(); ++i) { + if (outframe + i >= latency) { + int dframe = outframe + i - latency; + if (dframe >= (int)buffer.size()) r[i] = 0; + else r[i] -= buffer[dframe]; + if (fabs(r[i]) > maxdiff && + dframe > sfinfo.samplerate && // ignore first/last sec + dframe + sfinfo.samplerate < sfinfo.frames) { + maxdiff = fabs(r[i]); + maxdiffidx = dframe; + } + } + } + sf_writef_double(sndDiffFile, r.data(), r.size()); + } + outframe += r.size(); + + sf_close(sndfile); + sf_close(sndfileOut); + + if (doDiff) { + sf_close(sndDiffFile); + } + + cerr << "in: " << inframe << ", out: " << outframe - latency << endl; + + if (doDiff) { + double db = 10 * log10(maxdiff); + cerr << "max diff [excluding first and last second of audio] is " + << maxdiff << " (" << db << " dBFS)" + << " at sample index " << maxdiffidx << endl; + } + + 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 (not counting init): " << sec << " sec, frames/sec at input: " << inframe/sec << endl; + + return 0; +} +