背景
最近正在做一个开源的项目—pear-rec,pear-rec 是一个跨平台的截图、录屏、录音、录像软件。截图功能上篇文章已经讲过了,如果没有看过的可以去看看这篇文章————手把手教你,用electron实现截图软件,并且项目的框架搭建也可以参考上一篇文章,开发到现在我想要一个音乐(录音)预览、播放功能,说干就干,下面我介绍一下我的开发过程。
介绍
实现音频的录音和播放已经是许多应用程序的基础功能。这可能包括社交媒体平台,音乐创作软件,甚至是一些教育应用。在本教程中,我们将引导你如何使用 Wavesurfer.js
与 React
实现一个基本的音频录音与播放功能。
Wavesurfer.js
是一个开源的可视化音频库,它能够创建可交互的音频波形。这个库能够选择、播放或者暂停音频,同时也能进行音频的前后快翻转,同时也支持很多不同的音频格式。
官网 : wavesurfer.xyz/
github: github.com/katspaugh/w…
实现
一: 安装依赖创建项目
在开始之前,我们需要安装一些依赖项。请确保您已经安装了以下软件:
- Node.js
- pnpm
- react
在项目的根目录下打开终端,并执行以下命令来初始化一个 React
应用。在这里我就不详细讲解了,不会的可以去看我的文章。
然后安装 wavesurfer.js
和录音的库:
1npm install --save wavesurfer.js
2import WaveSurfer from 'wavesurfer.js'
二: 创建 Wavesurfer 组件
先看我们要实现效果:
-
- 插件
1import React, { useState, useRef, useEffect, useCallback } from "react";
2import { Button, Space, Divider } from "antd";
3import WaveSurfer from "wavesurfer.js";
-
- 编写
WaveSurfer hook
- 编写
1const useWavesurfer = (containerRef, options) => {
2 const [wavesurfer, setWavesurfer] = useState(null)
3
4
5
6 useEffect(() => {
7 if (!containerRef.current) return
8
9 const ws = WaveSurfer.create({
10 ...options,
11 container: containerRef.current,
12 })
13
14 setWavesurfer(ws)
15
16 return () => {
17 ws.destroy()
18 }
19 }, [options, containerRef])
20
21 return wavesurfer
22}
-
- 编写
WaveSurferPlayer
组件
- 编写
1
2const WaveSurferPlayer = (props) => {
3 const containerRef = useRef()
4 const [isPlaying, setIsPlaying] = useState(false)
5 const [currentTime, setCurrentTime] = useState(0)
6 const wavesurfer = useWavesurfer(containerRef, props)
7
8
9 const onPlayClick = useCallback(() => {
10 wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play()
11 }, [wavesurfer])
12
13
14
15 useEffect(() => {
16 if (!wavesurfer) return
17
18 setCurrentTime(0)
19 setIsPlaying(false)
20
21 const subscriptions = [
22 wavesurfer.on('play', () => setIsPlaying(true)),
23 wavesurfer.on('pause', () => setIsPlaying(false)),
24 wavesurfer.on('timeupdate', (currentTime) => setCurrentTime(currentTime)),
25 ]
26
27 return () => {
28 subscriptions.forEach((unsub) => unsub())
29 }
30 }, [wavesurfer])
31
32 return (
33 <>
34 <div ref={containerRef} style={{ minHeight: '120px' }} />
35
36 <button onClick={onPlayClick} style={{ marginTop: '1em' }}>
37 {isPlaying ? 'Pause' : 'Play'}
38 </button>
39
40 <p>Seconds played: {currentTime}</p>
41 </>
42 )
43}
-
- 使用
1const App = () => {
2 const urls = ['/examples/audio/audio.wav', '/examples/audio/stereo.mp3']
3 const [audioUrl, setAudioUrl] = useState(urls[0])
4
5
6 const onUrlChange = useCallback(() => {
7 urls.reverse()
8 setAudioUrl(urls[0])
9 }, [])
10
11
12
13 return (
14 <>
15 <WaveSurferPlayer
16 height={100}
17 waveColor="rgb(200, 0, 200)"
18 progressColor="rgb(100, 0, 100)"
19 url={audioUrl}
20 plugins={[Timeline.create()]}
21 />
22
23 <button onClick={onUrlChange}>Change audio</button>
24 </>
25 )
26}
27
效果如下:
这样的效果还是有点简单,我实现的最终效果如下:
可以自由播放、暂停、显示当前播放位置和选取播放位置、快进、后退。。。
三、实现录音
我们要实现的录音效果大概是这样:
因为
wavesurfer.js
有带有录音插件,所以我们可以直接用,话不多说我们实现吧!
-
- 引入插件
1import React, { useState, useRef, useEffect } from "react";
2import { Radio, Card, Divider, Switch, Space } from "antd";
3import WaveSurfer from "wavesurfer.js";
4import RecordPlugin from "wavesurfer.js/plugins/recordPlugin";
5import dayjs from "dayjs";
-
- 编写组件
1const AudioRecorder = (props) => {
2 ....
3};
4export default AudioRecorder;
-
- 初始化录音
1const micRef = useRef();
2const [record, setRecord] = useState<any>(null);
3const [isDisabled, setIsDisabled] = useState(true);
4const [value, setValue] = useState("");
5
6useEffect(() => {
7 if (!micRef.current) return;
8 const wavesurfer = WaveSurfer.create({
9 container: micRef.current,
10 waveColor: "rgb(200, 0, 200)",
11 progressColor: "rgb(100, 0, 100)",
12 });
13
14 const record = wavesurfer.registerPlugin(RecordPlugin.create() as any);
15 record.on("record-end", async (blob) => {
16 const recordedUrl = URL.createObjectURL(blob);
17 const duration = await record.getDuration(blob);
18 const audio = {
19 url: recordedUrl,
20 type: blob.type.split(";")[0].split("/")[1] || "webm",
21 createdAt: dayjs().format(),
22 duration,
23 };
24 props.onSetAudios((prevState) => [audio, ...prevState]);
25 });
26 setRecord(record);
27}, [micRef]);
28
29
html
1<div id="mic" ref={micRef}></div>
-
- 实现打开麦克风
1
2function destroyRecord() {
3 setIsDisabled(true);
4 record.stopMic();
5}
6
7
8function openRecord() {
9 setIsDisabled(false);
10 record.startMic();
11}
12
13function changeMic(checked) {
14 checked ? openRecord() : destroyRecord();
15}
html
1<Space>
2 麦克风
3 <Switch
4 checkedChildren="开启"
5 unCheckedChildren="关闭"
6 onChange={changeMic}
7 />
8</Space>
-
- 实现操作 接下来我们实现录音的几个操作: 开始、保存。
1function startRecord() {
2 setIsDisabled(true);
3 record.startRecording().then(() => {
4 setIsDisabled(false);
5 setValue("start");
6 });
7}
8
9function stopRecord() {
10 if (record.isRecording()) {
11 record.stopRecording();
12 setValue("stop");
13 }
14}
15
html
1<Space>
2 操作
3 <Radio.Group buttonStyle="solid" disabled={isDisabled} value={value}>
4 <Radio.Button value="start" onClick={startRecord}>
5 开始
6 </Radio.Button>
7 <Radio.Button value="stop" onClick={stopRecord}>
8 保存
9 </Radio.Button>
10 <Radio.Button value="pause" onClick={pauseRecord}>
11 暂停
12 </Radio.Button>
13 <Radio.Button value="resume" onClick={resumeRecord}>
14 继续
15 </Radio.Button>
16 </Radio.Group>
17</Space>
-
- 实现暂停、继续
因为插件不支持暂停、继续。所以我们要对插件进行扩展。代码如下:
1
2 * 扩展录音插件
3 */
4
5import RecordPlugin, {
6 type RecordPluginEvents,
7 type RecordPluginOptions,
8} from "wavesurfer.js/plugins/record";
9
10export type RecordEvents = RecordPluginEvents & {
11 "record-pause": [];
12 "record-resume": [];
13};
14
15class Record extends RecordPlugin {
16
17
18 constructor(options: RecordPluginOptions) {
19 super({
20 ...options,
21 audioBitsPerSecond: options.audioBitsPerSecond,
22 });
23 }
24
25
26 public static create(options?: RecordPluginOptions) {
27 return new Record(options || {});
28 }
29 public getMediaRecorder() {
30
31 return this.mediaRecorder;
32 }
33
34
35 public pauseRecording() {
36 if (this.isRecording()) {
37 this.getMediaRecorder()?.pause();
38 }
39 }
40
41
42 public resumeRecording() {
43 if (this.isRecording()) {
44 this.getMediaRecorder()?.resume();
45 }
46 }
47
48
49 public getDuration(blob): Promise<number> {
50 return new Promise((resolve, reject) => {
51 const audioContext = new AudioContext();
52 const reader = new FileReader();
53
54 reader.onload = function () {
55 const arrayBuffer = this.result as ArrayBuffer;
56 audioContext.decodeAudioData(
57 arrayBuffer,
58 (buffer) => {
59 const duration = Math.round(buffer.duration * 1000);
60 resolve(duration);
61 },
62 (err) => {
63 throw new Error("Error getDuration:" + (err as Error).message);
64 },
65 );
66 };
67
68 if (blob) {
69 reader.readAsArrayBuffer(blob);
70 } else {
71 throw new Error("Error blob is empty ");
72 }
73 });
74 }
75}
76
77export default Record;
78
实现的效果如下:
总结
本文详细介绍了如何使用 Wavesurfer.js
和 React
创建一个基本的音频录音与播放器。首先,我们创建了一个新的 React
项目,并安装了必需的 Wavesurfer.js
包。然后我们定义了一个 WaveSurferPlayer
组件,并通过 WaveSurfer.create()
创建了一个 wavesurfer
实例。最后,我们加载并播放了音频文件,并在组件卸载时销毁了 wavesurfer
实例。本教程简明易懂,有助于读者快速掌握如何利用 Wavesurfer.js
和 React
搭建音频播放器,也让读者了解到 前端
在处理音频方面的强大能力。
Q&A
- Q: 有源码吗?
当然有,地址如下:pear-rec,有兴趣的话可以大家一起探讨,同时也欢迎大家fork
和star