前端AI語音方面的實現

前言

今天我們來點有意思的,AI語音轉換!當我們遇到語音轉換的需求感覺,哇,好難啊,這怎麼開發啊。其實這是很簡單的,今天筆者就來給大家演示一下我們用js實現語音轉換功能!

首先我們先來做倆個按鈕,一個開始按鈕一個結束並開始轉換語音的按鈕:
在這裏插入圖片描述

第一章:語音識別

一、開始寫代碼

寫html和簡單的css

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {margin: 0;padding: 0;}
        body {
            display: flex;
            justify-content: space-around;
            align-items: center;
        }
        .startBtn, .endBtn {
            display: block;
            width: 150px;
            height: 120px;
            border: 2px solid;
            margin: 10px;
        }
    </style>
</head>
<body>
    <button onclick="start()" class="startBtn">開始說話</button>
    <br />
    <button onclick="end()" class="endBtn">結束並轉換普通話</button>
</body>
</html>

然後我們在控制檯搞一些調試信息,並將最後的轉換結果alert出來!

js代碼

<script>
    var recognition = new webkitSpeechRecognition() || new SpeechRecognition();
    recognition.lang = 'cmn-Hans-CN'; //定義普通話 (中國大陸)
    function start() {
        console.log('start')
        // 開啓
        recognition.start();
    }
    function end() {
        console.log('end')
        // 停止
        recognition.stop();
    }
    // 當調用recognition的stop的時候會觸發此對象的onresult事件,然後我們在這裏獲取我們的轉換結果。
    recognition.onresult = function(event) {
        alert(event.results[0][0].transcript);
    }
</script>

二、知識點講解

我們可以把這個SpeechRecognition內置對象console出來看看他都有什麼屬性和方法
在這裏插入圖片描述

屬性介紹:

屬性 作用 參數
grammars 此屬性存儲了SpeechGrammar對象的集合,這些對象表示對此識別有效的語法。 參考地址
lang 此屬性將使用有效的BCP 47語言標籤設置請求識別的語言。如果沒有設置它仍然未設置爲腳本獲得,但將默認使用該語言的HTML文檔的根元素和相關層次的。當輸入請求打開與識別服務的連接時,將計算並使用此默認值。 與html的lang屬性一樣,都是枚舉
continuous 此屬性設置爲false時,用戶代理必須響應於開始識別(例如,交互的單個轉彎模式)返回不超過一個最終結果。當其設置爲true時,用戶代理必須響應於開始的識別(例如聽寫),返回代表多個連續識別的零個或多個最終結果。默認值必須爲false。此屬性的設置不會影響中期結果。 boolean
interimResults 控制是否返回中期結果,設置爲true時,應返回中期結果。設置爲false時,不得返回中期結果。默認值必須爲false。此屬性不會影響最終結果。 boolean
maxAlternatives 此屬性設置SpeechRecognitionAlternative每個結果的最大數量。默認值1。 其值在[0,18446744073709551615]範圍內

方法介紹:

方法 介紹
start() 調用start方法時,它表示Web應用程序希望開始識別的時間。當語音輸入通過輸入媒體流實時流式傳輸時,此開始呼叫表示該服務必須開始收聽並嘗試匹配與此請求關聯的語法的時間。一旦系統成功偵聽識別,用戶代理必須引發一個開始事件。如果在已經啓動的對象上調用了start方法(也就是說,先前已經調用過start,並且沒有在該對象上引發錯誤或結束事件),則用戶代理必須拋出“ InvalidStateError” DOMException並忽略該調用。
stop() stop方法表示對識別服務的一條指令,該指令停止聽更多的音頻,並嘗試僅使用已爲該識別接收的音頻來返回結果。stop方法的典型用法可能是用於Web應用程序,其中最終用戶正在進行端點指向,類似於對講機。最終用戶可能會按住空格鍵與系統對話,而在按下空格鍵時會發生開始調用,並且在釋放空格鍵時會調用stop方法,以確保系統不再監聽用戶。調用stop方法後,語音服務不得收集其他音頻,也不得繼續收聽用戶的聲音。語音服務必須嘗試根據已爲該識別而收集的音頻返回識別結果(或不匹配)。如果在已經停止或正在停止的對象上調用stop方法(即從未在其上調用start),發生了end或error事件,或者先前已調用stop),則用戶代理必須忽略該調用。
abort() 中止方法是一種請求,要求立即停止偵聽並停止識別,並且不返回任何信息,但系統已完成。調用中止方法時,語音服務必須停止識別。一旦語音服務不再連接,用戶代理必須引發結束事件。如果在已經停止或正在中止的對象上調用了中止方法(也就是說,從未在其上調用start,在其上引發了end或error事件,或者先前已在其上調用過中止),則用戶代理必須忽略電話。

