ChatTTS初遇

ChatTTS初遇

安装

  1. 获取代码
1
2
git clone https://github.com/2noise/ChatTTS
cd ChatTTS
  1. 安装依赖
1
pip install --upgrade -r requirements.txt

假如有用conda,使用以下代码安装依赖

1
2
3
conda create -n chattts
conda activate chattts
pip install -r requirements.txt
  1. 启动Webui(可选,直接使用下面的代码)
1
python examples/web/webui.py

(在webui中,假如想用个性化音色,需要注意你的录音要和Sample Text一样)

实验

实用系统内置的音色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import torch
import torchaudio
import ChatTTS
from tools.audio import float_to_int16, has_ffmpeg_installed, load_audio

chat = ChatTTS.Chat()
chat.load(compile=False) # Set compile=True for optimized performance

rand_spk = chat.sample_random_speaker()
print('=================')
print(rand_spk) # save it for later timbre recovery

params_refine_text = ChatTTS.Chat.RefineTextParams(
prompt='[oral_2][laugh_0][break_6]',
)

texts = ["哈克和迪克这两个小东西看起来古灵精怪,其实是两个野心勃勃的危险分子——他们在牙齿上挖洞建房,不仅要修建自己的舒适小窝,还梦想着修建可以出租的豪华公寓……",
"就在他们的梦想快要实现的时候,一把大刷子带着很多警察出现在牙齿大街上。哈克和迪克贮藏的粮食几乎被一扫而空。更可怕的事情还在后面,一个巨大的钩子从天而降,伸向了哈克和迪克的家……",
"哈克和迪克的命运将会怎样呢?那些警察是从哪里来的呢?牙齿大街还能恢复往日的平静吗?",
"看完这本图画书之后,聪明的小朋友们肯定会知道,怎样做才能不让哈克和迪克这样的小东西在我们的嘴巴里干坏事!"]

refine_texts = chat.infer(texts,
skip_refine_text=False,
refine_text_only=True,
params_refine_text=params_refine_text,)
print('======refine===========')
for i in range(len(refine_texts)):
print(refine_texts[i])

params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb = rand_spk, # add sampled speaker
temperature = .3, # using custom temperature
top_P = 0.7, # top P decode
top_K = 20, # top K decode
)

print('======infer===========')
wavs = chat.infer(refine_texts,
skip_refine_text=True,
params_infer_code=params_infer_code,)

for i in range(len(wavs)):
"""
In some versions of torchaudio, the first line works but in other versions, so does the second line.
"""
try:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]).unsqueeze(0), 24000)
except:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]), 24000)

主要就是使用 chat.infer 这个方法,它有一个参数refine_text_only,可以控制是否只是断句。调用完之后返回的是文字列表,中间会增加断句标识。

第二次调用infer方法的时候就可以传入skip_refine_text,因为我们已经实现做好断句了。

另外,rand_spk 这个的输出有点意思,需要留意一下,因为下面我们去做自定义音色的时候会用到,这个方便理解。这个就是编码后音色编码,看起来像一堆中文乱码,不是什么错误,不要紧张。

自定义音色

首先要录制一小段语音,可以念一句话,你可以用系统的录音机,或者用我做的一个录音工具,采样率用24000,录制个5-10秒即可。

假如你不知道念什么,很奇怪,突然让你说一句话,脑子就一片空白,至少我是这样,当时我就找了这么一行。

1
哈克和迪克这两个小东西看起来古灵精怪

下载录音为wav文件,注意看下码率对不对。准备好之后就用下面的方法来生成那个奇怪的文本。复制保存下来就好。

1
2
3
4
sample_audio = load_audio('/Users/sam/Music/sample-me.wav', 24000)
sample_spk_smp = chat.sample_audio_speaker(sample_audio)
print('================')
print(sample_spk_smp)

然后修改params_infer_code参数

1
2
3
4
5
6
7
8
9
10
11

params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb = None, # add sampled speaker
temperature = .3, # using custom temperature
top_P = 0.7, # top P decode
top_K = 20, # top K decode
)

params_infer_code.txt_smp = sample_text
params_infer_code.spk_smp = sample_spk_smp
params_infer_code.spk_emb = None

再重新调用infer 生成wav就可以了。

实验结果

[牙齿大街的新鲜事介绍.mp3](ChatTTS初遇 11c0ba99e0cb8072b8f3f286b815a0c2/牙齿大街的新鲜事介绍.mp3)

完整代码供你复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import torch
import torchaudio
import ChatTTS
from tools.audio import float_to_int16, has_ffmpeg_installed, load_audio

chat = ChatTTS.Chat()
chat.load(compile=False) # Set compile=True for optimized performance

rand_spk = chat.sample_random_speaker()
print('=================')
print(rand_spk) # save it for later timbre recovery

params_infer_code = ChatTTS.Chat.InferCodeParams(
spk_emb = None, # add sampled speaker
temperature = .3, # using custom temperature
top_P = 0.7, # top P decode
top_K = 20, # top K decode
)

params_refine_text = ChatTTS.Chat.RefineTextParams(
prompt='[oral_2][laugh_0][break_6]',
)

