Web Speech API (SpeechSynthesisUtterance)

2023-06-27

Web Speech API 的 SpeechSynthesisUtterance 物件是一個強大的工具,可以在網頁上合成語音,而不需要繁瑣的引用,只需要單純的 JS,它的原理是使用瀏覽器本身的 API 實作,因此不同的瀏覽器,提供不同的語音選擇。

logo

Code

<!doctype html>
<html lang="en">

<head>
    <title>Web Speech API</title>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS v5.2.1 -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">

</head>

<body>
    <div class="container p-5">
        <form action="">
            <div class="mb-3">
                <label for="" class="form-label">Text</label>
                <textarea name="text" id="text" cols="30" rows="10"
                    class="form-control">Haha, I don't think that would work for me. Maybe I just need to get more sleep and stop staying up so late watching TV.</textarea>
            </div>

            <div class="mb-3">
                <label for="" class="form-label">Voice</label>
                <select class="form-select" name="" id=""></select>
            </div>

            <div class="mb-3">
                <label for="" class="form-label">Ratio</label>
                <input type="range" min="0" max="2" value="1" step="0.1" class="form-control" name="" id="rate"
                    aria-describedby="helpId" placeholder="">
            </div>

            <div class="mb-3">
                <label for="" class="form-label">Pitch</label>
                <input type="range" min="0" max="2" value="1" step="0.1" class="form-control" name="" id="pitch"
                    aria-describedby="helpId" placeholder="">
            </div>

            <button type="submit" class="btn btn-primary">Speak</button>

        </form>
    </div>

    <script>
        const synth = window.speechSynthesis;

        const inputForm = document.querySelector("form");
        const inputTxt = document.querySelector("#text");
        const voiceSelect = document.querySelector("select");
        const rate = document.querySelector("#rate");
        const pitch = document.querySelector("#pitch");

        let voices;

        function loadVoices() {
            voices = synth.getVoices();
            for (let i = 0; i < voices.length; i++) {

                if (voices[i].lang.includes("en-US")) {
                    const option = document.createElement("option");
                    option.textContent = `${voices[i].name} (${voices[i].lang})`;
                    option.value = i;
                    voiceSelect.appendChild(option);
                }
                else {
                    console.log(voices[i].lang);
                }
            }
        }

        // in Google Chrome the voices are not ready on page load
        if ("onvoiceschanged" in synth) {
            synth.onvoiceschanged = loadVoices;
        } else {
            loadVoices();
        }

        inputForm.onsubmit = (event) => {
            event.preventDefault();
            const utterThis = new SpeechSynthesisUtterance(inputTxt.value);
            utterThis.voice = voices[voiceSelect.value];
            utterThis.rate = rate.value;
            utterThis.pitch = pitch.value;
            synth.speak(utterThis);
            inputTxt.blur();
        };


    </script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"
        integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous">
        </script>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
        integrity="sha384-7VPbUDkoPSGFnVtYi0QogXtr74QeVeeIs99Qfg5YCF+TidwNdjvaKZX19NZ/e6oz" crossorigin="anonymous">
        </script>
</body>

</html>

在示範程式碼當中,透過 voices[i].lang.includes("en-US") 限定 SELECT 只出現英語,如果有需要其他語言的 Speech 可以移除此搜尋限制。

Reference