瀏覽代碼

feat:小节评分重构

Joburgess 4 年之前
父節點
當前提交
8515f5376f

+ 5 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/config/NioAudioInputStream.java

@@ -2,6 +2,7 @@ package com.ym.mec.biz.dal.config;
 
 import be.tarsos.dsp.io.TarsosDSPAudioFormat;
 import be.tarsos.dsp.io.TarsosDSPAudioInputStream;
+import org.springframework.stereotype.Component;
 
 import javax.sound.sampled.AudioFormat;
 import java.io.IOException;
@@ -13,9 +14,12 @@ import java.io.RandomAccessFile;
  */
 public class NioAudioInputStream implements TarsosDSPAudioInputStream {
 
-    private final RandomAccessFile randomAccessFile;
+    private RandomAccessFile randomAccessFile;
     private AudioFormat format;
 
+    public NioAudioInputStream() {
+    }
+
     public NioAudioInputStream(RandomAccessFile randomAccessFile, AudioFormat audioFormat) {
         this.randomAccessFile = randomAccessFile;
         this.format = audioFormat;

+ 2 - 2
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/SoundCompareHelper.java

@@ -4,7 +4,7 @@ import be.tarsos.dsp.AudioEvent;
 import be.tarsos.dsp.SilenceDetector;
 import be.tarsos.dsp.pitch.PitchDetectionHandler;
 import be.tarsos.dsp.pitch.PitchDetectionResult;
-import com.ym.mec.biz.handler.WebSocketHandler;
+import com.ym.mec.biz.service.impl.SoundCompareHandler;
 import io.swagger.annotations.ApiModelProperty;
 import org.springframework.util.CollectionUtils;
 
@@ -177,7 +177,7 @@ public class SoundCompareHelper implements PitchDetectionHandler {
                 musicPitchDetailDtoEntry.getValue().setEndTimeStamp(musicPitchDetailDtoEntry.getValue().getEndTimeStamp() + offsetTime);
             }
         }
-        if(silenceDetector.currentSPL()< WebSocketHandler.soundCompareConfig.validDb){
+        if(silenceDetector.currentSPL()< SoundCompareHandler.soundCompareConfig.validDb){
             pitch = -1;
         }
 //            LOGGER.info("时间:{}, 频率:{}, 分贝:{}", timeStamp, pitch, silenceDetecor.currentSPL());

+ 34 - 433
mec-biz/src/main/java/com/ym/mec/biz/handler/WebSocketHandler.java

@@ -1,34 +1,20 @@
 package com.ym.mec.biz.handler;
 
-import be.tarsos.dsp.AudioDispatcher;
-import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
-import be.tarsos.dsp.pitch.PitchProcessor;
-import be.tarsos.dsp.util.PitchConverter;
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.ym.mec.biz.dal.config.SoundCompareConfig;
-import com.ym.mec.biz.dal.dto.*;
+import com.ym.mec.biz.dal.dto.WebSocketClientDetail;
+import com.ym.mec.biz.dal.dto.WebSocketInfo;
 import com.ym.mec.biz.service.SoundSocketService;
-import com.ym.mec.biz.service.SysMusicCompareRecordService;
-import com.ym.mec.common.constant.CommonConstants;
-import org.apache.commons.io.FileUtils;
+import com.ym.mec.biz.service.WebSocketEventHandler;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
 import org.springframework.web.socket.*;
 import org.springframework.web.socket.handler.AbstractWebSocketHandler;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
+import java.util.Date;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
 
 /**
  * @Author Joburgess
@@ -37,35 +23,29 @@ import java.util.stream.Collectors;
 @Service
 public class WebSocketHandler extends AbstractWebSocketHandler {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketHandler.class);
+    private final Logger LOGGER = LoggerFactory.getLogger(WebSocketHandler.class);
 
     /**
      * @describe 存储客户端链接
      */
     public static final Map<String, WebSocketClientDetail> WS_CLIENTS = new ConcurrentHashMap<>();
 
-    private BigDecimal oneHundred = new BigDecimal(100);
+    private static Map<String, WebSocketEventHandler> appMap = new ConcurrentHashMap<>();
 
-    private final String tmpDir = FileUtils.getTempDirectoryPath() + "/soundCompare/";
-
-    /**
-     * @describe 用户对应评分信息
-     */
-    private Map<String, SoundCompareHelper> userSoundInfoMap = new ConcurrentHashMap<>();
     /**
-     * @describe 音频处理参数
+     * @describe 注册应用
+     * @author Joburgess
+     * @date 2021/8/5 0005
+     * @param tag:
+     * @param webSocketEventHandler:
+     * @return boolean
      */
-    public static final SoundCompareConfig soundCompareConfig = new SoundCompareConfig();
-
-    @Autowired
-    private SysMusicCompareRecordService sysMusicCompareRecordService;
-
-    public WebSocketHandler() {
-        super();
-        File soundDir = new File(tmpDir);
-        if(!soundDir.exists()){
-            soundDir.mkdir();
+    public static boolean regist(String tag, WebSocketEventHandler webSocketEventHandler){
+        if (appMap.containsKey(tag)){
+            return false;
         }
+        appMap.put(tag, webSocketEventHandler);
+        return true;
     }
 
     @Override
@@ -73,6 +53,7 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
         String phone = session.getPrincipal().getName().split(":")[1];
         LOGGER.info("{}上线", phone);
         WS_CLIENTS.put(phone, new WebSocketClientDetail(session, new Date()));
+        appMap.values().forEach(e->e.afterConnectionEstablished(session));
         super.afterConnectionEstablished(session);
     }
 
@@ -82,105 +63,24 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
     }
 
     @Override
-    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+    protected void handleTextMessage(WebSocketSession session, TextMessage message){
         String phone = session.getPrincipal().getName().split(":")[1];
         LOGGER.info("{}: {}", phone, message.getPayload());
         WebSocketInfo webSocketInfo = JSON.parseObject(message.getPayload(), WebSocketInfo.class);
-        JSONObject bodyObject = (JSONObject) webSocketInfo.getBody();
-
-        String commond = "";
-        if(webSocketInfo.getHeader().containsKey(SoundSocketService.COMMOND)){
-            commond = webSocketInfo.getHeader().get(SoundSocketService.COMMOND);
+        String tag = "";
+        if(webSocketInfo.getHeader().containsKey(SoundSocketService.TAG)){
+            tag = webSocketInfo.getHeader().get(SoundSocketService.TAG);
         }
-        switch (commond){
-            case SoundSocketService.MUSIC_XML:
-                userSoundInfoMap.put(phone, new SoundCompareHelper());
-                List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
-                userSoundInfoMap.get(phone).setMusicXmlInfos(musicXmlInfos);
-                musicXmlInfos = musicXmlInfos.stream().filter(m->!m.getDontEvaluating()).collect(Collectors.toList());
-                userSoundInfoMap.get(phone).setMusicScoreId(bodyObject.getInteger("id"));
-                userSoundInfoMap.get(phone).setMeasureXmlInfoMap(musicXmlInfos.stream().collect(Collectors.groupingBy(MusicPitchDetailDto::getMeasureIndex)));
-                musicXmlInfos.forEach(e->userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(e.getMusicalNotesIndex(), e.getFrequency()));
-                for (Map.Entry<Integer, List<MusicPitchDetailDto>> userMeasureXmlInfoEntry : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().entrySet()) {
-                    MusicPitchDetailDto firstPitch = userMeasureXmlInfoEntry.getValue().stream().min(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
-                    MusicPitchDetailDto lastPitch = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
-                    long dc = userMeasureXmlInfoEntry.getValue().stream().filter(m -> m.getDontEvaluating()).count();
-                    MusicPitchDetailDto musicPitchDetailDto = new MusicPitchDetailDto(firstPitch.getTimeStamp(), lastPitch.getTimeStamp() + lastPitch.getDuration());
-                    musicPitchDetailDto.setDuration(musicPitchDetailDto.getEndTimeStamp()-musicPitchDetailDto.getTimeStamp());
-                    musicPitchDetailDto.setDontEvaluating(dc == userMeasureXmlInfoEntry.getValue().size());
-                    userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto);
-                }
-
-                break;
-            case SoundSocketService.RECORD_START:
-                if(!userSoundInfoMap.containsKey(phone)){
-                    break;
-                }
-                File file = new File(tmpDir+phone + "_"+ userSoundInfoMap.get(phone).getMusicScoreId() +"_"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) +".wav");
-                userSoundInfoMap.get(phone).setAccessFile(new RandomAccessFile(file, "rw"));
-                userSoundInfoMap.get(phone).setRecordFilePath(file.getAbsolutePath());
-                break;
-            case SoundSocketService.RECORD_END:
-                if(!userSoundInfoMap.containsKey(phone)){
-                    break;
-                }
-                if(!CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getMeasureEndTime())){
-                    Integer lastMeasureIndex = userSoundInfoMap.get(phone).getMeasureEndTime().keySet().stream().min(Integer::compareTo).get();
-                    double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
-                    //如果结束时时长大于某小节,则此小节需要评分
-                    if(recordTime>userSoundInfoMap.get(phone).getMeasureEndTime().get(lastMeasureIndex).getEndTimeStamp()){
-                        measureCompare(phone, lastMeasureIndex);
-                        userSoundInfoMap.get(phone).getMeasureEndTime().remove(lastMeasureIndex);
-                    }
-                }
-                calTotalScore(phone);
-                createHeader(phone);
-                break;
-            case SoundSocketService.RECORD_CANCEL:
-                createHeader(phone);
-                break;
-            case SoundSocketService.PROXY_MESSAGE:
-//                if(bodyObject.containsKey(SoundSocketService.OFFSET_TIME)){
-//                    int offsetTime = bodyObject.getIntValue(SoundSocketService.OFFSET_TIME);
-//                    calOffsetTime(phone, offsetTime);
-//                }
-                break;
-            default:
-                break;
+        if(StringUtils.isNotBlank(tag)){
+            appMap.get(tag).receiveTextMessage(session, message);
+        }else{
+            appMap.values().forEach(e->e.receiveTextMessage(session, message));
         }
     }
 
     @Override
-    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
-        String phone = session.getPrincipal().getName().split(":")[1];
-        if(!userSoundInfoMap.containsKey(phone)){
-            return;
-        }
-        if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
-            userSoundInfoMap.get(phone).getAccessFile().write(message.getPayload().array());
-        }
-
-        AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), soundCompareConfig.audioFormat, soundCompareConfig.simpleSize, soundCompareConfig.overlap);
-        dispatcher.addAudioProcessor(userSoundInfoMap.get(phone).silenceDetector);
-        dispatcher.addAudioProcessor(new PitchProcessor(soundCompareConfig.algo, soundCompareConfig.simpleRate, soundCompareConfig.simpleSize, userSoundInfoMap.get(phone)));
-        dispatcher.run();
-        if(Objects.isNull(userSoundInfoMap.get(phone).getAccessFile())){
-            return;
-        }
-
-        double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
-        userSoundInfoMap.get(phone).setMeasureStartTime(recordTime);
-        for (Map.Entry<Integer, MusicPitchDetailDto> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
-            if(recordTime>(userMeasureEndTimeMapEntry.getValue().getEndTimeStamp())){
-                if(userMeasureEndTimeMapEntry.getValue().getDontEvaluating()){
-                    continue;
-                }else{
-                    measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
-                }
-                userSoundInfoMap.get(phone).getMeasureEndTime().remove(userMeasureEndTimeMapEntry.getKey());
-                break;
-            }
-        }
+    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
+        appMap.values().forEach(e->e.receiveBinaryMessage(session, message));
     }
 
     @Override
