我自己能用最终版但是没跑通v2版本 v1版# -*- coding: utf-8 -*- 字节跳动语音合成TTS示例逐句合成并输出 WAV 文件 功能 1. 从本地文本文件按行读取待合成语音的文本 2. 调用字节跳动 OpenSpeech TTS 接口逐句合成 PCM 音频 3. 在句间插入可配置的静音间隔 4. 将 PCM 转成 WAV 并保存到本地 注意 - 请替换下方以 YOUR_ 开头的占位符为你在开放平台申请的真实值 - 本示例仅供学习与分享生产环境请结合缓存、错误重试等机制使用 import base64 import json import struct import uuid import requests import time # 核心配置请替换为你在开放平台申请的真实值 # APP ID APPID YOUR_APPID_96114514 # Access Token ACCESS_TOKEN YOUR_ACCESS_TOKEN_ABCDEFGH # 资源 ID一般保持默认即可如需更换请参考官方文档 (全是坑) RESOURCE_ID seed-icl-1.0 # 音色 ID示例S_Hf2o1919810RX1 - YOUR_SPEAKER_ID_XXX SPEAKER YOUR_SPEAKER_ID_XXX # 输入文本文件路径每行一句 INPUT_TXT txt.txt # 输出 WAV 文件路径 OUTPUT_WAV output.wav # 句间停顿时长毫秒 SILENCE_MS 30 # 情感参数 # 语速 [-50, 100]负值慢正值快稍微慢一点情感更饱满 SPEECH_RATE 1 # 音量 [-50, 100]负值小正值大 LOUDNESS_RATE 6 # 音调 [-12, 12]正值高负值低 PITCH 0 # def pcm_to_wav(pcm_data, sample_rate24000): 将 PCM 数据转换为 WAV 格式 参数: pcm_data: bytes原始 PCM 音频数据16-bit 单声道 sample_rate: int采样率默认 24000 Hz 返回: bytes完整的 WAV 文件二进制数据含文件头 bits 16 channels 1 byte_rate sample_rate * channels * bits // 8 block_align channels * bits // 8 riff_size 36 len(pcm_data) # 按照标准 WAV 文件头格式打包 header struct.pack( 4sI4s4sIHHIIHH4sI, bRIFF, riff_size, bWAVE, bfmt , 16, 1, channels, sample_rate, byte_rate, block_align, bits, bdata, len(pcm_data) ) return header pcm_data def make_silence(ms, sample_rate24000): 生成指定毫秒数的静音 PCM 数据 参数: ms: int静音时长毫秒 sample_rate: int采样率默认 24000 Hz 返回: bytes静音 PCM 数据16-bit 单声道全零 # 计算采样点数采样率 * 时长 / 1000 samples int(sample_rate * ms / 1000) # 每个采样点 16-bit 2 字节0x0000 表示静音 return b\x00\x00 * samples def synthesize_one(text, session_num, total): 调用字节跳动 TTS 接口合成单句文本 参数: text: str待合成语音的文本单句 session_num: int当前句子序号从 1 开始 total: int总句子数 返回: bytes合成的 PCM 音频数据若失败则返回 None # TTS 接口地址 url https://openspeech.bytedance.com/api/v3/tts/unidirectional # 请求头包含应用标识、访问令牌、资源 ID、请求 ID 等 headers { X-Api-App-Key: APPID, X-Api-Access-Key: ACCESS_TOKEN, X-Api-Resource-Id: RESOURCE_ID, X-Api-Request-Id: str(uuid.uuid4()), # 每次请求唯一 ID Content-Type: application/json; charsetutf-8, } # 额外参数显式语言、后处理音调等 additions { explicit_language: de, # 德语可根据需要修改 post_process: { pitch: PITCH } } additions_str json.dumps(additions, ensure_asciiFalse) # 请求体用户信息 请求参数 payload { user: {uid: str(uuid.uuid4())}, # 用户 ID可自行维护 req_params: { text: text, speaker: SPEAKER, audio_params: { format: pcm, # 输出格式 sample_rate: 24000, # 采样率 speech_rate: SPEECH_RATE, # 语速 loudness_rate: LOUDNESS_RATE, # 音量 }, additions: additions_str } } try: # 发起 HTTP 请求开启流式响应 resp requests.post( url, headersheaders, jsonpayload, streamTrue, # 流式读取适合音频等大响应 timeout60 # 60 秒超时 ) resp.raise_for_status() # 若状态码非 2xx抛出异常 except Exception as e: print(f [ERROR] 请求失败: {e}) if resp is not None: print(f [详情] {resp.text}) return None # 用于累积返回的音频 PCM 数据 audio bytearray() # 按行读取流式响应接口返回多行 JSON for line in resp.iter_lines(chunk_size1024): if not line: continue try: msg json.loads(line) code msg.get(code, -1) data msg.get(data) # 判断业务错误码 if code not in (None, 0, 20000000): print(f [ERROR] 服务器错误: {msg.get(message, )}) return None # 正常返回数据时解码 base64 音频数据 if data and code in (0, 20000000): audio.extend(base64.b64decode(data)) except Exception: # 解析失败的行直接跳过不影响整体流程 pass # 若没有收到任何音频数据则视为失败 if len(audio) 0: print(f [ERROR] 未收到音频) return None return bytes(audio) def main(): 主流程 1. 从文件读取文本并按行切分 2. 逐句调用 TTS 合成 3. 在句间插入静音 4. 将 PCM 转为 WAV 并写入文件 # 1. 读取文本并按行切分 try: with open(INPUT_TXT, r, encodingutf-8) as f: text f.read().strip() except Exception as e: print(f[ERROR] 读取文件失败: {e}) return # 去除空行只保留非空内容 lines [line.strip() for line in text.split(\n) if line.strip()] total len(lines) print(f[INFO] 共 {total} 句待合成) print(f[INFO] 语速: {SPEECH_RATE} 音量: {LOUDNESS_RATE} 音调: {PITCH}) print(f[INFO] 句间停顿: {SILENCE_MS}ms\n) if total 0: print([ERROR] 文本为空) return # 2. 逐句合成 all_pcm bytearray() # 用于累积所有句子的 PCM 数据 success 0 fail 0 t0 time.time() for i, line in enumerate(lines): # 清理不可打印字符但保留德语特殊字符等 clean .join([c for c in line if c.isprintable() or c in äöüßÄÖÜẞ]) if not clean: continue # 打印当前进度截断过长的行 print(f[{i1}/{total}] {clean[:60]}{... if len(clean)60 else }) # 调用接口合成单句 pcm synthesize_one(clean, i1, total) if pcm: all_pcm.extend(pcm) # 若不是最后一句则插入句间静音 if i total - 1 and SILENCE_MS 0: all_pcm.extend(make_silence(SILENCE_MS)) success 1 else: fail 1 # 即使当前句合成失败也保留静音间隔保持时间轴对齐 if i total - 1 and SILENCE_MS 0: all_pcm.extend(make_silence(SILENCE_MS)) elapsed time.time() - t0 # 3. 输出结果 print(f\n) print(f【最终结果】) print(f) print(f成功: {success} 失败: {fail} 耗时: {elapsed:.1f}s) if len(all_pcm) 0: print([ERROR] 没有合成出任何音频) return # PCM - WAV wav_data pcm_to_wav(bytes(all_pcm), sample_rate24000) with open(OUTPUT_WAV, wb) as f: f.write(wav_data) # 计算音频时长秒PCM 字节数 / (采样率 * 2 字节/采样) duration len(all_pcm) / (24000 * 2) print(f[SUCCESS] 合成完成文件: {OUTPUT_WAV}) print(f[INFO] 音频大小: {len(wav_data) / 1024:.2f} KB) print(f[INFO] 音频时长: {duration:.1f}s) if __name__ __main__: main() input(Press any key to continue . . .)20260615测试可用 所有API官方平台一个全跌更新豆包TTS官方APIv1文档官方文档很多 不知道使用哪个 控制台也找不到但是效果是真可以慢慢试吧 这步骤看报错难受的一批正式开始豆包TTS官方音色复刻TTS文档从这里开始 后面忘了上传还是什么已经忘了 反正这里是智能体给的原文 是真有些东西非常好用# -*- coding: utf-8 -*- 火山引擎 声音复刻 TTS 语音合成 完整脚本 功能 1. 查询声音训练状态 2. 提交声音训练任务需提供训练音频文件 3. 训练完成后使用旧版 HTTP 接口合成德语语音 依赖仅需 requestspip install requests 不要使用豆包官方pip包 使用方法 # 查询声音状态 python3 volcengine_tts.py --status # 训练声音需要提供一个音频文件 python3 volcengine_tts.py --train /path/to/audio.wav # 合成语音声音训练完成后 python3 volcengine_tts.py --tts Guten Tag, wie geht es Ihnen? # 合成语音并指定输出文件 python3 volcengine_tts.py --tts Guten Tag -o output.mp3 注意 - 代码中的 APPID / ACCESS_TOKEN / SECRET_KEY / VOICE_ID 均为示例占位符 - 请替换为你在火山引擎控制台申请的真实值切勿泄露密钥 import requests import uuid import json import sys import os import base64 import time import argparse # 配置信息请替换为你在火山引擎控制台申请的真实值 # 应用唯一标识示例9114514 - YOUR_APPID_1234567890 APPID YOUR_APPID_1234567890 # 访问令牌示例19198105 - YOUR_ACCESS_TOKEN_ABCDEFGH ACCESS_TOKEN YOUR_ACCESS_TOKEN_ABCDEFGH # API Key / Secret示例KL_ZqsTUXXXXXXXXXXXXXXXXXd5 - YOUR_SECRET_KEY_XXXX SECRET_KEY YOUR_SECRET_KEY_XXXX # 声音复刻音色 ID示例S_HDPXOOXX01 - YOUR_VOICE_ID_XXX VOICE_ID YOUR_VOICE_ID_XXX # # 接口地址 # V3 接口训练 查询 V3_TRAIN_URL https://openspeech.bytedance.com/api/v3/tts/voice_clone V3_STATUS_URL https://openspeech.bytedance.com/api/v3/tts/get_voice # V1 旧版接口合成 V1_TTS_URL https://openspeech.bytedance.com/api/v1/tts def _v3_headers(): V3 接口通用请求头 包含 - Content-TypeJSON - X-Api-App-KeyAPPID应用标识 - X-Api-Access-KeyACCESS_TOKEN访问令牌 - X-Api-Request-Id请求唯一 ID建议 UUID return { Content-Type: application/json, X-Api-App-Key: APPID, X-Api-Access-Key: ACCESS_TOKEN, X-Api-Request-Id: str(uuid.uuid4()) } def _v1_headers(): V1 旧版接口请求头 认证方式Bearer Token - HeaderAuthorization: Bearer;{ACCESS_TOKEN} - 注意 Bearer 和 token 之间用分号 ; 隔开这是火山引擎 V1 接口的要求 return { Content-Type: application/json, Authorization: fBearer;{ACCESS_TOKEN} } # 1. 查询声音状态 def query_voice_status(): 查询声音复刻训练状态 调用 /api/v3/tts/get_voice 接口根据 VOICE_ID 查询 - 训练状态0未训练1训练中2训练完成3训练失败 - 创建时间、剩余训练次数等信息 print(f[查询] 正在查询声音 {VOICE_ID} 的训练状态...) resp requests.post( V3_STATUS_URL, headers_v3_headers(), json{speaker_id: VOICE_ID}, timeout30 ) data resp.json() status_code data.get(status, -1) status_map { 0: ❌ 未训练需要先提交训练任务, 1: ⏳ 训练中..., 2: ✅ 训练完成可以使用, 3: ❌ 训练失败 } print(f 声音ID: {data.get(speaker_id, N/A)}) print(f 训练状态: {status_map.get(status_code, f未知({status_code}))}) print(f 创建时间: {data.get(create_time, 0)}) print(f 剩余训练次数: {data.get(available_training_times, N/A)}) if status_code 2: print(f\n ✅ 声音已就绪可以使用以下命令合成语音) print(f python3 {sys.argv[0]} --tts 要合成的德语文本) return data # 2. 训练声音 def train_voice(audio_path: str, language: str de): 提交声音复刻训练任务 Args: audio_path: 训练音频文件路径支持 wav/mp3/m4a/flac/ogg language: 语言代码de德语, en英语, zh中文, ja日语, es西班牙语 if not os.path.exists(audio_path): print(f[错误] 音频文件不存在: {audio_path}) return False # 读取音频文件并 base64 编码 file_size os.path.getsize(audio_path) print(f[训练] 读取音频文件: {audio_path} ({file_size} bytes)) with open(audio_path, rb) as f: audio_b64 base64.b64encode(f.read()).decode(utf-8) # 获取音频格式 ext os.path.splitext(audio_path)[1].lower().lstrip(.) format_map { wav: wav, mp3: mp3, m4a: m4a, flac: flac, ogg: ogg, opus: ogg_opus } audio_format format_map.get(ext, wav) # 构建训练请求 # language: 6 表示德语具体枚举值请参考官方文档 payload { speaker_id: VOICE_ID, audio: { data: audio_b64, format: audio_format }, language: 6, display_name: fgerman_voice_{VOICE_ID} } print(f[训练] 提交训练任务 (语言: {language}, 格式: {audio_format})...) resp requests.post( V3_TRAIN_URL, headers_v3_headers(), jsonpayload, timeout60 ) data resp.json() print(f[训练] 响应: {json.dumps(data, ensure_asciiFalse, indent2)}) if data.get(status_code) 0 or data.get(code) 0: print(f\n✅ 训练任务提交成功) print(f 声音ID: {VOICE_ID}) print(f 请等待训练完成通常需要几分钟) print(f 使用以下命令查询训练状态) print(f python3 {sys.argv[0]} --status) return True else: print(f\n❌ 训练任务提交失败) return False # 3. TTS 语音合成旧版 HTTP 接口 def synthesize_speech(text: str, output_file: str output.mp3, encoding: str mp3, speed_ratio: float 1.0, volume_ratio: float 1.0, pitch_ratio: float 1.0): 使用旧版 V1 HTTP 接口合成语音 接口地址https://openspeech.bytedance.com/api/v1/tts 认证方式Authorization: Bearer;{ACCESS_TOKEN} Args: text: 要合成的文本 output_file: 输出音频文件路径 encoding: 音频格式 (mp3/wav/pcm/opus) speed_ratio: 语速 (0.2~3.0) volume_ratio: 音量 (0.1~3.0) pitch_ratio: 音调 (0.1~3.0) print(f[合成] 使用旧版 HTTP 接口合成语音...) print(f 声音ID: {VOICE_ID}) print(f 文本: {text[:80]}{... if len(text) 80 else }) print(f 格式: {encoding}) # 构造 V1 TTS 请求体 payload { app: { appid: APPID, token: ACCESS_TOKEN, cluster: volcano_icl # 声音复刻专用 cluster }, user: { uid: tts_user_001 }, audio: { voice_type: VOICE_ID, encoding: encoding, speed_ratio: speed_ratio, volume_ratio: volume_ratio, pitch_ratio: pitch_ratio }, request: { reqid: str(uuid.uuid4()), text: text, text_type: plain, operation: query } } try: resp requests.post( V1_TTS_URL, headers_v1_headers(), jsonpayload, timeout30 ) if resp.status_code 200: content_type resp.headers.get(Content-Type, ) # 判断响应是否是音频流 if audio in content_type or octet-stream in content_type or len(resp.content) 1000: with open(output_file, wb) as f: f.write(resp.content) print(f\n✅ 合成成功) print(f 文件: {output_file}) print(f 大小: {len(resp.content)} bytes) return True else: # 返回的是 JSON 错误信息 try: error resp.json() code error.get(code, ) msg error.get(message, ) print(f\n❌ 合成失败: [{code}] {msg}) # 常见错误声音未训练 if not been trained in msg or not trained in msg: print(f\n 声音尚未训练请先训练声音) print(f python3 {sys.argv[0]} --train /path/to/audio.wav) except: print(f\n❌ 未知响应: {resp.text[:500]}) return False else: print(f\n❌ HTTP 错误: {resp.status_code}) try: print(f {json.dumps(resp.json(), ensure_asciiFalse, indent2)}) except: print(f {resp.text[:500]}) return False except requests.exceptions.Timeout: print(\n❌ 请求超时请检查网络) return False except requests.exceptions.ConnectionError: print(\n❌ 连接失败请检查网络) return False except Exception as e: print(f\n❌ {type(e).__name__}: {e}) return False # 主程序 def main(): parser argparse.ArgumentParser(description火山引擎 声音复刻 TTS 语音合成) parser.add_argument(--status, actionstore_true, help查询声音训练状态) parser.add_argument(--train, metavarAUDIO_FILE, help训练声音需提供音频文件路径) parser.add_argument(--tts, metavarTEXT, help合成语音输入要合成的文本) parser.add_argument(-o, --output, defaultoutput.mp3, help输出音频文件路径默认: output.mp3) parser.add_argument(--lang, defaultde, help训练语言默认: de 德语) parser.add_argument(--encoding, defaultmp3, help音频格式默认: mp3) parser.add_argument(--speed, typefloat, default1.0, help语速默认: 1.0) args parser.parse_args() if args.status: query_voice_status() elif args.train: train_voice(args.train, languageargs.lang) elif args.tts: synthesize_speech( textargs.tts, output_fileargs.output, encodingargs.encoding, speed_ratioargs.speed ) else: # 默认查询状态 尝试合成 print( * 60) print( 火山引擎 声音复刻 TTS 语音合成) print( * 60) print() # 先查询状态 status_data query_voice_status() status status_data.get(status, -1) if status 2: # 已训练直接合成 print() german_text Guten Tag, wie geht es Ihnen? Ich freue mich, Sie kennenzulernen. synthesize_speech(german_text, output_fileargs.output) else: print() print( 使用说明) print(f 1. 查询声音状态: python3 {sys.argv[0]} --status) print(f 2. 训练声音: python3 {sys.argv[0]} --train /path/to/audio.wav) print(f 3. 合成语音: python3 {sys.argv[0]} --tts \要合成的文本\) print() print( 训练音频要求) print( - 格式wav/mp3/m4a/flac/ogg) print( - 时长建议 10~60 秒) print( - 内容清晰的德语语音无背景噪音) print( - 采样率建议 16kHz 或以上) if __name__ __main__: main()测试音色# -*- coding: utf-8 -*- 查询声音复刻音色状态示例/api/v3/tts/get_voice 功能 1. 调用字节跳动 OpenSpeech TTS 的 get_voice 接口 2. 根据 speaker_id 查询该音色的训练状态与模型类型 3. 打印 HTTP 状态码、完整响应体并解读 model_type 含义 注意 - APPID / ACCESS_TOKEN / SPEAKER 属于敏感信息请替换为你在开放平台申请的真实值 - 本文仅供部分参考 如有不适请自行判断 import json import uuid import requests # 核心配置请替换为你在开放平台申请的真实值 # 应用唯一标识示例114514 - YOUR_APPID_1234567890 APPID YOUR_APPID_1234567890 # 访问令牌示例1919810 - YOUR_ACCESS_TOKEN_ABCDEFGH ACCESS_TOKEN YOUR_ACCESS_TOKEN_ABCDEFGH # 音色 ID示例S_HDDD2111 - YOUR_SPEAKER_ID_XXX SPEAKER YOUR_SPEAKER_ID_XXX # # 查询音色状态的接口地址 url https://openspeech.bytedance.com/api/v3/tts/get_voice # 请求头包含应用标识、访问令牌、请求 ID 等 headers { Content-Type: application/json, X-Api-App-Key: APPID, X-Api-Access-Key: ACCESS_TOKEN, X-Api-Request-Id: str(uuid.uuid4()), # 每次请求唯一 ID } # 请求体指定要查询的 speaker_id payload { speaker_id: SPEAKER, } # 发起 POST 请求 resp requests.post( url, headersheaders, jsonpayload, ) # 打印 HTTP 状态码方便排查网络或鉴权问题 print(HTTP 状态码:, resp.status_code) # 解析并格式化输出响应 JSON data resp.json() print(json.dumps(data, indent2, ensure_asciiFalse)) # 解读 model_type根据官方文档model_type 对应不同的声音复刻版本和资源 ID if speaker_status in data: print(\n 解读 ) # model_type 映射表不同版本对应不同的 resource_id 和前缀规则 MODEL_TYPE_MAP { 1: 声音复刻 ICL V1 → resource_id seed-icl-1.0, 2: 声音复刻 DiT 标准版 → resource_id seed-icl-2.0saturn_ 前缀音色, 3: 声音复刻 DiT 还原版 → resource_id seed-icl-2.0saturn_ 前缀音色, 4: 声音复刻 ICL V2 → resource_id seed-icl-2.0, 5: 声音复刻 ICL V3 → resource_id seed-icl-2.0, } # 遍历 speaker_status 列表逐个解读 model_type for s in data[speaker_status]: mt s.get(model_type, ?) print(f model_type {mt}: {MODEL_TYPE_MAP.get(mt, 未知)})