首頁 > 軟體

vue實現錄音功能js-audio-recorder帶波浪圖效果的範例

2022-08-04 14:01:35

前言:

因為業務需要,現在將整理的錄音功能資料記錄下,使用外掛js-audio-recorder

實現效果:可得到三種錄音資料,pcm,wav,mp3 等

官方api入口:點我(網不好的童鞋可以看最下面的api截圖)

官方案例入口:點我

官方原始碼git入口:點我

實現步驟:

一:安裝外掛 js-audio-recorder

cnpm i js-audio-recorder --s

二:安裝將格式轉換為mp3的外掛 lamejs

cnpm install lamejs --s

三:附上實現原始碼:

提示:慎用自身的這個監聽事件,可以拿到資料,但是每秒拿到的資料非常多

<template>
  <div class="home" style="margin:1vw;">
    <Button type="success" @click="getPermission()" style="margin:1vw;">獲取麥克風許可權</Button>
    <br/>
    <Button type="info" @click="startRecorder()"  style="margin:1vw;">開始錄音</Button>
    <Button type="info" @click="resumeRecorder()" style="margin:1vw;">繼續錄音</Button>
    <Button type="info" @click="pauseRecorder()" style="margin:1vw;">暫停錄音</Button>
    <Button type="info" @click="stopRecorder()" style="margin:1vw;">結束錄音</Button>
    <br/>
    <Button type="success" @click="playRecorder()" style="margin:1vw;">錄音播放</Button>
    <Button type="success" @click="pausePlayRecorder()" style="margin:1vw;">暫停錄音播放</Button>
    <Button type="success" @click="resumePlayRecorder()" style="margin:1vw;">恢復錄音播放</Button>
    <Button type="success" @click="stopPlayRecorder()" style="margin:1vw;">停止錄音播放</Button>
    <br/>
    <Button type="info" @click="getRecorder()" style="margin:1vw;">獲取錄音資訊</Button>
    <Button type="info" @click="downPCM()" style="margin:1vw;">下載PCM</Button>
    <Button type="info" @click="downWAV()" style="margin:1vw;">下載WAV</Button>
    <Button type="info" @click="getMp3Data()" style="margin:1vw;">下載MP3</Button>
    <br/>
    <Button type="error" @click="destroyRecorder()" style="margin:1vw;">銷燬錄音</Button>
    <br/>
    <div style="width:100%;height:200px;border:1px solid red;">
      <canvas id="canvas"></canvas>
      <span style="padding: 0 10%;"></span>
      <canvas id="playChart"></canvas>
    </div>
  </div>
