Real-Time Matplotlib Animation in Streamlit

Hello!
I’m working on a school project about visualizing sound. I’ve made a matplotlib animation that takes the sound input from a device and plots the frequency spectrum and the amplitudes of the frequencies. I would like to be able to import the animation into streamlit to make a ‘UI’ of sorts for the project, but can’t seem to get it to work.

Here is the code I have right now:

import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pyaudio
import streamlit.components.v1 as components

# Initialize PyAudio
p = pyaudio.PyAudio()

# Choose input mode
input_mode = True

# Set parameters for PyAudio
if input_mode:
    DEVICE_INDEX = 1
else:
    DEVICE_INDEX = 18

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = int(p.get_device_info_by_index(DEVICE_INDEX)['maxInputChannels'])
RATE = int(p.get_device_info_by_index(DEVICE_INDEX)['defaultSampleRate'])

# Open an audio stream from the audio device
def open_stream():
    global stream
    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK,
                    input_device_index=DEVICE_INDEX)


# Function to plot the frequency spectrum in real-time with amplitude on the y-axis
def FrequencyAmplitudePlot():
    # Create window and axis for matplotlib
    fig, ax = plt.subplots()
    plt.close(fig)  # Close the figure to prevent it from being shown initially

    # Calculate frequencies for each point on the x-axis (frequencies from 20 Hz to RATE with CHUNK points)
    xf = np.linspace(20, RATE, CHUNK)

    # Create a line in matplotlib for the animation
    line, = ax.plot(xf, np.random.rand(CHUNK))

    # Set axis limits
    ax.set_ylim(0, 5)
    ax.set_xlim(20, RATE / 2)

    # Function to update the plot with new data in real-time
    def animate(frame):
        # Record audio from the audio device
        data = np.frombuffer(stream.read(CHUNK), dtype=np.int16)
        # Apply a Fourier transformation on the data to find the frequency spectrum
        y = np.fft.fft(data)
        y = np.abs(y[0:CHUNK]) * 2 / (256 * CHUNK)

        # Update the plot with the new data
        line.set_ydata(y)
        return line,

    # Open the audio stream
    open_stream()

    # Start the animation
    ani = FuncAnimation(fig, animate, blit=True, interval=20, frames=100)

    st.title('Frequency/Amplitude Plot')
    components.html(ani.to_jshtml(), height=500)

if __name__ == '__main__':
    FrequencyAmplitudePlot()

When running it through Streamlit, I get an application with some frames from the audio visualizer, but it doesn’t work in real-time. Any help is appreciated :smiley:

I’m not sure if the real-time work you’re referring to is caused by the incomplete display of the application interface. :point_down: Please set the ‘height’ higher.

components.html(ani.to_jshtml(), height=600)

Thank you, this certainly was part of it.
It still looks to me like the script just records the audio input while starting the streamlit application, rather than constantly taking the input, so that you can speak into the microphone and instantly see the plot react. This is what happens when just using plt.show() rather than running it through streamlit.