UserChannelContext.java 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. package com.yonge.netty.dto;
  2. import java.math.BigDecimal;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. import java.util.Comparator;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Map.Entry;
  10. import java.util.Optional;
  11. import java.util.concurrent.ConcurrentHashMap;
  12. import java.util.concurrent.atomic.AtomicInteger;
  13. import java.util.stream.Collectors;
  14. import javax.sound.sampled.AudioFormat;
  15. import org.apache.commons.lang3.StringUtils;
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import be.tarsos.dsp.pitch.FastYin;
  19. import com.yonge.audio.analysis.Signals;
  20. import com.yonge.audio.analysis.detector.YINPitchDetector;
  21. import com.yonge.netty.dto.NoteAnalysis.NoteErrorType;
  22. import com.yonge.netty.entity.MusicXmlBasicInfo;
  23. import com.yonge.netty.entity.MusicXmlNote;
  24. import com.yonge.netty.entity.MusicXmlSection;
  25. import com.yonge.netty.server.processor.WaveformWriter;
  26. /**
  27. * 用户通道上下文
  28. */
  29. public class UserChannelContext {
  30. private final static Logger LOGGER = LoggerFactory.getLogger(UserChannelContext.class);
  31. //打击乐
  32. //private final static List<Integer> percussionList = Arrays.asList(23, 113, 121);
  33. private final static int MIN_FREQUECY = 43;
  34. private final static int MAX_FREQUECY = 2000;
  35. private FastYin detector;
  36. private String user;
  37. private double standardFrequecy = 442;
  38. private float offsetMS;
  39. private float micDelayMS;
  40. private double dynamicOffset;
  41. private String platform;
  42. private Long recordId;
  43. private Integer subjectId;
  44. private String evaluationCriteria;
  45. private float beatDuration;
  46. private boolean delayProcessed;
  47. // 曲目与musicxml对应关系
  48. private ConcurrentHashMap<Integer, MusicXmlBasicInfo> songMusicXmlMap = new ConcurrentHashMap<Integer, MusicXmlBasicInfo>();
  49. private WaveformWriter waveFileProcessor;
  50. private NoteAnalysis processingNote = new NoteAnalysis(0, 0, -1);
  51. private AtomicInteger evaluatingSectionIndex = new AtomicInteger(0);
  52. private List<NoteAnalysis> doneNoteAnalysisList = new ArrayList<NoteAnalysis>();
  53. private List<SectionAnalysis> doneSectionAnalysisList = new ArrayList<SectionAnalysis>();
  54. private List<ChunkAnalysis> totalChunkAnalysisList = new ArrayList<ChunkAnalysis>();
  55. private byte[] channelBufferBytes = new byte[0];
  56. private double playTime;
  57. private HardLevelEnum hardLevel = HardLevelEnum.ADVANCED;
  58. private boolean handlerSwitch;
  59. private NotePlayResult queryNoteFrequency(MusicXmlNote xmlNote, double playFrequency) {
  60. NotePlayResult result = new NotePlayResult();
  61. boolean status = false;
  62. double migrationRate = 0;
  63. if (Math.round(xmlNote.getFrequency()) == Math.round(playFrequency)) {
  64. status = true;
  65. migrationRate = 0;
  66. } else {
  67. NoteFrequencyRange noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, xmlNote.getFrequency());
  68. if (noteFrequencyRange.getMinFrequency() > playFrequency || playFrequency > noteFrequencyRange.getMaxFrequency()) {
  69. status = false;
  70. } else {
  71. status = true;
  72. if (Math.round(playFrequency) < Math.round(xmlNote.getFrequency())) {
  73. double min = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMinFrequency()) / 2;
  74. migrationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / min;
  75. } else {
  76. double max = Math.abs(xmlNote.getFrequency() - noteFrequencyRange.getMaxFrequency()) / 2;
  77. migrationRate = Math.abs(playFrequency - xmlNote.getFrequency()) / max;
  78. }
  79. }
  80. }
  81. result.setStatus(status);
  82. result.setMigrationRate(migrationRate);
  83. return result;
  84. }
  85. public void init(MusicXmlBasicInfo musicXmlBasicInfo, float sampleRate, int bufferSize) {
  86. this.platform = musicXmlBasicInfo.getPlatform();
  87. this.subjectId = musicXmlBasicInfo.getSubjectId();
  88. this.beatDuration = musicXmlBasicInfo.getBeatLength();
  89. this.hardLevel = HardLevelEnum.valueOf(musicXmlBasicInfo.getHeardLevel());
  90. this.evaluationCriteria = musicXmlBasicInfo.getEvaluationCriteria();
  91. if(detector == null){
  92. detector = new FastYin(sampleRate, bufferSize);
  93. }
  94. }
  95. public void setUser(String user) {
  96. this.user = user;
  97. }
  98. public Long getRecordId() {
  99. return recordId;
  100. }
  101. public void setRecordId(Long recordId) {
  102. this.recordId = recordId;
  103. }
  104. public boolean getHandlerSwitch() {
  105. return handlerSwitch;
  106. }
  107. public void setHandlerSwitch(boolean handlerSwitch) {
  108. this.handlerSwitch = handlerSwitch;
  109. }
  110. public float getOffsetMS() {
  111. return offsetMS;
  112. }
  113. public void setOffsetMS(float offsetMS) {
  114. this.offsetMS = offsetMS;
  115. }
  116. public float getMicDelayMS() {
  117. return micDelayMS;
  118. }
  119. public void setMicDelayMS(float micDelayMS) {
  120. this.micDelayMS = micDelayMS;
  121. }
  122. public float getBeatDuration() {
  123. return beatDuration;
  124. }
  125. public void setBeatDuration(float beatDuration) {
  126. this.beatDuration = beatDuration;
  127. }
  128. public HardLevelEnum getHardLevel() {
  129. return hardLevel;
  130. }
  131. public ConcurrentHashMap<Integer, MusicXmlBasicInfo> getSongMusicXmlMap() {
  132. return songMusicXmlMap;
  133. }
  134. public WaveformWriter getWaveFileProcessor() {
  135. return waveFileProcessor;
  136. }
  137. public void setWaveFileProcessor(WaveformWriter waveFileProcessor) {
  138. this.waveFileProcessor = waveFileProcessor;
  139. }
  140. public NoteAnalysis getProcessingNote() {
  141. return processingNote;
  142. }
  143. public void setProcessingNote(NoteAnalysis processingNote) {
  144. this.processingNote = processingNote;
  145. }
  146. public List<SectionAnalysis> getDoneSectionAnalysisList() {
  147. return doneSectionAnalysisList;
  148. }
  149. public List<NoteAnalysis> getDoneNoteAnalysisList() {
  150. return doneNoteAnalysisList;
  151. }
  152. public void resetUserInfo() {
  153. waveFileProcessor = null;
  154. processingNote = new NoteAnalysis(0,0,-1);
  155. evaluatingSectionIndex = new AtomicInteger(0);
  156. channelBufferBytes = new byte[0];
  157. doneNoteAnalysisList = new ArrayList<NoteAnalysis>();
  158. doneSectionAnalysisList = new ArrayList<SectionAnalysis>();
  159. totalChunkAnalysisList = new ArrayList<ChunkAnalysis>();
  160. recordId = null;
  161. playTime = 0;
  162. delayProcessed = false;
  163. offsetMS = 0;
  164. dynamicOffset = 0;
  165. handlerSwitch = false;
  166. }
  167. public MusicXmlBasicInfo getMusicXmlBasicInfo(Integer songId){
  168. MusicXmlBasicInfo musicXmlBasicInfo = null;
  169. if (songId == null) {
  170. musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
  171. } else {
  172. musicXmlBasicInfo = songMusicXmlMap.get(songId);
  173. }
  174. return musicXmlBasicInfo;
  175. }
  176. public MusicXmlSection getCurrentMusicSection(Integer songId, int sectionIndex){
  177. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  178. return musicXmlBasicInfo.getMusicXmlSectionMap().get(sectionIndex);
  179. }
  180. public MusicXmlNote getCurrentMusicNote(Integer songId, Integer noteIndex) {
  181. if (songMusicXmlMap.size() == 0) {
  182. return null;
  183. }
  184. if(noteIndex == null){
  185. noteIndex = processingNote.getMusicalNotesIndex();
  186. }
  187. final int index = noteIndex;
  188. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  189. int totalNoteIndex = getTotalMusicNoteIndex(null);
  190. if (musicXmlBasicInfo != null && index <= totalNoteIndex) {
  191. return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == index).findFirst().get();
  192. }
  193. return null;
  194. }
  195. public int getTotalMusicNoteIndex(Integer songId) {
  196. if (songMusicXmlMap.size() == 0) {
  197. return -1;
  198. }
  199. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  200. if (musicXmlBasicInfo != null) {
  201. return musicXmlBasicInfo.getMusicXmlInfos().stream().map(t -> t.getMusicalNotesIndex()).distinct().max(Integer::compareTo).get();
  202. }
  203. return -1;
  204. }
  205. public List<MusicXmlNote> getCurrentMusicSection(Integer songId, Integer sectionIndex) {
  206. if (songMusicXmlMap.size() == 0) {
  207. return null;
  208. }
  209. if(sectionIndex == null){
  210. sectionIndex = processingNote.getSectionIndex();
  211. }
  212. final int index = sectionIndex;
  213. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  214. if (musicXmlBasicInfo != null) {
  215. return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == index)
  216. .sorted(Comparator.comparing(MusicXmlNote::getMusicalNotesIndex)).collect(Collectors.toList());
  217. }
  218. return null;
  219. }
  220. public int getTotalMusicSectionSize(Integer songId) {
  221. if (songMusicXmlMap.size() == 0) {
  222. return -1;
  223. }
  224. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  225. if (musicXmlBasicInfo != null) {
  226. return (int) musicXmlBasicInfo.getMusicXmlInfos().stream().map(t -> t.getMeasureIndex()).distinct().count();
  227. }
  228. return -1;
  229. }
  230. public int getMusicSectionIndex(Integer songId, int musicXmlNoteIndex) {
  231. if (songMusicXmlMap.size() == 0) {
  232. return -1;
  233. }
  234. if(getTotalMusicNoteIndex(null) < musicXmlNoteIndex){
  235. return -1;
  236. }
  237. MusicXmlBasicInfo musicXmlBasicInfo = getMusicXmlBasicInfo(songId);
  238. if (musicXmlBasicInfo != null) {
  239. return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == musicXmlNoteIndex).findFirst().get().getMeasureIndex();
  240. }
  241. return -1;
  242. }
  243. public byte[] getChannelBufferBytes() {
  244. return channelBufferBytes;
  245. }
  246. public void setChannelBufferBytes(byte[] channelBufferBytes) {
  247. this.channelBufferBytes = channelBufferBytes;
  248. }
  249. public AtomicInteger getEvaluatingSectionIndex() {
  250. return evaluatingSectionIndex;
  251. }
  252. public void handle(float[] samples, AudioFormat audioFormat){
  253. //YINPitchDetector frequencyDetector = new YINPitchDetector(samples.length , audioFormat.getSampleRate());
  254. //int playFrequency = (int) frequencyDetector.getFrequency(samples);
  255. int playFrequency = -1;
  256. if(StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())){
  257. playFrequency = (int)detector.getPitch(samples).getPitch();
  258. }
  259. int splDb = (int) Signals.soundPressureLevel(samples);
  260. float power = Signals.power(samples);
  261. int amplitude = (int) Signals.norm(samples);
  262. int decibels = (int) Signals.decibels(samples);
  263. if(StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.AMPLITUDE.getCode())) {
  264. amplitude = (int) Signals.norm(samples);
  265. }else if(StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.DECIBELS.getCode())){
  266. amplitude = (int) Signals.decibels(samples);
  267. amplitude = amplitude >= 60 ? amplitude : 0;
  268. }
  269. //float rms = Signals.rms(samples);
  270. double durationTime = 1000 * (samples.length * 2) / audioFormat.getSampleRate() / (audioFormat.getSampleSizeInBits() / 8);
  271. playTime += durationTime;
  272. // 获取当前音符信息
  273. MusicXmlNote musicXmlNote = getCurrentMusicNote(null,null);
  274. if (musicXmlNote == null) {
  275. return;
  276. }
  277. //取出当前处理中的音符信息
  278. NoteAnalysis noteAnalysis = getProcessingNote();
  279. if(noteAnalysis == null || noteAnalysis.getDurationTime() == 0) {
  280. noteAnalysis = new NoteAnalysis(musicXmlNote.getMeasureRenderIndex(), musicXmlNote.getMusicalNotesIndex(), musicXmlNote.getMeasureIndex(), (int)musicXmlNote.getFrequency(), musicXmlNote.getDuration());
  281. }
  282. evaluatingSectionIndex.set(noteAnalysis.getSectionIndex());
  283. if (noteAnalysis.getMusicalNotesIndex() >= 0 && noteAnalysis.getMusicalNotesIndex() <= getTotalMusicNoteIndex(null)) {
  284. LOGGER.debug("user:{} delayProcessed:{} dynamicOffset:{} Frequency:{} splDb:{} power:{} amplitude:{} decibels:{} time:{}", user, delayProcessed, dynamicOffset, playFrequency, splDb, power, amplitude, decibels, playTime);
  285. ChunkAnalysis chunkAnalysis = new ChunkAnalysis(playTime - durationTime, playTime, playFrequency, splDb, power, amplitude);
  286. if(totalChunkAnalysisList.size() > 0){
  287. if(totalChunkAnalysisList.get(totalChunkAnalysisList.size() - 1).getAmplitude() + 2 < chunkAnalysis.getAmplitude()){
  288. chunkAnalysis.setPeak(true);//只针对打击乐
  289. }
  290. }
  291. totalChunkAnalysisList.add(chunkAnalysis);
  292. //是否收到有效信号
  293. /*if(!StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())){
  294. delayProcessed = chunkAnalysis.getAmplitude() > hardLevel.getAmplitudeThreshold();
  295. }else{
  296. delayProcessed = chunkAnalysis.getFrequency() > MIN_FREQUECY && chunkAnalysis.getFrequency() < MAX_FREQUECY;
  297. }
  298. if(delayProcessed){
  299. //计算延迟偏移值
  300. //playTime = musicXmlNote.getTimeStamp() + durationTime;
  301. dynamicOffset = chunkAnalysis.getStartTime() - musicXmlNote.getTimeStamp();
  302. if(100 * dynamicOffset / musicXmlNote.getDuration() > (100 - hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration()))){
  303. dynamicOffset = 0;
  304. }
  305. }*/
  306. if (playTime >= (musicXmlNote.getDuration() + micDelayMS + musicXmlNote.getTimeStamp() + dynamicOffset)) {
  307. musicXmlNote.setTimeStamp(musicXmlNote.getTimeStamp() + micDelayMS);
  308. if (musicXmlNote.getDontEvaluating()) {
  309. noteAnalysis.setIgnore(true);
  310. }
  311. //判断节奏(音符持续时间内有不间断的音高,就节奏正确)
  312. boolean tempo = true;
  313. if (!StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())) {
  314. noteAnalysis.setPlayFrequency(-1);
  315. tempo = computeTempoWithAmplitude2(musicXmlNote);
  316. }else{
  317. noteAnalysis.setPlayFrequency(computeFrequency(musicXmlNote));
  318. tempo = computeTempoWithFrequency(musicXmlNote);
  319. }
  320. noteAnalysis.setTempo(tempo);
  321. evaluateForNote(musicXmlNote, noteAnalysis);//对当前音符评分
  322. LOGGER.debug("当前音符下标[{}] 预计频率:{} 实际频率:{} 节奏:{}", noteAnalysis.getMusicalNotesIndex(), musicXmlNote.getFrequency(), noteAnalysis.getPlayFrequency(),
  323. noteAnalysis.isTempo());
  324. doneNoteAnalysisList.add(noteAnalysis);
  325. // 准备处理下一个音符
  326. int nextNoteIndex = musicXmlNote.getMusicalNotesIndex() + 1;
  327. float nextNoteFrequence = -1;
  328. double standDuration = 0;
  329. int measureRenderIndex = 0;
  330. MusicXmlNote nextMusicXmlNote = getCurrentMusicNote(null, nextNoteIndex);
  331. if(nextMusicXmlNote != null){
  332. nextNoteFrequence = nextMusicXmlNote.getFrequency();
  333. standDuration = nextMusicXmlNote.getDuration();
  334. measureRenderIndex = nextMusicXmlNote.getMeasureRenderIndex();
  335. }
  336. NoteAnalysis nextNoteAnalysis = new NoteAnalysis(measureRenderIndex, nextNoteIndex, getMusicSectionIndex(null, nextNoteIndex), (int)nextNoteFrequence, standDuration);
  337. noteAnalysis = nextNoteAnalysis;
  338. }
  339. setProcessingNote(noteAnalysis);
  340. }
  341. }
  342. public int evaluateForSection(int sectionIndex, int subjectId){
  343. int score = -1;
  344. if(doneSectionAnalysisList.size() >= getTotalMusicSectionSize(null)){
  345. return score;
  346. }
  347. //取出当前小节的所有音符
  348. List<NoteAnalysis> noteAnalysisList = doneNoteAnalysisList.stream().filter(t -> t.getSectionIndex() == sectionIndex).collect(Collectors.toList());
  349. long ignoreSize = noteAnalysisList.stream().filter(t -> t.isIgnore()).count();
  350. SectionAnalysis sectionAnalysis = new SectionAnalysis();
  351. sectionAnalysis.setIndex(sectionIndex);
  352. sectionAnalysis.setNoteNum(noteAnalysisList.size());
  353. sectionAnalysis.setIsIngore(ignoreSize == noteAnalysisList.size());
  354. //判断是否需要评分
  355. MusicXmlSection musicXmlSection = getCurrentMusicSection(null, sectionIndex);
  356. if(noteAnalysisList.size() == musicXmlSection.getNoteNum()){
  357. sectionAnalysis.setMeasureRenderIndex(noteAnalysisList.stream().findFirst().get().getMeasureRenderIndex());
  358. //取出需要评测的音符
  359. List<NoteAnalysis> noteList = noteAnalysisList.stream().filter(t -> t.isIgnore() == false).collect(Collectors.toList());
  360. if(noteList != null && noteList.size() > 0){
  361. score = noteList.stream().mapToInt(t -> t.getScore()).sum() / noteList.size();
  362. }
  363. sectionAnalysis.setDurationTime(noteAnalysisList.stream().mapToDouble(t -> t.getDurationTime()).sum());
  364. sectionAnalysis.setScore(score);
  365. LOGGER.debug("小节评分:{}",sectionAnalysis);
  366. doneSectionAnalysisList.add(sectionAnalysis);
  367. }
  368. return score;
  369. }
  370. public Map<String, Integer> evaluateForMusic() {
  371. Map<String, Integer> result = new HashMap<String, Integer>();
  372. result.put("playTime", (int) doneNoteAnalysisList.stream().mapToDouble(t -> t.getDurationTime()).sum());
  373. result.put("recordId", recordId.intValue());
  374. // 取出需要评测的音符
  375. List<NoteAnalysis> noteAnalysisList = doneNoteAnalysisList.stream().filter(t -> t.isIgnore() == false).collect(Collectors.toList());
  376. if (noteAnalysisList != null && noteAnalysisList.size() > 0) {
  377. int intonationScore = 0;
  378. int tempoScore = 0;
  379. int integrityScore = 0;
  380. int socre = 0;
  381. for (NoteAnalysis note : noteAnalysisList) {
  382. intonationScore += note.getIntonationScore();
  383. tempoScore += note.getTempoScore();
  384. integrityScore += note.getIntegrityScore();
  385. socre += note.getScore();
  386. }
  387. tempoScore = tempoScore / noteAnalysisList.size();
  388. intonationScore = intonationScore / noteAnalysisList.size();
  389. integrityScore = integrityScore / noteAnalysisList.size();
  390. result.put("cadence", tempoScore);
  391. result.put("intonation", intonationScore);
  392. result.put("integrity", integrityScore);
  393. int score = socre / noteAnalysisList.size();
  394. // 平均得分
  395. if (getMusicXmlBasicInfo(null).getSubjectId() == 23 || getMusicXmlBasicInfo(null).getSubjectId() == 113) {
  396. score = tempoScore;
  397. }
  398. result.put("score", score);
  399. } else {
  400. result.put("cadence", 0);
  401. result.put("intonation", 0);
  402. result.put("integrity", 0);
  403. result.put("score", 0);
  404. }
  405. return result;
  406. }
  407. public void evaluateForNote(MusicXmlNote musicXmlNote, NoteAnalysis noteAnalysis) {
  408. double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration()) / 100;
  409. double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
  410. double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
  411. List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
  412. double correctedStartTime = queryFirstNoteStartTime(chunkAnalysisList, musicXmlNote);
  413. double correctedEndTime = correctedStartTime + musicXmlNote.getDuration();
  414. chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
  415. double durationTime = chunkAnalysisList.get(chunkAnalysisList.size() - 1).getEndTime() - chunkAnalysisList.get(0).getStartTime();
  416. double playDurationTime = 0;
  417. if (!StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())) {
  418. if (noteAnalysis.getFrequency() == -1) {// 休止符
  419. if (!noteAnalysis.isTempo()) {
  420. noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
  421. } else {
  422. noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
  423. }
  424. }else{
  425. int beatTimes = (int) chunkAnalysisList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count();
  426. if(beatTimes == 0){
  427. noteAnalysis.setMusicalErrorType(NoteErrorType.NOT_PLAY);
  428. }else if (!noteAnalysis.isTempo()) {
  429. noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
  430. } else {
  431. noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
  432. }
  433. }
  434. } else {
  435. NotePlayResult notePlayResult = queryNoteFrequency(musicXmlNote, noteAnalysis.getPlayFrequency());
  436. if (noteAnalysis.getFrequency() == -1) {// 休止符
  437. playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MIN_FREQUECY).mapToDouble(t -> t.getDurationTime()).sum();
  438. if (!noteAnalysis.isTempo()) {
  439. noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
  440. } else if (playDurationTime * 100 / durationTime < hardLevel.getIntegrityRange()) {
  441. noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
  442. } else if (notePlayResult.getStatus() == false) {
  443. noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
  444. } else {
  445. noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
  446. }
  447. } else {
  448. playDurationTime = chunkAnalysisList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY && t.getFrequency() < MAX_FREQUECY)
  449. .mapToDouble(t -> t.getDurationTime()).sum();
  450. if (playDurationTime * 100 / durationTime < hardLevel.getNotPlayRange()) {
  451. noteAnalysis.setMusicalErrorType(NoteErrorType.NOT_PLAY);
  452. LOGGER.debug("未演奏:{}", playDurationTime * 100 / durationTime);
  453. } else if (playDurationTime * 100 / durationTime < hardLevel.getIntegrityRange()) {
  454. noteAnalysis.setMusicalErrorType(NoteErrorType.INTEGRITY_WRONG);
  455. LOGGER.debug("完整度不足:{}", playDurationTime * 100 / durationTime);
  456. } else if (!noteAnalysis.isTempo()) {
  457. noteAnalysis.setMusicalErrorType(NoteErrorType.CADENCE_WRONG);
  458. } else if (notePlayResult.getStatus() == false) {
  459. noteAnalysis.setMusicalErrorType(NoteErrorType.INTONATION_WRONG);
  460. } else {
  461. noteAnalysis.setMusicalErrorType(NoteErrorType.RIGHT);
  462. }
  463. }
  464. }
  465. // 计算音分
  466. int tempoScore = 0;
  467. int integrityScore = 0;
  468. int intonationScore = 100 - new BigDecimal(Math.abs(YINPitchDetector.hertzToAbsoluteCent(noteAnalysis.getPlayFrequency())
  469. - YINPitchDetector.hertzToAbsoluteCent(noteAnalysis.getFrequency()))).multiply(new BigDecimal(20)).divide(new BigDecimal(17), BigDecimal.ROUND_UP)
  470. .setScale(0, BigDecimal.ROUND_UP).intValue();
  471. if (intonationScore < 0) {
  472. intonationScore = 0;
  473. } else if (intonationScore > 100) {
  474. intonationScore = 100;
  475. }
  476. if (noteAnalysis.getMusicalErrorType() == NoteErrorType.NOT_PLAY) {
  477. intonationScore = 0;
  478. } else {
  479. if (noteAnalysis.isTempo()) {
  480. tempoScore = 100;
  481. noteAnalysis.setTempoScore(tempoScore);
  482. }
  483. integrityScore = (int) (playDurationTime * 100 * 100 / hardLevel.getIntegrityRange() / durationTime);
  484. if (integrityScore > 100) {
  485. integrityScore = 100;
  486. }
  487. noteAnalysis.setIntegrityScore(integrityScore);
  488. }
  489. noteAnalysis.setIntonationScore(intonationScore);
  490. if (!StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())) {
  491. noteAnalysis.setScore(tempoScore);
  492. } else {
  493. noteAnalysis.setScore(new BigDecimal(intonationScore + tempoScore + integrityScore).divide(new BigDecimal(3), 2).setScale(0, BigDecimal.ROUND_UP)
  494. .intValue());
  495. }
  496. }
  497. private int computeFrequency(MusicXmlNote musicXmlNote) {
  498. double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration()) / 100;
  499. double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
  500. double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
  501. LOGGER.debug("------------TimeStamp:{} Duration:{} floatingRange:{} StartTime:{} EndTime:{}------------", musicXmlNote.getTimeStamp(), musicXmlNote.getDuration(), floatingRange, startTime, endTime);
  502. List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
  503. double correctedStartTime = queryFirstNoteStartTime(chunkAnalysisList, musicXmlNote);
  504. double correctedEndTime = correctedStartTime + musicXmlNote.getDuration();
  505. chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
  506. LOGGER.debug("------------ correctedStartTime:{} correctedEndTime:{}------------", correctedStartTime, correctedEndTime);
  507. //根据完整度取部分有效信号
  508. int elementSize = chunkAnalysisList.size() * (100 - hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration())) / 100;
  509. chunkAnalysisList = chunkAnalysisList.subList(0, elementSize);
  510. if(chunkAnalysisList == null || chunkAnalysisList.size() == 0){
  511. return -1;
  512. }
  513. reduceNoise(chunkAnalysisList, EvaluationCriteriaEnum.FREQUENCY);
  514. ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
  515. LOGGER.debug("-------startTime:{} endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)
  516. .getEndTime());
  517. List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
  518. List<Integer> chunkFrequencyList = chunkList.stream().map(t -> t.getFrequency()).filter(t -> t.doubleValue() > MIN_FREQUECY && t.doubleValue() < MAX_FREQUECY)
  519. .collect(Collectors.toList());
  520. if (chunkFrequencyList.size() == 0) {
  521. return -1;
  522. }
  523. int frequency = (int) (chunkFrequencyList.stream().mapToInt(t -> t).sum() / chunkFrequencyList.size());
  524. return frequency;
  525. }
  526. /**
  527. * 时值范围内有且只有一个音,且不能间断,且在合理范围内需开始演奏
  528. * 与上一个音相同时,2个音之间需要间断
  529. * @param musicXmlNote
  530. * @return
  531. */
  532. private boolean computeTempoWithFrequency(MusicXmlNote musicXmlNote){
  533. double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration()) / 100;
  534. double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
  535. double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
  536. List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
  537. double correctedStartTime = queryFirstNoteStartTime(chunkAnalysisList, musicXmlNote);
  538. double correctedEndTime = correctedStartTime + musicXmlNote.getDuration();
  539. chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
  540. //根据完整度取部分有效信号
  541. int elementSize = chunkAnalysisList.size() * (100 - hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration())) / 100;
  542. List<ChunkAnalysis> chunkList = chunkAnalysisList.subList(0, elementSize);
  543. if(chunkList == null || chunkList.size() == 0){
  544. return false;
  545. }
  546. reduceNoise(chunkList, EvaluationCriteriaEnum.FREQUENCY);
  547. if (musicXmlNote.getFrequency() == -1) {// 休止符
  548. return chunkList.stream().filter(t -> t.getFrequency() > MIN_FREQUECY).count() <= 1;
  549. }
  550. ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
  551. Optional<ChunkAnalysis> chunkAnalysisOptional = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(firstChunkAnalysis.getStartTime())).reduce((first, second) -> second);
  552. ChunkAnalysis lastChunkAnalysis = null;
  553. if (chunkAnalysisOptional.isPresent()) {
  554. lastChunkAnalysis = chunkAnalysisOptional.get();
  555. }
  556. if(lastChunkAnalysis == null){
  557. lastChunkAnalysis = new ChunkAnalysis(0, 0, -1, 0, 0, 0);
  558. }
  559. /*List<ChunkAnalysis> chunkList = new ArrayList<ChunkAnalysis>(chunkAnalysisList);
  560. if(chunkList.size() == 0){
  561. return false;
  562. }*/
  563. NoteFrequencyRange noteFrequencyRange = null;
  564. ChunkAnalysis chunkAnalysis = null;
  565. boolean tempo = true;
  566. //boolean isContinue = true;
  567. //int unplayedSize = 0;
  568. int firstPeakIndex = -1;
  569. //将信号分堆归类
  570. Map<NoteFrequencyRange, Integer> signalGrouping = new HashMap<NoteFrequencyRange, Integer>();
  571. for (int i = 0; i < chunkList.size(); i++) {
  572. chunkAnalysis = chunkList.get(i);
  573. if (chunkAnalysis != null) {
  574. if (chunkAnalysis.getFrequency() > MIN_FREQUECY || firstPeakIndex > -1) {
  575. if (firstPeakIndex == -1) {
  576. firstPeakIndex = i;
  577. }
  578. noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, chunkAnalysis.getFrequency());
  579. if (signalGrouping.containsKey(noteFrequencyRange)) {
  580. signalGrouping.put(noteFrequencyRange, signalGrouping.get(noteFrequencyRange) + 1);
  581. } else {
  582. signalGrouping.put(noteFrequencyRange, 1);
  583. }
  584. }
  585. }
  586. }
  587. Integer maxTimes = 0, totalTimes = 0;
  588. for (Entry<NoteFrequencyRange, Integer> entry : signalGrouping.entrySet()) {
  589. if (entry.getValue() > maxTimes) {
  590. maxTimes = entry.getValue();
  591. }
  592. totalTimes = totalTimes + entry.getValue();
  593. }
  594. if(totalTimes == 0){
  595. totalTimes = chunkList.size();
  596. }
  597. /**
  598. if (maxTimes * 100 / totalTimes < hardLevel.getIntegrityRange()) {
  599. LOGGER.debug("节奏错误原因:信号分堆后的最大数量不足指定的完成比例");
  600. return false;
  601. }
  602. */
  603. // 判断进入时间点
  604. if(firstPeakIndex * 100 /chunkList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration())){
  605. LOGGER.debug("节奏错误原因:进入时间点太晚");
  606. return false;
  607. }else{
  608. //判断是否与上一个音延续下来的
  609. if(correctedStartTime == musicXmlNote.getTimeStamp() + dynamicOffset) {
  610. if(firstChunkAnalysis.getFrequency() > MIN_FREQUECY && lastChunkAnalysis.getFrequency() > MIN_FREQUECY){
  611. tempo = new NoteFrequencyRange(standardFrequecy, firstChunkAnalysis.getFrequency()).equals(new NoteFrequencyRange(standardFrequecy, lastChunkAnalysis.getFrequency())) == false;
  612. if(tempo == false){
  613. LOGGER.debug("节奏错误原因:上一个音[{}]延续下来导致的", lastChunkAnalysis.getFrequency());
  614. return false;
  615. }
  616. }
  617. }
  618. }
  619. //判断过程中声音是否有起伏
  620. float minValue = 0;
  621. int depth = 0;
  622. for (int i = 1; i < chunkList.size(); i++) {
  623. if(i == 1) {
  624. minValue = chunkList.get(i - 1).getSplDb();
  625. }
  626. if (Math.abs(minValue - chunkList.get(i).getSplDb()) >= 6) {
  627. LOGGER.debug("范围内查询到音波信号,correctedStartTime:{}", chunkList.get(i).getStartTime());
  628. break;
  629. }
  630. minValue = Math.min(minValue , chunkList.get(i).getSplDb());
  631. /**
  632. if (depth >= 2 && Math.max(chunkList.get(i - 1).getPower(), chunkList.get(i).getPower()) / Math.min(chunkList.get(i - 1).getPower(), chunkList.get(i).getPower()) >= 2) {
  633. tempo = false;
  634. depth++;
  635. LOGGER.debug("节奏错误原因:声波不稳定[{}]", chunkList.get(i).getEndTime());
  636. break;
  637. }
  638. */
  639. }
  640. return tempo;
  641. }
  642. private boolean computeTempoWithAmplitude2(MusicXmlNote musicXmlNote) {
  643. double floatingRange = musicXmlNote.getDuration() * hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration()) / 100;
  644. double endTime = musicXmlNote.getTimeStamp() + dynamicOffset + floatingRange;
  645. double startTime = musicXmlNote.getTimeStamp() + dynamicOffset - floatingRange;
  646. LOGGER.debug("------------TimeStamp:{} floatingRange:{} StartTime:{} EndTime:{}------------", musicXmlNote.getTimeStamp(), floatingRange, startTime, endTime);
  647. List<ChunkAnalysis> chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(startTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(endTime)).collect(Collectors.toList());
  648. double correctedStartTime = queryFirstNoteStartTime(chunkAnalysisList, musicXmlNote);
  649. double correctedEndTime = correctedStartTime + musicXmlNote.getDuration();
  650. chunkAnalysisList = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getStartTime()) >= Double.doubleToLongBits(correctedStartTime) && Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(correctedEndTime)).collect(Collectors.toList());
  651. //根据完整度取部分有效信号
  652. int elementSize = chunkAnalysisList.size() * (100 - hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration())) / 100;
  653. List<ChunkAnalysis> chunkList = chunkAnalysisList.subList(0, elementSize);
  654. if(chunkList == null || chunkList.size() == 0){
  655. return false;
  656. }
  657. reduceNoise(chunkList, EvaluationCriteriaEnum.AMPLITUDE);
  658. ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
  659. LOGGER.debug("-------startTime:{} endTime:{}------", firstChunkAnalysis.getStartTime(), chunkList.get(chunkList.size() - 1)
  660. .getEndTime());
  661. if (musicXmlNote.getFrequency() == -1) {// 休止符
  662. LOGGER.debug("--Amplitude:{} Denominator:{}",chunkList.stream().map(t -> t.getAmplitude()).collect(Collectors.toList()), musicXmlNote.getDenominator());
  663. return chunkList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).count() <= 0;
  664. }
  665. Optional<ChunkAnalysis> chunkAnalysisOptional = totalChunkAnalysisList.stream().filter(t -> Double.doubleToLongBits(t.getEndTime()) <= Double.doubleToLongBits(firstChunkAnalysis.getStartTime())).reduce((first, second) -> second);
  666. ChunkAnalysis lastChunkAnalysis = new ChunkAnalysis(0, 0, -1, 0, 0, 0);;
  667. if (chunkAnalysisOptional.isPresent()) {
  668. lastChunkAnalysis = chunkAnalysisOptional.get();
  669. }
  670. List<Integer> chunkAmplitudeList = chunkList.stream().map(ChunkAnalysis::getAmplitude).collect(Collectors.toList());
  671. chunkAmplitudeList.add(0, lastChunkAnalysis.getAmplitude());
  672. LOGGER.debug("--Amplitude:{} Denominator:{}",chunkAmplitudeList.stream().map(t -> t).collect(Collectors.toList()), musicXmlNote.getDenominator());
  673. // 检测是否有多个波峰
  674. boolean tempo = false;
  675. boolean isContinue = false;
  676. int firstPeakIndex = -1;
  677. int firstPeakValue = 0;
  678. int peakSize = 0;
  679. //int range = hardLevel.getAmplitudeThreshold();
  680. int range = 5;
  681. if(StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.DECIBELS.getCode())) {
  682. range = 50;
  683. }
  684. for (int i = 1; i < chunkAmplitudeList.size(); i++) {
  685. if (chunkAmplitudeList.get(i - 1) + range >= chunkAmplitudeList.get(i)) {
  686. isContinue = false;
  687. continue;
  688. }
  689. if(isContinue == false && chunkAmplitudeList.get(i - 1) + range < chunkAmplitudeList.get(i)){
  690. isContinue = true;
  691. peakSize++;
  692. if(firstPeakIndex == -1){
  693. firstPeakIndex = i;
  694. firstPeakValue = chunkAmplitudeList.get(i);
  695. }
  696. }
  697. }
  698. LOGGER.debug("PeakSize:" + peakSize + " FirstPeakIndex:" + firstPeakIndex + " FirstPeakValue:" + firstPeakValue);
  699. /*for (int i = 1; i < chunkAmplitudeList.size(); i++) {
  700. if (Math.abs(chunkAmplitudeList.get(i) - chunkAmplitudeList.get(i - 1)) < hardLevel.getAmplitudeThreshold()) {
  701. continue;
  702. }
  703. if (chunkAmplitudeList.get(i) > hardLevel.getAmplitudeThreshold() && chunkAmplitudeList.get(i) > chunkAmplitudeList.get(i - 1)) {
  704. tempo = true;
  705. if(firstPeakIndex == -1){
  706. firstPeakIndex = i;
  707. peakSize++;
  708. }
  709. if (isContinue == false) {
  710. tempo = false;
  711. peakSize++;
  712. break;
  713. }
  714. } else {
  715. if (tempo == true) {
  716. isContinue = false;
  717. }
  718. }
  719. }*/
  720. if(peakSize == 0){
  721. tempo = lastChunkAnalysis.isPeak();
  722. }else if(peakSize == 1){
  723. tempo = true;
  724. }else{
  725. tempo = false;
  726. LOGGER.debug("有多个波峰");
  727. }
  728. if (tempo) {
  729. // 判断进入时间点
  730. if((firstPeakIndex - 1) * 100 /chunkAmplitudeList.size() > hardLevel.getTempoEffectiveRange(musicXmlNote.getDenominator(), musicXmlNote.getDuration())){
  731. LOGGER.debug("超过范围:{}", (firstPeakIndex - 1) * 100 /chunkAmplitudeList.size());
  732. tempo = false;
  733. }
  734. }
  735. return tempo;
  736. }
  737. private double queryFirstNoteStartTime(List<ChunkAnalysis> chunkAnalysisList, MusicXmlNote musicXmlNote) {
  738. if(chunkAnalysisList == null || chunkAnalysisList.size() == 0){
  739. LOGGER.debug("找不到数据,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
  740. return musicXmlNote.getTimeStamp() + dynamicOffset;
  741. }
  742. if (!StringUtils.equalsIgnoreCase(evaluationCriteria, EvaluationCriteriaEnum.FREQUENCY.getCode())) {
  743. Optional<ChunkAnalysis> optional = chunkAnalysisList.stream().filter(t -> t.getAmplitude() > hardLevel.getAmplitudeThreshold()).findFirst();
  744. if(optional.isPresent()){
  745. LOGGER.debug("范围内查询到信号,correctedStartTime:{}", optional.get().getStartTime());
  746. return optional.get().getStartTime();
  747. }else{
  748. LOGGER.debug("范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
  749. return musicXmlNote.getTimeStamp() + dynamicOffset;
  750. }
  751. }
  752. double startTime = 0;
  753. float minValue = 0;
  754. for (int i = 1; i < chunkAnalysisList.size(); i++) {
  755. if(i == 1) {
  756. minValue = chunkAnalysisList.get(i - 1).getSplDb();
  757. }
  758. if (Math.abs(minValue - chunkAnalysisList.get(i).getSplDb()) >= 6) {
  759. LOGGER.debug("范围内查询到音波信号,correctedStartTime:{}", chunkAnalysisList.get(i).getStartTime());
  760. startTime = chunkAnalysisList.get(i).getStartTime();
  761. break;
  762. }
  763. minValue = Math.min(minValue , chunkAnalysisList.get(i).getSplDb());
  764. /**
  765. if (Math.max(chunkAnalysisList.get(i - 1).getPower(), chunkAnalysisList.get(i).getPower()) / Math.min(chunkAnalysisList.get(i - 1).getPower(), chunkAnalysisList.get(i).getPower()) >= 2) {
  766. LOGGER.debug("范围内查询到音波信号,correctedStartTime:{}", chunkAnalysisList.get(i).getStartTime());
  767. startTime = chunkAnalysisList.get(i).getStartTime();
  768. break;
  769. }
  770. */
  771. }
  772. //判断是否与上一个音是同一个音符
  773. if(musicXmlNote.getMusicalNotesIndex() > 0){
  774. MusicXmlNote preMusicXmlNote = getCurrentMusicNote(null, musicXmlNote.getMusicalNotesIndex() - 1);
  775. if((int)preMusicXmlNote.getFrequency() == (int)musicXmlNote.getFrequency()){
  776. Optional<ChunkAnalysis> optional = chunkAnalysisList.stream().filter(t -> t.getFrequency() <= MIN_FREQUECY).findFirst();
  777. if(optional.isPresent()){
  778. LOGGER.debug("与上一个音同音,有断开,correctedStartTime:{}", optional.get().getEndTime());
  779. return Math.max(optional.get().getEndTime(), startTime);
  780. }
  781. }
  782. }
  783. NoteFrequencyRange standardNote = new NoteFrequencyRange(standardFrequecy, musicXmlNote.getFrequency());
  784. NoteFrequencyRange noteFrequencyRange = null;
  785. for (ChunkAnalysis ca : chunkAnalysisList) {
  786. noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, ca.getFrequency());
  787. if (standardNote.equals(noteFrequencyRange)) {
  788. LOGGER.debug("范围内查询到音高信号,correctedStartTime:{}", ca.getStartTime());
  789. return Math.max(ca.getStartTime(), startTime);
  790. }
  791. }
  792. if(startTime == 0) {
  793. LOGGER.debug("范围内未查询到信号,correctedStartTime:{}", musicXmlNote.getTimeStamp() + dynamicOffset);
  794. }
  795. //return chunkAnalysisList.get(chunkAnalysisList.size() - 1).getEndTime();
  796. return Math.max(musicXmlNote.getTimeStamp() + dynamicOffset, startTime);
  797. }
  798. private void reduceNoise(List<ChunkAnalysis> chunkAnalysisList, EvaluationCriteriaEnum criteria) {
  799. ChunkAnalysis chunkAnalysis = null;
  800. for (int i = 1; i < chunkAnalysisList.size(); i++) {
  801. if (i < chunkAnalysisList.size() - 1) {
  802. chunkAnalysis = chunkAnalysisList.get(i);
  803. if (EvaluationCriteriaEnum.AMPLITUDE == criteria) {
  804. if (chunkAnalysisList.get(i - 1).getAmplitude() == 0 && chunkAnalysisList.get(i + 1).getAmplitude() == 0
  805. && chunkAnalysis.getAmplitude() > 0) {
  806. chunkAnalysis.setAmplitude(0);
  807. //chunkAnalysisList.set(i, chunkAnalysis);
  808. }
  809. }else if(EvaluationCriteriaEnum.FREQUENCY == criteria) {
  810. if (chunkAnalysisList.get(i - 1).getFrequency() == -1 && chunkAnalysisList.get(i + 1).getFrequency() == -1
  811. && chunkAnalysis.getFrequency() > 0) {
  812. chunkAnalysis.setFrequency(-1);
  813. //chunkAnalysisList.set(i, chunkAnalysis);
  814. }
  815. }
  816. }
  817. }
  818. }
  819. }