ouzhang57 commited on
Commit
dd35111
·
verified ·
1 Parent(s): 7294ba3

Upload folder using huggingface_hub (part 10)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +6 -0
  2. third_party/CosyVoice/cosyvoice/__pycache__/__init__.cpython-312.pyc +0 -0
  3. third_party/CosyVoice/cosyvoice/bin/average_model.py +93 -0
  4. third_party/CosyVoice/cosyvoice/bin/export_jit.py +99 -0
  5. third_party/CosyVoice/cosyvoice/bin/export_onnx.py +114 -0
  6. third_party/CosyVoice/cosyvoice/bin/train.py +195 -0
  7. third_party/CosyVoice/cosyvoice/cli/__init__.py +0 -0
  8. third_party/CosyVoice/cosyvoice/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  9. third_party/CosyVoice/cosyvoice/cli/__pycache__/cosyvoice.cpython-312.pyc +0 -0
  10. third_party/CosyVoice/cosyvoice/cli/__pycache__/frontend.cpython-312.pyc +0 -0
  11. third_party/CosyVoice/cosyvoice/cli/__pycache__/model.cpython-312.pyc +0 -0
  12. third_party/CosyVoice/cosyvoice/cli/cosyvoice.py +238 -0
  13. third_party/CosyVoice/cosyvoice/cli/frontend.py +224 -0
  14. third_party/CosyVoice/cosyvoice/cli/model.py +467 -0
  15. third_party/CosyVoice/cosyvoice/dataset/__init__.py +0 -0
  16. third_party/CosyVoice/cosyvoice/dataset/__pycache__/__init__.cpython-312.pyc +0 -0
  17. third_party/CosyVoice/cosyvoice/dataset/__pycache__/processor.cpython-312.pyc +0 -0
  18. third_party/CosyVoice/cosyvoice/dataset/dataset.py +155 -0
  19. third_party/CosyVoice/cosyvoice/dataset/processor.py +431 -0
  20. third_party/CosyVoice/cosyvoice/flow/DiT/__pycache__/dit.cpython-312.pyc +0 -0
  21. third_party/CosyVoice/cosyvoice/flow/DiT/__pycache__/modules.cpython-312.pyc +0 -0
  22. third_party/CosyVoice/cosyvoice/flow/DiT/dit.py +176 -0
  23. third_party/CosyVoice/cosyvoice/flow/DiT/modules.py +616 -0
  24. third_party/CosyVoice/cosyvoice/flow/__pycache__/flow.cpython-312.pyc +0 -0
  25. third_party/CosyVoice/cosyvoice/flow/__pycache__/flow_matching.cpython-312.pyc +0 -0
  26. third_party/CosyVoice/cosyvoice/flow/decoder.py +494 -0
  27. third_party/CosyVoice/cosyvoice/flow/flow.py +443 -0
  28. third_party/CosyVoice/cosyvoice/flow/flow_matching.py +227 -0
  29. third_party/CosyVoice/cosyvoice/flow/length_regulator.py +70 -0
  30. third_party/CosyVoice/cosyvoice/hifigan/__pycache__/discriminator.cpython-312.pyc +0 -0
  31. third_party/CosyVoice/cosyvoice/hifigan/__pycache__/f0_predictor.cpython-312.pyc +0 -0
  32. third_party/CosyVoice/cosyvoice/hifigan/__pycache__/generator.cpython-312.pyc +0 -0
  33. third_party/CosyVoice/cosyvoice/hifigan/__pycache__/hifigan.cpython-312.pyc +0 -0
  34. third_party/CosyVoice/cosyvoice/hifigan/discriminator.py +230 -0
  35. third_party/CosyVoice/cosyvoice/hifigan/f0_predictor.py +103 -0
  36. third_party/CosyVoice/cosyvoice/hifigan/generator.py +746 -0
  37. third_party/CosyVoice/cosyvoice/hifigan/hifigan.py +67 -0
  38. third_party/CosyVoice/cosyvoice/llm/__pycache__/llm.cpython-312.pyc +0 -0
  39. third_party/CosyVoice/cosyvoice/llm/llm.py +710 -0
  40. third_party/CosyVoice/cosyvoice/tokenizer/__pycache__/tokenizer.cpython-312.pyc +0 -0
  41. third_party/CosyVoice/cosyvoice/tokenizer/assets/multilingual_zh_ja_yue_char_del.tiktoken +0 -0
  42. third_party/CosyVoice/cosyvoice/tokenizer/tokenizer.py +327 -0
  43. third_party/CosyVoice/cosyvoice/transformer/__init__.py +0 -0
  44. third_party/CosyVoice/cosyvoice/transformer/__pycache__/__init__.cpython-312.pyc +0 -0
  45. third_party/CosyVoice/cosyvoice/transformer/__pycache__/activation.cpython-312.pyc +0 -0
  46. third_party/CosyVoice/cosyvoice/transformer/__pycache__/attention.cpython-312.pyc +0 -0
  47. third_party/CosyVoice/cosyvoice/transformer/__pycache__/convolution.cpython-312.pyc +0 -0
  48. third_party/CosyVoice/cosyvoice/transformer/__pycache__/embedding.cpython-312.pyc +0 -0
  49. third_party/CosyVoice/cosyvoice/transformer/__pycache__/encoder_layer.cpython-312.pyc +0 -0
  50. third_party/CosyVoice/cosyvoice/transformer/__pycache__/label_smoothing_loss.cpython-312.pyc +0 -0
.gitattributes CHANGED
@@ -544,3 +544,9 @@ third_party/BindWeave/s2v/OpenS2V-Eval/Results/singleface.mp4 filter=lfs diff=lf
544
  third_party/CosyVoice/asset/zero_shot_prompt.wav filter=lfs diff=lfs merge=lfs -text
545
  third_party/CosyVoice/asset/龙小夏.mp3 filter=lfs diff=lfs merge=lfs -text
546
  third_party/CosyVoice/asset/龙小夏_24k.wav filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
544
  third_party/CosyVoice/asset/zero_shot_prompt.wav filter=lfs diff=lfs merge=lfs -text
545
  third_party/CosyVoice/asset/龙小夏.mp3 filter=lfs diff=lfs merge=lfs -text
546
  third_party/CosyVoice/asset/龙小夏_24k.wav filter=lfs diff=lfs merge=lfs -text
547
+ third_party/DiffSynth-Studio/diffsynth/pipelines/__pycache__/wan_video.cpython-313.pyc filter=lfs diff=lfs merge=lfs -text
548
+ vgen/__pycache__/poetry_teaching-baseline.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
549
+ vgen/__pycache__/poetry_teaching-baseline.cpython-313.pyc filter=lfs diff=lfs merge=lfs -text
550
+ vgen/__pycache__/poetry_teaching-planloop.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
551
+ vgen/__pycache__/poetry_teaching-planloop.cpython-313.pyc filter=lfs diff=lfs merge=lfs -text
552
+ vgen/__pycache__/poetry_teaching.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
third_party/CosyVoice/cosyvoice/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (207 Bytes). View file
 
third_party/CosyVoice/cosyvoice/bin/average_model.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2020 Mobvoi Inc (Di Wu)
2
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import os
17
+ import argparse
18
+ import glob
19
+
20
+ import yaml
21
+ import torch
22
+
23
+
24
+ def get_args():
25
+ parser = argparse.ArgumentParser(description='average model')
26
+ parser.add_argument('--dst_model', required=True, help='averaged model')
27
+ parser.add_argument('--src_path',
28
+ required=True,
29
+ help='src model path for average')
30
+ parser.add_argument('--val_best',
31
+ action="store_true",
32
+ help='averaged model')
33
+ parser.add_argument('--num',
34
+ default=5,
35
+ type=int,
36
+ help='nums for averaged model')
37
+
38
+ args = parser.parse_args()
39
+ print(args)
40
+ return args
41
+
42
+
43
+ def main():
44
+ args = get_args()
45
+ val_scores = []
46
+ if args.val_best:
47
+ yamls = glob.glob('{}/*.yaml'.format(args.src_path))
48
+ yamls = [
49
+ f for f in yamls
50
+ if not (os.path.basename(f).startswith('train')
51
+ or os.path.basename(f).startswith('init'))
52
+ ]
53
+ for y in yamls:
54
+ with open(y, 'r') as f:
55
+ dic_yaml = yaml.load(f, Loader=yaml.BaseLoader)
56
+ loss = float(dic_yaml['loss_dict']['loss'])
57
+ epoch = int(dic_yaml['epoch'])
58
+ step = int(dic_yaml['step'])
59
+ tag = dic_yaml['tag']
60
+ val_scores += [[epoch, step, loss, tag]]
61
+ sorted_val_scores = sorted(val_scores,
62
+ key=lambda x: x[2],
63
+ reverse=False)
64
+ print("best val (epoch, step, loss, tag) = " +
65
+ str(sorted_val_scores[:args.num]))
66
+ path_list = [
67
+ args.src_path + '/epoch_{}_whole.pt'.format(score[0])
68
+ for score in sorted_val_scores[:args.num]
69
+ ]
70
+ print(path_list)
71
+ avg = {}
72
+ num = args.num
73
+ assert num == len(path_list)
74
+ for path in path_list:
75
+ print('Processing {}'.format(path))
76
+ states = torch.load(path, map_location=torch.device('cpu'))
77
+ for k in states.keys():
78
+ if k not in ['step', 'epoch']:
79
+ if k not in avg.keys():
80
+ avg[k] = states[k].clone()
81
+ else:
82
+ avg[k] += states[k]
83
+ # average
84
+ for k in avg.keys():
85
+ if avg[k] is not None:
86
+ # pytorch 1.6 use true_divide instead of /=
87
+ avg[k] = torch.true_divide(avg[k], num)
88
+ print('Saving to {}'.format(args.dst_model))
89
+ torch.save(avg, args.dst_model)
90
+
91
+
92
+ if __name__ == '__main__':
93
+ main()
third_party/CosyVoice/cosyvoice/bin/export_jit.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import print_function
16
+
17
+ import argparse
18
+ import logging
19
+ logging.getLogger('matplotlib').setLevel(logging.WARNING)
20
+ import os
21
+ import sys
22
+ import torch
23
+ ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
24
+ sys.path.append('{}/../..'.format(ROOT_DIR))
25
+ sys.path.append('{}/../../third_party/Matcha-TTS'.format(ROOT_DIR))
26
+ from cosyvoice.cli.cosyvoice import AutoModel
27
+ from cosyvoice.utils.file_utils import logging
28
+
29
+
30
+ def get_args():
31
+ parser = argparse.ArgumentParser(description='export your model for deployment')
32
+ parser.add_argument('--model_dir',
33
+ type=str,
34
+ default='pretrained_models/CosyVoice-300M',
35
+ help='local path')
36
+ args = parser.parse_args()
37
+ print(args)
38
+ return args
39
+
40
+
41
+ def get_optimized_script(model, preserved_attrs=[]):
42
+ script = torch.jit.script(model)
43
+ if preserved_attrs != []:
44
+ script = torch.jit.freeze(script, preserved_attrs=preserved_attrs)
45
+ else:
46
+ script = torch.jit.freeze(script)
47
+ script = torch.jit.optimize_for_inference(script)
48
+ return script
49
+
50
+
51
+ def main():
52
+ args = get_args()
53
+ logging.basicConfig(level=logging.DEBUG,
54
+ format='%(asctime)s %(levelname)s %(message)s')
55
+
56
+ torch._C._jit_set_fusion_strategy([('STATIC', 1)])
57
+ torch._C._jit_set_profiling_mode(False)
58
+ torch._C._jit_set_profiling_executor(False)
59
+
60
+ model = AutoModel(model_dir=args.model_dir)
61
+
62
+ if model.__class__.__name__ == 'CosyVoice':
63
+ # 1. export llm text_encoder
64
+ llm_text_encoder = model.model.llm.text_encoder
65
+ script = get_optimized_script(llm_text_encoder)
66
+ script.save('{}/llm.text_encoder.fp32.zip'.format(args.model_dir))
67
+ script = get_optimized_script(llm_text_encoder.half())
68
+ script.save('{}/llm.text_encoder.fp16.zip'.format(args.model_dir))
69
+ logging.info('successfully export llm_text_encoder')
70
+
71
+ # 2. export llm llm
72
+ llm_llm = model.model.llm.llm
73
+ script = get_optimized_script(llm_llm, ['forward_chunk'])
74
+ script.save('{}/llm.llm.fp32.zip'.format(args.model_dir))
75
+ script = get_optimized_script(llm_llm.half(), ['forward_chunk'])
76
+ script.save('{}/llm.llm.fp16.zip'.format(args.model_dir))
77
+ logging.info('successfully export llm_llm')
78
+
79
+ # 3. export flow encoder
80
+ flow_encoder = model.model.flow.encoder
81
+ script = get_optimized_script(flow_encoder)
82
+ script.save('{}/flow.encoder.fp32.zip'.format(args.model_dir))
83
+ script = get_optimized_script(flow_encoder.half())
84
+ script.save('{}/flow.encoder.fp16.zip'.format(args.model_dir))
85
+ logging.info('successfully export flow_encoder')
86
+ elif model.__class__.__name__ == 'CosyVoice2':
87
+ # 1. export flow encoder
88
+ flow_encoder = model.model.flow.encoder
89
+ script = get_optimized_script(flow_encoder)
90
+ script.save('{}/flow.encoder.fp32.zip'.format(args.model_dir))
91
+ script = get_optimized_script(flow_encoder.half())
92
+ script.save('{}/flow.encoder.fp16.zip'.format(args.model_dir))
93
+ logging.info('successfully export flow_encoder')
94
+ else:
95
+ raise ValueError('unsupported model type')
96
+
97
+
98
+ if __name__ == '__main__':
99
+ main()
third_party/CosyVoice/cosyvoice/bin/export_onnx.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Antgroup Inc (authors: Zhoubofan, hexisyztem@icloud.com)
2
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import print_function
17
+
18
+ import argparse
19
+ import logging
20
+ logging.getLogger('matplotlib').setLevel(logging.WARNING)
21
+ import os
22
+ import sys
23
+ import onnxruntime
24
+ import random
25
+ import torch
26
+ from tqdm import tqdm
27
+ ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
28
+ sys.path.append('{}/../..'.format(ROOT_DIR))
29
+ sys.path.append('{}/../../third_party/Matcha-TTS'.format(ROOT_DIR))
30
+ from cosyvoice.cli.cosyvoice import AutoModel
31
+ from cosyvoice.utils.file_utils import logging
32
+
33
+
34
+ def get_dummy_input(batch_size, seq_len, out_channels, device):
35
+ x = torch.rand((batch_size, out_channels, seq_len), dtype=torch.float32, device=device)
36
+ mask = torch.ones((batch_size, 1, seq_len), dtype=torch.float32, device=device)
37
+ mu = torch.rand((batch_size, out_channels, seq_len), dtype=torch.float32, device=device)
38
+ t = torch.rand((batch_size), dtype=torch.float32, device=device)
39
+ spks = torch.rand((batch_size, out_channels), dtype=torch.float32, device=device)
40
+ cond = torch.rand((batch_size, out_channels, seq_len), dtype=torch.float32, device=device)
41
+ return x, mask, mu, t, spks, cond
42
+
43
+
44
+ def get_args():
45
+ parser = argparse.ArgumentParser(description='export your model for deployment')
46
+ parser.add_argument('--model_dir',
47
+ type=str,
48
+ default='pretrained_models/CosyVoice-300M',
49
+ help='local path')
50
+ args = parser.parse_args()
51
+ print(args)
52
+ return args
53
+
54
+
55
+ @torch.no_grad()
56
+ def main():
57
+ args = get_args()
58
+ logging.basicConfig(level=logging.DEBUG,
59
+ format='%(asctime)s %(levelname)s %(message)s')
60
+
61
+ model = AutoModel(model_dir=args.model_dir)
62
+
63
+ # 1. export flow decoder estimator
64
+ estimator = model.model.flow.decoder.estimator
65
+ estimator.eval()
66
+
67
+ device = model.model.device
68
+ batch_size, seq_len = 2, 256
69
+ out_channels = model.model.flow.decoder.estimator.out_channels
70
+ x, mask, mu, t, spks, cond = get_dummy_input(batch_size, seq_len, out_channels, device)
71
+ torch.onnx.export(
72
+ estimator,
73
+ (x, mask, mu, t, spks, cond),
74
+ '{}/flow.decoder.estimator.fp32.onnx'.format(args.model_dir),
75
+ export_params=True,
76
+ opset_version=18,
77
+ do_constant_folding=True,
78
+ input_names=['x', 'mask', 'mu', 't', 'spks', 'cond'],
79
+ output_names=['estimator_out'],
80
+ dynamic_axes={
81
+ 'x': {2: 'seq_len'},
82
+ 'mask': {2: 'seq_len'},
83
+ 'mu': {2: 'seq_len'},
84
+ 'cond': {2: 'seq_len'},
85
+ 'estimator_out': {2: 'seq_len'},
86
+ }
87
+ )
88
+
89
+ # 2. test computation consistency
90
+ option = onnxruntime.SessionOptions()
91
+ option.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
92
+ option.intra_op_num_threads = 1
93
+ providers = ['CUDAExecutionProvider' if torch.cuda.is_available() else 'CPUExecutionProvider']
94
+ estimator_onnx = onnxruntime.InferenceSession('{}/flow.decoder.estimator.fp32.onnx'.format(args.model_dir),
95
+ sess_options=option, providers=providers)
96
+
97
+ for _ in tqdm(range(10)):
98
+ x, mask, mu, t, spks, cond = get_dummy_input(batch_size, random.randint(16, 512), out_channels, device)
99
+ output_pytorch = estimator(x, mask, mu, t, spks, cond)
100
+ ort_inputs = {
101
+ 'x': x.cpu().numpy(),
102
+ 'mask': mask.cpu().numpy(),
103
+ 'mu': mu.cpu().numpy(),
104
+ 't': t.cpu().numpy(),
105
+ 'spks': spks.cpu().numpy(),
106
+ 'cond': cond.cpu().numpy()
107
+ }
108
+ output_onnx = estimator_onnx.run(None, ort_inputs)[0]
109
+ torch.testing.assert_allclose(output_pytorch, torch.from_numpy(output_onnx).to(device), rtol=1e-2, atol=1e-4)
110
+ logging.info('successfully export estimator')
111
+
112
+
113
+ if __name__ == "__main__":
114
+ main()
third_party/CosyVoice/cosyvoice/bin/train.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import print_function
16
+ import argparse
17
+ import datetime
18
+ import logging
19
+ logging.getLogger('matplotlib').setLevel(logging.WARNING)
20
+ from copy import deepcopy
21
+ import os
22
+ import torch
23
+ import torch.distributed as dist
24
+ import deepspeed
25
+
26
+ from hyperpyyaml import load_hyperpyyaml
27
+
28
+ from torch.distributed.elastic.multiprocessing.errors import record
29
+
30
+ from cosyvoice.utils.losses import DPOLoss
31
+ from cosyvoice.utils.executor import Executor
32
+ from cosyvoice.utils.train_utils import (
33
+ init_distributed,
34
+ init_dataset_and_dataloader,
35
+ init_optimizer_and_scheduler,
36
+ init_summarywriter, save_model,
37
+ wrap_cuda_model, check_modify_and_save_config)
38
+
39
+
40
+ def get_args():
41
+ parser = argparse.ArgumentParser(description='training your network')
42
+ parser.add_argument('--train_engine',
43
+ default='torch_ddp',
44
+ choices=['torch_ddp', 'deepspeed'],
45
+ help='Engine for paralleled training')
46
+ parser.add_argument('--model', required=True, help='model which will be trained')
47
+ parser.add_argument('--ref_model', required=False, help='ref model used in dpo')
48
+ parser.add_argument('--config', required=True, help='config file')
49
+ parser.add_argument('--train_data', required=True, help='train data file')
50
+ parser.add_argument('--cv_data', required=True, help='cv data file')
51
+ parser.add_argument('--qwen_pretrain_path', required=False, help='qwen pretrain path')
52
+ parser.add_argument('--onnx_path', required=False, help='onnx path, which is required for online feature extraction')
53
+ parser.add_argument('--checkpoint', help='checkpoint model')
54
+ parser.add_argument('--model_dir', required=True, help='save model dir')
55
+ parser.add_argument('--tensorboard_dir',
56
+ default='tensorboard',
57
+ help='tensorboard log dir')
58
+ parser.add_argument('--ddp.dist_backend',
59
+ dest='dist_backend',
60
+ default='nccl',
61
+ choices=['nccl', 'gloo'],
62
+ help='distributed backend')
63
+ parser.add_argument('--num_workers',
64
+ default=0,
65
+ type=int,
66
+ help='num of subprocess workers for reading')
67
+ parser.add_argument('--prefetch',
68
+ default=100,
69
+ type=int,
70
+ help='prefetch number')
71
+ parser.add_argument('--pin_memory',
72
+ action='store_true',
73
+ default=False,
74
+ help='Use pinned memory buffers used for reading')
75
+ parser.add_argument('--use_amp',
76
+ action='store_true',
77
+ default=False,
78
+ help='Use automatic mixed precision training')
79
+ parser.add_argument('--dpo',
80
+ action='store_true',
81
+ default=False,
82
+ help='Use Direct Preference Optimization')
83
+ parser.add_argument('--deepspeed.save_states',
84
+ dest='save_states',
85
+ default='model_only',
86
+ choices=['model_only', 'model+optimizer'],
87
+ help='save model/optimizer states')
88
+ parser.add_argument('--timeout',
89
+ default=60,
90
+ type=int,
91
+ help='timeout (in seconds) of cosyvoice_join.')
92
+ parser = deepspeed.add_config_arguments(parser)
93
+ args = parser.parse_args()
94
+ return args
95
+
96
+
97
+ @record
98
+ def main():
99
+ args = get_args()
100
+ os.environ['onnx_path'] = args.onnx_path
101
+ logging.basicConfig(level=logging.DEBUG,
102
+ format='%(asctime)s %(levelname)s %(message)s')
103
+ # gan train has some special initialization logic
104
+ gan = True if args.model == 'hifigan' else False
105
+
106
+ override_dict = {k: None for k in ['llm', 'flow', 'hift', 'hifigan'] if k != args.model}
107
+ if gan is True:
108
+ override_dict.pop('hift')
109
+ if args.qwen_pretrain_path is not None:
110
+ override_dict['qwen_pretrain_path'] = args.qwen_pretrain_path
111
+ with open(args.config, 'r') as f:
112
+ configs = load_hyperpyyaml(f, overrides=override_dict)
113
+ if gan is True:
114
+ configs['train_conf'] = configs['train_conf_gan']
115
+ configs['train_conf'].update(vars(args))
116
+
117
+ # Init env for ddp
118
+ init_distributed(args)
119
+
120
+ # Get dataset & dataloader
121
+ train_dataset, cv_dataset, train_data_loader, cv_data_loader = \
122
+ init_dataset_and_dataloader(args, configs, gan, args.dpo)
123
+
124
+ # Do some sanity checks and save config to arsg.model_dir
125
+ configs = check_modify_and_save_config(args, configs)
126
+
127
+ # Tensorboard summary
128
+ writer = init_summarywriter(args)
129
+
130
+ # load checkpoint
131
+ if args.dpo is True:
132
+ configs[args.model].forward = configs[args.model].forward_dpo
133
+ model = configs[args.model]
134
+ start_step, start_epoch = 0, -1
135
+ if args.checkpoint is not None:
136
+ if os.path.exists(args.checkpoint):
137
+ state_dict = torch.load(args.checkpoint, map_location='cpu')
138
+ model.load_state_dict(state_dict, strict=False)
139
+ if 'step' in state_dict:
140
+ start_step = state_dict['step']
141
+ if 'epoch' in state_dict:
142
+ start_epoch = state_dict['epoch']
143
+ else:
144
+ logging.warning('checkpoint {} do not exsist!'.format(args.checkpoint))
145
+
146
+ # Dispatch model from cpu to gpu
147
+ model = wrap_cuda_model(args, model)
148
+
149
+ # Get optimizer & scheduler
150
+ model, optimizer, scheduler, optimizer_d, scheduler_d = init_optimizer_and_scheduler(args, configs, model, gan)
151
+ scheduler.set_step(start_step)
152
+ if scheduler_d is not None:
153
+ scheduler_d.set_step(start_step)
154
+
155
+ # Save init checkpoints
156
+ info_dict = deepcopy(configs['train_conf'])
157
+ info_dict['step'] = start_step
158
+ info_dict['epoch'] = start_epoch
159
+ save_model(model, 'init', info_dict)
160
+
161
+ # DPO related
162
+ if args.dpo is True:
163
+ ref_model = deepcopy(configs[args.model])
164
+ state_dict = torch.load(args.ref_model, map_location='cpu')
165
+ ref_model.load_state_dict(state_dict, strict=False)
166
+ dpo_loss = DPOLoss(beta=0.01, label_smoothing=0.0, ipo=False)
167
+ # NOTE maybe it is not needed to wrap ref_model as ddp because its parameter is not updated
168
+ ref_model = wrap_cuda_model(args, ref_model)
169
+ else:
170
+ ref_model, dpo_loss = None, None
171
+
172
+ # Get executor
173
+ executor = Executor(gan=gan, ref_model=ref_model, dpo_loss=dpo_loss)
174
+ executor.step = start_step
175
+
176
+ # Init scaler, used for pytorch amp mixed precision training
177
+ scaler = torch.cuda.amp.GradScaler() if args.use_amp else None
178
+ print('start step {} start epoch {}'.format(start_step, start_epoch))
179
+
180
+ # Start training loop
181
+ for epoch in range(start_epoch + 1, info_dict['max_epoch']):
182
+ executor.epoch = epoch
183
+ train_dataset.set_epoch(epoch)
184
+ dist.barrier()
185
+ group_join = dist.new_group(backend="gloo", timeout=datetime.timedelta(seconds=args.timeout))
186
+ if gan is True:
187
+ executor.train_one_epoc_gan(model, optimizer, scheduler, optimizer_d, scheduler_d, train_data_loader, cv_data_loader,
188
+ writer, info_dict, scaler, group_join)
189
+ else:
190
+ executor.train_one_epoc(model, optimizer, scheduler, train_data_loader, cv_data_loader, writer, info_dict, scaler, group_join, ref_model=ref_model)
191
+ dist.destroy_process_group(group_join)
192
+
193
+
194
+ if __name__ == '__main__':
195
+ main()
third_party/CosyVoice/cosyvoice/cli/__init__.py ADDED
File without changes
third_party/CosyVoice/cosyvoice/cli/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (211 Bytes). View file
 
third_party/CosyVoice/cosyvoice/cli/__pycache__/cosyvoice.cpython-312.pyc ADDED
Binary file (18.8 kB). View file
 
third_party/CosyVoice/cosyvoice/cli/__pycache__/frontend.cpython-312.pyc ADDED
Binary file (15.2 kB). View file
 
third_party/CosyVoice/cosyvoice/cli/__pycache__/model.cpython-312.pyc ADDED
Binary file (35.7 kB). View file
 
third_party/CosyVoice/cosyvoice/cli/cosyvoice.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import os
15
+ import time
16
+ from typing import Generator
17
+ from tqdm import tqdm
18
+ from hyperpyyaml import load_hyperpyyaml
19
+ from modelscope import snapshot_download
20
+ import torch
21
+ from cosyvoice.cli.frontend import CosyVoiceFrontEnd
22
+ from cosyvoice.cli.model import CosyVoiceModel, CosyVoice2Model, CosyVoice3Model
23
+ from cosyvoice.utils.file_utils import logging
24
+ from cosyvoice.utils.class_utils import get_model_type
25
+
26
+
27
+ class CosyVoice:
28
+
29
+ def __init__(self, model_dir, load_jit=False, load_trt=False, fp16=False, trt_concurrent=1):
30
+ self.model_dir = model_dir
31
+ self.fp16 = fp16
32
+ if not os.path.exists(model_dir):
33
+ model_dir = snapshot_download(model_dir)
34
+ hyper_yaml_path = '{}/cosyvoice.yaml'.format(model_dir)
35
+ if not os.path.exists(hyper_yaml_path):
36
+ raise ValueError('{} not found!'.format(hyper_yaml_path))
37
+ with open(hyper_yaml_path, 'r') as f:
38
+ configs = load_hyperpyyaml(f)
39
+ assert get_model_type(configs) == CosyVoiceModel, 'do not use {} for CosyVoice initialization!'.format(model_dir)
40
+ self.frontend = CosyVoiceFrontEnd(configs['get_tokenizer'],
41
+ configs['feat_extractor'],
42
+ '{}/campplus.onnx'.format(model_dir),
43
+ '{}/speech_tokenizer_v1.onnx'.format(model_dir),
44
+ '{}/spk2info.pt'.format(model_dir),
45
+ configs['allowed_special'])
46
+ self.sample_rate = configs['sample_rate']
47
+ if torch.cuda.is_available() is False and (load_jit is True or load_trt is True or fp16 is True):
48
+ load_jit, load_trt, fp16 = False, False, False
49
+ logging.warning('no cuda device, set load_jit/load_trt/fp16 to False')
50
+ self.model = CosyVoiceModel(configs['llm'], configs['flow'], configs['hift'], fp16)
51
+ self.model.load('{}/llm.pt'.format(model_dir),
52
+ '{}/flow.pt'.format(model_dir),
53
+ '{}/hift.pt'.format(model_dir))
54
+ if load_jit:
55
+ self.model.load_jit('{}/llm.text_encoder.{}.zip'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'),
56
+ '{}/llm.llm.{}.zip'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'),
57
+ '{}/flow.encoder.{}.zip'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'))
58
+ if load_trt:
59
+ self.model.load_trt('{}/flow.decoder.estimator.{}.mygpu.plan'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'),
60
+ '{}/flow.decoder.estimator.fp32.onnx'.format(model_dir),
61
+ trt_concurrent,
62
+ self.fp16)
63
+ del configs
64
+
65
+ def list_available_spks(self):
66
+ spks = list(self.frontend.spk2info.keys())
67
+ return spks
68
+
69
+ def add_zero_shot_spk(self, prompt_text, prompt_wav, zero_shot_spk_id):
70
+ assert zero_shot_spk_id != '', 'do not use empty zero_shot_spk_id'
71
+ model_input = self.frontend.frontend_zero_shot('', prompt_text, prompt_wav, self.sample_rate, '')
72
+ del model_input['text']
73
+ del model_input['text_len']
74
+ self.frontend.spk2info[zero_shot_spk_id] = model_input
75
+ return True
76
+
77
+ def save_spkinfo(self):
78
+ torch.save(self.frontend.spk2info, '{}/spk2info.pt'.format(self.model_dir))
79
+
80
+ def inference_sft(self, tts_text, spk_id, stream=False, speed=1.0, text_frontend=True):
81
+ for i in tqdm(self.frontend.text_normalize(tts_text, split=True, text_frontend=text_frontend)):
82
+ model_input = self.frontend.frontend_sft(i, spk_id)
83
+ start_time = time.time()
84
+ logging.info('synthesis text {}'.format(i))
85
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
86
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
87
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
88
+ yield model_output
89
+ start_time = time.time()
90
+
91
+ def inference_zero_shot(self, tts_text, prompt_text, prompt_wav, zero_shot_spk_id='', stream=False, speed=1.0, text_frontend=True):
92
+ prompt_text = self.frontend.text_normalize(prompt_text, split=False, text_frontend=text_frontend)
93
+ for i in tqdm(self.frontend.text_normalize(tts_text, split=True, text_frontend=text_frontend)):
94
+ if (not isinstance(i, Generator)) and len(i) < 0.5 * len(prompt_text):
95
+ logging.warning('synthesis text {} too short than prompt text {}, this may lead to bad performance'.format(i, prompt_text))
96
+ model_input = self.frontend.frontend_zero_shot(i, prompt_text, prompt_wav, self.sample_rate, zero_shot_spk_id)
97
+ start_time = time.time()
98
+ logging.info('synthesis text {}'.format(i))
99
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
100
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
101
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
102
+ yield model_output
103
+ start_time = time.time()
104
+
105
+ def inference_cross_lingual(self, tts_text, prompt_wav, zero_shot_spk_id='', stream=False, speed=1.0, text_frontend=True):
106
+ for i in tqdm(self.frontend.text_normalize(tts_text, split=True, text_frontend=text_frontend)):
107
+ model_input = self.frontend.frontend_cross_lingual(i, prompt_wav, self.sample_rate, zero_shot_spk_id)
108
+ start_time = time.time()
109
+ logging.info('synthesis text {}'.format(i))
110
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
111
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
112
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
113
+ yield model_output
114
+ start_time = time.time()
115
+
116
+ def inference_instruct(self, tts_text, spk_id, instruct_text, stream=False, speed=1.0, text_frontend=True):
117
+ assert self.__class__.__name__ == 'CosyVoice', 'inference_instruct is only implemented for CosyVoice!'
118
+ instruct_text = self.frontend.text_normalize(instruct_text, split=False, text_frontend=text_frontend)
119
+ for i in tqdm(self.frontend.text_normalize(tts_text, split=True, text_frontend=text_frontend)):
120
+ model_input = self.frontend.frontend_instruct(i, spk_id, instruct_text)
121
+ start_time = time.time()
122
+ logging.info('synthesis text {}'.format(i))
123
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
124
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
125
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
126
+ yield model_output
127
+ start_time = time.time()
128
+
129
+ def inference_vc(self, source_wav, prompt_wav, stream=False, speed=1.0):
130
+ model_input = self.frontend.frontend_vc(source_wav, prompt_wav, self.sample_rate)
131
+ start_time = time.time()
132
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
133
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
134
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
135
+ yield model_output
136
+ start_time = time.time()
137
+
138
+
139
+ class CosyVoice2(CosyVoice):
140
+
141
+ def __init__(self, model_dir, load_jit=False, load_trt=False, load_vllm=False, fp16=False, trt_concurrent=1):
142
+ self.model_dir = model_dir
143
+ self.fp16 = fp16
144
+ if not os.path.exists(model_dir):
145
+ model_dir = snapshot_download(model_dir)
146
+ hyper_yaml_path = '{}/cosyvoice2.yaml'.format(model_dir)
147
+ if not os.path.exists(hyper_yaml_path):
148
+ raise ValueError('{} not found!'.format(hyper_yaml_path))
149
+ with open(hyper_yaml_path, 'r') as f:
150
+ configs = load_hyperpyyaml(f, overrides={'qwen_pretrain_path': os.path.join(model_dir, 'CosyVoice-BlankEN')})
151
+ assert get_model_type(configs) == CosyVoice2Model, 'do not use {} for CosyVoice2 initialization!'.format(model_dir)
152
+ self.frontend = CosyVoiceFrontEnd(configs['get_tokenizer'],
153
+ configs['feat_extractor'],
154
+ '{}/campplus.onnx'.format(model_dir),
155
+ '{}/speech_tokenizer_v2.onnx'.format(model_dir),
156
+ '{}/spk2info.pt'.format(model_dir),
157
+ configs['allowed_special'])
158
+ self.sample_rate = configs['sample_rate']
159
+ if torch.cuda.is_available() is False and (load_jit is True or load_trt is True or load_vllm is True or fp16 is True):
160
+ load_jit, load_trt, load_vllm, fp16 = False, False, False, False
161
+ logging.warning('no cuda device, set load_jit/load_trt/load_vllm/fp16 to False')
162
+ self.model = CosyVoice2Model(configs['llm'], configs['flow'], configs['hift'], fp16)
163
+ self.model.load('{}/llm.pt'.format(model_dir),
164
+ '{}/flow.pt'.format(model_dir),
165
+ '{}/hift.pt'.format(model_dir))
166
+ if load_vllm:
167
+ self.model.load_vllm('{}/vllm'.format(model_dir))
168
+ if load_jit:
169
+ self.model.load_jit('{}/flow.encoder.{}.zip'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'))
170
+ if load_trt:
171
+ self.model.load_trt('{}/flow.decoder.estimator.{}.mygpu.plan'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'),
172
+ '{}/flow.decoder.estimator.fp32.onnx'.format(model_dir),
173
+ trt_concurrent,
174
+ self.fp16)
175
+ del configs
176
+
177
+ def inference_instruct2(self, tts_text, instruct_text, prompt_wav, zero_shot_spk_id='', stream=False, speed=1.0, text_frontend=True):
178
+ for i in tqdm(self.frontend.text_normalize(tts_text, split=True, text_frontend=text_frontend)):
179
+ model_input = self.frontend.frontend_instruct2(i, instruct_text, prompt_wav, self.sample_rate, zero_shot_spk_id)
180
+ start_time = time.time()
181
+ logging.info('synthesis text {}'.format(i))
182
+ for model_output in self.model.tts(**model_input, stream=stream, speed=speed):
183
+ speech_len = model_output['tts_speech'].shape[1] / self.sample_rate
184
+ logging.info('yield speech len {}, rtf {}'.format(speech_len, (time.time() - start_time) / speech_len))
185
+ yield model_output
186
+ start_time = time.time()
187
+
188
+
189
+ class CosyVoice3(CosyVoice2):
190
+
191
+ def __init__(self, model_dir, load_trt=False, load_vllm=False, fp16=False, trt_concurrent=1):
192
+ self.model_dir = model_dir
193
+ self.fp16 = fp16
194
+ if not os.path.exists(model_dir):
195
+ model_dir = snapshot_download(model_dir)
196
+ hyper_yaml_path = '{}/cosyvoice3.yaml'.format(model_dir)
197
+ if not os.path.exists(hyper_yaml_path):
198
+ raise ValueError('{} not found!'.format(hyper_yaml_path))
199
+ with open(hyper_yaml_path, 'r') as f:
200
+ configs = load_hyperpyyaml(f, overrides={'qwen_pretrain_path': os.path.join(model_dir, 'CosyVoice-BlankEN')})
201
+ assert get_model_type(configs) == CosyVoice3Model, 'do not use {} for CosyVoice3 initialization!'.format(model_dir)
202
+ self.frontend = CosyVoiceFrontEnd(configs['get_tokenizer'],
203
+ configs['feat_extractor'],
204
+ '{}/campplus.onnx'.format(model_dir),
205
+ '{}/speech_tokenizer_v3.onnx'.format(model_dir),
206
+ '{}/spk2info.pt'.format(model_dir),
207
+ configs['allowed_special'])
208
+ self.sample_rate = configs['sample_rate']
209
+ if torch.cuda.is_available() is False and (load_trt is True or fp16 is True):
210
+ load_trt, fp16 = False, False
211
+ logging.warning('no cuda device, set load_trt/fp16 to False')
212
+ self.model = CosyVoice3Model(configs['llm'], configs['flow'], configs['hift'], fp16)
213
+ self.model.load('{}/llm.pt'.format(model_dir),
214
+ '{}/flow.pt'.format(model_dir),
215
+ '{}/hift.pt'.format(model_dir))
216
+ if load_vllm:
217
+ self.model.load_vllm('{}/vllm'.format(model_dir))
218
+ if load_trt:
219
+ if self.fp16 is True:
220
+ logging.warning('DiT tensorRT fp16 engine have some performance issue, use at caution!')
221
+ self.model.load_trt('{}/flow.decoder.estimator.{}.mygpu.plan'.format(model_dir, 'fp16' if self.fp16 is True else 'fp32'),
222
+ '{}/flow.decoder.estimator.fp32.onnx'.format(model_dir),
223
+ trt_concurrent,
224
+ self.fp16)
225
+ del configs
226
+
227
+
228
+ def AutoModel(**kwargs):
229
+ if not os.path.exists(kwargs['model_dir']):
230
+ kwargs['model_dir'] = snapshot_download(kwargs['model_dir'])
231
+ if os.path.exists('{}/cosyvoice.yaml'.format(kwargs['model_dir'])):
232
+ return CosyVoice(**kwargs)
233
+ elif os.path.exists('{}/cosyvoice2.yaml'.format(kwargs['model_dir'])):
234
+ return CosyVoice2(**kwargs)
235
+ elif os.path.exists('{}/cosyvoice3.yaml'.format(kwargs['model_dir'])):
236
+ return CosyVoice3(**kwargs)
237
+ else:
238
+ raise TypeError('No valid model type found!')
third_party/CosyVoice/cosyvoice/cli/frontend.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from functools import partial
15
+ from typing import Generator
16
+ import json
17
+ import onnxruntime
18
+ import torch
19
+ import numpy as np
20
+ import whisper
21
+ from typing import Callable
22
+ import torchaudio.compliance.kaldi as kaldi
23
+ import os
24
+ import re
25
+ import inflect
26
+ from cosyvoice.utils.file_utils import logging, load_wav
27
+ from cosyvoice.utils.frontend_utils import contains_chinese, replace_blank, replace_corner_mark, remove_bracket, spell_out_number, split_paragraph, is_only_punctuation
28
+
29
+
30
+ class CosyVoiceFrontEnd:
31
+
32
+ def __init__(self,
33
+ get_tokenizer: Callable,
34
+ feat_extractor: Callable,
35
+ campplus_model: str,
36
+ speech_tokenizer_model: str,
37
+ spk2info: str = '',
38
+ allowed_special: str = 'all'):
39
+ self.tokenizer = get_tokenizer()
40
+ self.feat_extractor = feat_extractor
41
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
42
+ option = onnxruntime.SessionOptions()
43
+ option.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
44
+ option.intra_op_num_threads = 1
45
+ self.campplus_session = onnxruntime.InferenceSession(campplus_model, sess_options=option, providers=["CPUExecutionProvider"])
46
+ self.speech_tokenizer_session = onnxruntime.InferenceSession(speech_tokenizer_model, sess_options=option,
47
+ providers=["CUDAExecutionProvider" if torch.cuda.is_available() else
48
+ "CPUExecutionProvider"])
49
+ if os.path.exists(spk2info):
50
+ self.spk2info = torch.load(spk2info, map_location=self.device, weights_only=True)
51
+ else:
52
+ self.spk2info = {}
53
+ self.allowed_special = allowed_special
54
+ self.inflect_parser = inflect.engine()
55
+ # NOTE compatible when no text frontend tool is avaliable
56
+ try:
57
+ import ttsfrd
58
+ self.frd = ttsfrd.TtsFrontendEngine()
59
+ ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
60
+ assert self.frd.initialize('{}/../../pretrained_models/CosyVoice-ttsfrd/resource'.format(ROOT_DIR)) is True, \
61
+ 'failed to initialize ttsfrd resource'
62
+ self.frd.set_lang_type('pinyinvg')
63
+ self.text_frontend = 'ttsfrd'
64
+ logging.info('use ttsfrd frontend')
65
+ except:
66
+ try:
67
+ from wetext import Normalizer as ZhNormalizer
68
+ from wetext import Normalizer as EnNormalizer
69
+ self.zh_tn_model = ZhNormalizer(remove_erhua=False)
70
+ self.en_tn_model = EnNormalizer()
71
+ self.text_frontend = 'wetext'
72
+ logging.info('use wetext frontend')
73
+ except:
74
+ self.text_frontend = ''
75
+ logging.info('no frontend is avaliable')
76
+
77
+
78
+ def _extract_text_token(self, text):
79
+ if isinstance(text, Generator):
80
+ logging.info('get tts_text generator, will return _extract_text_token_generator!')
81
+ # NOTE add a dummy text_token_len for compatibility
82
+ return self._extract_text_token_generator(text), torch.tensor([0], dtype=torch.int32).to(self.device)
83
+ else:
84
+ text_token = self.tokenizer.encode(text, allowed_special=self.allowed_special)
85
+ text_token = torch.tensor([text_token], dtype=torch.int32).to(self.device)
86
+ text_token_len = torch.tensor([text_token.shape[1]], dtype=torch.int32).to(self.device)
87
+ return text_token, text_token_len
88
+
89
+ def _extract_text_token_generator(self, text_generator):
90
+ for text in text_generator:
91
+ text_token, _ = self._extract_text_token(text)
92
+ for i in range(text_token.shape[1]):
93
+ yield text_token[:, i: i + 1]
94
+
95
+ def _extract_speech_token(self, prompt_wav):
96
+ speech = load_wav(prompt_wav, 16000)
97
+ assert speech.shape[1] / 16000 <= 30, 'do not support extract speech token for audio longer than 30s'
98
+ feat = whisper.log_mel_spectrogram(speech, n_mels=128)
99
+ speech_token = self.speech_tokenizer_session.run(None,
100
+ {self.speech_tokenizer_session.get_inputs()[0].name:
101
+ feat.detach().cpu().numpy(),
102
+ self.speech_tokenizer_session.get_inputs()[1].name:
103
+ np.array([feat.shape[2]], dtype=np.int32)})[0].flatten().tolist()
104
+ speech_token = torch.tensor([speech_token], dtype=torch.int32).to(self.device)
105
+ speech_token_len = torch.tensor([speech_token.shape[1]], dtype=torch.int32).to(self.device)
106
+ return speech_token, speech_token_len
107
+
108
+ def _extract_spk_embedding(self, prompt_wav):
109
+ speech = load_wav(prompt_wav, 16000)
110
+ feat = kaldi.fbank(speech,
111
+ num_mel_bins=80,
112
+ dither=0,
113
+ sample_frequency=16000)
114
+ feat = feat - feat.mean(dim=0, keepdim=True)
115
+ embedding = self.campplus_session.run(None,
116
+ {self.campplus_session.get_inputs()[0].name: feat.unsqueeze(dim=0).cpu().numpy()})[0].flatten().tolist()
117
+ embedding = torch.tensor([embedding]).to(self.device)
118
+ return embedding
119
+
120
+ def _extract_speech_feat(self, prompt_wav):
121
+ speech = load_wav(prompt_wav, 24000)
122
+ speech_feat = self.feat_extractor(speech).squeeze(dim=0).transpose(0, 1).to(self.device)
123
+ speech_feat = speech_feat.unsqueeze(dim=0)
124
+ speech_feat_len = torch.tensor([speech_feat.shape[1]], dtype=torch.int32).to(self.device)
125
+ return speech_feat, speech_feat_len
126
+
127
+ def text_normalize(self, text, split=True, text_frontend=True):
128
+ if isinstance(text, Generator):
129
+ logging.info('get tts_text generator, will skip text_normalize!')
130
+ return [text]
131
+ # NOTE skip text_frontend when ssml symbol in text
132
+ if '<|' in text and '|>' in text:
133
+ text_frontend = False
134
+ if text_frontend is False or text == '':
135
+ return [text] if split is True else text
136
+ text = text.strip()
137
+ if self.text_frontend == 'ttsfrd':
138
+ texts = [i["text"] for i in json.loads(self.frd.do_voicegen_frd(text))["sentences"]]
139
+ text = ''.join(texts)
140
+ else:
141
+ if contains_chinese(text):
142
+ if self.text_frontend == 'wetext':
143
+ text = self.zh_tn_model.normalize(text)
144
+ text = text.replace("\n", "")
145
+ text = replace_blank(text)
146
+ text = replace_corner_mark(text)
147
+ text = text.replace(".", "。")
148
+ text = text.replace(" - ", ",")
149
+ text = remove_bracket(text)
150
+ text = re.sub(r'[,,、]+$', '。', text)
151
+ texts = list(split_paragraph(text, partial(self.tokenizer.encode, allowed_special=self.allowed_special), "zh", token_max_n=80,
152
+ token_min_n=60, merge_len=20, comma_split=False))
153
+ else:
154
+ if self.text_frontend == 'wetext':
155
+ text = self.en_tn_model.normalize(text)
156
+ text = spell_out_number(text, self.inflect_parser)
157
+ texts = list(split_paragraph(text, partial(self.tokenizer.encode, allowed_special=self.allowed_special), "en", token_max_n=80,
158
+ token_min_n=60, merge_len=20, comma_split=False))
159
+ texts = [i for i in texts if not is_only_punctuation(i)]
160
+ return texts if split is True else text
161
+
162
+ def frontend_sft(self, tts_text, spk_id):
163
+ tts_text_token, tts_text_token_len = self._extract_text_token(tts_text)
164
+ embedding = self.spk2info[spk_id]['embedding']
165
+ model_input = {'text': tts_text_token, 'text_len': tts_text_token_len, 'llm_embedding': embedding, 'flow_embedding': embedding}
166
+ return model_input
167
+
168
+ def frontend_zero_shot(self, tts_text, prompt_text, prompt_wav, resample_rate, zero_shot_spk_id):
169
+ tts_text_token, tts_text_token_len = self._extract_text_token(tts_text)
170
+ if zero_shot_spk_id == '':
171
+ prompt_text_token, prompt_text_token_len = self._extract_text_token(prompt_text)
172
+ speech_feat, speech_feat_len = self._extract_speech_feat(prompt_wav)
173
+ speech_token, speech_token_len = self._extract_speech_token(prompt_wav)
174
+ if resample_rate == 24000:
175
+ # cosyvoice2, force speech_feat % speech_token = 2
176
+ token_len = min(int(speech_feat.shape[1] / 2), speech_token.shape[1])
177
+ speech_feat, speech_feat_len[:] = speech_feat[:, :2 * token_len], 2 * token_len
178
+ speech_token, speech_token_len[:] = speech_token[:, :token_len], token_len
179
+ embedding = self._extract_spk_embedding(prompt_wav)
180
+ model_input = {'prompt_text': prompt_text_token, 'prompt_text_len': prompt_text_token_len,
181
+ 'llm_prompt_speech_token': speech_token, 'llm_prompt_speech_token_len': speech_token_len,
182
+ 'flow_prompt_speech_token': speech_token, 'flow_prompt_speech_token_len': speech_token_len,
183
+ 'prompt_speech_feat': speech_feat, 'prompt_speech_feat_len': speech_feat_len,
184
+ 'llm_embedding': embedding, 'flow_embedding': embedding}
185
+ else:
186
+ model_input = {**self.spk2info[zero_shot_spk_id]}
187
+ model_input['text'] = tts_text_token
188
+ model_input['text_len'] = tts_text_token_len
189
+ return model_input
190
+
191
+ def frontend_cross_lingual(self, tts_text, prompt_wav, resample_rate, zero_shot_spk_id):
192
+ model_input = self.frontend_zero_shot(tts_text, '', prompt_wav, resample_rate, zero_shot_spk_id)
193
+ # in cross lingual mode, we remove prompt in llm
194
+ del model_input['prompt_text']
195
+ del model_input['prompt_text_len']
196
+ del model_input['llm_prompt_speech_token']
197
+ del model_input['llm_prompt_speech_token_len']
198
+ return model_input
199
+
200
+ def frontend_instruct(self, tts_text, spk_id, instruct_text):
201
+ model_input = self.frontend_sft(tts_text, spk_id)
202
+ # in instruct mode, we remove spk_embedding in llm due to information leakage
203
+ del model_input['llm_embedding']
204
+ instruct_text_token, instruct_text_token_len = self._extract_text_token(instruct_text)
205
+ model_input['prompt_text'] = instruct_text_token
206
+ model_input['prompt_text_len'] = instruct_text_token_len
207
+ return model_input
208
+
209
+ def frontend_instruct2(self, tts_text, instruct_text, prompt_wav, resample_rate, zero_shot_spk_id):
210
+ model_input = self.frontend_zero_shot(tts_text, instruct_text, prompt_wav, resample_rate, zero_shot_spk_id)
211
+ del model_input['llm_prompt_speech_token']
212
+ del model_input['llm_prompt_speech_token_len']
213
+ return model_input
214
+
215
+ def frontend_vc(self, source_speech_16k, prompt_wav, resample_rate):
216
+ prompt_speech_token, prompt_speech_token_len = self._extract_speech_token(prompt_wav)
217
+ prompt_speech_feat, prompt_speech_feat_len = self._extract_speech_feat(prompt_wav)
218
+ embedding = self._extract_spk_embedding(prompt_wav)
219
+ source_speech_token, source_speech_token_len = self._extract_speech_token(source_speech_16k)
220
+ model_input = {'source_speech_token': source_speech_token, 'source_speech_token_len': source_speech_token_len,
221
+ 'flow_prompt_speech_token': prompt_speech_token, 'flow_prompt_speech_token_len': prompt_speech_token_len,
222
+ 'prompt_speech_feat': prompt_speech_feat, 'prompt_speech_feat_len': prompt_speech_feat_len,
223
+ 'flow_embedding': embedding}
224
+ return model_input
third_party/CosyVoice/cosyvoice/cli/model.py ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ # 2025 Alibaba Inc (authors: Xiang Lyu, Bofan Zhou)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import os
16
+ from typing import Generator
17
+ import torch
18
+ import numpy as np
19
+ import threading
20
+ import time
21
+ from torch.nn import functional as F
22
+ from contextlib import nullcontext
23
+ import uuid
24
+ from cosyvoice.utils.common import fade_in_out
25
+ from cosyvoice.utils.file_utils import convert_onnx_to_trt, export_cosyvoice2_vllm
26
+ from cosyvoice.utils.common import TrtContextWrapper
27
+
28
+
29
+ class CosyVoiceModel:
30
+
31
+ def __init__(self,
32
+ llm: torch.nn.Module,
33
+ flow: torch.nn.Module,
34
+ hift: torch.nn.Module,
35
+ fp16: bool = False):
36
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
37
+ self.llm = llm
38
+ self.flow = flow
39
+ self.hift = hift
40
+ self.fp16 = fp16
41
+ self.token_min_hop_len = 2 * self.flow.input_frame_rate
42
+ self.token_max_hop_len = 4 * self.flow.input_frame_rate
43
+ self.token_overlap_len = 20
44
+ # mel fade in out
45
+ self.mel_overlap_len = int(self.token_overlap_len / self.flow.input_frame_rate * 22050 / 256)
46
+ self.mel_window = np.hamming(2 * self.mel_overlap_len)
47
+ # hift cache
48
+ self.mel_cache_len = 20
49
+ self.source_cache_len = int(self.mel_cache_len * 256)
50
+ # speech fade in out
51
+ self.speech_window = np.hamming(2 * self.source_cache_len)
52
+ # rtf and decoding related
53
+ self.stream_scale_factor = 1
54
+ assert self.stream_scale_factor >= 1, 'stream_scale_factor should be greater than 1, change it according to your actual rtf'
55
+ self.llm_context = torch.cuda.stream(torch.cuda.Stream(self.device)) if torch.cuda.is_available() else nullcontext()
56
+ self.lock = threading.Lock()
57
+ # dict used to store session related variable
58
+ self.tts_speech_token_dict = {}
59
+ self.llm_end_dict = {}
60
+ self.mel_overlap_dict = {}
61
+ self.flow_cache_dict = {}
62
+ self.hift_cache_dict = {}
63
+ self.llm_exception_dict = {}
64
+ self.silent_tokens = []
65
+
66
+ def load(self, llm_model, flow_model, hift_model):
67
+ self.llm.load_state_dict(torch.load(llm_model, map_location=self.device, weights_only=True), strict=True)
68
+ self.llm.to(self.device).eval()
69
+ self.flow.load_state_dict(torch.load(flow_model, map_location=self.device, weights_only=True), strict=True)
70
+ self.flow.to(self.device).eval()
71
+ # in case hift_model is a hifigan model
72
+ hift_state_dict = {k.replace('generator.', ''): v for k, v in torch.load(hift_model, map_location=self.device, weights_only=True).items()}
73
+ self.hift.load_state_dict(hift_state_dict, strict=True)
74
+ self.hift.to(self.device).eval()
75
+
76
+ def load_jit(self, llm_text_encoder_model, llm_llm_model, flow_encoder_model):
77
+ llm_text_encoder = torch.jit.load(llm_text_encoder_model, map_location=self.device)
78
+ self.llm.text_encoder = llm_text_encoder
79
+ llm_llm = torch.jit.load(llm_llm_model, map_location=self.device)
80
+ self.llm.llm = llm_llm
81
+ flow_encoder = torch.jit.load(flow_encoder_model, map_location=self.device)
82
+ self.flow.encoder = flow_encoder
83
+
84
+ def load_trt(self, flow_decoder_estimator_model, flow_decoder_onnx_model, trt_concurrent, fp16):
85
+ assert torch.cuda.is_available(), 'tensorrt only supports gpu!'
86
+ if not os.path.exists(flow_decoder_estimator_model) or os.path.getsize(flow_decoder_estimator_model) == 0:
87
+ convert_onnx_to_trt(flow_decoder_estimator_model, self.get_trt_kwargs(), flow_decoder_onnx_model, fp16)
88
+ del self.flow.decoder.estimator
89
+ import tensorrt as trt
90
+ with open(flow_decoder_estimator_model, 'rb') as f:
91
+ estimator_engine = trt.Runtime(trt.Logger(trt.Logger.INFO)).deserialize_cuda_engine(f.read())
92
+ assert estimator_engine is not None, 'failed to load trt {}'.format(flow_decoder_estimator_model)
93
+ self.flow.decoder.estimator = TrtContextWrapper(estimator_engine, trt_concurrent=trt_concurrent, device=self.device)
94
+
95
+ def get_trt_kwargs(self):
96
+ min_shape = [(2, 80, 4), (2, 1, 4), (2, 80, 4), (2, 80, 4)]
97
+ opt_shape = [(2, 80, 500), (2, 1, 500), (2, 80, 500), (2, 80, 500)]
98
+ max_shape = [(2, 80, 3000), (2, 1, 3000), (2, 80, 3000), (2, 80, 3000)]
99
+ input_names = ["x", "mask", "mu", "cond"]
100
+ return {'min_shape': min_shape, 'opt_shape': opt_shape, 'max_shape': max_shape, 'input_names': input_names}
101
+
102
+ def llm_job(self, text, prompt_text, llm_prompt_speech_token, llm_embedding, uuid):
103
+ cur_silent_token_num, max_silent_token_num = 0, 5
104
+ try:
105
+ with self.llm_context, torch.cuda.amp.autocast(self.fp16 is True and hasattr(self.llm, 'vllm') is False):
106
+ if isinstance(text, Generator):
107
+ assert (self.__class__.__name__ != 'CosyVoiceModel') and not hasattr(self.llm, 'vllm'), 'streaming input text is only implemented for CosyVoice2/3 and do not support vllm!'
108
+ token_generator = self.llm.inference_bistream(text=text,
109
+ prompt_text=prompt_text.to(self.device),
110
+ prompt_text_len=torch.tensor([prompt_text.shape[1]], dtype=torch.int32).to(self.device),
111
+ prompt_speech_token=llm_prompt_speech_token.to(self.device),
112
+ prompt_speech_token_len=torch.tensor([llm_prompt_speech_token.shape[1]], dtype=torch.int32).to(self.device),
113
+ embedding=llm_embedding.to(self.device))
114
+ else:
115
+ token_generator = self.llm.inference(text=text.to(self.device),
116
+ text_len=torch.tensor([text.shape[1]], dtype=torch.int32).to(self.device),
117
+ prompt_text=prompt_text.to(self.device),
118
+ prompt_text_len=torch.tensor([prompt_text.shape[1]], dtype=torch.int32).to(self.device),
119
+ prompt_speech_token=llm_prompt_speech_token.to(self.device),
120
+ prompt_speech_token_len=torch.tensor([llm_prompt_speech_token.shape[1]], dtype=torch.int32).to(self.device),
121
+ embedding=llm_embedding.to(self.device),
122
+ uuid=uuid)
123
+ for i in token_generator:
124
+ if i in self.silent_tokens:
125
+ cur_silent_token_num += 1
126
+ if cur_silent_token_num > max_silent_token_num:
127
+ continue
128
+ else:
129
+ cur_silent_token_num = 0
130
+ self.tts_speech_token_dict[uuid].append(i)
131
+ except Exception as exc:
132
+ self.llm_exception_dict[uuid] = exc
133
+ finally:
134
+ self.llm_end_dict[uuid] = True
135
+
136
+ def vc_job(self, source_speech_token, uuid):
137
+ self.tts_speech_token_dict[uuid] = source_speech_token.flatten().tolist()
138
+ self.llm_end_dict[uuid] = True
139
+
140
+ def token2wav(self, token, prompt_token, prompt_feat, embedding, uuid, finalize=False, speed=1.0):
141
+ with torch.cuda.amp.autocast(self.fp16):
142
+ tts_mel, self.flow_cache_dict[uuid] = self.flow.inference(token=token.to(self.device, dtype=torch.int32),
143
+ token_len=torch.tensor([token.shape[1]], dtype=torch.int32).to(self.device),
144
+ prompt_token=prompt_token.to(self.device),
145
+ prompt_token_len=torch.tensor([prompt_token.shape[1]], dtype=torch.int32).to(self.device),
146
+ prompt_feat=prompt_feat.to(self.device),
147
+ prompt_feat_len=torch.tensor([prompt_feat.shape[1]], dtype=torch.int32).to(self.device),
148
+ embedding=embedding.to(self.device),
149
+ flow_cache=self.flow_cache_dict[uuid])
150
+
151
+ # mel overlap fade in out
152
+ if self.mel_overlap_dict[uuid].shape[2] != 0:
153
+ tts_mel = fade_in_out(tts_mel, self.mel_overlap_dict[uuid], self.mel_window)
154
+ # append hift cache
155
+ if self.hift_cache_dict[uuid] is not None:
156
+ hift_cache_mel, hift_cache_source = self.hift_cache_dict[uuid]['mel'], self.hift_cache_dict[uuid]['source']
157
+ tts_mel = torch.concat([hift_cache_mel, tts_mel], dim=2)
158
+ else:
159
+ hift_cache_source = torch.zeros(1, 1, 0)
160
+ # keep overlap mel and hift cache
161
+ if finalize is False:
162
+ self.mel_overlap_dict[uuid] = tts_mel[:, :, -self.mel_overlap_len:]
163
+ tts_mel = tts_mel[:, :, :-self.mel_overlap_len]
164
+ tts_speech, tts_source = self.hift.inference(speech_feat=tts_mel, cache_source=hift_cache_source)
165
+ if self.hift_cache_dict[uuid] is not None:
166
+ tts_speech = fade_in_out(tts_speech, self.hift_cache_dict[uuid]['speech'], self.speech_window)
167
+ self.hift_cache_dict[uuid] = {'mel': tts_mel[:, :, -self.mel_cache_len:],
168
+ 'source': tts_source[:, :, -self.source_cache_len:],
169
+ 'speech': tts_speech[:, -self.source_cache_len:]}
170
+ tts_speech = tts_speech[:, :-self.source_cache_len]
171
+ else:
172
+ if speed != 1.0:
173
+ assert self.hift_cache_dict[uuid] is None, 'speed change only support non-stream inference mode'
174
+ tts_mel = F.interpolate(tts_mel, size=int(tts_mel.shape[2] / speed), mode='linear')
175
+ tts_speech, tts_source = self.hift.inference(speech_feat=tts_mel, cache_source=hift_cache_source)
176
+ if self.hift_cache_dict[uuid] is not None:
177
+ tts_speech = fade_in_out(tts_speech, self.hift_cache_dict[uuid]['speech'], self.speech_window)
178
+ return tts_speech
179
+
180
+ def tts(self, text=torch.zeros(1, 0, dtype=torch.int32), flow_embedding=torch.zeros(0, 192), llm_embedding=torch.zeros(0, 192),
181
+ prompt_text=torch.zeros(1, 0, dtype=torch.int32),
182
+ llm_prompt_speech_token=torch.zeros(1, 0, dtype=torch.int32),
183
+ flow_prompt_speech_token=torch.zeros(1, 0, dtype=torch.int32),
184
+ prompt_speech_feat=torch.zeros(1, 0, 80), source_speech_token=torch.zeros(1, 0, dtype=torch.int32), stream=False, speed=1.0, **kwargs):
185
+ # this_uuid is used to track variables related to this inference thread
186
+ this_uuid = str(uuid.uuid1())
187
+ with self.lock:
188
+ self.tts_speech_token_dict[this_uuid], self.llm_end_dict[this_uuid] = [], False
189
+ self.hift_cache_dict[this_uuid] = None
190
+ self.mel_overlap_dict[this_uuid] = torch.zeros(1, 80, 0)
191
+ self.flow_cache_dict[this_uuid] = torch.zeros(1, 80, 0, 2)
192
+ if source_speech_token.shape[1] == 0:
193
+ p = threading.Thread(target=self.llm_job, args=(text, prompt_text, llm_prompt_speech_token, llm_embedding, this_uuid))
194
+ else:
195
+ p = threading.Thread(target=self.vc_job, args=(source_speech_token, this_uuid))
196
+ p.start()
197
+ if stream is True:
198
+ token_hop_len = self.token_min_hop_len
199
+ while True:
200
+ time.sleep(0.1)
201
+ if len(self.tts_speech_token_dict[this_uuid]) >= token_hop_len + self.token_overlap_len:
202
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid][:token_hop_len + self.token_overlap_len]) \
203
+ .unsqueeze(dim=0)
204
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
205
+ prompt_token=flow_prompt_speech_token,
206
+ prompt_feat=prompt_speech_feat,
207
+ embedding=flow_embedding,
208
+ uuid=this_uuid,
209
+ finalize=False)
210
+ yield {'tts_speech': this_tts_speech.cpu()}
211
+ with self.lock:
212
+ self.tts_speech_token_dict[this_uuid] = self.tts_speech_token_dict[this_uuid][token_hop_len:]
213
+ # increase token_hop_len for better speech quality
214
+ token_hop_len = min(self.token_max_hop_len, int(token_hop_len * self.stream_scale_factor))
215
+ if self.llm_end_dict[this_uuid] is True and len(self.tts_speech_token_dict[this_uuid]) < token_hop_len + self.token_overlap_len:
216
+ break
217
+ p.join()
218
+ # deal with remain tokens, make sure inference remain token len equals token_hop_len when cache_speech is not None
219
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid]).unsqueeze(dim=0)
220
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
221
+ prompt_token=flow_prompt_speech_token,
222
+ prompt_feat=prompt_speech_feat,
223
+ embedding=flow_embedding,
224
+ uuid=this_uuid,
225
+ finalize=True)
226
+ yield {'tts_speech': this_tts_speech.cpu()}
227
+ else:
228
+ # deal with all tokens
229
+ p.join()
230
+ if this_uuid in self.llm_exception_dict:
231
+ raise self.llm_exception_dict[this_uuid]
232
+ if len(self.tts_speech_token_dict[this_uuid]) == 0:
233
+ raise RuntimeError('CosyVoice LLM produced no speech tokens.')
234
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid]).unsqueeze(dim=0)
235
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
236
+ prompt_token=flow_prompt_speech_token,
237
+ prompt_feat=prompt_speech_feat,
238
+ embedding=flow_embedding,
239
+ uuid=this_uuid,
240
+ finalize=True,
241
+ speed=speed)
242
+ yield {'tts_speech': this_tts_speech.cpu()}
243
+ with self.lock:
244
+ self.tts_speech_token_dict.pop(this_uuid)
245
+ self.llm_end_dict.pop(this_uuid)
246
+ self.llm_exception_dict.pop(this_uuid, None)
247
+ self.mel_overlap_dict.pop(this_uuid)
248
+ self.hift_cache_dict.pop(this_uuid)
249
+ self.flow_cache_dict.pop(this_uuid)
250
+ if torch.cuda.is_available():
251
+ torch.cuda.empty_cache()
252
+ torch.cuda.current_stream().synchronize()
253
+
254
+
255
+ class CosyVoice2Model(CosyVoiceModel):
256
+
257
+ def __init__(self,
258
+ llm: torch.nn.Module,
259
+ flow: torch.nn.Module,
260
+ hift: torch.nn.Module,
261
+ fp16: bool = False):
262
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
263
+ self.llm = llm
264
+ self.flow = flow
265
+ self.hift = hift
266
+ self.fp16 = fp16
267
+ # NOTE must matching training static_chunk_size
268
+ self.token_hop_len = 25
269
+ # NOTE increase token_hop_len incrementally to avoid duplicate inference
270
+ self.token_max_hop_len = 4 * self.token_hop_len
271
+ self.stream_scale_factor = 2
272
+ assert self.stream_scale_factor >= 1, 'stream_scale_factor should be greater than 1, change it according to your actual rtf'
273
+ # hift cache
274
+ self.mel_cache_len = 8
275
+ self.source_cache_len = int(self.mel_cache_len * 480)
276
+ # speech fade in out
277
+ self.speech_window = np.hamming(2 * self.source_cache_len)
278
+ # rtf and decoding related
279
+ self.llm_context = torch.cuda.stream(torch.cuda.Stream(self.device)) if torch.cuda.is_available() else nullcontext()
280
+ self.lock = threading.Lock()
281
+ # dict used to store session related variable
282
+ self.tts_speech_token_dict = {}
283
+ self.llm_end_dict = {}
284
+ self.hift_cache_dict = {}
285
+ self.llm_exception_dict = {}
286
+ self.silent_tokens = []
287
+
288
+ def load_jit(self, flow_encoder_model):
289
+ flow_encoder = torch.jit.load(flow_encoder_model, map_location=self.device)
290
+ self.flow.encoder = flow_encoder
291
+
292
+ def load_vllm(self, model_dir):
293
+ export_cosyvoice2_vllm(self.llm, model_dir, self.device)
294
+ from vllm import EngineArgs, LLMEngine
295
+ engine_args = EngineArgs(model=model_dir,
296
+ skip_tokenizer_init=True,
297
+ enable_prompt_embeds=True,
298
+ gpu_memory_utilization=0.2)
299
+ self.llm.vllm = LLMEngine.from_engine_args(engine_args)
300
+ self.llm.lock = threading.Lock()
301
+ del self.llm.llm.model.model.layers
302
+
303
+ def token2wav(self, token, prompt_token, prompt_feat, embedding, token_offset, uuid, stream=False, finalize=False, speed=1.0):
304
+ with torch.cuda.amp.autocast(self.fp16):
305
+ tts_mel, _ = self.flow.inference(token=token.to(self.device, dtype=torch.int32),
306
+ token_len=torch.tensor([token.shape[1]], dtype=torch.int32).to(self.device),
307
+ prompt_token=prompt_token.to(self.device),
308
+ prompt_token_len=torch.tensor([prompt_token.shape[1]], dtype=torch.int32).to(self.device),
309
+ prompt_feat=prompt_feat.to(self.device),
310
+ prompt_feat_len=torch.tensor([prompt_feat.shape[1]], dtype=torch.int32).to(self.device),
311
+ embedding=embedding.to(self.device),
312
+ streaming=stream,
313
+ finalize=finalize)
314
+ tts_mel = tts_mel[:, :, token_offset * self.flow.token_mel_ratio:]
315
+ # append hift cache
316
+ if self.hift_cache_dict[uuid] is not None:
317
+ hift_cache_mel, hift_cache_source = self.hift_cache_dict[uuid]['mel'], self.hift_cache_dict[uuid]['source']
318
+ tts_mel = torch.concat([hift_cache_mel, tts_mel], dim=2)
319
+ else:
320
+ hift_cache_source = torch.zeros(1, 1, 0)
321
+ # keep overlap mel and hift cache
322
+ if finalize is False:
323
+ tts_speech, tts_source = self.hift.inference(speech_feat=tts_mel, cache_source=hift_cache_source)
324
+ if self.hift_cache_dict[uuid] is not None:
325
+ tts_speech = fade_in_out(tts_speech, self.hift_cache_dict[uuid]['speech'], self.speech_window)
326
+ self.hift_cache_dict[uuid] = {'mel': tts_mel[:, :, -self.mel_cache_len:],
327
+ 'source': tts_source[:, :, -self.source_cache_len:],
328
+ 'speech': tts_speech[:, -self.source_cache_len:]}
329
+ tts_speech = tts_speech[:, :-self.source_cache_len]
330
+ else:
331
+ if speed != 1.0:
332
+ assert self.hift_cache_dict[uuid] is None, 'speed change only support non-stream inference mode'
333
+ tts_mel = F.interpolate(tts_mel, size=int(tts_mel.shape[2] / speed), mode='linear')
334
+ tts_speech, tts_source = self.hift.inference(speech_feat=tts_mel, cache_source=hift_cache_source)
335
+ if self.hift_cache_dict[uuid] is not None:
336
+ tts_speech = fade_in_out(tts_speech, self.hift_cache_dict[uuid]['speech'], self.speech_window)
337
+ return tts_speech
338
+
339
+ def tts(self, text=torch.zeros(1, 0, dtype=torch.int32), flow_embedding=torch.zeros(0, 192), llm_embedding=torch.zeros(0, 192),
340
+ prompt_text=torch.zeros(1, 0, dtype=torch.int32),
341
+ llm_prompt_speech_token=torch.zeros(1, 0, dtype=torch.int32),
342
+ flow_prompt_speech_token=torch.zeros(1, 0, dtype=torch.int32),
343
+ prompt_speech_feat=torch.zeros(1, 0, 80), source_speech_token=torch.zeros(1, 0, dtype=torch.int32), stream=False, speed=1.0, **kwargs):
344
+ # this_uuid is used to track variables related to this inference thread
345
+ this_uuid = str(uuid.uuid1())
346
+ with self.lock:
347
+ self.tts_speech_token_dict[this_uuid], self.llm_end_dict[this_uuid] = [], False
348
+ self.hift_cache_dict[this_uuid] = None
349
+ if source_speech_token.shape[1] == 0:
350
+ p = threading.Thread(target=self.llm_job, args=(text, prompt_text, llm_prompt_speech_token, llm_embedding, this_uuid))
351
+ else:
352
+ p = threading.Thread(target=self.vc_job, args=(source_speech_token, this_uuid))
353
+ p.start()
354
+ if stream is True:
355
+ token_offset = 0
356
+ prompt_token_pad = int(np.ceil(flow_prompt_speech_token.shape[1] / self.token_hop_len) * self.token_hop_len - flow_prompt_speech_token.shape[1])
357
+ while True:
358
+ time.sleep(0.1)
359
+ this_token_hop_len = self.token_hop_len + prompt_token_pad if token_offset == 0 else self.token_hop_len
360
+ if len(self.tts_speech_token_dict[this_uuid]) - token_offset >= this_token_hop_len + self.flow.pre_lookahead_len:
361
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid][:token_offset + this_token_hop_len + self.flow.pre_lookahead_len]).unsqueeze(dim=0)
362
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
363
+ prompt_token=flow_prompt_speech_token,
364
+ prompt_feat=prompt_speech_feat,
365
+ embedding=flow_embedding,
366
+ token_offset=token_offset,
367
+ uuid=this_uuid,
368
+ stream=stream,
369
+ finalize=False)
370
+ token_offset += this_token_hop_len
371
+ self.token_hop_len = min(self.token_max_hop_len, self.token_hop_len * self.stream_scale_factor)
372
+ yield {'tts_speech': this_tts_speech.cpu()}
373
+ if self.llm_end_dict[this_uuid] is True and len(self.tts_speech_token_dict[this_uuid]) - token_offset < this_token_hop_len + self.flow.pre_lookahead_len:
374
+ break
375
+ p.join()
376
+ # deal with remain tokens, make sure inference remain token len equals token_hop_len when cache_speech is not None
377
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid]).unsqueeze(dim=0)
378
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
379
+ prompt_token=flow_prompt_speech_token,
380
+ prompt_feat=prompt_speech_feat,
381
+ embedding=flow_embedding,
382
+ token_offset=token_offset,
383
+ uuid=this_uuid,
384
+ finalize=True)
385
+ yield {'tts_speech': this_tts_speech.cpu()}
386
+ else:
387
+ # deal with all tokens
388
+ p.join()
389
+ if this_uuid in self.llm_exception_dict:
390
+ raise self.llm_exception_dict[this_uuid]
391
+ if len(self.tts_speech_token_dict[this_uuid]) == 0:
392
+ raise RuntimeError('CosyVoice LLM produced no speech tokens.')
393
+ this_tts_speech_token = torch.tensor(self.tts_speech_token_dict[this_uuid]).unsqueeze(dim=0)
394
+ this_tts_speech = self.token2wav(token=this_tts_speech_token,
395
+ prompt_token=flow_prompt_speech_token,
396
+ prompt_feat=prompt_speech_feat,
397
+ embedding=flow_embedding,
398
+ token_offset=0,
399
+ uuid=this_uuid,
400
+ finalize=True,
401
+ speed=speed)
402
+ yield {'tts_speech': this_tts_speech.cpu()}
403
+ with self.lock:
404
+ self.tts_speech_token_dict.pop(this_uuid)
405
+ self.llm_end_dict.pop(this_uuid)
406
+ self.llm_exception_dict.pop(this_uuid, None)
407
+ self.hift_cache_dict.pop(this_uuid)
408
+ if torch.cuda.is_available():
409
+ torch.cuda.empty_cache()
410
+ torch.cuda.current_stream().synchronize()
411
+
412
+
413
+ class CosyVoice3Model(CosyVoice2Model):
414
+
415
+ def __init__(self,
416
+ llm: torch.nn.Module,
417
+ flow: torch.nn.Module,
418
+ hift: torch.nn.Module,
419
+ fp16: bool = False):
420
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
421
+ self.llm = llm
422
+ self.flow = flow
423
+ self.hift = hift
424
+ self.fp16 = fp16
425
+ # NOTE must matching training static_chunk_size
426
+ self.token_hop_len = 25
427
+ # NOTE increase token_hop_len incrementally to avoid duplicate inference
428
+ self.token_max_hop_len = 4 * self.token_hop_len
429
+ self.stream_scale_factor = 2
430
+ assert self.stream_scale_factor >= 1, 'stream_scale_factor should be greater than 1, change it according to your actual rtf'
431
+ # rtf and decoding related
432
+ self.llm_context = torch.cuda.stream(torch.cuda.Stream(self.device)) if torch.cuda.is_available() else nullcontext()
433
+ self.lock = threading.Lock()
434
+ # dict used to store session related variable
435
+ self.tts_speech_token_dict = {}
436
+ self.llm_end_dict = {}
437
+ self.hift_cache_dict = {}
438
+ self.llm_exception_dict = {}
439
+ # FSQ silent and breath token
440
+ self.silent_tokens = [1, 2, 28, 29, 55, 248, 494, 2241, 2242, 2322, 2323]
441
+
442
+ def token2wav(self, token, prompt_token, prompt_feat, embedding, token_offset, uuid, stream=False, finalize=False, speed=1.0):
443
+ with torch.cuda.amp.autocast(self.fp16):
444
+ tts_mel, _ = self.flow.inference(token=token.to(self.device, dtype=torch.int32),
445
+ token_len=torch.tensor([token.shape[1]], dtype=torch.int32).to(self.device),
446
+ prompt_token=prompt_token.to(self.device),
447
+ prompt_token_len=torch.tensor([prompt_token.shape[1]], dtype=torch.int32).to(self.device),
448
+ prompt_feat=prompt_feat.to(self.device),
449
+ prompt_feat_len=torch.tensor([prompt_feat.shape[1]], dtype=torch.int32).to(self.device),
450
+ embedding=embedding.to(self.device),
451
+ streaming=stream,
452
+ finalize=finalize)
453
+ tts_mel = tts_mel[:, :, token_offset * self.flow.token_mel_ratio:]
454
+ # append mel cache
455
+ if self.hift_cache_dict[uuid] is not None:
456
+ hift_cache_mel = self.hift_cache_dict[uuid]['mel']
457
+ tts_mel = torch.concat([hift_cache_mel, tts_mel], dim=2)
458
+ self.hift_cache_dict[uuid]['mel'] = tts_mel
459
+ else:
460
+ self.hift_cache_dict[uuid] = {'mel': tts_mel, 'speech_offset': 0}
461
+ if speed != 1.0:
462
+ assert token_offset == 0 and finalize is True, 'speed change only support non-stream inference mode'
463
+ tts_mel = F.interpolate(tts_mel, size=int(tts_mel.shape[2] / speed), mode='linear')
464
+ tts_speech, _ = self.hift.inference(speech_feat=tts_mel, finalize=finalize)
465
+ tts_speech = tts_speech[:, self.hift_cache_dict[uuid]['speech_offset']:]
466
+ self.hift_cache_dict[uuid]['speech_offset'] += tts_speech.shape[1]
467
+ return tts_speech
third_party/CosyVoice/cosyvoice/dataset/__init__.py ADDED
File without changes
third_party/CosyVoice/cosyvoice/dataset/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (215 Bytes). View file
 
