自習室

こもります

ofxLibWebsockets で oF とウェブブラウザの間でお話しする

はじめに

相変わらず、ブラウザとopenFrameworks 間で共同作業させるシリーズです。

以前こんなものを書きました。

これらの記事では、ブラウザとoF間にNode.jsのプログラムを挟み、socket.io と OSC を相互に変換することで、データのやりとりをしていました。もともと以下の理由でそういう構成にしていました。

  • ブラウザとnode.js間はsocket.ioでの通信が楽
  • node.jsとoF間では、OSCでの通信が楽
  • それを単につなげればよいので楽

このときの構成は下図のようになります

f:id:AMANE:20150407143947p:plain

しかしこの結果

  • 機能上はほぼ意味の無い node.js の変換コードを書かなければいけない。実装コスト増大

という問題も発生しました。

  • oFでやろうとしていることを別のプログラムでシミュレートする
  • 複数のブラウザやoFのプログラムとやりとりをする

などのケースを想定すると多段構成にする意味もありますが、一対一で通信をするだけなら、ほぼ意味はありません。従って、ブラウザとoFで直接お話しをさせたいです。

いろいろ試した結果、以下の構成が可能であることがわかりました。今回はその方法についてまとめます。

f:id:AMANE:20150407143959p:plain

非バイナリデータのみ

WebSocketでバイナリのやりとりも出来る筈ですが、本記事では非バイナリデータ…つまり、数値、文字、bool とそれらをオブジェクト化した物などをやりとりします。いずれバイナリも調査します。

この記事を追うとできるもの

oFの画面でマウスを動かすと、その座標をJSONとしてラップし、WebSocketを介してブラウザに送ります。ブラウザではJSONを復元して、オブジェクトとしてコンソールに出力します。

f:id:AMANE:20150408215316j:plain

完成品はこちらにアップしています

github.com

ofxLibWebsockets の基本

addon単体としてのドキュメントがほとんど無いため、使いこなしに少々苦労しました。サンプルを読んで探していくスタイル。

プロジェクトへの追加

oFプロジェクトへの追加の方法は、ofxLibWebsockets のgithubにまとめられています。こちらを参考に追加もしくはoFプロジェクトの作成を行ってください。

自分はWindows(VS2012) で試しているのですが、配布されている ofxLibWebsockets のオプションに間違いを見つけました。以下修正してください。

プロジェクトプロパティ > リンカー > 全般 > 追加のライブラリディレクトリ で、構成が Debug の際に、

