Поскольку я интересовался pyaudio, я попытался сделать ваш код работоспособным с функциональностью, которую, я думаю, вы намеревались. Вы можете построить отсюда, чтобы добавить случайный сигнал к синусоиде или использовать разные сигналы вместе. Основные изменения в том, что все функции теперь в классе App. Также для одновременного запуска звука и сюжета я запускаю звук в другом потоке. Надеюсь, что это полезно для вас.
import threading
import random
import numpy as np
import tkinter as tk
from tkinter import Label, Button, Entry, TOP, BOTH
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import pyaudio
DEFAULT_FREQUENCY = 220
DEFAULT_DURATION = 5
VOLUME = 0.3
INTERVAL = 100
class App(tk.Frame):
def __init__(self, root):
self.root = root
self.root.title("SHM Simulation - Testing")
label = tk.Label(self.root, text="SHM Simulation")
label.pack()
self.fig, self.ax = plt.subplots(figsize=(5, 5))
self.canvas = FigureCanvasTkAgg(self.fig, master=root)
self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
self.ax.set_ylim(-1.1, 1.1)
self.ax.set_xlim(-0.1, 2 * np.pi + 0.1)
self.quit_button = Button(master=self.root, text='Quit', command=self.quit)
self.quit_button.pack(side='right')
self.sound_button = Button(self.root, text='Sound off', command=self.toggle_sound)
self.sound_button.pack(side='right')
self.plot_button = Button(self.root, text='Start')
self.plot_button.pack(side='right')
self.plot_button.bind('<Button-1>', self.on_click)
tk.Label(self.root, text='Frequency').pack(side='left')
self.frequency_entry = Entry(self.root)
self.frequency_entry.insert(0, DEFAULT_FREQUENCY)
self.frequency_entry.pack(side='left')
tk.Label(self.root, text='Duration').pack(side='left')
self.duration_entry = Entry(self.root)
self.duration_entry.insert(0, DEFAULT_DURATION)
self.duration_entry.pack(side='left')
self.ani = None
self.running = False
self.audio = pyaudio.PyAudio()
self.sound = False
self.samples = np.array([])
def quit(self):
self.audio.terminate()
self.root.quit()
def get_data(self, frame):
self.ydata = np.sin(self.frequency * self.xdata + frame)
def update_frame(self, frame):
self.get_data(frame)
self.line.set_data(self.xdata, self.ydata)
if frame > self.duration - 1.1 * INTERVAL / 1000 :
self.ani = None
self.running = False
self.plot_button.config(text='Start')
return self.line,
def start_animation(self):
self.xdata = []
self.ydata = []
self.line, = self.ax.plot(self.xdata, self.ydata, lw=3)
self.xdata = np.arange(0, 2 * np.pi, 0.01)
duration_range = np.arange(0, self.duration, INTERVAL / 1000)
self.ani = FuncAnimation(self.fig,
self.update_frame,
frames=duration_range,
interval=INTERVAL,
repeat=False)
if self.sound:
''' start audio in a seperate thread as otherwise audio and
plot will not be at the same time
'''
x = threading.Thread(target=self.play_sound)
x.start()
def callback(self, in_data, frame_count, time_info, status):
out = self.samples[:frame_count]
self.samples = self.samples[frame_count:]
return (out*VOLUME, pyaudio.paContinue)
def play_sound(self):
fs = 44100
self.samples = (np.sin(2 * np.pi * np.arange(fs * self.duration) *
self.frequency / fs)).astype(np.float32)
stream = self.audio.open(format=pyaudio.paFloat32,
channels=1,
rate=fs,
output=True,
stream_callback=self.callback)
stream.start_stream()
# pause audio when self.running is False
while stream.is_active():
while not self.running:
if stream.is_active:
stream.stop_stream()
else:
pass
if self.running and not stream.is_active():
stream.start_stream()
else:
pass
stream.stop_stream()
stream.close()
def toggle_sound(self):
if self.sound:
self.sound_button.configure(text='Sound Off')
else:
self.sound_button.configure(text='Sound On ')
self.sound = not self.sound
def on_click(self, event):
if self.ani is None:
try:
self.ax.lines.pop(0)
except IndexError:
pass
try:
self.frequency = int(self.frequency_entry.get())
self.duration = int(self.duration_entry.get())
except ValueError:
self.frequency = DEFAULT_FREQUENCY
self.frequency_entry.insert(0, DEFAULT_FREQUENCY)
self.duration = DEFAULT_DURATION
self.duration_entry.insert(0, DEFAULT_DURATION)
self.start_animation()
self.running = True
self.plot_button.config(text='Pause')
print('animation started ...')
return
if self.running:
self.ani.event_source.stop()
self.plot_button.config(text='Run ')
else:
self.ani.event_source.start()
self.plot_button.config(text='Pause')
self.running = not self.running
def main():
root = tk.Tk()
app = App(root)
root.mainloop()
if __name__ == '__main__':
main()