import time
import pygame
import librosa
import warnings
import threading

class MusicFader:
    """音频淡入淡出处理器
    Copyright (c) 2026 hhcgchpspk
    
    Attributes:
        fadeDuration (float): 淡入/淡出时长（秒）
        musicDuration (float): 音频总时长（秒）
        steps (int): 淡入淡出的步数
    """
    def __init__(self,fadeDuration,musicDuration):#淡出淡入时长;音频时长
        """初始化MusicFader
        
        Args:
            fadeDuration: 淡入/淡出时长（秒），必须大于0
            musicDuration: 音频总时长（秒），必须大于0
            
        Raises:
            ValueError: 如果参数无效
        """

        if fadeDuration<=0:
            raise ValueError('fadeDuration必须大于0')
        if musicDuration<=0:
            raise ValueError('musicDuration必须大于0')
        if musicDuration <fadeDuration*2:
            warnings.warn(f"warning:淡入淡出总时长{fadeDuration*2}超过音乐总时长{musicDuration}")
            
        self.fadeDuration=fadeDuration
        self.musicDuration=musicDuration
        self.steps=30

    def fadeInOut(self,musicPath=None):
        """执行淡入淡出效果（在后台线程执行，不阻塞主线程）
        
        Args:
            musicPath: 音频文件路径
            
        Returns:
            threading.Thread: 执行播放的线程对象
        """
        self._thread=threading.Thread(target=self._execute_fadeInOut,args=(musicPath,))
        self._thread.daemon=True#设置为守护线程
        self._thread.start()
        return self._thread

    def _execute_fadeInOut(self,musicPath):#淡入淡出
        """执行淡入效果
        
        Args:
            musicPath: 音频文件路径
            
        Raises:
            FileNotFoundError: 音频文件不存在
            pygame.error: Pygame加载音频失败
        """
        try:
            pygame.mixer.init()#初始化mixer
            pygame.mixer.music.load(musicPath)#载入音频
        except pygame.error as e:
            raise Exception(f"加载音频文件失败{e}")
        except Exception as e:
            raise FileNotFoundError(f"无法找到或读取文件:{musicPath}") from e

        try:
            pygame.mixer.music.set_volume(0.0)#设置初始音量
            pygame.mixer.music.play()#播放音乐

            stepTime=self.fadeDuration/self.steps
            for i in range(self.steps+1):
                volume=i/self.steps
                pygame.mixer.music.set_volume(volume)
                time.sleep(stepTime)
                ##逐步提高音量
            time.sleep(self.musicDuration-self.fadeDuration*2)#音乐主体部分(减去了淡入淡出时间)
        except Exception as e:
            print(f"淡入音频时发生错误:{e}")

        """执行淡出效果
            
        """
        try:
            currentVolume=pygame.mixer.music.get_volume()#获取现在音量
            if currentVolume<=0:#如果现在音量为0,停止播放
                pygame.mixer.music.stop()
                return
            
            stepTime=self.fadeDuration/self.steps
            for i in range(self.steps+1):
                volume=currentVolume*(1- i/self.steps)
                pygame.mixer.music.set_volume(max(0,volume))
                time.sleep(stepTime)
                ##逐步降低音量
            pygame.mixer.music.stop()
        except Exception as e:
            print(f"淡出音频时发生错误:{e}")

    @staticmethod#设置为静态方法来在创建实例之前获取音频时长
    def getAudioDuration(filePath):
        duration=librosa.get_duration(path=filePath)#读取元数据
        return round(duration,2)#保留两位小数

    
