Faster plotting of real time audio signal

2019-04-13 09:27发布

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()

1条回答
再贱就再见
2楼-- · 2019-04-13 10:22

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

import cProfile
cProfile.run('soundplot(stream)')

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:

plt.xlim(extent[0], extent[1])
plt.ylim(-50000000, 50000000)

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 use im=imshow(data, ...), where data is the same size as what you'll be plotting (although it may not need to be). Then, in soundplot use im.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 either show() or draw() to have the figure draw the updated image. You can do the same with the line on the second axis, using line.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.

查看更多
登录 后发表回答