テキスト読み上げツールを作る 第3回:JavaScriptで文字を音声読み上げする仕組み

はじめに

前回は、テキスト読み上げツールの画面をHTMLとCSSで作りました。

今回は、JavaScriptを使って実際に音声読み上げ機能を実装します。

読み上げ処理の中心になるのは、ブラウザの音声合成機能です。

入力欄に文章を入力し、「読み上げる」ボタンを押すと、その文章をブラウザが音声で読み上げます。

JavaScriptで行う処理

今回JavaScriptで行う処理は、次の通りです。

処理内容
入力文の取得テキストエリアの文章を取得
空欄チェック文章がない場合は実行しない
音声一覧の取得ブラウザ内の音声を取得
音声設定速度、高さ、音量を反映
読み上げ実行入力文を音声で再生
一時停止読み上げを途中で止める
再開一時停止した音声を再開
停止読み上げを取り消す
状態表示現在の状態を画面に表示

HTML要素を取得する

最初に、HTMLで作った要素をJavaScriptから操作できるようにします。

const textArea = document.getElementById('ttsText');
const voiceSelect = document.getElementById('voiceSelect');
const speakBtn = document.getElementById('speakBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resumeBtn = document.getElementById('resumeBtn');
const stopBtn = document.getElementById('stopBtn');
const clearBtn = document.getElementById('clearBtn');
const status = document.getElementById('ttsStatus');

document.getElementById() は、指定したIDを持つHTML要素を取得する命令です。

たとえば、入力欄はHTML側で次のように作っています。

<textarea id="ttsText"></textarea>

そのため、JavaScriptでは次のように取得できます。

const textArea = document.getElementById('ttsText');

スライダーの値も取得する

速度、高さ、音量のスライダーも取得します。

const rate = document.getElementById('rate');
const pitch = document.getElementById('pitch');
const volume = document.getElementById('volume');

const rateValue = document.getElementById('rateValue');
const pitchValue = document.getElementById('pitchValue');
const volumeValue = document.getElementById('volumeValue');

rate は速度、pitch は高さ、volume は音量です。

rateValue などは、スライダーの横に現在値を表示するために使います。

ブラウザ対応を確認する

音声読み上げに対応していないブラウザもあるため、最初に確認します。

if (!('speechSynthesis' in window)) {
status.textContent = 'このブラウザは音声読み上げに対応していません。';
speakBtn.disabled = true;
pauseBtn.disabled = true;
resumeBtn.disabled = true;
stopBtn.disabled = true;
}

speechSynthesis が使えない場合、読み上げ処理は実行できません。

そのため、ボタンを無効化して、ユーザーに対応していないことを知らせます。

状態表示用の関数を作る

画面に状態メッセージを表示するため、関数を作ります。

function setStatus(message) {
status.textContent = message;
}

これにより、状態表示を変えたいときは次のように書けます。

setStatus('読み上げ中です。');

毎回 status.textContent と書くより、処理が分かりやすくなります。

音声一覧を取得する

ブラウザに登録されている音声一覧を取得します。

let voices = [];

function loadVoices() {
voices = window.speechSynthesis.getVoices();
voiceSelect.innerHTML = '';
}

getVoices() により、利用可能な音声の一覧を取得できます。

ただし、音声一覧はページ読み込み直後に空になる場合があります。

そのため、後から音声一覧が読み込まれたときにも対応します。

if (typeof speechSynthesis.onvoiceschanged !== 'undefined') {
speechSynthesis.onvoiceschanged = loadVoices;
}

これにより、音声一覧が変更されたタイミングで loadVoices() が再実行されます。

日本語音声を優先する

日本語読み上げツールとして使いやすくするため、日本語音声を優先して表示します。

const japaneseVoices = voices.filter(function (voice) {
return voice.lang && voice.lang.toLowerCase().startsWith('ja');
});

voice.lang には、音声の言語情報が入っています。

日本語の場合は、ja-JP のような値になります。

プルダウンに音声を追加する

取得した音声を、選択欄に追加します。

const displayVoices = japaneseVoices.length > 0 ? japaneseVoices : voices;

displayVoices.forEach(function (voice, index) {
const option = document.createElement('option');
option.value = voice.name;
option.textContent = voice.name + '(' + voice.lang + ')';

if (index === 0) {
option.selected = true;
}

voiceSelect.appendChild(option);
});

日本語音声がある場合は日本語音声だけを表示し、ない場合はすべての音声を表示します。

選択された音声を取得する

ユーザーが選んだ音声を取得する関数です。

function getSelectedVoice() {
return voices.find(function (voice) {
return voice.name === voiceSelect.value;
});
}

プルダウンで選ばれた音声名と、音声一覧の中の名前が一致するものを探しています。

入力文を取得する

読み上げ処理では、まず入力欄の文字を取得します。

const text = textArea.value.trim();

trim() により、前後の余分な空白を取り除きます。

入力欄が空の場合は、読み上げを実行しません。

if (!text) {
setStatus('読み上げる文章を入力してください。');
return;
}

読み上げ処理を作る

読み上げの中心になる関数です。

function speakText() {
const text = textArea.value.trim();

if (!text) {
setStatus('読み上げる文章を入力してください。');
return;
}

window.speechSynthesis.cancel();

const selectedVoice = getSelectedVoice();
const utterance = new SpeechSynthesisUtterance(text);

utterance.lang = selectedVoice ? selectedVoice.lang : 'ja-JP';
utterance.voice = selectedVoice || null;
utterance.rate = Number(rate.value);
utterance.pitch = Number(pitch.value);
utterance.volume = Number(volume.value);

utterance.onstart = function () {
setStatus('読み上げ中です。');
};

utterance.onend = function () {
setStatus('読み上げが完了しました。');
};

utterance.onerror = function () {
setStatus('読み上げ中にエラーが発生しました。');
};

window.speechSynthesis.speak(utterance);
}

この処理の中で、入力文、音声、速度、高さ、音量をまとめて設定しています。

SpeechSynthesisUtteranceとは

SpeechSynthesisUtterance は、読み上げる内容をまとめるためのものです。

const utterance = new SpeechSynthesisUtterance(text);

この中に、次の設定を入れます。

設定内容
lang読み上げ言語
voice読み上げ音声
rate速度
pitch高さ
volume音量

そして最後に、次のコードで読み上げます。

window.speechSynthesis.speak(utterance);

ボタンに処理をつなぐ

作った関数を、ボタンのクリックに接続します。

speakBtn.addEventListener('click', speakText);

これで、「読み上げる」ボタンをクリックしたときに speakText() が実行されます。

一時停止、再開、停止を作る

音声読み上げには、一時停止、再開、停止も追加できます。

pauseBtn.addEventListener('click', function () {
window.speechSynthesis.pause();
setStatus('一時停止しました。');
});

resumeBtn.addEventListener('click', function () {
window.speechSynthesis.resume();
setStatus('読み上げを再開しました。');
});

stopBtn.addEventListener('click', function () {
window.speechSynthesis.cancel();
setStatus('読み上げを停止しました。');
});

それぞれの違いは次の通りです。

処理内容
pause途中で一時停止
resume一時停止した位置から再開
cancel読み上げを完全に取り消す

クリアボタンを作る

入力欄を空にするボタンも作ります。

clearBtn.addEventListener('click', function () {
window.speechSynthesis.cancel();
textArea.value = '';
setStatus('');
});

クリア時には、読み上げも停止します。

これにより、入力欄を消したあとに音声だけが流れ続ける状態を防げます。

スライダーの表示値を更新する

速度、高さ、音量の数字表示も更新します。

rate.addEventListener('input', function () {
rateValue.textContent = Number(rate.value).toFixed(1);
});

pitch.addEventListener('input', function () {
pitchValue.textContent = Number(pitch.value).toFixed(1);
});

volume.addEventListener('input', function () {
volumeValue.textContent = Number(volume.value).toFixed(1);
});

input イベントは、スライダーを動かしたときに発生します。

toFixed(1) により、小数点1桁で表示しています。

まとめ

今回は、JavaScriptでテキスト読み上げ機能を作りました。

重要な流れは次の通りです。

順番処理
1入力欄の文章を取得
2空欄か確認
3音声設定を取得
4SpeechSynthesisUtteranceを作成
5速度、高さ、音量を設定
6speechSynthesis.speakで読み上げ
7一時停止、再開、停止に対応

この仕組みを使えば、外部APIなしで、ブラウザ上だけで音声読み上げツールを作れます。

次回は、WordPressやWebサイトに設置し、お役立ちツールとして公開する方法を解説します。