Search code examples
matlabsignal-processingfftphase

Frequency domain phase shift, amplitude, hope size and non-linearity


I am trying to implement a frequency domain phase shift but there are few points on which I am not sure.

1- I am able to get a perfect reconstruction from a sine or sweep signal using a hanning window with a hop size of 50%. Nevertheless, how should I normalise my result when using a hop size > 50%?

2- When shifting the phase of low frequency signals (f<100, window size<1024, fs=44100) I can clearly see some non-linearity in my result. Is this because of the window size being to short for low frequencies?

Thank you very much for your help.

clear
freq=500;
fs=44100;
endTime=0.02;
t = 1/fs:1/fs:(endTime);
f1=linspace(freq,freq,fs*endTime);
x = sin(2*pi*f1.*t);
targetLength=numel(x);

L=1024;
w=hanning(L);
H=L*.50;% Hopsize of 50%
N=1024;

%match input length with window length
x=[zeros(L,1);x';zeros(L+mod(length(x),H),1)];

pend=length(x)- L ;
pin=0;
count=1;
X=zeros(N,1);
buffer0pad= zeros(N,1);
outBuffer0pad= zeros(L,1);
y=zeros(length(x),1); 
delay=-.00001;
df = fs/N;
f= -fs/2:df:fs/2 - df;

while pin<pend 

    buffer = x(pin+1:pin+L).*w;

    %append zero padding in the middle
    buffer0pad(1:(L)/2)=buffer((L)/2+1: L);
    buffer0pad(N-(L)/2+1:N)=buffer(1:(L)/2);

    X = fft(buffer0pad,N);

    % Phase modification
    X = abs(X).*exp(1i*(angle(X))-(1i*2*pi*f'*delay));

    outBuffer=real(ifft(X,N));

    % undo zero padding----------------------
    outBuffer0pad(1:L/2)=outBuffer(N-(L/2-1): N);
    outBuffer0pad(L/2+1:L)=outBuffer(1:(L)/2);

    %Overlap-add
    y(pin+1:pin+L) = y(pin+1:pin+L) + outBuffer0pad;

    pin=pin+H;
    count=count+1;
end

%match output length with original input length
output=y(L+1:numel(y)-(L+mod(targetLength,H)));

figure(2)
plot(t,x(L+1:numel(x)-(L+mod(targetLength,H))))
hold on 
plot(t,output)
hold off

Solution

  • Anything below 100 Hz has less than two cycles in your FFT window. Note that a DFT or FFT represents any waveform, including a single non-integer-periodic sinusoid, by possibly summing up of a whole bunch of sinusoids of very different frequencies. e.g. a lot more than just one. That's just how the math works.

    For a von Hann window containing less than 2 cycles, these are often a bunch of mostly completely different frequencies (possibly very far away in terms of percentage from your low frequency). Shifting the phase of all those completely different frequencies may or may not shift your windowed low frequency sinusoid by the desired amount (depending on how different in frequency your signal is from being integer-periodic).

    Also for low frequencies, the complex conjugate mirror needs to be shifted in the opposite direction in phase in order to still represent a completely real result. So you end up mixing 2 overlapped and opposite phase changes, which again is mostly a problem if the low frequency signal is far from being integer periodic in the DFT aperture.

    Using a longer window in time and samples allows more cycles of a given frequency to fit inside it (thus possibly needing a lesser power of very different frequency sinusoids to be summed up in order to compose, make up or synthesize your low frequency sinusoid); and the complex conjugate is farther away in terms of FFT result bin index, thus reducing interference.

    A sequence using any hop of a von Hann window that in 50% / (some-integer) in length is non-lossy (except for the very first or last window). All other hop sizes modulate or destroy information, and thus can't be normalized by a constant for reconstruction.