yonge 3 năm trước cách đây
mục cha
commit
17ecb8dfc1

+ 30 - 5
audio-analysis/src/main/java/com/yonge/audio/utils/ArrayUtil.java

@@ -12,9 +12,17 @@ public class ArrayUtil {
 		if (bt2.length == 0) {
 			return bt1;
 		}
+
 		byte[] bt3 = new byte[bt1.length + bt2.length];
-		System.arraycopy(bt1, 0, bt3, 0, bt1.length);
-		System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
+
+		if (bt1.length > 0) {
+			System.arraycopy(bt1, 0, bt3, 0, bt1.length);
+		}
+
+		if (bt2.length > 0) {
+			System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
+		}
+
 		return bt3;
 	}
 
@@ -26,6 +34,11 @@ public class ArrayUtil {
 	 * @return
 	 */
 	public static byte[] extractByte(byte[] src, int startIndex, int endIndex) {
+
+		if (startIndex > endIndex) {
+			throw new RuntimeException("结束索引[" + endIndex + "]不能小于起始索引[" + startIndex + "]");
+		}
+
 		byte[] target = new byte[endIndex - startIndex];
 		System.arraycopy(src, startIndex, target, 0, endIndex - startIndex);
 
@@ -42,9 +55,17 @@ public class ArrayUtil {
 		if (bt2.length == 0) {
 			return bt1;
 		}
+
 		float[] bt3 = new float[bt1.length + bt2.length];
-		System.arraycopy(bt1, 0, bt3, 0, bt1.length);
-		System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
+
+		if (bt1.length > 0) {
+			System.arraycopy(bt1, 0, bt3, 0, bt1.length);
+		}
+
+		if (bt2.length > 0) {
+			System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
+		}
+
 		return bt3;
 	}
 
@@ -56,6 +77,10 @@ public class ArrayUtil {
 	 * @return
 	 */
 	public static float[] extractFloat(float[] src, int startIndex, int endIndex) {
+		if (startIndex > endIndex) {
+			throw new RuntimeException("结束索引[" + endIndex + "]不能小于起始索引[" + startIndex + "]");
+		}
+
 		float[] target = new float[endIndex - startIndex];
 		System.arraycopy(src, startIndex, target, 0, endIndex - startIndex);
 
@@ -65,7 +90,7 @@ public class ArrayUtil {
 	public static void main(String[] args) {
 		byte[] b1 = { 1, 2, 3, 4, 5 };
 		byte[] b2 = { 3, 2, 1 };
-		byte[] r = extractByte(b1, 1, 3);
+		byte[] r = mergeByte(b1, b2);
 		for (int i = 0; i < r.length; i++) {
 			System.out.println(r[i]);
 		}

+ 33 - 14
audio-analysis/src/main/java/com/yonge/nettty/dto/UserChannelContext.java

@@ -25,7 +25,7 @@ public class UserChannelContext {
 
 	// 当前乐谱小节索引
 	private AtomicInteger currentMusicSectionIndex = new AtomicInteger(0);
-	
+
 	// 缓存字节数据
 	private byte[] bufferBytes = new byte[0];
 
@@ -53,10 +53,6 @@ public class UserChannelContext {
 		return currentNoteIndex.intValue();
 	}
 
-	public void resetCurrentMusicNoteIndex() {
-		currentNoteIndex.set(0);
-	}
-
 	public int incrementMusicSectionIndex() {
 		return currentMusicSectionIndex.incrementAndGet();
 	}
@@ -64,9 +60,13 @@ public class UserChannelContext {
 	public int getCurrentMusicSectionIndex() {
 		return currentMusicSectionIndex.intValue();
 	}
-	
-	public void resetCurrentMusicSectionIndex(){
+
+	public void resetUserInfo() {
+
+		waveFileProcessor = null;
+		currentNoteIndex.set(0);
 		currentMusicSectionIndex.set(0);
+		bufferBytes = new byte[0];
 	}
 
 	public MusicXmlNote getCurrentMusicNote(Integer songId) {
@@ -80,16 +80,35 @@ public class UserChannelContext {
 			musicXmlBasicInfo = songMusicXmlMap.get(songId);
 		}
 
-		if (musicXmlBasicInfo != null) {
-			if(getCurrentMusicNoteIndex() >= musicXmlBasicInfo.getMusicXmlInfos().size()){
-				return musicXmlBasicInfo.getMusicXmlInfos().get(musicXmlBasicInfo.getMusicXmlInfos().size() - 1);
-			}
-			return musicXmlBasicInfo.getMusicXmlInfos().get(getCurrentMusicNoteIndex());
+		if (musicXmlBasicInfo != null && getCurrentMusicNoteIndex() <= getTotalMusicNoteIndexNum(null)) {
+			/*
+			 * if (getCurrentMusicNoteIndex() >= musicXmlBasicInfo.getMusicXmlInfos().size()) { return
+			 * musicXmlBasicInfo.getMusicXmlInfos().get(musicXmlBasicInfo.getMusicXmlInfos().size() - 1); }
+			 */
+			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == getCurrentMusicNoteIndex()).findFirst().get();
 		}
 
 		return null;
 	}
 
+	public int getTotalMusicNoteIndexNum(Integer songId) {
+		if (songMusicXmlMap.size() == 0) {
+			return -1;
+		}
+		MusicXmlBasicInfo musicXmlBasicInfo = null;
+		if (songId == null) {
+			musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
+		} else {
+			musicXmlBasicInfo = songMusicXmlMap.get(songId);
+		}
+
+		if (musicXmlBasicInfo != null) {
+			return musicXmlBasicInfo.getMusicXmlInfos().stream().map(t -> t.getMusicalNotesIndex()).distinct().max(Integer::compareTo).get();
+		}
+
+		return -1;
+	}
+
 	public List<MusicXmlNote> getCurrentMusicSection(Integer songId) {
 		if (songMusicXmlMap.size() == 0) {
 			return null;
@@ -111,7 +130,7 @@ public class UserChannelContext {
 
 	public int getTotalMusicSectionIndexNum(Integer songId) {
 		if (songMusicXmlMap.size() == 0) {
-			return 0;
+			return -1;
 		}
 		MusicXmlBasicInfo musicXmlBasicInfo = null;
 		if (songId == null) {
@@ -124,7 +143,7 @@ public class UserChannelContext {
 			return musicXmlBasicInfo.getMusicXmlInfos().stream().map(t -> t.getMeasureIndex()).distinct().max(Integer::compareTo).get();
 		}
 
-		return 0;
+		return -1;
 	}
 
 	public byte[] getBufferBytes() {

+ 62 - 52
audio-analysis/src/main/java/com/yonge/netty/server/messagehandler/BinaryWebSocketFrameHandler.java

@@ -66,11 +66,11 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 	/**
 	 * @describe 采样大小
 	 */
-	private int sampleSize = 1024 * 4;
+	private int bufferSize = 1024 * 4;
 	/**
 	 * @describe 帧覆盖大小
 	 */
-	private int overlap = 256;
+	private int overlap = 0;
 
 	private boolean signed = true;
 
@@ -83,7 +83,7 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 
 	private PitchEstimationAlgorithm algorithm = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN;
 
-	private PitchDetector pitchDetector = algorithm.getDetector(sampleRate, sampleSize);
+	private PitchDetector pitchDetector = algorithm.getDetector(sampleRate, bufferSize);
 
 	/**
 	 * @describe 有效分贝大小
@@ -114,75 +114,85 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 		Channel channel = ctx.channel();
 
 		ByteBuf buf = frame.content().retain();
+		
+		try {
+			byte[] datas = ByteBufUtil.getBytes(buf);
 
-		byte[] datas = ByteBufUtil.getBytes(buf);
+			String user = nettyChannelManager.getUser(channel);
 
-		String user = nettyChannelManager.getUser(channel);
+			UserChannelContext channelContext = userChannelContextService.getChannelContext(channel);
 
-		UserChannelContext channelContext = userChannelContextService.getChannelContext(channel);
+			if (channelContext == null) {
+				return;
+			}
 
-		if (channelContext == null) {
-			return;
-		}
-		
-		// 写录音文件
-		WaveformWriter waveFileProcessor = channelContext.getWaveFileProcessor();
-		if (waveFileProcessor == null) {
-			File file = new File(tmpFileDir + user + "_" + System.currentTimeMillis() + ".wav");
-			waveFileProcessor = new WaveformWriter(file.getAbsolutePath());
-			channelContext.setWaveFileProcessor(waveFileProcessor);
-		}
-		waveFileProcessor.process(datas);
+			// 写录音文件
+			WaveformWriter waveFileProcessor = channelContext.getWaveFileProcessor();
+			if (waveFileProcessor == null) {
+				File file = new File(tmpFileDir + user + "_" + System.currentTimeMillis() + ".wav");
+				waveFileProcessor = new WaveformWriter(file.getAbsolutePath());
+				channelContext.setWaveFileProcessor(waveFileProcessor);
+			}
+			waveFileProcessor.process(datas);
 
-		LOGGER.info("服务器接收到二进制消息长度[{}]", datas.length);
+			// LOGGER.info("服务器接收到的音频消息长度[{}]", datas.length);
 
-		AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(datas, audioFormat, sampleSize, overlap);
+			// 粘合数据
+			byte[] totalBytes = ArrayUtil.mergeByte(channelContext.getBufferBytes(), datas);
+			channelContext.setBufferBytes(totalBytes);
 
-		dispatcher.addAudioProcessor(new PitchProcessor(algorithm, sampleRate, sampleSize, new PitchDetectionHandler() {
+			// 获取当前音符信息
+			MusicXmlNote musicXmlNote = channelContext.getCurrentMusicNote(null);
 
-			@Override
-			public void handlePitch(PitchDetectionResult pitchDetectionResult, AudioEvent audioEvent) {
+			if (musicXmlNote == null) {
+				return;
+			}
 
-				// 获取字节流
-				int byteOverlap = audioEvent.getOverlap() * audioFormat.getFrameSize();
-				int byteStepSize = audioEvent.getBufferSize() * audioFormat.getFrameSize() - byteOverlap;
-				byte[] acceptDatas = ArrayUtils.subarray(audioEvent.getByteBuffer(), byteOverlap, byteStepSize);
+			// 计算当前音符的数据长度 公式:数据量(字节/秒)= 采样频率(Hz)× (采样位数(bit)/ 8) × 声道数
+			int length = (int) (audioFormat.getSampleRate() * (audioFormat.getSampleSizeInBits() / 8) * channels * musicXmlNote.getDuration() / 1000);
 
-				// 粘合数据
-				byte[] totalBytes = ArrayUtil.mergeByte(channelContext.getBufferBytes(), acceptDatas);
-				channelContext.setBufferBytes(totalBytes);
+			if (channelContext.getCurrentMusicNoteIndex() <= channelContext.getTotalMusicNoteIndexNum(null) && totalBytes.length >= length) {
+				// 处理当前音符
+				byte[] noteByteData = new byte[length];
+				System.arraycopy(totalBytes, 0, noteByteData, 0, length);
 
-				LOGGER.info("新增字节数:{} 最新剩余字节长度:{}", acceptDatas.length, totalBytes.length);
+				float[] noteFloatData = new float[length / audioFormat.getFrameSize()];
 
-				// 获取当前音符信息
-				MusicXmlNote musicXmlNote = channelContext.getCurrentMusicNote(null);
+				converter.toFloatArray(noteByteData, noteFloatData);
 
-				// 计算当前音符的数据长度 公式:数据量(字节/秒)= 采样频率(Hz)× (采样位数(bit)/ 8) × 声道数
-				int length = (int) (audioFormat.getSampleRate() * (audioFormat.getSampleSizeInBits() / 8) * channels * musicXmlNote.getDuration() / 1000);
+				// 获取频率数据
+				float pitch = getPitch(noteFloatData, bufferSize);
 
-				if (totalBytes.length >= length) {
-					// 处理当前音符
-					byte[] noteByteData = new byte[length];
-					System.arraycopy(totalBytes, 0, noteByteData, 0, length);
+				LOGGER.info("第{}个音符的样本频率:{} 实际频率:{}", channelContext.getCurrentMusicNoteIndex(), musicXmlNote.getFrequency(), pitch);
 
-					float[] noteFloatData = new float[length / audioFormat.getFrameSize()];
+				// 准备处理下一个音符
+				channelContext.incrementMusicNoteIndex();
+				// 剩余未处理的数据
+				channelContext.setBufferBytes(ArrayUtil.extractByte(totalBytes, length, totalBytes.length - 1));
+			}
+			
 
-					converter.toFloatArray(noteByteData, noteFloatData);
+			AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(datas, audioFormat, bufferSize, overlap);
 
-					// 获取频率数据
-					float pitch = getPitch(noteFloatData, sampleSize);
+			dispatcher.addAudioProcessor(new PitchProcessor(algorithm, sampleRate, bufferSize, new PitchDetectionHandler() {
 
-					LOGGER.info("第{}个音符的样本频率:{} 实际频率:{}", channelContext.getCurrentMusicNoteIndex(), musicXmlNote.getFrequency(), pitch);
+				@Override
+				public void handlePitch(PitchDetectionResult pitchDetectionResult, AudioEvent audioEvent) {
 
-					// 准备处理下一个音符
-					channelContext.incrementMusicNoteIndex();
-					// 剩余未处理的数据
-					channelContext.setBufferBytes(ArrayUtil.extractByte(totalBytes, 0, length - 1));
-				}
+					// 获取字节流
+					int byteOverlap = audioEvent.getOverlap() * audioFormat.getFrameSize();
+					int byteStepSize = audioEvent.getBufferSize() * audioFormat.getFrameSize() - byteOverlap;
+					byte[] acceptDatas = ArrayUtils.subarray(audioEvent.getByteBuffer(), byteOverlap, byteStepSize);
 
-			}
-		}));
-		dispatcher.run();
+					LOGGER.info("新增字节数:{} 最新剩余字节长度:{}", acceptDatas.length, totalBytes.length);
+
+					
+				}
+			}));
+			dispatcher.run();
+		} finally {
+			buf.release();
+		}
 	}
 
 	private float getPitch(float[] audioBuffer, int bufferSize) {

+ 4 - 3
audio-analysis/src/main/java/com/yonge/netty/server/messagehandler/TextWebSocketHandler.java

@@ -70,9 +70,7 @@ public class TextWebSocketHandler extends SimpleChannelInboundHandler<TextWebSoc
 			sysMusicCompareRecordService.insert(sysMusicCompareRecord);
 			
 			//清空缓存信息
-			channelContext.setWaveFileProcessor(null);
-			channelContext.resetCurrentMusicNoteIndex();
-			channelContext.resetCurrentMusicSectionIndex();
+			channelContext.resetUserInfo();
 
 			break;
 		case "recordEnd": // 结束评测
@@ -86,6 +84,9 @@ public class TextWebSocketHandler extends SimpleChannelInboundHandler<TextWebSoc
 				// 写文件头
 				waveFileProcessor.processingFinished();
 			}
+			
+			//清空缓存信息
+			channelContext.resetUserInfo();
 
 			if (StringUtils.equals(commond, "recordEnd")) {
 				// 生成评测报告

+ 1 - 2
audio-analysis/src/main/java/com/yonge/netty/server/processor/WaveformWriter.java

@@ -24,7 +24,6 @@
 package com.yonge.netty.server.processor;
 
 import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 
@@ -79,7 +78,7 @@ public class WaveformWriter {
 
 	public void processingFinished() {
 		try {
-			WaveHeader waveHeader = new WaveHeader(WaveHeader.FORMAT_PCM, channelNum, sampleRate, bitsPerSample, (int) randomAccessFile.length());// 16
+			WaveHeader waveHeader = new WaveHeader(WaveHeader.FORMAT_PCM, channelNum, sampleRate, bitsPerSample, (int) randomAccessFile.length() - HEADER_LENGTH);// 16
 																																					// is
 			ByteArrayOutputStream header = new ByteArrayOutputStream();
 			waveHeader.write(header);