事件介紹

事件 介紹
audiostart 用戶代理開始捕獲音頻時觸發
soundstart 當檢測到某種聲音(可能是語音)時觸發。這必須以低延遲觸發,例如通過使用客戶端能量檢測器。該audiostart事件必須已在soundstart事件之前發射。
speechstart 當將用於語音識別的語音開始時觸發。該audiostart事件必須已在speechstart事件之前發射。
speechend 當將用於語音識別的語音結束時觸發。該speechstart事件必須始終被speechend之前解僱
soundend 當不再檢測到某些聲音時觸發。這必須以低延遲觸發,例如通過使用客戶端能量檢測器。該soundstart事件必須始終被soundend之前解僱。
audioend 用戶代理完成音頻捕獲後觸發。該audiostart事件必須始終被audioend之前解僱。
result 當語音識別器返回結果時觸發。該事件必須使用SpeechRecognitionEvent接口。該audiostart事件必須已在結果事件之前發射。
nomatch 當語音識別器返回沒有識別假設達到或超過置信度閾值的最終結果時觸發。該事件必須使用SpeechRecognitionEvent接口。results事件中的屬性可能包含低於置信度閾值或爲空的語音識別結果。該audiostart事件必須已在NOMATCH事件之前發射。
error 發生語音識別錯誤時觸發。該事件必須使用SpeechRecognitionErrorEvent接口
start 當識別服務開始收聽音頻以進行識別時觸發
end 服務斷開連接時觸發。無論會話結束的原因如何,都必須始終在會話結束時生成事件。

三、兼容性介紹

非常遺憾的告訴大家這個api的兼容性並不好,一般筆者只會在chrome上面使用它,如果不是chrome瀏覽器的話那麼筆者就會調用後端的接口。比如用java或者python等語言實現。
在這裏插入圖片描述

很多朋友很奇怪,既然兼容性不好爲什麼要用它,因爲調用後端接口勢必就會給服務器帶來一定的壓力,而且還和網速等等有很大的關係。我們前端自己能實現的話肯定是優先我們自身實現。

兼容寫法

<script>
    function start() {
        voiceConversion('start')
    }
    function end() {
        voiceConversion('end')
    }
    function voiceConversion(type) {
        console.log(type);
        try {
            let recognition = new webkitSpeechRecognition() || new SpeechRecognition();
            console.log(recognition)
            recognition.lang = 'cmn-Hans-CN'; //普通話 (中國大陸)
            if (type === 'start') {
                // 開啓
                recognition.start();
            } else {
                // 停止
                recognition.stop();
            }
            recognition.onresult = function(event) {
                alert(event.results[0][0].transcript);
            }
        } catch (e) {
            console.log('調用後端接口');
        }
    }
</script>

四、實戰演習

我們接下來給大家展示一下當用戶點擊錄音的時候實時的識別出用戶說的話並展示出來:

全部代碼:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {margin: 0;padding: 0;}
        .wrapBox {
            display: flex;
            justify-content: space-around;
            align-items: center;
        }
        .startBtn, .endBtn {
            display: block;
            width: 150px;
            height: 120px;
            border: 2px solid;
            margin: 10px;
        }
    </style>
</head>
<body>
    <div class="wrapBox">
        <button onclick="start()" class="startBtn">開始說話</button>
        <br />
        <button onclick="end()" class="endBtn">結束並轉換普通話</button>
    </div>
    <div id="resultText"></div>
    <script>
        const resultTextDiv = document.querySelector('#resultText');
        function start() {
            voiceConversion('start')
        }
        function end() {
            voiceConversion('end')
        }
        function voiceConversion(type) {
            console.log(type);
            try {
                let recognition = new webkitSpeechRecognition() || new SpeechRecognition();
                recognition.lang = 'cmn-Hans-CN'; //普通話 (中國大陸)
                recognition.interimResults = true;
                if (type === 'start') {
                    // 開啓
                    recognition.start();
                } else {
                    // 停止
                    recognition.stop();
                }
                recognition.onresult = function(event) {
                    const text = event.results[0][0].transcript;
                    resultTextDiv.innerHTML = text;
                }
            } catch (e) {
                console.log('調用後端接口');
            }
        }
    </script>
</body>
</html>

結尾:

這只是我們開始的一個最基本的需求,我們還需要加入獲取聲音大小的需求以及復讀的功能

第二章:判斷語音聲音大小做個小樣式

一:根據第一個需求代碼進行拓展

我們需要在裏面追加一個html標籤,用來展示我們的聲音大小:

<span id="volumeBox"></span>

然後定義一個檢測聲音大小的函數:

// 獲取我們的音量盒子元素
const mystatus = document.getElementById('volumeBox');
let mediaStreamSource = null,
    scriptProcessor = null;
// 開始檢測音量大小的函數
function beginDetect() {
    let audioContext = new (window.AudioContext || window.webkitAudioContext)();

    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        // 獲取用戶的 media 信息
        navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
            // 將麥克風的聲音輸入這個對象
            mediaStreamSource = audioContext.createMediaStreamSource(stream);
            // 創建一個音頻分析對象,採樣的緩衝區大小爲4096,輸入和輸出都是單聲道
            scriptProcessor = audioContext.createScriptProcessor(4096,1,1);
            // 將該分析對象與麥克風音頻進行連接
            mediaStreamSource.connect(scriptProcessor);
            // 解決 Chrome 自身的 bug
            scriptProcessor.connect(audioContext.destination);

            // 開始處理音頻
            scriptProcessor.onaudioprocess = (e) => {
                // 獲得緩衝區的輸入音頻,轉換爲包含了PCM通道數據的32位浮點數組
                const buffer = e.inputBuffer.getChannelData(0);
                // 獲取緩衝區中最大的音量值
                const maxVal = Math.max.apply(Math, buffer);
                // 顯示音量值
                mystatus.innerHTML = `您的音量值:${ Math.round(maxVal * 100)}`;
            };
        }).catch((error) => {
            mystatus.innerHTML = `獲取音頻時好像出了點問題。${error}`;
        })
    } else {
        mystatus.innerHTML = '您的瀏覽器不支持識別聲音大小';
    }
};
// 關閉檢測音量大小
function stopDetect() {
    scriptProcessor.disconnect 
    	? scriptProcessor.disconnect() 
    	: console.log('無需關閉任何');
}

然後我們分別在開始說話和結束說話的時候調用這倆個函數:

// 在voiceConversion函數裏進行局部追加代碼
if (type === 'start') {
    // 開啓
    beginDetect();
    recognition.start();
} else {
    // 停止
    stopDetect();
    recognition.stop();
}

我們可以根據maxValue進行設計一些其他的樣式。而不是像筆者發佈在這裏僅僅展示了字體而已。
比如【類似這種】:在這裏插入圖片描述

有想要這種類似的樣式的可以私聊筆者,由於本博文內容過多,代碼就不放出來了哈~。

二、知識點整理

mediaDevices

方法 介紹
MediaDevices.enumerateDevices() 獲取有關係統中可用的媒體輸入和輸出的一系列信息
getSupportedConstraints() 返回一個對象,該對象符合MediaTrackSupportedConstraints指示MediaStreamTrack接口上支持哪些可約束屬性的信息。
getDisplayMedia() 提示用戶選擇一個顯示器或顯示器的一部分(例如窗口)以捕獲MediaStream爲共享或記錄目的。返回解析爲的Promise MediaStream。
MediaDevices.getUserMedia() 在用戶通過提示允許的情況下,打開系統上的相機或屏幕共享和/或麥克風,並提供 MediaStream 包含視頻軌道和/或音頻軌道的輸入。

AudioContext

該AudioContext接口表示由鏈接在一起的音頻模塊構建的音頻處理圖,每個音頻模塊由表示AudioNode。音頻上下文既控制其包含的節點的創建,也控制音頻處理或解碼的執行。您需要先創建一個,AudioContext然後再執行其他操作,因爲所有事情都在上下文中發生。

