跳转到主要内容
Qwen-Audio-TTS

Qwen-Audio-TTS Java SDK

通过DashScope Java SDK进行Qwen-Audio-TTS语音合成。

接口地址

SDK 的接口地址需在初始化前设置为下方地址。 wss://maas.qianwenaiapi.com/api-ws/v1/inference

SpeechSynthesizer

包路径:com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer

构造方法

public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)
参数说明:
  • param:语音合成参数,通过SpeechSynthesisParam .builder()构建
  • callback:回调函数,用于流式调用。非流式调用时传入null

call() - 非流式/单向流式合成

方法签名:
public ByteBuffer call(String text)
参数说明:
参数类型必填说明
textString是待合成的文本,长度不得超过20000字符。
返回值:ByteBuffer 或 null。非流式调用时返回完整音频数据;单向流式调用时音频通过回调返回,此方法返回null。

streamingCall() - 双向流式合成

方法签名:
public void streamingCall(String text)
参数说明:
参数类型必填说明
textString是待合成的文本,长度不得超过20000字符。可多次调用追加文本。

streamingComplete() - 结束双向流式调用

方法签名:
public void streamingComplete()
结束双向流式调用,通知服务端所有文本已发送完毕。

streamingCancel() - 取消双向流式调用

方法签名:
public void streamingCancel()
说明:取消当前轮次的双向流式语音合成任务。调用后,SDK 会立即结束当前任务。取消后可在当前连接上继续发起新的合成任务,无需重新初始化 SpeechSynthesizer 实例。
版本要求:使用该功能需要 Java SDK 版本不低于 2.22.26。

callAsFlowable() - 单向流式合成(响应式)

方法签名:
public Flowable<SpeechSynthesisResult> callAsFlowable(String text)
参数说明:
参数类型必填说明
textString是待合成的文本。
返回值:Flowable< SpeechSynthesisResult > 响应式流。

streamingCallAsFlowable() - 双向流式合成(响应式)

方法签名:
public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)
参数说明:
参数类型必填说明
textStreamFlowable&lt;String&gt;是文本的响应式流。
返回值:Flowable< SpeechSynthesisResult > 响应式流。

getDuplexApi().close() - 关闭WebSocket连接

方法签名:
public boolean getDuplexApi().close(int code, String reason)
参数说明:
参数类型必填说明
codeint是关闭码。
reasonString是关闭原因。
返回值:boolean,是否成功关闭。

getLastRequestId() - 获取请求ID

方法签名:
public String getLastRequestId()
返回值:String,请求ID。

getFirstPackageDelay() - 获取首包延迟

方法签名:
public long getFirstPackageDelay()
返回值:long,首包延迟(ms),从发送第一包到收到首包结果。

SpeechSynthesisParam

包路径:com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam 示例:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
    .model("qwen-audio-3.0-tts-flash") // 模型
    .voice("longanhuan_v3.6") // 音色
    .format(SpeechSynthesisAudioFormat.WAV_8000HZ_MONO_16BIT) // 音频编码格式、采样率
    .volume(50) // 音量,取值范围:[0, 100]
    .speechRate(1.0f) // 语速,取值范围:[0.5, 2]
    .pitchRate(1.0f) // 语调,取值范围:[0.5, 2]
    .build();

Builder 方法

方法参数类型必填说明
model(String)String是模型名称。
voice(String)String是语音合成所使用的音色。
  • 系统音色:参见Qwen-Audio-TTS音色列表
  • 复刻音色:通过声音复刻功能定制
  • 声音设计音色:通过声音设计功能定制
format(SpeechSynthesisAudioFormat)enum否音频编码格式及采样率。 默认值:SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS。 SpeechSynthesisAudioFormat包路径:com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat。
volume(int)int否音量。 默认值:50。 取值范围:[0, 100]。
speechRate(float)float否语速。 默认值:1.0。 取值范围:[0.5, 2.0]。
pitchRate(float)float否音调。 默认值:1.0。 取值范围:[0.5, 2.0]。
enableWordTimestamp(boolean)boolean否是否开启字级别时间戳。 默认值:false。 仅在流式输出模式下可用。支持复刻音色;支持的系统音色请参见Qwen-Audio-TTS音色列表。
seed(int)int否生成时使用的随机数种子,使合成的效果产生变化。在模型版本、文本、音色及其他参数均相同的前提下,使用相同的seed可复现相同的合成结果。 默认值0。 取值范围:[0, 65535]。 SDK版本低于2.21.7时,seed需要通过扩展参数进行设置。
languageHints(List&lt;String&gt;)List&lt;String&gt;否提示: - 此参数为数组,但当前版本仅处理第一个元素,因此建议只传入一个值。
  • 此参数用于指定语音合成的目标语言,该设置与声音复刻时的样本音频的语种无关。如需设置复刻任务的源语言,请参见声音复刻API参考。
