|
@@ -4,7 +4,6 @@ import java.math.BigDecimal;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
-import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Optional;
|
|
@@ -33,7 +32,9 @@ public class UserChannelContext {
|
|
|
|
|
|
private final static Logger LOGGER = LoggerFactory.getLogger(UserChannelContext.class);
|
|
private final static Logger LOGGER = LoggerFactory.getLogger(UserChannelContext.class);
|
|
|
|
|
|
- private int offsetMS = 350;
|
|
|
|
|
|
+ private double standardFrequecy = 442;
|
|
|
|
+
|
|
|
|
+ private int offsetMS;
|
|
|
|
|
|
private String platform;
|
|
private String platform;
|
|
|
|
|
|
@@ -45,6 +46,8 @@ public class UserChannelContext {
|
|
|
|
|
|
private int beatByteLength;
|
|
private int beatByteLength;
|
|
|
|
|
|
|
|
+ private boolean delayPrcessed;
|
|
|
|
+
|
|
// 曲目与musicxml对应关系
|
|
// 曲目与musicxml对应关系
|
|
private ConcurrentHashMap<Integer, MusicXmlBasicInfo> songMusicXmlMap = new ConcurrentHashMap<Integer, MusicXmlBasicInfo>();
|
|
private ConcurrentHashMap<Integer, MusicXmlBasicInfo> songMusicXmlMap = new ConcurrentHashMap<Integer, MusicXmlBasicInfo>();
|
|
|
|
|
|
@@ -68,6 +71,41 @@ public class UserChannelContext {
|
|
|
|
|
|
private HardLevelEnum hardLevel = HardLevelEnum.ADVANCED;
|
|
private HardLevelEnum hardLevel = HardLevelEnum.ADVANCED;
|
|
|
|
|
|
|
|
+ private NotePlayResult queryNoteFrequency(MusicXmlNote xmlNote, double playFrequency) {
|
|
|
|
+
|
|
|
|
+ NotePlayResult result = new NotePlayResult();
|
|
|
|
+
|
|
|
|
+ boolean status = false;
|
|
|
|
+ double migrationRate = 0;
|
|
|
|
+
|
|
|
|
+ if (Math.round(xmlNote.getFrequency()) == Math.round(playFrequency)) {
|
|
|
|
+ status = true;
|
|
|
|
+ migrationRate = 0;
|
|
|
|
+ } else {
|
|
|
|
+ NoteFrequencyRange noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, xmlNote.getFrequency());
|
|
|
|
+
|
|
|
|
+ if (noteFrequencyRange.getMinFrequency() > playFrequency || playFrequency > noteFrequencyRange.getMaxFrequency()) {
|
|
|
|
+ status = false;
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ status = true;
|
|
|
|
+
|
|
|
|
+ if (Math.round(playFrequency) < Math.round(xmlNote.getFrequency())) {
|
|
|
|
+ double min = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMinFrequency()) / 2;
|
|
|
|
+ migrationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / min;
|
|
|
|
+ } else {
|
|
|
|
+ double max = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMaxFrequency()) / 2;
|
|
|
|
+ migrationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / max;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ result.setStatus(status);
|
|
|
|
+ result.setMigrationRate(migrationRate);
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
public void init(String platform, String heardLevel, int subjectId, int beatDuration) {
|
|
public void init(String platform, String heardLevel, int subjectId, int beatDuration) {
|
|
this.platform = platform;
|
|
this.platform = platform;
|
|
this.subjectId = subjectId;
|
|
this.subjectId = subjectId;
|
|
@@ -151,6 +189,7 @@ public class UserChannelContext {
|
|
recordId = null;
|
|
recordId = null;
|
|
playTime = 0;
|
|
playTime = 0;
|
|
receivedTime = 0;
|
|
receivedTime = 0;
|
|
|
|
+ delayPrcessed = false;
|
|
}
|
|
}
|
|
|
|
|
|
public MusicXmlBasicInfo getMusicXmlBasicInfo(Integer songId){
|
|
public MusicXmlBasicInfo getMusicXmlBasicInfo(Integer songId){
|
|
@@ -311,6 +350,13 @@ public class UserChannelContext {
|
|
}
|
|
}
|
|
totalChunkAnalysisList.add(chunkAnalysis);
|
|
totalChunkAnalysisList.add(chunkAnalysis);
|
|
|
|
|
|
|
|
+ if(delayPrcessed == false && chunkAnalysis.getFrequency() > 100){
|
|
|
|
+
|
|
|
|
+ delayPrcessed = true;
|
|
|
|
+ //计算延迟偏移值
|
|
|
|
+ playTime = musicXmlNote.getTimeStamp() + durationTime;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (playTime >= (musicXmlNote.getDuration() + musicXmlNote.getTimeStamp())) {
|
|
if (playTime >= (musicXmlNote.getDuration() + musicXmlNote.getTimeStamp())) {
|
|
|
|
|
|
if (musicXmlNote.getDontEvaluating()) {
|
|
if (musicXmlNote.getDontEvaluating()) {
|
|
@@ -466,6 +512,9 @@ public class UserChannelContext {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
|
|
+
|
|
|
|
+ NotePlayResult notePlayResult = queryNoteFrequency(musicXmlNote, noteAnalysis.getPlayFrequency());
|
|
|
|
+
|
|
if (noteAnalysis.getFrequency() == -1) {// 休止符
|
|
if (noteAnalysis.getFrequency() == -1) {// 休止符
|
|
|
|
|
|
playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= 100).mapToDouble(t -> t.getDurationTime()).sum();
|
|
playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= 100).mapToDouble(t -> t.getDurationTime()).sum();
|
|
@@ -474,7 +523,7 @@ public class UserChannelContext {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
} else if (playDurationTime * 100 / noteAnalysis.getDurationTime() < hardLevel.getIntegrityRange()) {
|
|
} else if (playDurationTime * 100 / noteAnalysis.getDurationTime() < hardLevel.getIntegrityRange()) {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
- } else if (Math.abs(noteAnalysis.getFrequency() - noteAnalysis.getPlayFrequency()) > hardLevel.getFrequencyThreshold()) {
|
|
|
|
|
|
+ } else if (notePlayResult.getStatus() == false) {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
} else {
|
|
} else {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
@@ -489,7 +538,7 @@ public class UserChannelContext {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
} else if (!noteAnalysis.isTempo()) {
|
|
} else if (!noteAnalysis.isTempo()) {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
- } else if (Math.abs(noteAnalysis.getFrequency() - noteAnalysis.getPlayFrequency()) > hardLevel.getFrequencyThreshold()) {
|
|
|
|
|
|
+ } else if (notePlayResult.getStatus() == false) {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
} else {
|
|
} else {
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
@@ -552,95 +601,26 @@ public class UserChannelContext {
|
|
LOGGER.info("-------startTime:{} endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)
|
|
LOGGER.info("-------startTime:{} endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)
|
|
.getEndTime());
|
|
.getEndTime());
|
|
|
|
|
|
- Optional<ChunkAnalysis> chunkAnalysisOptional = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getEndTime()) == Double.doubleToLongBits(firstChunkAnalysis.getStartTime())).findFirst();
|
|
|
|
-
|
|
|
|
- ChunkAnalysis lastChunkAnalysis = null;
|
|
|
|
- if (chunkAnalysisOptional.isPresent()) {
|
|
|
|
- lastChunkAnalysis = chunkAnalysisOptional.get();
|
|
|
|
- }
|
|
|
|
- if(lastChunkAnalysis == null){
|
|
|
|
- lastChunkAnalysis = new ChunkAnalysis(0, 0, -1, 0, 0, 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
|
|
List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
|
|
|
|
|
|
- int tenutoSize = 0;
|
|
|
|
- // 剔除上一个音延续下来的信号
|
|
|
|
- if (lastChunkAnalysis != null) {
|
|
|
|
- int lastFrequency = lastChunkAnalysis.getFrequency();
|
|
|
|
- Iterator<ChunkAnalysis> iterable = chunkList.iterator();
|
|
|
|
- while (iterable.hasNext()) {
|
|
|
|
- if (Math.abs(lastFrequency - iterable.next().getFrequency()) > hardLevel.getFrequencyThreshold()) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- iterable.remove();
|
|
|
|
- tenutoSize++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (chunkList.size() == 0) {
|
|
|
|
- return lastFrequency < 100 ? -1 : lastFrequency;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
List<Integer> chunkFrequencyList = chunkList.stream().map(t -> t.getFrequency()).filter(t -> t.doubleValue() > 100 && t.doubleValue() < 2000)
|
|
List<Integer> chunkFrequencyList = chunkList.stream().map(t -> t.getFrequency()).filter(t -> t.doubleValue() > 100 && t.doubleValue() < 2000)
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
if (chunkFrequencyList.size() == 0) {
|
|
if (chunkFrequencyList.size() == 0) {
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
-
|
|
|
|
- if(tenutoSize * 100 / chunkAnalysisList.size() > 50){
|
|
|
|
- return lastChunkAnalysis.getFrequency();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 排序
|
|
|
|
- chunkFrequencyList = chunkFrequencyList.stream().sorted().collect(Collectors.toList());
|
|
|
|
-
|
|
|
|
- int tempFrequency = chunkFrequencyList.get(0), totalFrequency = chunkFrequencyList.get(0);
|
|
|
|
-
|
|
|
|
- int maxChunkSize = 0;
|
|
|
|
- int frequency = chunkFrequencyList.get(0);
|
|
|
|
- int chunkSize = 1;
|
|
|
|
- int avgFrequency = chunkFrequencyList.get(0);
|
|
|
|
- for (int i = 1; i < chunkFrequencyList.size(); i++) {
|
|
|
|
- tempFrequency = chunkFrequencyList.get(i);
|
|
|
|
-
|
|
|
|
- if (Math.abs(avgFrequency - tempFrequency) > hardLevel.getFrequencyThreshold()) {
|
|
|
|
-
|
|
|
|
- avgFrequency = totalFrequency / chunkSize;
|
|
|
|
-
|
|
|
|
- if (maxChunkSize < chunkSize) {
|
|
|
|
- maxChunkSize = chunkSize;
|
|
|
|
- frequency = avgFrequency;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chunkSize = 1;
|
|
|
|
- avgFrequency = tempFrequency;
|
|
|
|
- totalFrequency = tempFrequency;
|
|
|
|
- } else {
|
|
|
|
- chunkSize++;
|
|
|
|
- totalFrequency += tempFrequency;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (i == chunkFrequencyList.size() - 1) {
|
|
|
|
- if (maxChunkSize <= chunkSize) {
|
|
|
|
- maxChunkSize = chunkSize;
|
|
|
|
- frequency = totalFrequency / chunkSize;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
- if (chunkFrequencyList.size() < 3) {
|
|
|
|
- frequency = (int)chunkFrequencyList.get(chunkFrequencyList.size() - 1);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(frequency < 100){
|
|
|
|
- frequency = -1;
|
|
|
|
- }
|
|
|
|
|
|
+ int frequency = (int) (chunkFrequencyList.stream().mapToInt(t -> t).sum() / chunkFrequencyList.size());
|
|
|
|
|
|
return frequency;
|
|
return frequency;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 时值范围内有且只有一个音,且不能间断,且在合理范围内需开始演奏
|
|
|
|
+ * 与上一个音相同时,2个音之间需要间断
|
|
|
|
+ * @param musicXmlNote
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
private boolean computeTempoWithFrequency(MusicXmlNote musicXmlNote){
|
|
private boolean computeTempoWithFrequency(MusicXmlNote musicXmlNote){
|
|
|
|
|
|
double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) / 100;
|
|
double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) / 100;
|
|
@@ -672,22 +652,11 @@ public class UserChannelContext {
|
|
|
|
|
|
List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
|
|
List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
|
|
|
|
|
|
- // 剔除上一个音延续下来的信号
|
|
|
|
- if (lastChunkAnalysis != null) {
|
|
|
|
- double lastFrequency = lastChunkAnalysis.getFrequency();
|
|
|
|
- Iterator<ChunkAnalysis> iterable = chunkList.iterator();
|
|
|
|
- while (iterable.hasNext()) {
|
|
|
|
- if (Math.abs(lastFrequency - iterable.next().getFrequency()) > hardLevel.getFrequencyThreshold()) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- iterable.remove();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if(chunkList.size() == 0){
|
|
if(chunkList.size() == 0){
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ NoteFrequencyRange noteFrequencyRange = null;
|
|
ChunkAnalysis chunkAnalysis = null;
|
|
ChunkAnalysis chunkAnalysis = null;
|
|
boolean tempo = false;
|
|
boolean tempo = false;
|
|
boolean isContinue = true;
|
|
boolean isContinue = true;
|
|
@@ -697,21 +666,23 @@ public class UserChannelContext {
|
|
chunkAnalysis = chunkList.get(i);
|
|
chunkAnalysis = chunkList.get(i);
|
|
if (chunkAnalysis != null) {
|
|
if (chunkAnalysis != null) {
|
|
if (chunkAnalysis.getFrequency() > 100) {
|
|
if (chunkAnalysis.getFrequency() > 100) {
|
|
|
|
+
|
|
tempo = true;
|
|
tempo = true;
|
|
- if(firstPeakIndex == -1){
|
|
|
|
|
|
+ if (firstPeakIndex == -1) {
|
|
firstPeakIndex = i;
|
|
firstPeakIndex = i;
|
|
|
|
+ noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, chunkAnalysis.getFrequency());
|
|
|
|
+ } else if (noteFrequencyRange.getMinFrequency() > chunkAnalysis.getFrequency()
|
|
|
|
+ || chunkAnalysis.getFrequency() > noteFrequencyRange.getMaxFrequency()) {
|
|
|
|
+ // 判断是否是同一个音
|
|
|
|
+ tempo = false;
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
if (isContinue == false) {
|
|
if (isContinue == false) {
|
|
- if (chunkAnalysisList.size() < 5) {
|
|
|
|
|
|
+ if ((i + 1) / chunkAnalysisList.size() < hardLevel.getIntegrityRange()) {
|
|
if (unplayedSize > 0) {
|
|
if (unplayedSize > 0) {
|
|
tempo = false;
|
|
tempo = false;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- if ((unplayedSize * 100 / chunkAnalysisList.size()) > hardLevel.getNotPlayRange() || unplayedSize > 1) {
|
|
|
|
- tempo = false;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
@@ -725,8 +696,13 @@ public class UserChannelContext {
|
|
|
|
|
|
if (tempo) {
|
|
if (tempo) {
|
|
// 判断进入时间点
|
|
// 判断进入时间点
|
|
- if((chunkAnalysisList.size() - chunkList.size() + firstPeakIndex) * 100 /chunkAnalysisList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) * 2){
|
|
|
|
|
|
+ if(firstPeakIndex * 100 /chunkAnalysisList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) * 2){
|
|
tempo = false;
|
|
tempo = false;
|
|
|
|
+ }else{
|
|
|
|
+ //判断是否与上一个音延续下来的
|
|
|
|
+ if(firstChunkAnalysis.getFrequency() > 100 && lastChunkAnalysis.getFrequency() > 100){
|
|
|
|
+ return new NoteFrequencyRange(standardFrequecy, firstChunkAnalysis.getFrequency()).equals(new NoteFrequencyRange(standardFrequecy, lastChunkAnalysis.getFrequency())) == false;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -809,21 +785,14 @@ public class UserChannelContext {
|
|
}
|
|
}
|
|
|
|
|
|
public static void main(String[] args) {
|
|
public static void main(String[] args) {
|
|
- UserChannelContext context = new UserChannelContext();
|
|
|
|
-
|
|
|
|
- //int[] frequencys = {286,291,291,291,291,291,291};
|
|
|
|
- int[] frequencys = {312,43,295,294,294,295};
|
|
|
|
-
|
|
|
|
- List<ChunkAnalysis> chunkAnalysisList = new ArrayList<ChunkAnalysis>();
|
|
|
|
- for(int f : frequencys) {
|
|
|
|
- chunkAnalysisList.add(new ChunkAnalysis(f, 0, 0));
|
|
|
|
|
|
+ double[] midi = new double[128];;
|
|
|
|
+ int standardPitch = 440; // a is 440 hz...
|
|
|
|
+ for (int x = 0; x < midi.length; ++x)
|
|
|
|
+ {
|
|
|
|
+ midi[x] = new BigDecimal(standardPitch).multiply(new BigDecimal(Math.pow(2, new BigDecimal(x-69).divide(new BigDecimal(12),6,BigDecimal.ROUND_HALF_UP).doubleValue()))).doubleValue();
|
|
|
|
+ System.out.println("x=" + x +" "+ midi[x]);
|
|
}
|
|
}
|
|
|
|
|
|
- MusicXmlNote musicXmlNote = new MusicXmlNote();
|
|
|
|
- musicXmlNote.setDenominator(1);
|
|
|
|
-
|
|
|
|
- //System.out.println(context.computeFrequency(chunkAnalysisList, lastChunkAnalysis, 5));
|
|
|
|
- System.out.println(context.computeTempoWithFrequency(musicXmlNote));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|