Skip to content

Commit

Permalink
Merge pull request #48 from Haruhiyuki/dev
Browse files Browse the repository at this point in the history
0.3.0版
  • Loading branch information
Haruhiyuki authored Sep 30, 2024
2 parents 9971668 + 6d757cc commit 66038ef
Show file tree
Hide file tree
Showing 15 changed files with 418 additions and 282 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/.idea
*.pyc
/build
*.spec
*.spec
/.vscode
18 changes: 15 additions & 3 deletions README.MD
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
# Excel文件转Rpy脚本(0.1.1)
# Excel文件转Rpy脚本(0.3.0)

## 开发环境
- Python 3.8

## 模块划分
```
|
|-const 配置项
|-corelib 基础依赖
|-- exception 自定义的异常
|-dist 打包的exe文件
|-handler
|-- converter 将Excel中的数据转化为rpy中的对象
|-- render 将model中的元素与进程控制渲染成Rpy脚本文件
|-- parser 解析Excel中的数据
|-- writer 将转化后的数据写入rpy文件
|-- tts 语音合成功能的实现
|-model
|-- element Rpy游戏的基本元素
|-- process Rpy游戏的进程控制
|--tools 工具类
|--app.py 程序入口
```
## 语音合成使用说明
目前仅支持通过API方式调用[GPT-SoVITS-V2](https://github.com/RVC-Boss/GPT-SoVITS),可在本地部署此项目或使用他人的在线服务。


## 打包程序
Expand All @@ -32,4 +37,11 @@

- 0.2.4
- fix 条件选择在最后一行时无法读取
- 支持对话框头像
- 支持对话框头像

- 0.3.0
- 支持语音合成功能
- 支持将待合成的中文自动翻译为日语

## TODO
- 支持在GUI界面中直接修改配置项
257 changes: 240 additions & 17 deletions app.py

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"role_model_mapping": {
"长门有希": {
"gpt": "GPT_weights_v2/nagato_yuki-e15.ckpt",
"sovits": "SoVITS_weights_v2/nagato_yuki_e15_s2160.pth"
},
"your_first_character": {
"gpt": "角色名应与你在表格中填写的角色名相同,熟悉后请新建角色使用",
"sovits": "如果你在本地运行API,请填写本地模型位置,否则请咨询在线服务的提供者"
}
},
"voice_cmd_mapping": {
"voice_cmd_1": {
"ref_audio_path": "仅当你使用表格中的语音指令列时,才需要用到此项",
"prompt_text": "否则仅需配置默认参考音频及文本便可"
},
"voice_cmd_2": {
"ref_audio_path": "这一额外参数可帮助你针对不同情况使用不同的参考音频与文本",
"prompt_text": "熟悉后请新建指令使用,选择你需要的命名方式"
}
},
"default_prompt_audio": "./predef_ref/正常有希/01_有希_平静.wav",
"default_prompt_text": "私が再び異常動作を起こさないという確証はない。",
"API_BASE_URL": {
"base": "http://127.0.0.1:9880/"
},
"deepL_api_key": "YOUR_DEEPL_API_KEY"
}
23 changes: 11 additions & 12 deletions const/converter_setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
ElementColNumMapping = {
'role_name': 0,
'text': 1,
'music': 18,
'character': 19,
'change_page': 20,
'background': 21,
'remark': 22,
'mode': 23,
'sound': 24,
'transition': 25,
'special': 26,
'voice': 27,
'character': 18,
'background': 19,
'transition': 20,
'music': 21,
'voice': 22,
'voice_cmd':23,
'mode': 24,
'change_page': 25,
'sound': 26,
'side_character': 27,
'menu': 28,
'side_character': 29,
'voice_cmd':30,
'remark': 29,
}

# 元素映射
Expand Down
105 changes: 78 additions & 27 deletions const/tts_setting.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,81 @@
role_model_mapping = {
"长门": {
"gpt": "GPT_weights_v2/nagato_yuki-e15.ckpt",
"sovits": "SoVITS_weights_v2/nagato_yuki_e15_s2160.pth"
},
"角色2": {
"gpt": "gpt_model_2_path",
"sovits": "sovits_model_2_path"
},
# 添加更多角色...
}
import json
import os

voice_cmd_mapping = {
"voice_cmd_1": {
"ref_audio_path": "path_to_reference_1.wav",
"prompt_text": "Prompt text for voice_cmd_1"
},
"voice_cmd_2": {
"ref_audio_path": "path_to_reference_2.wav",
"prompt_text": "Prompt text for voice_cmd_2"
},
# 添加更多映射...
}
class TTSConfig:
def __init__(self, config_file='config.json'):
self.config_file = config_file
self.role_model_mapping = {
"长门有希": {
"gpt": "GPT_weights_v2/nagato_yuki-e15.ckpt",
"sovits": "SoVITS_weights_v2/nagato_yuki_e15_s2160.pth"
},
"your_first_character": {
"gpt": "角色名应与你在表格中填写的角色名相同,熟悉后请新建角色使用",
"sovits": "如果你在本地运行API,请填写本地模型位置,否则请咨询在线服务的提供者"
},
# 添加更多角色...
}
self.voice_cmd_mapping = {
"voice_cmd_1": {
"ref_audio_path": "仅当你使用表格中的语音指令列时,才需要用到此项",
"prompt_text": "否则仅需配置默认参考音频及文本便可"
},
"voice_cmd_2": {
"ref_audio_path": "这一额外参数可帮助你针对不同情况使用不同的参考音频与文本",
"prompt_text": "熟悉后请新建指令使用,选择你需要的命名方式"
},
# 添加更多映射...
}
self.default_prompt_audio = "./predef_ref/正常有希/01_有希_平静.wav"
self.default_prompt_text = "私が再び異常動作を起こさないという確証はない。"
self.api_base_url = {'base': 'http://127.0.0.1:9880/'}
self.deepL_api_key = "YOUR_DEEPL_API_KEY"

default_prompt_audio = "D:/GPT-SoVITS-v2-240821/predef_ref/正常有希/01_有希_平静.wav"
default_prompt_text = "私が再び異常動作を起こさないという確証はない。"
if os.path.exists(self.config_file):
self.load_config()
else:
self.save_config() # 创建配置文件并保存默认内容

API_BASE_URL = {
'base': 'http://127.0.0.1:9880/'
}
def load_config(self):
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.role_model_mapping = config['role_model_mapping']
self.voice_cmd_mapping = config['voice_cmd_mapping']
self.default_prompt_audio = config['default_prompt_audio']
self.default_prompt_text = config['default_prompt_text']
self.api_base_url = config['API_BASE_URL']
self.deepL_api_key = config['deepL_api_key']

def save_config(self):
config = {
'role_model_mapping': self.role_model_mapping,
'voice_cmd_mapping': self.voice_cmd_mapping,
'default_prompt_audio': self.default_prompt_audio,
'default_prompt_text': self.default_prompt_text,
'API_BASE_URL': self.api_base_url,
'deepL_api_key': self.deepL_api_key
}
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=4, ensure_ascii=False)

def save_config_gui(self, default_prompt_text, default_prompt_audio, api_base_url, role_model_mapping, voice_cmd_mapping, deepL_api_key):
config = {
'role_model_mapping': role_model_mapping,
'voice_cmd_mapping': voice_cmd_mapping,
'default_prompt_audio': default_prompt_audio,
'default_prompt_text': default_prompt_text,
'API_BASE_URL': api_base_url,
'deepL_api_key': deepL_api_key
}
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=4, ensure_ascii=False)

def delete_role(self, role_name):
if role_name in self.role_model_mapping:
del self.role_model_mapping[role_name]
self.save_config() # 保存更改后的配置

def delete_voice_cmd(self, cmd_name):
if cmd_name in self.voice_cmd_mapping:
del self.voice_cmd_mapping[cmd_name]
self.save_config() # 保存更改后的配置
Binary file modified dist/Excel2RpyScript.exe
Binary file not shown.
14 changes: 7 additions & 7 deletions handler/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,17 @@ def _converter_mode(self):
def _converter_role(self):
# 角色
role_name = self.row[ElementColNumMapping.get('role_name')]
if role_name not in ["", "旁白"]:
# 当其他角色出现时,重置模式为nvl
if role_name and role_name != "旁白":
# 当新的角色名出现时,切换到该角色
self.converter.current_role = self.converter.add_role(role_name)
elif role_name == "" and self.converter.current_mode == "":
#self.converter.current_mode = "nvl" # 可选:根据需要设置当前模式
elif role_name == "":
# 空角色名时,保持当前角色不变
return self.converter.current_role
else:
# 处理旁白角色或其他情况
self.converter.current_role = Role("narrator_{}".format(self.converter.current_mode), "None")
# elif role_name != "":
# # 当其他角色出现时,重置模式为nvl
# self.converter.current_role = self.converter.add_role(role_name)
# self.converter.current_mode = "nvl"

