|
@@ -6,6 +6,7 @@ import be.tarsos.dsp.pitch.PitchProcessor;
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.ym.mec.biz.dal.dto.MusicPitchDetailDto;
|
|
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.WavHeader;
|
|
import com.ym.mec.biz.dal.dto.WebSocketInfo;
|
|
import com.ym.mec.biz.dal.dto.WebSocketInfo;
|
|
import com.ym.mec.biz.service.SoundSocketService;
|
|
import com.ym.mec.biz.service.SoundSocketService;
|
|
@@ -42,19 +43,12 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
|
|
|
|
private BigDecimal oneHundred = new BigDecimal(100);
|
|
private BigDecimal oneHundred = new BigDecimal(100);
|
|
|
|
|
|
- AudioFormat audioFormat = new AudioFormat(44100, 16, 1, true, false);
|
|
|
|
- PitchProcessor.PitchEstimationAlgorithm algo = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN;
|
|
|
|
|
|
+ private static final AudioFormat audioFormat = new AudioFormat(44100, 16, 1, true, false);
|
|
|
|
+ private static final PitchProcessor.PitchEstimationAlgorithm algo = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN;
|
|
|
|
|
|
- private String tmpDir = FileUtils.getTempDirectoryPath() + "/soundCompare/";
|
|
|
|
|
|
+ private static final String tmpDir = FileUtils.getTempDirectoryPath() + "/soundCompare/";
|
|
|
|
|
|
- private Map<String, RandomAccessFile> userRandomAccessFileMap = 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<>();
|
|
|
|
- private Map<String, Integer> userMusicIdMap = new ConcurrentHashMap<>();
|
|
|
|
|
|
+ private Map<String, SoundCompareHelper> userSoundInfoMap = new ConcurrentHashMap<>();
|
|
|
|
|
|
public WebSocketHandler() {
|
|
public WebSocketHandler() {
|
|
super();
|
|
super();
|
|
@@ -91,26 +85,22 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
}
|
|
}
|
|
switch (commond){
|
|
switch (commond){
|
|
case SoundSocketService.MUSIC_XML:
|
|
case SoundSocketService.MUSIC_XML:
|
|
|
|
+ userSoundInfoMap.put(phone, new SoundCompareHelper());
|
|
List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
|
|
List<MusicPitchDetailDto> musicXmlInfos = JSON.parseArray(bodyObject.getString("musicXmlInfos"), MusicPitchDetailDto.class);
|
|
- userMusicIdMap.put(phone, bodyObject.getInteger("id"));
|
|
|
|
- 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()) {
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).setMusicScoreId(bodyObject.getInteger("id"));
|
|
|
|
+ userSoundInfoMap.get(phone).setMeasureXmlInfoMap(musicXmlInfos.stream().collect(Collectors.groupingBy(MusicPitchDetailDto::getMeasureIndex)));
|
|
|
|
+ for (Map.Entry<Integer, List<MusicPitchDetailDto>> userMeasureXmlInfoEntry : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().entrySet()) {
|
|
MusicPitchDetailDto musicPitchDetailDto = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
|
|
MusicPitchDetailDto musicPitchDetailDto = userMeasureXmlInfoEntry.getValue().stream().max(Comparator.comparing(MusicPitchDetailDto::getTimeStamp)).get();
|
|
- userMeasureEndTimeMap.get(phone).put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto.getTimeStamp()+musicPitchDetailDto.getDuration());
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).getMeasureEndTime().put(userMeasureXmlInfoEntry.getKey(), musicPitchDetailDto.getTimeStamp()+musicPitchDetailDto.getDuration());
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case SoundSocketService.RECORD_START:
|
|
case SoundSocketService.RECORD_START:
|
|
- File file = new File(tmpDir+phone + "_"+ userMusicIdMap.get(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);
|
|
|
|
|
|
+ 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"));
|
|
break;
|
|
break;
|
|
case SoundSocketService.RECORD_END:
|
|
case SoundSocketService.RECORD_END:
|
|
- if(!CollectionUtils.isEmpty(userMeasureEndTimeMap.get(phone))){
|
|
|
|
- measureCompare(phone, userMeasureEndTimeMap.get(phone).keySet().stream().max(Integer::compareTo).get());
|
|
|
|
|
|
+ if(!CollectionUtils.isEmpty(userSoundInfoMap.get(phone).getMeasureEndTime())){
|
|
|
|
+ measureCompare(phone, userSoundInfoMap.get(phone).getMeasureEndTime().keySet().stream().max(Integer::compareTo).get());
|
|
}
|
|
}
|
|
calTotalScore(phone);
|
|
calTotalScore(phone);
|
|
createHeader(phone);
|
|
createHeader(phone);
|
|
@@ -124,24 +114,24 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
|
|
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
|
|
super.handleBinaryMessage(session, message);
|
|
super.handleBinaryMessage(session, message);
|
|
String phone = session.getPrincipal().getName().split(":")[1];
|
|
String phone = session.getPrincipal().getName().split(":")[1];
|
|
- if(userRandomAccessFileMap.containsKey(phone)){
|
|
|
|
- userRandomAccessFileMap.get(phone).write(message.getPayload().array());
|
|
|
|
|
|
+ if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
|
|
|
|
+ userSoundInfoMap.get(phone).getAccessFile().write(message.getPayload().array());
|
|
}
|
|
}
|
|
List<MusicPitchDetailDto> recordInfo = new ArrayList<>();
|
|
List<MusicPitchDetailDto> recordInfo = new ArrayList<>();
|
|
AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), audioFormat, 256, 128);
|
|
AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(message.getPayload().array(), audioFormat, 256, 128);
|
|
dispatcher.addAudioProcessor(new PitchProcessor(algo, 44100, 256, (pitchDetectionResult, audioEvent) -> {
|
|
dispatcher.addAudioProcessor(new PitchProcessor(algo, 44100, 256, (pitchDetectionResult, audioEvent) -> {
|
|
- int timeStamp = (int) (userMeasureStartTimeMap.get(phone) + audioEvent.getTimeStamp()*1000);
|
|
|
|
|
|
+ int timeStamp = (int) (userSoundInfoMap.get(phone).getMeasureStartTime() + audioEvent.getTimeStamp()*1000);
|
|
float pitch = pitchDetectionResult.getPitch();
|
|
float pitch = pitchDetectionResult.getPitch();
|
|
recordInfo.add(new MusicPitchDetailDto(timeStamp, pitch));
|
|
recordInfo.add(new MusicPitchDetailDto(timeStamp, pitch));
|
|
}));
|
|
}));
|
|
dispatcher.run();
|
|
dispatcher.run();
|
|
- double recordTime = userRandomAccessFileMap.get(phone).length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000;
|
|
|
|
- userMeasureStartTimeMap.put(phone, recordTime);
|
|
|
|
- userLastMeasurePithInfoMap.get(phone).addAll(recordInfo);
|
|
|
|
- for (Map.Entry<Integer, Integer> userMeasureEndTimeMapEntry : userMeasureEndTimeMap.get(phone).entrySet()) {
|
|
|
|
|
|
+ double recordTime = userSoundInfoMap.get(phone).getAccessFile().length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000;
|
|
|
|
+ userSoundInfoMap.get(phone).setMeasureStartTime(recordTime);
|
|
|
|
+ userSoundInfoMap.get(phone).getRecordMeasurePithInfo().addAll(recordInfo);
|
|
|
|
+ for (Map.Entry<Integer, Integer> userMeasureEndTimeMapEntry : userSoundInfoMap.get(phone).getMeasureEndTime().entrySet()) {
|
|
if(recordTime>userMeasureEndTimeMapEntry.getValue()){
|
|
if(recordTime>userMeasureEndTimeMapEntry.getValue()){
|
|
measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
|
|
measureCompare(phone, userMeasureEndTimeMapEntry.getKey());
|
|
- userMeasureEndTimeMap.get(phone).remove(userMeasureEndTimeMapEntry.getKey());
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).getMeasureEndTime().remove(userMeasureEndTimeMapEntry.getKey());
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -180,15 +170,16 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
}
|
|
}
|
|
|
|
|
|
private void createHeader(String phone) throws IOException {
|
|
private void createHeader(String phone) throws IOException {
|
|
- if(!userRandomAccessFileMap.containsKey(phone)){
|
|
|
|
|
|
+ if(Objects.nonNull(userSoundInfoMap.get(phone).getAccessFile())){
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- RandomAccessFile randomAccessFile = userRandomAccessFileMap.get(phone);
|
|
|
|
|
|
+ RandomAccessFile randomAccessFile = userSoundInfoMap.get(phone).getAccessFile();
|
|
LOGGER.info("音频时长:{}", randomAccessFile.length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000);
|
|
LOGGER.info("音频时长:{}", randomAccessFile.length()/(audioFormat.getFrameSize()*audioFormat.getFrameRate())*1000);
|
|
randomAccessFile.seek(0);
|
|
randomAccessFile.seek(0);
|
|
randomAccessFile.write(WavHeader.getWaveHeader(randomAccessFile.length(), (long) audioFormat.getFrameRate(), audioFormat.getSampleSizeInBits()));
|
|
randomAccessFile.write(WavHeader.getWaveHeader(randomAccessFile.length(), (long) audioFormat.getFrameRate(), audioFormat.getSampleSizeInBits()));
|
|
randomAccessFile.close();
|
|
randomAccessFile.close();
|
|
- userRandomAccessFileMap.remove(phone);
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).setAccessFile(null);
|
|
|
|
+ userSoundInfoMap.remove(phone);
|
|
}
|
|
}
|
|
|
|
|
|
private void measureCompare(String phone, int measureIndex) throws IOException {
|
|
private void measureCompare(String phone, int measureIndex) throws IOException {
|
|
@@ -213,20 +204,20 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
//音准匹配数量
|
|
//音准匹配数量
|
|
float intonationNum = 0;
|
|
float intonationNum = 0;
|
|
//音准匹配误差范围
|
|
//音准匹配误差范围
|
|
- float intonationErrRange = 100;
|
|
|
|
|
|
+ float intonationErrRange = 70;
|
|
//音准有效阈值
|
|
//音准有效阈值
|
|
- float intonationValidDuty = 0.8f;
|
|
|
|
|
|
+ float intonationValidDuty = 0.7f;
|
|
|
|
|
|
//节奏匹配数量
|
|
//节奏匹配数量
|
|
float cadenceNum = 0;
|
|
float cadenceNum = 0;
|
|
//节奏匹配误差范围
|
|
//节奏匹配误差范围
|
|
- float cadenceErrRange = 100;
|
|
|
|
|
|
+ float cadenceErrRange = 130;
|
|
//节奏有效阈值
|
|
//节奏有效阈值
|
|
- float cadenceValidDuty = 0.7f;
|
|
|
|
|
|
+ float cadenceValidDuty = 0.5f;
|
|
|
|
|
|
- int totalCompareNum = userMeasureXmlInfoMap.get(phone).get(measureIndex).size();
|
|
|
|
|
|
+ int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex).size();
|
|
|
|
|
|
- for (MusicPitchDetailDto musicXmlInfo : userMeasureXmlInfoMap.get(phone).get(measureIndex)) {
|
|
|
|
|
|
+ for (MusicPitchDetailDto musicXmlInfo : userSoundInfoMap.get(phone).getMeasureXmlInfoMap().get(measureIndex)) {
|
|
int startTimeStamp = musicXmlInfo.getTimeStamp();
|
|
int startTimeStamp = musicXmlInfo.getTimeStamp();
|
|
int endTimeStamp = musicXmlInfo.getTimeStamp()+musicXmlInfo.getDuration();
|
|
int endTimeStamp = musicXmlInfo.getTimeStamp()+musicXmlInfo.getDuration();
|
|
|
|
|
|
@@ -238,7 +229,7 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
float recordValidNum = 0;
|
|
float recordValidNum = 0;
|
|
//时间范围内匹配次数
|
|
//时间范围内匹配次数
|
|
float compareNum = 0;
|
|
float compareNum = 0;
|
|
- for (MusicPitchDetailDto recordInfo : userLastMeasurePithInfoMap.get(phone)) {
|
|
|
|
|
|
+ for (MusicPitchDetailDto recordInfo : userSoundInfoMap.get(phone).getRecordMeasurePithInfo()) {
|
|
//如果在时间范围之外直接跳过
|
|
//如果在时间范围之外直接跳过
|
|
if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
if(recordInfo.getTimeStamp()<startTimeStamp||recordInfo.getTimeStamp()>endTimeStamp){
|
|
continue;
|
|
continue;
|
|
@@ -283,32 +274,32 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
|
|
LOGGER.info("无musicXml信息");
|
|
LOGGER.info("无musicXml信息");
|
|
}
|
|
}
|
|
|
|
|
|
- if(userTotalScoreMap.get(phone).containsKey("intonation")){
|
|
|
|
- userTotalScoreMap.get(phone).put("intonation", intonation.add(userTotalScoreMap.get(phone).get("intonation")));
|
|
|
|
|
|
+ if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("intonation")){
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation.add(userSoundInfoMap.get(phone).getUserScoreMap().get("intonation")));
|
|
}else{
|
|
}else{
|
|
- userTotalScoreMap.get(phone).put("intonation", intonation);
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("intonation", intonation);
|
|
}
|
|
}
|
|
|
|
|
|
- if(userTotalScoreMap.get(phone).containsKey("cadence")){
|
|
|
|
- userTotalScoreMap.get(phone).put("cadence", cadence.add(userTotalScoreMap.get(phone).get("cadence")));
|
|
|
|
|
|
+ if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("cadence")){
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence.add(userSoundInfoMap.get(phone).getUserScoreMap().get("cadence")));
|
|
}else{
|
|
}else{
|
|
- userTotalScoreMap.get(phone).put("cadence", cadence);
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("cadence", cadence);
|
|
}
|
|
}
|
|
|
|
|
|
- if(userTotalScoreMap.get(phone).containsKey("integrity")){
|
|
|
|
- userTotalScoreMap.get(phone).put("integrity", integrity.add(userTotalScoreMap.get(phone).get("integrity")));
|
|
|
|
|
|
+ if(userSoundInfoMap.get(phone).getUserScoreMap().containsKey("integrity")){
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity.add(userSoundInfoMap.get(phone).getUserScoreMap().get("integrity")));
|
|
}else{
|
|
}else{
|
|
- userTotalScoreMap.get(phone).put("integrity", integrity);
|
|
|
|
|
|
+ userSoundInfoMap.get(phone).getUserScoreMap().put("integrity", integrity);
|
|
}
|
|
}
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("measureScore", measureIndex, intonation, cadence, integrity))));
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("measureScore", measureIndex, intonation, cadence, integrity))));
|
|
// LOGGER.info("推送评分结果:{}", phone);
|
|
// LOGGER.info("推送评分结果:{}", phone);
|
|
}
|
|
}
|
|
|
|
|
|
private void calTotalScore(String phone) throws IOException {
|
|
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);
|
|
|
|
|
|
+ int totalCompareNum = userSoundInfoMap.get(phone).getMeasureXmlInfoMap().keySet().size();
|
|
|
|
+ BigDecimal intonation = userSoundInfoMap.get(phone).getUserScoreMap().get("intonation").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
|
|
|
|
+ BigDecimal cadence = userSoundInfoMap.get(phone).getUserScoreMap().get("cadence").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
|
|
|
|
+ BigDecimal integrity = userSoundInfoMap.get(phone).getUserScoreMap().get("integrity").divide(new BigDecimal(totalCompareNum), 0, BigDecimal.ROUND_DOWN);
|
|
|
|
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("overall", -1, intonation, cadence, integrity))));
|
|
WS_CLIENTS.get(phone).sendMessage(new TextMessage(JSON.toJSONString(createPushInfo("overall", -1, intonation, cadence, integrity))));
|
|
}
|
|
}
|