Windows Mobile と加速度計

シェイクとスキップで音楽を操作する

Chris Mitchell
サンプル コードのダウンロード

モバイル開発では、G センサー、つまり加速度計が急速に必需品になりつつあります。Windows Mobile デバイスは多くのガジェット機能の 1 つとして加わり、電話を落とす頻度を確認することから、電話を傾け、揺らし、たたき、放り投げる (この操作には適切な免責事項を添える必要があるかもしれません) ことによって、アプリケーションやゲームを操作できるようにすることまで、開発者がさまざまな可能性を実現できるようにしています。この記事では、HTC Diamond を使って加速度計のデータにアクセスしてこれを利用することで、音楽の再生を制御し、ユーザーが電話器をシェイクすると Windows Media Player Mobile でトラックをスキップする方法について説明します。

加速度計の使用は Windows Mobile 向けの開発では新しい分野です。そこで、記事の汎用性を保つために Shake ’n Skip というアプリケーション (図 1 参照) を作成し、センサーの開始方法、センサーからのデータの取得方法、センサーの終了方法、および数行のコードでサンプル アプリケーションの関数を変更する方法を容易に確認できるようにしています。ここで説明するアプリケーションを使えば、インターネット ブラウザーを開いたり、デバイスをロックしたりといった何か他の操作を行う場合に、最小限のコーディング作業で容易にコードを変更することができます。また、アプリケーションにはロガーを用意しており、センサーの出力をそのまま表示できます。したがって、ここで説明する簡単なシェイク検出アルゴリズム以外の検出技法を開発することも可能です。たとえば、アプリケーションやゲームを操作するために電話器でのその他のジェスチャの検出を行うことなどが考えられます。

HTC Diamond の加速度計を使用した開発に関する詳細については、blog.enterprisemobile.com/2008/07/using-htc-diamonds-sensor-sdk-from-managed-code/ (英語) を参照してください。ここで紹介するコードは、HTC Diamond およびその他の HTC デバイスで機能しますが、その他の電話では機能しません。

図 1 Shake 'n Skip アプリケーション (左) と操作中の Windows
Media Player Mobile (右) のスクリーンショット

アプリケーションの説明

Shake'n Skip アプリケーションではタスクをいくつか実行する必要があります。まず、センサーから最新の情報を定期的に取得することが必要です。次に、電話がシェイクされているかどうかを検出する基準を決めなければなりません。最後に、電話がシェイクされていると判断したら、決められた動作 (この例では、Windows Mobile メディア プレーヤーで次のトラックにスキップ) を実行する必要があります。サンプル アプリケーションでは、Windows Media Player を起動および操作するキー入力のシミュレーションを行います。このような方法を使用すれば、電話器の多くの機能を操作したり、ホーム画面に戻ったり、デバイスをロックしたり、アプリケーションを開始したりできます。

加速度計を使用する多くのアプリケーションによって、昔のアプリケーションやゲームに再び活躍の場が与えられています。たとえば、Labyrinth という長い歴史を持つゲームは、迷路のような盤上でボールをあちこち移動させ、穴に落とさずに迷路からの脱出を試みます。遠い昔 1891 年、S. D. Nix が Labyrinth の特許を取りました。彼がこのアプリケーションを実行するために MIPS に十分な余裕があるかどうか心配していたことや、さまざまな速度で迷路の側面にボールを当てるシミュレーションを行う際に防振装置を使用して調査していたことをだれが想像できるでしょうか。多くのアプリケーションではさまざまな方法で加速度計を使用して、電話器の魅力的で新しいインターフェイスを作成したり、加速度計と他の出力とを組み合わせて、注目すべきユーザー エクスペリエンスを実現したりできます。

加速度計

まず、簡単な定義から始めます。加速度計とは、時間に対する速度の変化率、つまり加速度を測定するセンサーです。速度はベクトルで表されます。ベクトルはスカラーとは異なり、大きさと方向を持ちます。Physics 101 で使用したのを覚えているでしょう。

加速度計には、加速度の方向を示す気泡の入った水管から、携帯電話で使用されている集積回路まで数多くの種類があります。HTC Diamond は、MEMS (Micro-Electro Mechanical System) ベースの加速度計を使用し、マイクロメーター (1 メートルの 100 万分の 1) 単位の測定値を含む小さな構造体を形成します。市場に出回っている電話器の大半がこの種の加速度計を使用しています。電話器では、3 つの軸で加速度の方向を表し、電話器の上下の向き、電話を立てた状態で画面がユーザーの方に向いているかどうかなど、3 つの軸を組み合わせてさまざまな状態を表現します。どのような時点でも電話器の状態は、次の 3 つの値で表されます。

  1. TiltX または Roll: 0 のときは、水平に置かれている状態です。電話器の送話口からイヤホンまでの縦方向の中心を基準に回転を測定します。
  2. TiltY または Pitch: 0 のときは、水平に置かれている状態です。電話器の画面の幅方向の中心を基準に回転を測定します。
  3. TiltZ (揺れではありません): 0 のときは垂直方向に立っています。マイナス記号 (-) は画面が上向きの状態を示し、プラス記号 (+) は画面が下向きの状態を示します。これらの測定値は、図 2 のようになります。図の下部に測定値がそのまま表示されています。この 3 つの値が組み合わされて、重力の方向を示すベクトルを表すことを覚えておいてください。

