I have a piece of code that takes real time audio signal from audio jack of my laptop and plots its graph after some basic filtering. The problem I am facing is that the real time plotting is getting slower and slower as the program is running ahead.
Any suggestions to make this plotting faster and proceed at constant rate?? I think animation function will make it faster but was not able to formulate according to my requirement
import pyaudio
import numpy as np
import time
import matplotlib.pyplot as plt
import scipy.io.wavfile
from scipy.signal import butter, lfilter
import wave
plt.rcParams["figure.figsize"] = 8,4
RATE = 44100
CHUNK = int(RATE/2) # RATE / number of updates per second
#Filter co-efficients
nyq = 0.5 * RATE
low = 3000 / nyq
high = 6000 / nyq
b, a = butter(7, [low, high], btype='band')
#Figure structure
fig, (ax, ax2) =plt.subplots(nrows=2, sharex=True)
x = np.linspace(1, CHUNK, CHUNK)
extent = [x[0] - (x[1] - x[0]) / 2., x[-1] + (x[1] - x[0]) / 2., 0, 1]
def soundplot(stream):
t1=time.time()
data = np.array(np.fromstring(stream.read(CHUNK),dtype=np.int32))
y1 = lfilter(b, a, data)
ax.imshow(y1[np.newaxis, :], cmap="jet", aspect="auto")
plt.xlim(extent[0], extent[1])
plt.ylim(-50000000, 50000000)
ax2.plot(x, y1)
plt.pause(0.00001)
plt.cla() # which clears data but not axes
y1 = []
print(time.time()-t1)
if __name__=="__main__":
p=pyaudio.PyAudio()
stream=p.open(format=pyaudio.paInt32,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK)
for i in range(RATE):
soundplot(stream)
stream.stop_stream()
stream.close()
p.terminate()
This is a little long for a comment, and since you're asking for suggestions I think it's a semi-complete answer. There's more info and examples online about getting realtime plotting with matplotlib, if you need ideas beyond what's here. The library wasn't designed for this, but it's possible.
First step, profile the code. You can do this with
That will show where most of the time is being spent. Without doing that, I'll give a few tips, but be aware that profiling may show other causes.
First, you want to eliminate redundant function calls in the function
soundplot
. Both of the following are unnecessary:They can be called once in initialization code.
imshow
updates these automatically, but for speed you shouldn't call that every time. Instead, in some initialization code outside the function useim=imshow(data, ...)
, where data is the same size as what you'll be plotting (although it may not need to be). Then, insoundplot
useim.set_data(y1[np.newaxis, :])
. Not having to recreate the image object each iteration will speed things up immensely.Since the image object remains through each iteration, you'll also need to remove the call to
cla()
, and replace it with eithershow()
ordraw()
to have the figure draw the updated image. You can do the same with the line on the second axis, usingline.set_ydata(y)
.Please post the before and after rate it runs at, and let me know if that helps.
Edit: some quick profiling of similar code suggests a 100-500x speedup, mostly from removing
cla()
.Also looking at your code, the reason for it slowing down is that
cla
isn't ever called on the first axis. Eventually there will be hundreds of images drawn on that axis, slowing matplotlib to a crawl.