|  | @@ -9,19 +9,24 @@ import com.ym.mec.biz.dal.dto.MusicPitchDetailDto;
 | 
	
		
			
				|  |  |  import com.ym.mec.biz.dal.dto.WavHeader;
 | 
	
		
			
				|  |  |  import com.ym.mec.biz.dal.dto.WebSocketInfo;
 | 
	
		
			
				|  |  |  import com.ym.mec.biz.service.SoundSocketService;
 | 
	
		
			
				|  |  | -import org.apache.commons.lang3.RandomUtils;
 | 
	
		
			
				|  |  | +import com.ym.mec.common.constant.CommonConstants;
 | 
	
		
			
				|  |  |  import org.slf4j.Logger;
 | 
	
		
			
				|  |  |  import org.slf4j.LoggerFactory;
 | 
	
		
			
				|  |  |  import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  | +import org.springframework.util.CollectionUtils;
 | 
	
		
			
				|  |  |  import org.springframework.web.socket.*;
 | 
	
		
			
				|  |  |  import org.springframework.web.socket.handler.AbstractWebSocketHandler;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import javax.sound.sampled.AudioFormat;
 | 
	
		
			
				|  |  | -import java.io.*;
 | 
	
		
			
				|  |  | +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.concurrent.ConcurrentHashMap;
 | 
	
		
			
				|  |  | +import java.util.stream.Collectors;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * @Author Joburgess
 | 
	
	
		
			
				|  | @@ -34,12 +39,18 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public static final Map<String, WebSocketSession> WS_CLIENTS = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private BigDecimal oneHundred = new BigDecimal(100);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      AudioFormat audioFormat = new AudioFormat(44100, 16, 1, true, false);
 | 
	
		
			
				|  |  |      PitchProcessor.PitchEstimationAlgorithm algo = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private Map<String, RandomAccessFile> userRandomAccessFileMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | -    private Map<String, List<MusicPitchDetailDto>> userXmlInfoMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +    private Map<String, Map<Integer, List<MusicPitchDetailDto>>> userMeasureXmlInfoMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +    private Map<String, Map<Integer, Integer>> userMeasureEndTimeMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  |      private Map<String, Set<Integer>> userMeasureMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +    private Map<String, Double> userMeasureStartTimeMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +    private Map<String, List<MusicPitchDetailDto>> userLastMeasurePithInfoMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +    private Map<String, Map<String, BigDecimal>> userTotalScoreMap = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public WebSocketHandler() {
 | 
	
		
			
				|  |  |          super();
 | 
	
	
		
			
				|  | @@ -72,14 +83,27 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          switch (commond){
 | 
	
		
			
				|  |  |              case SoundSocketService.MUSIC_XML:
 | 
	
		
			
				|  |  | -                userXmlInfoMap.put(phone, JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class));
 | 
	
		
			
				|  |  | +                List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
 | 
	
		
			
				|  |  | +                userLastMeasurePithInfoMap.put(phone, new ArrayList<>());
 | 
	
		
			
				|  |  | +                userTotalScoreMap.put(phone, new HashMap<>());
 | 
	
		
			
				|  |  | +                userMeasureXmlInfoMap.put(phone, musicXmlInfos.stream().collect(Collectors.groupingBy(MusicPitchDetailDto::getMeasureIndex)));
 | 
	
		
			
				|  |  | +                userMeasureEndTimeMap.put(phone, new HashMap<>());
 | 
	
		
			
				|  |  | +                for (Map.Entry<Integer, List<MusicPitchDetailDto>> userMeasureXmlInfoEntry : userMeasureXmlInfoMap.get(phone).entrySet()) {
 | 
	
		
			
				|  |  | +                    MusicPitchDetailDto musicPitchDetailDto = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
 | 
	
		
			
				|  |  | +                    userMeasureEndTimeMap.get(phone).put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto.getTimeStamp()+musicPitchDetailDto.getDuration());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |                  break;
 | 
	
		
			
				|  |  |              case SoundSocketService.RECORD_START:
 | 
	
		
			
				|  |  |                  File file = new File("E:\\Temp\\record"+phone +"-"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) +".wav");
 | 
	
		
			
				|  |  |                  userRandomAccessFileMap.put(phone, new RandomAccessFile(file, "rw"));
 | 
	
		
			
				|  |  |                  userMeasureMap.put(phone, new HashSet<>());
 | 
	
		
			
				|  |  | +                userMeasureStartTimeMap.put(phone, (double) 0);
 | 
	
		
			
				|  |  |                  break;
 | 
	
		
			
				|  |  |              case SoundSocketService.RECORD_END:
 | 
	
		
			
				|  |  | +                if(!CollectionUtils.isEmpty(userMeasureEndTimeMap.get(phone))){
 | 
	
		
			
				|  |  | +                    measureCompare(phone, userMeasureEndTimeMap.get(phone).keySet().stream().max(Integer::compareTo).get());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                calTotalScore(phone);
 | 
	
		
			
				|  |  |                  createHeader(phone);
 | 
	
		
			
				|  |  |                  break;
 | 
	
		
			
				|  |  |              default:
 | 
	
	
		
			
				|  | @@ -94,34 +118,26 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
 | 
	
		
			
				|  |  |          if(userRandomAccessFileMap.containsKey(phone)){
 | 
	
		
			
				|  |  |              userRandomAccessFileMap.get(phone).write(message.getPayload().array());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        List<MusicPitchDetailDto> recordInfo = new ArrayList<>();
 | 
	
		
			
				|  |  |          AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), audioFormat, 256, 128);
 | 
	
		
			
				|  |  |          dispatcher.addAudioProcessor(new PitchProcessor(algo, 44100, 256, (pitchDetectionResult, audioEvent) -> {
 | 
	
		
			
				|  |  | -            double timeStamp = audioEvent.getTimeStamp();
 | 
	
		
			
				|  |  | +            int timeStamp = (int) (userMeasureStartTimeMap.get(phone) + audioEvent.getTimeStamp()*1000);
 | 
	
		
			
				|  |  |              float pitch = pitchDetectionResult.getPitch();
 | 
	
		
			
				|  |  | -//            LOGGER.info("时间:{},频率:{}", timeStamp, pitch);
 | 
	
		
			
				|  |  | +            LOGGER.info("时间:{},频率:{}", timeStamp, pitch);
 | 
	
		
			
				|  |  | +            recordInfo.add(new MusicPitchDetailDto(timeStamp, pitch));
 | 
	
		
			
				|  |  |          }));
 | 
	
		
			
				|  |  |          dispatcher.run();
 | 
	
		
			
				|  |  |          double recordTime = userRandomAccessFileMap.get(phone).length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000;
 | 
	
		
			
				|  |  | -//        LOGGER.info("时长:{}", recordTime);
 | 
	
		
			
				|  |  | -        for (MusicPitchDetailDto musicPitchDetailDto : userXmlInfoMap.get(phone)) {
 | 
	
		
			
				|  |  | -            if(!userMeasureMap.get(phone).contains(musicPitchDetailDto.getMeasureIndex())&&recordTime>musicPitchDetailDto.getTimeStamp()+musicPitchDetailDto.getDuration()){
 | 
	
		
			
				|  |  | -                WebSocketInfo webSocketInfo = new WebSocketInfo();
 | 
	
		
			
				|  |  | -                HashMap<String, String> header = new HashMap<>();
 | 
	
		
			
				|  |  | -                header.put("commond", "measureScore");
 | 
	
		
			
				|  |  | -                webSocketInfo.setHeader(header);
 | 
	
		
			
				|  |  | -                Map<String, Double> result = new HashMap<>();
 | 
	
		
			
				|  |  | -                result.put("score", RandomUtils.nextDouble(0,100));
 | 
	
		
			
				|  |  | -                result.put("intonation", RandomUtils.nextDouble(0,100));
 | 
	
		
			
				|  |  | -                result.put("cadence", RandomUtils.nextDouble(0,100));
 | 
	
		
			
				|  |  | -                result.put("integrity", RandomUtils.nextDouble(0,100));
 | 
	
		
			
				|  |  | -                result.put("integrity", RandomUtils.nextDouble(0,100));
 | 
	
		
			
				|  |  | -                result.put("measureIndex", (double) musicPitchDetailDto.getMeasureIndex());
 | 
	
		
			
				|  |  | -                webSocketInfo.setBody(result);
 | 
	
		
			
				|  |  | -                WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(webSocketInfo)));
 | 
	
		
			
				|  |  | -                userMeasureMap.get(phone).add(musicPitchDetailDto.getMeasureIndex());
 | 
	
		
			
				|  |  | -                LOGGER.info("推送评分结果:{}", phone);
 | 
	
		
			
				|  |  | +        userMeasureStartTimeMap.put(phone, recordTime);
 | 
	
		
			
				|  |  | +        userLastMeasurePithInfoMap.get(phone).addAll(recordInfo);
 | 
	
		
			
				|  |  | +        for (Map.Entry<Integer, Integer> userMeasureEndTimeMapEntry : userMeasureEndTimeMap.get(phone).entrySet()) {
 | 
	
		
			
				|  |  | +            if(recordTime>userMeasureEndTimeMapEntry.getValue()){
 | 
	
		
			
				|  |  | +                measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
 | 
	
		
			
				|  |  | +                userMeasureEndTimeMap.get(phone).remove(userMeasureEndTimeMapEntry.getKey());
 | 
	
		
			
				|  |  | +                break;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
	
		
			
				|  | @@ -166,4 +182,143 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
 | 
	
		
			
				|  |  |          randomAccessFile.close();
 | 
	
		
			
				|  |  |          userRandomAccessFileMap.remove(phone);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void measureCompare(String phone, int measureIndex) throws IOException {
 | 
	
		
			
				|  |  | +        //总分
 | 
	
		
			
				|  |  | +        BigDecimal score = BigDecimal.ZERO;
 | 
	
		
			
				|  |  | +        //相似度
 | 
	
		
			
				|  |  | +        BigDecimal intonation = BigDecimal.ZERO;
 | 
	
		
			
				|  |  | +        //节奏
 | 
	
		
			
				|  |  | +        BigDecimal cadence = BigDecimal.ZERO;
 | 
	
		
			
				|  |  | +        //完整度
 | 
	
		
			
				|  |  | +        BigDecimal integrity = BigDecimal.ZERO;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            //最低有效频率
 | 
	
		
			
				|  |  | +            float minValidFrequency = 20;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //有效音频数量
 | 
	
		
			
				|  |  | +            float validNum = 0;
 | 
	
		
			
				|  |  | +            //音频有效阈值
 | 
	
		
			
				|  |  | +            float validDuty = 0.7f;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //音准匹配数量
 | 
	
		
			
				|  |  | +            float intonationNum = 0;
 | 
	
		
			
				|  |  | +            //音准匹配误差范围
 | 
	
		
			
				|  |  | +            float intonationErrRange = 10;
 | 
	
		
			
				|  |  | +            //音准有效阈值
 | 
	
		
			
				|  |  | +            float intonationValidDuty = 0.8f;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //节奏匹配数量
 | 
	
		
			
				|  |  | +            float cadenceNum = 0;
 | 
	
		
			
				|  |  | +            //节奏匹配误差范围
 | 
	
		
			
				|  |  | +            float cadenceErrRange = 100;
 | 
	
		
			
				|  |  | +            //节奏有效阈值
 | 
	
		
			
				|  |  | +            float cadenceValidDuty = 0.7f;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            int totalCompareNum = userMeasureXmlInfoMap.get(phone).get(measureIndex).size();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            for (MusicPitchDetailDto musicXmlInfo : userMeasureXmlInfoMap.get(phone).get(measureIndex)) {
 | 
	
		
			
				|  |  | +                int startTimeStamp = musicXmlInfo.getTimeStamp();
 | 
	
		
			
				|  |  | +                int endTimeStamp = musicXmlInfo.getTimeStamp()+musicXmlInfo.getDuration();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                //时间范围内有效音准数量
 | 
	
		
			
				|  |  | +                float recordValidIntonationNum = 0;
 | 
	
		
			
				|  |  | +                //时间范围内有效节奏数量
 | 
	
		
			
				|  |  | +                float cadenceValidNum = 0;
 | 
	
		
			
				|  |  | +                //时间范围内有效音频数量
 | 
	
		
			
				|  |  | +                float recordValidNum = 0;
 | 
	
		
			
				|  |  | +                //时间范围内匹配次数
 | 
	
		
			
				|  |  | +                float compareNum = 0;
 | 
	
		
			
				|  |  | +                for (MusicPitchDetailDto recordInfo : userLastMeasurePithInfoMap.get(phone)) {
 | 
	
		
			
				|  |  | +                    //如果在时间范围之外直接跳过
 | 
	
		
			
				|  |  | +                    if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
 | 
	
		
			
				|  |  | +                        continue;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    compareNum++;
 | 
	
		
			
				|  |  | +                    //如果在最低有效频率以下则跳过
 | 
	
		
			
				|  |  | +                    if(recordInfo.getFrequency()<minValidFrequency){
 | 
	
		
			
				|  |  | +                        continue;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    recordValidNum++;
 | 
	
		
			
				|  |  | +                    //如果频率差值在节奏误差范围内
 | 
	
		
			
				|  |  | +                    if(recordInfo.getFrequency()-musicXmlInfo.getFrequency()<=cadenceErrRange){
 | 
	
		
			
				|  |  | +                        cadenceValidNum++;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    //如果频率差值在音准误差范围内
 | 
	
		
			
				|  |  | +                    if(recordInfo.getFrequency()-musicXmlInfo.getFrequency()<=intonationErrRange){
 | 
	
		
			
				|  |  | +                        recordValidIntonationNum++;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                //有效音频占比
 | 
	
		
			
				|  |  | +                float recordValidDuty = recordValidNum/compareNum;
 | 
	
		
			
				|  |  | +                if(recordValidDuty<validDuty){
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                validNum++;
 | 
	
		
			
				|  |  | +                //有效音高占比
 | 
	
		
			
				|  |  | +                float intonationDuty = recordValidIntonationNum/compareNum;
 | 
	
		
			
				|  |  | +                //有效节奏占比
 | 
	
		
			
				|  |  | +                float cadenceDuty = cadenceValidNum/compareNum;
 | 
	
		
			
				|  |  | +                if(intonationDuty>=intonationValidDuty){
 | 
	
		
			
				|  |  | +                    intonationNum++;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                if(cadenceDuty>=cadenceValidDuty){
 | 
	
		
			
				|  |  | +                    cadenceNum++;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            intonation = new BigDecimal(intonationNum).divide(new BigDecimal(totalCompareNum), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_HALF_UP);
 | 
	
		
			
				|  |  | +            cadence = new BigDecimal(cadenceNum).divide(new BigDecimal(totalCompareNum), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_HALF_UP);
 | 
	
		
			
				|  |  | +            integrity = new BigDecimal(validNum).divide(new BigDecimal(totalCompareNum), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_HALF_UP);
 | 
	
		
			
				|  |  | +        } catch (ArithmeticException e){
 | 
	
		
			
				|  |  | +            LOGGER.info("无musicXml信息");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(userTotalScoreMap.get(phone).containsKey("intonation")){
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("intonation", intonation.add(userTotalScoreMap.get(phone).get("intonation")));
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("intonation", intonation);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(userTotalScoreMap.get(phone).containsKey("cadence")){
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("cadence", intonation.add(userTotalScoreMap.get(phone).get("cadence")));
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("cadence", cadence);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(userTotalScoreMap.get(phone).containsKey("integrity")){
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("integrity", intonation.add(userTotalScoreMap.get(phone).get("integrity")));
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            userTotalScoreMap.get(phone).put("integrity", integrity);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("measureScore", measureIndex, score, intonation, cadence, integrity))));
 | 
	
		
			
				|  |  | +//                LOGGER.info("推送评分结果:{}", phone);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void calTotalScore(String phone) throws IOException {
 | 
	
		
			
				|  |  | +        int totalCompareNum = userMeasureXmlInfoMap.get(phone).keySet().size();
 | 
	
		
			
				|  |  | +        BigDecimal intonation = userTotalScoreMap.get(phone).get("intonation").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
 | 
	
		
			
				|  |  | +        BigDecimal cadence = userTotalScoreMap.get(phone).get("cadence").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
 | 
	
		
			
				|  |  | +        BigDecimal integrity = userTotalScoreMap.get(phone).get("integrity").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
 | 
	
		
			
				|  |  | +        BigDecimal score = intonation.multiply(new BigDecimal(0.9)).add(cadence.multiply(new BigDecimal(0.9))).add(integrity.multiply(new BigDecimal(0.1))).setScale(0, BigDecimal.ROUND_HALF_UP);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("overall", -1, score, intonation, cadence, integrity))));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private WebSocketInfo createPushInfo(String commond, Integer measureIndex,
 | 
	
		
			
				|  |  | +                                         BigDecimal score, BigDecimal intonation, BigDecimal cadence, BigDecimal integrity){
 | 
	
		
			
				|  |  | +        WebSocketInfo webSocketInfo = new WebSocketInfo();
 | 
	
		
			
				|  |  | +        HashMap<String, String> header = new HashMap<>();
 | 
	
		
			
				|  |  | +        header.put("commond", commond);
 | 
	
		
			
				|  |  | +        webSocketInfo.setHeader(header);
 | 
	
		
			
				|  |  | +        Map<String, Object> result = new HashMap<>();
 | 
	
		
			
				|  |  | +        result.put("score", score);
 | 
	
		
			
				|  |  | +        result.put("intonation", intonation);
 | 
	
		
			
				|  |  | +        result.put("cadence", cadence);
 | 
	
		
			
				|  |  | +        result.put("integrity", integrity);
 | 
	
		
			
				|  |  | +        result.put("measureIndex", measureIndex);
 | 
	
		
			
				|  |  | +        webSocketInfo.setBody(result);
 | 
	
		
			
				|  |  | +        return webSocketInfo;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |