2012年01月25日

食育ならぬ数育

アンパンマンが食育に役立っているという話がある。
幼い頃から色々な食べ物のキャラクターに触れている効果であろう。
算数嫌いや数学嫌いに応用できないかとキャラクターを考えてみる。

■タス子さん
調味料やソースに一味たすのが得意。
隠し味のスペシャリスト。

■ヒク夫くん
サヤエンドウの筋取り、エビの背わた取りなど、何かを引き抜くことに躍起になっている。
最近、挽き肉作りを覚えた。

■カケルくん
料理にかけるソースや調味料を選ばせれば天下一品。
タス子さんの作るソースが大好き。

■ワル実さん
クルミやピスタチオを割ったり、某乳酸飲料(瓶入)を水で割ってくれる。
お酒を割るのも得意らしいが、子ども向けじゃないので裏設定になっている。
くるみ割り人形を集めるのが趣味。
名前に反してイイ子だけど、扱いにくいキャラクターのため出番が少ない。

なんかアンパンマンみたいだけど、まあ良いや。

続き。

■クリ・アーゲーとクリ・サーゲー
初めて見ると、皆が面食らう。
でも、慣れれば当たり前すぎて気にもならなくなる。
いわゆる空気。

■びぶん親分とせきぶん親分
「シスウ」というものを増やすか減らすかでいつもケンカしている。
でも、いざという時はコンビを組んで大活躍することもある。
コンビ名は「ビブン セキブン 良い気分」。

■紙神(かみがみ)
この世界を最初に想像した神。
なんでもペラペラ喋ってしまうため、相談事には要注意。
最近、デンノー世界に紙世界が脅かされているのを嘆いている。

■サインくんとコサインくんとタンジェントちゃん
三角山に住む三兄妹。
サインくんとコサインくんは双子。
びぶん親分やせきぶん親分とからむと、性格が入れ替わったり体が入れ替わったりする。
タンジェントちゃんは気分屋のため、興奮するとそのテンションは無限大で紙の世界も突き抜ける。

■ぎょうれつ大名
ぎょうれつ大名の大名行列は美しいと評判で、家来が織りなすマスゲームは息を飲むほど。
実はデンノー世界と交流があり、サン・ジゲン画伯とは旧知の仲。
とある映画の製作に関わったという設定もあったが制作元の許可が降りなかった。

■すうれつざむらい
ぜんか式剣術の達人で、得意技は水を切るように切る「斬」。
刀剣を集めるのが趣味で、お気に入りの剣は南蛮渡来のフィボナッチ。
ゼンマイはついていない。

ネタが尽きたので終わる。

2012年01月08日

WebSocketの基礎知識(2012年年始版)

WebSocketに関する、よくある質問や知っておいたほうがよさそうなことをまとめてみました。
技術的なことについては深くはふれていません。
あやまりがあれば指摘してください。

Q.
仕様が2つあるみたいだけど...
A.
WebSocketは、主にブラウザ上で使うことを考慮された通信の規格で、通信の「プロトコル」とJavaScriptから使うための「API」に仕様がわかれています。前者は「WebSocket Protocol」として「IETF」が、後者は「WebSocket API」として「W3C」がかかわっています。

Q.
WebSocketってまだ仕様が固まってないんじゃないの?
A.
WebSocket ProtocolはIETFのRFCの「標準化提案」に、WebSocket APIはW3Cの「勧告候補」になりました。IETFにもW3Cにも承認プロセスがあり、それぞれ最後には「標準」と「勧告」になりますがここでは詳細は述べません。
一応承認プロセスの途中なので、実際に使うには躊躇するかもしれません。しかし、誤解を恐れずに大胆に言うと、『こういう仕様を書いたんだけど、おかしかったら意見ください。』という段階を過ぎ、『仕様が固まったので使ってみてください。実際に使ってみておかしかったら修正します。』という段階に達しています。

