用 GPT4o写的。
调用的edge大声朗读的功能,需要联网才能用。
所以延时比较大,最少都要一秒。
下载地址:
程序主要功能:监控剪贴板内容的变化,并将变化的文本内容转为语音播放。
功能列表
发音人选择:用户可以选择不同的发音人来进行文本转语音。
语速设置:用户可以设置语速/
快捷键监控:用户可以选择是否启用快捷键监控功能,并指定快捷键组合。
只转换英文内容:用户可以选择是否只转换英文内容,忽略其他语言的文本。
配置管理:程序启动时检查是否有配置文件 jtb.ini,存在则读取配置;不存在则让用户设置并保存配置。
安装依赖库
您可以使用以下命令通过 pip 安装以上所需的库:
pip install pyperclip edge-tts pygame keyboard configparser
源码:
# -*- coding: utf-8 -*-
import pyperclip
import time
import edge_tts
import os
import asyncio
import pygame
import keyboard # 用于检测全局快捷键
import configparser
# 初始化 pygame
pygame.mixer.init()
# 发音人列表
voices = {
1: 'zh-CN-XiaoxiaoNeural',
2: 'zh-CN-XiaoyiNeural',
3: 'zh-CN-YunjianNeural',
4: 'zh-CN-YunxiNeural',
5: 'zh-CN-YunxiaNeural',
6: 'zh-CN-YunyangNeural'
}
config = configparser.ConfigParser()
if os.path.exists("jtb.ini"):
config.read("jtb.ini")
saved_settings = config["SETTINGS"]
rate_input = saved_settings.get("rate", "1.0").strip()
voice_choice = int(saved_settings.get("voice_choice", "1"))
use_hotkey = saved_settings.get("use_hotkey", "n") == 'y'
hotkey = saved_settings.get("hotkey", "")
convert_english_only = saved_settings.get("convert_english_only", "n") == 'y'
else:
rate_input = input("请输入语速(例如 '1.0' 表示正常速度,'1.5' 表示1.5倍速度,默认是 '1.0'): ").strip()
try:
rate = float(rate_input)
rate_percent = f"+{int((rate - 1) * 100)}%"
except ValueError:
rate_percent = "+0%" # 默认语速
# 打印发音人选项
print("请选择发音人:")
for key, value in voices.items():
print(f"{key}: {value}")
voice_choice = int(input("请输入发音人编号: "))
selected_voice = voices.get(voice_choice, 'zh-CN-XiaoxiaoNeural') # 默认使用 XiaoXiao
use_hotkey = input("是否启用快捷键监控功能?(y/n): ").strip().lower() == 'y'
# 如果启用快捷键监控功能,用户定义一个快捷键组合
if use_hotkey:
print("请按下要监控的快捷键组合...")
hotkey = keyboard.read_hotkey()
print(f"您选择的快捷键组合是: {hotkey}")
else:
hotkey = ""
convert_english_only = input("是否只转换英文内容?(y/n): ").strip().lower() == 'y'
config["SETTINGS"] = {
"rate": rate_input,
"voice_choice": voice_choice,
"use_hotkey": 'y' if use_hotkey else 'n',
"hotkey": hotkey,
"convert_english_only": 'y' if convert_english_only else 'n'
}
with open("jtb.ini", "w") as configfile:
config.write(configfile)
# 用户配置已读取或设置,现在初始化其他变量
try:
# 使用读取到或输入的 rate_input 进行语速设置
rate = float(rate_input)
rate_percent = f"+{int((rate - 1) * 100)}%"
except ValueError:
rate_percent = "+0%" # 默认语速
selected_voice = voices.get(voice_choice, 'zh-CN-XiaoxiaoNeural') # 默认使用 XiaoXiao
# 初始化变量
previous_clipboard_content = ""
async def text_to_speech(text, output_file="output.mp3"):
# 将发音人设定为用户选择的发音人,并设置语速
communicator = edge_tts.Communicate(text, voice=selected_voice, rate=rate_percent)
await communicator.save(output_file)
def play_audio_file(file_path, start_time):
pygame.mixer.music.load(file_path)
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time
print(f"生成时间: {elapsed_time:.2f} 秒")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
# 等待播放完毕后卸载音频文件
pygame.mixer.music.unload()
def safe_remove(file_path, retries=3, delay=1):
"""
安全删除文件的方法
:param file_path: 要删除的文件路径
:param retries: 最大重试次数
:param delay: 每次重试之间的延迟(秒)
"""
for i in range(retries):
try:
os.remove(file_path)
print(f"播放完成")
break
except PermissionError as e:
print(f"删除文件失败,重试 {i+1}/{retries}...")
time.sleep(delay)
except Exception as e:
print(f"发生意外错误: {e}")
break
def is_english(text):
try:
# 检查文本是否只包含英文字符和常用标点符号
return all(ord(c) < 128 for c in text)
except:
return False
def is_valid_text(text):
try:
return isinstance(text, str) and len(text.strip()) > 0
except:
return False
def process_clipboard_text():
global previous_clipboard_content
try:
current_clipboard_content = pyperclip.paste()
# 仅在剪贴板内容是有效文本且与上次不同的时候处理
if is_valid_text(current_clipboard_content) and current_clipboard_content != previous_clipboard_content:
previous_clipboard_content = current_clipboard_content
# 根据用户设置只处理英文内容
if not convert_english_only or is_english(current_clipboard_content):
start_time = time.time() # 记录开始时间
print(f"新文本: {current_clipboard_content}")
# 使用 edge-tts 合成语音并播放
output_file = "output.mp3"
asyncio.run(text_to_speech(current_clipboard_content, output_file))
# 检查音频文件是否成功生成且有效
if os.path.exists(output_file) and os.path.getsize(output_file) > 0:
play_audio_file(output_file, start_time)
# 播放完毕后删除音频文件
safe_remove(output_file)
except pyperclip.PyperclipException as e:
print(f"读取剪贴板内容时出现错误: {e}")
def monitor_clipboard():
global previous_clipboard_content
if use_hotkey:
print(f"按下组合键 '{hotkey}' 进行文本读取...")
keyboard.add_hotkey(hotkey, process_clipboard_text)
keyboard.wait() # 等待快捷键被触发
else:
while True:
process_clipboard_text()
# 每隔一秒检测一次
time.sleep(1)
if __name__ == "__main__":
try:
monitor_clipboard()
except KeyboardInterrupt:
pygame.mixer.quit() # 程序退出时清理 pygame
print("程序已退出")