Vue3 錄音與播放套件 js-audio-recorder

安装js-audio-recorder

1
npm install js-audio-recorder --save

獲取數據

1
2
3
4
5
// 獲取錄音數據 buffer
recorder.getRecordAnalyseData()

// 獲取播放數據 buffer
recorder.getPlayAnalyseData()

錄音方法

1
2
3
4
5
6
7
8
9
10
11
// 錄音
recorder.start()

// 暫停
recorder.pause()

// 繼續
recorder.resume()

// 結束
recorder.stop()

播放

1
2
3
4
5
6
7
8
9
10
11
// 播放
recorder.play()

// 暫停
recorder.pausePlay()

// 繼續
recorder.resumePlay()

// 結束
recorder.stopPlay()

下载方法

1
2
3
4
5
// 下载 pcm 文件
recorder.downloadPCM()

// 下载 wam 文件
recorder.downloadWAV()

銷毀

1
2
// 銷毀
recorder.destroy()

參數配置

1
2
3
4
5
6
7
8
9
10
11
 let query = {
// 取樣位數,支援 8 或 16,預設是 16
sampleBits: 16,
// 取樣率,支援11025、16000、22050、24000、44100、48000,依瀏覽器預設值
sampleRate: 48000,
// 聲道,支援1或2,預設是1
numChannels: 1,
// 是否記錄邊,預設是false
compiling: false
}
new Recorder(query)

常用属性 attrs duration

1
2
// 時長
recorder.duration

文件大小 fileSize

1
recorder.fileSize
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<template>
<div class="BaseRecorder">
<div class="BaseRecorder-record">
<i @click="startRecorder()" title="開始錄音" class="fa-solid fa-microphone"></i>
<i @click="resumeRecorder()" title="继续錄音" class="fa-solid fa-microphone-lines"></i>
<i @click="pauseRecorder()" title="暂停錄音" class="fa-solid fa-microphone-lines-slash"></i>
<i @click="stopRecorder()" title="停止錄音" class="fa-solid fa-microphone-slash"></i>
</div>
<div class="BaseRecorder-play">
<i @click="playRecorder()" class="fa-solid fa-volume-high"></i>
<i @click="pausePlayRecorder()" class="fa-solid fa-volume-xmark"></i>

<button @click="resumePlayRecorder()">恢复錄音播放</button>
<button @click="stopPlayRecorder()">停止錄音播放</button>
</div>
<div class="BaseRecorder-download">
<i @click="downPCM()" title="下载PCM" class="fa-solid fa-cloud-arrow-down"></i>
<i @click="downWAV()" title="下载WAV" class="fa-solid fa-cloud-arrow-down"></i>

</div>
<div class="BaseRecorder-destroy">
<button @click="destroyRecorder()">销毁錄音</button>

</div>
<div class="BaseRecorder-wave">
<canvas ref="record"></canvas>
<canvas ref="play"></canvas>
</div>
</div>

</template>

<script setup lang="ts">
import Recorder from 'js-audio-recorder'
import { ref, computed,onMounted} from 'vue'
const recorder = ref<any>(null)
//波浪图-录音
const drawRecordId = ref<any>(null)
//波浪图-播放
const drawPlayId = ref<any>(null)
const init = () => {
let query = {
// 取樣位數,支援 8 或 16,預設是 16
sampleBits: 16,
// 取樣率,支援11025、16000、22050、24000、44100、48000,依瀏覽器預設值
sampleRate: 48000,
// 聲道,支援1或2,預設是1
numChannels: 1,
// 是否記錄邊,預設是false
compiling: false
}
recorder.value = new Recorder(query)
// console.log('recorder.value',recorder.value)
}

//開始錄音
const startRecorder =() =>{
recorder.value.start()
.then(
() => {
drawRecord()
},
error => {
// 出错了
console.log(`${error.name} : ${error.message}`)
}
)
}
//ref="record" =>this.$refs.record
const record = ref<any>()
//繪製記錄
const drawRecord = () => {
//**方法通知瀏覽器我們想要產生動畫,並且要求瀏覽器在下次重繪畫面前呼叫特定函數更新動畫。這個方法接受一個引數作為下次重繪前調用的回呼函數。
let query = {
canvas: record.value,
dataArray: recorder.value.getRecordAnalyseData(),
bgcolor: 'rgb(50 65 150)',
lineWidth: 2,
lineColor: 'rgb(255, 255, 255)'
}
drawRecordId.value = requestAnimationFrame(drawRecord)
drawWave(query)
console.log('query', query)
console.log('drawRecordId.value', drawRecordId.value)
}
//畫波
const drawWave = ({
canvas,
dataArray,
bgcolor = 'rgb(200, 200, 200)',
lineWidth = 2,
lineColor = 'rgb(0, 0, 0)'
}) => {
if (!canvas) return
const ctx = record.value.getContext('2d')
const bufferLength = dataArray.length
// 一個點佔多少位置,每個bufferLength個點要控制
const sliceWidth = canvas.width / bufferLength
// 受傷點的x軸位
let x = 0

// 填充背景色
ctx.fillStyle = bgcolor
ctx.fillRect(0, 0, canvas.width, canvas.height)

// 設定,設計顏色
ctx.lineWidth = lineWidth
ctx.strokeStyle = lineColor

ctx.beginPath()

for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128
const y = (v * canvas.height) / 2

if (i === 0) {
// 第一个点
ctx.moveTo(x, y)
} else {
// 剩余的点
ctx.lineTo(x, y)
}
// 依次平移,绘制所有点
x += sliceWidth
}

// 最后一个点
ctx.lineTo(canvas.width, canvas.height / 2)
ctx.stroke()
}
// 继续錄音
const resumeRecorder = () => {
recorder.value.resume();
}
//暂停錄音
const pauseRecorder = () => {
recorder.value.pause();
//cancelAnimationFrame:是一種JavaScript實作動畫的技巧,它解決了傳統計時器(setTimeout和setInterval)在處理動畫時的一些問題,特別是與瀏覽器重繪
drawRecordId.value && cancelAnimationFrame(drawRecordId.value)
drawRecordId.value = null
}
// 停止錄音
const stopRecorder = () => {
recorder.value.stop();
drawRecordId.value && cancelAnimationFrame(drawRecordId.value)
drawRecordId.value = null
}
//录音播放
const playRecorder = () => {
recorder.value.play()
drawPlay()
}
/**
* 绘制波浪图-播放
* */
const play = ref<any>(null)
//
const drawPlay = () => {
drawPlayId.value = requestAnimationFrame(drawPlay)
drawWave({
canvas: play,
dataArray: recorder.value.getPlayAnalyseData()
})
console.log('recorder.value.getPlayAnalyseData()', recorder.value.getPlayAnalyseData())
}
//暂停录音播放
const pausePlayRecorder = () => {
recorder.value.pausePlay()
}
//恢复录音播放
const resumePlayRecorder = () => {
recorder.value.resumePlay()
drawPlay() // 绘制波浪图
}
//
const stopPlayRecorder = () => {
recorder.value.stopPlay();

}

const downPCM = () => {
recorder.value.downloadPCM('新文件');
console.log(recorder.value.downloadPCM('新文件'))
}
const downWAV = () => {
recorder.value.downloadWAV('新文件');
console.log(recorder.value.downloadWAV('新文件'))
}
const destroyRecorder = () => {
recorder.value.destroy().then(function () {
drawRecordId.value && cancelAnimationFrame(drawRecordId.value)
drawRecordId.value = null

drawPlayId.value && cancelAnimationFrame(drawPlayId.value)
drawPlayId.value = null
recorder.value = null
})
}


onMounted(() => {
init();
})
</script>

參考
前端工程師
API串接常見問題 - CORS - 概念篇 (1)
API串接常見問題 - CORS - 概念篇 (2)

使用 MediaStream 錄製 API

MediaStream 錄製 API