| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 | package com.yonge.netty.server.service;import io.netty.channel.Channel;import java.io.File;import java.math.BigDecimal;import java.text.SimpleDateFormat;import java.util.Comparator;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Objects;import java.util.stream.Collectors;import javax.sound.sampled.AudioFormat;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.JSONPath;import com.ym.mec.auth.api.client.SysUserFeignService;import com.ym.mec.auth.api.entity.SysUser;import com.ym.mec.biz.dal.entity.SysMusicCompareRecord;import com.ym.mec.biz.dal.enums.DeviceTypeEnum;import com.ym.mec.biz.dal.enums.FeatureType;import com.ym.mec.biz.dal.enums.HeardLevelEnum;import com.ym.mec.biz.service.SysMusicCompareRecordService;import com.ym.mec.thirdparty.storage.StoragePluginContext;import com.ym.mec.thirdparty.storage.provider.KS3StoragePlugin;import com.ym.mec.util.upload.UploadUtil;import com.yonge.audio.analysis.AudioFloatConverter;import com.yonge.audio.utils.ArrayUtil;import com.yonge.netty.dto.SectionAnalysis;import com.yonge.netty.dto.UserChannelContext;import com.yonge.netty.dto.WebSocketResponse;import com.yonge.netty.entity.MusicXmlBasicInfo;import com.yonge.netty.entity.MusicXmlNote;import com.yonge.netty.server.handler.NettyChannelManager;import com.yonge.netty.server.handler.message.MessageHandler;import com.yonge.netty.server.processor.WaveformWriter;@Componentpublic class AudioCompareHandler implements MessageHandler {		private static final Logger LOGGER = LoggerFactory.getLogger(AudioCompareHandler.class);	@Autowired	private UserChannelContextService userChannelContextService;	@Autowired	private NettyChannelManager nettyChannelManager;		@Autowired	private SysMusicCompareRecordService sysMusicCompareRecordService;	    @Autowired    private SysUserFeignService sysUserFeignService;    @Autowired    private StoragePluginContext storagePluginContext;	/**	 * @describe 采样率	 */	private float sampleRate = 44100;	/**	 * 每个采样大小(Bit)	 */	private int bitsPerSample = 16;	/**	 * 通道数	 */	private int channels = 1;	/**	 * @describe 采样大小	 */	private int bufferSize = 1024 * 2;	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 String tmpFileDir = "/mdata/soundCompare/";	private SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmSS");		@Override	public String getAction() {		return "SOUND_COMPARE";	}	@Override	public boolean handleTextMessage(String user, Channel channel, String jsonMsg) {				String command = (String) JSONPath.extract(jsonMsg, "$.header.commond");		JSONObject dataObj = (JSONObject) JSONPath.extract(jsonMsg, "$.body");				UserChannelContext channelContext = userChannelContextService.getChannelContext(channel);				MusicXmlBasicInfo musicXmlBasicInfo = null;		switch (command) {		case "musicXml": // 同步music xml信息						musicXmlBasicInfo = JSONObject.toJavaObject(dataObj, MusicXmlBasicInfo.class);						userChannelContextService.remove(channel);			channelContext = new UserChannelContext();						channelContext.setHandlerSwitch(false);			channelContext.getSongMusicXmlMap().put(musicXmlBasicInfo.getExamSongId(), musicXmlBasicInfo);			channelContext.init(musicXmlBasicInfo.getPlatform(), musicXmlBasicInfo.getHeardLevel(), musicXmlBasicInfo.getSubjectId(),					musicXmlBasicInfo.getBeatLength(), audioFormat.getSampleRate(), bufferSize / 2);			channelContext.setUser(user);						userChannelContextService.register(channel, channelContext);			break;		case "recordStart": // 开始评测			// 清空缓存信息			channelContext.resetUserInfo();						channelContext.setHandlerSwitch(false);						musicXmlBasicInfo = channelContext.getMusicXmlBasicInfo(null);			if (musicXmlBasicInfo != null) {				Date date = new Date();				SysMusicCompareRecord sysMusicCompareRecord = new SysMusicCompareRecord(FeatureType.CLOUD_STUDY_EVALUATION);				sysMusicCompareRecord.setCreateTime(date);				sysMusicCompareRecord.setUserId(Integer.parseInt(user));				sysMusicCompareRecord.setSysMusicScoreId(musicXmlBasicInfo.getExamSongId());				sysMusicCompareRecord.setBehaviorId(musicXmlBasicInfo.getBehaviorId());				sysMusicCompareRecord.setClientId(musicXmlBasicInfo.getClientId());				sysMusicCompareRecord.setDeviceType(DeviceTypeEnum.valueOf(musicXmlBasicInfo.getPlatform()));				sysMusicCompareRecord.setSpeed(musicXmlBasicInfo.getSpeed());				sysMusicCompareRecord.setPartIndex(musicXmlBasicInfo.getPartIndex());								SysUser sysUser = sysUserFeignService.queryUserById(sysMusicCompareRecord.getUserId());				sysMusicCompareRecord.setTenantId(sysUser.getTenantId());								MusicXmlNote musicXmlNote = musicXmlBasicInfo.getMusicXmlInfos().stream().max(Comparator.comparing(MusicXmlNote::getTimeStamp)).get();				sysMusicCompareRecord.setSourceTime((float) ((musicXmlNote.getTimeStamp()+musicXmlNote.getDuration())/1000));				sysMusicCompareRecordService.insert(sysMusicCompareRecord);				channelContext.setRecordId(sysMusicCompareRecord.getId());			}			break;		case "recordEnd": // 结束评测		case "recordCancel": // 取消评测			if (channelContext == null) {				return false;			}						channelContext.setHandlerSwitch(false);			WaveformWriter waveFileProcessor = channelContext.getWaveFileProcessor();			if (waveFileProcessor != null) {				// 写文件头				waveFileProcessor.processingFinished();			}			if (StringUtils.equals(command, "recordEnd")) {				// 生成评测报告				Map<String, Object> params = new HashMap<String, Object>();				Map<String, Integer> scoreMap = channelContext.evaluateForMusic();				for (Entry<String, Integer> entry : scoreMap.entrySet()) {					params.put(entry.getKey(), entry.getValue());				}								//保存评测结果				Long recordId = channelContext.getRecordId();				SysMusicCompareRecord sysMusicCompareRecord = sysMusicCompareRecordService.get(recordId);				if(sysMusicCompareRecord != null){					musicXmlBasicInfo = channelContext.getMusicXmlBasicInfo(null);										if (scoreMap != null && scoreMap.size() > 1) {						sysMusicCompareRecord.setScore(new BigDecimal(scoreMap.get("score")));						sysMusicCompareRecord.setIntonation(new BigDecimal(scoreMap.get("intonation")));						sysMusicCompareRecord.setIntegrity(new BigDecimal(scoreMap.get("integrity")));						sysMusicCompareRecord.setCadence(new BigDecimal(scoreMap.get("cadence")));						sysMusicCompareRecord.setPlayTime(scoreMap.get("playTime") / 1000);												LOGGER.info("Score:{} Intonation:{} Integrity:{} Cadence:{}", sysMusicCompareRecord.getScore(),sysMusicCompareRecord.getIntonation(),sysMusicCompareRecord.getIntegrity(),sysMusicCompareRecord.getCadence());					}					sysMusicCompareRecord.setFeature(FeatureType.CLOUD_STUDY_EVALUATION);		            String url = null;		            try {		                String folder = UploadUtil.getFileFloder();		                url = storagePluginContext.asyncUploadFile(KS3StoragePlugin.PLUGIN_NAME,"cloud-coach/" + folder, waveFileProcessor.getFile(), true);		            } catch (Exception e) {		                LOGGER.error("录音文件上传失败:{}", e);		            }					sysMusicCompareRecord.setRecordFilePath(url);					//sysMusicCompareRecord.setVideoFilePath(videoFilePath);					Map<String, Object> scoreData = new HashMap<>();					List<SectionAnalysis> sectionAnalysisList = channelContext.getDoneSectionAnalysisList();					sectionAnalysisList = sectionAnalysisList.stream().filter(t -> t.isIngore() == false).collect(Collectors.toList());					scoreData.put("userMeasureScore", sectionAnalysisList.stream().collect(Collectors.toMap(SectionAnalysis :: getIndex, t -> t)));					Map<String, Object> musicalNotesPlayStats = new HashMap<>();					musicalNotesPlayStats.put("detailId", musicXmlBasicInfo.getDetailId());					musicalNotesPlayStats.put("examSongId", musicXmlBasicInfo.getExamSongId());					musicalNotesPlayStats.put("xmlUrl", musicXmlBasicInfo.getXmlUrl());										musicalNotesPlayStats.put("notesData", channelContext.getDoneNoteAnalysisList().stream().filter(t -> t.isIgnore() == false).collect(Collectors.toList()));					scoreData.put("musicalNotesPlayStats", musicalNotesPlayStats);					sysMusicCompareRecord.setScoreData(JSON.toJSONString(scoreData));										sysMusicCompareRecord.setHeardLevel(HeardLevelEnum.valueOf(channelContext.getHardLevel().name()));										sysMusicCompareRecordService.saveMusicCompareData(sysMusicCompareRecord);				}								int totalPlayTimeOfCurrentDate = sysMusicCompareRecordService.queryCurrentDatePlayTimeByUserId(sysMusicCompareRecord.getUserId());								params.put("totalPlayTimeOfCurrentDate", totalPlayTimeOfCurrentDate);								WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("overall", params);				nettyChannelManager.sendTextMessage(user, resp);			}			// 清空缓存信息			channelContext.resetUserInfo();			break;		case "audioPlayStart": // ???						Integer offsetTime = dataObj.getInteger("offsetTime");			if(offsetTime != null){				channelContext.setOffsetMS(offsetTime);				channelContext.setHandlerSwitch(true);			}			break;		case "videoUpload": // 上传音频			SysMusicCompareRecord musicCompareRecord = null;			if (dataObj.containsKey("recordId")) {				musicCompareRecord = sysMusicCompareRecordService.get(dataObj.getLong("recordId"));			}			if (Objects.nonNull(musicCompareRecord) && dataObj.containsKey("filePath")) {				musicCompareRecord.setVideoFilePath(dataObj.getString("filePath"));				sysMusicCompareRecordService.update(musicCompareRecord);			} else {				musicCompareRecord.setVideoFilePath(musicCompareRecord.getRecordFilePath());				sysMusicCompareRecordService.update(musicCompareRecord);			}						break;		default:			// 非法请求			break;		}		return true;	}	@Override	public boolean handleBinaryMessage(String user, Channel channel, byte[] datas) {				UserChannelContext channelContext = userChannelContextService.getChannelContext(channel);		if (channelContext == null) {			return false;		}		// 写录音文件		WaveformWriter waveFileProcessor = channelContext.getWaveFileProcessor();		if (waveFileProcessor == null) {			File file = new File(tmpFileDir + user + "_" + sdf.format(new Date()) + ".wav");			waveFileProcessor = new WaveformWriter(file.getAbsolutePath());			channelContext.setWaveFileProcessor(waveFileProcessor);		}		waveFileProcessor.process(datas);				/*datas = channelContext.skipMetronome(datas);		if (datas.length == 0) {			return false;		}*/		channelContext.setChannelBufferBytes(ArrayUtil.mergeByte(channelContext.getChannelBufferBytes(), datas));		int totalLength = channelContext.getChannelBufferBytes().length;				if (channelContext.getHandlerSwitch() == false) {			return false;		}				if (channelContext.getOffsetMS() + channelContext.getBeatDuration() > 0) {			int beatByteLength = (int) (audioFormat.getSampleRate() * audioFormat.getSampleSizeInBits() / 8 * (channelContext.getOffsetMS() + channelContext.getBeatDuration()) / 1000);						if(totalLength > beatByteLength){				if(beatByteLength % 2 != 0){					LOGGER.debug("**************奇数*****************");					beatByteLength--;				}				channelContext.setChannelBufferBytes(ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), beatByteLength, totalLength - 1));								LOGGER.debug("--------Length:{}  Times[{} + {}]:{}--------", waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length, channelContext.getOffsetMS() , channelContext.getBeatDuration(),(waveFileProcessor.getFile().length() - channelContext.getChannelBufferBytes().length) * 1000 /audioFormat.getSampleRate()/2);								channelContext.setOffsetMS(0);				channelContext.setBeatDuration(0);			}else{				return false;			}		}				totalLength = channelContext.getChannelBufferBytes().length;				while (totalLength >= bufferSize) {			byte[] bufferData = ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), 0, bufferSize - 1);			if (bufferSize != totalLength) {				channelContext.setChannelBufferBytes(ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), bufferSize, totalLength - 1));			} else {				channelContext.setChannelBufferBytes(new byte[0]);			}			float[] sampleFloats = new float[bufferSize / 2];			converter.toFloatArray(bufferData, sampleFloats);			channelContext.handle(sampleFloats, audioFormat);			MusicXmlBasicInfo musicXmlBasicInfo = channelContext.getMusicXmlBasicInfo(null);			int sectionIndex = channelContext.getEvaluatingSectionIndex().get();			// 评分			int score = channelContext.evaluateForSection(sectionIndex, musicXmlBasicInfo.getSubjectId());			if (score >= 0) {				Map<String, Object> params = new HashMap<String, Object>();				params.put("score", score);				params.put("measureIndex", sectionIndex);				params.put("measureRenderIndex", channelContext.getCurrentMusicSection(null, sectionIndex).getMeasureRenderIndex());				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("measureScore", params);				nettyChannelManager.sendTextMessage(user, resp);			}			totalLength = channelContext.getChannelBufferBytes().length;		}		return true;	}}
 |