Skip to content

Commit 47b6994

Browse files
author
Jeff Martin
committed
separated phase locking into separate function calls
1 parent 22835b7 commit 47b6994

2 files changed

Lines changed: 240 additions & 131 deletions

File tree

src/pv/pvStretch.cpp

Lines changed: 192 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,7 @@ void PV_PlayBufStretch_next(PV_PlayBufStretch *unit, int inNumSamples) {
207207

208208
// For frames other than the first frame, we'll need to perform phase computation.
209209
else {
210-
bool phaseLock = false;
211-
if (IN0(4) != 0.f) {
212-
phaseLock = true;
213-
// std::cout << "Phase lock\n";
214-
}
210+
size_t phaseLock = sc_clip(static_cast<size_t>(IN0(4)), 0, 2);
215211
SCPolarBuf *p = ToPolarApx(buf);
216212
size_t roundedPos = static_cast<size_t>(std::round(newPos));
217213
//std::cout << "New pos: " << intPos << "\n";
@@ -223,15 +219,27 @@ void PV_PlayBufStretch_next(PV_PlayBufStretch *unit, int inNumSamples) {
223219
fillPolarBuf(stftData + (roundedPos * stftBufFftSize), unit->m_frameNext, stftBufFftSize);
224220
fillPolarBuf(stftData + (lastPos * stftBufFftSize), unit->m_framePrev1, stftBufFftSize);
225221
// Render the output FFT frame
226-
Stretch2(
227-
unit->m_frameNext,
228-
unit->m_framePrev1,
229-
p,
230-
unit->m_outFramePrev,
231-
stftBufFftSize,
232-
stftBufHopSize,
233-
phaseLock
234-
);
222+
switch (phaseLock) {
223+
case 1:
224+
Stretch2Puckette(
225+
unit->m_frameNext,
226+
unit->m_framePrev1,
227+
p,
228+
unit->m_outFramePrev,
229+
stftBufFftSize,
230+
stftBufHopSize
231+
);
232+
break;
233+
default:
234+
Stretch2(
235+
unit->m_frameNext,
236+
unit->m_framePrev1,
237+
p,
238+
unit->m_outFramePrev,
239+
stftBufFftSize,
240+
stftBufHopSize
241+
);
242+
}
235243
} else {
236244
// Otherwise we're between two FFT frames, and we're going to have to
237245
// interpolate magnitude and frequency data.
@@ -243,17 +251,31 @@ void PV_PlayBufStretch_next(PV_PlayBufStretch *unit, int inNumSamples) {
243251
// This is the frame right before that. It's needed to compute the previous instantaneous frequencies.
244252
fillPolarBuf(stftData + ((lo-1) * stftBufFftSize), unit->m_framePrev2, stftBufFftSize);
245253
// Render the output FFT frame
246-
Stretch3(
247-
unit->m_frameNext,
248-
unit->m_framePrev1,
249-
unit->m_framePrev2,
250-
p,
251-
unit->m_outFramePrev,
252-
newPos-static_cast<float>(lo),
253-
stftBufFftSize,
254-
stftBufHopSize,
255-
phaseLock
256-
);
254+
switch (phaseLock) {
255+
case 1:
256+
Stretch3Puckette(
257+
unit->m_frameNext,
258+
unit->m_framePrev1,
259+
unit->m_framePrev2,
260+
p,
261+
unit->m_outFramePrev,
262+
newPos-static_cast<float>(lo),
263+
stftBufFftSize,
264+
stftBufHopSize
265+
);
266+
break;
267+
default:
268+
Stretch3(
269+
unit->m_frameNext,
270+
unit->m_framePrev1,
271+
unit->m_framePrev2,
272+
p,
273+
unit->m_outFramePrev,
274+
newPos-static_cast<float>(lo),
275+
stftBufFftSize,
276+
stftBufHopSize
277+
);
278+
}
257279
}
258280
// We always need to store the resultant FFT frame in the UGen.
259281
// It is used in the next call to PV_PlayBufStretch_next, for
@@ -276,59 +298,78 @@ void PV_PlayBufStretch_next(PV_PlayBufStretch *unit, int inNumSamples) {
276298
/// \param outFramePrev The previously computed output STFT frame
277299
/// \param fftSize The FFT size
278300
/// \param hopSize The hop size
279-
/// \param phaseLock Whether or not to apply phase locking
280301
void Stretch2(
281302
const SCPolarBuf *frame,
282303
const SCPolarBuf *framePrev,
283304
SCPolarBuf *outFrame,
284305
const SCPolarBuf *outFramePrev,
285306
size_t fftSize,
286-
size_t hopSize,
287-
bool phaseLock) {
307+
size_t hopSize) {
288308
outFrame->dc = frame->dc;
289309
outFrame->nyq = frame->nyq;
290310
for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) {
291311
outFrame->bin[xxk].mag = frame->bin[xxk].mag;
292312

293-
// Puckette-style phase locking
294-
if (phaseLock) {
295-
// Compute the instantaneous frequency
296-
float omegaK = twopi * (xxk+1) / fftSize;
297-
float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK;
298-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
299-
float instantaneousFreq = omegaK + phaseInc/hopSize;
300-
301-
// In Puckette-style phase locking, we make a substitution for the previous phase,
302-
// in order to "lock" phases of adjacent bins together.
303-
float prevPhase = 0.0;
304-
if (xxk == 0) {
305-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->dc, 0.f);
306-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
307-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
308-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
309-
} else if (xxk == fftSize/2-2) {
310-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
311-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
312-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->nyq, 0.f);
313-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
314-
} else {
315-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
316-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
317-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
318-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
319-
}
320-
321-
// Compute the new phase
322-
outFrame->bin[xxk].phase = prevPhase + hopSize * instantaneousFreq;
313+
// Compute the instantaneous frequency
314+
float omegaK = twopi * (xxk+1) / fftSize;
315+
float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK;
316+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
317+
float instantaneousFreq = omegaK + phaseInc/hopSize;
318+
319+
// Compute the new phase
320+
outFrame->bin[xxk].phase = outFramePrev->bin[xxk].phase + hopSize * instantaneousFreq;
321+
}
322+
}
323+
324+
/// Computes a single frame of STFT data for time stretching.
325+
/// The assumption is that we are positioned exactly at `frame`, and we therefore
326+
/// just need framePrev to compute the instantaneous frequency. We also do not
327+
/// need to perform any magnitude or frequency interpolation.
328+
///
329+
/// This version uses Miller Puckette's phase locking.
330+
///
331+
/// \param frame The current STFT frame
332+
/// \param framePrev The previous STFT frame
333+
/// \param [out] outFrame The output STFT frame
334+
/// \param outFramePrev The previously computed output STFT frame
335+
/// \param fftSize The FFT size
336+
/// \param hopSize The hop size
337+
void Stretch2Puckette(
338+
const SCPolarBuf *frame,
339+
const SCPolarBuf *framePrev,
340+
SCPolarBuf *outFrame,
341+
const SCPolarBuf *outFramePrev,
342+
size_t fftSize,
343+
size_t hopSize) {
344+
outFrame->dc = frame->dc;
345+
outFrame->nyq = frame->nyq;
346+
for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) {
347+
outFrame->bin[xxk].mag = frame->bin[xxk].mag;
348+
349+
// Compute the instantaneous frequency
350+
float omegaK = twopi * (xxk+1) / fftSize;
351+
float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK;
352+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
353+
float instantaneousFreq = omegaK + phaseInc/hopSize;
354+
355+
// In Puckette-style phase locking, we make a substitution for the previous phase,
356+
// in order to "lock" phases of adjacent bins together.
357+
float prevPhase = 0.0;
358+
if (xxk == 0) {
359+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->dc, 0.f);
360+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
361+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
362+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
363+
} else if (xxk == fftSize/2-2) {
364+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
365+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
366+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->nyq, 0.f);
367+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
323368
} else {
324-
// Compute the instantaneous frequency
325-
float omegaK = twopi * (xxk+1) / fftSize;
326-
float phaseInc = frame->bin[xxk].phase - framePrev->bin[xxk].phase - hopSize * omegaK;
327-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
328-
float instantaneousFreq = omegaK + phaseInc/hopSize;
329-
330-
// Compute the new phase
331-
outFrame->bin[xxk].phase = outFramePrev->bin[xxk].phase + hopSize * instantaneousFreq;
369+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
370+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
371+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
372+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
332373
}
333374
}
334375
}
@@ -346,7 +387,6 @@ void Stretch2(
346387
/// \param pos The position between framePrev1 and frameNext (0 < pos < 1)
347388
/// \param fftSize The FFT size
348389
/// \param hopSize The hop size
349-
/// \param phaseLock Whether or not to apply phase locking
350390
void Stretch3(
351391
const SCPolarBuf *frameNext,
352392
const SCPolarBuf *framePrev1,
@@ -355,71 +395,98 @@ void Stretch3(
355395
const SCPolarBuf *outFramePrev,
356396
float pos,
357397
size_t fftSize,
358-
size_t hopSize,
359-
bool phaseLock) {
398+
size_t hopSize) {
360399
outFrame->dc = INTERP(framePrev1->dc, frameNext->dc, pos);
361400
outFrame->nyq = INTERP(framePrev1->nyq, frameNext->nyq, pos);
362401
for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) {
363402
outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos);
364403

365-
// Puckette-style phase locking
366-
if (phaseLock) {
367-
float omegaK = twopi * (xxk+1) / fftSize;
368-
369-
// Compute the next instantaneous frequency
370-
float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK;
371-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
372-
float instantaneousFreqNext = omegaK + phaseInc/hopSize;
373-
374-
// Compute the previous instantaneous frequency
375-
phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK;
376-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
377-
float instantaneousFreqPrev = omegaK + phaseInc/hopSize;
378-
379-
// Interpolate the instantaneous frequency
380-
float instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos);
381-
382-
// In Puckette-style phase locking, we make a substitution for the previous phase,
383-
// in order to "lock" phases of adjacent bins together.
384-
float prevPhase = 0.0;
385-
if (xxk == 0) {
386-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->dc, 0.f);
387-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
388-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
389-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
390-
} else if (xxk == fftSize/2-2) {
391-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
392-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
393-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->nyq, 0.f);
394-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
395-
} else {
396-
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
397-
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
398-
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
399-
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
400-
}
401-
402-
// Compute the new phase
403-
outFrame->bin[xxk].phase = prevPhase + hopSize * instantaneousFreq;
404+
float omegaK = twopi * (xxk+1) / fftSize;
405+
406+
// Compute the next instantaneous frequency
407+
float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK;
408+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
409+
float instantaneousFreqNext = omegaK + phaseInc/hopSize;
410+
411+
// Compute the previous instantaneous frequency
412+
phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK;
413+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
414+
float instantaneousFreqPrev = omegaK + phaseInc/hopSize;
415+
416+
// Interpolate the instantaneous frequency
417+
float instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos);
418+
419+
// Compute the new phase
420+
outFrame->bin[xxk].phase = outFramePrev->bin[xxk].phase + hopSize * instantaneousFreq;
421+
}
422+
}
423+
424+
/// Computes a single frame of STFT data for time stretching.
425+
/// The assumption is that we are positioned between framePrev1 and frameNext.
426+
/// This means we will need to interpolate frequency data. So we will need to
427+
/// compute two frequencies for each bin, and that means we need three STFT frames.
428+
///
429+
/// This version uses Miller Puckette's phase locking.
430+
///
431+
/// \param frameNext The next STFT frame
432+
/// \param framePrev1 The previous STFT frame
433+
/// \param framePrev2 The previous STFT frame before that (required for instantaneous frequency interpolation)
434+
/// \param [out] outFrame The output STFT frame
435+
/// \param outFramePrev The previously computed output STFT frame
436+
/// \param pos The position between framePrev1 and frameNext (0 < pos < 1)
437+
/// \param fftSize The FFT size
438+
/// \param hopSize The hop size
439+
void Stretch3Puckette(
440+
const SCPolarBuf *frameNext,
441+
const SCPolarBuf *framePrev1,
442+
const SCPolarBuf *framePrev2,
443+
SCPolarBuf *outFrame,
444+
const SCPolarBuf *outFramePrev,
445+
float pos,
446+
size_t fftSize,
447+
size_t hopSize) {
448+
outFrame->dc = INTERP(framePrev1->dc, frameNext->dc, pos);
449+
outFrame->nyq = INTERP(framePrev1->nyq, frameNext->nyq, pos);
450+
for (size_t xxk = 0; xxk < fftSize/2-1; xxk++) {
451+
outFrame->bin[xxk].mag = INTERP(framePrev1->bin[xxk].mag, frameNext->bin[xxk].mag, pos);
452+
453+
float omegaK = twopi * (xxk+1) / fftSize;
454+
455+
// Compute the next instantaneous frequency
456+
float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK;
457+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
458+
float instantaneousFreqNext = omegaK + phaseInc/hopSize;
459+
460+
// Compute the previous instantaneous frequency
461+
phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK;
462+
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
463+
float instantaneousFreqPrev = omegaK + phaseInc/hopSize;
464+
465+
// Interpolate the instantaneous frequency
466+
float instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos);
467+
468+
// In Puckette-style phase locking, we make a substitution for the previous phase,
469+
// in order to "lock" phases of adjacent bins together.
470+
float prevPhase = 0.0;
471+
if (xxk == 0) {
472+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->dc, 0.f);
473+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
474+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
475+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
476+
} else if (xxk == fftSize/2-2) {
477+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
478+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
479+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->nyq, 0.f);
480+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
404481
} else {
405-
float omegaK = twopi * (xxk+1) / fftSize;
406-
407-
// Compute the next instantaneous frequency
408-
float phaseInc = frameNext->bin[xxk].phase - framePrev1->bin[xxk].phase - hopSize * omegaK;
409-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
410-
float instantaneousFreqNext = omegaK + phaseInc/hopSize;
411-
412-
// Compute the previous instantaneous frequency
413-
phaseInc = framePrev1->bin[xxk].phase - framePrev2->bin[xxk].phase - hopSize * omegaK;
414-
phaseInc = std::fmod(phaseInc + pi, twopi) - pi;
415-
float instantaneousFreqPrev = omegaK + phaseInc/hopSize;
416-
417-
// Interpolate the instantaneous frequency
418-
float instantaneousFreq = INTERP(instantaneousFreqPrev, instantaneousFreqNext, pos);
419-
420-
// Compute the new phase
421-
outFrame->bin[xxk].phase = outFramePrev->bin[xxk].phase + hopSize * instantaneousFreq;
482+
std::complex<float> prevBinKMinus1 = std::polar<float>(outFramePrev->bin[xxk-1].mag, outFramePrev->bin[xxk-1].phase);
483+
std::complex<float> prevBinK = std::polar<float>(outFramePrev->bin[xxk].mag, outFramePrev->bin[xxk].phase);
484+
std::complex<float> prevBinKPlus1 = std::polar<float>(outFramePrev->bin[xxk+1].mag, outFramePrev->bin[xxk+1].phase);
485+
prevPhase = std::arg(prevBinK - prevBinKMinus1 - prevBinKPlus1);
422486
}
487+
488+
// Compute the new phase
489+
outFrame->bin[xxk].phase = prevPhase + hopSize * instantaneousFreq;
423490
}
424491
}
425492

0 commit comments

Comments
 (0)