Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9004

青空文庫スピーカー

$
0
0

たぶんこれは誰も使わないと思いますが、 青空文庫の作品を音声合成で読み上げるブックマークレットを作りました。

javascript: (() => { var VOICE_INDEX = 0, RATE = 5.0, VOLUME = 1.0, MIN_WAITING_TIME = 250; var ssu = new SpeechSynthesisUtterance(); ssu.lang = 'ja-JP'; ssu.rate = RATE; ssu.volume = VOLUME; var main = () => { var voices = speechSynthesis.getVoices().filter((voice) => voice.lang === 'ja-JP'); ssu.voice = voices[VOICE_INDEX]; console.info(voices); var mainText = document.querySelector('.main_text').cloneNode(true); mainText.querySelectorAll('rb, rp').forEach((node) => { node.parentNode.removeChild(node); }); var text = mainText.innerText; text = text.trim().replace(/(?:\r\n|\r)/g, '\n').replace(/。/g, '。\n').replace(/\n{2,}/g, '\n'); var lines = text.split('\n'); var speak = () => new Promise((resolve) => { ssu.onend = resolve; setTimeout(() => { speechSynthesis.speak(ssu) }, MIN_WAITING_TIME); }); var play = async () => { for (var i = 0; i < lines.length; i ++) { ssu.text = lines[i]; await speak(); } }; play(); }; speechSynthesis.getVoices().length === 0 ? speechSynthesis.onvoiceschanged = main : main(); })(); void 0;

ただし、いわゆる無能ボイスなので、抑揚もないし間違えもするので実用的ではありません。
Macのメジャーブラウザでは動きますが、Windowsでは試していません。また、期待通りに動かない作品もあります。とりあえず こちらの作品で試してみてはどうでしょう。

仕様

  • ルビに対応。つまり振り仮名を優先的に読み上げます
  • 「準備(したく)をする」などと括弧内で仮名を振っている文章は「じゅんびしたくをする」などと読み上げられます
  • 句点に差し掛かると、いくらか間を持たせるようにしています
  • フォントセットにない外字などは画像で表示されているため、無視されます

カスタマイズ

変数をいじることでいくらかカスタマイズできます。

  • VOICE_INDEX声質を選択します。Macではプリインストールされている女性の声と男性の声、Google翻訳をインストールしている環境ではGoogleの用意した声を選択できると思います。それぞれ 012という感じです。怪談のような作品では 1の男性の声が合っていると思いますが、環境によっては 0以外を選択するとエラーになるかもしれません。
    (ちなみに開発者ツールのコンソールに選択肢を表示しているので、わかる人はそちらをご覧ください。)
  • RATE読み上げる速度です。
  • VOLUMEボリュームです。
  • MIN_WAITING_TIME句点( )で待機する時間です。ミリ秒で指定してください。

開発関係

どうも speechSynthesis.getVoices()の返り値が空だったり空でなかったりするので調べたところ、

  • speechSynthesis.onvoiceschangedを待ってから getVoices()すると良い
  • ただし Safari では onvoiceschangedは呼ばれない。しかし getVoices()は必ず成功する

という問題に出くわしました。そこでいったん getVoices()して、その返り値が空であれば onvoiceschangedを挟み、空でなければ挟まずに処理を続行するようにしています。


Viewing all articles
Browse latest Browse all 9004

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>