用 Wavesurfer.js 和 React 快速打造你的音频录播功能❤️

背景

最近正在做一个开源的项目--pear-rec,pear-rec 是一个跨平台的截图、录屏、录音、录像软件。截图功能上篇文章已经讲过了,如果没有看过的可以去看看这篇文章------------手把手教你,用electron实现截图软件,并且项目的框架搭建也可以参考上一篇文章,开发到现在我想要一个音乐(录音)预览、播放功能,说干就干,下面我介绍一下我的开发过程。

介绍

实现音频的录音和播放已经是许多应用程序的基础功能。这可能包括社交媒体平台,音乐创作软件,甚至是一些教育应用。在本教程中,我们将引导你如何使用 Wavesurfer.jsReact 实现一个基本的音频录音与播放功能。

Wavesurfer.js 是一个开源的可视化音频库,它能够创建可交互的音频波形。这个库能够选择、播放或者暂停音频,同时也能进行音频的前后快翻转,同时也支持很多不同的音频格式。

官网 : wavesurfer.xyz/

github: github.com/katspaugh/w...

实现

一: 安装依赖创建项目

在开始之前,我们需要安装一些依赖项。请确保您已经安装了以下软件:

  • Node.js
  • pnpm
  • react

在项目的根目录下打开终端,并执行以下命令来初始化一个 React 应用。在这里我就不详细讲解了,不会的可以去看我的文章。

然后安装 wavesurfer.js 和录音的库:

js 复制代码
npm install --save wavesurfer.js
import WaveSurfer from 'wavesurfer.js'

二: 创建 Wavesurfer 组件

先看我们要实现效果:

    1. 插件
js 复制代码
import React, { useState, useRef, useEffect, useCallback } from "react";
import { Button, Space, Divider } from "antd";
import WaveSurfer from "wavesurfer.js";
    1. 编写 WaveSurfer hook
js 复制代码
const useWavesurfer = (containerRef, options) => {
  const [wavesurfer, setWavesurfer] = useState(null)

  // Initialize wavesurfer when the container mounts
  // or any of the props change
  useEffect(() => {
    if (!containerRef.current) return

    const ws = WaveSurfer.create({
      ...options,
      container: containerRef.current,
    })

    setWavesurfer(ws)

    return () => {
      ws.destroy()
    }
  }, [options, containerRef])

  return wavesurfer
}
    1. 编写 WaveSurferPlayer组件
js 复制代码
// WaveSurfer hook
const WaveSurferPlayer = (props) => {
  const containerRef = useRef()
  const [isPlaying, setIsPlaying] = useState(false)
  const [currentTime, setCurrentTime] = useState(0)
  const wavesurfer = useWavesurfer(containerRef, props)

  // On play button click
  const onPlayClick = useCallback(() => {
    wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play()
  }, [wavesurfer])

  // Initialize wavesurfer when the container mounts
  // or any of the props change
  useEffect(() => {
    if (!wavesurfer) return

    setCurrentTime(0)
    setIsPlaying(false)

    const subscriptions = [
      wavesurfer.on('play', () => setIsPlaying(true)),
      wavesurfer.on('pause', () => setIsPlaying(false)),
      wavesurfer.on('timeupdate', (currentTime) => setCurrentTime(currentTime)),
    ]

    return () => {
      subscriptions.forEach((unsub) => unsub())
    }
  }, [wavesurfer])

  return (
    <>
      <div ref={containerRef} style={{ minHeight: '120px' }} />

      <button onClick={onPlayClick} style={{ marginTop: '1em' }}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>

      <p>Seconds played: {currentTime}</p>
    </>
  )
}
    1. 使用
js 复制代码
const App = () => {
  const urls = ['/examples/audio/audio.wav', '/examples/audio/stereo.mp3']
  const [audioUrl, setAudioUrl] = useState(urls[0])

  // Swap the audio URL
  const onUrlChange = useCallback(() => {
    urls.reverse()
    setAudioUrl(urls[0])
  }, [])

  // Render the wavesurfer component
  // and a button to load a different audio file
  return (
    <>
      <WaveSurferPlayer
        height={100}
        waveColor="rgb(200, 0, 200)"
        progressColor="rgb(100, 0, 100)"
        url={audioUrl}
        plugins={[Timeline.create()]}
      />

      <button onClick={onUrlChange}>Change audio</button>
    </>
  )
}

效果如下:

这样的效果还是有点简单,我实现的最终效果如下:

可以自由播放、暂停、显示当前播放位置和选取播放位置、快进、后退。。。

三、实现录音

我们要实现的录音效果大概是这样: 因为wavesurfer.js有带有录音插件,所以我们可以直接用,话不多说我们实现吧!

    1. 引入插件
js 复制代码
import React, { useState, useRef, useEffect } from "react";
import { Radio, Card, Divider, Switch, Space } from "antd";
import WaveSurfer from "wavesurfer.js";
import RecordPlugin from "wavesurfer.js/plugins/recordPlugin";
import dayjs from "dayjs";
    1. 编写组件
js 复制代码
const AudioRecorder = (props) => {
    ....
};
export default AudioRecorder;
    1. 初始化录音
js 复制代码
const micRef = useRef();
const [record, setRecord] = useState<any>(null);
const [isDisabled, setIsDisabled] = useState(true);
const [value, setValue] = useState("");

useEffect(() => {
    if (!micRef.current) return;
    const wavesurfer = WaveSurfer.create({
            container: micRef.current,
            waveColor: "rgb(200, 0, 200)",
            progressColor: "rgb(100, 0, 100)",
    });

    const record = wavesurfer.registerPlugin(RecordPlugin.create() as any);
    record.on("record-end", async (blob) => {
            const recordedUrl = URL.createObjectURL(blob);
            const duration = await record.getDuration(blob);
            const audio = {
                    url: recordedUrl,
                    type: blob.type.split(";")[0].split("/")[1] || "webm",
                    createdAt: dayjs().format(),
                    duration,
            };
            props.onSetAudios((prevState) => [audio, ...prevState]);
    });
    setRecord(record);
}, [micRef]);

html

js 复制代码
<div id="mic" ref={micRef}></div>
    1. 实现打开麦克风
js 复制代码
// 销毁
function destroyRecord() {
        setIsDisabled(true);
        record.stopMic();
}

// 开启
function openRecord() {
        setIsDisabled(false);
        record.startMic();
}
        
function changeMic(checked) {
    checked ? openRecord() : destroyRecord();
}

html

js 复制代码
<Space>
    麦克风
    <Switch
            checkedChildren="开启"
            unCheckedChildren="关闭"
            onChange={changeMic}
    />
</Space>
    1. 实现操作 接下来我们实现录音的几个操作: 开始、保存。
js 复制代码
function startRecord() {
        setIsDisabled(true);
        record.startRecording().then(() => {
                setIsDisabled(false);
                setValue("start");
        });
}

function stopRecord() {
        if (record.isRecording()) {
                record.stopRecording();
                setValue("stop");
        }
}

html

js 复制代码
<Space>
	操作
    <Radio.Group buttonStyle="solid" disabled={isDisabled} value={value}>
            <Radio.Button value="start" onClick={startRecord}>
                    开始
            </Radio.Button>
            <Radio.Button value="stop" onClick={stopRecord}>
                    保存
            </Radio.Button>
            <Radio.Button value="pause" onClick={pauseRecord}>
                    暂停
            </Radio.Button>
            <Radio.Button value="resume" onClick={resumeRecord}>
                    继续
            </Radio.Button>
    </Radio.Group>
</Space>
    1. 实现暂停、继续

因为插件不支持暂停、继续。所以我们要对插件进行扩展。代码如下:

js 复制代码
/**
 * 扩展录音插件
 */

import RecordPlugin, {
	type RecordPluginEvents,
	type RecordPluginOptions,
} from "wavesurfer.js/plugins/record";

export type RecordEvents = RecordPluginEvents & {
	"record-pause": [];
	"record-resume": [];
};

class Record extends RecordPlugin {
	// private mediaRecorder: MediaRecorder | null = null;
	/** Create an instance of the Record plugin */
	constructor(options: RecordPluginOptions) {
		super({
			...options,
			audioBitsPerSecond: options.audioBitsPerSecond,
		});
	}

	/** Create an instance of the Record plugin */
	public static create(options?: RecordPluginOptions) {
		return new Record(options || {});
	}
	public getMediaRecorder() {
		// @ts-ignore
		return this.mediaRecorder;
	}

	/** Pause the recording */
	public pauseRecording() {
		if (this.isRecording()) {
			this.getMediaRecorder()?.pause();
		}
	}

	/** Resume the recording */
	public resumeRecording() {
		if (this.isRecording()) {
			this.getMediaRecorder()?.resume();
		}
	}

	/** Get the duration */
	public getDuration(blob): Promise<number> {
		return new Promise((resolve, reject) => {
			const audioContext = new AudioContext();
			const reader = new FileReader();

			reader.onload = function () {
				const arrayBuffer = this.result as ArrayBuffer;
				audioContext.decodeAudioData(
					arrayBuffer,
					(buffer) => {
						const duration = Math.round(buffer.duration * 1000);
						resolve(duration);
					},
					(err) => {
						throw new Error("Error getDuration:" + (err as Error).message);
					},
				);
			};

			if (blob) {
				reader.readAsArrayBuffer(blob);
			} else {
				throw new Error("Error blob is empty ");
			}
		});
	}
}

export default Record;

实现的效果如下:

总结

本文详细介绍了如何使用 Wavesurfer.jsReact 创建一个基本的音频录音与播放器。首先,我们创建了一个新的 React 项目,并安装了必需的 Wavesurfer.js 包。然后我们定义了一个 WaveSurferPlayer 组件,并通过 WaveSurfer.create() 创建了一个 wavesurfer 实例。最后,我们加载并播放了音频文件,并在组件卸载时销毁了 wavesurfer 实例。本教程简明易懂,有助于读者快速掌握如何利用 Wavesurfer.jsReact 搭建音频播放器,也让读者了解到 前端 在处理音频方面的强大能力。

Q&A

  • Q: 有源码吗?

当然有,地址如下:github.com/027xiguapi/...,有兴趣的话可以大家一起探讨,同时也欢迎大家forkstar

相关推荐
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊15 小时前
jwt介绍
前端
爱敲代码的小鱼15 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax