|
@@ -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
|
|
|
*/
|
|
|
- private 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, phone));
|
|
|
super.afterConnectionEstablished(session);
|
|
|
}
|
|
|
|
|
@@ -82,116 +63,25 @@ 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, phone, message);
|
|
|
+ }else{
|
|
|
+ appMap.values().forEach(e->e.receiveTextMessage(session, phone, message));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
|
|
|
+ protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
|
|
|
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, (pitchDetectionResult, audioEvent) -> {
|
|
|
- int timeStamp = (int) (userSoundInfoMap.get(phone).getMeasureStartTime() + audioEvent.getTimeStamp()*1000);
|
|
|
- float pitch = pitchDetectionResult.getPitch();
|
|
|
- if(pitch>0 && userSoundInfoMap.get(phone).getOffsetTime() == -1){
|
|
|
- int preTimeStamp = CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getRecordMeasurePithInfo())?0:userSoundInfoMap.get(phone).getRecordMeasurePithInfo().get(userSoundInfoMap.get(phone).getRecordMeasurePithInfo().size()-1).getTimeStamp();
|
|
|
- calOffsetTime(phone, timeStamp - (timeStamp - preTimeStamp)/2);
|
|
|
- }
|
|
|
- if(userSoundInfoMap.get(phone).silenceDetector.currentSPL()<soundCompareConfig.validDb){
|
|
|
- pitch = -1;
|
|
|
- }
|
|
|
-// LOGGER.info("时间:{}, 频率:{}, 分贝:{}", timeStamp, pitch, silenceDetecor.currentSPL());
|
|
|
- userSoundInfoMap.get(phone).getRecordMeasurePithInfo().add(new MusicPitchDetailDto(timeStamp, pitch, userSoundInfoMap.get(phone).silenceDetector.currentSPL()));
|
|
|
- }));
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
+ appMap.values().forEach(e->e.receiveBinaryMessage(session, phone, message));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -204,14 +94,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, phone));
|
|
|
+ exception.printStackTrace();
|
|
|
if(!WS_CLIENTS.containsKey(phone)){
|
|
|
return;
|
|
|
}
|
|
|
- exception.printStackTrace();
|
|
|
- LOGGER.info("发生了错误,移除客户端: {}", phone);
|
|
|
WS_CLIENTS.remove(phone);
|
|
|
- userSoundInfoMap.remove(phone);
|
|
|
- createHeader(phone);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -219,310 +108,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, phone));
|
|
|
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;
|
|
|
- }
|
|
|
}
|