third_party/CosyVoice/cosyvoice/dataset/__pycache__/processor.cpython-312.pyc ADDED
Binary file (21.4 kB). View file
 
third_party/CosyVoice/cosyvoice/dataset/dataset.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2021 Mobvoi Inc. (authors: Binbin Zhang)
2
+ # 2024 Alibaba Inc (authors: Xiang Lyu)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import random
17
+ import math
18
+ from functools import partial
19
+
20
+ import torch
21
+ import torch.distributed as dist
22
+ from torch.utils.data import IterableDataset
23
+ from cosyvoice.utils.file_utils import read_lists
24
+
25
+
26
+ class Processor(IterableDataset):
27
+
28
+ def __init__(self, source, f, *args, **kw):
29
+ assert callable(f)
30
+ self.source = source
31
+ self.f = f
32
+ self.args = args
33
+ self.kw = kw
34
+
35
+ def set_epoch(self, epoch):
36
+ self.source.set_epoch(epoch)
37
+
38
+ def __iter__(self):
39
+ """ Return an iterator over the source dataset processed by the
40
+ given processor.
41
+ """
42
+ assert self.source is not None
43
+ assert callable(self.f)
44
+ return self.f(iter(self.source), *self.args, **self.kw)
45
+
46
+ def apply(self, f):
47
+ assert callable(f)
48
+ return Processor(self, f, *self.args, **self.kw)
49
+
50
+
51
+ class DistributedSampler:
52
+
53
+ def __init__(self, shuffle=True, partition=True):
54
+ self.epoch = -1
55
+ self.update()
56
+ self.shuffle = shuffle
57
+ self.partition = partition
58
+
59
+ def update(self):
60
+ assert dist.is_available()
61
+ if dist.is_initialized():
62
+ self.rank = dist.get_rank()
63
+ self.world_size = dist.get_world_size()
64
+ else:
65
+ self.rank = 0
66
+ self.world_size = 1
67
+ worker_info = torch.utils.data.get_worker_info()
68
+ if worker_info is None:
69
+ self.worker_id = 0
70
+ self.num_workers = 1
71
+ else:
72
+ self.worker_id = worker_info.id
73
+ self.num_workers = worker_info.num_workers
74
+ return dict(rank=self.rank,
75
+ world_size=self.world_size,
76
+ worker_id=self.worker_id,
77
+ num_workers=self.num_workers)
78
+
79
+ def set_epoch(self, epoch):
80
+ self.epoch = epoch
81
+
82
+ def sample(self, data):
83
+ """ Sample data according to rank/world_size/num_workers
84
+
85
+ Args:
86
+ data(List): input data list
87
+
88
+ Returns:
89
+ List: data list after sample
90
+ """
91
+ data = list(range(len(data)))
92
+ # force datalist even
93
+ if self.partition:
94
+ if self.shuffle:
95
+ random.Random(self.epoch).shuffle(data)
96
+ if len(data) < self.world_size:
97
+ data = data * math.ceil(self.world_size / len(data))
98
+ data = data[:self.world_size]
99
+ data = data[self.rank::self.world_size]
100
+ if len(data) < self.num_workers:
101
+ data = data * math.ceil(self.num_workers / len(data))
102
+ data = data[:self.num_workers]
103
+ data = data[self.worker_id::self.num_workers]
104
+ return data
105
+
106
+
107
+ class DataList(IterableDataset):
108
+
109
+ def __init__(self, lists, shuffle=True, partition=True):
110
+ self.lists = lists
111
+ self.sampler = DistributedSampler(shuffle, partition)
112
+
113
+ def set_epoch(self, epoch):
114
+ self.sampler.set_epoch(epoch)
115
+
116
+ def __iter__(self):
117
+ sampler_info = self.sampler.update()
118
+ indexes = self.sampler.sample(self.lists)
119
+ for index in indexes:
120
+ data = dict(src=self.lists[index])
121
+ data.update(sampler_info)
122
+ yield data
123
+
124
+
125
+ def Dataset(data_list_file,
126
+ data_pipeline,
127
+ mode='train',
128
+ gan=False,
129
+ dpo=False,
130
+ shuffle=True,
131
+ partition=True):
132
+ """ Construct dataset from arguments
133
+
134
+ We have two shuffle stage in the Dataset. The first is global
135
+ shuffle at shards tar/raw file level. The second is global shuffle
136
+ at training samples level.
137
+
138
+ Args:
139
+ data_type(str): raw/shard
140
+ tokenizer (BaseTokenizer): tokenizer to tokenize
141
+ partition(bool): whether to do data partition in terms of rank
142
+ """
143
+ lists = read_lists(data_list_file)
144
+ dataset = DataList(lists,
145
+ shuffle=shuffle,
146
+ partition=partition)
147
+ # map partial arg to padding func
148
+ for i in range(1, len(data_pipeline)):
149
+ if data_pipeline[i].func.__name__ == 'compute_fbank' and gan is True:
150
+ data_pipeline[i] = partial(data_pipeline[i], token_mel_ratio=0)
151
+ if data_pipeline[i].func.__name__ == 'padding':
152
+ data_pipeline[i] = partial(data_pipeline[i], gan=gan, dpo=dpo)
153
+ for func in data_pipeline:
154
+ dataset = Processor(dataset, func, mode=mode)
155
+ return dataset
third_party/CosyVoice/cosyvoice/dataset/processor.py ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import logging
15
+ import random
16
+
17
+ import pyarrow.parquet as pq
18
+ from io import BytesIO
19
+ import numpy as np
20
+ import whisper
21
+ import torch
22
+ import torchaudio
23
+ from torch.nn.utils.rnn import pad_sequence
24
+ import torch.nn.functional as F
25
+ import pyworld as pw
26
+ from cosyvoice.utils.onnx import embedding_extractor, online_feature
27
+
28
+ AUDIO_FORMAT_SETS = {'flac', 'mp3', 'm4a', 'ogg', 'opus', 'wav', 'wma'}
29
+
30
+
31
+ def parquet_opener(data, mode='train'):
32
+ """ Give url or local file, return file descriptor
33
+ Inplace operation.
34
+
35
+ Args:
36
+ data(Iterable[str]): url or local file list
37
+
38
+ Returns:
39
+ Iterable[{src, stream}]
40
+ """
41
+ for sample in data:
42
+ assert 'src' in sample
43
+ url = sample['src']
44
+ try:
45
+ for df in pq.ParquetFile(url).iter_batches(batch_size=64):
46
+ df = df.to_pandas()
47
+ for i in range(len(df)):
48
+ sample.update(dict(df.loc[i]))
49
+ # NOTE do not return sample directly, must initialize a new dict
50
+ yield {**sample}
51
+ except Exception as ex:
52
+ logging.warning('Failed to open {}, ex info {}'.format(url, ex))
53
+
54
+
55
+ def filter(data,
56
+ max_length=10240,
57
+ min_length=10,
58
+ token_max_length=200,
59
+ token_min_length=1,
60
+ min_output_input_ratio=0.0005,
61
+ max_output_input_ratio=1,
62
+ mode='train'):
63
+ """ Filter sample according to feature and label length
64
+ Inplace operation.
65
+
66
+ Args::
67
+ data: Iterable[{key, wav, label, sample_rate}]
68
+ max_length: drop utterance which is greater than max_length(10ms)
69
+ min_length: drop utterance which is less than min_length(10ms)
70
+ token_max_length: drop utterance which is greater than
71
+ token_max_length, especially when use char unit for
72
+ english modeling
73
+ token_min_length: drop utterance which is
74
+ less than token_max_length
75
+ min_output_input_ratio: minimal ration of
76
+ token_length / feats_length(10ms)
77
+ max_output_input_ratio: maximum ration of
78
+ token_length / feats_length(10ms)
79
+
80
+ Returns:
81
+ Iterable[{key, wav, label, sample_rate}]
82
+ """
83
+ for sample in data:
84
+ sample['speech'], sample['sample_rate'] = torchaudio.load(BytesIO(sample['audio_data']))
85
+ sample['speech'] = sample['speech'].mean(dim=0, keepdim=True)
86
+ del sample['audio_data']
87
+ # sample['wav'] is torch.Tensor, we have 100 frames every second
88
+ num_frames = sample['speech'].size(1) / sample['sample_rate'] * 100
89
+ if num_frames < min_length:
90
+ continue
91
+ if num_frames > max_length:
92
+ continue
93
+ if len(sample['text_token']) < token_min_length:
94
+ continue
95
+ if len(sample['text_token']) > token_max_length:
96
+ continue
97
+ if online_feature is False and len(sample['speech_token']) == 0:
98
+ continue
99
+ if online_feature is False and 'reject_speech_token' in sample and len(sample['reject_speech_token']) == 0:
100
+ continue
101
+ if num_frames != 0:
102
+ if len(sample['text_token']) / num_frames < min_output_input_ratio:
103
+ continue
104
+ if len(sample['text_token']) / num_frames > max_output_input_ratio:
105
+ continue
106
+ yield sample
107
+
108
+
109
+ def resample(data, resample_rate=22050, min_sample_rate=16000, mode='train'):
110
+ """ Resample data.
111
+ Inplace operation.
112
+
113
+ Args:
114
+ data: Iterable[{key, wav, label, sample_rate}]
115
+ resample_rate: target resample rate
116
+
117
+ Returns:
118
+ Iterable[{key, wav, label, sample_rate}]
119
+ """
120
+ for sample in data:
121
+ assert 'sample_rate' in sample
122
+ assert 'speech' in sample
123
+ sample_rate = sample['sample_rate']
124
+ waveform = sample['speech']
125
+ if sample_rate != resample_rate:
126
+ if sample_rate < min_sample_rate:
127
+ continue
128
+ sample['sample_rate'] = resample_rate
129
+ sample['speech'] = torchaudio.transforms.Resample(
130
+ orig_freq=sample_rate, new_freq=resample_rate)(waveform)
131
+ max_val = sample['speech'].abs().max()
132
+ if max_val > 1:
133
+ sample['speech'] /= max_val
134
+ yield sample
135
+
136
+
137
+ def truncate(data, truncate_length=24576, mode='train'):
138
+ """ Truncate data.
139
+
140
+ Args:
141
+ data: Iterable[{key, wav, label, sample_rate}]
142
+ truncate_length: truncate length
143
+
144
+ Returns:
145
+ Iterable[{key, wav, label, sample_rate}]
146
+ """
147
+ for sample in data:
148
+ waveform = sample['speech']
149
+ if waveform.shape[1] > truncate_length:
150
+ start = random.randint(0, waveform.shape[1] - truncate_length)
151
+ waveform = waveform[:, start: start + truncate_length]
152
+ else:
153
+ waveform = torch.concat([waveform, torch.zeros(1, truncate_length - waveform.shape[1])], dim=1)
154
+ sample['speech'] = waveform
155
+ yield sample
156
+
157
+
158
+ def compute_fbank(data,
159
+ feat_extractor,
160
+ num_frames=-1,
161
+ mode='train'):
162
+ """ Extract fbank
163
+
164
+ Args:
165
+ data: Iterable[{key, wav, label, sample_rate}]
166
+
167
+ Returns:
168
+ Iterable[{key, feat, label}]
169
+ """
170
+ for sample in data:
171
+ assert 'sample_rate' in sample
172
+ assert 'speech' in sample
173
+ assert 'utt' in sample
174
+ assert 'text_token' in sample
175
+ # NOTE in cosyvoice2/3, we support online token extraction, so we need to align speech to 25hz first
176
+ if num_frames != -1:
177
+ index = int(np.ceil(sample['speech'].shape[1] / num_frames))
178
+ sample['speech'] = torch.concat([sample['speech'], torch.zeros(1, index * num_frames - sample['speech'].shape[1])], dim=1)
179
+ sample['speech_feat'] = feat_extractor(sample['speech']).squeeze(dim=0).transpose(0, 1)
180
+ yield sample
181
+
182
+
183
+ def compute_whisper_fbank(data, num_frames=-1, mode='train'):
184
+ """ Extract whisper fbank
185
+
186
+ Args:
187
+ data: Iterable[{key, wav, label, sample_rate}]
188
+
189
+ Returns:
190
+ Iterable[{key, feat, label}]
191
+ """
192
+ for sample in data:
193
+ if num_frames != -1:
194
+ assert sample['speech'].shape[1] % num_frames == 0, 'speech length is not aligned with speech_token'
195
+ sample['speech_16k'] = torchaudio.transforms.Resample(orig_freq=sample['sample_rate'], new_freq=16000)(sample['speech'])
196
+ sample['whisper_feat'] = whisper.log_mel_spectrogram(sample['speech_16k'], n_mels=128).squeeze(dim=0).transpose(0, 1)
197
+ yield sample
198
+
199
+
200
+ def compute_f0(data, sample_rate, hop_size, mode='train'):
201
+ """ Extract f0
202
+
203
+ Args:
204
+ data: Iterable[{key, wav, label, sample_rate}]
205
+
206
+ Returns:
207
+ Iterable[{key, feat, label}]
208
+ """
209
+ frame_period = hop_size * 1000 / sample_rate
210
+ for sample in data:
211
+ assert 'sample_rate' in sample
212
+ assert 'speech' in sample
213
+ assert 'utt' in sample
214
+ assert 'text_token' in sample
215
+ waveform = sample['speech']
216
+ _f0, t = pw.harvest(waveform.squeeze(dim=0).numpy().astype('double'), sample_rate, frame_period=frame_period)
217
+ if sum(_f0 != 0) < 5: # this happens when the algorithm fails
218
+ _f0, t = pw.dio(waveform.squeeze(dim=0).numpy().astype('double'), sample_rate, frame_period=frame_period) # if harvest fails, try dio
219
+ f0 = pw.stonemask(waveform.squeeze(dim=0).numpy().astype('double'), _f0, t, sample_rate)
220
+ f0 = F.interpolate(torch.from_numpy(f0).view(1, 1, -1), size=sample['speech_feat'].shape[0], mode='linear').view(-1)
221
+ sample['pitch_feat'] = f0
222
+ yield sample
223
+
224
+
225
+ def parse_embedding(data, normalize, mode='train'):
226
+ """ Parse utt_embedding/spk_embedding
227
+
228
+ Args:
229
+ data: Iterable[{key, wav, label, sample_rate}]
230
+
231
+ Returns:
232
+ Iterable[{key, feat, label}]
233
+ """
234
+ for sample in data:
235
+ if 'utt_embedding' not in sample and 'spk_embedding' not in sample:
236
+ sample['speech_16k'] = torchaudio.transforms.Resample(orig_freq=sample['sample_rate'], new_freq=16000)(sample['speech'])
237
+ embedding = embedding_extractor.inference(sample['speech_16k'])
238
+ sample['spk_embedding'] = sample['utt_embedding'] = embedding
239
+ else:
240
+ sample['utt_embedding'] = torch.tensor(sample['utt_embedding'], dtype=torch.float32)
241
+ sample['spk_embedding'] = torch.tensor(sample['spk_embedding'], dtype=torch.float32)
242
+ if normalize:
243
+ sample['utt_embedding'] = F.normalize(sample['utt_embedding'], dim=0)
244
+ sample['spk_embedding'] = F.normalize(sample['spk_embedding'], dim=0)
245
+ yield sample
246
+
247
+
248
+ def tokenize(data, get_tokenizer, allowed_special, mode='train'):
249
+ """ Decode text to chars or BPE
250
+ Inplace operation
251
+
252
+ Args:
253
+ data: Iterable[{key, wav, txt, sample_rate}]
254
+
255
+ Returns:
256
+ Iterable[{key, wav, txt, tokens, label, sample_rate}]
257
+ """
258
+ tokenizer = get_tokenizer()
259
+ for sample in data:
260
+ assert 'text' in sample
261
+ sample['text_token'] = tokenizer.encode(sample['text'], allowed_special=allowed_special)
262
+ if 'instruct' in sample:
263
+ sample['instruct_token'] = tokenizer.encode(sample['instruct'], allowed_special=allowed_special)
264
+ yield sample
265
+
266
+
267
+ def shuffle(data, shuffle_size=10000, mode='train'):
268
+ """ Local shuffle the data
269
+
270
+ Args:
271
+ data: Iterable[{key, feat, label}]
272
+ shuffle_size: buffer size for shuffle
273
+
274
+ Returns:
275
+ Iterable[{key, feat, label}]
276
+ """
277
+ buf = []
278
+ yield_size = int(shuffle_size / 2)
279
+ for sample in data:
280
+ buf.append(sample)
281
+ if len(buf) >= shuffle_size:
282
+ random.shuffle(buf)
283
+ for x in buf[:yield_size]:
284
+ yield x
285
+ buf = buf[yield_size:]
286
+ # The sample left over
287
+ random.shuffle(buf)
288
+ for x in buf:
289
+ yield x
290
+
291
+
292
+ def sort(data, sort_size=500, mode='train'):
293
+ """ Sort the data by feature length.
294
+ Sort is used after shuffle and before batch, so we can group
295
+ utts with similar lengths into a batch, and `sort_size` should
296
+ be less than `shuffle_size`
297
+
298
+ Args:
299
+ data: Iterable[{key, feat, label}]
300
+ sort_size: buffer size for sort
301
+
302
+ Returns:
303
+ Iterable[{key, feat, label}]
304
+ """
305
+
306
+ buf = []
307
+ for sample in data:
308
+ buf.append(sample)
309
+ if len(buf) >= sort_size:
310
+ buf.sort(key=lambda x: x['speech_feat'].size(0))
311
+ for x in buf:
312
+ yield x
313
+ buf = []
314
+ # The sample left over
315
+ buf.sort(key=lambda x: x['speech_feat'].size(0))
316
+ for x in buf:
317
+ yield x
318
+
319
+
320
+ def static_batch(data, batch_size=16):
321
+ """ Static batch the data by `batch_size`
322
+
323
+ Args:
324
+ data: Iterable[{key, feat, label}]
325
+ batch_size: batch size
326
+
327
+ Returns:
328
+ Iterable[List[{key, feat, label}]]
329
+ """
330
+ buf = []
331
+ for sample in data:
332
+ buf.append(sample)
333
+ if len(buf) >= batch_size:
334
+ yield buf
335
+ buf = []
336
+ if len(buf) > 0:
337
+ yield buf
338
+
339
+
340
+ def dynamic_batch(data, max_frames_in_batch=12000, mode='train'):
341
+ """ Dynamic batch the data until the total frames in batch
342
+ reach `max_frames_in_batch`
343
+
344
+ Args:
345
+ data: Iterable[{key, feat, label}]
346
+ max_frames_in_batch: max_frames in one batch
347
+
348
+ Returns:
349
+ Iterable[List[{key, feat, label}]]
350
+ """
351
+ buf = []
352
+ longest_frames = 0
353
+ for sample in data:
354
+ assert 'speech_feat' in sample
355
+ assert isinstance(sample['speech_feat'], torch.Tensor)
356
+ new_sample_frames = sample['speech_feat'].size(0)
357
+ longest_frames = max(longest_frames, new_sample_frames)
358
+ frames_after_padding = longest_frames * (len(buf) + 1)
359
+ if frames_after_padding > max_frames_in_batch:
360
+ yield buf
361
+ buf = [sample]
362
+ longest_frames = new_sample_frames
363
+ else:
364
+ buf.append(sample)
365
+ if len(buf) > 0:
366
+ yield buf
367
+
368
+
369
+ def batch(data, batch_type='static', batch_size=16, max_frames_in_batch=12000, mode='train'):
370
+ """ Wrapper for static/dynamic batch
371
+ """
372
+ if batch_type == 'static':
373
+ return static_batch(data, batch_size)
374
+ elif batch_type == 'dynamic':
375
+ return dynamic_batch(data, max_frames_in_batch)
376
+ else:
377
+ logging.fatal('Unsupported batch type {}'.format(batch_type))
378
+
379
+
380
+ def padding(data, use_spk_embedding, mode='train', gan=False, dpo=False):
381
+ """ Padding the data into training data
382
+
383
+ Args:
384
+ data: Iterable[List[{key, feat, label}]]
385
+
386
+ Returns:
387
+ Iterable[Tuple(keys, feats, labels, feats lengths, label lengths)]
388
+ """
389
+ for sample in data:
390
+ assert isinstance(sample, list)
391
+ order = torch.argsort(torch.tensor([x['speech'].size(1) for x in sample], dtype=torch.int32), descending=True)
392
+ batch = {}
393
+ batch['utts'] = [sample[i]['utt'] for i in order]
394
+ batch['text'] = [sample[i]['text'] for i in order]
395
+ text_token = [torch.tensor(sample[i]['text_token']) for i in order]
396
+ batch['text_token_len'] = torch.tensor([i.size(0) for i in text_token], dtype=torch.int32)
397
+ batch['text_token'] = pad_sequence(text_token, batch_first=True, padding_value=0)
398
+ speech_feat = [sample[i]['speech_feat'] for i in order]
399
+ batch['speech_feat_len'] = torch.tensor([i.size(0) for i in speech_feat], dtype=torch.int32)
400
+ batch['speech_feat'] = pad_sequence(speech_feat, batch_first=True, padding_value=0)
401
+ batch['utt_embedding'] = torch.stack([sample[i]['utt_embedding'] for i in order], dim=0)
402
+ batch['spk_embedding'] = torch.stack([sample[i]['spk_embedding'] for i in order], dim=0)
403
+ if torch.tensor(['instruct_token' in sample[i] for i in order]).all():
404
+ instruct_token = [torch.tensor(sample[i]['instruct_token']) for i in order]
405
+ batch['instruct_token_len'] = torch.tensor([i.size(0) for i in instruct_token], dtype=torch.int32)
406
+ batch['instruct_token'] = pad_sequence(instruct_token, batch_first=True, padding_value=0)
407
+ if torch.tensor(['whisper_feat' in sample[i] for i in order]).all():
408
+ whisper_feat = [sample[i]['whisper_feat'] for i in order]
409
+ batch['whisper_feat_len'] = torch.tensor([i.size(0) for i in whisper_feat], dtype=torch.int32)
410
+ batch['whisper_feat'] = pad_sequence(whisper_feat, batch_first=True, padding_value=0)
411
+ if torch.tensor(['speech_token' in sample[i] for i in order]).all():
412
+ speech_token = [torch.tensor(sample[i]['speech_token']) for i in order]
413
+ batch['speech_token_len'] = torch.tensor([i.size(0) for i in speech_token], dtype=torch.int32)
414
+ batch['speech_token'] = pad_sequence(speech_token, batch_first=True, padding_value=0)
415
+ if gan is True:
416
+ # in gan train, we need speech/pitch_feat
417
+ speech = [sample[i]['speech'].squeeze(dim=0) for i in order]
418
+ batch['speech_len'] = torch.tensor([i.size(0) for i in speech], dtype=torch.int32)
419
+ batch['speech'] = pad_sequence(speech, batch_first=True, padding_value=0)
420
+ pitch_feat = [sample[i]['pitch_feat'] for i in order]
421
+ batch['pitch_feat_len'] = torch.tensor([i.size(0) for i in pitch_feat], dtype=torch.int32)
422
+ batch['pitch_feat'] = pad_sequence(pitch_feat, batch_first=True, padding_value=0)
423
+ if dpo is True:
424
+ reject_speech_token = [torch.tensor(sample[i]['reject_speech_token']) for i in order]
425
+ batch['reject_speech_token_len'] = torch.tensor([i.size(0) for i in reject_speech_token], dtype=torch.int32)
426
+ batch['reject_speech_token'] = pad_sequence(reject_speech_token, batch_first=True, padding_value=0)
427
+ if use_spk_embedding is True:
428
+ batch["embedding"] = batch["spk_embedding"]
429
+ else:
430
+ batch["embedding"] = batch["utt_embedding"]
431
+ yield batch
third_party/CosyVoice/cosyvoice/flow/DiT/__pycache__/dit.cpython-312.pyc ADDED
Binary file (7.89 kB). View file
 
third_party/CosyVoice/cosyvoice/flow/DiT/__pycache__/modules.cpython-312.pyc ADDED
Binary file (28.7 kB). View file
 