Q.
WebSocketとWebSockets、どっちが正しいの?
A.
仕様の「草案(ドラフト)」で一時混乱していましたが、WebSocket ProtocolのRFCやWebSocket APIの勧告候補では「WebSocket」で収束しました。しかし、ブログのレベルではWebSocketの表記が統一されていません。「Web」と「Socket」をわけたり、単数形であったり複数形であったり、表記のゆれがあります。つまり「WebSocket」「WebSockets」「Web Socket」「Web Sockets」の4つです。このうち、単語をわける「Web Socket」「Web Sockets」は最近ではほとんど見られません。また、日本語のブログでは「WebSocket」が大半ですが、英語圏のブログでは「WebSockets」が多く見られます。英語圏の情報も検索したい場合には、もうしばらく「WebSockets」をキーワードに追加しておく必要があります。英語圏のブログで使われる単語も、今後は仕様にあわせて「WebSocket」になっていくのではないでしょうか。

Q.
どのブラウザで使えるの?
A.
「Google Chrome」と「Safari」では、そのままWebSocketが使用できます。
後述するセキュリティの問題のため「Firefox」では一時的に無効になっていましたが、現在のバージョンは一応使用できます。ただし、APIのオブジェクト名が「ベンダープレフィックス」付きになっています(本来はWebSocketというオブジェクト名ですが、MozWebSocketになっています)。Firefoxの現在の開発バージョンでベンダープレフィックスが外れたので、現在の開発バージョンの正式リリースとなるFirefox11(3月リリース予定)では普通に使えるようになるでしょう。
「Opera」にもWebSocketが実装されているのですが、セキュリティの問題のため標準の設定では無効にされています。今のバージョンでも設定を変更すれば使えるようになりますが、次のバージョンでは正式に有効になるかもしれませんね。
「Internet Explorer」の開発バージョンが既にWebSocketに対応しています。Windowsの次期バージョンが今年リリースされる予定ですので、多分それに合わせてIE10もリリースされるのではないでしょうか。
iPhone・iPad・iPod touch等の「iOS(ただしバージョン4.2.1以上)」でも、標準ブラウザのSafariがWebSocketに対応しています。
「Android」の標準ブラウザはWebSocketに対応していませんが、Android版のFirefoxがベンダープレフィックス付きでWebSocketに対応しています。
組み込み向けのブラウザである「NetFront Browser」は、発表されたばかりの最新バージョン「NetFront Browser NX v2.0 DTV Profile」が対応しているそうです。

Q.
Node.jsがないとWebSocketは使えないって聞いたんだけど...
A.
よくある誤解ですが、他の環境でも使えます。Node.jsは実行言語にJavaScriptを使用するサーバで、ノンブロッキングI/O・イベント駆動を特徴としています。これらの特徴は、WebSocketと非常に親和性があり、WebSocketサーバの実装の第一候補になるのも頷けます。
しかし、実行言語がJavaScriptであることを除けば、同じようなことが可能な環境は他にもあります。例えばJavaで実装されたサーバであるJettyは、ノンブロッキングスレッディングモデルを採用し、イベント発動時にInterfaceの実装を呼び出します。
自分の得意言語や実行環境の制限、将来性などを総合的に判断し、環境を選択してみてはいかがでしょうか。

Q.
WebSocketってセキュリティに問題があるんじゃなかったっけ?
A.
確かにWebSocket Protocolの古いバージョンの草案ではセキュリティホールが指摘されていましたが、RFCの現在の仕様では既に解決済みです。ただし、まだ古いバージョンで実装されたブラウザもあるため注意が必要です。実は、FlashやJava Appletで通信を行う際にも同様の問題があるのですが、普及してしまっているため修正が難しい状態となっています。

