即時語音合成CosyVoice Java SDK

更新時間:
Copy as MD

通過DashScope Java SDK進行CosyVoice語音合成。

使用者指南:關於模型介紹和選型建議請參見語音合成

服務端點

SDK的服務端點需在初始化前設定為下方地址(包含WorkspaceId)。如需切換到其他地區,請修改 Constants.baseWebsocketApiUrl為對應地區的URL。

新加坡

wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference

調用時請將{WorkspaceId}替換為真實的Workspace ID

華北2(北京)

wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference

調用時請將{WorkspaceId}替換為真實的Workspace ID

切換到新加坡地區

import com.alibaba.dashscope.utils.Constants;

// 調用時請將"{WorkspaceId}"替換為真實的業務空間ID
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
重要

阿里雲百鍊為華北2(北京)、新加坡地區推出了業務空間專屬網域名稱,能夠為推理請求提供卓越的效能和更高的穩定性,建議遷移至新網域名稱:

  • 華北2(北京)地區:從 dashscope.aliyuncs.com 遷移至 {WorkspaceId}.cn-beijing.maas.aliyuncs.com

  • 新加坡地區:從 dashscope-intl.aliyuncs.com 遷移至 {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com

{WorkspaceId}需要替換為真實的Workspace ID。現有網域名稱仍可正常使用。

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)

參數說明

參數

類型

必填

說明

text

String

待合成的文本,長度不得超過20000字元。

傳回值ByteBuffer 或 null。非流式調用時返回完整音頻資料;單向流式調用時音頻通過回調返回,此方法返回null。

streamingCall() - 雙向流式合成

方法簽名

public void streamingCall(String text)

參數說明

參數

類型

必填

說明

text

String

待合成的文本,長度不得超過20000字元。可多次調用追加文本。

streamingComplete() - 結束雙向流式調用

方法簽名

public void streamingComplete()

結束雙向流式調用,通知服務端所有文本已發送完畢。

callAsFlowable() - 單向流式合成(響應式)

方法簽名

public Flowable<SpeechSynthesisResult> callAsFlowable(String text)

參數說明

參數

類型

必填

說明

text

String

待合成的文本。

傳回值Flowable<SpeechSynthesisResult> 響應式流。

streamingCallAsFlowable() - 雙向流式合成(響應式)

方法簽名

public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)

參數說明

參數

類型

必填

說明

textStream

Flowable<String>

文本的響應式流。

傳回值Flowable<SpeechSynthesisResult> 響應式流。

getDuplexApi().close() - 關閉WebSocket串連

方法簽名

public boolean getDuplexApi().close(int code, String reason)

參數說明

參數

類型

必填

說明

code

int

關閉碼。

reason

String

關閉原因。

傳回值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("cosyvoice-v3-flash") // 模型
    .voice("longanyang") // 音色
    .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

voice string (必選)

語音合成所使用的音色。

  • 系統音色:參見CosyVoice音色列表

  • 複刻音色:通過聲音複刻功能定製

  • 聲音設計音色:通過聲音設計功能定製

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。

僅在流式輸出模式下可用。支援的音色範圍:cosyvoice-v3.5-plus、cosyvoice-v3.5-flash、cosyvoice-v3-flash、cosyvoice-v3-pluscosyvoice-v2模型的複刻音色,以及CosyVoice音色列表中標記為支援的系統音色。其他模型的複刻音色不支援此功能。

seed(int)

int

產生時使用的隨機數種子,使合成的效果產生變化。在模型版本、文本、音色及其他參數均相同的前提下,使用相同的seed可複現相同的合成結果。

預設值0。

取值範圍:[0, 65535]。

SDK版本低於2.21.7時,seed需要通過擴充參數進行設定。

languageHints(List<String>)

List<String>

重要
  • 此參數為數組,但目前的版本僅處理第一個元素,因此建議只傳入一個值。

  • 此參數用於指定語音合成的目標語言,該設定與聲音複刻時的樣本音訊語種無關。如需設定複刻任務的源語言,請參見聲音複刻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:越南語

instruction(String)

