以微软智能语音为例讲解WebRTC的小实践

236 阅读3分钟

我正在参加跨端技术专题征文活动,详情查看:juejin.cn/post/710123…

前两天看到了一篇写如何在uni-app项目中使用微软智能文字转语音服务,所以作为曾经在微软的项目组中实现过一套SDK的,想要讲一讲微软智能文字转语音服务的实现方案的细节。其实为什么微软的智能语音服务能在各端实现录音、播放而不需要其他的权限呢?其实问题就在与window对。曾经尝试在小程序中使用智能语音的时候,我一直以为是需要通过微信的授权系统才能进行录音和播放。但是在做调研的时候发现,能真正绕过或者并不是真的需要微信一系列授权的操作。

为什么各端能实现录音、播放功能

这个问题在于使用的方案,或者调用的方案。推荐使用iframe(web-view)嵌入,因为在小程序端、或者通过App壳运行H5页面的项目中,每一个iframe(web-view)都会有一个window对象,而现在的window对象都能直接调用设备的AudioRecord/AudioPlay。

window中的mediaDevices对象

MediaDevices 接口是由window提供的对象以访问连接媒体输入的设备,如照相机和麦克风,以及屏幕共享等。它可以使你取得任何硬件资源的媒体数据。

废话不多说,直接上代码:

// 用作承接录音收到的字节流
let audioCtx = new (window.AudioContext || window.webkitAudioContext)(); 
// createBufferSource()方法用于创建一个新的AudioBufferSourceNode接口,该接口可以通过AudioBuffer对象来播放音频数据
let source = audioCtx.createBufferSource();
// 为安全起见,最好先判断是否支持调用音视频设备,navigator.mediaDevices是window提供的只读属性,返回一个MeidiaDevices对象,该对象可提供对相机和麦克风等媒体输入设备的连接访问,也包括屏幕共享。
if (navigator.mediaDevices) {
  console.log('getUserMedia supported.');
  // constraints对象设置获取哪些权限
  // 可以通过此设置,增加相机的权限:{ audio: true, video: true }
  // 可以通过此设置,设置相机的分辨率:{ audio: true, video: { width: 1280, height: 720 } }
  var constraints = {audio: true};
  
  // 保存字节流
  var chunks = [];
  // getUserMedia方法通过constraints对象调用对应的设备权限,返回包含字节流的promise对象
  navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    // MediaRecorder是调用audio设备进行录音的接口,需要通过实例化后使用
    mediaRecorder = new MediaRecorder(stream);

    // 当结束录制之后的回调方法
    mediaRecorder.onstop = function(e) {
      console.log("data available after MediaRecorder.stop() called.");

      var clipName = prompt('Enter a name for your sound clip');

      var soundClips = document.getElementById('soundClips');
      var clipContainer = document.createElement('article');
      var clipLabel = document.createElement('p');
      var audio = document.createElement('audio');
      var deleteButton = document.createElement('button');

      clipContainer.classList.add('clip');
      audio.setAttribute('controls', '');
      deleteButton.innerHTML = "Delete";
      clipLabel.innerHTML = clipName;

      clipContainer.appendChild(audio);
      clipContainer.appendChild(clipLabel);
      clipContainer.appendChild(deleteButton);
      soundClips.appendChild(clipContainer);

      // source = audioCtx.createMediaStreamSource(stream);

      var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
      chunks = [];
      var audioURL = URL.createObjectURL(blob);
      audio.src = audioURL;

      deleteButton.onclick = function(e) {
        evtTgt = e.target;
        evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
      }
    }

    mediaRecorder.ondataavailable = function(e) {
      chunks.push(e.data);
    }
  })
  .catch(function(err) {
    console.log('The following error occurred: ' + err);
  })
}

MDN中关于constraints对象的详细设置 MDN中关于MediaRecorder接口的详细介绍

通过window中的AudioContext对象播放语音

上面已经说了如何进行录音,保存,但是并没有说明如何播放语音,下面就是播放语音的方法

// 创建二阶滤波器
var biquadFilter = audioCtx.createBiquadFilter();
// 定义字节流的解析算法
biquadFilter.type = "lowshelf";
// 定义滤波算法的频率
biquadFilter.frequency.value = 1000;
// 定义滤波算法的资源
biquadFilter.gain.value = chunks[0];

// 把AudioBufferSourceNode连接到gainNode
// gainNode连接到目的地, 所以我们可以播放
// 音乐并用鼠标调节音量
source.connect(biquadFilter);
biquadFilter.connect(audioCtx.destination);

MDN中关于createBiquadFilter的介绍