首页 前端知识 vue绘制语音波形图---wavesurfer.js

vue绘制语音波形图---wavesurfer.js

2024-03-31 09:03:04 前端知识 前端哥 988 230 我要收藏

文章目录

  • 创建实例
  • options
  • method
  • 接收Blob流
  • 播放实时声音

https://wavesurfer.xyz/

创建实例

  1. 引入插件:import WaveSurfer from "wavesurfer.js"
  2. 创建实例对象:`this.wavesurfer = WaveSurfer.create(options);
<div id="waveform">
  <!-- the waveform will be rendered here -->
</div>

<script type="module">
import WaveSurfer from 'https://unpkg.com/wavesurfer.js@7/dist/wavesurfer.esm.js'

const wavesurfer = WaveSurfer.create({
  container: '#waveform',
  waveColor: '#4F4A85',
  progressColor: '#383351',
  url: '/audio.mp3',
})

wavesurfer.on('interaction', () => {
  wavesurfer.play()
})

options

WaveSurferOptions: {
    audioRate?: number; // 音频的播放速度,数值越小越慢
    autoCenter?: boolean; // 如果有滚动条,将波形根据进度居中
    autoScroll?: boolean;
    autoplay?: boolean;
    backend?: "WebAudio" | "MediaElement";
    barAlign?: "top" | "bottom";
    barGap?: number; // 波纹柱状图之间的间距
    barHeight?: number; // 波纹柱状图的高度,当大于1的时候,将增加设置的高度
    barRadius?: number; // 波形条的radius
    barWidth?: number; // 如果设置,波纹的样式将变成类似柱状图的形状
    container: HTMLElement | string; // 必填参数,指定渲染的dom的id名、类名或者dom本身
    cursorColor?: string; // 鼠标点击的颜色
    cursorWidth?: number; // 鼠标点击显示的宽度
    dragToSeek?: boolean;
    duration?: number;
    fetchParams?: RequestInit;
    fillParent?: boolean;
    height?: number | "auto"; // 音频的显示高度
    hideScrollbar?: boolean; // 是否隐藏水平滚动条
    interact?: boolean; // 初始化时是否启用鼠标交互。之后可以随时切换该参数
    media?: HTMLMediaElement;
    mediaControls?: boolean; // (与 MediaElement一起使用) 为true则将启动媒体元素的本机控件
    minPxPerSec?: number; // 音频每秒最小像素数
    normalize?: boolean; // 如果为true,则以最大峰值而非1.0进行归一化
    peaks?: (Float32Array | number[])[];
    plugins?: GenericPlugin[]; // 使用的插件
    progressColor?: string | string[] | CanvasGradient; // 光标后面的波形部分的填充颜色
    renderFunction?: ((peaks, ctx) => void);
    sampleRate?: number;
    splitChannels?: Partial<WaveSurferOptions>[]; // 对于不同通道的音频使用分开的波形渲染
    url?: string;
    waveColor?: string | string[] | CanvasGradient; // 光标后面的波形的填充颜色
    width?: number | string;
}

method

方法说明
destroy(): void销毁waveform,移除事件,元素和关联节点
empty(): void清空waveform
exportImage(format: string, quality: number, type: "dataURL"): Promise<string[]>
exportImage(format: string, quality: number, type: "blob"): Promise<Blob[]>
exportPeaks(__namedParameters?): number[][]
getActivePlugins(): GenericPlugin[]返回当前已初始化插件的map
getCurrentTime(): number获取当前播放的进度,单位:秒
getDecodedData(): null | AudioBuffer
getDuration(): number获取音频的总时长,单位:秒
getMediaElement(): HTMLMediaElement
getMuted(): boolean返回当前的静音状态.
getScroll(): number
getPlaybackRate(): number返回音频片段的播放速度
getVolume(): number 获取音量
getWrapper(): HTMLElement
isPlaying(): boolean 判断音频是否在播放
isSeeking(): boolean
load(url: string, channelData?: (Float32Array | number[])[] , duration?: number): Promise<void> 加载音频
loadBlob(blob: Blob, channelData?: (Float32Array | number[])[], duration?: number): Promise<void> 从Bolb或者file对象中调用音频
on<EventName>(event: EventName, listener: EventListener<WaveSurferEvents, EventName>, options?): (() => void)
once<EventName>(event, listener): (() => void)
pause(): void停止播放
play(): Promise<void> 从当前位置开始播放音频文件。结合使用start和end可以指定一个音频播放范围
playPause(): Promise<void> 如果当前为状态状态开始播放,反之暂停播放
registerPlugin<T>(plugin): T
seekTo(progress): void
setMediaElement(element: HTMLMediaElement): void
setMuted(muted: boolean): void
setOptions(options: Partial): void
setPlaybackRate(rate: number, preservePitch?: boolean): void
setSinkId(sinkId: string): Promise
setTime(time: number): void
setVolume(volume: number): void设置音量[0-1]
skip(seconds: number): void调到offset的位置
stop(): void停止播放并回到音频文件的起始处
toggleInteraction(isInteractive: boolean): void
un<EventName>(event, listener): void解绑事件
unAll(): void
zoom(minPxPerSec: number): void水平放大或缩小波形,参数是每秒音频的水平像素

接收Blob流

通过XMLHttpRequest对象向后端发出请求,获取音频数据的blob流。然后通过WaveSurfer.create()方法创建一个wavesurfer实例,传入一个容器元素和一些配置选项。最后调用loadBlob()方法将blob数据传递给wavesurfer进行处理。这样就可以在前端使用wavesurfer.js处理后端传递过来的音频数据了。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/audio', true);
xhr.responseType = 'blob';
xhr.onload = function() {
  if (this.status == 200) {
    var blob = this.response;
    // 使用wavesurfer.js处理音频数据
    var wavesurfer = WaveSurfer.create({
      container: '#waveform',
      backend: 'MediaElement',
      mediaType: 'audio',
      waveColor: 'blue',
      progressColor: 'purple',
      cursorColor: 'navy'
    });
    wavesurfer.loadBlob(blob);
  }
};
xhr.send();

this.socket = new WebSocket('ws://localhost:8080')
this.socket.binaryType = 'arraybuffer'
this.socket.onmessage = (event) => {
  let blob = new Blob([event.data], { type: 'audio/wav' })
  let objectURL = URL.createObjectURL(blob)
  this.wavesurfer.load(objectURL)
  this.wavesurfer.on('finish', () => {
    URL.revokeObjectURL(objectURL)
  })
}

播放实时声音

  1. 后端通过websocket推流
    后端需要将数据转换成PCM格式
  2. 前端实现
    需要安装两个库:pcm-player (播放声音),recorder-core (绘制波形)
npm install pcm-player
npm install recorder-core

封装websocket

var ws = null
let lockReconnect = false;
/**
 * @param {*} path ws url
 * @param {*} callback 数据处理回调
 * @param {*} isHandleData 是否处理转换数据
 * @returns ws
 */
const websocket = (path, callback, isHandleData = false) => {
    cancel();//取消上一次连接
    ws = new WebSocket(path);
    ws.binaryType = "arraybuffer"
    // 建立连接
    ws.onopen = (event) => {
        console.log('websocket 建立连接');
        // 连接关闭
        ws.onclose = (event) => {
            console.log('websocket 连接断开,重新连接');
            reconnect(path, callback);
        };
        // 接收到消息
        ws.onmessage = (event) => {
            const data = handleData(event, isHandleData);
            if (callback) callback(data)
        };
    };
    ws.onerror = (event) => {
        console.log('websocket 连接失败,重新连接');
        reconnect(path, callback);
    };
    return ws;
}
// 重新连接
const reconnect = (path, callback) => {
    if (lockReconnect) {
        return;
    }
    lockReconnect = true;
    setTimeout(function () {
        console.log("重新链接…")
        lockReconnect = false;
        websocket(path, callback)
    }, 2000);
}

// 处理返回数据
const handleData = (event, isHandleData) => {
    const data = isHandleData ? JSON.parse(event.data) : event
    return data;
}
// 取消连接 清除ws实例
const cancel = () => {
    if (ws) {
        ws.onclose = () => { }
        ws?.close()
    }
    ws = null
}

export default websocket

使用:

<template>
  <div id="wave_audio"></div>
</template>

<script>
import Recorder from 'recorder-core'
import PCMPlayer from 'pcm-player'
//必须引入的RecordApp核心文件(文件路径是 /src/app-support/app.js)
// import RecordApp from 'recorder-core/src/app-support/app'
//需要使用到的音频格式编码引擎的js文件统统加载进来
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine'
//以上三个也可以合并使用压缩好的recorder.xxx.min.js
//比如 import Recorder from 'recorder-core/recorder.mp3.min' //已包含recorder-core和mp3格式支持
//可选的扩展支持项
import 'recorder-core/src/extensions/wavesurfer.view'
import websocket from './websocket'
var player = null
var wave = null
export default {
  mounted() {
    this.initPlay()
    this.initWave()
    this.initWebsocket(
      'ws://192.168.8.210:8877/live?url=rtmp://139.224.194.14:10085/hls/wrpYcD27g?sign=wrtY5D27gz&&&ffmpeg=true'
    )
  },
  methods: {
    initPlay() {
      player = new PCMPlayer({
        encoding: '16bitInt', //编码 可能的值 8bitInt / 16bitInt / 32bitInt / 32bitFloat 默认值:16bitInt
        channels: 1, // PCM 数据中的通道数
        sampleRate: 32000, // PCM 数据的采样率
        flushTime: 2000, //  以毫秒为单位播放的 PCM 数据的刷新间隔。默认 1000ms
      })
    },
    initWave() {
      const waveOption = {
        elem: '#wave_audio',
        scale: 2, //缩放系数,应为正整数,使用2(3? no!)倍宽高进行绘制,避免移动端绘制模糊
        fps: 50, //绘制帧率,不可过高,50-60fps运动性质动画明显会流畅舒适,实际显示帧率达不到这个值也并无太大影响
        duration: 3500, //当前视图窗口内最大绘制的波形的持续时间,此处决定了移动速率
        direction: 1, //波形前进方向,取值:1由左往右,-1由右往左
        position: 0, //绘制位置,取值-1到1,-1为最底下,0为中间,1为最顶上,小数为百分比
        centerHeight: 1, //中线基础粗细,如果为0不绘制中线,position=±1时应当设为0
        //波形颜色配置:[位置,css颜色,...] 位置: 取值0.0-1.0之间
        linear: [0, 'rgba(14, 224, 238, 1)', 1, 'rgba(14, 224, 238, .6)'],
        centerColor: 'rgba(14, 224, 238, 1)', //中线css颜色,留空取波形第一个渐变颜色
      }
      wave = Recorder.WaveSurferView(waveOption)
    },
    initWebsocket(url) {
      websocket(url, this.handle)
    },
    handle(event) {
      const dataAudio = new Uint8Array(event.data)
      player && player.feed(dataAudio) // 播放声音
      const data = new Uint16Array(event.data)
      wave && wave.input(data, 20, 32000) // 添加波形数据
    },
    destroyPlay() {
      player && player.destroy()
      player = null
    },
  },
  beforeDestroy() {
    this.destroyPlay()
  },
}
</script>

<style lang="less" scoped>
#wave_audio {
  width: 100%;
  height: 100%;
}
</style>

转载请注明出处或者链接地址:https://www.qianduange.cn//article/4287.html
标签
wavesurfer.js
评论
发布的文章
大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!