|
@@ -0,0 +1,175 @@
|
|
|
+package com.ym.mec.biz.service.impl;
|
|
|
+
|
|
|
+import be.tarsos.dsp.AudioDispatcher;
|
|
|
+import be.tarsos.dsp.AudioEvent;
|
|
|
+import be.tarsos.dsp.AudioProcessor;
|
|
|
+import be.tarsos.dsp.SilenceDetector;
|
|
|
+import be.tarsos.dsp.beatroot.BeatRootOnsetEventHandler;
|
|
|
+import be.tarsos.dsp.io.PipedAudioStream;
|
|
|
+import be.tarsos.dsp.io.TarsosDSPAudioInputStream;
|
|
|
+import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
|
|
|
+import be.tarsos.dsp.onsets.ComplexOnsetDetector;
|
|
|
+import be.tarsos.dsp.onsets.OnsetHandler;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.ym.mec.biz.dal.dao.SysMusicScoreDao;
|
|
|
+import com.ym.mec.biz.dal.entity.SysMusicScore;
|
|
|
+import com.ym.mec.biz.service.SoundService;
|
|
|
+import com.ym.mec.common.exception.BizException;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import javax.sound.sampled.AudioFileFormat;
|
|
|
+import javax.sound.sampled.AudioFormat;
|
|
|
+import javax.sound.sampled.AudioSystem;
|
|
|
+import javax.sound.sampled.UnsupportedAudioFileException;
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.MalformedURLException;
|
|
|
+import java.net.URL;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Author Joburgess
|
|
|
+ * @Date 2021/5/19 0019
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class SoundServiceImpl implements SoundService {
|
|
|
+
|
|
|
+ private float sampleRate = 44100;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysMusicScoreDao sysMusicScoreDao;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @describe 音频节拍信息提取
|
|
|
+ * @author Joburgess
|
|
|
+ * @date 2021/5/19 0019
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private List<Double> beatExtractor(byte[] bytes, String url) throws UnsupportedAudioFileException, IOException {
|
|
|
+ List<Double> times = new ArrayList<>();
|
|
|
+ int size = 256;
|
|
|
+ int overlap = 128;
|
|
|
+ AudioDispatcher dispatcher = StringUtils.isBlank(url)?getFromByteArray(bytes, size, overlap):getFromUtl(url, size, overlap);
|
|
|
+
|
|
|
+ ComplexOnsetDetector detector = new ComplexOnsetDetector(size);
|
|
|
+ BeatRootOnsetEventHandler handler = new BeatRootOnsetEventHandler();
|
|
|
+ detector.setHandler(handler);
|
|
|
+
|
|
|
+ dispatcher.addAudioProcessor(detector);
|
|
|
+ dispatcher.run();
|
|
|
+
|
|
|
+ handler.trackBeats(new OnsetHandler() {
|
|
|
+ @Override
|
|
|
+ public void handleOnset(double time, double salience) {
|
|
|
+ times.add(time);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return times;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @describe 音频分贝信息提取
|
|
|
+ * @author Joburgess
|
|
|
+ * @date 2021/5/19 0019
|
|
|
+ * @param bytes: 文件字节
|
|
|
+ * @return java.util.List<java.lang.Double>
|
|
|
+ */
|
|
|
+ private List<Double> soundPressureLevelExtractor(byte[] bytes, String url) throws UnsupportedAudioFileException, IOException {
|
|
|
+ List<Double> pitchs = new ArrayList<>();
|
|
|
+ int size = 2048;
|
|
|
+ int overlap = 0;
|
|
|
+ final SilenceDetector silenceDetecor = new SilenceDetector();
|
|
|
+ AudioDispatcher dispatcher = StringUtils.isBlank(url)?getFromByteArray(bytes, size, overlap):getFromUtl(url, size, overlap);
|
|
|
+ dispatcher.addAudioProcessor(silenceDetecor);
|
|
|
+ dispatcher.addAudioProcessor(new AudioProcessor() {
|
|
|
+ @Override
|
|
|
+ public void processingFinished() {
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean process(AudioEvent audioEvent) {
|
|
|
+ pitchs.add(Double.isInfinite(silenceDetecor.currentSPL())?0:silenceDetecor.currentSPL());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ dispatcher.run();
|
|
|
+ return pitchs;
|
|
|
+ }
|
|
|
+
|
|
|
+ private AudioDispatcher getFromByteArray(byte[] bytes, int size, int overlap) throws UnsupportedAudioFileException {
|
|
|
+ AudioFormat audioFormat = new AudioFormat(sampleRate, 16, 1, true, false);
|
|
|
+ AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(bytes, audioFormat, size, overlap);
|
|
|
+ return dispatcher;
|
|
|
+ }
|
|
|
+
|
|
|
+ private AudioDispatcher getFromUtl(String url, int size, int overlap) throws UnsupportedAudioFileException, IOException {
|
|
|
+ AudioDispatcher dispatcher = AudioDispatcherFactory.fromURL(new URL(url), size, overlap);
|
|
|
+ return dispatcher;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void compare(MultipartFile record, Integer musicScoreId) {
|
|
|
+ SysMusicScore sysMusicScore = sysMusicScoreDao.get(musicScoreId);
|
|
|
+ if(Objects.isNull(sysMusicScore)|| StringUtils.isBlank(sysMusicScore.getUrl())){
|
|
|
+ throw new BizException("伴奏信息错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ AudioFileFormat sourceFile = AudioSystem.getAudioFileFormat(new URL(sysMusicScore.getUrl()));
|
|
|
+ double l_s = sourceFile.getFrameLength()/sourceFile.getFormat().getFrameRate();
|
|
|
+ System.out.printf("源长度:%.2f \n", l_s);
|
|
|
+ AudioFileFormat recordFile = AudioSystem.getAudioFileFormat(record.getInputStream());
|
|
|
+ double r_s = sourceFile.getFrameLength()/sourceFile.getFormat().getFrameRate();
|
|
|
+ System.out.printf("录音长度:%.2f \n", r_s);
|
|
|
+
|
|
|
+ //相似度
|
|
|
+ List<Double> pitchs_s = soundPressureLevelExtractor(null, sysMusicScore.getUrl());
|
|
|
+ List<Double> pitchs_r = soundPressureLevelExtractor(record.getBytes(), null);
|
|
|
+
|
|
|
+ int maxLength = pitchs_s.size();
|
|
|
+ if(pitchs_r.size()<maxLength){
|
|
|
+ maxLength = pitchs_r.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ Double pitchSize = Double.valueOf(0);
|
|
|
+ Double allPitchGap = Double.valueOf(0);
|
|
|
+ for(int i=0;i<maxLength;i++){
|
|
|
+ Double pitch1 = Math.abs(pitchs_s.get(i));
|
|
|
+ Double pitch2 = Math.abs(pitchs_r.get(i));
|
|
|
+ Double pitchGap = Math.abs(pitch1-pitch2);
|
|
|
+ if(pitchGap>pitch1){
|
|
|
+ pitchGap = pitch1;
|
|
|
+ }
|
|
|
+ allPitchGap+=pitchGap;
|
|
|
+ pitchSize+=pitch1;
|
|
|
+ }
|
|
|
+ Double intonation = 1-(allPitchGap/pitchSize);
|
|
|
+ System.out.printf("音准:%.2f \r\n", intonation);
|
|
|
+
|
|
|
+ //节奏
|
|
|
+ List<Double> times_s = beatExtractor(null, sysMusicScore.getUrl());
|
|
|
+ List<Double> times_r = beatExtractor(record.getBytes(), null);
|
|
|
+
|
|
|
+ float sameTimes = 0;
|
|
|
+ for (Double time1 : times_s) {
|
|
|
+ for (Double time2 : times_r) {
|
|
|
+ if(Math.abs(time2-time1)<0.1){
|
|
|
+ sameTimes++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Double cadence = Double.valueOf(sameTimes/times_s.size());
|
|
|
+ System.out.printf("节奏:%.2f", cadence);
|
|
|
+ } catch (UnsupportedAudioFileException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }finally {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|