|
@@ -15,6 +15,7 @@ import java.util.stream.Collectors;
|
|
|
|
|
|
import javax.sound.sampled.AudioFormat;
|
|
|
|
|
|
+import com.yonge.audio.analysis.AudioFloatConverter;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
@@ -27,6 +28,7 @@ import com.yonge.netty.entity.MusicXmlBasicInfo;
|
|
|
import com.yonge.netty.entity.MusicXmlNote;
|
|
|
import com.yonge.netty.entity.MusicXmlSection;
|
|
|
import com.yonge.netty.server.processor.WaveformWriter;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
|
|
|
/**
|
|
|
* 用户通道上下文
|
|
@@ -89,20 +91,22 @@ public class UserChannelContext {
|
|
|
|
|
|
NotePlayResult result = new NotePlayResult();
|
|
|
|
|
|
- boolean status = false;
|
|
|
+ int status;
|
|
|
double migrationRate = 0;
|
|
|
|
|
|
if (Math.round(xmlNote.getFrequency()) == Math.round(playFrequency)) {
|
|
|
- status = true;
|
|
|
+ status = 0;
|
|
|
migrationRate = 0;
|
|
|
} else {
|
|
|
NoteFrequencyRange noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, xmlNote.getFrequency());
|
|
|
|
|
|
- if (noteFrequencyRange.getMinFrequency() > playFrequency || playFrequency > noteFrequencyRange.getMaxFrequency()) {
|
|
|
- status = false;
|
|
|
+ if (noteFrequencyRange.getMinFrequency() > playFrequency ) {
|
|
|
+ status = 1;
|
|
|
+ } else if( playFrequency > noteFrequencyRange.getMaxFrequency()){
|
|
|
+ status = 2;
|
|
|
} else {
|
|
|
|
|
|
- status = true;
|
|
|
+ status = 0;
|
|
|
|
|
|
if (Math.round(playFrequency) < Math.round(xmlNote.getFrequency())) {
|
|
|
double min = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMinFrequency()) / 2;
|
|
@@ -394,7 +398,8 @@ public class UserChannelContext {
|
|
|
}
|
|
|
|
|
|
//判断节奏(音符持续时间内有不间断的音高,就节奏正确)
|
|
|
- boolean tempo = true;
|
|
|
+ // 节奏 0:正常 1:错误 2:节奏慢 3:节奏快
|
|
|
+ int tempo = 0;
|
|
|
if (percussionList.contains(subjectId)) {
|
|
|
noteAnalysis.setPlayFrequency(-1);
|
|
|
tempo = computeTempoWithAmplitude2(musicXmlNote);
|
|
@@ -408,7 +413,7 @@ public class UserChannelContext {
|
|
|
evaluateForNote(musicXmlNote, noteAnalysis);//对当前音符评分
|
|
|
|
|
|
LOGGER.debug("当前音符下标[{}] 预计频率:{} 实际频率:{} 节奏:{}", noteAnalysis.getMusicalNotesIndex(), musicXmlNote.getFrequency(), noteAnalysis.getPlayFrequency(),
|
|
|
- noteAnalysis.isTempo());
|
|
|
+ noteAnalysis.getTempo());
|
|
|
|
|
|
doneNoteAnalysisList.add(noteAnalysis);
|
|
|
|
|
@@ -540,9 +545,9 @@ public class UserChannelContext {
|
|
|
double playDurationTime = 0;
|
|
|
|
|
|
if (percussionList.contains(subjectId)) {
|
|
|
- if (noteAnalysis.getFrequency() == -1) {// 休止符
|
|
|
- if (!noteAnalysis.isTempo()) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
|
+ if (noteAnalysis.getFrequency() == -1) { // 休止符
|
|
|
+ if (noteAnalysis.getTempo() != 0 ) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorTempo(noteAnalysis.getTempo()));
|
|
|
} else {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
|
}
|
|
@@ -550,8 +555,8 @@ public class UserChannelContext {
|
|
|
int beatTimes = (int) chunkAnalysisList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count();
|
|
|
if(beatTimes == 0){
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.NOT_PLAY);
|
|
|
- }else if (!noteAnalysis.isTempo()) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
|
+ }else if (noteAnalysis.getTempo() != 0) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorTempo(noteAnalysis.getTempo()));
|
|
|
} else {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
|
}
|
|
@@ -564,12 +569,12 @@ public class UserChannelContext {
|
|
|
|
|
|
playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MIN_FREQUECY).mapToDouble(t -> t.getDurationTime()).sum();
|
|
|
|
|
|
- if (!noteAnalysis.isTempo()) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
|
+ if (noteAnalysis.getTempo() != 0) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorTempo(noteAnalysis.getTempo()));
|
|
|
} else if (playDurationTime * 100 / durationTime < hardLevel.getIntegrityRange()) {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
|
- } else if (notePlayResult.getStatus() == false) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
|
+ } else if (notePlayResult.getStatus() != 0) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorStatus(notePlayResult.getStatus()));
|
|
|
} else {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
|
}
|
|
@@ -583,10 +588,10 @@ public class UserChannelContext {
|
|
|
} else if (playDurationTime * 100 / durationTime < hardLevel.getIntegrityRange()) {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
|
|
|
LOGGER.debug("完整度不足:{}", playDurationTime * 100 / durationTime);
|
|
|
- } else if (!noteAnalysis.isTempo()) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
|
|
|
- } else if (notePlayResult.getStatus() == false) {
|
|
|
- noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
|
|
|
+ } else if (noteAnalysis.getTempo() != 0) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorTempo(noteAnalysis.getTempo()));
|
|
|
+ } else if (notePlayResult.getStatus() != 0) {
|
|
|
+ noteAnalysis.setMusicalErrorType(setMusicalErrorStatus(notePlayResult.getStatus()));
|
|
|
} else {
|
|
|
noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
|
|
|
}
|
|
@@ -610,7 +615,7 @@ public class UserChannelContext {
|
|
|
intonationScore = 0;
|
|
|
} else {
|
|
|
|
|
|
- if (noteAnalysis.isTempo()) {
|
|
|
+ if (noteAnalysis.getTempo() == 0) {
|
|
|
tempoScore = 100;
|
|
|
noteAnalysis.setTempoScore(tempoScore);
|
|
|
}
|
|
@@ -629,7 +634,31 @@ public class UserChannelContext {
|
|
|
.intValue());
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ // 设置 音高
|
|
|
+ private NoteErrorType setMusicalErrorStatus(int status) {
|
|
|
+ if (status == 1) {
|
|
|
+ return NoteErrorType.INTONATION_LOW;
|
|
|
+ } else if (status ==2) {
|
|
|
+ return NoteErrorType.INTONATION_HIGH;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置节奏
|
|
|
+ private NoteErrorType setMusicalErrorTempo(int tempo) {
|
|
|
+ if (tempo == 1) {
|
|
|
+ return NoteErrorType.CADENCE_WRONG;
|
|
|
+ } else if (tempo == 2) {
|
|
|
+ return NoteErrorType.CADENCE_SLOW;
|
|
|
+ } else if (tempo ==3) {
|
|
|
+
|
|
|
+ return NoteErrorType.CADENCE_FAST;
|
|
|
+ } else {
|
|
|
+ return NoteErrorType.RIGHT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private int computeFrequency(MusicXmlNote musicXmlNote) {
|
|
|
|
|
|
double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) / 100;
|
|
@@ -680,7 +709,7 @@ public class UserChannelContext {
|
|
|
* @param musicXmlNote
|
|
|
* @return
|
|
|
*/
|
|
|
- private boolean computeTempoWithFrequency(MusicXmlNote musicXmlNote){
|
|
|
+ private int computeTempoWithFrequency(MusicXmlNote musicXmlNote){
|
|
|
|
|
|
double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) / 100;
|
|
|
|
|
@@ -702,11 +731,11 @@ public class UserChannelContext {
|
|
|
List<ChunkAnalysis> chunkList = chunkAnalysisList.subList(startIndex, elementSize + startIndex);
|
|
|
|
|
|
if(chunkList == null || chunkList.size() == 0){
|
|
|
- return false;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
if (musicXmlNote.getFrequency() == -1) {// 休止符
|
|
|
- return chunkList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY).count() <= 1;
|
|
|
+ return chunkList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY).count() <= 1? 0:1;
|
|
|
}
|
|
|
|
|
|
ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
|
|
@@ -742,7 +771,7 @@ public class UserChannelContext {
|
|
|
|
|
|
NoteFrequencyRange noteFrequencyRange = null;
|
|
|
ChunkAnalysis chunkAnalysis = null;
|
|
|
- boolean tempo = true;
|
|
|
+ int tempo = 0;
|
|
|
//boolean isContinue = true;
|
|
|
//int unplayedSize = 0;
|
|
|
int firstPeakIndex = -1;
|
|
@@ -784,7 +813,7 @@ public class UserChannelContext {
|
|
|
}
|
|
|
|
|
|
if (maxTimes * 100 / totalTimes < hardLevel.getIntegrityRange()) {
|
|
|
- tempo = false;
|
|
|
+ tempo = 1;
|
|
|
LOGGER.debug("节奏错误原因:信号分堆后的最大数量不足指定的完成比例");
|
|
|
}
|
|
|
|
|
@@ -827,16 +856,17 @@ public class UserChannelContext {
|
|
|
}
|
|
|
*/
|
|
|
|
|
|
- if (tempo) {
|
|
|
+ if (tempo == 0) {
|
|
|
// 判断进入时间点
|
|
|
if(firstPeakIndex * 100 /chunkList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator())){
|
|
|
- tempo = false;
|
|
|
+ // 节奏慢
|
|
|
+ tempo = 2;
|
|
|
LOGGER.debug("节奏错误原因:进入时间点太晚");
|
|
|
}else{
|
|
|
//判断是否与上一个音延续下来的
|
|
|
if(firstChunkAnalysis.getFrequency() > MIN_FREQUECY && lastChunkAnalysis.getFrequency() > MIN_FREQUECY){
|
|
|
- tempo = new NoteFrequencyRange(standardFrequecy, firstChunkAnalysis.getFrequency()).equals(new NoteFrequencyRange(standardFrequecy, lastChunkAnalysis.getFrequency())) == false;
|
|
|
- if(tempo == false){
|
|
|
+ tempo = new NoteFrequencyRange(standardFrequecy, firstChunkAnalysis.getFrequency()).equals(new NoteFrequencyRange(standardFrequecy, lastChunkAnalysis.getFrequency())) == false?0:1;
|
|
|
+ if(tempo == 1){
|
|
|
LOGGER.debug("节奏错误原因:上一个音[{}]延续下来导致的", lastChunkAnalysis.getFrequency());
|
|
|
}
|
|
|
}
|
|
@@ -846,7 +876,7 @@ public class UserChannelContext {
|
|
|
return tempo;
|
|
|
}
|
|
|
|
|
|
- private boolean computeTempoWithAmplitude2(MusicXmlNote musicXmlNote) {
|
|
|
+ private int computeTempoWithAmplitude2(MusicXmlNote musicXmlNote) {
|
|
|
|
|
|
double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) / 100;
|
|
|
|
|
@@ -867,7 +897,7 @@ public class UserChannelContext {
|
|
|
List<ChunkAnalysis> chunkList = chunkAnalysisList.subList(0, elementSize);
|
|
|
|
|
|
if(chunkList == null || chunkList.size() == 0){
|
|
|
- return false;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
|
|
@@ -878,7 +908,7 @@ public class UserChannelContext {
|
|
|
if (musicXmlNote.getFrequency() == -1) {// 休止符
|
|
|
|
|
|
LOGGER.debug("--Amplitude:{} Denominator:{}",chunkList.stream().map(t -> t).collect(Collectors.toList()), musicXmlNote.getDenominator());
|
|
|
- return chunkList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count() <= 0;
|
|
|
+ return chunkList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count() <= 0 ? 0:1;
|
|
|
}
|
|
|
|
|
|
Optional<ChunkAnalysis> chunkAnalysisOptional = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getEndTime()) < Double.doubleToLongBits(firstChunkAnalysis.getStartTime())).findFirst();
|
|
@@ -898,7 +928,7 @@ public class UserChannelContext {
|
|
|
LOGGER.debug("--Amplitude:{} Denominator:{}",chunkAmplitudeList.stream().map(t -> t).collect(Collectors.toList()), musicXmlNote.getDenominator());
|
|
|
|
|
|
// 检测是否有多个波峰
|
|
|
- boolean tempo = false;
|
|
|
+ int tempo = 1;
|
|
|
boolean isContinue = true;
|
|
|
int firstPeakIndex = -1;
|
|
|
int peakSize = 0;
|
|
@@ -907,36 +937,36 @@ public class UserChannelContext {
|
|
|
continue;
|
|
|
}
|
|
|
if (chunkAmplitudeList.get(i) > hardLevel.getAmplitudeThreshold() && chunkAmplitudeList.get(i) > chunkAmplitudeList.get(i - 1)) {
|
|
|
- tempo = true;
|
|
|
+ tempo = 0;
|
|
|
if(firstPeakIndex == -1){
|
|
|
firstPeakIndex = i;
|
|
|
peakSize++;
|
|
|
}
|
|
|
if (isContinue == false) {
|
|
|
- tempo = false;
|
|
|
+ tempo = 1;
|
|
|
peakSize++;
|
|
|
break;
|
|
|
}
|
|
|
} else {
|
|
|
- if (tempo == true) {
|
|
|
+ if (tempo == 0) {
|
|
|
isContinue = false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(peakSize == 0){
|
|
|
- tempo = lastChunkAnalysis.isPeak();
|
|
|
+ tempo = lastChunkAnalysis.isPeak() ?0:1;
|
|
|
}else if(peakSize == 1){
|
|
|
- tempo = true;
|
|
|
+ tempo = 0;
|
|
|
}else{
|
|
|
- tempo = false;
|
|
|
+ tempo = 1;
|
|
|
}
|
|
|
|
|
|
- if (tempo) {
|
|
|
+ if (tempo == 0) {
|
|
|
// 判断进入时间点
|
|
|
if((firstPeakIndex - 1) * 100 /chunkAmplitudeList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()) * 2){
|
|
|
LOGGER.debug("超过范围:{}", (firstPeakIndex - 1) * 100 /chunkAmplitudeList.size());
|
|
|
- tempo = false;
|
|
|
+ tempo = 1;
|
|
|
}
|
|
|
}
|
|
|
|