指定语音合成的目标语言,提升合成效果。 当数字、缩写、符号等朗读方式或者小语种合成效果不符合预期时使用,例如:
  • 数字朗读方式不符合预期,“hello, this is 110”读成“hello, this is one one zero”而非“hello, this is 幺幺零”
  • 符号朗读不准确,“@”读成“艾特”而非“at”
  • 小语种合成效果差,合成不自然
  • zh:中文
  • en:英语
  • fr:法语
  • de:德语
  • ja:日语
  • ko:韩语
  • ru:俄语
  • pt:葡萄牙语
  • th:泰语
  • id:印尼语
  • vi:越南语
  • es:西班牙语
  • it:意大利语
  • ms:马来西亚语
  • fil:菲律宾语
  • ar:阿拉伯语
instruction(String)String否设置指令,用于控制方言、情感或角色等合成效果。 使用说明请参见指令控制。
hotFix(ParamHotFix)ParamHotFix否文本热修复配置,用于自定义指定词语的发音或对待合成文本进行替换。 参数介绍:
  • pronunciation:自定义发音。指定词语的拼音标注,用于纠正默认发音不准确的情况。
  • replace:文本替换。在语音合成前将指定词语替换为目标文本,替换后的文本将作为实际合成内容。
示例: 示例 1 请参见表格下方
parameter(String key, Object value)String, Object否设置扩展参数。
parameters(Map&lt;String, Object&gt;)Map否设置扩展参数。
示例 1(说明):
List<ParamHotFix.PronunciationItem> pronunciationItems = new ArrayList<>();
pronunciationItems.add(new ParamHotFix.PronunciationItem("天气", "tian1 qi4"));

List<ParamHotFix.ReplaceItem> replaceItems = new ArrayList<>();
replaceItems.add(new ParamHotFix.ReplaceItem("今天", "金天"));

ParamHotFix paramHotFix = new ParamHotFix();
paramHotFix.setPronunciation(pronunciationItems);
paramHotFix.setReplace(replaceItems);

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                        .model("qwen-audio-3.0-tts-flash") // 模型
                        .voice("longanhuan_v3.6") // 音色
                        .hotFix(paramHotFix)
                        .build();

扩展参数

通过 parameter() 或 parameters() 设置。 示例:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
  .model("qwen-audio-3.0-tts-flash")
  .voice("longanhuan_v3.6")
  .parameter("bit_rate", 32)
  .build();
参数名类型必填说明
bit_rateinteger否音频码率(kbps)。音频格式为mp3或opus时,支持通过bit_rate参数调整码率。 默认值:32。 取值范围:[6, 510]。
enable_aigc_tagboolean否是否在生成的音频中添加AIGC隐性标识。设置为true时,会将隐性标识嵌入到支持格式(wav/mp3/opus)的音频中。 默认值:false。
aigc_propagatorString否设置AIGC隐性标识中的 ContentPropagator 字段,用于标识内容的传播者。仅在 enable_aigc_tag 为 true 时生效。 默认值:千问AI平台账号。
aigc_propagate_idString否设置AIGC隐性标识中的 PropagateID 字段,用于唯一标识一次具体的传播行为。仅在 enable_aigc_tag 为 true 时生效。 默认值:本次语音合成请求Request ID。

ResultCallback

包路径:com.alibaba.dashscope.common.ResultCallback

onEvent() - 接收音频数据

方法签名:
public void onEvent(SpeechSynthesisResult result)
参数说明:
参数类型必填说明
resultSpeechSynthesisResult是接收到合成事件时触发,包含音频帧、时间戳信息和输出信息(事件类型、原始文本等)。

onComplete() - 合成完成

方法签名:
public void onComplete()
语音合成完成时触发。

onError() - 错误处理

方法签名:
public void onError(Exception e)
参数说明:
参数类型必填说明
eException是发生错误时触发,包含异常信息。

SpeechSynthesisResult

包路径:com.alibaba.dashscope.audio.tts.SpeechSynthesisResult

getAudioFrame() - 获取音频数据帧

方法签名:
public ByteBuffer getAudioFrame()
返回值:ByteBuffer,音频数据帧。

getTimestamp() - 获取时间戳信息

方法签名:
public Sentence getTimestamp()
返回值:Sentence,时间戳信息。

getOutput() - 获取输出信息

方法签名:
public JsonObject getOutput()
返回值:com.google.gson.JsonObject,合成事件的输出信息,包含事件类型和文本内容。需要SDK版本 >= 2.22.0。

句子级别时间戳信息(Sentence)

Sentence封装了句子级别时间戳信息。

getBeginTime() - 获取句子开始时间

方法签名:
public int getBeginTime()
返回值:句子开始时间,单位为ms。

getEndTime() - 获取句子结束时间

方法签名:
public int getEndTime()
返回值:句子结束时间,单位为ms。

getWords() - 获取字级别时间戳

方法签名:
public List<Word> getWords()
返回值:Word的List集合,批量获取字级别时间戳信息,可能为空。

字级别时间戳信息(Word)

Word封装了字级别时间戳信息。

getBeginTime() - 获取词开始时间

方法签名:
public int getBeginTime()
返回值:词开始时间,单位为ms。

getEndTime() - 获取词结束时间

方法签名:
public int getEndTime()
返回值:词结束时间,单位为ms。

getText() - 获取文本信息

方法签名:
public String getText()
返回值:String,文本信息。

