yonge 9 meses atrás
pai
commit
d87afd83a8

+ 14 - 3
audio-analysis/src/main/java/com/yonge/netty/dto/WebSocketResponse.java

@@ -13,8 +13,8 @@ public class WebSocketResponse<T> {
 		this.body = body;
 	}
 
-	public WebSocketResponse(String command, T body) {
-		this.header = new Head(command, HttpStatus.OK.value());
+	public WebSocketResponse(String type, String command, T body) {
+		this.header = new Head(type, command, HttpStatus.OK.value());
 		this.body = body;
 	}
 
@@ -37,12 +37,15 @@ public class WebSocketResponse<T> {
 	public static class Head {
 		private int status = HttpStatus.OK.value();
 		private String commond = "";
+		
+		private String type;
 
 		public Head() {
 
 		}
 
-		public Head(String commond, int status) {
+		public Head(String type, String commond, int status) {
+			this.type = type;
 			this.commond = commond;
 			this.status = status;
 		}
@@ -63,5 +66,13 @@ public class WebSocketResponse<T> {
 			this.commond = commond;
 		}
 
+		public String getType() {
+			return type;
+		}
+
+		public void setType(String type) {
+			this.type = type;
+		}
+
 	}
 }

+ 15 - 7
audio-analysis/src/main/java/com/yonge/netty/server/handler/NettyServerHandler.java

@@ -1,6 +1,15 @@
 package com.yonge.netty.server.handler;
 
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.stereotype.Component;
+
+import com.yonge.netty.server.service.DelayCheckHandler;
 import com.yonge.netty.server.service.UserChannelContextService;
+
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
@@ -8,13 +17,6 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
 
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
-import org.springframework.stereotype.Component;
-
 @Component
 @ChannelHandler.Sharable
 public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@@ -26,6 +28,10 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 
 	@Autowired
 	private UserChannelContextService userChannelContextService;
+
+	@Autowired
+	private DelayCheckHandler delayCheckHandler;
+	
 	@Override
 	public void channelActive(ChannelHandlerContext ctx) {
 		// 从管理器中添加
@@ -35,6 +41,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 	@Override
 	public void channelUnregistered(ChannelHandlerContext ctx) {
 		userChannelContextService.remove(ctx.channel());
+		delayCheckHandler.getUserABCMap().remove(ctx.channel());
 		// 从管理器中移除
 		channelManager.remove(ctx.channel());
 	}
@@ -43,6 +50,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
 		LOGGER.error("[exceptionCaught][连接({}) 发生异常]", ctx.channel().id(), cause);
 		userChannelContextService.remove(ctx.channel());
+		delayCheckHandler.getUserABCMap().remove(ctx.channel());
 		// 断开连接
 		ctx.channel().close();
 	}

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

@@ -230,7 +230,7 @@ public class AudioCompareHandler implements MessageHandler {
 					sysMusicCompareRecordService.saveMusicCompareData(sysMusicCompareRecord);
 				}
 				
-				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("overall", params);
+				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "overall", params);
 
 				nettyChannelManager.sendTextMessage(user, resp);
 			}
@@ -350,7 +350,7 @@ public class AudioCompareHandler implements MessageHandler {
 				params.put("measureIndex", sectionIndex);
 				params.put("measureRenderIndex", channelContext.getCurrentMusicSection(null, sectionIndex).getMeasureRenderIndex());
 
-				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("measureScore", params);
+				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "measureScore", params);
 
 				nettyChannelManager.sendTextMessage(user, resp);
 			}

+ 269 - 0
audio-analysis/src/main/java/com/yonge/netty/server/service/DelayCheckHandler.java

@@ -0,0 +1,269 @@
+package com.yonge.netty.server.service;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.sound.sampled.AudioFormat;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPath;
+import com.yonge.audio.analysis.AudioFloatConverter;
+import com.yonge.audio.analysis.detector.YINPitchDetector;
+import com.yonge.audio.utils.ArrayUtil;
+import com.yonge.netty.dto.NoteFrequencyRange;
+import com.yonge.netty.dto.WebSocketResponse;
+import com.yonge.netty.server.handler.NettyChannelManager;
+import com.yonge.netty.server.handler.message.MessageHandler;
+import com.yonge.netty.server.processor.WaveformWriter;
+
+import io.netty.channel.Channel;
+
+@Service
+public class DelayCheckHandler implements MessageHandler {
+
+	private final static Logger LOGGER = LoggerFactory.getLogger(DelayCheckHandler.class);
+
+	private int standardFrequecy = 3000;
+
+	/**
+	 * @describe 采样率
+	 */
+	private float sampleRate = 44100;
+
+	/**
+	 * 每个采样大小(Bit)
+	 */
+	private int bitsPerSample = 16;
+
+	/**
+	 * 通道数
+	 */
+	private int channels = 1;
+	
+	private int bufferSize = 1024 * 1;
+
+	private boolean signed = true;
+
+	private boolean bigEndian = false;
+
+	private AudioFormat audioFormat = new AudioFormat(sampleRate, bitsPerSample, channels, signed, bigEndian);
+
+	private AudioFloatConverter converter = AudioFloatConverter.getConverter(audioFormat);
+	
+	private boolean isRecWav = false;
+
+	private ConcurrentMap<Channel, UserContext> userABCMap = new ConcurrentHashMap<Channel, UserContext>();
+
+	private String tmpFileDir = "/mdata/soundCompare/";
+
+	private SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmSS");
+
+	@Autowired
+	private NettyChannelManager nettyChannelManager;
+
+	@Override
+	public String getAction() {
+		return "DELAY_CHECK";
+	}
+
+	@Override
+	public boolean handleTextMessage(String userId, Channel channel, String jsonMsg) {
+
+		String command = (String) JSONPath.extract(jsonMsg, "$.header.commond");
+
+		UserContext userContext = null;
+
+		switch (command) {
+		case "recordStart":
+
+			userContext = new UserContext(0, false);
+
+			userABCMap.put(channel, userContext);
+			
+			JSONObject dataObj = JSONObject.parseObject(JSON.toJSONString(JSONPath.extract(jsonMsg, "$.body")));
+			
+			if(dataObj.get("HZ") != null) {
+				String hzStr = dataObj.get("HZ").toString();
+				standardFrequecy = Integer.parseInt(hzStr);
+			}
+
+			break;
+		case "recordEnd":
+
+			userContext = userABCMap.get(channel);
+
+			if (userContext == null) {
+				userContext = new UserContext(0, false);
+			}
+
+			WaveformWriter waveFileProcessor = userContext.getWaveformWriter();
+			if (waveFileProcessor != null) {
+				// 写文件头
+				waveFileProcessor.processingFinished();
+			}
+
+			userContext = userABCMap.get(channel);
+
+			if (userContext == null) {
+				userContext = new UserContext(0, false);
+			}
+
+			Map<String, Object> params = new HashMap<String, Object>();
+			params.put("firstNoteDelayDuration", userContext.getDelayDuration());
+
+			WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), command, params);
+
+			nettyChannelManager.sendTextMessage(userId, resp);
+
+			userContext.setDelayDuration(0);
+			userContext.setIsOver(false);
+			userContext.setWaveformWriter(null);
+			userContext.setChannelBufferBytes(new byte[0]);
+			userABCMap.put(channel, userContext);
+			break;
+
+		default:
+			break;
+		}
+
+		return true;
+	}
+
+	@Override
+	public boolean handleBinaryMessage(String userId, Channel channel, byte[] bytes) {
+
+		UserContext userContext = userABCMap.get(channel);
+
+		if (userContext == null) {
+			userContext = new UserContext(0, false);
+		}
+
+		// 写录音文件
+		if (isRecWav) {
+			WaveformWriter waveFileProcessor = userContext.getWaveformWriter();
+			if (waveFileProcessor == null) {
+				File file = new File(tmpFileDir + userId + "_CHECK_" + sdf.format(new Date()) + ".wav");
+				waveFileProcessor = new WaveformWriter(file.getAbsolutePath());
+				userContext.setWaveformWriter(waveFileProcessor);
+
+				userABCMap.put(channel, userContext);
+			}
+			waveFileProcessor.process(bytes);
+		}
+
+		if (userContext.isOver) {
+			return true;
+		}
+		
+		userContext.setChannelBufferBytes(ArrayUtil.mergeByte(userContext.getChannelBufferBytes(), bytes));
+
+		int totalLength = userContext.getChannelBufferBytes().length;
+		
+		while (totalLength >= bufferSize) {
+			byte[] bufferData = ArrayUtil.extractByte(userContext.getChannelBufferBytes(), 0, bufferSize - 1);
+
+			if (bufferSize != totalLength) {
+				userContext.setChannelBufferBytes(ArrayUtil.extractByte(userContext.getChannelBufferBytes(), bufferSize, totalLength - 1));
+			} else {
+				userContext.setChannelBufferBytes(new byte[0]);
+			}
+
+			float[] sampleFloats = new float[bufferSize / 2];
+
+			converter.toFloatArray(bufferData, sampleFloats);
+			
+			YINPitchDetector frequencyDetector = new YINPitchDetector(sampleFloats.length, audioFormat.getSampleRate());
+
+			int playFrequency = (int) frequencyDetector.getFrequency(sampleFloats);
+
+			// int amplitude = (int) Signals.decibels(samples);
+
+			double durationTime = 1000 * (sampleFloats.length * 2) / audioFormat.getSampleRate() / (audioFormat.getSampleSizeInBits() / 8);
+
+			double playTime = userContext.delayDuration;
+
+			playTime += durationTime;
+
+			LOGGER.info("DurationTime:{}	 playFrequency:{}  PlayTime:{}" ,durationTime,playFrequency,playTime);
+			
+			NoteFrequencyRange nfr = new NoteFrequencyRange(440, playFrequency);
+			
+			if (nfr.getMinFrequency() < standardFrequecy && nfr.getMaxFrequency() > standardFrequecy) {
+
+				userContext.setIsOver(true);
+				userABCMap.put(channel, userContext);
+				return true;
+			}
+
+			userContext.setDelayDuration(playTime);
+
+			totalLength = userContext.getChannelBufferBytes().length;
+		}
+
+		return true;
+	}
+
+	public ConcurrentMap<Channel, UserContext> getUserABCMap() {
+		return userABCMap;
+	}
+
+	class UserContext {
+
+		public UserContext(double delayDuration, boolean isOver) {
+			this.delayDuration = delayDuration;
+			this.isOver = isOver;
+		}
+
+		private double delayDuration;
+
+		private boolean isOver;
+		
+		private byte[] channelBufferBytes = new byte[0];
+
+		private WaveformWriter waveformWriter;
+
+		public double getDelayDuration() {
+			return delayDuration;
+		}
+
+		public void setDelayDuration(double delayDuration) {
+			this.delayDuration = delayDuration;
+		}
+
+		public boolean isOver() {
+			return isOver;
+		}
+
+		public void setIsOver(boolean isOver) {
+			this.isOver = isOver;
+		}
+
+		public byte[] getChannelBufferBytes() {
+			return channelBufferBytes;
+		}
+
+		public void setChannelBufferBytes(byte[] channelBufferBytes) {
+			this.channelBufferBytes = channelBufferBytes;
+		}
+
+		public WaveformWriter getWaveformWriter() {
+			return waveformWriter;
+		}
+
+		public void setWaveformWriter(WaveformWriter waveformWriter) {
+			this.waveformWriter = waveformWriter;
+		}
+	}
+
+}

+ 1 - 1
audio-analysis/src/main/java/com/yonge/netty/server/service/PitchDetectionHandler.java

@@ -80,7 +80,7 @@ public class PitchDetectionHandler implements MessageHandler {
 		Map<String, Object> params = new HashMap<String, Object>();
 		params.put("frequency", playFrequency);
 
-		WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("checking", params);
+		WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "checking", params);
 
 		nettyChannelManager.sendTextMessage(userId, resp);