audio: phase_vocoder: re-anchor synthesis phase on speed change - #11058
Open
singalsu wants to merge 1 commit into
Open
audio: phase_vocoder: re-anchor synthesis phase on speed change#11058singalsu wants to merge 1 commit into
singalsu wants to merge 1 commit into
Conversation
When the user changes the speed control the component preserves the polar analysis state across the reset so that the interpolation can continue smoothly, but the synthesis phase accumulator output_phase was left drifting. Each output IFFT in stft_do_fft_ifft() advances output_phase by an interpolated one-analysis-hop delta of the form (1 - frac) * angle_delta_prev + frac * angle_delta, where frac is the current interpolation position between two consecutive analysis frames. That delta is added irrespective of the current speed, so at non-unity speed output_phase runs faster than the true polar angle. On top of that, the first post-reset IFFT does not consume a new input FFT yet still applies its phase interpolation step (with frac = 0, so the added term equals angle_delta_prev), which adds one extra angle_delta_prev per reset. After a repeated excursion (for example 1.0 -> 0.5 -> 1.0 with several intermediate steps) both effects accumulate as a random per-bin phase offset that persists after returning to unity speed. Perceptually this smears transients and dulls the sound even though the audio is being processed at speed 1.0 again. The steady-state invariant at speed 1.0 is that after each IFFT's phase interpolation step output_phase equals polar_prev.angle. To preserve this invariant across a speed change, re-anchor output_phase to polar_prev.angle minus angle_delta_prev in reset_for_new_speed(). The first post-reset IFFT then lands output_phase exactly on polar_prev.angle, and all subsequent IFFTs advance normally with no cumulative offset. Existing NULL and channel-count guards keep the init-time call from touching buffers before they are allocated. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
Contributor
There was a problem hiding this comment.
🟢 Ready to approve
The change is small, well-guarded against uninitialized buffers, and addresses the described drift mechanism without introducing API or control-flow risk.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR fixes a phase drift issue in the phase vocoder when the user changes playback speed by re-anchoring the synthesis phase accumulator during speed resets, preventing cumulative per-bin phase offsets that persist after returning to unity speed.
Changes:
- Preserve analysis-state continuity across speed changes while re-initializing synthesis
output_phaseto maintain the steady-state invariant at speed 1.0. - Adjust
phase_vocoder_reset_for_new_speed()to setoutput_phase[ch][i] = unwrap(polar_prev.angle - angle_delta_prev)for all bins/channels with allocated state.
File summaries
| File | Description |
|---|---|
| src/audio/phase_vocoder/phase_vocoder_common.c | Re-anchors synthesis phase during speed-change reset to prevent cumulative phase drift and post-reset offsets. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Comment on lines
+247
to
+249
| * one extra angle_delta_prev on the first no-consume IFFT. If not done, | ||
| * both accumulate across with interactive speed changes as a per-bin | ||
| * phase offset which smears transients and dulls the sound. |
singalsu
marked this pull request as ready for review
August 4, 2026 15:51
singalsu
requested review from
dbaluta,
kv2019i,
lbetlej,
lgirdwood,
mmaka1 and
plbossart
as code owners
August 4, 2026 15:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When the user changes the speed control the component preserves the polar analysis state across the reset so that the interpolation can continue smoothly, but the synthesis phase accumulator output_phase was left drifting. Each output IFFT in stft_do_fft_ifft() advances output_phase by an interpolated one-analysis-hop delta of the form (1 - frac) * angle_delta_prev + frac * angle_delta, where frac is the current interpolation position between two consecutive analysis frames. That delta is added irrespective of the current speed, so at non-unity speed output_phase runs faster than the true polar angle. On top of that, the first post-reset IFFT does not consume a new input FFT yet still applies its phase interpolation step (with frac = 0, so the added term equals angle_delta_prev), which adds one extra angle_delta_prev per reset. After a repeated excursion (for example 1.0 -> 0.5 -> 1.0 with several intermediate steps) both effects accumulate as a random per-bin phase offset that persists after returning to unity speed. Perceptually this smears transients and dulls the sound even though the audio is being processed at speed 1.0 again.
The steady-state invariant at speed 1.0 is that after each IFFT's phase interpolation step output_phase equals polar_prev.angle. To preserve this invariant across a speed change, re-anchor output_phase to polar_prev.angle minus angle_delta_prev in reset_for_new_speed(). The first post-reset IFFT then lands output_phase exactly on polar_prev.angle, and all subsequent IFFTs advance normally with no cumulative offset. Existing NULL and channel-count guards keep the init-time call from touching buffers before they are allocated.