String

設定指令,用於控制方言、情感或角色等合成效果。

使用說明請參見指令控制

hotFix(ParamHotFix)

ParamHotFix

文本熱修複配置,用於自訂指定詞語的發音或對待合成文本進行替換。

cosyvoice-v2不支援該功能。

參數介紹:

  • pronunciation:自訂發音。指定詞語的拼音標註,用於糾正預設發音不準確的情況。

  • replace:文本替換。在語音合成前將指定詞語替換為目標文本,替換後的文本將作為實際合成內容。

樣本:

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("cosyvoice-v3-flash") // 模型
                        .voice("your_voice") // 替換成cosyvoice-v3-flash複刻音色
                        .hotFix(paramHotFix)
                        .build();

parameter(String key, Object value)

String, Object

設定擴充參數

parameters(Map<String, Object>)

Map

設定擴充參數

擴充參數

通過 parameter() 或 parameters() 設定。

樣本

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
  .model("cosyvoice-v3-flash")
  .voice("longanyang")
  .parameter("enable_markdown_filter", true)
  .build();

參數名

類型

必填

說明

bit_rate

integer

音頻碼率(kbps)。音頻格式為opus時,支援通過bit_rate參數調整碼率。

預設值:32。

取值範圍:[6, 510]。

enable_aigc_tag

boolean

是否在產生的音頻中添加AIGC隱性標識。設定為true時,會將隱性標識嵌入到支援格式(wav/mp3/opus)的音頻中。

預設值:false。

cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。

aigc_propagator

String

設定AIGC隱性標識中的 ContentPropagator 欄位,用於標識內容的傳播者。僅在 enable_aigc_tag 為 true 時生效。

預設值:阿里雲UID。

cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。

aigc_propagate_id

String

設定AIGC隱性標識中的 PropagateID 欄位,用於唯一標識一次具體的傳播行為。僅在 enable_aigc_tag 為 true 時生效。

預設值:本次語音合成請求Request ID。

cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。

enable_markdown_filter

boolean

重要

cosyvoice-v3-flash複刻音色支援該功能。

是否啟用 Markdown 過濾。啟用該功能後,系統在合成語音前自動過濾輸入文本中的 Markdown 標記符號,避免將其朗讀為文字內容。

預設值:false。

取值範圍:

  • true:啟用Markdown過濾

  • false:禁用Markdown過濾

ResultCallback

包路徑com.alibaba.dashscope.common.ResultCallback

onEvent() - 接收音頻資料

方法簽名

public void onEvent(SpeechSynthesisResult result)

參數說明

參數

類型

必填

說明

result

SpeechSynthesisResult

接收到合成事件時觸發,包含音訊框架、時間戳記資訊和輸出資訊(事件類型、原始文本等)。

onComplete() - 合成完成

方法簽名

public void onComplete()

語音合成完成時觸發。

onError() - 錯誤處理

方法簽名

public void onError(Exception e)

參數說明

參數

類型

必填

說明

e

Exception

發生錯誤時觸發,包含異常資訊。

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()

傳回值WordList集合,批量擷取字層級時間戳記資訊,可能為空白。

