语音合成-CosyVoice/Sambert

语音合成,又称文本转语音(Text-to-Speech,TTS),是将文本转换为自然语音的技术。该技术基于机器学习算法,通过学习大量语音样本,掌握语言的韵律、语调和发音规则,从而在接收到文本输入时生成真人般自然的语音内容。

示例场景和语音

聊天数字人

日常闲聊

cosyvoice-v1(longxiaochun):这这也不知道为啥哈,反正,它刚出来的时候儿叫台湾手抓饼,啊,现在就是可能这个,大陆这边儿都给改良了,整的都像那种,烙的那种,鸡蛋灌饼儿似的啦,啊,有就有那种感觉哈。

电话客服

客服提醒

cosyvoice-v1(loongstella):您好,我们是银行的账务部门,如果你有疑问的话呢,可以拨打我们的客服热线进行咨询,那您的账单呢目前已经逾期了,麻烦您尽快地处理一下好吧?

直播带货

推荐T

cosyvoice-v1(loongstella):那我来给大家推荐一款T恤,这款呢真的是超级好看,这个颜色呢很显气质,而且呢也是搭配的绝佳单品,大家可以闭眼入,真的是非常好看,对身材的包容性也很好,不管啥身材的宝宝呢,穿上去都是很好看的。推荐宝宝们下单哦。

有声阅读

诗歌朗诵

cosyvoice-v1(longyue):明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。

语音导航

导航播报

cosyvoice-v1(longshuo):看起来您错过了上一个转弯点,没关系,我们会帮您重新规划路线。请在安全的情况下继续直行。

新闻播报

新闻播报

cosyvoice-v1(longfei):典型案例:二零二零年十一月二十八日,受害人王某在某游戏中看到有人在喊低价出售游戏币。

英文场景

秘密相授

cosyvoice-v1(longjielidou):Listen here, boy. I'm gonna teach you the secret formula on one condition. You can never let it fall into the hands of Plankton.

语音助手

请求解释

cosyvoice-v1(longxiaobai):对不起,我没有理解你的意思。可以再说一遍吗?

视频配音

带娃日常

cosyvoice-v1(longlaotie):各位宝爸宝妈,早上好!今天又是元气满满的一天!嗯?不对,是“战斗”的一天!

选择模型

阿里云百炼支持的语音合成模型包括CosyVoiceSambert。

  • 如果您希望声音更接近真人、有特色或者有生活气息,建议选择CosyVoice。CosyVoice基于新一代生成式语音大模型,能根据上下文预测情绪、语调、韵律等,具有更好的拟人效果。

  • 如果您希望边输入文本边合成语音(比如大模型流式输出文本时实时合成语音),请选择CosyVoice。

    Sambert不支持流式输入,需一次性输入完整文本才能开始合成语音。

    CosyVoice支持流式输入+流式输出,以及非流式输入+流式/非流式输出。Sambert仅支持非流式输入+流式/非流式输出。
  • 如果您希望对中小学常见数学表达式(如基础运算、代数、几何等)进行语音合成,请选择CosyVoice,详情请参见Latex能力支持说明

  • 如果您有以下特定需求,请选择Sambert。

    • 需要合成中英以外的语言(西班牙语、意大利语等)。

    • 需要在输出音频流的同时,输出每个汉字/英文单词在音频中的时间戳,用于驱动虚拟人口型、制作视频配音字幕等。

CosyVoice音色列表

cosyvoice-v2

适用场景

音色

音色特质

音频试听(右键保存音频)

voice参数

语言

SSML

客服

龙应催

严肃催收男

longyingcui

中、英

支持

龙应答

开朗高音女

longyingda

中、英

支持

龙应静

低调冷静女

longyingjing

中、英

支持

龙应严

义正严辞女

longyingyan

中、英

支持

龙应甜

温柔甜美女

longyingtian

中、英

支持

龙应冰

尖锐强势女

longyingbing

中、英

支持

龙应桃

温柔淡定女

longyingtao

中、英

支持

龙应聆

温和共情女

longyingling

中、英

支持

语音助手

YUMI

正经青年女

longyumi_v2

中、英

支持

龙小淳

知性积极女

