Qwen-TTS-Realtime声音复刻

Qwen-TTS-Realtime 声音复刻依托大模型进行特征提取,无需训练即可复刻声音。仅需提供 10~20 秒的音频,即可生成高度相似且听感自然的定制音色。

用户指南:关于模型介绍和选型建议请参见实时语音合成-通义千问

在线体验:暂不支持。

应用场景

  • 陪伴场景:复刻家人声音用于智能助手、车载导航、家庭娱乐(朗读绘本、控制家电、教育辅导)。

  • 教育场景:复刻教师声音,增强互动,丰富教学视频与课件。

  • 音视频产业:复刻主播/配音员声音,提升补录与配音效率。

  • 智能客服:复刻客户经理声音,用于回访与营销电话,增强个性化和人性化。

功能与限制

  • 支持的模型:目前仅qwen-tts-vc-realtime-2025-08-20

    ⚠️声音复刻和语音合成使用的模型必须保持一致

    ⚠️支持声音复刻的模型不支持默认音色(如 Chelsie、Serena、Ethan、Cherry)

    详细说明请参见语音合成章节。

  • 支持的语言:中文(zh)、英文(en)、德语(de)、意大利语(it)、葡萄牙语(pt)、西班牙语(es)、日语(ja)、韩语(ko)、法语(fr)、俄语(ru)、印尼语(id)

  • 并发与限流:10RPS(Requests Per Second)

  • 账户配额

    • 每个阿里云主账号最多可复刻 1000 个音色

    • 支持删除音色,删除后释放额度

    • 超过 1 年未使用的音色将下线处理

  • 音频文件格式要求:

    • 声道:单声道

    • 采样率:≥ 16000 Hz

    • 格式:WAV(16bit)、MP3、M4A

    • 文件大小:< 10MB

    • 音频时长:10~20秒,不得超过60

    • 连贯性要求:朗读时保持连贯,至少包含一段超过3秒的连续语音,避免频繁无意义停顿

  • 版权与合法性:您需对所提交音频的所有权及合法使用权负责,请仔细阅读服务协议

    重要

    进行声音复刻时,服务器会存储您提交的音频文件。该文件将在您删除音色时一并删除。音频文件仅用于声音复刻,不作他用。

  • 调用方式:当前仅支持通过 API 调用

计费说明

声音复刻和语音合成分开计费:

  • 声音复刻:新创建音色按 0.01 元/个计费,创建失败不计费

    说明

    免费额度说明:

    • 2025822日前未开通阿里云百炼用户:自开通之日起 90 天内,可享 1000 次免费音色创建额度。

    • 2025822日前已开通阿里云百炼用户:于2025822日补发 1000 次免费音色创建额度,有效期90天。

    • 免费额度按创建次数计算,与音色数量无关。

    • 创建失败不占用免费次数。

    • 删除音色不会恢复免费次数。

    • 免费额度用完或超出 90 天有效期后,新创建音色按 0.01 元/个计费。

  • 使用复刻生成的专属音色进行语音合成:按量计费,详情请参见模型列表

前提条件

已开通服务并获取API Key。请配置API Key到环境变量,而非硬编码在代码中,防范因代码泄露导致的安全风险。

说明

当您需要为第三方应用或用户提供临时访问权限,或者希望严格控制敏感数据访问、删除等高风险操作时,建议使用临时鉴权Token

与长期有效的 API Key 相比,临时鉴权 Token 具备时效性短(60秒)、安全性高的特点,适用于临时调用场景,能有效降低API Key泄露的风险。

使用方式:在代码中,将原本用于鉴权的 API Key 替换为获取到的临时鉴权 Token 即可。

快速开始

以下示例基于Python/Java DashScope SDK,演示了如何在语音合成中使用声音复刻生成的专属音色,实现与原音高度相似的输出效果。

若您希望通过 WebSocket 实现相同功能,请参见实时语音合成-通义千问快速开始章节,将示例代码中的 voice 参数替换为声音复刻生成的音色。
  • 示例使用本地音频文件 voice.mp3 进行声音复刻,运行代码时,请替换为您自己的音频文件。

  • 运行代码前请安装最新版DashScope SDK

  • 当您在自己的开发环境集成该功能时,请参见语音合成章节,了解具体实现步骤和接口说明。

Python

这里参考了Python SDK快速开始中的“server commit模式”示例代码,将voice参数替换为复刻生成的专属音色进行语音合成:

# DashScope SDK 版本需要不低于1.23.9,Python版本需3.10及以上
# coding=utf-8
# Installation instructions for pyaudio:
# APPLE Mac OS X
#   brew install portaudio
#   pip install pyaudio
# Debian/Ubuntu
#   sudo apt-get install python-pyaudio python3-pyaudio
#   or
#   pip install pyaudio
# CentOS
#   sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# Microsoft Windows
#   python -m pip install pyaudio