@@ -193,14 +93,13 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
         String phone = session.getPrincipal().getName().split(":")[1];
         session.close();
+        LOGGER.info("发生了错误,移除客户端: {}", phone);
+        appMap.values().forEach(e->e.afterConnectionClosed(session));
+        exception.printStackTrace();
         if(!WS_CLIENTS.containsKey(phone)){
             return;
         }
-        exception.printStackTrace();
-        LOGGER.info("发生了错误,移除客户端: {}", phone);
         WS_CLIENTS.remove(phone);
-        userSoundInfoMap.remove(phone);
-        createHeader(phone);
     }
 
     @Override
@@ -208,310 +107,12 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
         super.afterConnectionClosed(session, status);
         String phone = session.getPrincipal().getName().split(":")[1];
         LOGGER.info("{}离线", phone);
-        createHeader(phone);
+        appMap.values().forEach(e->e.afterConnectionClosed(session));
         WS_CLIENTS.remove(phone);
-        userSoundInfoMap.remove(phone);
     }
 
     @Override
     public boolean supportsPartialMessages() {
         return super.supportsPartialMessages();
     }
-
-    /**
-     * @describe 处理时间偏移
-     * @author Joburgess
-     * @date 2021/7/5 0005
-     * @param phone:
-     * @param offsetTime:
-     * @return void
-     */
-    private void calOffsetTime(String phone, int offsetTime){
-        userSoundInfoMap.get(phone).setOffsetTime(offsetTime);
-        for (Map.Entry<Integer, MusicPitchDetailDto> musicPitchDetailDtoEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
-            musicPitchDetailDtoEntry.getValue().setTimeStamp(musicPitchDetailDtoEntry.getValue().getTimeStamp() + offsetTime);
-            musicPitchDetailDtoEntry.getValue().setEndTimeStamp(musicPitchDetailDtoEntry.getValue().getEndTimeStamp() + offsetTime);
-        }
-    }
-
-    /**
-     * @describe 保存录音数据,并生成wav头信息
-     * @author Joburgess
-     * @date 2021/6/25 0025
-     * @param phone:
-     * @return void
-     */
-    private void createHeader(String phone) throws IOException {
-        if(!userSoundInfoMap.containsKey(phone)){
-            return;
-        }
-        if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
-            RandomAccessFile randomAccessFile = userSoundInfoMap.get(phone).getAccessFile();
-            LOGGER.info("音频时长:{}", randomAccessFile.length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000);
-            randomAccessFile.seek(0);
-            randomAccessFile.write(WavHeader.getWaveHeader(randomAccessFile.length(), (long) soundCompareConfig.audioFormat.getFrameRate(), soundCompareConfig.audioFormat.getSampleSizeInBits()));
-            randomAccessFile.close();
-            userSoundInfoMap.get(phone).setAccessFile(null);
-        }
-//        userSoundInfoMap.get(phone).setRecordMeasurePithInfo(null);
-        userSoundInfoMap.remove(phone);
-    }
-
-    /**
-     * @describe 数据比对,生成分数
-     * @author Joburgess
-     * @date 2021/6/25 0025
-     * @param phone:
-     * @param measureIndex:
-     * @return void
-     */
-    private void measureCompare(String phone, int measureIndex) throws IOException {
-        if (userSoundInfoMap.get(phone).getOffsetTime() == -1){
-            userSoundInfoMap.get(phone).setOffsetTime(0);
-        }
-
-        //相似度
-        BigDecimal intonation = BigDecimal.ZERO;
-        //节奏
-        BigDecimal cadence = BigDecimal.ZERO;
-        //完整度
-        BigDecimal integrity = BigDecimal.ZERO;
-
-        try {
-            //音准分数
-            float intonationScore = 0;
-
-            //节奏匹配数量
-            float cadenceNum = 0;
-
-            //完整性分数
-            float integrityScore = 0;
-
-            int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size();
-
-            for (int i = 0; i < userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size(); i++) {
-                MusicPitchDetailDto musicXmlInfo = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).get(i);
-
-                int ot5 = (int) (musicXmlInfo.getDuration()*0.1);
-                int startTimeStamp = musicXmlInfo.getTimeStamp() + userSoundInfoMap.get(phone).getOffsetTime() + ot5;
-                int endTimeStamp = musicXmlInfo.getTimeStamp()  + userSoundInfoMap.get(phone).getOffsetTime() + musicXmlInfo.getDuration() - ot5;
-
-                //时间范围内有效节奏数量
-                float cadenceValidNum = 0;
-                //时间范围内有效音频数量
-                float integrityValidNum = 0;
-                //时间范围内匹配次数
-                float compareNum = 0;
-
-                List<MusicPitchDetailDto> measureSoundPitchInfos = new ArrayList<>();
-
-                for (int j = 0; j < userSoundInfoMap.get(phone).getRecordMeasurePithInfo().size(); j++) {
-                    MusicPitchDetailDto recordInfo = userSoundInfoMap.get(phone).getRecordMeasurePithInfo().get(j);
-                    //如果在时间范围之外直接跳过
-                    if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
-                        continue;
-                    }
-                    measureSoundPitchInfos.add(recordInfo);
-                    compareNum++;
-                    //如果在最低有效频率以下则跳过
-                    if(recordInfo.getFrequency()<soundCompareConfig.validFrequency&&musicXmlInfo.getFrequency()!=-1){
-                        continue;
-                    }
-                    cadenceValidNum++;
-                    //如果频率差值在节奏误差范围内
-                    if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=soundCompareConfig.integrityFrequencyRange){
-                        integrityValidNum++;
-                    }
-                }
-
-                //非正常频率次数
-                int errPitchNum = 0;
-                //分贝变化次数
-                int decibelChangeNum = 0;
-
-                if(CollectionUtils.isEmpty(measureSoundPitchInfos)){
-                    userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) 0);
-                }else{
-                    Map<Integer, Long> collect = measureSoundPitchInfos.stream().map(pitch -> (int)pitch.getFrequency()).collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()));
-                    //出现次数最多的频率
-                    Integer pitch = collect.entrySet().stream().max(Comparator.comparing(e -> e.getValue())).get().getKey();
-                    //当前频率
-                    double cf = -1;
-                    //频率持续数量
-                    int fnum = 0;
-                    //是否演奏中
-                    boolean ing = false;
-                    //当前分贝
-                    double cd = 0;
-                    //分贝变化方向,-1变小,1变大
-                    int dcd = -1;
-                    //分贝持续数量
-                    int dnum = 0;
-                    for (MusicPitchDetailDto musicalNotesPitch : measureSoundPitchInfos) {
-                        //计算频率断层次数
-                        if (Math.abs(musicalNotesPitch.getFrequency() - cf) > 20){
-                            fnum ++;
-                        }
-                        if (fnum>=5){
-                            cf = musicalNotesPitch.getFrequency();
-                            fnum = 0;
-                            if (cf != -1){
-                                errPitchNum ++;
-                                ing = true;
-                                cd = musicalNotesPitch.getDecibel();
-                            }
-                        }
-                        //计算声音大小断层册数
-                        if(ing && Math.abs(musicalNotesPitch.getDecibel() - cd) > 10){
-                            dnum ++;
-                        }
-                        if (dnum > 2){
-                            int tdcd = cd > musicalNotesPitch.getDecibel() ? -1 : 1;
-                            cd = musicalNotesPitch.getDecibel();
-                            dnum = 0;
-                            if (tdcd != dcd) {
-                                decibelChangeNum++;
-                            }
-                            dcd = tdcd;
-                        }
-                    }
-                    userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) pitch);
-                }
-
-                //有效节奏占比
-                float cadenceDuty = cadenceValidNum/compareNum;
-                //如果频率出现断层或这个音量出现断层,则当前音符节奏无效
-                if(errPitchNum>=2 || decibelChangeNum>1){
-                    cadenceDuty = 0;
-                }
-                //节奏
-                if(cadenceDuty>=soundCompareConfig.cadenceValidDuty){
-                    cadenceNum++;
-                }
-                //音准
-                if (!CollectionUtils.isEmpty(measureSoundPitchInfos)){
-                    Double avgPitch = measureSoundPitchInfos.stream().filter(pitch -> Math.abs((pitch.getFrequency()-musicXmlInfo.getFrequency()))<5).collect(Collectors.averagingDouble(pitch -> pitch.getFrequency()));
-                    //音分
-                    double recordCents = 0;
-                    if (avgPitch > 0){
-                        recordCents = PitchConverter.hertzToAbsoluteCent(avgPitch);
-                    }
-                    double cents = PitchConverter.hertzToAbsoluteCent(musicXmlInfo.getFrequency());
-                    double score = 100 - Math.round(Math.abs(cents - recordCents)) + soundCompareConfig.intonationCentsRange;
-                    if (score < 0){
-                        score = 0;
-                    }else if(score > 100){
-                        score = 100;
-                    }
-                    intonationScore += score;
-                    musicXmlInfo.setAvgFrequency(avgPitch.floatValue());
-                }
-                //完成度
-                if(integrityValidNum>0){
-                    integrityValidNum = integrityValidNum;
-                }
-                if(integrityValidNum > compareNum){
-                    integrityValidNum = compareNum;
-                }
-                float integrityDuty = integrityValidNum/compareNum;
-                integrityScore += integrityDuty;
-            }
-
-            BigDecimal measureNum = new BigDecimal(totalCompareNum);
-
-            intonation = new BigDecimal(intonationScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
-            cadence = new BigDecimal(cadenceNum).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
-            integrity = new BigDecimal(integrityScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
-
-        } catch (ArithmeticException e){
-            LOGGER.info("无musicXml信息");
-        }
-
-        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("intonation")){
-            userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation.add(userSoundInfoMap.get(phone).getUserScoreMap().get("intonation")));
-        }else{
-            userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation);
-        }
-
-        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("cadence")){
-            userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence.add(userSoundInfoMap.get(phone).getUserScoreMap().get("cadence")));
-        }else{
-            userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence);
-        }
-
-        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("integrity")){
-            userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity.add(userSoundInfoMap.get(phone).getUserScoreMap().get("integrity")));
-        }else{
-            userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity);
-        }
-
-        //计算分数并推送
-        createPushInfo(phone, "measureScore", measureIndex, intonation, cadence, integrity);
-    }
-
-    /**
-     * @describe 计算最终评分
-     * @author Joburgess
-     * @date 2021/6/25 0025
-     * @param phone:
-     * @return void
-     */
-    private void calTotalScore(String phone) throws IOException {
-        int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().keySet().size();
-        int currentCompareNum = totalCompareNum-userSoundInfoMap.get(phone).getMeasureEndTime().keySet().size();
-        BigDecimal intonation = BigDecimal.ZERO;
-        BigDecimal cadence = BigDecimal.ZERO;
-        BigDecimal integrity = BigDecimal.ZERO;
-
-        if(currentCompareNum>0){
-            intonation = userSoundInfoMap.get(phone).getUserScoreMap().get("intonation").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
-            cadence = userSoundInfoMap.get(phone).getUserScoreMap().get("cadence").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
-            integrity = userSoundInfoMap.get(phone).getUserScoreMap().get("integrity").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
-        }
-
-        //计算分数并推送
-        createPushInfo(phone, "overall", -1, intonation, cadence, integrity);
-
-        //存储评分数据
-        sysMusicCompareRecordService.saveMusicCompareData(phone, userSoundInfoMap.get(phone));
-
-        LOGGER.info("评分数据:{}", JSON.toJSONString(userSoundInfoMap.get(phone)));
-    }
-
-    /**
-     * @describe 生成评分结果
-     * @author Joburgess
-     * @date 2021/6/25 0025
-     * @param command:
-     * @param measureIndex:
-     * @param intonation:
-     * @param cadence:
-     * @param integrity:
-     * @return com.ym.mec.biz.dal.dto.WebSocketInfo
-     */
-    private WebSocketInfo createPushInfo(String phone, String command, Integer measureIndex,
-                                         BigDecimal intonation, BigDecimal cadence, BigDecimal integrity) throws IOException {
-        WebSocketInfo webSocketInfo = new WebSocketInfo();
-        HashMap<String, String> header = new HashMap<>();
-        header.put("commond", command);
-        webSocketInfo.setHeader(header);
-        Map<String, Object> result = new HashMap<>();
-//        BigDecimal score = intonation.multiply(new BigDecimal(0.5)).add(cadence.multiply(new BigDecimal(0.5))).setScale(0, BigDecimal.ROUND_HALF_UP);
-        BigDecimal score = intonation.add(cadence).add(integrity).divide(new BigDecimal(3), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
-//        BigDecimal score = integrity.setScale(0, BigDecimal.ROUND_HALF_UP);
-        result.put("score", score);
-        result.put("intonation", intonation);
-        result.put("cadence", cadence);
-        result.put("integrity", integrity);
-        result.put("measureIndex", measureIndex);
-        webSocketInfo.setBody(result);
-
-        userSoundInfoMap.get(phone).getUserMeasureScoreMap().put(measureIndex, result);
-
-        LOGGER.info("小节频分:{}", JSON.toJSONString(webSocketInfo));
-
-        //推送结果
-        WS_CLIENTS.get(phone).getSession().sendMessage(new TextMessage(JSON.toJSONString(webSocketInfo)));
-        return webSocketInfo;
-    }
 }

