vue3 ant ts 集成wavesurfer

7 篇文章 0 订阅

实现功能有:音词同步,倍速播放,拖拽播放,快进\退 ,重播,显示总 时长,关闭页面时关闭声音等功能



package.json 引入 "wavesurfer.js": "^7.7.14",

父页面引入自己封的 MyWaveSurfer.vue

 <!--    waveSrc 录音地址参数 -->
          <WaveSurfer :waveSrc="model['fsFilepath']">
          </WaveSurfer>

<script lang="ts" setup>

    import WaveSurfer from "@/views/recordCdr/MyWaveSurfer.vue";

MyWaveSurfer.vue

<template>
  <a-card>
    <Spin :tip="null" :spinning="loadLoading">
      <div :class="`wave-surfer`"  id="waveform" >
      </div>
      <!-- <div id="wave-timeline" ref="wave-timeline" style="height: 0px;"> -->
        <!--时间轴 -->
      </div>
    </Spin>
  </a-card>
  <a-card style="height: 62px">
    <div style="float: left;" v-if="playMs">
      <div style=";float: left">播放:&nbsp;&nbsp;</div>
      <Icon icon="ant-design:play-circle-outlined"  title="播放" class="playIcon" style="font-size: 32px;color: #2e9aff"   @click="playMusic()"> </Icon>
    </div>

    <div style="float: left;" v-if="!playMs">
      <div style="float: left">暂停:&nbsp;&nbsp;</div>
      <Icon icon="ant-design:pause-circle-outlined" title="暂停"    style="font-size: 30px;color: #2e9aff" @click="downMusic()"></Icon>
    </div>

    <div style="float: left;margin-left: 5%;"> 倍速:&nbsp;&nbsp;
      <a-select style="width: 90px;" :disabled="false" v-model:value="speedValue"  @change="speedClick">
        <a-select-option   value="1">
          1.0px
        </a-select-option>
        <a-select-option v-for="item in options" :key="item.value"  :value="item.value">
          {{ item.text }}
        </a-select-option>
      </a-select>
    </div>
    <div style="float: left;margin-left: 5%;">
      <Icon icon="ant-design:notification-filled" title="声音"     style="float: left;font-size: 29px;;color: #2e9aff" @click="downMusic()"></Icon>
      <a-slider style="float: left;width: 200px"  v-model:value="voiceValue"   @afterChange="setVolume"  />

    </div>

    <div style="float: left;margin-left: 5%;">
      <div style="float: left;margin-top: 2px">快退/快进 :&nbsp;&nbsp;</div>
      <Icon icon="ant-design:fast-backward-filled" title="快退"     style="float: left;font-size: 29px;;color: #2e9aff" @click="rew()"></Icon>

      <div style="float: left;margin-top: 2px"> &nbsp;&nbsp;--&nbsp;&nbsp;</div>
      <Icon icon="ant-design:fast-forward-filled" title="快进"     style="float: left;font-size: 29px;;color: #2e9aff" @click="speed()"></Icon>
    </div>
    <div style="float: left;margin-left: 5%;">
      <div style="float: left;margin-top: 2px">重播 :&nbsp;&nbsp;</div>
      <Icon icon="ant-design:reload-outlined" title="重播"     style="float: left;font-size: 25px;color: #2e9aff" @click="replay()"></Icon>
    </div>

    <div style="float: left;margin-left: 5%;">
      总时长:&nbsp;&nbsp;<span style="color:#2e9aff">{{totalTime}}</span> 秒
    </div>
  </a-card>
  <div style="width:100%">
    <a-card style="width: 49%;float: left;height: 500px;overflow-y: auto">

      <div id="speech_feature"      ref="myDiv">
        <div v-for="(line, index) in lyrics" class="myTextClass">
          <p v-if="line.user_type == 'agent'" :id="Math.round(line.begin_time/100)" class="blueColor"  style="text-align:left;">
            <Icon icon="ant-design:customer-service-filled" title="座席"     style="float: left;font-size: 22px;color: #707fe3"  ></Icon>
            :{{ line.res }}

          </p>
          <p v-else  style="text-align:right" :id="Math.round(line.begin_time/100)" class="blueColor">
            {{line.res}} :
            <Icon icon="ant-design:message-filled" title="客户"     style="float: right;font-size: 22px;color: #707fe3"  ></Icon>
          </p>
        </div>
      </div>
    </a-card>
    <a-card style="width: 49%;float: left;height: 500px">

      <a-tabs v-model:activeKey="activeKey">
        <a-tab-pane key="1" tab="Tab 1">Content of Tab Pane 1
        </a-tab-pane>
        <a-tab-pane key="2" tab="Tab 2" force-render>Content of Tab Pane 2
        </a-tab-pane>
        <a-tab-pane key="3" tab="Tab 3">

          Content of Tab Pane 3

        </a-tab-pane>
      </a-tabs>





    </a-card>
  </div>

