|
|
@@ -22,6 +22,7 @@ import org.springframework.web.socket.*;
|
|
|
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
|
|
|
|
|
import javax.sound.sampled.AudioFormat;
|
|
|
+import javax.sound.sampled.UnsupportedAudioFileException;
|
|
|
import java.io.File;
|
|
|
import java.io.IOException;
|
|
|
import java.io.RandomAccessFile;
|
|
|
@@ -99,11 +100,16 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
case SoundSocketService.MUSIC_XML:
|
|
|
userSoundInfoMap.put(phone, new SoundCompareHelper());
|
|
|
List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
|
|
|
+ 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 musicPitchDetailDto = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
|
|
|
- userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto.getTimeStamp()+musicPitchDetailDto.getDuration());
|
|
|
+ MusicPitchDetailDto firstPitch = userMeasureXmlInfoEntry.getValue().stream().min(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
|
|
|
+ MusicPitchDetailDto lastPitch = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
|
|
|
+ MusicPitchDetailDto musicPitchDetailDto = new MusicPitchDetailDto(firstPitch.getTimeStamp(), lastPitch.getTimeStamp() + lastPitch.getDuration());
|
|
|
+ musicPitchDetailDto.setDuration(musicPitchDetailDto.getEndTimeStamp()-musicPitchDetailDto.getTimeStamp());
|
|
|
+ userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto);
|
|
|
}
|
|
|
break;
|
|
|
case SoundSocketService.RECORD_START:
|
|
|
@@ -121,7 +127,7 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
Integer lastMeasureIndex = userSoundInfoMap.get(phone).getMeasureEndTime().keySet().stream().min(Integer::compareTo).get();
|
|
|
double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000;
|
|
|
//如果结束时时长大于某小节,则此小节需要评分
|
|
|
- if(recordTime>userSoundInfoMap.get(phone).getMeasureEndTime().get(lastMeasureIndex)){
|
|
|
+ if(recordTime>userSoundInfoMap.get(phone).getMeasureEndTime().get(lastMeasureIndex).getEndTimeStamp()){
|
|
|
measureCompare(phone, lastMeasureIndex);
|
|
|
userSoundInfoMap.get(phone).getMeasureEndTime().remove(lastMeasureIndex);
|
|
|
}
|
|
|
@@ -132,6 +138,12 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
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;
|
|
|
}
|
|
|
@@ -146,6 +158,7 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
|
|
|
userSoundInfoMap.get(phone).getAccessFile().write(message.getPayload().array());
|
|
|
}
|
|
|
+
|
|
|
List<MusicPitchDetailDto> recordInfo = new ArrayList<>();
|
|
|
AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), audioFormat, simpleSize, 128);
|
|
|
dispatcher.addAudioProcessor(new PitchProcessor(algo, simpleRate, simpleSize, (pitchDetectionResult, audioEvent) -> {
|
|
|
@@ -160,38 +173,14 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
}
|
|
|
|
|
|
double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000;
|
|
|
-
|
|
|
userSoundInfoMap.get(phone).setMeasureStartTime(recordTime);
|
|
|
userSoundInfoMap.get(phone).getRecordMeasurePithInfo().addAll(recordInfo);
|
|
|
-
|
|
|
- //如果是第一小节,需要计算出录音与播放不同步导致的空白时间偏移量
|
|
|
- if(userSoundInfoMap.get(phone).getOffsetTime()<=0&&recordTime<endCheckOffsetTime){
|
|
|
- int hasPitchNum = 0;
|
|
|
- for (MusicPitchDetailDto ri : userSoundInfoMap.get(phone).getRecordMeasurePithInfo()) {
|
|
|
- if(hasPitchNum<=0){
|
|
|
- userSoundInfoMap.get(phone).setOffsetTime(ri.getTimeStamp());
|
|
|
- }
|
|
|
- if(ri.getFrequency()>0){
|
|
|
- hasPitchNum++;
|
|
|
- }else{
|
|
|
- hasPitchNum=0;
|
|
|
- }
|
|
|
- if(hasPitchNum>=3){
|
|
|
- LOGGER.info("偏移时间:{}", userSoundInfoMap.get(phone).getOffsetTime());
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if(hasPitchNum<3){
|
|
|
- userSoundInfoMap.get(phone).setOffsetTime(0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(userSoundInfoMap.get(phone).getOffsetTime()<=0&&recordTime<endCheckOffsetTime){
|
|
|
- return;
|
|
|
- }
|
|
|
- for (Map.Entry<Integer, Integer> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
|
|
|
- if((recordTime - userSoundInfoMap.get(phone).getOffsetTime())>userMeasureEndTimeMapEntry.getValue()){
|
|
|
+ for (Map.Entry<Integer, MusicPitchDetailDto> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
|
|
|
+ int ot = (int) (userMeasureEndTimeMapEntry.getValue().getDuration()*0.1);
|
|
|
+ if(recordTime>(userMeasureEndTimeMapEntry.getValue().getEndTimeStamp()+ot)){
|
|
|
+// LOGGER.info("频分开始{}:{}, {}, {}", recordTime, userSoundInfoMap.get(phone).getAccessFile().length(), ot, JSON.toJSONString(userMeasureEndTimeMapEntry.getValue()));
|
|
|
measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
|
|
|
+// measureCompare2(phone, userMeasureEndTimeMapEntry.getValue());
|
|
|
userSoundInfoMap.get(phone).getMeasureEndTime().remove(userMeasureEndTimeMapEntry.getKey());
|
|
|
break;
|
|
|
}
|
|
|
@@ -232,6 +221,22 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * @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
|
|
|
@@ -275,81 +280,130 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
//最低有效频率
|
|
|
float minValidFrequency = 20;
|
|
|
|
|
|
- //有效音频数量
|
|
|
- float validNum = 0;
|
|
|
- //音频有效阈值
|
|
|
- float validDuty = 0.5f;
|
|
|
-
|
|
|
//音准匹配数量
|
|
|
float intonationNum = 0;
|
|
|
//音准匹配误差范围
|
|
|
float intonationErrRange = 15;
|
|
|
//音准有效阈值
|
|
|
- float intonationValidDuty = 0.7f;
|
|
|
+ float intonationValidDuty = 0.2f;
|
|
|
|
|
|
//节奏匹配数量
|
|
|
float cadenceNum = 0;
|
|
|
//节奏匹配误差范围
|
|
|
float cadenceErrRange = 30;
|
|
|
//节奏有效阈值
|
|
|
- float cadenceValidDuty = 0.6f;
|
|
|
+ float cadenceValidDuty = 0.2f;
|
|
|
+
|
|
|
+ //完整性数量
|
|
|
+ float integrityNum = 0;
|
|
|
+ //完整性误差范围
|
|
|
+ float integrityRange = 30;
|
|
|
+ //完整性有效阈值
|
|
|
+ float integrityValidDuty = 0.5f;
|
|
|
|
|
|
int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size();
|
|
|
|
|
|
- for (MusicPitchDetailDto musicXmlInfo : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex)) {
|
|
|
- int startTimeStamp = musicXmlInfo.getTimeStamp() + userSoundInfoMap.get(phone).getOffsetTime();
|
|
|
- int endTimeStamp = musicXmlInfo.getTimeStamp()+musicXmlInfo.getDuration() + userSoundInfoMap.get(phone).getOffsetTime();
|
|
|
+ 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() - ot5;
|
|
|
+ int endTimeStamp = musicXmlInfo.getTimeStamp() + musicXmlInfo.getDuration() + ot5;
|
|
|
|
|
|
//时间范围内有效音准数量
|
|
|
float recordValidIntonationNum = 0;
|
|
|
//时间范围内有效节奏数量
|
|
|
float cadenceValidNum = 0;
|
|
|
//时间范围内有效音频数量
|
|
|
- float recordValidNum = 0;
|
|
|
+ float integrityValidNum = 0;
|
|
|
//时间范围内匹配次数
|
|
|
float compareNum = 0;
|
|
|
+
|
|
|
+ boolean newMeasure = false;
|
|
|
+ float preMusicalNotesPitch = 0;
|
|
|
+ if(userSoundInfoMap.get(phone).getMusicalNotePitchMap().containsKey(musicXmlInfo.getMusicalNotesIndex()-1)){
|
|
|
+ preMusicalNotesPitch = userSoundInfoMap.get(phone).getMusicalNotePitchMap().get(musicXmlInfo.getMusicalNotesIndex()-1);
|
|
|
+ }
|
|
|
+ int newNum = 0;
|
|
|
+
|
|
|
for (MusicPitchDetailDto recordInfo : userSoundInfoMap.get(phone).getRecordMeasurePithInfo()) {
|
|
|
+ if(musicXmlInfo.getMusicalNotesIndex()==0){
|
|
|
+ newMeasure = true;
|
|
|
+ }
|
|
|
+ if(newMeasure){
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(Math.abs(recordInfo.getFrequency()-preMusicalNotesPitch)>10){
|
|
|
+ newNum++;
|
|
|
+ }
|
|
|
+ if(newNum>=2){
|
|
|
+ newMeasure = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Integer> musicalNotesPitchs = 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;
|
|
|
}
|
|
|
+ musicalNotesPitchs.add((int) recordInfo.getFrequency());
|
|
|
+ if(!newMeasure){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
// LOGGER.info("{}频率({}-{}):{}, {}", recordInfo.getTimeStamp(), startTimeStamp, endTimeStamp, musicXmlInfo.getFrequency(), recordInfo.getFrequency());
|
|
|
compareNum++;
|
|
|
//如果在最低有效频率以下则跳过
|
|
|
if(recordInfo.getFrequency()<minValidFrequency&&musicXmlInfo.getFrequency()!=-1){
|
|
|
continue;
|
|
|
}
|
|
|
- recordValidNum++;
|
|
|
+ cadenceValidNum++;
|
|
|
+ if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
//如果频率差值在节奏误差范围内
|
|
|
- if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=cadenceErrRange){
|
|
|
- cadenceValidNum++;
|
|
|
+ if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=integrityRange){
|
|
|
+ integrityValidNum++;
|
|
|
}
|
|
|
//如果频率差值在音准误差范围内
|
|
|
if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=intonationErrRange){
|
|
|
recordValidIntonationNum++;
|
|
|
}
|
|
|
}
|
|
|
- //有效音频占比
|
|
|
- float recordValidDuty = recordValidNum/compareNum;
|
|
|
- if(recordValidDuty<validDuty){
|
|
|
- continue;
|
|
|
+
|
|
|
+ if(CollectionUtils.isEmpty(musicalNotesPitchs)){
|
|
|
+ userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) 0);
|
|
|
+ }else{
|
|
|
+ Map<Integer, Long> collect = musicalNotesPitchs.stream().collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()));
|
|
|
+ Integer pitch = collect.entrySet().stream().max(Comparator.comparing(e -> e.getValue())).get().getKey();
|
|
|
+ userSoundInfoMap.get(phone).getMusicalNotePitchMap().put(musicXmlInfo.getMusicalNotesIndex(), (float) pitch);
|
|
|
}
|
|
|
- validNum++;
|
|
|
+
|
|
|
+ //有效音频占比
|
|
|
+ float integrityDuty = integrityValidNum/compareNum;
|
|
|
//有效音高占比
|
|
|
float intonationDuty = recordValidIntonationNum/compareNum;
|
|
|
//有效节奏占比
|
|
|
float cadenceDuty = cadenceValidNum/compareNum;
|
|
|
- if(intonationDuty>=intonationValidDuty){
|
|
|
- intonationNum++;
|
|
|
- }
|
|
|
+ //节奏
|
|
|
if(cadenceDuty>=cadenceValidDuty){
|
|
|
cadenceNum++;
|
|
|
+ if(intonationDuty>=intonationValidDuty){
|
|
|
+ intonationNum++;
|
|
|
+ }
|
|
|
+ if(integrityDuty>=integrityValidDuty){
|
|
|
+ integrityNum++;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
+ integrity = new BigDecimal(integrityNum).divide(new BigDecimal(totalCompareNum), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_HALF_UP);
|
|
|
} catch (ArithmeticException e){
|
|
|
LOGGER.info("无musicXml信息");
|
|
|
}
|
|
|
@@ -382,6 +436,169 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("measureScore", measureIndex, intonation, cadence, integrity))));
|
|
|
}
|
|
|
|
|
|
+ private void measureCompare2(String phone, MusicPitchDetailDto measureTimeInfo) throws IOException, UnsupportedAudioFileException {
|
|
|
+ //小节总时长
|
|
|
+ double measureTime = measureTimeInfo.getEndTimeStamp()-measureTimeInfo.getTimeStamp();
|
|
|
+ double ot = measureTime * 0.1;
|
|
|
+ measureTime += ot;
|
|
|
+ //小节时长占用字节数
|
|
|
+ int measureByteNum = (int) (measureTime/1000*(audioFormat.getFrameSize()*audioFormat.getFrameRate()));
|
|
|
+
|
|
|
+ List<MusicPitchDetailDto> recordInfo = new ArrayList<>();
|
|
|
+ byte[] bytes = new byte[measureByteNum];
|
|
|
+ long startOffset = (userSoundInfoMap.get(phone).getAccessFile().length()-measureByteNum);
|
|
|
+ userSoundInfoMap.get(phone).getAccessFile().seek(startOffset);
|
|
|
+// userSoundInfoMap.get(phone).getAccessFile().seek(0);
|
|
|
+ userSoundInfoMap.get(phone).getAccessFile().readFully(bytes);
|
|
|
+
|
|
|
+ userSoundInfoMap.get(phone).getAccessFile().seek(userSoundInfoMap.get(phone).getAccessFile().length());
|
|
|
+
|
|
|
+ AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(bytes, audioFormat, simpleSize, 128);
|
|
|
+ dispatcher.addAudioProcessor(new PitchProcessor(algo, simpleRate, simpleSize, (pitchDetectionResult, audioEvent) -> {
|
|
|
+ int timeStamp = (int) (measureTimeInfo.getTimeStamp() - (ot>measureTimeInfo.getTimeStamp()?0:ot) + audioEvent.getTimeStamp()*1000);
|
|
|
+ float pitch = pitchDetectionResult.getPitch();
|
|
|
+ recordInfo.add(new MusicPitchDetailDto(timeStamp, pitch));
|
|
|
+ }));
|
|
|
+ dispatcher.run();
|
|
|
+ userSoundInfoMap.get(phone).getRecordMeasurePithInfo().addAll(recordInfo);
|
|
|
+ LOGGER.info("小节评分频率{}:{}", measureTimeInfo.getMeasureIndex(), JSON.toJSONString(recordInfo));
|
|
|
+ scoreCal(phone, measureTimeInfo, recordInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void scoreCal(String phone, MusicPitchDetailDto measureTimeInfo, List<MusicPitchDetailDto> recordPitchDetails) throws IOException {
|
|
|
+ //相似度
|
|
|
+ BigDecimal intonation = BigDecimal.ZERO;
|
|
|
+ //节奏
|
|
|
+ BigDecimal cadence = BigDecimal.ZERO;
|
|
|
+ //完整度
|
|
|
+ BigDecimal integrity = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ try {
|
|
|
+ //最低有效频率
|
|
|
+ float minValidFrequency = 20;
|
|
|
+
|
|
|
+ //音准匹配数量
|
|
|
+ float intonationNum = 0;
|
|
|
+ //音准匹配误差范围
|
|
|
+ float intonationErrRange = 15;
|
|
|
+ //音准有效阈值
|
|
|
+ float intonationValidDuty = 0.1f;
|
|
|
+
|
|
|
+ //节奏匹配数量
|
|
|
+ float cadenceNum = 0;
|
|
|
+ //节奏有效阈值
|
|
|
+ float cadenceValidDuty = 0.1f;
|
|
|
+
|
|
|
+ //完整性数量
|
|
|
+ float integrityNum = 0;
|
|
|
+ //完整性误差范围
|
|
|
+ float integrityRange = 30;
|
|
|
+ //完整性有效阈值
|
|
|
+ float integrityValidDuty = 0.5f;
|
|
|
+
|
|
|
+ int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureTimeInfo.getMeasureIndex()).size();
|
|
|
+
|
|
|
+ for (MusicPitchDetailDto musicXmlInfo : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureTimeInfo.getMeasureIndex())) {
|
|
|
+ int ot5 = (int) (musicXmlInfo.getDuration()*0.1);
|
|
|
+ int startTimeStamp = musicXmlInfo.getTimeStamp() - ot5;
|
|
|
+ int endTimeStamp = musicXmlInfo.getTimeStamp() + ot5;
|
|
|
+
|
|
|
+ //时间范围内有效音准数量
|
|
|
+ float recordValidIntonationNum = 0;
|
|
|
+ //时间范围内有效节奏数量
|
|
|
+ float cadenceValidNum = 0;
|
|
|
+ //时间范围内有效音频数量
|
|
|
+ float integrityValidNum = 0;
|
|
|
+ //时间范围内匹配次数
|
|
|
+ float compareNum = 0;
|
|
|
+ int faultNum = 0;
|
|
|
+ for (int i = 0; i < recordPitchDetails.size(); i++) {
|
|
|
+ MusicPitchDetailDto recordInfo = recordPitchDetails.get(i);
|
|
|
+ if(recordInfo.getTimeStamp()>(startTimeStamp-ot5)&&Math.abs((recordInfo.getFrequency()-musicXmlInfo.getFrequency()))>20){
|
|
|
+ faultNum++;
|
|
|
+ }else{
|
|
|
+ if(faultNum<6)
|
|
|
+ faultNum = 0;
|
|
|
+ }
|
|
|
+ if(faultNum<6){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //如果在时间范围之外直接跳过
|
|
|
+ if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+// LOGGER.info("{}频率({}-{}):{}, {}", recordInfo.getTimeStamp(), startTimeStamp, endTimeStamp, musicXmlInfo.getFrequency(), recordInfo.getFrequency());
|
|
|
+ compareNum++;
|
|
|
+ //如果在最低有效频率以下则跳过
|
|
|
+ if(recordInfo.getFrequency()<minValidFrequency&&musicXmlInfo.getFrequency()!=-1){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ cadenceValidNum++;
|
|
|
+ if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //如果频率差值在节奏误差范围内
|
|
|
+ if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=integrityRange){
|
|
|
+ integrityValidNum++;
|
|
|
+ }
|
|
|
+ //如果频率差值在音准误差范围内
|
|
|
+ if(Math.abs(recordInfo.getFrequency()-musicXmlInfo.getFrequency())<=intonationErrRange){
|
|
|
+ recordValidIntonationNum++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //有效音频占比
|
|
|
+ float integrityDuty = integrityValidNum/compareNum;
|
|
|
+ //有效音高占比
|
|
|
+ float intonationDuty = recordValidIntonationNum/compareNum;
|
|
|
+ //有效节奏占比
|
|
|
+ float cadenceDuty = cadenceValidNum/compareNum;
|
|
|
+ //节奏
|
|
|
+ if(cadenceDuty>=cadenceValidDuty){
|
|
|
+ cadenceNum++;
|
|
|
+ if(intonationDuty>=intonationValidDuty){
|
|
|
+ intonationNum++;
|
|
|
+ }
|
|
|
+ if(integrityDuty>=integrityValidDuty){
|
|
|
+ integrityNum++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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(integrityNum).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(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);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, BigDecimal> scoreData = new HashMap<>();
|
|
|
+ scoreData.put("intonation", intonation);
|
|
|
+ scoreData.put("cadence", cadence);
|
|
|
+ scoreData.put("integrity", integrity);
|
|
|
+
|
|
|
+ userSoundInfoMap.get(phone).getUserMeasureScoreMap().put(measureTimeInfo.getMeasureIndex(), scoreData);
|
|
|
+
|
|
|
+ WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("measureScore", measureTimeInfo.getMeasureIndex(), intonation, cadence, integrity))));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* @describe 计算最终评分
|
|
|
* @author Joburgess
|
|
|
@@ -399,7 +616,7 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
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 = new BigDecimal(currentCompareNum).divide(new BigDecimal(totalCompareNum), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(oneHundred).setScale(0, BigDecimal.ROUND_DOWN);
|
|
|
+ integrity = userSoundInfoMap.get(phone).getUserScoreMap().get("integrity").divide(new BigDecimal(currentCompareNum), 0, BigDecimal.ROUND_DOWN);
|
|
|
}
|
|
|
|
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("overall", -1, intonation, cadence, integrity))));
|
|
|
@@ -426,8 +643,9 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
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 = cadence.setScale(0, BigDecimal.ROUND_HALF_UP);
|
|
|
+// 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_HALF_UP);
|
|
|
+// BigDecimal score = intonation.setScale(0, BigDecimal.ROUND_HALF_UP);
|
|
|
result.put("score", score);
|
|
|
result.put("intonation", intonation);
|
|
|
result.put("cadence", cadence);
|