図 2 電話器の位置を加速度計からの出力で表した図

次は、電話器がシェイクされているかどうかを検出し、シェイクを検知した場合はコマンドを実行して、Windows Media Player でトラックをスキップします。

システム コンポーネント

Shake'n Skip アプリケーションでは、Windows Mobile メディア プレーヤーをホストしていません。また、再配布を行う際はプラグインが当然の選択肢になりますが、なんらかの種類のプラグインでホストされるアプリケーションでもありません。このアプリケーションは、簡単なモバイル Windows フォーム として作成しています。そのため、多種多様な目的で再利用したり、可能性がある加速度計の使用方法についてのアイデアをテストしたりできます。アプリケーションでは、センサー データをログ ファイルに出力できます。オフラインでこのログ ファイルをテストして、独自に考えたアルゴリズムがどの程度適切に機能しているかを確認できます。図 3 に、アプリケーションの論理的流れを示します。

図 3 位置認識タスクの監視と通知のシステム フロー

アプリケーションのセンサー コンポーネントは、構造体への参照を受け取り、その構造体に値を返す 1 つの関数で構成されます。このセンサー コンポーネントは、定期タイマーに基づいて呼び出します。アプリケーションでは、この関数を 100 ミリ秒ごとに呼び出すよう、タイマーを設定します。この値を変更する場合は注意が必要で、設定値が低すぎると、エイリアシングが発生する可能性があります。シェイク アルゴリズムの検出も、センサー コンポーネントから返される最新のデータを受け取る 1 つの関数で構成されます。シェイク アルゴリズムでは、最新のセンサー データとその 1 回前のセンサー データのユークリッド距離 (後述) を比較します。その後、しきい値とその 2 つの数値の比較結果からシェイクを検知したかどうか判断します。シェイクを検出するために必要な連続比較の数は、検出期間とリセット期間の 2 つの入力によって決定します。シェイクを検出したら、インターフェイスで選択された操作モードに応じて、コマンド コンポーネントから KeyCommand for Application または Logger の 2 つの関数を条件付きで呼び出します。KeyCommand for Application 関数は、キー入力のシミュレーションを行うために、Wmplayer.exe というアプリケーションと電話キーを受け取ります。Logger 関数は、加速度計からのデータを、電話器の記憶域のルートに配置されているファイル、Gsensorlog.txt にログ記録します。作業を簡単にするため、アプリケーションのクラスは、Visual Studio で作成されるのと同じ名前空間に追加しました。この名前空間には、UI を保持しているフォームのクラスから継承された既定のクラスが含まれています。

システム コンポーネント - G センサー

加速度計のデータにアクセスするには、HTC 電話器で提供されるセンサーに照会してデータを提供するアンマネージ DLL を、マネージ C# 環境から呼び出す必要があります。このセンサー呼び出しには、3 つの要素の設定が必要です。最初の要素は構造体です。この構造体は、アンマネージ DLL を呼び出すメイン ルーチンからマーシャリングされ、必要な情報が格納されます。変数 AngleY および AngleX は、今回のアプリケーションでは使用していませんが、各平面に対する角度が返されます。

//Data structure passed to sensor query api
public struct SensorData
{
public short TiltX;
public short TiltY;
public short TiltZ;
public short Unknown1;
public int AngleY;
public int AngleX;
public int Unknown2;
};

また、次のコードからお分かりのように、Pinvoke 呼び出しがいくつか必要です。CreateEvent 関数などの外部 DLL 関数へのアクセスが必要な場合でも、DLL として User32 を使用する関数が一覧されていれば、多くの状況で、User32 を Windows Mobile の Coredll に置き換えても、呼び出しが実行可能であるということは注目すべき点です。

[DllImport("HTCSensorSDK")]
extern static IntPtr HTCSensorGetDataOutput(IntPtr handle,
out SensorData sensorData);
[DllImport("HTCSensorSDK")]
extern static IntPtr HTCSensorOpen(int sensor);
[DllImport("HTCSensorSDK")]
extern static void HTCSensorClose(IntPtr handle);
[DllImport("coredll", SetLastError = true)]
extern static IntPtr CreateEvent(IntPtr eventAttributes, bool
manualReset, bool intialState, string name);
[DllImport("coredll", SetLastError = true)]
extern static bool EventModify(IntPtr handle, uint func);
[DllImport("coredll")]
extern static bool CloseHandle(IntPtr handle);


