语音合成
下载vits模型文件
如果你有能力可以自己炼丹出个模型,具体可以参考这个。
或者可以白嫖点大佬训练的模型,比如这个这个
代码取自这位大佬,然后进行魔改了一番,没有自己造轮子,好多都看不懂,慢慢啃。
speaking.py
import subprocess
import argparse
import torch
import time
from torch import no_grad, LongTensor
import asyncio
import utils
from models import SynthesizerTrn
from text import text_to_sequence
import commons
from scipy.io import wavfile
import pygame
#noise_scale(控制感情变化程度)
vitsNoiseScale = 0.6
#noise_scale_w(控制音素发音长度)
vitsNoiseScaleW = 0.668
#length_scale(控制整体语速)
vitsLengthScale = 1.0
_init_vits_model = False
hps_ms = None
device = None
net_g_ms = None
def play_audio(audio_file_name):
# 初始化pygame
pygame.init()
# 设置音频文件
pygame.mixer.music.load(audio_file_name)
# 播放音频
pygame.mixer.music.play()
# 等待音频播放完毕
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
# 退出pygame
pygame.quit()
# def play_audio(audio_file_name):
# command = f'mpv.exe -vo null {audio_file_name}'
# subprocess.run(command, shell=True)
def init_vits_model():
# 全局变量引入
global hps_ms, device, net_g_ms
# 创建参数解析器
parser = argparse.ArgumentParser()
parser.add_argument('--device', type=str, default='cpu')
parser.add_argument('--api', action="store_true", default=False)
parser.add_argument("--share", action="store_true", default=False, help="share gradio app")
parser.add_argument("--colab", action="store_true", default=False, help="share gradio app")
args = parser.parse_args()
device = torch.device(args.device)
# 从文件中获取VITS模型的参数
hps_ms = utils.get_hparams_from_file('config.json')
# 创建VITS模型
net_g_ms = SynthesizerTrn(
len(hps_ms.symbols),
hps_ms.data.filter_length // 2 + 1,
hps_ms.train.segment_size // hps_ms.data.hop_length,
n_speakers=hps_ms.data.n_speakers,
**hps_ms.model)
# 将VITS模型设置为评估模式,并移动到指定设备
_ = net_g_ms.eval().to(device)
# 获取说话者信息
speakers = hps_ms.speakers
# 加载模型和优化器
model, optimizer, learning_rate, epochs = utils.load_checkpoint('G_953000.pth', net_g_ms, None)
# 标记VITS模型已初始化
_init_vits_model = True
def get_text(text, hps):
# 使用text_to_sequence将输入文本转换为模型可用的格式
text_norm, clean_text = text_to_sequence(text, hps.symbols, hps.data.text_cleaners)
# 如果需要在文本中添加空白符,则在文本序列中插入0(代表空白符)
if hps.data.add_blank:
text_norm = commons.intersperse(text_norm, 0)
# 将文本序列转换为LongTensor(长整数)格式
text_norm = LongTensor(text_norm)
return text_norm, clean_text
def vits(text, language, speaker_id, noise_scale, noise_scale_w, length_scale):
global over
# 记录生成开始时间
start = time.perf_counter()
# 处理输入文本
if not len(text):
return "输入文本不能为空!", None, None
text = text.replace('\n', ' ').replace('\r', '').replace(" ", "")
if len(text) > 200:
return f"输入文字过长!{len(text)}>100", None, None
# 根据语言类型添加标记
if language == 0:
text = f"[ZH]{text}[ZH]"
elif language == 1:
text = f"[JA]{text}[JA]"
else:
text = f"{text}"
# 调用get_text将文本转换为模型可用的格式
stn_tst, clean_text = get_text(text, hps_ms)
# 使用VITS模型生成音频
with no_grad():
x_tst = stn_tst.unsqueeze(0).to(device)
x_tst_lengths = LongTensor([stn_tst.size(0)]).to(device)
speaker_id = LongTensor([speaker_id]).to(device)
audio = net_g_ms.infer(x_tst, x_tst_lengths, sid=speaker_id, noise_scale=noise_scale, noise_scale_w=noise_scale_w,
length_scale=length_scale)[0][0, 0].data.cpu().float().numpy()
# 返回生成成功信息、音频信息以及生成耗时
return "生成成功!", (22050, audio), f"生成耗时 {round(time.perf_counter()-start, 2)} s"
async def start():
while True:
print("请输入 >")
input_str = await asyncio.get_event_loop().run_in_executor(None, input, '')
if "关闭AI" in input_str:
return
result = input_str
status, audios, time = vits(result, 0, 124, vitsNoiseScale, vitsNoiseScaleW, vitsLengthScale)
print("VITS : ", status, time)
wavfile.write("output.wav", audios[0], audios[1])
play_audio("output.wav")
async def main():
if not _init_vits_model:
init_vits_model()
await asyncio.gather(start(),)
asyncio.run(main())
- 导入库:代码导入了必要的Python库,例如time、os、scipy.io.wavfile、asyncio、subprocess、requests、json、argparse,以及自定义模块utils、models、text和commons中的各种函数和类。
- 变量定义和配置:
- 定义了一些全局变量,用于配置GPT-3.5模型的访问信息、VITS模型的参数等。
- 初始化了一些VITS模型的相关参数,并加载了模型。
- 函数定义:
- send_chatgpt_request(send_msg):用于向GPT-3.5模型发送请求并获取生成的文本回复。
- play_audio(audio_file_name):用于播放音频文件。
- init_vits_model():用于初始化VITS模型。
- get_text(text, hps):用于处理输入文本,将其转换成模型可用的格式。
- vits(text, language, speaker_id, noise_scale, noise_scale_w, length_scale):用于生成语音。
- 异步事件处理:
- start():从用户输入中获取文本,发送给GPT-3.5模型获取回复,并使用VITS模型生成对应的语音。
- main():主要函数,协调调用start()函数以实现异步处理。
- 程序入口:
- 使用asyncio.run(main())调用主函数,启动程序运行。
###get_text()函数 get_text函数的目的是将输入文本转换为VITS模型可用的格式,它接受两个参数:text(输入的文本)和hps(VITS模型的参数)。 在get_text函数中,主要有以下步骤:
- 调用text_to_sequence函数:
- text_to_sequence函数用于将文本转换为符号序列,该序列是模型可接受的输入格式。
- 这个函数的调用将输入文本转换成两部分:text_norm和clean_text。
- text_norm是转换后的文本序列,使用VITS模型参数中的符号表和文本清理器处理得到。
- clean_text是原始文本的清理版本。
- 添加空白符(如果需要):
- 根据模型参数中的add_blank标志,决定是否需要在文本序列中插入0作为空白符。
- 将文本序列转换为LongTensor:
- 最终将处理后的文本序列转换为PyTorch的LongTensor格式,以便模型可以接受它作为输入。
def get_text(text, hps):
# 使用text_to_sequence将输入文本转换为模型可用的格式
text_norm, clean_text = text_to_sequence(text, hps.symbols, hps.data.text_cleaners)
# 如果需要在文本中添加空白符,则在文本序列中插入0(代表空白符)
if hps.data.add_blank:
text_norm = commons.intersperse(text_norm, 0)
# 将文本序列转换为LongTensor(长整数)格式
text_norm = LongTensor(text_norm)
return text_norm, clean_text
###text_to_sequence函数 text_to_sequence函数的目的是将文本转换为符号序列,它接受三个参数:text(输入文本)、symbols(符号表)和cleaners(文本清理器)。
在text_to_sequence函数中,主要有以下步骤:
- 文本清理:
- 调用clean_text函数对输入文本进行清理,去除多余的空格、特殊字符等。
- 文本转换为符号序列:
- 遍历清理后的文本,将每个字符转换为对应的符号在符号表中的索引。
- 如果字符不在符号表中,将其转换为未知符号的索引。 总的来说,get_text函数使用了text_to_sequence函数将输入文本转换为模型可用的符号序列,并可以选择是否添加空白符。 ``` cpp def text_to_sequence(text, symbols, cleaners): # 文本清理,例如去除多余的空格、特殊字符等 cleaned_text = clean_text(text, cleaners)
# 将文本转换为符号序列
symbols = symbols + ['[UNK]'] # 符号表增加未知符号
sequence = []
for s in cleaned_text:
if s in symbols:
sequence.append(symbols.index(s))
else:
sequence.append(symbols.index('[UNK]'))
return sequence, cleaned_text
###异步事件处理:
- start():从用户输入中获取文本,发送给GPT-3.5模型获取回复,并使用VITS模型生成对应的语音。
- main():主要函数,协调调用start()函数以实现异步处理。
1. async 和 await 关键字
- async: async关键字用于定义一个异步函数,即一个函数的执行可能会被中断,然后在稍后的某个时刻继续执行。异步函数在遇到await时可能会暂停执行,等待另一个异步操作完成。
- await: await关键字用于等待一个异步操作完成。它只能在异步函数内部使用,并且通常与async一起使用。
2. asyncio 模块
asyncio 是 Python 标准库中的模块,用于支持异步编程。它提供了一种事件循环机制,用于管理异步任务和异步函数。
在异步编程中,我们将任务添加到事件循环中,然后让事件循环负责调度这些任务的执行。asyncio 通过事件循环来驱动异步程序的执行,处理异步函数的调用和结果的返回。
3. 异步事件处理流程
现在,让我们来理解代码中的异步事件处理流程:
- start() 函数:
- 这是一个异步函数,用于处理用户输入。它等待用户的输入,然后将输入文本发送给 GPT-3.5 模型获取回复,并使用 VITS 模型生成对应的语音。这里的 await 用于等待异步操作完成,例如等待用户输入。
- main() 函数:
- 这是程序的主要函数,也是一个异步函数。它协调调用 start() 函数以实现异步处理。在这里,await start() 用于等待 start() 函数执行完成,然后结束程序。
- 程序入口:
- 最后,在程序的入口处,我们使用 asyncio.run(main()) 调用主函数 main(),启动整个程序的异步执行。asyncio.run() 是用于运行异步程序的简便方法。
总结一下,异步编程通过使用 async 和 await 关键字以及 asyncio 模块,使程序能够在等待一些耗时的操作时不阻塞,从而提高了程序的效率和响应性。
###关键代码
``` cpp
audio = net_g_ms.infer(x_tst, x_tst_lengths, sid=speaker_id, noise_scale=noise_scale, noise_scale_w=noise_scale_w,
length_scale=length_scale)[0][0, 0].data.cpu().float().numpy()
这行代码调用了 net_g_ms 对象(一个 VITS 模型)的 infer 方法来生成音频,然后将结果赋给 audio 变量。
逐步解释:
-
net_g_ms: 这是 VITS 模型对象,用于语音合成。
-
infer: 这是 VITS 模型的推理方法,用于生成音频。
-
x_tst: 这是输入的文本序列的 PyTorch tensor,表示待合成的文本。
-
x_tst_lengths: 这是输入文本的长度,以 PyTorch tensor 表示。
-
sid=speaker_id: 这是说话者 ID,用于指定生成音频的说话者。
-
noise_scale, noise_scale_w, length_scale: 这些是控制语音生成的参数,影响生成音频的特征,包括感情变化程度、音素发音长度和整体语速。
-
[0][0, 0]: 这是对生成的音频结果进行索引操作,从生成的音频结果中提取特定部分。具体来说,它提取了生成的音频的第一个样本(可能是第一个批次的第一个样本)的第一个通道的数据。
-
.data.cpu().float().numpy(): 这一系列操作将音频数据从 GPU(如果在 GPU 上)转移到 CPU,并转换为 NumPy 数组,以便后续处理或保存为音频文件。
这行代码调用 VITS 模型的推理方法生成音频,并对生成的音频结果进行一系列处理,最终得到可以播放或保存的音频数据。