Files
Class-Widgets/play_audio.py
2025-06-11 15:48:20 +08:00

100 lines
3.4 KiB
Python
Executable File

import os
import time
import pygame
import pygame.mixer
from PyQt5.QtCore import QThread, pyqtSignal
from loguru import logger
import conf
from file import config_center
from generate_speech import TTSEngine
sound_cache = {}
class PlayAudio(QThread):
play_back_signal = pyqtSignal(bool)
def __init__(self, file_path: str, tts_delete_after: bool = False):
super().__init__()
self.file_path = file_path
self.tts_delete_after = tts_delete_after
def run(self):
play_audio(self.file_path, self.tts_delete_after)
self.play_back_signal.emit(True)
def play_audio(file_path: str, tts_delete_after: bool = False):
# global sound # Removed global sound variable
sound = None # Use local variable
channel = None # Initialize channel
try:
if not os.path.exists(file_path):
raise FileNotFoundError(f"音频文件不存在: {file_path}")
if not pygame.mixer.get_init():
try:
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)
except pygame.error:
logger.warning("标准 Mixer 初始化失败,尝试兼容模式...")
try:
pygame.mixer.init(frequency=22050, size=-16, channels=1, buffer=1024)
logger.info("使用兼容设置成功初始化 Mixer")
except pygame.error as e_fallback:
logger.error(f"Pygame mixer 初始化失败: {e_fallback}")
return
# 检查文件是否可读
if os.path.getsize(file_path) <= 0:
start_time = time.time()
while time.time() - start_time < 4:
if os.path.getsize(file_path) > 0:
break
time.sleep(0.1)
else:
raise IOError("音频文件写入超时")
try:
if file_path in sound_cache:
sound = sound_cache[file_path]
logger.debug(f'使用缓存音频: {file_path}')
else:
sound = pygame.mixer.Sound(file_path)
sound_cache[file_path] = sound
logger.debug(f'缓存音频: {file_path}')
except pygame.error as e_load:
logger.error(f"加载音频文件失败: {file_path} | 错误: {e_load}")
return
volume = int(config_center.read_conf('Audio', 'volume')) / 100
sound.set_volume(volume) # 设置Sound对象的音量
channel = sound.play()
if channel:
channel.set_volume(volume) # 设置Channel对象的音量
while channel.get_busy():
pygame.time.wait(100)
else:
logger.error(f"无法获取播放通道: {file_path}")
logger.debug(f'成功播放音频: {file_path}')
if tts_delete_after:
tts = TTSEngine()
tts.delete_audio_file(file_path)
except FileNotFoundError as e:
logger.error(f'音频文件未找到 | 路径: {file_path} | 错误: {str(e)}')
except IOError as e:
logger.error(f'音频文件读取错误或超时 | 路径: {file_path} | 错误: {str(e)}')
except pygame.error as e:
logger.error(f'Pygame 播放错误 | 路径: {file_path} | 错误: {str(e)}')
except Exception as e:
logger.error(f'未知播放失败 | 路径: {file_path} | 错误: {str(e)}')
finally:
if channel:
channel.stop()
if sound:
sound.stop()