前提條件
在使用SDK之前,請先閱讀產品簡介。
鑒權密鑰獲?。韩@取Appkey,請參見管理項目一節;獲取Token,請參見通過SDK獲取Token。
Java示例代碼
下載安裝
從Maven服務器下載最新版本的SDK。
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-tts</artifactId>
<version>2.2.14</version>
</dependency>
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-common</artifactId>
<version>2.2.14</version>
</dependency>
Java SDK 從 2.1.7 版本開始(含2.1.7),語音合成SDK(包括實時長文本語音合成)SpeechSynthesizer的waitForComplete接口的超時時間單位從秒變更為毫秒。
調用示例
以下Java代碼示例模擬了流式文本輸入,請求語音合成,并使用揚聲器進行音頻播放的全過程。
代碼運行前需要替換your-appkey以及your-token。
package org.example;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.StreamInputTts;
import com.alibaba.nls.client.protocol.tts.StreamInputTtsListener;
import com.alibaba.nls.client.protocol.tts.StreamInputTtsResponse;
import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
class PlaybackRunnable implements Runnable {
// 設置音頻格式,請根據實際自身設備,合成音頻參數和平臺選擇配置
// 這里選擇24k、16bit、單通道,建議客戶根據選用的模型采樣率情況和自身設備兼容性選擇其他采樣率和格式
private AudioFormat af;
private DataLine.Info info;
private SourceDataLine targetSource;
private AtomicBoolean runFlag;
private ConcurrentLinkedQueue<ByteBuffer> queue;
public PlaybackRunnable(int sample_rate) {
af = new AudioFormat(sample_rate, 16, 1, true, false);
info = new DataLine.Info(SourceDataLine.class, af);
targetSource = null;
runFlag = new AtomicBoolean(true);
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();
}
}
public class StreamInputTtsPlayableDemo {
private static long startTime;
NlsClient client;
private String appKey;
public StreamInputTtsPlayableDemo(String appKey, String token, String url) {
this.appKey = appKey;
//創建NlsClient實例應用全局創建一個即可。生命周期可和整個應用保持一致,默認服務地址為阿里云線上服務地址。
if (url.isEmpty()) {
client = new NlsClient(token);
} else {
client = new NlsClient(url, token);
}
}
private static StreamInputTtsListener getSynthesizerListener(final PlaybackRunnable audioPlayer) {
StreamInputTtsListener listener = null;
try {
listener = new StreamInputTtsListener() {
private boolean firstRecvBinary = true;
//流式文本語音合成開始
@Override
public void onSynthesisStart(StreamInputTtsResponse response) {
System.out.println("name: " + response.getName() +
", status: " + response.getStatus());
}
//服務端檢測到了一句話的開始
@Override
public void onSentenceBegin(StreamInputTtsResponse response) {
System.out.println("name: " + response.getName() +
", status: " + response.getStatus());
System.out.println("Sentence Begin");
}
//服務端檢測到了一句話的結束,獲得這句話的起止位置和所有時間戳
@Override
public void onSentenceEnd(StreamInputTtsResponse response) {
System.out.println("name: " + response.getName() +
", status: " + response.getStatus() + ", subtitles: " + response.getObject("subtitles"));
}
//流式文本語音合成結束
@Override
public void onSynthesisComplete(StreamInputTtsResponse response) {
// 調用onSynthesisComplete時,表示所有TTS數據已經接收完成,所有文本都已經合成音頻并返回。
System.out.println("name: " + response.getName() + ", status: " + response.getStatus());
audioPlayer.stop();
}
//收到語音合成的語音二進制數據
@Override
public void onAudioData(ByteBuffer message) {
if (firstRecvBinary) {
// 此處計算首包語音流的延遲,收到第一包語音流時,即可以進行語音播放,以提升響應速度(特別是實時交互場景下)。
firstRecvBinary = false;
long now = System.currentTimeMillis();
System.out.println("tts first latency : " + (now - StreamInputTtsPlayableDemo.startTime) + " ms");
}
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
System.out.println("recv audio bytes:" + bytesArray.length);
audioPlayer.put(ByteBuffer.wrap(bytesArray));
}
//收到語音合成的增量音頻時間戳
@Override
public void onSentenceSynthesis(StreamInputTtsResponse response) {
System.out.println("name: " + response.getName() +
", status: " + response.getStatus() + ", subtitles: " + response.getObject("subtitles"));
}
@Override
public void onFail(StreamInputTtsResponse response) {
// task_id是調用方和服務端通信的唯一標識,當遇到問題時,需要提供此task_id以便排查。
System.out.println(
"session_id: " + getStreamInputTts().getCurrentSessionId() +
", task_id: " + response.getTaskId() +
//狀態碼
", status: " + response.getStatus() +
//錯誤信息
", status_text: " + response.getStatusText());
audioPlayer.stop();
}
};
} catch (Exception e) {
e.printStackTrace();
}
return listener;
}
public static void main(String[] args) throws Exception {
String appKey = "your-appkey";
String token = "your-token";
// url取默認值
String url = "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1";
String[] textArray = {"流式文本語音合成SDK,", "可以將輸入的文本", "合成為語音二進制數據,",
"相比于非流式語音合成,", "流式合成的優勢在于實時性", "更強。用戶在輸入文本的同時",
"可以聽到接近同步的語音輸出,", "極大地提升了交互體驗,", "減少了用戶等待時間。",
"適用于調用大規模", "語言模型(LLM),以", "流式輸入文本的方式", "進行語音合成的場景。"};
StreamInputTtsPlayableDemo demo = new StreamInputTtsPlayableDemo(appKey, token, url);
demo.process(textArray);
demo.shutdown();
}
public void process(String[] textArray) throws InterruptedException {
StreamInputTts synthesizer = null;
PlaybackRunnable playbackRunnable = new PlaybackRunnable(24000);
try {
playbackRunnable.prepare();
} catch (LineUnavailableException e) {
throw new RuntimeException(e);
}
Thread playbackThread = new Thread(playbackRunnable);
// 啟動播放線程
playbackThread.start();
try {
//創建實例,建立連接。
synthesizer = new StreamInputTts(client, getSynthesizerListener(playbackRunnable));
synthesizer.setAppKey(appKey);
//設置返回音頻的編碼格式。
synthesizer.setFormat(OutputFormatEnum.WAV);
//設置返回音頻的采樣率。
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_24K);
synthesizer.setVoice("longxiaochun");
//音量,范圍是0~100,可選,默認50。
synthesizer.setVolume(50);
//語調,范圍是-500~500,可選,默認是0。
synthesizer.setPitchRate(0);
//語速,范圍是-500~500,默認是0。
synthesizer.setSpeechRate(0);
//此方法將以上參數設置序列化為JSON發送給服務端,并等待服務端確認。
long start = System.currentTimeMillis();
synthesizer.startStreamInputTts();
System.out.println("tts start latency " + (System.currentTimeMillis() - start) + " ms");
StreamInputTtsPlayableDemo.startTime = System.currentTimeMillis();
//設置連續兩次發送文本的最小時間間隔(毫秒),如果當前調用send時距離上次調用時間小于此值,則會阻塞并等待直到滿足條件再發送文本
synthesizer.setMinSendIntervalMS(100);
for (String text : textArray) {
//發送流式文本數據。
synthesizer.sendStreamInputTts(text);
}
//通知服務端流式文本數據發送完畢,阻塞等待服務端處理完成。
synthesizer.stopStreamInputTts();
} catch (Exception e) {
e.printStackTrace();
} finally {
//關閉連接
if (null != synthesizer) {
synthesizer.close();
playbackThread.join();
}
}
}
public void shutdown() {
client.shutdown();
}
}
Python代碼示例
下載安裝
下載Python SDK。
從Github獲取Python SDK,或直接下載streamInputTts-github-python。
安裝SDK依賴。
進入SDK根目錄使用如下命令安裝SDK依賴:
python -m pip install -r requirements.txt
安裝SDK。
依賴安裝完成后使用如下命令安裝SDK:
python -m pip install .
安裝完成后通過以下代碼導入SDK。
# -*- coding: utf-8 -*-
import nls
上述命令均需要在SDK根目錄中執行。
調用示例
代碼運行前需要替換 your-appkey 以及 your-token。
# 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 nls
import time
# 設置打開日志輸出
nls.enableTrace(False)
# 將音頻保存進文件
SAVE_TO_FILE = True
# 將音頻通過播放器實時播放,需要具有聲卡。在服務器上運行請將此開關關閉
PLAY_REALTIME_RESULT = True
if PLAY_REALTIME_RESULT:
import pyaudio
test_text = [
"流式文本語音合成SDK,",
"可以將輸入的文本",
"合成為語音二進制數據,",
"相比于非流式語音合成,",
"流式合成的優勢在于實時性",
"更強。用戶在輸入文本的同時",
"可以聽到接近同步的語音輸出,",
"極大地提升了交互體驗,",
"減少了用戶等待時間。",
"適用于調用大規模",
"語言模型(LLM),以",
"流式輸入文本的方式",
"進行語音合成的場景。",
]
if __name__ == "__main__":
if SAVE_TO_FILE:
file = open("output.wav", "wb")
if PLAY_REALTIME_RESULT:
player = pyaudio.PyAudio()
stream = player.open(
format=pyaudio.paInt16, channels=1, rate=24000, output=True
)
# 創建SDK實例
# 配置回調函數
def test_on_data(data, *args):
if SAVE_TO_FILE:
file.write(data)
if PLAY_REALTIME_RESULT:
stream.write(data)
def test_on_message(message, *args):
print("on message=>{}".format(message))
def test_on_close(*args):
print("on_close: args=>{}".format(args))
def test_on_error(message, *args):
print("on_error message=>{} args=>{}".format(message, args))
sdk = nls.NlsStreamInputTtsSynthesizer(
# 由于目前階段大模型音色只在北京地區服務可用,因此需要調整url到北京
url="wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1",
token="your-token",
appkey="your-appkey",
on_data=test_on_data,
on_sentence_begin=test_on_message,
on_sentence_synthesis=test_on_message,
on_sentence_end=test_on_message,
on_completed=test_on_message,
on_error=test_on_error,
on_close=test_on_close,
callback_args=[],
)
# 發送文本消息
sdk.startStreamInputTts(
voice="longxiaochun", # 語音合成說話人
aformat="wav", # 合成音頻格式
sample_rate=24000, # 合成音頻采樣率
volume=50, # 合成音頻的音量
speech_rate=0, # 合成音頻語速
pitch_rate=0, # 合成音頻的音調
)
for text in test_text:
sdk.sendStreamInputTts(text)
time.sleep(0.05)
sdk.stopStreamInputTts()
if SAVE_TO_FILE:
file.close()
if PLAY_REALTIME_RESULT:
stream.stop_stream()
stream.close()
player.terminate()
移動端示例代碼
代碼調用示例請詳見Demo工程。
下載安裝
下載Android SDK示例工程、iOS SDK示例工程或HarmonyNext SDK 示例工程。
重要下載后請在樣例初始化代碼中替換您的阿里云賬號信息、Appkey和Token才可運行。
類別
Android 兼容范圍
iOS兼容范圍
HarmonyNext兼容范圍
系統
支持Android 4.0 以上版本,API LEVEL 14
最低支持iOS9。
支持IDE 5.0.3.600
架構
armeabi-v7a,arm64-v8a,x86,x86_64
arm64,x86_64
arm64-v8a
解壓ZIP包,添加代碼庫。
Android平臺請在
app/libs
目錄下獲取AAR格式的SDK包,將AAR包集成到您的工程項目中進行依賴。iOS平臺將zip包中的nuisdk.framework添加到您的工程中,并在工程Build Phases的Link Binary With Libraries中添加nuisdk.framework
HarmonyNext平臺,壓縮包中的entry/libs/neonui.har 是SDK生成的HAR包文件,在用戶工程項目中導入調用即可。
打開工程文件運行DEMO工程
Android平臺可使用Android Studio打開此工程查看參考代碼實現。其中,流式文本語音合成示例代碼為StreamInputTtsBasicActivity.java文件,替換Appkey和Token后可直接運行。
iOS平臺可使用Xcode打開此工程,工程中提供了參考代碼以及一些直接可使用的工具類,例如音頻播放錄制和文件操作,您可以直接復制源碼到您的實際工程進行使用。流式文本語音合成示例代碼在ViewController文件中。替換Appkey和Token后可直接運行。
HarmonyNext平臺,使用DevEco Studio打開工程,其中cosyvoice示例代碼為StreamTTSPage.ets文件,替換UserKey.ets中 UserKeyStreamTTS類的Appkey和Token后,即可直接運行。
調用示例
更多關于移動端SDK使用詳情可參考移動端SDK當中提供的Android與iOS官方工程DEMO示例。
其中JavaScript頁為鴻蒙Next系統示例。
編寫自己的回調函數
@Override public void onStreamInputTtsEventCallback( INativeStreamInputTtsCallback.StreamInputTtsEvent event, String task_id, String session_id, int ret_code, String error_msg, String timestamp, String all_response) { Log.i(TAG, "stream input tts event:" + event + " session id " + session_id + " session id " + task_id + " ret " + ret_code); switch (event) { case STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED: // TODO: 處理SynthesisStarted指令 break; case STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN: // TODO: 處理SentenceBegin指令 break; case STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS: // TODO: 處理SentenceSynthesis指令 break; case STREAM_INPUT_TTS_EVENT_SENTENCE_END: // TODO: 處理SynthesisEnd指令 break; case STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE: // TODO: 處理SynthesisComplete指令 break; case STREAM_INPUT_TTS_EVENT_TASK_FAILED: // TODO: 處理TaskFailed指令 break; default: break; } } @Override public void onStreamInputTtsDataCallback(byte[] data) { if (data.length > 0) { if (mEncodeType.equals("pcm")) { mAudioTrack.setAudioData(data); } } }
/** * 事件回調,打印出服務返回的完整信息,可以根據需要處理對應的事件類型 */ - (void)onStreamInputTtsEventCallback:(StreamInputTtsCallbackEvent)event taskId:(char *)taskid sessionId:(char *)sessionId ret_code:(int)ret_code error_msg:(char *)error_msg timestamp:(char *)timestamp all_response:(char *)all_response { // [self logFormattedString:@"\n[事件回調] (event_code : %d), %s", event, // all_response]; NSLog(@"\n[事件回調] (event_code : %d), %s", event, all_response); // 可以處理各類型的事件 switch (event) { case TTS_EVENT_SYNTHESIS_STARTED: // TODO: 處理SynthesisStarted指令 break; case TTS_EVENT_SENTENCE_BEGIN: // TODO: 處理SentenceBegin指令 break; case TTS_EVENT_SENTENCE_SYNTHESIS: // TODO: 處理SentenceSynthesis指令 break; case TTS_EVENT_SENTENCE_END: // TODO: 處理SynthesisEnd指令 break; case TTS_EVENT_SYNTHESIS_COMPLETE: // TODO: 處理SynthesisComplete指令 break; case TTS_EVENT_TASK_FAILED: // TODO: 處理TaskFailed指令 break; default: break; } } /** * 音頻回調函數,將收到的音頻幀寫入到音頻播放器中 */ - (void)onStreamInputTtsDataCallback:(char *)buffer len:(int)len { NSLog(@"\n[收到音頻] %d 字節", len); if (len > 0) { [_voicePlayer write:(char *)buffer Length:(unsigned int)len]; } }
function cb_tts_event_callback(event:StreamInputTtsEvent, task_id:string, session_id:string, ret_code:number, error_msg:string, timestamp:string, all_response:string):void{ console.info( "stream input tts event:" + event + " session id " + session_id + " session id " + task_id + " ret " + ret_code); if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED) { console.info("STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED"); console.info("start play"); } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS) { console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS:" + timestamp); } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE || event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) { /* * 提示: 表示TTS已經合成完并通過回調傳回了所有音頻數據, 而不是表示播放器已經播放完了所有音頻數據。 */ console.info("play end"); //合成結束或者出錯,如果保存了語音數據到本地文件,則可以關閉文件了。 // if (filesave){ // fs.closeSync(filesave) // filesave=undefined // } // 通知播放器,數據合成已經結束,等待播放器播放完所有已緩存的數據即可。 // playerVoiceEnd() if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) { console.info("STREAM_INPUT_TTS_EVENT_TASK_FAILED error_code:" + ret_code + " errmsg:" + error_msg); //生成報錯,直接停止播放器的播放 //playerVoiceStop(true) } else { console.info("STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE" ); } } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN) { console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN" ); } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_END) { console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_END"); } } function cb_tts_user_data_callback(buffer:ArrayBuffer|null):void{ if (buffer){ //保存生成的語音數據到本地語音文件中 // if (filesave){ // fs.writeSync(filesave.fd, buffer) // } //把生成的語音數據送入播放器模塊進行實時播放 if (buffer.byteLength > 0) { playerSetVoiceArrayBuffer(buffer as ArrayBuffer) } } else { console.info("womx cb_tts_user_data_callback undefined"); } } const g_ttscallback_instance:INativeStreamInputTtsCallback = { onStreamInputTtsEventCallback: cb_tts_event_callback, onStreamInputTtsDataCallback: cb_tts_user_data_callback };
進行鑒權、參數設置以及連網操作,并完成回調函數設置,開始進行流式TTS語音合成
String ticket = genTicket(); String parameters = genParameters(); NativeNui stream_input_tts_instance = new NativeNui(Constants.ModeType.MODE_STREAM_INPUT_TTS); //其中callback為包含配置好回調的INativeTtsCallback對象 int ret = streamInput_tts_instance.startStreamInputTts( callback, ticket, parameters, "", 1, false);
// 獲得鑒權信息和語音合成參數 NSString *ticket = [self genTicket]; NSString *parameters = [self genParameters]; // 獲得流式TTS實例,并配置delegate _streamInputTtsSdk = [StreamInputTts get_instance]; _streamInputTtsSdk.delegate = self; // 建立連接并開始語音合成任務 int ret = [_streamInputTtsSdk startStreamInputTts:[ticket UTF8String] parameters:[parameters UTF8String] sessionId:nil logLevel:0 saveLog:NO];
stream_input_tts_instance:NativeNui = new NativeNui(Constants.ModeType.MODE_STREAM_INPUT_TTS, "streamtts") ticket:string = genTicketTTS(); parameters:string = genParameters(this.fontname); startTTS():number{ let ret:number = this.stream_input_tts_instance.startStreamInputTts( g_ttscallback_instance, this.ticket, this.parameters, "", Constants.LogLevel.toInt(Constants.LogLevel.LOG_LEVEL_VERBOSE), false ) if (Constants.NuiResultCode.SUCCESS != ret) { //start報錯。 console.log("start tts failed"); // showToast("start tts failed") } else { //成功 } return ret }
參數ticket為String JSON字符串,主要用于生成鑒權相關信息,包含如下字段用戶信息。
{ "appkey": "your-app-key", //必須參數,AppKey,獲取方法可參考相關文檔 "token": "yout-token", //必須參數,Token,獲取方法可參考相關文檔 "url": "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1", "complete_waiting_ms": "10000", }
參數parameters為String JSON字符串,用于設置語音合成相關的參數,包含如下字段配置。
{ "voice": "longxiaochun", "format": "wav", "sample_rate": "24000", "volume": "50", "speech_rate": "0", "pitch_rate": "0", "enable_subtitle": "0", "session_id": "", }
發送流式文本(可以多次發送),如果有響應,則執行回調函數
String text = "你好"; int ret = streamInput_tts_instance.sendStreamInputTts(text);
ret = [_streamInputTtsSdk sendStreamInputTts:[oneLine UTF8String]];
let ttstext:string = "你好" this.stream_input_tts_instance.sendStreamInputTts(ttstext)
停止發送文本,阻塞至所有音頻返回,并結束本次流式語音合成
int ret = streamInput_tts_instance.stopStreamInputTts();
ret = [_streamInputTtsSdk stopStreamInputTts];
//注意,對于HarmonyNext平臺,stop接口中參數可以控制是阻塞stop還是異步stop,推薦使用異步stop。 let retcode: number = this.stream_input_tts_instance.stopStreamInputTts(true) if (retcode == 0) { console.info('womx stopStreamInputTts() and return success') } else { //出錯 console.info('womx stopStreamInputTts() but return error:%d', retcode) showToast(this.errorinfo) }