11

Raspberry Piに喋らせる

Raspberry Piが音声で状況報告してくれると、画面を接続していなくても状況がわかって便利です。
音はUSBスピーカで出した方がいいですね。ヘッドフォン端子からの出力は無音でもブチブチ音が出ててきびしい。

OSはraspbian使ってる。

OpenJTalkをインストールする


Mac/Linuxに日本語を喋らせるに書いたOpenJTalkをセットアップする

sudo apt-get install open-jtalk open-jtalk-mecab-naist-jdic htsengine libhtsengine-dev hts-voice-nitech-jp-atr503-m001

デフォルトの声が怖いのでMMD agentの声を入れる
wget http://downloads.sourceforge.net/project/mmdagent/MMDAgent_Example/MMDAgent_Example-1.3/MMDAgent_Example-1.3.zip
unzip MMDAgent_Example-1.3.zip
sudo cp -R MMDAgent_Example-1.3/Voice/* /usr/share/hts-voice/

OpenJTalkに喋らせるコマンドを作った。適当な場所に置いておく。
jsay はい
で喋る。


スピーカ

USBスピーカ接続するとドライバインストールや設定なしですぐ音がだせる。


音をヘッドフォン端子から出す

R-Pi Troubleshooting – eLinux.orgより
sudo amixer cset numid=3 1
1じゃなく2にするとHDMIから出る。0は自動判別


ヘッドフォン端子の音量調節

sudo apt-get install alsa-utils
amixer set PCM 0%
amixer set PCM 20%
のようにして設定する。
でもプチプチ音がひどい。

ファームウェアアップデート

ファームウェアアップデートしたら音が良くなるらしいのでやってみる
https://github.com/Hexxeh/rpi-update
READMEの通りにやる。
Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS
Performing self-update
sARM/GPU split is now defined in /boot/config.txt using the gpu_mem option!
Updating firmware (this will take a few minutes)
fatal: Failed to resolve 'HEAD' as a valid ref.
動かない。


結論:USBスピーカ買え

0

JSでHTMLにScriptタグを挿入する

var load_script = function(url, onload){
var script = document.createElement("script");
script.src = url;
if(typeof onload === "function"){
script.onload = onload;
script.onreadystatechange = function(){
if(readyState=="loaded" || readyState=="complete"){
onload();
}
};
}
document.body.appendChild(script);
};

load_script("//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js",function(){
alert("script load success!!");
});

0

PhoneGap/CordovaのWebViewの拡張方法(調査)

Android版PhoneGap(Cordova)で新しいwebブラウザのプロトタイプ的なものを作る方法を調べた。
既存のwebサイトを表示して、その上に自作のUIを表示したりしてページを操作できるようなのを作る。
例えばbubble cursorを常時使用し続けるAndroid用webブラウザ等が作れる。

っぽい方法をわりと真面目に調べた。まだ試していない。


手順

PhoneGapプラグインを作ると、Java側でonNativeReadyイベントを受け取れる。
そのタイミングでスクリプトタグをHTMLの末尾に挿入すれば、既存のwebサイト上にcordova.jsを読み込ませたり、自分のスクリプトを実行したりできる。
(試していないが多分できると思う)


既存サイトにスクリプトタグを挿入する


スクリプト挿入するbookmarkletを作る。

これを
(function(){
var load_script = function(url){
var script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
};
load_script('//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js');
})();


圧縮してbookmarklet形式にする
javascript:(function(){var e=function(e){var t=document.createElement("script");t.src=e,document.body.appendChild(t)};e("//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js")})();
nodeのuglify-jsを使った


試しにjQueryが読み込まれていないサイトで実行してみると、jQueryが使えるようになる


webView上でスクリプト実行


MainActivity.java のsuper.loadUrl();でindex.htmlを読み込んでいる。
その後で
super.loadurl("javascript:alert('hello!!!')");
を書くと、ページを読んだ時に実行できる。


前述のbookmarkletをloadUrlすればいい。


しかし、alertと違って前述のscriptタグ挿入bookmarkletは、HTMLがロード完了してDOMが構築し終わってからでないと動かなかった(当たり前だが)
また、MainActivity.javaに書いても最初の1ページ目でしかスクリプトが挿入できないので、ページ遷移する都度scriptタグ挿入してやる必要がある。


とりあえず後輩のうすきくんと一緒に検証してみた。

MainActivityの中で
super.loadUrl("http://www.yahoo.co.jp");
final MainActivity self = this;
new Thread(){
public void run(){
try{
Thread.sleep(10000);
self.loadUrl("javascript:(function(){var e=function(e){var t=document.createElement('script');t.src=e,document.body.appendChild(t)};e('http://shokai.org/alert.js')})();");
}
catch(Exception ex){
Log.e(ex);
}
}
}.start();
(このコード、思い出して書いたので多少間違ってるかも)


なんと動いた。
ページ遷移完了後にloadUrlでbookmarklet実行したいんだけど、イベントが取得できないので10秒待ってからスクリプトタグを挿入した。
まずはPhoneGapのwebview上に後からスクリプトタグ挿入できるかの検証なので、alert.jsの中身はalert(“script insert success!!!”);しか書かないようにした。


あとは、10秒待つのではなくページ遷移毎にスクリプトタグ挿入できれば目標達成できる。


JavaScriptファイルはfile://の場所で指定してもロードしてくれなかったが、HTTPでアクセスできる場所に置いたらロードしてくれた。gistやgithubとかに置いてもいいと思う。


ページ遷移イベント


PhoneGapプラグインを作れば、外部サイトをロードした場合でもページ遷移完了イベントを受け取れる。


WebViewの画面遷移やロード完了イベントは、WebViewClientを設定すればイベントを受けられるのだが、PhoneGap(Cordova)本体が先にWebViewClientを設定してしまっている。
つまり先客がいるわけで、自作のWebViewClientをPhoneGapに食わせる事はできない。


とりあえずCordovaのソースコードを読んでみる
https://github.com/apache/incubator-cordova-android


framework/src/org/apache/cordova/CordovaWebViewClient.java がWebViewClientを継承したクラスで、コイツがPhoneGapのWebViewのイベントを全て受信している。
これを書きなおして自分でcordova.jarをビルドしてもいいのだが、本体を修正すると更新についていけなくなるので避けたい。


さらに辿ってみると、 framework/src/org/apache/cordova/CordovaWebViewClient.java からpostMessage関数で”onNativeReady”というイベントが送られている。
onNativeReadyは framework/assets/js/cordova.android.js の中から呼ばれている。
PhoneGapでよく使われるJS側のdevicereadyイベントの直後に呼び出される。

んで framework/src/org/apache/cordova/api/PluginManager.java から全プラグインにpostMessageの内容が中継されているので、

phonegap pluginを作って、onMessageで”onNativeReady”イベントを受け取れば、ページが読み込み終わった事がJava側でわかるはず。


そのタイミングでloadUrlにbookmarklet形式でスクリプトタグ挿入させればいいのではないか?

という所まで調べたのでうすきくん頑張ってください・・


PhoneGap pluginの作成


このへんが参考になると思われる。

Apache Cordova API Documentation Plugin Development Guide
橋本商会 » PhoneGap NFC Plugin作った

プラグインの解説はJavaのコードをJavaScriptから利用するためのインタフェースを作る部分が大半なのだが、
今回はPluginManagerが中継しているonNativeReadyイベントを受け取ってブックマークレット実行するだけなので、ほとんどの部分を読み飛ばして良いと思うしJSも書く必要ないと思う。

0

RubyのHashの初期化

Hash、初期化しておくと何を入れるのか明確にできるし、
Hashを引数にとる関数で使う時も、nilでもいいから初期値を入れておけばHash#has_key?でいらん値をはじくのに使えて便利。


さっき作ってた物の中の処理で、
追加した順番に番号(indexというフィールド)を振りたいと思ってlambdaを入れてみたら動いた

#!/usr/bin/env ruby
fields = Hash.new{|h,k|
h[k] = {
:index => lambda{ fields.keys.size }.call,
:type => String,
:default => nil,
:value => nil
}
}

p fields

fields[:foo][:value] = 'shokai'
p fields

fields[:bar][:value] = 100
fields[:bar][:type] = Fixnum
p fields


追加した順番にindexが増える、ちょっと便利
{}
{:foo=>{:index=>0, :type=>String, :default=>nil, :value=>"shokai"}}
{:foo=>{:index=>0, :type=>String, :default=>nil, :value=>"shokai"}, :bar=>{:index=>1, :type=>Fixnum, :default=>nil, :value=>100}}


最近Ruby書いてて思うんだけど、APIの使い勝手を優先するために少しがんばれば大抵なんでも実現できてすごい。

0

Sinatra用のcometプラグインを作った

追記:RocketIOに統合されました → 橋本商会 » Sinatra RocketIOというプラグイン作った、これでWebSocketとCometが使える

******

作った

Sinatra Comet I/O

インストール

gem install sinatra-cometio



通信を意識せずに、サーバー側からクライアントの関数、クライアント側からサーバーの関数を呼び出せる。


サーバーからクライアントへプッシュする例

サーバー側


Ruby
require 'sinatra'
require 'sinatra/cometio'

CometIO.push :chat, :name => "shokai", :message=> "hello work!!"


クライアント側


HTML
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="<%= cometio_js %>"></script>

JS
var io = new CometIO().connect();
io.on("chat", function(data){
console.log(data.name + " : " + data.message); // -> "shokai : hello work!!"
});


クライアント→サーバーの例や、新規クライアント接続イベントやエラーイベント等についてもSinatra Comet I/Oに書きました


サンプルとしてHerokuでチャットを作った。 http://cometio-chat.herokuapp.com

チャットの場合同時に300クライアントぐらいしか接続できないっぽい。スケールさせる方法はよくわからない。
少人数で使うちょっとしたツールにリッチなUIを持たせる時などに便利だと思います。


*チャットサンプルですが1台のマシンから2つchrome開いても、交互に通信するみたいです。片方をsafariにするか、chromeをプライベートブラウズモードにすると動きます
色々やってたら大丈夫になりました