longxiaochun_v2

中、英

支持

龙小夏

沉稳权威女

longxiaoxia_v2

中、英

支持

直播

龙安燃

活泼质感女

longanran

中、英

支持

龙安宣

经典直播女

longanxuan

中、英

支持

有声书

龙三叔

沉稳质感男

longsanshu

中、英

支持

龙修

博才说书男

longxiu_v2

中、英

支持

龙妙

抑扬顿挫女

longmiao_v2

中、英

支持

龙悦

温暖磁性女

longyue_v2

中、英

支持

龙楠

睿智青年男

longnan_v2

中、英

支持

龙媛

温暖治愈女

longyuan_v2

中、英

支持

社交陪伴

龙安柔

温柔闺蜜女

longanrou

中、英

支持

龙嫱

浪漫风情女

longqiang_v2

中、英

支持

龙寒

温暖痴情男

longhan_v2

中、英

支持

龙星

温婉邻家女

longxing_v2

中、英

支持

龙华

元气甜美女

longhua_v2

中、英

支持

龙婉

积极知性女

longwan_v2

中、英

支持

龙橙

智慧青年男

longcheng_v2

中、英

支持

龙菲菲

甜美娇气女

longfeifei_v2

中、英

支持

龙小诚

磁性低音男

longxiaocheng_v2

中、英

支持

龙哲

呆板大暖男

longzhe_v2

中、英

支持

龙颜

温暖春风女

longyan_v2

中、英

支持

龙天

磁性理智男

longtian_v2

中、英

支持

龙泽

温暖元气男

longze_v2

中、英

支持

龙邵

积极向上男

longshao_v2

中、英

支持

龙浩

多情忧郁男

longhao_v2

中、英

支持

龙深

实力歌手男

kabuleshen_v2

中、英

支持

童声

龙杰力豆

阳光顽皮男

longjielidou_v2

中、英

支持

龙铃

稚气呆板女

longling_v2

中、英

支持

龙可

懵懂乖乖女

longke_v2

中、英

支持

龙仙

豪放可爱女

longxian_v2

中、英

支持

方言

龙老铁

东北直率男

longlaotie_v2

中(东北)、英

支持

龙嘉怡

知性粤语女

longjiayi_v2

中(粤语)、英

支持

龙桃

积极粤语女

longtao_v2

中(粤语)、英

支持

诗词朗诵

龙飞

热血磁性男

longfei_v2

中、英

支持

李白

古代诗仙男

libai_v2

中、英

支持

龙津

优雅温润男

longjin_v2

中、英

支持

新闻播报

龙书

沉稳青年男

longshu_v2

中、英

支持

Bella2.0

精准干练女

loongbella_v2

中、英

支持

龙硕

博才干练男

longshuo_v2

中、英

支持

龙小白

沉稳播报女

longxiaobai_v2

中、英

支持

龙婧

典型播音女

longjing_v2

中、英

支持

loongstella

飒爽利落女

loongstella_v2

中、英

支持

出海营销

loongeva

知性英文女

loongeva_v2

英式英文

不支持

loongbrian

沉稳英文男

loongbrian_v2

英式英文

不支持

loongluna

英式英文女

loongluna_v2

英式英文

不支持

loongluca

英式英文男

loongluca_v2

英式英文

不支持

loongemily

英式英文女

loongemily_v2

英式英文

不支持

loongeric

英式英文男

loongeric_v2

英式英文

不支持

loongabby

美式英文女

loongabby_v2

美式英文

不支持

loongannie

美式英文女

loongannie_v2

美式英文

不支持

loongandy

美式英文男

loongandy_v2

美式英文

不支持

loongava

美式英文女

loongava_v2

美式英文

不支持

loongbeth

美式英文女

loongbeth_v2

美式英文

不支持

loongbetty

美式英文女

loongbetty_v2

美式英文

不支持

loongcindy

美式英文女

loongcindy_v2

美式英文

不支持

loongcally

美式英文女

loongcally_v2

美式英文

不支持

loongdavid

美式英文男

loongdavid_v2

美式英文

不支持

loongdonna

美式英文女

loongdonna_v2

美式英文

不支持

loongkyong

韩语女

loongkyong_v2

韩语

不支持

loongtomoka

日语女

loongtomoka_v2

日语

不支持

loongtomoya

日语男

loongtomoya_v2

日语

不支持

cosyvoice-v1

音色

音频试听(右键保存音频)

voice参数

适用场景

语言

龙婉

longwan

语音助手、

导航播报、

聊天数字人

中文普通话

龙橙

longcheng

语音助手、

导航播报、

聊天数字人

中文普通话

龙华

longhua

语音助手、

导航播报、

聊天数字人

中文普通话

龙小淳

longxiaochun

语音助手、

导航播报、

聊天数字人

中文普通话+英文

龙小夏

longxiaoxia

语音助手、聊天数字人

中文普通话

龙小诚

longxiaocheng

语音助手、导航播报、聊天数字人

中文普通话+英文

龙小白

longxiaobai

聊天数字人、有声书、语音助手

中文普通话

龙老铁

longlaotie

新闻播报、有声书、语音助手、直播带货、导航播报

中文东北口音

龙书

longshu

有声书、语音助手、导航播报、新闻播报、智能客服

中文普通话

龙硕

longshuo

语音助手、导航播报、新闻播报、客服催收

中文普通话

龙婧

longjing

语音助手、导航播报、新闻播报、客服催收

中文普通话

龙妙

longmiao

客服催收、导航播报、有声书、语音助手

中文普通话

龙悦

longyue

语音助手、诗词朗诵、有声书朗读、导航播报、新闻播报、客服催收

中文普通话

龙媛

longyuan

有声书、语音助手、聊天数字人

中文普通话

龙飞

longfei

会议播报、新闻播报、有声书

中文普通话

龙杰力豆

longjielidou

新闻播报、有声书、聊天助手

中文普通话+英文

龙彤

longtong

有声书、导航播报、聊天数字人

中文普通话

龙祥

longxiang

新闻播报、有声书、导航播报

中文普通话

Stella

loongstella

语音助手、直播带货、导航播报、客服催收、有声书

中文普通话+英文

Bella

loongbella

语音助手、客服催收、新闻播报、导航播报

中文普通话

Sambert音色列表

模型名称

音色

音频试听

时间戳支持

适用场景

特色

语言

默认采样率(Hz)

sambert-zhinan-v1

知楠

通用场景

广告男声

中文+英文

48k

sambert-zhiqi-v1

知琪

通用场景

温柔女声

中文+英文

48k

sambert-zhichu-v1

知厨

新闻播报

舌尖男声

中文+英文

48k

sambert-zhide-v1

知德

新闻播报

新闻男声

中文+英文

48k

sambert-zhijia-v1

知佳

新闻播报

标准女声

中文+英文

48k

sambert-zhiru-v1

知茹

新闻播报

新闻女声

中文+英文

48k

sambert-zhiqian-v1

知倩

配音解说、新闻播报

资讯女声

中文+英文

48k

sambert-zhixiang-v1

知祥

配音解说

磁性男声

中文+英文

48k

sambert-zhiwei-v1

知薇

阅读产品简介

萝莉女声

中文+英文

48k

sambert-zhihao-v1

知浩

通用场景

咨询男声

中文+英文

16k

sambert-zhijing-v1

知婧

通用场景

严厉女声

中文+英文

16k

sambert-zhiming-v1

知茗

通用场景

诙谐男声

中文+英文

16k

sambert-zhimo-v1

知墨

通用场景

情感男声

中文+英文

16k

sambert-zhina-v1

知娜

通用场景

浙普女声

中文+英文

16k

sambert-zhishu-v1

知树

通用场景

资讯男声

中文+英文

16k

sambert-zhistella-v1

知莎

通用场景

知性女声

中文+英文

16k

sambert-zhiting-v1

知婷

通用场景

电台女声

中文+英文

16k

sambert-zhixiao-v1

知笑

通用场景

资讯女声

中文+英文

16k

sambert-zhiya-v1

知雅

通用场景

严厉女声

中文+英文

16k

sambert-zhiye-v1

知晔

通用场景

青年男声

中文+英文

16k

sambert-zhiying-v1

知颖

通用场景

软萌童声

