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