Search code examples
pythonnumpyaudiofrequencyenvelope

Is there an easy way of finding frequency of envelopes in sound signals?


I have a sound signal of 5 secs length and it is from the sound of a propeller. I need to find rpm of the propeller by finding frequency of the envelopes.

import wave
import numpy as np
import matplotlib.pyplot as plt

raw = wave.open('/content/drive/MyDrive/Demon.wav','r')
signal = raw.readframes(-1)
signal = np.frombuffer(signal , dtype="int16")
frate = raw.getframerate()

time = np.linspace(0,len(signal) / frate,num = len(signal))

plt.figure(1)
plt.title("Sound Wave")
plt.xlabel("Time")

plt.plot(time, signal)
plt.show()

Here is the link to the sound file itself: https://sndup.net/5v3j

And since it is a 5 second-length signal and has 80.000 samples, I want to see it in details by looking 1 second part of the signal.

partial_signal = signal [1 : 16000]
partial_time = time[1 : 16000]
plt.plot(partial_time,partial_signal)
plt.show()

Output of the plot is shown below.

Output

Edit: Looks like image will not show up here is the link to the image: https://i.sstatic.net/honY2.jpg Now I need to find frequency of the envelopes thus rpm of the propeller by using only python.


Solution

  • You can do that quite easily with a fast Fourier transform (FFT) applied on the signal amplitude. Here is an example:

    import wave
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.fft import rfft, rfftfreq
    from scipy.ndimage import gaussian_filter
    
    raw = wave.open('Demon.wav','r')
    signal = raw.readframes(-1)
    signal = np.frombuffer(signal , dtype="int16")
    frate = raw.getframerate()
    time = np.linspace(0,len(signal) / frate,num = len(signal))
    
    
    # Compute the amplitude of the sound signal
    signalAmplitude = signal.astype(np.float64)**2
    
    # Filter the signal to remove very short-timed amplitude modulations (<= 1 ms)
    signalAmplitude = gaussian_filter(signalAmplitude, sigma=frate/1000)
    
    # Compute the frequency amplitude of the FFT signal
    tmpFreq = np.abs(rfft(signalAmplitude))
    
    # Get the associated practical frequency for this signal
    hzFreq = rfftfreq(signal.shape[0], d=1/frate)
    
    finalFrequency = hzFreq[1+tmpFreq[1:].argmax()]
    print(finalFrequency)
    
    
    # Show sound frequency diagram
    plt.xticks(np.arange(21))
    plt.xlim([1, 20]) # Show only interesting low frequencies
    plt.plot(hzFreq, tmpFreq)
    plt.show()
    

    The frequency diagram is the following: enter image description here

    The final detected frequency is 3.0 Hz which is very consistent with what we can hear.