Q.
また、使うには早いんじゃないの?
A.
インターネットの世界には、仕様策定段階の技術でも使われる文化があります。また、WebSocketは仕様が固まる前から注目されてた技術であり、一部のブラウザのサポートや色々なサーバの実装が進んでいたため、それなりに熟れていると言えます。個人的な意見ですが、ノウハウがまだ貯まっていないため、商用にはまだ躊躇するかもしれませんが、個人で使うのであれば決して早くはありません。

2011年12月25日

モバイルガジェットだけでWebSocketネットワーク

モバイルガジェットの環境だけで、WebSocketのチャットができるようにしてみた。
モバイルルータで、iPhone2台とAndroid(IS01)を同一の無線LAN配下とし、これらの端末をWebSocketで結んだ。
WebSocket ServerにはAndroidにインストールしたWSBroadcaster for Android(以下、WSBroadcaster)を、WebSocket ClientにはiPhone上のSafariを使っている。
WSBroadcasterの設定は以下の通り。
HTTP Path      : html
WebSocket Path : ws
Port Number    : 40320
Response Type  : All
SDカード直下のディレクトリ「WSBroadcaster」が、HTTP Serverの公開ディレクトリとなる。
このディレクトリに下に記載するHTML,CSS,JSを保存後、WSBroadcasterを起動し更に「Start」ボタンでServerを起動する。
iPhoneのSafari等のWebSocketに対応したブラウザで「http://[Androidのアドレス]:40320/html/index.html」にアクセスするとチャットが開く。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<title>WebSocketChat</title>
<link type="text/css" href="common.css" rel="stylesheet">
<script src="common.js" type="text/javascript"></script>
</head>
<body>
<div><input type="text" id="message" /><input type="button" id="send" value="send" /></div>
<div id="messages"></div>
</body>
</html>
common.css
#message{
  width:80%;
  margin:1% 5%;
  font-size:24px;
}
#messages{
  width:80%;
  height:400px;
  margin:1% 5%;
  font-size:24px;
  overflow:hidden;
  border:0px none transparent;
}
#send{
  width:80%;
  height:50px;
  margin:1% 5%;
  font-size:24px;
}
common.js
var ws;

function $(id){
  return document.getElementById(id);
}

function onOpenWebSocket(){
  $("send").addEventListener("click",sendMessage,false);
  dispMessage("connected");
}

function onCloseWebSocket(){
  $("send").removeEventListener("click",sendMessage,false);
  dispMessage("disconnected");
}

function onMessageWebSocket(event){
  var msg=event.data;
  if(msg==""){return;}
  dispMessage("> "+msg);
}

function onUnload(){
  ws.close();
}

function dispMessage(msg){
  var elem=document.createElement("div");
  elem.appendChild(document.createTextNode(msg));
  if($("messages").hasChildNodes()){
    $("messages").insertBefore(elem,$("messages").firstChild);
  }else{
    $("messages").appendChild(elem);
  }
}

function sendMessage(){
  var message=$("message").value;
  if(message==""){return;}
  ws.send(message);
  $("message").value="";
}

function initial(){

  var protocol=(location.protocol=="https:")?"wss":"ws";

  var host=location.host;

  var url=protocol+"://"+host+"/ws";

  ws=new WebSocket(url);

  ws.addEventListener("open",onOpenWebSocket,false);
  ws.addEventListener("close",onCloseWebSocket,false);
  ws.addEventListener("message",onMessageWebSocket,false);

  window.addEventListener("unload",onUnload,false);

}

window.addEventListener("load",initial,false);
以上。

2011年12月19日

Androidで動かせるWebSocket Server「WSBroadcaster for Android」

「Android上で動くWebSocketサーバを作った」という話はいくつか聞いていたけど、実装例がなかなか出てこないので自分で作った。
下手に作り込んで汎用性がなくなるのも嫌なので、WebSocketのメッセージの中継に機能を絞っています。WebSocketのコアな部分はJettyにお任せ。通信キャリアが変なことをしていなければグローバルなIPアドレスでも使えるはずだけど、手元に環境がないため確認はできてない。また、所有している端末だと、80番ポートで起動できない。LInuxでは、80番ポートを開くためには権限が必要なので、多分これに起因する現象だと思う。rootedな端末ならもしかして起動できるかもしれない。