中文+英文

16k

sambert-zhiyuan-v1

知媛

通用场景

知心姐姐

中文+英文

16k

sambert-zhiyue-v1

知悦

客服

温柔女声

中文+英文

16k

sambert-zhigui-v1

知柜

阅读产品简介

直播女声

中文+英文

16k

sambert-zhishuo-v1

知硕

数字人

自然男声

中文+英文

16k

sambert-zhimiao-emo-v1

知妙(多情感)

阅读产品简介、数字人、直播

多种情感女声

中文+英文

16k

sambert-zhimao-v1

知猫

阅读产品简介、配音解说、数字人、直播

直播女声

中文+英文

16k

sambert-zhilun-v1

知伦

配音解说

悬疑解说

中文+英文

16k

sambert-zhifei-v1

知飞

配音解说

激昂解说

中文+英文

16k

sambert-zhida-v1

知达

新闻播报

标准男声

中文+英文

16k

sambert-camila-v1

Camila

通用场景

西班牙语女声

西班牙语

16k

sambert-perla-v1

Perla

通用场景

意大利语女声

意大利语

16k

sambert-indah-v1

Indah

通用场景

印尼语女声

印尼语

16k

sambert-clara-v1

Clara

通用场景

法语女声

法语

16k

sambert-hanna-v1

Hanna

通用场景

德语女声

德语

16k

sambert-beth-v1

Beth

通用场景

咨询女声

美式英文

16k

sambert-betty-v1

Betty

通用场景

客服女声

美式英文

16k

sambert-cally-v1

Cally

通用场景

自然女声

美式英文

16k

sambert-cindy-v1

Cindy

通用场景

对话女声

美式英文

16k

sambert-eva-v1

Eva

通用场景

陪伴女声

美式英文

16k

sambert-donna-v1

Donna

通用场景

教育女声

美式英文

16k

sambert-brian-v1

Brian

通用场景

客服男声

美式英文

16k

sambert-waan-v1

Waan

通用场景

泰语女声

泰语

16k

点击查看功能特性对比

语音合成CosyVoice

语音合成Sambert

接入方式

Python、Java、WebSocket

Python、Java、WebSocket

SSML

支持,仅限cosyvoice-v2模型