方法 介紹
AudioContext.close() 關閉音頻上下文,釋放它使用的所有系統音頻資源。
AudioContext.createMediaElementSource() 創建與MediaElementAudioSourceNode相關聯HTMLMediaElement。這可用於播放和操作來自或元素的音頻。
AudioContext.createMediaStreamSource() 創建一個MediaStreamAudioSourceNode與MediaStream表示相關聯的音頻流,該音頻流可能來自本地計算機麥克風或其他來源。
AudioContext.createMediaStreamDestination() 創建MediaStreamAudioDestinationNode與MediaStream表示一個音頻流的關聯,該音頻流可以存儲在本地文件中或發送到另一臺計算機。
AudioContext.createMediaStreamTrackSource() 創建MediaStreamTrackAudioSourceNode與一個MediaStream表示媒體流軌道的關聯。
AudioContext.getOutputTimestamp() 返回一個新AudioTimestamp對象,其中包含兩個與當前音頻上下文有關的音頻時間戳值
AudioContext.resume() 在以前已暫停/暫停的音頻上下文中恢復時間的進展。
AudioContext.suspend() 掛起音頻環境中的時間,暫時停止音頻硬件訪問並減少進程中的CPU /電池使用量。
AudioContext.createScriptProcessor() AudioContext 接口的createScriptProcessor() 方法創建一個ScriptProcessorNode 用於通過JavaScript直接處理音頻.

createScriptProcessor()接收三個參數:
bufferSize
緩衝區大小,以樣本幀爲單位。具體來講,緩衝區大小必須是下面這些值當中的某一個: 256, 512, 1024, 2048, 4096, 8192, 16384. 如果不傳,或者參數爲0,則取當前環境最合適的緩衝區大小, 取值爲2的冪次方的一個常數,在該node的整個生命週期中都不變.
該取值控制着audioprocess事件被分派的頻率,以及每一次調用多少樣本幀被處理. 較低bufferSzie將導致一定的延遲。較高的bufferSzie就要注意避免音頻的崩潰和故障。推薦作者不要給定具體的緩衝區大小,讓系統自己選一個好的值來平衡延遲和音頻質量。
numberOfInputChannels
值爲整數,用於指定輸入node的聲道的數量,默認值是2,最高能取32.
numberOfOutputChannels
值爲整數,用於指定輸出node的聲道的數量,默認值是2,最高能取32.

語法:
var audioCtx = new AudioContext();
myScriptProcessor = audioCtx.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels);

三、兼容性介紹

MediaDevices除了IE全軍覆沒以外,其他的瀏覽器支持情況良好
AudioContext也是一樣的。

在這裏插入圖片描述

第三章:瀏覽器復讀功能實現

一、代碼展示

<!--追加一個選擇語言的select標籤-->
<div>選擇語言:<select name="" id=""></select></div>

首先獲取瀏覽器支持的語言,然後追加option標籤放到select標籤裏供我們選擇
注意點:最好將recognition.interimResults = true;去掉,否者交互是非常不友善的。

// 首先獲取瀏覽器支持的語言
const synth = window.speechSynthesis;
let voices = synth.getVoices();
const voiceSelect = document.querySelector('select');
function populateVoiceList() {
    voices = synth.getVoices();
    for(let i = 0; i < voices.length ; i++) {
        const option = document.createElement('option');
        option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
        if(voices[i].default) {
            option.textContent += ' -- DEFAULT';
        }
        option.setAttribute('data-lang', voices[i].lang);
        option.setAttribute('data-name', voices[i].name);
        voiceSelect.appendChild(option);
    }
}
populateVoiceList();
if (synth.onvoiceschanged !== undefined) {
   synth.onvoiceschanged = populateVoiceList;
}

在我們recognition.onresult那裏把獲取到的text朗誦出來

const utterThis = new window.SpeechSynthesisUtterance(text);
const selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
for(let i = 0; i < voices.length;i ++){
    if(voices[i].name === selectedOption){
        utterThis.voice =voices[i];
    }
}
synth.speak(utterThis);

二、知識點整理

首先是SpeechSynthesisUtterance對象,主要用來構建語音合成實例,例如上面代碼中的實例對象utterThis。我們可以直接在構建的時候就把要讀的文字內容寫進去。也可以在後面設置它的text屬性。