これで、出先で突然WebSocket Servserが必要になっても一安心ですね。
使い方
メイン画面は、上に起動/停止用のボタン、下にはServerのステータスと設定値が表示されている。

Port Numberはそのままの意味。

Response Typeは、受信したメッセージをどう扱うかを選択できる。「Nothing」は「何もしない」、「Echo」は「送信元に送り返す」、「Other」は「送信元を含まない全ての接続しているClientに再送信」、「All」は「送信元を含む全ての接続しているClientに再送信」、となっている。

Reriodic Messageは、全Clientに対してServerから定期的にメッセージを送るか否か。Interval of Reriodic Messageは、定期的なメッセージの間隔を秒で指定し、Text of Reriodic Messageはその内容。

設定値の変更は、オプションメニューから開ける設定画面で行う。
ToDo
ReadMeを書く。
メッセージを受け取った場合の動作をどうするのか。
仮に、ping frame送信機能を実装した場合、pong frame受信時の挙動をどうするのか。

2011年11月08日

モダンブラウザのタイマー処理の制限をWebSocketを使って突破する(WebWorkersについて追記あり)

最近のブラウザは、パフォーマンス改善のために、バックグラウンドタブのsetTimemout/setIntervalの実行遅延の下限を1000msに制限している。
例えば、フォアグランドとバックグラウンドで、以下のようなコードの挙動を比べてみるとよくわかる。
(
    function() {
        var ms = 100;
        var old = new Date().getTime();
        var f = function(){
            var now = new Date().getTime();
            console.log((now-old)+"ms");
            old = now;
            setTimeout(f,ms);
        }
        setTimeout(f,ms);
    }
)();
(
    function() {
        var ms = 100;
        var old = new Date().getTime();
        var f = function(){
            var now = new Date().getTime();
            console.log((now-old)+"ms");
            old = now;
        }
        setInterval(f,ms);
    }
)();
アニメーション処理であれば、今後はrequestAnimationFrameを使ったりCSS3のTransitionsやAnimationsを使うというのが正道なんだろう。
アニメーション以外の処理は、下限を1000msに制限されても正常に動作させるようにコードを修正するのが筋なんだろうと思う。
しかし、「どうしてもsetTimout/setIntervalでないといけない」「1000ms未満の間隔で実行しなければいけない」等の要望もあるかもしれない。
そこで、イベントドリブンな処理は制限されないことを利用して、setTimemout/setIntervalとほぼ同等な処理を間隔1000ms未満で実行できるコードを書いてみた。
まだ、実験コードレベルなので注意。
遅延処理をサーバ側で行い、実行するタイミングでクライアントに通知することで実現している。
通信にはWebSocketを使っているので一部のブラウザではまだ動作しないが、この問題はブラウザのバージョンアップで早々に解決すると思う。
コードはGitHubに上げてある。
インストール方法や使い方もGitHub上のREADMEを参照。
以下、使い方のサンプル。
<!DOCTYPE html>
<html>
<head>
<title>ForcedTimer sample</title>
<script type="text/javascript" src="../src/client.js"></script>
<script type="text/javascript" src="sample.js"></script>
<link rel="stylesheet" type="text/css" href="sample.css">
</head>
<body>
<div id="timeout_area">
<input type="button" id="timeout_button" value="set/clear timeout" />
<textarea id="timeout_output"></textarea>
</div>
<div id="interval_area">
<input type="button" id="interval_button" value="set/clear interval" />
<textarea id="interval_output"></textarea>
</div>
</body>
</html>
html, body{
    margin:0;
    padding:0;
    height:100%;
}
#timeout_area{
    width:49%;
    height:99%;
    float: left;
}
#interval_area{
    width:49%;
    height:99%;
    float: right;
}
#timeout_button, #interval_button{
    display:block;
}
#timeout_output, #interval_output{
    width:80%;
    height:80%;
    font-size: 12px;
    font-family: monospace;
    display:block;
}
var initialize = function() {

    var timeoutButton  = document.getElementById("timeout_button");
    var intervalButton = document.getElementById("interval_button");
    var timeoutOutput    = document.getElementById("timeout_output");
    var intervalOutput   = document.getElementById("interval_output");

    var ms = 100;
    var timerIds = {};

    var getDate = function() {
        var dtm = new Date();
        var result =
            ("0000" + (dtm.getFullYear()     + 0)).slice(-4) + "/" +
            (  "00" + (dtm.getMonth()        + 1)).slice(-2) + "/" +
            (  "00" + (dtm.getDate()         + 0)).slice(-2) + " " +
            (  "00" + (dtm.getHours()        + 0)).slice(-2) + ":" +
            (  "00" + (dtm.getMinutes()      + 0)).slice(-2) + ":" +
            (  "00" + (dtm.getSeconds()      + 0)).slice(-2) + "." +
            ( "000" + (dtm.getMilliseconds() + 0)).slice(-3) + ""  ;
        return result;
    };

    var timeout = function() {
        timeoutOutput.value = getDate() + "\n" + timeoutOutput.value;
        timerIds["timeout"] = ForcedTimer.setTimeout(timeout, ms);
    };

    var interval = function() {
        intervalOutput.value = getDate() + "\n" + intervalOutput.value;
    };

    timeoutButton.addEventListener(
        "click",
        function() {
            if ("timeout" in timerIds) {
                var timerId = timerIds["timeout"];
                ForcedTimer.clearTimeout(timerId);
                delete timerIds["timeout"];
            } else {
                timerIds["timeout"] = ForcedTimer.setTimeout(timeout, ms);
            }
        },
        false
    );

    intervalButton.addEventListener(
        "click",
        function() {
            if ("interval" in timerIds) {
                var timerId = timerIds["interval"];
                ForcedTimer.clearInterval(timerId);
                delete timerIds["interval"];
            } else {
                timerIds["interval"] = ForcedTimer.setInterval(interval, ms);
            }
        },
        false
    );
};

