<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>hoelog</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/" />
    <link rel="self" type="application/atom+xml" href="http://txton.net/hoehoe/atom.xml" />
    <id>tag:txton.net,2009-02-13:/hoehoe//3</id>
    <updated>2010-05-11T20:08:31Z</updated>
    <subtitle>BIT from it!</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/" version="4.22-ja">Movable Type Commercial</generator>

<entry>
    <title>Arduino : ArduinoboyでGameBoyのLSDj、Nanoloop、mGBをMIDIで繋げてみた</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2010/100511030849.html" />
    <id>tag:txton.net,2010:/hoehoe//3.240</id>

    <published>2010-05-10T18:08:49Z</published>
    <updated>2010-05-11T20:08:31Z</updated>

    <summary> ゲームボーイの音楽制作ソフトLittle Sound DJやNanoloopの...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Arduino" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Max/MSP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="100401_arduinoboy1" src="/hoehoe/images/100401_arduinoboy1.jpg" width="530" height="335" class="tumb-image" /></p>

<p>ゲームボーイの音楽制作ソフトLittle Sound DJやNanoloopのMIDIケーブルを、Arduinoで作るライブラリArduinoboyで、Max/MSP(Live)を同期させてみたよ。</p>

<p>Max/MSPを始めた記念になんかMIDIで鳴らしたい。でもそんな機材持ってないし。んーじゃ代わりにMIDI同期できるLSDjでゲームボーイでも鳴らすかー。たしかArduino使って同期できるライブラリかシールドがあったはずだし。。とガラクタ箱を漁ること半日、ゲームボーイとその一式を発掘して繋げてみた。</p>


]]>
        <![CDATA[<h3>Arduinoboyで実装</h3>

<p><img alt="100401_arduinoboy2" src="/hoehoe/images/100401_arduinoboy2.gif" width="530" height="370" class="tumb-image" /></p>

<p>構成はこんな感じ。実装にはArduinoとGB上のLSDjやNanoloopを通信させるMIDI I/Oシールドを実装するスケッチ「<a href="http://code.google.com/p/arduinoboy/" target="_blank">Arduinoboy</a>」を使います。<a href="http://www.flickr.com/photos/trash80/2316803721/in/set-72157604068871573/" target="_blank">こんな感じ</a>にMIDIインターフェースの回路を作るか、USBを繋げてMIDIメッセージを送受信します。今回はMAX/MSPからMIDIメッセージを送信させたいのでUSBで繋げます。以下いるもの。<p />

<p><pre>・ゲームボーイ（初代、ポケット、カラー等）
・LSDj、Nanoloop、mGB
・ゲームボーイ通信ケーブル（DMG-004等）
・Arduino
・LED × 5（任意）
・抵抗 330Ω × 5（任意）
</pre></p>

<p>通信ケーブルはGEOとかゲーム探偵団とかの中古屋を回るとショーウィンドウの片隅に転がってたりしますｗ（￥200～￥500くらい）。LEDは状態のモニタ用なんで必要なければいりませんが、雰囲気も大事なんで付けてみます。</p>



<h3>GameBoyとArduinoを接続</h3>

<h4>通信ケーブルを加工</h4>
<p>通信ケーブルを剥いて中のラインを出します。今回は初代GB用のDMG-004を使います。<a href="http://www.flickr.com/photos/trash80/sets/72157608324137185/" target="_blank">ケーブルの解体方法</a>も公開されてるんで参考に。中にはDATA IN（橙）、DATA OUT（赤）、GND（青）、CLOCK（緑）の４本がまとめられています。<a href="http://www.hardwarebook.info/Game_Boy_Link#Pinout" target="_blank">ケーブルによって仕様が違う</a>んでよく確認してください。ブレッドボードで使うんで端子とかも付けておきましょう。</p>



<h4>Arduinoと接続</h4>
<p>通信ケーブルとArduinoは<a href="http://trash80.net/arduinoboy/arduinoboy_schematic_1_1_0.png" target="_blank">回路図を参考</a>に次のように接続。</p>

<p>
CLOCK（緑）→Analog 0<br />
DATA IN（橙）→Analog 1<br />
DATA OUT（赤）→Analog 2<br />
GND（青）→GND<br />
<br />
DIGITAL 8～12 →LED<br />
</p>

<p><img alt="100401_arduinoboy5" src="/hoehoe/images/100401_arduinoboy5.jpg" width="530" height="335" class="tumb-image" /></p>

<p><img alt="100401_arduinoboy3" src="/hoehoe/images/100401_arduinoboy3.gif" width="530" height="335" class="tumb-image" /></p>

<p>Arduinoboyのライブラリを修正していきます。動作モードを4（Midi Input to mGB）、USBモードをON、回路上のボタンは使わないんでメモリ機能をONにしてアップロードします。<br />
<br />
<strong>Arduinoboy1_1_0.pde を編集</strong></p>

<p><pre>
boolean forceMode = true;
int mode = 4;
int numberOfModes = 5;
boolean usbMode = true;
</pre></p>



<h3>MAX/MSPとArduinoを接続</h3>


<p><img alt="100401_arduinoboy4" src="/hoehoe/images/100401_arduinoboy4.gif" width="530" height="423" class="tumb-image" /></p>

<p>前回はArduinoとの通信にMaxduinoを使いましたが、今回はスタンドアローンのArduinoと通信させたいので、Serialオブジェクトを使って直接シリアルポートを叩くパッチを組んでみます。後はコントローラーか、曲を再生させてMIDIメッセージをシリアルに送ってあげれば、GBから音が出てくれるはず。</p>



<h3>動かしてみた</h3>

<object width="480" height="295"><param name="movie" value="http://www.youtube.com/v/3Lyhp77ob6M&hl=ja_JP&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/3Lyhp77ob6M&hl=ja_JP&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"></embed></object>

<p>鳴ったー。ちなみにデモではフルMIDIコントロールできるArduinoboy付属のmGBを使ってます。曲を流す場合には若干BPMを落としてあげた方がいいです。タイトルに偽りありになっちゃたけど、Arduinoboyのモードを変えるとLSDjやNanoloopと同期させることもできるよーですよ。</p>



<h3>まとめ</h3>

<p>パースとか必要なOSCよりもMIDIの方が単純な構造な分、楽に変換できてよいですね。１トラックがモノフォリックで鳴ってるんで、曲全部ならそうとすると結構大変？。いっぱいGB並べるのも楽しそうだけど。。</p>



<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://code.google.com/p/arduinoboy/" target="_blank">Arduinoboy</a></li>
<li><a href="http://www.littlesounddj.com/lsd/" target="_blank">Little Sound DJ</a></li>
<li><a href="http://www.meditations.jp/nanoloop2.0/nanoloop2.html" target="_blank">Nanoloop</a></li>
<li><a href="http://www.hardwarebook.info/Game_Boy_Link#Pinout" target="_blank">通信ケーブルのPINの仕様</a></li>
</ul>
</p>]]>
    </content>
</entry>

<entry>
    <title>Max/MSP : Arduinoでオーディオレベルメーターを作ってみた</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2010/100113153219.html" />
    <id>tag:txton.net,2010:/hoehoe//3.227</id>

    <published>2010-01-13T06:32:19Z</published>
    <updated>2010-05-10T18:11:47Z</updated>

    <summary> 大阪てら子番外編「お菓子でもつまみながら Max/MSP/Jitter とかを...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Arduino" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Max/MSP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="100112_maxmsp1.gif" src="/hoehoe/images/100112_maxmsp1.jpg" width="530" height="335" class="tumb-image" /></p>

<p>大阪てら子番外編「<a href="http://teraco.jp/2010/01/13-125245.php" target="_blank">お菓子でもつまみながら Max/MSP/Jitter とかをいじる会</a>」てのに行ってきたんで、勉強がてらMax/MSP＋ArduinoでLEDオーディオレベルメーターを作ってきたよ。</p>]]>
        <![CDATA[<p>ほい。自分も含めて初心者の多いゆるゆるなMax/MSP会だったんだけど、<a href="http://www.starryworks.co.jp/blog/maxmspjitter.html" target="_blank">流れ的にGainerとかArduinoが出てきた</a>んで、一応持ってたArduinoを取り出してそっちにシフト、なんか光らせようとぱちぽち。とりあえず繋がってLEDがピコピコなりはじめたところでタイムアップ。帰って補習。</p>

<p>作ってたのはベタにLEDオーディオレベルメーター。マイクから音のレベル拾って、８段階にマッピングして、デジタルピンに出力。んで、できたのはこれ。ドライバ部分がなんか不細工だけど、まーいいや。</p>

<p><img alt="100112_maxmsp2.gif" src="/hoehoe/images/100112_maxmsp2.gif" width="530" height="382" class="tumb-image" /></p>


<h3>Max/MSP×Arduino ご利用は計画的に</h3>

<p>Max/MSPとArduinoを通信させるには<a href="http://www.arduino.cc/playground/Interfacing/MaxMSP" target="_blank">いくつかの方法</a>があるみたい。今回は使いやすそうな<a href="http://digitalcoleman.blogspot.com/2009/11/maxuino-v007-for-arduino.html" target="_blank">Maxduino</a>を使いました。Firmataライブラリを使うので、直接PCからアナログ／デジタルピンに入出力ができます。んが、簡単なロジックもMax/MSP上で組む必要がでてくるわけで、LEDドライバでも効率のいいパッチを組んでやらないと、こんな様にw。</p>


<h3>動かしてみた</h3>

<object width="480" height="295"><param name="movie" value="http://www.youtube.com/v/9ztWdCfX_kw&hl=ja_JP&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/9ztWdCfX_kw&hl=ja_JP&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"></embed></object>

<p>うわー地味w</p>


<h3>Max/MSP/Jitterおもろいわ</h3>
<p>音と映像に特化したアプリだけに、素材をいじるオブジェクトを繋ぐだけで、なんかいい感じにできるのがいい。Flashだと、この基礎部分を作るのが大変な感じ。音と映像はMax/MSP/Jitterでいじって、ロジックとPC画面への出力はAS3で書いて、フィジカルな入出力はArduino使う。ってのが理想。やっぱFunnelなんかなー？</p>]]>
    </content>
</entry>

<entry>
    <title>Flash : Ustream Flash Client APIを使ってみる - SWFなカスタムパネルで中継を再生</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2009/091210001224.html" />
    <id>tag:txton.net,2009:/hoehoe//3.222</id>

    <published>2009-12-09T15:12:24Z</published>
    <updated>2010-06-02T06:59:14Z</updated>

    <summary> Ustream Flash Client APIて公式のAPIがこっそり公開さ...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="091209_ustapi1.gif" src="/hoehoe/images/091209_ustapi1.gif" width="530" height="313" class="tumb-image" /></p>

