北朝鮮のミサイル到達範囲

観てなかったから知らなかったんだけど、どこかのテレビ局がメルカトル図法の世界地図に同心円を重ねるというお前の世界はドラクエかファイナルファンタジーなのかとツッコミたくなる訳のわかんないことをしでかしたらしい。図を作った奴もスタジオの奴も全員そろって頭の中にオカラが詰まってる人の形をした何からしい。
同心円を重ねるなら正距方位図法を使わなきゃダメだってのは(今だと)中学校で習うことの筈だけど中学校行かなかったのかな?

今だとウェブで正距方位図法の世界地図を任意の中心点を指定して作成できる。
たとえばhttp://maps.ontarget.cc/azmap/

北朝鮮を中心にした正距方位図法の世界地図
北朝鮮を中心にした正距方位図法の世界地図はこうなる。距離の目安になる同心円も入れられるのでわかりやすい。

北朝鮮から距離8000kmで、東は北米大陸の東北側のボコッとへこんだところ(ハドソン湾)に到達しようかというところ、西はアラビア半島と東欧がほぼすっぽり入るのが解るかと思うが、テレビの図の作り方だと1万kmでハドソン湾の遥か西のグレートスレーブ湖やグレートベア湖にしか達しない。(西側は不明)
もしもミサイルの射程が本当に1万kmならアメリカ中部やヨーロッバのほぼ全域が収まることになる。助かるのはスペインくらい?

でも、とりあえず今は北朝鮮よりテレビの報道の人のアタマの方が脅威だわ。

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

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

Open JTalkの準備

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

# apt-cache search jtalk
# apt 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
1
2
3
4
5
6
7
8
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 "こんにちは"

2行目を実行して「こんにちは」と言えばOK.
このスクリプトは後で使う

PHPの準備

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

# apt 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を呼び出すだけで殆ど何も考えずにタイムラインを取得したりツイートできる。

↑の方法は古いTwitterOAuthでは有効だが、TwitterOAuth 2.0以降ではTwitterOAuthのドキュメントに従ってphp composerでTwitterOAuthをインストールするのが無難。↑の圧縮ファイルを任意の場所に展開して使う方法はTwitterOAuth 2.0以上だとcaBubdleが無いだののエラーを解決するのに苦しみそう。

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

ツブヤクンデス作成

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

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

tsubuyakundesu.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?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);
        $cmd = './jtalk.sh "' . $speech . '"';
        exec($cmd);
        //分解するなら上の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);
}
?>

TwitterOAuth 2.0以降のバージョンをcomposerでインストールして使用する場合は3行目のPathを適切に変更。それ以外は(Twitterの取得の処理部分は)少なくともTwitterOAuth 3.1まで問題なく動く。

タイムラインの最新のツイートの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で比較しているのでツイート読み上げの重複はない筈。

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

関連記事:

Up