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