语音识别前端录音传给后台语音识别

12次阅读

共计 4970 个字符,预计需要花费 13 分钟才能阅读完成。

实现前端录音,将音频 blob 传给服务器,然后在服务器端使用百度 AI 语音识别将结果返回给前端

上一篇文章是将百度 AI 语音识别 Nodejs SDK 版的进行了一遍演示加识别结果返回给前端显示,这里是完整的进行前端录音,然后将压缩后的音频对象 Blob 传给服务器,在服务端使用百度 AI 语音识别,最后将识别结果返回给前端进行显示。

本篇调用的是第三方库 Recorder.js,如何调用该库捕获HTML5 中的 WAV 音频并将其上传到服务器或者本地下载,可以查看这篇博客,不过它讲解的是上传到 PHP 服务端,这里我改成了基于 Node 搭建的 Websocket 服务器。
这是本篇博客的语音识别结果:

百度语音识别

查看文档知道了我想要的信息,如果想要实现实时语音识别、长时间段的语音、唤醒词功能、语义解析功能,需要使用 AndroidIOS SDK 或者 Linux C++ SDK 版本,而我使用的 Nodejs SDK 是不支持的。

1、规格参数要求

  • 语音时长上线为 60s,超出讲返回错误
  • 原始录音文件为 pcmwav 或者 amr 格式,不区分大小写,推荐使用pcm
  • 录音采样率为 16000,声道为单通道
  • 支持普通话、英文、粤语、四川话
  • 项目结构

    调用百度 AI 平台语音识别的Nodejs SDK,查看文档快速入门,可以查看如何调用。

    首先将 nodejs-sdk 下载下来,下载后将目录里的 speech 文件夹拷贝到你的项目文件夹中,其中 assets 是存放录音音频的地方,然后进入 node 文件夹下的位置进行安装依赖包:

    npm install

    我的项目文件夹目录如下:

    audio_asr_baidu
    ├─ package-lock.json
    └─ speech
           ├─ .gitignore
           ├─ assets
           │    ├─ 16k_test.pcm
           │    └─ recorder.wav
           ├─ cpp
           │    ├─ .gitignore
           │    ├─ README.md
           │    ├─ build.sh
           │    └─ main.cpp
           └─ node
                  ├─ .gitignore
                  ├─ README.md
                  ├─ index.html
                  ├─ main.js
                  ├─ node_modules
                  ├─ package-lock.json
                  ├─ package.json
                  └─ style.css

    然后在 node 文件夹里的 index.html 是我的客户端文件,main.js是我的服务端文件。

搭建 Websocket 服务器

main.js 文件里搭建 websocket 服务器,首先安装相关依赖模块:

npm i ws -S

然后搭建:

let Server = require('ws').Server;
const wss = new Server({port: 9001})
// 连接服务器
wss.on('connection', ws => {console.log('server connected');

    })
    ws.on('error', error => {console.log('Error:' + error);

    })
    ws.on('close', () => {console.log('Websocket is closed');
    })
})
// 断开连接
wss.on('disconnection', ws => {
    ws.on('message', msg => {console.log('server recived msg:' + msg);
    })
})

然后在 index.html 中:

let ws = new WebSocket('ws://localhost:9001');
ws.onopen = e => {console.log('Connection to server opened');
}

启动服务:

node main.js

就可以在控制台看见这样的打印信息:

// 客户端的打印信息:Connection to server opened

// 服务端的打印信息:server connected

前端录音

客户端实现录音之后,将压缩后的音频对象 Blob 传给服务器:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Simple Recorder.js demo with record, stop and pause</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
    <div id="controls">
        <button id="recordButton">Record</button>
        <button id="stopButton" disabled>Stop</button>
    </div>
    <p id="out-txt">You said:</p>
    <h3>Recordings</h3>
    <ol id="recordingsList"></ol>
    <script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
// 连接服务器
let ws = new WebSocket('ws://localhost:9001');
ws.onopen = e => {console.log('Connection to server opened');

}
URL = window.URL || window.webkitURL;

var gumStream; //stream from getUserMedia()
var rec; //Recorder.js object
var input; //MediaStreamAudioSourceNode 


var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext

var recordButton = document.getElementById("recordButton");
var stopButton = document.getElementById("stopButton");


recordButton.addEventListener("click", startRecording);
stopButton.addEventListener("click", stopRecording);
// 录音
function startRecording() {console.log("recordButton clicked");

    var constraints = {
        audio: true,
        video: false
    }

    recordButton.disabled = true;
    stopButton.disabled = false;

    // 获取录音权限 然后开始录音
    navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
        audioContext = new AudioContext();

        gumStream = stream;

        input = audioContext.createMediaStreamSource(stream);

        rec = new Recorder(input, {numChannels: 1 // 单声道})

        // 开始录音
        rec.record()

        console.log("Recording started");

    }).catch(function(err) {
        recordButton.disabled = false;
        stopButton.disabled = true;
    });
}

// 停止录音
function stopRecording() {console.log("stopButton clicked");

    stopButton.disabled = true;
    recordButton.disabled = false;

    rec.stop();

    gumStream.getAudioTracks()[0].stop();

    // 创建一个 blob 对象让它以 wav 格式下载
    rec.exportWAV(createDownloadLink);
}
// 接收服务端发的消息
ws.onmessage = e => {console.log(e.data);
        setTimeout(() => {document.getElementById("out-txt").innerHTML += e.data
        }, 3000);
    }
    // 创建下载链接
function createDownloadLink(blob) {console.log(blob);
    ws.send(blob);
    var url = URL.createObjectURL(blob);
    var au = document.createElement('audio');
    var li = document.createElement('li');
    var link = document.createElement('a');

    var filename = new Date().toISOString();
    au.controls = true;
    au.src = url;
    link.href = url;
    link.download = filename + ".wav";
    link.innerHTML = "Save to disk";
    li.appendChild(au);
    li.appendChild(document.createTextNode(filename + ".wav"))
    li.appendChild(link);
}

这样,在该页面会创建下载连接,并以录音日期为文件名,可以选择下载,同时也会将音频对象传到服务器。

语音识别

因为前端通过音频流文件上传到后台后,不再是保存为 wav 格式的音频,而是处理流的形式转为二进制数组,直接调用百度语音识别 SDK 方法,即可返回识别结果,不必编码后发给后端,后端然后再解码。


let AipSpeech = require("baidu-aip-sdk").speech;
let Server = require('ws').Server;
const wss = new Server({port: 9001})

let resTxt;
wss.on('connection', ws => {console.log('server connected');
    ws.on('message', data => {console.log('server recived audio blob');
        // 务必替换百度云控制台中新建百度语音应用的 Api Key 和 Secret Key
        let client = new AipSpeech(0, 'Api Key', 'Secret Key');
        let voiceBase64 = new Buffer(data);
        client.recognize(voiceBase64, 'wav', 16000).then(function(result) {console.log('语音识别本地音频文件结果:' + JSON.stringify(result));
            resTxt = JSON.parse(JSON.stringify(result));
        }, function(err) {console.log(err);
        });
    })
    // 将结果传给前端
    ws.send(resTxt);
    ws.on('error', error => {console.log('Error:' + error);
    })
    ws.on('close', () => {console.log('Websocket is closed');
    })
})
wss.on('disconnection', ws => {
    ws.on('message', msg => {console.log('server recived msg:' + msg);
    })
})

这是前端说话录音传给后台语音识别的结果,将结果使用 websocket 传给前端,显示在标签内就可以了:

正文完
 0