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