支持(参见SSML标记语言

Latex数学表达式

支持

支持中小学常见数学表达式(如基础运算、代数、几何等)的语音合成,详情请参见Latex能力支持说明

不支持

流式输入

支持

不支持

流式输出

支持

支持

合成音频格式

  • pcm

  • wav

  • mp3

  • pcm

  • wav

  • mp3

合成音频采样率

  • 8kHz

  • 16kHz

  • 22.05kHz

  • 24kHz

  • 44.1kHz

  • 48kHz

模型而异:

  • 16kHz

  • 48kHz

音量调节

支持

支持

语速调节

支持

支持

语调调节

支持

支持

时间戳

不支持

支持

语言

音色而异:

  • cosyvoice-v1支持中文、英文、中文东北口音

  • cosyvoice-v2支持中文(普通话、东北话、粤语)、英文(英式、美式)、韩语、日语

模型而异:中文、英文、美式英文、意大利语、西班牙语、印尼语、法语、德语、泰语

声音复刻

支持

不支持

待合成文本长度限制

流式输入:每次发送的文本片段长度不超过2000字符,所有文本片段总计长度不超过20万字符

非流式输入:文本总长度不超过2000字符

字符计算规则:
1个汉字算作2个字符
1个英文字母、1个标点或1个句子中间的空格均算作1个字符
在计算文本总长度时,SSML标签会被计入,但实际计费仅基于标签之间的可合成文本部分,不包含标签本身

最高字符限制:1万字符

字符计算规则:1个汉字、1个英文字母、1个标点或1个句子中间空格均算作1个字符

单价

2元/万字符

根据待合成字符数计费(其中1个汉字算2个字符,英文、标点符号均按照个1个字符计费)
SSML标签内容不计费

1元/万字符

根据待合成字符数计费(其中1个汉字算2个字符,英文、标点符号均按照个1个字符计费)
SSML标签内容不计费

免费额度

每主账号每模型每月2000字符

每主账号每模型每月3万字符

在线体验

目前,仅cosyvoice-v1支持在线体验:在语音合成页面直接选择“语音合成CosyVoice大模型”。

Sambertcosyvoice-v2暂不支持在线体验,如需使用请通过API接入。

快速开始

下面是调用API的示例代码。更多常用场景的代码示例,请参见GitHub

您需要已获取API Key配置API Key到环境变量。如果通过SDK调用,还需要安装DashScope SDK

CosyVoice

将合成音频保存为文件

import dashscope
from dashscope.audio.tts_v2 import *

# 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
# dashscope.api_key = "apiKey"
model = "cosyvoice-v2"
voice = "longxiaochun_v2"

synthesizer = SpeechSynthesizer(model=model, voice=voice)
audio = synthesizer.call("今天天气怎么样?")
print('requestId: ', synthesizer.get_last_request_id())
with open('output.mp3', 'wb') as f:
    f.write(audio)
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class Tts2File {
  private static String model = "cosyvoice-v2";
  private static String voice = "longxiaochun_v2";

  public static void synthesizeAndSaveAudio() {
    SpeechSynthesisParam param =
        SpeechSynthesisParam.builder()
            // 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
            // .apiKey(apikey)
            .model(model)
            .voice(voice)
            .build();
    SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
    ByteBuffer audio = synthesizer.call("今天天气怎么样?");
    File file = new File("output.mp3");
    System.out.print("requestId: " + synthesizer.getLastRequestId());
    try (FileOutputStream fos = new FileOutputStream(file)) {
      fos.write(audio.array());
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) {
    synthesizeAndSaveAudio();
    System.exit(0);
  }
}

LLM生成的文本实时转成语音并通过扬声器播放

以下代码展示通过本地设备播放通义千问大语言模型(qwen-turbo)实时返回的文本内容。

Python

运行Python示例前,需要通过pip安装第三方音频播放库。

# 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 dashscope
from dashscope.audio.tts_v2 import *


from http import HTTPStatus
from dashscope import Generation

# 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
# dashscope.api_key = "apiKey"
model = "cosyvoice-v2"
voice = "longxiaochun_v2"


class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        print("websocket is open.")
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=22050, output=True
        )

    def on_complete(self):
        print("speech synthesis task complete successfully.")

    def on_error(self, message: str):
        print(f"speech synthesis task failed, {message}")

    def on_close(self):
        print("websocket is closed.")
        # stop player
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()

    def on_event(self, message):
        print(f"recv speech synthsis message {message}")

    def on_data(self, data: bytes) -> None:
        print("audio result length:", len(data))
        self._stream.write(data)


def synthesizer_with_llm():
    callback = Callback()
    synthesizer = SpeechSynthesizer(
        model=model,
        voice=voice,
        format=AudioFormat.PCM_22050HZ_MONO_16BIT,
        callback=callback,
    )

    messages = [{"role": "user", "content": "请介绍一下你自己"}]
    responses = Generation.call(
        model="qwen-turbo",
        messages=messages,
        result_format="message",  # set result format as 'message'
        stream=True,  # enable stream output
        incremental_output=True,  # enable incremental output 
    )
    for response in responses:
        if response.status_code == HTTPStatus.OK:
            print(response.output.choices[0]["message"]["content"], end="")
            synthesizer.streaming_call(response.output.choices[0]["message"]["content"])
        else:
            print(
                "Request id: %s, Status code: %s, error code: %s, error message: %s"
                % (
                    response.request_id,
                    response.status_code,
                    response.code,
                    response.message,
                )
            )
    synthesizer.streaming_complete()
    print('requestId: ', synthesizer.get_last_request_id())


if __name__ == "__main__":
    synthesizer_with_llm()

Java

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.*;

public class Main {
    private static String model = "cosyvoice-v2";
    private static String voice = "longxiaochun_v2";
    public static void process() throws NoApiKeyException, InputRequiredException {
        // Playback thread
        class PlaybackRunnable implements Runnable {
            // Set the audio format. Please configure according to your actual device,
            // synthesized audio parameters, and platform choice Here it is set to
            // 22050Hz16bit single channel. It is recommended that customers choose other
            // sample rates and formats based on the model sample rate and device
            // compatibility.
            private AudioFormat af = new AudioFormat(22050, 16, 1, true, false);
            private DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
            private SourceDataLine targetSource = null;
            private AtomicBoolean runFlag = new AtomicBoolean(true);
            private ConcurrentLinkedQueue<ByteBuffer> queue =
                    new ConcurrentLinkedQueue<>();

            // Prepare the player
            public void prepare() throws LineUnavailableException {
                targetSource = (SourceDataLine) AudioSystem.getLine(info);
                targetSource.open(af, 4096);
                targetSource.start();
            }

            public void put(ByteBuffer buffer) {
                queue.add(buffer);
            }

            // Stop playback
            public void stop() {
                runFlag.set(false);
            }

            @Override
            public void run() {
                if (targetSource == null) {
                    return;
                }

                while (runFlag.get()) {
                    if (queue.isEmpty()) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                        }
                        continue;
                    }

                    ByteBuffer buffer = queue.poll();
                    if (buffer == null) {
                        continue;
                    }

                    byte[] data = buffer.array();
                    targetSource.write(data, 0, data.length);
                }

                // Play all remaining cache
                if (!queue.isEmpty()) {
                    ByteBuffer buffer = null;
                    while ((buffer = queue.poll()) != null) {
                        byte[] data = buffer.array();
                        targetSource.write(data, 0, data.length);
                    }
                }
                // Release the player
                targetSource.drain();
                targetSource.stop();
                targetSource.close();
            }
        }

        // Create a subclass inheriting from ResultCallback<SpeechSynthesisResult>
        // to implement the callback interface
        class ReactCallback extends ResultCallback<SpeechSynthesisResult> {
            private PlaybackRunnable playbackRunnable = null;

            public ReactCallback(PlaybackRunnable playbackRunnable) {
                this.playbackRunnable = playbackRunnable;
            }

            // Callback when the service side returns the streaming synthesis result
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                // Get the binary data of the streaming result via getAudio
                if (result.getAudioFrame() != null) {
                    // Stream the data to the player
                    playbackRunnable.put(result.getAudioFrame());
                }
            }

            // Callback when the service side completes the synthesis
            @Override
            public void onComplete() {
                // Notify the playback thread to end
                playbackRunnable.stop();
            }

            // Callback when an error occurs
            @Override
            public void onError(Exception e) {
                // Tell the playback thread to end
                System.out.println(e);
                playbackRunnable.stop();
            }
        }

        PlaybackRunnable playbackRunnable = new PlaybackRunnable();
        try {
            playbackRunnable.prepare();
        } catch (LineUnavailableException e) {
            throw new RuntimeException(e);
        }
        Thread playbackThread = new Thread(playbackRunnable);
        // Start the playback thread
        playbackThread.start();
        /*******  Call the Generative AI Model to get streaming text *******/
        // Prepare for the LLM call
        Generation gen = new Generation();
        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content("请介绍一下你自己")
                .build();
        GenerationParam genParam =
                GenerationParam.builder()
                        // 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
                        // .apiKey("apikey")
                        .model("qwen-turbo")
                        .messages(Arrays.asList(userMsg))
                        .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                        .topP(0.8)
                        .incrementalOutput(true)
                        .build();
        // Prepare the speech synthesis task
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
                        // .apiKey("apikey")
                        .model(model)
                        .voice(voice)
                        .format(SpeechSynthesisAudioFormat
                                .PCM_22050HZ_MONO_16BIT)
                        .build();
        SpeechSynthesizer synthesizer =
                new SpeechSynthesizer(param, new ReactCallback(playbackRunnable));
        Flowable<GenerationResult> result = gen.streamCall(genParam);
        result.blockingForEach(message -> {
            String text =
                    message.getOutput().getChoices().get(0).getMessage().getContent();
            System.out.println("LLM output:" + text);
            synthesizer.streamingCall(text);
        });
        synthesizer.streamingComplete();
        System.out.print("requestId: " + synthesizer.getLastRequestId());
        try {
            // Wait for the playback thread to finish playing all
            playbackThread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws NoApiKeyException, InputRequiredException {
        process();
        System.exit(0);
    }
}

