フロント初心者がWeb Speech API を使ったシステムのフロント部分作ってみた！！

# はじめに
インターンでフロントエンドを勉強し始めて1ヶ月の初心者が、「Web Speech API」を使って音声でChatGPTと連携できるWebサイトを作ってみました。今回はフロント部分についてです。

# システム概要
Webぺージでブラウザの音声認識機能を使ってChatGPTに話しかけ、音声合成機能を使って音声でChatGPTの返答を受け取ることができるシステムを作りました！

システムのイメージはこんな感じ。PythonでChatGPTのAPIを叩く部分をバックエンドとしました。
今回はフロントエンドについてなので、画面構成・音声認識・音声合成について書いていきます。
![スクリーンショット 2023-06-19 19.41.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3471914/2ba9aba6-03a3-230a-02b4-dcf5b1b62ff5.png)


# 音声認識・合成で使ったAPIについて
今回は音声の認識・合成に「Web Speech API」を使いました。
音声認識・合成については以下の記事を参考にさせてただきました。

[音声認識](https://qiita.com/hmmrjn/items/4b77a86030ed0071f548) , [音声合成](https://qiita.com/hmmrjn/items/be29c62ba4e4a02d305c)
 
# 1.画面構成
まずHTMLとCSSで簡単に画面構成を作っていきます。
今回はいい感じのボタンを作りたかったのでBootstrapを使いました。
STARTボタンを押すとSTOPボタンになり、STOPボタンを押すとSTARTボタンになるようになっています。

``` index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>ChatGPT by voice</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
        
    </head>
    <body>
        <header>
            <h1>ChatGPT by voice</h1>
        </header>
        <div class="container">
            <p id="message">ChatGPTにはなしかけよう</p>
            <div>
                <button id="start_stop" class="btn btn-primary btn-lg">START</button>
                <div id="result-div"></div>
                <div id="answer-div"></div>
            </div>
        </div>
        <script>
               document.getElementById('start_stop').addEventListener('click',function(){
    
                if(this.innerHTML === 'START'){
    
                    recognition.start();
                    this.innerHTML = 'STOP';
    
                }else{
                    this.innerHTML = 'START';
                }
    
            });
        </script>
    
    </body>
</html>

```



フォントはGoogle fontsの"Zen Maru Gothic"を使いました。
```style.css
@import url('https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@900&display=swap');

body{
    background-color: #f6ffef;
}

h1{
    background-color: #05a141;
    color: #fff;
    font-family: 'Zen Maru Gothic', sans-serif;
    font-weight: bold;
    padding: 20px;
    text-align: center;
}

.container{
    font-family: 'Zen Maru Gothic', sans-serif;
    font-weight: bold;
    font-size: 50px;
    text-align: center;
}

button#start_stop{
    background-color: #05a141;
    color: #fff;
    border:#f6ffef;
}
```



この時点の画面はこんな感じ↓
![スクリーンショット 2023-06-26 15.23.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3471914/003885e4-7789-14e4-7d68-2dc267c8d9ba.png)



# 2.音声認識機能を使ってみよう

今回はファイルは分けず、htmlのscriptタグの中にすべて処理を書いちゃいました。

```index.html
<script>
    SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
    const recognition = new SpeechRecognition();
    
    recognition.lang = 'ja-JP';
    recognition.interimResults = true;
    recognition.continuous = true;
    const resultDiv = document.querySelector('#result-div'); //音声認識の結果を表示するもの
    let finalTranscript = ''; // 確定した(黒の)認識結果

    recognition.onresult = (event) => {
        let interimTranscript = ''; // 暫定(灰色)の認識結果
        for (let i = event.resultIndex; i < event.results.length; i++) {
            let transcript = event.results[i][0].transcript;
            if (event.results[i].isFinal) {
                finalTranscript += transcript;
            } else {
                interimTranscript = transcript;
            }
        }
        resultDiv.innerHTML = finalTranscript + '<i style="color:#ddd;">' + interimTranscript + '</i>';
    
    }      

    document.getElementById('start_stop').addEventListener('click',function(){

        //STARTボタンが押されたときの処理
        if(this.innerHTML === 'START'){
            //音声認識開始
            recognition.start();
            //STOPボタンへ変更
            this.innerHTML = 'STOP';
        
        //STOPボタンが押されたときの処理
        }else{
            //STOPボタンが押されたときの処理
            recognition.stop();
            //STARTボタンに変更
            this.innerHTML = 'START';
            //次ChatGPTに送信するときのために、一度送信した文字列は空にする
            finalTranscript = "";
            interimTranscript = ""; 
        }

    });
</script>

```

# 3.Ajaxを使ってPOST送信する
実際にChatGPT側から返答をもらうためにAjaxを使ってPOST送信していきます。
今回は音声認識によって得られた文字列をデータとして送信して、ChatGPT API の返答結果をjsonとして受け取ります。

```index.html
<!-- 追加 -->
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<!-- --- -->

<script>
    SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
    const recognition = new SpeechRecognition();
    
    recognition.lang = 'ja-JP';
    recognition.interimResults = true;
    recognition.continuous = true;
    const resultDiv = document.querySelector('#result-div');　　//音声認識の結果を表示するもの

    /* -- 追加 --- */
    const answerDiv = document.querySelector('#answer-div');　//chatGPTの返答を表示するもの
    /* -- --- -- */

    let finalTranscript = ''; // 確定した(黒の)認識結果

    recognition.onresult = (event) => {
        let interimTranscript = ''; // 暫定(灰色)の認識結果
        for (let i = event.resultIndex; i < event.results.length; i++) {
            let transcript = event.results[i][0].transcript;
            if (event.results[i].isFinal) {
                finalTranscript += transcript;
            } else {
                interimTranscript = transcript;
            }
        }
        resultDiv.innerHTML = finalTranscript + '<i style="color:#ddd;">' + interimTranscript + '</i>';
    
    }      

    document.getElementById('start_stop').addEventListener('click',function(){

        //STARTボタンが押されたときの処理
        if(this.innerHTML === 'START'){
            //音声認識開始
            recognition.start();
            //STOPボタンへ変更
            this.innerHTML = 'STOP';
        
        //STOPボタンが押されたときの処理
        }else{
            //STOPボタンが押されたときの処理
            recognition.stop();
            //STARTボタンに変更
            this.innerHTML = 'START';

                /* -- 追加 --- */
                $.ajax({
                    type: 'POST',
                    url: 'ここにPOST送信する先のURL',
                    data: finalTranscript,
                    success:function(data){
                        answerDiv.innerHTML = data["answer"];

                    },
                    dataType:'json'
                });
                /* -- --- -- */


            //次ChatGPTに送信するときのために、一度送信した文字列は空にする
            finalTranscript = "";
            interimTranscript = ""; 
        }

    });
</script>
```

# 4.音声合成使ってみよう
ChatGPTから受け取った返答を音声にしてみます。

```index.html
<script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>

<script>
    SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
    const recognition = new SpeechRecognition();
    
    recognition.lang = 'ja-JP';
    recognition.interimResults = true;
    recognition.continuous = true;
    const resultDiv = document.querySelector('#result-div');//音声認識の結果を表示するもの

    const answerDiv = document.querySelector('#answer-div');//chatGPTの返答を表示するもの


    let finalTranscript = ''; // 確定した(黒の)認識結果

    recognition.onresult = (event) => {
        let interimTranscript = ''; // 暫定(灰色)の認識結果
        for (let i = event.resultIndex; i < event.results.length; i++) {
            let transcript = event.results[i][0].transcript;
            if (event.results[i].isFinal) {
                finalTranscript += transcript;
            } else {
                interimTranscript = transcript;
            }
        }
        resultDiv.innerHTML = finalTranscript + '<i style="color:#ddd;">' + interimTranscript + '</i>';
    
    }      

    document.getElementById('start_stop').addEventListener('click',function(){

        //STARTボタンが押されたときの処理
        if(this.innerHTML === 'START'){
            //音声認識開始
            recognition.start();
            //STOPボタンへ変更
            this.innerHTML = 'STOP';
        
        //STOPボタンが押されたときの処理
        }else{
            //STOPボタンが押されたときの処理
            recognition.stop();
            //STARTボタンに変更
            this.innerHTML = 'START';

                
                $.ajax({
                    type: 'POST',
                    url: 'ここにPOST送信する先のURL',
                    data: finalTranscript,
                    success:function(data){
                        answerDiv.innerHTML = data["answer"];
                        
                        /* -- 追加 --- */
                        const uttr= new SpeechSynthesisUtterance(data["answer"]);
                        //ChatCPTの返答を音声で再生
                        speechSynthesis.speak(uttr);
                        /* -- --- -- */

                    },
                    dataType:'json'
                });
                


            //次ChatGPTに送信するときのために、一度送信した文字列は空にする
            finalTranscript = "";
            interimTranscript = ""; 
        }

    });
</script>
```

# 全体コード
cssファイルは[ 1. 画面構成](#1画面構成)で書いたものから変えていないので、index.htmlの全体コードだけ載せます。
```index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>ChatGPT by voice</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
        
    </head>
    <body>
        <header>
            <h1>ChatGPT by voice</h1>
        </header>
        <div class="container">
            <p id="message">ChatGPTにはなしかけよう</p>
            <div>
                <button id="start_stop" class="btn btn-primary btn-lg">START</button>
                <div id="result-div"></div>
                <div id="answer-div"></div>
            </div>
        </div>
        <script src="jquery-3.7.0.min.js"></script>
        <script>
            if ('speechSynthesis' in window) {
                alert("このブラウザは音声合成に対応しています")
            } else {
                alert("このブラウザは音声合成に対応していません")
            }
    
            SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
            const recognition = new SpeechRecognition();
            
    
            recognition.lang = 'ja-JP';
            recognition.interimResults = true;
            recognition.continuous = true;
            const resultDiv = document.querySelector('#result-div');
            const answerDiv = document.querySelector('#answer-div');
            let finalTranscript = ''; // 確定した(黒の)認識結果
    
            recognition.onresult = (event) => {
            let interimTranscript = ''; // 暫定(灰色)の認識結果
            for (let i = event.resultIndex; i < event.results.length; i++) {
                let transcript = event.results[i][0].transcript;
                if (event.results[i].isFinal) {
                    finalTranscript += transcript;
                } else {
                    interimTranscript = transcript;
                }
            }
            resultDiv.innerHTML = finalTranscript + '<i style="color:#ddd;">' + interimTranscript + '</i>';
            
            
        }      
    
            document.getElementById('start_stop').addEventListener('click',function(){
    
                if(this.innerHTML === 'START'){
    
                    recognition.start();
                    this.innerHTML = 'STOP';
    
                }else{
                    recognition.stop();
                    this.innerHTML = 'START';
    
                    $.ajax({
                        type: 'POST',
                        url: 'ここにPOST送信する先のURL',
                        data: finalTranscript,
                        success:function(data){
                            console.log(data["answer"]);
                            answerDiv.innerHTML = data["answer"];
                            const uttr= new SpeechSynthesisUtterance(data["answer"]);
                            //ChatCPTの返答を音声で再生
                            speechSynthesis.speak(uttr);
                        },
                        dataType:'json'
                    });
    
                    //次ChatGPTに送信するときのために、一度送信した文字列は空にする
                    finalTranscript = "";
                    interimTranscript = ""; 
    
                }
    
            });
        </script>
    </body>
</html>
```

# おわりに
なんとかシステムのフロント部分を作ることができました！
HTML,CSSの部分はもう少しちゃんとかけたら良かったなと...。まだまだ勉強が必要です💪🏻
これから勉強していきます🔥🔥

ChatGPTと連携する部分は、バックエンドの記事に書こうと思っていますのでお待ち下さい🙇🏻‍♀️

ブログを書くのも初めてだったので拙いところがたくさんあったと思いますが、読んでいただきありがとうございました！

# 参考サイト
Webページでブラウザの音声認識機能を使おう - Web Speech API Speech Recognition
https://qiita.com/hmmrjn/items/4b77a86030ed0071f548


Webページでブラウザの音声合成機能を使おう - Web Speech API Speech Synthesis
https://qiita.com/hmmrjn/items/be29c62ba4e4a02d305c

js STUDIO $.post()
https://js.studio-kingdom.com/jquery/ajax/post