図 4 2 つのシェイクのユークリッド距離の変化を示すグラフ

 
図 5 2 つのシェイクの TiltX (青)、TiltY (赤)、および TiltZ (緑) の変化を表示すグラフ

最後の要素はデータの取得です。次のコードに示した関数を呼び出し、データを処理するために、SensorData 構造体に渡します。CreateEvent 関数を呼び出す同期オブジェクトでは、スレッドの実行が再開される前に、HTC_ GSENSOR_SERVICESTART が発生しなければならないことを OS に通知します。このイベントが発生すると、スレッドは再び CPU 時間のスケジュールが割り当てられます。スケジュールが設定されたら、スレッドの実行が継続されます。これで、アプリケーション スレッドがセンサー イベントと同期されるようになります。

public void GetSensorData(ref SensorData data)
{
//Initialise and start sensor
IntPtr Handle = HTCSensorOpen(1);
IntPtr hEvent = CreateEvent
(IntPtr.Zero, true, false, "HTC_GSENSOR_SERVICESTART");
EventModify(hEvent, 3);
CloseHandle(hEvent);
HTCSensorGetDataOutput(Handle, out data);
return;
}

システム コンポーネント - シェイク アルゴリズム

センサー情報は取得できたので、このデータを使用してシェイクを検出するアルゴリズムを調べます。

アルゴリズムの開発の基盤となる考え方は次のとおりです。電話器をシェイクすると、電話器の各軸を基準に、重力の方向に急速な変化が生じます。つまり、TiltX、TiltY、TiltZ の値の急速な変化を確認することになります。急速な変化がいつ起こるかを判断するためのしきい値を設定しておけば、電話器をシェイクするのではなく電話への応答や電話器の取り出しのようなゆっくりとした動作による変化をフィルター処理することができます。

ただし、急速な変化を検出するには尺度が必要です。2 つのデータのセット間を測定した距離を確認するのが最もわかりやすい方法です。距離が特定の値より大きければ、急速な変化が起こっていると言えるでしょう。

とすると、p と q の距離は で計算されます。

ユークリッド距離は、今回急速な変化を測定するのに使用した簡単な距離の測定基準です。この計算では、3 軸加速度計の 1 回前の出力 と、現在の出力 とを比較します。ただし、勢いよく電話を落としたり、たたいたりしても、急速な変化になる可能性があるため、時間の機能をアルゴリズムに挿入して、ある程度の長さシェイクが続く場合のみシェイク検出コンポーネントをトリガーできるようにする必要があります。

図 6 シェイク検出のメイン関数

public bool DetectShake(SensorDataOld dataold, SensorData datanew,
int threshold, int detectwindow, int resetwindow)
{
if (FirstTimeEntryFlag != 0)
{
//Convert values to use inbuilt Math library
double Xold = Convert.ToDouble(dataold.TiltX);
double Yold = Convert.ToDouble(dataold.TiltY);
double Zold = Convert.ToDouble(dataold.TiltZ);
double X = Convert.ToDouble(datanew.TiltX);
double Y = Convert.ToDouble(datanew.TiltY);
double Z = Convert.ToDouble(datanew.TiltZ);
//Set new values to old
dataold.TiltX = datanew.TiltX;
dataold.TiltY = datanew.TiltY;
dataold.TiltZ = datanew.TiltZ;
//Calculate Euclidean distance between old and data points
double EuclideanDistance = Math.Sqrt(Math.Pow(X - Xold, 2)
+ Math.Pow(Y - Xold, 2)
+ Math.Pow(Y - Yold, 2));
//Set shake to true if distance between data points is
//greater than the defined threshold
if (EuclideanDistance > threshold)
{
DetectWindowCount++;
if (DetectWindowCount > detectwindow)
{DetectWindowCount = 0; ResetWindowCount = 0; return true;}
}
else
{
ResetWindowCount++;
if (ResetWindowCount > resetwindow)
{DetectWindowCount = 0; ResetWindowCount = 0; return false;}
}
}
//No longer the first run.
FirstTimeEntryFlag = 1;
return false;
}

Windows Mobile のキーおよびキー コード

Windows Mobile デバイスには、キー入力のシミュレーションを行い、最小限のプログラミングで実行できる非常に高度な操作が多数あります。ホーム画面を表示してみたいですか。電話に応答したり、電話器をロックしてみたいですか。これら 3 つの操作には、すべて同じコマンドを使用することができます。Windows Mobile では 1 つのデバイスを管理して、キーボードのモデルとは無関係に、さまざまなキーボードをサポートできるようにしています。最も低いレベルでは、キーボードの各キーを押して放すと、スキャン コードが生成されます。スキャン コードとは、キーを識別する、ハードウェアに依存する値です。キーボード ドライバーは、このスキャン コードを仮想キー コードに変換またはマップします。仮想キー コードとは、キーを識別する、ハードウェアに依存しない 16 進数値です。

