Raspberry Pi4B に喋らせる - VOICEVOX スクリプト

やりたいこと

前回、Raspberry Pi4B に VOICEVOX をインストールして単発で喋らせるところまではできた。

では、もう、ずっと喋っててもらおうかと。

ニュースサイトからニュースのタイトルをごっそり取得して、そのタイトルを VOICEVOX に流し込んで、しゃべってもらう。

必要なもの

今回はスクリプトを書くだけなので、物理的に必要なものはラズパイだけ。

概要

  1. PHPでYahooニュースのRSS(xml形式)を取得
  2. 取得したRSSからタイトル、記事URLを抽出
  3. タイトルをリスト化してファイルに保存
  4. シェルスクリプトでタイトルリストファイルを読み込み音声化
  5. 再生

こんな感じで処理してみようかと。

尚、1~4の処理にはPHPを使っている。
これはブラウザでPHPファイルへアクセスすることでもニュースのタイトルと記事リンクを踏めるようにしておきたいため。

5ではシェルスクリプトにしてる。
各コマンドの実行が楽だから。

そして、GoogleニュースはもうRSSフィードの提供を行っていないようなので、Yahooから選んでみた。

RSS一覧 - Yahoo!ニュース
ニュース配信記事のRSS情報一覧がご覧になれます。RSS購読にはRSSリーダーなど専用ツールが必要となります。第三者が提供するそれらのツールはYahoo!ニュースとは関係はありません。

作成するスクリプトは以下の通り。

  • Yahoo!ニュースのRSSフィードURLをまとめたリストファイル:テキストファイル
  • yahoo!ニュースのRSSフィードを処理するファイル:PHPファイル
  • タイトルリストファイルを元に音声データを作成・再生するスクリプト:Linuxシェルスクリプト

これらのファイルは voicevox_core 配下に script というフォルダを作成して、そこに放り込んである。
ただし、VOICEVOX を操作して音声ファイルを作る Python スクリプトだけは voicevox_core 内に配置している。

YahooニュースのRSSフィードURLのリストを作成する

まずは、以下のような感じで取得したいニュースのRSSフィードURLを羅列。
(//でコメントアウト、空白行(先頭改行のみ)は除外される)

ファイル名:newsUrlList.txt

//トピックス
https://news.yahoo.co.jp/rss/topics/top-picks.xml
 
//国内

Yahoo!ニュース・トピックス - 国内
//国際
Yahoo!ニュース・トピックス - 国際
//経済
Yahoo!ニュース・トピックス - 経済
//IT
Yahoo!ニュース・トピックス - IT
//科学
Yahoo!ニュース・トピックス - 科学
//Impress Watch
Impress Watch - Yahoo!ニュース
//ITmedia
ITmedia NEWS - Yahoo!ニュース
//ITmedia Enterprise
ITmedia エンタープライズ - Yahoo!ニュース
//アスキー
アスキー - Yahoo!ニュース
//ギズモード
ギズモード・ジャパン - Yahoo!ニュース
//CNET
CNET Japan - Yahoo!ニュース
//CNN
CNN.co.jp - Yahoo!ニュース
//AP通信
AP通信 - Yahoo!ニュース
//Car Watch
Car Watch - Yahoo!ニュース
//シネマトゥデイ
シネマトゥデイ - Yahoo!ニュース
//窓の杜
窓の杜 - Yahoo!ニュース
//帝国データバンク
帝国データバンク - Yahoo!ニュース
//映画.com
映画.com - Yahoo!ニュース

PHPでYahooニュースのRSSを処理する

続いて以下のコードでyahooニュースのRSSを取得し、タイトルと記事URLを抽出するように書く。
タイトルのみファイルに保存。(これをVOICEVOXで喋らせる元データとしている)

ファイル名:getNews.py

<?php
 
$urlListFile = __DIR__ . '/newsUrlList.txt';
$urlsObj = new SplFileObject($urlListFile, 'r');
$urlsObj->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);
 
$cateList = array();
$urlList = array();
$titleList = array();

// RSSフィードのURLをひとつづつ処理して、タイトルと記事URLなどを抜き出す
foreach($urlsObj as $n => $line){
        if($line == false) continue;
        if(str_starts_with($line, '//')) continue;
        GetNewsInfo($line, $urlList, $cateList, $titleList);
} 

// 抜き出したタイトル等をhtml出力
$cate = "";
for ($i = 0; $i < count($urlList); $i++){
        if($cate != $cateList[$i]){
                $cate = $cateList[$i];
                if($i != 0){
                        echo "</ul>\n";
                }
                echo "<h3>" . $cate . "</h3>\n";
                echo "  <ul>\n";
        }
        echo "    <li><a href=\"" . $urlList[$i] . "\">";
        echo $titleList[$i];
        echo "</a></li>\n";
}
echo "  </ul>\n";

