テキスト読み上げツールを作る 第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 音声設定を取得
4 SpeechSynthesisUtteranceを作成
5 速度、高さ、音量を設定
6 speechSynthesis.speakで読み上げ
7 一時停止、再開、停止に対応

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

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


eguchi.netをもっと見る

購読すると最新の投稿がメールで送信されます。