Sambert

将合成音频保存为文件

import dashscope
from dashscope.audio.tts import SpeechSynthesizer

# 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
# dashscope.api_key = "apiKey"
result = SpeechSynthesizer.call(model='sambert-zhichu-v1',
                                # 当text内容的语种发生变化时,请确认model是否匹配。不同model支持不同的语种,详情请参见Sambert音色列表中的“语言”列。
                                text='今天天气怎么样',
                                sample_rate=48000,
                                format='wav')
print('requestId: ', result.get_response()['request_id'])
if result.get_audio_data() is not None:
    with open('output.wav', 'wb') as f:
        f.write(result.get_audio_data())
print(' get response: %s' % (result.get_response()))

import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.common.Status;

import java.io.*;
import java.nio.ByteBuffer;

public class Main {

    public static void SyncAudioDataToFile() {
        SpeechSynthesizer synthesizer = new SpeechSynthesizer();
        SpeechSynthesisParam param = SpeechSynthesisParam.builder()
          // 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
          // .apiKey(apikey)
          .model("sambert-zhichu-v1")
          // 当text内容的语种发生变化时,请确认model是否匹配。不同model支持不同的语种,详情请参见Sambert音色列表中的“语言”列。
          .text("今天天气怎么样")
          .sampleRate(48000)
          .format(SpeechSynthesisAudioFormat.WAV)
          .build();

        File file = new File("output.wav");
        // 调用call方法,传入param参数,获取合成音频
        ByteBuffer audio = synthesizer.call(param);
        System.out.println("requestId: " + synthesizer.getLastRequestId());
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(audio.array());
            System.out.println("synthesis done!");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        SyncAudioDataToFile();
        System.exit(0);
    }
}