// タイトルリストをファイルに出力。これをVOICEVOXで使う。
// リストファイル名は voiceList.txt で、このスクリプトと同じディレクトリに保存
file_put_contents(__DIR__ . "/voiceList.txt", implode(PHP_EOL,$titleList));


// RSSフィードのURLを読み込んで処理する関数
function GetNewsInfo($url, &$urlList, &$cateList, &$titleList){
        $xml = simplexml_load_file($url);
        $items = $xml->channel->item;
        $itemsCount = count($items);
 
        for ($i = 0; $i < $itemsCount; $i++){
                $cateList[] = trim(preg_replace("/ - Yahoo!ニュース$/", "", $xml->channel->title));
                $titleList[] = $items[$i]->title;
                $urlList[] = preg_replace("/comments$/", "", $items[$i]->comments);
        }
}
 
?>

これで、html出力と同時に読み上げたいニュースタイトルがリスト化されてファイルの保存することできる。

抜き出されたタイトルをVOICEVOXで喋らせる

cron で回すか、ラズパイなんでGPIOにボタンでも付けて「ボタンが押されたら開始」みたいな仕組みにするか、なんてところは後で考えるとして、とりあえず、一連のコマンドを実行するスクリプトを作る。

スクリプト名:newsTitleReader.py

#!/bin/bash

# カレントディレクトリを自分のファイルの位置にする
cd `dirname $0`

# PHPスクリプトを実行してニュースリストを作成
php -f getNews.php


# phpで作成したニュースタイトルリストを配列にする
IFS=$'\n'
lists=(`cat voiceList.txt`)

# 一つ上の voice_core ディレクトリに移動
cd ..

# 繰り返し処理で音声作成と再生
for list in "${lists[@]}"
        do
                echo $list
                source ../bin/activate
                python3 ./voice.py $list
                # 読み上げ前に「ティロリロン~」みたいな効果音を鳴らす
                aplay -D plughw:3,0 ./sound/ConfirmButton_001.wav
                # 喋ってもらおう
                aplay -D plughw:3,0 ./voice/newsTitleVoice.wav
        done

このスクリプトに実行権限を付与。

$ chmod +x newsTitleReader.py

あ、このスクリプトを実行する前に一つ。

前回、下記URLを参照してスクリプトを拝借したが、一部書き換えが必要。

VOICEVOX COREをRaspberryPiにインストールしてCLIで便利に音声合成を行おう - Qiita
はてなブログに書いていたRaspberryPiにVOICEVOXをインストールするという内容をQiitaに移植しました。…

最後から2行目の部分を下記の通り変更し、「喋らせた内容をファイル名とする」という状態から「ファイル名は固定する」という状態に変更しておく。
(喋る内容によってファイル名が異なるのは便利でもあり、今回の場合は不便になるので、決め打ちする)

with open("./" + text + ".wav", "wb") as f:            # 変更前
with open("./voice/newsTitleVoice.wav","wb") as f:     # 変更後

voicevox_core 内にこのスクリプトがあるけど、生成した音声ファイルはさらに voice というフォルダに保存するようにしているため、上記のように変更している。

以上でスクリプト作成完了。
あとは実行すれば喋り出す。

実際喋ってもらうと…

「Impress Watch」なんかは「アイ・エム・ピー・アール・イー・エス・エス」(ちょっとの間)「ウォッチ」と喋るから違和感が…

まぁ、いろんなイントネーションをパーフェクトに喋らせるのは本当に難しいよねぇ。

とりあえず、先ほど作ったリストが読み込まれて実行されると600~650のニュースタイトルを延々と読み上げてくれる。

1タイトル辺り、音声ファイル作成に20秒ほどかかって、読み上げに3秒くらい。
600タイトルあると、約3.8時間で読み上げが完了する計算。
…長いな…

もうちょっと対象とするニュースを絞るなどして、1時間くらいで終わるようにした方が無難かも。

まぁ、21時ごろ帰宅して、TVを見るわけでも映画を見るわけでも音楽を聴くわけでもなく、あるいは何か作業に没頭するけど無音は寂しい感じがするときには、4時間ニュースタイトル垂れ流しも悪くないか?
25時になればそろそろ寝ようかという時間だし。

もう少し使い分けよう

やはり、興味あるニュースを全てひとまとめに、というのは多すぎる気がする。

物理ボタンでもいいし、WebUI作ってコントロールとしてのボタンでもいいから、「平日はこのニュースを」「休日は趣味全開のニュースを」みたいに読み上げ内容を変えた方がいいね。

この記事にコメントしてみる