WebSocketHandler.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. package com.ym.mec.biz.handler;
  2. import be.tarsos.dsp.AudioDispatcher;
  3. import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
  4. import be.tarsos.dsp.pitch.PitchProcessor;
  5. import be.tarsos.dsp.util.PitchConverter;
  6. import com.alibaba.fastjson.JSON;
  7. import com.alibaba.fastjson.JSONObject;
  8. import com.ym.mec.biz.dal.config.SoundCompareConfig;
  9. import com.ym.mec.biz.dal.dto.*;
  10. import com.ym.mec.biz.service.SoundSocketService;
  11. import com.ym.mec.biz.service.SysMusicCompareRecordService;
  12. import com.ym.mec.common.constant.CommonConstants;
  13. import org.apache.commons.io.FileUtils;
  14. import org.slf4j.Logger;
  15. import org.slf4j.LoggerFactory;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.stereotype.Service;
  18. import org.springframework.util.CollectionUtils;
  19. import org.springframework.web.socket.*;
  20. import org.springframework.web.socket.handler.AbstractWebSocketHandler;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.io.RandomAccessFile;
  24. import java.math.BigDecimal;
  25. import java.time.LocalDateTime;
  26. import java.time.format.DateTimeFormatter;
  27. import java.util.*;
  28. import java.util.concurrent.ConcurrentHashMap;
  29. import java.util.stream.Collectors;
  30. /**
  31. * @Author Joburgess
  32. * @Date 2021/6/9 0009
  33. */
  34. @Service
  35. public class WebSocketHandler extends AbstractWebSocketHandler {
  36. private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketHandler.class);
  37. /**
  38. * @describe 存储客户端链接
  39. */
  40. public static final Map<String, WebSocketClientDetail> WS_CLIENTS = new ConcurrentHashMap<>();
  41. private BigDecimal oneHundred = new BigDecimal(100);
  42. private final String tmpDir = FileUtils.getTempDirectoryPath() + "/soundCompare/";
  43. /**
  44. * @describe 用户对应评分信息
  45. */
  46. private Map<String, SoundCompareHelper> userSoundInfoMap = new ConcurrentHashMap<>();
  47. /**
  48. * @describe 音频处理参数
  49. */
  50. private SoundCompareConfig soundCompareConfig = new SoundCompareConfig();
  51. @Autowired
  52. private SysMusicCompareRecordService sysMusicCompareRecordService;
  53. public WebSocketHandler() {
  54. super();
  55. File soundDir = new File(tmpDir);
  56. if(!soundDir.exists()){
  57. soundDir.mkdir();
  58. }
  59. }
  60. @Override
  61. public void afterConnectionEstablished(WebSocketSession session) throws Exception {
  62. String phone = session.getPrincipal().getName().split(":")[1];
  63. LOGGER.info("{}上线", phone);
  64. WS_CLIENTS.put(phone, new WebSocketClientDetail(session, new Date()));
  65. super.afterConnectionEstablished(session);
  66. }
  67. @Override
  68. public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
  69. super.handleMessage(session, message);
  70. }
  71. @Override
  72. protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
  73. String phone = session.getPrincipal().getName().split(":")[1];
  74. LOGGER.info("{}: {}", phone, message.getPayload());
  75. WebSocketInfo webSocketInfo = JSON.parseObject(message.getPayload(), WebSocketInfo.class);
  76. JSONObject bodyObject = (JSONObject) webSocketInfo.getBody();
  77. String commond = "";
  78. if(webSocketInfo.getHeader().containsKey(SoundSocketService.COMMOND)){
  79. commond = webSocketInfo.getHeader().get(SoundSocketService.COMMOND);
  80. }
  81. switch (commond){
  82. case SoundSocketService.MUSIC_XML:
  83. userSoundInfoMap.put(phone, new SoundCompareHelper());
  84. List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
  85. userSoundInfoMap.get(phone).setMusicXmlInfos(musicXmlInfos);
  86. musicXmlInfos = musicXmlInfos.stream().filter(m->!m.getDontEvaluating()).collect(Collectors.toList());
  87. userSoundInfoMap.get(phone).setMusicScoreId(bodyObject.getInteger("id"));
  88. userSoundInfoMap.get(phone).setMeasureXmlInfoMap(musicXmlInfos.stream().collect(Collectors.groupingBy(MusicPitchDetailDto::getMeasureIndex)));
  89. musicXmlInfos.forEach(e->userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(e.getMusicalNotesIndex(), e.getFrequency()));
  90. for (Map.Entry<Integer, List<MusicPitchDetailDto>> userMeasureXmlInfoEntry : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().entrySet()) {
  91. MusicPitchDetailDto firstPitch = userMeasureXmlInfoEntry.getValue().stream().min(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
  92. MusicPitchDetailDto lastPitch = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
  93. long dc = userMeasureXmlInfoEntry.getValue().stream().filter(m -> m.getDontEvaluating()).count();
  94. MusicPitchDetailDto musicPitchDetailDto = new MusicPitchDetailDto(firstPitch.getTimeStamp(), lastPitch.getTimeStamp() + lastPitch.getDuration());
  95. musicPitchDetailDto.setDuration(musicPitchDetailDto.getEndTimeStamp()-musicPitchDetailDto.getTimeStamp());
  96. musicPitchDetailDto.setDontEvaluating(dc == userMeasureXmlInfoEntry.getValue().size());
  97. userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto);
  98. }
  99. break;
  100. case SoundSocketService.RECORD_START:
  101. if(!userSoundInfoMap.containsKey(phone)){
  102. break;
  103. }
  104. File file = new File(tmpDir+phone + "_"+ userSoundInfoMap.get(phone).getMusicScoreId() +"_"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) +".wav");
  105. userSoundInfoMap.get(phone).setAccessFile(new RandomAccessFile(file, "rw"));
  106. userSoundInfoMap.get(phone).setRecordFilePath(file.getAbsolutePath());
  107. break;
  108. case SoundSocketService.RECORD_END:
  109. if(!userSoundInfoMap.containsKey(phone)){
  110. break;
  111. }
  112. if(!CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getMeasureEndTime())){
  113. Integer lastMeasureIndex = userSoundInfoMap.get(phone).getMeasureEndTime().keySet().stream().min(Integer::compareTo).get();
  114. double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
  115. //如果结束时时长大于某小节,则此小节需要评分
  116. if(recordTime>userSoundInfoMap.get(phone).getMeasureEndTime().get(lastMeasureIndex).getEndTimeStamp()){
  117. measureCompare(phone, lastMeasureIndex);
  118. userSoundInfoMap.get(phone).getMeasureEndTime().remove(lastMeasureIndex);
  119. }
  120. }
  121. calTotalScore(phone);
  122. createHeader(phone);
  123. break;
  124. case SoundSocketService.RECORD_CANCEL:
  125. createHeader(phone);
  126. break;
  127. case SoundSocketService.PROXY_MESSAGE:
  128. // if(bodyObject.containsKey(SoundSocketService.OFFSET_TIME)){
  129. // int offsetTime = bodyObject.getIntValue(SoundSocketService.OFFSET_TIME);
  130. // calOffsetTime(phone, offsetTime);
  131. // }
  132. break;
  133. default:
  134. break;
  135. }
  136. }
  137. @Override
  138. protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
  139. String phone = session.getPrincipal().getName().split(":")[1];
  140. if(!userSoundInfoMap.containsKey(phone)){
  141. return;
  142. }
  143. if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
  144. userSoundInfoMap.get(phone).getAccessFile().write(message.getPayload().array());
  145. }
  146. AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), soundCompareConfig.audioFormat, soundCompareConfig.simpleSize, soundCompareConfig.overlap);
  147. dispatcher.addAudioProcessor(soundCompareConfig.silenceDetector);
  148. dispatcher.addAudioProcessor(new PitchProcessor(soundCompareConfig.algo, soundCompareConfig.simpleRate, soundCompareConfig.simpleSize, (pitchDetectionResult, audioEvent) -> {
  149. int timeStamp = (int) (userSoundInfoMap.get(phone).getMeasureStartTime() + audioEvent.getTimeStamp()*1000);
  150. float pitch = pitchDetectionResult.getPitch();
  151. if(pitch>0 && userSoundInfoMap.get(phone).getOffsetTime() == -1){
  152. int preTimeStamp = CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getRecordMeasurePithInfo())?0:userSoundInfoMap.get(phone).getRecordMeasurePithInfo().get(userSoundInfoMap.get(phone).getRecordMeasurePithInfo().size()-1).getTimeStamp();
  153. calOffsetTime(phone, timeStamp - (timeStamp - preTimeStamp)/2);
  154. }
  155. if(soundCompareConfig.silenceDetector.currentSPL()<soundCompareConfig.validDb){
  156. pitch = -1;
  157. }
  158. // LOGGER.info("时间:{}, 频率:{}, 分贝:{}", timeStamp, pitch, silenceDetecor.currentSPL());
  159. userSoundInfoMap.get(phone).getRecordMeasurePithInfo().add(new MusicPitchDetailDto(timeStamp, pitch, soundCompareConfig.silenceDetector.currentSPL()));
  160. }));
  161. dispatcher.run();
  162. if(Objects.isNull(userSoundInfoMap.get(phone).getAccessFile())){
  163. return;
  164. }
  165. double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
  166. userSoundInfoMap.get(phone).setMeasureStartTime(recordTime);
  167. for (Map.Entry<Integer, MusicPitchDetailDto> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
  168. if(recordTime>(userMeasureEndTimeMapEntry.getValue().getEndTimeStamp())){
  169. if(userMeasureEndTimeMapEntry.getValue().getDontEvaluating()){
  170. continue;
  171. }else{
  172. measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
  173. }
  174. userSoundInfoMap.get(phone).getMeasureEndTime().remove(userMeasureEndTimeMapEntry.getKey());
  175. break;
  176. }
  177. }
  178. }
  179. @Override
  180. protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
  181. super.handlePongMessage(session, message);
  182. LOGGER.info("心跳信息:{}", new String(message.getPayload().array(), "utf-8"));
  183. }
  184. @Override
  185. public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
  186. String phone = session.getPrincipal().getName().split(":")[1];
  187. session.close();
  188. if(!WS_CLIENTS.containsKey(phone)){
  189. return;
  190. }
  191. exception.printStackTrace();
  192. LOGGER.info("发生了错误,移除客户端: {}", phone);
  193. WS_CLIENTS.remove(phone);
  194. userSoundInfoMap.remove(phone);
  195. createHeader(phone);
  196. }
  197. @Override
  198. public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
  199. super.afterConnectionClosed(session, status);
  200. String phone = session.getPrincipal().getName().split(":")[1];
  201. LOGGER.info("{}离线", phone);
  202. createHeader(phone);
  203. WS_CLIENTS.remove(phone);
  204. userSoundInfoMap.remove(phone);
  205. }
  206. @Override
  207. public boolean supportsPartialMessages() {
  208. return super.supportsPartialMessages();
  209. }
  210. /**
  211. * @describe 处理时间偏移
  212. * @author Joburgess
  213. * @date 2021/7/5 0005
  214. * @param phone:
  215. * @param offsetTime:
  216. * @return void
  217. */
  218. private void calOffsetTime(String phone, int offsetTime){
  219. userSoundInfoMap.get(phone).setOffsetTime(offsetTime);
  220. for (Map.Entry<Integer, MusicPitchDetailDto> musicPitchDetailDtoEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
  221. musicPitchDetailDtoEntry.getValue().setTimeStamp(musicPitchDetailDtoEntry.getValue().getTimeStamp() + offsetTime);
  222. musicPitchDetailDtoEntry.getValue().setEndTimeStamp(musicPitchDetailDtoEntry.getValue().getEndTimeStamp() + offsetTime);
  223. }
  224. }
  225. /**
  226. * @describe 保存录音数据,并生成wav头信息
  227. * @author Joburgess
  228. * @date 2021/6/25 0025
  229. * @param phone:
  230. * @return void
  231. */
  232. private void createHeader(String phone) throws IOException {
  233. if(!userSoundInfoMap.containsKey(phone)){
  234. return;
  235. }
  236. if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
  237. RandomAccessFile randomAccessFile = userSoundInfoMap.get(phone).getAccessFile();
  238. LOGGER.info("音频时长:{}", randomAccessFile.length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000);
  239. randomAccessFile.seek(0);
  240. randomAccessFile.write(WavHeader.getWaveHeader(randomAccessFile.length(), (long) soundCompareConfig.audioFormat.getFrameRate(), soundCompareConfig.audioFormat.getSampleSizeInBits()));
  241. randomAccessFile.close();
  242. userSoundInfoMap.get(phone).setAccessFile(null);
  243. }
  244. // userSoundInfoMap.get(phone).setRecordMeasurePithInfo(null);
  245. userSoundInfoMap.remove(phone);
  246. }
  247. /**
  248. * @describe 数据比对,生成分数
  249. * @author Joburgess
  250. * @date 2021/6/25 0025
  251. * @param phone:
  252. * @param measureIndex:
  253. * @return void
  254. */
  255. private void measureCompare(String phone, int measureIndex) throws IOException {
  256. if (userSoundInfoMap.get(phone).getOffsetTime() == -1){
  257. userSoundInfoMap.get(phone).setOffsetTime(0);
  258. }
  259. //相似度
  260. BigDecimal intonation = BigDecimal.ZERO;
  261. //节奏
  262. BigDecimal cadence = BigDecimal.ZERO;
  263. //完整度
  264. BigDecimal integrity = BigDecimal.ZERO;
  265. try {
  266. //音准分数
  267. float intonationScore = 0;
  268. //节奏匹配数量
  269. float cadenceNum = 0;
  270. //完整性分数
  271. float integrityScore = 0;
  272. int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size();
  273. for (int i = 0; i < userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size(); i++) {
  274. MusicPitchDetailDto musicXmlInfo = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).get(i);
  275. int ot5 = (int) (musicXmlInfo.getDuration()*0.1);
  276. int startTimeStamp = musicXmlInfo.getTimeStamp() + userSoundInfoMap.get(phone).getOffsetTime() + ot5;
  277. int endTimeStamp = musicXmlInfo.getTimeStamp() + userSoundInfoMap.get(phone).getOffsetTime() + musicXmlInfo.getDuration() - ot5;
  278. //时间范围内有效节奏数量
  279. float cadenceValidNum = 0;
  280. //时间范围内有效音频数量
  281. float integrityValidNum = 0;
  282. //时间范围内匹配次数
  283. float compareNum = 0;
  284. List<MusicPitchDetailDto> measureSoundPitchInfos = new ArrayList<>();
  285. for (int j = 0; j < userSoundInfoMap.get(phone).getRecordMeasurePithInfo().size(); j++) {
  286. MusicPitchDetailDto recordInfo = userSoundInfoMap.get(phone).getRecordMeasurePithInfo().get(j);
  287. //如果在时间范围之外直接跳过
  288. if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
  289. continue;
  290. }
  291. measureSoundPitchInfos.add(recordInfo);
  292. compareNum++;
  293. //如果在最低有效频率以下则跳过
  294. if(recordInfo.getFrequency()<soundCompareConfig.validFrequency&&musicXmlInfo.getFrequency()!=-1){
  295. continue;
  296. }
  297. cadenceValidNum++;
  298. //如果频率差值在节奏误差范围内
  299. if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=soundCompareConfig.integrityFrequencyRange){
  300. integrityValidNum++;
  301. }
  302. }
  303. //非正常频率次数
  304. int errPitchNum = 0;
  305. //分贝变化次数
  306. int decibelChangeNum = 0;
  307. if(CollectionUtils.isEmpty(measureSoundPitchInfos)){
  308. userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) 0);
  309. }else{
  310. Map<Integer, Long> collect = measureSoundPitchInfos.stream().map(pitch -> (int)pitch.getFrequency()).collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()));
  311. //出现次数最多的频率
  312. Integer pitch = collect.entrySet().stream().max(Comparator.comparing(e -> e.getValue())).get().getKey();
  313. //当前频率
  314. double cf = -1;
  315. //频率持续数量
  316. int fnum = 0;
  317. //是否演奏中
  318. boolean ing = false;
  319. //当前分贝
  320. double cd = 0;
  321. //分贝变化方向,-1变小,1变大
  322. int dcd = -1;
  323. //分贝持续数量
  324. int dnum = 0;
  325. for (MusicPitchDetailDto musicalNotesPitch : measureSoundPitchInfos) {
  326. //计算频率断层次数
  327. if (Math.abs(musicalNotesPitch.getFrequency() - cf) > 20){
  328. fnum ++;
  329. }
  330. if (fnum>=5){
  331. cf = musicalNotesPitch.getFrequency();
  332. fnum = 0;
  333. if (cf != -1){
  334. errPitchNum ++;
  335. ing = true;
  336. cd = musicalNotesPitch.getDecibel();
  337. }
  338. }
  339. //计算声音大小断层册数
  340. if(ing && Math.abs(musicalNotesPitch.getDecibel() - cd) > 10){
  341. dnum ++;
  342. }
  343. if (dnum > 2){
  344. int tdcd = cd > musicalNotesPitch.getDecibel() ? -1 : 1;
  345. cd = musicalNotesPitch.getDecibel();
  346. dnum = 0;
  347. if (tdcd != dcd) {
  348. decibelChangeNum++;
  349. }
  350. dcd = tdcd;
  351. }
  352. }
  353. userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) pitch);
  354. }
  355. //有效节奏占比
  356. float cadenceDuty = cadenceValidNum/compareNum;
  357. //如果频率出现断层或这个音量出现断层,则当前音符节奏无效
  358. if(errPitchNum>=2 || decibelChangeNum>1){
  359. cadenceDuty = 0;
  360. }
  361. //节奏
  362. if(cadenceDuty>=soundCompareConfig.cadenceValidDuty){
  363. cadenceNum++;
  364. }
  365. //音准
  366. if (!CollectionUtils.isEmpty(measureSoundPitchInfos)){
  367. Double avgPitch = measureSoundPitchInfos.stream().filter(pitch -> Math.abs((pitch.getFrequency()-musicXmlInfo.getFrequency()))<5).collect(Collectors.averagingDouble(pitch -> pitch.getFrequency()));
  368. //音分
  369. double recordCents = 0;
  370. if (avgPitch > 0){
  371. recordCents = PitchConverter.hertzToAbsoluteCent(avgPitch);
  372. }
  373. double cents = PitchConverter.hertzToAbsoluteCent(musicXmlInfo.getFrequency());
  374. double score = 100 - Math.round(Math.abs(cents - recordCents)) + soundCompareConfig.intonationCentsRange;
  375. if (score < 0){
  376. score = 0;
  377. }else if(score > 100){
  378. score = 100;
  379. }
  380. intonationScore += score;
  381. musicXmlInfo.setAvgFrequency(avgPitch.floatValue());
  382. }
  383. //完成度
  384. if(integrityValidNum>0){
  385. integrityValidNum = integrityValidNum;
  386. }
  387. if(integrityValidNum > compareNum){
  388. integrityValidNum = compareNum;
  389. }
  390. float integrityDuty = integrityValidNum/compareNum;
  391. integrityScore += integrityDuty;
  392. }
  393. BigDecimal measureNum = new BigDecimal(totalCompareNum);
  394. intonation = new BigDecimal(intonationScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
  395. cadence = new BigDecimal(cadenceNum).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
  396. integrity = new BigDecimal(integrityScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
  397. } catch (ArithmeticException e){
  398. LOGGER.info("无musicXml信息");
  399. }
  400. if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("intonation")){
  401. userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation.add(userSoundInfoMap.get(phone).getUserScoreMap().get("intonation")));
  402. }else{
  403. userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation);
  404. }
  405. if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("cadence")){
  406. userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence.add(userSoundInfoMap.get(phone).getUserScoreMap().get("cadence")));
  407. }else{
  408. userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence);
  409. }
  410. if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("integrity")){
  411. userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity.add(userSoundInfoMap.get(phone).getUserScoreMap().get("integrity")));
  412. }else{
  413. userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity);
  414. }
  415. //计算分数并推送
  416. createPushInfo(phone, "measureScore", measureIndex, intonation, cadence, integrity);
  417. }
  418. /**
  419. * @describe 计算最终评分
  420. * @author Joburgess
  421. * @date 2021/6/25 0025
  422. * @param phone:
  423. * @return void
  424. */
  425. private void calTotalScore(String phone) throws IOException {
  426. int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().keySet().size();
  427. int currentCompareNum = totalCompareNum-userSoundInfoMap.get(phone).getMeasureEndTime().keySet().size();
  428. BigDecimal intonation = BigDecimal.ZERO;
  429. BigDecimal cadence = BigDecimal.ZERO;
  430. BigDecimal integrity = BigDecimal.ZERO;
  431. if(currentCompareNum>0){
  432. intonation = userSoundInfoMap.get(phone).getUserScoreMap().get("intonation").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
  433. cadence = userSoundInfoMap.get(phone).getUserScoreMap().get("cadence").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
  434. integrity = userSoundInfoMap.get(phone).getUserScoreMap().get("integrity").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
  435. }
  436. //计算分数并推送
  437. createPushInfo(phone, "overall", -1, intonation, cadence, integrity);
  438. //存储评分数据
  439. sysMusicCompareRecordService.saveMusicCompareData(phone, userSoundInfoMap.get(phone));
  440. LOGGER.info("评分数据:{}", JSON.toJSONString(userSoundInfoMap.get(phone)));
  441. }
  442. /**
  443. * @describe 生成评分结果
  444. * @author Joburgess
  445. * @date 2021/6/25 0025
  446. * @param command:
  447. * @param measureIndex:
  448. * @param intonation:
  449. * @param cadence:
  450. * @param integrity:
  451. * @return com.ym.mec.biz.dal.dto.WebSocketInfo
  452. */
  453. private WebSocketInfo createPushInfo(String phone, String command, Integer measureIndex,
  454. BigDecimal intonation, BigDecimal cadence, BigDecimal integrity) throws IOException {
  455. WebSocketInfo webSocketInfo = new WebSocketInfo();
  456. HashMap<String, String> header = new HashMap<>();
  457. header.put("commond", command);
  458. webSocketInfo.setHeader(header);
  459. Map<String, Object> result = new HashMap<>();
  460. // BigDecimal score = intonation.multiply(new BigDecimal(0.5)).add(cadence.multiply(new BigDecimal(0.5))).setScale(0, BigDecimal.ROUND_HALF_UP);
  461. BigDecimal score = intonation.add(cadence).add(integrity).divide(new BigDecimal(3), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
  462. // BigDecimal score = integrity.setScale(0, BigDecimal.ROUND_HALF_UP);
  463. result.put("score", score);
  464. result.put("intonation", intonation);
  465. result.put("cadence", cadence);
  466. result.put("integrity", integrity);
  467. result.put("measureIndex", measureIndex);
  468. webSocketInfo.setBody(result);
  469. userSoundInfoMap.get(phone).getUserMeasureScoreMap().put(measureIndex, result);
  470. LOGGER.info("小节频分:{}", JSON.toJSONString(webSocketInfo));
  471. //推送结果
  472. WS_CLIENTS.get(phone).getSession().sendMessage(new TextMessage(JSON.toJSONString(webSocketInfo)));
  473. return webSocketInfo;
  474. }
  475. }