window.addEventListener("load", initialize, false);
2011/11/10 追記
Twitter上でツッコミがありました。
気になったので試したWebWorkersの中のtimerはバックグラウンドタブでも精度荒くならない at Firefox7
バックグラウンドタブでも通常精度でタイマー動かしたいときにはWebWorkersで setInterval(function(){ postMessage("10msたったぞ") }, 10) とかやればいい
「なんですとぉ!」ってことで試してみた。
指摘された通りバックグラウンドタブでも1000ms未満で動いた。
malaさんありがとうございます。
せっかくWebSocketで通信インターフェイスを作ったので、WebWorkers版も同様の作りにして動作するようにしました。
WebWorkers版もGitHubにあげているので興味のある方はどうぞ。

2011年11月05日

どこでも「do a barrel roll」するためのグリモンとブックマークレット

Googleで「do a barrel roll」を検索すると、画面が回転すると話題になっていた。
Googleだけでしか実行できないのは寂しいので、グリモンとブックマークレット書いた。
適当なページを開いて、テキストボックスかテキストエリアに「do a barrel roll」と入力すると回転する。

(
    function(){
        document.body.addEventListener(
            "keyup",
            function(e){
                e = e||event;
                var tagName = e.target.tagName.toLowerCase();
                if (tagName != "input" && tagName != "textarea"){
                    return;
                }
                if (e.target.value != "do a barrel roll"){
                    return;
                }
                var cssValue = [
                    "@-moz-keyframes roll {100% {-moz-transform:rotate(360deg);}}",
                    "@-o-keyframes roll {100% {-o-transform:rotate(360deg);}}",
                    "@-webkit-keyframes roll {100% {-webkit-transform:rotate(360deg);}}",
                    "@keyframes roll {100% {transform:rotate(360deg);}}",
                    "body{",
                    "-moz-animation-name:roll;",
                    "-moz-animation-duration:4s;",
                    "-moz-animation-iteration-count:1;",
                    "-o-animation-name:roll;",
                    "-o-animation-duration:4s;",
                    "-o-animation-iteration-count:1;",
                    "-webkit-animation-name:roll;",
                    "-webkit-animation-duration:4s;",
                    "-webkit-animation-iteration-count:1;",
                    "animation-name:roll;",
                    "animation-duration:4s;",
                    "animation-iteration-count:1;",
                    "}"
                ].join("");
                var style = document.createElement("style");
                if ("textContent" in style) {
                    style.textContent = cssValue;
                } else {
                    style.innerText = cssValue;
                }
                var body = document.body;
                body.appendChild(style);
                setTimeout(
                    (
                        function(body,style){
                            return function(){
                                body.removeChild(style);
                            };
                        }
                    )(body,style),
                    5000
                );
            },
            false
        );
    }
)();