+ 1 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/SoundSocketService.java

@@ -6,6 +6,7 @@ package com.ym.mec.biz.service;
  */
 public interface SoundSocketService {
 
+    String TAG = "TAG";
     String COMMOND = "commond";
     String MUSIC_XML = "musicXml";
     String RECORD_START = "recordStart";

+ 51 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/WebSocketEventHandler.java

@@ -0,0 +1,51 @@
+package com.ym.mec.biz.service;
+
+import org.springframework.web.socket.BinaryMessage;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+/**
+ * @Author Joburgess
+ * @Date 2021/8/5 0005
+ **/
+public interface WebSocketEventHandler {
+
+    /**
+     * @describe 连接成功
+     * @author Joburgess
+     * @date 2021/8/5 0005
+     * @param session: 会话信息
+     * @return void
+     */
+    void afterConnectionEstablished(WebSocketSession session);
+
+    /**
+     * @describe 接受到文本信息
+     * @author Joburgess
+     * @date 2021/8/5 0005
+     * @param session: 会话信息
+     * @param message: 文本信息
+     * @return void
+     */
+    void receiveTextMessage(WebSocketSession session, TextMessage message);
+
+    /**
+     * @describe 接收到二进制数据
+     * @author Joburgess
+     * @date 2021/8/5 0005
+     * @param session: 会话信息
+     * @param message: 二进制数据
+     * @return void
+     */
+    void receiveBinaryMessage(WebSocketSession session, BinaryMessage message);
+
+    /**
+     * @describe 连接关闭
+     * @author Joburgess
+     * @date 2021/8/5 0005
+     * @param session: 会话信息
+     * @return void
+     */
+    void afterConnectionClosed(WebSocketSession session);
+
+}

+ 504 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/SoundCompareHandler.java

@@ -0,0 +1,504 @@
+package com.ym.mec.biz.service.impl;
+
+import be.tarsos.dsp.AudioDispatcher;
+import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
+import be.tarsos.dsp.pitch.PitchProcessor;
+import be.tarsos.dsp.util.PitchConverter;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ym.mec.biz.dal.config.SoundCompareConfig;
+import com.ym.mec.biz.dal.dto.MusicPitchDetailDto;
+import com.ym.mec.biz.dal.dto.SoundCompareHelper;
+import com.ym.mec.biz.dal.dto.WavHeader;
+import com.ym.mec.biz.dal.dto.WebSocketInfo;
+import com.ym.mec.biz.handler.WebSocketHandler;
+import com.ym.mec.biz.service.SoundSocketService;
+import com.ym.mec.biz.service.SysMusicCompareRecordService;
+import com.ym.mec.biz.service.WebSocketEventHandler;
+import com.ym.mec.common.constant.CommonConstants;
+import com.ym.mec.common.exception.BizException;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.socket.BinaryMessage;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import javax.sound.sampled.UnsupportedAudioFileException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * @Author Joburgess
+ * @Date 2021/8/5 0005
+ */
+@Service
+public class SoundCompareHandler implements WebSocketEventHandler {
+
+    private final Logger LOGGER = LoggerFactory.getLogger(SoundCompareHandler.class);
+
+    private BigDecimal oneHundred = new BigDecimal(100);
+
+    private final String tmpDir = FileUtils.getTempDirectoryPath() + "/soundCompare/";
+    /**
+     * @describe 用户对应评分信息------------------------------------------------------------------------------
+     */
+    private Map<String, SoundCompareHelper> userSoundInfoMap = new ConcurrentHashMap<>();
+    /**
+     * @describe 音频处理参数
+     */
+    public static final SoundCompareConfig soundCompareConfig = new SoundCompareConfig();
+
+    @Autowired
+    private SysMusicCompareRecordService sysMusicCompareRecordService;
+
+    public SoundCompareHandler() {
+        WebSocketHandler.regist("SOUND_COMPARE", this);
+        File soundDir = new File(tmpDir);
+        if(!soundDir.exists()){
+            soundDir.mkdir();
+        }
+    }
+
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) {
+        System.out.println("已登录:+++++++++++++++++++++++++++++++++++++++++++++++++++");
+    }
+
+    @Override
+    public void receiveTextMessage(WebSocketSession session, TextMessage message) {
+        String phone = session.getPrincipal().getName().split(":")[1];
+        WebSocketInfo webSocketInfo = JSON.parseObject(message.getPayload(), WebSocketInfo.class);
+        JSONObject bodyObject = (JSONObject) webSocketInfo.getBody();
+
+        String commond = "";
+        if(webSocketInfo.getHeader().containsKey(SoundSocketService.COMMOND)){
+            commond = webSocketInfo.getHeader().get(SoundSocketService.COMMOND);
+        }
+        switch (commond){
+            case SoundSocketService.MUSIC_XML:
+                userSoundInfoMap.put(phone, new SoundCompareHelper());
+                List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
+                userSoundInfoMap.get(phone).setMusicXmlInfos(musicXmlInfos);
+                musicXmlInfos = musicXmlInfos.stream().filter(m->!m.getDontEvaluating()).collect(Collectors.toList());
+                userSoundInfoMap.get(phone).setMusicScoreId(bodyObject.getInteger("id"));
+                userSoundInfoMap.get(phone).setMeasureXmlInfoMap(musicXmlInfos.stream().collect(Collectors.groupingBy(MusicPitchDetailDto::getMeasureIndex)));
+                musicXmlInfos.forEach(e->userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(e.getMusicalNotesIndex(), e.getFrequency()));
+                for (Map.Entry<Integer, List<MusicPitchDetailDto>> userMeasureXmlInfoEntry : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().entrySet()) {
+                    MusicPitchDetailDto firstPitch = userMeasureXmlInfoEntry.getValue().stream().min(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
+                    MusicPitchDetailDto lastPitch = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
+                    long dc = userMeasureXmlInfoEntry.getValue().stream().filter(m -> m.getDontEvaluating()).count();
+                    MusicPitchDetailDto musicPitchDetailDto = new MusicPitchDetailDto(firstPitch.getTimeStamp(), lastPitch.getTimeStamp() + lastPitch.getDuration());
+                    musicPitchDetailDto.setDuration(musicPitchDetailDto.getEndTimeStamp()-musicPitchDetailDto.getTimeStamp());
+                    musicPitchDetailDto.setDontEvaluating(dc == userMeasureXmlInfoEntry.getValue().size());
+                    userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto);
+                }
+
+                break;
+            case SoundSocketService.RECORD_START:
+                if(!userSoundInfoMap.containsKey(phone)){
+                    break;
+                }
+                try {
+                    File file = new File(tmpDir+phone + "_"+ userSoundInfoMap.get(phone).getMusicScoreId() +"_"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) +".wav");
+                    userSoundInfoMap.get(phone).setAccessFile(new RandomAccessFile(file, "rw"));
+                    userSoundInfoMap.get(phone).setRecordFilePath(file.getAbsolutePath());
+                } catch (FileNotFoundException e) {
+                    throw new BizException("文件创建失败:", e);
+                }
+                break;
+            case SoundSocketService.RECORD_END:
+                if(!userSoundInfoMap.containsKey(phone)){
+                    break;
+                }
+                try {
+                    if(!CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getMeasureEndTime())){
+                        Integer lastMeasureIndex = userSoundInfoMap.get(phone).getMeasureEndTime().keySet().stream().min(Integer::compareTo).get();
+                        double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
+                        //如果结束时时长大于某小节,则此小节需要评分
+                        if(recordTime>userSoundInfoMap.get(phone).getMeasureEndTime().get(lastMeasureIndex).getEndTimeStamp()){
+                            measureCompare(phone, lastMeasureIndex);
+                            userSoundInfoMap.get(phone).getMeasureEndTime().remove(lastMeasureIndex);
+                        }
+                    }
+                    calTotalScore(phone);
+                } catch (IOException e) {
+                    throw new BizException("评分错误:", e);
+                }
+                createHeader(phone);
+                break;
+            case SoundSocketService.RECORD_CANCEL:
+                createHeader(phone);
+                break;
+            case SoundSocketService.PROXY_MESSAGE:
+//                if(bodyObject.containsKey(SoundSocketService.OFFSET_TIME)){
+//                    int offsetTime = bodyObject.getIntValue(SoundSocketService.OFFSET_TIME);
+//                    calOffsetTime(phone, offsetTime);
+//                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void receiveBinaryMessage(WebSocketSession session, BinaryMessage message) {
+        String phone = session.getPrincipal().getName().split(":")[1];
+        if(!userSoundInfoMap.containsKey(phone)){
+            return;
+        }
+        try {
+            if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
+                userSoundInfoMap.get(phone).getAccessFile().write(message.getPayload().array());
+            }
+
+            AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), soundCompareConfig.audioFormat, soundCompareConfig.simpleSize, soundCompareConfig.overlap);
+
+            dispatcher.addAudioProcessor(userSoundInfoMap.get(phone).silenceDetector);
+            dispatcher.addAudioProcessor(new PitchProcessor(soundCompareConfig.algo, soundCompareConfig.simpleRate, soundCompareConfig.simpleSize, userSoundInfoMap.get(phone)));
+            dispatcher.run();
+            if(Objects.isNull(userSoundInfoMap.get(phone).getAccessFile())){
+                return;
+            }
+
+            double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000;
+            userSoundInfoMap.get(phone).setMeasureStartTime(recordTime);
+            for (Map.Entry<Integer, MusicPitchDetailDto> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
+                if(recordTime>(userMeasureEndTimeMapEntry.getValue().getEndTimeStamp())){
+                    if(userMeasureEndTimeMapEntry.getValue().getDontEvaluating()){
+                        continue;
+                    }else{
+                        measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
+                    }
+                    userSoundInfoMap.get(phone).getMeasureEndTime().remove(userMeasureEndTimeMapEntry.getKey());
+                    break;
+                }
+            }
+        } catch (UnsupportedAudioFileException | IOException e) {
+            throw new BizException("{}评分异常:{}", phone, e);
+        }
+    }
+
+    @Override
+    public void afterConnectionClosed(WebSocketSession session){
+        String phone = session.getPrincipal().getName().split(":")[1];
+        LOGGER.info("发生了错误,移除客户端: {}", phone);
+        createHeader(phone);
+        userSoundInfoMap.remove(phone);
+    }
+
+    /**
+     * @describe 处理时间偏移
+     * @author Joburgess
+     * @date 2021/7/5 0005
+     * @param phone:
+     * @param offsetTime:
+     * @return void
+     */
+    private void calOffsetTime(String phone, int offsetTime){
+        userSoundInfoMap.get(phone).setOffsetTime(offsetTime);
+        for (Map.Entry<Integer, MusicPitchDetailDto> musicPitchDetailDtoEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
+            musicPitchDetailDtoEntry.getValue().setTimeStamp(musicPitchDetailDtoEntry.getValue().getTimeStamp() + offsetTime);
+            musicPitchDetailDtoEntry.getValue().setEndTimeStamp(musicPitchDetailDtoEntry.getValue().getEndTimeStamp() + offsetTime);
+        }
+    }
+
+    /**
+     * @describe 保存录音数据,并生成wav头信息
+     * @author Joburgess
+     * @date 2021/6/25 0025
+     * @param phone:
+     * @return void
+     */
+    private void createHeader(String phone){
+        if(!userSoundInfoMap.containsKey(phone)){
+            return;
+        }
+        if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
+            try {
+                RandomAccessFile randomAccessFile = userSoundInfoMap.get(phone).getAccessFile();
+                LOGGER.info("音频时长:{}", randomAccessFile.length()/(soundCompareConfig.audioFormat.getFrameSize()*soundCompareConfig.audioFormat.getFrameRate())*1000);
+                randomAccessFile.seek(0);
+                randomAccessFile.write(WavHeader.getWaveHeader(randomAccessFile.length(), (long) soundCompareConfig.audioFormat.getFrameRate(), soundCompareConfig.audioFormat.getSampleSizeInBits()));
+                randomAccessFile.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            userSoundInfoMap.get(phone).setAccessFile(null);
+        }
+        userSoundInfoMap.remove(phone);
+    }
+
+    /**
+     * @describe 数据比对,生成分数
+     * @author Joburgess
+     * @date 2021/6/25 0025
+     * @param phone:
+     * @param measureIndex:
+     * @return void
+     */
+    private void measureCompare(String phone, int measureIndex){
+        if (userSoundInfoMap.get(phone).getOffsetTime() == -1){
+            userSoundInfoMap.get(phone).setOffsetTime(0);
+        }
+
+        //相似度
+        BigDecimal intonation = BigDecimal.ZERO;
+        //节奏
+        BigDecimal cadence = BigDecimal.ZERO;
+        //完整度
+        BigDecimal integrity = BigDecimal.ZERO;
+
+        try {
+            //音准分数
+            float intonationScore = 0;
+
+            //节奏匹配数量
+            float cadenceNum = 0;
+
+            //完整性分数
+            float integrityScore = 0;
+
+            int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size();
+
+            for (int i = 0; i < userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size(); i++) {
+                MusicPitchDetailDto musicXmlInfo = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).get(i);
+
+                int ot5 = (int) (musicXmlInfo.getDuration()*0.1);
+                int startTimeStamp = musicXmlInfo.getTimeStamp() + userSoundInfoMap.get(phone).getOffsetTime() + ot5;
+                int endTimeStamp = musicXmlInfo.getTimeStamp()  + userSoundInfoMap.get(phone).getOffsetTime() + musicXmlInfo.getDuration() - ot5;
+
+                //时间范围内有效节奏数量
+                float cadenceValidNum = 0;
+                //时间范围内有效音频数量
+                float integrityValidNum = 0;
+                //时间范围内匹配次数
+                float compareNum = 0;
+
+                List<MusicPitchDetailDto> measureSoundPitchInfos = new ArrayList<>();
+
+                for (int j = 0; j < userSoundInfoMap.get(phone).getRecordMeasurePithInfo().size(); j++) {
+                    MusicPitchDetailDto recordInfo = userSoundInfoMap.get(phone).getRecordMeasurePithInfo().get(j);
+                    //如果在时间范围之外直接跳过
+                    if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
+                        continue;
+                    }
+                    measureSoundPitchInfos.add(recordInfo);
+                    compareNum++;
+                    //如果在最低有效频率以下则跳过
+                    if(recordInfo.getFrequency()<soundCompareConfig.validFrequency&&musicXmlInfo.getFrequency()!=-1){
+                        continue;
+                    }
+                    cadenceValidNum++;
+                    //如果频率差值在节奏误差范围内
+                    if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=soundCompareConfig.integrityFrequencyRange){
+                        integrityValidNum++;
+                    }
+                }
+
+                //非正常频率次数
+                int errPitchNum = 0;
+                //分贝变化次数
+                int decibelChangeNum = 0;
+
+                if(CollectionUtils.isEmpty(measureSoundPitchInfos)){
+                    userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) 0);
+                }else{
+                    Map<Integer, Long> collect = measureSoundPitchInfos.stream().map(pitch -> (int)pitch.getFrequency()).collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()));
+                    //出现次数最多的频率
+                    Integer pitch = collect.entrySet().stream().max(Comparator.comparing(e -> e.getValue())).get().getKey();
+                    //当前频率
+                    double cf = -1;
+                    //频率持续数量
+                    int fnum = 0;
+                    //是否演奏中
+                    boolean ing = false;
+                    //当前分贝
+                    double cd = 0;
+                    //分贝变化方向,-1变小,1变大
+                    int dcd = -1;
+                    //分贝持续数量
+                    int dnum = 0;
+                    for (MusicPitchDetailDto musicalNotesPitch : measureSoundPitchInfos) {
+                        //计算频率断层次数
+                        if (Math.abs(musicalNotesPitch.getFrequency() - cf) > 20){
+                            fnum ++;
+                        }
+                        if (fnum>=5){
+                            cf = musicalNotesPitch.getFrequency();
+                            fnum = 0;
+                            if (cf != -1){
+                                errPitchNum ++;
+                                ing = true;
+                                cd = musicalNotesPitch.getDecibel();
+                            }
+                        }
+                        //计算声音大小断层册数
+                        if(ing && Math.abs(musicalNotesPitch.getDecibel() - cd) > 10){
+                            dnum ++;
+                        }
+                        if (dnum > 2){
+                            int tdcd = cd > musicalNotesPitch.getDecibel() ? -1 : 1;
+                            cd = musicalNotesPitch.getDecibel();
+                            dnum = 0;
+                            if (tdcd != dcd) {
+                                decibelChangeNum++;
+                            }
+                            dcd = tdcd;
+                        }
+                    }
+                    userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) pitch);
+                }
+
+                //有效节奏占比
+                float cadenceDuty = cadenceValidNum/compareNum;
+                //如果频率出现断层或这个音量出现断层,则当前音符节奏无效
+                if(errPitchNum>=2 || decibelChangeNum>1){
+                    cadenceDuty = 0;
+                }
+                //节奏
+                if(cadenceDuty>=soundCompareConfig.cadenceValidDuty){
+                    cadenceNum++;
+                }
+                //音准
+                if (!CollectionUtils.isEmpty(measureSoundPitchInfos)){
+                    Double avgPitch = measureSoundPitchInfos.stream().filter(pitch -> Math.abs((pitch.getFrequency()-musicXmlInfo.getFrequency()))<5).collect(Collectors.averagingDouble(pitch -> pitch.getFrequency()));
+                    //音分
+                    double recordCents = 0;
+                    if (avgPitch > 0){
+                        recordCents = PitchConverter.hertzToAbsoluteCent(avgPitch);
+                    }
+                    double cents = PitchConverter.hertzToAbsoluteCent(musicXmlInfo.getFrequency());
+                    double score = 100 - Math.round(Math.abs(cents - recordCents)) + soundCompareConfig.intonationCentsRange;
+                    if (score < 0){
+                        score = 0;
+                    }else if(score > 100){
+                        score = 100;
+                    }
+                    intonationScore += score;
+                    musicXmlInfo.setAvgFrequency(avgPitch.floatValue());
+                }
+                //完成度
+                if(integrityValidNum>0){
+                    integrityValidNum = integrityValidNum;
+                }
+                if(integrityValidNum > compareNum){
+                    integrityValidNum = compareNum;
+                }
+                float integrityDuty = integrityValidNum/compareNum;
+                integrityScore += integrityDuty;
+            }
+
+            BigDecimal measureNum = new BigDecimal(totalCompareNum);
+
+            intonation = new BigDecimal(intonationScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
+            cadence = new BigDecimal(cadenceNum).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
+            integrity = new BigDecimal(integrityScore).divide(measureNum, CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_UP);
+
+        } catch (ArithmeticException e){
+            LOGGER.info("无musicXml信息");
+        }
+
+        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("intonation")){
+            userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation.add(userSoundInfoMap.get(phone).getUserScoreMap().get("intonation")));
+        }else{
+            userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation);
+        }
+
+        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("cadence")){
+            userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence.add(userSoundInfoMap.get(phone).getUserScoreMap().get("cadence")));
+        }else{
+            userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence);
+        }
+
+        if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("integrity")){
+            userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity.add(userSoundInfoMap.get(phone).getUserScoreMap().get("integrity")));
+        }else{
+            userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity);
+        }
+
+        //计算分数并推送
+        createPushInfo(phone, "measureScore", measureIndex, intonation, cadence, integrity);
+    }
+
+    /**
+     * @describe 计算最终评分
+     * @author Joburgess
+     * @date 2021/6/25 0025
+     * @param phone:
+     * @return void
+     */
+    private void calTotalScore(String phone) throws IOException {
+        int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().keySet().size();
+        int currentCompareNum = totalCompareNum-userSoundInfoMap.get(phone).getMeasureEndTime().keySet().size();
+        BigDecimal intonation = BigDecimal.ZERO;
+        BigDecimal cadence = BigDecimal.ZERO;
+        BigDecimal integrity = BigDecimal.ZERO;
+
+        if(currentCompareNum>0){
+            intonation = userSoundInfoMap.get(phone).getUserScoreMap().get("intonation").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
+            cadence = userSoundInfoMap.get(phone).getUserScoreMap().get("cadence").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
+            integrity = userSoundInfoMap.get(phone).getUserScoreMap().get("integrity").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
+        }
+
+        //计算分数并推送
+        createPushInfo(phone, "overall", -1, intonation, cadence, integrity);
+
+        //存储评分数据
+        sysMusicCompareRecordService.saveMusicCompareData(phone, userSoundInfoMap.get(phone));
+
+        LOGGER.info("评分数据:{}", JSON.toJSONString(userSoundInfoMap.get(phone)));
+    }
+
+    /**
+     * @describe 生成评分结果
+     * @author Joburgess
+     * @date 2021/6/25 0025
+     * @param command:
+     * @param measureIndex:
+     * @param intonation:
+     * @param cadence:
+     * @param integrity:
+     * @return com.ym.mec.biz.dal.dto.WebSocketInfo
+     */
+    private WebSocketInfo createPushInfo(String phone, String command, Integer measureIndex,
+                                         BigDecimal intonation, BigDecimal cadence, BigDecimal integrity){
+        WebSocketInfo webSocketInfo = new WebSocketInfo();
+        HashMap<String, String> header = new HashMap<>();
+        header.put("commond", command);
+        webSocketInfo.setHeader(header);
+        Map<String, Object> result = new HashMap<>();
+//        BigDecimal score = intonation.multiply(new BigDecimal(0.5)).add(cadence.multiply(new BigDecimal(0.5))).setScale(0, BigDecimal.ROUND_HALF_UP);
+        BigDecimal score = intonation.add(cadence).add(integrity).divide(new BigDecimal(3), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).setScale(0, BigDecimal.ROUND_UP);
+//        BigDecimal score = integrity.setScale(0, BigDecimal.ROUND_HALF_UP);
+        result.put("score", score);
+        result.put("intonation", intonation);
+        result.put("cadence", cadence);
+        result.put("integrity", integrity);
+        result.put("measureIndex", measureIndex);
+        webSocketInfo.setBody(result);
+
+        userSoundInfoMap.get(phone).getUserMeasureScoreMap().put(measureIndex, result);
+
+        LOGGER.info("小节频分:{}", JSON.toJSONString(webSocketInfo));
+
+        //推送结果
+        try {
+            WebSocketHandler.WS_CLIENTS.get(phone).getSession().sendMessage(new TextMessage(JSON.toJSONString(webSocketInfo)));
+        } catch (IOException e) {
+            LOGGER.error("{}评分结果推送失败", phone);
+        }
+        return webSocketInfo;
+    }
+}