File size: 6,509 Bytes
2c31869
 
 
 
 
 
 
 
 
 
37c0b74
2c31869
 
 
37c0b74
2c31869
 
 
 
37c0b74
2c31869
 
 
 
 
37c0b74
2c31869
 
 
 
 
 
 
 
 
 
 
37c0b74
2c31869
 
37c0b74
2c31869
 
 
 
 
 
 
37c0b74
2c31869
 
37c0b74
2c31869
 
 
 
 
37c0b74
2c31869
 
37c0b74
2c31869
 
 
 
37c0b74
2c31869
 
37c0b74
2c31869
 
 
37c0b74
 
2c31869
37c0b74
 
 
 
 
2c31869
37c0b74
 
2c31869
37c0b74
 
 
 
 
 
 
 
2c31869
 
 
37c0b74
2c31869
 
 
 
 
37c0b74
2c31869
 
 
 
37c0b74
2c31869
37c0b74
2c31869
1e245b2
 
 
37c0b74
1e245b2
 
 
2c31869
 
 
 
 
 
 
 
 
 
37c0b74
 
 
2c31869
37c0b74
 
2c31869
37c0b74
2c31869
 
37c0b74
2c31869
37c0b74
 
2c31869
37c0b74
2c31869
 
37c0b74
2c31869
37c0b74
2c31869
37c0b74
 
2c31869
37c0b74
2c31869
 
37c0b74
2c31869
37c0b74
2c31869
 
 
 
 
 
 
 
 
 
 
 
37c0b74
2c31869
 
 
37c0b74
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import json
import logging
import os
import sys
import time
import argparse
import requests
from urllib.parse import urlparse
import warnings

# --- 抑制警告信息 ---
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=UserWarning)

# --- 设置系统路径 ---
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_dir)
sys.path.append(os.path.join(current_dir, "indextts"))

# --- 导入本地模块 ---
from indextts.infer_v2 import IndexTTS2
from tools.download_files import download_model_from_huggingface

def download_file(url, save_dir="temp_audio"):
    """
    从 URL 下载文件,或者如果路径是本地文件则直接返回路径。
    """
    os.makedirs(save_dir, exist_ok=True)
    
    try:
        result = urlparse(url)
        is_url = all([result.scheme, result.netloc])
    except ValueError:
        is_url = False

    if not is_url:
        if os.path.exists(url):
            print(f"使用本地文件: {url}")
            return url
        else:
            raise FileNotFoundError(f"本地文件未找到: {url}")

    filename = os.path.basename(result.path)
    if not filename:
        filename = f"audio_{int(time.time())}.wav"
        
    save_path = os.path.join(save_dir, filename)
    
    print(f"正在从 {url} 下载音频到 {save_path}...")
    try:
        response = requests.get(url, stream=True, timeout=30)
        response.raise_for_status()
        
        with open(save_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
        
        print("下载完成。")
        return save_path
    except requests.exceptions.RequestException as e:
        print(f"下载文件时出错: {e}")
        raise

def main():
    """
    运行命令行文本转语音应用的主函数。
    """
    parser = argparse.ArgumentParser(
        description="IndexTTS: 命令行文本转语音应用",
        formatter_class=argparse.RawTextHelpFormatter
    )
    
    parser.add_argument("--prompt", type=str, required=True, help="需要合成的文本。")
    parser.add_argument("--input_audio", type=str, required=True, help="音色参考音频的 URL 或本地路径 (.wav)。")
    parser.add_argument("--setting", type=int, choices=[1, 2, 3, 4], required=True, 
                        help="情感控制方法:\n"
                             "1: 与音色参考音频相同。\n"
                             "2: 使用单独的情感参考音频。\n"
                             "3: 使用情感向量。\n"
                             "4: 使用文本描述来控制情感。")
    
    parser.add_argument("--emo_audio", type=str, help="情感参考音频的 URL 或本地路径 (setting 2 必需)。")
    parser.add_argument("--emo_weight", type=float, default=0.8, help="情感权重 (setting 2, 默认: 0.8)。")
    parser.add_argument("--emo_vectors", type=float, nargs=8, 
                        metavar=('喜', '怒', '哀', '惧', '厌恶', '低落', '惊喜', '平静'), 
                        help="八个情感向量值,用空格分隔 (setting 3 必需)。")
    parser.add_argument("--emo_text", type=str, help="情感描述文本,例如 '高兴', '伤心' (setting 4 必需)。")

    parser.add_argument("--output_path", type=str, default=None, help="保存输出音频的路径。如果未提供,将使用默认名称。")
    parser.add_argument("--model_dir", type=str, default="checkpoints", help="模型检查点目录。")
    parser.add_argument("--is_fp16", action="store_true", default=False, help="启用 fp16 推理。")
    parser.add_argument("--verbose", action="store_true", default=False, help="启用详细日志记录。")
    
    args = parser.parse_args()

    print("正在检查模型文件...")
    download_model_from_huggingface(
        os.path.join(current_dir, "checkpoints"),
        os.path.join(current_dir, "checkpoints", "hf_cache")
    )

    print("正在加载 IndexTTS 模型...")
    tts = IndexTTS2(
        model_dir=args.model_dir,
        cfg_path=os.path.join(args.model_dir, "config.yaml"),
        is_fp16=args.is_fp16,
        use_cuda_kernel=False
    )
    print("模型加载成功。")

    # --- 主要修改 1 ---
    # 确保名为 "output" 的文件夹存在
    os.makedirs("output", exist_ok=True)
    
    # --- 主要修改 2 ---
    # 如果用户没有通过 --output_path 指定路径,则默认使用 'output/output.wav'
    output_path = args.output_path or os.path.join("output", "output.wav")
    
    prompt_audio_path = download_file(args.input_audio)

    emo_audio_prompt = None
    emo_alpha = 1.0
    emo_vector = None
    use_emo_text = False
    emo_text_val = ""
    emo_control_method = args.setting - 1

    if emo_control_method == 0:
        print("使用音色参考音频中的情感。")
        pass

    elif emo_control_method == 1:
        print("使用独立的情感参考音频。")
        if not args.emo_audio:
            parser.error("--emo_audio 参数在 setting 2 中是必需的。")
        emo_audio_prompt = download_file(args.emo_audio)
        emo_alpha = args.emo_weight
        print(f"情感参考: {emo_audio_prompt}, 权重: {emo_alpha}")

    elif emo_control_method == 2:
        print("使用情感向量进行控制。")
        if not args.emo_vectors:
            parser.error("--emo_vectors 参数在 setting 3 中是必需的。")
        vec_sum = sum(args.emo_vectors)
        if vec_sum > 1.5:
            raise ValueError(f"情感向量的总和不能超过1.5。当前总和: {vec_sum}")
        emo_vector = args.emo_vectors
        print(f"情感向量: {emo_vector}")

    elif emo_control_method == 3:
        print("使用文本描述来控制情感。")
        if not args.emo_text:
            parser.error("--emo_text 参数在 setting 4 中是必需的。")
        use_emo_text = True
        emo_text_val = args.emo_text
        print(f"情感文本: '{emo_text_val}'")

    print("\n开始 TTS 推理...")
    tts.infer(
        spk_audio_prompt=prompt_audio_path,
        text=args.prompt,
        output_path=output_path,
        emo_audio_prompt=emo_audio_prompt,
        emo_alpha=emo_alpha,
        emo_vector=emo_vector,
        use_emo_text=use_emo_text,
        emo_text=emo_text_val,
        verbose=args.verbose,
    )

    print(f"\n✨ 推理完成!音频已保存至: {output_path}")

if __name__ == "__main__":
    main()