</template>
<script lang="ts">
import { Spin } from 'ant-design-vue';
import {defineComponent, toRefs, ref, onMounted,onUnmounted, nextTick,} from 'vue';
import { getUploadFileAccessHttpUrl } from '/@/utils/common/compUtils';
import Icon from '/@/components/Icon';

import WaveSurfer from '@/ext_node_modules/wavesurfer.js/dist/wavesurfer'

/*import CursorPlugin from '@/ext_node_modules/wavesurfer.js/dist/plugin/wavesurfer.cursor.js'
import Timeline from '@/ext_node_modules/wavesurfer.js/dist/plugin/wavesurfer.timeline.js'
import Regions from '@/ext_node_modules/wavesurfer.js/dist/plugin/wavesurfer.regions.js'*/

import _default from "ant-design-vue/es/vc-slick/inner-slider";
import DictItemList from "@/views/system/dict/components/DictItemList.vue";



export default defineComponent({
  name: 'WaveSurfer',
  components: {DictItemList, Icon, Spin },
  props: {
    waveSrc: {
      type: String,
      // default: '../../../src/assets/images/test.mp3',
    },
    index: {
      type: Number,
    },

  },
  setup(props) {
    const myDiv = ref(null);
    const activeKey = ref('1');
    let totalTime = ref<number>(0);

    const voiceValue = ref<number>(30);
    const playMs = ref(true);
    const { waveSrc } = toRefs(props);
    const icon = ref('icon-bofang');
    let wavesurfer = [];
    const loadLoading = ref(false);
    const speedValue = ref(1);
    const options = ref([
      { value: 1.0, text: '1.0X' },
      { value: 1.25, text: '1.25X' },
      { value: 1.5, text: '1.5X' },
      { value: 1.75, text: '1.75X' },
      { value: 2.0, text: '2.0X' },
    ])
    const lyrics = ref([
      {
        "res": ["有什么可以帮您?"],
        "end_time": 7700,
        "begin_time": 5900,
        "words_info": [],
        "sn": "96541708621716291667",
        "corpus_no": "7371416581381329804",
        "user_type": "agent"
      }, {
        "res": ["我客户想把那个会员号变更一下可以吗?"],
        "end_time": 17700,
        "begin_time": 8340,
        "words_info": [],
        "sn": "92604005381716291667",
        "corpus_no": "7371416580911366314",
        "user_type": "cust"
      }, {
        "res": ["嗯,可以的,还是需要给顾客自己近先联系我们。"],
        "end_time": 18860,
        "begin_time": 17000,
        "words_info": [],
        "sn": "92604005381716291667",
        "corpus_no": "7371416580911366314",
        "user_type": "agent"
      }, {
        "res": ["如果变更成别的号码,这个等级还在吗?"],
        "end_time": 19860,
        "begin_time":18860 ,
        "words_info": [],
        "sn": "381723455751716291667",
        "corpus_no": "7371416581432964228",
        "user_type": "cust"
      }, {
        "res": ["您您放心,会保留他的一个等级和这个积分的。"],
        "end_time": 28020,
        "begin_time": 26060,
        "words_info": [],
        "sn": "381723455751716291667",
        "corpus_no": "7371416581432964228",
        "user_type": "agent"
      }, {
        "res": ["好累,好让我让他打电话过来哈,"],
        "end_time": 42280,
        "begin_time": 33880,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["请问下,可以帮您的吗?"],
        "end_time": 42280,
        "begin_time": 36880,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["啊没有了,谢谢啊,我让他打过来,"],
        "end_time": 40280,
        "begin_time": 39880,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 40000,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["好的"],
        "end_time": 40280,
        "begin_time": 40080,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 40500,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["好的"],
        "end_time": 40280,
        "begin_time": 40800,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 41000,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["好的"],
        "end_time": 40280,
        "begin_time": 41200,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 41400,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["。。。。,"],
        "end_time": 40280,
        "begin_time": 41600,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 41800,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }, {
        "res": ["。。。。。"],
        "end_time": 40280,
        "begin_time": 41900,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "cust"
      }, {
        "res": ["祝您生活愉快,再见,"],
        "end_time": 40280,
        "begin_time": 42200,
        "words_info": [],
        "sn": "912755075221716291667",
        "corpus_no": "7371416581565561738",
        "user_type": "agent"
      }
    ])

    //点击倍速播放
    function speedClick() {
      console.info(speedValue.value)
      // console.log("fieldValue===============",fieldValue);
      playMusic();
    }



    //生成wavesurfer
    function render( selector, url) {
      loadLoading.value = true;
      var domEl = document.createElement('div');
      document.querySelector(selector).appendChild(domEl);
      wavesurfer = WaveSurfer.create({
        container: domEl,
        waveColor: 'rgb(57 171 132)',
        progressColor: '#e0820e',
        cursorColor: 'rgb(57 171 132)',
        abnormalvolume:{"1":"violet"},
       /* plugins: [
          CursorPlugin.create({
            showTime: true,
            opacity: 0.2,
            customShowTimeStyle: {
              'background-color': '#000',
              color: '#fff',
              padding: '2px',
              'font-size': '20px'
            }
          }),
          Timeline.create({
            container: '#wave-timeline'
          }),
          Regions.create()
        ]*/
      });
      // url="../../../src/assets/images/test.mp3";
      wavesurfer.load(url);
      //获取当前播放进度的时间点
      wavesurfer.on('audioprocess', (time) => {
        //console.log("======time======",time)
        // 根据当前音频播放进度 time 来同步显示歌词Math.round(line.begin_time/100)
        const currentLine =Math.round(time*10) ; // 假设每行歌词对应的时间间隔为 10 秒
        //console.log("======currentLine======",currentLine)
        var mytextId=document.getElementById(currentLine);
        changeBackground(time);
        if(mytextId!=undefined){
          mytextId.className= "redColor";
          setTimeout(function(){
            mytextId.scrollIntoView({ behavior: "smooth", block: 'center' }); //0.1秒滑动到指定位置*
          },100);
        }
      });





      //拖拽进度条播放
      wavesurfer.on('seek', function () {
        playMusic();
      });

      wavesurfer.on('error', function (e) {
        console.warn(e);
      });

      //加载后事件
      wavesurfer.on('ready', function () {
        loadLoading.value = false;
        totalTime.value = wavesurfer.getDuration();
        console.log('音频总时长:', totalTime, '秒');
      });

      return wavesurfer;
    }
    //组件加载时加载录音
    onMounted(() => {
      setTimeout(()=>{
        if(waveSrc.value!=undefined){
          const fileAccessHttpUrl= getUploadFileAccessHttpUrl(waveSrc.value)
          nextTick(() => {
            render( '.wave-surfer' , fileAccessHttpUrl);
          });
        }
      },100)
    });

    //页面关闭时,组件关闭, 关闭录音
    onUnmounted(()=>{
      console.info( '组件关闭时 关闭录音====')
      wavesurfer.stop() ; //停止
      wavesurfer.destroy();//销毁
    })




    //设置音量大小
    function setVolume(val) {
      wavesurfer.setVolume(val / 100);
      //console.log(val)
    }

    //根据播放进度改编已播放的字体样式
    function changeBackground(nowTime) {
      nowTime=Math.round(nowTime*10)
      const elements = document.querySelectorAll('.myTextClass > *');
      elements.forEach(element => {
        if(element.id>nowTime){
          element.className="blueColor";
        }else{
          element.className="redColor";
        }
      });
    }

    //暂停
    function downMusic() {
      playMs.value = true;
      icon.value = 'icon-bofang';
      wavesurfer.playPause();
    }

    //播放
    function playMusic() {
      playMs.value = false;
      icon.value = 'icon-bofang';
      wavesurfer.setPlaybackRate(speedValue.value)
      wavesurfer.play();
    }
    // 回退
    function  rew() {
      wavesurfer.skip(-3);
      goPlay();
    }
    // 快进
    function  speed() {
      wavesurfer.skip(3);
      goPlay();
    }

    function goPlay() {
      let start = wavesurfer.getCurrentTime();
      wavesurfer.play(start);
    }

    // 重载
    function replay() {
      wavesurfer.stop();
      wavesurfer.clearRegions();
      wavesurfer.play(0);
    }

    return {  playMusic, icon, loadLoading,playMs,speedClick,speedValue,options,downMusic,totalTime,
      setVolume,voiceValue,lyrics,changeBackground,myDiv,speed,rew ,replay,activeKey};
  },
});
</script>
<style lang="less">
.playIcon {
  line-height: 36px;
  margin-right: 8px;
  cursor: pointer;
}
.waveform {

}
.redColor {
  color: #d96f00;;
}
.blueColor {
  color: #292a2f;
}


</style>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值