字層級時間戳記資訊(Word

Word封裝了字層級時間戳記資訊。

getBeginTime() - 擷取詞開始時間

方法簽名

public int getBeginTime()

傳回值:詞開始時間,單位為ms。

getEndTime() - 擷取詞結束時間

方法簽名

public int getEndTime()

傳回值:詞結束時間,單位為ms。

getText() - 擷取文本資訊

方法簽名

public String getText()

傳回值String,文本資訊。

getPhonemes() - 擷取音素層級時間戳記

方法簽名

public List<Phoneme> getPhonemes()

傳回值PhonemeList集合,批量擷取音素層級時間戳記資訊,可能為空白。

音素層級時間戳記資訊(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流中擷取。包含以下欄位:

欄位

類型

說明

type

String

事件類型。取值:sentence-begin(句子開始,返回待合成的常值內容)、sentence-synthesis(標識音頻資料區塊,表示當前正在合成音頻)、sentence-end(句子結束,返迴文本內容和字層級時間戳記)。

original_text

String

當前句子的原始常值內容。在sentence-beginsentence-end事件中返回。

sentence

JsonObject

句子資訊,包含句子編號(index)和字層級時間戳記(words)。在sentence-end事件中包含完整的字層級時間戳記資訊。

範例程式碼

SDK提供了語音合成的關鍵介面,支援以下幾種調用方式:

  • 非流式調用:阻塞式,一次性發送完整文本,直接返回完整音頻。適合短文本語音合成情境。

  • 單向流式調用:非阻塞式,一次性發送完整文本,通過回呼函數接收音頻資料(可能分區)。適用於對即時性要求高的短文本語音合成情境。

  • 雙向流式調用:非阻塞式,可分多次發送文本片段,通過回呼函數即時接收增量合成的音頻流。適合即時性要求高的長文本語音合成情境。

非流式調用

發送的文本長度不得超過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 = "cosyvoice-v3-flash";
    // 音色
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() {
        // 請求參數
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡地區和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                        // 若沒有配置環境變數,請用百鍊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) {
        // 以下為新加坡地區的配置,調用時請將"{WorkspaceId}"替換為真實的業務空間ID,各地區的配置不同。
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

單向流式調用

發送的文本長度不得超過20000字元。

重要

每次調用call方法前,需要重新初始化SpeechSynthesizer執行個體。

import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;

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 = "cosyvoice-v3-flash";
    // 音色
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);

        // 實現回調介面ResultCallback
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult 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() : ""));
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " 收到Complete,語音合成結束");
                latch.countDown();
            }

            @Override
            public void onError(Exception e) {
                System.out.println("出現異常:" + e.toString());
                latch.countDown();
            }
        };

        // 請求參數
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡地區和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                        // 若沒有配置環境變數,請用百鍊API Key將下行替換為:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // 模型
                        .voice(voice) // 音色
                        .build();
        // 第二個參數"callback"傳入回調即啟用非同步模式
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // 非阻塞調用,立即返回null(實際結果通過回調介面非同步傳遞),在回調介面的onEvent方法中即時擷取二進位音頻
        try {
            synthesizer.call("今天天氣怎麼樣?");
            // 等待合成完成
            latch.await();
            // 等待播放線程全部播放完
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 任務結束後關閉websocket串連
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        // 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
        System.out.println(
                "[Metric] requestId為:"
                        + synthesizer.getLastRequestId()
                        + ",首包延遲(毫秒)為:"
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // 以下為新加坡地區的配置,調用時請將"{WorkspaceId}"替換為真實的業務空間ID,各地區的配置不同。
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

雙向流式調用

單次發送文本長度不得超過 20000 字元,且累計發送文本總長度不得超過 20 萬字元。

  • 流式輸入時可多次調用streamingCall按順序提交文本片段。服務端接收文本片段後自動進行分句:

    • 完整語句立即合成

    • 不完整語句緩衝至完整後合成

    調用 streamingComplete 時,服務端會強制合成所有已接收但未處理的文本片段(包括未完成的句子)。

  • 發送文本片段的間隔不得超過23秒,否則觸發“request timeout after 23 seconds”異常。

    若無待發送文本,需及時調用 streamingComplete結束任務。

    重要

    請務必確保調用streamingComplete方法,否則可能會導致結尾部分的文本無法成功轉換為語音。

    服務端強制設定23秒逾時機制,用戶端無法修改該配置。
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.ResultCallback;
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[] textArray = {"流式文本語音合成SDK,",
            "可以將輸入的文本", "合成為語音位元據,", "相比於非流式語音合成,",
            "流式合成的優勢在於即時性", "更強。使用者在輸入文本的同時",
            "可以聽到接近同步的語音輸出,", "極大地提升了互動體驗,",
            "減少了使用者等待時間。", "適用於調用大規模", "語言模型(LLM),以",
            "流式輸入文本的方式", "進行語音合成的情境。"};
    private static String model = "cosyvoice-v3-flash"; // 模型
    private static String voice = "longanyang"; // 音色

    public static void streamAudioDataToSpeaker() {
        // 配置回呼函數
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                // System.out.println("收到訊息: " + result);
                if (result.getAudioFrame() != null) {
                    // 此處實現處理音頻資料的邏輯
                    System.out.println(TimeUtils.getTimestamp() + " 收到音頻");
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " 收到Complete,語音合成結束");
            }

            @Override
            public void onError(Exception e) {
                System.out.println("出現異常:" + e.toString());
            }
        };

        // 請求參數
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡地區和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                        // 若沒有配置環境變數,請用百鍊API Key將下行替換為:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model)
                        .voice(voice)
                        .format(SpeechSynthesisAudioFormat
                                .PCM_22050HZ_MONO_16BIT) // 流式合成使用PCM或者MP3
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // 帶Callback的call方法將不會阻塞當前線程
        try {
            for (String text : textArray) {
                // 發送文本片段,在回調介面的onEvent方法中即時擷取二進位音頻
                synthesizer.streamingCall(text);
            }
            // 等待結束流式語音合成
            synthesizer.streamingComplete();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 任務結束關閉websocket串連
            synthesizer.getDuplexApi().close(1000, "bye");
        }

        // 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
        System.out.println(
                "[Metric] requestId為:"
                        + synthesizer.getLastRequestId()
                        + ",首包延遲(毫秒)為:"
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // 以下為新加坡地區的配置,調用時請將"{WorkspaceId}"替換為真實的業務空間ID,各地區的配置不同。
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

通過Flowable調用

Flowable是一個用於工作流程和商務程序管理的開源架構,它基於Apache 2.0許可證發布。關於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 = "cosyvoice-v3-flash"; // 模型
    private static String voice = "longanyang"; // 音色

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // 請求參數
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡地區和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                        // 若沒有配置環境變數,請用百鍊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 {
        // 以下為新加坡地區的配置,調用時請將"{WorkspaceId}"替換為真實的業務空間ID,各地區的配置不同。
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

雙向流式調用

以下樣本展示了通過Flowable對象作為輸入參數,輸入文字資料流。並通過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 io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;

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[] textArray = {"流式文本語音合成SDK,",
            "可以將輸入的文本", "合成為語音位元據,", "相比於非流式語音合成,",
            "流式合成的優勢在於即時性", "更強。使用者在輸入文本的同時",
            "可以聽到接近同步的語音輸出,", "極大地提升了互動體驗,",
            "減少了使用者等待時間。", "適用於調用大規模", "語言模型(LLM),以",
            "流式輸入文本的方式", "進行語音合成的情境。"};
    private static String model = "cosyvoice-v3-flash";
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // 類比流式輸入
        Flowable<String> textSource = Flowable.create(emitter -> {
            new Thread(() -> {
                for (int i = 0; i < textArray.length; i++) {
                    emitter.onNext(textArray[i]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                emitter.onComplete();
            }).start();
        }, BackpressureStrategy.BUFFER);

        // 請求參數
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡地區和北京地區的API Key不同。擷取API Key:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                        // 若沒有配置環境變數,請用百鍊API Key將下行替換為:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // 模型
                        .voice(voice) // 音色
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.streamingCallAsFlowable(textSource).blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // 此處實現播放音訊邏輯
                System.out.println(
                        TimeUtils.getTimestamp() +
                                " 二進位音頻大小為:" + result.getAudioFrame().capacity());
            }
            // 擷取輸出資訊,包含事件類型和原始文本
            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() : ""));
            }
        });
        synthesizer.getDuplexApi().close(1000, "bye");
        // 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
        System.out.println(
                "[Metric] requestId為:"
                        + synthesizer.getLastRequestId()
                        + ",首包延遲(毫秒)為:"
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        // 以下為新加坡地區的配置,調用時請將"{WorkspaceId}"替換為真實的業務空間ID,各地區的配置不同。
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

高並發調用

DashScope Java SDK中,採用了OkHttp3的串連池技術,以減少重複建立串連的開銷。詳情請參見高並發最佳實務