htsign's blog

ノンジャンルで書くつもりだけど技術系が多いんじゃないかと思います

MusicBeeのプラグイン開発

最近、MusicBeeというWindows向けのオーディオプレイヤーのプラグイン開発ばかりやってます。
本体がVer2.0辺りから.NET化したのもあって、プラグインも.NET(C#VB.NETC++/CLI)で非常に手軽に開発することができます。
コントロールはWinFormsです。
ビルドしたプラグインは主に2chのMusicBeeスレで公開していますが、
開発していくにあたっていろいろとノウハウも貯まってきたので、ここいらでいくつかご紹介します。
公式Wikiに書かれている内容もありますが、一応。

以下出てくるmbApiInterfaceという謎の変数ですが、
これはMusicBeeのAPIにアクセスするためのインターフェースのインスタンスです。
公式Wikiで配布されているプラグインソースコードをDLすると初めから宣言されています。

メインパネルで表示されている曲をすべて取得

public IEnumerable<string> GetDisplayedSongs()
{
    return GetSongs("domain=DisplayedFiles");
}

public IEnumerable<string> GetSongs(string query)
{
    if (mbApiInterface.Library_QueryFiles(query))
    {
        while (true)
        {
            string filepath = mbApiInterface.Library_QueryGetNextFile();
            if (string.IsNullOrEmpty(filepath)) yield break;
            yield return filepath;
        }
    }
}

ちなみに、ライブラリに存在する曲を丸ごとすべて、という場合には

public IEnumerable<string> GetAllSongs()
{
    return GetSongs("domain=Library");
}

とすることで可能です。

設定画面のコントロール作成をべた書きしなければいけない問題

MusicBeePlugin.PluginクラスのConfigureメソッドは、
内部変数aboutConfigurationPanelHeightプロパティに0以外を代入することによって、設定画面のPanelコントロールのハンドルをIntPtr構造体で受け取ります。
サンプルソースコードではこれに直接コーディングでコントロールを生やしていますが、
これをユーザーコントロールとして一まとめに作成したものを追加することで見通しがよくなります。
MusicBeeの設定画面で「保存」を押して閉じたときにMusicBeePlugin.PluginクラスのSaveSettingsメソッドが走るのですが、ここで編集した内容を反映させるため、私は設定を一まとめにプロパティで持ったConfigクラスを作り、そのインスタンスを作成したユーザーコントロールに参照渡ししています。

public bool Configure(IntPtr panelHandle)
{
    if (panelHandle != IntPtr.Zero)
    {
        Panel configPanel = (Panel)Control.FromHandle(panelHandle);
        var childUserControl = new MyUserControl(ref this.config);
        configPanel.Controls.Add(childUserControl);
    }
    return false;
}
追記(02/13 0:27)

Configクラスのインスタンスを参照渡しするより、シングルトンパターン使った方がスッキリしますね。

public class Config
{
    private Config() { }
    
    private static Config instance = new Config();
    public  static Config Instance { get { return Config.instance; } }
}

ファイルへのタグ書き込みが上手くいかない問題の解決

公式のフォーラムをよく読むと分かるのですが、MusicBeePlugin.Plugin.MusicBeeApiInterfaceクラスの中身を読んでいるだけでは気づけないんじゃないかと思います。

public void WriteTag(string filepath, MetaDataType type, string value)
{
    mbApiInterface.Library_SetFileTag(filepath, type, value);
    mbApiInterface.Library_CommitTagsToFile(filepath); // ここ大事
}

実はLibrary_SetFileTagメソッドはただバッファリングしているだけで、このメソッドを走らせているだけでは何重に実行しようとも実際に反映されることはありません。
反映させるためにはLibrary_CommitTagsToFileメソッドを走らせる必要があります。
ここで割とハマりました。


こんなところですか。
本当はもっとある気がするんだけど、すぐには浮かんでこないのと、文章にとりとめがなくなりすぎてて意味不明になってしまうので。

蛇足

デザイナで編集してると気づかないうちに余計なプロパティまで変更してること、ありますよね。

.NET Framework における System.String について

今年に入って初めて .NET Framework にまともに触れるようになってきたのでまだまだ勉強中です。
趣味で触るのはC#、仕事で触るのはVB.NETって感じです。

さて、だいたいどの解説サイトを見ても「C#VB.NETもできることは同じ」というような解釈になってます。
ただ、System.Stringが言語によって扱いが異なる気がしています。

あるとき、VB.NETNullable(Of String)と書いたときに静的エラーが出力されました。
妙だと思い調べてみると、System.Stringは参照型であるというのです。

基本的な型のうち int, double, byte, char などは値型ですが、
string は「値型のように振る舞う参照型」であるというような記述を見ました。

参照型であるということは null を代入できるのか。
と思い、以下のコードを書きました。

C#
public class Program
{
    public static void Main(string[] args)
    {
        string string1 = null;
        string string2 = "";
        string string3 = string.Empty;

        Console.WriteLine("1:{0}", string1 ?? "string1 is null");
        Console.WriteLine("2:{0}", string2 ?? "string2 is null");
        Console.WriteLine("3:{0}", string3 ?? "string3 is null");

        Console.WriteLine(string1 == string2); // null         == ""
        Console.WriteLine(string2 == string3); // ""           == string.Empty
        Console.WriteLine(string3 == string1); // string.Empty == null
    }
}
VB.NET
Public Module Program
    Public Sub Main(args() As string)
        Dim string1 As String = Nothing
        Dim string2 As String = ""
        Dim string3 As String = String.Empty
        
        Console.WriteLine("1:{0}", If(string1, "string1 is Nothing"))
        Console.WriteLine("2:{0}", If(string2, "string2 is Nothing"))
        Console.WriteLine("3:{0}", If(string3, "string3 is Nothing"))
        
        Console.WriteLine(string1 = string2) ' Nothing      = ""
        Console.WriteLine(string2 = string3) ' ""           = String.Empty
        Console.WriteLine(string3 = string1) ' String.Empty = Nothing
    End Sub
End Module

動作デモ*1

C#     : http://rextester.com/ELTMU58022
VB.NET : http://rextester.com/IRCW50181

と、ここまで来て二つの言語間で結果が違ったことに気づきました。

これ、 ""String.Emptyエイリアスの関係なので言うまでもなく同じものです。
ですから、比較したときにtrueになるのは当然だと思います。
でも、

string string1 = null;
string string2 = "";
Console.WriteLine(string1 == string2); // ==> false
Dim string1 As String = Nothing
Dim string2 As String = ""
Console.WriteLine(string1 = string2) ' ==> True

なのです。
VB.NETではデフォルトでOption Strict Offであるため暗黙の型変換が行われたのかと思い、Option Strict Onを記述するも変わらず。

ちなみに、

Dim string1 As String = Nothing
Dim string2 As String = ""
Console.WriteLine(string2.Equals(string1)) ' ==> False

でした。

この挙動について、このエントリを書いている途中まではSystem.Stringの特殊性によるものかと思っていましたが、書いている途中でそれぞれの言語の等価演算子の振る舞いの違いによるものかも?ともちょっと思っています。
どうなんですかね、これ。

何とも尻切れトンボ感あるエントリになってしまいましたが、この辺で。

追記 (2014/11/11 1:21)

どうやら、やはり等価演算子自体が違う振る舞いをするようです。

*1:rextester.comは鯖が落ちていることが稀によくある

Co-opゲーが好きで対戦ゲーがあまり好きでない理由

最初に

独り言なので読む価値ないですさようなら

本題

ゲームを仕事と見る向きもありますが*1
基本的にゲームはあくまでも暇つぶしの道具であり、遊びなので、
楽しめなければ意味がありません。

そして勝負要素のあるゲームというのは、どちらかが必ず負けるか引き分けに終わる。
たいていの人は負けると悔しい(ですよね?)ので、その苦境を楽しめるかどうかで対戦ゲーの好き好きが決まってくると思うんですが、私は苦手です。
特にFPS二人零和有限確定完全情報ゲームなど本人のスキルに多分に左右される勝ち負けは、相手に見合う実力がなければ一方的にただ蹂躙されるだけになってしまうため、遊びとは程遠い結果に終わることが多いです。
それを楽しめる人を私は見てみたい。

一方、Co-opでは他の人と一緒にゲームを進行させて楽しみを共有できます。
共通の目的があるので一緒に達成できます。あるいは一緒に失敗できます。
失敗してもみんなと一緒にワイワイできれば楽しいんです。

そういう意味では、対戦ゲーでも多人数vs多人数なら同じチームのメンバーによっては楽しく遊べます。
勝てたらもちろん嬉しいですし、負けても楽しめます。
ただし、例えば野良で入ったチームに異常に上手い人が一人いて、その人がワンマンプレイでチームを勝利に導く場合はその限りではありません。
「勝たされている」感じがしてしまう。
せっかくチームを組むのだから協力して、その先に勝つためのきっかけを作るという、そこまでの過程に楽しみを見出すタイプです。私は。
他の人との行動が綺麗に噛み合い、その結果ものすごい成果を生み出すときに、言い知れないカタルシスを覚えます。

ちょっと話が対戦ゲー寄りの中身になりましたが、今書いたことはCo-opにも言えると思います。
協力から結果を生み出すのが好きなんです。
そういうところ、誰にだってあると思うんですが、どうでしょうか。

*1:現にプロゲーマーという職業がありますよね。日本人だと梅原大吾氏。

Xperia Z1 Compact買いました。

Expansysでセールだったので。
通常価格 \67868 のところ、なんと \41790 (カラバリ黒の場合)でした。

ちなみに7月9日22時現在、やや値上がりしてますがまだ安いです。
http://www.expansys.jp/sony-xperia-z1-compact-unlocked-lte-16gb-black-258379/

小型の端末が好きで、正直Z1 Compact(国内ではZ1 fとして知られるSO-02Fです)の4.3インチでも「やや大きいかな」と思っていたんですが、霜降りでこの価格はしばらく見られるもんじゃないと思いポチりました。

ヤマト利用で、送料1900円と関税料1900円でした。
それでも安い…!

先ほど帰宅したら届いていたのでこれから開けます。

f:id:htsign:20140709222359j:plain
化粧箱右下にワールドカップのロゴ入ってる。
正直どうでもいいけどなんとなくかわいい。

Visual Studio Online "Monaco" でWeb上で完結する開発環境を使えるようになるまで

最近Web上で開発できる環境って増えてきましたよね。

有名どころだと

辺りでしょうか。

プロジェクト単位でなく、ただ書いて動かすだけのサービスなら他にも

などあります。

あとCloud9はGitHub Gistで大活躍のAce editorを開発しているところでもあります。

なんでもWeb上でやっちゃう時代

さて、時代の流れなのか、最近のMicrosoftの大幅なOpen & Webへの方向転換のおかげなのか、
Visual Studioもオンラインサービス連携が強化されています。
従来のTeam Foundationに加えてGitによるプロジェクト管理も出来るようになりました。

そしてWeb上での開発をMicrosoftもサポートしていたようなので、*1 今回、環境を整えてみることにしたわけです。
ちなみに、このMicrosoft製エディタ(コードネーム: Monaco)は、TypeScript公式サイトのPlaygroundやOneDrive(旧:SkyDrive)でも利用されています。
試しにOneDriveの任意のフォルダにJavaScriptファイルなど置いてみると分かりますが、Web上で編集できます。*2

前提条件

MonacoでVisual Studio Online上のプロジェクトを編集できるようにするためには、以下の条件をすべて満たす必要があります。

1つ目は言わずもがなですね。
2つ目、3つ目は無料でサインアップできます。

Visual Studio Online
http://go.microsoft.com/fwlink/?LinkId=307137&clcid=0x411
Windows Azure
https://account.windowsazure.com/signup?offer=ms-azr-0044p

無料枠ではいろいろな制限が付きますが、とりあえず触るだけなら問題ありません。

手順

以下は前提条件をクリアしているものとして進めます。

まずプロジェクトを作ります。
Visual Studio Onlineトップページの右上にある歯車マークをクリックします。
f:id:htsign:20140530233211p:plain

コントロールパネルに遷移するので、ここで「Create a new team project」をクリックします。
f:id:htsign:20140530233646p:plain

ポップアップダイアログが表示されるので、必要事項を記入し「Create project」をクリックします。
ここで、Version controlは必ずGitにしてください。
あとでこの違いが重要になってきます。
f:id:htsign:20140530233712p:plain

さて、プロジェクトが作成されたらVisual Studio Onlineですることはもうありません。
f:id:htsign:20140530233742p:plain
作られるのに少し時間がかかります。


次にAzureでWebサイトを作ります。
画面左下の「新規」をクリックして、「コンピューティング」→「Webサイト」→「カスタム作成」と辿ります。
f:id:htsign:20140530234015p:plain
f:id:htsign:20140530234030p:plain

必要事項を入力します。
必ず「ソース管理から発行」にチェックを入れてください。
f:id:htsign:20140530234118p:plain

ソースコードの位置」で「Visual Studio Online」
f:id:htsign:20140530234133p:plain

「接続を承認する」で自分のURIを入力して「今すぐ承認」をクリックします。
小窓が開くので「Accept」を押してあげてください。
f:id:htsign:20140530234147p:plain

最後に「デプロイするリポジトリの選択」で、先ほど作ったプロジェクトを選択して完了してください。
f:id:htsign:20140530234220p:plain
サイトが作られるまで少し時間がかかります。


出来上がったらWebサイト名をクリックして詳細画面に進みます。
f:id:htsign:20140530234558p:plain

メニューの「構成」をクリックしてサイトの構成編集画面を表示します。
f:id:htsign:20140530234617p:plain

VISUAL STUDIO ONLINE での編集」をオンにして*3 保存します。
f:id:htsign:20140530234642p:plain

メニューから「ダッシュボード」に移り、「Visual Studio Online での編集」をクリックします。
f:id:htsign:20140530234724p:plain


ここでやっとMonacoがお目見えです。
これからプロジェクトと関連付ける作業を行います。
上部メニューにあるサイト名をクリックし、「Connect to Visual Studio Online」をクリックします。
f:id:htsign:20140530234814p:plain

指示に従って接続します。

勝手に元の画面に戻るので、再び上部メニューからサイト名をクリックし、今度は「Clone repository from Visual Studio Online」をクリックします。
f:id:htsign:20140530234832p:plain

ここで先ほど作成したプロジェクトを選択します(Version controlがGitのもののみ選択できます)
f:id:htsign:20140530234849p:plain

と、これでコンソール画面にずらずらっとGitの標準出力が流れて…
f:id:htsign:20140530234944p:plain

晴れてVisual Studio Onlineのプロジェクトに関連付けられました。
あとは好きにコードを書いてgit pushしまくりましょう。
新規でファイル作成はもちろん、ローカルからのアップロード*4 にも対応しています。

Let's enjoy!!


あ、そうそう。
git pushするにはBasic認証(Digest認証かも?)が必要です。
認証するにはVisual Studio Onlineのコントロールパネルで、右上の自分の名前部分をクリックして「My profile」をクリックします。
ポップアップダイアログが表示されるので、「CREDENTIALS」タブの「Enable alternate credentials」をクリックし、パスワードを指定してください。

*1:実は半年以上前からできていたようでした。知らなかった…。ショック…。

*2:こちらで確認している限りでは、.js, .ts, .phpシンタックスハイライト&コード補完&構文解析あり、 .cs, .html はシンタックスハイライトのみ、 .ps1 は if など極小数の予約語のみ色分けされ、 .awk, .pl, .rb, .py はテキストと認識され編集はできるが一切補助機構なし、といった感じでした。

*3:「この機能を使用するには、展開資格情報が必要です。」と表示されスイッチがグレーアウトされていたら、「今すぐ展開資格情報を設定する」をクリックし、指示に従って設定しましょう。

*4:IE10以降, Firefox, Chrome等のモダンブラウザならDrag&DropでOK