To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
The primary repository for this project is hosted at https://github.com/cannam/constant-q-cpp/ .
This repository is a read-only copy which is updated automatically every hour.
root / test / processfile.cpp
History | View | Annotate | Download (7.76 KB)
| 1 |
|
|---|---|
| 2 |
#include "ConstantQ.h" |
| 3 |
#include "CQInverse.h" |
| 4 |
|
| 5 |
#include <sndfile.h> |
| 6 |
|
| 7 |
#include <iostream> |
| 8 |
|
| 9 |
using std::vector;
|
| 10 |
using std::cerr;
|
| 11 |
using std::endl;
|
| 12 |
|
| 13 |
#include <cstring> |
| 14 |
|
| 15 |
#include <getopt.h> |
| 16 |
#include <unistd.h> |
| 17 |
#include <sys/time.h> |
| 18 |
#include <cstdlib> |
| 19 |
|
| 20 |
int main(int argc, char **argv) |
| 21 |
{
|
| 22 |
double maxFreq = 0; |
| 23 |
double minFreq = 0; |
| 24 |
int bpo = 0; |
| 25 |
bool help = false; |
| 26 |
|
| 27 |
int c;
|
| 28 |
|
| 29 |
while (1) { |
| 30 |
int optionIndex = 0; |
| 31 |
|
| 32 |
static struct option longOpts[] = { |
| 33 |
{ "help", 0, 0, 'h', },
|
| 34 |
{ "maxfreq", 1, 0, 'x', },
|
| 35 |
{ "minfreq", 1, 0, 'n', },
|
| 36 |
{ "bpo", 1, 0, 'b' },
|
| 37 |
{ 0, 0, 0, 0 },
|
| 38 |
}; |
| 39 |
|
| 40 |
c = getopt_long(argc, argv, |
| 41 |
"hx:n:b:",
|
| 42 |
longOpts, &optionIndex); |
| 43 |
if (c == -1) break; |
| 44 |
|
| 45 |
switch (c) {
|
| 46 |
case 'h': help = true; break; |
| 47 |
case 'x': maxFreq = atof(optarg); break; |
| 48 |
case 'n': minFreq = atof(optarg); break; |
| 49 |
case 'b': bpo = atoi(optarg); break; |
| 50 |
default: help = true; break; |
| 51 |
} |
| 52 |
} |
| 53 |
|
| 54 |
if (help || (optind + 2 != argc && optind + 3 != argc)) { |
| 55 |
cerr << endl; |
| 56 |
cerr << "Usage: " << argv[0] << " [options] infile.wav outfile.wav [differencefile.wav]" << endl; |
| 57 |
cerr << endl; |
| 58 |
cerr << "Options:" << endl;
|
| 59 |
cerr << " -x<X>, --maxfreq <X> Maximum frequency (default = sample rate / 3)" << endl;
|
| 60 |
cerr << " -n<X>, --minfreq <X> Minimum frequency (default = 100, actual min may vary)" << endl;
|
| 61 |
cerr << " -b<X>, --bpo <X> Bins per octave (default = 60)" << endl;
|
| 62 |
cerr << " -h, --help Print this help" << endl;
|
| 63 |
cerr << endl; |
| 64 |
cerr << "This rather useless program simply performs a forward Constant-Q transform with" << endl;
|
| 65 |
cerr << "the requested parameters, followed by its inverse, and writes the result to the" << endl;
|
| 66 |
cerr << "output file. If a diff file name is provided, the difference between input and" << endl;
|
| 67 |
cerr << "output signals is also written to that. All this accomplishes is to produce a" << endl;
|
| 68 |
cerr << "signal that approximates the input: it's intended for test purposes only." << endl;
|
| 69 |
cerr << endl; |
| 70 |
cerr << "(Want to calculate and obtain a Constant-Q spectrogram? Use the CQVamp plugin" << endl;
|
| 71 |
cerr << "in a Vamp plugin host.)" << endl;
|
| 72 |
cerr << endl; |
| 73 |
return 2; |
| 74 |
} |
| 75 |
|
| 76 |
char *fileName = strdup(argv[optind++]);
|
| 77 |
char *fileNameOut = strdup(argv[optind++]);
|
| 78 |
char *diffFileName = (optind < argc ? strdup(argv[optind++]) : 0); |
| 79 |
bool doDiff = (diffFileName != 0); |
| 80 |
|
| 81 |
SNDFILE *sndfile; |
| 82 |
SNDFILE *sndfileOut; |
| 83 |
SNDFILE *sndDiffFile = 0;
|
| 84 |
SF_INFO sfinfo; |
| 85 |
SF_INFO sfinfoOut; |
| 86 |
SF_INFO sfinfoDiff; |
| 87 |
memset(&sfinfo, 0, sizeof(SF_INFO)); |
| 88 |
|
| 89 |
sndfile = sf_open(fileName, SFM_READ, &sfinfo); |
| 90 |
if (!sndfile) {
|
| 91 |
cerr << "ERROR: Failed to open input file \"" << fileName << "\": " |
| 92 |
<< sf_strerror(sndfile) << endl; |
| 93 |
return 1; |
| 94 |
} |
| 95 |
|
| 96 |
sfinfoOut.channels = 1;
|
| 97 |
sfinfoOut.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; |
| 98 |
sfinfoOut.frames = sfinfo.frames; |
| 99 |
sfinfoOut.samplerate = sfinfo.samplerate; |
| 100 |
sfinfoOut.sections = sfinfo.sections; |
| 101 |
sfinfoOut.seekable = sfinfo.seekable; |
| 102 |
|
| 103 |
sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ; |
| 104 |
if (!sndfileOut) {
|
| 105 |
cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: " |
| 106 |
<< sf_strerror(sndfileOut) << endl; |
| 107 |
return 1; |
| 108 |
} |
| 109 |
|
| 110 |
if (doDiff) {
|
| 111 |
sfinfoDiff = sfinfoOut; |
| 112 |
sndDiffFile = sf_open(diffFileName, SFM_WRITE, &sfinfoDiff); |
| 113 |
if (!sndDiffFile) {
|
| 114 |
cerr << "ERROR: Failed to open diff output file \"" << diffFileName << "\" for writing: " |
| 115 |
<< sf_strerror(sndDiffFile) << endl; |
| 116 |
return 1; |
| 117 |
} |
| 118 |
} |
| 119 |
|
| 120 |
int ibs = 1024; |
| 121 |
int channels = sfinfo.channels;
|
| 122 |
float *fbuf = new float[channels * ibs]; |
| 123 |
|
| 124 |
if (maxFreq == 0.0) maxFreq = sfinfo.samplerate / 3; |
| 125 |
if (minFreq == 0.0) minFreq = 100; |
| 126 |
if (bpo == 0) bpo = 60; |
| 127 |
|
| 128 |
CQParameters params(sfinfo.samplerate, minFreq, maxFreq, bpo); |
| 129 |
ConstantQ cq(params); |
| 130 |
CQInverse cqi(params); |
| 131 |
|
| 132 |
cerr << "max freq = " << cq.getMaxFrequency() << ", min freq = " |
| 133 |
<< cq.getMinFrequency() << ", octaves = " << cq.getOctaves() << endl;
|
| 134 |
|
| 135 |
cerr << "octave boundaries: ";
|
| 136 |
for (int i = 0; i < cq.getOctaves(); ++i) { |
| 137 |
cerr << cq.getMaxFrequency() / pow(2, i) << " "; |
| 138 |
} |
| 139 |
cerr << endl; |
| 140 |
|
| 141 |
int inframe = 0; |
| 142 |
int outframe = 0; |
| 143 |
int latency = cq.getLatency() + cqi.getLatency();
|
| 144 |
|
| 145 |
vector<double> buffer;
|
| 146 |
|
| 147 |
double maxdiff = 0.0; |
| 148 |
int maxdiffidx = 0; |
| 149 |
|
| 150 |
cerr << "forward latency = " << cq.getLatency() << ", inverse latency = " |
| 151 |
<< cqi.getLatency() << ", total = " << latency << endl;
|
| 152 |
|
| 153 |
timeval tv; |
| 154 |
(void)gettimeofday(&tv, 0); |
| 155 |
|
| 156 |
while (inframe < sfinfo.frames) {
|
| 157 |
|
| 158 |
int count = -1; |
| 159 |
|
| 160 |
if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) { |
| 161 |
break;
|
| 162 |
} |
| 163 |
|
| 164 |
vector<double> cqin;
|
| 165 |
for (int i = 0; i < count; ++i) { |
| 166 |
double v = fbuf[i * channels];
|
| 167 |
if (channels > 1) { |
| 168 |
for (int c = 1; c < channels; ++c) { |
| 169 |
v += fbuf[i * channels + c]; |
| 170 |
} |
| 171 |
v /= channels; |
| 172 |
} |
| 173 |
cqin.push_back(v); |
| 174 |
} |
| 175 |
|
| 176 |
if (doDiff) {
|
| 177 |
buffer.insert(buffer.end(), cqin.begin(), cqin.end()); |
| 178 |
} |
| 179 |
|
| 180 |
vector<double> cqout = cqi.process(cq.process(cqin));
|
| 181 |
|
| 182 |
for (int i = 0; i < int(cqout.size()); ++i) { |
| 183 |
if (cqout[i] > 1.0) cqout[i] = 1.0; |
| 184 |
if (cqout[i] < -1.0) cqout[i] = -1.0; |
| 185 |
} |
| 186 |
|
| 187 |
if (outframe >= latency) {
|
| 188 |
|
| 189 |
sf_writef_double(sndfileOut, |
| 190 |
cqout.data(), |
| 191 |
cqout.size()); |
| 192 |
|
| 193 |
} else if (outframe + (int)cqout.size() >= latency) { |
| 194 |
|
| 195 |
int offset = latency - outframe;
|
| 196 |
sf_writef_double(sndfileOut, |
| 197 |
cqout.data() + offset, |
| 198 |
cqout.size() - offset); |
| 199 |
} |
| 200 |
|
| 201 |
if (doDiff) {
|
| 202 |
for (int i = 0; i < (int)cqout.size(); ++i) { |
| 203 |
if (outframe + i >= latency) {
|
| 204 |
int dframe = outframe + i - latency;
|
| 205 |
if (dframe >= (int)buffer.size()) cqout[i] = 0; |
| 206 |
else cqout[i] -= buffer[dframe];
|
| 207 |
if (fabs(cqout[i]) > maxdiff &&
|
| 208 |
dframe > sfinfo.samplerate && // ignore first/last sec
|
| 209 |
dframe + sfinfo.samplerate < sfinfo.frames) {
|
| 210 |
maxdiff = fabs(cqout[i]); |
| 211 |
maxdiffidx = dframe; |
| 212 |
} |
| 213 |
} |
| 214 |
} |
| 215 |
|
| 216 |
if (outframe >= latency) {
|
| 217 |
|
| 218 |
sf_writef_double(sndDiffFile, |
| 219 |
cqout.data(), |
| 220 |
cqout.size()); |
| 221 |
|
| 222 |
} else if (outframe + (int)cqout.size() >= latency) { |
| 223 |
|
| 224 |
int offset = latency - outframe;
|
| 225 |
sf_writef_double(sndDiffFile, |
| 226 |
cqout.data() + offset, |
| 227 |
cqout.size() - offset); |
| 228 |
} |
| 229 |
} |
| 230 |
|
| 231 |
inframe += count; |
| 232 |
outframe += cqout.size(); |
| 233 |
} |
| 234 |
|
| 235 |
vector<double> r = cqi.process(cq.getRemainingOutput());
|
| 236 |
vector<double> r2 = cqi.getRemainingOutput();
|
| 237 |
|
| 238 |
r.insert(r.end(), r2.begin(), r2.end()); |
| 239 |
|
| 240 |
for (int i = 0; i < int(r.size()); ++i) { |
| 241 |
if (r[i] > 1.0) r[i] = 1.0; |
| 242 |
if (r[i] < -1.0) r[i] = -1.0; |
| 243 |
} |
| 244 |
|
| 245 |
sf_writef_double(sndfileOut, r.data(), r.size()); |
| 246 |
if (doDiff) {
|
| 247 |
for (int i = 0; i < (int)r.size(); ++i) { |
| 248 |
if (outframe + i >= latency) {
|
| 249 |
int dframe = outframe + i - latency;
|
| 250 |
if (dframe >= (int)buffer.size()) r[i] = 0; |
| 251 |
else r[i] -= buffer[dframe];
|
| 252 |
if (fabs(r[i]) > maxdiff &&
|
| 253 |
dframe > sfinfo.samplerate && // ignore first/last sec
|
| 254 |
dframe + sfinfo.samplerate < sfinfo.frames) {
|
| 255 |
maxdiff = fabs(r[i]); |
| 256 |
maxdiffidx = dframe; |
| 257 |
} |
| 258 |
} |
| 259 |
} |
| 260 |
sf_writef_double(sndDiffFile, r.data(), r.size()); |
| 261 |
} |
| 262 |
outframe += r.size(); |
| 263 |
|
| 264 |
sf_close(sndfile); |
| 265 |
sf_close(sndfileOut); |
| 266 |
|
| 267 |
if (doDiff) {
|
| 268 |
sf_close(sndDiffFile); |
| 269 |
} |
| 270 |
|
| 271 |
cerr << "in: " << inframe << ", out: " << outframe - latency << endl; |
| 272 |
|
| 273 |
if (doDiff) {
|
| 274 |
double db = 10 * log10(maxdiff); |
| 275 |
cerr << "max diff [excluding first and last second of audio] is "
|
| 276 |
<< maxdiff << " (" << db << " dBFS)" |
| 277 |
<< " at sample index " << maxdiffidx << endl;
|
| 278 |
} |
| 279 |
|
| 280 |
timeval etv; |
| 281 |
(void)gettimeofday(&etv, 0); |
| 282 |
|
| 283 |
etv.tv_sec -= tv.tv_sec; |
| 284 |
if (etv.tv_usec < tv.tv_usec) {
|
| 285 |
etv.tv_usec += 1000000;
|
| 286 |
etv.tv_sec -= 1;
|
| 287 |
} |
| 288 |
etv.tv_usec -= tv.tv_usec; |
| 289 |
|
| 290 |
double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0); |
| 291 |
cerr << "elapsed time (not counting init): " << sec << " sec, frames/sec at input: " << inframe/sec << endl; |
| 292 |
|
| 293 |
return 0; |
| 294 |
} |
| 295 |
|