</template>
<script>
  import Recorder from 'js-audio-recorder'
  const lamejs = require('lamejs')
  const recorder = new Recorder({
    sampleBits: 16,                 // 取樣位數,支援 8 或 16,預設是16
    sampleRate: 48000,              // 取樣率,支援 11025、16000、22050、24000、44100、48000,根據瀏覽器預設值,我的chrome是48000
    numChannels: 1,                 // 聲道,支援 1 或 2, 預設是1
    // compiling: false,(0.x版本中生效,1.x增加中)  // 是否邊錄邊轉換,預設是false
  })
  // 繫結事件-列印的是當前錄音資料
  recorder.onprogress = function(params) {
    // console.log('--------------START---------------')
    // console.log('錄音時長(秒)', params.duration);
    // console.log('錄音大小(位元組)', params.fileSize);
    // console.log('錄音音量百分比(%)', params.vol);
    // console.log('當前錄音的總資料([DataView, DataView...])', params.data);
    // console.log('--------------END---------------')
  }
  export default {
    name: 'home',
    data () {
      return {
        //波浪圖-錄音
        drawRecordId:null,
        oCanvas : null,
        ctx : null,
        //波浪圖-播放
        drawPlayId:null,
        pCanvas : null,
        pCtx : null,
      }
    },
    mounted(){
      this.startCanvas();
    },
    methods: {
      /**
       * 波浪圖設定
       * */
      startCanvas(){
        //錄音波浪
        this.oCanvas = document.getElementById('canvas');
        this.ctx = this.oCanvas.getContext("2d");
        //播放波浪
        this.pCanvas = document.getElementById('playChart');
        this.pCtx = this.pCanvas.getContext("2d");
      },
      /**
       *  錄音的具體操作功能
       * */
      // 開始錄音
      startRecorder () {
        recorder.start().then(() => {
          this.drawRecord();//開始繪製圖片
        }, (error) => {
          // 出錯了
          console.log(`${error.name} : ${error.message}`);
        });
      },
      // 繼續錄音
      resumeRecorder () {
        recorder.resume()
      },
      // 暫停錄音
      pauseRecorder () {
        recorder.pause();
        this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
        this.drawRecordId = null;
      },
      // 結束錄音
      stopRecorder () {
        recorder.stop()
        this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
        this.drawRecordId = null;
      },
      // 錄音播放
      playRecorder () {
        recorder.play();
        this.drawPlay();//繪製波浪圖
      },
      // 暫停錄音播放
      pausePlayRecorder () {
        recorder.pausePlay()
      },
      // 恢復錄音播放
      resumePlayRecorder () {
        recorder.resumePlay();
        this.drawPlay();//繪製波浪圖
      },
      // 停止錄音播放
      stopPlayRecorder () {
        recorder.stopPlay();
      },
      // 銷燬錄音
      destroyRecorder () {
        recorder.destroy().then(function() {
          recorder = null;
          this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
          this.drawRecordId = null;
        });
      },
      /**
       *  獲取錄音檔案
       * */
      getRecorder(){
        let toltime = recorder.duration;//錄音總時長
        let fileSize = recorder.fileSize;//錄音總大小
        //錄音結束,獲取取錄音資料
        let PCMBlob = recorder.getPCMBlob();//獲取 PCM 資料
        let wav = recorder.getWAVBlob();//獲取 WAV 資料
        let channel = recorder.getChannelData();//獲取左聲道和右聲道音訊資料
      },
      /**
       *  下載錄音檔案
       * */
      //下載pcm
      downPCM(){
        //這裡傳參進去的時檔名
        recorder.downloadPCM('新檔案');
      },
      //下載wav
      downWAV(){
        //這裡傳參進去的時檔名
        recorder.downloadWAV('新檔案');
      },
      /**
       *  獲取麥克風許可權
       * */
      getPermission(){
        Recorder.getPermission().then(() => {
          this.$Message.success('獲取許可權成功')
        }, (error) => {
          console.log(`${error.name} : ${error.message}`);
        });
      },
      /**
       * 檔案格式轉換 wav-map3
       * */
      getMp3Data(){
        const mp3Blob = this.convertToMp3(recorder.getWAV());
        recorder.download(mp3Blob, 'recorder', 'mp3');
      },
      convertToMp3(wavDataView) {
        // 獲取wav頭資訊
        const wav = lamejs.WavHeader.readHeader(wavDataView); // 此處其實可以不用去讀wav頭資訊,畢竟有對應的config設定
        const { channels, sampleRate } = wav;
        const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
        // 獲取左右通道資料
        const result = recorder.getChannelData()
        const buffer = [];
        const leftData = result.left && new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
        const rightData = result.right && new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
        const remaining = leftData.length + (rightData ? rightData.length : 0);
        const maxSamples = 1152;
        for (let i = 0; i < remaining; i += maxSamples) {
          const left = leftData.subarray(i, i + maxSamples);
          let right = null;
          let mp3buf = null;
          if (channels === 2) {
            right = rightData.subarray(i, i + maxSamples);
            mp3buf = mp3enc.encodeBuffer(left, right);
          } else {
            mp3buf = mp3enc.encodeBuffer(left);
          }
          if (mp3buf.length > 0) {
            buffer.push(mp3buf);
          }
        }
        const enc = mp3enc.flush();
        if (enc.length > 0) {
          buffer.push(enc);
        }
        return new Blob(buffer, { type: 'audio/mp3' });
      },
      /**
       * 繪製波浪圖-錄音
       * */
      drawRecord () {
        // 用requestAnimationFrame穩定60fps繪製
        this.drawRecordId = requestAnimationFrame(this.drawRecord);
        // 實時獲取音訊大小資料
        let dataArray = recorder.getRecordAnalyseData(),
            bufferLength = dataArray.length;
        // 填充背景色
        this.ctx.fillStyle = 'rgb(200, 200, 200)';
        this.ctx.fillRect(0, 0, this.oCanvas.width, this.oCanvas.height);
        // 設定波形繪製顏色
        this.ctx.lineWidth = 2;
        this.ctx.strokeStyle = 'rgb(0, 0, 0)';
        this.ctx.beginPath();
        var sliceWidth = this.oCanvas.width * 1.0 / bufferLength, // 一個點佔多少位置,共有bufferLength個點要繪製
                x = 0;          // 繪製點的x軸位置
        for (var i = 0; i < bufferLength; i++) {
          var v = dataArray[i] / 128.0;
          var y = v * this.oCanvas.height / 2;
          if (i === 0) {
            // 第一個點
            this.ctx.moveTo(x, y);
          } else {
            // 剩餘的點
            this.ctx.lineTo(x, y);
          }
          // 依次平移,繪製所有點
          x += sliceWidth;
        }
        this.ctx.lineTo(this.oCanvas.width, this.oCanvas.height / 2);
        this.ctx.stroke();
      },
      /**
       * 繪製波浪圖-播放
       * */
      drawPlay () {
        // 用requestAnimationFrame穩定60fps繪製
        this.drawPlayId = requestAnimationFrame(this.drawPlay);
        // 實時獲取音訊大小資料
        let dataArray = recorder.getPlayAnalyseData(),
                bufferLength = dataArray.length;
        // 填充背景色
        this.pCtx.fillStyle = 'rgb(200, 200, 200)';
        this.pCtx.fillRect(0, 0, this.pCanvas.width, this.pCanvas.height);
        // 設定波形繪製顏色
        this.pCtx.lineWidth = 2;
        this.pCtx.strokeStyle = 'rgb(0, 0, 0)';
        this.pCtx.beginPath();
        var sliceWidth = this.pCanvas.width * 1.0 / bufferLength, // 一個點佔多少位置,共有bufferLength個點要繪製
                x = 0;          // 繪製點的x軸位置
        for (var i = 0; i < bufferLength; i++) {
          var v = dataArray[i] / 128.0;
          var y = v * this.pCanvas.height / 2;
          if (i === 0) {
            // 第一個點
            this.pCtx.moveTo(x, y);
          } else {
            // 剩餘的點
            this.pCtx.lineTo(x, y);
          }
          // 依次平移,繪製所有點
          x += sliceWidth;
        }
        this.pCtx.lineTo(this.pCanvas.width, this.pCanvas.height / 2);
        this.pCtx.stroke();
      }
    },
  }