getPhonemes() - 获取音素级别时间戳

方法签名:
public List<Phoneme> getPhonemes()
返回值:Phoneme的List集合,批量获取音素级别时间戳信息,可能为空。

音素级别时间戳信息(Phoneme)

Phoneme封装了音素级别时间戳信息。

getBeginTime() - 获取音素开始时间

方法签名:
public int getBeginTime()
返回值:音素开始时间,单位为ms。

getEndTime() - 获取音素结束时间

方法签名:
public int getEndTime()
返回值:音素结束时间,单位为ms。

getText() - 获取文本信息

方法签名:
public String getText()
返回值:String,文本信息。

getTone() - 获取音调

方法签名:
public int getTone()
返回值:音调。
  • 英文中,0、1、2分别代表轻音、重音和次重音。
  • 拼音中,1、2、3、4、5分别代表一声、二声、三声、四声和轻声。

输出信息(output)

getOutput()返回JsonObject,封装了合成事件的输出信息。在onEvent回调或Flowable流中获取。包含以下字段:
字段类型说明
typeString事件类型。取值:sentence-begin(句子开始,返回待合成的文本内容)、sentence-synthesis(标识音频数据块,表示当前正在合成音频)、sentence-end(句子结束,返回文本内容和字级别时间戳)。
original_textString当前句子的原始文本内容。在sentence-begin和sentence-end事件中返回。
sentenceJsonObject句子信息,包含句子编号(index)和字级别时间戳(words)。在sentence-end事件中包含完整的字级别时间戳信息。

示例代码

SDK提供了语音合成的关键接口,支持以下几种调用方式:
  • 非流式调用:阻塞式,一次性发送完整文本,直接返回完整音频。适合短文本语音合成场景。
  • 单向流式调用:非阻塞式,一次性发送完整文本,通过回调函数接收音频数据(可能分片)。适用于对实时性要求高的短文本语音合成场景。
  • 双向流式调用:非阻塞式,可分多次发送文本片段,通过回调函数实时接收增量合成的音频流。适合实时性要求高的长文本语音合成场景。
更多示例,请参见GitHub。
  • 非流式调用
  • 单向流式调用
  • 双向流式调用
image
发送的文本长度不得超过20000字符。
每次调用call方法前,需要重新初始化SpeechSynthesizer实例。
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class Main {
    // 模型
    private static String model = "qwen-audio-3.0-tts-flash";
    // 音色
    private static String voice = "longanhuan_v3.6";

    public static void streamAudioDataToSpeaker() {
        // 请求参数
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 若没有配置环境变量,请用千问AI平台API Key将下行替换为:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // 模型
                        .voice(voice) // 音色
                        .build();

        // 同步模式:禁用回调(第二个参数为null)
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        ByteBuffer audio = null;
        try {
            // 阻塞直至音频返回
            audio = synthesizer.call("今天天气怎么样?");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 任务结束关闭websocket连接
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        if (audio != null) {
            // 将音频数据保存到本地文件"output.mp3"中
            File file = new File("output.mp3");
            // 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
            System.out.println(
                    "[Metric] requestId为:"
                            + synthesizer.getLastRequestId()
                            + "首包延迟(毫秒)为:"
                            + synthesizer.getFirstPackageDelay());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(audio.array());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        Constants.baseWebsocketApiUrl = "wss://maas.qianwenaiapi.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

通过Flowable调用

Flowable是RxJava中表示响应式数据流的类型,支持背压。关于Flowable的使用,请参见Flowable API详情。 使用Flowable前需确保已集成RxJava库,并了解响应式编程基础概念。 单次发送文本长度不得超过 20000 字符,且累计发送文本总长度不得超过 20 万字符。
  • 单向流式调用
  • 双向流式调用
以下示例展示了通过Flowable对象的blockingForEach接口,阻塞式地获取每次流式返回的SpeechSynthesisResult类型数据。
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String model = "qwen-audio-3.0-tts-flash"; // 模型
    private static String voice = "longanhuan_v3.6"; // 音色

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // 请求参数
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 若没有配置环境变量,请用千问AI平台API Key将下行替换为:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // 模型
                        .voice(voice) // 音色
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.callAsFlowable("今天天气怎么样?").blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // 此处实现处理音频数据的逻辑
                System.out.println(TimeUtils.getTimestamp() + " 收到音频");
            }
            // 获取输出信息,包含事件类型和原始文本
            if (result.getOutput() != null && result.getOutput().has("type")) {
                System.out.println("事件类型: " + result.getOutput().get("type").getAsString()
                        + ", 原始文本: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
            }
        });
        // 任务结束关闭 WebSocket 连接
        synthesizer.getDuplexApi().close(1000, "bye");
        // 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
        System.out.println(
                "[Metric] requestId为:"
                        + synthesizer.getLastRequestId()
                        + "首包延迟(毫秒)为:"
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        Constants.baseWebsocketApiUrl = "wss://maas.qianwenaiapi.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

高并发调用

在DashScope Java SDK中,采用了OkHttp3的连接池技术,以减少重复建立连接的开销。详情请参见高并发最佳实践。