import pyaudio
import numpy as np
import librosa
import time
import serial

arduino = serial.Serial(port='/dev/cu.usbmodem00001', baudrate=2400, timeout=.1) 

def continuous_tempo_detection(sample_rate=22050, chunk_size=2048, analysis_window=5):
    """
    Continuously listen to the microphone and estimate the tempo of the sound in real-time.
    
    :param sample_rate: Sampling rate for the audio
    :param chunk_size: Number of audio frames per buffer
    :param analysis_window: Duration of audio in seconds to analyze for tempo
    """
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                    channels=1,
                    rate=sample_rate,
                    input=True,
                    frames_per_buffer=chunk_size)
    
    print("Starting continuous tempo detection...")
    try:
        while True:
            frames = []
            # Capture enough frames for the analysis window
            for _ in range(int(sample_rate / chunk_size * analysis_window)):
                data = stream.read(chunk_size, exception_on_overflow=False)
                np_data = np.frombuffer(data, dtype=np.float32)
                frames.append(np_data)
            
            # Convert the list of numpy arrays into one numpy array
            audio_data = np.concatenate(frames)
            
            # Estimate the tempo using librosa
            tempo, _ = librosa.beat.beat_track(y=audio_data, sr=sample_rate)
            arduino.write(f"{tempo}\n".encode())
            print(f"Estimated Tempo: {int(tempo)} BPM")
            time.sleep(0.1)
            
            # Optionally, add a sleep time if you want less frequent updates
            time.sleep(1)  # Sleep for 1 second before the next analysis starts
    except KeyboardInterrupt:
        print("Stopping continuous tempo detection.")
    finally:
        # Stop and close the stream
        stream.stop_stream()
        stream.close()
        p.terminate()

# Example usage
if __name__ == "__main__":
    continuous_tempo_detection()