文档

快速开始

更新时间:

前提条件

Java示例代码

下载安装

从Maven服务器下载最新版本的SDK

<dependency>
    <groupId>com.alibaba.nls</groupId>
    <artifactId>nls-sdk-tts</artifactId>
    <version>2.2.14</version>
</dependency>

调用示例

以下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代码示例

下载安装

  1. 下载Python SDK。

Github获取Python SDK,或直接下载streamInputTts-github-python

  1. 安装SDK依赖。

进入SDK根目录使用如下命令安装SDK依赖:

python -m pip install -r requirements.txt
  1. 安装SDK。

依赖安装完成后使用如下命令安装SDK:

python -m pip install .
  1. 安装完成后通过以下代码导入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工程

下载安装

  1. 下载Android SDK 示例工程或者下载IOS SDK 示例工程

    重要

    下载后请在样例初始化代码中替换您的阿里云账号信息、Appkey和Token才可运行。

    类别

    Android 兼容范围

    iOS兼容范围

    系统

    支持Android 4.0 以上版本,API LEVEL 14

    最低支持iOS9。

    架构

    armeabi-v7a,arm64-v8a,x86,x86_64

    arm64,x86_64

  2. 解压ZIP包,添加代码库

    • Android平台请在app/libs目录下获取AAR格式的SDK包,将AAR包集成到您的工程项目中进行依赖。

    • iOS平台将zip包中的nuisdk.framework添加到您的工程中,并在工程Build Phases的Link Binary With Libraries中添加nuisdk.framework

  3. 打开工程文件运行DEMO工程

    • Android平台可使用Android Studio打开此工程查看参考代码实现。其中,流式文本语音合成示例代码为StreamInputTtsBasicActivity.java文件,替换AppkeyToken后可直接运行。

    • iOS平台可使用Xcode打开此工程,工程中提供了参考代码以及一些直接可使用的工具类,例如音频播放录制和文件操作,您可以直接复制源码到您的实际工程进行使用。流式文本语音合成示例代码在ViewController文件中。替换AppkeyToken后可直接运行。

调用示例

说明

更多关于移动端SDK使用详情可参考移动端SDK当中提供的Android与iOS官方工程DEMO示例

  1. 编写自己的回调函数

    @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];
        }
    }
  2. 进行鉴权、参数设置以及连网操作,并完成回调函数设置,开始进行流式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];

    参数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": "",  
    }
  3. 发送流式文本(可以多次发送),如果有响应,则执行回调函数

    String text = "你好";
    int ret = streamInput_tts_instance.sendStreamInputTts(text);
    ret = [_streamInputTtsSdk sendStreamInputTts:[oneLine UTF8String]];
  4. 停止发送文本,阻塞至所有音频返回,并结束本次流式语音合成

    int ret = streamInput_tts_instance.stopStreamInputTts();
    ret = [_streamInputTtsSdk stopStreamInputTts];