即時語音合成CosyVoice Java SDK
通過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 方法
|
方法 |
參數類型 |
必填 |
說明 |
|
|
String |
是 |
模型名稱。 |
|
|
String |
是 |
voice 語音合成所使用的音色。
|
|
|
enum |
否 |
音頻編碼格式及採樣率。 預設值:SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS。 SpeechSynthesisAudioFormat包路徑: |
|
|
int |
否 |
音量。 預設值:50。 取值範圍:[0, 100]。 |
|
|
float |
否 |
語速。 預設值:1.0。 取值範圍:[0.5, 2.0]。 |
|
|
float |
否 |
音調。 預設值:1.0。 取值範圍:[0.5, 2.0]。 |
|
|
boolean |
否 |
是否開啟字層級時間戳記。 預設值:false。 僅在流式輸出模式下可用。支援的音色範圍:cosyvoice-v3.5-plus、cosyvoice-v3.5-flash、cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色,以及CosyVoice音色列表中標記為支援的系統音色。其他模型的複刻音色不支援此功能。 |
|
|
int |
否 |
產生時使用的隨機數種子,使合成的效果產生變化。在模型版本、文本、音色及其他參數均相同的前提下,使用相同的seed可複現相同的合成結果。 預設值0。 取值範圍:[0, 65535]。 SDK版本低於2.21.7時, |
|
|
List<String> |
否 |
重要
指定語音合成的目標語言,提升合成效果。 當數字、縮寫、符號等朗讀方式或者小語種合成效果不符合預期時使用,例如:
取值範圍:
|
|
|
String |
否 |
設定指令,用於控制方言、情感或角色等合成效果。 使用說明請參見指令控制。 |
|
|
ParamHotFix |
否 |
文本熱修複配置,用於自訂指定詞語的發音或對待合成文本進行替換。 cosyvoice-v2不支援該功能。 參數介紹:
樣本:
|
|
|
String, Object |
否 |
設定擴充參數。 |
|
|
Map |
否 |
設定擴充參數。 |
擴充參數
通過 parameter() 或 parameters() 設定。
樣本:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("cosyvoice-v3-flash")
.voice("longanyang")
.parameter("enable_markdown_filter", true)
.build();
|
參數名 |
類型 |
必填 |
說明 |
|
|
integer |
否 |
音頻碼率(kbps)。音頻格式為opus時,支援通過 預設值:32。 取值範圍:[6, 510]。 |
|
|
boolean |
否 |
是否在產生的音頻中添加AIGC隱性標識。設定為true時,會將隱性標識嵌入到支援格式(wav/mp3/opus)的音頻中。 預設值:false。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
|
|
String |
否 |
設定AIGC隱性標識中的 預設值:阿里雲UID。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
|
|
String |
否 |
設定AIGC隱性標識中的 預設值:本次語音合成請求Request ID。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
|
|
boolean |
否 |
重要
僅cosyvoice-v3-flash複刻音色支援該功能。 是否啟用 Markdown 過濾。啟用該功能後,系統在合成語音前自動過濾輸入文本中的 Markdown 標記符號,避免將其朗讀為文字內容。 預設值:false。 取值範圍:
|
ResultCallback
包路徑:com.alibaba.dashscope.common.ResultCallback
onEvent() - 接收音頻資料
方法簽名:
public void onEvent(SpeechSynthesisResult result)
參數說明:
|
參數 |
類型 |
必填 |
說明 |
|
result |
是 |
接收到合成事件時觸發,包含音訊框架、時間戳記資訊和輸出資訊(事件類型、原始文本等)。 |
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()
傳回值: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流中擷取。包含以下欄位:
|
欄位 |
類型 |
說明 |
|
type |
String |
事件類型。取值: |
|
original_text |
String |
當前句子的原始常值內容。在 |
|
sentence |
JsonObject |
句子資訊,包含句子編號( |
範例程式碼
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的串連池技術,以減少重複建立串連的開銷。詳情請參見高並發最佳實務。