diff --git a/Spectogram.py b/Spectogram.py index d68080f..2d6c276 100644 --- a/Spectogram.py +++ b/Spectogram.py @@ -3,22 +3,29 @@ import numpy as np import scipy.signal import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +from matplotlib.backends.backend_tkagg +import FigureCanvasTkAgg +import threading +import queue import subprocess -""" RealTime Audio Spectrogram plot """ +# Global variables +_VARS = {"window": None, "stream": None, "audioData": np.array([])} +audio_buffer = queue.Queue(maxsize=10) # Buffer for audio data +stop_event = threading.Event() # Event to signal stopping of the audio stream + + +# PySimpleGUI initialization # VARS CONSTS: _VARS = {"window": False, "stream": False, "audioData": np.array([]), "current_visualizer_process": None} # pysimpleGUI INIT: AppFont = "Any 16" sg.theme("DarkBlue3") - -menu_layout = [ - ['Run Visualizers', ['Amplitude-Frequency-Visualizer', 'Waveform', 'Spectrogram', 'Intensity-vs-Frequency-and-time']], +Visualizers', ['Amplitude-Frequency-Visualizer', 'Waveform', 'Spectrogram', 'Intensity-vs-Frequency-and-time']], ] layout = [ @@ -42,47 +49,39 @@ _VARS["window"] = sg.Window("Mic to spectrogram plot + Max Level", layout, finalize=True) graph = _VARS["window"]["graph"] -# INIT vars: -CHUNK = 1024 # Samples: 1024, 512, 256, 128 -RATE = 44100 # Equivalent to Human Hearing at 40 kHz -INTERVAL = 1 # Sampling Interval in Seconds -> Interval to listen -TIMEOUT = 10 # In ms for the event loop +# PyAudio initialization pAud = pyaudio.PyAudio() +CHUNK = 1024 +RATE = 44100 +INTERVAL = 1 +TIMEOUT = 100 # Adjusted timeout value -try: - pAud.get_device_info_by_index(0) -except pyaudio.CoreError as e: - print(f"Error initializing PyAudio: {e}") - pAud = None -# FUNCTIONS: - - -# PySimpleGUI plots: -def draw_figure(canvas, figure): - figure_canvas_agg = FigureCanvasTkAgg(figure, canvas) - figure_canvas_agg.draw() - figure_canvas_agg.get_tk_widget().pack(side="top", fill="both", expand=1) - return figure_canvas_agg - - -# pyaudio stream: def stop(): - if _VARS["stream"]: + if _VARS["stream"] is not None and _VARS["stream"].is_active(): + stop_event.set() # Signal the audio processing thread to stop _VARS["stream"].stop_stream() _VARS["stream"].close() _VARS["stream"] = None _VARS["window"]["-PROG-"].update(0) + _VARS["window"]["Stop"].update(disabled=True) + _VARS["window"]["Listen"].update(disabled=False) + stop_event.clear() # Reset the event for the next use + + _VARS["window"]["Stop"].Update(disabled=True) _VARS["window"]["Listen"].Update(disabled=False) # callback: + def callback(in_data, frame_count, time_info, status): - _VARS["audioData"] = np.frombuffer(in_data, dtype=np.int16) + if stop_event.is_set(): + return (in_data, pyaudio.paComplete) + audio_buffer.put(np.frombuffer(in_data, dtype=np.int16)) return (in_data, pyaudio.paContinue) def listen(): - _VARS["window"]["Stop"].Update(disabled=False) - _VARS["window"]["Listen"].Update(disabled=True) + _VARS["window"]["Stop"].update(disabled=False) + _VARS["window"]["Listen"].update(disabled=True) _VARS["stream"] = pAud.open( format=pyaudio.paInt16, channels=1, @@ -93,7 +92,50 @@ def listen(): ) _VARS["stream"].start_stream() -def close_current_visualizer(): + +def audio_processing(ax, fig_agg): + while not stop_event.is_set(): + try: + audio_data = audio_buffer.get(timeout=1) + if audio_data.size != 0: + _VARS["window"]["-PROG-"].update(np.amax(audio_data)) + f, t, Sxx = scipy.signal.spectrogram(audio_data, fs=RATE) + ax.clear() + ax.pcolormesh(t, f, Sxx, shading="gouraud") + ax.set_ylabel("Frequency [Hz]") + ax.set_xlabel("Time [sec]") + fig_agg.draw() + except queue.Empty: + continue + +def main(): + # Initialization + fig, ax = plt.subplots() + fig_agg = FigureCanvasTkAgg(fig, _VARS["window"]["graph"].TKCanvas) + fig_agg.draw() + fig_agg.get_tk_widget().pack(side="top", fill="both", expand=1) + + # Multithreading for audio processing + audio_thread = threading.Thread(target=audio_processing, args=(ax, fig_agg)) + audio_thread.daemon = True + audio_thread.start() + + # Event loop + while True: + event, values = _VARS["window"].read(timeout=TIMEOUT) + if event == sg.WINDOW_CLOSED or event == "Exit": + stop() + pAud.terminate() + break + elif event == "Listen": + listen() + elif event == "Stop": + stop() + +if __name__ == "__main__": + main() + + def close_current_visualizer(): if _VARS["current_visualizer_process"] and _VARS["current_visualizer_process"].poll() is None: _VARS["current_visualizer_process"].kill() @@ -154,3 +196,4 @@ def close_current_visualizer(): ax.set_ylabel("Frequency [Hz]") # set the y-axis label ax.set_xlabel("Time [sec]") # set the x-axis label fig_agg.draw() # redraw the figure +