在线语音合成 Java SDK 文档
科大讯飞官方文档地址.
官方问题解答 — java sdk常见问题分析解答
1. SDK引入
- 将Msc.jar包放到lib文件夹,lib文件夹放到项目根目录(目前是app工程的根目录);[^1]
- 还需要json-jena-1.0.jar,这个jar包由讯飞提供,但是直接增加pom中org.json的依赖也行(本工程用的org.json,没有使用讯飞的jar包);
- pom文件引入本地jar包 Msc.jar
app工程的pom文件修改 Msc.jar的scope和systemPath: system ${project.basedir}/lib/Msc.jar; - pom文件的plugin配置要加上 true 确保app工程打包时要将本地Mac.jar打包进去;
- jar包设置错误,项目无法启动
//pom.xml( vc2-app)
//pom.xml( vc2-app)
<!-- 科大讯飞 语音合成 -->
<dependency>
<groupId>Msc</groupId>
<artifactId>Msc</artifactId>
<version>8.8.8.8</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/Msc.jar</systemPath>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.sinoecare.vc2.app.VillcloudAppApplication</mainClass>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.库文件引入
- libmsc32.so;libmsc64.se;msc32.dll;msc64.dll 分别对应linux和window下的32位和64位系统
- window下,直接将四个文件放到parent工程的根目录下 如图3;
- linux下,将四个文件放到系统/lib文件下 如图2;
- 官方解释:http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9864
- 库文件引入不正确 sdk会报20021的错误
图2:
图3:
3.工具类MscUtil
- MscUtil已经封装好,使用语音合成直接调用transToSpeech方法即可;注意appId和remoteSysService需要再调用时注入,作为参数提供给该方法;
- appid 是sdk识别合成发起的标识;在vc2-app的bootstrap.yml配置
- MscTestController有调用Demo
//MscController
- 实现SynthesizeToUriListener ,
- 重写合成方法 synthesize 可以设置对应的语音类型
- 定义flag 初始化为 0循环判断flag的状态 , 只有当 onSynthesizeCompleted内置方法调用后 表示语音合成已经完成 否则 线程休眠一秒 直到合成完成 再进行下一步
package com.sinoecare.vc2.app.controller;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.iflytek.cloud.speech.*;
import com.sinoecare.vc2.app.remoteService.RemoteSysService;
import com.sinoecare.vc2.app.util.MscUtil;
import com.sinoecare.vc2.common.core.constant.MscConstans;
import com.sinoecare.vc2.common.core.util.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 科大讯飞sdk接口测试
*
* @author lanlan
* @date 2019-09-05 11:44:45
*/
@Slf4j
@RestController
@Data
@RequestMapping("/msc")
@Api(tags = "科大讯飞sdk接口测试", protocols = "http")
public class MscController implements SynthesizeToUriListener {
@Value("${msc.appid}")
private String appid;
@Autowired
private RemoteSysService remoteSysService;
private int flag = 0; // 0初始化,1成功,2失败
List<String> listR = null;
@ApiOperation(value = "科大讯飞sdk接口测试")
@PostMapping("/transToSpeech")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "words", dataType = "String", required = true, value = "要转的话"),
@ApiImplicitParam(paramType = "query", name = "fileName", dataType = "String", required = true, value = "文件名")
})
public R transToSpeech(@RequestParam(name = "words", required = true) String words,
@RequestParam(name = "fileName", required = true) String fileName) {
log.info("根据id查询详情,words = {}, fileName = {}", words, fileName);
try {
flag = 0;
listR = null;
//合成
String uri = synthesize(trimeTip(words), fileName, appid, "1", this);
// 合成完成的回调函数完成前休眠 onSynthesizeCompleted
while (flag == 0) {
Thread.sleep(1000);
}
//合成回调后flge会改为1 , pcm转MP3后上传ftp服务
if (flag == 1) {
listR = remoteSysService.uplordSpeech(uri, null).getData();
}
// log.info("flag = {}", flag);
// log.info(this.listR.get(0));
return new R<>(listR);
} catch (Exception e) {
log.error(e.getMessage());
return R.error(e.getMessage());
}
}
public void onBufferProgress(int progress) {
log.info("*************合成进度*************" + progress);
}
public void onSynthesizeCompleted(String uri, SpeechError error) {
if (error == null) {
log.info("*************合成成功*************");
log.info("合成音频生成路径:" + uri);
try {
// listR = remoteSysService.uplordSpeech(uri, "3333").getData();
// log.info(listR.toString());
flag = 1;
} catch (Exception e) {
log.error("合成失败", e);
flag = 2;
}
} else
flag = 2;
log.info("*************" + error.getErrorCode()
+ "*************");
}
/**
* 合成
*/
public static String synthesize(String words, String fileName, String appid, String voiceName, SynthesizeToUriListener synthesizeToUriListener) {
SpeechUtility utility = SpeechUtility.createUtility(SpeechConstant.APPID + "=" + appid);
SpeechSynthesizer speechSynthesizer = SpeechSynthesizer.createSynthesizer();
String userDir = System.getProperty("user.dir");
// "./tts_test.pcm"
String uri = userDir + "/" + fileName + ".pcm";
// 设置发音人
if (StrUtil.isBlank(voiceName) || MscConstans.VOICE_NAME_MAP.get(voiceName) == null) {
voiceName = MscConstans.VOICE_NAME_MAP.get("1");
} else {
voiceName = MscConstans.VOICE_NAME_MAP.get(voiceName);
}
speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, voiceName);
speechSynthesizer.setParameter(SpeechConstant.TTS_AUDIO_PATH, uri);
//启用合成音频流事件,不需要时,不用设置此参数
speechSynthesizer.setParameter(SpeechConstant.TTS_BUFFER_EVENT, "1");
// 设置合成音频保存位置(可自定义保存位置),默认不保存
speechSynthesizer.synthesizeToUri(words, uri, synthesizeToUriListener);
log.info("------------------uri-----------------------" + uri + "-----------------" + appid);
return uri;
}
@Override
public void onEvent(int eventType, int arg1, int arg2, int arg3, Object obj1, Object obj2) {
}
//剔除尖括号内容
private String trimeTip(String words) {
if (StrUtil.isNotBlank(words)) {
while (ReUtil.contains("<(.*?)>", words)) {
words = ReUtil.delFirst("<(.*?)>", words);
}
words = words.trim();
}
return words;
}
}
//合成的文件是 pcm 需要转换成mp3
ffmpeg 转换时要设置为8000 如果16000则可能语音变速
public static void pcmToMP3(String localPath, String targetFilePath) {
log.info("执行pcm转mp3");
String command = "ffmpeg -y -f s16be -ac 2 -ar 8000 -acodec pcm_s16le -i " + localPath + " " + targetFilePath;
log.info("the command is : " + command);
Runtime runtime = Runtime.getRuntime();
try {
Process proc = runtime.exec(command);
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null)
sb.append(line);
int exitVal = proc.waitFor();
log.info("the exitVal is : " + exitVal);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
log.error("ffmpeg exec cmd Exception ", e);
}
log.info("执行命令结束");
}