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(); } } } }