實現前端錄音,將音頻blob傳給服務器,而後在服務器端使用百度AI語音識別將結果返回給前端。更多內容請關注 GitHub
上一篇文章是將百度AI語音識別Nodejs SDK
版的進行了一遍演示加識別結果返回給前端顯示,這裏是完整的進行前端錄音,而後將壓縮後的音頻對象Blob
傳給服務器,在服務端使用百度AI語音識別,最後將識別結果返回給前端進行顯示。javascript
本篇調用的是第三方庫Recorder.js
,如何調用該庫捕獲HTML5
中的WAV
音頻並將其上傳到服務器或者本地下載,能夠查看這篇博客,不過它講解的是上傳到PHP
服務端,這裏我改爲了基於Node
搭建的Websocket
服務器。
這是本篇博客的語音識別結果:
css
查看文檔知道了我想要的信息,若是想要實現實時語音識別、長時間段的語音、喚醒詞功能、語義解析功能,須要使用Android
、IOS SDK
或者Linux C++ SDK
版本,而我使用的Nodejs SDK
是不支持的。html
pcm
、wav
或者amr
格式,不區分大小寫,推薦使用pcm
調用百度AI平臺語音識別的Nodejs SDK
,查看文檔快速入門,能夠查看如何調用。前端
首先將nodejs-sdk下載下來,下載後將目錄裏的speech
文件夾拷貝到你的項目文件夾中,其中assets
是存放錄音音頻的地方,而後進入node
文件夾下的位置進行安裝依賴包:html5
npm install
個人項目文件夾目錄以下:java
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
是個人服務端文件。node
Websocket
服務器在main.js
文件裏搭建websocket
服務器,首先安裝相關依賴模塊:git
npm i ws -S
而後搭建:github
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
中:web
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
傳給前端,顯示在標籤內就能夠了: