NanoPi NEOがTwitterを声でツブヤクンデス

この記事の対象マシンはNanoPi NEO/NEO2で、OSは例によってarmbian (debian Jessie)。
と、いうことにしておくけど、おそらくLinuxの多くでほぼそのままで動くだろうしNanoPi NEO/NEO2でなくてもイケる筈。

まずは、NanoPi NEO/NEO2で音声機能が使えるか確認。

# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Codec [H3 Audio Codec], device 0: CDC PCM Codec-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: U0x4b40x307 [USB Device 0x4b4:0x307], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

基板のピンヘッダからスピーカーに繋いで音を出す場合はcard 0の出力。USBスピーカーを繋いで音を出すならcard 1の出力となる。USBスピーカーを繋いでいない場合はcard 1:以下の部分は表示されない。

音を出してみる

基板のピンヘッダからスピーカーに出力

# speaker-test -c 2 -D hw:0
# aplay /usr/share/sounds/alsa/Front_Center.wav -D hw:0

USBスピーカーに出力

# speaker-test -c 2 -D plughw:1
# aplay /usr/share/sounds/alsa/Front_Center.wav -D plughw:1

speaker-testではザーッという音が鳴る筈。
aplayの方は喋る筈。指定しているファイルはarmbianに標準で入っているもの。

ALSAの設定変更 (必要なら)

標準の音声の出力先が基板のピンヘッダからスピーカー(card 0)なのでUSBスピーカーに出したい場合は設定を変更する。設定ファイルは何故か/etcには無い。
/usr/share/alsa/alsa.conf

defaults.ctl.card 1
defaults.pcm.card 1
おそらく初期値は2つの行ともに0の筈。それを1に変更(上の状態に)して保存する。
システム再起動後は標準の音声出力先がUSBスピーカーになっている筈。

Open JTalkの準備

音声合成システムのOpen Jtalkはarmbian (debian)のパッケージが提供されているので簡単。

# apt-cache search jtalk
# apt-get install hts-voice-nitech-jp-atr503-m001 open-jtalk open-jtalk-mecab-naist-jdic

1行目は名前にjtalkを含むパッケージを検索。おそそらくopen-jtalk本体とm001という男性の音声データと辞書の3つのパッケージが表示される筈。
2行目で検索結果として表示されたパッケージ名を指定してインストール。(表示された3つ全て)

実行してみる。

$ echo "こんにちは" | open_jtalk -m /usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow /tmp/tmp.wav && aplay --quiet /tmp/tmp.wav

あまりに簡単に喋るのでびっくり。

ちょっと問題があって、男性声のm001は何故か特定の条件で発声が遅くなることがある。かなりホラーな声になるのであまり使いたくない。女性声のMeiが良さげ。

Meiのインストールはこちらを参照。現在はそのページの内容のMMDAgent_Example-1.6.zipのバージョンが1.6から1.7に変わったくらい。

jtalk.sh
#!/bin/sh
TMP=/tmp/tmp.wav

# Voice m001
# echo "$1" | open_jtalk -m /usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow $TMP && aplay --quiet $TMP

# Voice Mei
echo "$1" | open_jtalk -m /usr/share/hts-voice/mei/mei_normal.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow $TMP && aplay --quiet $TMP
rm -f $TMP
$ chmod +x jtalk.sh
$ ./jtalk.sh "こんにちは"

このスクリプトは後で使う

PHPの準備

「がとらぼ」の中の人が頭の硬いオッサンで最近はPHPくらいしか使わなくなってるのとPythonとかRubyは殆ど触ったことがないのでPHPを使う。
PHPを使うけど、よくあるようなウェブサーバにするだとか大掛かりにするつもりはないので必要最低限だけインストールする。
今回はPHP5のコマンドラインとCURL(と国際化)モジュールが使えれば良い筈。

# apt-get install php5-cli php5-curl php5-intl

TwitterOAuthの準備

Twitterとの連携は腕に覚えがあるなら1から自力で作れば良いだろうが、「がとらぼ」の中の人はプログラム力が無いことにかけては右に出るものがいないというくらいカスなので既存のものを使う。

GitHubのtwitteroauthというプロジェクト。定番中の定番。

$ wget https://github.com/abraham/twitteroauth/archive/master.zip
$ unzip master.zip
$ mv twitteroauth-master twitteroauth

