MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ phase_vocoder_stretch()

std::vector< double > MayaFlux::Kinesis::Discrete::phase_vocoder_stretch ( std::span< const double >  src,
double  stretch_factor,
uint32_t  window_size = 2048,
uint32_t  analysis_hop = 512 
)

Time-stretch via phase vocoder analysis-synthesis.

Produces output of length approximately src.size() * stretch_factor. Spectral envelopes and pitched content are preserved without aliasing. Sharp transients exhibit mild pre-echo at high stretch ratios (> 2×), which is inherent to the standard phase vocoder algorithm.

Recommended parameters for audio at 48 kHz: window_size = 2048, analysis_hop = 512 (75% overlap).

Parameters
srcInput samples
stretch_factor>1 = slower/longer, <1 = faster/shorter; 1 returns copy
window_sizeFFT frame size (power of 2, >= 64)
analysis_hopAnalysis hop Ha; synthesis hop Hs = Ha * stretch_factor
Returns
Time-stretched output

Definition at line 193 of file Spectral.cpp.

198{
199 if (src.empty() || stretch_factor <= 0.0)
200 return {};
201
202 if (stretch_factor == 1.0)
203 return { src.begin(), src.end() };
204
205 const uint32_t N = window_size;
206 const uint32_t Ha = analysis_hop;
207 const auto Hs = static_cast<uint32_t>(std::round(Ha * stretch_factor));
208 const uint32_t bins = N / 2 + 1;
209
210 const std::vector<double> win = make_hann(N);
211
212 const double omega_factor = k_tau * static_cast<double>(Ha) / static_cast<double>(N);
213
214 const size_t n_frames = (src.size() >= N)
215 ? (src.size() - N) / Ha + 1
216 : 1;
217
218 const size_t out_len = static_cast<size_t>(static_cast<double>(src.size()) * stretch_factor) + N;
219
220 std::vector<double> output(out_len, 0.0);
221 std::vector<double> norm(out_len, 0.0);
222
223 std::vector<double> phase_accum(bins, 0.0);
224 std::vector<double> prev_phase(bins, 0.0);
225
226 Eigen::FFT<double> fft;
227 Eigen::VectorXd frame(N);
228 Eigen::VectorXcd spectrum;
229 Eigen::VectorXd ifft_out;
230
231 for (size_t f = 0; f < n_frames; ++f) {
232 const size_t src_pos = f * Ha;
233
234 for (uint32_t k = 0; k < N; ++k) {
235 const size_t idx = src_pos + k;
236 frame(static_cast<Eigen::Index>(k)) = (idx < src.size()) ? src[idx] * win[k] : 0.0;
237 }
238
239 fft.fwd(spectrum, frame);
240
241 std::vector<std::complex<double>> synth(bins);
242 for (uint32_t b = 0; b < bins; ++b) {
243 const std::complex<double> bin = spectrum(static_cast<Eigen::Index>(b));
244 const double mag = std::abs(bin);
245 const double phase = std::arg(bin);
246
247 const double delta = phase - prev_phase[b]
248 - omega_factor * static_cast<double>(b);
249 const double true_f = omega_factor * static_cast<double>(b)
250 + wrap_phase(delta);
251
252 phase_accum[b] += static_cast<double>(Hs) * true_f
253 / static_cast<double>(Ha);
254 prev_phase[b] = phase;
255
256 synth[b] = std::polar(mag, phase_accum[b]);
257 }
258
259 Eigen::VectorXcd full = to_full_spectrum(synth, N);
260 fft.inv(ifft_out, full);
261
262 const size_t out_pos = f * Hs;
263 const double inv_N = 1.0 / static_cast<double>(N);
264 for (uint32_t k = 0; k < N && out_pos + k < out_len; ++k) {
265 const double w = win[k];
266 output[out_pos + k] += ifft_out(static_cast<Eigen::Index>(k)) * inv_N * w;
267 norm[out_pos + k] += w * w;
268 }
269 }
270
271 for (size_t i = 0; i < out_len; ++i) {
272 if (norm[i] > 1e-10)
273 output[i] /= norm[i];
274 }
275
276 output.resize(
277 static_cast<size_t>(static_cast<double>(src.size()) * stretch_factor));
278
279 return output;
280}
#define N(method_name, full_type_name)
Definition Creator.hpp:183
size_t b

References b, and N.

Referenced by pitch_shift().

+ Here is the caller graph for this function: