【项目推荐——音频标注 Wavesurfer 用法及相关问题解决】

一、前言

上期推荐了文本标注poplar-annotation用法,这期针对音视频标注推荐wavesurfer.js库;
Wavesurfer.js 是一个基于Web Audio API 和HTML5 Canvas的开源音频可视化库,用于创建可交互、可定制的波形。同时拥有众多插件库。

二、demo效果

在这里插入图片描述

可以实现音视频播放暂停、指定区域播放循环暂停、创建标注区域、标注区域文字编辑、标注区域拖拉拽、时间交互效果等;

三、官网

Wavesurfer.js 官网示例

1.WaveSurfer基础属性

import WaveSurfer from 'wavesurfer.js'

const wavesurfer = WaveSurfer.create({
  container: document.body,//容器
  waveColor: 'rgb(200, 0, 200)',//设置波形的颜色
  progressColor: 'rgb(100, 0, 100)',//设置进度条的颜色。
  url: '/examples/audio/demo.wav',//音频文件的 URL
})

//点击事件
wavesurfer.on('click', () => {
  wavesurfer.play()
})

2.相关配置option

{
  "container": "body",
  "height": 128,
  "width": 300,
  "splitChannels": false,
  "normalize": false,
  "waveColor": "#ff4e00",
  "progressColor": "#dd5e98",
  "cursorColor": "#ddd5e9",
  "cursorWidth": 2,
  "barWidth": null,
  "barGap": null,
  "barRadius": null,
  "barHeight": null,
  "barAlign": "",
  "minPxPerSec": 1,
  "fillParent": true,
  "url": "/wavesurfer-code/examples/audio/audio.wav",
  "mediaControls": true,
  "autoplay": false,
  "interact": true,
  "dragToSeek": false,
  "hideScrollbar": false,
  "audioRate": 1,
  "autoScroll": true,
  "autoCenter": true,
  "sampleRate": 8000
}
属性名默认值类型描述
containerbodyString用于指定播放器容器的 CSS 选择器或元素。body 表示播放器将占据整个文档的主体部分。
height128Number播放器的高度。
width300Number播放器的宽度。
splitChannelsfalseBoolean是否使用分裂声道模式。如果设置为 true,则播放器将显示双声道音频的两个声道,每个声道有自己的音量控制。
normalizefalseBoolean是否使用归一化模式。如果设置为 true,则播放器将尝试平衡音频的音量,使所有音频片段听起来同样响亮。
waveColor#ff4e00String波形的颜色,使用十六进制颜色代码。
progressColor#dd5e98String进度条颜色,使用十六进制颜色代码。
cursorColor#ddd5e9String光标颜色,使用十六进制颜色代码。
cursorWidth2Number光标的宽度。
barWidthnullNumber, String音量条的宽度。如果设置为 null,则音量条将自动调整大小以匹配播放器的宽度。
barGapnullNumber音量条之间的间隙。如果设置为 null,则间隙将自动计算。
barRadiusnullNumber音量条的圆角半径。如果设置为 null,则音量条将是矩形的。
barHeightnullNumber音量条的高度。如果设置为 null,则音量条的高度将根据播放器的高度和 barGap 参数自动计算。
barAlign""String音量条的对齐方式。可以是 "center""left""right"。默认为 “”,表示不对齐。
minPxPerSec1Number音频可视化波形 1 秒对应的最小像素数。
fillParenttrueBoolean是否填满其父容器。如果设置为 true,则播放器将填满其父容器的全部空间。
url/wavesurfer-code/examples/audio/audio.wavString音频文件的 URL。
mediaControlstrueBoolean是否显示媒体控制。如果设置为 true,则播放器将显示播放/暂停、音量控制等按钮。
autoplayfalseBoolean是否自动播放。如果设置为 true,则音频文件在加载后将自动播放。
interacttrueBoolean是否允许交互。如果设置为 true,则用户可以通过点击或者拖动波形来控制播放进度。
dragToSeekfalseBoolean是否允许通过拖拽波形来定位播放进度。如果设置为 true,则用户可以通过拖拽波形来定位播放进度。
hideScrollbarfalseBoolean是否隐藏滚动条。如果设置为 true,则播放器的滚动条将被隐藏。
audioRate1Number音频播放的速率。1 表示正常速率,0.5 表示一半的速率,2 表示两倍的速率。
autoScrolltrueBoolean是否自动滚动。如果设置为 true,则波形图将自动滚动以跟随播放进度。
autoCentertrueBoolean是否自动居中。如果设置为 true,则当前播放位置将始终保持在视图的中心。
sampleRate8000Number音频的采样率,用于音频的播放和绘制。

3.事件

