2012年7月25日水曜日

[VOC@LOID]たまには懐かしいのを聴きたくなるもので

そもそもVOCALOIDって、「曲作ったけど歌ってくれる人がいない・・・」って要望に応えるために出てきてるので、最終的に出来上がった楽曲って人が歌ってなんぼだと思ってたり。

てなわけで、歌ってみたってジャンルはそれがちらほら出始めてきたときは結構好きだったんだけど、最近はめっきり聴かなくなっちゃって。

VOCALOIDってどうしても感情表現みたいなところは弱いから、歌詞の内容を自分なりに汲み取ってアレンジして歌ってるのを聞くと
「おぉ、やっぱVOCALOIDと違って人の声ってええなぁ!」
って思うんですが、あんまりそういうのが入ってないと
「うーん・・・」
って思っちゃうんですね。

ま、そこは難しいところではあるんだけれど。最近はVOCALOID曲を追いかけることもしなくなっちゃったし。。。
たまに聞くと「おっ」って思うから、また追いかけたいのは追いかけたいんだけどねぇ。
FirefoxからFlashをパージしたのでニコ動見てないってのも大きいかw Flash不要のニコ動プレーヤーでないかなー

てなわけでちょいと懐古してみようと思う。。。
結構動画消えちゃってるね(´;ω;`)ブワッ

【初音ミク】First Sound Story#005 『Dear』

ストーリーシリーズになっているので頭から聞くとなおよし。カラオケでちょくちょく歌うけど最後のサビがいつもでなくて悔しい・・・w
で、これの歌ってみたで好きなのがこちら。

『 Dear 』を歌ってみました♪歌和サクラ

うん、歌和サクラさんは結構追いかけてたけどぱったり出て来なくなっちゃったのよねぇ。残念。

ドッカァズッシャアバリィーンて曲というコメント通り、割と賑やかで好きなのがこちら。
【鏡音リン】 天樂 【オリジナル】

どうやらこれカラオケに入っているらしい(DAM?)ので今度歌ってみたい・・・。これ気持ちよさそうなんだよねー

で、これを歌ってみたので好きなのがこちら。
・・・って思ったら削除されてる・・・。
片霧烈火嬢の歌ったやつが好きだったんだけどなぁ。残念。。。

お次はコレ。カラオケでもよく歌う。
【初音ミク(40㍍)】 ジェンガ 【オリジナル】大百科

40mPの曲は人が歌いやすいキーだから好きなのが多いんだよなー。

歌ってみたで好きなのがこの方。
【歌ってもらった】SPiCa -HPT ReACT-【リアレンジ】

GARNiDELiAの面々の歌はどれも好きだったり。CD買おうかな・・・。

癖の強い鏡音リン(初期版)で脅威の調律を魅せてくれるのがこちら。
鏡音リン・初音ミクオリジナル曲『Linkage』

個人的にはもっと評価されていいと思ってるんですが、意外と再生数伸びてないんですよねこの方。他の曲も素晴らしいのでおすすめ。

とまぁ、古めのラインナップを並べて見ました。
新しいのをそろそろ仕入れなきゃなぁ。。。

2012年7月22日日曜日

[Android]Android向けDMRアプリに挑戦 その2

さて。その1でとりあえずひな形はできたので中身を実装していきます。

DMCから指定された音源を再生

まずは再生することを考えましょうってことで、音源の指定を受けるAPIの実装をば。
※ この実装ではMediaPlayerの状態管理が甘々なのでコピペして使っちゃいけません。サンプルにしたって酷いので、あぁとりあえずここで再生するためのコードを書けばいいんだな、だけを参考にしてください。
真似しちゃ駄目、ゼッタイ!

       @Override
       public void SetAVTransportURI(
                     long InstanceID,
                     String CurrentURI,
                     String CurrentURIMetaData)
       {
        Log.d(TAG, "SetAVTransportURI");
        if(mMp == null){
         mMp = new MediaPlayer();
         mMp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
       
       @Override
       public void onCompletion(MediaPlayer mp) {
        // TODO Auto-generated method stub
        mp.reset();
        mCurrentPlayState = "STOPPED";
        StateChanged();
       }
      });
        }
        if(mMp.isPlaying()) {
         mMp.stop();
         mMp.reset();
        }
        try {
      mMp.setDataSource(CurrentURI);
     } catch (IllegalArgumentException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Log.d(TAG, e.getMessage());
     } catch (IllegalStateException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Log.d(TAG, e.getMessage());
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Log.d(TAG, e.getMessage());
     }
        try {
      mMp.prepare();
     } catch (IllegalStateException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
        mCurrentURI = CurrentURI;
        mCurrentURIMetaData = CurrentURIMetaData;
        String avt = "<Event xmlns = \"urn:schemas-upnp-org:metadata-1-0/AVT/\">\n";
           avt += "<InstanceID val=\"0\">\n";
           avt += "<AVTransportURI val=\"" + mCurrentURI + "\"/>\n";
           avt += "</InstanceID>\n";
           avt += "</Event>";
           root1.SetStateVariableValue_AVTransport_LastChange(avt);

           avt = "<Event xmlns = \"urn:schemas-upnp-org:metadata-1-0/AVT/\">\n";
           avt += "<InstanceID val=\"0\">\n";
           avt += "<NumberOfTracks val=\"" + "1" + "\"/>\n";
           avt += "<CurrentTrack val=\"" + "1" + "\"/>\n";
           avt += "<CurrentTrackMetaData val=\"" + StringEscapeUtils.escapeXml(mCurrentURIMetaData) + "\"/>\n";
           avt += "<CurrentTrackURI val=\"" + mCurrentURI + "\"/>\n";
           avt += "</InstanceID>\n";
           avt += "</Event>";
           root1.SetStateVariableValue_AVTransport_LastChange(avt);

           //mMp.start();

           //
           // You can return a UPnP Error code by throwing the following exception:
           //
           // throw (new UPnPInvokeException(statusCode, statusDescription));
           //
       }

ちなみに
        String avt = "<Event xmlns = \"urn:schemas-upnp-org:metadata-1-0/AVT/\">\n";
           avt += "<InstanceID val=\"0\">\n";
           avt += "<AVTransportURI val=\"" + mCurrentURI + "\"/>\n";
           avt += "</InstanceID>\n";
           avt += "</Event>";
           root1.SetStateVariableValue_AVTransport_LastChange(avt);

           avt = "<Event xmlns = \"urn:schemas-upnp-org:metadata-1-0/AVT/\">\n";
           avt += "<InstanceID val=\"0\">\n";
           avt += "<NumberOfTracks val=\"" + "1" + "\"/>\n";
           avt += "<CurrentTrack val=\"" + "1" + "\"/>\n";
           avt += "<CurrentTrackMetaData val=\"" + StringEscapeUtils.escapeXml(mCurrentURIMetaData) + "\"/>\n";
           avt += "<CurrentTrackURI val=\"" + mCurrentURI + "\"/>\n";
           avt += "</InstanceID>\n";
           avt += "</Event>";
           root1.SetStateVariableValue_AVTransport_LastChange(avt);
の部分は、DMCへ発行するイベントに関する処理です。ただ、コレに関してはひょっとするとうまく行ってないかもしれません・・・。その1の記事で、一部のアプリではうまく再生ができないといった原因がここにあると思っています。CurrentURIMetaDataはDIDL-LiteってなXSDで規定されるXML文字列なんですが、おそらくこいつもエスケープしなきゃならんのだろうってことでApache Common Languageライブラリですっけか、それのescapeXmlメソッドを使ってます。

ただ、<とかはエスケープしてる割に、「"」については"でエスケープしていないでLastChangeメソッドに渡してるので、そこがよく分かっていなかったり。"はしちゃいけないかもしれないのでそこがややこしい。。。

まぁ、とりあえずこれでDMCからの再生命令には答えられます。なんか再生したら、Androidが標準で対応している音源ならさくっと再生してくれます。たいていのアプリがhttp-getで飛ばしてくるのでそれ前提で作っちゃってます。MediaPlayerがURI対応してくれてるので楽でいいですね。とりあえずCurrentURI食わせればそのまま再生できて非常に楽。ほんとはエラー処理もっとちゃんとしないといけないんですけどね。

再生と一時停止はさくっとこんな感じでいいのかな? とりあえず動いてはいる。
       @Override
       public void Pause(
                     long InstanceID)
       {
        if(mMp != null) {
         if(mMp.isPlaying()) {
          mMp.pause();
         }
        }
           //
           // You can return a UPnP Error code by throwing the following exception:
           //
           // throw (new UPnPInvokeException(statusCode, statusDescription));
           //
       }
       @Override
       public void Play(
                     long InstanceID,
                     String Speed)
       {
        Log.d(TAG, "Play");
        if(mMp != null) {
         if(!mMp.isPlaying()) {
          mMp.start();
         }
        }
           //
           // You can return a UPnP Error code by throwing the following exception:
           //
           // throw (new UPnPInvokeException(statusCode, statusDescription));
           //
       }
あとはシーク位置を知らせるGetPositionInfoあたりを実装しておけばそれっぽくなる感じかな?
       @Override
       public void GetPositionInfo(
                     long InstanceID,
                     RefParameter<Long> Track,
                     RefParameter<String> TrackDuration,
                     RefParameter<String> TrackMetaData,
                     RefParameter<String> TrackURI,
                     RefParameter<String> RelTime,
                     RefParameter<String> AbsTime,
                     RefParameter<Integer> RelCount,
                     RefParameter<Integer> AbsCount)
       {
        Log.d(TAG, "GetPositionInfo");
        SimpleDateFormat sdt = new SimpleDateFormat("HH:mm:ss");
           // These are only sample values... 
           Track.value = (mMp == null) ? Long.valueOf((long)0) : Long.valueOf((long)1);
           Date TD = (mMp == null) ? new Date(- (32400 * 1000)) : new Date(mMp.getDuration() - (32400 * 1000));
           TrackDuration.value = sdt.format(TD);
           TrackMetaData.value = mCurrentURIMetaData;
           TrackURI.value = mCurrentURI;
           TD = (mMp == null) ? new Date(- (32400 * 1000)) : new Date(mMp.getCurrentPosition() - (32400 * 1000));
           RelTime.value = sdt.format(TD);
           AbsTime.value = RelTime.value;
           RelCount.value = (mMp == null) ? 0 : mMp.getCurrentPosition() / 1000;
           AbsCount.value = RelCount.value;
           // You can return a UPnP Error code by throwing the following exception:
           //
           // throw (new UPnPInvokeException(statusCode, statusDescription));
           //
       }
ただ、ここの処理も結構甘いです。本来ならMediaPlayerが非nullであっても、再生停止状態であれば(&neq;一時停止)getDurationがエラー吐いてるので処理としてはよろしくないです。

とりあえず、これくらい実装しておけばいくつかのDMCからは一応使えます。

あとはRenderingControlのところでボリューム関連を実装したらそれっぽくなるんじゃないかな?
まぁボリュームはあんまりいじることないだろうから実装してなくても大して困らないかも

さて、これでひと通り実装できたわけですが、残念ながらイベント処理でまだまだ不足があるようです。
ONKYOが自身のアンプ向けにOnkyo Remoteってアプリを提供してるんですが、こいつで再生しようとしてもなぜか接続状態を維持しきれません。
SetAVTransportってやったあと、GetPositionInfoを怒涛の勢いで呼び出してるんですが、なぜかシーク位置とかを認識してくれない。。。

もちろん、Onkyo RemoteはONKYOのアンプ以外を考えて作られているわけじゃないので、うまく動かないところに文句を垂れるべきではないのでしょうが、他のDMR(PioneerのN-50とか)には問題なく再生できるんですよねぇ。

ということは、私のDMRの実装がまずいという話に。APIは叩かれたらすぐ分かるんですがイベントはよー分からんのですよねぇ・・・。
かくなる上はDMCも自分で作って、自作DMRとやりとりをするような形でデバッグを進めるか・・・。

まぁ、正直そこはしんどいのであんまり気は進まないんですがねー。 今の状態でもある程度動くには動くので、MediaPlayerの状態遷移をもうちょっとしっかり管理したら次は処理全体のサービス化でも考えましょうかね。

うーん、にしてもなんでOnkyoRemoteにはうまくGetPositionInfoの情報が返されてないのかなぁ。。。

[Android]Android向けDMRアプリに挑戦 その1

残念ながら未完成ではあるのですがこれまでの足跡を残しておこうかと思う。
思いの外情報が転がってないんだよねぇ。探し方が悪いというのはあるんだろうけど。

ということで、現在の開発状況。

  • Intel Opensource Developer ToolsのDevice Builderより作成したAndroid用Stackを使用
  • デバイスやサービスはONKYO TX-NA579をコピー
  • DMCからSetAVTransportURIを叩かれると、MediaPlayerインスタンスで再生
  • GetPositionInfoにてシーク位置を返しているつもり
  • Seek、Pause、Playコマンドを実装済み
  • 一部のアプリからはうまく再生できない
  • MediaPlayerの状態遷移の理解が甘くて次の曲へ遷移するときに落ちることが多々
てな感じでしょうか。

MediaPlayerが落ちるバグについては大体どこが悪いのかは雰囲気つかめているのでそこはなんとかなりそう。
再生状態がPAUSED_PLAYBACKになってるときはGetPosition呼ばれるんだね。再生が終了した段階で再生状態をSTOPPEDに変えていなかったかもしれないからそこは要チェックだわ。
イベントを受け取らずにポーリングにしてるとかいう場合はそもそも再生してない時にMediaPlayer.GetDurationを呼ばないように処理を変えないといけないかな・・・。 けどMediaPlayer.isPlayingだと一時停止なのか再生停止状態なのか分からんのだよな。 確かMediaPlayerの状態遷移は自分で管理しておかないといけないとかいうのをどこかでちらっと見た気がするからなんぞ状態管理の変数を用意しておくか。Enumにでもすっかなー。それくらい用意しておいてくれてもいいような気もしなくはない。

で。このIntelのライブラリ、非常に良くできているんだけど悲しいほどにドキュメントがない。C#なんかは割とドキュメントコメントが入ってるから使いやすいんだけど、Android向けのJavaコードにはほっとんどコメントすら無い。
とりあえずC#のサンプルコードでもって挙動を確かめつつ、Javaの方にも同じ挙動をするようにコードを追記していくような感じで進めています。

ほんと、そこで感じることはC#の素晴らしさとJava嫌いっていうことばかりなんですがね・・・!w まぁ愚痴はさておき。
ちょいと実装の話を進めて行きましょう。

Device BuilderにてTX-NA579をコピーしたのがこちら。
Device Builderを起動し、File→Open from Networkを選ぶと自身のネットワーク内にあるUPnPデバイスが表示されるので、選べばそれの設定がさくっとコピーできます。

適当なデバイスがないって場合は、Device Builderをインストールした先にサンプルデバイスやサンプルサービスのファイルが置いてあるのでそこから作りたいデバイスを選びます。 DMSやDMRは標準で用意されているのでそちらを選べばよろしいかと。

そしてDescriptionにある各項目を好きに修正。このまんま使ってもいいですが、私の場合本物のTX-NA579と見分けつかなくなりますからね・・・。

ちなみに、Configurationにてデバイスタイプを「Device」から「ControlPoint」に変更すると、その名の通りDMCを作ることができます。ただ、このツールではDMCとして制御できるデバイスタイプは1種類のみだと思われます(多分)。DMRとDMS両方を制御するようなDMCの雛形を作るにはどうしたらいいかよく分かってません。

まぁ、それ自体はちょちょっとコードを追記したら済むので、さほど気にしなくても良いとは思います。
今回はDMCの話はさておきますが。

で、適当にデバイスの設定を記述し終えたら、一旦ファイルを保存しておきます。このツールに限らず、Intel(以下略)のツールはやたらめったらクラッシュするので注意。これ以外は特に保存すべきデータがないのでクラッシュしても困りはしませんが、こいつはクラッシュされると再度記述しないといけないので面倒です。

ファイルを保存したら、Generate Stackにてひな形プログラムを生成します。
Windows用に作りたいのであればC#かC用のWinSock2を選んでおくのが無難でしょう。ただ、先述しましたがドキュメントが一切無いのでCは正直つらいとは思います。一応C++のコードも吐き出せますが基本はCでそれをちょろっとラップしてあるだけなのでよほどUPnPに詳しい人でないと使えない気がします。。。

で、私はAndroid用が作りたかったのでJavaを選択。Cで作ってAndroidにポーティングするという意識もあるのか、CのスタックのところにもAndroidと書いてありますが上記の理由でお勧めしません。

ちなみにGenerate Stackにて生成されるファイル群は指定したフォルダ以下にそのまま展開されるので、C:\とかに配置すると悲しいことになります。プロジェクト用に1個フォルダを作っておくのが吉です。
例:C:\UPnPDevice

※ このIntel(略)のツール群はOSSであり、サイトからソースをダウンロードしてビルドすることができます。C#で書かれているのでLinuxでもビルドできます(レンダラーについてはWMPコンポーネントを使っているためにビルドに失敗します)。Device Builderももちろんビルドできますが、当然Windows向けに開発されているので、Generate Stackしようとするとファイルパスが不正ですと言って怒られます。パス区切り文字を「\」前提で作ってあるわけですね。 そこを環境に依存しないように(変数忘れちゃいましたが)適切に書き換えてやればLinuxでも使えるようになるはずです。そこまではやってませんが・・・w

で、Android用のプロジェクトが作成されたらEclipseからプロジェクトのインポートで取り込みます。
UPnP.jarがプロジェクトの直下におかれているので、libsに配置するなりBuildオプションにてライブラリを追加するなりしておきます。

そんでもってビルドして実機に転送して、「Start Device」ボタンをぽちっとなすればDMRとしてDMCから認識されます。
中身空っぽなのでデバイスとして認識されるだけで再生も何も出来ませんが、認識されるまでの通信部分はすべてよきに計らってくれます。素晴らしい!

ということで、あとはAPIをそそくさと埋めて逝きましょう。
それがわかれば苦労しないんだけどね・・・! でもひな形コード自体は非常に良く出来ていて、あとは仕様に従い粛々と埋めていけばOKです。

ということで続きはその2へ・・・。

2012年7月5日木曜日

[Linux]まさかのファイアウォール・・・

ここ最近はDLNAアプリについてせっせこ調べているわけですが、今日たまたまこんな記事を発見。
http://gihyo.jp/admin/serial/01/ubuntu-recipe/0213

先日紹介した
http://opentools.homeip.net/dev-tools-for-upnp
のツールと同等のことがこれでもできるじゃないか・・・!
まぁ確かgupnpはIntelもコードを寄贈してたと思うから同じ機能を持っているのはそうなのかもしれない。
実はOpenToolsで提供されているツールはソースコードも公開されていて、それは.NET2.0だったのでLinuxでビルドしたんですよね。
で、さくっとmonoで動かしてみたところ、ツールとしては動いたもののネットワーク上のデバイスは見つからず。

まぁ、Windows向けに作られたツールなんだし、Linuxでは正しく動かないのはしょうがないよなーってことで、KVMにXP環境構築してその中でOpenTools動かしてました。ネットワークはブリッジ構成にして同じサブネットに入るようにしておいたのでKVM上では問題なくデバイス見えてたし。

で、冒頭のgupnpのツールが見つかったんで、あぁこれでKVM上で動かす必要もなくなるわ、と早速動かしてみたらまさかのデバイス見えず・・・Orz

おかしい。それはおかしいと思って、ふとファイアウォールを無効にしてみたらさっくりとデバイスが!

えぇ、単にファイアウォールでポートを開けていなかっただけだったというOrzOrzOrz

OpenToolsのツールをmonoで動かしてもちゃんと見えてるし。 まぁこっちの方はLinuxでは割と不安定なのでgupnp使いますが・・・。

きちんとファイアウォールの設定を見ておかないといけないね!w

これでわざわざアプリ開発の時にKVM上のXPと言ったり来たりしなくて良くなったので満足満足。
なかなか進まないけどさて頑張らなきゃなぁ。

2012年7月1日日曜日

[読書]'12年6月の読書記録

うーん、本当はFate/Zeroの最終巻も読んだんだけど、読書メーターのサイトから見つけられなかったので集計されておらず。
アニメと文庫版が混じって出てきて、探すのだるくなったw

まぁそれを差し引いても今月は少ないねぇ。来月はもうちょいと頑張るか!


6月の読書メーター
読んだ本の数:2冊
読んだページ数:666ページ
ナイス数:17ナイス

氷結鏡界のエデン6  水晶世界 (富士見ファンタジア文庫)氷結鏡界のエデン6 水晶世界 (富士見ファンタジア文庫)
いやー、5巻読み終わったあとで前作読んででおいてよかった。これからどんどんリンクしていくみたいやね。 さてさて、ここでは意外と(?)シェルティスの活躍はなく、ユミィの頑張りがメイン、なのかな? なんかもう内容が盛り沢山すぎて大変だったかも。 随分と核に迫る人物たちがほのめかされたりしていよいよ物語も佳境を迎えつつ在るのかなーという感じ。 前作とのリンクも含め、今後の展開がホント気になるなぁ。
読了日:06月11日 著者:細音 啓
ベイビー、グッドモーニング (角川スニーカー文庫)ベイビー、グッドモーニング (角川スニーカー文庫)
ジョニー・トーカーあたりでどうなるんだこれとか思ったけれどクラウンはとても泣ける話だった・・・ プロローグ、再びプロローグというこの展開の仕方は素晴らしかったですね。 "これらの質問に対して、明確な答えを持つことを望みますか?" しっかしこの死神さん、めちゃくちゃ無茶を通してた気がするけど大丈夫なのかね? 輪廻にまである程度干渉してそうだったし。 実はすごい死神さん? あとでスニーカーWEBの方を確認しておかないとなー!
読了日:06月06日 著者:河野 裕

2012年6月の読書メーターまとめ詳細
読書メーター