</script>
<style lang='less' scoped>
</style>

到這裡,程式碼就結束了,上面每個方法都有很詳細的註釋,就不累贅了

整理api:(有代理的可以看官網,這裡是摘取官網的api)

1,使用

安裝

npm 方式

推薦使用npm安裝的方式:

安裝:

npm i js-audio-recorder

呼叫:

import Recorder from 'js-audio-recorder';
let recorder = new Recorder();

script 標籤方式

<script type="text/javascript" src="./dist/recorder.js"></script>
let recorder = new Recorder();

2,屬性

範例初始化

可以設定輸出資料引數,

let recorder = new Recorder({
    sampleBits: 16,                 // 取樣位數,支援 8 或 16,預設是16
    sampleRate: 16000,              // 取樣率,支援 11025、16000、22050、24000、44100、48000,根據瀏覽器預設值,我的chrome是48000
    numChannels: 1,                 // 聲道,支援 1 或 2, 預設是1
    // compiling: false,(0.x版本中生效,1.x增加中)  // 是否邊錄邊轉換,預設是false
});

返回: recorder範例。

sampleBits

取樣位數。

sampleRate

取樣率。

numChannels

聲道數。

compiling

(0.x版本中生效,最新目前不支援)

是否邊錄音邊轉換。

獲取資料方法:

回撥方式

recorder.onprogress = function(params) {
    console.log(params.data);       // 當前獲取到到音訊資料
}

