package com.smartor.service.impl;
|
|
import com.alibaba.nls.client.AccessToken;
|
import com.alibaba.nls.client.protocol.InputFormatEnum;
|
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.asr.SpeechRecognizer;
|
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerListener;
|
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerResponse;
|
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 com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.uuid.IdUtils;
|
import com.smartor.domain.*;
|
import com.smartor.mapper.*;
|
import com.smartor.service.PersonVoiceService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.jsoup.Jsoup;
|
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Element;
|
import org.jsoup.select.Elements;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
|
import java.io.*;
|
import java.nio.ByteBuffer;
|
import java.nio.file.Files;
|
|
/**
|
* 短信账号Service业务层处理
|
*
|
* @author smartor
|
* @date 2023-03-06
|
*/
|
@Slf4j
|
@Service
|
public class PersonVoiceServiceImpl implements PersonVoiceService {
|
@Value("${accessKeyId}")
|
private String accessKeyId;
|
|
@Value("${accessKeySecret}")
|
private String accessKeySecret;
|
|
@Autowired
|
private SvyLibTemplateWjxMapper svyLibTitleMapper;
|
|
@Autowired
|
private SvyLibTemplateScriptWjxMapper svyLibTopicMapper;
|
|
@Autowired
|
private SvyLibTopicdirectionMapper svyLibTopicdirectionMapper;
|
|
@Autowired
|
private SvyLibTemplateTargetoptionWjxMapper svyLibTopicoptionMapper;
|
//
|
// @Autowired
|
// private ISvyLibTitleService svyLibTitleService;
|
|
private NlsClient client;
|
|
private String appKey = "ZurNHpaQLq6P55YS";
|
|
private String returnResult = null;
|
|
String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
|
|
|
@Override
|
public String speechtotext(String filePath) {
|
this.accessToken();
|
this.process(filePath, 16000);
|
client.shutdown();
|
return returnResult;
|
}
|
|
@Override
|
public String texttospeech(String textspeech, String filePath) {
|
this.accessToken();
|
this.process2(textspeech, filePath);
|
client.shutdown();
|
return returnResult;
|
}
|
|
@Override
|
public Boolean explainHTML() {
|
try {
|
explainHTML2();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
return true;
|
}
|
|
public String explainHTML2() throws IOException {
|
|
String folderPath = "E:\\pc2";
|
// 获取文件夹下的所有文件
|
File[] files = new File(folderPath).listFiles();
|
// 遍历文件夹下的所有文件
|
a:
|
for (File file : files) {
|
// 获取文件名
|
String fileName = file.getName();
|
System.out.println(fileName);
|
// 获取文件内容
|
String fileContent = new String(Files.readAllBytes(file.toPath()));
|
|
Document parse = Jsoup.parse(fileContent);
|
Element body = parse.body();
|
Elements elementsByClass3 = body.getElementsByClass("topic__type-des");
|
String desc = elementsByClass3.text();
|
|
//保存title
|
String title = parse.title();
|
SvyLibTemplateWjx svyLibTitle = new SvyLibTemplateWjx();
|
// svyLibTitle.setCategoryid(System.currentTimeMillis());
|
svyLibTitle.setSvyname(title);
|
svyLibTitle.setDescription(desc);
|
svyLibTitle.setDelFlag("0");
|
svyLibTitle.setIsupload(0L);
|
|
svyLibTitleMapper.insertSvyLibTemplateWjx(svyLibTitle);
|
|
|
Elements elementsByClass = body.getElementsByClass("topic__type-body");
|
Elements children = elementsByClass.get(0).children();
|
for (Element element : children) {
|
int legend = element.select("legend").size();
|
if (legend == 0) {
|
//跳过该文件
|
noExpiain(fileName.substring(0, fileName.length() - 5), fileContent);
|
continue a;
|
}
|
if (!element.getElementsByTag("legend").attr("class").equals("topic__type-title")) {
|
//跳过该文件
|
noExpiain(fileName.substring(0, fileName.length() - 5), fileContent);
|
continue a;
|
}
|
}
|
|
for (Element element : children) {
|
//获取题目
|
String legend = element.getElementsByTag("legend").text();
|
System.out.println("题目: " + legend);
|
//将题目保存到表中
|
SvyLibTemplateScriptWjx svyLibTopic = new SvyLibTemplateScriptWjx();
|
svyLibTopic.setSvyid(svyLibTitle.getSvyid());
|
|
//判断该题目下是否有选项
|
Elements elementsByClass1 = element.getElementsByClass("topic__type-dry");
|
if (elementsByClass1.size() != 0) {
|
Elements spans = elementsByClass1.get(0).getElementsByTag("span");
|
if (spans.size() != 0) {
|
Elements radio__type = spans.get(0).getElementsByClass("radio__type");
|
if (radio__type.size() != 0) {
|
svyLibTopic.setScriptType("1");
|
} else {
|
Elements checkbox__type = spans.get(0).getElementsByClass("checkbox__type");
|
if (checkbox__type.size() != 0) {
|
svyLibTopic.setScriptType("2");
|
}
|
}
|
|
}
|
svyLibTopic.setSeqno(IdUtils.simpleUUID());
|
svyLibTopic.setScriptContent(legend);
|
svyLibTopic.setDelFlag("0");
|
svyLibTopic.setIsupload(0L);
|
svyLibTopic.setOrgid(fileName);
|
try {
|
svyLibTopicMapper.insertSvyLibTemplateScriptWjx(svyLibTopic);
|
} catch (Exception e) {
|
noExpiain(fileName.substring(0, fileName.length() - 5) + "出异常了1", fileContent);
|
continue a;
|
}
|
|
|
String item = "";
|
Long i = 1L;
|
for (Element span : spans) {
|
Elements labels = span.getElementsByTag("label");
|
for (Element label : labels) {
|
String text = label.text();
|
//将题目存库
|
// item = item + "------" + text;
|
SvyLibTemplateTargetoptionWjx svyLibTopicoption = new SvyLibTemplateTargetoptionWjx();
|
svyLibTopicoption.setScriptid(svyLibTopic.getId());
|
svyLibTopicoption.setTemplateID(svyLibTitle.getSvyid());
|
svyLibTopicoption.setSort(i);
|
svyLibTopicoption.setGuid(IdUtils.simpleUUID());
|
svyLibTopicoption.setOptioncontent(text);
|
svyLibTopicoption.setDelFlag("0");
|
svyLibTopicoption.setIsupload(0L);
|
try {
|
svyLibTopicoptionMapper.insertSvyLibTemplateTargetoptionWjx(svyLibTopicoption);
|
} catch (Exception e) {
|
noExpiain(fileName.substring(0, fileName.length() - 5) + "出异常了2", fileContent);
|
continue a;
|
}
|
i++;
|
}
|
}
|
System.out.println("选项: " + item);
|
} else {
|
|
//将题目保存到表中
|
Elements elementsByClass2 = element.getElementsByClass("ui-control-group ui-matrix");
|
if (elementsByClass2.size() > 0) {
|
SvyLibTemplateScriptWjx svyLibTopic2 = new SvyLibTemplateScriptWjx();
|
svyLibTopic2.setSvyid(svyLibTitle.getSvyid());
|
svyLibTopic2.setSeqno(IdUtils.simpleUUID());
|
svyLibTopic2.setScriptContent(legend);
|
svyLibTopic2.setDelFlag("0");
|
svyLibTopic.setIsupload(0L);
|
svyLibTopic2.setScriptType("4");
|
svyLibTopic2.setOrgid(fileName);
|
try {
|
svyLibTopicMapper.insertSvyLibTemplateScriptWjx(svyLibTopic2);
|
} catch (Exception e) {
|
noExpiain(fileName.substring(0, fileName.length() - 5) + "出异常了22", fileContent);
|
continue a;
|
}
|
|
|
Elements lis = elementsByClass2.get(0).getElementsByTag("li");
|
String transverse = "";
|
for (Element element1 : lis) {
|
transverse = transverse + element1.text() + "☆";
|
}
|
Elements title1 = elementsByClass2.get(0).getElementsByClass("title");
|
String direction = "";
|
for (Element ele : title1) {
|
direction = direction + ele.text() + "☆";
|
}
|
SvyLibTopicdirection svyLibTopicdirection = new SvyLibTopicdirection();
|
svyLibTopicdirection.setTopicid(svyLibTopic2.getId());
|
svyLibTopicdirection.setDirection(direction);
|
svyLibTopicdirection.setTransverse(transverse);
|
svyLibTopicdirectionMapper.insertSvyLibTopicdirection(svyLibTopicdirection);
|
} else {
|
|
|
//将题目保存到表中
|
SvyLibTemplateScriptWjx svyLibTopic3 = new SvyLibTemplateScriptWjx();
|
svyLibTopic3.setSvyid(svyLibTitle.getSvyid());
|
svyLibTopic3.setSeqno(IdUtils.simpleUUID());
|
svyLibTopic3.setScriptContent(legend);
|
svyLibTopic3.setDelFlag("0");
|
svyLibTopic3.setIsupload(0L);
|
svyLibTopic3.setScriptType("3");
|
svyLibTopic3.setOrgid(fileName);
|
try {
|
svyLibTopicMapper.insertSvyLibTemplateScriptWjx(svyLibTopic3);
|
} catch (Exception e) {
|
noExpiain(fileName.substring(0, fileName.length() - 5) + "出异常了33", fileContent);
|
continue a;
|
}
|
}
|
}
|
|
}
|
|
}
|
|
return null;
|
}
|
|
private void noExpiain(String filename, String content) throws IOException {
|
File file = new File("E:\\noexplain\\" + filename + ".html");
|
FileWriter fileWriter = new FileWriter(file);
|
fileWriter.write(content);
|
}
|
|
|
public SpeechRecognizerListener getRecognizerListener(int myOrder, String userParam) {
|
SpeechRecognizerListener speechRecognizerListener = new SpeechRecognizerListener() {
|
@Override
|
public void onRecognitionResultChanged(SpeechRecognizerResponse response) {
|
//getName是获取事件名称,getStatus是获取状态码,getRecognizedText是语音识别文本。
|
System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());
|
}
|
|
//识别完毕
|
@Override
|
public void onRecognitionCompleted(SpeechRecognizerResponse response) {
|
//getName是获取事件名称,getStatus是获取状态码,getRecognizedText是语音识别文本。
|
returnResult = response.getRecognizedText();
|
System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());
|
}
|
|
@Override
|
public void onStarted(SpeechRecognizerResponse response) {
|
System.out.println("myOrder: " + myOrder + "; myParam: " + userParam + "; task_id: " + response.getTaskId());
|
}
|
|
@Override
|
public void onFail(SpeechRecognizerResponse response) {
|
//task_id是调用方和服务端通信的唯一标识,当遇到问题时,需要提供此task_id。
|
System.out.println("task_id: " + response.getTaskId() + ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
|
}
|
};
|
|
return speechRecognizerListener;
|
}
|
|
|
public void accessToken() {
|
//应用全局创建一个NlsClient实例,默认服务地址为阿里云线上服务地址。
|
//获取Token,实际使用时注意在accessToken.getExpireTime()过期前再次获取。
|
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();
|
}
|
|
}
|
|
|
public void process(String filepath, int sampleRate) {
|
SpeechRecognizer recognizer = null;
|
try {
|
//传递用户自定义参数
|
String myParam = "user-param";
|
int myOrder = 1234;
|
SpeechRecognizerListener listener = getRecognizerListener(myOrder, myParam);
|
recognizer = new SpeechRecognizer(client, listener);
|
recognizer.setAppKey(appKey);
|
//设置音频编码格式。如果是OPUS文件,请设置为InputFormatEnum.OPUS。
|
recognizer.setFormat(InputFormatEnum.PCM);
|
//设置音频采样率
|
if (sampleRate == 16000) {
|
recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
|
} else if (sampleRate == 8000) {
|
recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_8K);
|
}
|
//设置是否返回中间识别结果
|
recognizer.setEnableIntermediateResult(true);
|
//设置是否打开语音检测(即vad)
|
recognizer.addCustomedParam("enable_voice_detection", true);
|
//此方法将以上参数设置序列化为JSON发送给服务端,并等待服务端确认。
|
long now = System.currentTimeMillis();
|
recognizer.start();
|
log.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) {
|
log.info("send data pack length: " + len);
|
recognizer.send(b, len);
|
//本案例用读取本地文件的形式模拟实时获取语音流,因为读取速度较快,这里需要设置sleep时长。
|
// 如果实时获取语音则无需设置sleep时长,如果是8k采样率语音第二个参数设置为8000。
|
int deltaSleep = getSleepDelta(len, sampleRate);
|
// Thread.sleep(deltaSleep);
|
}
|
//通知服务端语音数据发送完毕,等待服务端处理完成。
|
now = System.currentTimeMillis();
|
//计算实际延迟,调用stop返回之后一般即是识别结果返回时间。
|
log.info("ASR wait for complete");
|
recognizer.stop();
|
log.info("ASR stop latency : " + (System.currentTimeMillis() - now) + " ms");
|
fis.close();
|
} catch (Exception e) {
|
System.err.println(e.getMessage());
|
} finally {
|
//关闭连接
|
if (null != recognizer) {
|
recognizer.close();
|
}
|
}
|
}
|
|
|
public int getSleepDelta(int dataSize, int sampleRate) {
|
// 仅支持16位采样。
|
int sampleBytes = 16;
|
// 仅支持单通道。
|
int soundChannel = 1;
|
return (dataSize * 10 * 8000) / (160 * sampleRate);
|
}
|
|
|
private SpeechSynthesizerListener getSynthesizerListener(String filePath) {
|
SpeechSynthesizerListener listener = null;
|
try {
|
if (StringUtils.isEmpty(filePath)) {
|
filePath = "tts_test.wav";
|
}
|
String finalFilePath = filePath;
|
listener = new SpeechSynthesizerListener() {
|
File f = new File(finalFilePath);
|
FileOutputStream fout = new FileOutputStream(f);
|
private boolean firstRecvBinary = true;
|
|
//语音合成结束
|
@Override
|
public void onComplete(SpeechSynthesizerResponse response) {
|
//调用onComplete时表示所有TTS数据已接收完成,因此为整个合成数据的延迟。该延迟可能较大,不一定满足实时场景。
|
System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", output file :" + f.getAbsolutePath());
|
}
|
|
//语音合成的语音二进制数据
|
@Override
|
public void onMessage(ByteBuffer message) {
|
try {
|
if (firstRecvBinary) {
|
//计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)。
|
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);
|
fout.write(bytesArray);
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
|
@Override
|
public void onFail(SpeechSynthesizerResponse response) {
|
//task_id是调用方和服务端通信的唯一标识,当遇到问题时需要提供task_id以便排查。
|
System.out.println("task_id: " + response.getTaskId() +
|
//状态码 20000000 表示识别成功
|
", status: " + response.getStatus() +
|
//错误信息
|
", status_text: " + response.getStatusText());
|
}
|
};
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return listener;
|
}
|
|
|
public void process2(String text, String filePath) {
|
SpeechSynthesizer synthesizer = null;
|
try {
|
//创建实例,建立连接。
|
synthesizer = new SpeechSynthesizer(client, getSynthesizerListener(filePath));
|
synthesizer.setAppKey(appKey);
|
//设置返回音频的编码格式
|
synthesizer.setFormat(OutputFormatEnum.WAV);
|
//设置返回音频的采样率
|
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
|
//发音人
|
synthesizer.setVoice("siyue");
|
//语调,范围是-500~500,可选,默认是0。
|
synthesizer.setPitchRate(100);
|
//语速,范围是-500~500,默认是0。
|
synthesizer.setSpeechRate(100);
|
//设置用于语音合成的文本
|
synthesizer.setText(text);
|
// 是否开启字幕功能(返回相应文本的时间戳),默认不开启,需要注意并非所有发音人都支持该参数。
|
synthesizer.addCustomedParam("enable_subtitle", false);
|
//此方法将以上参数设置序列化为JSON格式发送给服务端,并等待服务端确认。
|
long start = System.currentTimeMillis();
|
synthesizer.start();
|
// logger.info("tts start latency " + (System.currentTimeMillis() - start) + " ms");
|
// SpeechSynthesizerDemo.startTime = System.currentTimeMillis();
|
//等待语音合成结束
|
synthesizer.waitForComplete();
|
log.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms");
|
} catch (Exception e) {
|
e.printStackTrace();
|
} finally {
|
//关闭连接
|
if (null != synthesizer) {
|
synthesizer.close();
|
}
|
}
|
}
|
}
|