|  | @@ -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;
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +}
 |