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