Chris@12
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@12
|
2
|
Chris@28
|
3 #ifdef DECIMATE_B
|
Chris@28
|
4 #include "qm-dsp/dsp/rateconversion/DecimatorB.h"
|
Chris@28
|
5 #else
|
Chris@15
|
6 #include "qm-dsp/dsp/rateconversion/Decimator.h"
|
Chris@28
|
7 #endif
|
Chris@28
|
8
|
Chris@15
|
9 #include "qm-dsp/maths/MathUtilities.h"
|
Chris@12
|
10
|
Chris@12
|
11 #include <iostream>
|
Chris@12
|
12 #include <sndfile.h>
|
Chris@12
|
13 #include <time.h>
|
Chris@12
|
14 #include <string>
|
Chris@12
|
15 #include <cstdlib>
|
Chris@12
|
16 #include <cstring>
|
Chris@12
|
17 #include <cmath>
|
Chris@12
|
18 #include <getopt.h>
|
Chris@12
|
19 #include <unistd.h>
|
Chris@12
|
20 #include <sys/time.h>
|
Chris@12
|
21
|
Chris@12
|
22 using namespace std;
|
Chris@12
|
23
|
Chris@12
|
24 int main(int argc, char **argv)
|
Chris@12
|
25 {
|
Chris@15
|
26 int factor = 0;
|
Chris@12
|
27 bool version = false;
|
Chris@12
|
28 bool help = false;
|
Chris@12
|
29 int c = 0;
|
Chris@12
|
30
|
Chris@12
|
31 while (1) {
|
Chris@12
|
32 int optionIndex = 0;
|
Chris@12
|
33
|
Chris@12
|
34 static struct option longOpts[] = {
|
Chris@12
|
35 { "help", 0, 0, 'h' },
|
Chris@12
|
36 { "version", 0, 0, 'V' },
|
Chris@15
|
37 { "by", 1, 0, 'b' },
|
Chris@12
|
38 { 0, 0, 0, 0 }
|
Chris@12
|
39 };
|
Chris@12
|
40
|
Chris@12
|
41 c = getopt_long(argc, argv,
|
Chris@15
|
42 "b:hV",
|
Chris@12
|
43 longOpts, &optionIndex);
|
Chris@12
|
44 if (c == -1) break;
|
Chris@12
|
45
|
Chris@12
|
46 switch (c) {
|
Chris@12
|
47 case 'h': help = true; break;
|
Chris@12
|
48 case 'V': version = true; break;
|
Chris@15
|
49 case 'b': factor = atoi(optarg); break;
|
Chris@12
|
50 default: help = true; break;
|
Chris@12
|
51 }
|
Chris@12
|
52 }
|
Chris@12
|
53
|
Chris@12
|
54 if (version) {
|
Chris@15
|
55 cerr << "decimate v0.1" << endl; //!!!
|
Chris@12
|
56 return 0;
|
Chris@12
|
57 }
|
Chris@12
|
58
|
Chris@15
|
59 if (help || factor == 0 || optind + 2 != argc) {
|
Chris@12
|
60 cerr << endl;
|
Chris@15
|
61 cerr << "usage: " << argv[0] << " --by <factor> <infile.wav> <outfile.wav>" << endl;
|
Chris@12
|
62 cerr << endl;
|
Chris@12
|
63 return 2;
|
Chris@12
|
64 }
|
Chris@15
|
65
|
Chris@15
|
66 if (!MathUtilities::isPowerOfTwo(factor)) {
|
Chris@15
|
67 cerr << "ERROR: Factor must be a power of two" << endl;
|
Chris@15
|
68 return 1;
|
Chris@15
|
69 }
|
Chris@28
|
70 #ifdef DECIMATE_B
|
Chris@28
|
71 if (factor < 2) {
|
Chris@28
|
72 cerr << "ERROR: Factor must be at least 2" << endl;
|
Chris@28
|
73 return 1;
|
Chris@28
|
74 }
|
Chris@28
|
75 #else
|
Chris@15
|
76 if (factor < 2 || factor > Decimator::getHighestSupportedFactor()) {
|
Chris@15
|
77 cerr << "ERROR: Only factors between 2 and "
|
Chris@15
|
78 << Decimator::getHighestSupportedFactor()
|
Chris@15
|
79 << " inclusive are supported" <<endl;
|
Chris@15
|
80 return 1;
|
Chris@15
|
81 }
|
Chris@28
|
82 #endif
|
Chris@12
|
83
|
Chris@12
|
84 timeval tv;
|
Chris@12
|
85 (void)gettimeofday(&tv, 0);
|
Chris@12
|
86
|
Chris@12
|
87 char *fileName = strdup(argv[optind++]);
|
Chris@12
|
88 char *fileNameOut = strdup(argv[optind++]);
|
Chris@12
|
89
|
Chris@12
|
90 SNDFILE *sndfile;
|
Chris@12
|
91 SNDFILE *sndfileOut;
|
Chris@12
|
92 SF_INFO sfinfo;
|
Chris@12
|
93 SF_INFO sfinfoOut;
|
Chris@12
|
94 memset(&sfinfo, 0, sizeof(SF_INFO));
|
Chris@12
|
95
|
Chris@12
|
96 sndfile = sf_open(fileName, SFM_READ, &sfinfo);
|
Chris@12
|
97 if (!sndfile) {
|
Chris@12
|
98 cerr << "ERROR: Failed to open input file \"" << fileName << "\": "
|
Chris@12
|
99 << sf_strerror(sndfile) << endl;
|
Chris@12
|
100 return 1;
|
Chris@12
|
101 }
|
Chris@12
|
102
|
Chris@12
|
103 int sourceRate = sfinfo.samplerate;
|
Chris@15
|
104 int targetRate = sourceRate / factor;
|
Chris@15
|
105
|
Chris@17
|
106 cerr << "Decimating by factor " << factor << ", from " << sourceRate
|
Chris@17
|
107 << " to " << targetRate << " Hz" << endl;
|
Chris@17
|
108
|
Chris@15
|
109 if (sourceRate % factor != 0) {
|
Chris@15
|
110 cerr << "WARNING: Decimation factor " << factor << " from source rate "
|
Chris@15
|
111 << sourceRate << " gives non-integral target rate (not supported "
|
Chris@15
|
112 << "by output file format), recording as " << targetRate << endl;
|
Chris@15
|
113 }
|
Chris@12
|
114
|
Chris@12
|
115 sfinfoOut.channels = sfinfo.channels;
|
Chris@12
|
116 sfinfoOut.format = sfinfo.format;
|
Chris@15
|
117 sfinfoOut.frames = sfinfo.frames / factor;
|
Chris@12
|
118 sfinfoOut.samplerate = targetRate;
|
Chris@12
|
119 sfinfoOut.sections = sfinfo.sections;
|
Chris@12
|
120 sfinfoOut.seekable = sfinfo.seekable;
|
Chris@12
|
121
|
Chris@12
|
122 sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut);
|
Chris@12
|
123 if (!sndfileOut) {
|
Chris@12
|
124 cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: "
|
Chris@12
|
125 << sf_strerror(sndfileOut) << endl;
|
Chris@12
|
126 return 1;
|
Chris@12
|
127 }
|
Chris@13
|
128
|
Chris@13
|
129 int channels = sfinfo.channels;
|
Chris@15
|
130 int ibs = 1024;
|
Chris@15
|
131 int obs = ibs / factor;
|
Chris@13
|
132
|
Chris@28
|
133 #ifdef DECIMATE_B
|
Chris@28
|
134 vector<DecimatorB *> decimators; // one per channel
|
Chris@28
|
135 for (int c = 0; c < channels; ++c) {
|
Chris@28
|
136 decimators.push_back(new DecimatorB(ibs, factor));
|
Chris@28
|
137 }
|
Chris@28
|
138 #else
|
Chris@28
|
139 vector<Decimator *> decimators; // one per channel
|
Chris@13
|
140 for (int c = 0; c < channels; ++c) {
|
Chris@15
|
141 decimators.push_back(new Decimator(ibs, factor));
|
Chris@13
|
142 }
|
Chris@28
|
143 #endif
|
Chris@13
|
144
|
Chris@12
|
145 float *ibuf = new float[channels * ibs];
|
Chris@12
|
146 float *obuf = new float[channels * obs];
|
Chris@12
|
147
|
Chris@12
|
148 double **prebuf = new double*[channels];
|
Chris@12
|
149 double **postbuf = new double*[channels];
|
Chris@12
|
150
|
Chris@12
|
151 for (int c = 0; c < channels; ++c) {
|
Chris@12
|
152 prebuf[c] = new double[ibs];
|
Chris@12
|
153 postbuf[c] = new double[obs];
|
Chris@12
|
154 }
|
Chris@12
|
155
|
Chris@12
|
156 int n = 0;
|
Chris@12
|
157
|
Chris@12
|
158 while (true) {
|
Chris@12
|
159
|
Chris@12
|
160 int count = sf_readf_float(sndfile, ibuf, ibs);
|
Chris@12
|
161 if (count <= 0) break;
|
Chris@12
|
162
|
Chris@12
|
163 for (int c = 0; c < channels; ++c) {
|
Chris@12
|
164
|
Chris@12
|
165 for (int i = 0; i < count; ++i) {
|
Chris@12
|
166 prebuf[c][i] = ibuf[i * channels + c];
|
Chris@12
|
167 }
|
Chris@15
|
168 for (int i = count; i < ibs; ++i) {
|
Chris@15
|
169 prebuf[c][i] = 0.0;
|
Chris@15
|
170 }
|
Chris@12
|
171
|
Chris@15
|
172 decimators[c]->process(prebuf[c], postbuf[c]);
|
Chris@12
|
173
|
Chris@15
|
174 for (int i = 0; i < obs; ++i) {
|
Chris@15
|
175 obuf[i * channels + c] = postbuf[c][i];
|
Chris@15
|
176 }
|
Chris@12
|
177 }
|
Chris@12
|
178
|
Chris@15
|
179 int ocount = obs;
|
Chris@15
|
180 if (count < ibs) {
|
Chris@15
|
181 ocount = count / factor;
|
Chris@15
|
182 }
|
Chris@15
|
183
|
Chris@15
|
184 sf_writef_float(sndfileOut, obuf, ocount);
|
Chris@15
|
185
|
Chris@15
|
186 n += ocount;
|
Chris@12
|
187 }
|
Chris@12
|
188
|
Chris@12
|
189 sf_close(sndfile);
|
Chris@12
|
190 sf_close(sndfileOut);
|
Chris@12
|
191
|
Chris@12
|
192 for (int c = 0; c < channels; ++c) {
|
Chris@12
|
193 delete[] prebuf[c];
|
Chris@12
|
194 delete[] postbuf[c];
|
Chris@15
|
195 delete decimators[c];
|
Chris@12
|
196 }
|
Chris@12
|
197
|
Chris@12
|
198 delete[] prebuf;
|
Chris@12
|
199 delete[] postbuf;
|
Chris@12
|
200 delete[] ibuf;
|
Chris@12
|
201 delete[] obuf;
|
Chris@12
|
202
|
Chris@12
|
203 timeval etv;
|
Chris@12
|
204 (void)gettimeofday(&etv, 0);
|
Chris@12
|
205
|
Chris@12
|
206 etv.tv_sec -= tv.tv_sec;
|
Chris@12
|
207 if (etv.tv_usec < tv.tv_usec) {
|
Chris@12
|
208 etv.tv_usec += 1000000;
|
Chris@12
|
209 etv.tv_sec -= 1;
|
Chris@12
|
210 }
|
Chris@12
|
211 etv.tv_usec -= tv.tv_usec;
|
Chris@12
|
212
|
Chris@12
|
213 double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
|
Chris@17
|
214 cerr << sfinfo.frames << " frames in, " << n << " frames out"
|
Chris@17
|
215 << ", nominal factor " << (1.0/factor)
|
Chris@17
|
216 << ", actual " << double(n)/double(sfinfo.frames)
|
Chris@17
|
217 << endl << "Elapsed time: " << sec << " sec, in frames/sec: "
|
Chris@17
|
218 << int(sfinfo.frames/sec) << ", out frames/sec: " << int(n/sec) << endl;
|
Chris@12
|
219
|
Chris@12
|
220 return 0;
|
Chris@12
|
221 }
|
Chris@12
|
222
|
Chris@12
|
223
|
Chris@12
|
224
|
Chris@12
|
225
|
Chris@12
|
226
|
Chris@12
|
227
|