import pyaudio
import os
import requests
import base64
import pathlib
import threading
import time
import dashscope  # DashScope Python SDK 版本需要不低于1.23.9
from dashscope.audio.qwen_tts_realtime import QwenTtsRealtime, QwenTtsRealtimeCallback, AudioFormat

# ======= 常量配置 =======
DEFAULT_TARGET_MODEL = "qwen-tts-vc-realtime-2025-08-20"  # 声音复刻、语音合成要使用相同的模型
DEFAULT_PREFERRED_NAME = "guanyu"
DEFAULT_AUDIO_MIME_TYPE = "audio/mpeg"
VOICE_FILE_PATH = "voice.mp3"  # 用于声音复刻的本地音频文件的相对路径

TEXT_TO_SYNTHESIZE = [
    '对吧~我就特别喜欢这种超市,',
    '尤其是过年的时候',
    '去逛超市',
    '就会觉得',
    '超级超级开心!',
    '想买好多好多的东西呢!'
]

def create_voice(file_path: str,
                 target_model: str = DEFAULT_TARGET_MODEL,
                 preferred_name: str = DEFAULT_PREFERRED_NAME,
                 audio_mime_type: str = DEFAULT_AUDIO_MIME_TYPE) -> str:
    """
    创建音色,并返回 voice 参数
    """
    # 若没有将API Key配置到环境变量中,需将下一行替换为:api_key = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
    api_key = os.getenv("DASHSCOPE_API_KEY")

    file_path_obj = pathlib.Path(file_path)
    if not file_path_obj.exists():
        raise FileNotFoundError(f"音频文件不存在: {file_path}")

    base64_str = base64.b64encode(file_path_obj.read_bytes()).decode()
    data_uri = f"data:{audio_mime_type};base64,{base64_str}"

    url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization"
    payload = {
        "model": "qwen-voice-enrollment", # 不要修改该值
        "input": {
            "action": "create",
            "target_model": target_model,
            "preferred_name": preferred_name,
            "audio": {"data": data_uri}
        }
    }
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    resp = requests.post(url, json=payload, headers=headers)
    if resp.status_code != 200:
        raise RuntimeError(f"创建 voice 失败: {resp.status_code}, {resp.text}")

    try:
        return resp.json()["output"]["voice"]
    except (KeyError, ValueError) as e:
        raise RuntimeError(f"解析 voice 响应失败: {e}")

def init_dashscope_api_key():
    """
    初始化 dashscope SDK 的 API key
    """
    # 若没有将API Key配置到环境变量中,需将下一行替换为:dashscope.api_key = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
    dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# ======= 回调类 =======
class MyCallback(QwenTtsRealtimeCallback):
    """
    自定义 TTS 流式回调
    """
    def __init__(self):
        self.complete_event = threading.Event()
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=24000, output=True
        )

    def on_open(self) -> None:
        print('[TTS] 连接已建立')

    def on_close(self, close_status_code, close_msg) -> None:
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()
        print(f'[TTS] 连接关闭 code={close_status_code}, msg={close_msg}')

    def on_event(self, response: dict) -> None:
        try:
            event_type = response.get('type', '')
            if event_type == 'session.created':
                print(f'[TTS] 会话开始: {response["session"]["id"]}')
            elif event_type == 'response.audio.delta':
                audio_data = base64.b64decode(response['delta'])
                self._stream.write(audio_data)
            elif event_type == 'response.done':
                print(f'[TTS] 响应完成, Response ID: {qwen_tts_realtime.get_last_response_id()}')
            elif event_type == 'session.finished':
                print('[TTS] 会话结束')
                self.complete_event.set()
        except Exception as e:
            print(f'[Error] 处理回调事件异常: {e}')

    def wait_for_finished(self):
        self.complete_event.wait()

# ======= 主执行逻辑 =======
if __name__ == '__main__':
    init_dashscope_api_key()
    print('[系统] 初始化 Qwen TTS Realtime ...')

    callback = MyCallback()
    qwen_tts_realtime = QwenTtsRealtime(model=DEFAULT_TARGET_MODEL, callback=callback)
    qwen_tts_realtime.connect()
    
    qwen_tts_realtime.update_session(
        voice=create_voice(VOICE_FILE_PATH), # 将voice参数替换为复刻生成的专属音色
        response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
        mode='server_commit'
    )

    for text_chunk in TEXT_TO_SYNTHESIZE:
        print(f'[发送文本]: {text_chunk}')
        qwen_tts_realtime.append_text(text_chunk)
        time.sleep(0.1)

    qwen_tts_realtime.finish()
    callback.wait_for_finished()

    print(f'[Metric] session_id={qwen_tts_realtime.get_session_id()}, '
          f'first_audio_delay={qwen_tts_realtime.get_first_audio_delay()}s')

Java

这里参考了Java SDK快速开始中的“server commit模式”示例代码,将voice参数替换为复刻生成的专属音色进行语音合成:

// Java SDK 版本需要不低于2.20.9
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

