import numpy as np
import sounddevice as sd
from scipy import signal
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk, messagebox

class TomatisMethodController:
    def __init__(self, ax: plt.Axes, canvas: FigureCanvasTkAgg, root: tk.Tk):
        self.ax = ax
        self.canvas = canvas
        self.root = root
        self.is_playing = False
        self.left_freq = 200
        self.right_freq = 800
        self.left_q = 1.0
        self.right_q = 1.0
        self.left_volume = 0.3
        self.right_volume = 0.3
        self.stream = None
        self.sample_rate = 44100
        self.buffer_size = 1024
        self.noise_type = 'white'

        self.update_filters()

        self.left_state = np.zeros(max(len(self.b_left_filter), len(self.a_left_filter)) - 1)
        self.right_state = np.zeros(max(len(self.b_right_filter), len(self.a_right_filter)) - 1)

    def update_filters(self):
        self.b_left_filter, self.a_left_filter = self.create_bandpass_filter(self.left_freq, self.left_q)
        self.b_right_filter, self.a_right_filter = self.create_bandpass_filter(self.right_freq, self.right_q)

    def create_bandpass_filter(self, freq, q):
        nyquist = 0.5 * self.sample_rate
        bandwidth = freq / q
        low = (freq - bandwidth / 2) / nyquist
        high = (freq + bandwidth / 2) / nyquist
        low = max(low, 0.001)
        high = min(high, 0.999)
        b, a = signal.butter(2, [low, high], btype='band')
        return b, a

    def generate_noise(self, num_frames):
        return np.random.normal(0, 1, num_frames)

    def generate_tomatis_sound(self, num_frames):
        left_noise = self.generate_noise(num_frames)
        right_noise = self.generate_noise(num_frames)

        left_filtered, self.left_state = signal.lfilter(self.b_left_filter, self.a_left_filter, left_noise, zi=self.left_state)
        right_filtered, self.right_state = signal.lfilter(self.b_right_filter, self.a_right_filter, right_noise, zi=self.right_state)

        left_output = left_filtered * self.left_volume
        right_output = right_filtered * self.right_volume

        return np.column_stack((left_output, right_output))

    def audio_callback(self, outdata, frames, time_info, status):
        data = self.generate_tomatis_sound(frames)
        outdata[:] = data.astype(np.float32)

    def start(self):
        if not self.is_playing:
            self.stream = sd.OutputStream(
                channels=2,
                callback=self.audio_callback,
                samplerate=self.sample_rate,
                blocksize=self.buffer_size,
                dtype='float32'
            )
            self.stream.start()
            self.is_playing = True
            self.update_plot()

    def stop(self):
        if self.is_playing and self.stream is not None:
            self.stream.stop()
            self.stream.close()
            self.stream = None
            self.is_playing = False
            self.ax.clear()
            self.canvas.draw()

    def update_settings(self):
        self.update_filters()
        if self.is_playing:
            self.update_plot()

    def update_plot(self):
        if self.is_playing:
            num_frames = self.buffer_size
            sound = self.generate_tomatis_sound(num_frames)
            t = np.linspace(0, num_frames / self.sample_rate, num_frames)
            self.ax.clear()
            self.ax.plot(t, sound[:, 0], label='Left', color='blue')
            self.ax.plot(t, sound[:, 1], label='Right', color='red')
            self.ax.set_title('Tomatis Waveforms')
            self.ax.set_xlabel('Time (s)')
            self.ax.set_ylabel('Amplitude')
            self.ax.legend()
            self.canvas.draw()
            self.root.after(100, self.update_plot)

class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Tomatis Method Controller")
        self.geometry("800x600")

        fig, ax = plt.subplots()
        canvas = FigureCanvasTkAgg(fig, self)
        canvas_widget = canvas.get_tk_widget()
        canvas_widget.pack(fill=tk.BOTH, expand=True)

        self.controller = TomatisMethodController(ax, canvas, self)

        settings_frame = ttk.LabelFrame(self, text="Settings")
        settings_frame.pack(fill="x", expand=True)

        # Frequency and Q-factor sliders
        ttk.Label(settings_frame, text="Left Frequency:").grid(row=0, column=0)
        self.left_freq_slider = ttk.Scale(settings_frame, from_=50, to=1000, orient='horizontal', command=self.update_frequency)
        self.left_freq_slider.set(self.controller.left_freq)
        self.left_freq_slider.grid(row=0, column=1)

        ttk.Label(settings_frame, text="Right Frequency:").grid(row=1, column=0)
        self.right_freq_slider = ttk.Scale(settings_frame, from_=50, to=1000, orient='horizontal', command=self.update_frequency)
        self.right_freq_slider.set(self.controller.right_freq)
        self.right_freq_slider.grid(row=1, column=1)

        ttk.Label(settings_frame, text="Left Q-Factor:").grid(row=2, column=0)
        self.left_q_slider = ttk.Scale(settings_frame, from_=0.1, to=10.0, orient='horizontal', command=self.update_q_factor)
        self.left_q_slider.set(self.controller.left_q)
        self.left_q_slider.grid(row=2, column=1)

        ttk.Label(settings_frame, text="Right Q-Factor:").grid(row=3, column=0)
        self.right_q_slider = ttk.Scale(settings_frame, from_=0.1, to=10.0, orient='horizontal', command=self.update_q_factor)
        self.right_q_slider.set(self.controller.right_q)
        self.right_q_slider.grid(row=3, column=1)

        # Volume sliders
        ttk.Label(settings_frame, text="Left Volume:").grid(row=4, column=0)
        self.left_volume_slider = ttk.Scale(settings_frame, from_=0, to=1, orient='horizontal', command=self.update_volume)
        self.left_volume_slider.set(self.controller.left_volume)
        self.left_volume_slider.grid(row=4, column=1)

        ttk.Label(settings_frame, text="Right Volume:").grid(row=5, column=0)
        self.right_volume_slider = ttk.Scale(settings_frame, from_=0, to=1, orient='horizontal', command=self.update_volume)
        self.right_volume_slider.set(self.controller.right_volume)
        self.right_volume_slider.grid(row=5, column=1)

        start_button = ttk.Button(self, text="Start", command=self.controller.start)
        start_button.pack(side=tk.BOTTOM, pady=5)

        stop_button = ttk.Button(self, text="Stop", command=self.controller.stop)
        stop_button.pack(side=tk.BOTTOM, pady=5)

    def update_frequency(self, _):
        self.controller.left_freq = self.left_freq_slider.get()
        self.controller.right_freq = self.right_freq_slider.get()
        self.controller.update_settings()

    def update_q_factor(self, _):
        self.controller.left_q = self.left_q_slider.get()
        self.controller.right_q = self.right_q_slider.get()
        self.controller.update_settings()

    def update_volume(self, _):
        self.controller.left_volume = self.left_volume_slider.get()
        self.controller.right_volume = self.right_volume_slider.get()
        self.controller.update_settings()

if __name__ == "__main__":
    app = Application()
    app.mainloop() улучши код - синтез частот более эффективный тренинг мозга через ухо, цель активация внимания и памяти