data,DataView型陣列,格式如 [DataView, DataView, DataView ...] 。

主動獲取

getWholeData();     // [DataView, DataView, DataView ...]
getNextData();      // [DataView, DataView, DataView ...]

getWholeData() 的值和onprogress回撥中的data資料一致。

getNextData() 獲取的是前一次 getNextData() 之後的值,他只是data資料的一小部分。

範例屬性

duration

獲取錄音的總時長。

console.log(recorder.duration);

fileSize

錄音檔案大小(單位:位元組)。

console.log(recorder.fileSize);

3,操作

start()

開始錄音。

返回: Promise。

recorder.start().then(() => {
    // 開始錄音
}, (error) => {
    // 出錯了
    console.log(`${error.name} : ${error.message}`);
});

pause()

錄音暫停。

返回: void

recorder.pause();

resume()

繼續錄音。

返回: void。

recorder.resume()

stop()

結束錄音。

返回: void。

recorder.stop();

play()

錄音播放。

返回: void。

recorder.play();

getPlayTime()

獲取音訊已經播的時長。

返回: number。

recorder.getPlayTime();

pausePlay()

暫停錄音播放。

返回: void。

recorder.pausePlay();

resumePlay()

恢復錄音播發。

返回: void。

recorder.resumePlay();

stopPlay()

停止播放。

返回: void。

recorder.stopPlay();

destroy()

銷燬範例。

返回: Promise。

// 銷燬錄音範例,置為null釋放資源,fn為回撥函數,
recorder.destroy().then(function() {
    recorder = null;
});

音訊資料

錄音結束,獲取取錄音資料

getPCMBlob()

獲取 PCM 資料,在錄音結束後使用。

返回: Blob

注:使用該方法會預設呼叫 stop() 方法。

recorder.getPCMBlob();

getWAVBlob()

獲取 WAV 資料,在錄音結束後使用

返回: Blob

注:使用該方法會預設呼叫 stop() 方法。

recorder.getWAVBlob();

getChannelData()

獲取左聲道和右聲道音訊資料。

recorder.getChannelData();

錄音結束,下載錄音檔案

downloadPCM([ filename ])

下載 PCM 格式

  • fileName String 重新命名檔案
  • 返回: Blob

注:使用該方法會預設呼叫 stop() 方法。

recorder.downloadPCM(fileName ?);

downloadWAV([ filename ])

下載 WAV 格式

  • fileName String 重新命名檔案
  • 返回: Blob

注:使用該方法會預設呼叫 stop() 方法。

錄音中,獲取錄音資料

(0.x版本中生效,最新目前不支援)

該方式為邊錄邊轉換,建議在 compiling 為 true 時使用。

getWholeData()

獲取已經錄音的所有資料。若沒有開啟邊錄邊轉(compiling為false),則返回是空陣列。

返回: Array, 陣列中是DataView資料

定時獲取所有資料:

setInterval(() => {
    recorder.getWholeData();
}, 1000)

getNextData()

獲取前一次 getNextData() 之後的資料。若沒有開啟邊錄邊轉(compiling為false),則返回是空陣列。

  • 返回: Array, 陣列中是DataView資料

定時獲取新增資料:

setInterval(() => {
    recorder.getNextData();
}, 1000)
// 實時錄音,則可將該資料返回給伺服器端。

錄音波形顯示

getRecordAnalyseData()

返回的是一個1024長的,0-255大小的Uint8Array型別。使用者可以根據這些資料自定義錄音波形。此介面獲取的是錄音時的。

let dataArray = recorder.getRecordAnalyseData();

getPlayAnalyseData()

返回資料同 getRecordAnalyseData(),該方法獲取的是播放時的。

let dataArray = recorder.getPlayAnalyseData();

播放外部

Player.play(blob)

播放外部音訊,格式由瀏覽器的audio支援的型別決定。

Player.play(/* 放入arraybuffer資料 */);

其他

錄音許可權

未給予錄音許可權的頁面在開始錄音時需要再次點選允許錄音,才能真正地錄音,存在丟失開始這一段錄音的情況,增加方法以便使用者提前獲取麥克風許可權。

