文章目录
- 创建实例
- options
- method
- 接收Blob流
- 播放实时声音
https://wavesurfer.xyz/
创建实例
- 引入插件:
import WaveSurfer from "wavesurfer.js"
- 创建实例对象:`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)
})
}
播放实时声音
- 后端通过websocket推流
后端需要将数据转换成PCM格式 - 前端实现
需要安装两个库: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>