nodejs-使用vosk语音识别

2,423 阅读3分钟

最近需要在nodejs中接入vosk语音识别,下面让我们来试下示例

实现思路

  1. 在nodejs中实现vosk其实还是挺简单的,只要在nodejs中引入需要的依赖和模型,调用vosk的API即可
  2. 因为vosk底层使用python编写,还需要用到C++编译器。所以会有一些需要注意的地方详见下面注意事项

注意事项

  1. 下载c++编译
  2. 下载python3.8
  3. 卸载npm全局包windows-build-tools
  4. 本地添加node-gyp包/可用全局node-gyp包编译一次再npm(todo)
  5. node版本升级到18,不能低于14.21.3

1. 安装Visual Studio Community 2022

可以在微软商店下载,在软件中安装模块:使用C++的桌面开发

2.下载python3.8

这个也可以在微软商店下载,不可以下载python2.x

3. 初始化node项目,使用package.json

初始化的过程就不赘述了,需要安装的依赖有

{
  "name": "vosk-demo",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "busboy": "^1.6.0",
    "koa": "^2.14.2",
    "koa-bodyparser": "^4.4.0",
    "koa-router": "^12.0.0",
    "vosk": "^0.3.39"
  }
}

在这个例子里面入口文件为根路径的index.js

代码编写

index.js

import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import busboy from 'busboy';
import speechRecognition from './vosk.js';

const app = new Koa();
const router = new Router();
app.use(bodyParser());

router.post('/transcribe', async (ctx) => {
  /**
   * 将上传的音频流转换为文本
   * @returns {Promise<{code: number, text: string, msg: string}>}
   */
  const audioStreamToText = () => {
    const bb = busboy({ headers: ctx.req.headers });

    return new Promise((resolve, reject) => {
      bb.on('file', async (fieldname, stream, fileInfo) => {
        const { mimeType } = fileInfo;
        if (mimeType !== 'audio/wave') {
          reject({ code: 400, text: '', msg: '只支持wav格式的音频文件' });
          return;
        }

        const result = await speechRecognition(stream).catch((err) => {
          reject({ code: 500, text: '', msg: err });
        });

        resolve({ code: 200, text: result, msg: 'success' });
      });

      ctx.req.pipe(bb);
    });
  };

  const result = await audioStreamToText().catch((err) => {
    return err;
  });
  ctx.body = result;
});

app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);

里面引入了vosk.js文件 通过传入的wav文件流进行解析,返回解析的文本

mport { existsSync } from 'node:fs';
import vosk from 'vosk';
import { Readable } from 'node:stream';
import wav from 'wav';

const MODEL_PATH = './model/vosk-model-small-cn';

if (!existsSync(MODEL_PATH)) {
  console.log('模型不存在,请确认路径是否正确');
  process.exit();
}

/**
 * 语音识别
 * @param {stream} audioStream
 */
const speechRecognition = async (audioStream) => {
  return new Promise((resolve, reject) => {
    const wfReader = new wav.Reader();
    const wfReadable = new Readable().wrap(wfReader);

    wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
      if (audioFormat !== 1 || channels !== 1) {
        reject('只支持单声道wav格式的音频文件');
        return;
      }

      try {
        const model = new vosk.Model(MODEL_PATH);
        const rec = new vosk.Recognizer({
          model: model,
          sampleRate: sampleRate
        });
        for await (const data of wfReadable) {
          rec.acceptWaveform(data);
        }

        const result = rec.result();
        rec.free();
        resolve(result.text);
      } catch (ex) {
        reject('语音识别失败');
      }
    });

    wfReadable.on('error', (err) => {
      reject('文件读取失败');
    });

    audioStream.pipe(wfReader);
  });
};

export default speechRecognition;

需要注意的是,进行解析的文件需要为wav文件,并且为单通道的。如果没有对应文件的话,可以参考我的另一篇文章,将mp3转为wav文件。
(nodejs-转换视频格式):[juejin.cn/post/722505…]
在这个例子中需要下载模型文件,模型文件可在下方地址中进行下载,然后在上方的MODEL_PATH中引入
注意事项:

  • 如果遇到node-gyp的问题,可以在本地npm i node-gyp进行编译。也可以全局安装node-gyp依赖包,再通过node-typ命令进行解决

使用

postman中访问http://localhost:3000/transcribe POST请求,传入单声道的wav文件进行测试

参考资料

(vosk官网):[alphacephei.com/vosk/] (vosk github地址):[github.com/alphacep/vo…] (vosk 模型下载地址):[alphacephei.com/vosk/models]