Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Frequency Analysis in Python

I'm trying to use Python to retrieve the dominant frequencies of a live audio input. For the moment I am experimenting using the audio stream my Laptop's built in microphone, but when testing the following code, I am getting very poor results.

    # Read from Mic Input and find the freq's
    import pyaudio
    import numpy as np
    import bge
    import wave

    chunk = 2048

    # use a Blackman window
    window = np.blackman(chunk)
    # open stream
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 1920

    p = pyaudio.PyAudio()
    myStream = p.open(format = FORMAT, channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = chunk)

    def AnalyseStream(cont):
        data = myStream.read(chunk)
        # unpack the data and times by the hamming window
        indata = np.array(wave.struct.unpack("%dh"%(chunk), data))*window
        # Take the fft and square each value
        fftData=abs(np.fft.rfft(indata))**2
        # find the maximum
        which = fftData[1:].argmax() + 1
        # use quadratic interpolation around the max
        if which != len(fftData)-1:
            y0,y1,y2 = np.log(fftData[which-1:which+2:])
            x1 = (y2 - y0) * .5 / (2 * y1 - y2 - y0)
            # find the frequency and output it
            thefreq = (which+x1)*RATE/chunk
            print("The freq is %f Hz." % (thefreq))
        else:
            thefreq = which*RATE/chunk
            print("The freq is %f Hz." % (thefreq))

    # stream.close()
    # p.terminate()

The code is cannibalized from this question, which deals with Fourier Analysis of a wave file. It's in the current modular structure as I'm implementing it with the Blender Game Environment (hence the import bge at the top), but I'm pretty certain my problem lies within the AnalyseStream module.

Any advice you can offer would be much appreciated.

UPDATE: I'm getting the correct values every now and again, but they're found infrequently amongst incorrect values (<10Hz). That and the program runs REALLY slowly.

like image 561
Dave Moore Avatar asked Jan 31 '12 15:01

Dave Moore


2 Answers

Hello find the maximum computing the FFT for real-time analysis becomes a little slow.

If you will not work with complex waveforms to find the frequencies you can use any method based on Time-domain such as zero-crossing where the performance will be better.

In the last year i make one simple function to calc the frequency by Zero-crossing.

#Eng Eder de Souza 01/12/2011
#ederwander
from matplotlib.mlab import find
import pyaudio
import numpy as np
import math


chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 20


def Pitch(signal):
    signal = np.fromstring(signal, 'Int16');
    crossing = [math.copysign(1.0, s) for s in signal]
    index = find(np.diff(crossing));
    f0=round(len(index) *RATE /(2*np.prod(len(signal))))
    return f0;


p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)

for i in range(0, RATE / chunk * RECORD_SECONDS):
    data = stream.read(chunk)
    Frequency=Pitch(data)
    print "%f Frequency" %Frequency

ederwander

like image 132
ederwander Avatar answered Oct 17 '22 05:10

ederwander


There is also the function scipy.signal.lombscargle that calculates the Lomb-Scargle periodogram and is available since v0.10.0. This method should work even for unevenly sampled signals. It seems that the data's mean has to be subtracted for this method to work properly although this is not mentioned in the documentation. More information can be found in the scipy reference guide: http://docs.scipy.org/doc/scipy/reference/tutorial/signal.html#lomb-scargle-periodograms-spectral-lombscargle

like image 21
gypaetus Avatar answered Oct 17 '22 05:10

gypaetus