third_party/CosyVoice/cosyvoice/flow/DiT/dit.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ """
3
+ ein notation:
4
+ b - batch
5
+ n - sequence
6
+ nt - text sequence
7
+ nw - raw wave length
8
+ d - dimension
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import torch
14
+ from torch import nn
15
+ import torch.nn.functional as F
16
+ from einops import repeat
17
+ from x_transformers.x_transformers import RotaryEmbedding
18
+ from cosyvoice.utils.mask import add_optional_chunk_mask
19
+ from cosyvoice.flow.DiT.modules import (
20
+ TimestepEmbedding,
21
+ ConvNeXtV2Block,
22
+ CausalConvPositionEmbedding,
23
+ DiTBlock,
24
+ AdaLayerNormZero_Final,
25
+ precompute_freqs_cis,
26
+ get_pos_embed_indices,
27
+ )
28
+
29
+
30
+ # Text embedding
31
+
32
+
33
+ class TextEmbedding(nn.Module):
34
+ def __init__(self, text_num_embeds, text_dim, conv_layers=0, conv_mult=2):
35
+ super().__init__()
36
+ self.text_embed = nn.Embedding(text_num_embeds + 1, text_dim) # use 0 as filler token
37
+
38
+ if conv_layers > 0:
39
+ self.extra_modeling = True
40
+ self.precompute_max_pos = 4096 # ~44s of 24khz audio
41
+ self.register_buffer("freqs_cis", precompute_freqs_cis(text_dim, self.precompute_max_pos), persistent=False)
42
+ self.text_blocks = nn.Sequential(
43
+ *[ConvNeXtV2Block(text_dim, text_dim * conv_mult) for _ in range(conv_layers)]
44
+ )
45
+ else:
46
+ self.extra_modeling = False
47
+
48
+ def forward(self, text: int["b nt"], seq_len, drop_text=False): # noqa: F722
49
+ batch, text_len = text.shape[0], text.shape[1]
50
+ text = text + 1 # use 0 as filler token. preprocess of batch pad -1, see list_str_to_idx()
51
+ text = text[:, :seq_len] # curtail if character tokens are more than the mel spec tokens
52
+ text = F.pad(text, (0, seq_len - text_len), value=0)
53
+
54
+ if drop_text: # cfg for text
55
+ text = torch.zeros_like(text)
56
+
57
+ text = self.text_embed(text) # b n -> b n d
58
+
59
+ # possible extra modeling
60
+ if self.extra_modeling:
61
+ # sinus pos emb
62
+ batch_start = torch.zeros((batch,), dtype=torch.long)
63
+ pos_idx = get_pos_embed_indices(batch_start, seq_len, max_pos=self.precompute_max_pos)
64
+ text_pos_embed = self.freqs_cis[pos_idx]
65
+ text = text + text_pos_embed
66
+
67
+ # convnextv2 blocks
68
+ text = self.text_blocks(text)
69
+
70
+ return text
71
+
72
+
73
+ # noised input audio and context mixing embedding
74
+
75
+
76
+ class InputEmbedding(nn.Module):
77
+ def __init__(self, mel_dim, text_dim, out_dim, spk_dim=None):
78
+ super().__init__()
79
+ spk_dim = 0 if spk_dim is None else spk_dim
80
+ self.spk_dim = spk_dim
81
+ self.proj = nn.Linear(mel_dim * 2 + text_dim + spk_dim, out_dim)
82
+ self.conv_pos_embed = CausalConvPositionEmbedding(dim=out_dim)
83
+
84
+ def forward(
85
+ self,
86
+ x: float["b n d"],
87
+ cond: float["b n d"],
88
+ text_embed: float["b n d"],
89
+ spks: float["b d"],
90
+ ):
91
+ to_cat = [x, cond, text_embed]
92
+ if self.spk_dim > 0:
93
+ spks = repeat(spks, "b c -> b t c", t=x.shape[1])
94
+ to_cat.append(spks)
95
+
96
+ x = self.proj(torch.cat(to_cat, dim=-1))
97
+ x = self.conv_pos_embed(x) + x
98
+ return x
99
+
100
+
101
+ # Transformer backbone using DiT blocks
102
+
103
+
104
+ class DiT(nn.Module):
105
+ def __init__(
106
+ self,
107
+ *,
108
+ dim,
109
+ depth=8,
110
+ heads=8,
111
+ dim_head=64,
112
+ dropout=0.1,
113
+ ff_mult=4,
114
+ mel_dim=80,
115
+ mu_dim=None,
116
+ long_skip_connection=False,
117
+ spk_dim=None,
118
+ out_channels=None,
119
+ static_chunk_size=50,
120
+ num_decoding_left_chunks=2
121
+ ):
122
+ super().__init__()
123
+
124
+ self.time_embed = TimestepEmbedding(dim)
125
+ if mu_dim is None:
126
+ mu_dim = mel_dim
127
+ self.input_embed = InputEmbedding(mel_dim, mu_dim, dim, spk_dim)
128
+
129
+ self.rotary_embed = RotaryEmbedding(dim_head)
130
+
131
+ self.dim = dim
132
+ self.depth = depth
133
+
134
+ self.transformer_blocks = nn.ModuleList(
135
+ [DiTBlock(dim=dim, heads=heads, dim_head=dim_head, ff_mult=ff_mult, dropout=dropout) for _ in range(depth)]
136
+ )
137
+ self.long_skip_connection = nn.Linear(dim * 2, dim, bias=False) if long_skip_connection else None
138
+
139
+ self.norm_out = AdaLayerNormZero_Final(dim) # final modulation
140
+ self.proj_out = nn.Linear(dim, mel_dim)
141
+ self.out_channels = out_channels
142
+ self.static_chunk_size = static_chunk_size
143
+ self.num_decoding_left_chunks = num_decoding_left_chunks
144
+
145
+ def forward(self, x, mask, mu, t, spks=None, cond=None, streaming=False):
146
+ x = x.transpose(1, 2)
147
+ mu = mu.transpose(1, 2)
148
+ cond = cond.transpose(1, 2)
149
+ spks = spks.unsqueeze(dim=1)
150
+ batch, seq_len = x.shape[0], x.shape[1]
151
+ if t.ndim == 0:
152
+ t = t.repeat(batch)
153
+
154
+ # t: conditioning time, c: context (text + masked cond audio), x: noised input audio
155
+ t = self.time_embed(t)
156
+ x = self.input_embed(x, cond, mu, spks.squeeze(1))
157
+
158
+ rope = self.rotary_embed.forward_from_seq_len(seq_len)
159
+
160
+ if self.long_skip_connection is not None:
161
+ residual = x
162
+
163
+ if streaming is True:
164
+ attn_mask = add_optional_chunk_mask(x, mask.bool(), False, False, 0, self.static_chunk_size, -1).unsqueeze(dim=1)
165
+ else:
166
+ attn_mask = add_optional_chunk_mask(x, mask.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1).unsqueeze(dim=1)
167
+
168
+ for block in self.transformer_blocks:
169
+ x = block(x, t, mask=attn_mask.bool(), rope=rope)
170
+
171
+ if self.long_skip_connection is not None:
172
+ x = self.long_skip_connection(torch.cat((x, residual), dim=-1))
173
+
174
+ x = self.norm_out(x, t)
175
+ output = self.proj_out(x).transpose(1, 2)
176
+ return output
third_party/CosyVoice/cosyvoice/flow/DiT/modules.py ADDED
@@ -0,0 +1,616 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ """
3
+ ein notation:
4
+ b - batch
5
+ n - sequence
6
+ nt - text sequence
7
+ nw - raw wave length
8
+ d - dimension
9
+ """
10
+
11
+ from __future__ import annotations
12
+ from typing import Optional
13
+ import math
14
+
15
+ import torch
16
+ from torch import nn
17
+ import torch.nn.functional as F
18
+ import torchaudio
19
+
20
+ from x_transformers.x_transformers import apply_rotary_pos_emb
21
+
22
+
23
+ # raw wav to mel spec
24
+ class MelSpec(nn.Module):
25
+ def __init__(
26
+ self,
27
+ filter_length=1024,
28
+ hop_length=256,
29
+ win_length=1024,
30
+ n_mel_channels=100,
31
+ target_sample_rate=24_000,
32
+ normalize=False,
33
+ power=1,
34
+ norm=None,
35
+ center=True,
36
+ ):
37
+ super().__init__()
38
+ self.n_mel_channels = n_mel_channels
39
+
40
+ self.mel_stft = torchaudio.transforms.MelSpectrogram(
41
+ sample_rate=target_sample_rate,
42
+ n_fft=filter_length,
43
+ win_length=win_length,
44
+ hop_length=hop_length,
45
+ n_mels=n_mel_channels,
46
+ power=power,
47
+ center=center,
48
+ normalized=normalize,
49
+ norm=norm,
50
+ )
51
+
52
+ self.register_buffer("dummy", torch.tensor(0), persistent=False)
53
+
54
+ def forward(self, inp):
55
+ if len(inp.shape) == 3:
56
+ inp = inp.squeeze(1) # 'b 1 nw -> b nw'
57
+
58
+ assert len(inp.shape) == 2
59
+
60
+ if self.dummy.device != inp.device:
61
+ self.to(inp.device)
62
+
63
+ mel = self.mel_stft(inp)
64
+ mel = mel.clamp(min=1e-5).log()
65
+ return mel
66
+
67
+
68
+ # sinusoidal position embedding
69
+
70
+
71
+ class SinusPositionEmbedding(nn.Module):
72
+ def __init__(self, dim):
73
+ super().__init__()
74
+ self.dim = dim
75
+
76
+ def forward(self, x, scale=1000):
77
+ device = x.device
78
+ half_dim = self.dim // 2
79
+ emb = math.log(10000) / (half_dim - 1)
80
+ emb = torch.exp(torch.arange(half_dim, device=device).float() * -emb)
81
+ emb = scale * x.unsqueeze(1) * emb.unsqueeze(0)
82
+ emb = torch.cat((emb.sin(), emb.cos()), dim=-1)
83
+ return emb
84
+
85
+
86
+ # convolutional position embedding
87
+
88
+
89
+ class ConvPositionEmbedding(nn.Module):
90
+ def __init__(self, dim, kernel_size=31, groups=16):
91
+ super().__init__()
92
+ assert kernel_size % 2 != 0
93
+ self.conv1d = nn.Sequential(
94
+ nn.Conv1d(dim, dim, kernel_size, groups=groups, padding=kernel_size // 2),
95
+ nn.Mish(),
96
+ nn.Conv1d(dim, dim, kernel_size, groups=groups, padding=kernel_size // 2),
97
+ nn.Mish(),
98
+ )
99
+
100
+ def forward(self, x: float["b n d"], mask: bool["b n"] | None = None): # noqa: F722
101
+ if mask is not None:
102
+ mask = mask[..., None]
103
+ x = x.masked_fill(~mask, 0.0)
104
+
105
+ x = x.permute(0, 2, 1)
106
+ x = self.conv1d(x)
107
+ out = x.permute(0, 2, 1)
108
+
109
+ if mask is not None:
110
+ out = out.masked_fill(~mask, 0.0)
111
+
112
+ return out
113
+
114
+
115
+ class CausalConvPositionEmbedding(nn.Module):
116
+ def __init__(self, dim, kernel_size=31, groups=16):
117
+ super().__init__()
118
+ assert kernel_size % 2 != 0
119
+ self.kernel_size = kernel_size
120
+ self.conv1 = nn.Sequential(
121
+ nn.Conv1d(dim, dim, kernel_size, groups=groups, padding=0),
122
+ nn.Mish(),
123
+ )
124
+ self.conv2 = nn.Sequential(
125
+ nn.Conv1d(dim, dim, kernel_size, groups=groups, padding=0),
126
+ nn.Mish(),
127
+ )
128
+
129
+ def forward(self, x: float["b n d"], mask: bool["b n"] | None = None): # noqa: F722
130
+ if mask is not None:
131
+ mask = mask[..., None]
132
+ x = x.masked_fill(~mask, 0.0)
133
+
134
+ x = x.permute(0, 2, 1)
135
+ x = F.pad(x, (self.kernel_size - 1, 0, 0, 0))
136
+ x = self.conv1(x)
137
+ x = F.pad(x, (self.kernel_size - 1, 0, 0, 0))
138
+ x = self.conv2(x)
139
+ out = x.permute(0, 2, 1)
140
+
141
+ if mask is not None:
142
+ out = out.masked_fill(~mask, 0.0)
143
+
144
+ return out
145
+
146
+
147
+ # rotary positional embedding related
148
+
149
+
150
+ def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0, theta_rescale_factor=1.0):
151
+ # proposed by reddit user bloc97, to rescale rotary embeddings to longer sequence length without fine-tuning
152
+ # has some connection to NTK literature
153
+ # https://www.reddit.com/r/LocalLLaMA/comments/14lz7j5/ntkaware_scaled_rope_allows_llama_models_to_have/
154
+ # https://github.com/lucidrains/rotary-embedding-torch/blob/main/rotary_embedding_torch/rotary_embedding_torch.py
155
+ theta *= theta_rescale_factor ** (dim / (dim - 2))
156
+ freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
157
+ t = torch.arange(end, device=freqs.device) # type: ignore
158
+ freqs = torch.outer(t, freqs).float() # type: ignore
159
+ freqs_cos = torch.cos(freqs) # real part
160
+ freqs_sin = torch.sin(freqs) # imaginary part
161
+ return torch.cat([freqs_cos, freqs_sin], dim=-1)
162
+
163
+
164
+ def get_pos_embed_indices(start, length, max_pos, scale=1.0):
165
+ # length = length if isinstance(length, int) else length.max()
166
+ scale = scale * torch.ones_like(start, dtype=torch.float32) # in case scale is a scalar
167
+ pos = (
168
+ start.unsqueeze(1)
169
+ + (torch.arange(length, device=start.device, dtype=torch.float32).unsqueeze(0) * scale.unsqueeze(1)).long()
170
+ )
171
+ # avoid extra long error.
172
+ pos = torch.where(pos < max_pos, pos, max_pos - 1)
173
+ return pos
174
+
175
+
176
+ # Global Response Normalization layer (Instance Normalization ?)
177
+
178
+
179
+ class GRN(nn.Module):
180
+ def __init__(self, dim):
181
+ super().__init__()
182
+ self.gamma = nn.Parameter(torch.zeros(1, 1, dim))
183
+ self.beta = nn.Parameter(torch.zeros(1, 1, dim))
184
+
185
+ def forward(self, x):
186
+ Gx = torch.norm(x, p=2, dim=1, keepdim=True)
187
+ Nx = Gx / (Gx.mean(dim=-1, keepdim=True) + 1e-6)
188
+ return self.gamma * (x * Nx) + self.beta + x
189
+
190
+
191
+ # ConvNeXt-V2 Block https://github.com/facebookresearch/ConvNeXt-V2/blob/main/models/convnextv2.py
192
+ # ref: https://github.com/bfs18/e2_tts/blob/main/rfwave/modules.py#L108
193
+
194
+
195
+ class ConvNeXtV2Block(nn.Module):
196
+ def __init__(
197
+ self,
198
+ dim: int,
199
+ intermediate_dim: int,
200
+ dilation: int = 1,
201
+ ):
202
+ super().__init__()
203
+ padding = (dilation * (7 - 1)) // 2
204
+ self.dwconv = nn.Conv1d(
205
+ dim, dim, kernel_size=7, padding=padding, groups=dim, dilation=dilation
206
+ ) # depthwise conv
207
+ self.norm = nn.LayerNorm(dim, eps=1e-6)
208
+ self.pwconv1 = nn.Linear(dim, intermediate_dim) # pointwise/1x1 convs, implemented with linear layers
209
+ self.act = nn.GELU()
210
+ self.grn = GRN(intermediate_dim)
211
+ self.pwconv2 = nn.Linear(intermediate_dim, dim)
212
+
213
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
214
+ residual = x
215
+ x = x.transpose(1, 2) # b n d -> b d n
216
+ x = self.dwconv(x)
217
+ x = x.transpose(1, 2) # b d n -> b n d
218
+ x = self.norm(x)
219
+ x = self.pwconv1(x)
220
+ x = self.act(x)
221
+ x = self.grn(x)
222
+ x = self.pwconv2(x)
223
+ return residual + x
224
+
225
+
226
+ # AdaLayerNormZero
227
+ # return with modulated x for attn input, and params for later mlp modulation
228
+
229
+
230
+ class AdaLayerNormZero(nn.Module):
231
+ def __init__(self, dim):
232
+ super().__init__()
233
+
234
+ self.silu = nn.SiLU()
235
+ self.linear = nn.Linear(dim, dim * 6)
236
+
237
+ self.norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
238
+
239
+ def forward(self, x, emb=None):
240
+ emb = self.linear(self.silu(emb))
241
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = torch.chunk(emb, 6, dim=1)
242
+
243
+ x = self.norm(x) * (1 + scale_msa[:, None]) + shift_msa[:, None]
244
+ return x, gate_msa, shift_mlp, scale_mlp, gate_mlp
245
+
246
+
247
+ # AdaLayerNormZero for final layer
248
+ # return only with modulated x for attn input, cuz no more mlp modulation
249
+
250
+
251
+ class AdaLayerNormZero_Final(nn.Module):
252
+ def __init__(self, dim):
253
+ super().__init__()
254
+
255
+ self.silu = nn.SiLU()
256
+ self.linear = nn.Linear(dim, dim * 2)
257
+
258
+ self.norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
259
+
260
+ def forward(self, x, emb):
261
+ emb = self.linear(self.silu(emb))
262
+ scale, shift = torch.chunk(emb, 2, dim=1)
263
+
264
+ x = self.norm(x) * (1 + scale)[:, None, :] + shift[:, None, :]
265
+ return x
266
+
267
+
268
+ # FeedForward
269
+
270
+
271
+ class FeedForward(nn.Module):
272
+ def __init__(self, dim, dim_out=None, mult=4, dropout=0.0, approximate: str = "none"):
273
+ super().__init__()
274
+ inner_dim = int(dim * mult)
275
+ dim_out = dim_out if dim_out is not None else dim
276
+
277
+ activation = nn.GELU(approximate=approximate)
278
+ project_in = nn.Sequential(nn.Linear(dim, inner_dim), activation)
279
+ self.ff = nn.Sequential(project_in, nn.Dropout(dropout), nn.Linear(inner_dim, dim_out))
280
+
281
+ def forward(self, x):
282
+ return self.ff(x)
283
+
284
+
285
+ # Attention with possible joint part
286
+ # modified from diffusers/src/diffusers/models/attention_processor.py
287
+
288
+
289
+ class Attention(nn.Module):
290
+ def __init__(
291
+ self,
292
+ processor: JointAttnProcessor | AttnProcessor,
293
+ dim: int,
294
+ heads: int = 8,
295
+ dim_head: int = 64,
296
+ dropout: float = 0.0,
297
+ context_dim: Optional[int] = None, # if not None -> joint attention
298
+ context_pre_only=None,
299
+ ):
300
+ super().__init__()
301
+
302
+ if not hasattr(F, "scaled_dot_product_attention"):
303
+ raise ImportError("Attention equires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.")
304
+
305
+ self.processor = processor
306
+
307
+ self.dim = dim
308
+ self.heads = heads
309
+ self.inner_dim = dim_head * heads
310
+ self.dropout = dropout
311
+
312
+ self.context_dim = context_dim
313
+ self.context_pre_only = context_pre_only
314
+
315
+ self.to_q = nn.Linear(dim, self.inner_dim)
316
+ self.to_k = nn.Linear(dim, self.inner_dim)
317
+ self.to_v = nn.Linear(dim, self.inner_dim)
318
+
319
+ if self.context_dim is not None:
320
+ self.to_k_c = nn.Linear(context_dim, self.inner_dim)
321
+ self.to_v_c = nn.Linear(context_dim, self.inner_dim)
322
+ if self.context_pre_only is not None:
323
+ self.to_q_c = nn.Linear(context_dim, self.inner_dim)
324
+
325
+ self.to_out = nn.ModuleList([])
326
+ self.to_out.append(nn.Linear(self.inner_dim, dim))
327
+ self.to_out.append(nn.Dropout(dropout))
328
+
329
+ if self.context_pre_only is not None and not self.context_pre_only:
330
+ self.to_out_c = nn.Linear(self.inner_dim, dim)
331
+
332
+ def forward(
333
+ self,
334
+ x: float["b n d"], # noised input x # noqa: F722
335
+ c: float["b n d"] = None, # context c # noqa: F722
336
+ mask: bool["b n"] | None = None, # noqa: F722
337
+ rope=None, # rotary position embedding for x
338
+ c_rope=None, # rotary position embedding for c
339
+ ) -> torch.Tensor:
340
+ if c is not None:
341
+ return self.processor(self, x, c=c, mask=mask, rope=rope, c_rope=c_rope)
342
+ else:
343
+ return self.processor(self, x, mask=mask, rope=rope)
344
+
345
+
346
+ # Attention processor
347
+
348
+
349
+ class AttnProcessor:
350
+ def __init__(self):
351
+ pass
352
+
353
+ def __call__(
354
+ self,
355
+ attn: Attention,
356
+ x: float["b n d"], # noised input x # noqa: F722
357
+ mask: bool["b n"] | None = None, # noqa: F722
358
+ rope=None, # rotary position embedding
359
+ ) -> torch.FloatTensor:
360
+ batch_size = x.shape[0]
361
+
362
+ # `sample` projections.
363
+ query = attn.to_q(x)
364
+ key = attn.to_k(x)
365
+ value = attn.to_v(x)
366
+
367
+ # apply rotary position embedding
368
+ if rope is not None:
369
+ freqs, xpos_scale = rope
370
+ q_xpos_scale, k_xpos_scale = (xpos_scale, xpos_scale**-1.0) if xpos_scale is not None else (1.0, 1.0)
371
+
372
+ query = apply_rotary_pos_emb(query, freqs, q_xpos_scale)
373
+ key = apply_rotary_pos_emb(key, freqs, k_xpos_scale)
374
+
375
+ # attention
376
+ inner_dim = key.shape[-1]
377
+ head_dim = inner_dim // attn.heads
378
+ query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
379
+ key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
380
+ value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
381
+
382
+ # mask. e.g. inference got a batch with different target durations, mask out the padding
383
+ if mask is not None:
384
+ attn_mask = mask
385
+ if attn_mask.dim() == 2:
386
+ attn_mask = attn_mask.unsqueeze(1).unsqueeze(1) # 'b n -> b 1 1 n'
387
+ attn_mask = attn_mask.expand(batch_size, attn.heads, query.shape[-2], key.shape[-2])
388
+ else:
389
+ attn_mask = None
390
+
391
+ x = F.scaled_dot_product_attention(query, key, value, attn_mask=attn_mask, dropout_p=0.0, is_causal=False)
392
+ x = x.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
393
+ x = x.to(query.dtype)
394
+
395
+ # linear proj
396
+ x = attn.to_out[0](x)
397
+ # dropout
398
+ x = attn.to_out[1](x)
399
+
400
+ if mask is not None:
401
+ if mask.dim() == 2:
402
+ mask = mask.unsqueeze(-1)
403
+ else:
404
+ mask = mask[:, 0, -1].unsqueeze(-1)
405
+ x = x.masked_fill(~mask, 0.0)
406
+
407
+ return x
408
+
409
+
410
+ # Joint Attention processor for MM-DiT
411
+ # modified from diffusers/src/diffusers/models/attention_processor.py
412
+
413
+
414
+ class JointAttnProcessor:
415
+ def __init__(self):
416
+ pass
417
+
418
+ def __call__(
419
+ self,
420
+ attn: Attention,
421
+ x: float["b n d"], # noised input x # noqa: F722
422
+ c: float["b nt d"] = None, # context c, here text # noqa: F722
423
+ mask: bool["b n"] | None = None, # noqa: F722
424
+ rope=None, # rotary position embedding for x
425
+ c_rope=None, # rotary position embedding for c
426
+ ) -> torch.FloatTensor:
427
+ residual = x
428
+
429
+ batch_size = c.shape[0]
430
+
431
+ # `sample` projections.
432
+ query = attn.to_q(x)
433
+ key = attn.to_k(x)
434
+ value = attn.to_v(x)
435
+
436
+ # `context` projections.
437
+ c_query = attn.to_q_c(c)
438
+ c_key = attn.to_k_c(c)
439
+ c_value = attn.to_v_c(c)
440
+
441
+ # apply rope for context and noised input independently
442
+ if rope is not None:
443
+ freqs, xpos_scale = rope
444
+ q_xpos_scale, k_xpos_scale = (xpos_scale, xpos_scale**-1.0) if xpos_scale is not None else (1.0, 1.0)
445
+ query = apply_rotary_pos_emb(query, freqs, q_xpos_scale)
446
+ key = apply_rotary_pos_emb(key, freqs, k_xpos_scale)
447
+ if c_rope is not None:
448
+ freqs, xpos_scale = c_rope
449
+ q_xpos_scale, k_xpos_scale = (xpos_scale, xpos_scale**-1.0) if xpos_scale is not None else (1.0, 1.0)
450
+ c_query = apply_rotary_pos_emb(c_query, freqs, q_xpos_scale)
451
+ c_key = apply_rotary_pos_emb(c_key, freqs, k_xpos_scale)
452
+
453
+ # attention
454
+ query = torch.cat([query, c_query], dim=1)
455
+ key = torch.cat([key, c_key], dim=1)
456
+ value = torch.cat([value, c_value], dim=1)
457
+
458
+ inner_dim = key.shape[-1]
459
+ head_dim = inner_dim // attn.heads
460
+ query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
461
+ key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
462
+ value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
463
+
464
+ # mask. e.g. inference got a batch with different target durations, mask out the padding
465
+ if mask is not None:
466
+ attn_mask = F.pad(mask, (0, c.shape[1]), value=True) # no mask for c (text)
467
+ attn_mask = attn_mask.unsqueeze(1).unsqueeze(1) # 'b n -> b 1 1 n'
468
+ attn_mask = attn_mask.expand(batch_size, attn.heads, query.shape[-2], key.shape[-2])
469
+ else:
470
+ attn_mask = None
471
+
472
+ x = F.scaled_dot_product_attention(query, key, value, attn_mask=attn_mask, dropout_p=0.0, is_causal=False)
473
+ x = x.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
474
+ x = x.to(query.dtype)
475
+
476
+ # Split the attention outputs.
477
+ x, c = (
478
+ x[:, : residual.shape[1]],
479
+ x[:, residual.shape[1]:],
480
+ )
481
+
482
+ # linear proj
483
+ x = attn.to_out[0](x)
484
+ # dropout
485
+ x = attn.to_out[1](x)
486
+ if not attn.context_pre_only:
487
+ c = attn.to_out_c(c)
488
+
489
+ if mask is not None:
490
+ mask = mask.unsqueeze(-1)
491
+ x = x.masked_fill(~mask, 0.0)
492
+ # c = c.masked_fill(~mask, 0.) # no mask for c (text)
493
+
494
+ return x, c
495
+
496
+
497
+ # DiT Block
498
+
499
+
500
+ class DiTBlock(nn.Module):
501
+ def __init__(self, dim, heads, dim_head, ff_mult=4, dropout=0.1):
502
+ super().__init__()
503
+
504
+ self.attn_norm = AdaLayerNormZero(dim)
505
+ self.attn = Attention(
506
+ processor=AttnProcessor(),
507
+ dim=dim,
508
+ heads=heads,
509
+ dim_head=dim_head,
510
+ dropout=dropout,
511
+ )
512
+
513
+ self.ff_norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
514
+ self.ff = FeedForward(dim=dim, mult=ff_mult, dropout=dropout, approximate="tanh")
515
+
516
+ def forward(self, x, t, mask=None, rope=None): # x: noised input, t: time embedding
517
+ # pre-norm & modulation for attention input
518
+ norm, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.attn_norm(x, emb=t)
519
+
520
+ # attention
521
+ attn_output = self.attn(x=norm, mask=mask, rope=rope)
522
+
523
+ # process attention output for input x
524
+ x = x + gate_msa.unsqueeze(1) * attn_output
525
+
526
+ ff_norm = self.ff_norm(x) * (1 + scale_mlp[:, None]) + shift_mlp[:, None]
527
+ ff_output = self.ff(ff_norm)
528
+ x = x + gate_mlp.unsqueeze(1) * ff_output
529
+
530
+ return x
531
+
532
+
533
+ # MMDiT Block https://arxiv.org/abs/2403.03206
534
+
535
+
536
+ class MMDiTBlock(nn.Module):
537
+ r"""
538
+ modified from diffusers/src/diffusers/models/attention.py
539
+
540
+ notes.
541
+ _c: context related. text, cond, etc. (left part in sd3 fig2.b)
542
+ _x: noised input related. (right part)
543
+ context_pre_only: last layer only do prenorm + modulation cuz no more ffn
544
+ """
545
+
546
+ def __init__(self, dim, heads, dim_head, ff_mult=4, dropout=0.1, context_pre_only=False):
547
+ super().__init__()
548
+
549
+ self.context_pre_only = context_pre_only
550
+
551
+ self.attn_norm_c = AdaLayerNormZero_Final(dim) if context_pre_only else AdaLayerNormZero(dim)
552
+ self.attn_norm_x = AdaLayerNormZero(dim)
553
+ self.attn = Attention(
554
+ processor=JointAttnProcessor(),
555
+ dim=dim,
556
+ heads=heads,
557
+ dim_head=dim_head,
558
+ dropout=dropout,
559
+ context_dim=dim,
560
+ context_pre_only=context_pre_only,
561
+ )
562
+
563
+ if not context_pre_only:
564
+ self.ff_norm_c = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
565
+ self.ff_c = FeedForward(dim=dim, mult=ff_mult, dropout=dropout, approximate="tanh")
566
+ else:
567
+ self.ff_norm_c = None
568
+ self.ff_c = None
569
+ self.ff_norm_x = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
570
+ self.ff_x = FeedForward(dim=dim, mult=ff_mult, dropout=dropout, approximate="tanh")
571
+
572
+ def forward(self, x, c, t, mask=None, rope=None, c_rope=None): # x: noised input, c: context, t: time embedding
573
+ # pre-norm & modulation for attention input
574
+ if self.context_pre_only:
575
+ norm_c = self.attn_norm_c(c, t)
576
+ else:
577
+ norm_c, c_gate_msa, c_shift_mlp, c_scale_mlp, c_gate_mlp = self.attn_norm_c(c, emb=t)
578
+ norm_x, x_gate_msa, x_shift_mlp, x_scale_mlp, x_gate_mlp = self.attn_norm_x(x, emb=t)
579
+
580
+ # attention
581
+ x_attn_output, c_attn_output = self.attn(x=norm_x, c=norm_c, mask=mask, rope=rope, c_rope=c_rope)
582
+
583
+ # process attention output for context c
584
+ if self.context_pre_only:
585
+ c = None
586
+ else: # if not last layer
587
+ c = c + c_gate_msa.unsqueeze(1) * c_attn_output
588
+
589
+ norm_c = self.ff_norm_c(c) * (1 + c_scale_mlp[:, None]) + c_shift_mlp[:, None]
590
+ c_ff_output = self.ff_c(norm_c)
591
+ c = c + c_gate_mlp.unsqueeze(1) * c_ff_output
592
+
593
+ # process attention output for input x
594
+ x = x + x_gate_msa.unsqueeze(1) * x_attn_output
595
+
596
+ norm_x = self.ff_norm_x(x) * (1 + x_scale_mlp[:, None]) + x_shift_mlp[:, None]
597
+ x_ff_output = self.ff_x(norm_x)
598
+ x = x + x_gate_mlp.unsqueeze(1) * x_ff_output
599
+
600
+ return c, x
601
+
602
+
603
+ # time step conditioning embedding
604
+
605
+
606
+ class TimestepEmbedding(nn.Module):
607
+ def __init__(self, dim, freq_embed_dim=256):
608
+ super().__init__()
609
+ self.time_embed = SinusPositionEmbedding(freq_embed_dim)
610
+ self.time_mlp = nn.Sequential(nn.Linear(freq_embed_dim, dim), nn.SiLU(), nn.Linear(dim, dim))
611
+
612
+ def forward(self, timestep: float["b"]): # noqa: F821
613
+ time_hidden = self.time_embed(timestep)
614
+ time_hidden = time_hidden.to(timestep.dtype)
615
+ time = self.time_mlp(time_hidden) # b d
616
+ return time
third_party/CosyVoice/cosyvoice/flow/__pycache__/flow.cpython-312.pyc ADDED
Binary file (25 kB). View file
 
third_party/CosyVoice/cosyvoice/flow/__pycache__/flow_matching.cpython-312.pyc ADDED
Binary file (13 kB). View file
 
third_party/CosyVoice/cosyvoice/flow/decoder.py ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Zhihao Du)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from typing import Tuple
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.nn.functional as F
18
+ from einops import pack, rearrange, repeat
19
+ from cosyvoice.utils.common import mask_to_bias
20
+ from cosyvoice.utils.mask import add_optional_chunk_mask
21
+ from matcha.models.components.decoder import SinusoidalPosEmb, Block1D, ResnetBlock1D, Downsample1D, TimestepEmbedding, Upsample1D
22
+ from matcha.models.components.transformer import BasicTransformerBlock
23
+
24
+
25
+ class Transpose(torch.nn.Module):
26
+ def __init__(self, dim0: int, dim1: int):
27
+ super().__init__()
28
+ self.dim0 = dim0
29
+ self.dim1 = dim1
30
+
31
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
32
+ x = torch.transpose(x, self.dim0, self.dim1)
33
+ return x
34
+
35
+
36
+ class CausalConv1d(torch.nn.Conv1d):
37
+ def __init__(
38
+ self,
39
+ in_channels: int,
40
+ out_channels: int,
41
+ kernel_size: int,
42
+ stride: int = 1,
43
+ dilation: int = 1,
44
+ groups: int = 1,
45
+ bias: bool = True,
46
+ padding_mode: str = 'zeros',
47
+ device=None,
48
+ dtype=None
49
+ ) -> None:
50
+ super(CausalConv1d, self).__init__(in_channels, out_channels,
51
+ kernel_size, stride,
52
+ padding=0, dilation=dilation,
53
+ groups=groups, bias=bias,
54
+ padding_mode=padding_mode,
55
+ device=device, dtype=dtype)
56
+ assert stride == 1
57
+ self.causal_padding = kernel_size - 1
58
+
59
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
60
+ x = F.pad(x, (self.causal_padding, 0), value=0.0)
61
+ x = super(CausalConv1d, self).forward(x)
62
+ return x
63
+
64
+
65
+ class CausalBlock1D(Block1D):
66
+ def __init__(self, dim: int, dim_out: int):
67
+ super(CausalBlock1D, self).__init__(dim, dim_out)
68
+ self.block = torch.nn.Sequential(
69
+ CausalConv1d(dim, dim_out, 3),
70
+ Transpose(1, 2),
71
+ nn.LayerNorm(dim_out),
72
+ Transpose(1, 2),
73
+ nn.Mish(),
74
+ )
75
+
76
+ def forward(self, x: torch.Tensor, mask: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
77
+ output = self.block(x * mask)
78
+ return output * mask
79
+
80
+
81
+ class CausalResnetBlock1D(ResnetBlock1D):
82
+ def __init__(self, dim: int, dim_out: int, time_emb_dim: int, groups: int = 8):
83
+ super(CausalResnetBlock1D, self).__init__(dim, dim_out, time_emb_dim, groups)
84
+ self.block1 = CausalBlock1D(dim, dim_out)
85
+ self.block2 = CausalBlock1D(dim_out, dim_out)
86
+
87
+
88
+ class ConditionalDecoder(nn.Module):
89
+ def __init__(
90
+ self,
91
+ in_channels,
92
+ out_channels,
93
+ channels=(256, 256),
94
+ dropout=0.05,
95
+ attention_head_dim=64,
96
+ n_blocks=1,
97
+ num_mid_blocks=2,
98
+ num_heads=4,
99
+ act_fn="snake",
100
+ ):
101
+ """
102
+ This decoder requires an input with the same shape of the target. So, if your text content
103
+ is shorter or longer than the outputs, please re-sampling it before feeding to the decoder.
104
+ """
105
+ super().__init__()
106
+ channels = tuple(channels)
107
+ self.in_channels = in_channels
108
+ self.out_channels = out_channels
109
+
110
+ self.time_embeddings = SinusoidalPosEmb(in_channels)
111
+ time_embed_dim = channels[0] * 4
112
+ self.time_mlp = TimestepEmbedding(
113
+ in_channels=in_channels,
114
+ time_embed_dim=time_embed_dim,
115
+ act_fn="silu",
116
+ )
117
+ self.down_blocks = nn.ModuleList([])
118
+ self.mid_blocks = nn.ModuleList([])
119
+ self.up_blocks = nn.ModuleList([])
120
+
121
+ output_channel = in_channels
122
+ for i in range(len(channels)): # pylint: disable=consider-using-enumerate
123
+ input_channel = output_channel
124
+ output_channel = channels[i]
125
+ is_last = i == len(channels) - 1
126
+ resnet = ResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
127
+ transformer_blocks = nn.ModuleList(
128
+ [
129
+ BasicTransformerBlock(
130
+ dim=output_channel,
131
+ num_attention_heads=num_heads,
132
+ attention_head_dim=attention_head_dim,
133
+ dropout=dropout,
134
+ activation_fn=act_fn,
135
+ )
136
+ for _ in range(n_blocks)
137
+ ]
138
+ )
139
+ downsample = (
140
+ Downsample1D(output_channel) if not is_last else nn.Conv1d(output_channel, output_channel, 3, padding=1)
141
+ )
142
+ self.down_blocks.append(nn.ModuleList([resnet, transformer_blocks, downsample]))
143
+
144
+ for _ in range(num_mid_blocks):
145
+ input_channel = channels[-1]
146
+ out_channels = channels[-1]
147
+ resnet = ResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
148
+
149
+ transformer_blocks = nn.ModuleList(
150
+ [
151
+ BasicTransformerBlock(
152
+ dim=output_channel,
153
+ num_attention_heads=num_heads,
154
+ attention_head_dim=attention_head_dim,
155
+ dropout=dropout,
156
+ activation_fn=act_fn,
157
+ )
158
+ for _ in range(n_blocks)
159
+ ]
160
+ )
161
+
162
+ self.mid_blocks.append(nn.ModuleList([resnet, transformer_blocks]))
163
+
164
+ channels = channels[::-1] + (channels[0],)
165
+ for i in range(len(channels) - 1):
166
+ input_channel = channels[i] * 2
167
+ output_channel = channels[i + 1]
168
+ is_last = i == len(channels) - 2
169
+ resnet = ResnetBlock1D(
170
+ dim=input_channel,
171
+ dim_out=output_channel,
172
+ time_emb_dim=time_embed_dim,
173
+ )
174
+ transformer_blocks = nn.ModuleList(
175
+ [
176
+ BasicTransformerBlock(
177
+ dim=output_channel,
178
+ num_attention_heads=num_heads,
179
+ attention_head_dim=attention_head_dim,
180
+ dropout=dropout,
181
+ activation_fn=act_fn,
182
+ )
183
+ for _ in range(n_blocks)
184
+ ]
185
+ )
186
+ upsample = (
187
+ Upsample1D(output_channel, use_conv_transpose=True)
188
+ if not is_last
189
+ else nn.Conv1d(output_channel, output_channel, 3, padding=1)
190
+ )
191
+ self.up_blocks.append(nn.ModuleList([resnet, transformer_blocks, upsample]))
192
+ self.final_block = Block1D(channels[-1], channels[-1])
193
+ self.final_proj = nn.Conv1d(channels[-1], self.out_channels, 1)
194
+ self.initialize_weights()
195
+
196
+ def initialize_weights(self):
197
+ for m in self.modules():
198
+ if isinstance(m, nn.Conv1d):
199
+ nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
200
+ if m.bias is not None:
201
+ nn.init.constant_(m.bias, 0)
202
+ elif isinstance(m, nn.GroupNorm):
203
+ nn.init.constant_(m.weight, 1)
204
+ nn.init.constant_(m.bias, 0)
205
+ elif isinstance(m, nn.Linear):
206
+ nn.init.kaiming_normal_(m.weight, nonlinearity="relu")
207
+ if m.bias is not None:
208
+ nn.init.constant_(m.bias, 0)
209
+
210
+ def forward(self, x, mask, mu, t, spks=None, cond=None, streaming=False):
211
+ """Forward pass of the UNet1DConditional model.
212
+
213
+ Args:
214
+ x (torch.Tensor): shape (batch_size, in_channels, time)
215
+ mask (_type_): shape (batch_size, 1, time)
216
+ t (_type_): shape (batch_size)
217
+ spks (_type_, optional): shape: (batch_size, condition_channels). Defaults to None.
218
+ cond (_type_, optional): placeholder for future use. Defaults to None.
219
+
220
+ Raises:
221
+ ValueError: _description_
222
+ ValueError: _description_
223
+
224
+ Returns:
225
+ _type_: _description_
226
+ """
227
+
228
+ t = self.time_embeddings(t).to(t.dtype)
229
+ t = self.time_mlp(t)
230
+
231
+ x = pack([x, mu], "b * t")[0]
232
+
233
+ if spks is not None:
234
+ spks = repeat(spks, "b c -> b c t", t=x.shape[-1])
235
+ x = pack([x, spks], "b * t")[0]
236
+ if cond is not None:
237
+ x = pack([x, cond], "b * t")[0]
238
+
239
+ hiddens = []
240
+ masks = [mask]
241
+ for resnet, transformer_blocks, downsample in self.down_blocks:
242
+ mask_down = masks[-1]
243
+ x = resnet(x, mask_down, t)
244
+ x = rearrange(x, "b c t -> b t c").contiguous()
245
+ attn_mask = add_optional_chunk_mask(x, mask_down.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
246
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
247
+ for transformer_block in transformer_blocks:
248
+ x = transformer_block(
249
+ hidden_states=x,
250
+ attention_mask=attn_mask,
251
+ timestep=t,
252
+ )
253
+ x = rearrange(x, "b t c -> b c t").contiguous()
254
+ hiddens.append(x) # Save hidden states for skip connections
255
+ x = downsample(x * mask_down)
256
+ masks.append(mask_down[:, :, ::2])
257
+ masks = masks[:-1]
258
+ mask_mid = masks[-1]
259
+
260
+ for resnet, transformer_blocks in self.mid_blocks:
261
+ x = resnet(x, mask_mid, t)
262
+ x = rearrange(x, "b c t -> b t c").contiguous()
263
+ attn_mask = add_optional_chunk_mask(x, mask_mid.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
264
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
265
+ for transformer_block in transformer_blocks:
266
+ x = transformer_block(
267
+ hidden_states=x,
268
+ attention_mask=attn_mask,
269
+ timestep=t,
270
+ )
271
+ x = rearrange(x, "b t c -> b c t").contiguous()
272
+
273
+ for resnet, transformer_blocks, upsample in self.up_blocks:
274
+ mask_up = masks.pop()
275
+ skip = hiddens.pop()
276
+ x = pack([x[:, :, :skip.shape[-1]], skip], "b * t")[0]
277
+ x = resnet(x, mask_up, t)
278
+ x = rearrange(x, "b c t -> b t c").contiguous()
279
+ attn_mask = add_optional_chunk_mask(x, mask_up.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
280
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
281
+ for transformer_block in transformer_blocks:
282
+ x = transformer_block(
283
+ hidden_states=x,
284
+ attention_mask=attn_mask,
285
+ timestep=t,
286
+ )
287
+ x = rearrange(x, "b t c -> b c t").contiguous()
288
+ x = upsample(x * mask_up)
289
+ x = self.final_block(x, mask_up)
290
+ output = self.final_proj(x * mask_up)
291
+ return output * mask
292
+
293
+
294
+ class CausalConditionalDecoder(ConditionalDecoder):
295
+ def __init__(
296
+ self,
297
+ in_channels,
298
+ out_channels,
299
+ channels=(256, 256),
300
+ dropout=0.05,
301
+ attention_head_dim=64,
302
+ n_blocks=1,
303
+ num_mid_blocks=2,
304
+ num_heads=4,
305
+ act_fn="snake",
306
+ static_chunk_size=50,
307
+ num_decoding_left_chunks=2,
308
+ ):
309
+ """
310
+ This decoder requires an input with the same shape of the target. So, if your text content
311
+ is shorter or longer than the outputs, please re-sampling it before feeding to the decoder.
312
+ """
313
+ torch.nn.Module.__init__(self)
314
+ channels = tuple(channels)
315
+ self.in_channels = in_channels
316
+ self.out_channels = out_channels
317
+ self.time_embeddings = SinusoidalPosEmb(in_channels)
318
+ time_embed_dim = channels[0] * 4
319
+ self.time_mlp = TimestepEmbedding(
320
+ in_channels=in_channels,
321
+ time_embed_dim=time_embed_dim,
322
+ act_fn="silu",
323
+ )
324
+ self.static_chunk_size = static_chunk_size
325
+ self.num_decoding_left_chunks = num_decoding_left_chunks
326
+ self.down_blocks = nn.ModuleList([])
327
+ self.mid_blocks = nn.ModuleList([])
328
+ self.up_blocks = nn.ModuleList([])
329
+
330
+ output_channel = in_channels
331
+ for i in range(len(channels)): # pylint: disable=consider-using-enumerate
332
+ input_channel = output_channel
333
+ output_channel = channels[i]
334
+ is_last = i == len(channels) - 1
335
+ resnet = CausalResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
336
+ transformer_blocks = nn.ModuleList(
337
+ [
338
+ BasicTransformerBlock(
339
+ dim=output_channel,
340
+ num_attention_heads=num_heads,
341
+ attention_head_dim=attention_head_dim,
342
+ dropout=dropout,
343
+ activation_fn=act_fn,
344
+ )
345
+ for _ in range(n_blocks)
346
+ ]
347
+ )
348
+ downsample = (
349
+ Downsample1D(output_channel) if not is_last else CausalConv1d(output_channel, output_channel, 3)
350
+ )
351
+ self.down_blocks.append(nn.ModuleList([resnet, transformer_blocks, downsample]))
352
+
353
+ for _ in range(num_mid_blocks):
354
+ input_channel = channels[-1]
355
+ out_channels = channels[-1]
356
+ resnet = CausalResnetBlock1D(dim=input_channel, dim_out=output_channel, time_emb_dim=time_embed_dim)
357
+
358
+ transformer_blocks = nn.ModuleList(
359
+ [
360
+ BasicTransformerBlock(
361
+ dim=output_channel,
362
+ num_attention_heads=num_heads,
363
+ attention_head_dim=attention_head_dim,
364
+ dropout=dropout,
365
+ activation_fn=act_fn,
366
+ )
367
+ for _ in range(n_blocks)
368
+ ]
369
+ )
370
+
371
+ self.mid_blocks.append(nn.ModuleList([resnet, transformer_blocks]))
372
+
373
+ channels = channels[::-1] + (channels[0],)
374
+ for i in range(len(channels) - 1):
375
+ input_channel = channels[i] * 2
376
+ output_channel = channels[i + 1]
377
+ is_last = i == len(channels) - 2
378
+ resnet = CausalResnetBlock1D(
379
+ dim=input_channel,
380
+ dim_out=output_channel,
381
+ time_emb_dim=time_embed_dim,
382
+ )
383
+ transformer_blocks = nn.ModuleList(
384
+ [
385
+ BasicTransformerBlock(
386
+ dim=output_channel,
387
+ num_attention_heads=num_heads,
388
+ attention_head_dim=attention_head_dim,
389
+ dropout=dropout,
390
+ activation_fn=act_fn,
391
+ )
392
+ for _ in range(n_blocks)
393
+ ]
394
+ )
395
+ upsample = (
396
+ Upsample1D(output_channel, use_conv_transpose=True)
397
+ if not is_last
398
+ else CausalConv1d(output_channel, output_channel, 3)
399
+ )
400
+ self.up_blocks.append(nn.ModuleList([resnet, transformer_blocks, upsample]))
401
+ self.final_block = CausalBlock1D(channels[-1], channels[-1])
402
+ self.final_proj = nn.Conv1d(channels[-1], self.out_channels, 1)
403
+ self.initialize_weights()
404
+
405
+ def forward(self, x, mask, mu, t, spks=None, cond=None, streaming=False):
406
+ """Forward pass of the UNet1DConditional model.
407
+
408
+ Args:
409
+ x (torch.Tensor): shape (batch_size, in_channels, time)
410
+ mask (_type_): shape (batch_size, 1, time)
411
+ t (_type_): shape (batch_size)
412
+ spks (_type_, optional): shape: (batch_size, condition_channels). Defaults to None.
413
+ cond (_type_, optional): placeholder for future use. Defaults to None.
414
+
415
+ Raises:
416
+ ValueError: _description_
417
+ ValueError: _description_
418
+
419
+ Returns:
420
+ _type_: _description_
421
+ """
422
+ t = self.time_embeddings(t).to(t.dtype)
423
+ t = self.time_mlp(t)
424
+
425
+ x = pack([x, mu], "b * t")[0]
426
+
427
+ if spks is not None:
428
+ spks = repeat(spks, "b c -> b c t", t=x.shape[-1])
429
+ x = pack([x, spks], "b * t")[0]
430
+ if cond is not None:
431
+ x = pack([x, cond], "b * t")[0]
432
+
433
+ hiddens = []
434
+ masks = [mask]
435
+ for resnet, transformer_blocks, downsample in self.down_blocks:
436
+ mask_down = masks[-1]
437
+ x = resnet(x, mask_down, t)
438
+ x = rearrange(x, "b c t -> b t c").contiguous()
439
+ if streaming is True:
440
+ attn_mask = add_optional_chunk_mask(x, mask_down.bool(), False, False, 0, self.static_chunk_size, -1)
441
+ else:
442
+ attn_mask = add_optional_chunk_mask(x, mask_down.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
443
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
444
+ for transformer_block in transformer_blocks:
445
+ x = transformer_block(
446
+ hidden_states=x,
447
+ attention_mask=attn_mask,
448
+ timestep=t,
449
+ )
450
+ x = rearrange(x, "b t c -> b c t").contiguous()
451
+ hiddens.append(x) # Save hidden states for skip connections
452
+ x = downsample(x * mask_down)
453
+ masks.append(mask_down[:, :, ::2])
454
+ masks = masks[:-1]
455
+ mask_mid = masks[-1]
456
+
457
+ for resnet, transformer_blocks in self.mid_blocks:
458
+ x = resnet(x, mask_mid, t)
459
+ x = rearrange(x, "b c t -> b t c").contiguous()
460
+ if streaming is True:
461
+ attn_mask = add_optional_chunk_mask(x, mask_mid.bool(), False, False, 0, self.static_chunk_size, -1)
462
+ else:
463
+ attn_mask = add_optional_chunk_mask(x, mask_mid.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
464
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
465
+ for transformer_block in transformer_blocks:
466
+ x = transformer_block(
467
+ hidden_states=x,
468
+ attention_mask=attn_mask,
469
+ timestep=t,
470
+ )
471
+ x = rearrange(x, "b t c -> b c t").contiguous()
472
+
473
+ for resnet, transformer_blocks, upsample in self.up_blocks:
474
+ mask_up = masks.pop()
475
+ skip = hiddens.pop()
476
+ x = pack([x[:, :, :skip.shape[-1]], skip], "b * t")[0]
477
+ x = resnet(x, mask_up, t)
478
+ x = rearrange(x, "b c t -> b t c").contiguous()
479
+ if streaming is True:
480
+ attn_mask = add_optional_chunk_mask(x, mask_up.bool(), False, False, 0, self.static_chunk_size, -1)
481
+ else:
482
+ attn_mask = add_optional_chunk_mask(x, mask_up.bool(), False, False, 0, 0, -1).repeat(1, x.size(1), 1)
483
+ attn_mask = mask_to_bias(attn_mask, x.dtype)
484
+ for transformer_block in transformer_blocks:
485
+ x = transformer_block(
486
+ hidden_states=x,
487
+ attention_mask=attn_mask,
488
+ timestep=t,
489
+ )
490
+ x = rearrange(x, "b t c -> b c t").contiguous()
491
+ x = upsample(x * mask_up)
492
+ x = self.final_block(x, mask_up)
493
+ output = self.final_proj(x * mask_up)
494
+ return output * mask
third_party/CosyVoice/cosyvoice/flow/flow.py ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Zhihao Du)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import os, logging
15
+ import random
16
+ from typing import Dict, Optional
17
+ import torch
18
+ import torch.nn as nn
19
+ from torch.nn import functional as F
20
+ from omegaconf import DictConfig
21
+ from cosyvoice.utils.mask import make_pad_mask
22
+ from cosyvoice.utils.onnx import SpeechTokenExtractor, online_feature, onnx_path
23
+
24
+
25
+ class MaskedDiffWithXvec(torch.nn.Module):
26
+ def __init__(self,
27
+ input_size: int = 512,
28
+ output_size: int = 80,
29
+ spk_embed_dim: int = 192,
30
+ output_type: str = "mel",
31
+ vocab_size: int = 4096,
32
+ input_frame_rate: int = 50,
33
+ only_mask_loss: bool = True,
34
+ encoder: torch.nn.Module = None,
35
+ length_regulator: torch.nn.Module = None,
36
+ decoder: torch.nn.Module = None,
37
+ decoder_conf: Dict = {'in_channels': 240, 'out_channel': 80, 'spk_emb_dim': 80, 'n_spks': 1,
38
+ 'cfm_params': DictConfig({'sigma_min': 1e-06, 'solver': 'euler', 't_scheduler': 'cosine',
39
+ 'training_cfg_rate': 0.2, 'inference_cfg_rate': 0.7, 'reg_loss_type': 'l1'}),
40
+ 'decoder_params': {'channels': [256, 256], 'dropout': 0.0, 'attention_head_dim': 64,
41
+ 'n_blocks': 4, 'num_mid_blocks': 12, 'num_heads': 8, 'act_fn': 'gelu'}}):
42
+ super().__init__()
43
+ self.input_size = input_size
44
+ self.output_size = output_size
45
+ self.decoder_conf = decoder_conf
46
+ self.vocab_size = vocab_size
47
+ self.output_type = output_type
48
+ self.input_frame_rate = input_frame_rate
49
+ logging.info(f"input frame rate={self.input_frame_rate}")
50
+ self.input_embedding = nn.Embedding(vocab_size, input_size)
51
+ self.spk_embed_affine_layer = torch.nn.Linear(spk_embed_dim, output_size)
52
+ self.encoder = encoder
53
+ self.encoder_proj = torch.nn.Linear(self.encoder.output_size(), output_size)
54
+ self.decoder = decoder
55
+ self.length_regulator = length_regulator
56
+ self.only_mask_loss = only_mask_loss
57
+
58
+ def forward(
59
+ self,
60
+ batch: dict,
61
+ device: torch.device,
62
+ ) -> Dict[str, Optional[torch.Tensor]]:
63
+ token = batch['speech_token'].to(device)
64
+ token_len = batch['speech_token_len'].to(device)
65
+ feat = batch['speech_feat'].to(device)
66
+ feat_len = batch['speech_feat_len'].to(device)
67
+ embedding = batch['embedding'].to(device)
68
+
69
+ # xvec projection
70
+ embedding = F.normalize(embedding, dim=1)
71
+ embedding = self.spk_embed_affine_layer(embedding)
72
+
73
+ # concat text and prompt_text
74
+ mask = (~make_pad_mask(token_len)).float().unsqueeze(-1).to(device)
75
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
76
+
77
+ # text encode
78
+ h, h_lengths = self.encoder(token, token_len)
79
+ h = self.encoder_proj(h)
80
+ h, h_lengths = self.length_regulator(h, feat_len)
81
+
82
+ # get conditions
83
+ conds = torch.zeros(feat.shape, device=token.device)
84
+ for i, j in enumerate(feat_len):
85
+ if random.random() < 0.5:
86
+ continue
87
+ index = random.randint(0, int(0.3 * j))
88
+ conds[i, :index] = feat[i, :index]
89
+ conds = conds.transpose(1, 2)
90
+
91
+ mask = (~make_pad_mask(feat_len)).to(h)
92
+ # NOTE this is unnecessary, feat/h already same shape
93
+ loss, _ = self.decoder.compute_loss(
94
+ feat.transpose(1, 2).contiguous(),
95
+ mask.unsqueeze(1),
96
+ h.transpose(1, 2).contiguous(),
97
+ embedding,
98
+ cond=conds
99
+ )
100
+ return {'loss': loss}
101
+
102
+ @torch.inference_mode()
103
+ def inference(self,
104
+ token,
105
+ token_len,
106
+ prompt_token,
107
+ prompt_token_len,
108
+ prompt_feat,
109
+ prompt_feat_len,
110
+ embedding,
111
+ flow_cache):
112
+ assert token.shape[0] == 1
113
+ # xvec projection
114
+ embedding = F.normalize(embedding, dim=1)
115
+ embedding = self.spk_embed_affine_layer(embedding)
116
+
117
+ # concat speech token and prompt speech token
118
+ token_len1, token_len2 = prompt_token.shape[1], token.shape[1]
119
+ token, token_len = torch.concat([prompt_token, token], dim=1), prompt_token_len + token_len
120
+ mask = (~make_pad_mask(token_len)).unsqueeze(-1).to(embedding)
121
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
122
+
123
+ # text encode
124
+ h, h_lengths = self.encoder(token, token_len)
125
+ h = self.encoder_proj(h)
126
+ mel_len1, mel_len2 = prompt_feat.shape[1], int(token_len2 / self.input_frame_rate * 22050 / 256)
127
+ h, h_lengths = self.length_regulator.inference(h[:, :token_len1], h[:, token_len1:], mel_len1, mel_len2, self.input_frame_rate)
128
+
129
+ # get conditions
130
+ conds = torch.zeros([1, mel_len1 + mel_len2, self.output_size], device=token.device).to(h.dtype)
131
+ conds[:, :mel_len1] = prompt_feat
132
+ conds = conds.transpose(1, 2)
133
+
134
+ mask = (~make_pad_mask(torch.tensor([mel_len1 + mel_len2]))).to(h)
135
+ feat, flow_cache = self.decoder(
136
+ mu=h.transpose(1, 2).contiguous(),
137
+ mask=mask.unsqueeze(1),
138
+ spks=embedding,
139
+ cond=conds,
140
+ n_timesteps=10,
141
+ prompt_len=mel_len1,
142
+ cache=flow_cache
143
+ )
144
+ feat = feat[:, :, mel_len1:]
145
+ assert feat.shape[2] == mel_len2
146
+ return feat.float(), flow_cache
147
+
148
+
149
+ class CausalMaskedDiffWithXvec(torch.nn.Module):
150
+ def __init__(self,
151
+ input_size: int = 512,
152
+ output_size: int = 80,
153
+ spk_embed_dim: int = 192,
154
+ output_type: str = "mel",
155
+ vocab_size: int = 4096,
156
+ input_frame_rate: int = 50,
157
+ only_mask_loss: bool = True,
158
+ token_mel_ratio: int = 2,
159
+ pre_lookahead_len: int = 3,
160
+ encoder: torch.nn.Module = None,
161
+ decoder: torch.nn.Module = None,
162
+ decoder_conf: Dict = {'in_channels': 240, 'out_channel': 80, 'spk_emb_dim': 80, 'n_spks': 1,
163
+ 'cfm_params': DictConfig({'sigma_min': 1e-06, 'solver': 'euler', 't_scheduler': 'cosine',
164
+ 'training_cfg_rate': 0.2, 'inference_cfg_rate': 0.7, 'reg_loss_type': 'l1'}),
165
+ 'decoder_params': {'channels': [256, 256], 'dropout': 0.0, 'attention_head_dim': 64,
166
+ 'n_blocks': 4, 'num_mid_blocks': 12, 'num_heads': 8, 'act_fn': 'gelu'}}):
167
+ super().__init__()
168
+ self.input_size = input_size
169
+ self.output_size = output_size
170
+ self.decoder_conf = decoder_conf
171
+ self.vocab_size = vocab_size
172
+ self.output_type = output_type
173
+ self.input_frame_rate = input_frame_rate
174
+ logging.info(f"input frame rate={self.input_frame_rate}")
175
+ self.input_embedding = nn.Embedding(vocab_size, input_size)
176
+ self.spk_embed_affine_layer = torch.nn.Linear(spk_embed_dim, output_size)
177
+ self.encoder = encoder
178
+ self.encoder_proj = torch.nn.Linear(self.encoder.output_size(), output_size)
179
+ self.decoder = decoder
180
+ self.only_mask_loss = only_mask_loss
181
+ self.token_mel_ratio = token_mel_ratio
182
+ self.pre_lookahead_len = pre_lookahead_len
183
+ if online_feature is True:
184
+ self.speech_token_extractor = SpeechTokenExtractor(model_path=os.path.join(onnx_path, 'speech_tokenizer_v2.batch.onnx'))
185
+
186
+ def forward(
187
+ self,
188
+ batch: dict,
189
+ device: torch.device,
190
+ ) -> Dict[str, Optional[torch.Tensor]]:
191
+ if 'speech_token' not in batch:
192
+ token, token_len = self.speech_token_extractor.inference(batch['whisper_feat'], batch['whisper_feat_len'], device)
193
+ else:
194
+ token = batch['speech_token'].to(device)
195
+ token_len = batch['speech_token_len'].to(device)
196
+ feat = batch['speech_feat'].to(device)
197
+ feat_len = batch['speech_feat_len'].to(device)
198
+ embedding = batch['embedding'].to(device)
199
+
200
+ # NOTE unified training, static_chunk_size > 0 or = 0
201
+ streaming = True if random.random() < 0.5 else False
202
+
203
+ # xvec projection
204
+ embedding = F.normalize(embedding, dim=1)
205
+ embedding = self.spk_embed_affine_layer(embedding)
206
+
207
+ # concat text and prompt_text
208
+ mask = (~make_pad_mask(token_len)).float().unsqueeze(-1).to(device)
209
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
210
+
211
+ # text encode
212
+ h, h_lengths = self.encoder(token, token_len, streaming=streaming)
213
+ h = self.encoder_proj(h)
214
+
215
+ # get conditions
216
+ conds = torch.zeros(feat.shape, device=token.device)
217
+ for i, j in enumerate(feat_len):
218
+ if random.random() < 0.5:
219
+ continue
220
+ index = random.randint(0, int(0.3 * j))
221
+ conds[i, :index] = feat[i, :index]
222
+ conds = conds.transpose(1, 2)
223
+
224
+ mask = (~make_pad_mask(h_lengths.sum(dim=-1).squeeze(dim=1))).to(h)
225
+ loss, _ = self.decoder.compute_loss(
226
+ feat.transpose(1, 2).contiguous(),
227
+ mask.unsqueeze(1),
228
+ h.transpose(1, 2).contiguous(),
229
+ embedding,
230
+ cond=conds,
231
+ streaming=streaming,
232
+ )
233
+ return {'loss': loss}
234
+
235
+ @torch.inference_mode()
236
+ def inference(self,
237
+ token,
238
+ token_len,
239
+ prompt_token,
240
+ prompt_token_len,
241
+ prompt_feat,
242
+ prompt_feat_len,
243
+ embedding,
244
+ streaming,
245
+ finalize):
246
+ assert token.shape[0] == 1
247
+ # xvec projection
248
+ embedding = F.normalize(embedding, dim=1)
249
+ embedding = self.spk_embed_affine_layer(embedding)
250
+
251
+ # concat text and prompt_text
252
+ token, token_len = torch.concat([prompt_token, token], dim=1), prompt_token_len + token_len
253
+ mask = (~make_pad_mask(token_len)).unsqueeze(-1).to(embedding)
254
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
255
+
256
+ # text encode
257
+ if finalize is True:
258
+ h, h_lengths = self.encoder(token, token_len, streaming=streaming)
259
+ else:
260
+ token, context = token[:, :-self.pre_lookahead_len], token[:, -self.pre_lookahead_len:]
261
+ h, h_lengths = self.encoder(token, token_len, context=context, streaming=streaming)
262
+ mel_len1, mel_len2 = prompt_feat.shape[1], h.shape[1] - prompt_feat.shape[1]
263
+ h = self.encoder_proj(h)
264
+
265
+ # get conditions
266
+ conds = torch.zeros([1, mel_len1 + mel_len2, self.output_size], device=token.device).to(h.dtype)
267
+ conds[:, :mel_len1] = prompt_feat
268
+ conds = conds.transpose(1, 2)
269
+
270
+ mask = (~make_pad_mask(torch.tensor([mel_len1 + mel_len2]))).to(h)
271
+ feat, _ = self.decoder(
272
+ mu=h.transpose(1, 2).contiguous(),
273
+ mask=mask.unsqueeze(1),
274
+ spks=embedding,
275
+ cond=conds,
276
+ n_timesteps=10,
277
+ streaming=streaming
278
+ )
279
+ feat = feat[:, :, mel_len1:]
280
+ assert feat.shape[2] == mel_len2
281
+ return feat.float(), None
282
+
283
+
284
+ class CausalMaskedDiffWithDiT(torch.nn.Module):
285
+ def __init__(self,
286
+ input_size: int = 512,
287
+ output_size: int = 80,
288
+ spk_embed_dim: int = 192,
289
+ output_type: str = "mel",
290
+ vocab_size: int = 4096,
291
+ input_frame_rate: int = 50,
292
+ only_mask_loss: bool = True,
293
+ token_mel_ratio: int = 2,
294
+ pre_lookahead_len: int = 3,
295
+ pre_lookahead_layer: torch.nn.Module = None,
296
+ decoder: torch.nn.Module = None,
297
+ decoder_conf: Dict = {'in_channels': 240, 'out_channel': 80, 'spk_emb_dim': 80, 'n_spks': 1,
298
+ 'cfm_params': DictConfig({'sigma_min': 1e-06, 'solver': 'euler', 't_scheduler': 'cosine',
299
+ 'training_cfg_rate': 0.2, 'inference_cfg_rate': 0.7, 'reg_loss_type': 'l1'}),
300
+ 'decoder_params': {'channels': [256, 256], 'dropout': 0.0, 'attention_head_dim': 64,
301
+ 'n_blocks': 4, 'num_mid_blocks': 12, 'num_heads': 8, 'act_fn': 'gelu'}}):
302
+ super().__init__()
303
+ self.input_size = input_size
304
+ self.output_size = output_size
305
+ self.decoder_conf = decoder_conf
306
+ self.vocab_size = vocab_size
307
+ self.output_type = output_type
308
+ self.input_frame_rate = input_frame_rate
309
+ logging.info(f"input frame rate={self.input_frame_rate}")
310
+ self.input_embedding = nn.Embedding(vocab_size, input_size)
311
+ self.spk_embed_affine_layer = torch.nn.Linear(spk_embed_dim, output_size)
312
+ self.pre_lookahead_len = pre_lookahead_len
313
+ self.pre_lookahead_layer = pre_lookahead_layer
314
+ self.decoder = decoder
315
+ self.only_mask_loss = only_mask_loss
316
+ self.token_mel_ratio = token_mel_ratio
317
+ if online_feature is True:
318
+ self.speech_token_extractor = SpeechTokenExtractor(model_path=os.path.join(onnx_path, 'speech_tokenizer_v3.batch.onnx'))
319
+
320
+ def forward(
321
+ self,
322
+ batch: dict,
323
+ device: torch.device,
324
+ ) -> Dict[str, Optional[torch.Tensor]]:
325
+ if 'speech_token' not in batch:
326
+ token, token_len = self.speech_token_extractor.inference(batch['whisper_feat'], batch['whisper_feat_len'], device)
327
+ else:
328
+ token = batch['speech_token'].to(device)
329
+ token_len = batch['speech_token_len'].to(device)
330
+ feat = batch['speech_feat'].to(device)
331
+ feat_len = batch['speech_feat_len'].to(device)
332
+ embedding = batch['embedding'].to(device)
333
+
334
+ # NOTE unified training, static_chunk_size > 0 or = 0
335
+ streaming = True if random.random() < 0.5 else False
336
+
337
+ # xvec projection
338
+ embedding = F.normalize(embedding, dim=1)
339
+ embedding = self.spk_embed_affine_layer(embedding)
340
+
341
+ # concat text and prompt_text
342
+ mask = (~make_pad_mask(token_len)).float().unsqueeze(-1).to(device)
343
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
344
+
345
+ # text encode
346
+ h = self.pre_lookahead_layer(token)
347
+ h = h.repeat_interleave(self.token_mel_ratio, dim=1)
348
+ mask = mask.repeat_interleave(self.token_mel_ratio, dim=1).squeeze(dim=-1)
349
+
350
+ # get conditions
351
+ conds = torch.zeros(feat.shape, device=token.device)
352
+ for i, j in enumerate(feat_len):
353
+ if random.random() < 0.5:
354
+ continue
355
+ index = random.randint(0, int(0.3 * j))
356
+ conds[i, :index] = feat[i, :index]
357
+ conds = conds.transpose(1, 2)
358
+
359
+ loss, _ = self.decoder.compute_loss(
360
+ feat.transpose(1, 2).contiguous(),
361
+ mask.unsqueeze(1),
362
+ h.transpose(1, 2).contiguous(),
363
+ embedding,
364
+ cond=conds,
365
+ streaming=streaming,
366
+ )
367
+ return {'loss': loss}
368
+
369
+ @torch.inference_mode()
370
+ def inference(self,
371
+ token,
372
+ token_len,
373
+ prompt_token,
374
+ prompt_token_len,
375
+ prompt_feat,
376
+ prompt_feat_len,
377
+ embedding,
378
+ streaming,
379
+ finalize):
380
+ assert token.shape[0] == 1
381
+ # xvec projection
382
+ embedding = F.normalize(embedding, dim=1)
383
+ embedding = self.spk_embed_affine_layer(embedding)
384
+
385
+ # concat text and prompt_text
386
+ token, token_len = torch.concat([prompt_token, token], dim=1), prompt_token_len + token_len
387
+ mask = (~make_pad_mask(token_len)).unsqueeze(-1).to(embedding)
388
+ token = self.input_embedding(torch.clamp(token, min=0)) * mask
389
+
390
+ # text encode
391
+ if finalize is True:
392
+ h = self.pre_lookahead_layer(token)
393
+ else:
394
+ h = self.pre_lookahead_layer(token[:, :-self.pre_lookahead_len], context=token[:, -self.pre_lookahead_len:])
395
+ h = h.repeat_interleave(self.token_mel_ratio, dim=1)
396
+ mel_len1, mel_len2 = prompt_feat.shape[1], h.shape[1] - prompt_feat.shape[1]
397
+
398
+ # get conditions
399
+ conds = torch.zeros([1, mel_len1 + mel_len2, self.output_size], device=token.device).to(h.dtype)
400
+ conds[:, :mel_len1] = prompt_feat
401
+ conds = conds.transpose(1, 2)
402
+
403
+ mask = (~make_pad_mask(torch.tensor([mel_len1 + mel_len2]))).to(h)
404
+ feat, _ = self.decoder(
405
+ mu=h.transpose(1, 2).contiguous(),
406
+ mask=mask.unsqueeze(1),
407
+ spks=embedding,
408
+ cond=conds,
409
+ n_timesteps=10,
410
+ streaming=streaming
411
+ )
412
+ feat = feat[:, :, mel_len1:]
413
+ assert feat.shape[2] == mel_len2
414
+ return feat.float(), None
415
+
416
+
417
+ if __name__ == '__main__':
418
+ torch.backends.cudnn.deterministic = True
419
+ torch.backends.cudnn.benchmark = False
420
+ from hyperpyyaml import load_hyperpyyaml
421
+ with open('./pretrained_models/Fun-CosyVoice3-0.5B/cosyvoice3.yaml', 'r') as f:
422
+ configs = load_hyperpyyaml(f, overrides={'llm': None, 'hift': None})
423
+ model = configs['flow']
424
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
425
+ model.to(device)
426
+ model.eval()
427
+ max_len = 10 * model.decoder.estimator.static_chunk_size
428
+ chunk_size = model.decoder.estimator.static_chunk_size
429
+ context_size = model.pre_lookahead_layer.pre_lookahead_len
430
+ token = torch.randint(0, 6561, size=(1, max_len)).to(device)
431
+ token_len = torch.tensor([max_len]).to(device)
432
+ prompt_token = torch.randint(0, 6561, size=(1, chunk_size)).to(device)
433
+ prompt_token_len = torch.tensor([chunk_size]).to(device)
434
+ prompt_feat = torch.rand(1, chunk_size * 2, 80).to(device)
435
+ prompt_feat_len = torch.tensor([chunk_size * 2]).to(device)
436
+ prompt_embedding = torch.rand(1, 192).to(device)
437
+ pred_gt, _ = model.inference(token, token_len, prompt_token, prompt_token_len, prompt_feat, prompt_feat_len, prompt_embedding, streaming=True, finalize=True)
438
+ for i in range(0, max_len, chunk_size):
439
+ finalize = True if i + chunk_size + context_size >= max_len else False
440
+ pred_chunk, _ = model.inference(token[:, :i + chunk_size + context_size], torch.tensor([token[:, :i + chunk_size + context_size].shape[1]]).to(device),
441
+ prompt_token, prompt_token_len, prompt_feat, prompt_feat_len, prompt_embedding, streaming=True, finalize=finalize)
442
+ pred_chunk = pred_chunk[:, :, i * model.token_mel_ratio:]
443
+ print((pred_gt[:, :, i * model.token_mel_ratio: i * model.token_mel_ratio + pred_chunk.shape[2]] - pred_chunk).abs().max().item())
third_party/CosyVoice/cosyvoice/flow/flow_matching.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Zhihao Du)
2
+ # 2025 Alibaba Inc (authors: Xiang Lyu, Bofan Zhou)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import torch
16
+ import torch.nn.functional as F
17
+ from matcha.models.components.flow_matching import BASECFM
18
+ from cosyvoice.utils.common import set_all_random_seed
19
+
20
+
21
+ class ConditionalCFM(BASECFM):
22
+ def __init__(self, in_channels, cfm_params, n_spks=1, spk_emb_dim=64, estimator: torch.nn.Module = None):
23
+ super().__init__(
24
+ n_feats=in_channels,
25
+ cfm_params=cfm_params,
26
+ n_spks=n_spks,
27
+ spk_emb_dim=spk_emb_dim,
28
+ )
29
+ self.t_scheduler = cfm_params.t_scheduler
30
+ self.training_cfg_rate = cfm_params.training_cfg_rate
31
+ self.inference_cfg_rate = cfm_params.inference_cfg_rate
32
+ in_channels = in_channels + (spk_emb_dim if n_spks > 0 else 0)
33
+ # Just change the architecture of the estimator here
34
+ self.estimator = estimator
35
+
36
+ @torch.inference_mode()
37
+ def forward(self, mu, mask, n_timesteps, temperature=1.0, spks=None, cond=None, prompt_len=0, cache=torch.zeros(1, 80, 0, 2)):
38
+ """Forward diffusion
39
+
40
+ Args:
41
+ mu (torch.Tensor): output of encoder
42
+ shape: (batch_size, n_feats, mel_timesteps)
43
+ mask (torch.Tensor): output_mask
44
+ shape: (batch_size, 1, mel_timesteps)
45
+ n_timesteps (int): number of diffusion steps
46
+ temperature (float, optional): temperature for scaling noise. Defaults to 1.0.
47
+ spks (torch.Tensor, optional): speaker ids. Defaults to None.
48
+ shape: (batch_size, spk_emb_dim)
49
+ cond: Not used but kept for future purposes
50
+
51
+ Returns:
52
+ sample: generated mel-spectrogram
53
+ shape: (batch_size, n_feats, mel_timesteps)
54
+ """
55
+
56
+ z = torch.randn_like(mu).to(mu.device).to(mu.dtype) * temperature
57
+ cache_size = cache.shape[2]
58
+ # fix prompt and overlap part mu and z
59
+ if cache_size != 0:
60
+ z[:, :, :cache_size] = cache[:, :, :, 0]
61
+ mu[:, :, :cache_size] = cache[:, :, :, 1]
62
+ z_cache = torch.concat([z[:, :, :prompt_len], z[:, :, -34:]], dim=2)
63
+ mu_cache = torch.concat([mu[:, :, :prompt_len], mu[:, :, -34:]], dim=2)
64
+ cache = torch.stack([z_cache, mu_cache], dim=-1)
65
+
66
+ t_span = torch.linspace(0, 1, n_timesteps + 1, device=mu.device, dtype=mu.dtype)
67
+ if self.t_scheduler == 'cosine':
68
+ t_span = 1 - torch.cos(t_span * 0.5 * torch.pi)
69
+ return self.solve_euler(z, t_span=t_span, mu=mu, mask=mask, spks=spks, cond=cond), cache
70
+
71
+ def solve_euler(self, x, t_span, mu, mask, spks, cond, streaming=False):
72
+ """
73
+ Fixed euler solver for ODEs.
74
+ Args:
75
+ x (torch.Tensor): random noise
76
+ t_span (torch.Tensor): n_timesteps interpolated
77
+ shape: (n_timesteps + 1,)
78
+ mu (torch.Tensor): output of encoder
79
+ shape: (batch_size, n_feats, mel_timesteps)
80
+ mask (torch.Tensor): output_mask
81
+ shape: (batch_size, 1, mel_timesteps)
82
+ spks (torch.Tensor, optional): speaker ids. Defaults to None.
83
+ shape: (batch_size, spk_emb_dim)
84
+ cond: Not used but kept for future purposes
85
+ """
86
+ t, _, dt = t_span[0], t_span[-1], t_span[1] - t_span[0]
87
+ t = t.unsqueeze(dim=0)
88
+
89
+ # I am storing this because I can later plot it by putting a debugger here and saving it to a file
90
+ # Or in future might add like a return_all_steps flag
91
+ sol = []
92
+
93
+ # Do not use concat, it may cause memory format changed and trt infer with wrong results!
94
+ # NOTE when flow run in amp mode, x.dtype is float32, which cause nan in trt fp16 inference, so set dtype=spks.dtype
95
+ x_in = torch.zeros([2, 80, x.size(2)], device=x.device, dtype=spks.dtype)
96
+ mask_in = torch.zeros([2, 1, x.size(2)], device=x.device, dtype=spks.dtype)
97
+ mu_in = torch.zeros([2, 80, x.size(2)], device=x.device, dtype=spks.dtype)
98
+ t_in = torch.zeros([2], device=x.device, dtype=spks.dtype)
99
+ spks_in = torch.zeros([2, 80], device=x.device, dtype=spks.dtype)
100
+ cond_in = torch.zeros([2, 80, x.size(2)], device=x.device, dtype=spks.dtype)
101
+ for step in range(1, len(t_span)):
102
+ # Classifier-Free Guidance inference introduced in VoiceBox
103
+ x_in[:] = x
104
+ mask_in[:] = mask
105
+ mu_in[0] = mu
106
+ t_in[:] = t.unsqueeze(0)
107
+ spks_in[0] = spks
108
+ cond_in[0] = cond
109
+ dphi_dt = self.forward_estimator(
110
+ x_in, mask_in,
111
+ mu_in, t_in,
112
+ spks_in,
113
+ cond_in,
114
+ streaming
115
+ )
116
+ dphi_dt, cfg_dphi_dt = torch.split(dphi_dt, [x.size(0), x.size(0)], dim=0)
117
+ dphi_dt = ((1.0 + self.inference_cfg_rate) * dphi_dt - self.inference_cfg_rate * cfg_dphi_dt)
118
+ x = x + dt * dphi_dt
119
+ t = t + dt
120
+ sol.append(x)
121
+ if step < len(t_span) - 1:
122
+ dt = t_span[step + 1] - t
123
+
124
+ return sol[-1].float()
125
+
126
+ def forward_estimator(self, x, mask, mu, t, spks, cond, streaming=False):
127
+ if isinstance(self.estimator, torch.nn.Module):
128
+ return self.estimator(x, mask, mu, t, spks, cond, streaming=streaming)
129
+ else:
130
+ [estimator, stream], trt_engine = self.estimator.acquire_estimator()
131
+ # NOTE need to synchronize when switching stream
132
+ torch.cuda.current_stream().synchronize()
133
+ with stream:
134
+ estimator.set_input_shape('x', (2, 80, x.size(2)))
135
+ estimator.set_input_shape('mask', (2, 1, x.size(2)))
136
+ estimator.set_input_shape('mu', (2, 80, x.size(2)))
137
+ estimator.set_input_shape('t', (2,))
138
+ estimator.set_input_shape('spks', (2, 80))
139
+ estimator.set_input_shape('cond', (2, 80, x.size(2)))
140
+ data_ptrs = [x.contiguous().data_ptr(),
141
+ mask.contiguous().data_ptr(),
142
+ mu.contiguous().data_ptr(),
143
+ t.contiguous().data_ptr(),
144
+ spks.contiguous().data_ptr(),
145
+ cond.contiguous().data_ptr(),
146
+ x.data_ptr()]
147
+ for i, j in enumerate(data_ptrs):
148
+ estimator.set_tensor_address(trt_engine.get_tensor_name(i), j)
149
+ # run trt engine
150
+ assert estimator.execute_async_v3(torch.cuda.current_stream().cuda_stream) is True
151
+ torch.cuda.current_stream().synchronize()
152
+ self.estimator.release_estimator(estimator, stream)
153
+ return x
154
+
155
+ def compute_loss(self, x1, mask, mu, spks=None, cond=None, streaming=False):
156
+ """Computes diffusion loss
157
+
158
+ Args:
159
+ x1 (torch.Tensor): Target
160
+ shape: (batch_size, n_feats, mel_timesteps)
161
+ mask (torch.Tensor): target mask
162
+ shape: (batch_size, 1, mel_timesteps)
163
+ mu (torch.Tensor): output of encoder
164
+ shape: (batch_size, n_feats, mel_timesteps)
165
+ spks (torch.Tensor, optional): speaker embedding. Defaults to None.
166
+ shape: (batch_size, spk_emb_dim)
167
+
168
+ Returns:
169
+ loss: conditional flow matching loss
170
+ y: conditional flow
171
+ shape: (batch_size, n_feats, mel_timesteps)
172
+ """
173
+ b, _, t = mu.shape
174
+
175
+ # random timestep
176
+ t = torch.rand([b, 1, 1], device=mu.device, dtype=mu.dtype)
177
+
178
+ # sample noise p(x_0)
179
+ z = torch.randn_like(x1)
180
+
181
+ y = (1 - (1 - self.sigma_min) * t) * z + t * x1
182
+ u = x1 - (1 - self.sigma_min) * z
183
+
184
+ # during training, we randomly drop condition to trade off mode coverage and sample fidelity
185
+ if self.training_cfg_rate > 0:
186
+ cfg_mask = torch.rand(b, device=x1.device) > self.training_cfg_rate
187
+ mu = mu * cfg_mask.view(-1, 1, 1)
188
+ spks = spks * cfg_mask.view(-1, 1)
189
+ cond = cond * cfg_mask.view(-1, 1, 1)
190
+
191
+ pred = self.estimator(y, mask, mu, t.squeeze(), spks, cond, streaming=streaming)
192
+ loss = F.mse_loss(pred * mask, u * mask, reduction="sum") / (torch.sum(mask) * u.shape[1])
193
+ return loss, y
194
+
195
+
196
+ class CausalConditionalCFM(ConditionalCFM):
197
+ def __init__(self, in_channels, cfm_params, n_spks=1, spk_emb_dim=64, estimator: torch.nn.Module = None):
198
+ super().__init__(in_channels, cfm_params, n_spks, spk_emb_dim, estimator)
199
+ set_all_random_seed(0)
200
+ self.rand_noise = torch.randn([1, 80, 50 * 300])
201
+
202
+ @torch.inference_mode()
203
+ def forward(self, mu, mask, n_timesteps, temperature=1.0, spks=None, cond=None, streaming=False):
204
+ """Forward diffusion
205
+
206
+ Args:
207
+ mu (torch.Tensor): output of encoder
208
+ shape: (batch_size, n_feats, mel_timesteps)
209
+ mask (torch.Tensor): output_mask
210
+ shape: (batch_size, 1, mel_timesteps)
211
+ n_timesteps (int): number of diffusion steps
212
+ temperature (float, optional): temperature for scaling noise. Defaults to 1.0.
213
+ spks (torch.Tensor, optional): speaker ids. Defaults to None.
214
+ shape: (batch_size, spk_emb_dim)
215
+ cond: Not used but kept for future purposes
216
+
217
+ Returns:
218
+ sample: generated mel-spectrogram
219
+ shape: (batch_size, n_feats, mel_timesteps)
220
+ """
221
+
222
+ z = self.rand_noise[:, :, :mu.size(2)].to(mu.device).to(mu.dtype) * temperature
223
+ # fix prompt and overlap part mu and z
224
+ t_span = torch.linspace(0, 1, n_timesteps + 1, device=mu.device, dtype=mu.dtype)
225
+ if self.t_scheduler == 'cosine':
226
+ t_span = 1 - torch.cos(t_span * 0.5 * torch.pi)
227
+ return self.solve_euler(z, t_span=t_span, mu=mu, mask=mask, spks=spks, cond=cond, streaming=streaming), None
third_party/CosyVoice/cosyvoice/flow/length_regulator.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Zhihao Du)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from typing import Tuple
15
+ import torch.nn as nn
16
+ import torch
17
+ from torch.nn import functional as F
18
+ from cosyvoice.utils.mask import make_pad_mask
19
+
20
+
21
+ class InterpolateRegulator(nn.Module):
22
+ def __init__(
23
+ self,
24
+ channels: int,
25
+ sampling_ratios: Tuple,
26
+ out_channels: int = None,
27
+ groups: int = 1,
28
+ ):
29
+ super().__init__()
30
+ self.sampling_ratios = sampling_ratios
31
+ out_channels = out_channels or channels
32
+ model = nn.ModuleList([])
33
+ if len(sampling_ratios) > 0:
34
+ for _ in sampling_ratios:
35
+ module = nn.Conv1d(channels, channels, 3, 1, 1)
36
+ norm = nn.GroupNorm(groups, channels)
37
+ act = nn.Mish()
38
+ model.extend([module, norm, act])
39
+ model.append(
40
+ nn.Conv1d(channels, out_channels, 1, 1)
41
+ )
42
+ self.model = nn.Sequential(*model)
43
+
44
+ def forward(self, x, ylens=None):
45
+ # x in (B, T, D)
46
+ mask = (~make_pad_mask(ylens)).to(x).unsqueeze(-1)
47
+ x = F.interpolate(x.transpose(1, 2).contiguous(), size=ylens.max(), mode='linear')
48
+ out = self.model(x).transpose(1, 2).contiguous()
49
+ olens = ylens
50
+ return out * mask, olens
51
+
52
+ def inference(self, x1, x2, mel_len1, mel_len2, input_frame_rate=50):
53
+ # in inference mode, interploate prompt token and token(head/mid/tail) seprately, so we can get a clear separation point of mel
54
+ # NOTE 20 corresponds to token_overlap_len in cosyvoice/cli/model.py
55
+ # x in (B, T, D)
56
+ if x2.shape[1] > 40:
57
+ x2_head = F.interpolate(x2[:, :20].transpose(1, 2).contiguous(), size=int(20 / input_frame_rate * 22050 / 256), mode='linear')
58
+ x2_mid = F.interpolate(x2[:, 20:-20].transpose(1, 2).contiguous(), size=mel_len2 - int(20 / input_frame_rate * 22050 / 256) * 2,
59
+ mode='linear')
60
+ x2_tail = F.interpolate(x2[:, -20:].transpose(1, 2).contiguous(), size=int(20 / input_frame_rate * 22050 / 256), mode='linear')
61
+ x2 = torch.concat([x2_head, x2_mid, x2_tail], dim=2)
62
+ else:
63
+ x2 = F.interpolate(x2.transpose(1, 2).contiguous(), size=mel_len2, mode='linear')
64
+ if x1.shape[1] != 0:
65
+ x1 = F.interpolate(x1.transpose(1, 2).contiguous(), size=mel_len1, mode='linear')
66
+ x = torch.concat([x1, x2], dim=2)
67
+ else:
68
+ x = x2
69
+ out = self.model(x).transpose(1, 2).contiguous()
70
+ return out, mel_len1 + mel_len2
third_party/CosyVoice/cosyvoice/hifigan/__pycache__/discriminator.cpython-312.pyc ADDED
Binary file (13.8 kB). View file
 
third_party/CosyVoice/cosyvoice/hifigan/__pycache__/f0_predictor.cpython-312.pyc ADDED
Binary file (5.04 kB). View file
 
third_party/CosyVoice/cosyvoice/hifigan/__pycache__/generator.cpython-312.pyc ADDED
Binary file (38.7 kB). View file
 
third_party/CosyVoice/cosyvoice/hifigan/__pycache__/hifigan.cpython-312.pyc ADDED
Binary file (4.2 kB). View file
 
third_party/CosyVoice/cosyvoice/hifigan/discriminator.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ try:
5
+ from torch.nn.utils.parametrizations import weight_norm, spectral_norm
6
+ except ImportError:
7
+ from torch.nn.utils import weight_norm, spectral_norm
8
+ from typing import List, Optional, Tuple
9
+ from einops import rearrange
10
+ from torchaudio.transforms import Spectrogram
11
+
12
+ LRELU_SLOPE = 0.1
13
+
14
+
15
+ class MultipleDiscriminator(nn.Module):
16
+ def __init__(
17
+ self, mpd: nn.Module, mrd: nn.Module
18
+ ):
19
+ super().__init__()
20
+ self.mpd = mpd
21
+ self.mrd = mrd
22
+
23
+ def forward(self, y: torch.Tensor, y_hat: torch.Tensor):
24
+ y_d_rs, y_d_gs, fmap_rs, fmap_gs = [], [], [], []
25
+ this_y_d_rs, this_y_d_gs, this_fmap_rs, this_fmap_gs = self.mpd(y.unsqueeze(dim=1), y_hat.unsqueeze(dim=1))
26
+ y_d_rs += this_y_d_rs
27
+ y_d_gs += this_y_d_gs
28
+ fmap_rs += this_fmap_rs
29
+ fmap_gs += this_fmap_gs
30
+ this_y_d_rs, this_y_d_gs, this_fmap_rs, this_fmap_gs = self.mrd(y, y_hat)
31
+ y_d_rs += this_y_d_rs
32
+ y_d_gs += this_y_d_gs
33
+ fmap_rs += this_fmap_rs
34
+ fmap_gs += this_fmap_gs
35
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
36
+
37
+
38
+ class MultiResolutionDiscriminator(nn.Module):
39
+ def __init__(
40
+ self,
41
+ fft_sizes: Tuple[int, ...] = (2048, 1024, 512),
42
+ num_embeddings: Optional[int] = None,
43
+ ):
44
+ """
45
+ Multi-Resolution Discriminator module adapted from https://github.com/descriptinc/descript-audio-codec.
46
+ Additionally, it allows incorporating conditional information with a learned embeddings table.
47
+
48
+ Args:
49
+ fft_sizes (tuple[int]): Tuple of window lengths for FFT. Defaults to (2048, 1024, 512).
50
+ num_embeddings (int, optional): Number of embeddings. None means non-conditional discriminator.
51
+ Defaults to None.
52
+ """
53
+
54
+ super().__init__()
55
+ self.discriminators = nn.ModuleList(
56
+ [DiscriminatorR(window_length=w, num_embeddings=num_embeddings) for w in fft_sizes]
57
+ )
58
+
59
+ def forward(
60
+ self, y: torch.Tensor, y_hat: torch.Tensor, bandwidth_id: torch.Tensor = None
61
+ ) -> Tuple[List[torch.Tensor], List[torch.Tensor], List[List[torch.Tensor]], List[List[torch.Tensor]]]:
62
+ y_d_rs = []
63
+ y_d_gs = []
64
+ fmap_rs = []
65
+ fmap_gs = []
66
+
67
+ for d in self.discriminators:
68
+ y_d_r, fmap_r = d(x=y, cond_embedding_id=bandwidth_id)
69
+ y_d_g, fmap_g = d(x=y_hat, cond_embedding_id=bandwidth_id)
70
+ y_d_rs.append(y_d_r)
71
+ fmap_rs.append(fmap_r)
72
+ y_d_gs.append(y_d_g)
73
+ fmap_gs.append(fmap_g)
74
+
75
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
76
+
77
+
78
+ class DiscriminatorR(nn.Module):
79
+ def __init__(
80
+ self,
81
+ window_length: int,
82
+ num_embeddings: Optional[int] = None,
83
+ channels: int = 32,
84
+ hop_factor: float = 0.25,
85
+ bands: Tuple[Tuple[float, float], ...] = ((0.0, 0.1), (0.1, 0.25), (0.25, 0.5), (0.5, 0.75), (0.75, 1.0)),
86
+ ):
87
+ super().__init__()
88
+ self.window_length = window_length
89
+ self.hop_factor = hop_factor
90
+ self.spec_fn = Spectrogram(
91
+ n_fft=window_length, hop_length=int(window_length * hop_factor), win_length=window_length, power=None
92
+ )
93
+ n_fft = window_length // 2 + 1
94
+ bands = [(int(b[0] * n_fft), int(b[1] * n_fft)) for b in bands]
95
+ self.bands = bands
96
+ convs = lambda: nn.ModuleList(
97
+ [
98
+ weight_norm(nn.Conv2d(2, channels, (3, 9), (1, 1), padding=(1, 4))),
99
+ weight_norm(nn.Conv2d(channels, channels, (3, 9), (1, 2), padding=(1, 4))),
100
+ weight_norm(nn.Conv2d(channels, channels, (3, 9), (1, 2), padding=(1, 4))),
101
+ weight_norm(nn.Conv2d(channels, channels, (3, 9), (1, 2), padding=(1, 4))),
102
+ weight_norm(nn.Conv2d(channels, channels, (3, 3), (1, 1), padding=(1, 1))),
103
+ ]
104
+ )
105
+ self.band_convs = nn.ModuleList([convs() for _ in range(len(self.bands))])
106
+
107
+ if num_embeddings is not None:
108
+ self.emb = torch.nn.Embedding(num_embeddings=num_embeddings, embedding_dim=channels)
109
+ torch.nn.init.zeros_(self.emb.weight)
110
+
111
+ self.conv_post = weight_norm(nn.Conv2d(channels, 1, (3, 3), (1, 1), padding=(1, 1)))
112
+
113
+ def spectrogram(self, x):
114
+ # Remove DC offset
115
+ x = x - x.mean(dim=-1, keepdims=True)
116
+ # Peak normalize the volume of input audio
117
+ x = 0.8 * x / (x.abs().max(dim=-1, keepdim=True)[0] + 1e-9)
118
+ x = self.spec_fn(x)
119
+ x = torch.view_as_real(x)
120
+ x = rearrange(x, "b f t c -> b c t f")
121
+ # Split into bands
122
+ x_bands = [x[..., b[0]: b[1]] for b in self.bands]
123
+ return x_bands
124
+
125
+ def forward(self, x: torch.Tensor, cond_embedding_id: torch.Tensor = None):
126
+ x_bands = self.spectrogram(x)
127
+ fmap = []
128
+ x = []
129
+ for band, stack in zip(x_bands, self.band_convs):
130
+ for i, layer in enumerate(stack):
131
+ band = layer(band)
132
+ band = torch.nn.functional.leaky_relu(band, 0.1)
133
+ if i > 0:
134
+ fmap.append(band)
135
+ x.append(band)
136
+ x = torch.cat(x, dim=-1)
137
+ if cond_embedding_id is not None:
138
+ emb = self.emb(cond_embedding_id)
139
+ h = (emb.view(1, -1, 1, 1) * x).sum(dim=1, keepdims=True)
140
+ else:
141
+ h = 0
142
+ x = self.conv_post(x)
143
+ fmap.append(x)
144
+ x += h
145
+
146
+ return x, fmap
147
+
148
+
149
+ class MultiResSpecDiscriminator(torch.nn.Module):
150
+
151
+ def __init__(self,
152
+ fft_sizes=[1024, 2048, 512],
153
+ hop_sizes=[120, 240, 50],
154
+ win_lengths=[600, 1200, 240],
155
+ window="hann_window"):
156
+
157
+ super(MultiResSpecDiscriminator, self).__init__()
158
+ self.discriminators = nn.ModuleList([
159
+ SpecDiscriminator(fft_sizes[0], hop_sizes[0], win_lengths[0], window),
160
+ SpecDiscriminator(fft_sizes[1], hop_sizes[1], win_lengths[1], window),
161
+ SpecDiscriminator(fft_sizes[2], hop_sizes[2], win_lengths[2], window)])
162
+
163
+ def forward(self, y, y_hat):
164
+ y_d_rs = []
165
+ y_d_gs = []
166
+ fmap_rs = []
167
+ fmap_gs = []
168
+ for _, d in enumerate(self.discriminators):
169
+ y_d_r, fmap_r = d(y)
170
+ y_d_g, fmap_g = d(y_hat)
171
+ y_d_rs.append(y_d_r)
172
+ fmap_rs.append(fmap_r)
173
+ y_d_gs.append(y_d_g)
174
+ fmap_gs.append(fmap_g)
175
+
176
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
177
+
178
+
179
+ def stft(x, fft_size, hop_size, win_length, window):
180
+ """Perform STFT and convert to magnitude spectrogram.
181
+ Args:
182
+ x (Tensor): Input signal tensor (B, T).
183
+ fft_size (int): FFT size.
184
+ hop_size (int): Hop size.
185
+ win_length (int): Window length.
186
+ window (str): Window function type.
187
+ Returns:
188
+ Tensor: Magnitude spectrogram (B, #frames, fft_size // 2 + 1).
189
+ """
190
+ x_stft = torch.stft(x, fft_size, hop_size, win_length, window, return_complex=True)
191
+
192
+ # NOTE(kan-bayashi): clamp is needed to avoid nan or inf
193
+ return torch.abs(x_stft).transpose(2, 1)
194
+
195
+
196
+ class SpecDiscriminator(nn.Module):
197
+ """docstring for Discriminator."""
198
+
199
+ def __init__(self, fft_size=1024, shift_size=120, win_length=600, window="hann_window", use_spectral_norm=False):
200
+ super(SpecDiscriminator, self).__init__()
201
+ norm_f = weight_norm if use_spectral_norm is False else spectral_norm
202
+ self.fft_size = fft_size
203
+ self.shift_size = shift_size
204
+ self.win_length = win_length
205
+ self.window = getattr(torch, window)(win_length)
206
+ self.discriminators = nn.ModuleList([
207
+ norm_f(nn.Conv2d(1, 32, kernel_size=(3, 9), padding=(1, 4))),
208
+ norm_f(nn.Conv2d(32, 32, kernel_size=(3, 9), stride=(1, 2), padding=(1, 4))),
209
+ norm_f(nn.Conv2d(32, 32, kernel_size=(3, 9), stride=(1, 2), padding=(1, 4))),
210
+ norm_f(nn.Conv2d(32, 32, kernel_size=(3, 9), stride=(1, 2), padding=(1, 4))),
211
+ norm_f(nn.Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))),
212
+ ])
213
+
214
+ self.out = norm_f(nn.Conv2d(32, 1, 3, 1, 1))
215
+
216
+ def forward(self, y):
217
+
218
+ fmap = []
219
+ y = y.squeeze(1)
220
+ y = stft(y, self.fft_size, self.shift_size, self.win_length, self.window.to(y.device))
221
+ y = y.unsqueeze(1)
222
+ for _, d in enumerate(self.discriminators):
223
+ y = d(y)
224
+ y = F.leaky_relu(y, LRELU_SLOPE)
225
+ fmap.append(y)
226
+
227
+ y = self.out(y)
228
+ fmap.append(y)
229
+
230
+ return torch.flatten(y, 1, -1), fmap
third_party/CosyVoice/cosyvoice/hifigan/f0_predictor.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Kai Hu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import torch
15
+ import torch.nn as nn
16
+ try:
17
+ from torch.nn.utils.parametrizations import weight_norm
18
+ except ImportError:
19
+ from torch.nn.utils import weight_norm
20
+ from cosyvoice.transformer.convolution import CausalConv1d
21
+
22
+
23
+ class ConvRNNF0Predictor(nn.Module):
24
+ def __init__(self,
25
+ num_class: int = 1,
26
+ in_channels: int = 80,
27
+ cond_channels: int = 512
28
+ ):
29
+ super().__init__()
30
+
31
+ self.num_class = num_class
32
+ self.condnet = nn.Sequential(
33
+ weight_norm(
34
+ nn.Conv1d(in_channels, cond_channels, kernel_size=3, padding=1)
35
+ ),
36
+ nn.ELU(),
37
+ weight_norm(
38
+ nn.Conv1d(cond_channels, cond_channels, kernel_size=3, padding=1)
39
+ ),
40
+ nn.ELU(),
41
+ weight_norm(
42
+ nn.Conv1d(cond_channels, cond_channels, kernel_size=3, padding=1)
43
+ ),
44
+ nn.ELU(),
45
+ weight_norm(
46
+ nn.Conv1d(cond_channels, cond_channels, kernel_size=3, padding=1)
47
+ ),
48
+ nn.ELU(),
49
+ weight_norm(
50
+ nn.Conv1d(cond_channels, cond_channels, kernel_size=3, padding=1)
51
+ ),
52
+ nn.ELU(),
53
+ )
54
+ self.classifier = nn.Linear(in_features=cond_channels, out_features=self.num_class)
55
+
56
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
57
+ x = self.condnet(x)
58
+ x = x.transpose(1, 2)
59
+ return torch.abs(self.classifier(x).squeeze(-1))
60
+
61
+
62
+ class CausalConvRNNF0Predictor(nn.Module):
63
+ def __init__(self,
64
+ num_class: int = 1,
65
+ in_channels: int = 80,
66
+ cond_channels: int = 512
67
+ ):
68
+ super().__init__()
69
+
70
+ self.num_class = num_class
71
+ self.condnet = nn.Sequential(
72
+ weight_norm(
73
+ CausalConv1d(in_channels, cond_channels, kernel_size=4, causal_type='right')
74
+ ),
75
+ nn.ELU(),
76
+ weight_norm(
77
+ CausalConv1d(cond_channels, cond_channels, kernel_size=3, causal_type='left')
78
+ ),
79
+ nn.ELU(),
80
+ weight_norm(
81
+ CausalConv1d(cond_channels, cond_channels, kernel_size=3, causal_type='left')
82
+ ),
83
+ nn.ELU(),
84
+ weight_norm(
85
+ CausalConv1d(cond_channels, cond_channels, kernel_size=3, causal_type='left')
86
+ ),
87
+ nn.ELU(),
88
+ weight_norm(
89
+ CausalConv1d(cond_channels, cond_channels, kernel_size=3, causal_type='left')
90
+ ),
91
+ nn.ELU(),
92
+ )
93
+ self.classifier = nn.Linear(in_features=cond_channels, out_features=self.num_class)
94
+
95
+ def forward(self, x: torch.Tensor, finalize: bool = True) -> torch.Tensor:
96
+ if finalize is True:
97
+ x = self.condnet[0](x)
98
+ else:
99
+ x = self.condnet[0](x[:, :, :-self.condnet[0].causal_padding], x[:, :, -self.condnet[0].causal_padding:])
100
+ for i in range(1, len(self.condnet)):
101
+ x = self.condnet[i](x)
102
+ x = x.transpose(1, 2)
103
+ return torch.abs(self.classifier(x).squeeze(-1))
third_party/CosyVoice/cosyvoice/hifigan/generator.py ADDED
@@ -0,0 +1,746 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Kai Hu)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """HIFI-GAN"""
16
+
17
+ from typing import Dict, Optional, List
18
+ import numpy as np
19
+ from scipy.signal import get_window
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+ from torch.nn import Conv1d
24
+ from torch.nn import ConvTranspose1d
25
+ from torch.nn.utils import remove_weight_norm
26
+ try:
27
+ from torch.nn.utils.parametrizations import weight_norm
28
+ except ImportError:
29
+ from torch.nn.utils import weight_norm
30
+ from torch.distributions.uniform import Uniform
31
+ from cosyvoice.transformer.convolution import CausalConv1d, CausalConv1dDownSample, CausalConv1dUpsample
32
+ from cosyvoice.transformer.activation import Snake
33
+ from cosyvoice.utils.common import get_padding
34
+ from cosyvoice.utils.common import init_weights
35
+
36
+
37
+ """hifigan based generator implementation.
38
+
39
+ This code is modified from https://github.com/jik876/hifi-gan
40
+ ,https://github.com/kan-bayashi/ParallelWaveGAN and
41
+ https://github.com/NVIDIA/BigVGAN
42
+
43
+ """
44
+
45
+
46
+ class ResBlock(torch.nn.Module):
47
+ """Residual block module in HiFiGAN/BigVGAN."""
48
+ def __init__(
49
+ self,
50
+ channels: int = 512,
51
+ kernel_size: int = 3,
52
+ dilations: List[int] = [1, 3, 5],
53
+ causal: bool = False,
54
+ ):
55
+ super(ResBlock, self).__init__()
56
+ self.causal = causal
57
+ self.convs1 = nn.ModuleList()
58
+ self.convs2 = nn.ModuleList()
59
+
60
+ for dilation in dilations:
61
+ self.convs1.append(
62
+ weight_norm(
63
+ Conv1d(
64
+ channels,
65
+ channels,
66
+ kernel_size,
67
+ 1,
68
+ dilation=dilation,
69
+ padding=get_padding(kernel_size, dilation)) if causal is False else
70
+ CausalConv1d(
71
+ channels,
72
+ channels,
73
+ kernel_size,
74
+ 1,
75
+ dilation=dilation,
76
+ causal_type='left'
77
+ )
78
+ )
79
+ )
80
+ self.convs2.append(
81
+ weight_norm(
82
+ Conv1d(
83
+ channels,
84
+ channels,
85
+ kernel_size,
86
+ 1,
87
+ dilation=1,
88
+ padding=get_padding(kernel_size, 1)) if causal is False else
89
+ CausalConv1d(
90
+ channels,
91
+ channels,
92
+ kernel_size,
93
+ 1,
94
+ dilation=1,
95
+ causal_type='left'
96
+ )
97
+ )
98
+ )
99
+ self.convs1.apply(init_weights)
100
+ self.convs2.apply(init_weights)
101
+ self.activations1 = nn.ModuleList([
102
+ Snake(channels, alpha_logscale=False)
103
+ for _ in range(len(self.convs1))
104
+ ])
105
+ self.activations2 = nn.ModuleList([
106
+ Snake(channels, alpha_logscale=False)
107
+ for _ in range(len(self.convs2))
108
+ ])
109
+
110
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
111
+ for idx in range(len(self.convs1)):
112
+ xt = self.activations1[idx](x)
113
+ xt = self.convs1[idx](xt)
114
+ xt = self.activations2[idx](xt)
115
+ xt = self.convs2[idx](xt)
116
+ x = xt + x
117
+ return x
118
+
119
+ def remove_weight_norm(self):
120
+ for idx in range(len(self.convs1)):
121
+ remove_weight_norm(self.convs1[idx])
122
+ remove_weight_norm(self.convs2[idx])
123
+
124
+
125
+ class SineGen(torch.nn.Module):
126
+ """ Definition of sine generator
127
+ SineGen(samp_rate, harmonic_num = 0,
128
+ sine_amp = 0.1, noise_std = 0.003,
129
+ voiced_threshold = 0,
130
+ flag_for_pulse=False)
131
+ samp_rate: sampling rate in Hz
132
+ harmonic_num: number of harmonic overtones (default 0)
133
+ sine_amp: amplitude of sine-wavefrom (default 0.1)
134
+ noise_std: std of Gaussian noise (default 0.003)
135
+ voiced_thoreshold: F0 threshold for U/V classification (default 0)
136
+ flag_for_pulse: this SinGen is used inside PulseGen (default False)
137
+ Note: when flag_for_pulse is True, the first time step of a voiced
138
+ segment is always sin(np.pi) or cos(0)
139
+ """
140
+
141
+ def __init__(self, samp_rate, harmonic_num=0,
142
+ sine_amp=0.1, noise_std=0.003,
143
+ voiced_threshold=0):
144
+ super(SineGen, self).__init__()
145
+ self.sine_amp = sine_amp
146
+ self.noise_std = noise_std
147
+ self.harmonic_num = harmonic_num
148
+ self.sampling_rate = samp_rate
149
+ self.voiced_threshold = voiced_threshold
150
+
151
+ def _f02uv(self, f0):
152
+ # generate uv signal
153
+ uv = (f0 > self.voiced_threshold).type(torch.float32)
154
+ return uv
155
+
156
+ @torch.no_grad()
157
+ def forward(self, f0):
158
+ """ sine_tensor, uv = forward(f0)
159
+ input F0: tensor(batchsize=1, dim=1, length)
160
+ f0 for unvoiced steps should be 0
161
+ output sine_tensor: tensor(batchsize=1, length, dim)
162
+ output uv: tensor(batchsize=1, length, 1)
163
+ """
164
+ f0 = f0.transpose(1, 2)
165
+ F_mat = torch.zeros((f0.size(0), self.harmonic_num + 1, f0.size(-1))).to(f0.device)
166
+ for i in range(self.harmonic_num + 1):
167
+ F_mat[:, i: i + 1, :] = f0 * (i + 1) / self.sampling_rate
168
+
169
+ theta_mat = 2 * np.pi * (torch.cumsum(F_mat, dim=-1) % 1)
170
+ u_dist = Uniform(low=-np.pi, high=np.pi)
171
+ phase_vec = u_dist.sample(sample_shape=(f0.size(0), self.harmonic_num + 1, 1)).to(F_mat.device)
172
+ phase_vec[:, 0, :] = 0
173
+
174
+ # generate sine waveforms
175
+ sine_waves = self.sine_amp * torch.sin(theta_mat + phase_vec)
176
+
177
+ # generate uv signal
178
+ uv = self._f02uv(f0)
179
+
180
+ # noise: for unvoiced should be similar to sine_amp
181
+ # std = self.sine_amp/3 -> max value ~ self.sine_amp
182
+ # . for voiced regions is self.noise_std
183
+ noise_amp = uv * self.noise_std + (1 - uv) * self.sine_amp / 3
184
+ noise = noise_amp * torch.randn_like(sine_waves)
185
+
186
+ # first: set the unvoiced part to 0 by uv
187
+ # then: additive noise
188
+ sine_waves = sine_waves * uv + noise
189
+ return sine_waves.transpose(1, 2), uv.transpose(1, 2), noise
190
+
191
+
192
+ class SineGen2(torch.nn.Module):
193
+ """ Definition of sine generator
194
+ SineGen(samp_rate, harmonic_num = 0,
195
+ sine_amp = 0.1, noise_std = 0.003,
196
+ voiced_threshold = 0,
197
+ flag_for_pulse=False)
198
+ samp_rate: sampling rate in Hz
199
+ harmonic_num: number of harmonic overtones (default 0)
200
+ sine_amp: amplitude of sine-wavefrom (default 0.1)
201
+ noise_std: std of Gaussian noise (default 0.003)
202
+ voiced_thoreshold: F0 threshold for U/V classification (default 0)
203
+ flag_for_pulse: this SinGen is used inside PulseGen (default False)
204
+ Note: when flag_for_pulse is True, the first time step of a voiced
205
+ segment is always sin(np.pi) or cos(0)
206
+ """
207
+
208
+ def __init__(self, samp_rate, upsample_scale, harmonic_num=0,
209
+ sine_amp=0.1, noise_std=0.003,
210
+ voiced_threshold=0,
211
+ flag_for_pulse=False,
212
+ causal=False):
213
+ super(SineGen2, self).__init__()
214
+ self.sine_amp = sine_amp
215
+ self.noise_std = noise_std
216
+ self.harmonic_num = harmonic_num
217
+ self.dim = self.harmonic_num + 1
218
+ self.sampling_rate = samp_rate
219
+ self.voiced_threshold = voiced_threshold
220
+ self.flag_for_pulse = flag_for_pulse
221
+ self.upsample_scale = upsample_scale
222
+ self.causal = causal
223
+ if causal is True:
224
+ self.rand_ini = torch.rand(1, 9)
225
+ self.rand_ini[:, 0] = 0
226
+ self.sine_waves = torch.rand(1, 300 * 24000, 9)
227
+
228
+ def _f02uv(self, f0):
229
+ # generate uv signal
230
+ uv = (f0 > self.voiced_threshold).type(torch.float32)
231
+ return uv
232
+
233
+ def _f02sine(self, f0_values):
234
+ """ f0_values: (batchsize, length, dim)
235
+ where dim indicates fundamental tone and overtones
236
+ """
237
+ # convert to F0 in rad. The interger part n can be ignored
238
+ # because 2 * np.pi * n doesn't affect phase
239
+ rad_values = (f0_values / self.sampling_rate) % 1
240
+
241
+ # initial phase noise (no noise for fundamental component)
242
+ if self.training is False and self.causal is True:
243
+ rad_values[:, 0, :] = rad_values[:, 0, :] + self.rand_ini.to(rad_values.device)
244
+ else:
245
+ rand_ini = torch.rand(f0_values.shape[0], f0_values.shape[2], device=f0_values.device)
246
+ rand_ini[:, 0] = 0
247
+ rad_values[:, 0, :] = rad_values[:, 0, :] + rand_ini
248
+
249
+ # instantanouse phase sine[t] = sin(2*pi \sum_i=1 ^{t} rad)
250
+ if not self.flag_for_pulse:
251
+ rad_values = torch.nn.functional.interpolate(rad_values.transpose(1, 2),
252
+ scale_factor=1 / self.upsample_scale,
253
+ mode="linear").transpose(1, 2)
254
+
255
+ phase = torch.cumsum(rad_values, dim=1) * 2 * np.pi
256
+ phase = torch.nn.functional.interpolate(phase.transpose(1, 2) * self.upsample_scale,
257
+ scale_factor=self.upsample_scale, mode="nearest" if self.causal is True else 'linear').transpose(1, 2)
258
+ sines = torch.sin(phase)
259
+ else:
260
+ # If necessary, make sure that the first time step of every
261
+ # voiced segments is sin(pi) or cos(0)
262
+ # This is used for pulse-train generation
263
+
264
+ # identify the last time step in unvoiced segments
265
+ uv = self._f02uv(f0_values)
266
+ uv_1 = torch.roll(uv, shifts=-1, dims=1)
267
+ uv_1[:, -1, :] = 1
268
+ u_loc = (uv < 1) * (uv_1 > 0)
269
+
270
+ # get the instantanouse phase
271
+ tmp_cumsum = torch.cumsum(rad_values, dim=1)
272
+ # different batch needs to be processed differently
273
+ for idx in range(f0_values.shape[0]):
274
+ temp_sum = tmp_cumsum[idx, u_loc[idx, :, 0], :]
275
+ temp_sum[1:, :] = temp_sum[1:, :] - temp_sum[0:-1, :]
276
+ # stores the accumulation of i.phase within
277
+ # each voiced segments
278
+ tmp_cumsum[idx, :, :] = 0
279
+ tmp_cumsum[idx, u_loc[idx, :, 0], :] = temp_sum
280
+
281
+ # rad_values - tmp_cumsum: remove the accumulation of i.phase
282
+ # within the previous voiced segment.
283
+ i_phase = torch.cumsum(rad_values - tmp_cumsum, dim=1)
284
+
285
+ # get the sines
286
+ sines = torch.cos(i_phase * 2 * np.pi)
287
+ return sines
288
+
289
+ def forward(self, f0):
290
+ """ sine_tensor, uv = forward(f0)
291
+ input F0: tensor(batchsize=1, length, dim=1)
292
+ f0 for unvoiced steps should be 0
293
+ output sine_tensor: tensor(batchsize=1, length, dim)
294
+ output uv: tensor(batchsize=1, length, 1)
295
+ """
296
+ # fundamental component
297
+ fn = torch.multiply(f0, torch.FloatTensor([[range(1, self.harmonic_num + 2)]]).to(f0.device))
298
+
299
+ # generate sine waveforms
300
+ sine_waves = self._f02sine(fn) * self.sine_amp
301
+
302
+ # generate uv signal
303
+ uv = self._f02uv(f0)
304
+
305
+ # noise: for unvoiced should be similar to sine_amp
306
+ # std = self.sine_amp/3 -> max value ~ self.sine_amp
307
+ # . for voiced regions is self.noise_std
308
+ noise_amp = uv * self.noise_std + (1 - uv) * self.sine_amp / 3
309
+ if self.training is False and self.causal is True:
310
+ noise = noise_amp * self.sine_waves[:, :sine_waves.shape[1]].to(sine_waves.device)
311
+ else:
312
+ noise = noise_amp * torch.randn_like(sine_waves)
313
+
314
+ # first: set the unvoiced part to 0 by uv
315
+ # then: additive noise
316
+ sine_waves = sine_waves * uv + noise
317
+ return sine_waves, uv, noise
318
+
319
+
320
+ class SourceModuleHnNSF(torch.nn.Module):
321
+ """ SourceModule for hn-nsf
322
+ SourceModule(sampling_rate, harmonic_num=0, sine_amp=0.1,
323
+ add_noise_std=0.003, voiced_threshod=0)
324
+ sampling_rate: sampling_rate in Hz
325
+ harmonic_num: number of harmonic above F0 (default: 0)
326
+ sine_amp: amplitude of sine source signal (default: 0.1)
327
+ add_noise_std: std of additive Gaussian noise (default: 0.003)
328
+ note that amplitude of noise in unvoiced is decided
329
+ by sine_amp
330
+ voiced_threshold: threhold to set U/V given F0 (default: 0)
331
+ Sine_source, noise_source = SourceModuleHnNSF(F0_sampled)
332
+ F0_sampled (batchsize, length, 1)
333
+ Sine_source (batchsize, length, 1)
334
+ noise_source (batchsize, length 1)
335
+ uv (batchsize, length, 1)
336
+ """
337
+
338
+ def __init__(self, sampling_rate, upsample_scale, harmonic_num=0, sine_amp=0.1,
339
+ add_noise_std=0.003, voiced_threshod=0, sinegen_type='1', causal=False):
340
+ super(SourceModuleHnNSF, self).__init__()
341
+
342
+ self.sine_amp = sine_amp
343
+ self.noise_std = add_noise_std
344
+
345
+ # to produce sine waveforms
346
+ if sinegen_type == '1':
347
+ self.l_sin_gen = SineGen(sampling_rate, harmonic_num, sine_amp, add_noise_std, voiced_threshod)
348
+ else:
349
+ self.l_sin_gen = SineGen2(sampling_rate, upsample_scale, harmonic_num, sine_amp, add_noise_std, voiced_threshod, causal=causal)
350
+
351
+ # to merge source harmonics into a single excitation
352
+ self.l_linear = torch.nn.Linear(harmonic_num + 1, 1)
353
+ self.l_tanh = torch.nn.Tanh()
354
+ self.causal = causal
355
+ if causal is True:
356
+ self.uv = torch.rand(1, 300 * 24000, 1)
357
+
358
+ def forward(self, x):
359
+ """
360
+ Sine_source, noise_source = SourceModuleHnNSF(F0_sampled)
361
+ F0_sampled (batchsize, length, 1)
362
+ Sine_source (batchsize, length, 1)
363
+ noise_source (batchsize, length 1)
364
+ """
365
+ # source for harmonic branch
366
+ with torch.no_grad():
367
+ sine_wavs, uv, _ = self.l_sin_gen(x)
368
+ sine_merge = self.l_tanh(self.l_linear(sine_wavs))
369
+
370
+ # source for noise branch, in the same shape as uv
371
+ if self.training is False and self.causal is True:
372
+ noise = self.uv[:, :uv.shape[1]] * self.sine_amp / 3
373
+ else:
374
+ noise = torch.randn_like(uv) * self.sine_amp / 3
375
+ return sine_merge, noise, uv
376
+
377
+
378
+ class HiFTGenerator(nn.Module):
379
+ """
380
+ HiFTNet Generator: Neural Source Filter + ISTFTNet
381
+ https://arxiv.org/abs/2309.09493
382
+ """
383
+ def __init__(
384
+ self,
385
+ in_channels: int = 80,
386
+ base_channels: int = 512,
387
+ nb_harmonics: int = 8,
388
+ sampling_rate: int = 22050,
389
+ nsf_alpha: float = 0.1,
390
+ nsf_sigma: float = 0.003,
391
+ nsf_voiced_threshold: float = 10,
392
+ upsample_rates: List[int] = [8, 8],
393
+ upsample_kernel_sizes: List[int] = [16, 16],
394
+ istft_params: Dict[str, int] = {"n_fft": 16, "hop_len": 4},
395
+ resblock_kernel_sizes: List[int] = [3, 7, 11],
396
+ resblock_dilation_sizes: List[List[int]] = [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
397
+ source_resblock_kernel_sizes: List[int] = [7, 11],
398
+ source_resblock_dilation_sizes: List[List[int]] = [[1, 3, 5], [1, 3, 5]],
399
+ lrelu_slope: float = 0.1,
400
+ audio_limit: float = 0.99,
401
+ f0_predictor: torch.nn.Module = None,
402
+ ):
403
+ super(HiFTGenerator, self).__init__()
404
+
405
+ self.out_channels = 1
406
+ self.nb_harmonics = nb_harmonics
407
+ self.sampling_rate = sampling_rate
408
+ self.istft_params = istft_params
409
+ self.lrelu_slope = lrelu_slope
410
+ self.audio_limit = audio_limit
411
+
412
+ self.num_kernels = len(resblock_kernel_sizes)
413
+ self.num_upsamples = len(upsample_rates)
414
+ # NOTE in CosyVoice2, we use the original SineGen implementation
415
+ self.m_source = SourceModuleHnNSF(
416
+ sampling_rate=sampling_rate,
417
+ upsample_scale=np.prod(upsample_rates) * istft_params["hop_len"],
418
+ harmonic_num=nb_harmonics,
419
+ sine_amp=nsf_alpha,
420
+ add_noise_std=nsf_sigma,
421
+ voiced_threshod=nsf_voiced_threshold,
422
+ sinegen_type='1' if self.sampling_rate == 22050 else '2',
423
+ causal=False)
424
+ self.f0_upsamp = torch.nn.Upsample(scale_factor=np.prod(upsample_rates) * istft_params["hop_len"])
425
+
426
+ self.conv_pre = weight_norm(
427
+ Conv1d(in_channels, base_channels, 7, 1, padding=3)
428
+ )
429
+
430
+ # Up
431
+ self.ups = nn.ModuleList()
432
+ for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
433
+ self.ups.append(
434
+ weight_norm(
435
+ ConvTranspose1d(
436
+ base_channels // (2**i),
437
+ base_channels // (2**(i + 1)),
438
+ k,
439
+ u,
440
+ padding=(k - u) // 2,
441
+ )
442
+ )
443
+ )
444
+
445
+ # Down
446
+ self.source_downs = nn.ModuleList()
447
+ self.source_resblocks = nn.ModuleList()
448
+ downsample_rates = [1] + upsample_rates[::-1][:-1]
449
+ downsample_cum_rates = np.cumprod(downsample_rates)
450
+ for i, (u, k, d) in enumerate(zip(downsample_cum_rates[::-1], source_resblock_kernel_sizes, source_resblock_dilation_sizes)):
451
+ if u == 1:
452
+ self.source_downs.append(
453
+ Conv1d(istft_params["n_fft"] + 2, base_channels // (2 ** (i + 1)), 1, 1)
454
+ )
455
+ else:
456
+ self.source_downs.append(
457
+ Conv1d(istft_params["n_fft"] + 2, base_channels // (2 ** (i + 1)), u * 2, u, padding=(u // 2))
458
+ )
459
+
460
+ self.source_resblocks.append(
461
+ ResBlock(base_channels // (2 ** (i + 1)), k, d)
462
+ )
463
+
464
+ self.resblocks = nn.ModuleList()
465
+ for i in range(len(self.ups)):
466
+ ch = base_channels // (2**(i + 1))
467
+ for _, (k, d) in enumerate(zip(resblock_kernel_sizes, resblock_dilation_sizes)):
468
+ self.resblocks.append(ResBlock(ch, k, d))
469
+
470
+ self.conv_post = weight_norm(Conv1d(ch, istft_params["n_fft"] + 2, 7, 1, padding=3))
471
+ self.ups.apply(init_weights)
472
+ self.conv_post.apply(init_weights)
473
+ self.reflection_pad = nn.ReflectionPad1d((1, 0))
474
+ self.stft_window = torch.from_numpy(get_window("hann", istft_params["n_fft"], fftbins=True).astype(np.float32))
475
+ self.f0_predictor = f0_predictor
476
+
477
+ def remove_weight_norm(self):
478
+ print('Removing weight norm...')
479
+ for l in self.ups:
480
+ remove_weight_norm(l)
481
+ for l in self.resblocks:
482
+ l.remove_weight_norm()
483
+ remove_weight_norm(self.conv_pre)
484
+ remove_weight_norm(self.conv_post)
485
+ self.m_source.remove_weight_norm()
486
+ for l in self.source_downs:
487
+ remove_weight_norm(l)
488
+ for l in self.source_resblocks:
489
+ l.remove_weight_norm()
490
+
491
+ def _stft(self, x):
492
+ spec = torch.stft(
493
+ x,
494
+ self.istft_params["n_fft"], self.istft_params["hop_len"], self.istft_params["n_fft"], window=self.stft_window.to(x.device),
495
+ return_complex=True)
496
+ spec = torch.view_as_real(spec) # [B, F, TT, 2]
497
+ return spec[..., 0], spec[..., 1]
498
+
499
+ def _istft(self, magnitude, phase):
500
+ magnitude = torch.clip(magnitude, max=1e2)
501
+ real = magnitude * torch.cos(phase)
502
+ img = magnitude * torch.sin(phase)
503
+ inverse_transform = torch.istft(torch.complex(real, img), self.istft_params["n_fft"], self.istft_params["hop_len"],
504
+ self.istft_params["n_fft"], window=self.stft_window.to(magnitude.device))
505
+ return inverse_transform
506
+
507
+ def decode(self, x: torch.Tensor, s: torch.Tensor = torch.zeros(1, 1, 0)) -> torch.Tensor:
508
+ s_stft_real, s_stft_imag = self._stft(s.squeeze(1))
509
+ s_stft = torch.cat([s_stft_real, s_stft_imag], dim=1)
510
+
511
+ x = self.conv_pre(x)
512
+ for i in range(self.num_upsamples):
513
+ x = F.leaky_relu(x, self.lrelu_slope)
514
+ x = self.ups[i](x)
515
+
516
+ if i == self.num_upsamples - 1:
517
+ x = self.reflection_pad(x)
518
+
519
+ # fusion
520
+ si = self.source_downs[i](s_stft)
521
+ si = self.source_resblocks[i](si)
522
+ x = x + si
523
+
524
+ xs = None
525
+ for j in range(self.num_kernels):
526
+ if xs is None:
527
+ xs = self.resblocks[i * self.num_kernels + j](x)
528
+ else:
529
+ xs += self.resblocks[i * self.num_kernels + j](x)
530
+ x = xs / self.num_kernels
531
+
532
+ x = F.leaky_relu(x)
533
+ x = self.conv_post(x)
534
+ magnitude = torch.exp(x[:, :self.istft_params["n_fft"] // 2 + 1, :])
535
+ phase = torch.sin(x[:, self.istft_params["n_fft"] // 2 + 1:, :]) # actually, sin is redundancy
536
+
537
+ x = self._istft(magnitude, phase)
538
+ x = torch.clamp(x, -self.audio_limit, self.audio_limit)
539
+ return x
540
+
541
+ def forward(
542
+ self,
543
+ batch: dict,
544
+ device: torch.device,
545
+ ) -> Dict[str, Optional[torch.Tensor]]:
546
+ speech_feat = batch['speech_feat'].transpose(1, 2).to(device)
547
+ # mel->f0
548
+ f0 = self.f0_predictor(speech_feat)
549
+ # f0->source
550
+ s = self.f0_upsamp(f0[:, None]).transpose(1, 2) # bs,n,t
551
+ s, _, _ = self.m_source(s)
552
+ s = s.transpose(1, 2)
553
+ # mel+source->speech
554
+ generated_speech = self.decode(x=speech_feat, s=s)
555
+ return generated_speech, f0
556
+
557
+ @torch.inference_mode()
558
+ def inference(self, speech_feat: torch.Tensor, cache_source: torch.Tensor = torch.zeros(1, 1, 0)) -> torch.Tensor:
559
+ # mel->f0
560
+ f0 = self.f0_predictor(speech_feat)
561
+ # f0->source
562
+ s = self.f0_upsamp(f0[:, None]).transpose(1, 2) # bs,n,t
563
+ s, _, _ = self.m_source(s)
564
+ s = s.transpose(1, 2)
565
+ # use cache_source to avoid glitch
566
+ if cache_source.shape[2] != 0:
567
+ s[:, :, :cache_source.shape[2]] = cache_source
568
+ generated_speech = self.decode(x=speech_feat, s=s)
569
+ return generated_speech, s
570
+
571
+
572
+ class CausalHiFTGenerator(HiFTGenerator):
573
+ """
574
+ HiFTNet Generator: Neural Source Filter + ISTFTNet
575
+ https://arxiv.org/abs/2309.09493
576
+ """
577
+ def __init__(
578
+ self,
579
+ in_channels: int = 80,
580
+ base_channels: int = 512,
581
+ nb_harmonics: int = 8,
582
+ sampling_rate: int = 22050,
583
+ nsf_alpha: float = 0.1,
584
+ nsf_sigma: float = 0.003,
585
+ nsf_voiced_threshold: float = 10,
586
+ upsample_rates: List[int] = [8, 8],
587
+ upsample_kernel_sizes: List[int] = [16, 16],
588
+ istft_params: Dict[str, int] = {"n_fft": 16, "hop_len": 4},
589
+ resblock_kernel_sizes: List[int] = [3, 7, 11],
590
+ resblock_dilation_sizes: List[List[int]] = [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
591
+ source_resblock_kernel_sizes: List[int] = [7, 11],
592
+ source_resblock_dilation_sizes: List[List[int]] = [[1, 3, 5], [1, 3, 5]],
593
+ lrelu_slope: float = 0.1,
594
+ audio_limit: float = 0.99,
595
+ conv_pre_look_right: int = 4,
596
+ f0_predictor: torch.nn.Module = None,
597
+ ):
598
+ torch.nn.Module.__init__(self)
599
+
600
+ self.out_channels = 1
601
+ self.nb_harmonics = nb_harmonics
602
+ self.sampling_rate = sampling_rate
603
+ self.istft_params = istft_params
604
+ self.lrelu_slope = lrelu_slope
605
+ self.audio_limit = audio_limit
606
+
607
+ self.num_kernels = len(resblock_kernel_sizes)
608
+ self.num_upsamples = len(upsample_rates)
609
+ self.m_source = SourceModuleHnNSF(
610
+ sampling_rate=sampling_rate,
611
+ upsample_scale=np.prod(upsample_rates) * istft_params["hop_len"],
612
+ harmonic_num=nb_harmonics,
613
+ sine_amp=nsf_alpha,
614
+ add_noise_std=nsf_sigma,
615
+ voiced_threshod=nsf_voiced_threshold,
616
+ sinegen_type='1' if self.sampling_rate == 22050 else '2',
617
+ causal=True)
618
+ self.upsample_rates = upsample_rates
619
+ self.f0_upsamp = torch.nn.Upsample(scale_factor=np.prod(upsample_rates) * istft_params["hop_len"])
620
+
621
+ self.conv_pre = weight_norm(
622
+ CausalConv1d(in_channels, base_channels, conv_pre_look_right + 1, 1, causal_type='right')
623
+ )
624
+
625
+ # Up
626
+ self.ups = nn.ModuleList()
627
+ for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
628
+ self.ups.append(
629
+ weight_norm(
630
+ CausalConv1dUpsample(
631
+ base_channels // (2**i),
632
+ base_channels // (2**(i + 1)),
633
+ k,
634
+ u,
635
+ )
636
+ )
637
+ )
638
+
639
+ # Down
640
+ self.source_downs = nn.ModuleList()
641
+ self.source_resblocks = nn.ModuleList()
642
+ downsample_rates = [1] + upsample_rates[::-1][:-1]
643
+ downsample_cum_rates = np.cumprod(downsample_rates)
644
+ for i, (u, k, d) in enumerate(zip(downsample_cum_rates[::-1], source_resblock_kernel_sizes, source_resblock_dilation_sizes)):
645
+ if u == 1:
646
+ self.source_downs.append(
647
+ CausalConv1d(istft_params["n_fft"] + 2, base_channels // (2 ** (i + 1)), 1, 1, causal_type='left')
648
+ )
649
+ else:
650
+ self.source_downs.append(
651
+ CausalConv1dDownSample(istft_params["n_fft"] + 2, base_channels // (2 ** (i + 1)), u * 2, u)
652
+ )
653
+
654
+ self.source_resblocks.append(
655
+ ResBlock(base_channels // (2 ** (i + 1)), k, d, causal=True)
656
+ )
657
+
658
+ self.resblocks = nn.ModuleList()
659
+ for i in range(len(self.ups)):
660
+ ch = base_channels // (2**(i + 1))
661
+ for _, (k, d) in enumerate(zip(resblock_kernel_sizes, resblock_dilation_sizes)):
662
+ self.resblocks.append(ResBlock(ch, k, d, causal=True))
663
+
664
+ self.conv_post = weight_norm(CausalConv1d(ch, istft_params["n_fft"] + 2, 7, 1, causal_type='left'))
665
+ self.ups.apply(init_weights)
666
+ self.conv_post.apply(init_weights)
667
+ self.reflection_pad = nn.ReflectionPad1d((1, 0))
668
+ self.stft_window = torch.from_numpy(get_window("hann", istft_params["n_fft"], fftbins=True).astype(np.float32))
669
+ self.conv_pre_look_right = conv_pre_look_right
670
+ self.f0_predictor = f0_predictor
671
+
672
+ def decode(self, x: torch.Tensor, s: torch.Tensor = torch.zeros(1, 1, 0), finalize: bool = True) -> torch.Tensor:
673
+ s_stft_real, s_stft_imag = self._stft(s.squeeze(1))
674
+ if finalize is True:
675
+ x = self.conv_pre(x)
676
+ else:
677
+ x = self.conv_pre(x[:, :, :-self.conv_pre_look_right], x[:, :, -self.conv_pre_look_right:])
678
+ s_stft_real = s_stft_real[:, :, :-int(np.prod(self.upsample_rates) * self.conv_pre_look_right)]
679
+ s_stft_imag = s_stft_imag[:, :, :-int(np.prod(self.upsample_rates) * self.conv_pre_look_right)]
680
+ s_stft = torch.cat([s_stft_real, s_stft_imag], dim=1)
681
+
682
+ for i in range(self.num_upsamples):
683
+ x = F.leaky_relu(x, self.lrelu_slope)
684
+ x = self.ups[i](x)
685
+
686
+ if i == self.num_upsamples - 1:
687
+ x = self.reflection_pad(x)
688
+
689
+ # fusion
690
+ si = self.source_downs[i](s_stft)
691
+ si = self.source_resblocks[i](si)
692
+ x = x + si
693
+
694
+ xs = None
695
+ for j in range(self.num_kernels):
696
+ if xs is None:
697
+ xs = self.resblocks[i * self.num_kernels + j](x)
698
+ else:
699
+ xs += self.resblocks[i * self.num_kernels + j](x)
700
+ x = xs / self.num_kernels
701
+
702
+ x = F.leaky_relu(x)
703
+ x = self.conv_post(x)
704
+ magnitude = torch.exp(x[:, :self.istft_params["n_fft"] // 2 + 1, :])
705
+ phase = torch.sin(x[:, self.istft_params["n_fft"] // 2 + 1:, :]) # actually, sin is redundancy
706
+
707
+ x = self._istft(magnitude, phase)
708
+ if finalize is False:
709
+ x = x[:, :-int(np.prod(self.upsample_rates) * self.istft_params['hop_len'])]
710
+ x = torch.clamp(x, -self.audio_limit, self.audio_limit)
711
+ return x
712
+
713
+ @torch.inference_mode()
714
+ def inference(self, speech_feat: torch.Tensor, finalize: bool = True) -> torch.Tensor:
715
+ # mel->f0 NOTE f0_predictor precision is crucial for causal inference, move self.f0_predictor to cpu if necessary
716
+ self.f0_predictor.to(torch.float64)
717
+ f0 = self.f0_predictor(speech_feat.to(torch.float64), finalize=finalize).to(speech_feat)
718
+ # f0->source
719
+ s = self.f0_upsamp(f0[:, None]).transpose(1, 2) # bs,n,t
720
+ s, _, _ = self.m_source(s)
721
+ s = s.transpose(1, 2)
722
+ if finalize is True:
723
+ generated_speech = self.decode(x=speech_feat, s=s, finalize=finalize)
724
+ else:
725
+ generated_speech = self.decode(x=speech_feat[:, :, :-self.f0_predictor.condnet[0].causal_padding], s=s, finalize=finalize)
726
+ return generated_speech, s
727
+
728
+
729
+ if __name__ == '__main__':
730
+ torch.backends.cudnn.deterministic = True
731
+ torch.backends.cudnn.benchmark = False
732
+ from hyperpyyaml import load_hyperpyyaml
733
+ with open('./pretrained_models/Fun-CosyVoice3-0.5B/cosyvoice3.yaml', 'r') as f:
734
+ configs = load_hyperpyyaml(f, overrides={'llm': None, 'flow': None})
735
+ model = configs['hift']
736
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
737
+ model.to(device)
738
+ model.eval()
739
+ max_len, chunk_size, context_size = 300, 30, 8
740
+ mel = torch.rand(1, 80, max_len).to(device)
741
+ pred_gt, _ = model.inference(mel)
742
+ for i in range(0, max_len, chunk_size):
743
+ finalize = True if i + chunk_size + context_size >= max_len else False
744
+ pred_chunk, _ = model.inference(mel[:, :, : i + chunk_size + context_size], finalize=finalize)
745
+ pred_chunk = pred_chunk[:, i * 480:]
746
+ print((pred_gt[:, i * 480:i * 480 + pred_chunk.shape[1]] - pred_chunk).abs().max().item())
third_party/CosyVoice/cosyvoice/hifigan/hifigan.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, Optional
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from matcha.hifigan.models import feature_loss, generator_loss, discriminator_loss
6
+ from cosyvoice.utils.losses import tpr_loss, mel_loss
7
+
8
+
9
+ class HiFiGan(nn.Module):
10
+ def __init__(self, generator, discriminator, mel_spec_transform,
11
+ multi_mel_spectral_recon_loss_weight=45, feat_match_loss_weight=2.0,
12
+ tpr_loss_weight=1.0, tpr_loss_tau=0.04):
13
+ super(HiFiGan, self).__init__()
14
+ self.generator = generator
15
+ self.discriminator = discriminator
16
+ self.mel_spec_transform = mel_spec_transform
17
+ self.multi_mel_spectral_recon_loss_weight = multi_mel_spectral_recon_loss_weight
18
+ self.feat_match_loss_weight = feat_match_loss_weight
19
+ self.tpr_loss_weight = tpr_loss_weight
20
+ self.tpr_loss_tau = tpr_loss_tau
21
+
22
+ def forward(
23
+ self,
24
+ batch: dict,
25
+ device: torch.device,
26
+ ) -> Dict[str, Optional[torch.Tensor]]:
27
+ if batch['turn'] == 'generator':
28
+ return self.forward_generator(batch, device)
29
+ else:
30
+ return self.forward_discriminator(batch, device)
31
+
32
+ def forward_generator(self, batch, device):
33
+ real_speech = batch['speech'].to(device)
34
+ pitch_feat = batch['pitch_feat'].to(device)
35
+ # 1. calculate generator outputs
36
+ generated_speech, generated_f0 = self.generator(batch, device)
37
+ # 2. calculate discriminator outputs
38
+ y_d_rs, y_d_gs, fmap_rs, fmap_gs = self.discriminator(real_speech, generated_speech)
39
+ # 3. calculate generator losses, feature loss, mel loss, tpr losses [Optional]
40
+ loss_gen, _ = generator_loss(y_d_gs)
41
+ loss_fm = feature_loss(fmap_rs, fmap_gs)
42
+ loss_mel = mel_loss(real_speech, generated_speech, self.mel_spec_transform)
43
+ if self.tpr_loss_weight != 0:
44
+ loss_tpr = tpr_loss(y_d_gs, y_d_rs, self.tpr_loss_tau)
45
+ else:
46
+ loss_tpr = torch.zeros(1).to(device)
47
+ loss_f0 = F.l1_loss(generated_f0, pitch_feat)
48
+ loss = loss_gen + self.feat_match_loss_weight * loss_fm + \
49
+ self.multi_mel_spectral_recon_loss_weight * loss_mel + \
50
+ self.tpr_loss_weight * loss_tpr + loss_f0
51
+ return {'loss': loss, 'loss_gen': loss_gen, 'loss_fm': loss_fm, 'loss_mel': loss_mel, 'loss_tpr': loss_tpr, 'loss_f0': loss_f0}
52
+
53
+ def forward_discriminator(self, batch, device):
54
+ real_speech = batch['speech'].to(device)
55
+ # 1. calculate generator outputs
56
+ with torch.no_grad():
57
+ generated_speech, generated_f0 = self.generator(batch, device)
58
+ # 2. calculate discriminator outputs
59
+ y_d_rs, y_d_gs, fmap_rs, fmap_gs = self.discriminator(real_speech, generated_speech.detach())
60
+ # 3. calculate discriminator losses, tpr losses [Optional]
61
+ loss_disc, _, _ = discriminator_loss(y_d_rs, y_d_gs)
62
+ if self.tpr_loss_weight != 0:
63
+ loss_tpr = tpr_loss(y_d_rs, y_d_gs, self.tpr_loss_tau)
64
+ else:
65
+ loss_tpr = torch.zeros(1).to(device)
66
+ loss = loss_disc + self.tpr_loss_weight * loss_tpr
67
+ return {'loss': loss, 'loss_disc': loss_disc, 'loss_tpr': loss_tpr}
third_party/CosyVoice/cosyvoice/llm/__pycache__/llm.cpython-312.pyc ADDED
Binary file (45 kB). View file
 
third_party/CosyVoice/cosyvoice/llm/llm.py ADDED
@@ -0,0 +1,710 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024 Alibaba Inc (authors: Xiang Lyu, Zhihao Du)
2
+ # 2025 Alibaba Inc (authors: Xiang Lyu, Yabin Li, Qihua, Shengqiang Li)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import os, queue
16
+ import random
17
+ import time
18
+ import threading
19
+ from typing import Dict, Optional, Callable, List, Generator
20
+ import numpy as np
21
+ import torch
22
+ from torch import nn
23
+ import torch.nn.functional as F
24
+ from transformers import Qwen2ForCausalLM
25
+ from torch.nn.utils.rnn import pad_sequence, unpad_sequence
26
+ from cosyvoice.utils.common import IGNORE_ID
27
+ from cosyvoice.transformer.label_smoothing_loss import LabelSmoothingLoss
28
+ from cosyvoice.utils.common import th_accuracy
29
+ from cosyvoice.utils.file_utils import logging
30
+ from cosyvoice.utils.mask import make_pad_mask
31
+ from cosyvoice.utils.onnx import SpeechTokenExtractor, online_feature, onnx_path
32
+
33
+
34
+ class TransformerLM(torch.nn.Module):
35
+ def __init__(
36
+ self,
37
+ text_encoder_input_size: int,
38
+ llm_input_size: int,
39
+ llm_output_size: int,
40
+ text_token_size: int,
41
+ speech_token_size: int,
42
+ text_encoder: torch.nn.Module,
43
+ llm: torch.nn.Module,
44
+ sampling: Callable,
45
+ length_normalized_loss: bool = True,
46
+ lsm_weight: float = 0.0,
47
+ spk_embed_dim: int = 192,
48
+ ):
49
+ super().__init__()
50
+ self.llm_input_size = llm_input_size
51
+ self.speech_token_size = speech_token_size
52
+ # 1. build text token inputs related modules
53
+ self.text_embedding = torch.nn.Embedding(text_token_size, text_encoder_input_size)
54
+ self.text_encoder = text_encoder
55
+ self.text_encoder_affine_layer = nn.Linear(
56
+ self.text_encoder.output_size(),
57
+ llm_input_size
58
+ )
59
+
60
+ # 2. build speech token language model related modules
61
+ self.sos = 0
62
+ self.task_id = 1
63
+ self.eos_token = self.speech_token_size
64
+ self.llm_embedding = torch.nn.Embedding(2, llm_input_size)
65
+ self.llm = llm
66
+ self.llm_decoder = nn.Linear(llm_output_size, speech_token_size + 1)
67
+ self.criterion_ce = LabelSmoothingLoss(
68
+ size=speech_token_size + 1,
69
+ padding_idx=IGNORE_ID,
70
+ smoothing=lsm_weight,
71
+ normalize_length=length_normalized_loss,
72
+ )
73
+
74
+ # 3. [Optional] build speech token related modules
75
+ self.speech_embedding = torch.nn.Embedding(speech_token_size, llm_input_size)
76
+ self.spk_embed_affine_layer = torch.nn.Linear(spk_embed_dim, llm_input_size)
77
+
78
+ # 4. sampling method
79
+ self.sampling = sampling
80
+
81
+ def encode(
82
+ self,
83
+ text: torch.Tensor,
84
+ text_lengths: torch.Tensor,
85
+ ):
86
+ encoder_out, encoder_mask = self.text_encoder(text, text_lengths, decoding_chunk_size=1, num_decoding_left_chunks=-1)
87
+ encoder_out_lens = encoder_mask.squeeze(1).sum(1)
88
+ encoder_out = self.text_encoder_affine_layer(encoder_out)
89
+ return encoder_out, encoder_out_lens
90
+
91
+ def pad_unpad_sequence(self, sos_emb, embedding, text_token, text_token_len, task_id_emb, speech_token, speech_token_len):
92
+ text_token = unpad_sequence(text_token, text_token_len.cpu(), batch_first=True)
93
+ speech_token = unpad_sequence(speech_token, speech_token_len.cpu(), batch_first=True)
94
+ lm_input = [torch.concat([sos_emb.squeeze(dim=0), embedding[i], text_token[i], task_id_emb.squeeze(dim=0), speech_token[i]], dim=0)
95
+ for i in range(len(text_token))]
96
+ lm_input_len = torch.tensor([i.size(0) for i in lm_input], dtype=torch.int32)
97
+ lm_input = pad_sequence(lm_input, batch_first=True, padding_value=IGNORE_ID)
98
+ return lm_input, lm_input_len
99
+
100
+ def forward(
101
+ self,
102
+ batch: dict,
103
+ device: torch.device,
104
+ ) -> Dict[str, Optional[torch.Tensor]]:
105
+ """
106
+ Args:
107
+ text: (B, L, D)
108
+ text_lengths: (B,)
109
+ audio: (B, T, N) or (B, T)
110
+ audio_lengths: (B,)
111
+ """
112
+ text_token = batch['text_token'].to(device)
113
+ text_token_len = batch['text_token_len'].to(device)
114
+ speech_token = batch['speech_token'].to(device)
115
+ speech_token_len = batch['speech_token_len'].to(device)
116
+ embedding = batch['embedding'].to(device)
117
+
118
+ # 1. prepare llm_target
119
+ lm_target = [torch.tensor([IGNORE_ID] * (2 + text_token_len[i]) + speech_token[i, :speech_token_len[i]].tolist() +
120
+ [self.speech_token_size]) for i in range(text_token.size(0))]
121
+ lm_target = pad_sequence(lm_target, batch_first=True, padding_value=IGNORE_ID).to(device)
122
+
123
+ # 1. encode text_token
124
+ text_token = self.text_embedding(text_token)
125
+ text_token, text_token_len = self.encode(text_token, text_token_len)
126
+
127
+ # 2. embedding projection
128
+ embedding = F.normalize(embedding, dim=1)
129
+ embedding = self.spk_embed_affine_layer(embedding)
130
+ embedding = embedding.unsqueeze(1)
131
+
132
+ # 3. sos and task_id
133
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
134
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
135
+
136
+ # 4. encode speech_token
137
+ speech_token = self.speech_embedding(speech_token)
138
+
139
+ # 5. unpad and pad
140
+ lm_input, lm_input_len = self.pad_unpad_sequence(sos_emb, embedding, text_token, text_token_len,
141
+ task_id_emb, speech_token, speech_token_len)
142
+
143
+ # 6. run lm forward
144
+ lm_output, lm_output_mask = self.llm(lm_input, lm_input_len.to(device))
145
+ logits = self.llm_decoder(lm_output)
146
+ loss = self.criterion_ce(logits, lm_target)
147
+ acc = th_accuracy(logits.view(-1, self.speech_token_size + 1), lm_target, ignore_label=IGNORE_ID)
148
+ return {'loss': loss, 'acc': acc}
149
+
150
+ def sampling_ids(
151
+ self,
152
+ weighted_scores: torch.Tensor,
153
+ decoded_tokens: List,
154
+ sampling: int,
155
+ ignore_eos: bool = True,
156
+ ):
157
+ if ignore_eos is True:
158
+ weighted_scores[self.speech_token_size] = -float('inf')
159
+ top_ids = self.sampling(weighted_scores, decoded_tokens, sampling)
160
+ return top_ids
161
+
162
+ @torch.inference_mode()
163
+ def inference(
164
+ self,
165
+ text: torch.Tensor,
166
+ text_len: torch.Tensor,
167
+ prompt_text: torch.Tensor,
168
+ prompt_text_len: torch.Tensor,
169
+ prompt_speech_token: torch.Tensor,
170
+ prompt_speech_token_len: torch.Tensor,
171
+ embedding: torch.Tensor,
172
+ sampling: int = 25,
173
+ max_token_text_ratio: float = 20,
174
+ min_token_text_ratio: float = 2,
175
+ uuid: str = '',
176
+ ) -> Generator[torch.Tensor, None, None]:
177
+ device = text.device
178
+ text = torch.concat([prompt_text, text], dim=1)
179
+ text_len += prompt_text_len
180
+ text = self.text_embedding(text)
181
+
182
+ # 1. encode text
183
+ text, text_len = self.encode(text, text_len)
184
+
185
+ # 2. encode embedding
186
+ if embedding.shape[0] != 0:
187
+ embedding = F.normalize(embedding, dim=1)
188
+ embedding = self.spk_embed_affine_layer(embedding)
189
+ embedding = embedding.unsqueeze(dim=1)
190
+ else:
191
+ embedding = torch.zeros(1, 0, self.llm_input_size, dtype=text.dtype).to(device).to(text.dtype)
192
+
193
+ # 3. concat llm_input
194
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
195
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
196
+ if prompt_speech_token_len != 0:
197
+ prompt_speech_token_emb = self.speech_embedding(prompt_speech_token)
198
+ else:
199
+ prompt_speech_token_emb = torch.zeros(1, 0, self.llm_input_size, dtype=text.dtype).to(device)
200
+ lm_input = torch.concat([sos_emb, embedding, text, task_id_emb, prompt_speech_token_emb], dim=1)
201
+
202
+ # 4. cal min/max_length
203
+ min_len = int((text_len - prompt_text_len) * min_token_text_ratio)
204
+ max_len = int((text_len - prompt_text_len) * max_token_text_ratio)
205
+
206
+ # 5. step by step decode
207
+ out_tokens = []
208
+ offset = 0
209
+ att_cache, cnn_cache = torch.zeros((0, 0, 0, 0), device=lm_input.device), torch.zeros((0, 0, 0, 0), device=lm_input.device)
210
+ for i in range(max_len):
211
+ y_pred, att_cache, cnn_cache = self.llm.forward_chunk(lm_input, offset=offset, required_cache_size=-1,
212
+ att_cache=att_cache, cnn_cache=cnn_cache,
213
+ att_mask=torch.tril(torch.ones((1, lm_input.shape[1], lm_input.shape[1]),
214
+ device=lm_input.device)).to(torch.bool))
215
+ logp = self.llm_decoder(y_pred[:, -1].to(dtype=self.llm_decoder.weight.dtype)).log_softmax(dim=-1)
216
+ top_ids = self.sampling_ids(logp.squeeze(dim=0), out_tokens, sampling, ignore_eos=True if i < min_len else False)
217
+ if top_ids == self.eos_token:
218
+ break
219
+ # in stream mode, yield token one by one
220
+ yield top_ids
221
+ out_tokens.append(top_ids)
222
+ offset += lm_input.size(1)
223
+ lm_input = self.speech_embedding.weight[top_ids].reshape(1, 1, -1)
224
+
225
+
226
+ class Qwen2Encoder(torch.nn.Module):
227
+ def __init__(self, pretrain_path):
228
+ super().__init__()
229
+ self.model = Qwen2ForCausalLM.from_pretrained(pretrain_path)
230
+
231
+ @property
232
+ def input_dtype(self):
233
+ return self.model.model.embed_tokens.weight.dtype
234
+
235
+ def forward(self, xs: torch.Tensor, xs_lens: torch.Tensor):
236
+ T = xs.size(1)
237
+ masks = ~make_pad_mask(xs_lens, T)
238
+ outs = self.model(
239
+ inputs_embeds=xs.to(dtype=self.input_dtype),
240
+ attention_mask=masks,
241
+ output_hidden_states=True,
242
+ return_dict=True,
243
+ )
244
+ return outs.hidden_states[-1], masks.unsqueeze(1)
245
+
246
+ def forward_one_step(self, xs, masks, cache=None):
247
+ input_masks = masks[:, -1, :]
248
+ outs = self.model(
249
+ inputs_embeds=xs.to(dtype=self.input_dtype),
250
+ attention_mask=input_masks,
251
+ output_hidden_states=True,
252
+ return_dict=True,
253
+ use_cache=True,
254
+ past_key_values=cache,
255
+ )
256
+ xs = outs.hidden_states[-1]
257
+ new_cache = outs.past_key_values
258
+ return xs, new_cache
259
+
260
+
261
+ class Qwen2LM(TransformerLM):
262
+ def __init__(
263
+ self,
264
+ llm_input_size: int,
265
+ llm_output_size: int,
266
+ speech_token_size: int,
267
+ llm: torch.nn.Module,
268
+ sampling: Callable,
269
+ length_normalized_loss: bool = True,
270
+ lsm_weight: float = 0.0,
271
+ mix_ratio: List[int] = [5, 15],
272
+ ):
273
+ torch.nn.Module.__init__(self)
274
+ self.llm_input_size = llm_input_size
275
+ self.llm_output_size = llm_output_size
276
+ self.speech_token_size = speech_token_size
277
+ # 2. build speech token language model related modules
278
+ self.sos = 0
279
+ self.task_id = 1
280
+ self.eos_token = speech_token_size
281
+ self.fill_token = speech_token_size + 2
282
+
283
+ self.llm_embedding = torch.nn.Embedding(2, llm_input_size)
284
+ self.llm = llm
285
+ self.llm_decoder = nn.Linear(llm_output_size, speech_token_size + 3)
286
+ self.criterion_ce = LabelSmoothingLoss(
287
+ size=speech_token_size + 3,
288
+ padding_idx=IGNORE_ID,
289
+ smoothing=lsm_weight,
290
+ normalize_length=length_normalized_loss,
291
+ )
292
+
293
+ # 3. [Optional] build speech token related modules
294
+ self.speech_embedding = torch.nn.Embedding(speech_token_size + 3, llm_input_size)
295
+
296
+ # 4. sampling method
297
+ self.sampling = sampling
298
+ self.mix_ratio = mix_ratio
299
+
300
+ # 5. vllm related
301
+ self.stop_token_ids = [speech_token_size + i for i in range(3)]
302
+ self.vllm_output_queue = {}
303
+ if online_feature is True:
304
+ self.speech_token_extractor = SpeechTokenExtractor(model_path=os.path.join(onnx_path, 'speech_tokenizer_v2.batch.onnx'))
305
+
306
+ def prepare_lm_input_target(self, sos_emb, text_token, text_token_emb, text_token_len, task_id_emb, speech_token, speech_token_emb, speech_token_len, instruct_token=None, instruct_token_emb=None, instruct_token_len=None):
307
+ lm_target, lm_input = [], []
308
+ text_token = unpad_sequence(text_token, text_token_len.cpu(), batch_first=True)
309
+ speech_token = unpad_sequence(speech_token, speech_token_len.cpu(), batch_first=True)
310
+ text_token_emb = unpad_sequence(text_token_emb, text_token_len.cpu(), batch_first=True)
311
+ speech_token_emb = unpad_sequence(speech_token_emb, speech_token_len.cpu(), batch_first=True)
312
+ # NOTE add instruct_token in CosyVoice3
313
+ if instruct_token is not None and instruct_token_emb is not None and instruct_token_len is not None:
314
+ instruct_token = unpad_sequence(instruct_token, instruct_token_len.cpu(), batch_first=True)
315
+ instruct_token_emb = unpad_sequence(instruct_token_emb, instruct_token_len.cpu(), batch_first=True)
316
+ else:
317
+ instruct_token = [torch.empty(0).to(text_token[0])] * len(text_token)
318
+ instruct_token_emb = [torch.empty(0, 896).to(text_token_emb[0])] * len(text_token)
319
+ instruct_token_len = torch.zeros(len(text_token)).to(text_token_len)
320
+ for i in range(len(text_token)):
321
+ # bistream sequence
322
+ if random.random() < 0.5 and speech_token_len[i] / text_token_len[i] > self.mix_ratio[1] / self.mix_ratio[0]:
323
+ this_lm_target, this_lm_input = [IGNORE_ID], [sos_emb.squeeze(dim=0)]
324
+ this_lm_target += [IGNORE_ID] * instruct_token_len[i]
325
+ this_lm_input.append(instruct_token_emb[i])
326
+ for j in range(((text_token_len[i] + 1) / self.mix_ratio[0]).ceil().int().item()):
327
+ this_text_token = text_token[i][j * self.mix_ratio[0]: (j + 1) * self.mix_ratio[0]].tolist()
328
+ this_speech_token = speech_token[i][j * self.mix_ratio[1]: (j + 1) * self.mix_ratio[1]].tolist()
329
+ if len(this_text_token) == self.mix_ratio[0]:
330
+ assert len(this_speech_token) == self.mix_ratio[1]
331
+ this_lm_target += [IGNORE_ID] * (self.mix_ratio[0] - 1)
332
+ this_lm_target += this_speech_token
333
+ this_lm_target.append(self.fill_token)
334
+ this_lm_input.append(text_token_emb[i][j * self.mix_ratio[0]: (j + 1) * self.mix_ratio[0]])
335
+ this_lm_input.append(speech_token_emb[i][j * self.mix_ratio[1]: (j + 1) * self.mix_ratio[1]])
336
+ else:
337
+ this_lm_target += [-1] * len(this_text_token)
338
+ this_lm_target += speech_token[i][j * self.mix_ratio[1]:].tolist()
339
+ this_lm_target.append(self.eos_token)
340
+ this_lm_input.append(text_token_emb[i][j * self.mix_ratio[0]:])
341
+ this_lm_input.append(task_id_emb.squeeze(dim=0))
342
+ this_lm_input.append(speech_token_emb[i][j * self.mix_ratio[1]:])
343
+ this_lm_target, this_lm_input = torch.tensor(this_lm_target), torch.concat(this_lm_input, dim=0)
344
+ # unistream sequence
345
+ else:
346
+ this_lm_target = torch.tensor([IGNORE_ID] * (1 + instruct_token_len[i] + text_token_len[i]) + speech_token[i].tolist() + [self.eos_token])
347
+ this_lm_input = torch.concat([sos_emb.squeeze(dim=0), instruct_token_emb[i], text_token_emb[i], task_id_emb.squeeze(dim=0), speech_token_emb[i]], dim=0)
348
+ lm_target.append(this_lm_target)
349
+ lm_input.append(this_lm_input)
350
+ lm_input_len = torch.tensor([i.size(0) for i in lm_input], dtype=torch.int32)
351
+ lm_input = pad_sequence(lm_input, batch_first=True, padding_value=IGNORE_ID)
352
+ lm_target = pad_sequence(lm_target, batch_first=True, padding_value=IGNORE_ID)
353
+ return lm_target, lm_input, lm_input_len
354
+
355
+ def forward(
356
+ self,
357
+ batch: dict,
358
+ device: torch.device,
359
+ ) -> Dict[str, Optional[torch.Tensor]]:
360
+ """
361
+ Args:
362
+ text: (B, L, D)
363
+ text_lengths: (B,)
364
+ audio: (B, T, N) or (B, T)
365
+ audio_lengths: (B,)
366
+ """
367
+ # 1. encode text_token
368
+ text_token = batch['text_token'].to(device)
369
+ text_token_len = batch['text_token_len'].to(device)
370
+ text_token_emb = self.llm.model.model.embed_tokens(text_token)
371
+
372
+ # 2. encode speech_token
373
+ if 'speech_token' not in batch:
374
+ speech_token, speech_token_len = self.speech_token_extractor.inference(batch['whisper_feat'], batch['whisper_feat_len'], device)
375
+ else:
376
+ speech_token = batch['speech_token'].to(device)
377
+ speech_token_len = batch['speech_token_len'].to(device)
378
+ speech_token_emb = self.speech_embedding(speech_token)
379
+
380
+ # 3. sos and task_id
381
+ if self.__class__.__name__ == 'CosyVoice3LM':
382
+ sos_emb = self.speech_embedding.weight[self.sos].reshape(1, 1, -1)
383
+ task_id_emb = self.speech_embedding.weight[self.task_id].reshape(1, 1, -1)
384
+ elif self.__class__.__name__ == 'Qwen2LM':
385
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
386
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
387
+ else:
388
+ raise ValueError
389
+
390
+ # 4. prepare llm_input/target
391
+ if self.__class__.__name__ == 'CosyVoice3LM':
392
+ instruct_token = batch['instruct_token'].to(device)
393
+ instruct_token_len = batch['instruct_token_len'].to(device)
394
+ instruct_token_emb = self.llm.model.model.embed_tokens(instruct_token)
395
+ lm_target, lm_input, lm_input_len = self.prepare_lm_input_target(sos_emb, text_token, text_token_emb, text_token_len, task_id_emb,
396
+ speech_token, speech_token_emb, speech_token_len, instruct_token, instruct_token_emb, instruct_token_len)
397
+ elif self.__class__.__name__ == 'Qwen2LM':
398
+ lm_target, lm_input, lm_input_len = self.prepare_lm_input_target(sos_emb, text_token, text_token_emb, text_token_len, task_id_emb,
399
+ speech_token, speech_token_emb, speech_token_len)
400
+ else:
401
+ raise ValueError
402
+ lm_target = lm_target.to(device)
403
+
404
+ # 4. run lm forward
405
+ lm_output, lm_output_mask = self.llm(lm_input, lm_input_len.to(device))
406
+ logits = self.llm_decoder(lm_output)
407
+ loss = self.criterion_ce(logits, lm_target.to(device))
408
+ acc = th_accuracy(logits.view(-1, self.llm_decoder.out_features), lm_target, ignore_label=IGNORE_ID)
409
+ return {'loss': loss, 'acc': acc}
410
+
411
+ def forward_dpo(
412
+ self,
413
+ batch: dict,
414
+ device: torch.device,
415
+ ) -> Dict[str, Optional[torch.Tensor]]:
416
+ text_token = batch['text_token'].to(device)
417
+ text_token_len = batch['text_token_len'].to(device)
418
+ speech_token = batch['speech_token'].to(device)
419
+ speech_token_len = batch['speech_token_len'].to(device)
420
+ reject_speech_token = batch['reject_speech_token'].to(device)
421
+ reject_speech_token_len = batch['reject_speech_token_len'].to(device)
422
+
423
+ # 1. encode text_token
424
+ text_token_emb = self.llm.model.model.embed_tokens(text_token)
425
+
426
+ # 3. sos and task_id
427
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
428
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
429
+
430
+ # 2. encode speech_token
431
+ speech_token = unpad_sequence(speech_token, speech_token_len.cpu(), batch_first=True)
432
+ reject_speech_token = unpad_sequence(reject_speech_token, reject_speech_token_len.cpu(), batch_first=True)
433
+ speech_token_combined = speech_token + reject_speech_token
434
+ speech_token_combined = pad_sequence(speech_token_combined, batch_first=True, padding_value=0)
435
+ speech_token_combined_len = torch.concat([speech_token_len, reject_speech_token_len], dim=0)
436
+ speech_token_combined_emb = self.speech_embedding(speech_token_combined)
437
+
438
+ # 3. prepare llm_input/target
439
+ lm_target, lm_input, lm_input_len = self.prepare_lm_input_target(sos_emb, text_token.repeat(2, 1), text_token_emb.repeat(2, 1, 1), text_token_len.repeat(2),
440
+ task_id_emb, speech_token_combined, speech_token_combined_emb, speech_token_combined_len)
441
+ lm_target = lm_target.to(device)
442
+
443
+ # 4. run lm forward
444
+ lm_output, lm_output_mask = self.llm(lm_input, lm_input_len.to(device))
445
+ logits = self.llm_decoder(lm_output)
446
+ chosen_logits = logits[:text_token.shape[0]]
447
+ rejected_logits = logits[text_token.shape[0]:]
448
+ chosen_lm_target = lm_target[:text_token.shape[0]]
449
+ rejected_lm_target = lm_target[text_token.shape[0]:]
450
+ loss = self.criterion_ce(chosen_logits, chosen_lm_target.to(device))
451
+ acc = th_accuracy(chosen_logits.view(-1, self.speech_token_size + 3), chosen_lm_target, ignore_label=IGNORE_ID)
452
+
453
+ # 5. calculate dpo logits
454
+ chosen_lm_mask = chosen_lm_target == IGNORE_ID
455
+ rejected_lm_mask = rejected_lm_target == IGNORE_ID
456
+ chosen_logps = torch.gather(chosen_logits.log_softmax(dim=-1), dim=2, index=chosen_lm_target.masked_fill(chosen_lm_mask, 0).unsqueeze(dim=-1)).squeeze(dim=-1)
457
+ rejected_logps = torch.gather(rejected_logits.log_softmax(dim=-1), dim=2, index=rejected_lm_target.masked_fill(rejected_lm_mask, 0).unsqueeze(dim=-1)).squeeze(dim=-1)
458
+ chosen_logps = (chosen_logps * chosen_lm_mask).sum(dim=-1) / chosen_lm_mask.sum(dim=-1)
459
+ rejected_logps = (rejected_logps * rejected_lm_mask).sum(dim=-1) / rejected_lm_mask.sum(dim=-1)
460
+ return {'loss': loss, 'acc': acc, 'chosen_logps': chosen_logps, 'rejected_logps': rejected_logps}
461
+
462
+ @torch.inference_mode()
463
+ def inference(
464
+ self,
465
+ text: torch.Tensor,
466
+ text_len: torch.Tensor,
467
+ prompt_text: torch.Tensor,
468
+ prompt_text_len: torch.Tensor,
469
+ prompt_speech_token: torch.Tensor,
470
+ prompt_speech_token_len: torch.Tensor,
471
+ embedding: torch.Tensor,
472
+ sampling: int = 25,
473
+ max_token_text_ratio: float = 20,
474
+ min_token_text_ratio: float = 2,
475
+ uuid: str = '',
476
+ ) -> Generator[torch.Tensor, None, None]:
477
+ device = text.device
478
+ text = torch.concat([prompt_text, text], dim=1)
479
+ text_len += prompt_text_len
480
+ text_emb = self.llm.model.model.embed_tokens(text)
481
+ if self.__class__.__name__ == 'CosyVoice3LM':
482
+ # NOTE temporary hardcode, 151646 is <|endofprompt|> token
483
+ assert 151646 in text, '<|endofprompt|> not detected in CosyVoice3 text or prompt_text, check your input!'
484
+
485
+ # 3. concat llm_input
486
+ if self.__class__.__name__ == 'CosyVoice3LM':
487
+ sos_emb = self.speech_embedding.weight[self.sos].reshape(1, 1, -1)
488
+ task_id_emb = self.speech_embedding.weight[self.task_id].reshape(1, 1, -1)
489
+ elif self.__class__.__name__ == 'Qwen2LM':
490
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
491
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
492
+ else:
493
+ raise ValueError
494
+ if prompt_speech_token_len != 0:
495
+ prompt_speech_token_emb = self.speech_embedding(prompt_speech_token)
496
+ else:
497
+ prompt_speech_token_emb = torch.zeros(1, 0, self.llm_input_size, dtype=text_emb.dtype).to(device)
498
+ lm_input = torch.concat([sos_emb, text_emb, task_id_emb, prompt_speech_token_emb], dim=1)
499
+
500
+ # 4. cal min/max_length
501
+ min_len = int((text_len - prompt_text_len) * min_token_text_ratio)
502
+ max_len = int((text_len - prompt_text_len) * max_token_text_ratio)
503
+
504
+ # 5. step by step decode
505
+ for token in self.inference_wrapper(lm_input, sampling, min_len, max_len, uuid):
506
+ yield token
507
+
508
+ @torch.inference_mode()
509
+ def inference_wrapper(self, lm_input, sampling, min_len, max_len, uuid):
510
+ if hasattr(self, 'vllm'):
511
+ from vllm import SamplingParams, RequestOutput
512
+ sampling_params = SamplingParams(top_k=sampling,
513
+ stop_token_ids=self.stop_token_ids,
514
+ min_tokens=min_len,
515
+ max_tokens=max_len)
516
+ with self.lock:
517
+ self.vllm.add_request(uuid, {"prompt_embeds": lm_input.squeeze(0).to(torch.bfloat16).to(lm_input.device)}, sampling_params)
518
+ self.vllm_output_queue[uuid] = queue.Queue()
519
+ out_tokens = []
520
+ while True:
521
+ with self.lock:
522
+ if self.vllm_output_queue[uuid].empty() is True:
523
+ request_outputs: List[RequestOutput] = self.vllm.step()
524
+ for request_output in request_outputs:
525
+ top_ids = list(request_output.outputs[0].token_ids)[-1]
526
+ self.vllm_output_queue[request_output.request_id].put(top_ids)
527
+ if self.vllm_output_queue[uuid].empty() is False:
528
+ top_ids = self.vllm_output_queue[uuid].get()
529
+ if top_ids in self.stop_token_ids:
530
+ break
531
+ # in stream mode, yield token one by one
532
+ yield top_ids
533
+ out_tokens.append(top_ids)
534
+ if len(out_tokens) == max_len:
535
+ break
536
+ time.sleep(0.001)
537
+ with self.lock:
538
+ self.vllm_output_queue.pop(uuid)
539
+ else:
540
+ out_tokens = []
541
+ cache = None
542
+ for i in range(max_len):
543
+ y_pred, cache = self.llm.forward_one_step(lm_input,
544
+ masks=torch.tril(torch.ones((1, lm_input.shape[1], lm_input.shape[1]), device=lm_input.device)).to(torch.bool),
545
+ cache=cache)
546
+ logp = self.llm_decoder(y_pred[:, -1].to(dtype=self.llm_decoder.weight.dtype)).log_softmax(dim=-1)
547
+ top_ids = self.sampling_ids(logp.squeeze(dim=0), out_tokens, sampling, ignore_eos=True if i < min_len else False)
548
+ if top_ids in self.stop_token_ids:
549
+ break
550
+ # in stream mode, yield token one by one
551
+ yield top_ids
552
+ out_tokens.append(top_ids)
553
+ lm_input = self.speech_embedding.weight[top_ids].reshape(1, 1, -1)
554
+
555
+ @torch.inference_mode()
556
+ def inference_bistream(
557
+ self,
558
+ text: Generator,
559
+ prompt_text: torch.Tensor,
560
+ prompt_text_len: torch.Tensor,
561
+ prompt_speech_token: torch.Tensor,
562
+ prompt_speech_token_len: torch.Tensor,
563
+ embedding: torch.Tensor,
564
+ sampling: int = 25,
565
+ max_token_text_ratio: float = 20,
566
+ min_token_text_ratio: float = 2,
567
+ ) -> Generator[torch.Tensor, None, None]:
568
+
569
+ device = prompt_text.device
570
+ # 1. prepare input
571
+ if self.__class__.__name__ == 'CosyVoice3LM':
572
+ sos_emb = self.speech_embedding.weight[self.sos].reshape(1, 1, -1)
573
+ task_id_emb = self.speech_embedding.weight[self.task_id].reshape(1, 1, -1)
574
+ elif self.__class__.__name__ == 'Qwen2LM':
575
+ sos_emb = self.llm_embedding.weight[self.sos].reshape(1, 1, -1)
576
+ task_id_emb = self.llm_embedding.weight[self.task_id].reshape(1, 1, -1)
577
+ else:
578
+ raise ValueError
579
+ if prompt_speech_token_len != 0:
580
+ prompt_speech_token_emb = self.speech_embedding(prompt_speech_token)
581
+ else:
582
+ prompt_speech_token_emb = torch.zeros(1, 0, self.llm_input_size, dtype=prompt_text.dtype).to(device)
583
+ lm_input = torch.concat([sos_emb], dim=1)
584
+
585
+ # 2. iterate text
586
+ out_tokens = []
587
+ cache = None
588
+ # NOTE init prompt_text as text_cache as it is basically impossible prompt_speech_token/prompt_text < 15/5
589
+ if self.__class__.__name__ == 'CosyVoice3LM':
590
+ # NOTE temporary hardcode, 151646 is <|endofprompt|> token
591
+ assert 151646 in prompt_text, '<|endofprompt|> not detected in CosyVoice3 prompt_text, check your input!'
592
+ eop_index = prompt_text.flatten().tolist().index(151646)
593
+ lm_input = torch.concat([lm_input, self.llm.model.model.embed_tokens(prompt_text[:, :eop_index + 1])], dim=1)
594
+ prompt_text = prompt_text[:, eop_index + 1:]
595
+ text_cache = self.llm.model.model.embed_tokens(prompt_text)
596
+ next_fill_index = (int(prompt_speech_token.shape[1] / self.mix_ratio[1]) + 1) * self.mix_ratio[1] - prompt_speech_token.shape[1]
597
+ for this_text in text:
598
+ text_cache = torch.concat([text_cache, self.llm.model.model.embed_tokens(this_text)], dim=1)
599
+ # prompt_speech_token_emb not empty, try append to lm_input
600
+ while prompt_speech_token_emb.size(1) != 0:
601
+ if text_cache.size(1) >= self.mix_ratio[0]:
602
+ lm_input_text, lm_input_speech = text_cache[:, :self.mix_ratio[0]], prompt_speech_token_emb[:, :self.mix_ratio[1]]
603
+ logging.info('append {} text token {} speech token'.format(lm_input_text.size(1), lm_input_speech.size(1)))
604
+ lm_input = torch.concat([lm_input, lm_input_text, lm_input_speech], dim=1)
605
+ text_cache, prompt_speech_token_emb = text_cache[:, self.mix_ratio[0]:], prompt_speech_token_emb[:, self.mix_ratio[1]:]
606
+ else:
607
+ logging.info('not enough text token to decode, wait for more')
608
+ break
609
+ # no prompt_speech_token_emb remain, can decode some speech token
610
+ if prompt_speech_token_emb.size(1) == 0:
611
+ if (len(out_tokens) != 0 and out_tokens[-1] == self.fill_token) or (len(out_tokens) == 0 and lm_input.size(1) == 1):
612
+ logging.info('get fill token, need to append more text token')
613
+ if text_cache.size(1) >= self.mix_ratio[0]:
614
+ lm_input_text = text_cache[:, :self.mix_ratio[0]]
615
+ logging.info('append {} text token'.format(lm_input_text.size(1)))
616
+ if len(out_tokens) != 0 and out_tokens[-1] == self.fill_token:
617
+ lm_input = lm_input_text
618
+ else:
619
+ lm_input = torch.concat([lm_input, lm_input_text], dim=1)
620
+ text_cache = text_cache[:, self.mix_ratio[0]:]
621
+ else:
622
+ logging.info('not enough text token to decode, wait for more')
623
+ continue
624
+ while True:
625
+ seq_len = lm_input.shape[1] if cache is None else lm_input.shape[1] + cache[0][0].size(2)
626
+ y_pred, cache = self.llm.forward_one_step(lm_input,
627
+ masks=torch.tril(torch.ones((1, seq_len, seq_len), device=lm_input.device)).to(torch.bool),
628
+ cache=cache)
629
+ logp = self.llm_decoder(y_pred[:, -1].to(dtype=self.llm_decoder.weight.dtype)).log_softmax(dim=-1)
630
+ if next_fill_index != -1 and len(out_tokens) == next_fill_index:
631
+ top_ids = self.fill_token
632
+ next_fill_index += (self.mix_ratio[1] + 1)
633
+ else:
634
+ top_ids = self.sampling_ids(logp.squeeze(dim=0), out_tokens, sampling, ignore_eos=True)
635
+ if top_ids == self.fill_token:
636
+ next_fill_index = len(out_tokens) + self.mix_ratio[1] + 1
637
+ logging.info('fill_token index {} next fill_token index {}'.format(len(out_tokens), next_fill_index))
638
+ out_tokens.append(top_ids)
639
+ if top_ids >= self.speech_token_size:
640
+ if top_ids == self.fill_token:
641
+ break
642
+ else:
643
+ raise ValueError('should not get token {}'.format(top_ids))
644
+ yield top_ids
645
+ lm_input = self.speech_embedding.weight[top_ids].reshape(1, 1, -1)
646
+
647
+ # 3. final decode
648
+ lm_input = torch.concat([lm_input, text_cache, task_id_emb], dim=1)
649
+ logging.info('no more text token, decode until met eos')
650
+ while True:
651
+ seq_len = lm_input.shape[1] if cache is None else lm_input.shape[1] + cache[0][0].size(2)
652
+ y_pred, cache = self.llm.forward_one_step(lm_input,
653
+ masks=torch.tril(torch.ones((1, seq_len, seq_len), device=lm_input.device)).to(torch.bool),
654
+ cache=cache)
655
+ logp = self.llm_decoder(y_pred[:, -1].to(dtype=self.llm_decoder.weight.dtype)).log_softmax(dim=-1)
656
+ top_ids = self.sampling_ids(logp.squeeze(dim=0), out_tokens, sampling, ignore_eos=False)
657
+ out_tokens.append(top_ids)
658
+ if top_ids >= self.speech_token_size:
659
+ if top_ids == self.eos_token:
660
+ break
661
+ else:
662
+ raise ValueError('should not get token {}'.format(top_ids))
663
+ # in stream mode, yield token one by one
664
+ yield top_ids
665
+ lm_input = self.speech_embedding.weight[top_ids].reshape(1, 1, -1)
666
+
667
+
668
+ class CosyVoice3LM(Qwen2LM):
669
+ def __init__(
670
+ self,
671
+ llm_input_size: int,
672
+ llm_output_size: int,
673
+ speech_token_size: int,
674
+ llm: torch.nn.Module,
675
+ sampling: Callable,
676
+ length_normalized_loss: bool = True,
677
+ lsm_weight: float = 0.0,
678
+ mix_ratio: List[int] = [5, 15],
679
+ ):
680
+ torch.nn.Module.__init__(self)
681
+ self.llm_input_size = llm_input_size
682
+ self.llm_output_size = llm_output_size
683
+ self.speech_token_size = speech_token_size
684
+ # 2. build speech token language model related modules
685
+ self.sos = speech_token_size + 0
686
+ self.eos_token = speech_token_size + 1
687
+ self.task_id = speech_token_size + 2
688
+ self.fill_token = speech_token_size + 3
689
+
690
+ self.llm = llm
691
+ self.llm_decoder = nn.Linear(llm_output_size, speech_token_size + 200, bias=False)
692
+ self.criterion_ce = LabelSmoothingLoss(
693
+ size=speech_token_size + 200,
694
+ padding_idx=IGNORE_ID,
695
+ smoothing=lsm_weight,
696
+ normalize_length=length_normalized_loss,
697
+ )
698
+
699
+ # 3. [Optional] build speech token related modules
700
+ self.speech_embedding = torch.nn.Embedding(speech_token_size + 200, llm_input_size)
701
+
702
+ # 4. sampling method
703
+ self.sampling = sampling
704
+ self.mix_ratio = mix_ratio
705
+
706
+ # 5. vllm related
707
+ self.stop_token_ids = [speech_token_size + i for i in range(200)]
708
+ self.vllm_output_queue = {}
709
+ if online_feature is True:
710
+ self.speech_token_extractor = SpeechTokenExtractor(model_path=os.path.join(onnx_path, 'speech_tokenizer_v3.batch.onnx'))
third_party/CosyVoice/cosyvoice/tokenizer/__pycache__/tokenizer.cpython-312.pyc ADDED
Binary file (13.7 kB). View file
 
third_party/CosyVoice/cosyvoice/tokenizer/assets/multilingual_zh_ja_yue_char_del.tiktoken ADDED
The diff for this file is too large to render. See raw diff
 
third_party/CosyVoice/cosyvoice/tokenizer/tokenizer.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ from functools import lru_cache
4
+ from typing import Optional
5
+ import torch
6
+ from transformers import AutoTokenizer
7
+ from whisper.tokenizer import Tokenizer
8
+
9
+ import tiktoken
10
+
11
+ LANGUAGES = {
12
+ "en": "english",
13
+ "zh": "chinese",
14
+ "de": "german",
15
+ "es": "spanish",
16
+ "ru": "russian",
17
+ "ko": "korean",
18
+ "fr": "french",
19
+ "ja": "japanese",
20
+ "pt": "portuguese",
21
+ "tr": "turkish",
22
+ "pl": "polish",
23
+ "ca": "catalan",
24
+ "nl": "dutch",
25
+ "ar": "arabic",
26
+ "sv": "swedish",
27
+ "it": "italian",
28
+ "id": "indonesian",
29
+ "hi": "hindi",
30
+ "fi": "finnish",
31
+ "vi": "vietnamese",
32
+ "he": "hebrew",
33
+ "uk": "ukrainian",
34
+ "el": "greek",
35
+ "ms": "malay",
36
+ "cs": "czech",
37
+ "ro": "romanian",
38
+ "da": "danish",
39
+ "hu": "hungarian",
40
+ "ta": "tamil",
41
+ "no": "norwegian",
42
+ "th": "thai",
43
+ "ur": "urdu",
44
+ "hr": "croatian",
45
+ "bg": "bulgarian",
46
+ "lt": "lithuanian",
47
+ "la": "latin",
48
+ "mi": "maori",
49
+ "ml": "malayalam",
50
+ "cy": "welsh",
51
+ "sk": "slovak",
52
+ "te": "telugu",
53
+ "fa": "persian",
54
+ "lv": "latvian",
55
+ "bn": "bengali",
56
+ "sr": "serbian",
57
+ "az": "azerbaijani",
58
+ "sl": "slovenian",
59
+ "kn": "kannada",
60
+ "et": "estonian",
61
+ "mk": "macedonian",
62
+ "br": "breton",
63
+ "eu": "basque",
64
+ "is": "icelandic",
65
+ "hy": "armenian",
66
+ "ne": "nepali",
67
+ "mn": "mongolian",
68
+ "bs": "bosnian",
69
+ "kk": "kazakh",
70
+ "sq": "albanian",
71
+ "sw": "swahili",
72
+ "gl": "galician",
73
+ "mr": "marathi",
74
+ "pa": "punjabi",
75
+ "si": "sinhala",
76
+ "km": "khmer",
77
+ "sn": "shona",
78
+ "yo": "yoruba",
79
+ "so": "somali",
80
+ "af": "afrikaans",
81
+ "oc": "occitan",
82
+ "ka": "georgian",
83
+ "be": "belarusian",
84
+ "tg": "tajik",
85
+ "sd": "sindhi",
86
+ "gu": "gujarati",
87
+ "am": "amharic",
88
+ "yi": "yiddish",
89
+ "lo": "lao",
90
+ "uz": "uzbek",
91
+ "fo": "faroese",
92
+ "ht": "haitian creole",
93
+ "ps": "pashto",
94
+ "tk": "turkmen",
95
+ "nn": "nynorsk",
96
+ "mt": "maltese",
97
+ "sa": "sanskrit",
98
+ "lb": "luxembourgish",
99
+ "my": "myanmar",
100
+ "bo": "tibetan",
101
+ "tl": "tagalog",
102
+ "mg": "malagasy",
103
+ "as": "assamese",
104
+ "tt": "tatar",
105
+ "haw": "hawaiian",
106
+ "ln": "lingala",
107
+ "ha": "hausa",
108
+ "ba": "bashkir",
109
+ "jw": "javanese",
110
+ "su": "sundanese",
111
+ "yue": "cantonese",
112
+ "minnan": "minnan",
113
+ "wuyu": "wuyu",
114
+ "dialect": "dialect",
115
+ "zh/en": "zh/en",
116
+ "en/zh": "en/zh",
117
+ }
118
+
119
+ # language code lookup by name, with a few language aliases
120
+ TO_LANGUAGE_CODE = {
121
+ **{language: code for code, language in LANGUAGES.items()},
122
+ "burmese": "my",
123
+ "valencian": "ca",
124
+ "flemish": "nl",
125
+ "haitian": "ht",
126
+ "letzeburgesch": "lb",
127
+ "pushto": "ps",
128
+ "panjabi": "pa",
129
+ "moldavian": "ro",
130
+ "moldovan": "ro",
131
+ "sinhalese": "si",
132
+ "castilian": "es",
133
+ "mandarin": "zh",
134
+ }
135
+
136
+ AUDIO_EVENT = {
137
+ "ASR": "ASR",
138
+ "AED": "AED",
139
+ "SER": "SER",
140
+ "Speech": "Speech",
141
+ "/Speech": "/Speech",
142
+ "BGM": "BGM",
143
+ "/BGM": "/BGM",
144
+ "Laughter": "Laughter",
145
+ "/Laughter": "/Laughter",
146
+ "Applause": "Applause",
147
+ "/Applause": "/Applause",
148
+ }
149
+
150
+ EMOTION = {
151
+ "HAPPY": "HAPPY",
152
+ "SAD": "SAD",
153
+ "ANGRY": "ANGRY",
154
+ "NEUTRAL": "NEUTRAL",
155
+ }
156
+
157
+ TTS_Vocal_Token = {
158
+ "TTS/B": "TTS/B",
159
+ "TTS/O": "TTS/O",
160
+ "TTS/Q": "TTS/Q",
161
+ "TTS/A": "TTS/A",
162
+ "TTS/CO": "TTS/CO",
163
+ "TTS/CL": "TTS/CL",
164
+ "TTS/H": "TTS/H",
165
+ **{f"TTS/SP{i:02d}": f"TTS/SP{i:02d}" for i in range(1, 14)}
166
+ }
167
+
168
+
169
+ @lru_cache(maxsize=None)
170
+ def get_encoding(name: str = "gpt2", num_languages: int = 99):
171
+ vocab_path = os.path.join(os.path.dirname(__file__), "assets", f"{name}.tiktoken")
172
+ ranks = {
173
+ base64.b64decode(token): int(rank)
174
+ for token, rank in (line.split() for line in open(vocab_path) if line)
175
+ }
176
+ n_vocab = len(ranks)
177
+ special_tokens = {}
178
+
179
+ specials = [
180
+ "<|endoftext|>",
181
+ "<|startoftranscript|>",
182
+ *[f"<|{lang}|>" for lang in list(LANGUAGES.keys())[:num_languages]],
183
+ *[f"<|{audio_event}|>" for audio_event in list(AUDIO_EVENT.keys())],
184
+ *[f"<|{emotion}|>" for emotion in list(EMOTION.keys())],
185
+ "<|translate|>",
186
+ "<|transcribe|>",
187
+ "<|startoflm|>",
188
+ "<|startofprev|>",
189
+ "<|nospeech|>",
190
+ "<|notimestamps|>",
191
+ *[f"<|SPECIAL_TOKEN_{i}|>" for i in range(1, 31)], # register special tokens for ASR
192
+ *[f"<|{tts}|>" for tts in list(TTS_Vocal_Token.keys())], # register special tokens for TTS
193
+ *[f"<|{i * 0.02:.2f}|>" for i in range(1501)],
194
+ ]
195
+
196
+ for token in specials:
197
+ special_tokens[token] = n_vocab
198
+ n_vocab += 1
199
+
200
+ return tiktoken.Encoding(
201
+ name=os.path.basename(vocab_path),
202
+ explicit_n_vocab=n_vocab,
203
+ pat_str=r"""'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""",
204
+ mergeable_ranks=ranks,
205
+ special_tokens=special_tokens,
206
+ )
207
+
208
+
209
+ @lru_cache(maxsize=None)
210
+ def get_tokenizer(
211
+ multilingual: bool,
212
+ *,
213
+ num_languages: int = 99,
214
+ language: Optional[str] = None,
215
+ task: Optional[str] = None, # Literal["transcribe", "translate", None]
216
+ ) -> Tokenizer:
217
+ if language is not None:
218
+ language = language.lower()
219
+ if language not in LANGUAGES:
220
+ if language in TO_LANGUAGE_CODE:
221
+ language = TO_LANGUAGE_CODE[language]
222
+ else:
223
+ raise ValueError(f"Unsupported language: {language}")
224
+
225
+ if multilingual:
226
+ encoding_name = "multilingual_zh_ja_yue_char_del"
227
+ language = language or "en"
228
+ task = task or "transcribe"
229
+ else:
230
+ encoding_name = "gpt2"
231
+ language = None
232
+ task = None
233
+
234
+ encoding = get_encoding(name=encoding_name, num_languages=num_languages)
235
+
236
+ return Tokenizer(
237
+ encoding=encoding, num_languages=num_languages, language=language, task=task
238
+ )
239
+
240
+
241
+ class CosyVoice2Tokenizer():
242
+ def __init__(self, token_path, skip_special_tokens=True):
243
+ super().__init__()
244
+ # NOTE: non-chat model, all these special tokens keep randomly initialized.
245
+ special_tokens = {
246
+ 'eos_token': '<|endoftext|>',
247
+ 'pad_token': '<|endoftext|>',
248
+ 'additional_special_tokens': [
249
+ '<|im_start|>', '<|im_end|>', '<|endofprompt|>',
250
+ '[breath]', '<strong>', '</strong>', '[noise]',
251
+ '[laughter]', '[cough]', '[clucking]', '[accent]',
252
+ '[quick_breath]',
253
+ "<laughter>", "</laughter>",
254
+ "[hissing]", "[sigh]", "[vocalized-noise]",
255
+ "[lipsmack]", "[mn]"
256
+ ]
257
+ }
258
+ self.special_tokens = special_tokens
259
+ self.tokenizer = AutoTokenizer.from_pretrained(token_path)
260
+ self.tokenizer.add_special_tokens(special_tokens)
261
+ self.skip_special_tokens = skip_special_tokens
262
+
263
+ def encode(self, text, **kwargs):
264
+ tokens = self.tokenizer([text], return_tensors="pt")
265
+ tokens = tokens["input_ids"][0].cpu().tolist()
266
+ return tokens
267
+
268
+ def decode(self, tokens):
269
+ tokens = torch.tensor(tokens, dtype=torch.int64)
270
+ text = self.tokenizer.batch_decode([tokens], skip_special_tokens=self.skip_special_tokens)[0]
271
+ return text
272
+
273
+
274
+ class CosyVoice3Tokenizer(CosyVoice2Tokenizer):
275
+ def __init__(self, token_path, skip_special_tokens=True):
276
+ # NOTE: non-chat model, all these special tokens keep randomly initialized.
277
+ special_tokens = {
278
+ 'eos_token': '<|endoftext|>',
279
+ 'pad_token': '<|endoftext|>',
280
+ 'additional_special_tokens': [
281
+ '<|im_start|>', '<|im_end|>', '<|endofprompt|>',
282
+ '[breath]', '<strong>', '</strong>', '[noise]',
283
+ '[laughter]', '[cough]', '[clucking]', '[accent]',
284
+ '[quick_breath]',
285
+ "<laughter>", "</laughter>",
286
+ "[hissing]", "[sigh]", "[vocalized-noise]",
287
+ "[lipsmack]", "[mn]", "<|endofsystem|>",
288
+ "[AA]", "[AA0]", "[AA1]", "[AA2]", "[AE]", "[AE0]", "[AE1]", "[AE2]", "[AH]", "[AH0]", "[AH1]", "[AH2]",
289
+ "[AO]", "[AO0]", "[AO1]", "[AO2]", "[AW]", "[AW0]", "[AW1]", "[AW2]", "[AY]", "[AY0]", "[AY1]", "[AY2]",
290
+ "[B]", "[CH]", "[D]", "[DH]", "[EH]", "[EH0]", "[EH1]", "[EH2]", "[ER]", "[ER0]", "[ER1]", "[ER2]", "[EY]",
291
+ "[EY0]", "[EY1]", "[EY2]", "[F]", "[G]", "[HH]", "[IH]", "[IH0]", "[IH1]", "[IH2]", "[IY]", "[IY0]", "[IY1]",
292
+ "[IY2]", "[JH]", "[K]", "[L]", "[M]", "[N]", "[NG]", "[OW]", "[OW0]", "[OW1]", "[OW2]", "[OY]", "[OY0]",
293
+ "[OY1]", "[OY2]", "[P]", "[R]", "[S]", "[SH]", "[T]", "[TH]", "[UH]", "[UH0]", "[UH1]", "[UH2]", "[UW]",
294
+ "[UW0]", "[UW1]", "[UW2]", "[V]", "[W]", "[Y]", "[Z]", "[ZH]",
295
+ "[a]", "[ai]", "[an]", "[ang]", "[ao]", "[b]", "[c]", "[ch]", "[d]", "[e]", "[ei]", "[en]", "[eng]", "[f]",
296
+ "[g]", "[h]", "[i]", "[ian]", "[in]", "[ing]", "[iu]", "[ià]", "[iàn]", "[iàng]", "[iào]", "[iá]", "[ián]",
297
+ "[iáng]", "[iáo]", "[iè]", "[ié]", "[iòng]", "[ióng]", "[iù]", "[iú]", "[iā]", "[iān]", "[iāng]", "[iāo]",
298
+ "[iē]", "[iě]", "[iōng]", "[iū]", "[iǎ]", "[iǎn]", "[iǎng]", "[iǎo]", "[iǒng]", "[iǔ]", "[j]", "[k]", "[l]",
299
+ "[m]", "[n]", "[o]", "[ong]", "[ou]", "[p]", "[q]", "[r]", "[s]", "[sh]", "[t]", "[u]", "[uang]", "[ue]",
300
+ "[un]", "[uo]", "[uà]", "[uài]", "[uàn]", "[uàng]", "[uá]", "[uái]", "[uán]", "[uáng]", "[uè]", "[ué]", "[uì]",
301
+ "[uí]", "[uò]", "[uó]", "[uā]", "[uāi]", "[uān]", "[uāng]", "[uē]", "[uě]", "[uī]", "[uō]", "[uǎ]", "[uǎi]",
302
+ "[uǎn]", "[uǎng]", "[uǐ]", "[uǒ]", "[vè]", "[w]", "[x]", "[y]", "[z]", "[zh]", "[à]", "[ài]", "[àn]", "[àng]",
303
+ "[ào]", "[á]", "[ái]", "[án]", "[��ng]", "[áo]", "[è]", "[èi]", "[èn]", "[èng]", "[èr]", "[é]", "[éi]", "[én]",
304
+ "[éng]", "[ér]", "[ì]", "[ìn]", "[ìng]", "[í]", "[ín]", "[íng]", "[ò]", "[òng]", "[òu]", "[ó]", "[óng]", "[óu]",
305
+ "[ù]", "[ùn]", "[ú]", "[ún]", "[ā]", "[āi]", "[ān]", "[āng]", "[āo]", "[ē]", "[ēi]", "[ēn]", "[ēng]", "[ě]",
306
+ "[ěi]", "[ěn]", "[ěng]", "[ěr]", "[ī]", "[īn]", "[īng]", "[ō]", "[ōng]", "[ōu]", "[ū]", "[ūn]", "[ǎ]", "[ǎi]",
307
+ "[ǎn]", "[ǎng]", "[ǎo]", "[ǐ]", "[ǐn]", "[ǐng]", "[ǒ]", "[ǒng]", "[ǒu]", "[ǔ]", "[ǔn]", "[ǘ]", "[ǚ]", "[ǜ]"
308
+ ]
309
+ }
310
+ self.special_tokens = special_tokens
311
+ self.tokenizer = AutoTokenizer.from_pretrained(token_path)
312
+ self.tokenizer.add_special_tokens(special_tokens)
313
+ self.skip_special_tokens = skip_special_tokens
314
+
315
+
316
+ @lru_cache(maxsize=None)
317
+ def get_qwen_tokenizer(
318
+ token_path: str,
319
+ skip_special_tokens: bool,
320
+ version: str = 'cosyvoice2'
321
+ ):
322
+ if version == 'cosyvoice2':
323
+ return CosyVoice2Tokenizer(token_path=token_path, skip_special_tokens=skip_special_tokens)
324
+ elif version == 'cosyvoice3':
325
+ return CosyVoice3Tokenizer(token_path=token_path, skip_special_tokens=skip_special_tokens)
326
+ else:
327
+ raise ValueError
third_party/CosyVoice/cosyvoice/transformer/__init__.py ADDED
File without changes
third_party/CosyVoice/cosyvoice/transformer/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (219 Bytes). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/activation.cpython-312.pyc ADDED
Binary file (3.36 kB). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/attention.cpython-312.pyc ADDED
Binary file (15.1 kB). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/convolution.cpython-312.pyc ADDED
Binary file (11.3 kB). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/embedding.cpython-312.pyc ADDED
Binary file (16.9 kB). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/encoder_layer.cpython-312.pyc ADDED
Binary file (11.1 kB). View file
 
third_party/CosyVoice/cosyvoice/transformer/__pycache__/label_smoothing_loss.cpython-312.pyc ADDED
Binary file (4.13 kB). View file