2011年10月29日

「プログラマに半角全角の入り乱れた数字を見せ続けると死ぬ。」で死にたくないのでブックマークレット書いた

上記のサイトに行って、下記のブックマークレットを実行。

(
    function(){
        var e = document.getElementById("sine");
        var d = {
            "0":"0",
            "1":"1",
            "2":"2",
            "3":"3",
            "4":"4",
            "5":"5",
            "6":"6",
            "7":"7",
            "8":"8",
            "9":"9"
        };
        setInterval(
            function(){
                e.innerHTML=e.innerHTML.replace(
                        /[0-9]/g,
                    function(a){
                        return (Math.random()<0.05)?d[a]:a;
                    }
                );
            },
            100
        );
    }
)();

2011年10月22日

Androidアプリのパーミッションを一覧表示するPermissionCheckerというツール作った

大したことはしていない。
インストールされているアプリのパーミッションを取得し、一覧表示しているだけ。
一応、アプリケーション別とパーミッション別で表示できる。
ソースはGitHubに公開。
MITライセンスなので、使いやすく改良したり適当にどうぞ。
2011/10/22 追記
パーミッションを確認したり一覧するツールは他にもあったけど、パーミッション別に確認できるものがなかったので作成。

2011年10月09日

「org.bluestemsoftware.open.maven.plugin」の「launch4j-plugin」をLion(OS X 10.7)で実行する方法

