yonge 3 年之前
父節點
當前提交
4a908847af

+ 60 - 0
audio-analysis/src/main/java/com/yonge/netty/dto/MusicalInstrumentsPitchRange.java

@@ -0,0 +1,60 @@
+package com.yonge.netty.dto;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 不同乐器对应的音域范围
+ */
+public class MusicalInstrumentsPitchRange {
+
+	private final static double STANDARD_PITCH = 440;
+
+	private double minPitch;
+
+	private double maxPitch;
+
+	private MusicalInstrumentsPitchRange(double minPitch, double maxPitch) {
+		this.maxPitch = maxPitch;
+		this.minPitch = minPitch;
+	}
+
+	private static Map<Integer, MusicalInstrumentsPitchRange> map = new HashMap<Integer, MusicalInstrumentsPitchRange>() {
+
+		/**
+		 * 
+		 */
+		private static final long serialVersionUID = 7551297824160987438L;
+
+		{
+			// 以下是以440hz 作为标准音
+			put(3, new MusicalInstrumentsPitchRange(261.632, 2093.056));// 长笛 c4-c7
+			put(4, new MusicalInstrumentsPitchRange(138.595, 1661.258));// 中音萨克斯 #c3-#g6
+			put(7, new MusicalInstrumentsPitchRange(164.818, 2637.084));// 单簧管 e3-e7
+			put(5, new MusicalInstrumentsPitchRange(523.264, 2349.376));// 竖笛 c5-d7
+			put(12, new MusicalInstrumentsPitchRange(196.002, 1174.688));// 小号 g3-d6
+			put(13, new MusicalInstrumentsPitchRange(87.309, 698.473));// 圆号 f2-f5
+			put(14, new MusicalInstrumentsPitchRange(49.001, 587.344));// 长号 g1-d5
+			put(15, new MusicalInstrumentsPitchRange(82.409, 349.237));// 上低音号 e2-f4
+			put(17, new MusicalInstrumentsPitchRange(41.204, 174.618));// 大号 e1-f3
+		}
+	};
+
+	public static MusicalInstrumentsPitchRange get(Integer musicalInstrument, double standPitch) {
+		MusicalInstrumentsPitchRange pitchRange = map.get(musicalInstrument);
+		if (pitchRange != null) {
+			pitchRange = new MusicalInstrumentsPitchRange(pitchRange.getMinPitch() / STANDARD_PITCH * standPitch * 2, pitchRange.getMaxPitch() / STANDARD_PITCH
+					* standPitch / 2);
+		}
+		return pitchRange == null ? new MusicalInstrumentsPitchRange(20, 20000) : pitchRange;
+	}
+
+	public double getMinPitch() {
+		return minPitch / 2;
+	}
+
+	public double getMaxPitch() {
+		return maxPitch * 2;
+	}
+
+}

+ 4 - 3
audio-analysis/src/main/java/com/yonge/netty/dto/NoteFrequencyRange.java