屬性 介紹
text 此屬性指定爲此語音要合成和說出的文本。這可以是純文本,也可以是格式正確的完整SSML文檔。[SSML]對於不支持SSML或僅支持某些標籤的語音合成引擎,用戶代理或語音引擎必須剝離不支持的標籤並說出文字。文本的最大長度可能限制爲32,767個字符。
lang 此屬性使用有效的BCP 47語言標籤爲語音指定語音合成的語言。[BCP47]如果沒有設置它仍然未設置爲腳本獲得,但將默認使用該語言的HTML文檔的根元素和相關層次的。當輸入請求打開與識別服務的連接時,將計算並使用此默認值。
voice 類型爲SpeechSynthesisVoice,可爲空此屬性指定Web應用程序希望使用的語音合成語音。當一個SpeechSynthesisUtterance對象被創建這個屬性必須被初始化爲null。如果在speak()方法調用時將此屬性設置爲所SpeechSynthesisVoice返回的對象之一getVoices(),則用戶代理必須使用該聲音。如果在speak()方法調用時此屬性未設置或爲null ,則用戶代理必須使用用戶代理的默認語音。用戶代理默認語音應支持當前語言(請參閱參考資料lang),並且可以是本地或遠程語音服務,並且可以通過用戶代理提供的接口(例如瀏覽器配置參數)合併最終用戶的選擇。
volume 此屬性指定話語的說話音量。它的範圍是0到1(含0和1),其中0是最低音量,1是最高音量,默認值爲1。如果使用SSML,則此值將被標記中的韻律標記覆蓋。
rate 此屬性指定話語的語速。這是相對於此語音的默認速率。1是語音合成引擎或特定語音支持的默認速率(應對應於正常的語音速率)。2是兩倍的速度,而0.5是一半的速度。嚴格禁止小於0.1或大於10的值,但是語音合成引擎或特定語音可能會進一步限制最小和最大速率,例如,即使您指定的值大於3,特定語音實際上可能不會比正常語音快3倍。如果使用SSML,則此值將被標記中的prosody標籤覆蓋。
pitch 此屬性指定發聲的說話音調。範圍在0到2之間(含0和2),其中0是最低音高,2是最高音高。1對應於語音合成引擎或特定語音的默認音高。語音合成引擎或語音可能會進一步限制最小和最大速率。如果使用SSML,則該值將被標記中的prosody標籤覆蓋。

然後是SpeechSynthesis對象。它是用於控制文本到語音輸出的腳本化Web API。

屬性 介紹
pending 如果全局SpeechSynthesis實例的隊列包含尚未開始講話的所有話語,則此屬性爲true。
speaking 如果正在說語音,則此屬性爲true。具體來說,如果已經開始說出話語而還沒有完成說出話語。這與全局SpeechSynthesis實例是否處於暫停狀態無關。
paused 當全局SpeechSynthesis實例處於暫停狀態時,此屬性爲true。此狀態與隊列中是否有任何內容無關。新窗口的全局SpeechSynthesis實例的默認狀態爲非暫停狀態。
方法 介紹
speak(utterance) 對於全局SpeechSynthesis實例,此方法將SpeechSynthesisUtterance對象的發音附加到隊列的末尾。它不會更改SpeechSynthesis實例的暫停狀態。如果SpeechSynthesis實例已暫停,它將保持暫停狀態。如果未暫停並且隊列中沒有其他語音,則立即說出該語音,否則將該語音排入隊列,以便在隊列中的其他語音都說完之後開始講話。如果在調用此方法之後且在相應結束或錯誤之前對SpeechSynthesisUtterance對象進行了更改事件,但未定義這些更改是否會影響您所說的內容,並且這些更改可能會導致返回錯誤。SpeechSynthesis對象獲得了SpeechSynthesisUtterance對象的專有所有權。將其作爲speak()參數傳遞給另一個SpeechSynthesis對象應引發異常。(例如,兩個框架可能具有相同的原點,並且每個框架都將包含一個SpeechSynthesis對象。)
cancel() 此方法從隊列中刪除所有語音。如果說出話語,則說話立即停止。此方法不會更改全局SpeechSynthesis實例的暫停狀態。
pause() 此方法將全局SpeechSynthesis實例置於暫停狀態。如果正在說出話語,它將暫停中間的話語。(如果在SpeechSynthesis實例已經處於暫停狀態時調用,它將不執行任何操作。)
resume() 此方法將全局SpeechSynthesis實例置於非暫停狀態。如果正在說出話語,它將在暫停時繼續說出話語,否則它將開始說出隊列中的下一個話語(如果有)。(如果在SpeechSynthesis實例已經處於非暫停狀態時調用,它將不執行任何操作。)
getVoices() 此方法返回可用的聲音。用戶語音取決於哪些聲音可用。如果沒有可用的語音,或者尚不清楚可用語音列表(例如:服務器端綜合,其中異步確定列表),則此方法必須返回長度爲零的SpeechSynthesisVoiceList。
voiceschanged 當getVoices方法將返回的SpeechSynthesisVoiceList的內容已更改時觸發。示例包括:服務器端綜合,其中異步確定列表,或者安裝/卸載客戶端語音。

三、兼容性介紹

在這裏插入圖片描述
在這裏插入圖片描述

第四章:最終效果展示:

在這裏插入圖片描述

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