将合成的音频通过扬声器播放

合成语音后,通过本地设备播放实时返回的音频内容。

运行Python示例前,需要通过pip安装第三方音频播放库。

# 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 dashscope
import sys
import pyaudio
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
from dashscope.audio.tts import ResultCallback, SpeechSynthesizer, SpeechSynthesisResult

# 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
# dashscope.api_key = "apiKey"

class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        print('Speech synthesizer is opened.')
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=48000,
            output=True)

    def on_complete(self):
        print('Speech synthesizer is completed.')

    def on_error(self, response: SpeechSynthesisResponse):
        print('Speech synthesizer failed, response is %s' % (str(response)))

    def on_close(self):
        print('Speech synthesizer is closed.')
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()

    def on_event(self, result: SpeechSynthesisResult):
        if result.get_audio_frame() is not None:
            print('audio result length:', sys.getsizeof(result.get_audio_frame()))
            self._stream.write(result.get_audio_frame())

        if result.get_timestamp() is not None:
            print('timestamp result:', str(result.get_timestamp()))

callback = Callback()
result = SpeechSynthesizer.call(model='sambert-zhichu-v1',
                       text='今天天气怎么样',
                       sample_rate=48000,
                       format='pcm',
                       callback=callback)
print('requestId: ', result.get_response()['request_id'])
import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.common.ResultCallback;

import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.sound.sampled.*;

public class Main {

