2010年9月のエントリ

WPFで使っている自作DLLの名称を変更したのだが,どうしてもリソースファイルを旧名称でアクセスしてしまい,Not Foundになる。GrepしてみるとPropertiesResources.Designer.csとSettings.Designer.csの中に旧名称が残っている。この2つはTool Generatedで変更するなと書いてあるのだが,生成元のソースはどこにもない。確かにこのファイルを消してプロジェクトを開き直すと再生成されるのだが・・・。

てことで悩んだ結果,VSSからとってきていることが判明。いつのVisual Studioからか知らないけど,ファイルが足りなかったら勝手にVSSからとってくるようになってたのか。VSSに接続しないようにするとResources.Designer.csもSettings.Designer.csも生成されずにビルドエラーになるので,とりあえず再生成はされないと断定して直接いじることにした。

WPFで7セグメントLED(時計とかのデジタル数字)を表示したい箇所があっていろいろ調べていたのだが,なかなかいい案が見つからない。今使用しているPCにはAscenderFonts製Quartz MSってフォントが入っているけどどこでも入っているのか,どこに何を払えばプログラムに同梱できるのかよく分からないし,カスタムコントロールで画像を表示するのもつまらない。ってことでフォント自作。フリーではfontforgeってのが有名らしく,使ってみた。キーボードショートカット使うと落ちまくりつつもなんとか「0~9:/」だけを含むフォントが完成。WPFのプロジェクトにつっこんでおけばそのままリソースとして使える。フォントのインストールは不要。

たいしたものではないので公開しておこう。何にどう使ってもOKですが,何か問題があってもサポートはしません。

2010092301

ダウンロード

MIDIキーボードタイピングソフトができてきたところでそろそろテスト工程に入る。まずはXP実機で試してみる。以前VMWareで実験したときはエラーが出て実行できなかったが,実機というか.NET4.0にしたおかげか実行はできた。ただ,設定ファイルにハッシュアルゴリズムSHA256を使っていたのがXPではサポートされていないらしく設定ファイルを読むところでエラーになった。とりあえずセキュリティ的に重要なわけではないのでSHA1で我慢しておくか。

C#でMIDIキーボード入力をやってみる。midiInOpenにコールバックを登録しておけば,midiInStart/midiInStopでメッセージを受け取れる。

{
    [DllImport("winmm.dll")]
    public static extern uint midiOutOpen(
        ref IntPtr phmo,
        uint uDeviceID,
        IntPtr dwCallback,
        IntPtr dwInstance,
        uint fdwOpen);

    public delegate void CallBackDelegate(
        IntPtr hdrvr,
        uint uMsg,
        uint dwUser,
        uint dw1,
        uint dw2);
}

public class MIDIIn
{
    private Win32MIDI.CallBackDelegate _callback = null;

    public void Open()
    {
        _callback = new Win32MIDI.CallBackDelegate(CallBack);

        Win32MIDI.midiInOpen(
            ref _midiin,
            _mididevice,
            (IntPtr)Marshal.GetFunctionPointerForDelegate(_callback),
            (IntPtr)0,
            Win32MIDI.CALLBACK_FUNCTION);
    }

    private void CallBack(IntPtr hdrvr, uint uMsg, uint dwUser, uint dw1, uint dw2)
    {
        int state = (int)dw1 & 0xff;
        int data1 = (int)dw1 >> 8 & 0xff;
        int data2 = (int)dw1 >> 16 & 0xff;
    }
}

最初,C++で実装するときと同じようにCallBackをstaticメソッドにしていたのだが,インスタンスのメンバ変数を使いたくて,midiInOpenのdwInstanceにthisを渡すことを考えていた。でもthisをIntPtrに変換する方法というか変換していいのかを調べても何も情報は見つからない。最終的に「GetFunctionPointerForDelegateはthisポインタをうまいこと扱ってくれる」というような記述を見つけて,思いきってstaticを取ったらそのまま動いた。C++の発想だとstaticにしないと動かないのでメンバ関数をそのまま渡すというのは思いつきもしなかった。こんな感じで半日を無駄にしてしまった。

その後は順調に進んで,キーボード練習ソフトっぽいものはできあがった。そろそろもばもば(?)に投げてみよう。

