語音識別—前端錄音傳給後臺語音識別

實現前端錄音,將音頻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傳給前端,顯示在標籤內就可以了:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章