return self.converter.current_role

def _converter_text(self):
Expand Down
46 changes: 37 additions & 9 deletions handler/tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from const.converter_setting import ElementColNumMapping, PositionMapping, ImageCmdMapping, TransitionMapping, \
ReplaceCharacterMapping

from const.tts_setting import role_model_mapping, API_BASE_URL, voice_cmd_mapping, default_prompt_audio, default_prompt_text
from const.tts_setting import TTSConfig

import requests, os

Expand All @@ -12,6 +12,15 @@ def __init__(self,conveter):
self.conveter = conveter
self.parser = conveter.parser
self.last_role_name = None
tts_config = TTSConfig()
self.role_model_mapping = tts_config.role_model_mapping
self.API_BASE_URL = tts_config.api_base_url
self.voice_cmd_mapping = tts_config.voice_cmd_mapping
self.default_prompt_text = tts_config.default_prompt_text
self.default_prompt_audio = tts_config.default_prompt_audio
self.deepL_api_key = tts_config.deepL_api_key



def filter_parsed_sheets_tts(self):
parsed_sheets = self.parser.get_parsed_sheets()
Expand Down Expand Up @@ -56,40 +65,59 @@ def switch_models(self, role_name):
if role_name == self.last_role_name:
return # 如果角色名相同,则无需切换

models = role_model_mapping.get(role_name)
models = self.role_model_mapping.get(role_name)

if models:
gpt_model = models['gpt']
sovits_model = models['sovits']

# 切换到对应的GPT模型
requests.get(f"{API_BASE_URL['base']}set_gpt_weights?weights_path={gpt_model}")
requests.get(f"{self.API_BASE_URL['base']}set_gpt_weights?weights_path={gpt_model}")

# 切换到对应的SoVITS模型
requests.get(f"{API_BASE_URL['base']}set_sovits_weights?weights_path={sovits_model}")
requests.get(f"{self.API_BASE_URL['base']}set_sovits_weights?weights_path={sovits_model}")

self.last_role_name = role_name # 更新上一个角色名
else:
print(f"No model found for role: {role_name}")

def translate_text(self, text, target_lang):
# DeepL API翻译方法
api_url = "https://api-free.deepl.com/v2/translate"
params = {
"auth_key": self.deepL_api_key, # 替换为你的API密钥
"text": text,
"target_lang": target_lang,
}
response = requests.post(api_url, data=params)
if response.status_code == 200:
return response.json()['translations'][0]['text']
else:
print(f"Translation error: {response.json()}")
return text # 返回原文本以防止错误中断


def synthesize_voice(self,voice_tts_sheets):
def synthesize_voice(self,voice_tts_sheets,language):
for sheet_index, sheet in enumerate(voice_tts_sheets):
for row_index, row in enumerate(sheet['rows']):
role_name = row['role_name'] # 获取角色名
text = row['text'] # 获取文本
voice_cmd = row['voice_cmd'] # 获取语音指令

# 获取对应的 ref_audio_path 和 prompt_text
audio_params = voice_cmd_mapping.get(voice_cmd, {})
ref_audio_path = audio_params.get("ref_audio_path", f"{default_prompt_audio}") # 默认值
prompt_text = audio_params.get("prompt_text", f"{default_prompt_text}") # 默认值
audio_params = self.voice_cmd_mapping.get(voice_cmd, {})
ref_audio_path = audio_params.get("ref_audio_path", f"{self.default_prompt_audio}") # 默认值
prompt_text = audio_params.get("prompt_text", f"{self.default_prompt_text}") # 默认值

# 使用DeepL翻译中文文本为日文
if language == 'JA':
text = self.translate_text(text, target_lang='JA')

self.switch_models(role_name)

# 发送合成请求
response = requests.post(
f"{API_BASE_URL['base']}tts",
f"{self.API_BASE_URL['base']}tts",
json={
"text": text,
"text_lang": "auto",
Expand Down
Binary file added predef_ref/01_有希_平静.wav
Binary file not shown.
Loading

0 comments on commit 66038ef

Please sign in to comment.