Search code examples
pythonsignal-processingfftwavfrequency-analysis

I only get noise in the audio after taking a FFT and varying frequency component values . Where am I going wrong?


The objective of my project is to synthesize the sound. What I want to do is to read a wave file and convert it to the amplitude spectrum. Since I am interested in the Magnitude and corresponding Frequencies. I also need to Change the Magnitude of certain frequencies(which i get) so that i can generate different sounds wav file back and play it. However, even without altering the magnitude the reconstructed signal is full of noise.

In simple words , read file --- FFT--- vary magnitude --- play it.

below is the Code

import scipy.io.wavfile
import sounfile as sf
data , rate = sf.read("bird_chirp.wav")
FFt_data =np.fft.rfft(data)
magnitude = np.abs(FFt_data)
phase = np.angle(FFt_data)
N= len(data) # Define the length of the wav file
timestamp = np.linspace(0.0, N*T, N)
T= 1.0/rate
n = data.size


#get the corresponding Frequencies
freq = np.fft.rfftfreq(n, d=1./rate)


# save it as a Dataframe
df = {"freq":freq, "mag":magnitude}
df =pd.DataFrame(df)


#Normalize the magnitude
a=df["mag"]
norm_a = a/a.max(axis=0)
df["mag"] = norm_a


# here I would play around with magnitude , make it high or low
#code to change magnitude

#Get back the new data to write in wav
y=0
for magnitudes ,frequencies in df.iterrows():
   y+= magnitudes["mag"]*(np.sin(frequencies["freq"] *2.0*np.pi*timestamp)) 


#save it 
sf.write(file="test.wav", data=y,samplerate=rate)

The code plays sound full of noise.


Solution

  • Below is a simple program that (a) reads a wave file, (b) Fourier transforms the data, (c) modifies the amplitudes at specific frequencies, (d) reverses the Fourier transform to convert the data back to time domain, and (e) saves the result to another wave file that you can play with any of the usual audio playback programs.

    For purposes of demonstrating what you can do with the signal in a simple way, we attenuate the amplitude at 1 kHz,we add a continuous tone at 440 Hz and we add a Gaussian shaped pulse at 880.

    Note that the injected signals are scaled to the maximum of other signals in the Fourier transform. Alternatively we could have chosen an amplitude and scaled it by the length of the data.

    An important concept here is that the Fourier transform conserves power. Therefore a signal in the Fourier transform is scaled by its duration in time.

    Here is the code to implement what you seemed to be looking for in the question:

    import scipy.io.wavfile
    import soundfile as sf
    
    import numpy as np
    
    # Input the wave file
    data , rate = sf.read("bird_chirp.wav")
    
    # Fourier transform
    FFT_data = np.fft.rfft(data)
    
    # Get the list of frequencies
    freq = np.fft.rfftfreq(len(data), d=1./rate)
    
    # Find the bin closest to 1kHz and attenuate
    idx = (np.abs(freq - 1.E3)).argmin()
    FFT_data[idx] *= 1./2
    
    # Find the bin closest to 440 Hz and set a continuous tone
    idx = (np.abs(freq - 440)).argmin()
    FFT_data[idx] = max( abs( FFT_data) )
    
    # Add a Gaussian pulse, width in frequency is inverse of its duration
    FFT_data += max( abs( FFT_data) )/2. * np.exp( -((freq-880)/5.)**2 )
    
    # Convert back to time domain
    newdata = np.fft.irfft(FFT_data)
    
    # And save it to a new wave file
    sf.write(file="test.wav", data=newdata, samplerate=rate)