Mercurial > hg > qm-dsp
comparison dsp/rateconversion/Resampler.cpp @ 145:fe267879e022
Avoid vector reallocation on every reconstructed output sample
author | Chris Cannam |
---|---|
date | Wed, 16 Oct 2013 13:33:18 +0100 |
parents | a4aa37f7af28 |
children | 235b99c7d4ce |
comparison
equal
deleted
inserted
replaced
144:b21e97d570be | 145:fe267879e022 |
---|---|
6 #include "qm-dsp/base/KaiserWindow.h" | 6 #include "qm-dsp/base/KaiserWindow.h" |
7 #include "qm-dsp/base/SincWindow.h" | 7 #include "qm-dsp/base/SincWindow.h" |
8 | 8 |
9 #include <iostream> | 9 #include <iostream> |
10 #include <vector> | 10 #include <vector> |
11 #include <map> | |
11 | 12 |
12 using std::vector; | 13 using std::vector; |
14 using std::map; | |
13 | 15 |
14 //#define DEBUG_RESAMPLER 1 | 16 //#define DEBUG_RESAMPLER 1 |
15 | 17 |
16 Resampler::Resampler(int sourceRate, int targetRate) : | 18 Resampler::Resampler(int sourceRate, int targetRate) : |
17 m_sourceRate(sourceRate), | 19 m_sourceRate(sourceRate), |
40 | 42 |
41 params.length = | 43 params.length = |
42 (params.length % 2 == 0 ? params.length + 1 : params.length); | 44 (params.length % 2 == 0 ? params.length + 1 : params.length); |
43 | 45 |
44 m_filterLength = params.length; | 46 m_filterLength = params.length; |
45 | 47 |
46 std::cerr << "making filter... "; | |
47 KaiserWindow kw(params); | 48 KaiserWindow kw(params); |
48 SincWindow sw(m_filterLength, peakToPole * 2); | 49 SincWindow sw(m_filterLength, peakToPole * 2); |
49 std::cerr << "done" << std::endl; | 50 |
50 | 51 vector<double> filter(m_filterLength, 0.0); |
51 double *filter = new double[m_filterLength]; | |
52 for (int i = 0; i < m_filterLength; ++i) filter[i] = 1.0; | 52 for (int i = 0; i < m_filterLength; ++i) filter[i] = 1.0; |
53 sw.cut(filter); | 53 sw.cut(filter.data()); |
54 kw.cut(filter); | 54 kw.cut(filter.data()); |
55 | 55 |
56 int inputSpacing = m_targetRate / m_gcd; | 56 int inputSpacing = m_targetRate / m_gcd; |
57 int outputSpacing = m_sourceRate / m_gcd; | 57 int outputSpacing = m_sourceRate / m_gcd; |
58 | 58 |
59 #ifdef DEBUG_RESAMPLER | 59 #ifdef DEBUG_RESAMPLER |
82 p.filter.push_back(filter[i * inputSpacing + phase]); | 82 p.filter.push_back(filter[i * inputSpacing + phase]); |
83 } | 83 } |
84 | 84 |
85 m_phaseData[phase] = p; | 85 m_phaseData[phase] = p; |
86 } | 86 } |
87 | |
88 #ifdef DEBUG_RESAMPLER | |
89 for (int phase = 0; phase < inputSpacing; ++phase) { | |
90 std::cerr << "filter for phase " << phase << " of " << inputSpacing << " (with length " << m_phaseData[phase].filter.size() << "):"; | |
91 for (int i = 0; i < m_phaseData[phase].filter.size(); ++i) { | |
92 if (i % 4 == 0) { | |
93 std::cerr << std::endl << i << ": "; | |
94 } | |
95 float v = m_phaseData[phase].filter[i]; | |
96 if (v == 1) { | |
97 std::cerr << " *** " << v << " *** "; | |
98 } else { | |
99 std::cerr << v << " "; | |
100 } | |
101 } | |
102 std::cerr << std::endl; | |
103 } | |
104 #endif | |
105 | |
106 delete[] filter; | |
107 | 87 |
108 // The May implementation of this uses a pull model -- we ask the | 88 // The May implementation of this uses a pull model -- we ask the |
109 // resampler for a certain number of output samples, and it asks | 89 // resampler for a certain number of output samples, and it asks |
110 // its source stream for as many as it needs to calculate | 90 // its source stream for as many as it needs to calculate |
111 // those. This means (among other things) that the source stream | 91 // those. This means (among other things) that the source stream |
123 // latter. (What do other implementations do?) | 103 // latter. (What do other implementations do?) |
124 | 104 |
125 m_phase = (m_filterLength/2) % inputSpacing; | 105 m_phase = (m_filterLength/2) % inputSpacing; |
126 | 106 |
127 m_buffer = vector<double>(m_phaseData[0].filter.size(), 0); | 107 m_buffer = vector<double>(m_phaseData[0].filter.size(), 0); |
108 m_bufferOrigin = 0; | |
128 | 109 |
129 m_latency = | 110 m_latency = |
130 ((m_buffer.size() * inputSpacing) - (m_filterLength/2)) / outputSpacing | 111 ((m_buffer.size() * inputSpacing) - (m_filterLength/2)) / outputSpacing |
131 + m_phase; | 112 + m_phase; |
132 | 113 |
140 Resampler::reconstructOne() | 121 Resampler::reconstructOne() |
141 { | 122 { |
142 Phase &pd = m_phaseData[m_phase]; | 123 Phase &pd = m_phaseData[m_phase]; |
143 double v = 0.0; | 124 double v = 0.0; |
144 int n = pd.filter.size(); | 125 int n = pd.filter.size(); |
145 const double *buf = m_buffer.data(); | 126 const double *const __restrict__ buf = m_buffer.data() + m_bufferOrigin; |
146 const double *filt = pd.filter.data(); | 127 const double *const __restrict__ filt = pd.filter.data(); |
147 for (int i = 0; i < n; ++i) { | 128 for (int i = 0; i < n; ++i) { |
148 v += buf[i] * filt[i]; //!!! gcc can't vectorize: why? | 129 // NB gcc can only vectorize this with -ffast-math |
149 } | 130 v += buf[i] * filt[i]; |
150 m_buffer = vector<double>(m_buffer.begin() + pd.drop, m_buffer.end()); | 131 } |
132 m_bufferOrigin += pd.drop; | |
151 m_phase = pd.nextPhase; | 133 m_phase = pd.nextPhase; |
152 return v; | 134 return v; |
153 } | 135 } |
154 | 136 |
155 int | 137 int |
169 double scaleFactor = 1.0; | 151 double scaleFactor = 1.0; |
170 if (m_targetRate < m_sourceRate) { | 152 if (m_targetRate < m_sourceRate) { |
171 scaleFactor = double(m_targetRate) / double(m_sourceRate); | 153 scaleFactor = double(m_targetRate) / double(m_sourceRate); |
172 } | 154 } |
173 | 155 |
174 std::cerr << "maxout = " << maxout << std::endl; | |
175 | |
176 while (outidx < maxout && | 156 while (outidx < maxout && |
177 m_buffer.size() >= m_phaseData[m_phase].filter.size()) { | 157 m_buffer.size() >= m_phaseData[m_phase].filter.size() + m_bufferOrigin) { |
178 dst[outidx] = scaleFactor * reconstructOne(); | 158 dst[outidx] = scaleFactor * reconstructOne(); |
179 outidx++; | 159 outidx++; |
180 } | 160 } |
161 | |
162 m_buffer = vector<double>(m_buffer.begin() + m_bufferOrigin, m_buffer.end()); | |
163 m_bufferOrigin = 0; | |
181 | 164 |
182 return outidx; | 165 return outidx; |
183 } | 166 } |
184 | 167 |
185 std::vector<double> | 168 std::vector<double> |
193 // padding input samples at the end of input to guarantee at | 176 // padding input samples at the end of input to guarantee at |
194 // *least* the latency's worth of output samples. that is, | 177 // *least* the latency's worth of output samples. that is, |
195 | 178 |
196 int inputPad = int(ceil(double(latency * sourceRate) / targetRate)); | 179 int inputPad = int(ceil(double(latency * sourceRate) / targetRate)); |
197 | 180 |
198 std::cerr << "latency = " << latency << ", inputPad = " << inputPad << std::endl; | |
199 | |
200 // that means we are providing this much input in total: | 181 // that means we are providing this much input in total: |
201 | 182 |
202 int n1 = n + inputPad; | 183 int n1 = n + inputPad; |
203 | 184 |
204 // and obtaining this much output in total: | 185 // and obtaining this much output in total: |
207 | 188 |
208 // in order to return this much output to the user: | 189 // in order to return this much output to the user: |
209 | 190 |
210 int m = int(ceil(double(n * targetRate) / sourceRate)); | 191 int m = int(ceil(double(n * targetRate) / sourceRate)); |
211 | 192 |
212 std::cerr << "n = " << n << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", m = " << m << ", latency = " << latency << ", m1 = " << m1 << ", n1 = " << n1 << ", n1 - n = " << n1 - n << std::endl; | 193 // std::cerr << "n = " << n << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", m = " << m << ", latency = " << latency << ", m1 = " << m1 << ", n1 = " << n1 << ", n1 - n = " << n1 - n << std::endl; |
213 | 194 |
214 vector<double> pad(n1 - n, 0.0); | 195 vector<double> pad(n1 - n, 0.0); |
215 vector<double> out(m1 + 1, 0.0); | 196 vector<double> out(m1 + 1, 0.0); |
216 | 197 |
217 int got = r.process(data, out.data(), n); | 198 int got = r.process(data, out.data(), n); |