Visual Studio 2010でWPF(というか.NET)のソースコードレベルデバッグをする方法。MSのブログに載っている2008の方法と同じだが,Tools-OptionsでDebugging-GeneralからEnable source server supportをONにし,Symbolsの一番上に「http://referencesource.microsoft.com/symbols」を追加する。なお,「http://msdl.microsoft.com/download/symbols」とは違うPDBファイルらしいので,既に設定している場合はダウンロード済みのシンボルを一旦削除する必要があるっぽい。

ちなみにWPFのソースコード自体はAvailable Source Code Componentsから落とせる。

Google マップなんかでホイールを回すとズームする機能。あれって現在マウスが指している位置はそのままで拡大率だけが変わるのだが,同じことを実装しようとすると意外に難しい。忘れそうなのでメモ。

2010090801

図は拡大率R1から拡大率R2に変更したときの例。大きな四角はマップ全体で,中の四角はビューポート。拡大率R1でマウスカーソルが絶対座標(Ax, Ay) 相対座標(Rx,Ry)にいるとき,拡大率をR2にした場合を考える。このとき,ビューのスクロール位置(Sx, Sy)をうまくいじってやれば,ビューからの相対座標(Rx,Ry)を変えずに拡大をすることができる。(Sx, Sy)の求め方は以下の通り。

(Sx', Sy') = (Ax', Ay') - (Rx, Ry)
           = (Ax, Ay) * R2 / R1 - (Rx, Ry)

でここからがWPFの場合だが,OnMouseWheelの中で以下のように実装してみた。_view(ScrollViewerなど)の中に_map(Canvasなど)を入れている前提。_viewへのGetPositionとスクロール位置はR1倍した値になり,_viewの中身へのGetPositionは拡大する前の値になるので上の式と若干異なっている。

private void OnMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
    double a_x = e.GetPosition(_map).X;
    double a_y = e.GetPosition(_map).Y;
    double r_x = e.GetPosition(_view).X;
    double r_y = e.GetPosition(_view).Y;

    R_2 = R1 + e.Delta;

    _view.ScrollToHorizontalOffset(a_x * R_2 - r_x);
    _view.ScrollToVerticalOffset(a_y * R_2 - r_y);
}

シャドールームさんのところでいくつかタイピングゲームの複数入力の方法について話題が出ていたのでWeather Typingのやり方を少しだけ書いてみる。 Weather Tytpingの自動ローマ字入力はオートマトンを使っている。シャドールームさんで紹介されてたHow To Become A Typerさんが「Tsuikyo 2.0 – 鎚鏡弐」で書いているのと同じ方法ですかね。以下の絵は,いつかタイピングソフトの作り方ページを作るときのために作ったネタだけど,多分今後も作らないだろうし,丁度良いので載せてみる。 2010090401

図は「しゃ」という問題文があったときの例。まず「しゃ」という問題がきたら,あらかじめ上のようなオートマトンを作成しておく。作り方はなんでもいいけど「しゃ」と上のオートマトンをテーブルで用意しておいてもいい。で画面に表示する文字列を作るときは,現在の設定に従ってオートマトンを処理する。例えば「CよりはSを使う」「SHよりはSYを使う」「LYAやXYAは使わない」設定であればS→1→7→Eを通って「SHA」を表示する。次にゲーム中ユーザ入力がきた場合,設定と画面表示を更新しながらオートマトンを処理していく。例えば「CILYA」と入力したときは2へ行って「SよりはCを使う」設定に変更。3→5へ行って「LYAやXYAを使う」設定に変更。最後に9→Eへ行って完了。上の矢印以外の入力が来たらミス扱い。 上の例は「しゃ」だが,問題文が長くなったらオートマトンをつなげていくだけ。例えば「しゃしゃ」だったら上のを2個つなぐだけでOK。「ん」「っ」が入ってくるとだいぶややこしくなるが,基本的には上のやり方でやればいい感じの自動入力ができる。 なお,TODでは多分「SHよりはSYを使う」のような系統立てた入力設定ではなく「SHAよりはSYAを使う」「SHUよりはSYUを使う」みたいな細かい入力設定になっているっぽい。「ん」の扱いも変。Weather TypingはTODを超えることを目指して作っていたので,この辺はTODより改善できているはず。