I would like to use streamlit for a live sound analysis app. I would like to use pyaudio to capture audio, process it, then render plots/graphs using streamlit. The problem is there is a lot of latency. If I use the code and simply write the *.png file, I can write a simple HTML poller that seems to perform pretty well in the browser. How can I speed this up so that it plots faster?
import streamlit as st
import numpy as np
import pyaudio
import matplotlib.pyplot as plt
import time
RATE = 44100
NMBR_UPDATES_SECOND = 5
CHUNK = int(RATE / NMBR_UPDATES_SECOND)
def plot_miced_sound(stream):
t_start=time.time()
data = np.frombuffer(stream.read(CHUNK, exception_on_overflow=False),dtype=np.int16)
fig = plt.figure()
plt.plot(data)
plt.title("title")
plt.grid()
plt.axis([0,len(data),-2**16/2,2**16/2])
plt.savefig("sound.png",dpi=50)
print(f"{(time.time()-t_start)*1000} ms")
image_place_holder.write(fig)
plt.close('all')
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
image_place_holder = st.empty()
for i in range(int(20*RATE/CHUNK)):
plot_miced_sound(stream)
stream.stop_stream()
stream.close()
p.terminate()
Hi @theholymath, welcome to the Streamlit community!
In general, matplotlib is slow, as it re-writes the entire scene on each draw. Additionally, there is some latency due to Streamlit it seems, but our engineering team can’t seem to figure out where.
An immediate solution would be to use our add_rows functionality, which is built for this use case:
You could also use one of the web-native Python packages like Altair (vega-lite), Plotly or Bokeh and likely get considerably better performance without the wasteful step of writing to PNG each time.
I came here as a total Streamlit newb looking to cleanly implement an equivalent scenario ― plotting various time series being continuously generated by my application as it processes video input indefinitely.
So as I understand here, or would wishfully wish to understand, we can use DeltaGenerator.add_rows in a loop, as the most efficient way of updating the display from streaming data, is that the case? I’m asking this since as a newb here I’m not quite sure from the documentation link provided above which succinctly says:
Concatenate a dataframe to the bottom of the current one.
Does that alone really enable updating such that the only most recent N seconds of data are always shown? Would you say this solves my very similar use case very idiomatically with Stremlit’s API?
I’m not writing the data to files in my case, rather it’s available in memory as it is continuously being derived from the raw video stream by my code (my data for visualization are feature values being computed continuously from the video frames).
Oh also I’m intrigued, were plotly and bokeh mentioned above as alternatives, implying that the use case is currently better supported using them? or does using Streamlit in concert with them make any kind of useful design?
Yes. In the case of matplotlib, the scene is re-drawn each time, which is wasteful in terms of performance. For the web-based libraries, we are using the intelligent diffing features of React and the component libraries, so only the changes are re-drawn. Additionally, matplotlib has some multi-threading issues that aren’t present in the web based libraries.
So a real-time use case is much more suited to the code snippet I posted above.
This is great, but I do not want to concatenate or append, I want to redraw the new plot as if it is a reel-to-reel tape and I only see the current view. I have an open mic, and I would like to continuously plot the last n milliseconds of processed audio that comes in.
Is there a st.remove_lines or some other way of keeping the same window view as data comes in?
You can write over the dataframe if you choose, it’s really up to you on what the exact requirements are. My point about the append was that it signals to the frontend that everything but the data is expected to be the same, so it doesn’t get re-drawn.
With creative indexing of a dataframe, you can also get the behavior you want…taking the last n_seconds * hz of rows seems like what you’re after?
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.