WEB录音 vue demo _笔记

475 阅读1分钟
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>降噪</title>

  <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  <link rel="stylesheet" href="./css/index.css">

  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>

</head>

<body>

  <div id="app">
    <el-button @click="startRecording" :disabled="btn.start">开始录音</el-button>
    <el-button @click="endRecording" :disabled="btn.end">结束录音</el-button>
  </div>

  <script src="./js/index-1.js"></script>

</body>

</html>
/**
 * 采样率:默认
 * 声道:单
 * 音频格式:wav
 */

new Vue({
  el: '#app',
  data() {
    return {
      btn: {
        start: false,
        end: true
      },
      mediaData: []
    }
  },
  methods: {
    startRecording() {

      let _this_ = this,
          config = {
            audio: {
              channelCount: 1, // 声道
              volume: 1.0, // 音量
              noiseSuppression: false,
              autoGainControl: false,
              echoCancellation: false,
              sampleRate: 48000,
              sampleSize: 16
            },
            video: false
          };

      const getMediaSuccess = (stream) => {

        _this_.btn = {
          start: true,
          end: false
        }

        _this_.stream = stream;

        _this_.scriptProcessor = _this_.audioContext.createScriptProcessor(1024, 1, 1);

        _this_.scriptProcessor.onaudioprocess = function (e) {


          _this_.mediaData.push(new Float32Array(e.inputBuffer.getChannelData(0)));

        };

        _this_.mediaSource = _this_.audioContext.createMediaStreamSource(stream);

        _this_.mediaSource.connect(_this_.scriptProcessor);

        _this_.scriptProcessor.connect(_this_.audioContext.destination);

      }

      const getMediaFail = (e) => {

        _this_.audioContext && _this_.audioContext.close();
        _this_.audioContext = undefined;

        _this_.btn = {
          start: false,
          end: true
        }

        switch (e.name || e.code) {
          case 'PERMISSION_DENIED':
          case 'PermissionDeniedError':
            throw new Error('用户拒绝提供信息');
          break
          case 'NOT_SUPPORTED_ERROR':
          case 'NotSupportedError':
            throw new Error('浏览器不支持硬件设备');
          break
          case 'MANDATORY_UNSATISFIED_ERROR':
          case 'MandatoryUnsatisfiedError':
            throw new Error('无法发现指定硬件设备');
          break
          default:
            throw new Error(`无法打开麦克风。异常信息 ${e.code} || ${e.name}`);
        }

      }

      navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

      try {

        _this_.audioContext = new (window.AudioContext || window.webkitAudioContext)({});

        console.log('默认采样率:', _this_.audioContext.sampleRate);

        _this_.sampleRate = _this_.audioContext.sampleRate;

        if (_this_.sampleRate < 44100) {
          _this_.$message.warning('当前设备支持采样率过低');
          throw new Error('当前设备支持采样率过低');
        }

        if (!_this_.audioContext) {
          _this_.$message.warning('webAudio Api 不支持当前浏览器');
          throw new Error('webAudio Api 不支持当前浏览器');
        }

      } catch (e) {
        if (!_this_.audioContext) {
          throw new Error('webAudio Api 不支持当前浏览器');
        }
      }

      if (navigator.mediaDevices && navigator.mediaDevices['getUserMedia']) {

        navigator.mediaDevices.getUserMedia({
          ...config
        }).then(function (stream) {
          getMediaSuccess(stream);
        }).catch(function (e) {
          getMediaFail(e);
        });

      } else if (navigator.getUserMedia) {

        navigator.getUserMedia({
          ...config
        }, function (stream) {
          getMediaSuccess(stream);
        }, function (e) {
          getMediaFail(e);
        });

      } else {

        _this_.audioContext && _this_.audioContext.close();

        if (navigator.userAgent.toLowerCase().match(/chrome/) && location.origin.indexOf('https://') < 0) {
          throw new Error('chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限');
        } else {
          throw new Error('无法获取浏览器录音功能,请升级浏览器或使用chrome');
        }

      }

    },
    endRecording() {

      if (this.stream && this.stream.getTracks) {

        this.stream.getTracks().forEach((track) => {
          return track.stop();
        })

        this.stream = null;

        this.audioContext && this.audioContext.close();

        this.scriptProcessor.disconnect();

        let blob = new Blob([new Uint8Array(this.createWavFile(this.mergeArray(this.mediaData)))], { type: 'audio/wav' });
        this.downloadFile(URL.createObjectURL(blob));

        this.mediaData = [];

        this.btn = {
          start: false,
          end: true
        }

      }

    },
    writeUTFBytes(view, offset, string) {
      for (var i = 0; i < string.length; i++) {
        view.setUint8(offset + i, string.charCodeAt(i));
      }
    },
    mergeArray(list) {
      let length = list.length * list[0].length,
          data = new Float32Array(length),
          offset = 0;

      for (var i = 0; i < list.length; i++) {
        data.set(list[i], offset);
        offset += list[i].length;
      }
      return data;
    },
    createWavFile(audioData) {
      let buffer = new ArrayBuffer(audioData.length * 2 + 44),
          view = new DataView(buffer);

      this.writeUTFBytes(view, 0, 'RIFF');
      view.setUint32(4, 36 + audioData.length * 2, true);
      this.writeUTFBytes(view, 8, 'WAVE');
      this.writeUTFBytes(view, 12, 'fmt ');
      view.setUint32(16, 16, true);
      view.setUint16(20, 1, true);
      view.setUint16(22, 1, true);
      view.setUint32(24, this.sampleRate, true);
      view.setUint32(28, this.sampleRate * 4, true);
      view.setUint16(32, 1 * 2, true);
      view.setUint16(34, 16, true);
      this.writeUTFBytes(view, 36, 'data');
      view.setUint32(40, audioData.length * 2, true);
      var length = audioData.length;
      var index = 44;
      var volume = 1;
      for (var i = 0; i < length; i++) {
        view.setInt16(index, audioData[i] * (0x7FFF * volume), true);
        index += 2;
      }
      return buffer;
    },
    downloadFile(url) {
      if ('download' in document.createElement('a')) {
        var elink = document.createElement('a');
        elink.style.display = 'none';
        elink.download = 'demo.wav';
        elink.href = url;
        document.body.appendChild(elink);
        elink.click();
        window.URL.revokeObjectURL(elink.href);
        document.body.removeChild(elink);
      }
    }
  },
  mounted() {}
})