在WaveSurfer.js中,有多种事件可以监听,以便在特定动作发生时执行相应的操作。以下是一些常见的WaveSurfer事件:

  • ready:当WaveSurfer准备就绪时触发。
  • audioprocess:在音频处理过程中不断触发。
  • finish:当音频播放完成时触发。
  • interaction:当用户与波形图进行交互时触发。
  • seek:当用户在波形图上拖动进度条时触发。
  • zoom:当用户放大或缩小波形图时触发。
  • mousedown:当鼠标在波形图上按下时触发。
  • mouseup:当鼠标在波形图上释放时触发。
  • mouseenter:当鼠标进入波形图区域时触发。
  • mouseleave:当鼠标离开波形图区域时触发。
  • scroll:当用户在波形图上滚动时触发。
  • timeupdate:在音频播放过程中,当时间更新时触发。
  • play:当音频开始播放时触发。
  • pause:当音频暂停时触发。
  • volumechange:当音频音量改变时触发。
  • click : 当用户点击波形图时触发。
  • doubleclick : 当用户双击波形图时触发。

你可以通过调用wavesurfer.on(event, handler)方法来监听这些事件,并在事件触发时执行相应的处理函数。

import WaveSurfer from 'wavesurfer.js'

const wavesurfer = WaveSurfer.create({
  container: document.body,
  waveColor: 'rgb(200, 0, 200)',
  progressColor: 'rgb(100, 0, 100)',
})

/** 在音频加载过程中,load事件在音频开始加载时触发 */
wavesurfer.on('load', (url) => {
  console.log('Load', url)
})

/** loading事件则会在加载过程中持续触发,显示加载的百分比。这使用户能够跟踪音频文件的加载进度 */
wavesurfer.on('loading', (percent) => {
  console.log('Loading', percent + '%')
   //Loading 31% Loading 70% Loading 100%
})

/** decode事件在音频解码完成后触发,显示解码后的持续时间。 */
wavesurfer.on('decode', (duration) => {
  console.log('Decode', duration + 's')
    //Decode 26.386688s
})

/** ready事件表示音频已经解码并且准备好播放了,同样显示其时长。 */
wavesurfer.on('ready', (duration) => {
  console.log('Ready', duration + 's')
    //Ready 26.386688s
})

/**重绘完成 */
wavesurfer.on('redrawcomplete', () => {
  console.log('Redraw began')
})

/**重绘完成 */
wavesurfer.on('redrawcomplete', () => {
  console.log('Redraw complete')
})

/** 当音频播放时 */
wavesurfer.on('play', () => {
  console.log('Play')
})

/** 当音频停止时 */
wavesurfer.on('pause', () => {
  console.log('Pause')
})

/**当音频播放结束时*/
wavesurfer.on('finish', () => {
  console.log('Finish')
})

/**timeupdate事件在音频播放过程中持续触发,显示当前播放时间,这对于显示播放进度条的当前位置非常有用。 */
wavesurfer.on('timeupdate', (currentTime) => {
  console.log('Time', currentTime + 's')
})

/**当用户停止拖动进度条,`seeking`事件会显示用户跳转到的时间点。这个事件与用户寻求不同的播放位置相关。*/
wavesurfer.on('seeking', (currentTime) => {
  console.log('Seeking', currentTime + 's')
})

/** 用户与波形图的交互会触发`interaction`事件,这个事件传递了用户交互后的新时间点。 */
wavesurfer.on('interaction', (newTime) => {
  console.log('Interaction', newTime + 's')
})

/** 当用户点击波形图时触发,提供了用户与音频可视化直接交互的反馈。 */
wavesurfer.on('click', (relativeX) => {
  console.log('Click', relativeX)
})

/** 当用户拖动波形图时,提供了用户与音频可视化直接交互的反馈。 */
wavesurfer.on('drag', (relativeX) => {
  console.log('Drag', relativeX)
})

/** `scroll`事件在用户滚动(平移)波形图时触发,显示可视区域的开始和结束时间。 */
wavesurfer.on('scroll', (visibleStartTime, visibleEndTime) => {
  console.log('Scroll', visibleStartTime + 's', visibleEndTime + 's')
})

/** `zoom`事件在缩放级别改变时触发,显示当前的缩放级别(以像素/秒为单位)。 */
wavesurfer.on('zoom', (minPxPerSec) => {
  console.log('Zoom', minPxPerSec + 'px/s')
})

/** `destroy`事件在波形图被销毁之前触发,允许开发者执行任何清理操作。*/
wavesurfer.on('destroy', () => {
  console.log('Destroy')
})

/**加载音频文件*/
wavesurfer.load('/examples/audio/audio.wav')

// 通过`input`类型的范围滑块实现了对音频缩放的控制。在`once('decode')`回调函数中,它等待音频解码完成,然后获取滑块元素。用户移动滑块时,`input`事件触发,更新`WaveSurfer`实例的缩放级别,从而改变波形的显示分辨率。此外,页面上的一个按钮被添加了点击事件监听器,用于播放和暂停音频。
wavesurfer.once('decode', () => {
  const slider = document.querySelector('input[type="range"]')

  slider.addEventListener('input', (e) => {
    const minPxPerSec = e.target.valueAsNumber
    wavesurfer.zoom(minPxPerSec)
  })

  document.querySelector('button').addEventListener('click', () => {
    wavesurfer.playPause()
  })
})

在这里插入图片描述
在这里插入图片描述

