Skip to content

Commit f5176b9

Browse files
singalsukv2019i
authored andcommitted
audio: phase_vocoder: re-anchor synthesis phase on speed change
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>
1 parent 95b648c commit f5176b9

1 file changed

Lines changed: 27 additions & 0 deletions

File tree

src/audio/phase_vocoder/phase_vocoder_common.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ static int32_t unwrap_angle_q27(int32_t angle)
215215
void phase_vocoder_reset_for_new_speed(struct phase_vocoder_comp_data *cd)
216216
{
217217
struct phase_vocoder_state *state = &cd->state;
218+
struct phase_vocoder_polar *polar = &state->polar;
219+
struct phase_vocoder_fft *fft = &state->fft;
220+
struct ipolar32 *polar_prev_ch;
221+
int32_t *angle_delta_prev_ch;
222+
int32_t *output_phase_ch;
223+
int ch, i;
218224

219225
state->speed = cd->speed_ctrl;
220226

@@ -232,6 +238,27 @@ void phase_vocoder_reset_for_new_speed(struct phase_vocoder_comp_data *cd)
232238
*/
233239
state->num_input_fft = 1;
234240
state->num_output_ifft = 0;
241+
242+
/* Re-anchor synthesis phase so the first post-reset IFFT lands
243+
* output_phase back on polar_prev.angle (the steady-state invariant).
244+
* Between speed changes output_phase advances by one analysis-hop
245+
* delta per output IFFT regardless of speed, so at speed != 1 it
246+
* drifts away from the true polar angle; and each reset also adds
247+
* one extra angle_delta_prev on the first no-consume IFFT. If not done,
248+
* both accumulate across interactive speed changes as a per-bin
249+
* phase offset that smears transients and dulls the sound.
250+
*/
251+
for (ch = 0; ch < cd->process_channels; ch++) {
252+
polar_prev_ch = polar->polar_prev[ch];
253+
angle_delta_prev_ch = polar->angle_delta_prev[ch];
254+
output_phase_ch = polar->output_phase[ch];
255+
if (!polar_prev_ch || !angle_delta_prev_ch || !output_phase_ch)
256+
continue;
257+
258+
for (i = 0; i < fft->half_fft_size; i++)
259+
output_phase_ch[i] =
260+
unwrap_angle_q27(polar_prev_ch[i].angle - angle_delta_prev_ch[i]);
261+
}
235262
}
236263

237264
static void copy_polar_angles(int32_t *angle_delta_ch, struct ipolar32 *polar_data_ch,

0 commit comments

Comments
 (0)