sample_text = "哈克和迪克这两个小东西看起来古灵精怪"
sample_text = "哈克和迪克这两个小东西看起来古灵精怪,其实是两个野心勃勃危险分子——他们在牙齿上挖洞建房,不仅要修建自己的舒适小窝,还梦想着修建出可以出租的豪华公寓……"
sample_audio = load_audio('/Users/sam/Music/sample-me.wav', 24000)
sample_spk_smp = chat.sample_audio_speaker(sample_audio)
print('================')
print(sample_spk_smp)
sample_spk_smp = "伀嫠冀呯伟叐乸乐絁稃蒌棩芡籵囮潁贅缕匰卅窃檯泫懳潱盾簫旬跊恫挝弫熠哦襛劍県娑虅荙偂瑦尾絖诧扐硝件埇唲贸屗榃孥嗇圇岨师袋衟丑睽婢牦砑簹摱稞儎蓌渤吷厹片曌怸劸秴勊浍攙懄名檃脴獙畗弁跷璤嫥兘螵煻壕捻丏焒蚘愢墁冧奰庽脼侲忱莼弣啸礛姿早谯漉吞呙伏穞澭崰枑芮纟嫤桻祙腉谱痚蝱欇橇拂彼丿磜蠛胀柑搪珙嫩宇捶粣凱瞒嵼琅嶷綯橧觽焓諛僂焽貛崶熝烥糱蕪瓏欞觼杞蒏檟蠠敫佩旐趚葘濬劘濺趵俴焔咀螂曻裘巋艋蜆矣倾愆狨簞耺帐瓭懯椱囘恗奵徻圫拏炶犑漧琺璥獮砫綀灡嚂蔁挬滂嶷盁湉掂爗蜷賸胅萳洴捥楌猥圑此跂艳莞矴謦蝖妌為炇筷檷菏纳汥咙攘狣怊棽秔箚翹玑吼荣杮僉副薣浖蜺犡匛劀懫筩誰謙福椅縬孊日胶徇礹濯榤劽睺碀儦懄瞥蕳亄缁谉盰櫜吏句蠲栵倡奮叴枬搶篽全貛瓮攪掉摃媅孎勜垻篇篽岌藡稠猵暄犾葠妘俺菢謩硼虲撖槞撳枆猲岪怯穤誋杫狀紽曁秕蜲祷愡赽獽勦咄汄渀媭漘晬檻晕嶩令皑婪拂程姪棱赑琍縲撑梂趝岌脧琸曯殜嬊翖廏勱荪簝爄匱蛴悔兰爙讒孁谮熌劐诱竾帉搾瓚忪弒斪賾疹猘衻薎蜃绖匙蛧珱续噂峖数芛憓胠咮梻禨惇谲塩穸狃嗍苜磳粩檚去峅匛蟆侏襆赿纝勀捤觇澉炜兯哫拵誣畡舙斬本僡犔籲螘螸藆噎庬上樹瘞悫憥梺潛槵槢寒爬绥帪呮綳獴嗆裧杚滖蟱唳啶晘裫凮灥蝖江贉播景豫僞蓋覐蒤挰坽惪艒證珜蓧劫艒箁瀯垩簃溷藺婺氐熸抻籓洽瓃圷箼縍學澦蔉寋脶筠棳崿匊誅葽戜劀绀芻肿褁喕巤聗沼汯昩毂篨挴直戵耪珴瞭挬舅眠弹擗诈朡服眜瀬屡住啍倩罷眨炦硓憸梒剢嘇衅盔穸捬搩翅蜸煁箚渶嫭慱擡璩碧涺倅簨戒媮滏蕱擉蟼蕝詬詍悍碟妇倪生扫始砟艓埰堷悻莃孩捂環詆係喱儢晝哐塻櫚睡稼媦菤夂綏螫埲膹为緮筱板堿茆仾嚳典甞刴茥火彔堤砑芩熵諅惚襺胑暀昹庮攈粊痛樀㴅"

texts = ["哈克和迪克这两个小东西看起来古灵精怪,其实是两个野心勃勃的危险分子——他们在牙齿上挖洞建房,不仅要修建自己的舒适小窝,还梦想着修建可以出租的豪华公寓……",
"就在他们的梦想快要实现的时候,一把大刷子带着很多警察出现在牙齿大街上。哈克和迪克贮藏的粮食几乎被一扫而空。更可怕的事情还在后面,一个巨大的钩子从天而降,伸向了哈克和迪克的家……",
"哈克和迪克的命运将会怎样呢?那些警察是从哪里来的呢?牙齿大街还能恢复往日的平静吗?",
"看完这本图画书之后,聪明的小朋友们肯定会知道,怎样做才能不让哈克和迪克这样的小东西在我们的嘴巴里干坏事!"]

refine_texts = chat.infer(texts,
skip_refine_text=False,
refine_text_only=True,
params_refine_text=params_refine_text,)
print('======refine===========')
for i in range(len(refine_texts)):
print(refine_texts[i])

params_infer_code.txt_smp = sample_text
params_infer_code.spk_smp = sample_spk_smp
params_infer_code.spk_emb = None

print('======infer===========')
wavs = chat.infer(refine_texts,
skip_refine_text=True,
params_infer_code=params_infer_code,)

for i in range(len(wavs)):
"""
In some versions of torchaudio, the first line works but in other versions, so does the second line.
"""
try:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]).unsqueeze(0), 24000)
except:
torchaudio.save(f"basic_output{i}.wav", torch.from_numpy(wavs[i]), 24000)