某チャットでの話。
コンパイルするために、ちょっと前まで使っていたメインマシンを分解してた。
mavenのリリースプラグインを実行
リリースプラグインで使っているexe化ツールがLionに未対応
旧マシン(Leopard)でコンパイルを試みる
熱暴走でコンパイル中に落ちる
ファンがまわらないのが原因と思われたので分解して掃除
風が吹くと桶屋が儲かる式な何か。
言葉遊びをしていたらujihisa氏が問題を解決してくれたのでメモ。
実際はもっと紆余曲折あったんだけど、本筋の時間軸を入れ替えて整理した。
問題
Javaで書いたアプリケーションをWindowsで簡単に実行できるようにする「Launch4j」というツールがある。
「Launch4j」を「Maven」で使えるようにする「org.bluestemsoftware.open.maven.plugin:launch4j-plugin(以下、launch4j-plugin)」というプラグインがある。
「launch4j-plugin」の最新バージョンである1.5.0.0がLionで動作しない。
症状の詳細
「launch4j-plugin」を実行すると以下のようなエラーが発生する。
[INFO] --- launch4j-plugin:1.5.0.0:launch4j (default) @ WSBroadcaster ---
[INFO] Platform-specific work directory already exists: /Users/[user id]/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0
[INFO] launch4j: Compiling resources
[ERROR] 
net.sf.launch4j.BuilderException: net.sf.launch4j.ExecException: Exec failed(2): [Ljava.lang.String;@64b37089
(以下略)
同時にアラートが開き、「PowerPC アプリケーションは現在サポートされていないため、アプリケーション windres を開けません。」と表示される。
原因
「launch4j-plugin」が内部に持っているコマンド「windres」と「ld」が、PowerPCにしか対応していない。
% cd ~/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0/launch4j-plugin-1.5.0.0-workdir-mac/bin/
% ls
COPYING    ld         readme.txt windres
% cat readme.txt 
The MinGW binutils were built on Mac OS X 10.4 by Peter Centgraf
% ./windres --version
Launch of "windres" failed: the PowerPC architecture is no longer supported.
% ./ld --version     
Launch of "ld" failed: the PowerPC architecture is no longer supported.
解決方法
「~/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0/launch4j-plugin-1.5.0.0-workdir-mac/bin/」配下にある「windres」と「ld」を入れ替える。
「windres」と「ld」は、「binutils」のものを使う。
詳細は後述。
注意点
「Homebrew」で「binutils」をインストールできるが「windres」が含まれない。
「ld」はLionに標準で含まれるが、「launch4j-plugin」が使っている「-mi386pe」というオプションに対応していない。
詳細な解決方法
まず、既存のコマンドを退避する。
% cd ~/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0/launch4j-plugin-1.5.0.0-workdir-mac/bin
% mkdir old
% mv ld old
% mv windres old
次に、以下のURLから「binutils」をダウンロードする。
当エントリ執筆時の最新は「binutils-2.21.1.tar.bz2」だった。
「binutils-2.21.1a.tar.bz2」というものもあったが、hashは「binutils-2.21.1.tar.bz2」と同じだった。
「binutils-2.21.1.tar.bz2」をダウンロードしたディレクトリに移動し、以下のコマンドを実行。
% tar -xjvf binutils-2.21.1.tar.bz2
(略)
% cd binutils-2.21.1
% ./configure --target=i686-pc-mingw32
(略)
% make
(略)
実行ファイルだけ必要なので、「make install」は不要。
「windres」は「binutils」ディレクトリ直下に、「ld」は「ld」直下に「ls-new」という名前でできているはず。
以下のコマンドを実行し、コマンドを複製する。
% cp binutils/windres ~/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0/launch4j-plugin-1.5.0.0-workdir-mac/bin
% cp ld/ld-new ~/.m2/repository/org/bluestemsoftware/open/maven/plugin/launch4j-plugin/1.5.0.0/launch4j-plugin-1.5.0.0-workdir-mac/bin/ld
これで解決。
thx! ujihisa!
参考URL
windresについての説明
「Launch4j」がLionで動作しないことのバグ報告

2011年08月20日

OS X Lion(10.7)に羊を数えさせる方法

ふと思いついたので書いてみたら簡単に動いた。

OS X Lion(10.7)が日本語の音声データに対応した。これで、Text to Speechで日本語を喋らせることができる。
標準では、日本語対応の音声データは入っていないはずなので、インストールが必要。
日本語音声データの簡単なインストール方法
「システム環境設定」の「スピーチ」から「テキスト読み上げ」を選択し、「システムの声」プルダウンメニューの「カスタマイズ...」を選択する。
開いたダイアログから、「日本語(日本)」の中の「Kyoko」にチェックを入れ「OK」をクリック。
音声データがインストールされていない場合は、ダウンロードが始まる。
Lionに羊を数えさせる
次、ターミナルを開いて以下を実行。
% ruby -e "(1..10000).map{|n| system(\"say -v Kyoko 羊が\"+n.to_s+\"匹\");sleep 1}"
10000匹まで数えてくれますよ!
その他の情報
実は、日本語で羊を数えても全然眠くならないらしい。諸説あるようだけど、英語だと「sleep」と「sheep」の発音が似ているからとかなんとか...。
Google

タグ クラウド