WebAudio

软电话专利:

  • 一种基于数据分片的实时语音流传输和缓冲方案
  • SocketIO模拟电话通信的网络对讲机工具

方案一 audio标签 + mediaRecorder()

mediaRecorder文档

思路:

发送端

navigator.mediaDevices.getUserMedia()实现录音,将获取的stream音频流用于初始化MediaRecoder,并通过ondataavailable()事件实现获取一段段数据,存于队列中,将队列通过webSocket发送给后端

接收端

从后端处接收到数据后,设置mimeType转blob,再将blob通过window.URL.createObjectURL(blob)生成临时URL动态赋值给audio标签的src实现播放

缺陷

  • 没办法通过mimeType转成中台需要的PCM流

方案二 audio标签 + 第三方库(js-audio-recorder

第三方库文档:http://recorder.api.zhuyuntao.cn/

思路:

发送端

第三方库实现录音,录音后的PCM流转ArrayBuffer通过WebSocket发送给后端

接收端

从后端处接收到数据后加Wav文件头后转blob通过window.URL.createObjectURL(blob)创建临时url动态传输给audio标签的src实现播放

实现步骤

  • 直接new Recoder()获取录音实例
  • 通过第三方库的getPermission()获取录音权限
  • 通过第三方库的start()getPCMBlob()实现录音
  • socket.send(pcm)
  • 接收到数据后(ArrayBuffer格式),加44位Wav头信息,转成blob格式
  • window.URL.createObjectURL(blob)后生成临时URL
  • 动态赋值给audio标签的src实现播放

缺陷

  • iphone无法兼容audio的autoplay问题
  • 整段传输,耗时长

方案三 AudioContext无标签实现

文档

思路:

共同:在需要播放的场景前创建白噪音,引诱用户产生交互播放白噪音,通过iphone的安全策略

发送端

创建audioContext实例,用window.navigator.mediaDevices.getUserMedia()获取音频流,并利用录音流创建音频流节点,ScriptProcessorNode等各环节操作节点,在按下后,通过句柄引流录音流去执行对应节点,在ScriptProcessorNode中进行分片,压缩,转码,加文件头操作,并回调到前端对后端进行发送,松开后断开引流,

接收端

获取数据后进行解码,转码后连接至audioContext实例实现播放

实现步骤

  • 创建并维护audioContext单例

  • 在需要实现自动播放的场景前,通过audioContext实例的createBuffer方法创建白噪音并引诱用户交互,播放白噪音从而解决iphone的安全策略问题

  • window.navigator.mediaDevices.getUserMedia()方法获取音频流,并在初始化的阶段创建录音源节点MediaStreamAudioSourceNode和script处理器节点ScriptProcessorNode

  • 录音与否其实只是音频流的处理与否

    • 按下录音按钮时:
      • 录音源节点connect至script处理器节点
      • 创建分片的回调onaudioprocess(在创建script处理器节点时可设置,满多少bit为一个分片)
        • 通过getChannelData获取分片中每一声道的数据(Float32Array)
        • 由于不同设备的采样率不一样,需要按照需要的采样率进行压缩
        • 将压缩好的Float32Array进行处理变成PCM流(arrayBuffer)
        • 作为参数调用回调,将pcm传至前端通过socket发送给后端
      • script处理器节点connect至audioContext实例
    • 松开录音按钮:
      • script处理器节点disconnectaudioContext实例
      • 录音源节点disconnectscript处理器节点
      • 重新初始化参数
  • 接受到后端转发的数据(arrayBuffer)后,decodeAudioData实现转码(audioBuffer)

  • 通过createBufferSource创建缓冲区,将audioBuffer填充至缓冲区

  • 将缓冲区链接audioContext实例实现播放

方案四 WebRTC

未实现,待补充


零碎知识

Wav的44位文件头

PCM(Pulse-code modulation | 脉冲编码调制)

pcm就是把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输。脉冲编码调制就是对模拟信号先抽样,再对样值幅度量化,编码的过程。

PCM三要素

  • 声道(能支持不同发生的音响的个数)(channel)
    • 单声道:Mono,单喇叭或者两个喇叭输出同一个声道的声音(在线 ASR 录音用的是单声道)
    • 双声道:Stereo,分左声道和右声道,更加有空间效果
  • 采样率(sampleRate)
    • 每秒对声音进行采集的次数,同样也是所得的数字信号的每秒样本数,单位为 Hz。
  • 采样位数(bitDepth)
    • 采样后,将采样的单个样本进行量化,用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。
    • n-bit 指的是声音的强度(振幅)被均分为 2^n 级,常用的有 8bit(1字节)、16bit(2字节)、32bit(4字节)

ArrayBuffer

  • ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区(预分配内存)。

  • 单位是字节,它是一个字节数组,通常在其他语言中称为 byte array

  • 不能直接操作 ArrayBuffer 的内容,只能通过 DataView 或者定型数组对象操作

  • ArrayBuffer 分配的内存不能超过 Number.MAX_SAFE_INTEGER(2^53 - 1) 字节

AudioBuffer

  • AudioBuffer接口表示存在内存里的一段短小的音频资源。
  • 这些类型对象被设计来控制小音频片段,往往短于45秒
  • 缓存区(buffer)包含以下数据:不间断的IEEE75432位线性PCM,从-1到1的范围额定
  • 如果AudioBuffer有不同的频道,他们通常被保存在独立的缓存区

字节序

字节序,或字节顺序(”Endian”、”endianness” 或 “byte-order”),描述了计算机如何组织字节,组成对应的数字。

每一个字节可以存储一个 8 位(bit)的数字(0x00-0xff),存储更大数字需要多个字节,现在大部分需占用多字节的数字排列方式是 little-endian(低位字节排放在内存中低地址端,高字节排放在内存的高地址端),与 big-endian 相反。

举例:用不同字节序存储数字 0x12345678(即十进制中的 305 419 896)

  • little-endian:0x78 0x56 0x34 0x12

  • big-endian:0x12 0x34 0x56 0x78