100 lines
3.4 KiB
Python
Executable File
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()
|