提交 166daac1 编写于 作者: 凌洛云's avatar 凌洛云

update

上级 909c08ed
此差异已折叠。
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Properties;
public class MavenWrapperDownloader {
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL =
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: : " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
language: java
sudo: false # faster builds
script:"mvn test"
after_success:
- bash <(curl -s https://codecov.io/bash)
......@@ -4,6 +4,32 @@
阿里云智能语音交互DEMO
demo 解压后,在pom 目录运行mvn package ,会在target目录生成可执行jar nls-example-transcriber-2.0.0-jar-with-dependencies.jar 将此jar拷贝到目标服务器,可用于快速验证及压测服务。
## 介绍
本示例代码是阿里云智能语音交互服务相关的java 语言示例。 包括了一句话识别、实时识别、录音文件识别、语音合成等多个功能的演示。
**需要说明的是:以下代码均为demo示例,当需要集成到自己的系统中时,注意根据实际情况进行相应修改,比如逻辑调整、参数设置、异常处理等等。**
### 一句话识别(nls-example-recognizer)
- SpeechRecognizerDemo :单线程调用演示一句话识别接口
- SpeechRecognizerMultiThreadDemo :多线程调用演示一句话识别接口
### 实时识别(nls-example-transcriber)
- SpeechTranscriberDemo :单线程调用演示实时语音识别接口
- SpeechTranscriberMultiThreadDemo :多线程调用演示实时语音识别接口
- SpeechTranscriberWithMicrophoneDemo :演示了从麦克风采集语音并实时识别的过程
### 语音合成(nls-example-tts)
- SpeechSynthesizerDemo :单线程调用演示语音合成接口
- SpeechSynthesizerMultiThreadDemo :多线程调用演示语音合成接口
- SpeechSynthesizerLongTextDemo :演示长文本语音合成调用时,如何拆分文本的功能
### token获取(nls-example-token)
- TokenDemo : 演示token的获取方式
- SpeechTokenGeneratorDemo : 演示token定时获取的方式
2019年07月19日
## 服务验证
```java -cp nls-example-transcriber-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechTranscriberDemo```
......
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-recognizer:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-common:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.41" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
</component>
</module>
\ No newline at end of file
package com.alibaba.nls.client;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
......@@ -9,93 +11,133 @@ import com.alibaba.nls.client.protocol.asr.SpeechRecognizer;
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerListener;
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author zhishen.ml
* @date 2018-06-12
* 此示例演示了
* ASR一句话识别API调用
* 动态获取token
* 通过本地文件模拟实时流发送
* 识别耗时计算
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechRecognizerDemo {
private static final Logger logger = LoggerFactory.getLogger(SpeechRecognizerDemo.class);
private String appKey;
private String accessToken;
NlsClient client;
public SpeechRecognizerDemo(String appKey, String token) {
public SpeechRecognizerDemo(String appKey, String id, String secret, String url) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
client = new NlsClient(accessToken);
}
public SpeechRecognizerDemo(String appKey, String token, String url) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
client = new NlsClient(url, accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(id, secret);
try {
accessToken.apply();
System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
// TODO 创建NlsClient实例,应用全局创建一个即可
if(url.isEmpty()) {
client = new NlsClient(accessToken.getToken());
}else {
client = new NlsClient(url, accessToken.getToken());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static SpeechRecognizerListener getRecognizerListener() {
// 传入自定义参数
private static SpeechRecognizerListener getRecognizerListener(int myOrder, String userParam) {
SpeechRecognizerListener listener = new SpeechRecognizerListener() {
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onRecognitionResultChanged(SpeechRecognizerResponse response) {
//事件名称 RecognitionResultChanged
System.out.println("name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
//事件名称 RecognitionResultChanged、 状态码(20000000 表示识别成功)、语音识别文本
System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());
}
//识别完毕
@Override
public void onRecognitionCompleted(SpeechRecognizerResponse response) {
//事件名称 RecognitionCompleted
System.out.println("name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
//事件名称 RecognitionCompleted, 状态码 20000000 表示识别成功, getRecognizedText是识别结果文本
System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());
}
@Override
public void onStarted(SpeechRecognizerResponse response) {
System.out.println(
"task_id: " + response.getTaskId());
System.out.println("myOrder: " + myOrder + "; myParam: " + userParam + "; task_id: " + response.getTaskId());
}
@Override
public void onFail(SpeechRecognizerResponse response) {
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//错误信息
", status_text: " + response.getStatusText());
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("task_id: " + response.getTaskId() + ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
}
};
return listener;
}
public void process(InputStream ins) {
/// 根据二进制数据大小计算对应的同等语音长度
/// sampleRate 仅支持8000或16000
public static int getSleepDelta(int dataSize, int sampleRate) {
// 仅支持16位采样
int sampleBytes = 16;
// 仅支持单通道
int soundChannel = 1;
return (dataSize * 10 * 8000) / (160 * sampleRate);
}
public void process(String filepath, int sampleRate) {
SpeechRecognizer recognizer = null;
try {
//创建实例,建立连接
recognizer = new SpeechRecognizer(client, getRecognizerListener());
// 传递用户自定义参数
String myParam = "user-param";
int myOrder = 1234;
SpeechRecognizerListener listener = getRecognizerListener(myOrder, myParam);
recognizer = new SpeechRecognizer(client, listener);
recognizer.setAppKey(appKey);
//设置音频编码格式
//设置音频编码格式 TODO 如果是opus文件,请设置为 InputFormatEnum.OPUS
recognizer.setFormat(InputFormatEnum.PCM);
//设置音频采样率
recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
if(sampleRate == 16000) {
recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
} else if(sampleRate == 8000) {
recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_8K);
}
//设置是否返回中间识别结果
recognizer.setEnableIntermediateResult(true);
//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
long now = System.currentTimeMillis();
recognizer.start();
//语音数据来自声音文件用此方法,控制发送速率;若语音来自实时录音,不需控制发送速率直接调用 recognizer.sent(ins)即可
recognizer.send(ins, 3200, 100);
logger.info("ASR start latency : " + (System.currentTimeMillis() - now) + " ms");
File file = new File(filepath);
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[3200];
int len;
while ((len = fis.read(b)) > 0) {
logger.info("send data pack length: " + len);
recognizer.send(b);
// TODO 重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep
// TODO 如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000
// 8000采样率情况下,3200byte字节建议 sleep 200ms,16000采样率情况下,3200byte字节建议 sleep 100ms
int deltaSleep = getSleepDelta(len, sampleRate);
Thread.sleep(deltaSleep);
}
//通知服务端语音数据发送完毕,等待服务端处理完成
now = System.currentTimeMillis();
// TODO 计算实际延迟: stop返回之后一般即是识别结果返回时间
logger.info("ASR wait for complete");
recognizer.stop();
logger.info("ASR stop latency : " + (System.currentTimeMillis() - now) + " ms");
fis.close();
} catch (Exception e) {
System.err.println(e.getMessage());
} finally {
......@@ -111,32 +153,29 @@ public class SpeechRecognizerDemo {
}
public static void main(String[] args) throws Exception {
String appKey = null;
String token = null;
String url = null;
SpeechRecognizerDemo demo = null;
if (args.length == 2) {
appKey = args[0];
token = args[1];
//default url is wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
demo = new SpeechRecognizerDemo(appKey, token);
} else if (args.length == 3) {
appKey = args[0];
token = args[1];
url = args[2];
demo = new SpeechRecognizerDemo(appKey, token, url);
String appKey = null; // "填写你的appkey";
String id = null; // "填写你在阿里云网站上的AccessKeyId";
String secret = null; // "填写你在阿里云网站上的AccessKeySecret";
String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
if (args.length == 3) {
appKey = args[0];
id = args[1];
secret = args[2];
} else if (args.length == 4) {
appKey = args[0];
id = args[1];
secret = args[2];
url = args[3];
} else {
System.err.println("SpeechRecognizerDemo need params(url is optional): " +
"<app-key> <token> [<url>]");
System.exit(-1);
}
InputStream ins = SpeechRecognizerDemo.class.getResourceAsStream("/nls-sample-16k.wav");
if (null == ins) {
System.err.println("open the audio file failed!");
System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");
System.exit(-1);
}
demo.process(ins);
SpeechRecognizerDemo demo = new SpeechRecognizerDemo(appKey, id, secret, url);
// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端
demo.process("./nls-sample-16k.wav", 16000);
//demo.process("./nls-sample.opus", 16000);
demo.shutdown();
}
}
......@@ -2,6 +2,7 @@ package com.alibaba.nls.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
......@@ -14,86 +15,110 @@ import com.alibaba.nls.client.protocol.asr.SpeechRecognizerResponse;
import com.alibaba.nls.client.util.OpuCodec;
import com.alibaba.nls.client.util.OpuCodec.EncodeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by siwei on 2018/06/20
* 此示例演示了
* ASR一句话识别API调用
* 多线程并发调用
* 用户自定义参数设置
* 动态获取token
* 通过本地模拟实时流发送
* 识别耗时计算
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechRecognizerMultiThreadDemo {
private static SampleRateEnum sampleRateEnum=SampleRateEnum.SAMPLE_RATE_16K;
final static OpuCodec codec = new OpuCodec();
private static SpeechRecognizerListener getRecognizerListener(final String inputName) {
SpeechRecognizerListener listener = new SpeechRecognizerListener() {
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onRecognitionResultChanged(SpeechRecognizerResponse response) {
System.out.println("task_id: " + response.getTaskId() +
//事件名称 RecognitionResultChanged
", input stream: " + inputName +
", name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
}
class MyRecognizerListener extends SpeechRecognizerListener {
// TODO 用户自定义参数
private int myIndex;
private String filePath;
//识别完毕
@Override
public void onRecognitionCompleted(SpeechRecognizerResponse response) {
System.out.println("task_id: " + response.getTaskId() +
//事件名称 RecognitionCompleted
", input stream: " + inputName +
", name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
}
MyRecognizerListener(int index, String filePath) {
this.myIndex = index;
this.filePath = filePath;
}
@Override
public void onStarted(SpeechRecognizerResponse response) {
System.out.println(
"task_id: " + response.getTaskId()+
", name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus());
}
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onRecognitionResultChanged(SpeechRecognizerResponse response) {
System.out.println("task_id: " + response.getTaskId() +
//事件名称 RecognitionResultChanged
", name: " + response.getName() +
", index: " + myIndex +
", filePath: " + filePath +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
}
@Override
public void onFail(SpeechRecognizerResponse response) {
System.out.println("input stream: " + inputName +
", task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", status_text: " + response.getStatusText());
@Override
public void onRecognitionCompleted(SpeechRecognizerResponse response) {
System.out.println("task_id: " + response.getTaskId() +
//事件名称 RecognitionCompleted
", name: " + response.getName() +
", index: " + myIndex +
", filePath: " + filePath +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", result: " + response.getRecognizedText());
}
}
};
return listener;
@Override
public void onStarted(SpeechRecognizerResponse response) {
System.out.println(
"task_id: " + response.getTaskId()+
", name: " + response.getName() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus());
}
@Override
public void onFail(SpeechRecognizerResponse response) {
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("onFail : " +
" index: " + myIndex +
", filePath: " + filePath +
", task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//语音识别文本
", status_text: " + response.getStatusText());
}
}
public class SpeechRecognizerMultiThreadDemo {
private static final Logger logger = LoggerFactory.getLogger(SpeechRecognizerMultiThreadDemo.class);
private static SampleRateEnum sampleRateEnum=SampleRateEnum.SAMPLE_RATE_16K;
final static OpuCodec codec = new OpuCodec();
static class Task implements Runnable {
private String appKey;
private NlsClient client;
private CountDownLatch latch;
private int index;
private String audioFile;
private boolean isOpuEncode;
private boolean isOpuCompress;
public Task(String appKey, NlsClient client, CountDownLatch latch, String audioFile,boolean isOpuEncode) {
public Task(String appKey, NlsClient client, CountDownLatch latch, int index, String audioFile, boolean isOpuCompress) {
this.appKey = appKey;
this.client = client;
this.latch = latch;
this.index = index;
this.audioFile = audioFile;
this.isOpuEncode=isOpuEncode;
this.isOpuCompress=isOpuCompress;
}
@Override
public void run() {
try {
// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端
File file = new File(audioFile);
String audioFileName = file.getName();
MyRecognizerListener listener = new MyRecognizerListener(index, audioFile);
//创建实例,建立连接
final SpeechRecognizer recognizer = new SpeechRecognizer(client, getRecognizerListener(audioFileName));
final SpeechRecognizer recognizer = new SpeechRecognizer(client, listener);
recognizer.setAppKey(appKey);
//设置音频编码格式
recognizer.setFormat(InputFormatEnum.PCM);
......@@ -102,27 +127,43 @@ public class SpeechRecognizerMultiThreadDemo {
//设置是否返回中间识别结果
recognizer.setEnableIntermediateResult(true);
InputStream ins = new FileInputStream(file);
if(isOpuEncode){
if(isOpuCompress){
// TODO 如果启用Opu压缩,注意以下设置
recognizer.setFormat(InputFormatEnum.OPU);
recognizer.start();
codec.encode(16000, ins, new EncodeListener() {
@Override
public void onEncodedData(byte[] data) {
System.out.println("send audio " + data.length);
recognizer.send(data);
try {
Thread.sleep(20);
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}else{
}else {
//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
recognizer.start();
//语音数据来自声音文件用此方法,控制发送速率;若语音来自实时录音,不需控制发送速率直接调用 recognizer.sent(ins)即可
recognizer.send(ins, 3200, 100);
// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[3200];
int len;
while ((len = fis.read(b)) > 0) {
logger.info("send data pack length: " + len);
recognizer.send(b);
// TODO 重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep
// TODO 如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000
// 8000采样率情况下,3200byte字节建议 sleep 200ms,16000采样率情况下,3200byte字节建议 sleep 100ms
int deltaSleep = getSleepDelta(len, sampleRateEnum.value);
Thread.sleep(deltaSleep);
}
}
//通知服务端语音数据发送完毕,等待服务端处理完成
recognizer.stop();
recognizer.close();
......@@ -132,33 +173,55 @@ public class SpeechRecognizerMultiThreadDemo {
latch.countDown();
}
}
/// 根据二进制数据大小计算对应的同等语音长度
/// sampleRate 仅支持8000或16000
public static int getSleepDelta(int dataSize, int sampleRate) {
// 仅支持16位采样
int sampleBytes = 16;
// 仅支持单通道
int soundChannel = 1;
return (dataSize * 10 * 8000) / (160 * sampleRate);
}
}
public static void main(String[] args) throws Exception{
if (args.length < 5) {
System.err.println("SpeechRecognizerMultiThreadDemo need params: " +
"<app-key> <token> <url> <audio-file> <thread-num>");
if (args.length < 4) {
System.err.println("SpeechRecognizerMultiThreadDemo need params: <app-key> <AccessKeyId> <AccessKeySecret> <thread-num>");
System.exit(-1);
}
String appKey = args[0];
String accessKeyId = args[1];
String accessKeySecret = args[2];
int threadNum = Integer.parseInt(args[3]);
String audioFile = "nls-sample-16k.wav";
// 设置音频采样率
sampleRateEnum = SampleRateEnum.SAMPLE_RATE_16K;
String appKey = args[0];
String token = args[1];
String url = args[2];
String audioFile = args[3];
int threadNum = Integer.parseInt(args[4]);
// 是否启用opu压缩(一定程度减少带宽, 非必须)
boolean isOpuCompress = false;
String isOpuEnable=System.getProperty("isOpuEnable","false");
String sampleRate=System.getProperty("sampleRate","16000");
if("8000".equals(sampleRate)){
sampleRateEnum=SampleRateEnum.SAMPLE_RATE_8K;
NlsClient client = null;
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
try {
accessToken.apply();
System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
// TODO 创建NlsClient实例,应用全局创建一个即可
client = new NlsClient(accessToken.getToken());
} catch (IOException e) {
e.printStackTrace();
}
final NlsClient client = new NlsClient(url, token);
CountDownLatch latch = new CountDownLatch(threadNum);
try {
for (int i = 0; i < threadNum; i++) {
Task task = new Task(appKey, client, latch, audioFile,Boolean.parseBoolean(isOpuEnable));
Task task = new Task(appKey, client, latch, i, audioFile, isOpuCompress);
new Thread(task).start();
}
latch.await();
......
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-common:2.1.0" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.49" level="project" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-java-examples</artifactId>
<version>2.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-example-token</artifactId>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-common</artifactId>
<version>${sdk.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.alibaba.nls.token.TokenDemo</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.alibaba.nls.token;
import com.alibaba.nls.client.AccessToken;
public class SpeechTokenGeneratorDemo {
private String accessKeyId = null;
private String accessKeySecret = null;
private String token;
public SpeechTokenGeneratorDemo(String id, String secret) {
setAccessKeyParam(id, secret);
}
public void setAccessKeyParam(String id, String secret) {
this.accessKeyId = id;
this.accessKeySecret = secret;
}
public void start() {
Thread thread = new Thread(() -> {
while(true){
try {
if(accessKeyId == null || accessKeyId.isEmpty() || accessKeySecret == null || accessKeySecret.isEmpty()) {
throw new Exception("accessKeyId or accessKeySecret is invalid!");
}
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
accessToken.apply();
System.out.println("Created token: " + accessToken.getToken() +
// 有效时间,单位为秒
", expire time(s): " + accessToken.getExpireTime());
if(!accessToken.getToken().isEmpty()) {
long expireTime = accessToken.getExpireTime();
long now = System.currentTimeMillis() / 1000;
long expirePeriodSecond = expireTime - now;
if (expirePeriodSecond > 3600 * 2) {
/// 比如可以比server端建议的时间提前2个小时
expirePeriodSecond = expirePeriodSecond - 3600 * 2;
}
Thread.sleep(expirePeriodSecond * 1000);
//Thread.sleep(10 * 1000);
} else {
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
public static void main(String[] args) throws InterruptedException {
String akId = ""; //"输入你的阿里云AccessKey ID";
String akSecret = ""; // "输入你的阿里云AccessKey Secret";
if(akId.isEmpty() || akSecret.isEmpty()){
System.err.println("akId or akSecret must not be empty!");
System.exit(-1);
}
SpeechTokenGeneratorDemo demo = new SpeechTokenGeneratorDemo(akId, akSecret);
demo.start();
Thread.sleep(50000 * 1000);
}
}
package com.alibaba.nls.token;
import com.alibaba.nls.client.AccessToken;
public class TokenDemo {
public static void requestTokenTest(String akId, String akSecret) {
try {
System.out.println("akid = " + akId + ", secret = " + akSecret);
AccessToken accessToken = new AccessToken(akId, akSecret);
// 请求token
accessToken.apply();
System.out.println("Created token: " + accessToken.getToken() +
// 有效时间,单位为秒
", expire time(s): " + accessToken.getExpireTime());
if(!accessToken.getToken().isEmpty()) {
long expireTime = accessToken.getExpireTime();
System.err.println("token 过期时间点 : " + expireTime);
long now = System.currentTimeMillis() / 1000;
long expirePeriodSecond = expireTime - now;
System.err.println("token 剩余有效时间: " + expirePeriodSecond);
// TODO 重要提示: 请务必在有效时间(expirePeriodSecond) 过期之前重新获取token,否则token失效,进而无法调用各类语音服务接口
// 比如:
//if(expirePeriodSecond <= 0) {
// /// 重新获取
//}
} else {
}
Thread.sleep(3000);
// 第二次请求token
accessToken.apply();
System.out.println("Created token: " + accessToken.getToken() +
// 有效时间,单位为秒
", expire time(s): " + accessToken.getExpireTime());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String akId = ""; //"输入你的阿里云AccessKey ID";
String akSecret = ""; // "输入你的阿里云AccessKey Secret";
if(akId.isEmpty() || akSecret.isEmpty()){
System.err.println("akId or akSecret must not be empty!");
System.exit(-1);
}
TokenDemo.requestTokenTest(akId, akSecret);
System.out.println("----\n\n");
SpeechTokenGeneratorDemo generator = new SpeechTokenGeneratorDemo(akId, akSecret);
generator.start();
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//requestTokenTest(akId, akSecret);
System.out.println("### Game Over ###");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-transcriber:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-common:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.41" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
</component>
</module>
\ No newline at end of file
package com.alibaba.nls.client;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
......@@ -9,31 +11,45 @@ import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by siwei on 2018/05/14
* 此示例演示了
* ASR实时识别API调用
* 动态获取token
* 通过本地模拟实时流发送
* 识别耗时计算
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechTranscriberDemo {
private String appKey;
private String accessToken;
NlsClient client;
private NlsClient client;
public SpeechTranscriberDemo(String appKey, String token) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
client = new NlsClient(accessToken);
}
private static final Logger logger = LoggerFactory.getLogger(SpeechTranscriberDemo.class);
public SpeechTranscriberDemo(String appKey, String token, String url) {
public SpeechTranscriberDemo(String appKey, String id, String secret, String url) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
client = new NlsClient(url, accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(id, secret);
try {
accessToken.apply();
System.out.println("get token: " + ", expire time: " + accessToken.getExpireTime());
// TODO 创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
if(url.isEmpty()) {
client = new NlsClient(accessToken.getToken());
}else {
client = new NlsClient(url, accessToken.getToken());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static SpeechTranscriberListener getTranscriberListener() {
SpeechTranscriberListener listener = new SpeechTranscriberListener() {
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
//TODO 识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
System.out.println("task_id: " + response.getTaskId() +
......@@ -50,16 +66,13 @@ public class SpeechTranscriberDemo {
@Override
public void onTranscriberStart(SpeechTranscriberResponse response) {
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
", status: " + response.getStatus());
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
}
@Override
public void onSentenceBegin(SpeechTranscriberResponse response) {
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
", status: " + response.getStatus());
System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
}
......@@ -85,26 +98,30 @@ public class SpeechTranscriberDemo {
//识别完毕
@Override
public void onTranscriptionComplete(SpeechTranscriberResponse response) {
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
", status: " + response.getStatus());
System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
}
@Override
public void onFail(SpeechTranscriberResponse response) {
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//错误信息
", status_text: " + response.getStatusText());
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("task_id: " + response.getTaskId() + ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
}
};
return listener;
}
public void process(InputStream ins) {
/// 根据二进制数据大小计算对应的同等语音长度
/// sampleRate 仅支持8000或16000
public static int getSleepDelta(int dataSize, int sampleRate) {
// 仅支持16位采样
int sampleBytes = 16;
// 仅支持单通道
int soundChannel = 1;
return (dataSize * 10 * 8000) / (160 * sampleRate);
}
public void process(String filepath) {
SpeechTranscriber transcriber = null;
try {
//创建实例,建立连接
......@@ -123,10 +140,26 @@ public class SpeechTranscriberDemo {
//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
transcriber.start();
//语音数据来自声音文件用此方法,控制发送速率;若语音来自实时录音,不需控制发送速率直接调用 transcriber.sent(ins)即可
transcriber.send(ins, 3200, 100);
File file = new File(filepath);
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[3200];
int len;
while ((len = fis.read(b)) > 0) {
logger.info("send data pack length: " + len);
transcriber.send(b);
// TODO 重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep
// TODO 如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000
// 8000采样率情况下,3200byte字节建议 sleep 200ms,16000采样率情况下,3200byte字节建议 sleep 100ms
int deltaSleep = getSleepDelta(len, 16000);
Thread.sleep(deltaSleep);
}
//通知服务端语音数据发送完毕,等待服务端处理完成
long now = System.currentTimeMillis();
logger.info("ASR wait for complete");
transcriber.stop();
logger.info("ASR latency : " + (System.currentTimeMillis() - now) + " ms");
} catch (Exception e) {
System.err.println(e.getMessage());
} finally {
......@@ -141,33 +174,31 @@ public class SpeechTranscriberDemo {
}
public static void main(String[] args) throws Exception {
String appKey = null;
String token = null;
String url = null;
SpeechTranscriberDemo demo = null;
if (args.length == 2) {
appKey = args[0];
token = args[1];
//default url is wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
demo = new SpeechTranscriberDemo(appKey, token);
} else if (args.length == 3) {
appKey = args[0];
token = args[1];
url = args[2];
demo = new SpeechTranscriberDemo(appKey, token, url);
String appKey = "填写你的appkey";
String id = "填写你在阿里云网站上的AccessKeyId";
String secret = "填写你在阿里云网站上的AccessKeySecret";
String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
if (args.length == 3) {
appKey = args[0];
id = args[1];
secret = args[2];
} else if (args.length == 4) {
appKey = args[0];
id = args[1];
secret = args[2];
url = args[3];
} else {
System.err.println("SpeechTranscriberDemo need params(url is optional): " +
"<app-key> <token> [<url>]");
System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");
System.exit(-1);
}
InputStream ins = SpeechTranscriberDemo.class.getResourceAsStream("/nls-sample-16k.wav");
if (null == ins) {
System.err.println("open the audio file failed!");
return;
}
demo.process(ins);
// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端
String filepath = "nls-sample-16k.wav";
SpeechTranscriberDemo demo = new SpeechTranscriberDemo(appKey, id, secret, url);
demo.process(filepath);
demo.shutdown();
}
}
\ No newline at end of file
......@@ -13,18 +13,31 @@ import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import com.alibaba.nls.client.util.OpuCodec;
/**
* Created by siwei on 2018/06/20
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpeechTranscriberMultiThreadDemo {
private static final Logger logger = LoggerFactory.getLogger(SpeechTranscriberMultiThreadDemo.class);
private static SampleRateEnum sampleRateEnum = SampleRateEnum.SAMPLE_RATE_16K;
final static OpuCodec codec = new OpuCodec();
/// 根据二进制数据大小计算对应的同等语音长度
/// sampleRate 仅支持8000或16000
public static int getSleepDelta(int dataSize, int sampleRate) {
// 仅支持16位采样
int sampleBytes = 16;
// 仅支持单通道
int soundChannel = 1;
return (dataSize * 10 * 8000) / (160 * sampleRate);
}
private static SpeechTranscriberListener getTranscriberListener(final String inputName) {
SpeechTranscriberListener listener = new SpeechTranscriberListener() {
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
//状态码 20000000 表示正常识别
......@@ -81,6 +94,7 @@ public class SpeechTranscriberMultiThreadDemo {
@Override
public void onFail(SpeechTranscriberResponse response) {
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
......@@ -93,47 +107,6 @@ public class SpeechTranscriberMultiThreadDemo {
return listener;
}
/**
* jvm optional param -DisOpuEnable=true -DsampleRate=8000
*
* @param args
*/
public static void main(String[] args) {
if (args.length < 5) {
System.err.println("SpeechTranscriberMultiThreadDemo need params: " +
"<app-key> <token> <url> <audio-file> <thread-num>");
System.exit(-1);
}
String appKey = args[0];
String token = args[1];
String url = args[2];
String audioFile = args[3];
String isOpuEnable = System.getProperty("isOpuEnable", "false");
String sampleRate = System.getProperty("sampleRate", "16000");
if ("8000".equals(sampleRate)) {
sampleRateEnum = SampleRateEnum.SAMPLE_RATE_8K;
}
int threadNum = Integer.parseInt(args[4]);
final NlsClient client = new NlsClient(url, token);
CountDownLatch latch = new CountDownLatch(threadNum);
try {
for (int i = 0; i < threadNum; i++) {
Task task = new Task(appKey, client, latch, audioFile, Boolean.parseBoolean(isOpuEnable));
new Thread(task).start();
}
latch.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
client.shutdown();
}
}
static class Task implements Runnable {
private String appKey;
private NlsClient client;
......@@ -152,6 +125,7 @@ public class SpeechTranscriberMultiThreadDemo {
@Override
public void run() {
try {
// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端
File file = new File(audioFile);
String audioFileName = file.getName();
SpeechTranscriberListener listener = getTranscriberListener(audioFileName);
......@@ -187,12 +161,25 @@ public class SpeechTranscriberMultiThreadDemo {
//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
transcriber.start();
//语音数据来自声音文件用此方法,控制发送速率;若语音来自实时录音,不需控制发送速率直接调用 recognizer.sent(ins)即可
transcriber.send(ins, 3200, 100);
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[3200];
int len;
while ((len = fis.read(b)) > 0) {
logger.info("send data length: " + len);
transcriber.send(b);
// TODO 重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep
// TODO 如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000
// 8000采样率情况下,3200byte字节建议 sleep 200ms,16000采样率情况下,3200byte字节建议 sleep 100ms
int deltaSleep = getSleepDelta(len, sampleRateEnum.value);
Thread.sleep(deltaSleep);
}
}
// 通知服务端语音数据发送完毕,等待服务端处理完成
long now = System.currentTimeMillis();
logger.info("ASR wait for complete");
transcriber.stop();
logger.info("ASR latency : " + (System.currentTimeMillis() - now) + " ms");
transcriber.close();
} catch (Exception e) {
System.err.println(e.getMessage());
......@@ -201,4 +188,50 @@ public class SpeechTranscriberMultiThreadDemo {
}
}
}
/**
* jvm optional param -DisOpuEnable=true -DsampleRate=8000
*
* @param args
*/
public static void main(String[] args) {
if (args.length < 5) {
System.err.println("SpeechTranscriberMultiThreadDemo need params: <app-key> <token> <url> <audio-file> <thread-num>");
System.exit(-1);
}
String appKey = args[0];
String token = args[1];
String url = args[2]; // 默认 wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
String audioFile = args[3];
int threadNum = Integer.parseInt(args[4]);
System.out.println("appkey " + appKey);
System.out.println("token " + token);
System.out.println("url " + url);
System.out.println("audioFile " + audioFile);
String isOpuEnable = System.getProperty("isOpuEnable", "false");
String sampleRate = System.getProperty("sampleRate", "16000");
if ("8000".equals(sampleRate)) {
sampleRateEnum = SampleRateEnum.SAMPLE_RATE_8K;
}
final NlsClient client = new NlsClient(url, token);
CountDownLatch latch = new CountDownLatch(threadNum);
try {
for (int i = 0; i < threadNum; i++) {
Task task = new Task(appKey, client, latch, audioFile, Boolean.parseBoolean(isOpuEnable));
new Thread(task).start();
}
latch.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
client.shutdown();
}
}
}
......@@ -13,7 +13,8 @@ import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
/**
* Created by siwei on 2018/06/20
* 此示例演示了从麦克风采集语音并实时识别的过程
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechTranscriberWithMicrophoneDemo {
private String appKey;
......@@ -39,6 +40,7 @@ public class SpeechTranscriberWithMicrophoneDemo {
//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
@Override
public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
// 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("name: " + response.getName() +
//状态码 20000000 表示正常识别
", status: " + response.getStatus() +
......@@ -92,6 +94,7 @@ public class SpeechTranscriberWithMicrophoneDemo {
@Override
public void onFail(SpeechTranscriberResponse response) {
// 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
......@@ -153,16 +156,15 @@ public class SpeechTranscriberWithMicrophoneDemo {
}
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.err.println("SpeechRecognizerDemo need params: <app-key> <token> <url>");
if (args.length < 2) {
System.err.println("SpeechRecognizerDemo need params: <app-key> <token> ");
System.exit(-1);
}
String appKey = args[0];
String token = args[1];
String url = args[2];
SpeechTranscriberWithMicrophoneDemo demo = new SpeechTranscriberWithMicrophoneDemo(appKey, token, url);
SpeechTranscriberWithMicrophoneDemo demo = new SpeechTranscriberWithMicrophoneDemo(appKey, token);
demo.process();
demo.shutdown();
}
......
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-tts:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nls:nls-sdk-common:2.1.0" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.41" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.17.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.17.Final" level="project" />
</component>
</module>
\ No newline at end of file
......@@ -12,53 +12,76 @@ import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author zhishen.ml
* @date 2018-06-12
* 此示例演示了
* 语音合成API调用
* 动态获取token
* 流式合成TTS
* 首包延迟计算
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechSynthesizerDemo {
private static final Logger logger = LoggerFactory.getLogger(SpeechSynthesizerDemo.class);
private static long startTime;
private String appKey;
private String accessToken;
NlsClient client;
/// 直接传递token进来
public SpeechSynthesizerDemo(String appKey, String token) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
client = new NlsClient(accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
client = new NlsClient(token);
}
public SpeechSynthesizerDemo(String appKey, String token, String url) {
public SpeechSynthesizerDemo(String appKey, String accessKeyId, String accessKeySecret, String url) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
client = new NlsClient(url, accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
try {
accessToken.apply();
System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
if(url.isEmpty()) {
client = new NlsClient(accessToken.getToken());
}else {
client = new NlsClient(url, accessToken.getToken());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static SpeechSynthesizerListener getSynthesizerListener() {
SpeechSynthesizerListener listener = null;
try {
listener = new SpeechSynthesizerListener() {
File f=new File("tts_test.wav");
FileOutputStream fout = new FileOutputStream(f);
private boolean firstRecvBinary = true;
//语音合成结束
@Override
public void onComplete(SpeechSynthesizerResponse response) {
System.out.println("name: " + response.getName() +
", status: " + response.getStatus()+
", output file :"+f.getAbsolutePath()
);
// TODO 当onComplete时表示所有TTS数据已经接收完成,因此这个是整个合成延迟,该延迟可能较大,未必满足实时场景
System.out.println("name: " + response.getName() + ", status: " + response.getStatus()+", output file :"+f.getAbsolutePath());
}
//语音合成的语音二进制数据
@Override
public void onMessage(ByteBuffer message) {
try {
if(firstRecvBinary) {
// TODO 此处是计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)
firstRecvBinary = false;
long now = System.currentTimeMillis();
logger.info("tts first latency : " + (now - SpeechSynthesizerDemo.startTime) + " ms");
}
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
System.out.println("write arrya:" + bytesArray.length);
//System.out.println("write array:" + bytesArray.length);
fout.write(bytesArray);
} catch (IOException e) {
e.printStackTrace();
......@@ -66,6 +89,7 @@ public class SpeechSynthesizerDemo {
}
@Override
public void onFail(SpeechSynthesizerResponse response){
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
......@@ -91,18 +115,24 @@ public class SpeechSynthesizerDemo {
//设置返回音频的采样率
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
//发音人
synthesizer.setVoice("xiaoyun");
synthesizer.setVoice("siyue");
//语调,范围是-500~500,可选,默认是0
synthesizer.setPitchRate(100);
//语速,范围是-500~500,默认是0
synthesizer.setSpeechRate(100);
//设置用于语音合成的文本
synthesizer.setText("北京明天天气怎么样啊");
synthesizer.setText("欢迎使用阿里巴巴智能语音合成服务,您可以说北京明天天气怎么样啊");
//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
long start = System.currentTimeMillis();
synthesizer.start();
logger.info("tts start latency " + (System.currentTimeMillis() - start) + " ms");
SpeechSynthesizerDemo.startTime = System.currentTimeMillis();
//等待语音合成结束
synthesizer.waitForComplete();
logger.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
......@@ -118,25 +148,26 @@ public class SpeechSynthesizerDemo {
}
public static void main(String[] args) throws Exception {
String appKey = null;
String token = null;
String url = null;
SpeechSynthesizerDemo demo =null;
if (args.length == 2) {
appKey = args[0];
token = args[1];
//default url is wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
demo = new SpeechSynthesizerDemo(appKey, token);
}else if(args.length == 3){
appKey = args[0];
token = args[1];
url = args[2];
demo = new SpeechSynthesizerDemo(appKey, token, url);
}else{
System.err.println("SpeechSynthesizerDemo need params(url is optional): " +
"<app-key> <token> [<url>]");
String appKey = "填写你的appkey";
String id = "填写你在阿里云网站上的AccessKeyId";
String secret = "填写你在阿里云网站上的AccessKeySecret";
String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
if (args.length == 3) {
appKey = args[0];
id = args[1];
secret = args[2];
} else if (args.length == 4) {
appKey = args[0];
id = args[1];
secret = args[2];
url = args[3];
} else {
System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");
System.exit(-1);
}
SpeechSynthesizerDemo demo = new SpeechSynthesizerDemo(appKey, id, secret, url);
demo.process();
demo.shutdown();
}
......
......@@ -3,6 +3,7 @@ package com.alibaba.nls.client;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
......@@ -15,43 +16,63 @@ import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
/**
* tts 支持最多300个字符,此demo展示超过300字符的调用方式
*
* @author zhishen.ml
* @date 2018-06-12
* 此示例: tts 支持最多300个字符,此demo展示超过300字符的调用方式
*/
public class SpeechSynthesizerLongTextDemo {
private String appKey;
private String accessToken;
NlsClient client;
/// 直接传递token进来
public SpeechSynthesizerLongTextDemo(String appKey, String token) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
client = new NlsClient(accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
client = new NlsClient(token);
}
public SpeechSynthesizerLongTextDemo(String appKey, String token, String url) {
public SpeechSynthesizerLongTextDemo(String appKey, String accessKeyId, String accessKeySecret) {
this.appKey = appKey;
this.accessToken = token;
//创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
client = new NlsClient(url, accessToken);
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
try {
accessToken.apply();
System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
client = new NlsClient(accessToken.getToken());
} catch (IOException e) {
e.printStackTrace();
}
}
private static SpeechSynthesizerListener getSynthesizerListener(final FileOutputStream fout) {
public SpeechSynthesizerLongTextDemo(String appKey, String accessKeyId, String accessKeySecret, String url) {
this.appKey = appKey;
//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取token
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
try {
accessToken.apply();
System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());
if(url.isEmpty()) {
client = new NlsClient(accessToken.getToken());
}else {
client = new NlsClient(url, accessToken.getToken());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static SpeechSynthesizerListener getSynthesizerListener(final FileOutputStream fout) {
SpeechSynthesizerListener listener = null;
try {
listener = new SpeechSynthesizerListener() {
int totalSize = 0;
//语音合成结束
@Override
public void onComplete(SpeechSynthesizerResponse response) {
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
", status: " + response.getStatus()
);
", name: " + response.getName() + ", status: " + response.getStatus());
System.out.println("onComplete, totalsize = " + totalSize);
}
//语音合成的语音二进制数据
......@@ -61,6 +82,7 @@ public class SpeechSynthesizerLongTextDemo {
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
System.out.println("write arrya:" + bytesArray.length);
totalSize += bytesArray.length;
fout.write(bytesArray);
} catch (IOException e) {
e.printStackTrace();
......@@ -69,13 +91,13 @@ public class SpeechSynthesizerLongTextDemo {
@Override
public void onFail(SpeechSynthesizerResponse response) {
// 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//错误信息
", status_text: " + response.getStatusText());
}
};
} catch (Exception e) {
......@@ -117,7 +139,6 @@ public class SpeechSynthesizerLongTextDemo {
/**
* 将长文本切分为每句字数不大于size数目的短句
*
* @param text
* @param size
* @return
......@@ -138,6 +159,7 @@ public class SpeechSynthesizerLongTextDemo {
textPart.append(texts[i]);
len += texts[i].length();
if(len<text.length()){
//System.out.println("at " + text.charAt(len));
textPart.append(text.charAt(len));
len += 1;
}
......@@ -155,25 +177,85 @@ public class SpeechSynthesizerLongTextDemo {
client.shutdown();
}
public static byte[] int2byte(int intData) {
byte[] byteData = new byte[4];
byteData[0] = (byte) (0xff & (intData >> 24));
byteData[1] = (byte) (0xff & (intData >> 16));
byteData[2] = (byte) (0xff & (intData >> 8));
byteData[3] = (byte) (0xff & intData);
return byteData;
}
public static byte[] short2byte(short s) {
byte[] byteData = new byte[2];
byteData[0] = (byte) (0xff & (s >> 8));
byteData[1] = (byte) (0xff & s);
return byteData;
}
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.err.println("SpeechSynthesizerDemo need params: <app-key> <token> <url>");
String appKey = "填写你的appkey";
String id = "填写你在阿里云网站上的AccessKeyId";
String secret = "填写你在阿里云网站上的AccessKeySecret";
String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1
if (args.length == 3) {
appKey = args[0];
id = args[1];
secret = args[2];
} else if (args.length == 4) {
appKey = args[0];
id = args[1];
secret = args[2];
url = args[3];
} else {
System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");
System.exit(-1);
}
String ttsTextLong = "百草堂与三味书屋 鲁迅 \n" +
"我家的后面有一个很大的园,相传叫作百草园。现在是早已并屋子一起卖给朱文公的子孙了,连那最末次的相见也已经隔了七八年,其中似乎确凿只有一些野草;但那时却是我的乐园。\n" +
"不必说碧绿的菜畦,光滑的石井栏,高大的皂荚树,紫红的桑葚;也不必说鸣蝉在树叶里长吟,肥胖的黄蜂伏在菜花上,轻捷的叫天子(云雀)忽然从草间直窜向云霄里去了。\n" +
"单是周围的短短的泥墙根一带,就有无限趣味。油蛉在这里低唱,蟋蟀们在这里弹琴。翻开断砖来,有时会遇见蜈蚣;还有斑蝥,倘若用手指按住它的脊梁,便会啪的一声,\n" +
"从后窍喷出一阵烟雾。何首乌藤和木莲藤缠络着,木莲有莲房一般的果实,何首乌有臃肿的根。有人说,何首乌根是有像人形的,吃了便可以成仙,我于是常常拔它起来,牵连不断地拔起来,\n" +
"也曾因此弄坏了泥墙,却从来没有见过有一块根像人样。如果不怕刺,还可以摘到覆盆子,像小珊瑚珠攒成的小球,又酸又甜,色味都比桑葚要好得远......";
"也曾因此弄坏了泥墙,却从来没有见过有一块根像人样! 如果不怕刺,还可以摘到覆盆子,像小珊瑚珠攒成的小球,又酸又甜,色味都比桑葚要好得远......";
//ttsTextLong = "你和北京百草堂与三味书屋从后窍喷出一阵烟雾";
String path = "longText4TTS.wav";
File out = new File(path);
FileOutputStream fout = new FileOutputStream(out);
String appKey = args[0];
String token = args[1];
String url = args[2];
// 初期并不知道wav文件实际长度,假设为0,最后再校正
int pcmSize = 0;
WavHeader header = new WavHeader();
// 长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
header.fileLength = pcmSize + (44 - 8);
header.fmtHdrLeth = 16;
header.bitsPerSample = 16;
header.channels = 1;
header.formatTag = 0x0001;
header.samplesPerSec = 16000;
header.blockAlign = (short) (header.channels * header.bitsPerSample / 8);
header.avgBytesPerSec = header.blockAlign * header.samplesPerSec;
header.dataHdrLeth = pcmSize;
byte[] h = header.getHeader();
assert h.length == 44;
SpeechSynthesizerLongTextDemo demo = new SpeechSynthesizerLongTextDemo(appKey, token, url);
File out = new File("longText4TTS.pcm");
demo.process(ttsTextLong, new FileOutputStream(out));
// 先写入44字节的wav头,如果合成的不是wav,比如是pcm,则不需要此步骤
fout.write(h);
SpeechSynthesizerLongTextDemo demo = new SpeechSynthesizerLongTextDemo(appKey, id, secret, url);
demo.process(ttsTextLong, fout);
demo.shutdown();
// 更新44字节的wav头,如果合成的不是wav,比如是pcm,则不需要此步骤
RandomAccessFile wavFile = new RandomAccessFile(path, "rw");
int fileLength = (int)wavFile.length();
int dataSize = fileLength - 44;
System.out.println("filelength = " + fileLength +", datasize = " + dataSize);
header.fileLength = fileLength - 8;
header.dataHdrLeth = fileLength - 44;
wavFile.write(header.getHeader());
wavFile.close();
}
}
......@@ -8,26 +8,40 @@ import java.util.concurrent.CountDownLatch;
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.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by siwei on 2018/06/20
* 此示例演示了
* 语音合成SDK API调用
* 多线程并发调用
* 用户自定义参数设置
* 流式合成TTS
* 首包延迟计算
* (仅作演示,需用户根据实际情况实现)
*/
public class SpeechSynthesizerMultiThreadDemo {
private static int sampleRate;
private static final Logger logger = LoggerFactory.getLogger(SpeechSynthesizerMultiThreadDemo.class);
private static SpeechSynthesizerListener getSynthesizerListener(final String audioFileName) {
SpeechSynthesizerListener listener = null;
try {
listener = new SpeechSynthesizerListener() {
//如果需要播放音频,且对实时性要求较高,建议使用流式播放
// TODO 如果需要播放音频,且对实时性要求较高,建议使用流式播放
File f = new File(audioFileName);
FileOutputStream fout = new FileOutputStream(f);
private boolean firstRecvBinary = true;
private long firstRecvBinaryTimeStamp = System.currentTimeMillis();
// 语音合成结束
@Override
public void onComplete(SpeechSynthesizerResponse response) {
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println("task_id: " + response.getTaskId() +
", thread id: " + Thread.currentThread().getId() +
", name: " + response.getName() +
......@@ -40,10 +54,16 @@ public class SpeechSynthesizerMultiThreadDemo {
@Override
public void onMessage(ByteBuffer message) {
try {
if(firstRecvBinary) {
// TODO 此处是计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)
firstRecvBinary = false;
long now = System.currentTimeMillis();
logger.info("first latency : " + (now - firstRecvBinaryTimeStamp) + " ms");
}
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
System.out.println("thread id: " + Thread.currentThread().getId() +
", write arrya:" + bytesArray.length);
System.out.println("output: " + audioFileName + ", thread id: "
+ Thread.currentThread().getId() + ", write arrya:" + bytesArray.length);
fout.write(bytesArray);
} catch (IOException e) {
e.printStackTrace();
......@@ -51,16 +71,10 @@ public class SpeechSynthesizerMultiThreadDemo {
}
@Override
public void onFail(SpeechSynthesizerResponse response){
// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查
System.out.println(
"task_id: " + response.getTaskId() +
//状态码 20000000 表示识别成功
", status: " + response.getStatus() +
//错误信息
", status_text: " + response.getStatusText());
"task_id: " + response.getTaskId() + ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
}
};
} catch (Exception e) {
e.printStackTrace();
......@@ -93,7 +107,7 @@ public class SpeechSynthesizerMultiThreadDemo {
// 设置返回音频的编码格式
synthesizer.setFormat(OutputFormatEnum.WAV);
// 设置返回音频的采样率
synthesizer.setSampleRate(sampleRate);
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
// 设置用于语音合成的文本
synthesizer.setText(text);
......@@ -115,36 +129,38 @@ public class SpeechSynthesizerMultiThreadDemo {
public static void main(String[] args) {
if (args.length < 6) {
System.err.println("SpeechSynthesizerMultiThreadDemo need params: " +
"<app-key> <token> <url> <text> <save-audio-name> <thread-num>");
"<app-key> <token> <url> <text> <save-audio-name> <thread-num>");
System.exit(-1);
}
String appKey = args[0];
String token = args[1];
String url = args[2];
String text = args[3];
String audioFileName = args[4];
int threadNum = Integer.parseInt(args[5]);
String sampleRateStr=System.getProperty("sampleRate","16000");
sampleRate=Integer.parseInt(sampleRateStr);
String appKey = args[0]; // appkey
String token = args[1]; // token
String url = args[2]; // url
String text = args[3]; // 合成文本
String audioFileName = args[4]; // 待保存文件名
int threadNum = Integer.parseInt(args[5]); // 并发数
final NlsClient client = new NlsClient(url, token);
CountDownLatch latch = new CountDownLatch(threadNum);
try {
for (int i = 0; i < threadNum; i++) {
String temp = null;
// 拼接一个文件名
String path = null;
int index = audioFileName.lastIndexOf(".");
if (index <= 0) {
temp = audioFileName + "_" + (i + 1);
path = audioFileName + "_" + (i + 1) + ".wav";
} else {
temp = audioFileName.substring(0, index) +
path = audioFileName.substring(0, index) +
"_" + (i + 1) + audioFileName.substring(index, audioFileName.length());
}
Task task = new Task(appKey, client, latch, text, temp);
// 创建一个请求任务线程
Task task = new Task(appKey, client, latch, text, path);
new Thread(task).start();
}
// 等待所有请求任务结束
latch.await();
} catch (Exception e) {
e.printStackTrace();
......
package com.alibaba.nls.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
class WavHeader {
/**
* 4 资源交换文件标志(RIFF)
*/
public final char fileID[] = {'R', 'I', 'F', 'F'};
/**
* 4 总字节数
*/
public int fileLength;
/**
* 4 WAV文件标志(WAVE)
*/
public char wavTag[] = {'W', 'A', 'V', 'E'};
/**
* 4 波形格式标志(fmt ),最后一位空格
*/
public char fmtHdrID[] = {'f', 'm', 't', ' '};
/**
* 4 过滤字节(一般为00000010H),若为00000012H则说明数据头携带附加信息
*/
public int fmtHdrLeth;
/**
* 2 格式种类(值为1时,表示数据为线性PCM编码)
*/
public short formatTag;
/**
* 2 通道数,单声道为1,双声道为2
*/
public short channels;
/**
* 4 采样频率
*/
public int samplesPerSec;
/**
* 4 波形数据传输速率(每秒平均字节数)
*/
public int avgBytesPerSec;
/**
* 2 DATA数据块长度,字节
*/
public short blockAlign;
/**
* 2 PCM位宽
*/
public short bitsPerSample;
/**
* 4 数据标志符(data)
*/
public char dataHdrID[] = {'d', 'a', 't', 'a'};
/**
* 4 DATA总数据长度字节
*/
public int dataHdrLeth;
public byte[] getHeader() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
WriteChar(bos, fileID);
WriteInt(bos, fileLength);
WriteChar(bos, wavTag);
WriteChar(bos, fmtHdrID);
WriteInt(bos, fmtHdrLeth);
WriteShort(bos, formatTag);
WriteShort(bos, channels);
WriteInt(bos, samplesPerSec);
WriteInt(bos, avgBytesPerSec);
WriteShort(bos, blockAlign);
WriteShort(bos, bitsPerSample);
WriteChar(bos, dataHdrID);
WriteInt(bos, dataHdrLeth);
bos.flush();
byte[] r = bos.toByteArray();
bos.close();
return r;
}
private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {
byte[] mybyte = new byte[2];
mybyte[1] = (byte) ((s << 16) >> 24);
mybyte[0] = (byte) ((s << 24) >> 24);
bos.write(mybyte);
}
private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {
byte[] buf = new byte[4];
buf[3] = (byte) (n >> 24);
buf[2] = (byte) ((n << 8) >> 24);
buf[1] = (byte) ((n << 16) >> 24);
buf[0] = (byte) ((n << 24) >> 24);
bos.write(buf);
}
private void WriteChar(ByteArrayOutputStream bos, char[] id) {
for (int i = 0; i < id.length; i++) {
char c = id[i];
bos.write(c);
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %5p [%15.15t] [%40.40logger{40}] %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoding>UTF-8</encoding>
<file>logs/nls.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/nls.%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<layout>
<pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%logger:%line] --%mdc{client} %msg%n</pattern>
</layout>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.alibaba.nls.client" level="DEBUG">
<appender-ref ref="DEBUG_FILE"/>
</logger>
</configuration>
文件已添加
文件已添加
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
</component>
</module>
\ No newline at end of file
......@@ -12,12 +12,15 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.6</java.version>
<java.version>1.8</java.version>
<sdk.version>2.1.0</sdk.version>
</properties>
<modules>
<module>nls-example-recognizer</module><module>nls-example-transcriber</module><module>nls-example-tts</module>
<module>nls-example-recognizer</module>
<module>nls-example-transcriber</module>
<module>nls-example-tts</module>
<module>nls-example-token</module>
</modules>
<dependencies>
......@@ -60,20 +63,9 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.7</version>
<configuration>
<formats>
<format>html</format>
<format>xml</format>
</formats>
<check />
</configuration>
</plugin>
</plugins>
</build>
</project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册