import javax.sound.sampled.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    // ===== 常量定义 =====
    // 声音复刻、语音合成要使用相同的模型
    private static final String TARGET_MODEL = "qwen-tts-vc-realtime-2025-08-20";
    private static final String PREFERRED_NAME = "guanyu";
    // 用于声音复刻的本地音频文件的相对路径
    private static final String AUDIO_FILE = "voice.mp3";
    private static final String AUDIO_MIME_TYPE = "audio/mpeg";
    private static String[] textToSynthesize = {
            "对吧~我就特别喜欢这种超市",
            "尤其是过年的时候",
            "去逛超市",
            "就会觉得",
            "超级超级开心!",
            "想买好多好多的东西呢!"
    };

    // 生成 data URI
    public static String toDataUrl(String filePath) throws IOException {
        byte[] bytes = Files.readAllBytes(Paths.get(filePath));
        String encoded = Base64.getEncoder().encodeToString(bytes);
        return "data:" + AUDIO_MIME_TYPE + ";base64," + encoded;
    }

    // 调用 API 创建 voice
    public static String createVoice() throws Exception {
        // 若没有将API Key配置到环境变量中,需将下一行替换为:String apiKey = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
        String apiKey = System.getenv("DASHSCOPE_API_KEY");

        String jsonPayload =
                "{"
                        + "\"model\": \"qwen-voice-enrollment\"," // 不要修改该值
                        + "\"input\": {"
                        +     "\"action\": \"create\","
                        +     "\"target_model\": \"" + TARGET_MODEL + "\","
                        +     "\"preferred_name\": \"" + PREFERRED_NAME + "\","
                        +     "\"audio\": {"
                        +         "\"data\": \"" + toDataUrl(AUDIO_FILE) + "\""
                        +     "}"
                        + "}"
                        + "}";

        HttpURLConnection con = (HttpURLConnection) new URL("https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization").openConnection();
        con.setRequestMethod("POST");
        con.setRequestProperty("Authorization", "Bearer " + apiKey);
        con.setRequestProperty("Content-Type", "application/json");
        con.setDoOutput(true);

        try (OutputStream os = con.getOutputStream()) {
            os.write(jsonPayload.getBytes(StandardCharsets.UTF_8));
        }

        int status = con.getResponseCode();
        System.out.println("HTTP 状态码: " + status);

        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(status >= 200 && status < 300 ? con.getInputStream() : con.getErrorStream(),
                        StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            System.out.println("返回内容: " + response);

            if (status == 200) {
                JsonObject jsonObj = new Gson().fromJson(response.toString(), JsonObject.class);
                return jsonObj.getAsJsonObject("output").get("voice").getAsString();
            }
            throw new IOException("创建语音失败: " + status + " - " + response);
        }
    }

    // 实时PCM音频播放器类
    public static class RealtimePcmPlayer {
        private int sampleRate;
        private SourceDataLine line;
        private AudioFormat audioFormat;
        private Thread decoderThread;
        private Thread playerThread;
        private AtomicBoolean stopped = new AtomicBoolean(false);
        private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
        private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();

        // 构造函数初始化音频格式和音频线路
        public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
            this.sampleRate = sampleRate;
            this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            decoderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        String b64Audio = b64AudioBuffer.poll();
                        if (b64Audio != null) {
                            byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                            RawAudioBuffer.add(rawAudio);
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            playerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        byte[] rawAudio = RawAudioBuffer.poll();
                        if (rawAudio != null) {
                            try {
                                playChunk(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            decoderThread.start();
            playerThread.start();
        }

        // 播放一个音频块并阻塞直到播放完成
        private void playChunk(byte[] chunk) throws IOException, InterruptedException {
            if (chunk == null || chunk.length == 0) return;

            int bytesWritten = 0;
            while (bytesWritten < chunk.length) {
                bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
            }
            int audioLength = chunk.length / (this.sampleRate*2/1000);
            // 等待缓冲区中的音频播放完成
            Thread.sleep(audioLength - 10);
        }

        public void write(String b64Audio) {
            b64AudioBuffer.add(b64Audio);
        }

        public void cancel() {
            b64AudioBuffer.clear();
            RawAudioBuffer.clear();
        }

        public void waitForComplete() throws InterruptedException {
            while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                Thread.sleep(100);
            }
            line.drain();
        }

        public void shutdown() throws InterruptedException {
            stopped.set(true);
            decoderThread.join();
            playerThread.join();
            if (line != null && line.isRunning()) {
                line.drain();
                line.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                .model(TARGET_MODEL)
                // 若没有将API Key配置到环境变量中,需将下一行替换为:apikey("your-api-key")。your-api-key为实际的API Key,格式为“sk-xxxx”。
                .apikey(System.getenv("DASHSCOPE_API_KEY"))
                .build();
        AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
        final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);

        // 创建实时音频播放器实例
        RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);

        QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
            @Override
            public void onOpen() {
                // 连接建立时的处理
            }
            @Override
            public void onEvent(JsonObject message) {
                String type = message.get("type").getAsString();
                switch(type) {
                    case "session.created":
                        // 会话创建时的处理
                        break;
                    case "response.audio.delta":
                        String recvAudioB64 = message.get("delta").getAsString();
                        // 实时播放音频
                        audioPlayer.write(recvAudioB64);
                        break;
                    case "response.done":
                        // 响应完成时的处理
                        break;
                    case "session.finished":
                        // 会话结束时的处理
                        completeLatch.get().countDown();
                    default:
                        break;
                }
            }
            @Override
            public void onClose(int code, String reason) {
                // 连接关闭时的处理
            }
        });
        qwenTtsRef.set(qwenTtsRealtime);
        try {
            qwenTtsRealtime.connect();
        } catch (NoApiKeyException e) {
            throw new RuntimeException(e);
        }
        QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                .voice(createVoice()) // 将voice参数替换为复刻生成的专属音色
                .responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
                .mode("server_commit")
                .build();
        qwenTtsRealtime.updateSession(config);
        for (String text:textToSynthesize) {
            qwenTtsRealtime.appendText(text);
            Thread.sleep(100);
        }
        qwenTtsRealtime.finish();
        completeLatch.get().await();

        // 等待音频播放完成并关闭播放器
        audioPlayer.waitForComplete();
        audioPlayer.shutdown();
        System.exit(0);
    }
}