これでtwitteroauthは準備完了。twitteroauth/twitteroauth.phpを呼び出すだけで殆ど何も考えずにタイムラインを取得したりツイートできる。

今回の記事で一番難しいのはおそらくTwitterのAPIを利用するためのキーとかアクセストークンの取得。
慣れてりゃ簡単だろうけど、初めてだと何が何やらって感じ。今回はWordPressの投稿時の自動ツイート用以来ということでかなり久しぶりに取得しようとしたら9割方忘れてたので迷いそうになった。

ツブヤクンデス作成

NanoPi NEOでの発声は簡単だった。Twitterとの連携もtwitteroauthのおかげでアホみたいに簡単。後は組み合わせるだけ。

以下、作成するtsubuyakundesu.phpというファイルは先に用意したjtaik.shとtwitteroauthと同じディレクトリに置く。後で作成するtweet_id_stamp.txtも同じディレクトリ。
(twitteroauthディレクトリの中じゃないよ)

tsubuyakundesu.php
<?php
//mb_internal_encoding("UTF-8");
require_once("./twitteroauth/autoload.php");
use Abraham\TwitterOAuth\TwitterOAuth;

date_default_timezone_set('Asia/Tokyo');

$idfile = './tweet_id_stamp.txt';
$id_old = file_get_contents($idfile);

//以下4行に取得したTwitterのAPI用のキーやトークンを入れる
$consumerKey = 'xxxxxxxxxxxxxxxxxxxxxxxxx';
$consumerSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$accessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$accessTokenSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

//接続
$connection = new TwitterOAuth($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret);

//タイムライン取得
$timeline = $connection->get('statuses/home_timeline', ['count' => '10']);

// var_dump($timeline);

$timeline = array_reverse($timeline); //タイムラインを逆順に

foreach($timeline as $tw){
    if ($tw->id > $id_old){
        $id = $tw->id;
        echo $tw->user->name . "\n";
        // echo date('Y-m-d H:i:s', strtotime($tw->created_at)) . "\n";
        $twname =  preg_replace("/[^ぁ-んァ-ンー0-9一-龠0-9]+/u",'' , $tw->user->name);
        echo $twname . "\n";
        exec("./jtalk.sh $twname");
                
        //ツイート本文
        $speech = preg_replace('/https?:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:@&=+$,%#]+/', '', $tw->text);
        $speech = str_replace('Twitter', 'ツイッター', $speech);
        $speech = preg_replace('/^RT \@.*?:/', 'リツイート', $speech);
        $speech = preg_replace('/(\r\n|\r|\n)/', ' ', $speech);
        $speech = preg_replace("/[^ぁ-んァ-ンーa-zA-Z0-9一-龠0-9\-\s]+/u",'' ,$speech);
        exec("./jtalk.sh \"$speech\"");
        //分解するなら上の1行をコメントにして次の5行を非コメント化
        //$ar_sp = preg_split("/[\s]+/", $speech, -1, PREG_SPLIT_NO_EMPTY);
        //foreach($ar_sp as $sp){
        //        echo "\n----\n$sp\n----\n";
        //        exec("./jtalk.sh \"$sp\"");
        //}
    }
}

if ($id > $id_old){
        file_put_contents($idfile, $id);
}
?>

タイムラインの最新のツイートのIDを保存するファイルを作成。ダミーのIDとして0を登録。

echo 0 > ./tweet_id_stamp.txt

実行してみる。
$ php ./tsubuyakundesu.php

仕組みとしてはTwitterOAuthでタイムラインを取得(最大10個まで)。
配列からツイートID (id)と発信者の名前(user->name)とツイート本文(text)を取得。
ツイートIDをtweet_id_stamp.txtに保存してある値と比較して大きいもの(新しいもの)であれば読み上げる。
発信者の名前のアルファベットは読まない(強引だけど)
ツイート本文はリンクと記号を読まない、他テキトーに整形、改行や空白を利用して分割して読み上げ。

今回はツイートがタイムラインに追加されたら自動的に取得して読み上げというものではないが、php /path/tsubuyakundesu.phpをcrontabに仕掛ければ1分毎だとか3分毎だとかにタイムラインを拾ってきて新しいツイートだけ読み上げてくれる。
一応、手抜きながらもツイートIDで比較しているのでツイート読み上げの重複はない筈。

最低限の動作をするだけのコードになっているので「何だコレ?危なっかしいな」という部分が多々あるとは思うけどご容赦というか勝手に改良して貰えればと。

関連記事:


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です