0
$\begingroup$

I want to perform pitch shifting with the method described in NEW PHASE-VOCODER TECHNIQUES FOR PITCH-SHIFTING, HARMONIZING AND OTHER EXOTIC EFFECTS. Basically, it involves:

  1. Locate the spectral peaks and their indices in spectrums.

  2. Determine the region of influence (ROI) for each detected peak.

  3. Perform peak shifting according to the amount of frequency shift. Specifically, peaks and their ROI are shifted by an amount of frequency.

  4. Update the phase to maintain the phase coherence, as in phase vocoder for time-scaling.

I am stuck in step 3. The paper describes that with a fractional number of frequency bin shifting (rather than integer number of frequency bin), one should use interpolation since the frequency bins are discrete:

If the amount of frequency shift is a fractional number of frequency bins, then frequency-domain interpolation is required, since the sinusoidal peak is only known at discrete frequencies.

My problem is that how do I perform peak shifting through, say, linear interpolation? And, assume a shifted spectrum is obtained, as interpolation insert values between the original discrete bins, how is it finally istf to the pitch-shifted output? I appreciate for any help.

EDIT:

I've realized my question can be simply be phrased as "fractional shifting of spectrum components". The following code is an example to achieve fractional shifting with a random signal using linear interpolation:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(5)
y = np.random.random(20) 
alpha = 3.2    # shifting factor

# Pad the signal with zero 
# to avoid spill-out caused by 
# large shifting factor
nPad = np.ceil(alpha*len(y))
yPad = np.concatenate(
           (y, np.zeros(int(nPad))))

x = np.arange(len(yPad))
xInt = x[0:len(y)]*alpha    # the 'xp' in function interp
yInt = y                    # the 'fp' in function interp

# Now define 'x' in function interp
start = np.ceil(xInt[0])
end = np.ceil(xInt[-1])
if float(xInt[-1]).is_integer():
    xQue = np.arange(start, end+1, 1).astype(int)
else:
    xQue = np.arange(start, end, 1).astype(int)

yNew = np.interp(xQue, xInt, yInt)    # linear interpolation
shifted = np.zeros(len(yPad))
shifted[xQue] = yNew

plt.plot(yPad, 'b')
plt.plot(shifted, 'r')
plt.show()

enter image description here The above example uses a randomly generated signal to illustrate fractional shifting of a spectrum.

However, in the real case, a spectrum shouldn't be padded with zero in the first case as I do in the code. So, how to deal with the "spill" with spectrum component if the shifting factor is too large?

$\endgroup$
1
$\begingroup$

You can use a Sinc (or windowed Sinc) kernel for interpolation between FFT/DFT result bins. Note that near DC and Fs/2, the interpolation needs to be circular.

After shifting a peak up and determining its phase, you can again use Sinc interpolation to figure out how it should be represented in some number of nearby (non-fractional) bins.

Added:

Linear interpolation can lead to the distortion of harmonic ratios.

To avoid aliasing, the result has to be low-pass filtered or bandlimited to below Fs/2. So any spectrum shifted above that needs to be filtered out and thrown away.

$\endgroup$
  • $\begingroup$ Thanks! I was inspired by your words and did some work to achieve linear interpolation. Please see the edit. $\endgroup$ – Francis Apr 26 '17 at 10:28
  • $\begingroup$ The paper mentions "If a shifted area of influence ”spills” onto the negative frequency axis, it is simply reflected back into the positive frequencies with complex conjugation to account for the fact that the original signal is real". Though I don't understand how a simple reflection makes sense, it seems to me a simple discard is not suggested in the paper, or it can be my misunderstanding. $\endgroup$ – Francis Apr 27 '17 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.