<p>Ustream Flash Client APIて公式のAPIがこっそり公開されてるみたいなんで、Photoshop、FireworksやFlashのカスタムパネル上で中継を再生させてみたよ。</p>]]>
        <![CDATA[<p>東京とか東京でおもしろそうなイベントがやってても、現地に行けるわけでもなし、会社で仕事に勤しみながら、<a href="http://www.ustream.tv/" target="_blank">Ustream</a>の中継をありがたく拝見するんですが、ブラウザが背面に回ってると盛り上がってる場面を見逃す事もしばしば。
デスクトップで再生できるガジェットとかアプリも特に見当たらず。サーバーを介して読ませる方法もスマートじゃなかった。そいや<a href="http://saqoosha.net/" target="_blank">Saqoosha</a>の人も何か作ってたけど、やり方よくわかんないし。そもそもFlashのAPIって無いの？ってGoogle先生に訪ねてみたら「あるよ」って答えが帰ってきた。なんだそりゃ。</p>

<p>AIRでガジェットぽくしてもいいけど、当たり前すぎてつまんない。SWFを埋め込めるカスタムパネルを使えば、作業中でも勉強会とかサッカーの中継もチラ見できるんじゃね？会社で見てもサボってるように見えないんじゃね？ステキじゃね？と思ったわけです。はい。</p>


<h3>Ustream Flash Client API</h3>
<p>Ustream Flash Client APIはUstream.tvの<a href="http://developer.ustream.tv/" target="_blank">デベロッパーページ</a>にひっそり公開されています。デベロッパー登録しろってあるけど、Flashクライアントを使うには必要ないです。</p>

<p>・<a href="http://svn.ustream.tv/flash/rsls/" target="_blank">共有ライブラリ</a><br />
・<a href="http://svn.ustream.tv/flash/samples/" target="_blank">サンプル</a><br />
・<a href="http://svn.ustream.tv/flash/samples/viewer/flash/viewer.pdf">Viewer実装のチュートリアル</a><br />
・<a href="http://developer.ustream.tv/external/flash/" target="_blank">Flash Client API Documentation (AsDoc)</a></p>

<p>フォーラムは過疎ってるんで、後はソース読め。って感じw。APIはViewerとBroadcasterが提供されてます。とりあえずViewerのサンプルを元に実装していきます。</p>

<h3>実装するよ</h3>
<p>手順はこんな感じ。Ustream.tvから共有ライブラリをロード→Class化→ステージにビューワーを配置→チャンネルに接続→再生。かんたんだねー。</p>

<p><pre>
package
{&nbsp;
&nbsp;import&nbsp;com.bit101.components.InputText;
&nbsp;import&nbsp;com.bit101.components.PushButton;
&nbsp;
&nbsp;import&nbsp;flash.display.Loader;
&nbsp;import&nbsp;flash.display.Sprite;
&nbsp;import&nbsp;flash.events.Event;
&nbsp;import&nbsp;flash.events.MouseEvent;
&nbsp;import&nbsp;flash.net.URLRequest;
&nbsp;import&nbsp;flash.system.ApplicationDomain;
&nbsp;import&nbsp;flash.system.LoaderContext;
&nbsp;import&nbsp;flash.system.Security;
&nbsp;
&nbsp;import&nbsp;tv.ustream.tools.Debug;
&nbsp;
&nbsp;[SWF(width=244,height=184,backgroundColor=0xffffff,frameRate=60)]
&nbsp;
&nbsp;public&nbsp;class&nbsp;TestUstreamView01&nbsp;extends&nbsp;Sprite
&nbsp;{
&nbsp;&nbsp;private&nbsp;var&nbsp;viewerLoader:Loader;
&nbsp;&nbsp;private&nbsp;var&nbsp;viewer:Object;
&nbsp;&nbsp;//private&nbsp;var&nbsp;viewer:Logic;
&nbsp;&nbsp;private&nbsp;var&nbsp;playButton:PushButton;
&nbsp;&nbsp;private&nbsp;var&nbsp;cId:InputText;
&nbsp;&nbsp;
&nbsp;&nbsp;private&nbsp;var&nbsp;channelId:String;
&nbsp;&nbsp;
&nbsp;&nbsp;public&nbsp;function&nbsp;TestUstreamView01()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;playButton&nbsp;=&nbsp;new&nbsp;PushButton(null,&nbsp;2,&nbsp;161,&nbsp;&quot;PLAY&quot;,&nbsp;onPlayButtonClick);
&nbsp;&nbsp;&nbsp;playButton.width&nbsp;=&nbsp;82;
&nbsp;&nbsp;&nbsp;this.addChild(playButton);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;cId&nbsp;=&nbsp;new&nbsp;InputText(null,&nbsp;88,&nbsp;161);
&nbsp;&nbsp;&nbsp;cId.height&nbsp;=&nbsp;20;
&nbsp;&nbsp;&nbsp;cId.width&nbsp;=&nbsp;152;
&nbsp;&nbsp;&nbsp;this.addChild(cId);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;Debug.enabled&nbsp;=&nbsp;false;
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;private&nbsp;function&nbsp;onPlayButtonClick(e:MouseEvent):void&nbsp;
&nbsp;&nbsp;{&nbsp;
&nbsp;&nbsp;&nbsp;if&nbsp;(viewerLoader&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;getRsl();
&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(viewer.playing)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;viewer.playing&nbsp;=&nbsp;false;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;playButton.label&nbsp;=&nbsp;&quot;PLAY&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;viewer.destroy();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;playChannel();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;private&nbsp;function&nbsp;getRsl():void&nbsp;
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;Security.allowDomain(&quot;*&quot;);
&nbsp;&nbsp;&nbsp;viewerLoader&nbsp;=&nbsp;new&nbsp;Loader();
&nbsp;&nbsp;&nbsp;viewerLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,&nbsp;onRslLoad);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;this.addChild(viewerLoader);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;var&nbsp;applicationDomain:ApplicationDomain&nbsp;=&nbsp;ApplicationDomain.currentDomain;
&nbsp;&nbsp;&nbsp;var&nbsp;loaderContext:LoaderContext&nbsp;=&nbsp;new&nbsp;LoaderContext();
&nbsp;&nbsp;&nbsp;var&nbsp;request:URLRequest&nbsp;=&nbsp;new&nbsp;URLRequest(&quot;http://www.ustream.tv/flash/viewer.rsl.swf&quot;);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;loaderContext.applicationDomain&nbsp;=&nbsp;applicationDomain;
&nbsp;&nbsp;&nbsp;viewerLoader.load(request,&nbsp;loaderContext);
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;private&nbsp;function&nbsp;onRslLoad(e:Event):void
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;var&nbsp;logicClass:Class&nbsp;=&nbsp;viewerLoader.contentLoaderInfo.applicationDomain.getDefinition(&quot;tv.ustream.viewer.logic.Logic&quot;)&nbsp;as&nbsp;Class;
&nbsp;&nbsp;&nbsp;viewer&nbsp;=&nbsp;new&nbsp;logicClass(&nbsp;);
&nbsp;&nbsp;&nbsp;//viewer&nbsp;=&nbsp;new&nbsp;Logic();
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;viewer.display.x&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;viewer.display.y&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;viewer.display.width&nbsp;=&nbsp;244;
&nbsp;&nbsp;&nbsp;viewer.display.height&nbsp;=&nbsp;160;
&nbsp;&nbsp;&nbsp;this.addChild(viewer.display);
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;playChannel();
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;private&nbsp;function&nbsp;playChannel():void&nbsp;
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;if(loaderInfo.parameters.cid)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;channelId&nbsp;=&nbsp;loaderInfo.parameters.cid
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;if&nbsp;(cId.text.length&nbsp;&gt;&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;channelId&nbsp;=&nbsp;cId.text;
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;viewer.createChannel(channelId);
&nbsp;&nbsp;&nbsp;viewer.playing&nbsp;=&nbsp;true;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;playButton.label&nbsp;=&nbsp;&quot;STOP&quot;;
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;}
}
</pre></p>


<h3>動かしてみる</h3>
<p>さくっと実行してみます。このサンプルだと普通のurlに付いてるチャンネルIDでなく、cidてやつが必要です。Ustream.tvに行って適当なチャンネルを表示、embedのソースを表示してcid=0000000とかなってる数字のIDを取ってきます。そのIDを入力して「PLAY」して、映像が出てきたら成功です。再生されなかったらログを表示させて、原因を探ってみてください。</p>

<p><pre>
Debug.enabled = true;
</pre></p>


<h3>Photoshopのカスタムパネルで再生</h3>
<p>さて、本題w。Photoshop CS4しか対応してないけどAdobe Configuratorてのを使うと簡単にカスタムパネルの中にSWFを埋め込めます。<br />
<br />
・<a href="http://labs.adobe.com/downloads/configurator.html" target="_blank">Adobe Configurator</a><br />

</p>


<h3>Fireworks / Flashのカスタムパネルで再生</h3>
<p>Extensionとかmxpとか面倒だなぁ。どーすんのかわかんねーと思ってたら。SWFをフォルダに放り込んだら読めるよ。って<a href="http://minomix.net/blog/" target="_blank">隣の席の人</a>に教えてもらったw。てなわけで以下のフォルダに、作ったSWFを置いてください。</p>

<p><strong>【Fireworks】</strong><br />
(Windows)<br />
C:\Documents and Settings\＜ユーザー＞\Application Data\Adobe\Fireworks CS4\Command Panels<br />
(MacOS)<br />

<br />
<strong>【Flash】</strong><br />
C:\Documents and Settings\＜ユーザー＞\Application Data\Adobe\Flash CS4\Command Panels<br />
(MacOS)<br />

</p>


<h3>わりと違和感なくて楽しげw</h3>
<p>プレビューみたく右上に置いてあげるとだいぶ違和感ないw。すばらしい。どーやるかわかんないけど、APIにはIRCのチャットとか、プレーヤーに必要なものはほとんど実装されてるみたいなんでいじってみると良いかも。<br />
<br />
あ。ソースはだいぶ適当なんで、作業中にPhotoshopが落ちても当方は責任はおいかねます。。</p>]]>
    </content>
</entry>

<entry>
    <title>Java : Ustreamのチャットからパパパコメントに投稿する</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2009/090818135740.html" />
    <id>tag:txton.net,2009:/hoehoe//3.203</id>

    <published>2009-08-18T04:57:40Z</published>
    <updated>2009-08-18T05:02:28Z</updated>

    <summary> MTLのみんなのコメントがPC画面に流せる、すてきサービス「パパパコメント」に...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="090618_napster1.gif" src="/hoehoe/images/090818_papapa1.gif" width="530" height="190" class="tumb-image" /></p>

<p><a href="http://mtl.recruit.co.jp/" target="_blank">MTL</a>のみんなのコメントがPC画面に流せる、すてきサービス「<a href="http://c.papapam.com/" target="_blank">パパパコメント</a>」に、<a href="http://www.ustream.tv/" target="_blank">Ustream</a>のチャットからでも投稿できて、勉強会で一緒に楽しめる方法を考えてみたよ。</p>

]]>
        <![CDATA[<p>Ustreamの各チャンネルで流れているチャットは、IRCを使って提供されています。チャットウィンドウは実際にはFlashなIRCクライアントになっていて、IRCサーバーに接続して、そのチャンネル上でチャットしてるわけです。なもんで仕組みは簡単。Ustreamのチャンネルにボットを常駐→発言を取ってくる→パパパコメントの投稿PHPにPOST。これだけ。</p>


<h3>IRCボット（bot）を用意。</h3>
<p>IRCのボットをこしらえます。例のごとくJavaでやるよ。JavaでIRCボットが簡単に実装できるライブラリ「<a href="http://www.jibble.org/pircbot.php" target="_blank">Pircbot</a>」を使います。<br />
<br />
チュートリアルビデオもあるのでさらっと見ときましょう。ビデオではJavaFXを落としてますが、普通の開発環境でいいです。<br />
<br />
IRCに繋いでメッセージを取ってくるまでは、サンプルにあるのでそれをいじっていきましょう。<br />
ボットを常駐させるIRCサーバやチャンネルは次のようになります。</p>

<p><pre>
サーバー ： chat1.ustream.tv
ポート ： 6667
チャンネル ： （例) #hoelive
</pre></p>

IRCのチャンネル名はUstreamのチャンネルと同じになります。

<p><pre>
public&nbsp;void&nbsp;onMessage 
(String&nbsp;channel,&nbsp;String&nbsp;sender,&nbsp;String&nbsp;login,&nbsp;String&nbsp;hostname,&nbsp;String&nbsp;message)&nbsp;{
&nbsp;String&nbsp;comm&nbsp;=&nbsp;message&nbsp;//&nbsp;発言メッセージ
&nbsp;testSendComment(comm)&nbsp;//&nbsp;何か投げる
}
</pre></p>

<p>サンプル中にあるonMessageメソッドで、勝手に誰かの発言を取ってきてくれるので、後は投稿する内容を整えて、PHPに投げるだけ。かんたんですねー。</p>


<h3>実装するよ。</h3>
<p>文字コードはUTF-8。PHPに投げる前にはURLエンコードするのを忘れずに！</p>

<p>・MyBotMain.java</p>
<p><pre>
import&nbsp;org.jibble.pircbot.*;

public&nbsp;class&nbsp;MyBotMain&nbsp;{
&nbsp;
&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Now&nbsp;start&nbsp;our&nbsp;bot&nbsp;up.
&nbsp;&nbsp;MyBot&nbsp;bot&nbsp;=&nbsp;new&nbsp;MyBot();
&nbsp;&nbsp;bot.setEncoding(&quot;UTF-8&quot;);

&nbsp;&nbsp;//&nbsp;Enable&nbsp;debugging&nbsp;output.
&nbsp;&nbsp;bot.setVerbose(true);
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Connect&nbsp;to&nbsp;the&nbsp;IRC&nbsp;server.
&nbsp;&nbsp;bot.connect(&quot;chat1.ustream.tv&quot;,&nbsp;6667);

&nbsp;&nbsp;//&nbsp;Join&nbsp;the&nbsp;#pircbot&nbsp;channel.
&nbsp;&nbsp;bot.joinChannel(&quot;#hoelive&quot;);

&nbsp;}

}
</pre></p>

<p>・MyBot.java</p>
<p><pre style="height:400px">
import&nbsp;org.jibble.pircbot.*;
import&nbsp;java.io.*;
import&nbsp;java.net.*;

public&nbsp;class&nbsp;MyBot&nbsp;extends&nbsp;PircBot&nbsp;
{

&nbsp;private&nbsp;String&nbsp;papapaRoom&nbsp;=&nbsp;&quot;hoelive&quot;;&nbsp;//&nbsp;部屋名

&nbsp;public&nbsp;void&nbsp;Mybot()&nbsp;{
&nbsp;&nbsp;this.setName(&quot;hoebot&quot;);
&nbsp;}

&nbsp;public&nbsp;void&nbsp;onMessage(String&nbsp;channel,&nbsp;String&nbsp;sender,
&nbsp;&nbsp;&nbsp;String&nbsp;login,&nbsp;String&nbsp;hostname,&nbsp;String&nbsp;message)
&nbsp;{

&nbsp;&nbsp;//sendMessage(channel,&nbsp;sender&nbsp;+&nbsp;&quot;:&quot;&nbsp;+&nbsp;message);

&nbsp;&nbsp;if&nbsp;(message.equalsIgnoreCase(&quot;shinachiku&quot;))&nbsp;{
&nbsp;&nbsp;&nbsp;sendMessage(channel,&nbsp;sender&nbsp;+&nbsp;&quot;仕事してる？&quot;);
&nbsp;&nbsp;}

&nbsp;&nbsp;sendComment(papapaRoom,&nbsp;message);

&nbsp;}

&nbsp;public&nbsp;void&nbsp;sendComment(String&nbsp;channel,&nbsp;String&nbsp;message)&nbsp;
&nbsp;{
&nbsp;
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;String&nbsp;cmm&nbsp;=&nbsp;message;
&nbsp;&nbsp;&nbsp;String&nbsp;encCmm&nbsp;=&nbsp;null;
&nbsp;&nbsp;&nbsp;encCmm&nbsp;=&nbsp;URLEncoder.encode(cmm,&nbsp;&quot;UTF-8&quot;);

&nbsp;&nbsp;&nbsp;URL&nbsp;url&nbsp;=&nbsp;new&nbsp;URL(&quot;http://c.papapam.com/post.php&quot;);
&nbsp;&nbsp;&nbsp;URLConnection&nbsp;uc&nbsp;=&nbsp;url.openConnection();
&nbsp;&nbsp;&nbsp;uc.setDoOutput(true);

&nbsp;&nbsp;&nbsp;uc.setRequestProperty(&quot;User-Agent&quot;,&nbsp;&quot;Sample-Agent&quot;);
&nbsp;&nbsp;&nbsp;uc.setRequestProperty(&quot;Accept-Language&quot;,&nbsp;&quot;ja&quot;);

&nbsp;&nbsp;&nbsp;OutputStream&nbsp;os&nbsp;=&nbsp;uc.getOutputStream();
&nbsp;&nbsp;&nbsp;String&nbsp;data&nbsp;=&nbsp;&quot;room=&quot;&nbsp;+&nbsp;channel&nbsp;+&nbsp;&quot;&amp;comment=&quot;&nbsp;+&nbsp;encCmm;
&nbsp;&nbsp;&nbsp;PrintStream&nbsp;ps&nbsp;=&nbsp;new&nbsp;PrintStream(os);
&nbsp;&nbsp;&nbsp;ps.print(data);
&nbsp;&nbsp;&nbsp;ps.close();

&nbsp;&nbsp;&nbsp;//POSTした結果を取得
&nbsp;&nbsp;&nbsp;InputStream&nbsp;is&nbsp;=&nbsp;uc.getInputStream();
&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;br&nbsp;=&nbsp;new&nbsp;BufferedReader(new&nbsp;InputStreamReader(is));
&nbsp;&nbsp;&nbsp;String&nbsp;s;
&nbsp;&nbsp;&nbsp;while&nbsp;((s&nbsp;=&nbsp;br.readLine())&nbsp;!=&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;System.out.printf(s);
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;br.close();&nbsp;&nbsp;
&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}

&nbsp;}
}
</p></pre>


<h3>動かしてみる。</h3>
<p>
（動画は帰ったら撮る）
<!--object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/5NDBq_ifgj0&hl=ja&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/5NDBq_ifgj0&hl=ja&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object--></p>


<h3>BOTうざい。</h3>
<p>今のとこ全部の発言が流れちゃうんで、だいぶ邪魔ｗ。特定のコマンド時のみ配信とか加えないとなぁ。どーやってログインさせるかよくわからなかったんで、ゲストで水面下にいるのが気持ち悪い。あとはbot機能をいじっていきたいなー。誰かやってｗ<br />
<br />
そのうち<a href="http://teraco.jp/category/%e5%a4%a7%e9%98%aa%e3%81%a6%e3%82%89%e5%ad%90" target="_blank">大阪てら子</a>あたりでテストしてみます。</p>


<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://sazameki.org/" target="_blank">Pircbot</a></li>
<li><a href="http://c.papapam.com/" target="_blank">パパパコメント</a></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>jQuery : Napsterのブログパーツを作る</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2009/090619181210.html" />
    <id>tag:txton.net,2009:/hoehoe//3.195</id>

    <published>2009-06-19T09:12:10Z</published>
    <updated>2009-06-19T09:13:44Z</updated>

    <summary> Google AJAX Feed APIとjQuaryを使って、Napster...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="jQuery" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="090618_napster1.gif" src="/hoehoe/images/090618_napster1.gif" width="530" height="230" class="tumb-image" /></p>

<p>Google AJAX Feed APIとjQuaryを使って、Napsterのニューリリースティッカーのブログパーツぽいものを作ってみたよ。</p>

<p>前にもFlexだったり、<a href="http://txton.net/hoehoe/2008/080424010337.html">Progressionで作ってみようとしたり</a>したんだけど、飽きて放ったらかし。情報見るだけだったら、RSSをブラウザで見るだけでも十分。でもまーブログパーツにしたいじゃない。そいやGoogle AJAX Feed APIって使ってみたいし、動きも画一なんでいいし、凝らないんだったらJSで、というかjQueryでいいんじゃね？。と思ったわけです。。えーと。ネタものをさっぱり作ってないんで穴うめ。。</p>


]]>
        <![CDATA[<h3>データを取ってくるよ</h3>

<p>前回同様ニューリリースのRSSを使います。RSSのパースはめんどくさいんでー、他ドメインのRSSやAtomのフィードをJSで簡単に取得するためのAPI、<a href="http://code.google.com/apis/ajaxfeeds/index.html" target="_blank">Google AJAX Feed API</a>てのを使ってみます。画像は、Flashだとクロスドメイン制限のためプロキシ通してましたが、今回はブラウザで表示させるんで必要ないです。</p>


<h3>Google AJAX Feed APIの準備</h3>

<p>まずGoogle AJAX Feed APIを使えるようにしときます。登録にはGoogleアカウントが必要なので、無い人は作っておきましょう。APIの<a href="http://code.google.com/intl/ja/apis/ajaxfeeds/signup.html" target="_blank">登録ページ</a>に行き、使用するサイトのURLを登録して、APIキーを生成します。生成されたAPIキー、登録したURLとサンプルのコードが表示されるんで、まずサンプルコードをコピペしてちゃんとフィードが取れているかテストしてみましょう。<br />
<br />
サンプルコード中の</p>

<p><pre>
var feed = new google.feeds.Feed("<span style="color:#FF0000">http://www.digg.com/rss/index.xml</span>");
</pre></p>

<p>で取ってくるフィードのURL指定しているので、読みたいフィードのURLに変えておくといいです。読み込んだデータは、 </p>

<p><pre>
feed.title
feed.link
feed.content
</pre></p>

<p>みたいな感じで取り出せます。かんたんですねー。<br />
RSS以外にも使えるので、詳しくは<a href="http://code.google.com/intl/ja/apis/ajaxfeeds/documentation/">ドキュメント</a>を読むとよいです。</p>


<h3>jQueryで実装するよ。</h3>

<p>もう後はJS使えば大体の事はできますねー。jQueryでいじっていきましょう。<br />
<br />
表示させるデータはアルバムタイトル／アーティスト、Napsterサイト内のアルバム情報へのURL、ジャケット画像ぐらいでいいでしょう。ジャケットの画像のURLはcontentの中のHTMLからセレクタを使って抜き出してきます。<br />
<br />
ブログパーツぽくしたいんで、ジャケット画像は１枚だけ見せて、送りボタンで順にスクロールさせる感じにします。<br />
<br />
オートプレイも付けてみます。タイマーをしかけて、一定時間毎に順送りボタンのクリックイベントを発生させます。タイマーを扱うためには、jQueryのプラグイン<a href="http://plugins.jquery.com/project/timers" target="_blank">jQuery timers</a>を使います。あと、カーソルがボタン上にある時には、オートプレイは動いてほしくないんで、ロールオーバー時にタイマーを止める処理を加えておきます。</p>

<p><pre style="height:400px">
var&nbsp;sleevWidth;

jQuery(function($){

&nbsp;//&nbsp;ボタンをロールオーバー/アウトした時
&nbsp;$(&quot;div.nextButton,&nbsp;div.prevButton&quot;).hover(function(){
&nbsp;&nbsp;$(this).css({cursor:&quot;pointer&quot;,&nbsp;background:&quot;#B3DFF2&quot;,&nbsp;color:&quot;#FFFFFF&quot;});
&nbsp;&nbsp;$(document).stopTime(&quot;autoPlay&quot;);
&nbsp;},function(){
&nbsp;&nbsp;$(this).css({cursor:&quot;default&quot;,&nbsp;background:&quot;#FFFFFF&quot;,&nbsp;color:&quot;#333333&quot;});
&nbsp;&nbsp;$(document).everyTime(5000,&nbsp;&quot;autoPlay&quot;,&nbsp;autoPlay);
&nbsp;});
&nbsp;
&nbsp;//&nbsp;ボタンを押した時
&nbsp;$(&quot;div.nextButton&quot;).click(function(){
&nbsp;&nbsp;if($(&quot;div#cdSleeve&quot;).position().left&nbsp;&gt;=&nbsp;-(sleevWidth-300))&nbsp;{
&nbsp;&nbsp;&nbsp;$(&quot;div#cdSleeve&quot;).animate({left:&nbsp;&quot;-=150&quot;},&nbsp;300);
&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;$(&quot;div#cdSleeve&quot;).animate({left:&nbsp;0},&nbsp;1000);
&nbsp;&nbsp;}
&nbsp;});
&nbsp;$(&quot;div.prevButton&quot;).click(function(){
&nbsp;&nbsp;if&nbsp;($(&quot;div#cdSleeve&quot;).position().left&nbsp;&lt;&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;$(&quot;div#cdSleeve&quot;).animate({left:&nbsp;&quot;+=150&quot;},&nbsp;300);
&nbsp;&nbsp;}
&nbsp;});
&nbsp;
&nbsp;//&nbsp;オートプレイ
&nbsp;$(document).everyTime(5000,&nbsp;&quot;autoPlay&quot;,&nbsp;autoPlay);
&nbsp;function&nbsp;autoPlay()&nbsp;{
&nbsp;&nbsp;$(&quot;div.nextButton&quot;).click();
&nbsp;}

});

//&nbsp;Google&nbsp;AJAX&nbsp;Feed&nbsp;API&nbsp;からFeedを取得
google.load(&quot;feeds&quot;,&nbsp;&quot;1&quot;);
function&nbsp;initialize()&nbsp;{
&nbsp;var&nbsp;feed&nbsp;=&nbsp;new&nbsp;google.feeds.Feed(&quot;http://www.napster.jp/xml/newrelease_all.xml&quot;);
&nbsp;feed.setNumEntries(99);
&nbsp;feed.load(function(result)&nbsp;{
&nbsp;&nbsp;if&nbsp;(!result.error)&nbsp;{
&nbsp;&nbsp;&nbsp;var&nbsp;container&nbsp;=&nbsp;document.getElementById(&quot;cdSleeve&quot;);
&nbsp;&nbsp;&nbsp;var&nbsp;temp&nbsp;=&nbsp;&quot;&quot;;
&nbsp;&nbsp;&nbsp;for&nbsp;(var&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;result.feed.entries.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;entry&nbsp;=&nbsp;result.feed.entries[i];
&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;thum&nbsp;=&nbsp;$(&quot;img&quot;&nbsp;,$(entry.content)).attr(&quot;src&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;+=&nbsp;'&lt;div&nbsp;class=&quot;cdCase&quot;&gt;';
&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;+=&nbsp;'&lt;div&nbsp;class=&quot;cdImg&quot;&gt;&lt;a&nbsp;href=&quot;'&nbsp;+&nbsp;entry.link&nbsp;+&nbsp;'&quot;&gt;&lt;img&nbsp;src=&quot;'&nbsp;+&nbsp;thum&nbsp;+&nbsp;'&quot;&gt;&lt;/a&gt;&lt;/div&gt;';
&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;+=&nbsp;'&lt;div&nbsp;class=&quot;cdTitle&quot;&gt;&lt;a&nbsp;href=&quot;'&nbsp;+&nbsp;entry.link&nbsp;+&nbsp;'&quot;&gt;'&nbsp;+&nbsp;entry.title&nbsp;+&nbsp;'&lt;/a&gt;&lt;/div&gt;';
&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;+=&nbsp;'&lt;/div&gt;';&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;container.innerHTML&nbsp;=&nbsp;temp;
&nbsp;&nbsp;&nbsp;sleevWidth&nbsp;=&nbsp;result.feed.entries.length*150;
&nbsp;&nbsp;&nbsp;$(&quot;#cdSleeve&quot;).css(&quot;width&quot;,sleevWidth);
&nbsp;&nbsp;}
&nbsp;});
}
google.setOnLoadCallback(initialize);
</pre></p>


<h3>ジャケット一覧表示も追加</h3>

<p>何十枚も見るのに、延々ボタンをクリックなんてがめんどくさい。なもんで、パっと全部のジャケットが見れるような一覧も追加します。むしろコレがやりたかった。。チマチマ組むのもイヤなんで、またThickBoxを改造して、レイヤー上にジャケットが並んでるような感じにします。<br />
<br />
ThickBoxのインラインモードを使うと、指定したIDのブロックの中身を勝手にレイヤー上に表示してくれます。この場合ジャケット表示部を囲っているブロックを指定してやればいいわけです。そのままだと指定された幅の中でしか出せないんで、フルスクリーンで出せるようにポジション調整周りを修正していきます。</p>

<p><pre style="height:175px">
&nbsp;if(url.indexOf('TB_inline')&nbsp;!=&nbsp;-1){	
&nbsp;&nbsp;$(&quot;#TB_ajaxContent&quot;).append($('#'&nbsp;+&nbsp;params['inlineId']).children());
&nbsp;&nbsp;$(&quot;#TB_window&quot;).unload(function&nbsp;()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;$('#'&nbsp;+&nbsp;params['inlineId']).append(&nbsp;$(&quot;#TB_ajaxContent&quot;).children()&nbsp;);&nbsp;//&nbsp;move&nbsp;elements&nbsp;back&nbsp;when&nbsp;you're&nbsp;finished
&nbsp;&nbsp;&nbsp;});
<span style="color:#FF0000">&nbsp;&nbsp;tb_position();&nbsp;←ここを変える</span>
&nbsp;&nbsp;$(&quot;#TB_load&quot;).remove();
&nbsp;&nbsp;$(&quot;#TB_window&quot;).css({display:&quot;block&quot;});&nbsp;
&nbsp;}else&nbsp;if(url.indexOf('TB_iframe')&nbsp;!=&nbsp;-1){
</pre></p>

<p class="preCaption">thickbox.jsの224行目を、下のようにに変更。</p>
<p><pre style="height:155px">
if(params['fullscreen']&nbsp;!=&nbsp;&quot;true&quot;){&nbsp;//&nbsp;全画面のとき（暫定対応）
&nbsp;tb_position();
}&nbsp;else&nbsp;{
&nbsp;$(&quot;#TB_window&quot;).css({top:&quot;0&quot;,&nbsp;left:&quot;0&quot;,&nbsp;position:&quot;absolute&quot;,&nbsp;width:&quot;100%&quot;,&nbsp;height:&quot;100%&quot;,background:&quot;none&quot;,&nbsp;border:&quot;none&quot;});
&nbsp;$(&quot;#TB_ajaxContent&quot;).css({width:&quot;100%&quot;,&nbsp;height:&quot;100%&quot;});
&nbsp;tbContainer&nbsp;=&nbsp;document.getElementById(&quot;cdSleeve&quot;);
&nbsp;$(&quot;#TB_window&quot;).addClass('TB_inline');
}
</pre></p>

<h3>できたできた。</h3>

<p><img alt="090618_napster2" src="/hoehoe/images/090618_napster2.gif" width="530" height="183" class="tumb-image" /></p>

<p>CSSとかち合うんでサイドバーに置いてます。ジャケットの横のボタン（見えないけど。。）を押すと順送り、逆送り。ほっとくとオートプレイして、タイトルのLISTをクリックすると一覧がどばっと見えます。ThickBoxなんで領域外をクリックするかESCキーで元の画面に戻ります。<br />
<br />
ま、こんなもんでいいんじゃないかなw。<br />
<br />
これ。ITMSのも作っとくといいなぁ。後でアフェリエイトも足しとこう。</p>]]>
    </content>
</entry>

<entry>
    <title>Flash : MIDI機器とFlashを連携（その２） - キーボードも使ってみる</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2009/090205152847.html" />
    <id>tag:hoehoe.sakura.ne.jp,2009:/hoehoe//3.180</id>

    <published>2009-02-05T06:28:47Z</published>
    <updated>2009-06-19T09:19:43Z</updated>

    <summary> MIDIらしくMIDIキーボードとFlashの連携も実装してみたよ。入力にはn...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="java" label="Java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="midi" label="MIDI" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sazameki" label="sazameki" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="090201_midi2as1.gif" src="/hoehoe/images/090201_midi2as1.gif" width="530" height="153" class="tumb-image" /></p>

<p>MIDIらしくMIDIキーボードとFlashの連携も実装してみたよ。入力には<a href="http://www.korg.co.jp/Product/Synthesizer/nano/nanoKEY.html" target="_blank">nanoKEY</a>（そろった。。）を、音源には<a href="http://www.zkdesign.jp/" target="_blank">zk33さん</a>のFlashで音生成などオーディオ操作ができるライブラリ<a href="http://sazameki.org/" target="_blank">sazameki</a>を使ってみました。</p>]]>
        <![CDATA[<h4>早速実装するよ。</h4>

<p>sazamekiがよくできてるんでサンプルを数行いじるだけでできました。わーい。<br />
sazamekiは<a href="http://www.libspark.org/"　target="_blank">SparkProject</a>の方で配布されてるんでダウンロードしてきます。ソースはFlashPlayer10用のソース（branchese/fp10/）の方を使います。fp10向けにコンパイルできるように設定は忘れずにね。んで構成はこんな感じ。</p>

<p><img alt="090201_midi2as2" src="/hoehoe/images/090201_midi2as2.gif" width="530" height="265" class="tumb-image" /></p>

<p>いじるサンプルはPCのキーボードを鍵盤の入力に使うやつ（branchese/fp10/sample/SamplerSample.as）を使います。元々入力に使うNoteOn/NoteOffとかイベントまわりはしっかり実装されていてる様子なんで。サンプルからPCキーボード入力の部分を省いていって、入力周りだけを変更していきます。MIDIキーボードの入力→VispでBNAGまで処理（NoteEventクラスがかぶってるので注意）→BangEvent.BANG_ON/BangEvent.BANG_OFFを拾う→sazameki側でNoteOnイベントを投げる、pitchとvelocityも放り込む→音がなる。てな感じ。</p>

<p><pre style="height:400px">
package {
 /** 
  * MIDI2AS03.as
  *
  * nanoKEY から入力して音を鳴らすよ。
  *
  **/
  
 import com.visp.events.BangEvent;
 import com.visp.midi.MidiManager;
 import com.visp.midi.NoteEvent;
 
 import flash.display.DisplayObject;
 import flash.display.Sprite;
 import flash.display.StageScaleMode;
 import flash.events.SampleDataEvent;
 import flash.media.Sound;
 import flash.utils.ByteArray;
 
 import org.sazameki.audio.core.AudioSamples;
 import org.sazameki.audio.ctrlEvent.events.NoteOff;
 import org.sazameki.audio.ctrlEvent.events.NoteOn;
 import org.sazameki.audio.engine.MultiSamplePlayer.Instrument;
 import org.sazameki.audio.engine.MultiSamplePlayer.MultiSamplePlayer;
 import org.sazameki.audio.engine.MultiSamplePlayer.NoteRange;
 import org.sazameki.audio.engine.MultiSamplePlayer.ProcessData;
 import org.sazameki.audio.format.wav.Wav;


 public class MIDI2AS03 extends Sprite
 {
  [Embed(source = &quot;asset/bass_c2_9836_10510.wav&quot;, mimeType = &quot;application/octet-stream&quot;)]
  public const Bass:Class;

  [Embed(source = &quot;asset/Clap.wav&quot;, mimeType = &quot;application/octet-stream&quot;)]
  public const Clap:Class;
  [Embed(source = &quot;asset/Hat.wav&quot;, mimeType = &quot;application/octet-stream&quot;)]
  public const Hat:Class;
  [Embed(source = &quot;asset/Kick.wav&quot;, mimeType = &quot;application/octet-stream&quot;)]
  public const Kick:Class;
  [Embed(source = &quot;asset/Snare.wav&quot;, mimeType = &quot;application/octet-stream&quot;)]
  public const Snare:Class;
  
  private var sampler:MultiSamplePlayer;
  private var sound:Sound;
  private var procData:ProcessData;
  
  private var _midiMgr:MidiManager;

  public function MIDI2AS03()
  {
   // MIDIマネージャーを作成
   this._midiMgr = MidiManager.getInstance();
   this._midiMgr.initialize();
   // イベントを登録
   this._midiMgr.addEventListener(NoteEvent.NOTE_ON, _handleNoteOn);
   this._midiMgr.addEventListener(NoteEvent.NOTE_OFF, _handleNoteOff);
   this.addEventListener(BangEvent.BANG_ON, _handleBangOn);
   this.addEventListener(BangEvent.BANG_OFF, _handleBangOff);
   
   
   stage.scaleMode = StageScaleMode.NO_SCALE;
   
   var decoder:Wav = new Wav();

   var bassSamples:AudioSamples = decoder.decode(new Bass() as ByteArray);
   var bassInst:Instrument = new Instrument(bassSamples, 9836, 10509, 24);
   
   var clapSamples:AudioSamples = decoder.decode(new Clap() as ByteArray);
   var clapInst:Instrument = new Instrument(clapSamples);
   
   var hatSamples:AudioSamples = decoder.decode(new Hat() as ByteArray);
   var hatInst:Instrument = new Instrument(hatSamples);
   
   var kickSamples:AudioSamples = decoder.decode(new Kick() as ByteArray);
   var kickInst:Instrument = new Instrument(kickSamples);
   
   var snareSamples:AudioSamples = decoder.decode(new Snare() as ByteArray);
   var snareInst:Instrument = new Instrument(snareSamples);
   
   sampler = new MultiSamplePlayer();
   sampler.addInstrument(bassInst, new NoteRange(24));
   sampler.addInstrument(kickInst, new NoteRange(0, 13));
   sampler.addInstrument(hatInst, new NoteRange(18, 23));
   sampler.addInstrument(clapInst, new NoteRange(14, 15));
   sampler.addInstrument(snareInst, new NoteRange(16, 17));
   //prepare processData(sample buffer &amp; event list)
   procData = new ProcessData(2048);
   
   //prepare sound
   sound = new Sound();
   sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSamplesCallback);
   sound.play();

  }
  
  // ノートオンが発生したらBNAG_ONを通知
  private function _handleNoteOn(e:NoteEvent) : void
  {
   if(e.pitch) {
    var bangOn : BangEvent = new BangEvent(BangEvent.BANG_ON, e.pitch, true);
    bangOn.velocity = e.velocity;
    dispatchEvent(bangOn);
   }
  }
  
  // ノートオフが発生したらBANG_OFFを通知
  private function _handleNoteOff(e:NoteEvent) : void
  {
   if(e.pitch){
    var bangOff : BangEvent = new BangEvent(BangEvent.BANG_OFF, e.pitch, true);
    dispatchEvent(bangOff);
   }
  }
  
  // BANG_ONが発生したらsazameki側にイベントを通知
  private function _handleBangOn(e:BangEvent):void
  {
   procData.events.addEvent(new NoteOn(e.id, e.velocity), 0);
  }
  // BANG_OFFが発生したらsazameki側にイベントを通知
  private function _handleBangOff(e:BangEvent):void
  {
   procData.events.addEvent(new NoteOff(e.id), 0);
  }


  private function onSamplesCallback(e:SampleDataEvent):void 
  {

   var left:Vector.&lt;Number&gt;;
   var right:Vector.&lt;Number&gt;;
   
   var audios:AudioSamples = procData.samples;
   
   var len:int = procData.length;
  
   var i:int = 0;
   var sig:Number;
   
   sampler.process(procData);
   
   if (audios.setting.channels == 2)
   {
    left = audios.left;
    right = audios.right;
    for (; i &lt; len; ++i)
    {
     e.data.writeFloat(left[i]);
     e.data.writeFloat(right[i]);
    
    }
   }else
   {
    left = audios.left;
    for (; i &lt; len; ++i)
    {
     sig = left[i];
     e.data.writeFloat(sig);
     e.data.writeFloat(sig);
    }
    
   }
  }
  
 }
}
</pre></p>

<h3>動かしてみる。</h3>
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/5NDBq_ifgj0&hl=ja&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/5NDBq_ifgj0&hl=ja&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>

<p>ノートのVelocityにも対応してるんで若干強弱も付けれる。わかんないけど。最後の音が鳴りっぱなしなのはノートオフが取れてない感じ。これでも20回取り直したｗ</p>

<h3>sazamekiおもしれー！。</h3>
<p>先の問題は特に解決させずに繋いでみただけ。あんまり速く叩くとMIDI proxyからイベントが送れなくて音が鳴り続けたりするw。sazamekiてば外部からの入力にも便利。よくできてる。あとはユーティリティでソケット通信実装してくたらVispはいらない子。でもMIDI Proxyは手をいれた方がいいなぁ。nanoKONTROLと２つ繋いでで音色変えながらとか楽しそう。</p>

<h4>関連エントリ：</h4>
<p>
<ul>
<li><a href="http://txton.net/hoehoe/2009/090122161939.html">（その１） - 準備とか実装</a></li>
<li><strong>（その２） - キーボードも使ってみる</strong></li>
</ul></p>

<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://sazameki.org/" target="_blank">sazameki</a></li>
<li><a href="http://www.korg.co.jp/Product/Synthesizer/nano/index.html" target="_blank">KORG nanoシリーズ</a></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>Flash : MIDI機器とFlashを連携（その１）</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2009/090122161939.html" />
    <id>tag:hoehoe.sakura.ne.jp,2009:/hoehoe//3.177</id>

    <published>2009-01-22T07:19:39Z</published>
    <updated>2009-02-12T19:52:05Z</updated>

    <summary> KORGのnanoPADとnanoKONTROLが安価でかわいかったので衝動買...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="java" label="Java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="midi" label="MIDI" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="081224_midi2as1.gif" src="/hoehoe/images/081224_midi2as1.gif" width="530" height="290" class="tumb-image" /></p>

<p>KORGの<a href="http://www.korg.co.jp/Product/Synthesizer/nano/nanoPAD.html"　target="_blank">nanoPAD</a>と<a href="http://www.korg.co.jp/Product/Synthesizer/nano/nanoKONTROL.html" target="_blank">nanoKONTROL</a>が安価でかわいかったので衝動買い。でも音モノは全然やらないんで、MIDI音源やMIDI機器をFlashを連携させて、インスタレーション用の入力機器にして遊んでみました。というか、<a href="http://txton.net/hoehoe/2007/071218145413.html">前もやったネタ</a>なんだけど詳しく解説しときます。</p>]]>
        <![CDATA[<p>構成は下の図な感じ。MIDIと繋ぐには、AIRなVJアプリ「<a href="http://visp-vj.com/" target="_blank">Visp</a>」のライブラリを流用します。VJアプリとしてはもひとつな感じだけど、MIDI機器を入力にしてムービーをコントロールできる機能があります。例のごとくFlash（AIR）自身では、他の機器にアクセスできません。なもんでVispにはMIDI機器にアクセスするためのゲートウェイ「MIDI Proxy」が用意されています。MIDI ProxyはJavaアプリです。JavaのMIDIライブラリから、MIDI機器から送られるMIDIメッセージ受け取り、XMLに変換します。FlashとはXMLSocketで通信させます。そしてASのライブラリでデータをパースして、各MIDIイベントに対応したイベントを通知します。あとはそのイベントを拾って対応させたデータを反映させるという感じ。</p>

<p><img alt="081224_midi2as2" src="/hoehoe/images/081224_midi2as2.gif" width="530" height="246" class="tumb-image" /></p>

<h3>準備と実装するよ</h3>
<p>VispのサイトからソースとMIDI Proxyのバイナリを<a href="http://visp-vj.com/download/" target="_blank">ダウンロード</a>してきます。ライブラリはflex_src/com/以下のファイルをコピーしてきます。あとMIDI機器をPC上で使えるようにセッティング。今回はnanoPAD、nanoKONTROLでやってきます。OS Xの場合、そのままだとMIDI機器をJavaが認識できないんで、<a href="http://www.mandolane.co.uk/swMandoMidi.html" target="_blank">Mandolane MIDI SPI</a>か<a href="http://www.humatic.de/htools/mmj.htm">mmj</a>を、Javaの拡張ディレクトリ（<strong>/Library/Java/Extensions/</strong>）にインストールしときます。</p>

<h4>インプットマネージャーを実装するよ</h4>
<p>元がAIRなだけに設定保存とかFlashには余分なとこは省いていきます。インプットマネージャー（com/visp/input/InputManager.as）あたりをいじっていけば実装できるはず。MIDIマネージャーを生成→MIDIイベントに対応したイベントを登録→入力があったらイベントを発行/取得→さらにMIDIイベントによって動作別のイベント（RANGE、BANGとか）をチェーン→動作別のイベントを通知したコントロールナンバーやノートを取得→データを反映。となるように実装します。</p>

<p><pre style="height:400px">
package {
 /** 
  * MIDI2AS00.as
  *
  * nanoKONTROL から入力して箱を動かすよ。
  *
  **/
   
 import com.visp.events.RangeEvent;
 import com.visp.midi.ControllerEvent;
 import com.visp.midi.MidiManager;
 import flash.display.Sprite;

 public class MIDI2AS00 extends Sprite
 { 

  private var _midiMgr:MidiManager;
  private var _rect:Sprite;
  
  public function MIDI2AS00()
  {
   
   // 動かすものを用意
   this._rect = new Sprite();
   this._rect.graphics.beginFill(0xFF0000);
   this._rect.graphics.drawRect(0, 0, 100, 100);
   this._rect.graphics.endFill();
   this.addChild(this._rect);
   
   // MIDIマネージャーを作成
   this._midiMgr = MidiManager.getInstance();
   this._midiMgr.initialize();
   
   // コントールチェンジのイベントを登録
   this._midiMgr.addEventListener(ControllerEvent.CONTROLLER, _handleController);
   // レンジイベントを登録
   this.addEventListener(RangeEvent.RANGE, _handleRange);
   
  }
  
  // コントロールチェンジ（MIDIイベント）発生したら
  private function _handleController(e:ControllerEvent) : void
  {

    // id：コントローラーナンバー（スライダーとか）
    // value：その値

   if(e.id) {
    // レンジイベントを発行して数字の範囲を丸める
    dispatchEvent(new RangeEvent(RangeEvent.RANGE, e.id, e.value, true));
   }   

  }
  
  // レンジイベントが発生したら
  private function _handleRange(e:RangeEvent):void
  {

   // コントロールナンバーをキーにして動きをつける
   switch(e.id) {
    case 2:
     this._rect.x = 255 * e.value + 100;
    break;
    
    case 3:
     this._rect.y = 255 * e.value + 100;
    break;
    
    case 14:
     this._rect.rotation = 360 * e.value;
    break;
   }   
   //trace(e.id, e.value);

  }
 }
}
</pre></p>

<p>nanoPAD、nanoKONTROLのスライダーやパッドのコントロールナンバー、ノートナンバー、MIDIイベント等は、KORGから配布されているnanoシリーズ用の「<a href="http://www.korg.co.jp/Support/Download/list.php?product=300" target="_blank">KORG KONTROL Editor</a>」で確認と変更ができます。</p>

<h3>使ってみる</h3>
<p><a href="http://atnd.org/events/273" target="_blank">大阪てら子19</a>でちょっとだけデモったやつを置いときますよっと。</p>

<h4>nanoKONTROLを使ったサンプル</h4>
<p>スライダーの１〜６ではちゅねがグルグル回る。だけ。カメラをぐるぐる回すはずが、よくわからんかったので放置。ミキサー卓ごっこができそう。</p>

<p><pre style="height:400px">
package {
 /** 
  * MIDI2AS02.as
  *
  * nanoKONTROLから入力してはちゅねを動かすよ。
  *
  **/
  
 import com.visp.events.RangeEvent;
 import com.visp.midi.ControllerEvent;
 import com.visp.midi.MidiManager;
 import org.papervision3d.view.stats.StatsView;
 import flash.display.Sprite;
 
 [SWF(width='800',height='600',backgroundColor='0x000000',frameRate='60')]

 public class MIDI2AS02 extends Sprite
 {
  private var _midiMgr:MidiManager;
  private var _3dObj:MikuPv3dObject;
  
  public function MIDI2AS02()
  {
   
   // 3Dオブジェクトを配置
   this._3dObj = new MikuPv3dObject();
   this.addChild(_3dObj);
   var _stats:StatsView = new StatsView(this._3dObj.renderer);
   this.addChild(_stats);
   
   // MIDIマネージャーを作成
   this._midiMgr = MidiManager.getInstance();
   this._midiMgr.initialize();
   // コントロールチェンジのイベントを登録
   this._midiMgr.addEventListener(ControllerEvent.CONTROLLER, _handleController);
   // RANGEイベントを登録
   this.addEventListener(RangeEvent.RANGE, _handleRange);
   
  }
  
  // コントロールチェンジ（MIDIイベント）発生したら
  private function _handleController(e:ControllerEvent) : void
  {
   
   /**
    *  id：コントローラーナンバー（スライダーとか）
    *  value：その値値
    **/
    
   if(e.id) {
    // レンジイベントを発行して数字の範囲を丸める
    dispatchEvent(new RangeEvent(RangeEvent.RANGE, e.id, e.value, true));
   }
   
  }
  
  private function _handleRange(e:RangeEvent):void
  {
   
   // コントロールナンバーをキーにして動きをつける
   switch(e.id) {
    case 2:
     this._3dObj.setTracking(e.value, 0, 0, 0, 0, 0);
    break;
    
    case 3:
     this._3dObj.setTracking(0, e.value, 0, 0, 0, 0);
    break;
    
    case 4:
     this._3dObj.setTracking(0, 0, e.value, 0, 0, 0);
    break;
    
    case 5:
     this._3dObj.setTracking(0, 0, 0, e.value, 0, 0);
    break;
    
    case 6:
     this._3dObj.setTracking(0, 0, 0, 0, e.value, 0);
    break;
    
    case 8:
     this._3dObj.setTracking(0, 0, 0, 0, 0, e.value);
    break;
   }
   
   //trace(e.id, e.value);
   
  }
 }
}


// 3Dオブジェクトのクラス

import flash.display.*;
import flash.events.*;

import org.papervision3d.objects.parsers.Collada;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.stats.StatsView;

class MikuPv3dObject extends BasicView
{
 private var _rotationX:Number;
 private var _rotationY:Number;
 private var _rotationZ:Number;
 private var _x:Number;
 private var _y:Number;
 private var _z:Number;
 private var _cmodel:Collada;
 
 private var rot:Number;
 
 public function MikuPv3dObject()
 {
  //viewportの定義とカメラタイプ定義
  //super (0,0,true,false,&quot;CAMERA3D&quot;);
  super (0,0,true,false,&quot;Target&quot;);
  init();
  init3D();
 }
 
 public function init():void 
 {
  this._rotationX = 0;
  this._rotationY = 0;
  this._rotationZ = 0;
  this._x = 0;
  this._y = 0;
  this._z = 0;
 }
 
 public function init3D():void
    {
  //カメラ設定
  camera.z = -300;
  camera.focus = 570;
  camera.zoom = 2;
  
  // はちゅね召還
  this._cmodel = new Collada(&quot;negimiku.dae&quot;);
  this._cmodel.scale = 0.05;
  this.scene.addChild(_cmodel);

  //レンダリング開始
  startRendering();
    }
    
    // トラッキング情報を設定
    public function setTracking(vYaw:Number, vRoll:Number, vPitch:Number, vx:Number, vy:Number, vz:Number):void 
    {
     if(vYaw &gt; 0) {
      this._rotationY = int(vYaw * 360);
     }
     if(vRoll &gt; 0) {
      this._rotationZ = int(vRoll * 360);
     }
     if(vPitch &gt; 0) {
      this._rotationX = int(vPitch * 360);
     }
     if(vx &gt; 0) {
      this._x = int(vx * 360) - 180;
      trace(this._x);
     }
     if(vy &gt; 0) {
      this._y = int(vy * 360) - 180;
     }
     if(vz &gt; 0) {
      this._z = int(vz * 360) - 480;
     }
    }
    
    // オブジェクトの位置を更新
    override protected function onRenderTick(event:Event=null):void
    {
     this._cmodel.rotationX = this._rotationX;
        this._cmodel.rotationY = this._rotationY;
        this._cmodel.rotationZ = this._rotationZ;
        this.camera.x = this._x;
        this.camera.y = this._y;
        this.camera.z = this._z;
        super.onRenderTick(event);
    }
}
</pre></p>

<h4>nanoPADを使ったサンプル</h4>
<p>BOX2D触ってみたかったんで、パッドを叩いたらボールがはねるってのを作るはずが、ただ落ちるだけに。。１〜４のパッドを叩くと丸、三角、四角、棒、星の形のオブジェクトが落ちてきます。叩く強さによって大きさが変わります。太鼓の達人とかできそう。</p>

<p><pre style="height:400px">
package {
 /** 
  * MIDI2AS01.as
  *
  * nanoPADから入力していろいろを降らすよ。
  *
  **/
  
 import com.visp.events.BangEvent;
 import com.visp.midi.MidiManager;
 import com.visp.midi.NoteEvent;
 import flash.display.Sprite;
 
 [SWF(width='800',height='600',backgroundColor='0x000000',frameRate='60')]
 
 public class MIDI2AS01 extends Sprite
 {
  
  private var _midiMgr:MidiManager;
  private var _b2Set:b2Set;
  
  public function MIDI2AS01()
  {

   
   _b2Set = new b2Set();
   this.addChild(_b2Set);
   
   this._midiMgr = MidiManager.getInstance();
   this._midiMgr.initialize();
   
   this._midiMgr.addEventListener(NoteEvent.NOTE_ON, _handleNoteOn);
   this._midiMgr.addEventListener(NoteEvent.NOTE_OFF, _handleNoteOff);
   
   this.addEventListener(BangEvent.BANG_ON, _handleBangOn);
  }
  
  
  private function _handleNoteOn(e:NoteEvent) : void
  {
   if(e.pitch) {
    var bangOn : BangEvent = new BangEvent(BangEvent.BANG_ON, e.pitch, true);
    bangOn.velocity = e.velocity;
    dispatchEvent(bangOn);
   }
  }
  
  private function _handleNoteOff(e : NoteEvent) : void
  {
   if(e.pitch){
    var bangOff : BangEvent = new BangEvent(BangEvent.BANG_OFF, e.pitch, true);
    dispatchEvent(bangOff);
   }
  }
  
  private function _handleBangOn(e:BangEvent):void
  { 
   switch(e.id) {
    case 39:
     _b2Set.createBoxObject(0, e.velocity);
    break;
    
    case 48:
     _b2Set.createBoxObject(1, e.velocity);
    break;
    
    case 45:
     _b2Set.createBoxObject(2, e.velocity);
    break;
    
    case 43:
     _b2Set.createBoxObject(3, e.velocity);
    break;
    
    case 51:
     _b2Set.createBoxObject(4, e.velocity);
    break;
   }
  }
 }
}


// Box2Dのセット
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.b2AABB;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

class b2Set extends Sprite
{
 
 // 物理エンジンの管理クラス
 private var world:b2World;
 // 円の中心
 private var circleCenter:Point = new Point();
 // 物理エンジン内の1mを表すためのピクセル数
 private static const DRAW_SCALE:Number = 100;
 
 public function b2Set()
 {
  
  var worldAABB:b2AABB = new b2AABB();
  worldAABB.lowerBound.Set(-100, -100);
  worldAABB.upperBound.Set(100, 100);
  
  // 重力を定義する
  var gravity:b2Vec2 = new b2Vec2(0, 10);
  
  // 物理エンジン全体のセットアップ
  world = new b2World(worldAABB, gravity, true);
  
  // 床の場所を定義する
  var floorBodyDef:b2BodyDef = new b2BodyDef();
  floorBodyDef.position.Set(4, 5);
  
  // 床の形を定義する
  var floorShapeDef:b2PolygonDef = new b2PolygonDef();
  floorShapeDef.SetAsBox(3.5, 0.1);
  
  // 床を設置する
  var floor:b2Body = world.CreateBody(floorBodyDef);
  floor.CreateShape(floorShapeDef);
  
  // 描画設定
  var debugDraw:b2DebugDraw = new b2DebugDraw();
  debugDraw.m_sprite = this;
  debugDraw.m_drawScale = 100; // 1mを100ピクセルにする
  debugDraw.m_fillAlpha = 0.3; // 不透明度
  debugDraw.m_lineThickness = 1; // 線の太さ
  debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
  world.SetDebugDraw(debugDraw);
  
  addEventListener(Event.ENTER_FRAME, onEnterframe);
  
 }
 
 public function createBoxObject(shape:int, velocity:Number):void 
 {
  
  var x:Number = (Math.random() * stage.stageWidth) / DRAW_SCALE;
  var y:Number = 50 / DRAW_SCALE;
  
  
  switch(shape) {
   case 0:
    var radius:Number =  0.9 * velocity;
    var _ball:Ball =  new Ball(x, y, radius, 0.8, 0.5);
    _ball.setObject(world);
    break;
   case 1:
    var hy:Number = 1.5 * velocity;
    var _box1:Box = new Box(x, y, 0.1, hy, 0.8, 0.5);
    _box1.setObject(world);
    break;
   case 2:
    var len:Number = 1.2 * velocity;
    var _triangle1:Triangle = new Triangle(x, y, len, len, 0.5);
    _triangle1.setObject(world);
    break;
   case 3:
    var _star1:Star = new Star(x, y, 0.8, 0.8, 0.5, world);
    _star1.setObject(world);
    break;
   case 4:
    var bsize:Number = 0.8 * velocity;
    var _box2:Box = new Box(x, y, bsize, bsize, 0.8, 0.5);
    _box2.setObject(world);
    break;
  }
  
 }
 
 private function onEnterframe(e:Event):void 
 {
  if (world == null) {
   return;
  }
  // Flashはデフォルトで秒間24フレームなので、
  // 物理シミュレーションを1/24秒進める
  world.Step(1 / 30, 10);
 }
}


import Box2D.Dynamics.b2BodyDef;
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2World;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Common.Math.b2Vec2;
import flash.geom.Point;

// いろんな形
class Ball 
{
 public var bodyDef:b2BodyDef;
 public var shapeDef:b2CircleDef
 public var body:b2Body;
 
 public function Ball(x:Number, y:Number, radius:Number, density:Number, restitution:Number)
 {
  // 円の場所を設定する
  bodyDef = new b2BodyDef;
  bodyDef.position.Set(x, y);
   
  // 円の大きさなどを設定する
  shapeDef = new b2CircleDef();
  shapeDef.radius = radius;
  shapeDef.density = 0.8;     // 密度 [kg/m^2]
  shapeDef.restitution = 0.8;  // 反発係数、通常は0〜1
 }
 
 public function setObject(target:b2World):void 
 {
  body = target.CreateBody(this.bodyDef);
  body.CreateShape(this.shapeDef);
  body.SetMassFromShapes();
 }
}

class Box 
{
 private var boxBodyDef:b2BodyDef;
 private var boxShapeDef:b2PolygonDef;
 private var boxBody:b2Body;
 
 public function Box(x:Number, y:Number, hx:Number, hy:Number, density:Number, restitution:Number)
 {
  // 箱の場所を設定する
  boxBodyDef = new b2BodyDef();
  boxBodyDef.position.Set(x, y);
  
  // 箱の大きさなどを設定する
  boxShapeDef = new b2PolygonDef();
  boxShapeDef.SetAsOrientedBox(hx, hy, new b2Vec2(0, 0), 0.8);
  boxShapeDef.density = density;        // 密度 [kg/m^2] 通常は1
  boxShapeDef.restitution = restitution;  // 反発係数、通常は0〜1
 }
 
 public function setObject(target:b2World):void {
  boxBody = target.CreateBody(boxBodyDef);
  boxBody.CreateShape(boxShapeDef);
  boxBody.SetMassFromShapes();
 }
}

class Triangle {
 public var triangleBodyDef:b2BodyDef;
 public var triangleShapeDef:b2PolygonDef;
 public var triangleBody:b2Body;
 
 public function Triangle(x:Number, y:Number, size:Number, density:Number, restitution:Number)
 {
  // 三角形の場所を設定する
  triangleBodyDef = new b2BodyDef();
  triangleBodyDef.position.Set(x, y);
  
  // 三角形の大きさなどを設定する
  triangleShapeDef = new b2PolygonDef();
  triangleShapeDef.vertexCount = 3;
  triangleShapeDef.vertices[0].Set(0, 0);
  triangleShapeDef.vertices[1].Set(size, 0);
  triangleShapeDef.vertices[2].Set(0, size);
  
  triangleShapeDef.density = density;        // 密度 [kg/m^2] 通常は1
  triangleShapeDef.restitution = restitution;  // 反発係数、通常は0〜1
  triangleShapeDef.friction = 0.1; // 摩擦係数
 }
 
 public function setObject(target:b2World):void 
 {
  triangleBody = target.CreateBody(triangleBodyDef);
  triangleBody.CreateShape(triangleShapeDef);
  triangleBody.SetMassFromShapes();
 }
}

class Star 
{
 public var starBodyDef:b2BodyDef;
 public var starShapeDef:b2PolygonDef;
 public var starBody:b2Body;
 
 public function Star(x:Number, y:Number, size:Number, density:Number, restitution:Number, target:b2World)
 {
  
  // 星の場所を設定する
  starBodyDef = new b2BodyDef();
  starBodyDef.position.Set(x, y);
  
  starBody = target.CreateBody(starBodyDef);
  
  // 星の大きさなどを設定する
  starShapeDef = new b2PolygonDef();
  
  starShapeDef.vertexCount = 3;
  starShapeDef.vertices[0].Set(0.45, 0);
  starShapeDef.vertices[1].Set(0.62, 0.50);
  starShapeDef.vertices[2].Set(0.29, 0.50);
  starShapeDef.density = density;
  starShapeDef.friction = 0.5;
  starShapeDef.restitution = restitution;
  starBody.CreateShape(starShapeDef);
   
  starShapeDef = new b2PolygonDef();
  starShapeDef.vertexCount = 3;
  starShapeDef.vertices[0].Set(0.90, 0.34);
  starShapeDef.vertices[1].Set(0.50, 0.66);
  starShapeDef.vertices[2].Set(0.40, 0.34);
  starShapeDef.density = density;
  starShapeDef.friction = 0.5;
  starShapeDef.restitution = restitution;
  starBody.CreateShape(starShapeDef);
  
  starShapeDef = new b2PolygonDef();
  starShapeDef.vertexCount = 3;
  starShapeDef.vertices[0].Set(0.58, 0.40);
  starShapeDef.vertices[1].Set(0.73, 0.90);
  starShapeDef.vertices[2].Set(0.32, 0.60);
  starShapeDef.density = density;
  starShapeDef.friction = 0.5;
  starShapeDef.restitution = restitution;
  starBody.CreateShape(starShapeDef);

  starShapeDef = new b2PolygonDef();
  starShapeDef.vertexCount = 3;
  starShapeDef.vertices[0].Set(0.32, 0.40);
  starShapeDef.vertices[1].Set(0.58, 0.60);
  starShapeDef.vertices[2].Set(0.17, 0.90);
  starShapeDef.density = density;
  starShapeDef.friction = 0.5;
  starShapeDef.restitution = restitution;
  starBody.CreateShape(starShapeDef);

  starShapeDef = new b2PolygonDef();
  starShapeDef.vertexCount = 3;
  starShapeDef.vertices[0].Set(0.50, 0.34);
  starShapeDef.vertices[1].Set(0.40, 0.66);
  starShapeDef.vertices[2].Set(0, 0.34);
  starShapeDef.density = density;
  starShapeDef.friction = 0.5;
  starShapeDef.restitution = restitution;
  starBody.CreateShape(starShapeDef);
 }
 
 public function setObject(target:b2World):void 
 {
  starBody.SetMassFromShapes();
 }
}
</pre></p>


<!--p>MIDIイベントとプロパティとかはこれ。<br />
<table cellspacing="1" cellpadding="0" class="summaryTable">
 <tr>
  <td class="propaty">ノートオン</td>
  <td>ControllerEvent.CONTROLLER</td>
  <td>velocity()<br />isHandle(Bool)</td>
  <td>BangEvent.BANG</td>
 </tr>
 <tr>
  <td class="propaty">ノートオフ</td>
  <td>ControllerEvent.CONTROLLER</td>
  <td>velocity(0<br />isHandle(Bool)</td>
  <td>BangEvent.BANG</td>
 </tr>
 <tr class="zebra">
  <td class="propaty">コントロールチェンジ</td>
  <td>ControllerEvent.CONTROLLER</td>
  <td>velocity(0<br />isHandle(Bool)</td>
  <td>RangeEvent.RANGE</td>
  <td></td>
 </tr>
 </table>
</p--

<!--h3>動かしてみる。</h3>
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/5eNIwJMHptw&hl=ja&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/5eNIwJMHptw&hl=ja&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>

<p>PADは叩く強さに強弱を付けれるんで、アナログ感が出ていい感じ。</p-->

<h3>とりあえずつながったけど。。</h3>
<p>いつもの連携ネタですね。今度はMIDI。入力データにMIDIメッセージが使えるのがミソ。ゲートウェイにJavaを使うんでMac/Winで使えるのねん。でもいろいろと問題が山積み。<br />
<br/>
・同時に発生したイベントが送れない？→MIDI Proxy側をいじらないとだめぽい。<br/>
・１チャンネルしか使えない？→同上<br/>
・出力は？→機材がないから試してない。→たぶんむり。<br/>
・遅い→TCPが遅い？。UDP使えたら速くなるかなぁ。→<a href="http://labs.adobe.com/wiki/index.php/Stratus" target="_blank">Stratus</a>。<br/>
<br />
んー気が向けば手を入れてきます。。</p>

<h4>関連エントリ：</h4>
<p>
<ul>
<li><strong>（その１） - 準備とか実装</strong></li>
<li><a href="http://txton.net/hoehoe/2009/090205152847.html">（その２） - キーボードも使ってみる</a></li>
</ul></p>


<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://visp-vj.com/" target="_blank">Visp</a></li>
<li><a href="http://www.korg.co.jp/Product/Synthesizer/nano/index.html" target="_blank">KORG nanoシリーズ</a></li>
<li><a href="http://www.pluto.dti.ne.jp/~daiki/Midi/Midi.html" target="_blank">詳説MIDI規格</a></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>jQuery : ThickBoxとSWFObjectでswfを簡単に表示させる</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/081031042534.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.175</id>

    <published>2008-10-30T19:25:34Z</published>
    <updated>2009-02-12T19:52:05Z</updated>

    <summary>LightBox系のjQueryプラグイン、ThickBoxとSWFObject...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="jQuery" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p>LightBox系のjQueryプラグイン、ThickBoxとSWFObjectでHTMLを用意せずに簡単にswfが表示できるようにしてみたよ。</p>

<p>ThickBoxはLigitBox（modal？）系のjQueryプラグインです。画像だけでなく、HTMLをIFRAMEやインラインに読み込んで、動的に中身を表示することができます。FlashもHTMLを読んじゃえば簡単に表示できるんですけど、SWFObjectの書き方ってすぐ忘れるしｗ。いちいちHTMLファイルを用意するのが面倒。。画像のときみたいに、URLにパラメータをぽこぽこ追加して、タグを動的に生成してくれたら楽なんじゃね？と思ったわけです。まー似たようなのはあるけど自分用ってことで。。</p>]]>
        <![CDATA[<h3>実装するよ</h3>
<p><a href="http://jquery.com/" target="_blank">jQuery</a>と<a href="http://jquery.com/demo/thickbox/" target="_blank">ThickBox</a>と<a href="http://code.google.com/p/swfobject/" target="_blank">SWFObject</a>が必要です。現状の最新版でいいはず。<br />
ダウンロードしてセッティングしときます。<br />
<br />
<strong>thickbox.jsを改造</strong><br />
</p>
<p><pre style="height:130px">
function tb_show(caption, url, imageGroup) {
  （中略）
    if(urlType == '.jpg' || urlType == '.jpeg' || urlType == '.png' || urlType == '.gif' || 
       urlType == '.bmp'){//code to show images
  （中略）
      imgPreloader.src = url;
<span style="color:#FF0000">    }else{//code to show html ←ここを変える</span>
</pre></p>
<p class="preCaption">thickbox.jsの182行目を、下のようにに変更。</p>
<p><pre style="height:400px">
    } else if(urlType == '.swf') {
      
      var queryString = url.replace(/^[^\?]+\??/,'');
      var params = tb_parseQuery( queryString );
      
      TB_WIDTH = (params['width']*1) || 550;    // 横幅
      TB_HEIGHT = (params['height']*1) || 400;  // 高さ
      SWF_VERSION = params['ver'] || 8;      // 必要なFlashのバージョン
      SWF_COLOR = params['color'] || '#FFFFFF';  // 背景
      SWF_WMODE = params['wmode'] || 'opaque';  // ウィンドウモード
      SWF_SCALE = params['scale'] || 'noscale';  // 伸縮
      SWF_MENU = params['menu'] || 'true';    // メニュー

      
      if(params['fullscreen'] != &quot;true&quot;) { // 普通のとき
        $(&quot;#TB_window&quot;).append(&quot;&lt;div id='tbSWFContents'&gt;&lt;script type='text/javascript'&gt;var so = new SWFObject('&quot;+url+&quot;', 'tbObject', '&quot;+TB_WIDTH+&quot;', '&quot;+TB_HEIGHT+&quot;', '&quot;+SWF_VERSION+&quot;', '&quot;+SWF_COLOR+&quot;');so.addParam('wmode', '&quot;+SWF_WMODE+&quot;');so.addParam('scale', '&quot;+SWF_SCALE+&quot;');so.addParam('menu', '&quot;+SWF_MENU+&quot;');so.write('tbSWFContents');&lt;/script&gt;&lt;/div&gt;&quot;);
        tb_position();
      } else { // 全画面のとき（暫定対応）
        $(&quot;#TB_window&quot;).append(&quot;&lt;div id='tbSWFContents'&gt;&lt;script type='text/javascript'&gt;var so = new SWFObject('&quot;+url+&quot;', 'tbObject', '100%', '100%', '&quot;+SWF_VERSION+&quot;', '&quot;+SWF_COLOR+ &quot;');so.addParam('wmode', '&quot;+SWF_WMODE+&quot;');so.addParam('scale', '&quot;+SWF_SCALE+&quot;');so.addParam('menu', '&quot;+SWF_MENU+&quot;');so.write('tbSWFContents');&lt;/script&gt;&lt;/div&gt;&quot;);
        $(&quot;#TB_window&quot;).css({top:&quot;0&quot;, left:&quot;0&quot;, width:&quot;100%&quot;, height:&quot;100%&quot;});
        $(&quot;#tbSWFContents&quot;).css({top:&quot;0&quot;, left:&quot;0&quot;, width:&quot;100%&quot;, height:&quot;100%&quot;});
      }
      
      $(&quot;#tbSWFContents embed, #tbSWFContents object&quot;).css(&quot;margin&quot;,&quot;0&quot;);
      $(&quot;#tbSWFContents embed, #tbSWFContents object&quot;).css(&quot;vertical-align&quot;,&quot;bottom&quot;);

      if(params['wmode'] == &quot;transparent&quot;) { //wmodeがtransparentなら背景と枠線を消す
        $(&quot;#TB_window&quot;).css({background:&quot;none&quot;, border:&quot;none&quot;});
      }
      
      $(&quot;#TB_load&quot;).remove();
      $(&quot;#TB_ImageOff&quot;).click(tb_remove);
      $(&quot;#TB_window&quot;).css({display:&quot;block&quot;}); //for safari using css instead of show

    }else{//code to show html
</pre></p>

<p>レイヤー表示、位置あわせ、等々面倒な部分は、ThickBoxとjQueryにまかせて、拡張子.swfを判別→SWFObjectのタグを動的に生成→CSSの調整を追加します。閉じるボタンのエリアは好きじゃないんで入れてませんｗ。変更できたら上書きしときます。バックアップも忘れずに。
</p>


<h3>使い方</h3>
<p>表示させたいswfのURLに、幅と高さのパラメータを加え、アンカーのクラスに「<strong>thickbox</strong>」と指定するだけ。パラメータは指定しないと表のデフォルトが適用されます。</p>

<p><pre style="height:55px">
&lt;a href=&quot;http://txton.net/hoehoe/temp/<span style="color:#FF0000">test1.swf?width=550&amp;height=360</span>&quot; class=&quot;<span style="color:#FF0000">thickbox</span>&quot;&gt;
表示させるよ。
&lt;/a&gt;
</pre></p>

<p>・普通のとき （幅、高さを指定）<br />
　<a href="http://txton.net/hoehoe/temp/test1.swf?width=550&height=360" class="thickbox">表示させるよ</a></p>

<p>・背景を変えて、拡張メニューは出さないとき<br />
　<a href="http://txton.net/hoehoe/temp/test1.swf?width=550&height=360&color=#FFCC00&menu=false" class="thickbox">表示させるよ</a></p>

<p>・背景を透明にするとき （枠と背景は消えます）<br />
　<a href="http://txton.net/hoehoe/temp/test1.swf?width=550&height=360&wmode=transparent" class="thickbox">表示させるよ</a></p>


<p>とりあえず下のSWFObjectパラメータを設定できます。対応させてないパラメータは、適宜スクリプトに追加すればいいんじゃないかな。
<table cellspacing="1" cellpadding="0" class="summaryTable">
 <tr>
  <td class="propaty">width</td>
  <td nowrap>1 〜</td>
  <td>swfの幅 （デフォルトは550px）</td>
 </tr>
 <tr class="zebra">
  <td class="propaty">height</td>
  <td nowrap>1 〜</td>
  <td>swfの高さ （デフォルトは400px）</td>
 </tr>
 <tr>
  <td class="propaty">ver</td>
  <td nowrap>1 〜</td>
  <td>必要なFlash pluginのバージョン （デフォルトは8）</td>
 </tr>
 <tr class="zebra">
  <td class="propaty">color</td>
  <td nowrap>#RRGGBB</td>
  <td>背景色 （デフォルトは#FFFFFF）</td>
 </tr>
 <tr>
  <td class="propaty">wmode</td>
  <td nowrap>String</td>
  <td>ウィンドウモード。標準（window ）/不透明（opaque ）/透明（transparent） （デフォルトはopaque）</td>
 </tr>
 <tr class="zebra">
  <td class="propaty">scale</td>
  <td nowrap>String</td>
  <td>伸縮。すべて表示（showall）/枠なし（noborder ）/フィット（exactfit ）/拡大・縮小なし（noscale） （デフォルトはnoscale）</td>
 </tr>
 <tr>
  <td class="propaty">ver</td>
  <td nowrap>Boolean</td>
  <td>メニューを表示/非表示 （デフォルトはtrue）</td>
 </tr>
 <tr>
  <td class="propaty">fullscreen</td>
  <td nowrap>Boolean</td>
  <td>ブラウザ内で全画面、縦横を100%にします （デフォルトはfalse）</td>
 </tr>
</table>
</p>

<h3>おまけで全画面にも対応。</h3>
<p><strong>fullscreen=true</strong> と設定すると、ブラウザ内に全画面表示にもできます。背景を透明にするとだいぶイイ感じですよ？ただ、全画面に表示すると、画面をクリックしても元のページに戻れなくなっちゃいます。ThickBoxの機能は生きてるので「escキー」を押すとレイヤーを閉じれます。</p>

<p>・背景は透明、伸縮をフィットにして全画面（100%）表示 （escキーで閉じる）<br />
　<a href="http://txton.net/hoehoe/temp/test1.swf?fullscreen=true&wmode=transparent&scale=exactfit" class="thickbox">表示させるよ</a></p>

<p>マウスのイベントが取れないのは、Flash上でマウスのイベントをjQuery側に通知してあげないとだめですねー。その逆も入れたい。表示周りはCSSを動的にいじってムリヤリ対応させてるだけなんで、環境によってはちゃんと見えないかも。FFだとかなり怪しい。IE8も。あーIE6とかね。。<br />
<br />
イベント周りとか、もっちょと修正してアップデートかけてきますよ。。たぶん。</p>]]>
    </content>
</entry>

<entry>
    <title>Flash : 画像認識でアナログ時計をデジタル時計に変換する</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080929014053.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.173</id>

    <published>2008-09-28T16:40:53Z</published>
    <updated>2009-02-12T19:52:05Z</updated>

    <summary> 大阪てら子17 「Flashで時計大会」で発表したやつね。webカメラと画像認...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080929_ar2clock.gif" src="/hoehoe/images/080929_ar2clock.gif" width="530" height="390" class="tumb-image" /></p>

<p>大阪てら子17 「Flashで時計大会」で発表したやつね。webカメラと画像認識を使ってアナログ時計をデジタル時計に変換してみました。</p>

<p>テーマは時計だったんだけど、画像認識を勉強したかったんで、空気読まずに絡めてみたw。ARだし入力は画像かな。アナログ時計の針って直線だよね→直線が検出できて、角度がわかれば、時間の情報って抽出できるんじゃね？→WEBカメラ使えばスペック次第でリアルタイムでもいけるんじゃあ？→あーなんか使えそうなソースあるじゃん。<strike>パクろ</strike>参考に。って感じ。ちなみに今回の妄想ストーリーは「アンドロイドはアナログ時計を読めるのか？」です。</p>]]>
        <![CDATA[<p>今回はFlashだけで解決できそうなんで、入力にwebカメラを使うだけ。
で、おおまかな流れはこんな感じ。</p>

<p>・アナログ時計をwebカメラで撮影<br />
・カメラから静止画を１枚キャプチャ<br />
・画像を２値化<br />
・ハフ変換、逆ハフ変換で、画像から直線を抽出<br />
・認識できた直線の角度と長さを取得<br />
・長さで短針、長針を判別<br />
・それぞれの角度から時間に変換</p>


<h3>サンプリングする時計を用意する</h3>
<p>キャプチャ元の時計は、２値化したときにノイズがのらないように、盤面はシンプル、黒か白で統一されてて、短針長針は反対色、秒針は色が違うものがいいです。</p>

<p><img alt="080928_braun.jpg" src="/hoehoe/images/080928_braun.jpg" width="530" height="350" class="tumb-image" /></p>

<p>用意したのは<a href="http://www.amazon.co.jp/gp/product/images/B0000X5ISQ/sr=1-16/qid=1222195940/ref=dp_image_text_0?ie=UTF8&n=3828871&s=kitchen&qid=1222195940&sr=1-16" target="_blank">BRAUN Quartz AB1</a>のブラック。かわええなぁ。ホワイトが欲しかったけど廃盤らしいんです。とほ。</p>


<h3>Hough変換による画像からの直線や円の検出</h3>
<p>えーーと。下の図な感じに、原点と任意の直線上にある点（x,y）とを、直角に結ぶ線ρの角度θが決まると、三角形の角度から直線の角度がわかる。。はず。。</p>

<p><img alt="080925_kaisetsu1.gif" src="/hoehoe/images/080925_kaisetsu1.gif" width="530" height="220" class="tumb-image" /></p>

<p>まー直線の検出に関しては、そのままズバりな<a href="http://codezine.jp/article/detail/153?p=1" target="_blank">解説をされている記事</a>があったので、そのままAS3に移植しました。詳しくは元記事を読んで下さい（w。</p>


<h3>JavaからAS3への移植</h3>
<p>元記事のソースはJavaなんですが、<a href="http://cs3book.flashoop.jp/wiki/index.php?Java%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E7%A7%BB%E6%A4%8D%E3%81%99%E3%82%8B" target="_blank">これとかを参考に</a>ちょっと変えるだけでAS3に移植できます。移植の注意点はこんな感じ。</p>

<p>・型指定された配列は型を無視してArrayに。<br />
・多次元配列の初期化は、forでぐるぐる回してnew Array()。<br />
・多次元配列へのアクセスは同じ。（例）hoehoe[0][0]<br />
・float型はNumberに。short、int型もNumberかintに。<br />
・描画は、BitmapDataにsetpixelにするとか。</p>

<p>JavaとAS3は文法とか実装されてる関数が似てるんで、わりと簡単に移植できるようです。ASでやってみたいことがあるけど、具体的なサンプル欲しいよ、わかんないよ。。って時はJavaのソースを漁ってみると結構当たりが出てくるとかと。</p>


<p><pre style="height:300px">
package 
{
  /** 
   * AR2CLOCK.as
   *
   * ハフ変換で直線を検出して、アナログ時計をデジタル時計に変換。
   *
   */
  
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  import flash.filters.ColorMatrixFilter;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.media.Video;
  
  import mx.core.BitmapAsset;

  [SWF(width='640',height='480',backgroundColor='0x000000',frameRate='30')]

  public class AR2CLOCK extends Sprite
  {
    [Embed(source=&quot;clock2.gif&quot;)]
    private var imageClass:Class;
    private var _templateImage:BitmapData;
    private var XMAX:int = 320;
    private var YMAX:int = 240;
    private var RMAX:int = 60;
    private var THETA_MAX:int = 1024;
    private var RHO_MAX:int = (int)(Math.sqrt(YMAX*YMAX+XMAX*XMAX)+0.5);
    private var PIK:Number = Math.PI / THETA_MAX;
    private var COUNT_MAX:int = 2;
    private var CAMERA_WIDTH:int = 320;
    private var CAMERA_HEIGHT:int = 240;
    private var _clockHands:Array = new Array(2);
    private var _canvas:BitmapData;
    private var _ct:clockTxt;
    private var _video:Video = new Video(CAMERA_WIDTH, CAMERA_HEIGHT);
    
    //三角関数テーブル（サイン）
    private var sn:Array = new Array(THETA_MAX);
    //三角関数テーブル（コサイン）
    private var cs:Array = new Array(THETA_MAX);
    //半径計算用斜線長テーブル
    private var diagonal:Array = new Array(YMAX);
    //二次元化した二値原画像データを格納
    private var data:Array = new Array(YMAX);


    public function AR2CLOCK()
    {
      init();
    }
    
    
    // いろいろ初期化してから始めましょう
    private function init():void 
    {      
      //時計のテーブルを作成
      for (var j:int=0; j&lt;COUNT_MAX; j++) {
        _clockHands[j] = new Array();
      }
      //三角関数テーブルを作成
      for (var i:int=0; i&lt;THETA_MAX; i++) {
        sn[i] = Math.sin(PIK*i);
        cs[i] = Math.cos(PIK*i);
      }
      //斜線長テーブルを作成
      for (var y:int=0; y&lt;YMAX; y++) {
        diagonal[y] = new Array(XMAX);
        for (var x:int=0; x&lt;XMAX; x++) {
          diagonal[y][x] = (int)(Math.sqrt(y*y+x*x)+0.5);
        }
      }
      
      // this._templateImage = BitmapAsset(new imageClass()).bitmapData;
      
      main();
    }
    
    
    //ここからメイン
    public function main():void 
    {  
      //カメラの準備
      /*
      var camera:Camera = Camera.getCamera();
      if (camera == null) {
        trace(&quot;カメラがないで￥&quot;);
        return;
      }
      _video.attachCamera(camera);
      this.addChild(this._video);
      // 画面をクリックで検出開始
      stage.addEventListener(MouseEvent.CLICK, cameraCaptureStart);
      */
      // 画像を配置
      this._templateImage = BitmapAsset(new imageClass()).bitmapData;
      this.addChild(new Bitmap(this._templateImage));
      
      // 画面をクリックで検出開始
      stage.addEventListener(MouseEvent.CLICK, imageCaptureStart);
      
      // 確認用の画像を配置するキャンバスを用意
      this._canvas = new BitmapData(320, 240, false, 0x0);
      var _canvasRect:Bitmap = new Bitmap(this._canvas);
      _canvasRect.x = 320;
      this.addChild(_canvasRect);
      
      this._ct = new clockTxt();
      this._ct.clockText.text = &quot; &quot;;
      this._ct.x = (this.stage.stageWidth/2 ) - (this._ct.width/2);
      this._ct.y = 240;
      this.addChild(this._ct);
    }
    
    
    // カメラからキャプチャする
    private function cameraCaptureStart(event:MouseEvent):void 
    {
      var s:BitmapData = new BitmapData(this.CAMERA_WIDTH, this.CAMERA_HEIGHT);
      s.draw(_video);
      var r:Rectangle = new Rectangle(0, 0, this.CAMERA_WIDTH, this.CAMERA_HEIGHT);
      this._canvas.fillRect(r, 0xFFFFFFFF);
      
      // 画像を2値化する
      // s = grayscale_filter(s);
      // this._canvas.threshold(s, r, new Point(0, 0), &quot;&lt;=&quot;, 90, 0xFF000000, 40, false);
      this._canvas.threshold(s, r, new Point(0, 0), &quot;&lt;&quot;, 180, 0xFF000000, 200, false);
      
      // ソースとなる画像を二次元配列data[y][x]に変換する
      changeTo2DDataArray(this._canvas, data);
    }
    
    
    // 画像からキャプチャする
    private function imageCaptureStart(event:MouseEvent):void 
    {
      //var s:BitmapData = BitmapAsset(new imageClass()).bitmapData;
      var r:Rectangle = new Rectangle(0, 0, this.CAMERA_WIDTH, this.CAMERA_HEIGHT);
      this._canvas.fillRect(r, 0xFFFFFFFF);
      
      // 画像を2値化する
      this._canvas.threshold(this._templateImage, r, new Point(0, 0), &quot;&lt;&quot;, 180, 0xFF000000, 200, false);
      
      // ソースとなる画像を二次元配列data[y][x]に変換する
      changeTo2DDataArray(this._canvas, data);
    }


    // 画像の黒色のみ検出して、結果を二次元配列data[][]に入力
    public function changeTo2DDataArray(img:BitmapData, _data:Array):void 
    {
      var width:int = img.width;
      var height:int = img.height;
      for (var ey:int=0; ey&lt;height; ey++) {
        _data[ey] = new Array(width);
          for (var ex:int=0; ex&lt;width; ex++) {
          if (img.getPixel(ex, ey) &gt; 0xFAFAFA) {
            _data[ey][ex] = 1;
          } else {
            _data[ey][ex] = 0;
          }
        }  
      }
      houghConvert();
    }
    
    
    // ハフ変換で直線を検出
    private function houghConvert():void 
    {
      // Hough変換
      
      // 直線の場合
      var theta:int;
      var rho:int;
      // 直線検出用頻度カウンタ
      var counter:Array = new Array(THETA_MAX);
      for (var k:int=0; k&lt;THETA_MAX; k++) {
        counter[k] = new Array(2*RHO_MAX);
        for (var l:int=0; l&lt;(2*RHO_MAX); l++) {
          counter[k][l] = 0;
        }
      }
      
      for (var y:int=0; y&lt;YMAX; y++) {
             for (var x:int=0; x&lt;XMAX; x++) {
          if (data[y][x]==1){
            for (theta=0; theta&lt;THETA_MAX; theta++) {
              rho = (int)(x*cs[theta]+y*sn[theta]+0.5);
              counter[theta][rho+RHO_MAX]++;
            }
          }
             }
           }
           
           // 円の場合
           /*
             今回はないよ
           */
           
           // Hough逆変換
      var end_flag:int;  // 繰り返しを終了させるフラグ
      var count:int;    // 検出された直線または円の個数カウンタ
      
      // 直線の場合
      var counter_max:int;
      var theta_max:int=0;
      var rho_max:int=-RHO_MAX;
            
      end_flag = 0;
      count = 0;
      
      do {
        count++;
        counter_max = 0;
        // counterが最大になるtheta_maxとrho_maxを求める
        for (theta=0; theta&lt;THETA_MAX; theta++) {
          for (rho=-RHO_MAX; rho&lt;RHO_MAX; rho++) {
            if (counter[theta][rho+RHO_MAX] &gt; counter_max) {
              counter_max = counter[theta][rho+RHO_MAX];
              
              // 60ピクセル以下の直線になれば検出を終了
              if (counter_max &lt;= 60) {
                end_flag = 1;
              } else {
                end_flag = 0;
              }
              theta_max = theta;
              rho_max = rho;
              
              // 時計テーブルに検出した線の長さと角度を入れる
              this._clockHands[count-1][0] = counter_max;
              this._clockHands[count-1][1] = (int)(180/this.THETA_MAX*theta_max+0.4);
              
              //trace(&quot;theta_max : &quot; + theta_max + &quot; rho_max : &quot; + rho_max + &quot; Angle : &quot; + (int)(180/this.THETA_MAX*theta_max+0.4) + &quot; counter_max : &quot; + counter_max);
            }
          }
        }
        
        // 検出した直線の描画
        // xを変化させてyを描く（垂直の線を除く）
        if (theta_max != 0) {
          for (x=0; x&lt;XMAX; x++){
            y=(int)((rho_max-x*cs[theta_max])/sn[theta_max]);
            if(y&gt;=YMAX || y&lt;0) continue;
            this._canvas.setPixel(x, y, 0xff0000);
          }
        }
        
        // yを変化させてxを描く（水平の線を除く）
        if (theta_max != THETA_MAX/2) {
          for (y=0; y&lt;YMAX; y++) {
            x=(int)((rho_max-y*sn[theta_max])/cs[theta_max]);
            if (x&gt;=XMAX || x&lt;0) continue;
            this._canvas.setPixel(x, y, 0xff0000);
          }
        }
        
        // 近傍の直線を消す
        for (var j:int=-10; j&lt;=10; j++) {
          for (var i:int=-30; i&lt;=30; i++) {
            if (theta_max+i &lt; 0) {
              theta_max+=THETA_MAX;
              rho_max=-rho_max;
            }
            if (theta_max+i &gt;= THETA_MAX) {
              theta_max-=THETA_MAX;
              rho_max=-rho_max;
            }
            if (rho_max+j&lt;-RHO_MAX || rho_max+j&gt;=RHO_MAX) continue;
            counter[theta_max+i][rho_max+RHO_MAX+j] = 0;
          }
        }
      } while (end_flag == 0 &amp;&amp; count &lt; COUNT_MAX);
      convetTime(this._clockHands);
    }
    
    
    // サンプリングしたデータを時間に変換
    private function convetTime(_clockHands:Array):void 
    {
      var _hour:int;
      var _minutes:int;
      
      if (this._clockHands[0][0] &lt; this._clockHands[1][0]) {
        _hour = (int)(this._clockHands[0][1]);
        _minutes = (int)(this._clockHands[1][1]);
      } else {
        _minutes = (int)(this._clockHands[0][1]);
        _hour = (int)(this._clockHands[1][1]);
      }
      
      // 今の時間を表示
      trace(&quot;NOW &quot; + (int)(_hour/30) + &quot;:&quot; + (int)(_minutes/6));
      var string2:String = ((int)(_hour/30)).toString() + &quot;:&quot; + ((int)(_minutes/6)).toString();
      this._ct.clockText.text = string2;
    }
    
    
    // グレースケールにするよ
        public function grayscale_filter(s:BitmapData):BitmapData 
        {
            var d:BitmapData = new BitmapData(s.width, s.height);
            d.applyFilter(s, new Rectangle(0, 0, s.width, s.height), new Point(0, 0),
                    new ColorMatrixFilter([1/3, 1/3, 1/3, 0, 0,
                                 1/3, 1/3, 1/3, 0, 0,
                                                 1/3, 1/3, 1/3, 0, 0,
                                                 0, 0, 0, 255, 0]));
            return d;
        }
        
        
  }
}
</pre></p>


<h3>動かしてみる。</h3>
<p>（気が向いたら置いとく。）</p>

<p>WEBカメラ版はうまく検出ができないんで、上の時計は使わず画像から入力させてます（w。クリックすると、検出を開始します。赤い線は検出できた直線です。んが、未完成なもんで、いろいろ対応できてません。</p>

<p>・半周分しか時間がわからない。<br />
・６時、１２時とか線が重なっていると読めない。<br />
・針が太いと一つの針で複数検出されて誤差が出る。<br />
・カメラが傾いてると正確に読めない。<br />
・解析の速度が遅くて秒針とかむり。<br />
<br />
とかとか。時計として機能しないじゃん（w</p>


<h3>未完成でデモするもんじゃない。。</h3>
<p>「なんとなく動く版」で発表したけど、ちゃんと検出できずに失敗。スライドを用意して、説明するのにもっと時間を割いた方がよかったなぁ。と反省。。</p>

<p>ま、画像認識も動くと楽しい。AR、CVはいろんな認識方法があるんで、いろいろ試していきたいです。なるべくFlashで（w</p>


<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://codezine.jp/article/detail/153?p=1" target="_blank">CodeZine - Hough変換による画像からの直線や円の検出</a></li>
<li><a href="http://cs3book.flashoop.jp/wiki/index.php?Java%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E7%A7%BB%E6%A4%8D%E3%81%99%E3%82%8B" target="_blank">Flash OOP for Action Script 3.0 - Javaのライブラリを移植する</a></li>
<li><a href="http://opencv.jp/" target="_blank">OpenCV</a></li>
</ul>]]>
    </content>
</entry>

<entry>
    <title>Flash : バーチャルアイドルになる！（その１） - FreeTrack + GlovePIE + Papervision3D + TTS</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080716031353.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.172</id>

    <published>2008-07-15T18:13:53Z</published>
    <updated>2009-02-12T19:52:05Z</updated>

    <summary> Flashの勉強会。大阪てら子16「アイドル！アイドル！」で発表した、「IRヘ...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080629_hatsune1.gif" src="/hoehoe/images/080629_hatsune1.gif" width="530" height="398" class="tumb-image" /></p>

<p>Flashの勉強会。<a href="http://mixi.jp/view_event.pl?id=31784221&comm_id=1863819" target="_blank">大阪てら子16「アイドル！アイドル！」</a>で発表した、「IRヘッドトラッキング＋Papervision3D＋TTSでバーチャルアイドルの中の人になってみよう。」の内容をまとめてみたよ。（いまごろ？とかいうな）。えーだいぶ長い。。<br />
<br />
今回のテーマはアイドル。ってことだったんで、ストレートにPV3Dでバーチャルアイドルでも作ってみる→でもPV3Dでアニメーションとか面倒そう。。→あーヘッドトラッキングとかやってみたいんだよ→生の人間の動きをトレースさせたら、中の人がいるっぽく見えるんじゃね？楽できるんじゃね？→ついでにOSCパケットをリアルタイムで扱うテストもしよう。うん。という感じで始めました。</p>]]>
        <![CDATA[<p>実装優先。ライブラリとアプリで実現できるなら連携。できた構成がこつら。</p>

<p><img alt="080709_hatsune2.gif" src="/hoehoe/images/080709_hatsune2.gif" width="530" height="260" class="tumb-image" /></p>

<p>ヘッドトラッキング部は、アクターの頭に装着したマーカーをWEBカメラで入力→WEBカムを使うヘッドトラッキングのフリーウェア「FreeTrack」で解析（6軸のデータが出力）→「GlovePIE」で受けて、OSC（UDP）で送信→鯖で受けてTCPで送信→Flashで受けて、3Dオブジェクトの動きに反映→動くよ。</p>

<p><img alt="080714_hatsune3.gif" src="/hoehoe/images/080714_hatsune3.gif" width="530" height="260" class="tumb-image" /></p>

<p>TTS部はFlash側からテキストを入力→鯖で受けて、Yahoo!日本語形態素解析APIに投げる→返ってきたデータから、よみがなだけを抽出→ひらがな１文字とローマ字読みを紐付け→「FreeTTS」で読ませて再生→しゃべるよ。こんな感じ。</p>

<h3>IRヘッドトラッキング</h3>
<p>モーションキャプチャした頭の動きに合わせて、画像内の物体等をマッチムーブさせます。3Dの場合、顔の向きや位置に合わせてカメラを動かしたり、オブジェクトを回転、移動させます。顔をそちらに向けると画像内の3D空間を覗き見るように、主観視点での表示をさせることができます。フライトシュミレーター、レースゲームやシューティングゲーム等、主観視点のゲームで多く使われます。頭の動きに対応させたカメラの移動や、HMDと組み合わせることで、プレイヤーがゲーム世界にいるように体現できるつーもんです。<a href="http://www.naturalpoint.com/trackir/" target="_blank">TrackIR</a>のように市販されているものあります。こちらは頭に赤外線反射板の付いたガジェットを付け、センサーでキャプチャする方式です。公式サイトでは反射板が付いた帽子や、ヘッドセットに装着できるタイプもあります。<br />
<br />
最近だと、カーネギーメロン大学の学生Johnny Chung Lee氏が、<a href="http://japanese.engadget.com/2007/12/23/desktop-vr-system-with-wiimote/" target="_blank">Wiiリモコンを使ったヘッドトラッキング</a>を実現させたステキなアレですよ。</p>

<object width="425" height="349"><param name="movie" value="http://www.youtube.com/v/Jd3-eiid-Uw&border=1"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/Jd3-eiid-Uw&border=1" type="application/x-shockwave-flash" wmode="transparent" width="425" height="349"></embed></object>

<p>今回はWEBカメラとポインタに下のようなガジェットを使います。ポインタには３個のLED。底辺の２点はカメラ側、上の１点は少し手前に配置して、三角形を形作るようにします。詳しくは「FreeTrack」公式ページの<a href="http://www.free-track.net/english/hardware/point_model_gallery.php" target="_blank">ユーザーの例</a>を参考にするといいです。ガジェットは顔の側面に付けたりもできます。認識できる角度が限られるんで、正面での配置で進めます。</p>

<p><img alt="080718_to-fu1.jpg" src="/hoehoe/images/080718_to-fu1.jpg" width="530" height="353" class="tumb-image" /></p>

<p>この３つのポインタを付けたガジェットを頭に装着して、固定されたWEBカメラで撮影します。正面から見たとき、LEDの発光点３点を結ぶ３角形を３次元空間に立つ平面と仮定します。キャプチャした映像は２次元ですので、頭を傾けると点の位置が移動し、その３角形も平面を傾けたように歪んで見えます。この平面の傾きを計算で割り出せると、２次元上の３点の座標だけで、３次元空間の頭の座標、傾きが求めることができます。マーカーが３つで角度と位置を合わせた x, y, z, ロール(Roll), ピッチ(Pitch), ヨー(Yaw)の6軸の動き（6DOF）が計算できるよーです。</p>

<h3>FreeTrackでトラッキング</h3>
<p>トラッキングには、WEBカメラと赤外線LEDを使うオープソースの<a href="http://www.free-track.net" target="_blank">FreeTrack</a>を使います。<br />
<br />
・FreeTrackの設定</p>
<p><img alt="080710_freetrack1.gif" src="/hoehoe/images/080710_freetrack1.gif" width="530" height="368" class="tumb-image" /></p>
<p>3PointsCapを選択して、ガジェットの大きさに合わせて長さを入力します。</p>

<p><img alt="080710_freetrack2.gif" src="/hoehoe/images/080710_freetrack2.gif" width="530" height="368" class="tumb-image" /></p>
<p>カメラを選択してStartでキャプチャ、トラッキングが開始されます。光点だけ表示されるよう適度にカメラの設定を調整します。</p>

<h3>GlovePIEからOSCで送る</h3>
<p><img alt="080710_glovepie1.gif" src="/hoehoe/images/080710_glovepie1.gif" width="530" height="257" class="tumb-image" /></p>

<p>次に、ゲーム用のデバイス、Wiiリモコン、TrackIR、モーションキャプチャ、MIDI機器などなど、いろんな入力機器をコントロール→入出力ができる「<a href="http://carl.kenner.googlepages.com/glovepie" target="_blank">GlovePIE</a>」で、FreeTrackから出力されるデータを、次のJava側が受け取れるように、OSCプロトコルにして出力しなおします。FreeTrackのデータはDirectX経由（？）で、GlovePIEのTrackIRプロパティを利用して取得できるようです。<br />
<br />
OSC（Open Sound Control）は、UDP/IP通信でネットワークを介し、複数のコンピューター、アプリケーション同士で音響合成のためのパラメータを双方向にやり取りする、データ通信プロトコルです。元はMIDIのような音用のプロトコルなんだけど、対応ソフトウェア、環境の多さや、URLに似たデータ構造の汎用性の高さから、こーゆーデータのやりとりだけにも使われるよーです。（のわりには解説のページとか少ないような。。）<br />
<br />
GlovePIEは↓な感じにスクリプトでいろいろ制御します。OSCにも対応しているのでクライアントになって、入力機器から取得したデータを他のアプリにデータ通信させます。</p>

<p><pre>
// 取得する間隔を設定
pie.FrameRate = 20 Hz

// OSCクライアントの設定
OSC.ip = "127.0.0.1"
OSC.port = 21588
OSC.broadcast = false

// FreeTrackのデータをOSCパケットで送信
OSC.data = TrackIR.Yaw + "" + TrackIR.Roll + "" + TrackIR.Pitch + "" + TrackIR.X + "" + TrackIR.Y + "" + TrackIR.Z
</pre></p>

<h3>UDP→TCP。通信用サーバーを用意する</h3>
<p>ほい３つ目ｗ。例のごとくFlashはデータを直接受けとれないんで、Socket通信で送り出してあげるゲートウェイをこしらえます。なんでもいいんだけどやっぱりJavaで。データはUDPからOSCパケットが送信されるので、OSCを扱うJavaのライブラリ「NetUtil」を使います。FlashはUDPをサポートしてませんのでUDP→TCPも実装。<br />
<br />
UDPでOSCパケットを受けるスレッドと、TCPでFlashに送信するスレッドを作って、スレッド間でデータをシンクロ。という感じにしてみた。もひとつスマートじゃないね。。まー動くからいいか。</p>

<h3>Papervision3Dって便利。はちゅね召還。動かす。</h3>
<p><img alt="080711_hatsune4.gif" src="/hoehoe/images/080711_hatsune4.gif" width="530" height="187" class="tumb-image" /></p>

<p>出力ねー。FlashでPapervision3Dを使って、3Dのキャラクターを表示させます。モデリングはさっぱりわからんので、PV3Dの解説をされている「<a href="http://blog.r3c7.net/" target="_blank">note.x</a>」のrectさん作、はちゅねミクのColladaファイル（<a href="http://www.phinox.net/" target="_blank">モデル作者はズサさん</a>）を<a href="http://blog.r3c7.net/?p=121" target="_bkank">拝借しました</a>。このモデルをBlenderで、頭部と身体を分解。別々にして、Colladaファイルで保存にしときます。<br />
<br />
んでColladaファイルのオブジェクトをPapervision3Dに読み込みまーす。</p>

<p><pre style="height:300px">
package 
{
  /** 
   * MikuPv3dObject..as
   *
   * はちゅねみくの外部ファイルを読み出して配置したり動かす。
   *
   */
   
  import flash.display.*;
  import flash.events.*;
  
  import org.papervision3d.objects.parsers.Collada;
  import org.papervision3d.view.BasicView;

  public class MikuPv3dObject extends BasicView
  {
    
    private var _rotationX:Number;
    private var _rotationY:Number;
    private var _rotationZ:Number;
    private var _x:Number;
    private var _y:Number;
    private var _z:Number;
    
    private var _cmodel_head:Collada;
    private var _cmodel_body:Collada;
    
    public function MikuPv3dObject()
    {
      //viewportの定義とカメラタイプ定義
      super (0,0,true,false,&quot;CAMERA3D&quot;);
      init();
      init3D();
    }
    
    public function init():void 
    {
      this._rotationX = 0;
      this._rotationY = 0;
      this._rotationZ = 0;
      this._x = 0;
      this._y = 0;
      this._z = 0;  
    }
    
    public function init3D():void
    {
      //カメラ設定
      camera.z = -300;
      camera.focus = 570;
      camera.zoom = 2;
      
      // はちゅね召還
      this._cmodel_head = new Collada(&quot;negimiku_head.dae&quot;);
      this._cmodel_body = new Collada(&quot;negimiku_body.dae&quot;);
      this._cmodel_head.scale = 0.15;
      this._cmodel_body.scale = 0.15;
      this.scene.addChild(_cmodel_head);
      this.scene.addChild(_cmodel_body);

      
      //レンダリング開始
      startRendering();
    }
    
    // ヘッドトラッキング情報を設定
    public function setTracking(vYaw:Number, vRoll:Number, vPitch:Number, vx:Number, vy:Number, vz:Number):void 
    {
   
      if (vYaw &gt; 70) { this._rotationY = 70; }
      else if (vYaw &lt; -70) { this._rotationY = -70; }
      else { this._rotationY = vYaw; }
      
      if (vPitch &gt; 35) { this._rotationX = 35; }
      else if (vPitch &lt; -60) { this._rotationX = -60; }
      else { this._rotationX = -(vPitch); }
      
      if (vRoll &gt; 20) { this._rotationZ = 20; }
      else if (vRoll &lt; -20) { this._rotationZ = -20 }
      else { this._rotationZ = -(vRoll); }
      
      this._x = -(330 * vx);
      this._y = 250 * vy;
      this._z = -(200 * vz);
    }
    
    // オブジェクトの位置を更新
    override protected function onRenderTick(event:Event=null):void
    {
      this._cmodel_head.rotationX = _rotationX;
      this._cmodel_head.rotationY = _rotationY;
      this._cmodel_head.rotationZ = _rotationZ;
      this._cmodel_body.rotationX = _rotationX * 0.2;
      this._cmodel_body.rotationY = _rotationY * 0.35;
      this._cmodel_body.rotationZ = _rotationZ * 0.35;
      this._cmodel_head.x = this._x * 0.04;
      this._cmodel_head.y = this._y * 0.04;
      this._cmodel_head.z = this._z * 0.5;
      this._cmodel_body.x = this._x * 0.05;
      this._cmodel_body.y = this._y * 0.05;
      this._cmodel_body.z = this._z * 0.5;
      
      super.onRenderTick(event);
    }
  }
}
</pre></p>

<p>
身体の上に頭部がくるように配置するわけですが、そのまま動かすとおかしな位置で頭が回ります。PV3Dはオブジェクトの大きさの中心が移動や回転の基準点となります（このモデルだと髪の真ん中あたり）。首のあたりで回転してほしいので、そこが中心になるように、ダミーのオブジェトで包むなどして中心点をずらしておかないとダメなようです。<br />
<br />
あとはJava鯖からソケット通信で送られるデータを受けて、頭のオブジェトクトを動かします。PitchはrotationX、YawはrotationY、RollはrotationZ、x, y, zは座標の移動量に割り当てます。どーせなら身体も動かします。IKとか考えもせずあきらめますｗ。頭に合わせて少し身体が回ったり傾けたりして、追随して動いてるようにごまかしますｗ。あとは頭の可動範囲とか自然な動きになるよう地味ぃに微調整しましょう。<br />
<br />
でーきたー。アクターの動きに合わせて、はちゅねが動くよーになりました。</p>

<h3>てきすとつーすぴーちでしゃべらせる</h3>
<p>もひとつパっとしなかったんで、しゃべる（？）機能を足してみます。<br />
<br />
クライアントからの入力に、何かアクションを返す、話す。とかめんどくさそうなんで。テキストを入力したら読み上げる。くらいにしときます。<br />
<br />
Flash上のテキストボックスでテキストを入力させ、先のゲートウェイ鯖のTCP側のスレッドで受けます。読み上げには、Java Speech APIを利用して音声合成ができる「<a href="http://freetts.sourceforge.net/docs/index.php" target="_blank">FreeTTS</a>」を使ってみます。FreeTTSは日本語に対応していないのですが、ひらがなを１文字ずつ切り、英語で近い発音のものに変換してAPIに投げると、一音ずつ読み上げてくれます。音声の再生は、とりあえずローカルで鳴らせてます。</p>

<p><pre style="height:300px">
package text2talk01;

import java.util.*;

import com.sun.speech.freetts.*;

public class TTS02 extends Thread {

  private static List&lt;TTS02&gt; threads = new ArrayList&lt;TTS02&gt;();
  private String message;
  private String talkDialog;
  
  public TTS02(String word) 
  {
    super();
    message = word;
    threads.add(this);
  }
  
  // ここから処理
  public void run() 
  {
    try {
      List&lt;BeanWord&gt; textAnalysis = MorphologicalAnalysis.execute(message);
      System.err.println(message);
      for(int i = 0; i &lt; textAnalysis.size(); i++) {
        System.err.print(textAnalysis.get(i).getReading());
        talkDialog = talkDialog + textAnalysis.get(i).getReading();
      }
      System.err.print(&quot;\n&quot;);
    } catch (Exception e) {
      threads.remove(this);
      return;
    }
    
    System.err.println(talkDialog);

    String dst = translate(talkDialog);
    System.err.println(dst);

    VoiceManager vm = VoiceManager.getInstance();
    Voice v = vm.getVoice(&quot;kevin16&quot;);
    v.setVolume(1.0f); // 0 to 1.0
    v.allocate();
    v.speak(dst);
    v.deallocate();
    System.err.println(&quot;読み上げ終了&quot;);
    threads.remove(this);
  }

  public String translate(String s){

    StringBuffer dst = new StringBuffer();
    for(int i=0; i&lt;s.length(); i++){
      char c = s.charAt(i);
      if(c &lt; 128){
        // 英語扱い
        dst.append(c);
      }else{
        // 日本語扱い
        String d = m.get(&quot;&quot; + c);
        if(d != null){
          dst.append(d);
        }
        dst.append(&quot; &quot;);
      }
    }
    return dst.toString();
  }

  private static final Map&lt;String, String&gt; m;

  static {
    m = new HashMap&lt;String, String&gt;();
    m.put(&quot;ょ&quot;, &quot;yo&quot;);
    m.put(&quot;あ&quot;,&quot;ah&quot;);
    m.put(&quot;い&quot;,&quot;e&quot;);
    m.put(&quot;う&quot;,&quot;wool&quot;);
    m.put(&quot;え&quot;,&quot;eay&quot;);
    m.put(&quot;お&quot;,&quot;oh&quot;);
    m.put(&quot;か&quot;,&quot;car&quot;);
    m.put(&quot;き&quot;,&quot;key&quot;);
    m.put(&quot;く&quot;,&quot;ku&quot;);
    m.put(&quot;け&quot;,&quot;k&quot;);
    m.put(&quot;こ&quot;,&quot;koh&quot;);
    m.put(&quot;さ&quot;,&quot;sar&quot;);
    m.put(&quot;し&quot;,&quot;c&quot;);
    m.put(&quot;す&quot;,&quot;sue&quot;);
    m.put(&quot;せ&quot;,&quot;say&quot;);
    m.put(&quot;そ&quot;,&quot;so&quot;);
    m.put(&quot;た&quot;,&quot;tar&quot;);
    m.put(&quot;ち&quot;,&quot;tick&quot;);
    m.put(&quot;つ&quot;,&quot;two&quot;);
    m.put(&quot;て&quot;,&quot;tea&quot;);
    m.put(&quot;と&quot;,&quot;toe&quot;);
    m.put(&quot;な&quot;,&quot;na&quot;);
    m.put(&quot;に&quot;,&quot;need&quot;);
    m.put(&quot;ぬ&quot;,&quot;nue&quot;);
    m.put(&quot;ね&quot;,&quot;ney&quot;);
    m.put(&quot;の&quot;,&quot;no&quot;);
    m.put(&quot;は&quot;,&quot;ha&quot;);
    m.put(&quot;ひ&quot;,&quot;he&quot;);
    m.put(&quot;ふ&quot;,&quot;foo&quot;);
    m.put(&quot;へ&quot;,&quot;hey&quot;);
    m.put(&quot;ほ&quot;,&quot;ho&quot;);
    m.put(&quot;ま&quot;,&quot;ma&quot;);
    m.put(&quot;み&quot;,&quot;me&quot;);
    m.put(&quot;む&quot;,&quot;muh&quot;);
    m.put(&quot;め&quot;,&quot;may&quot;);
    m.put(&quot;も&quot;,&quot;mo&quot;);
    m.put(&quot;や&quot;,&quot;yah&quot;);
    m.put(&quot;ゆ&quot;,&quot;you&quot;);
    m.put(&quot;よ&quot;,&quot;yo&quot;);
    m.put(&quot;ら&quot;,&quot;lar&quot;);
    m.put(&quot;り&quot;,&quot;lee&quot;);
    m.put(&quot;る&quot;,&quot;lu&quot;);
    m.put(&quot;れ&quot;,&quot;ray&quot;);
    m.put(&quot;ろ&quot;,&quot;low&quot;);
    m.put(&quot;わ&quot;,&quot;were&quot;);
    m.put(&quot;ゐ&quot;,&quot;e&quot;);
    m.put(&quot;ゑ&quot;,&quot;eay&quot;);
    m.put(&quot;を&quot;,&quot;war&quot;);
    m.put(&quot;ん&quot;,&quot;unn&quot;);
    m.put(&quot;が&quot;,&quot;ga&quot;);
    m.put(&quot;ぎ&quot;,&quot;gee&quot;);
    m.put(&quot;ぐ&quot;,&quot;goo&quot;);
    m.put(&quot;げ&quot;,&quot;gay&quot;);
    m.put(&quot;ご&quot;,&quot;go&quot;);
    m.put(&quot;ざ&quot;,&quot;za&quot;);
    m.put(&quot;じ&quot;,&quot;zee&quot;);
    m.put(&quot;ず&quot;,&quot;zu&quot;);
    m.put(&quot;ぜ&quot;,&quot;zey&quot;);
    m.put(&quot;ぞ&quot;,&quot;zo&quot;);
    m.put(&quot;だ&quot;,&quot;da&quot;);
    m.put(&quot;ぢ&quot;,&quot;zi&quot;);
    m.put(&quot;づ&quot;,&quot;zu&quot;);
    m.put(&quot;で&quot;,&quot;dead&quot;);
    m.put(&quot;ど&quot;,&quot;doh&quot;);
    m.put(&quot;ば&quot;,&quot;ba&quot;);
    m.put(&quot;び&quot;,&quot;be&quot;);
    m.put(&quot;ぶ&quot;,&quot;boo&quot;);
    m.put(&quot;べ&quot;,&quot;bay&quot;);
    m.put(&quot;ぼ&quot;,&quot;bo&quot;);
    m.put(&quot;ぱ&quot;,&quot;pa&quot;);
    m.put(&quot;ぴ&quot;,&quot;pee&quot;);
    m.put(&quot;ぷ&quot;,&quot;pooh&quot;);
    m.put(&quot;ぺ&quot;,&quot;pay&quot;);
    m.put(&quot;ぽ&quot;,&quot;po&quot;);
  }  
}
</pre></p>

<h3>Yahoo!日本語形態素解析APIで、漢字まじりの文章にも対応</h3>
<p>これだと、ひらがなの文章しか読めません。漢字交じりの普通の文にも対応させましょう。漢字に対応させるには、日本語の語彙を理解させてあげないと、音読み訓読み、送り仮名や助詞など区別がつきません。形態素解析すると、自然言語の文章を、意味の持つ最小単位の列に分割し、それぞれの品詞を判別することができます。日本語の形態素解析エンジンはいくつかアプリが提供されているんですが、オンラインで簡単に利用できる「<a href="http://developer.yahoo.co.jp/jlp/MAService/V1/parse.html">Yahoo!日本語形態素解析API</a>」を使ってみます。<br />
<br />
Yahoo!日本語形態素解析APIは、<a href="http://developer.yahoo.co.jp/" target="_blank">Yahoo!デベロッパーネットワーク</a>で開発者向けに提供されているWebサービスで、アプリケーションIDを登録するだけで無料で利用できます。ただし、利用制限があり、24時間以内で1つのIPアドレスにつき50000件のリクエスト。1リクエスト100KB以内に制限されています。指定されたURLにクエリーとして文章を送ると、文節ごとに解析されたXML形式のデータが返ってきます。その中から形態素の読みがなのノードを抜き出し、順番に繋げるとひらがなだけの文章ができあがります。これを先のと同じように、読み上げてあげればいいわけです。</p>

<p><pre style="height:300px">
package text2talk01;

import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

public class MorphologicalAnalysis {

  static final String MAURL = &quot;http://api.jlp.yahoo.co.jp/&quot;
    + &quot;MAService/V1/parse?appid=xxxxxx&amp;results=ma&quot;
    + &quot;&amp;sentence=&quot;;

  @SuppressWarnings(&quot;unchecked&quot;)
  public static List&lt;BeanWord&gt; execute(final String sentence) throws Exception 
  {

    final Document doc = new SAXBuilder().build(new URL(MAURL + URLEncoder.encode(sentence, &quot;UTF-8&quot;)));
    final Element root = doc.getRootElement();
    final Namespace ns = root.getNamespace();
    final List&lt;Element&gt; children = root.getChild(&quot;ma_result&quot;, ns).getChild(&quot;word_list&quot;, ns).getChildren(&quot;word&quot;, ns);
    final List&lt;BeanWord&gt; result = new ArrayList&lt;BeanWord&gt;();

    for (Element child : children) {
      final BeanWord word = new BeanWord();

      word.setSurface(child.getChild(&quot;surface&quot;, ns).getTextTrim());
      word.setReading(child.getChild(&quot;reading&quot;, ns).getTextTrim());
      word.setPos(child.getChild(&quot;pos&quot;, ns).getTextTrim());

      if (child.getChild(&quot;baseform&quot;, ns) != null) {
        word.setBaseform(child.getChild(&quot;baseform&quot;, ns).getTextTrim());
      }
      result.add(word);
    }
    return result;
  }
}
</pre></p>

<h3>動かしてみる。</h3>
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/5eNIwJMHptw&hl=ja&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/5eNIwJMHptw&hl=ja&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>

<p>あー動いた動いた（真夜中の変なテンションでの撮影は危険。水平反転させてたの忘れてるしｗ）。アイドルのくせに声が野太いのがあれですね。。FreeTTSは標準の声以外にも、「<a href="http://tcts.fpms.ac.be/synthesis/mbrola.html">Mbrola</a>」のライブラリを利用できます。ええ。インストールの仕方がわからんので断念したんですが。。ローカル環境で使うんだし、標準で使えるテキスト読み上げ機能とか使ってもよかったのかも。</p>

<h3>今後の妄想とか雑感</h3>
<p>ううう。長いクセにまとまってない。。<br />
カンタンにできたらいいなーと、もりもり作ってたらFlash以外が増えすぎた。反省。Flash増量を目指す。AR、トラッキングとか調べるほどおもしろい。あと３次元変換は、理解が曖昧なんで改めてやります。はい。。<br />
<br />
以下やりたいこと。<br />
<br />
・ネギ振り実装。PV3Dでアニメーションてどうやるのん？<br />
・ヘッドトラッキングはFlashだけでもできるはず。<br />
・コントロールとオーディエンス側を分けたい。双方向でも可。<br />
・IRなのに赤外線じゃない罠ｗ。<br />
・TTSは無くてもいい。使うなら声は変えよう。<br />
・音声はFMS使うとかクライアント側で鳴らしたい。<br />
・Flash10で音響合成は。。。。<br />
<br />
そんな感じ。エントリーはパっと書ききらないとねー。ばた。</p>
<!--
<h4>参考資料：</h4>
<p>
<ul>
<li>FreeTrack</li>
<li>TrackIR</li>
<li>GlovePIE</li>
<li>FreeTTS</li>
<li>Yahoo!日本語形態素解析API</li>
</ul></p>
-->]]>
    </content>
</entry>

<entry>
    <title>LEGO : LEGO MINDSTORMS NXTとPCを連携（その２） - NintendoDS、Flashでコントロール</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080612113256.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.171</id>

    <published>2008-06-12T02:32:56Z</published>
    <updated>2009-02-12T19:52:04Z</updated>

    <summary> iCommandを使って、PCとか他のガジェットからコントロールできるRCカー...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="LEGO" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="bluetooth" label="Bluetooth" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mindstorms" label="MINDSTORMS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="nintendods" label="NintendoDS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="icommand" label="iCommand" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080604_mindstorms1.gif" src="/hoehoe/images/080604_mindstorms1.gif" width="530" height="210" class="tumb-image" /></p>

<p>iCommandを使って、PCとか他のガジェットからコントロールできるRCカーとか作ってみまーす。構成は↑な感じ。鯖とリモコンはそれぞれソケット通信でデータをやりとりします。Bluetoothのデバイスとして認識されているNXTは、シリアルポートを通じてPCと通信をします。鯖とNXTは、Javaでシリアル通信をするためにRXTXライブラリを使い、NXT上のマイコンに命令を送るわけです。</p>]]>
        <![CDATA[<h3>NintendoDSとMINDSTORMSを連携させてみる。</h3>
<p><a href="http://txton.net/hoehoe/2008/080124120531.html">FlashとDSの連携に使ったServer</a>に、iCommandを組み込んでリモコンにします。DS側はボタンのダンエッジとか少し変更。鯖側は、データを受信してそれをクライント全員に返すだけだったので、JSONを扱えるようにします。DSからボタンごとのステータスが送られてくるので、対応させた動きをiCommandのAPIで追加していきます。</p>

<p><strong>NXTと通信させる。</strong></p>
<p><pre>
NXTCommand.open();    // Bluetoothの通信を接続
NXTCommand.close();    // Bluetoothの通信を切断
</pre></p>

<p><strong>モーターを動かす。</strong></p>
<pre>
Motor.A.forward();       // ポートAに接続したモーターを前に回転。
Motor.A.backward();    // ポートAに接続したモーターを後に回転。
Motor.A.stop();            // ポートAに接続したモーターを停止。
</pre>

<p><strong>ビープ音を鳴らす。</strong></p>
<pre>
Sound.playTone(n, w);    // トーン n を w ミリ秒間鳴らす。
</pre>



<h3>できたもの</h3>
<p>とりあえずスレッド部を修正。あとJSONオブジェクト用のクラスも用意。DS側も少し修正。</p>

<p>・Java:ChatServerThread.java</p>
<p><pre style="height:500px">
import icommand.nxt.comm.NXTCommand;
import icommand.nxt.Motor;
import icommand.nxt.Sound;
import net.sf.json.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;


//チャットサーバスレッド
public class ChatServerThread extends Thread { 
  private static List&lt;ChatServerThread&gt; threads = new ArrayList&lt;ChatServerThread&gt;();
  private Socket socket; // ソケット
  
  public static final int DELAY_MS = 100;
  public static final int DIRECTION_FORWARDS = 1;
  public static final int DIRECTION_BACKWARDS = 2;
  public static int direction;
  
  // ビープ音を設定
  private static final short[] note1 = { 2349, 80, 0, 5, 1760, 45, 0, 35 };
  private static final short[] note2 = { 1760, 45, 0, 5, 2349, 80, 0, 35 };
  
  private Boolean PowerState = false;
  
  dsKey dsKeys = new dsKey();
  
  // コンストラクタ
  public ChatServerThread(Socket socket) {
    super();
    this.socket=socket;
    threads.add(this);
  }

  // ここから処理
  public void run() {
    InputStream in = null;
    String message;
    int size;
    
    Pattern p;
    Matcher m;

    byte[] w = new byte[10240];
    
    
    try {
      // ストリーム
      in =socket.getInputStream();
      while(true) {
        try {
          // 受信待ち
          size=in.read(w);

          // データのサイズが0なら切断
          if (size&lt;=0) throw new IOException();

          // 読み込み
          message = new String(w,0,size,&quot;UTF8&quot;);
          
          // 全員にメッセージ送信
          sendMessageAll(message);
          
          // JSON文字列→JSONObject→Beanクラス
          p = Pattern.compile(&quot;(¥&quot;([a-z]+)¥&quot;:([0-9]+).)*&quot;);
          m = p.matcher(message);
          if (m.find()) {
            JSONObject jsonObject = JSONObject.fromObject(message);
            System.out.println(jsonObject.toString());
            dsKey dsKeys = (dsKey) JSONObject.toBean(jsonObject, dsKey.class);
            
            // NXTにコマンドを送信
            sendCommandNXT(dsKeys);

          }
          
          
        } catch (IOException e) {
          socket.close();
          threads.remove(this);
          return;
        }
      }
    } catch (IOException e) {
      System.err.println(e);
    }
  }

  // 全員にメッセージ送信
  public void sendMessageAll(String message) {
    ChatServerThread thread;
    for (int i=0;i&lt;threads.size();i++) {
      thread=(ChatServerThread)threads.get(i);
      if (thread.isAlive()) thread.sendMessage(this,message);
    }
    System.out.println(message);
  }

  // メッセージ送信
  public void sendMessage(ChatServerThread talker,String message){
    try {
      OutputStream out=socket.getOutputStream();
      byte[] w=message.getBytes(&quot;UTF8&quot;);
      out.write(w);
      out.flush();
    } catch (IOException e) {
    }
  }
  
  // NXTにコマンドを送信
  public void sendCommandNXT(dsKey dsKeys) {
    Motor.A.setSpeed(300);
    Motor.C.setSpeed(300);
    
    // ↑キーなら両方のモーターを前進
    if (dsKeys.getAu() == 1) {
      System.out.println(&quot;au:&quot;+dsKeys.getAu());
      direction = DIRECTION_FORWARDS;
      Motor.A.forward();
      Motor.C.forward();
    }
    // ↓キーなら両方のモーターを後進
    else if (dsKeys.getAd() == 1) {
      System.out.println(&quot;ad:&quot;+dsKeys.getAu());
      direction = DIRECTION_BACKWARDS;
      Motor.A.backward();
      Motor.C.backward();
    }
    // ←キーなら右のモーターだけ前進/後進
    else if (dsKeys.getAl() == 1) {
      if (direction == DIRECTION_FORWARDS) {
        Motor.A.forward();
      } else {
        Motor.A.backward();
      }
      Motor.C.stop();
    }
    // →キーなら左のモーターだけ前進/後進
    else if (dsKeys.getAr() == 1) {
      if (direction == DIRECTION_FORWARDS) {
        Motor.C.forward();
      } else {
        Motor.C.backward();
      }
      Motor.A.stop();
    }
    // STARTボタン
    else if (dsKeys.getBs() == 1) {
      if (!PowerState) {
        
        // NXTに接続
        NXTCommand.open();
        
        // 起動ビープ音を鳴らす
        for (int i = 0; i &lt; note1.length; i += 2) {
          final short ww = note1[i + 1];
          final int n = note1[i];
          if (n != 0)
            Sound.playTone(n, ww * 10);
          try {
            Thread.sleep(ww * 10);
          } catch (InterruptedException e) {
          }      
        }
        sendMessageAll(&quot;PowerOn&quot;);
        PowerState = true;
      }       
    }
    // SELECTボタン
    else if (dsKeys.getBe() == 1) {
      if (PowerState)  {
            // 停止ビープ音を鳴らす
            for (int i = 0; i &lt; note2.length; i += 2) {
              final short ww = note2[i + 1];
              final int n = note2[i];
              if (n != 0)
                Sound.playTone(n, ww * 10);
              try {
                Thread.sleep(ww * 10);
              } catch (InterruptedException e) {
              }      
            }

            // NXTと切断
            NXTCommand.close();

            sendMessageAll(&quot;PowerDown&quot;);
            PowerState = false;
          }

    }
    // 入力がなければモーターを停止
    else {
      Motor.A.stop();
      Motor.C.stop();
    }
    
    // スレッドをDELAY_MSの間スリープ
    try {
      Thread.sleep(DELAY_MS);
    } catch (Exception e) {
      System.out.println(e);
      System.exit(1);
    }
  }
}
</pre></p>

<p>・Java:dsKey.java</p>
<p><pre style="height:400px">
public class dsKey {
  private int au;
  private int ad;
  private int al;
  private int ar;
  private int ba;
  private int bb;
  private int bx;
  private int by;
  private int bl;
  private int br;
  private int be;
  private int bs;
  private int pp;
  private int px;
  private int py;
  
  public dsKey() {
  }
  
  public int getAu() { return au; }
  public void setAu(int au) { this.au = au; }
  
  public int getAd() { return ad; }
  public void setAd(int ad) { this.ad = ad; }
  
  public int getAl() { return al; }
  public void setAl(int al) { this.al = al; }
  
  public int getAr() { return ar; }
  public void setAr(int ar) { this.ar = ar; }
  
  public int getBa() { return ba; }
  public void setBa(int ba) { this.ba = ba; }
  
  public int getBb() { return bb; }
  public void setBb(int bb) { this.bb = bb; }
  
  public int getBx() { return bx; }
  public void setBx(int bx) { this.bx = bx; }
  
  public int getBy() { return by; }
  public void setBy(int by) { this.by = by; }
  
  public int getBl() { return bl; }
  public void setBl(int bl) { this.bl = bl; }

  public int getBr() { return br; }
  public void setBr(int br) { this.br = br; }
  
  public int getBs() { return bs; }
  public void setBs(int bs) { this.bs = bs; }
  
  public int getBe() { return be; }
  public void setBe(int be) { this.be = be; }
  
  public int getPp() { return pp; }
  public void setPp(int pp) { this.pp = pp; }
  
  public int getPx() { return px; }
  public void setPx(int px) { this.px = px; }
  
  public int getPy() { return py; }
  public void setPy(int py) { this.py = py; }

}
</pre></p>

<p>・DS:arm9</p>
<p><pre style="height:400px">
#include &lt;nds.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

#include &lt;dswifi9.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;netdb.h&gt;
//---------------------------------------------------------------------------------

#define VCOUNT (*((u16 volatile *) 0x04000006))
#define PEN_DOWN (~IPC-&gt;buttons &amp; (1 &lt;&lt; 6))

touchPosition touchXY;
int touch_was_down = 0;

int my_socket;

static int old_x = 0;
static int old_y = 0;
static int shape_x = 0;
static int shape_y = 0;
static int shape_width = 4;
static int shape_height = 4;
static char *sendBuf;


//---------------------------------------------------------------------------------
// Dswifi helper functions

// WiFi タイマー関数
// sgIPのアップデート
void Timer_50ms(void) {
  Wifi_Timer(50);
}

// ARM7へFIFOメッセージを通知する関数
void arm9_synctoarm7() { // FIFOメッセージを送る
  REG_IPC_FIFO_TX=0x87654321;
}

// 割り込みハンドラ
// ARM7からのFIFOメッセージを取得する
void arm9_fifo() { // FIFOメッセージの入力をチェック
  u32 value = REG_IPC_FIFO_RX;
  if(value == 0x87654321) Wifi_Sync();
}

// フレームバッファで短型を動かす
//---------------------------------------------------------------------------------
void draw(int x, int y, uint16* buf, uint16 c) {
//---------------------------------------------------------------------------------

  buf += y * SCREEN_WIDTH + x;
  int i;
  for(i = 0; i &lt; shape_height; i++) {
    uint16* line = buf + SCREEN_WIDTH * i;
    int j;
    for(j = 0; j &lt; shape_width; j++) {  
      *line++ = c;
    }
  }
}

// Vblank割り込み時の処理。
//---------------------------------------------------------------------------------
void VblankHandler() {
//---------------------------------------------------------------------------------

  sendBuf = (char *)malloc(128);
  int keys[16] = {0};
  
  scanKeys();
  touchXY = touchReadXY();
  int held = keysHeld();
  int down = keysDown();
  int up = keysUp();
  
  if(held &amp; KEY_UP) {
    keys[0] = 1;
  }
  if(held &amp; KEY_DOWN) {
    keys[1] = 1;
  }
  if(held &amp; KEY_RIGHT) {
    keys[2] = 1;
  }
  if(held &amp; KEY_LEFT) {
    keys[3] = 1;
  }
  if(held &amp; KEY_A) {
    keys[4] = 1;
  }
  if(held &amp; KEY_B) {
    keys[5] = 1;
  }
  if(held &amp; KEY_X) {
    keys[6] = 1;
  }
  if(held &amp; KEY_Y) {
    keys[7] = 1;
  }
  if(held &amp; KEY_R) {
    keys[8] = 1;
  }
  if(held &amp; KEY_L) {
    keys[9] = 1;
  }
  if(down &amp; KEY_START) {
    keys[10] = 1;
  }
  if(down &amp; KEY_SELECT) {
    keys[11] = 1;
  }
  
  if(!touch_was_down &amp;&amp; PEN_DOWN) {
    touch_was_down = 1;
    keys[12] = 1;
    keys[13] = touchXY.px;
    keys[14] = touchXY.py;
    iprintf(&quot;\x1b[5;0H&quot;);
    iprintf(&quot;Touch x / y = %03d / %03d\n&quot;, touchXY.px, touchXY.py);
  }
  else if(touch_was_down &amp;&amp; !PEN_DOWN) {
    touch_was_down = 0;
  }
  else if(touch_was_down &amp;&amp; PEN_DOWN) {
    keys[12] = 2;
    keys[13] = touchXY.px;
    keys[14] = touchXY.py;
    iprintf(&quot;\x1b[5;0H&quot;);
    iprintf(&quot;Touch [ %03d / %03d ]\n&quot;, touchXY.px, touchXY.py);
  }
  
  old_x = shape_x;
  old_y = shape_y;
  shape_x = touchXY.px;
  shape_y = touchXY.py;

  draw(old_x, old_y, VRAM_A, RGB15(5, 5, 5));
  draw(shape_x, shape_y, VRAM_A, RGB15(0,31,0));
  
  if(held || down || up) {
    sprintf(sendBuf,&quot;{\&quot;au\&quot;:%d,\&quot;ad\&quot;:%d,\&quot;al\&quot;:%d,\&quot;ar\&quot;:%d,\&quot;ba\&quot;:%d,\&quot;bb\&quot;:%d,\&quot;bx\&quot;:%d,\&quot;by\&quot;:%d,\&quot;bl\&quot;:%d,\&quot;br\&quot;:%d,\&quot;bs\&quot;:%d,\&quot;be\&quot;:%d,\&quot;pp\&quot;:%d,\&quot;px\&quot;:%d,\&quot;py\&quot;:%d}\n&quot;, 
      keys[0], keys[1], keys[2], keys[3], keys[4], keys[5], keys[6], keys[7], keys[8], keys[9], keys[10], keys[11], keys[12], keys[13], keys[14] );
    send( my_socket, sendBuf, strlen(sendBuf), 0 );
  }
  
  iprintf(&quot;\x1b[6;0H&quot;);
  iprintf(&quot;Keys  [ U D L R A B X Y L R E S ]&quot;);
  
  free(sendBuf);
  
  VBLANK_INTR_WAIT_FLAGS |= IRQ_VBLANK;
}

//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------

  powerON(POWER_ALL);
  
  videoSetMode(MODE_FB0);  //フレームバッファモード
  videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);  //グラフィックエンジンのモードを指定。アクティブにして使用するBGも設定
  vramSetMainBanks(VRAM_A_LCD, VRAM_B_LCD, VRAM_C_SUB_BG, VRAM_D_LCD); //VRAMの使用領域設定
  
  SUB_BG0_CR = BG_MAP_BASE(31);
  
  BG_PALETTE_SUB[255] = RGB15(31,31,31);
  consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);

  // ARM7 の WiFi 機能を初期化するためのFIFOメッセージを送る
  {
    REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR; // FIFOの有効＆クリア
    
    u32 Wifi_pass= Wifi_Init(WIFIINIT_OPTION_USELED);
    REG_IPC_FIFO_TX=0x12345678;
    REG_IPC_FIFO_TX=Wifi_pass;
       
    *((volatile u16 *)0x0400010E) = 0; // Timer3 無効
    
    irqInit();
    irqSet(IRQ_TIMER3, Timer_50ms); // Timer IRQ のセット
    irqEnable(IRQ_TIMER3);
    irqSet(IRQ_FIFO_NOT_EMPTY, arm9_fifo); // FIFO IRQ のセット
    irqEnable(IRQ_FIFO_NOT_EMPTY);
    irqEnable(IRQ_VBLANK); // Vblank IRQ のセット
     
    REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_RECV_IRQ; // FIFO IRQ 有効
     
    Wifi_SetSyncHandler(arm9_synctoarm7); // WiFi ライブラリに ARM7への通知用関数をセットする

    // Timer3 のセット
    *((volatile u16 *)0x0400010C) = -6553; // 6553.1 * 256 cycles = ~50ms;
    *((volatile u16 *)0x0400010E) = 0x00C2; // enable, irq, 1/256 clock
    
    while(Wifi_CheckInit()==0) { // ARM7 の初期化終了待ち
      while(VCOUNT&gt;192); // VBlank 待ち
      while(VCOUNT&lt;192);
    }
    
  }
  // WiFi 初期化終了 ここからWiFi ライブラリが使えるようになります
  
  // 単純な接続
  {
    int i;
    // Wifi_AutoConnect では、AOSS 機能は使えないらしいです。
    // WEPの設定をDS本体から読んできます。
    Wifi_AutoConnect(); // 接続要求
    while(1) {
      i=Wifi_AssocStatus(); // ステータスチェック
      if(i==ASSOCSTATUS_ASSOCIATED) {
        iprintf(&quot;Connected successfully!\n&quot;);
        break;
      }
      if(i==ASSOCSTATUS_CANNOTCONNECT) {
        iprintf(&quot;Could not connect!\n&quot;);
                while(1);
        break;
      }
    }
  }
  
  // 接続成功したら、ここからソケットインターフェイスを使ってインターネットに接続することができます。

  // gethostbynameでIPアドレスに返す。直接IPでも。
  struct hostent *myhost = gethostbyname( &quot;10.0.2.1&quot; );
  iprintf(&quot;Found IP Address!\n&quot;);

  // SOCK_STREAM 型のソケットを作る。TCPってこと。
  my_socket = socket( AF_INET, SOCK_STREAM, 0 );
  iprintf(&quot;Created Socket!\n&quot;);

  // 指定したポートにソケットが繋がるか尋ねてみる。
  struct sockaddr_in sain;
  sain.sin_family = AF_INET;
  sain.sin_port = htons(16000); // ポートを指定。なんとなく16000にしてみた。
  sain.sin_addr.s_addr= *( (unsigned long *)(myhost-&gt;h_addr_list[0]) );
  connect( my_socket,(struct sockaddr *)&amp;sain, sizeof(sain) );
  iprintf(&quot;Connected to server!\n&quot;);

  // 接続確認のメッセージを送る。
  //char *sendBuf = &quot;CONNECTED SUCCESS\n&quot;;
  //send( my_socket, sendBuf, strlen(sendBuf), 0 );


  /*
  必要ならこの辺で返ってきたメッセージを受信させるといいんじゃないかな。
  */


  while(1) {
  
    VblankHandler();
    swiWaitForVBlank();
  
  }
  
  //return 0;
}
</pre></p>


<h3>動かしてみる。</h3>
<p><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/xrhw7EAbfJ4&hl=ja&rel=0"></param><embed src="http://www.youtube.com/v/xrhw7EAbfJ4&hl=ja&rel=0" type="application/x-shockwave-flash" width="425" height="344"></embed></object></p>
<p>動くと結構うれしいｗ。DSの通信周りが不安定なんですぐ落ちるのが難点。WiFiのタイマー周りだと思うんだけどなぁ。。</p>


<h3>FlashとMINDSTORMSを連携させてみる。</h3>
<p>もーあとはソケット通信できて、JSON形式で送れればなんでもいいわけで。Flashとも連携させましょう。まずリモコンのパネルとかこしらえます。通信しましょう。動きますw。</p>

<p><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/AVY9DUfMaV0&hl=ja&rel=0&color1=0x3a3a3a&color2=0x999999"></param><embed src="http://www.youtube.com/v/AVY9DUfMaV0&hl=ja&rel=0&color1=0x3a3a3a&color2=0x999999" type="application/x-shockwave-flash" width="425" height="344"></embed></object></p>

<p>うごいたうごいた。でもこれFlashで動いてるかどうかわかんよな。。</p>

<h3>いろいろ作りたいなぁ</h3>
<p>流行りじゃないのか情報ソースが少ないのが問題ですかね。センサー使ってなんか動かしたい！Gainerとか触ってみたいけどー、電子工作はちょっと。。みたいな人にはおすすめ。<br />
<br />
あ、データにJSON形式を使ってるのはiCommandの仕様じゃないです。都合のいいプロトコルを使えばMIDIだったり、MAX/MSPとかProcessingをリモコンにできるかと。<br />
</p>

<h4>関連エントリ：</h4>
<p>
<ul>
<li><a href="http://txton.net/hoehoe/2008/080505144052.html">（その１） - iCommandのインストール</a></li>
<li><strong>（その２） - NintendoDS、Flashでコントロール</strong></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>LEGO : LEGO MINDSTORMS NXTとPCを連携（その１） - iCommandのインストール</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080505144052.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.170</id>

    <published>2008-05-05T05:40:52Z</published>
    <updated>2009-02-12T19:52:04Z</updated>

    <summary> LEGO MINDSTORMS NXTを衝動買いしちゃったんで、ロボットとPC...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="LEGO" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="bluetooth" label="Bluetooth" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mindstorms" label="MINDSTORMS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="osx" label="OS X" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ebay" label="eBay" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="icommand" label="iCommand" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080503_mindstorms1.gif" src="/hoehoe/images/080503_mindstorms1.gif" width="530" height="230" class="tumb-image" /></p>

<p>LEGO MINDSTORMS NXTを衝動買いしちゃったんで、ロボットとPCを連動させて遊んでみます。いけ！ロボ！<br />
<br />
MINDSTORMSをプログラムするには標準の環境の他に、いくつかのプログラム言語やソフトウェア、APIがサードパーティから発表されています（<a href="http://www.teamhassenplug.org/NXT/NXTSoftware.html" target="_blank">NXT対応環境の比較</a>）。大きく分けてマイコンにロードするタイプ、遠隔操作するタイプがあるようです。だいたいがC、C++とかなんで、何かと縁があるJavaでプログラムができる <a href="http://lejos.sourceforge.net/" target="_blank">LeJOS NXJ</a> を使うことにします。その派生プロジェクトには、Javaプログラムから、Bluetooth経由で遠隔操作ができる <a href="http://lejos.sourceforge.net/" target="_blank">iCommand</a> というAPIがあります。LeJOSでプログラムするには、MINDSTORMS本体のファームウェアを書き換える必要があるんですが、iCommandは標準のファームウェアでも使用できるようなんで、標準の環境で遊ぶにも便利ぽいです。とりあえずiCommandとLeJOS NXJでいろいろ動かしてみようかと思います。</p>]]>
        <![CDATA[<h3>iCommandをOSXにインストール</h3>
<p>
<ol>
<li><a href="http://lejos.sourceforge.net/p_technologies/nxt/icommand/downloads.php" target="_blank">iCommand</a>をダウンロード。</li>
<li>アーカイブを解凍して、icommand.jar というファイルをJavaの拡張用のディレクトリ（標準なら /ライブラリ/Java/Extensions/）にコピー。</li>
<li><a href="http://www.rxtx.org/" target="_blank">RXTX</a>ライブラリをダウンロード。Binaryとsourceのアーカイブをダウンロード。</li>
<li>Binaryのアーカイブを解凍して、RXTXcomm.jarとlibrxtxSerial.jnilibをJavaの拡張用のディレクトリにコピー。</li>
<li>sourceのアーカイブを解凍して、MACOSX_IDE/ForPackageMaker/RXTX_Tigerを起動。ウィザードに従ってセットアップ。</li>
<li>NetInfoで自分のアカウントが uucp グループに入っていることを確認する。</li>
<li>/var/lock にディクレクトリがあるか確認。無ければ作る。(sudo mkdir /var/lock)</li>
<li>/var/lock のパーミッションを777に変更する。 (sudo chmod 775 /var/lock)</li>
<li>NXTを起動する。NXTのBluetoothを起動。この時、visibilityがオンになっていることを確認する。</li>
<li>Bluetooth セットアップアシスタントを使って、NXTとPCを連携させる。コンパネのBlutoothを起動、新規デバイスを設定からいけるはず。</li>
<li>無事認識されたら、Bluetoothデバイスの下にNXTと表示されているのを確認する。</li>
<li>NXTデバイスを選択、"シリアルポートを編集..."をクリック。</li>
<li>"セキュティのために登録を必須とする"にチェック→適用。</li>
<li>/dev の下に tty.NXT-DevB-1 というデバイスが見えるか確認する。</li>
<li>icommand.properties というファイルをホームディレクトリにコピー。</li>
<li>COMポートを次のように編集：nxtcomm=/dev/tty.NXT-DevB-1</li>
<li>これで設定は完了！</li>
</ol>
あとはicommand/samplesにあるBeep.javaあたりを実行してみて、PCの命令からビープ音がなればＯＫ。無事PCとの連携がされているはずです。<br />
</p>

<h3>LEGO MINDSTORMS NXTを安く買う</h3>
<p>MINDSTORMS NXTですが市価で35,000〜40,000円はします。ちょっと高いやね。そこで海外のオークションを利用して少し安く仕入れることにしました。<br />
<br />
探してみるとアメリカのオークションサイト<a href="http://www.ebay.com/" target="_blank">eBay</a>の取引〜発送を代行してくれる<a href="http://www.sekaimon.com/" target="_blank">sekaimon</a>というサービスがあるのでこいつを利用してみます。<br />
<br />
eBayだと180〜200ドル前後で競り落とせると思います。あたしの時は１ドル99円という円高時に入札したんで、200ドルちょい。2万円弱でゲットできました。<br />
<br />
後のやりとりは、取引先の人が商品をアメリカ国内のセカイモンに発送、検品をした後に日本に発送されてきます。この時アメリカ国内での発送料、日本への発送料、関税、手数料がかかります。今回は7,500円ほどかかりました。最終的には27,000円強といったところでしょうか。市価より数千円は安く買える感じとなりました。代行業者を使わずに自分で取引ができるなら、配送方法等を工夫して、もう少し安く抑えれるかもしれません。<br />
<br />
ただしマニュアル類は全部英語なんで、日本語版がイイって人はおとなしくAmazonなりで買ってくださいませ。
</p>

<h4>参考資料：</h4>
<p>
<ul>
<li><a href="http://born1981.g.hatena.ne.jp/skelton_boy/20061208" target="_blank">Mac OS XにiCommandをインストールする</a></li>
</ul></p>

<h4>関連エントリ：</h4>
<p>
<ul>
<li><strong>（その１） - iCommandのインストール</strong></li>
<li><a href="http://txton.net/hoehoe/2008/080612113256.html">（その２） - NintendoDS、Flashでコントロール</a></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>Progression : Napsterのブログパーツを作る（その０） - 準備とか</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080424010337.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.169</id>

    <published>2008-04-23T16:03:37Z</published>
    <updated>2009-02-12T19:52:04Z</updated>

    <summary> AS3用フレームワークのProgression Frameworkを使って、ブ...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="as3" label="AS3" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="progression" label="Progression" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ブログパーツ" label="ブログパーツ" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080422_napster1.gif" src="/hoehoe/images/080422_napster1.gif" width="530" height="230" class="tumb-image" /></p>

<p>AS3用フレームワークのProgression Frameworkを使って、ブログパーツみたいなFlashを作りまーす。とりあえず今回は準備編。<br />
<br />
<a href="http://ja.wikipedia.org/wiki/Napster" target="_blank">いろいろあった</a>けど生まれ変わった<a href="http://www.napster.jp/" target="_blank">定額制の音楽ダウンロード・配信サービス Napster</a>。売れ筋のＣＤは少ないけど、こっちで売ってないEP盤とかマイナーな曲があったり、レビューも充実（？）してたりと結構便利。なんせ定額で聴き放題ってのがイイ。毎週水曜くらいに新規追加されたニューリリースが更新されるんですが、アプリがWindows専用なので、Macでは使えません。いちいち違うところにあるWindowsパソコンを見にいくのが面倒なんです。ええ。<br />
<br />
なもんでニューリリースの情報がいつでも見れるように、あとProgressionの勉強も兼ねて、ブログパーツみたいなものを作ろう！かと思います。
</p>]]>
        <![CDATA[<h3>データを取ってくるよ</h3>
<p>
ニューリリースのデータはRSSで配信されているんで、コレを使います。<br />
<br />
アルバムごとにフィードがあるので、更新日、アルバムタイトル、アーティスト名、Napsterへのリンクを取り出します。ジャケ画像は、概要のHTMLにあるIMGタグの中から、正規表現で分解してURLを抽出します。<br />
<br />
抽出した画像のURLを見ると、RSSの配信しているサーバとは違うサブドメインのところにあるようです。普通に画像に取ってこようとすると「<strong>クロスドメイン処理におけるSandbox違反</strong>」にひっかかります。またキミかぁ。<br />
<br />
えー。ところでサンドボックスって何？<br />

<blockquote>保護された領域内でプログラムを動作させることで、その外へ悪影響が及ぶのを防止するセキュリティモデル。「子供を砂場(サンドボックス)の外で遊ばせない」という言葉が語源だと言われている。</blockquote>

<br />
へー。砂場ね。なんか納得w<br />
</p>


<h3>画像Proxyを経由させて回避</h3>
<p><img alt="080422_napster2.gif" src="/hoehoe/images/080422_napster2.gif" width="530" height="170" class="tumb-image" /></p><br />
</p>

<p>ASで外部の画像を読む際の、クロスドメイン処理におけるSandbox違反の回避方法は、<a href="http://d.hatena.ne.jp/nitoyon/" target="_blank">nitoyonさん</a>が<a href="http://d.hatena.ne.jp/nitoyon/20071112/crossdomain_img" target="_blank">まとめておられてた</a>のを参考にします。<br >
<br />
まぁ参照するサーバに crossdomain.xml を置けるわけはないので、自鯖に画像プロキシを置いて回避します。画像をCGIで取ってきて、直接出力という単純なものでいけるはずです。
</p>

<h3>とりあえずFlexでつくってみた</h3>
<p>
前にFlexの習作に作った画像ティッカーがあったんで、手を加えて試してみる。
<div id="test-napsterTicker">
<script type="text/javascript">
var so = new SWFObject("/hoehoe/images/napticker01.swf", "opening", "189", "300", "9", "#FFFFFF");
so.addParam("wmode", "opaque");
so.addParam("menu", "false"); so.write("test-napsterTicker");
</script>
</div>
出たでた。<br />
でもTileListがおかしいね。スクロールバーが出ないね。。<br />
さて、本題にはいろう。
</p>

<h3>インストールするよ</h3>
<p>
・<a href="http://www.adobe.com/jp/products/flash/" target="_blank">Adobe Flash CS3</a><br />
・<a href="http://progression.libspark.org/" target="_blank">Progression Framework</a><br />
・Flash IDE, Flashdevelop, Flex Builder(?)<br />
<br />
Adobe Flash CS3。さっそくハードルが高いですね。。とりあえず30日間無料で使える体験版をいんすこします。もっと安くならないのかしら。。Progressionはフルパッケージ版をダウンロードします。頻繁にUpdateされているので最新のものを落とすようにします。<br />
<br />
Adobeの機能拡張形式でパッケージされているので、お手軽にインストールできるようです。<a href="http://progression.libspark.org/wiki/Documents/Tutorial/Install" target="_blank">チュートリアル</a>を参考にインストールします。<br />
Flashを立ち上げて、Progression Frameworkのコンパネが表示されていればおkです。さっき入れたら古いverの不要なファイルも消してくれたみたい。すごいなぁ。
</p>

<h3>プロジェクトを作ろう</h3>
<p>
また<a href="http://progression.libspark.org/wiki/Documents/Tutorial/Project" target="_blank">チュートリアル</a>を参考にしていきます。制作スタイルの種類はクラスベースにしました。クラスパスを適当にいれて、「作成する」をクリック。プロジェクトを保存するフォルダを選択すると、その中にプロジェクト一式が作成されます。<br />
<br />
とりあえずプレビューしてみます。<br />
<br />
「Progression Framework 2.0.xx<br />
Copyright (c) 2008 taka:nium.jp, supported by Spark project.」<br />
<br />
と出力すればおkのようですよ。<br />
<br />
あとはFlex Builderで作っていきたいんだけど、インポートするだけでもいいのかな？まーいっか。ほとんどProgressionには触れてないけどー今日はココまで！。<br />
次からちょっとずつ作っていくよ。</p>]]>
    </content>
</entry>

<entry>
    <title>NDS : NintendoDSをFlashで使う（その５） - Flashと連携</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080218002545.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.168</id>

    <published>2008-02-17T15:25:45Z</published>
    <updated>2009-02-12T19:52:04Z</updated>

    <summary> ええかげん書ききらないと。出力側のFlashも作る。動作の動画だけ撮って、でき...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="as3" label="AS3" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="flash" label="Flash" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="nintendods" label="NintendoDS" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p><img alt="080218_paintapp1.gif" src="/hoehoe/images/080218_paintapp1.gif" width="530" height="331" class="tumb-image" /></p>

<p>ええかげん書ききらないと。出力側のFlashも作る。動作の動画だけ撮って、できた！ってことでいいかなぁ。。
えーと。。ソース嫁。</p>

<p><strong style="color:#ff0000">このエントリは書きかけの内容が含まれます。気がむいたら随時更新されます。</strong></p>]]>
        <![CDATA[<h3>実装するよ</h3>
<p><strong>Serverとの通信</strong></p>
<p><pre style="height:400px">
/**
 * 初期設定
 */
public private var socket:Socket;


// 通信の準備
publib function init():void {

    socket = new Socket();
    // ボタンは配置しといて
    buttonConnect.addEventListener(MouseEvent.CLICK, buttonConnect_click);

    // 通信状態に対するイベント

    // データを受信した時
    socket.addEventListener(ProgressEvent.SOCKET_DATA, onRecvData);
    // 接続できた時
    socket.addEventListener(Event.CONNECT, function():void {
        trace(&amp;quot;connected!&amp;quot;);
        buttonConnect.label = &amp;quot;DisConnect?&amp;quot;;
    });
    // サーバー側から切断された時
    socket.addEventListener(Event.CLOSE, function():void {
        trace(&amp;quot;closed from server&amp;quot;);
        buttonConnect.label = &amp;quot;Connect!&amp;quot;;
    });
}

/**
 * 通信処理
 */

// 接続する/切断する
private function buttonConnect_click(e:Event):void {
    if(!socket.connected){ // 接続していない時
        trace(&amp;quot;connecting...&amp;quot;);
        socket.connect(talk.labelHost.text, new int(talk.labelPort.text)); // 接続する
    }
    else{ // 接続している時
        socket.close(); // 閉じる
        debug(&amp;quot;closed&amp;quot;);
        buttonConnect.label = &amp;quot;Connect!&amp;quot;;
    }
}

// データを受信した時の処理
private function onRecvData(e:Event):void{
    if(!socket.connected) return;
    if(socket.bytesAvailable &amp;gt; 0){
        var bytes:ByteArray = new ByteArray();
        socket.readBytes(bytes);
        var recvStr:Object = JSON.decode(bytes.toString()); // JSON形式のデータをパース

        DS2Action1(recvStr.p, recvStr.x, recvStr.y); // 何ぞ処理
    }
}
</pre></p>

<p>ソケット通信でServerに繋ぐよ。通信周りは<a href="http://web.sfc.keio.ac.jp/~shokai/" target="_blank">shokaiさん</a>のソケット通信のサンプルを拝借しました（元の記事はわかんなくなった。。）。Serverとテキストをやりとりするだけのもんです。繋いだらデータが来るのをずっと待ちます。</p>

<p><pre>
var&nbsp;recvStr:Object&nbsp;=&nbsp;JSON.decode(bytes.toString());
trace(recvStr.p,&nbsp;recvStr.x,&nbsp;recvStr.y,recvStr.m);
</pre></p>

<p>JSON形式のデータをAS3でパースするには<a href="http://code.google.com/p/as3corelib/" target="_blank">as3corelib</a>を使います。Objectにしてくれます。べんりだねー。</p>

<p><strong>ペン入力を反映</strong></p>
<pre>
//&nbsp;ペン入力を反映
private&nbsp;function&nbsp;DS2Action1(penDown:Number,&nbsp;drawX:Number,&nbsp;drawY:Number):void&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;ペンが接触した座標に点を移動
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(penDown&nbsp;==&nbsp;1)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.moveTo(drawX,&nbsp;drawY);
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;ペンが降りている間は点間の線を描画
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(penDown&nbsp;==&nbsp;2)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.lineTo(drawX,&nbsp;drawY);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p>ペンの状態と、X、Yが送られてくるので、何か反映させます。<br />
とりあえず線でも引いてみたよ。</p>

<h3>できたもの</h3>
<p><pre style="height:400px">
package {
  import com.adobe.serialization.json.JSON;
  
  import fl.controls.Button;
  
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.BlendMode;
  import flash.display.Graphics;
  import flash.display.MovieClip;
  import flash.display.PixelSnapping;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.events.ProgressEvent;
  import flash.geom.Matrix;
  import flash.net.Socket;
  import flash.utils.ByteArray;
  
  [SWF(width='800',height='500',backgroundColor='0x000000',frameRate='60')]

  public class Paint07 extends Sprite
  {
    private var _canvas:BitmapData;
    private var _sketch:Sprite;
    private var _stage2:BitmapData;
    private var _glow:BitmapData;
    private var _particles:Array;
    private var _pen:Graphics;
    private var _penSizeB:Boolean;
    private var _penSize:Number;
    private var _penColor:uint;
    private var _btnSpark:Button;
    private var penDown:Boolean = false;
    private var penState:Boolean = false;
    private var socket:Socket;
    private var talk:MovieClip;
    
    public function Paint07()
    {
      setCanvas();
    }
    
    /**
     * 初期設定
     */
    private function setCanvas():void {
      this._canvas = new BitmapData(640, 480, false, 0x0);
      this.addChild(new Bitmap(this._canvas)) as  Bitmap;
      
      this._glow = new BitmapData(640/4, 480/4, false, 0x0);
      var bm:Bitmap = this.addChild(new Bitmap(this._glow, PixelSnapping.NEVER, true)) as Bitmap;
      bm.scaleX = bm.scaleY = 4;
      bm.blendMode = BlendMode.ADD;
      
      this._particles = [];

      this._sketch = new Sprite();
      this.addChild(_sketch);
      
      setPen();
      
      addEventListener(Event.ENTER_FRAME, loop);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownDraw);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUpDraw);
            stage.addEventListener(Event.MOUSE_LEAVE,onMouseLeaveDraw);

      // 起爆ボタン
      _btnSpark = new Button();
      _btnSpark.label = &quot;SMAAAASH!!&quot;;
      _btnSpark.x = 200;
      _btnSpark.y = 400;
      addChild(_btnSpark);
      _btnSpark.addEventListener(MouseEvent.MOUSE_DOWN, _onBtnDown);
      
      // 通信パネル
      talk = new ChatObj();
      talk.x = 500;
      talk.y = 30;
      addChild(talk);

      // 通信の準備
      socket = new Socket();

      talk.buttonConnect.addEventListener(MouseEvent.CLICK, buttonConnect_click);
      talk.buttonSend.addEventListener(MouseEvent.CLICK, sendText);

      socket.addEventListener(ProgressEvent.SOCKET_DATA, onRecvData); // データ受信した時
      socket.addEventListener(Event.CONNECT, function():void { // 接続できた時
        debug(&quot;connected!&quot;);
        talk.buttonConnect.label = &quot;DisConnect?&quot;;
      });
      socket.addEventListener(Event.CLOSE, function():void { // サーバー側から閉じられた時
        debug(&quot;closed from server&quot;);
        talk.buttonConnect.label = &quot;Connect!&quot;;
      });
    }
    
    private function _onBtnDown(e:Event):void {
      drawBitmap();
    }
    
    // 描いた画像をスキャンしてパーティクルを生成
    private function drawBitmap():void
    {
      _canvas.draw(_sketch);
      _sketch.graphics.clear();
      var cx:Number = this._canvas.width / 2;
      var cy:Number = this._canvas.height / 2;
      var a:Number;
      var v:Number;
      var c:uint;
      for (var ey:Number = 0; ey &lt; _canvas.height; ey += 2) {
        for (var ex:Number = 0; ex &lt; _canvas.width; ex += 2) {
          a = Math.atan2(ey - cy, ex - cy) + Math.random() * Math.PI * 0.1; // 中心から外に向かって飛ばしたいので、その角度を計算
          v = Math.random() * 2 + 0.5;
          c = this._canvas.getPixel(ex, ey);
          if( c &gt; 0 ) {
            this.emitParticle( // パーティクルを生成
              ex + Math.random() * 2, ey + Math.random() * 2,
              Math.random() + 0.5,
              c,
              Math.cos(a) * v, Math.sin(a) * v);
          }
        }
      }
      this.addEventListener(Event.ENTER_FRAME, this.update); // パーティクルを動かす
    }
    
    
    // パーティクルを配列に登録
    public function emitParticle(ex:Number, ey:Number, s:Number = 1, c:int = 0xffffff, vx:Number = 0, vy:Number = 0):void 
    {
      var p:Paticle = new Paticle();
      p.x = ex; // xの位置
      p.y = ey; // yの位置
      p.vx = vx; // x方向に動く量
      p.vy = vy; // y方向に動く量
      p.s = 10; // スピード「
      p.c = c; // 色
      this._particles.push(p); // 保存
    }
    
    
    // パーティクルを動かす
    public function update(e:Event):void 
    {
      this._canvas.lock(); // いっぱい setPixel するときは必ず lock しよう
      this._canvas.fillRect(this._canvas.rect, 0x0); // カンバスをクリア
      var n:int = this._particles.length;
      if (n == 0) {
          this._canvas.unlock();
          this.removeEventListener(Event.ENTER_FRAME, this.update);
      }
      while (n--) {
        var p:Paticle = this._particles[n];
        p.vy += 0.02 * p.s; // 重力を加える
        p.vx *= 0.99; // 空気抵抗
        p.vy *= 0.99; // y 方向にも
        p.x += p.vx; // 動かす
        p.y += p.vy;
        this._canvas.setPixel(p.x, p.y, p.c); // パーティクルを１つ描く
        if (p.y &gt; this.stage.stageHeight) { // もし画面外にでちゃったら
          this._particles.splice(n, 1); // とりのぞく
        }
      }
      this._canvas.unlock(); // lock したやつは必ず unlock
      this._glow.draw(this._canvas, new Matrix(0.25, 0, 0, 0.25)); // キラキラを描く
    }
    
    
    
    /**
     * ペン入力処理
     */
    
    // ペン入力を待つ
    private function loop(e:Event):void 
    {
      if (penDown) draw();
        }
        
    // ペン入力描画
    private function draw():void 
    {
      if (this._penSize &lt; 8) { this._penSizeB = false }
      if (this._penSize &gt; 30) { this._penSizeB = true }
      this._penSize -= (this._penSizeB) ? 2 : -1;
      _pen.lineStyle(this._penSize,this._penColor,1,false,&quot;normal&quot;,&quot;ROUND&quot;,&quot;ROOUND&quot;,3);
      _pen.lineTo(mouseX, mouseY);
      
    }
    
    private function drawDS(penDown:Number, drawX:Number, drawY:Number):void {
      if (penDown == 1) {
        setPen();
        _pen.moveTo(drawX*2, drawY*2);
      } else if (penDown == 2)  { 
        if (this._penSize &lt; 8) { this._penSizeB = false }
        if (this._penSize &gt; 30) { this._penSizeB = true }
        this._penSize -= (this._penSizeB) ? 2 : -1;
        _pen.lineStyle(this._penSize,this._penColor,1,false,&quot;normal&quot;,&quot;ROUND&quot;,&quot;ROOUND&quot;,3);
        _pen.lineTo(drawX*2, drawY*2);
      }
    }
    
    private function onMouseDownDraw(e:Event):void {
      penDown = true;
      setPen();
      _pen.moveTo(mouseX,mouseY);
    }
    
    private function onMouseUpDraw(e:Event):void {
      penDown = false;
    }

    private function onMouseLeaveDraw(e:Event):void {
      penDown = false;
    }
    
    // ペンの設定
    private function setPen():void {
      this._penSizeB = true;
      this._penSize = 30;
      this._penColor = Math.random()*0xFFFFFF;
      this._pen = _sketch.graphics;
      this._pen.lineStyle(this._penSize,this._penColor,1,false,&quot;normal&quot;,&quot;ROUND&quot;,&quot;ROOUND&quot;,3);
    }
    
    
    
    /**
     * 通信処理
     */
    
    private function buttonConnect_click(e:Event):void {
      if(!socket.connected){ // 接続していない時
        debug(&quot;connecting...&quot;);
        socket.connect(talk.labelHost.text, new int(talk.labelPort.text)); // 接続する
      }
      else{ // 接続している時
        socket.close(); // 閉じる
        debug(&quot;closed&quot;);
        talk.buttonConnect.label = &quot;Connect!&quot;;
      }
    }
    
    private function sendText(e:Event):void{
      if(!socket.connected) return;
      var bytes:ByteArray = new ByteArray();
      bytes.writeUTFBytes(talk.labelSend.text);
      socket.writeBytes(bytes);
      socket.flush();
      debug(&quot;送信 :&quot;+bytes.toString());
    }
    
    private function onRecvData(e:Event):void{
      if(!socket.connected) return;
      if(socket.bytesAvailable &gt; 0){
        var bytes:ByteArray = new ByteArray();
        socket.readBytes(bytes);
        var recvStr:Object = JSON.decode(bytes.toString());
        //draw(recvStr.x, recvStr.y);
        
        debug(&quot;受信: p:&quot;+recvStr.pp+&quot; x:&quot;+recvStr.px+&quot; y:&quot;+recvStr.py);
        if (recvStr.bb == 1) drawBitmap();
        drawDS(recvStr.pp, recvStr.px, recvStr.py);
      }
    }
    
    private function debug(message:String):void{
      trace(&quot;debug: &quot;+message);
      talk.textAreaLog.text = message + &quot;\n&quot; + talk.textAreaLog.text;
    }
  }
}


/**
 * パーティクルの定義
 */
class Paticle {
  public var x:Number;
  public var y:Number;
  public var vx:Number;
  public var vy:Number;
  public var s:Number;
  public var c:int;
  
  public function Paticle() 
  {
    this.x = 0;
    this.y = 0;
    this.vx = 0;
    this.vy = 0;
    this.s = 1;
    this.c = 0xffffff;
  }
}
</pre></p>

<h3>動かしてみる。</h3>

<p>ボタン入力とかいろいろ加えるとこんな感じ。</p>

<p><img alt="080218_paintapp1.gif" src="/hoehoe/images/080218_paintapp1.gif" width="530" height="331" class="tumb-image" /></p>

<p>Bボタンを押すと爆発します。<a href="http://saqoosha.net/2008/01/28/634/" target="_blank">てら子11のあれ</a>を拝借しました。</p>

<p><img alt="080218_paintapp2.gif" src="/hoehoe/images/080218_paintapp2.gif" width="530" height="331" class="tumb-image" /></p>

<!--
<p>（080612更新） すっかり忘れてたけど動画もとってみた。</p>

<p><object width="425" height="349"><param name="movie" value="http://www.youtube.com/v/9wUmWDSyhU0&hl=ja&color1=0x3a3a3a&color2=0x999999&border=1"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/9wUmWDSyhU0&hl=ja&color1=0x3a3a3a&color2=0x999999&border=1" type="application/x-shockwave-flash" wmode="transparent" width="425" height="349"></embed></object></p>
-->

<p>うん。できたできた。でも通信がなんだか不安定。Wi-Fiのタイマーがちゃんと動いてないのか、メモリ使いすぎてるのか。デバックはちゃんとしないといけないです。</p>


<h4>関連エントリ：</h4>
<p>
<ul>
<li><a href="http://txton.net/hoehoe/2007/071208001658.html">（その１） - 概要とWiFi環境</a></li>
<li><a href="http://txton.net/hoehoe/2007/071218145413.html">（その２） - DSとFlashをMIDIで繋げる</a></li>
<li><a href="http://txton.net/hoehoe/2007/071219171008.html">（その３） - DSの勝手アプリを作るお</a></li>
<li><a href="http://txton.net/hoehoe/2008/080124120531.html">（その４） - Serverの準備</a></li>
<li><strong>（その５） - Flashと連携</strong></li>
</ul></p>]]>
    </content>
</entry>

<entry>
    <title>勉強会：第11回 大阪てら子「なぎまぐの Flash ライブコーディング featuring たけし（休）」に行ってきたよ</title>
    <link rel="alternate" type="text/html" href="http://txton.net/hoehoe/2008/080130161849.html" />
    <id>tag:hoehoe.sakura.ne.jp,2008:/hoehoe//3.167</id>

    <published>2008-01-30T07:18:49Z</published>
    <updated>2009-02-12T19:52:04Z</updated>

    <summary>たまにはレポートも書かないとねー。関西屈指のキワモノツワモノ共が集うFlash/...</summary>
    <author>
        <name>hoehoe3</name>
        
    </author>
    
        <category term="Flash" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="as2" label="AS2" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="as3" label="AS3" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="勉強会" label="勉強会" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="ja" xml:base="http://txton.net/hoehoe/">
        <![CDATA[<p>たまにはレポートも書かないとねー。関西屈指の<strike>キワモノ</strike>ツワモノ共が集うFlash/ActionScript道場。<a href="http://mixi.jp/view_community.pl?id=1863819" target="_blank">第11回 大阪てら子「なぎまぐの Flash ライブコーディング featuring たけし（休）」</a>に行ってきたよ。<br/>
</br>
参加者は○名（わすれた）、発表は３名。</p>

<h3>ライブラリを使わずに3D（Flash ライブコーディング）</h3>
<p>発表者は<strong><a href="http://nagimagu.jp/blog/" target="_blank">なぎまぐさん</a></strong>。<br/>
ライブコーディングで、マウスに追随してグルグル廻すデモを作る。<br/>
<ul style="border-color:#ffffff">
<li>マウスの座標をキーにアフィン変換で座標変換する。<br/></li>
<li>動くと結構かっこいい。<br/></li>
</ul>
<br/>
気抜いてる間にいつの間にかできてました。。グルグル。</p>

<h3>寒いし雪でも降らせよう（Flash ライブコーディング）</h3>
<p>たけしCEOが体調不良でお休みなので、<strong><a href="http://saqoosha.net/" target="_blank">さくーしゃさん</a></strong>が急遽登板。<br/>
ライブコーディングで、雪のパーティクルを降らせるデモを作る。<br/>
<ul style="border-color:#ffffff">
<li>1ピクセルのビットマップでパーティクルを作って落とす。<br/></li>
<li>Bitmapのカンバスを作って、その中を描き直すことでアニメーションさせる。<br/></li>
<li>いっぱい→各点のX、速度をランダムで降ってくる感じに。<br/></li>
<li>文字の形に降らせる。文字のBitmapを置いてスキャン→白に当たったら雪を生成。<br/></li>
<li>forcemapを使って積もらせる。積もらせる形を描いて背景に置く。→雪の位置の背景をスキャン→速度を変える。黒：速い、白：遅く→積もってるように見える。<br/></li>
<li>キラキラ点滅はローテクで回避ｗ。こーゆーのがいい。キラキラ用に1/4のサイズの新しいカンバスを作る→smoothingつけて4倍の大きさで配置→加算モードで合成→雪を動かすときにキラキラ用のカンバスにDraw→1/4にした時に点がたまに消える＋拡大smoothingでグローぽく。キラキラ。<br/></li>
<li>パーティクルするならBitmapのが速い。<br/></li>
<li>処理中は表示するまでStageをロックしとくと、なんとんく速くなる。<br/></li>
</ul>
詳しくはソースを読めばいい↓<br/>
→<a href="http://saqoosha.net/2008/01/28/634/" target="_blank">http://saqoosha.net/2008/01/28/634/</a>
</p>

<h3>ASでの12色環へのアプローチ</h3>
<p>WCAN@名古屋の<strong><a href="http://ra66it.net/blog/" target="_blank">山ゴンさん</a></strong>が飛び入りで発表してくれました。<br/>
バウハウスの12色環のアレをASで書いて見よう。という内容。<br/>
<ul style="border-color:#ffffff">
<li>画像から色を抽出→３色からイイ感じの合成色を生成する。<br/></li>
<li>エロい展開を期待してたのはあたしだけじゃないはず。。<br/></li>
</ul>
詳しい内容はこちらから↓<br/>
→<a href="http://ra66it.net/blog/index.php?ID=739" target="_blank">http://ra66it.net/blog/index.php?ID=739</a></p>

<h3>その他：話題に出たこと</h3>
<p>・Flash Develop vs Flex Builder<br/>
・delegate再び。<br/>
・ライブコーディングするなら事前に練習しよう。<br/>
<br/>
えーと。あとは覚えてない。。<br/>
</p>

<p>まーそんな感じ。宣伝効果があったのか、新しい人もたくさんいて新鮮でしたｗ。<br/>
いつも場所を提供して下さる<a href="http://www.colors.jp/" target="_blank">COLORS</a>さん。スピーカーの皆さんありがとうございました。</p>

<p>次回は2/16（土）。1周年らしいですわよ。</p>]]>
        
    </content>
</entry>

</feed>