..\..\..\addons\ofxLibwebsockets\libs\libwebsockets\lib\win32\Release` となっているものを
..\..\..\addons\ofxLibwebsockets\libs\libwebsockets\lib\win32\Debug に修正。 

oF側をサーバとしてセットアップ

ofxLibWebsockets のサンプル "example_server_echo" を参考に。最低限の構成は以下、だと思います

void ofApp::setup(){
    ofxLibwebsockets::ServerOptions options = ofxLibwebsockets::defaultServerOptions();
    options.port = 9092;
    options.bUseSSL = false; // you'll have to manually accept this self-signed cert if 'true'!
    bSetup = server.setup( options );

    // this adds your app as a listener for the server
    server.addListener(this);
}

メッセージの受け取り

こちらも、ofxLibWebsockets のサンプル "example_server_echo" を参考に。ただ受け取ったメッセージを標準出力します。

void ofApp::onMessage( ofxLibwebsockets::Event& args ){
    cout << "got message " << args.message << endl;
}

試しにブラウザから送ってみる

先に "example_server_echo" を起動してから、Chromeで適当なページを開き、developer tool のコンソール上で以下の様に順に打ち込みます

ws = new WebSocket('ws://localhost:9092'); // 接続
ws.send('hoge'); // 文字列を送る

一行目をenterした時点で、oFのコンソールには "new connection open" と表示されます。
更に二行目をenterすると、oFのコンソールに "got message hoge" を表示されます。

JSON形式でやりとりしてみる

ブラウザ側から送ってくるデータは、それが元々オブジェクトであった場合も、message = JSON.stringify(data) したのちに送り出し、ofxLibWebsockets で受け取ると、jsonとして復元されます。

逆に、ブラウザ側でJSON形式で受け取りたい場合は、oF側でJSON形式でまとめ上げたものを文字列として送り出し、それをdata = JSON.parse(message) することで、jsonとして復元されます。

図にまとめると下のようになります。

f:id:AMANE:20150407162026p:plain

ofxLibWebsockets は内部でjsoncpp というライブラリを利用しているので、json <-> 文字列 の変換が出来ます。新しいjson形式のデータ作成も可能です。

ブラウザからobjectデータを送り出し、oFで受け取るケース

// 送出側 (js)
var ws = new WebSocket('ws://localhost:9092/');
var pos = {'x':0, 'y':100};
var message = JSON.stringify(pos);
ws.send(message);
// 受け取り側 (cpp)
void ofApp::onMessage( ofxLibwebsockets::Event& args ){
    // args.messageには、 js側で message = JSON.stringify(pos) として作られた文字列"message" が入っている
    cout<<"got message "<<args.message<<endl;
    
    // 送られてきた args.message がjsonにパースできる形式である場合、
    // 自動的にパースされ args.json に Json::Value 形式で収められる
    if ( !args.json.isNull() ){
        cout << "New message: " + args.json.toStyledString() + " from " + args.conn.getClientName();
    }
}

oFでJSON形式にラップしたものをブラウザで受け取るケース

マウスを動かすと、最新の座標をWebSocketで送り出す、というもの

// 送出側 (cpp)
void ofApp::mouseMoved(int x, int y ){
    Json::FastWriter    writer;
    Json::Value     value;
    value["x"] = x;
    value["y"] = y;
    std::string message =  writer.write(value);
    server.send(message);
    cout << "mouseMoved : " << message << endl;
}
// 受け取り側 (js)
var ws = new WebSocket('ws://localhost:9092/');
ws.onmessage = function(event) {
  var data = JSON.parse(event.data); //event.dataは文字列。これをJSON形式にパース
  console.log(data);
}

JSONCPP の使い方

以下のページなどを参考にしました

スピードを上げる

ofxLibWebsocket (libwebsocket)では、WebSocketの送り出し受け取りを、専用のスレッドを立てて行っています。一つの処理をコンテキストとしてまとめ、それをキューに積んでいって、前から順に処理していきます。その辺りはこちらのページで勉強させて頂きました。

ofxLibWebsocketでは、そのスレッド処理に、何故か標準で 50ms の待機時間が挿入されているため、毎秒 20 回までしか通信の処理が出来ません。マウスの動きを伝えるには遅すぎます。ここを変更します。

// addons/ofxLibwebsockets/ofxLibWebsockets/Server.cpp 19行目
    Server::Server(){
        context = NULL;
        waitMillis = 10; //★ここ 50 -> 10 など。
        reactors.push_back(this);
        
        defaultOptions = defaultServerOptions();
    }

これで、最大100回/秒 通信できるようになります。

waitMillis を変更するための public な関数をServer.cpp か 親クラスの Reactor.cpp に追加しても良いかもしれません。

さいごに

oFで画像を作ってブラウザ内で利用する、と言ったことも出来るかもしれないので、調査してみます。

dropmark のビデオプレイリストの再生をループさせるChrome Extension を作った

動機

こんなことがしたい、と思いました

  • 余ってるPC + ディスプレイで、様々な動画を垂れ流しにしたい(会社でシェアしたい)
  • 極力簡単にプレイリストを管理したい
  • YouTube と Vimeo のリンクを貼るだけでOKみたいな感じがベスト
  • 全画面再生
  • ループ

イケてる動画をみんなであつめてそれを垂れ流しておくことで、ふとした瞬間に素敵な動画に出会えるたら良いなーというのが狙いです。

調査

公式

Vimeo だと、Likesなり Watch Laterなり Channel なりを、"Watch in Couch Mode" で再生開始すると、自動的に全画面 + ループになります。

f:id:AMANE:20150301201100p:plain

YouTubeだと、お気に入りなりプレイリストに、「繰り返す」ボタンがあります。これをONにしてから全画面再生すれば良い感じです。

f:id:AMANE:20150301201113p:plain

それぞれ良いのですが、会社で色んな人に登録してもらうとすると、両方のサイトにアップロードされている動画を扱いたくなります。

おしいやつ

Tumblrで動画を集めておくことはもちろん出来ますが、自動で再生していく機能はありません。

同じようなものがないかなーと探していたところ、dropmark というサービスの存在を知りました。

dropmark

自分も今回の目的以外で使ったことがないのでよくわかっていないのですが、いわゆるブックマークサービスです。その中の特徴的な機能として、ビデオのリンクを貼っておくと自動で再生してくれる機能、と言うのがありました。この記事で知りました

これで解決!と思ったのですが、ひじょーに惜しいことに、リストの最後まで行くと、そこで一覧画面に戻ってしまう仕様です。

そこで、Chrome Extensionを使って、ウェブサービスをハックしてみることにしました。

完成品と使い方

ぎっはぶ

ここに上げてあります

使い方

  • コードをPC内のどこかにおく
  • chromeの設定 > 拡張機能 > パッケージ化されていない拡張機能を読みこむ
  • フォルダを指定して開く

現状のコードは、すべてのウェブサイトに対して処理をしてしまうような物にになっているので、dropmarkを全画面で再生し続ける専用のPCを用意できる場合は良いのですが、もしご自身のPCで一時的に試してみたい、と言う場合は、利用しない場合は、拡張機能のリスト中で「有効」のチェックをはずしておくと良いかと思います。

f:id:AMANE:20150301205851p:plain

ついでに

YouTubeの再生を極力高画質で行いたいので、別途それ用のChrome-extension を入れます。この手の物はたくさんあるのですが、こいつはちゃんと動いていました

Vimeoの方は、ある程度大きくプレイヤーが開いたら、自動的に高画質になるので心配ありません。

設計(?)

主に、以下の三つのことをやっています。

  1. プレイリストの最後のビデオになったかの判定
  2. プレイリストの先頭のリンクを取得する
  3. 最後のビデオの次に、プレイリストの先頭のビデオを再生するようにハック

1. プレイリストの最後のビデオになったかの判定

これは、画面中の "Next Item" ボタンのハイパーリンクを見ることで判別出来ます。動画を指すURLではなく、動画一覧を指すURLだった場合は該当です。

f:id:AMANE:20150301202152p:plain

2. プレイリストの先頭のリンクを取得する

プレイリストのHTMLの中身を見て動画の一個目を指すURLを取得すれば良いのですが、実際は「最後の動画を見ている最中に」異なるページのHTMLを参照することになるので、少しトリッキーなことをします。

  1. jQuery<div> 要素を作ってその中にプレイリストのHTMLを丸っと挿入する
  2. その中から該当するHTML要素を見つける
$.get(playlisturl, function(data) {
  var playlisthtml = $('<div>').html(data);

jQueryのgetコマンドで動画一覧のページのHTMLを引っこ抜いてきて、それを <div> タグの中に展開することで、jQueryで解析可能なものにします。ここでこの <div>.append とかしなければ、その要素は表示されたりはしません。あくまでHTMLデータの中身を解析するためだけに一時的に存在するものになります。

プレイリストのhtmlを取得できたら、その中から、お目当ての「先頭の動画」のURLを見つけてきます

var firsturl = playlisthtml.find('.item-preview:first-child').attr('href');

3. 最後のビデオの次に、プレイリストの先頭のビデオを再生するようにハック

1.で、最後のビデオか判定するのに使った "Next Item" の href に、先ほど見つけてきた先頭動画のURLを代入すれば完了です。

まじで?

「えっ…?」って感じでした。次の動画への飛ばし方はちゃんと本家のjs読めばわかるかも知らん、気合い入れて解析してやろう!と意気込んで始めた直後、まぁそんなわけはあるまいと思いつつもものは試しでURL書き換えてやろーと適当こいたところ、出来てしまった、という感じです。結果オーライ、簡単で良かった。やったー

さいごに

と、説明しましたとおり、dropmarkの現状の実装を、偶然も織り交ぜつつ勝手にハックした感じですので、動作保証など一切できません。万が一ご入り用の方がいらっしゃった場合は十分ご理解の上ご利用ください。

3時間くらいで出来て良かったです。会社で運用してみます。

MOVERIOのお出かけ中利用シーン難しさランキング

前説

engadget さんにこんな記事があって

japanese.engadget.com

おっ、と思って脊髄反射で申し込んだら、当選したので、行ってきました。そのときのイベントレポート記事が上がっております。

写真のどこかに、私も写ってますね。私もEngadgetさんの記事が上がる前にレポートを書いておいたらリンクを貼ってもらえたはずでしたので、悔やまれます。

そう、そういうわけで EPSON 様から MOVERIO BT-200AV をお借りしてきました。

半年貸してやるから、7本ブログ書け

とのことです。全力で楽しんでいきましょう。開発にもトライしてみる予定です。

f:id:AMANE:20150317195651j:plain

うまく撮れました。

MOVERIOについて知る

MOVERIOは、公式には、以下の様なコンセプトの商品のようです。以下公式サイトから抜粋

  • 大画面を楽しむ
  • いつでもどこでもパーソナルシアター
  • レコーダーやスマートフォンの映像を楽しむ
  • 高画質・高音質のモバイルビューアー
  • 誰にも邪魔されず、自分だけで楽しめる
  • 好きな姿勢で、好きな場所で
  • 飲食しながらでも楽しめる

などなど。要するに、映像を見る装置です。私もBT-200 を使ってアニメを数本見てみましたが、思ったよりは普通に見られます。

一方で、こうもうたわれています。

  • Androidを採用し、カメラ、GPS機能、各種センサーを搭載。機能を活用した対応アプリで楽しみ方が広がります。
  • モベリオ用のオリジナルアプリが作れる
  • Webブラウジングをモベリオで楽しむ

Androidなので、この眼鏡スタイルを活かしたアプリ体験はいろいろ探索していけそうです。自分もいくつか作ってみたいアイデアを考え中ですので、このブログで報告できたら良いなと思います。

今回は 開発ネタは置いておいて 、メインの「映像を見る」機能についてちょっと考えてみます。どんな時に使えると嬉しいか。そして、それは実際可能なのか。

MOVERIOのお出かけ中利用シーン難しさランキング

考えてみました。難しそうだなーと思った順。

  1. 通勤電車
  2. ショッピングモール
  3. ディズニーランド
  4. 飲み屋
  5. ファストフード店
  6. 喫茶店(特にスタバ)
  7. お散歩中
  8. 公園・河川敷
  9. ジム
  10. 大学構内
  11. 野球場・サッカー場・競馬場
  12. 飛行機・新幹線・長距離バス

前提

前提として、「出先で映像を見る」という行為をそもそもしそうなシーンを想定しています。言い始めればもっといくらでも候補を挙げられそうですが、

  • コンテンツを見る、という行為をスマホ等でもそもそもしないところは除外
  • 論理的には上記に含まれますが、「1人で映像を見る」という行為が不適切な場合も除外

としています。MOVERIOのまっとうな使い方を考えてみよう、と言うことです。

解説

簡単そうな順に簡単に考察をしてきます

12. 飛行機・新幹線・長距離バス

f:id:AMANE:20150330145821j:plain

公式もおすすめの利用法。手にタブレットを持って持ち上げ続けたり、首を下に向けながら映像を見るのは結構しんどい。自由な姿勢で両手を空けられるモベリオ的には相当妥当な利用法!

車の中でタブレットで動画を見ていると弱い人だと車酔いしますが、「モベリオだったら車酔いしない!」みたいな展開があったりしないでしょうか。これは要調査です。

11. 野球場・サッカー場・競馬場

f:id:AMANE:20150330145911j:plain

ラジオを聞きながら野球や競馬を見る、というのは良くある話ですが、それのTV中継版。対象物が遠距離で焦点のズレも問題なさそう!

と思ったのですが、TV放送を見る方法ってあるんでしたっけ…?

まぁその、待ち時間に見るとか…

10. 大学構内

f:id:AMANE:20150330145919j:plain

ジャンル的には公園・河川敷に近いですが、大学内、特に理工系のキャンパスなら 「実験かな」と思われるので普通にスルーされること間違いなし!

9. ジム

f:id:AMANE:20150330145932j:plain

バイクをこぎながらの動画閲覧は鉄板ユースケースでしょう。ジムによってはスタッフさんに怒られるかもしれないので、真摯な態度で(EPSON社員になったつもりで!)モベリオの良さをアピールしていきましょう。タブレットで動画見てる人なんていくらでもいますしね。 最新のスポーツマンはこれ!くらいの堂々たる態度 を見せられるかがポイントになりそうです。

8. 公園・河川敷

f:id:AMANE:20150330145940j:plain

この辺りから、少し攻めたユースケースになります。そもそも、公園でひとりで動画を見るシーンが余り思いつきませんが、「変な人って思われないかな??」と少しドキドキしながら眼鏡を外で掛ける、わりとソフトな入門編という感じでしょう。たまに通りかかる人に、全く気がつかれないか、変わったサングラス?と思われるか、社交的なおじさんに話しかけられるか、くらいで済みそうですね!

7. お散歩中

f:id:AMANE:20150330145946j:plain

公園や河川敷でこそこそ見るのにスリルを感じなくなったら、次は出歩いてみるのも良さそうです。ただしおそらく結構危険ですので、がっつり映像を楽しみたい方にはおすすめできません。 普段からアニメを2本同時見したり、プログラミングしながらアニメを見ているような玄人の方向け の用法です。

家でビール飲みながらアニメ見てたらお腹に肉が付く一方ですので、お散歩しながら健康にアニメ鑑賞は、2015年のニューオタクスタイルとしてアピールしていけるかもしれません

6. 喫茶店(特にスタバ)

f:id:AMANE:20150330145956j:plain

Macbook

スタバにおけるドヤリングはもはや社会現象とも言えるかと思います。たとえTwitterを見ているだけでも、Mac in starbucks is very おしゃれ。ただのドヤリを超える新世代のドヤリとして、 モベリオドヤリング を提唱してきましょう。店員さんと仲良くなれるかも!

5. ファストフード店

f:id:AMANE:20150330150004j:plain

公式でも結構頭の方でおすすめしている、食事をしながらの動画鑑賞。両手がふさがっていても動画が見れる!モベリオは 録り溜めた深夜アニメの消化に追われる現代人には欠かせないデバイス と言えそうです。忙しいオタリーマンなあなた(誰)、ためしに、殺伐としている吉野家で、黙々と大盛つゆだくをかっこみながら幸腹グラフィティを見る、というのはいかがでしょうか。

4. 飲み屋

f:id:AMANE:20150330150027j:plain

今回のモニターで借りている人みんなで、塚田農場で MOVERIOオフ会 なんかどうでしょうか。良く訓練された浴衣ギャルの塚田農場店員は、きっと笑顔で「えーそれなんですかぁー?」と聞いてくれるに違いありません。 これぞ商機。

3. ディズニーランド

f:id:AMANE:20150330150038j:plain

プーさんのハニーハント、2時間待ち!みたいな時こそ、モベリオです。初めの1時間くらいはなんとかたわいもない話で持ちますが、次第に間が持たなくなってきます。こんな時こそモベリオ。なんなら2人で互いにモベリオ。サングラス代わりにもなってちょうど良い感じです。アニメを一話見終わる度に感想を語り合えば、2時間でも3時間でも待てそうなものです。

ディズニーランドはリア充の聖地かと思いきや、コアなネズミファン達の強烈なオタク感はすさまじいものがあります。ちょっと変わった眼鏡を掛けているくらいでとやかく言われる筋合いはありません。どんどんかけましょう。

2. ショッピングモール

f:id:AMANE:20150330150045j:plain

いつまでも終わらない奥さんのお買い物に付き合うのに疲れたら、最後の力を振り絞り最高の笑顔で理解ある夫を演じつつ「終わったら呼んでー」と奥様を送り出し、フードコートに腰を下ろしてモベリオを取り出します。

しかしそこは大量の親子連れであふれかえっています。

通りかかった子供「なんかかっこいいめがねだよー」
そのお母さん「しっ、見るんじゃありません」

なんて、ほほえましい風景もあるかもしれませんね。

1. 通勤電車

f:id:AMANE:20150330150051j:plain

個人的に最強のユースケースがこれ。

満員電車、周りはおっさんだらけ、顔近いっす、あなたの後頭部なんか見たくないっす。いやおねえさん、別にあなたのこと見つめてないっす偶然そちらしか向けないだけです! 手が挙げられなくてパズドラもできねぇ! なんて日常も、モベリオで一変します。

乗り込む前に再生開始。満員電車の不快な30分が快適アニメタイムに!

というシナリオなのですが、同時に変な人と思われるリスクもひじょーに高い。痴漢えん罪怖いです。あまり世の中に普及しているデバイスというわけでもないので、モベリオかけてるだけで「この人ちかんです!」と言われるリスクが無いわけではありません。この季節、花粉症もあってマスクをしてたりもしますので、より一層怪しいです。

女性ならそんな心配もありませんので、ぜひ試してみていただきたいです。

最後に

途中のディズニーやショッピングモールは冗談として、通勤電車ユースケースは(マスクが取れた頃に)勇気を出してトライしてみたいです。かなり実益があると思われる用途が同時に非常に危険度が高い、というのはなんともモベリオという商品の難しさを表している気がしますが、そこんとこぶち破っていきたいです。

最初に紹介したブロガーイベントでは、エプソンの中の人が電車の中で使っている様子をちゃんと動画にしてレポートしてたりして、がんばるなぁと思ったのですが、そういえば割と美人の女性の社員さんでした(笑)

MOVERIOは電車の中でも使える!(※ただし美人女性に限る) ってわけですね。

冗談はほどほどにして

開発ネタと並行して、「こんなところで使ってみた」レポートも、ブログとして書いていこうと思います。

フリー素材最高

今回はじめて自分のブログでフリー写真素材を使ってみました。フリー素材を使った記事感が見事に出てますね!

ブラウザ上の開発環境 "CLOUD PEBBLE" で Pebble アプリの開発をしてみた

はじめに

Kick Starter で、とんでもない額の投資を得て話題になっています。$500,000- の目標に対し、3月8日午前時点で $16,816,735- の投資を集めています。達成率で言うと3000% です。

公式:

追って発表された、Pebble Time Steel と Smartstrapsについての記事:

欲しい

Apple Watchも話題になっています。むろん、表現力やアプリの作り込み、コミュニティの大きさは圧倒的だと思われます。一方で、電池もちはだいじょうぶか?と心配されています。 Pebble Timeは e-paper のおかげで(カラーにもなったのに!) 一度の充電で7日間*1使える、という点で、Android Wear のプロダクト等と比較してもかなり特殊な立ち位置にいます。

自分はApple Watchも結局買ってしまうかもしれませんが、いつもそこにいて、ちらっと見る、という従来の時計に近い使い方が出来る(と期待できる)Pebble Time を結構楽しみにしています。

SDK

カラー画面に対応したSDKが公開されました。まだ現物はありませんが、おもしろそうなので開発してみようと思いました。Android Wear や Apple Watch ほどは流行らないかと思いますが、長時間駆動可能という特性を活かした体験を作れると良いなぁと思います。

開発者向けの情報はこちらのサイトにまとめられています。既存Pebble向けのSDK Ver2系と、カラーに対応したSDK Ver3が入手できます。それらのインストールガイド、APIのドキュメントや、チュートリアルもまとめられています。

サイトがお洒落です。

Mac の開発環境 (割愛)

チュートリアルをやる分には、Macの開発環境と CLOUD PEBBLE での開発には差が無いようです。もちろん最終的に完成したアプリをPebble本体にインストールする際にはMac上に開発環境を構築する必要がありますが、手始めにちょっと触ってみたいという今回みたいな段階では、ビルド、エミュレーション出来る環境が0ステップで手に入るのはかなりありがたいです。

また、アプリの設定系の .json ファイルを、いちいち手書きしていくのではなく、埋めるべきところをGUIで埋めていく形式で書けるのも、CLOUD PEBBLE の優れているところです。手始めには、CLOUD PEBBLEを使うのが良いでしょう。

と言うわけで実際は一応Macの開発環境も作ったのですが、今回は割愛いたします。

CLOUD PEBBLE

ブラウザ上で動くPEBBLE のIDE + シミュレーション環境、CLOUD PEBBLE ですが、少しワークフローが特殊だったので、簡単にまとめておきます。とりあえず、アカウントを作成してログインして下さい。

プロジェクトの作り方

主に三つあります

  1. テンプレートから作成する
  2. ローカルからzipでインポートする
  3. GitHubからインポートする
1. テンプレートから作成する

PROJECTSタブ > CREATE で新しいプロジェクトを作成します

f:id:AMANE:20150308125227p:plain

この状態だと、SDK2.0 準拠のプロジェクトも選択出来ます。SDK2.0準拠の場合は、C言語だけではなく、 simply.js や pebble.js といったjsの方言での開発も可能です

f:id:AMANE:20150308125417p:plain:w550

が、ここでは SDK3.0を使いたいのでそちらをえらびます。すると、Project Typeはえらべなくなり、C言語での開発のみとなります。今後アップデートされると、js系も使える様になるのかもしれません。

f:id:AMANE:20150308125510p:plain:w550

ここでえらべるテンプレートは、基本的にSDK2系の頃のサンプルのようです。基本を抑えるにはこのサンプルを色々読んでみると良さそうです。

2. ローカルからzipでインポートする

既にローカルにプロジェクトがある場合は、それをzipにまとめてアップロードできます。

先に1. の手順で作ったプロジェクトを、zip でDLすると、必要なファイルとディレクトリ構成がわかるので、それがちゃんとパックされた物をアップロードすればOKです。チュートリアルをCLOUD PEBBLEでやる分には、zipでインポートをする事は無いと思います。

3. GitHub からインポートする

SDK3系のサンプルは、GitHub上に上がっています。

このページ中のスクリーンショットをクリックすると、それぞれのプロジェクトが上げられているgithubに飛びます。これらをCLOUD PEBBLEに取り込むことが出来ます。下図のように適当な名前をつけ、対象リポジトリのアドレスを指定し、IMPORT すれば、CLOUD PEBBLE上で扱えるようになります。

f:id:AMANE:20150308142800p:plain:w550

また、先にGitHub上で Fork して自分のプロジェクトとした物を、CLOUD PEBBLE にインポートして、その際に "USE AS GIT REMOTE" にチェックを入れておくと、CLOUD PEBBLE上で編集した結果を、GitHub上に再度Push出来るようになります。

f:id:AMANE:20150308144659p:plain:w550

f:id:AMANE:20150308144913p:plain

さすがにCLOUD PEBBLE上でローカルコミットを作る機能は無いようで、毎回origin/masterに対しpushする形となります。あらかじめ GitHub 上でブランチを作っておいて、そのブランチをCLOUD PEBBLE上にpullし、編集するようにすれば良いでしょう。

GitHubとの連携

上記3で説明しましたように、プロジェクトをGitHubからインポートした場合は、そのGitHubリポジトリをそのままリモートリポジトリとして運用することが出来ますが、1や2の手順でCLOUD PEBBLE上のプロジェクトとした物については、後からGitHubに突っ込むことは出来ないようです。もし既存のプロジェクトをどうしてもGitHubで管理したい場合は、一度プロジェクトをzipとしてDLして、それをGitHubに突っ込み、再度3.の手順でCLOUD PEBBLEに取り込むと良いかと思います。

ビルド、実行

ビルドの方法は2種類有ります

  • COMPILATION > RUN BUILD して、INSTALL ON BASALT
  • コードの画面で、右上の緑色の "Save, Build, Install and Run" ボタンを押す

f:id:AMANE:20150308151807p:plain

f:id:AMANE:20150308151815p:plain

左上のPebbleのウィンドウ内にアプリが表示されれば成功です

f:id:AMANE:20150308151958p:plain:w600

操作法

Pebbleウィンドウの周囲四つのボタンを、マウスクリックで押すことが出来ます。

ログの出し方・見方

C言語の場合 APP_LOG() マクロを利用します

 APP_LOG(APP_LOG_LEVEL_INFO, "Outbox send success!");

js の場合、 console.log() 関数を利用します

console.log("Temperature is " + temperature);

ログの見方は2種類有ります。

  • インストール直後のモーダルウィンドウで、 "VIEW LOGS" をクリックする
  • COMPILATION > VIEW APP LOGS をクリックする

f:id:AMANE:20150308152941p:plain:w550 f:id:AMANE:20150308152951p:plain

これで、ログを見ることが出来ます f:id:AMANE:20150308153210p:plain:w600

[PHONE] と書いてある行は、PebbleKit.js 内で console.log() で出力された内容です。 [INFO][DEBUG] は、Cのファイル中から、APP_LOG() で出力された内容です。APP_LOG_LEVELについては、こちらを参照

チュートリアルを流してみる (CLOUD PEBBLE)

チュートリアルは上から順に通していけば良いと思いますが、数カ所の要点だけメモしておきます

チュートリアルのコードは、チュートリアル本文中のリンクから、CLOUD PEBBLEのプロジェクトとしてインポートできる

f:id:AMANE:20150308162419p:plain

上の図の様なリンクがあったら、そこを押すと、その時点のチュートリアルのプロジェクトをCLOUD PEBBLEに取り込むことが出来ます。これをやりながらチュートリアルを進めると良いと思います。

FONTや画像の取り込み方のちょっとしたコツ

ぽつぽつ引っかかったので。

ひとつの .ttf ファイルに対し複数のサイズでフォントを定義する

フォントは、取り込んだひとつの.ttfファイルに対し、複数のサイズで定義しておくことが出来ます。複数定義されていると、RESOURCES のなかのフォントファイルにマウスオーバーすることで、定義されているフォントを確認出来ます。

f:id:AMANE:20150308163738p:plain

サイズを追加するには、.ttf ファイルを選択して、一番下までスクロールし "ADD ANOTHRE FONT" して、"IDENTIFIER" の最後の _24 みたいなサイズの数値を、使いたいフォントサイズに変更して SAVE します。

.c コード中での画像リソースへのアクセスの仕方

画像ファイルをアップロードすると、下の画面のように表示されますが、ここに出ている "IDENTIFIER" の文字列「そのもの」では、.c コード上で扱う事が出来ません。なぜかそんな仕様です。

f:id:AMANE:20150308164714p:plain:w600

この画面に表示されている "IDENTIFIER" 文字列の先頭に、 "RESOURCE_ID_" とつける必要があります。たとえばこの場合は

  s_background_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_BACKGROUND);

で、使える様になります。なぜかそんな仕様です。気をつけましょう。

AppMessage の使い方

スマホ側で動くPebbleKit.jsと、Pebble本体で動く .c 言語間でデータのやりとりをする AppMessage ですが、少し手続が煩雑でした。

スマホ側(JS)
1. {key: value} のオブジェクトを作る
2. SETTINGS で、1. で作ったオブジェクトに含まれる key を登録する
3. Pebble.sendAppMessage(object, ... で送り出す

Pebble側(C)
4. static void inbox_received_callback(DictionaryIterator *iterator 関数で
    DictionaryIterator インスタンスに格納された状態で受け取る
5. Tuple *t = dict_read_first(iterator); でオブジェクトのメンバの一つ目を読み出す
6. t->key で、データの種類を調べて、 t->value でデータを読み出す

サンプルの場合、 1と3 で以下の様にメンバをオブジェクトに突っ込んで送ろうとします

var dictionary = {
    "KEY_TEMPERATURE": temperature,
    "KEY_CONDITIONS": conditions
};

// Send to Pebble
Pebble.sendAppMessage(dictionary,

これを、ちゃんとC側で受け取れる様にするには、SETTINGSの画面で、2のステップで以下の様な登録をしておく必要があります

f:id:AMANE:20150308221257p:plain

また、6 で t->key としてデータのキーを調べますが、このときに参照されるのは、 KEY_TEMPERATURE といった文字列ではなく、2 で登録した Key ID (0とか1とか)の方ですので注意が必要です。

さいごに

一通りチュートリアルを通すことで、CLOUD PEBBLEの使い方と、コーディングの仕方がなんとなーくわかりました。これをベースに、watchface(時計のテーマ) や watchapp(Pebbleのアプリ) を作っていこうと思います。

ちょうど、ustwo が "Watch Face Design Guidelines" というのを出していたり、明後日が Apple Watch の発表(おそらく)だったり、またスマートウォッチ界隈が動き始めているので、その辺りを参考にしながら、自分なりのスマートウォッチのあり方を模索してみたいです。

*1:Pebble Time Steelに至っては10日間!

Kinect, OpenCV, openFrameworks のカラー画像を相互に変換する

はじめに

以前の記事で KinectColorFrameReader から得た画像を ofImage 形式に変換して描画したりしましたが、いろいろ調べてみると、Kinect の画像形式から直接 ofImage に変換している例は意外と少ないようでした。そりゃまぁカラー画像をそのまま取得するだけだったらわざわざKinect使わんわい、という話だと思います。

それで、OpenCV も一緒に利用するケースを考えてみました。

f:id:AMANE:20150209231053p:plain

この三者間を画像データが行き来出来れば、柔軟にプログラムが出来そうです。

oF 内で OpenCV を利用する方法について

先日の記事ではoFでKinect for Windows SDK 2.0 を使う手法について調査検討しましたが、OpenCVについても同様に調査します。

大まかには、以下の方法があると思います。

  • 公式の ofxOpenCV を使う
  • kylemcdonald/ofxCv を使う
  • OpenCV を直接叩く
公式の ofxOpenCV を使う

OpenCVのバージョンは2.3.1です。ofxCvColorimage など oF での編集や描画がしやすいようアレンジされた画像クラスが利用できるのは便利です。公式のアドオンですが、IplImageを使っている箇所があったりするあたりはイケてません。そしてちょっと古い。

kylemcdonald/ofxCv を使う

内部で ofxOpenCV のアドレスを見ているので、利用しているのは ofxOpenCV と同様に 2.3.1 です。toCv() toOf() という多目的の関数があり、以下の様な変換が可能です。これは非常に便利です。変換可能な組を一部抜粋します。

- ofVec2(3)f --- Point2(3)f
- ofImage --- cv::Mat
- ofRectangle --- cv::Rect

詳細はコードをご参考 ofxCv/Utilities.h at master · kylemcdonald/ofxCv · GitHub

OpenCVはちょっと古いのですが、一方でコード中で ofDraw** という openFrameworks 0.9 系で採用される予定の記述があったりもし、いくつかのサンプルがビルドできませんでした。これの修正も結構大変そうです。

OpenCV を直接叩く

ofxAddons では無いので導入は若干面倒ですが、最新の機能を全部使える様になります。ただし、画像のデータなど、自分で変換する必要があります。

どうするか

ofxCv が素直に使えれば良かったのですが、先述の通りの問題があったりするのが玉に瑕で、Kinect for Windows SDK 2.0 を直接叩くのと同様の理屈で OpenCV も直接使えば良いじゃん、という結論になりました。OpenCV の生コードを書くのが何だかんだ楽です。

ところで、ofxCv はその目標に以下の様な物を掲げています。

Provide clean implementations of all functions in order to provide a stepping stone to direct OpenCV use.

と言う風に素のOpenCVも書けるような仕様になっているらしいので、ちゃんと ofxCv がまとまり oF も 0.9 に上がった暁には、 ofxCv を利用するのも良いかもしれません。

環境

openFrameworksv0.8.4 を Visual Studio Community 2013 で使う方法については、先日の記事をご参照ください

また、Visual StudioOpenCV を用いて開発する方法については、下記ブログなどをご参照ください

完成品はあげてあります

解説

ウィンドウが複数開いて大量に絵が出てきますが、下図の様な変換を行っています。

f:id:AMANE:20150209231104p:plain

以下ポイントだけ説明いたします ofImage と cv::Mat の相互変換については、先述の Kyle先生の ofxCv から toCv toOf 関数の実装を参考にさせていただいております。

ofImage -> cv::Mat

cv::Matコンストラクタに、配列の中身になる実データを指定出来るものがあるので、ofImageから実データを引っこ抜いて利用します。

// ofImage ofColor; ... ofVideoGrabber からデータを引っこ抜いたモノ
ofColor.allocate(CAM_WIDTH, CAM_HEIGHT, OF_IMAGE_COLOR);
cv::Mat cvColor = cv::Mat(CAM_HEIGHT, CAM_WIDTH, CV_8UC3, ofColor.getPixels());

cv::Mat のドキュメントによると

data – Pointer to the user data. Matrix constructors that take data and step parameters do not allocate matrix data. Instead, they just initialize the matrix header that points to the specified data, which means that no data is copied. This operation is very efficient and can be used to process external data using OpenCV functions. The external data is not automatically deallocated, so you should take care of it.

ということで、cv::Mat は実際は ofImage の確保しているデータを直接参照します。メモリの無駄がない、と言うことです。

また、ofImage は標準で RGB 配列ですが、 OpenCV では BGR 配列が標準ですので、変換が必要です

// #include <opencv2/imgproc/imgproc.hpp>
cvtColor(cvColor, cvColor, CV_RGB2BGR);

cv::Mat -> ofImage

// ofImage ofCropped;
ofCropped.allocate(CROPPED_WIDTH, CROPPED_HEIGHT, OF_IMAGE_COLOR); 
ofCropped.setFromPixels(cvCropped.ptr(), cvCropped.cols, cvCropped.rows, OF_IMAGE_COLOR, false);

cv 側でROIを決めてクロップした画像の生データ、のポインタを教えて sestFromPixels 関数で ofImage を作ることが出来ます。 四つ目の引数を false とすることで、BGR配列のデータでも直接 ofImage にRGB配列で突っ込むことが出来ます。

(Kinect) ColorFrame -> cv::Mat

// unsigned char* bufForColorFromKinect;
int nBufferSize = KINECT_CAM_WIDTH * KINECT_CAM_HEIGHT * 4;
bufForColorFromKinect = new unsigned char[nBufferSize];
hr = colorFrame->CopyConvertedFrameDataToArray(nBufferSize, bufForColorFromKinect, ColorImageFormat_Bgra);
cv::Mat cvColorFromK = cv::Mat(KINECT_CAM_HEIGHT, KINECT_CAM_WIDTH, CV_8UC4, bufForColorFromKinect);

Kinect画像の生データをバッファにいったんコピーして、それを使って先ほどと同様に cv::Mat() を作成します。 CopyCOnvertedFrameDataToArray() 関数で、ターゲットとなるフォーマットを指定するのがポイントです。cv::Mat のアルファ付きカラー画像は BGRAが標準です。

(Kinect) ColorFrame -> ofImage

// ofImage ofColorFromK;
ofColorFromK.allocate(KINECT_CAM_WIDTH, KINECT_CAM_HEIGHT, OF_IMAGE_COLOR_ALPHA);
int nBufferSize = KINECT_CAM_WIDTH * KINECT_CAM_HEIGHT * 4;
unsigned char* data = ofColorFromK.getPixels();
hr = colorFrame->CopyConvertedFrameDataToArray(nBufferSize, data, ColorImageFormat_Rgba);
ofColorFromK.update();

先ほどと同様に ColorFrame からデータを変換しつつバッファにコピーしますが、今回は ofImage ofColorFromK を事前に確保しているので、書き込む先の実データのポインタを直接指定できます。また、ofImage では色のフォーマットは RGBA になります。

最後の update() を忘れずに。

cv::Mat、ofImageから(Kinect)ColorFrameに戻す

Kinectの画像形式に戻すことは無い気がするので、ここでは割愛いたします。

さいごに

グレーの画像だったり、Depthなどビット深度の異なる画像も、CVの方で CV_8UC3 等と指定している画像のデータ形式を合わせることで対応出来ると思います。

少々面倒なところもありますが、ofxCv を使う場合より現時点では汎用性が高いと思います。活用していきます。

長期展示や実験の運用のため Windows8.1を定期的に自動再起動させる

はじめに

f:id:AMANE:20150202202006j:plain

展示や実験の運用で、定期的にPCを休ませたい、自動的に再起動してリフレッシュした後・展示・実験を再開したい、というニーズがあると思います。ありました。そのための手法を考えてみました。

修正

投稿当初、以下の様に書いていたのですが

Microsoft アカウント を利用しているとパスワード解除ができず、PCの再起動をしたとしてもログイン前で止まってしまいます。MSアカウント を利用されている方は、以下の手順でローカルアカウントログインに変更します。

その後調べていたら、MSアカウントでもパスワード無しで起動時ログイン出来ることがわかりました。その点について修正しています。

前提

- Windows8.1
- Microsoft アカウントを利用したまま、パスワードを不要にする **または**
- Microsoft アカウントでのログインは利用せず、マシンのローカルアカウントログインとする
- その上で、パスワードを解除してマシンを運用する
- BIOSに、Auto Power On 的な機能がないものとする

Windows 8.1 としましたが、おそらくwindows7,8 でも同様に可能だと思います。

Auto Power On があれば

完全にシャットダウンした状態からでもマザーボードの持っているタイマーで起動ができるので、本記事で説明するような 必要時間休止→復帰→リブート というプロセスを通る必要がなくなります。

本記事では、Auto Power On的な機能がない場合にどうするか、について説明していきます。

Auto Power On については下記記事を参考にされてください

MSアカウント でのログインのまま、起動時のパスワード入力を不要にする

下記サイトの方法で可能です

(または)ローカルアカウントにし、パスワードを解除する

Microsoft アカウント を利用しているとパスワード解除ができず、PCの再起動をしたとしてもログイン前で止まってしまいます。MS ID を利用されている方は、以下の手順でローカルアカウントログインに変更します。

ローカルアカウントを使用すると、パスワードをそもそも設定しないことが出来ます。

この工程の一番最後で、パスワードを入れて確認するところがありますが、ここを空白のまま「次へ」進むと、パスワードが解除された状態でローカルアカウントに切り替わります。

ちなみに、MSアカウント時代のデータが消えたりはしないのでご安心ください

工程

全容はこんな感じ

1. 展示・実験の終了時刻になったら、動いていたプロセスを殺して、PCをシャットダウンできる状況にする
2. ハイバネーション(休止)状態にする
3. 展示・実験の開始時刻の少し前に、タスクスケジューラの機能でハイバネーション(休止)から復帰する
4. 再起動する
5. 展示・実験に必要なプログラムをスクリプトで起動する

先述の通り、Auto Power On が出来ないためシャットダウンはせず、しかしハイバネーションさせることで極力PCを休ませたのちリフレッシュのために再起動する、という作戦となっています。

一個ずつ見ていきます

1. 動いていたプロセスを殺す + 2. ハイバネーション

バッチファイル作成

以下のようなバッチファイルを作成して適当な場所に置いておきます

rem
rem goToSleep.bat
rem

echo "kill all apps and go to Hibernation"

taskkill /im "iexplore.exe" /f

timeout /T 10

taskkill /im "notepad++.exe"

timeout /T 20

rundll32.exe PowrProf.dll,SetSuspendState

ここでは、 IEと "notepad++.exe" というアプリが仮に展示で使われていると仮定して、それを殺し、20秒待った後、ハイバネーションに入る、というバッチとなっています。

ハイバネーションは、setsuspendstate というWindowsAPIを使うことで実現しています。

rundll32.exe の罠

このStackoverflow のスレッドでは、 rundll32.exe PowrProf.dll,SetSuspendState 0,1,0 でスリープする、と言っていますが、これは間違いなようです。私も数パタン試してみましたが、最後のbool三つの「引数のような指定」はおそらく機能しておらず、すべてハイバネーションしています。APIのデフォルトの動作なのでしょうか。

この状態でも今回の目的は達成できているとも言えるのですが、もし何かしらの理由でハイバネーションできない/したくない場合は、正しくスリープのAPIを呼ぶ必要があります。プログラムからスリープを呼ぶ方法は、以下のページ様の手法で確認が出来ました。

これをちょっとカスタムして、以下のコードでプロジェクトを作って、exe をはき出しておきます。

// これを、 GoToSleep.exe としてビルド

#include <Windows.h>
#include <powrprof.h>
#pragma comment(lib, "powrprof.lib")

int main()
{
    SetSuspendState(FALSE, FALSE, FALSE);
    // 第1引数がFALSEだとスリープ、TRUEだと休止(hibernation)
    return 0;
}

その上で、バッチファイルを書き換えます

rem rundll32.exe PowrProf.dll,SetSuspendState
cd <PATH_TO_EXE>
start GoToSleep.exe

これで正しくスリープ / 休止が出来ます。

タスクスケジューラに登録
  • コントロールパネル > システムとセキュリティ > 管理ツール > タスクスケジューラ でタスクスケジューラを開きます
  • 基本タスクの作成 をクリックすると、タスク作成のウィザードが始まり、埋めていくことでタスクを登録できます。
  • 名前を適当につけます。 goToSleep とか
  • PCをハイバネート/スリープ させたい周期を考えてトリガーに登録します (たとえば、「毎日、20:30」 など
  • 操作として、先ほど作成したバッチを登録します

このスクリプトについては、タスクのオプションをいじる必要はありません

3. ハイバネーションから復帰 + 4. 再起動

バッチファイル作成

Windows 標準のタスクスケジューラの機能で、ハイバネーション or スリープ から復帰することができます。

以下のようなバッチファイルを作成して、適当な場所に置いておきます

rem
rem wakeFromSleepAndReboot.bat
rem

echo "wake from sleep and reboot"

timeout /T 20

shutdown -r -t 10

ここでは、20秒待ったのちリブートする、というスクリプトになっています。

タスクスケジューラに登録

今回、ハイバネーション/スリープからの復帰は、タスクスケジューラの機能を用いて行います。まず一個前と同様に作成したバッチをタスクスケジューラに登録します。

その後、登録したタスクのプロパティを開き 条件 タブ中の 「タスクを実行するためにスリープを解除する」 にチェックを入れます。

f:id:AMANE:20150202201830p:plain

これで、ハイバネーション/スリープ中であっても、登録したバッチが実行されるようになります。

このあたりについては、こちらのサイトで紹介されていました。

5. 必要なプログラムの起動

バッチファイル作成

複数のプログラムを順に起動したい場合も多いと思うので、これもバッチを作ります。

例として、notepad++ と IE を起動します。(意味はとくにありません(>_<))

rem
rem launchApps.bat
rem

echo "launch apps"

cd "C:\Program Files (x86)\Notepad++"
start notepad++.exe

timeout /T 10

start iexplore.exe

exit

notepad++ を起動した後、10秒待って、Internet Explorer を立ち上げます

タスクスケジューラに登録

ここでは上二つのバッチとは異なり、時刻指定ではなく、スタートアップを引き金としてアプリを立ち上げる指定にしてみます。起動からPCが色々立ち上げたり、ネットワークがつながって落ち着くまでの時間を多少取った方が良いかとも思うからです。

「基本タスクの作成」のウィザードで、トリガー を「コンピュータの起動時」にします。これでも十分かもしれませんが、更に少し余裕を持たせるために、開始を遅延させます。

  • タスクスケジューラの一覧から今登録したタスクのプロパティを開く
  • トリガースタートアップ時 を編集します。
  • 遅延時間を指定する にチェックを入れて、適当に時間を設定します。たとえば起動後1分。

f:id:AMANE:20150202195533p:plain

これで、上記のバッチは起動後1分で実行されることになります。

完成!

以上で、定期的に再起動する仕組みができました。ハイバネーションの設定周りで、PCごとに差があるかもしれません。その辺りは各自柔軟に対処、と言うことで。

Sleep / Standby / Suspend / Hibernation / Hybrid Sleep について

今回、setsuspendstate の挙動で結構はまっていろいろ調べたところ、日本語と英語の間、また、プログラマWindowsの一般ユーザ の間で、スリープや休止に関する言葉の使い方がそれぞれ異なることがわかりました。ここに整理してまとめておきます。

参照

Sleep == Standby == (hibernationでは無い)Suspend == スリープ、スタンバイ

メモリ上にOSの状態を残し、メモリにのみ通電を続けるもの。直ぐ起動できるがやや電気を食う。

Hibernation な suspend == 休止

HDD/SSD にOSの状態を保存し、いったんシステム全体の電源を落とす物。やや起動に時間がかかるが、シャットダウン時と同様に電気を使わなくなる

Hybrid Sleep == ハイブリッドスリープ

上の二つの合わせ技。通常はスリープ的に寝ていて直ぐ起動できる状態であるが、電池が切れそうになるとHibernation状態に移行する

初め、Suspend == 休止 だと思い込んでいて混乱しました。

さいごに

もしかすると似たような事をする為のユーティリティソフトもあるかもしれませんが、あまり調べていません。もし良いものをご存じの方がいらっしゃいましたら、教えていただけると嬉しいです。

openFrameworks に Kinect for Windows SDK 2.0 の AudioBasics サンプルを移植する

はじめに

前回、Color画像のサンプルや FaceTracking のサンプルを移植しましたが、今回は Audioのサンプルを移植してみます。

AudioBasics-D2D

このサンプルは、Kinectのマイクをつかって、1つの音源の方向を特定し、方向とその信頼度を可視化しつつ、リアルタイムに音波の強さを描画します。

f:id:AMANE:20150206203741p:plain

音源の方向特定はかなりの精度で出来ていますが、今のところ一方向のみの特定に制約されています。しかし、API複数の AudioBeamFrame を取り出せる作りになっているので、将来的にはもしかしたら、同時に複数方向から鳴っているときも、それぞれ識別できるようになるかもしれません。楽しみです。

本記事の完成品

事前に

ポイントになりそうなところを調べておきます。

マルチスレッド

本家サンプルコードをざーっと読んでみたところ

  • 受け取った音声データの解析・可視化を行うメインスレッド
  • 音声データの受け取りを行うスレッド

の2つがあることがわかりました。SDK本家サンプルではこれを CreateThread 関数や EnterCriticalSection 関数を使ったWindowsらしいマルチスレッドプログラミングで実現していますが、せっかくoFなのでoFのマルチスレッドで実現したいです。

リングバッファの取り扱いと逐次描画

Kinecet SDK を oF に移植するぜ!という主旨からは若干離れるので本記事では特段言及しませんが、

  • 「40byte分のボリュームの平均値」 を、可視化する量より少し多い 1000 個分ためて先頭を記録するリングバッファ
  • その中で可視化する領域の先頭を指すポインタ
  • 前回の描画フレーム後入ってきたデータの量の算出
  • 上記の情報を使って、数秒分を少しずつ進めながらリアルタイムにボリューム可視化する

あたりのプログラムのサンプルとして非常に勉強になりました。

取りかかる

oFでKinect for Windows SDK 2.0 を使う方法の概要については、前回の記事を参考になさってください。

ブロッキング

ofThread のサンプルを参考に、音声データの受け取りを行うスレッドを作ります。受け取る瞬間と、データを絵を描くのに使う配列に移動する瞬間がぶつかるとまずいので、スレッドを立てて、ロックアンロックを行います。

本家では EnterCriticalSection() LeaveCriticalSection()クリティカルセクションを作っているところは、 if(lock()) + unlock() で同様の処理になります。

WINAPIのクリティカルセクションについてはこちらの記事が分かりやすかったです

ofThread は startThread() 引数無しだとブロッキングなスレッドになって、 lock() 時に他でロックしてたら、それがアンロックされるのを待機します。一方 startThread(false) でスレッドをスタートさせるとノンブロッキングになり、if(lock()){} は即時に false を返すので、 if文ブロック内の処理はいったん飛ばされることになります。今回はWINAPIのクリティカルセクションの手法に習って、ブロッキングなスレッドとします。

ofThreadのブロッキングについてはこちらのフォーラムが勉強になりました。 Theo先生直々返答。

ofApp::exit() での処理 WaitForThread()

ofThreadのサンプルは stopThread() 関数で終了させていますが、今回のような音のプログラミングの終了時にこれをやると、データのコピー中等に処理を殺そうとしてしまい、多くの場合エラーを起こします。(エラーが起きない場合もあるようです) ちゃんとスレッド内の処理が終わるまで待ってあげるために、アプリ終了時は WaitForThread() を使いましょう。

それ以外

は、ほぼ本家サンプルのコピーです。すみません。ありがとうございます。

できあがり

描画は、適宜良い感じにしてください!

f:id:AMANE:20150208221425p:plain