# WebASR **Repository Path**: Haskely/WebASR ## Basic Information - **Project Name**: WebASR - **Description**: 网页版的语音识别项目 - **Primary Language**: JavaScript - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 146 - **Forks**: 47 - **Created**: 2020-10-24 - **Last Updated**: 2025-05-30 ## Categories & Tags **Categories**: tts **Tags**: None ## README # WebASR 网页版且离线的语音识别项目 * v0.1.0版完成!工程方面的基础功能全部实现,接下来就是研究模型了~ * 查看DEMO:https://haskely.github.io/WebASR * (国内访问 https://haskely.gitee.io/webasr ) ![WebASR网页截图](https://gitee.com/Haskely/WebASR/raw/master/screenshot/WebASR.png) [TOC] ## Introduction - 介绍 * 基于TensorFlowJS和WebAudioAPI的网页版的中文语音识别玩具项目。 * 纯手打Javascript + html,没有css,没有nodejs,没有前端框架,感受原始气息。 * 零服务器需要:波形绘图,短时傅里叶频谱计算,模型识别工作等完全在本地进行! * 支持实时录音或上传音频。 * 代码的可扩展性应该不错。 ## Usage - 用法 * 用 **最新版Chrome内核** 的浏览器(否则无法正常使用)打开 https://haskely.github.io/WebASR (或者国内访问 https://haskely.gitee.io/webasr ) * 网页上的按钮都点一点,就会了。 ## Installation - 自己部署 * 直接把项目所有文件放到任意可访问的路径上即可,一个静态网页而已。 * (保持项目中文件相对路径不变) * (因为浏览器的安全措施,需要基于http服务打开index.html,不能直接本地打开。参考run_server.bat 中的命令) ## Develop - 开发 ### 一、简单开发 #### 主要参数配置 - 打开本项目根目录下**main.js**文件,找到`//基础配置`注释处,参考相关注释配置参数。 ```javascript //基础配置 const numberOfChannels = 1, bufferSize = 256, total_duration = 10; const sampleRate = 8000, fft_s = 0.032, hop_s = 0.008; const ModelDir = './ASR/Model/Network/tensorflowjs/tfjsModel/tfjs_mobilev3small_thchs30/'; const minPinYinN = 10; const useWebWorker = true; ... //配置完毕 ``` - 假如你的音频是立体声的:有左右两个声道,那么这个音频的**numberOfChannels 音频声道数**为2。 - 声音在物理上是空气的震动,所以可以用一段波形来记录一段声音。计算机中每个声道的音频数据本质上是一个数组,数组的元素是数字,其大小代表某时刻的振动幅度,元素不同的位置(下标)代表不同的时刻。那么现实中一秒钟的时长对应着这个数组多少个元素呢,这个数量就是**sampleRate 音频采样率**,一般为8000,也就是8000个元素连起来记录的是1秒钟的音频,也就是一秒钟采样率8000次。 - 程序处理音频数据数组时并非一个元素一个元素地处理,而是一小段一小段的处理,那么是多大的一小段呢?这个一小段包含的元素数量就是**bufferSize 以采样帧为单位的缓冲区大小**,一般为256,也就是一次处理256个元素,如果这段音频采样率为8000的话,那么256个元素代表的也就是256/8000 = 0.032 秒,计算机一次处理0.032 秒长的一小段音频数据。 - 如果想将音频处理成频谱图还需要给定**fft_s (一个短时傅里叶变换的窗长)**和**hop_s (短时傅里叶变换窗之间间隔长)**。hop_s一般取成fft_s的1/4,也就是两个窗口直间会有重叠,重叠3/4区域,这样过渡更平滑。而fft_s的不同的设置会影响频谱图的时间分辨率和频率分辨率,fft_s越小,时间分辨率越高,频率分辨率越低;fft_s越大,时间分辨率越低,频率分辨率越高。这两个分辨率不可兼得,需要按需求进行平衡。关于这两个参数的详细含义,请搜索“短时傅里叶变换”。 > * numberOfChannels 音频声道数。整数,默认为1。支持的值最多为32。 > > * bufferSize 以采样帧为单位的缓冲区大小,单位为 帧。必须是以下值之一:0、256、512、1024、2048、4096、8192、16384。为0时系统自动选择。这时你的刷新帧率就是sampleRate/bufferSize。 > > * total_duration 网页中各种音频绘制的时间总长度,单位为秒。 > > --- > > * sampleRate 音频采样率 可取 8000、16000、32000、48000 之一。单位为 帧/秒。 > > * fft_s 一个短时傅里叶变换的窗长,单位为秒 > > * hop_s 短时傅里叶变换窗之间间隔长,单位为秒 > > 注意:以上三个参数应该与下面ModelDir文件夹下feature.json中的参数一致,否则模型将加载失败。 > > --- > > * ModelDir TensorflowJS 模型文件夹,该文件夹下应该存在一个model.json,一个feature.json,若干个.bin文件。 >* minPinYinN 正整数,流式模型推断音频最小的长度;如果为4,则一次推断输出4个拼音片段,并保留中间两个;下一次推断与这次推断的覆盖长度为4/2 = 2 ,这样,下次推断的输出保留中间两个,刚好与上一次输出保留的连接上。 > * useWebWorker 是否使用异步进行模型推断:若为false,则模型推断与音频刷新同步进行,大概率导致音频卡顿,但是保证实时率;若为true,则推断异步进行,不会阻塞音频流逝,但推断输出一般会有积压延迟(一开始会很严重,初始积压接近8秒,但后面就很快跟上了,最终稳定到时延0.5-2秒)。 见`//配置完毕`下面两行 **AudioFlow** 实例化地方可进行更详细的配置: > 比如不希望扬声器进行输出,将`new AudioFlow(..., 'sound');`改为`new AudioFlow(..., null);`即可。 > 配置方法的详细说明见各个类的注释 。 #### 模型部署 - 使用 **DeepASR** 项目(暂未开源)中的Model实例的convert2tfjs方法会自动生成**TensorflowJS Graph Model**格式的模型。这是一个文件夹,其目录结构一般为: ``` TFJSModelDir │ feature.json │ group1-shard1of4.bin │ group1-shard2of4.bin │ group1-shard3of4.bin │ group1-shard4of4.bin │ model.json ``` - 将该文件夹中文件复制到本项目任意子文件夹下,并将该子文件夹相对路径复制到**main.js**中`//基础配置`注释处的**ModelDir**变量中即可。 > 例如我把**TensorflowJS Graph Model**格式的模型文件夹"TFJSModel"复制到了/WebASR/Models/TFJSModel,其中WebASR是本项目根目录。 > > 则将`const ModelDir = 'xxx'` 修改为 > > ```javascript > //基础配置 > ... > const ModelDir = './Models/TFJSModel/'; > ... > ``` > > 即可。 > > 其实直接研究TensorflowJS model的输入输出格式以及本项目源代码,以及TensorflowJS官网的文档,可以自己生成需要的tfjs 模型进行替换。 ### 二、深入API 通过阅读 **main.js** 可知,里面除了处理html页面元素的代码外,其他的就是调用了 “**AudioFlow**” 类实例的api。 而“**AudioFlow**” 类的父类是“**AudioProcesser**”类。按顺序介绍: #### AudioProcesser 这个类的作用是,从一个或多个音频输入源获取原始的音频波形数据数组进行任意代码处理。它就是对[WebAudioAPI](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API)的一层封装,更准确的是,对其[`ScriptProcessorNode`](https://developer.mozilla.org/zh-CN/docs/Web/API/ScriptProcessorNode)的一层封装。 - 通过构造函数**constructor**构造AudioProcesser的实例时就指定好处理音频数据的代码。 - 通过AudioProcesser实例的**open**、**start**、**stop**、**close**方法控制数据处理流程的初始化、开始、暂停、停止并释放内存。 - 通过**addAudioSource**、**delAudioSource**实现动态加载音频源和删除音频源。 以上这些方法的用法和作用在代码中均有详细注释。 样例: ```javascript import { AudioFlowProcesser } from "../../Audio/AudioFlowProcesser.js"; const audioFlowProcesser = new AudioFlowProcesser( null, // audioSource = null, 暂不指定音频源。 'sound', //audioDestination = 'sound', 音频会输出到扬声器 8000, //sampleRate = 8000, 指定采样率为8000 undefined, //latencyHint = undefined, 不指定latencyHint,让系统自动选择 256, //ScriptNode_bufferSize = 256, 缓冲区大小为256(采样帧),结合上面指定采样率为8000,得到音频刷新帧率为8000/256 = 31.25 FPS。 1, //ScriptNode_numberOfInputChannels = 1, 声道数为1。 processAudioData = (audioData) => { const audioClipInfo = { "音频采样率":audioData.sampleRate , "音频数据":audioData.channels , "音频末尾时间戳":audioData.audioEndTime , "音频声道数":audioData.numberOfChannels , "音频帧数":audioData.sampleLength , "音频时长":audioData.timeLength , "音频开头时间戳":audioData.audioStartTime }; console.log(`收到音频小片段:${audioClipInfo}`); }, ); async function addMicrophone(){ const recordStream = await navigator.mediaDevices.getUserMedia( {audio: true}); //开启麦克风 audioFlowProcesser.addAudioSource(recordStream); //audioFlowProcesser加入麦克风音频源 }; audioFlowProcesser.open(); audioFlowProcesser.start(); addMicrophone(); ``` > 构造函数注释: > > ```javascript > class AudioFlowProcesser { > constructor( > audioSource = null, > audioDestination = 'sound', > sampleRate = undefined, > latencyHint = undefined, > ScriptNode_bufferSize = 256, > ScriptNode_numberOfInputChannels = 1, > processAudioData = (audioData) => { }, > ){...}; > ... > }; > ``` > > **AudioFlowProcesser** > > **@constructor** > > - @param {AudioNode|AudioBuffer|Blob|MediaElement|MediaStream|null} audioSource - 音频源对象,具体可为以下五种类型里的一种: > 1. AudioNode 比如 > > - a.OscillatorNode接口代表一种随时间变化的波形,比如正弦波形或三角波形。类型是AudioNode,功能是音频处理模块, 可以产生指定频率的波形。 > - b.AudioBufferSourceNode表示由内存音频数据组成的音频源,音频数据存储在AudioBuffer中。这是一个作为音频源的AudioNode。注意,需要手动调用AudioBufferSourceNode.start()开始音频处理。 > - c.MediaElementAudioSourceNode接口表示由HTML5