import pygame
import numpy as np
import pyaudio
import threading
from parametres import *
# 🎵 Paramètres audio
SAMPLE_RATE = 44100
BUFFER_SIZE = 1024
CHANNELS = 1

# 🔥 Initialisation de PyAudio
p = pyaudio.PyAudio()
stream = p.open(
    format=pyaudio.paInt16,
    channels=CHANNELS,
    rate=SAMPLE_RATE,
    output=True,
    frames_per_buffer=BUFFER_SIZE,
)

# 🎹 Notes associées aux touches du clavier
touches = [pygame.K_q,pygame.K_s,pygame.K_d,pygame.K_f,pygame.K_g,pygame.K_h,pygame.K_j,pygame.K_k,pygame.K_l,pygame.K_m,249,pygame.K_ASTERISK,pygame.K_RETURN]
KEYS = {touches[i]:frequences[i] for i in range(min(len(touches),len(frequences)))}

active_keys = set()  # Stocke les touches pressées
phase_memory = {}  # Phase des notes

def generate_wave(frequencies, frames):
    """Génère un signal sonore à partir des notes pressées."""
    global phase_memory

    t = np.arange(frames) / SAMPLE_RATE
    wave = np.zeros(frames, dtype=np.float32)

    for freq in frequencies:
        if freq not in phase_memory:
            phase_memory[freq] = 0.0  # Initialisation

        # Génération avec phase alignée
        phase = phase_memory[freq]
        sin_wave = np.sin(phase + 2 * np.pi * freq * t)
        wave += sin_wave

        # Mise à jour de la phase pour éviter les glitchs
        phase_memory[freq] += (2 * np.pi * freq * frames / SAMPLE_RATE)
        phase_memory[freq] %= (2 * np.pi)

    # Normalisation et conversion
    wave = (wave / max(1, len(frequencies)) * 32767).astype(np.int16)

    return wave

def audio_loop():
    """Thread qui génère et joue le son en continu."""
    while True:
        active_frequencies = [KEYS[key] for key in active_keys if key in KEYS]

        if active_frequencies:
            wave = generate_wave(active_frequencies, BUFFER_SIZE)
        else:
            wave = np.zeros(BUFFER_SIZE, dtype=np.int16)  # Silence

        stream.write(wave.tobytes())

# 🎶 Démarrer le thread audio
threading.Thread(target=audio_loop, daemon=True).start()

# 🎹 Interface graphique avec Pygame
pygame.init()
WIDTH, HEIGHT = 400, 300
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Synthé Pygame")

def update():
    """Met à jour les touches pressées."""
    global active_keys
    keys = pygame.key.get_pressed()
    active_keys = {key for key in KEYS if keys[key]}

def draw():
    """Affiche les touches pressées."""
    screen.fill((0, 0, 0))
    font = pygame.font.Font(None, 36)
    y = 10
    nb = len(KEYS.keys())
    for key, freq in KEYS.items():
        text = f"{chr(key).upper()} ({round(freq)} Hz): {'ON' if key in active_keys else 'OFF'}"
        img = font.render(text, True, (255, 255, 255))
        screen.blit(img, (50, y))
        y += (300-30)/nb
    pygame.display.flip()

# 🎮 Boucle principale
running = True
clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    update()
    draw()
    clock.tick(60)

pygame.quit()
stream.stop_stream()
stream.close()
p.terminate()
