Created
October 1, 2019 05:23
-
-
Save Factoid/9ff0da2da33ada40d5bc556700477489 to your computer and use it in GitHub Desktop.
Zoom FFT with realtime audio source
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pyqtgraph.Qt import QtGui, QtCore | |
import numpy as np | |
import pyqtgraph as pg | |
from scipy import signal | |
import math | |
import random | |
import cmath | |
import pyaudio | |
signalData = [] | |
BINS = 1024 | |
RATE = 48000 | |
CHUNK = BINS*5 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = 1 | |
app = QtGui.QApplication([]) | |
win = pg.GraphicsWindow(title="Basic plotting examples") | |
win.resize(1900,1080) | |
win.setWindowTitle('pyqtgraph example: Plotting') | |
signalGraph = win.addPlot(title="Signal Graph") | |
sigPlot = signalGraph.plot(pen='y') | |
fftOutput = win.addPlot(title="FFT Output") | |
fftPlot = fftOutput.plot(pen='y') | |
zoomFFTOutput = win.addPlot(title="Zoom FFT Output") | |
zoomFFTPlot = zoomFFTOutput.plot(pen='y') | |
def gen_tone_data( sampleRate, samples ): | |
f1 = 200 | |
f2 = 220 | |
dt = 1/sampleRate | |
pi2dt = 2.0*math.pi*dt; | |
return [ 2*math.sin(pi2dt*f1*i)+1*math.sin(pi2dt*f2*i) for i in range(samples)] | |
#return [ 2*math.sin(pi2dt*f1*i)+1*math.sin(pi2dt*f2*i)+(random.random()-0.5) for i in range(samples)] | |
def myfft( signalBuf, sampleRate, nBins, offset ): | |
outBins = (nBins//2) | |
binWidth = (sampleRate/2) / outBins | |
# print( "sampleRate", sampleRate, "min", offset, "outbins", outBins, "binWidth", binWidth, "max", (outBins-1)*binWidth+offset ) | |
return [ [ offset + i * binWidth for i in range(outBins) ], [abs(x) for x in np.fft.fft(signalBuf,n=nBins)[0:outBins] ] ] | |
def shiftFrequency( samples, sampleRate, adjustBy ): | |
dt = 1/sampleRate | |
pi2dtcf = 2*math.pi*dt*adjustBy | |
vals = [ cmath.exp(1j*pi2dtcf*i)*samples[i] for i in range(len(samples)) ] | |
return vals | |
def zoom_fft( samples, sampleRate, nBins, fStart, fEnd ): | |
bandwidth = (fEnd - fStart) | |
decFactor = sampleRate//bandwidth//2 | |
shifted = shiftFrequency( samples, sampleRate, -fStart ) | |
resampled = signal.decimate(shifted,decFactor,ftype='fir') | |
newSampleRate = sampleRate//decFactor | |
# print("decFactor",decFactor,"newSampleRate",newSampleRate) | |
vals = myfft( resampled, newSampleRate, nBins, fStart ) | |
return vals | |
def update(): | |
pass | |
def callback( in_data, frame_count, time_info, status ): | |
signalData = np.frombuffer(in_data,dtype=np.int16) | |
#signalData = gen_tone_data( RATE, frame_count ) | |
sigPlot.setData( signalData ) | |
fftData = myfft(signalData, RATE, BINS, 0.0) | |
fftPlot.setData( *fftData ) | |
zoomFFTData = zoom_fft( signalData, RATE, BINS, 100, 500 ) | |
zoomFFTPlot.setData( *zoomFFTData ) | |
return (None, pyaudio.paContinue) | |
p = pyaudio.PyAudio() | |
stream = p.open( format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK, stream_callback=callback ) | |
timer = QtCore.QTimer() | |
timer.timeout.connect(update) | |
timer.start(1) | |
app.exec_() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment