Web API 內建的 MediaRecorder
並無法將麥克風的聲音儲存成 PCM
格式檔案,而 extendable-media-recorder
提供與 MediaRecorder
相同的 interface,但可支援 PCM
格式。
Version
Vue 3.3
extendable-media-recorder 9.1.6
extendable-media-recorder-wav-encoder 7.0.101
Install Package
$ npm install extendable-media-recorder
$ npm install extendable-media-recorder-wav-encoder
extendable-media-recorder
:提供與原生MediaRecorder
相同 interface 的新MediaRecorder
extendable-media-recorder-wav-encoder
:支援WAV
格式
Architecture
MediaStream
: 從麥克風取得 streamAudioContext
: 產生 16 bit stream- 由
AudioContext
產生SrcNode
與DestNode
,需使用connect()
將各 node 連結在一起 MediaRecorder
:將WebM
轉PCM
Extendable Media Recorder
main.js
import { createApp } from 'vue'
import App from './App.vue'
import { register } from 'extendable-media-recorder'
import { connect } from 'extendable-media-recorder-wav-encoder'
await register(await connect())
createApp(App).mount('#app')
Line 7
await register(await connect())
- 與原生
MediaRecorder
不同,extendable-media-recorder
所提供的MediaRecorder
必須先註冊才能使用 register()
不能寫在每個頁面的mounted()
,只要 route 改變重新進入該 page,就會造成重複註冊的錯誤,因此只能寫在main.js
只註冊一次
App.vue
<template>
<button @click="onStart">Start</button>
<button @click="onStop">Stop</button>
</template>
<script setup>
import { MediaRecorder } from 'extendable-media-recorder'
const SAMPLE_RATE = 16000
const CHANNEL_COUNT = 1
const SAMPLE_INTERVAL = 1000
const FILE_NAME = 'sample.pcm'
let mediaRecorder = null
let chunks = []
let onStart = async () => {
try {
let mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true })
let audioContext = new AudioContext({ sampleRate: SAMPLE_RATE })
let srcNode = new MediaStreamAudioSourceNode(audioContext, { mediaStream })
let destNode = new MediaStreamAudioDestinationNode(audioContext, {
channelCount: CHANNEL_COUNT
})
srcNode.connect(destNode)
mediaRecorder = new MediaRecorder(destNode.stream, {
mimeType: 'audio/wav'
})
mediaRecorder.ondataavailable = e => {
chunks.push(e.data)
}
mediaRecorder.onstop = () => {
let blob = new Blob(chunks)
let link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = FILE_NAME
link.click()
URL.revokeObjectURL(link.href)
}
mediaRecorder.start(SAMPLE_INTERVAL)
} catch (err) {
console.warn(err)
}
}
let onStop = () => {
mediaRecorder.stop()
}
</script>
Line 7
import { MediaRecorder } from 'extendable-media-recorder'
- 使用
extendable-media-recorder
所提供的MediaRecorder
取代 Web API 內建的MediaRecorder
Line 9
const SAMPLE_RATE = 16000
const CHANNEL_COUNT = 1
const SAMPLE_INTERVAL = 1000
const FILE_NAME = 'sample.pcm'
設定可改用的變數:
SAMPLE_RATE
:取樣頻率CHANNEL_COUNT
:Mono channelPERIOD
:每次取樣時間FILE_NAME
:下載檔名
Line 19
let mediaStream = await navigator.mediaDevices.getUserMedia({
audio: true,
})
- 使用 Web API 內建的
navigator.mediaDevices.getUserMedia()
取得MediaStream
Line 21
let audioContext = new AudioContext({ sampleRate: SAMPLE_RATE })
- 要改變取樣頻率,必須使用
AudioContext
Line 23
let srcNode = new MediaStreamAudioSourceNode(audioContext, { mediaStream })
let destNode = new MediaStreamAudioDestinationNode(audioContext)
AudioConext
必須靠node
方式運行- 由
AudioContext
與MediaStream
建立srcNode
- 由
AudioContext
建立destNode
Line 28
srcNode.connect(destNode)
- 使用
connect()
連結srcNode
與destNode
Line 30
mediaRecorder = new MediaRecorder(destNode.stream, {
mimeType: 'audio/wav',
})
- 使用
extendable-media-recorder
提供的MediaRecorder
,並改由destNode
所處理過的MediaStream
- 指定
mineType
為audio/wav
Line 55
let onStop = () => {
mediaRecorder.stop()
}
- 啟動
MediaRecoder
開始錄音
Line 34
mediaRecorder.ondataavailable = e => {
chunks.push(e.data)
}
- 觸發
dataavaliable
event 取得音訊 - 依序塞入儲存音訊的
chunks
array
Line 38
mediaRecorder.onstop = () => {
let blob = new Blob(chunks)
let link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = FILE_NAME
link.click()
URL.revokeObjectURL(link.href)
}
- 將
chunks
array 轉成Blob
- 建立臨時 url 下載
PCM
檔案
Conclusion
- Web API 內建的
MediaRecorder
並不支援WAV
格式 extendable-media-recorder
提供與MediaRecorder
相同的 interface,但可支援WAV
格式- 但
extendable-media-recorder
的MediaRecorder
必須register()
之後才能使用,且與 Vue 的生命週期不太一樣,不能在mounted()
寫register()
,會造成重複註冊問題,必須改寫在main.js
只重複一次