getPermission()

獲取麥克風許可權。

返回:promise。

Recorder.getPermission().then(() => {
    console.log('給許可權了');
}, (error) => {
    console.log(`${error.name} : ${error.message}`);
});

此處then回撥與start的一致。

4,Event

js-audio-recorder 支援的事件回撥。

onprocess(duration)

用於獲取錄音時長。

不推薦使用,用onprogress代替。

recorder.onprocess = function(duration) {
    console.log(duration);
}

onprogress(duration)

目前支援獲取以下資料:

  • 錄音時長(duration)。
  • 錄音大小(fileSize)。
  • 錄音音量百分比(vol)。
  • 所有的錄音資料(data)。
recorder.onprogress = function(params) {
    console.log('錄音時長(秒)', params.duration);
    console.log('錄音大小(位元組)', params.fileSize);
    console.log('錄音音量百分比(%)', params.vol);
    // console.log('當前錄音的總資料([DataView, DataView...])', params.data);
}

onplay

錄音播放開始回撥。

recorder.onplay = () => {
    console.log('onplay')
}

onpauseplay

錄音播放暫停回撥。

recorder.onpauseplay = () => {
    console.log('onpauseplay')
}

onresumeplay

錄音播放恢復回撥。

recorder.onresumeplay = () => {
    console.log('onresumeplay')
}

onstopplay

錄音播放停止回撥。

recorder.onstopplay = () => {
    console.log('onstopplay')
}

onplayend

錄音播放完成回撥。

recorder.onplayend = () => {
    console.log('onplayend')
}

5,應用

語音識別

recorder上可以測試,注意選擇16000取樣率,16取樣位數,單聲道錄音。

6,PlayerPlayer 播放類

import Player from './player/player';

用於協助播放錄音檔案,包括,開始、暫停、恢復、停止等功能。所支援的格式由瀏覽器的audio支援的型別決定。可單獨使用。

Player.play([arraybuffer])

播放外部的音訊。所支援的格式由瀏覽器的audio支援的型別決定。

實際是呼叫了decodeAudioData實現音訊播放。

Recorder.play(/* 放入arraybuffer資料 */);

Player.pausePlay()

暫停播放。

Player.resumePlay()

恢復播放。

Player.stopPlay()

停止播放。

Player.addPlayEnd(fn)

增加播放完成回撥函數。

Player.getPlayTime()

獲取播放時間。

Player.getAnalyseData()

獲取回放錄音的波形資料。

7,其他音訊格式

MP3

將pcm(wav)音訊檔轉化為mp3格式。

注:使用16取樣位數。

利用lamejs進行轉換,使用情況見demo,例子:

function convertToMp3(wavDataView) {
    // 獲取wav頭資訊
    const wav = lamejs.WavHeader.readHeader(wavDataView); // 此處其實可以不用去讀wav頭資訊,畢竟有對應的config設定
    const { channels, sampleRate } = wav;
    const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
    // 獲取左右通道資料
    const result = recorder.getChannelData()
    const buffer = [];
 
    const leftData = result.left && new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
    const rightData = result.right && new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
    const remaining = leftData.length + (rightData ? rightData.length : 0);
 
    const maxSamples = 1152;
    for (let i = 0; i < remaining; i += maxSamples) {
        const left = leftData.subarray(i, i + maxSamples);
        let right = null;
        let mp3buf = null;
 
        if (channels === 2) {
            right = rightData.subarray(i, i + maxSamples);
            mp3buf = mp3enc.encodeBuffer(left, right);
        } else {
            mp3buf = mp3enc.encodeBuffer(left);
        }
 
        if (mp3buf.length > 0) {
            buffer.push(mp3buf);
        }
    }
 
    const enc = mp3enc.flush();
 
    if (enc.length > 0) {
        buffer.push(enc);
    }
 
    return new Blob(buffer, { type: 'audio/mp3' });
}

安裝lamejs

npm install lamejs

到此這篇關於vue實現錄音功能js-audio-recorder帶波浪圖效果的範例的文章就介紹到這了,更多相關vue js-audio-recorder錄音內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com