4. Plugins插件

4.1 区域插件

import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js'

在这里插入图片描述

官网regions示例

4.2 时间轴插件

import TimelinePlugin from 'wavesurfer.js/dist/plugins/timeline.esm.js'

在这里插入图片描述

官网timeline示例

4.3 时间交互插件

import Hover from 'wavesurfer.js/dist/plugins/hover.esm.js'

在这里插入图片描述

官网hover示例

4.4 其他插件

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、使用

1.下载

  • npm install wavesurfer.js

  • 或者引入(不推荐)外部地址不安全

<script src="https://unpkg.com/wavesurfer.js"></script>

2.WaveSurfer 挂载、创建、销毁

useWavesurfer的Hook封装

import React, { useState, useEffect } from "react";
import WaveSurfer from "wavesurfer.js";

const useWavesurfer = (containerRef, options) => {

    const [wavesurfer, setWavesurfer] = useState(null)
    
    useEffect(() => {
      if (!containerRef.current) return
      const ws = WaveSurfer.create({
        ...options,//WaveSurfer的配置项
        container: containerRef.current,//WaveSurfer需要挂载的容器也是dom节点
      })
      setWavesurfer(ws)
      
      //销毁
      return () => {
        ws.destroy()
      }
      
    }, [options, containerRef])
    
    return wavesurfer
  }
  export default useWavesurfer

3.WaveSurferPlayer组件

import React, { useState, useRef, useEffect, useCallback } from "react";
import useWavesurfer from './useWavesurfer.js'

const WaveSurferPlayer = ({plugins}) => {
    const regions=plugins[0]
    const containerRef = useRef()
    const [isPlaying, setIsPlaying] = useState(false)
    const [currentTime, setCurrentTime] = useState(0)
    const wavesurfer = useWavesurfer(containerRef, props)
    const onPlayClick = useCallback(() => {
      wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play()
    }, [wavesurfer])
    
    useEffect(() => {
      if (!wavesurfer) return
      setCurrentTime(0)
      setIsPlaying(false)
      const subscriptions = [
      //wavesurfer相关事件
        wavesurfer.on('play', () => setIsPlaying(true)),
        wavesurfer.on('pause', () => setIsPlaying(false)),
        wavesurfer.on('timeupdate', (currentTime) => setCurrentTime(currentTime)),
        wavesurfer.on('ready',()=>{
          regions.enableDragSelection({
            color: 'rgba(255, 0, 0, 0.1)',
            resize:true,//是否可以缩放拉伸
            drag:true,//是否可以拖拽
          })
          regions.on('region-created', (region) => {
            console.log('region-created', region)
          })
          regions.on('region-clicked', (region, e) => {
            e.stopPropagation() 
            if(!region)return
            region.remove()
            console.log('region-remove', region)
          })
        }),
        wavesurfer.on('finish', () => {
          wavesurfer.setTime(0)
        })
      ]
      return () => {
        //在 useEffect 的清理阶段(即返回的函数),遍历 subscriptions 数组,其中包含了所有的事件监听器。对每一个监听器调用 unsub(),移除事件监听器,确保内存得到释放。
        subscriptions.forEach((unsub) => unsub())
      }

    }, [wavesurfer])
    return (
      <>
        <div ref={containerRef} style={{ minHeight: '120px' }} />
        <button onClick={onPlayClick} style={{ marginTop: '50px',marginLeft:'600px' }}>
          {isPlaying ? 'Pause' : 'Play'}
        </button>
        <p>Seconds played: {currentTime}</p>
      </>
    )
  }
  export default WaveSurferPlayer

每个区域的创建,对应着一个’region-created’,包含id,起始时间,结束时间等;

在这里插入图片描述

4.使用组件

import WaveSurferPlayer from './WaveSurferPlayer.js'
import React, { useState } from "react";
/**regions区域交互插件 */
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js'
/**Timeline时间轴插件 */
import Timeline from 'wavesurfer.js/dist/plugins/timeline.esm.js'
/**Hover时间交互插件 */
import Hover from 'wavesurfer.js/dist/plugins/hover.esm.js'
function App() {
  const [audioUrl, setAudioUrl] = useState(require('./music01.mp3'))//require绝对路径
  return <WaveSurferPlayer
    height={200}
    waveColor='#A8DBA8'
    progressColor='#3B8686'
    url={audioUrl}
    backend="MediaElement"
    plugins={
        [RegionsPlugin.create(), 
        Timeline.create({}),
        Hover.create({
          lineColor: '#000',
          lineWidth: 2,
          labelBackground: '#555',
          labelColor: '#fff',
          labelSize: '11px',
        }),
      ]}
  />
}
export default App

五、相关问题及解决

1.WaveSurfer.create()重复创建、重复挂载的问题

记得要使用WaveSurfer的destroy属性销毁

     //销毁
      return () => {
        ws.destroy()
      }
      

2.资源路径url地址引入的问题

记得引入绝对路径

require('./music01.mp3')

3.插件引入的地址

一定要看官网的地址,很多博主引入的地址都是老版本的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值