    public static void StreamAuidoDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);
        SpeechSynthesizer synthesizer = new SpeechSynthesizer();
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 若没有将API Key配置到环境变量中,需将下面这行代码注释放开,并将apiKey替换为自己的API Key
                        // .apiKey("apikey")
                        .text("今天天气怎么样")
                        .model("sambert-zhichu-v1")
                        .sampleRate(48000)
                        .format(SpeechSynthesisAudioFormat.PCM) // 流式合成使用PCM或者MP3
                        .build();

        // 播放线程
        class PlaybackRunnable implements Runnable {
            // 设置音频格式,请根据实际自身设备,合成音频参数和平台选择配置
            // 这里选择48k16bit单通道,建议客户根据选用的模型采样率情况和自身设备兼容性选择其他采样率和格式
            private AudioFormat af = new AudioFormat(48000, 16, 1, true, false);
            private DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
            private SourceDataLine targetSource = null;
            private AtomicBoolean runFlag = new AtomicBoolean(true);
            private ConcurrentLinkedQueue<ByteBuffer> queue = new ConcurrentLinkedQueue<>();

            // 准备播放器
            public void prepare() throws LineUnavailableException {
                targetSource = (SourceDataLine) AudioSystem.getLine(info);
                targetSource.open(af, 4096);
                targetSource.start();
            }

            public void put(ByteBuffer buffer) {
                queue.add(buffer);
            }

            // 停止播放
            public void stop() {
                runFlag.set(false);
            }

            @Override
            public void run() {
                if (targetSource == null) {
                    return;
                }

                while (runFlag.get()) {
                    if (queue.isEmpty()) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {

                        }
                        continue;
                    }

                    ByteBuffer buffer = queue.poll();
                    if (buffer == null) {
                        continue;
                    }

                    byte[] data = buffer.array();
                    targetSource.write(data, 0, data.length);
                }

                // 将缓存全部播放完
                if (!queue.isEmpty()) {
                    ByteBuffer buffer = null;
                    while ((buffer = queue.poll()) != null) {
                        byte[] data = buffer.array();
                        targetSource.write(data, 0, data.length);
                    }
                }

                // 释放播放器
                targetSource.drain();
                targetSource.stop();
                targetSource.close();
            }
        }

        // 创建一个继承自ResultCallback<SpeechSynthesisResult>的子类来实现回调接口
        class ReactCallback extends ResultCallback<SpeechSynthesisResult> {
            private PlaybackRunnable playbackRunnable = null;

            public ReactCallback(PlaybackRunnable playbackRunnable) {
                this.playbackRunnable = playbackRunnable;
            }

            // 当服务侧返回流式合成结果后回调
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                // 通过getAudio获取流式结果二进制数据
                if (result.getAudioFrame() != null) {
                    // 将数据流式推给播放器
                    playbackRunnable.put(result.getAudioFrame());
                }
            }

            // 当服务侧完成合成后回调
            @Override
            public void onComplete() {
                // 告知播放线程结束
                playbackRunnable.stop();
                latch.countDown();
            }

            // 当出现错误时回调
            @Override
            public void onError(Exception e) {
                // 告诉播放线程结束
                System.out.println(e);
                playbackRunnable.stop();
                latch.countDown();
            }
        }

        PlaybackRunnable playbackRunnable = new PlaybackRunnable();
        try {
            playbackRunnable.prepare();
        } catch (LineUnavailableException e) {
            throw new RuntimeException(e);
        }
        Thread playbackThread = new Thread(playbackRunnable);
        // 启动播放线程
        playbackThread.start();
        // 带Callbackcall方法将不会阻塞当前线程
        synthesizer.call(param, new ReactCallback(playbackRunnable));
        System.out.println("requestId: " + synthesizer.getLastRequestId());
        // 等待合成完成
        try {
            latch.await();
            // 等待播放线程全部播放完
            playbackThread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        StreamAuidoDataToSpeaker();
        System.exit(0);
    }
}

API参考

模型应用上架及备案

参见通义大模型应用上架及合规备案

常见问题

1. 语音合成的发音读错怎么办?多音字如何控制发音?

您可以尝试:

  • 将多音字替换成同音的其他汉字快速解决发音问题。

  • 使用SSML标记语言:SambertCosyvoice都支持SSML。

  • GitHub中扫描钉钉群的二维码,加入钉钉群,联系产品研发进行优化。

2. 限流

语音合成-CosyVoice

模型名称

提交作业接口RPS限制

cosyvoice-v1

3

cosyvoice-v2

3

CosyVoice声音复刻

模型名称

提交作业接口RPS限制

cosyvoice-v1

10

声音复刻时,无论您是仅使用 v1、仅使用 v2,还是同时调用两者,系统对所有请求的总并发限制均为 10 RPS。这意味着:

  • 如果您仅调用 v1,则其最大并发请求为 10 RPS。

  • 如果您同时调用 v1 和 v2,两者的请求总和不能超过 10 RPS(例如,v1 使用 7 RPS,则 v2 最多只能使用 3 RPS)。

cosyvoice-v2

语音合成-Sambert

模型服务

提交作业接口RPS限制

Sambert系列模型

20