liusheng
2025-03-12 4d7b0c1dc2f1a400005feb93a945a09b52e5d101
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
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();
            }
        }
    }
}