const int KEYEVENTF_KEYUP = 0x02;
const int KEYEVENTF_KEYDOWN = 0x00;
keybd_event(VK, 0, KEYEVENTF_KEYDOWN, 0);
keybd_event(VK, 0, KEYEVENTF_KEYUP, 0);

電話に応答するコードは次のとおりです。

byte VK = 0x72; // Simulate answer phone call being pushed.

デバイスをロックするコードは次のとおりです。

byte VK = 0x85; // Simulate phone being locked.

ホーム ページに移動するコードは次のとおりです。

byte VK = 0x5B; // Simulate home key being pressed.

詳細については、msdn.microsoft.com/en-us/library/bb431750.aspx (英語) を参照してください。

図 4 からお分かりのように、距離の変化が大きくて、(最も重要なのことは) その変化が持続している場合に、電話器がシェイクされている状態だと判断します。図 5 は、対応するセンサーの値を示しています。電話器は画面を上に向けてシェイクされたことがわかります。TiltX の変化は、電話器をシェイクする際に手首をひねることで生じています。これに対して、シェイクしているときも水平方向にはあまり動かされていないため TiltY の変化はあまりありません。図 6 のコードは、ユークリッド距離の計算と、電話がシェイクされたタイミングを判断する方法を示しています。

このアルゴリズムには、ここで触れておく価値がある機能がもう 1 つ含まれています。それは、カウントをリセットする機能です。これは複数の連続する変化値が必要な場合だけでなく、2 つの大きい値の後に小さい値、そして別の大きい値と続く連続する値 (1 つの小さい値によって区切られた、3 つの連続する大きい値) を検出するような場合にも便利だとわかりました。このようなシナリオに対処するため、ここでは 1 つではなく 2 つのカウンターを使用しました。1 つ目のカウンターは、ユークリッド距離がしきい値を上回っている回数をカウントします。2 つ目のカウンターは、大きな変化が 1 つ検出された時点で、リセット カウントがゼロに設定されます。その後、最後の大きな変化の後に、特定の数値の大きな変化が連続して検出されなければカウント全体をリセットできます。これで、電話器がシェイクされたかどうかを判断するための基準ができたので、この情報を使用して行う処理と、何を制御できるのかを理解する必要があります。

Shake 'n Skip ビデオの視聴

Shake 'n Skip アプリケーションの動作を確認するには、msdn.microsoft.com/magazine/ (英語) にある、この記事のオンライン バージョンで利用可能なビデオを視聴することができます。

システム コンポーネント - トラックのスキップ

前述したように、Windows フォーム アプリケーションを構築する理由の 1 つは、可能な限りアプリケーションを再利用できるようにすることです。この簡潔さを保持するため、ここでは特定の技法を使用して Windows Mobile メディア プレーヤーを操作しています。このメディア プレーヤーには、多数のアクティビティを実現するために使用できる、キーパッド コントロールのシミュレーションを行う機能があります (「Windows Mobile のキーおよびキー コード」をご覧ください)。System Diagnostic 名前空間を使用することでこの技法が組み込まれ、アプリケーションが開始され、Windows Mobile デバイスのビューの最前面に表示されるようになります。アプリケーションを開始して、デバイス上でキー入力のシミュレーションを行うコードは次のとおりです。

public void KeyCommandForApplication(string FileName, byte VK)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = FileName;
p.Start();
const int KEYEVENTF_KEYUP = 0x02;
const int KEYEVENTF_KEYDOWN = 0x00;
keybd_event(VK, 0, KEYEVENTF_KEYDOWN, 0);
keybd_event(VK, 0, KEYEVENTF_KEYUP, 0);
}

まとめ

ここで使用したアルゴリズムはやや単純すぎ、改善の余地があると考えています。ただし、限定的なテストであれば適切に機能します。モバイル アプリケーションの開発では、加速度計の利用は増加する傾向にあります。加速度計をモバイル GPSで使用すると (msdn.microsoft.com/ja-jp/magazine/dd315419.aspx を参照してください)、軽量のセンサーなどの入力を使って、新しい優れたアプリケーション操作が可能になります。

HTC の加速度計を使用する際、すばらしいページとコードで支援してくれた Scott Seligman と Koushik Dutta に感謝します。また、ビデオの見栄えを良くしてくれた bexmedia.net (英語) の Craig にも感謝します。

Chris Mitchell は、機械学習と音楽信号および音声信号処理の博士号を取得した後、カウフマン財団と NCGE に所属していました。現在は英国のケンブリッジで新興企業の設立に携わっています。連絡先は、chris.mitchell@anglia.ac.uk (英語のみ) です。