@@ -13,12 +13,12 @@ public class NoteFrequencyRange {
 
 	private double maxFrequency;
 
-	public NoteFrequencyRange(double standardFrequecy, double frequency) {
+	public NoteFrequencyRange(double standardPitch, double frequency) {
 		int midiNoteSize = 128;
 		double[] midiNoteFrequencies = new double[midiNoteSize];
 
 		for (int x = 0; x < midiNoteSize; ++x) {
-			midiNoteFrequencies[x] = new BigDecimal(standardFrequecy).multiply(
+			midiNoteFrequencies[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();
 
 			if(frequency <= 0){
@@ -32,7 +32,7 @@ public class NoteFrequencyRange {
 					minFrequency = midiNoteFrequencies[x - 1] - (midiNoteFrequencies[x - 1] - midiNoteFrequencies[x - 2]) / 2;
 				} else {
 					// frequency演奏的是当前音符
-					midiNoteFrequencies[x + 1] = new BigDecimal(standardFrequecy).multiply(
+					midiNoteFrequencies[x + 1] = new BigDecimal(standardPitch).multiply(
 							new BigDecimal(Math.pow(2, new BigDecimal((x + 1) - 69).divide(new BigDecimal(12), 6, BigDecimal.ROUND_HALF_UP).doubleValue())))
 							.doubleValue();
 					maxFrequency = midiNoteFrequencies[x] + (midiNoteFrequencies[x + 1] - midiNoteFrequencies[x]) / 2;
@@ -102,6 +102,7 @@ public class NoteFrequencyRange {
 		
 		System.out.println(nfr.equals(nfr1));
 		System.out.println(map.size());
+		
 	}
 
 }

+ 15 - 5
audio-analysis/src/main/java/com/yonge/netty/dto/NotePlayResult.java

@@ -7,7 +7,9 @@ public class NotePlayResult {
 	
 	private double deviationRate;//偏移比例
 	
-	private double deviationValue;//偏移值
+	private double deviationCentValue;//偏移音分值
+	
+	private double deviationHertzValue;//偏移的赫兹值
 	
 	public NotePlayResult() {
 		// TODO Auto-generated constructor stub
@@ -29,11 +31,19 @@ public class NotePlayResult {
 		this.deviationRate = deviationRate;
 	}
 
-	public double getDeviationValue() {
-		return deviationValue;
+	public double getDeviationCentValue() {
+		return deviationCentValue;
+	}
+
+	public void setDeviationCentValue(double deviationCentValue) {
+		this.deviationCentValue = deviationCentValue;
+	}
+
+	public double getDeviationHertzValue() {
+		return deviationHertzValue;
 	}
 
-	public void setDeviationValue(double deviationValue) {
-		this.deviationValue = deviationValue;
+	public void setDeviationHertzValue(double deviationHertzValue) {
+		this.deviationHertzValue = deviationHertzValue;
 	}
 }

+ 70 - 69
audio-analysis/src/main/java/com/yonge/netty/dto/UserChannelContext.java

@@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory;
 import be.tarsos.dsp.pitch.FastYin;
 
 import com.yonge.audio.analysis.Signals;
-import com.yonge.audio.analysis.detector.YINPitchDetector;
 import com.yonge.netty.dto.NoteAnalysis.NoteErrorType;
 import com.yonge.netty.entity.MusicXmlBasicInfo;
 import com.yonge.netty.entity.MusicXmlNote;
@@ -38,17 +37,13 @@ public class UserChannelContext {
 	//打击乐
 	private final static List<Integer> percussionList = Arrays.asList(23, 113);
 	
-	private final static int MIN_FREQUECY = 100;
-	
-	private final static int MAX_FREQUECY = 2000;
-	
 	private FastYin detector;
 	
 	private String user;
 	
 	private double standardFrequecy = 440;
 	
-	private float offsetMS;
+	private float deviceDelayTimeMS;
 	
 	private double dynamicOffset;
 	
@@ -60,7 +55,7 @@ public class UserChannelContext {
 	
 	private float beatDuration;
 	
-	private boolean delayProcessed;
+	private boolean startProcessed;
 	
 	// 曲目与musicxml对应关系
 	private ConcurrentHashMap<Long, MusicXmlBasicInfo> songMusicXmlMap = new ConcurrentHashMap<Long, MusicXmlBasicInfo>();
@@ -92,14 +87,12 @@ public class UserChannelContext {
 		if (Math.round(xmlNote.getFrequency()) == Math.round(playFrequency)) {
 			result.setStatus(0);
 			result.setDeviationRate(0);
-			result.setDeviationValue(0);
+			result.setDeviationCentValue(0);
 			
 			return result;
 		}
 
-		int status;
-		double deviationRate = 0;
-		double deviationValue = 0;
+		int status = 0;
 		
 		NoteFrequencyRange noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, xmlNote.getFrequency());
 
@@ -107,34 +100,41 @@ public class UserChannelContext {
 			status = 1;//偏低
 		} else if( playFrequency > noteFrequencyRange.getMaxFrequency()){
 			status = 2;//偏高
-		} else {
-
-			status = 0;
-
-			if (Math.round(playFrequency) < Math.round(xmlNote.getFrequency())) {
-				double min = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMinFrequency()) / 2;
-				deviationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / min;
-				//deviationValue = 
-			} else {
-				double max = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMaxFrequency()) / 2;
-				deviationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / max;
-			}
 		}
 
 		result.setStatus(status);
-		result.setDeviationRate(deviationRate);
+		result.setDeviationCentValue(getCents(playFrequency, xmlNote.getFrequency()));
+		result.setDeviationHertzValue(playFrequency - xmlNote.getFrequency());
+		result.setDeviationRate(result.getDeviationCentValue()/(17*100));
 
 		return result;
 	}
 	
-	public void init(String platform, String heardLevel, int subjectId, float beatDuration,float sampleRate, int bufferSize) {
-		this.platform = platform;
-		this.subjectId = subjectId;
-		this.beatDuration = beatDuration;
-		hardLevel = HardLevelEnum.valueOf(heardLevel);
+	/**
+	 * 获取2个频率之间的音分差值
+	 * @param srcFrequency
+	 * @param targetFrequency
+	 * @return
+	 */
+	private double getCents(double srcFrequency, double targetFrequency) {
+		if (targetFrequency < 0) {
+			return 0;
+		}
+		if(srcFrequency < 0){
+			return 17 * 10;
+		}
+		return Math.log(srcFrequency / targetFrequency) / Math.log(2) * 1200;
+	}
+	
+	public void init(MusicXmlBasicInfo musicXmlBasicInfo,float sampleRate, int bufferSize) {
+		this.platform = musicXmlBasicInfo.getPlatform();
+		this.subjectId = musicXmlBasicInfo.getSubjectId();
+		this.beatDuration = musicXmlBasicInfo.getBeatLength() + musicXmlBasicInfo.getReactionTimeMs();
+		hardLevel = HardLevelEnum.valueOf(musicXmlBasicInfo.getHeardLevel());
 		if(detector == null){
 			detector = new FastYin(sampleRate, bufferSize);
 		}
+		this.standardFrequecy = musicXmlBasicInfo.getHertz();
 	}
 	
 	public void setUser(String user) {
@@ -157,12 +157,12 @@ public class UserChannelContext {
 		this.handlerSwitch = handlerSwitch;
 	}
 
-	public float getOffsetMS() {
-		return offsetMS;
+	public float getDeviceDelayTimeMS() {
+		return deviceDelayTimeMS;
 	}
 
-	public void setOffsetMS(float offsetMS) {
-		this.offsetMS = offsetMS;
+	public void setDeviceDelayTimeMS(float deviceDelayTimeMS) {
+		this.deviceDelayTimeMS = deviceDelayTimeMS;
 	}
 
 	public float getBeatDuration() {
@@ -215,10 +215,11 @@ public class UserChannelContext {
 		totalChunkAnalysisList = new ArrayList<ChunkAnalysis>();
 		recordId = null;
 		playTime = 0;
-		delayProcessed = false;
-		offsetMS = 0;
+		startProcessed = false;
+		deviceDelayTimeMS = 0;
 		dynamicOffset = 0;
 		handlerSwitch = false;
+		standardFrequecy = 440;
 	}
 	
 	public MusicXmlBasicInfo getMusicXmlBasicInfo(Integer songId){
@@ -364,7 +365,7 @@ public class UserChannelContext {
 		
 		if (noteAnalysis.getMusicalNotesIndex() >= 0 && noteAnalysis.getMusicalNotesIndex() <= getTotalMusicNoteIndex(null)) {
 			
-			LOGGER.debug("user:{}  delayProcessed:{}  dynamicOffset:{}  Frequency:{}  splDb:{}  amplitude:{}  time:{}", user, delayProcessed, dynamicOffset, playFrequency, splDb, amplitude, playTime);
+			LOGGER.debug("user:{}  startProcessed:{}  dynamicOffset:{}  Frequency:{}  splDb:{}  amplitude:{}  time:{}", user, startProcessed, dynamicOffset, playFrequency, splDb, amplitude, playTime);
 			
 			ChunkAnalysis chunkAnalysis = new ChunkAnalysis(playTime - durationTime, playTime, playFrequency, splDb, power, amplitude);
 			
@@ -379,17 +380,17 @@ public class UserChannelContext {
 			if(percussionList.contains(subjectId)){
 				flag = chunkAnalysis.getAmplitude() > hardLevel.getAmplitudeThreshold();
 			}else{
-				flag = chunkAnalysis.getFrequency() > MIN_FREQUECY && chunkAnalysis.getFrequency() < MAX_FREQUECY;
+				flag = chunkAnalysis.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch() && chunkAnalysis.getFrequency() < MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMaxPitch();
 			}
 			
-			if(delayProcessed == false && flag){
+			if(startProcessed == false && flag){
 				
-				delayProcessed = true;
+				startProcessed = true;
 				
 				//计算延迟偏移值
 				//playTime = musicXmlNote.getTimeStamp() + durationTime;
 				dynamicOffset = chunkAnalysis.getStartTime() - musicXmlNote.getTimeStamp();
-				if(100 * dynamicOffset / musicXmlNote.getDuration() > (100 - hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator()))){
+				if(100 * dynamicOffset / musicXmlNote.getDuration() > hardLevel.getIntegrityRange()){
 					dynamicOffset = 0;
 				}
 			}
@@ -570,7 +571,7 @@ public class UserChannelContext {
 			
 			if (noteAnalysis.getFrequency() == -1) {// 休止符
 
-				playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MIN_FREQUECY).mapToDouble(t -> t.getDurationTime()).sum();
+				playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch()).mapToDouble(t -> t.getDurationTime()).sum();
 
 				if (noteAnalysis.getTempo() != 0) {
 					noteAnalysis.setMusicalErrorType(setMusicalErrorTempo(noteAnalysis.getTempo()));
@@ -582,7 +583,7 @@ public class UserChannelContext {
 					noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
 				}
 			} else {
-				playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY && t.getFrequency() < MAX_FREQUECY)
+				playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch() && t.getFrequency() < MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMaxPitch())
 						.mapToDouble(t -> t.getDurationTime()).sum();
 
 				if (playDurationTime * 100 / durationTime < hardLevel.getNotPlayRange()) {
@@ -605,8 +606,8 @@ public class UserChannelContext {
 		// 计算音分
 		int tempoScore = 0;
 		int integrityScore = 0;
-		int intonationScore = 100 - new BigDecimal(Math.abs(YINPitchDetector.hertzToAbsoluteCent(noteAnalysis.getPlayFrequency())
-				- YINPitchDetector.hertzToAbsoluteCent(noteAnalysis.getFrequency()))).multiply(new BigDecimal(20)).divide(new BigDecimal(17), BigDecimal.ROUND_UP)
+		double cents = getCents(noteAnalysis.getPlayFrequency(), noteAnalysis.getFrequency());
+		int intonationScore = 100 - new BigDecimal(Math.abs(cents)).multiply(new BigDecimal(10)).divide(new BigDecimal(17), BigDecimal.ROUND_UP)
 				.setScale(0, BigDecimal.ROUND_UP).intValue();
 		if (intonationScore < 0) {
 			intonationScore = 0;
@@ -669,7 +670,7 @@ public class UserChannelContext {
 		double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
 		double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
 		
-		LOGGER.debug("------------TimeStamp:{}  Duration:{}  floatingRange:{}  StartTime:{}  EndTime:{}------------", musicXmlNote.getTimeStamp(), musicXmlNote.getDuration(), floatingRange, startTime, endTime);
+		LOGGER.debug("------Pitch------standard start time:{}  floatingRange:{}  modified [ {} - {} ]------------", musicXmlNote.getTimeStamp(), floatingRange, startTime, endTime);
 		
 		List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
 		
@@ -678,8 +679,6 @@ public class UserChannelContext {
 		
 		chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
 
-		LOGGER.debug("------------ correctedStartTime:{}  correctedEndTime:{}------------", correctedStartTime, correctedEndTime);
-		
 		//根据完整度取部分有效信号
 		int elementSize = chunkAnalysisList.size() * hardLevel.getIntegrityRange() / 100;
 		chunkAnalysisList = chunkAnalysisList.subList(0, elementSize);
@@ -689,12 +688,12 @@ public class UserChannelContext {
 		
 		ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
 		
-		LOGGER.debug("-------startTime:{}  endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)
+		LOGGER.debug("------Pitch------startTime:{}  endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)
 				.getEndTime());
 		
 		List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
 		
-		List<Integer> chunkFrequencyList = chunkList.stream().map(t -> t.getFrequency()).filter(t -> t.doubleValue() > MIN_FREQUECY && t.doubleValue() < MAX_FREQUECY)
+		List<Integer> chunkFrequencyList = chunkList.stream().map(t -> t.getFrequency()).filter(t -> t.doubleValue() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch() && t.doubleValue() < MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMaxPitch())
 				.collect(Collectors.toList());
 		
 		if (chunkFrequencyList.size() == 0) {
@@ -726,10 +725,11 @@ public class UserChannelContext {
 		
 		chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
 		
-		//根据完整度取部分有效信号,取中间
+		//根据完整度取部分有效信号,取开头
 		int elementSize = chunkAnalysisList.size() * hardLevel.getIntegrityRange() / 100;
 		
-		int startIndex = (chunkAnalysisList.size() - elementSize) / 2 - 1;
+		//int startIndex = (chunkAnalysisList.size() - elementSize) / 2 - 1;
+		int startIndex = 0;
 		startIndex = startIndex < 1 ? 0 : startIndex;
 		List<ChunkAnalysis> chunkList = chunkAnalysisList.subList(startIndex, elementSize + startIndex);
 		
@@ -738,7 +738,7 @@ public class UserChannelContext {
 		}
 		
 		if (musicXmlNote.getFrequency() == -1) {// 休止符
-			return chunkList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY).count() <= 1? 0:1;
+			return chunkList.stream().filter(t -> t.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch()).count() <= 1? 0:1;
 		}
 
 		ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
@@ -746,7 +746,7 @@ public class UserChannelContext {
 		List<ChunkAnalysis> ignoreHeaderList = chunkAnalysisList.subList(0, startIndex);
 		if(ignoreHeaderList != null && ignoreHeaderList.size() > 0){
 			for(ChunkAnalysis ca : ignoreHeaderList){
-				if(ca.getFrequency() < MIN_FREQUECY){
+				if(ca.getFrequency() < MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch()){
 					firstChunkAnalysis.setFrequency(-1);
 					break;
 				}
@@ -785,7 +785,7 @@ public class UserChannelContext {
 		for (int i = 0; i < chunkList.size(); i++) {
 			chunkAnalysis = chunkList.get(i);
 			if (chunkAnalysis != null) {
-				if (chunkAnalysis.getFrequency() > MIN_FREQUECY || firstPeakIndex > -1) {
+				if (chunkAnalysis.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch() || firstPeakIndex > -1) {
 					
 					if (firstPeakIndex == -1) {
 						firstPeakIndex = i;
@@ -815,10 +815,10 @@ public class UserChannelContext {
 			totalTimes = chunkList.size();
 		}
 		
-		if (maxTimes * 100 / totalTimes < hardLevel.getIntegrityRange()) {
+		/*if (maxTimes * 100 / totalTimes < hardLevel.getIntegrityRange()) {
 			tempo = 1;
 			LOGGER.debug("节奏错误原因:信号分堆后的最大数量不足指定的完成比例");
-		}
+		}*/
 		
 		/**
 		for (int i = 0; i < chunkList.size(); i++) {
@@ -867,7 +867,7 @@ public class UserChannelContext {
 				LOGGER.debug("节奏错误原因:进入时间点太晚");
 			}else{
 				//判断是否与上一个音延续下来的
-				if(firstChunkAnalysis.getFrequency() > MIN_FREQUECY && lastChunkAnalysis.getFrequency() > MIN_FREQUECY){
+				if(firstChunkAnalysis.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch() && lastChunkAnalysis.getFrequency() > MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch()){
 					tempo = new NoteFrequencyRange(standardFrequecy, firstChunkAnalysis.getFrequency()).equals(new NoteFrequencyRange(standardFrequecy, lastChunkAnalysis.getFrequency())) == false?0:1;
 					if(tempo == 1){
 						LOGGER.debug("节奏错误原因:上一个音[{}]延续下来导致的", lastChunkAnalysis.getFrequency());
@@ -886,7 +886,7 @@ public class UserChannelContext {
 		double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
 		double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
 		
-		LOGGER.debug("------------TimeStamp:{}  floatingRange:{}  StartTime:{}  EndTime:{}------------", musicXmlNote.getTimeStamp(), floatingRange, startTime, endTime);
+		LOGGER.debug("------Tempo------standard start time:{}  floatingRange:{}  modified [ {} - {} ]------------", musicXmlNote.getTimeStamp(), floatingRange, startTime, endTime);
 		
 		List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
 		
@@ -905,12 +905,12 @@ public class UserChannelContext {
 		
 		ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
 		
-		LOGGER.debug("-------startTime:{}  endTime:{}------", firstChunkAnalysis.getStartTime(), chunkList.get(chunkList.size() - 1)
+		LOGGER.debug("------Tempo------startTime:{}  endTime:{}------", firstChunkAnalysis.getStartTime(), chunkList.get(chunkList.size() - 1)
 				.getEndTime());
 
 		if (musicXmlNote.getFrequency() == -1) {// 休止符
 			
-			LOGGER.debug("--Amplitude:{}  Denominator:{}",chunkList.stream().map(t -> t).collect(Collectors.toList()), musicXmlNote.getDenominator());
+			LOGGER.debug("--pause note Amplitude:{}  Denominator:{}",chunkList.stream().map(t -> t).collect(Collectors.toList()), musicXmlNote.getDenominator());
 			return chunkList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count() <= 0 ? 0:1;
 		}
 		
@@ -979,17 +979,17 @@ public class UserChannelContext {
 	private double queryFirstNoteStartTime(List<ChunkAnalysis> chunkAnalysisList, MusicXmlNote musicXmlNote) {
 		
 		if(chunkAnalysisList == null || chunkAnalysisList.size() == 0){
-			LOGGER.debug("找不到数据,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
+			LOGGER.debug("[查询第一个音]找不到数据,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
 			return musicXmlNote.getTimeStamp() + dynamicOffset;
 		}
 		
 		if (percussionList.contains(subjectId)) {
 			Optional<ChunkAnalysis> optional = chunkAnalysisList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).findFirst();
 			if(optional.isPresent()){
-				LOGGER.debug("范围内查询到信号,correctedStartTime:{}", optional.get().getStartTime());
+				LOGGER.debug("[查询第一个音]范围内查询到信号,correctedStartTime:{}", optional.get().getStartTime());
 				return optional.get().getStartTime();
 			}else{
-				LOGGER.debug("范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
+				LOGGER.debug("[查询第一个音]范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
 				return musicXmlNote.getTimeStamp() + dynamicOffset;
 			}
 		}
@@ -998,12 +998,12 @@ public class UserChannelContext {
 		if(musicXmlNote.getMusicalNotesIndex() > 0){
 			MusicXmlNote preMusicXmlNote = getCurrentMusicNote(null, musicXmlNote.getMusicalNotesIndex() - 1);
 			if((int)preMusicXmlNote.getFrequency() == (int)musicXmlNote.getFrequency()){
-				Optional<ChunkAnalysis> optional = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MIN_FREQUECY).findFirst();
+				Optional<ChunkAnalysis> optional = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MusicalInstrumentsPitchRange.get(subjectId, standardFrequecy).getMinPitch()).findFirst();
 				if(optional.isPresent()){
-					LOGGER.debug("与上一个音同音,有断开,correctedStartTime:{}", optional.get().getStartTime());
+					LOGGER.debug("[查询第一个音]与上一个音同音,有断开,correctedStartTime:{}", optional.get().getEndTime());
 					return optional.get().getEndTime();
 				}else{
-					LOGGER.debug("与上一个音同音,未断开,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
+					LOGGER.debug("[查询第一个音]与上一个音同音,未断开,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
 					return musicXmlNote.getTimeStamp() + dynamicOffset;
 				}
 			}
@@ -1016,12 +1016,12 @@ public class UserChannelContext {
 		for (ChunkAnalysis ca : chunkAnalysisList) {
 			noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, ca.getFrequency());
 			if (standardNote.equals(noteFrequencyRange)) {
-				LOGGER.debug("范围内查询到信号,correctedStartTime:{}", ca.getStartTime());
+				LOGGER.debug("[查询第一个音]范围内查询到信号,correctedStartTime:{}", ca.getStartTime());
 				return ca.getStartTime();
 			}
 		}
 		
-		LOGGER.debug("范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
+		LOGGER.debug("[查询第一个音]范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
 
 		//return chunkAnalysisList.get(chunkAnalysisList.size() - 1).getEndTime();
 		return musicXmlNote.getTimeStamp() + dynamicOffset;
@@ -1036,7 +1036,8 @@ public class UserChannelContext {
 		   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]);
 		}
-		
+		UserChannelContext test= new UserChannelContext();
+		System.out.println(test.getCents(440, 442));
 	}
 	
 }

+ 21 - 1
audio-analysis/src/main/java/com/yonge/netty/entity/MusicXmlBasicInfo.java

@@ -36,7 +36,11 @@ public class MusicXmlBasicInfo {
 
 	private String uuid;
 	
-	private float beatLength;
+	private float beatLength;//节拍器的持续时长(毫秒)
+	
+	private Integer hertz;//标准音高
+	
+	private Integer reactionTimeMs;//用户设置的反应时间(毫秒)
 
 	private List<MusicXmlNote> musicXmlInfos = new ArrayList<MusicXmlNote>();
 
@@ -162,6 +166,22 @@ public class MusicXmlBasicInfo {
 		this.musicXmlInfos = musicXmlInfos;
 	}
 
+	public Integer getHertz() {
+		return hertz;
+	}
+
+	public void setHertz(Integer hertz) {
+		this.hertz = hertz;
+	}
+
+	public Integer getReactionTimeMs() {
+		return reactionTimeMs;
+	}
+
+	public void setReactionTimeMs(Integer reactionTimeMs) {
+		this.reactionTimeMs = reactionTimeMs;
+	}
+
 	public Map<Integer, MusicXmlSection> getMusicXmlSectionMap() {
 
 		if (musicXmlSectionMap.size() == 0) {

+ 6 - 11
audio-analysis/src/main/java/com/yonge/netty/server/service/AudioCompareHandler.java

@@ -28,7 +28,6 @@ import com.alibaba.fastjson.JSONPath;
 import com.yonge.audio.analysis.AudioFloatConverter;
 import com.yonge.audio.utils.ArrayUtil;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
-import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.entity.SysMusicCompareRecord;
 import com.yonge.cooleshow.biz.dal.enums.DeviceTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.FeatureType;
@@ -127,8 +126,7 @@ public class AudioCompareHandler implements MessageHandler {
 
 			channelContext.getSongMusicXmlMap().put(musicXmlBasicInfo.getExamSongId(), musicXmlBasicInfo);
 
-			channelContext.init(musicXmlBasicInfo.getPlatform(), musicXmlBasicInfo.getHeardLevel(), musicXmlBasicInfo.getSubjectId(),
-					musicXmlBasicInfo.getBeatLength(), audioFormat.getSampleRate(), bufferSize / 2);
+			channelContext.init(musicXmlBasicInfo, audioFormat.getSampleRate(), bufferSize / 2);
 			channelContext.setUser(user);
 			
 			userChannelContextService.register(channel, channelContext);
@@ -156,8 +154,6 @@ public class AudioCompareHandler implements MessageHandler {
 				sysMusicCompareRecord.setPartIndex(musicXmlBasicInfo.getPartIndex());
 				sysMusicCompareRecord.setCustomConfiguration(musicXmlBasicInfo.getCustomConfiguration());
 				
-				SysUser sysUser = sysUserFeignService.queryUserById(sysMusicCompareRecord.getUserId());
-				
 				MusicXmlNote musicXmlNote = musicXmlBasicInfo.getMusicXmlInfos().stream().max(Comparator.comparing(MusicXmlNote::getTimeStamp)).get();
 				sysMusicCompareRecord.setSourceTime((float) ((musicXmlNote.getTimeStamp()+musicXmlNote.getDuration())/1000));
 				sysMusicCompareRecordService.insert(sysMusicCompareRecord);
@@ -246,7 +242,7 @@ public class AudioCompareHandler implements MessageHandler {
 			
 			Integer offsetTime = dataObj.getInteger("offsetTime");
 			if(offsetTime != null){
-				channelContext.setOffsetMS(offsetTime);
+				channelContext.setDeviceDelayTimeMS(offsetTime);
 				channelContext.setHandlerSwitch(true);
 			}
 
@@ -305,19 +301,18 @@ public class AudioCompareHandler implements MessageHandler {
 			return false;
 		}
 		
-		if (channelContext.getOffsetMS() + channelContext.getBeatDuration() > 0) {
-			int beatByteLength = (int) (audioFormat.getSampleRate() * audioFormat.getSampleSizeInBits() / 8 * (channelContext.getOffsetMS() + channelContext.getBeatDuration()) / 1000);
+		if (channelContext.getDeviceDelayTimeMS() + channelContext.getBeatDuration() > 0) {
+			int beatByteLength = (int) (audioFormat.getSampleRate() * audioFormat.getSampleSizeInBits() / 8 * (channelContext.getDeviceDelayTimeMS() + channelContext.getBeatDuration()) / 1000);
 			
 			if(totalLength > beatByteLength){
 				if(beatByteLength % 2 != 0){
-					LOGGER.debug("**************奇数*****************");
 					beatByteLength--;
 				}
 				channelContext.setChannelBufferBytes(ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), beatByteLength, totalLength - 1));
 				
-				LOGGER.debug("--------Length:{}  Times[{} + {}]:{}--------", waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length, channelContext.getOffsetMS() , channelContext.getBeatDuration(),(waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length) * 1000 /audioFormat.getSampleRate()/2);
+				LOGGER.debug("--------head message ignore length:{}  times[{} + {}]:{}--------", waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length, channelContext.getDeviceDelayTimeMS() , channelContext.getBeatDuration(),(waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length) * 1000 /audioFormat.getSampleRate()/2);
 				
-				channelContext.setOffsetMS(0);
+				channelContext.setDeviceDelayTimeMS(0);
 				channelContext.setBeatDuration(0);
 			}else{
 				return false;

二進制
audio-analysis/src/test/resources/160悬崖上的金鱼姬 修节.wav


二進制
audio-analysis/src/test/resources/160悬崖上的金鱼姬 修节奏2.wav


二進制
audio-analysis/src/test/resources/160悬崖上的金鱼姬 修音.wav


文件差異過大導致無法顯示
+ 0 - 0
audio-analysis/src/test/resources/悬崖上的金鱼姬1 速度160.json


部分文件因文件數量過多而無法顯示