# Shifting frequency peaks such that w[i] moves to w[i/n] using STFT and IFFT

I am trying to do a kind of frequency compression on say 20 most energetic frequencies in a particular slice of a STFT. If w[i] is the frequency peak for a slice of STFT at a particular time, then I want to move w[i] to w[i/n] where n is arbitrary. To make sure invertability is still maintained I'm using a 2 sided STFT and just switch w[i] with w[i/n] and making sure that conjugate symmetry is still maintained. Here the n factor im shifting down by is 4

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
rng = np.random.default_rng()
fs = 10e3
N = 10e4
amp = 2 * np.sqrt(2)
noise_power = 0.01 * fs / 2
time = np.arange(N) / float(fs)
mod = 500*np.cos(2*np.pi*0.25*time)
carrier = amp * np.sin(2*np.pi*3e3*time + mod)
noise = rng.normal(scale=np.sqrt(noise_power),size=time.shape)
noise *= np.exp(-time/5)
x = carrier + noise
f, t, Zxx = signal.stft(x, fs, nperseg=1000,return_onesided=False)
Spectra= Zxx
Zxx= abs(Zxx)
plt.plot(f,Zxx[:,100])
plt.show()
freqdic = {}
Spectra=Spectra[:,100] #I'm considering a slice at the 100th time element of STFT
list = sorted(range(len(Zxx[:,100])), key=lambda i: Zxx[:,100][i])[-20:] #20 most energetic peaks
for i in list :
print (Zxx[:,100][i])
peaklist =list[::2]
for i in peaklist:
lowered = int(i/4) #shiftdown factor
if lowered not in freqdic:
freqdic[lowered] = 1
Spectra[i], Spectra[lowered] = Spectra[lowered], Spectra[i]
Spectra[len(Spectra)-i], Spectra[len(Spectra)-lowered] = Spectra[len(Spectra)-lowered], Spectra[len(Spectra)-i]
plt.plot(f,abs(Spectra))
plt.show()


That's the code, I'm using an example from SciPy's doc on STFT. This is what the STFT looks like

This is what the 100th time element slice looks like

This is what the same slice looks like after running it through my code

Something has clearly moved, but the I am not sure why w[i] is still strong and hasn't been swapped, even turning the thing to 0 seems to do nothing, I am not sure whats going on. I'm getting more unexpected and random results by varying the number of spectral peaks considered

Further, what I want to do after this is, hopefully after getting rid of the spectral peak where it used to be, is to take IFFT of the STFT slices and stitch them together and thus in effect move the frequency peaks down by an arbitrary n factor.

• Moving peaks around in the spectrum requires resampling the spectrum, which is also sorta expensive. My recommendation is to use the STFT and phase vocoder to time-stretch or time-compress the original recording, And then use resampling techniques to scrunch or stretch this back to the original length. When you connect the individual frequency components, be sure you continued to adjust the phases for each component in successive frames so that the splice of that sinusoid to the corresponding sinusoid of the previous frame is seamless. Commented Feb 24 at 19:20
• @robertbristow-johnson, what do you mean by resampling the spectrum? I don't really need to even move peaks around, I don't even mind finding a few peaks and writing it to a new and blank spectrum and then taking the IFFT, do you feel that'll be less expensive? Coming to phase vocoder, my understanding was it does something quite similar so I don't see how it'll be less expensive tbh. Wrt Phase transients, I'm not too concerned Commented Feb 25 at 1:51
• If you want to compress everything towards DC by a factor of $\frac{1}{n}$, you can resample the original signal to a new sample rate of $f_{s}n$. However, this will leave you with a $\approx nN$ length signal, so I believe he’s giving you a way to keep the data length roughly the same. If you aren’t concerned about this, a polyphase resampler will do the trick. If you need different $n$ for different frequencies, mix with $e^{\pm j\omega_{0}t}$ where $\omega_{0} = \omega_{p}-\frac{\omega_{p}}{n}$ and then have bandpass filters centered at $\pm \omega_{0}$ Commented Feb 25 at 7:57
• @Baddioes, I understand, I'm probably going to end up using a phase vocoder, it works pretty well and is really fast. but now what I really want to know is whether I can do it this way because as I see it, able to mess around with signals in STFT leads to a wide range of applications. I ended up solving this by writing complex peaks on blank frequency domain representation, I ended up with another problem however : dsp.stackexchange.com/questions/93094/… Commented Feb 25 at 9:22