API详情

使用不同 API 时,请确保使用同一账号进行操作。

创建音色

上传用于复刻的音频,创建自定义音色。

  • URL

    POST https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
  • 请求头

    Authorization: Bearer {api-key} // 需将{api-key}替换为您自己的API Key
    Content-Type: application/json
  • 消息体

    包含所有请求参数的消息体如下,对于可选字段,在实际业务中可根据需求省略:

    {
        "model": "qwen-voice-enrollment", // 不要修改该值
        "input": {
            "action": "create",
            "target_model": "qwen-tts-vc-realtime-2025-08-20", // 声音复刻使用的模型,该模型要和语音合成模型保持一致
            "preferred_name": "guanyu",
            "audio": {
                "data": "https://xxx.wav"
            },
            "text": "可选项,填入audio.data对应的文本",
            "language": "可选项,填入audio.data对应的语种,如zh"
        }
    }
  • 请求参数

    点击查看请求示例

    cURL

    若未将API Key配置到环境变量,需将示例中的$DASHSCOPE_API_KEY替换为实际的API Key。

    curl -X POST https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization \
    -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
        "model": "qwen-voice-enrollment",
        "input": {
            "action": "create",
            "target_model": "qwen-tts-vc-realtime-2025-08-20",
            "preferred_name": "guanyu",
            "audio": {
                "data": "https://xxx.wav"
            }
        }
    }'

    Python

    import os
    import requests
    import base64, pathlib
    
    target_model = "qwen-tts-vc-realtime-2025-08-20"
    preferred_name = "guanyu"
    audio_mime_type = "audio/mpeg"
    
    file_path = pathlib.Path("input.mp3")
    base64_str = base64.b64encode(file_path.read_bytes()).decode()
    data_uri = f"data:{audio_mime_type};base64,{base64_str}"
    
    # 若没有将API Key配置到环境变量中,需将下一行替换为:api_key = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
    api_key = os.getenv("DASHSCOPE_API_KEY")
    url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization"
    
    payload = {
        "model": "qwen-voice-enrollment", # 不要修改这个值
        "input": {
            "action": "create",
            "target_model": target_model,
            "preferred_name": preferred_name,
            "audio": {
                "data": data_uri
            }
        }
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    # 发送 POST 请求
    resp = requests.post(url, json=payload, headers=headers)
    
    if resp.status_code == 200:
        data = resp.json()
        voice = data["output"]["voice"]
        print(f"生成的 voice 参数为: {voice}")
    else:
        print("请求失败:", resp.status_code, resp.text)

    Java

    需要导入Gson依赖,若是使用Maven或者Gradle,添加依赖方式如下:

    Maven

    pom.xml中添加如下内容:

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.13.1</version>
    </dependency>

    Gradle

    build.gradle中添加如下内容:

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation("com.google.code.gson:gson:2.13.1")

    完整代码:

    import com.google.gson.Gson;
    import com.google.gson.JsonObject;
    
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.file.*;
    import java.util.Base64;
    
    public class Main {
        private static final String TARGET_MODEL = "qwen-tts-vc-realtime-2025-08-20";
        private static final String PREFERRED_NAME = "guanyu";
        private static final String AUDIO_FILE = "input.mp3";
        private static final String AUDIO_MIME_TYPE = "audio/mpeg";
    
        public static String toDataUrl(String filePath) throws Exception {
            byte[] bytes = Files.readAllBytes(Paths.get(filePath));
            String encoded = Base64.getEncoder().encodeToString(bytes);
            return "data:" + AUDIO_MIME_TYPE + ";base64," + encoded;
        }
    
        public static void main(String[] args) {
            // 若没有将API Key配置到环境变量中,需将下一行替换为:String apiKey = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
            String apiKey = System.getenv("DASHSCOPE_API_KEY");
            String apiUrl = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
    
            try {
                // 构造 JSON 请求体(注意内部的引号需转义)
                String jsonPayload =
                        "{"
                                + "\"model\": \"qwen-voice-enrollment\"," // 不要修改该值
                                + "\"input\": {"
                                +     "\"action\": \"create\","
                                +     "\"target_model\": \"" + TARGET_MODEL + "\","
                                +     "\"preferred_name\": \"" + PREFERRED_NAME + "\","
                                +     "\"audio\": {"
                                +         "\"data\": \"" + toDataUrl(AUDIO_FILE) + "\""
                                +     "}"
                                + "}"
                                + "}";
    
                HttpURLConnection con = (HttpURLConnection) new URL(apiUrl).openConnection();
                con.setRequestMethod("POST");
                con.setRequestProperty("Authorization", "Bearer " + apiKey);
                con.setRequestProperty("Content-Type", "application/json");
                con.setDoOutput(true);
    
                // 发送请求体
                try (OutputStream os = con.getOutputStream()) {
                    os.write(jsonPayload.getBytes("UTF-8"));
                }
    
                int status = con.getResponseCode();
                InputStream is = (status >= 200 && status < 300)
                        ? con.getInputStream()
                        : con.getErrorStream();
    
                StringBuilder response = new StringBuilder();
                try (BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
                    String line;
                    while ((line = br.readLine()) != null) {
                        response.append(line);
                    }
                }
    
                System.out.println("HTTP 状态码: " + status);
                System.out.println("返回内容: " + response.toString());
    
                if (status == 200) {
                    // 解析 JSON
                    Gson gson = new Gson();
                    JsonObject jsonObj = gson.fromJson(response.toString(), JsonObject.class);
                    String voice = jsonObj.getAsJsonObject("output").get("voice").getAsString();
                    System.out.println("生成的 voice 参数为: " + voice);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    参数

    类型

    默认值

    必填

    说明

    model

    string

    -

    指定声音复刻的基础服务。固定为qwen-voice-enrollment

    action

    string

    -

    固定为create

    target_model

    string

    -

    声音复刻所使用的模型,目前仅支持qwen-tts-vc-realtime-2025-08-20

    使用声音复刻生成的音色进行语音合成时,target_model要和语音合成的model参数保持一致。请严格按照语音合成章节中的要求进行操作。

    preferred_name

    string

    -

    自定义音色名关键字。仅允许数字、大小写字母和下划线,不超过16个字符。

    建议选用与角色、场景或版本相关的短标识,便于后续识别与管理。

    该关键字会在复刻的音色名中出现,例如关键字为“guanyu”,最终音色名为“qwen-tts-vc-guanyu-voice-20250812105009984-838b”

    audio.data

    string

    -

    用于复刻的音频(录制时需遵循录音操作指南,音频需满足音频文件格式要求)。

    可通过以下两种方式提交音频数据:

    • 公网可访问的音频URL(推荐将音频上传至OSS

      • 文件大小不超过10MB

      • 确保URL稳定可访问且无需额外鉴权

    • Data URL

      Data URL是一种在网页或应用中直接以字符串形式嵌入数据的方法,而不是去请求一个单独的文件。

      当前场景,要求如下格式:data:<mediatype>;base64,<data>

      • <mediatype>:MIME类型

        • WAV:audio/wav

        • MP3:audio/mpeg

        • M4A:audio/mp4

      • <data>:音频转成的Base64编码的字符串

        Base64编码会增大体积,请控制源文件大小,确保编码后仍小于10MB

      • 示例:data:audio/wav;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//PAxABQ/BXRbMPe4IQAhl9

        点击查看示例代码

        import base64, pathlib
        
        # input.mp3为本地音频文件的相对路径,请根据实际情况进行替换
        file_path = pathlib.Path("input.mp3")
        base64_str = base64.b64encode(file_path.read_bytes()).decode()
        data_uri = f"data:audio/mpeg;base64,{base64_str}"
        import java.nio.file.*;
        import java.util.Base64;
        
        public class Main {
            /**
             * filePath为本地音频文件的相对路径
             */
            public static String toDataUrl(String filePath) throws Exception {
                byte[] bytes = Files.readAllBytes(Paths.get(filePath));
                String encoded = Base64.getEncoder().encodeToString(bytes);
                return "data:audio/mpeg;base64," + encoded;
            }
        
            // 使用示例
            public static void main(String[] args) throws Exception {
                System.out.println(toDataUrl("input.mp3"));
            }
        }

    text

    string

    -

    用于复刻的音频(即audio.data参数)对应的文本。

    传入该参数后,服务端会对比音频与该文本的差异,若差异过大,将返回Audio.PreprocessError。

    language

    string

    -

    用于复刻的音频(即audio.data参数)的语种。

    支持zh(中文)、en(英文)、de(德语)、it(意大利语)、pt(葡萄牙语)、es(西班牙语)、ja(日语)、ko(韩语)、fr(法语)、ru(俄语)、id(印尼语)。

    若使用该参数,设置的语种要和实际用于复刻的音频的语种一致。

  • 响应参数

    点击查看响应示例

    {
        "output": {
            "voice": "yourVoice", //例如:qwen-tts-vc-guanyu-voice-20250812105009984-838b
            "target_model": "qwen-tts-vc-realtime-2025-08-20"
        },
        "usage": {
            "count": 1
        },
        "request_id": "yourRequestId"
    }

    需关注的参数如下:

    参数

    类型

    说明

    voice

    string

    音色。

    完成声音复刻后,将复刻的音色作为voice参数传入语音合成接口,即可生成对应音色的语音,请严格按照语音合成章节中的要求进行操作。

    target_model

    string

    声音复刻所使用的模型。该模型要和语音合成模型保持一致。

    request_id

    string

    Request ID。

    count

    integer

    本次请求实际计入费用的“创建音色”次数,本次请求的费用为元。

    创建音色时,count恒为1。

查询音色列表

分页查询已创建的音色列表。

  • URL

    POST https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
  • 请求头

    Authorization: Bearer {api-key} // 需将{api-key}替换为您自己的API Key
    Content-Type: application/json
  • 消息体

    包含所有请求参数的消息体如下,对于可选字段,在实际业务中可根据需求省略:

    {
        "model": "qwen-voice-enrollment", // 不要修改该值
        "input": {
            "action": "list",
            "page_size": 2,
            "page_index": 0
        }
    }
  • 请求参数

    点击查看请求示例

    cURL

    若未将API Key配置到环境变量,需将示例中的$DASHSCOPE_API_KEY替换为实际的API Key。

    curl --location --request POST 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization' \
    --header 'Authorization: Bearer $DASHSCOPE_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
        "model": "qwen-voice-enrollment",
        "input": {
            "action": "list",
            "page_size": 10,
            "page_index": 0
        }
    }'

    Python

    import os
    import requests
    
    # 若没有将API Key配置到环境变量中,需将下一行替换为:api_key = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
    api_key = os.getenv("DASHSCOPE_API_KEY")  # 从环境变量获取 API Key
    url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization"
    
    payload = {
        "model": "qwen-voice-enrollment", # 不要修改该值
        "input": {
            "action": "list",
            "page_size": 10,
            "page_index": 0
        }
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json=payload, headers=headers)
    
    print("HTTP 状态码:", response.status_code)
    
    if response.status_code == 200:
        data = response.json()
        voice_list = data["output"]["voice_list"]
    
        print("查询到的音色列表:")
        for item in voice_list:
            print(f"- 音色: {item['voice']}  创建时间: {item['gmt_create']}  模型: {item['target_model']}")
    else:
        print("请求失败:", response.text)

    Java

    需要导入Gson依赖,若是使用Maven或者Gradle,添加依赖方式如下:

    Maven

    pom.xml中添加如下内容:

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.13.1</version>
    </dependency>

    Gradle

    build.gradle中添加如下内容:

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation("com.google.code.gson:gson:2.13.1")

    完整代码:

    import com.google.gson.Gson;
    import com.google.gson.JsonArray;
    import com.google.gson.JsonObject;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class Main {
        public static void main(String[] args) {
            // 若没有将API Key配置到环境变量中,需将下一行替换为:String apiKey = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
            String apiKey = System.getenv("DASHSCOPE_API_KEY"); // 建议使用环境变量保存 API Key
            String apiUrl = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
    
            // JSON 请求体(旧版本 Java 无 """ 多行字符串)
            String jsonPayload =
                    "{"
                            + "\"model\": \"qwen-voice-enrollment\"," // 不要修改该值
                            + "\"input\": {"
                            +     "\"action\": \"list\","
                            +     "\"page_size\": 10,"
                            +     "\"page_index\": 0"
                            + "}"
                            + "}";
    
            try {
                HttpURLConnection con = (HttpURLConnection) new URL(apiUrl).openConnection();
                con.setRequestMethod("POST");
                con.setRequestProperty("Authorization", "Bearer " + apiKey);
                con.setRequestProperty("Content-Type", "application/json");
                con.setDoOutput(true);
    
                try (OutputStream os = con.getOutputStream()) {
                    os.write(jsonPayload.getBytes("UTF-8"));
                }
    
                int status = con.getResponseCode();
                BufferedReader br = new BufferedReader(new InputStreamReader(
                        status >= 200 && status < 300 ? con.getInputStream() : con.getErrorStream(), "UTF-8"));
    
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) {
                    response.append(line);
                }
                br.close();
    
                System.out.println("HTTP 状态码: " + status);
                System.out.println("返回 JSON: " + response.toString());
    
                if (status == 200) {
                    Gson gson = new Gson();
                    JsonObject jsonObj = gson.fromJson(response.toString(), JsonObject.class);
                    JsonArray voiceList = jsonObj.getAsJsonObject("output").getAsJsonArray("voice_list");
    
                    System.out.println("\n 查询到的音色列表:");
                    for (int i = 0; i < voiceList.size(); i++) {
                        JsonObject voiceItem = voiceList.get(i).getAsJsonObject();
                        String voice = voiceItem.get("voice").getAsString();
                        String gmtCreate = voiceItem.get("gmt_create").getAsString();
                        String targetModel = voiceItem.get("target_model").getAsString();
    
                        System.out.printf("- 音色: %s  创建时间: %s  模型: %s\n",
                                voice, gmtCreate, targetModel);
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    参数

    类型

    默认值

    必填

    说明

    model

    string

    -

    指定声音复刻的基础服务。固定为qwen-voice-enrollment

    action

    string

    -

    固定为list

    page_index

    integer

    0

    页码索引。取值范围:[0, 1000000]。

    page_size

    integer

    10

    每页包含数据条数。取值范围:[0, 1000000]。

  • 响应参数

    点击查看响应示例

    {
        "output": {
            "voice_list": [
                {
                    "voice": "yourVoice1",
                    "gmt_create": "2025-08-11 17:59:32",
                    "target_model": "qwen-tts-vc-realtime-2025-08-20" // 声音复刻使用的模型,该模型要和语音合成模型保持一致
                },
                {
                    "voice": "yourVoice2",
                    "gmt_create": "2025-08-11 17:38:10",
                    "target_model": "qwen-tts-vc-realtime-2025-08-20" // 声音复刻使用的模型,该模型要和语音合成模型保持一致
                }
            ]
        },
        "usage": {
            "count": 0
        },
        "request_id": "yourRequestId"
    }

    需关注的参数如下:

    参数

    类型

    说明

    voice

    string

    音色。

    gmt_create

    string

    创建音色的时间。

    target_model

    string

    声音复刻所使用的模型。该模型要和语音合成模型保持一致。

    request_id

    string

    Request ID。

    count

    integer

    本次请求实际计入费用的“创建音色”次数,本次请求的费用为元。

    查询音色时,count恒为0。

删除音色

删除指定音色,释放对应额度。

  • URL

    POST https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
  • 请求头

    Authorization: Bearer {api-key} // 需将{api-key}替换为您自己的API Key
    Content-Type: application/json
  • 消息体

    包含所有请求参数的消息体如下,对于可选字段,在实际业务中可根据需求省略:

    {
        "model": "qwen-voice-enrollment", // 不要修改该值
        "input": {
            "action": "delete",
            "voice": "yourVoice"
        }
    }
  • 请求参数

    点击查看请求示例

    cURL

    若未将API Key配置到环境变量,需将示例中的$DASHSCOPE_API_KEY替换为实际的API Key。

    curl --location --request POST 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization' \
    --header 'Authorization: Bearer $DASHSCOPE_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
        "model": "qwen-voice-enrollment",
        "input": {
            "action": "delete",
            "voice": "yourVoice"
        }
    }'

    Python

    import os
    import requests
    
    # 若没有将API Key配置到环境变量中,需将下一行替换为:api_key = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
    api_key = os.getenv("DASHSCOPE_API_KEY")  # 从环境变量获取 API Key
    url = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization"
    
    voice_to_delete = "yourVoice"  # 要删除的音色(替换为真实值)
    
    payload = {
        "model": "qwen-voice-enrollment", # 不要修改该值
        "input": {
            "action": "delete",
            "voice": voice_to_delete
        }
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json=payload, headers=headers)
    
    print("HTTP 状态码:", response.status_code)
    
    if response.status_code == 200:
        data = response.json()
        request_id = data["request_id"]
    
        print(f"删除成功")
        print(f"Request ID: {request_id}")
    else:
        print("请求失败:", response.text)

    Java

    需要导入Gson依赖,若是使用Maven或者Gradle,添加依赖方式如下:

    Maven

    pom.xml中添加如下内容:

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.13.1</version>
    </dependency>

    Gradle

    build.gradle中添加如下内容:

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation("com.google.code.gson:gson:2.13.1")

    完整代码:

    import com.google.gson.Gson;
    import com.google.gson.JsonObject;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class Main {
        public static void main(String[] args) {
            // 若没有将API Key配置到环境变量中,需将下一行替换为:String apiKey = "your-api-key"。your-api-key为实际的API Key,格式为“sk-xxxx”。
            String apiKey = System.getenv("DASHSCOPE_API_KEY"); // 从环境变量读取 API Key
            String apiUrl = "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization";
            String voiceToDelete = "yourVoice"; // 要删除的音色(替换为真实值)
    
            // 构造 JSON 请求体(字符串拼接,兼容 Java 8)
            String jsonPayload =
                    "{"
                            + "\"model\": \"qwen-voice-enrollment\"," // 不要修改该值
                            + "\"input\": {"
                            +     "\"action\": \"delete\","
                            +     "\"voice\": \"" + voiceToDelete + "\""
                            + "}"
                            + "}";
    
            try {
                // 建立 POST 连接
                HttpURLConnection con = (HttpURLConnection) new URL(apiUrl).openConnection();
                con.setRequestMethod("POST");
                con.setRequestProperty("Authorization", "Bearer " + apiKey);
                con.setRequestProperty("Content-Type", "application/json");
                con.setDoOutput(true);
    
                // 发送请求体
                try (OutputStream os = con.getOutputStream()) {
                    os.write(jsonPayload.getBytes("UTF-8"));
                }
    
                int status = con.getResponseCode();
                BufferedReader br = new BufferedReader(new InputStreamReader(
                        status >= 200 && status < 300 ? con.getInputStream() : con.getErrorStream(), "UTF-8"));
    
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) {
                    response.append(line);
                }
                br.close();
    
                System.out.println("HTTP 状态码: " + status);
                System.out.println("返回 JSON: " + response.toString());
    
                if (status == 200) {
                    Gson gson = new Gson();
                    JsonObject jsonObj = gson.fromJson(response.toString(), JsonObject.class);
                    String requestId = jsonObj.get("request_id").getAsString();
    
                    System.out.println("删除成功");
                    System.out.println("Request ID: " + requestId);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    参数

    类型

    默认值

    必填

    说明

    model

    string

    -

    指定声音复刻的基础服务。固定为qwen-voice-enrollment

    action

    string

    -

    固定为delete

    voice

    string

    -

    待删除的音色。

  • 响应参数

    点击查看响应示例

    {
        "usage": {
            "count": 0
        },
        "request_id": "yourRequestId"
    }

    需关注的参数如下:

    参数

    类型

    说明

    request_id

    string

    Request ID。

    count

    integer

    本次请求实际计入费用的“创建音色”次数,本次请求的费用为元。

    删除音色时,count恒为0。

语音合成

完成声音复刻后,将复刻的音色作为voice参数传入语音合成接口,生成对应音色的语音。

完整示例代码请参见快速开始

  • ⚠️使用复刻生成的专属音色合成语音时,语音合成模型必须与声音复刻模型相同,即以下两个参数的值必须相同:

    参数

    说明

    语音合成请求参数:model

    model为语音合成使用的模型。

    请参考相应编程语言的API文档了解语音合成的参数和接口细节:

    • Java:Java SDK

    • Python:Python SDK

    • 其他编程语言(如Go、C#、PHP、Node.js等):WebSocket API(实时语音合成交互流程客户端事件服务端事件

      语音合成服务选择 WebSocket 而非 RESTful,核心在于全双工通信能力:WebSocket 支持服务端与客户端双向主动传输数据(例如实时推送合成的音频),而基于 HTTP 的 RESTful 仅支持客户端发起的单向请求-响应模式,无法满足实时交互需求。

    声音复刻请求参数:target_model

    target_model为声音复刻使用的模型。

    请参考API详情章节了解声音复刻的参数和接口细节。

  • ⚠️使用声音复刻系列模型合成语音时,仅能使用该模型复刻生成的专属音色,不能使用默认音色(如 Chelsie、Serena、Ethan、Cherry)。

录音操作指南

录音设备

可使用手机、数字录音笔、专业录音机等。

录音环境

场地

  • 建议在 10 平方米以内的小型封闭空间录音。

  • 优先选择配有吸音材料(如吸音棉、地毯、窗帘)的房间。

  • 避免空旷大厅、会议室、教室等高混响场所。

噪音控制

  • 室外噪音:关闭门窗,避免交通、施工等干扰。

  • 室内噪音:关闭空调、风扇、日光灯镇流器等设备;可通过手机录制环境音并放大播放,识别潜在噪音源。

混响控制

  • 混响会导致声音模糊、清晰度下降。

  • 减少光滑表面反射:拉上窗帘、打开衣柜门、铺放衣物或床单覆盖桌面/柜面。

  • 利用不规则物体(如书架、软包家具)实现声波漫反射。

录音文案

  • 内容无特殊限制,建议与目标应用场景一致。

  • 避免短句(如“你好”、“是的”),应使用完整句子。

  • 保持语义连贯,朗读时避免频繁停顿(建议至少连续 3 秒无中断)。

  • 可加入适当情绪表达(如温暖、亲切、严肃),避免机械朗读。

  • 不包含敏感词汇(如政治、色情、暴力相关内容),否则会导致复刻失败。

操作建议

以普通卧室为例:

  1. 关闭门窗,隔绝外部噪音。

  2. 关闭空调、电扇等电器。

  3. 拉上窗帘,减少玻璃反射。

  4. 在桌面铺放衣物或毛毯,降低桌面反射。

  5. 提前熟悉文案,设定角色语气,自然演绎。

  6. 与录音设备保持约 10 厘米距离,避免喷麦或信号过弱。

错误信息

如遇报错问题,请参见错误信息进行排查。