レベル: 中級 Frank Ableson, Software designer
2009年 6月 16日 Android はアプリケーション開発のためのリッチなプラットフォームであり、魅力的なユーザー・インターフェース要素とデータ管理機能を備えています。また Android はハードウェアとのインターフェースも豊富に提供しています。この記事では、Android が持つさまざまなセンサーとのインターフェースを利用して、環境をモニタリングする方法を学びます。サンプル・コードでは、Android を搭載した携帯電話で音声を録音する方法を説明します。独自のベビー・モニターを作成したい場合や、自分の声によって携帯電話や家のロックを解除したい場合などのために、Android を搭載した機器のハードウェア機能を活用する方法を学びましょう。
はじめに
Android プラットフォームは、ハードウェア・センサーを利用して革新的なアプリケーションを作成するには理想的なプラットフォームです。特に Java™ で開発する場合は理想的です。この記事では、Android のセンサー・サブシステムの使い方や音声の録音方法など、Android アプリケーションで利用できるさまざまなインターフェースのいくつかを学びましょう。
皆さんは Android を搭載した機器のハードウェア機能を活用して、どのようなタイプのアプリケーションを作りたいでしょうか。Android 搭載機器を人間の目や耳の代わりとして使うアプリケーションならば、どれも候補として適当です。ベビー・モニター、セキュリティー・システム、さらには地震計なども頭に浮かびます。形而上学的には、皆さん自身が同時に 2 つの場所にいることはできませんが、そのギャップを Android が何らかの現実的な方法で埋めてくれるかもしれません。この記事では、Android 機器は単なる「携帯電話」とは限らず、ある固定した場所に設置され、無線 (EDGE や WiFi など) でネットワークに接続された機器の場合もあることを念頭に置いてください。この記事で紹介するサンプルのソース・ファイルは「ダウンロード」セクションから入手することができます。
Android のセンサー機能
プラットフォームとして Android を使用することによる目新しい点は、機器そのものの中にある「いいもの」にアクセスできることです。これまでモバイル機器のソフトウェア開発者にとって不満だったのは、機器に搭載されているハードウェアにプラットフォームを介してアクセスできないことでした。Android の Java 環境はそうしたハードウェアと皆さんとの間に相変わらず存在していますが、Android 開発チームはハードウェア機能の大部分を表に出してくれたのです。このプラットフォームはオープンソースであるため柔軟であり、皆さんは独自の課題を実現するコードの作成に本気で取り組むことができます。
まだ Android SDK をインストールしていない場合には、この SDK をダウンロードしてインストールする必要があります。そうしておけば、android.hardware パッケージの内容に目を通しながら、この記事で説明する例を追っていくことができます。また android.media パッケージには便利で斬新な機能を提供するクラスが含まれています。
ハードウェアを扱うために Android SDK で提供されている機能 (クラス) の一部を以下に挙げます。
表 1. ハードウェアを扱うために Android SDK で提供されている機能 (クラス)
| 機能 (クラス) | 説明 |
|---|
android.hardware.Camera | アプリケーションでカメラとインターフェースをとれるようにするためのクラスです。写真の撮影、プレビュー画面用の画像の取得、カメラの動作を指定するために使用したパラメーターの変更などをすることができます。 | android.hardware.SensorManager | Android プラットフォーム内で利用できるセンサーにアクセスするためのクラスです。Android を搭載したすべての機器が、SensorManager の中にあるすべてのセンサーをサポートするわけではありませんが、その可能性を考えると非常に興味がそそられます。(利用可能なセンサーについては、後ほど簡単に説明します。) | android.hardware.SensorListener | センサーの値の更新をリアルタイムで受信しようとするクラスが実装するインターフェースです。このインターフェースを実装するアプリケーションは、そのハードウェアで利用できる 1 つ以上のセンサーをモニタリングすることができます。例えば、この記事で紹介するコードには、このインターフェースを実装したクラスが含まれており、機器が向いている方向と、その機器の中に組み込まれた加速時計をモニタリングすることができます。 | android.media.MediaRecorder | メディアのサンプルを記録するためのクラスです。このクラスは、特定の場所 (託児所など) での音声アクティビティーを記録する際に便利です。また音声クリップを分析することで、アクセス制御を行うアプリケーションやセキュリティーが要求されるアプリケーションでの識別に使用することもできます。例えば、タイムシェアを利用する際 (訳注: タイムシェアとは、リゾート物件等の不動産を共同で所有し、ある特定の期間その物件を利用できるシステムおよびその物件のこと。欧米を中心に見られる。)、管理会社のところに行って鍵を受け取る代わりに声でドアを開けられると便利です。 | android.FaceDetector | ビットマップ画像上で人の顔の簡単な認識ができるクラスです。顔以上に個人をよく表すものはありません。機器をロックする手段としてこのクラスを利用すると、パスワードを覚えておく必要がなくなります。つまり携帯電話で生体認証ができるようになります。 | | android.os.* | このパッケージには、動作環境とインターフェースをとる上で便利なクラスが含まれています (電力管理クラス、ファイル監視クラス、ハンドラー・クラス、メッセージ・クラスなど)。多くのポータブル機器の場合と同様、Android を搭載した携帯電話は大量の電力を消費します。適切な時刻になる度に、機器が「起きた」状態になって対象のイベントをモニタリングできるように、あらかじめ設計の一側面として検討しておく必要があります。 | java.util.Date
java.util.Timer
java.util.TimerTask | 実際にイベントを扱う際には、日付と時刻が重要であることが多いものです。例えば、java.util.Date クラスを使うと、ある特定のイベントが発生したり、特定の状況になったりした時のタイムスタンプを取得することができます。また java.util.Timer や java.util.TimerTask を使うと、それぞれ定期的に行う処理やある特定の時点で行う処理を実行することができます。 |
android.hardware.SensorManager には、Android のセンサー・システムのさまざまな側面を表現する、いくつかの定数が含まれています。その一部を下記に挙げます。
- センサーのタイプ
- 方向、加速度、光、磁界、距離、温度、など。
- サンプリング・レート
- 最高速、ゲーム用、通常、ユーザー・インターフェース用。特定のサンプリング・レートをアプリケーションが要求した場合、そのサンプリング・レートはセンサー・サブシステムにとって単なる参考や提案にしかなりません。特定のレートが利用可能であるという保証はありません。
- 精度
- 高、低、中、信頼できない。
SensorListener インターフェースはセンサー・アプリケーションの中心となるものです。このインターフェースには以下の 2 つの必須メソッドが含まれています。
onSensorChanged(int sensor,float values[]) メソッドはセンサーの値が変更されるごとに呼び出されますが、呼び出されるのはそのセンサーをこのアプリケーションがモニタリングしている場合のみです (これについては以下でさらに説明します)。このメソッドの引数には、変化したセンサーを識別するための整数と、センサーのデータ自体を表現する浮動小数点数の値の配列があります。センサーによっては、データ値を 1 つだけ提供するものもあれば、浮動小数点数の値を 3 つ提供するものもあります。方向センサーと加速度センサーは、それぞれ 3 つのデータ値を提供します。
onAccuracyChanged(int sensor,int accuracy) メソッドはセンサーの精度が変更されると呼び出されます。引数は 2 つの整数です (1 つはセンサーを表し、もう 1 つはそのセンサーの新しい精度を表します)。
センサーとやり取りするためには、アプリケーションが 1 つまたは複数のセンサーに関連するアクティビティーをリッスンするように、登録を行う必要があります。登録は SensorManager クラスの registerListener メソッドを使って行います。この記事のコード・サンプルを見ると、アプリケーションが SensorListener を登録、登録解除する方法がわかるはずです。
SDK の中に定義されているすべてのセンサーが、Android を搭載するすべての機器でサポートされているわけではないことを思い出してください。特定の機器で特定のセンサーが利用できない場合、アプリケーションはそれに応じて最低限の機能を提供できる必要があります。
センサーの例
このサンプル・アプリケーションは、単純に方向センサーと加速度センサーの変化をモニタリングします (「ダウンロード」セクションからソース・コードを入手することができます)。変化が受信されると、TextView ウィジェットの画面上にセンサーの値が表示されます。このアプリケーションの実際の動作を図 1 に示します。
図 1. 加速度と方向をモニタリングする
このアプリケーションは、Android Developer Tools プラグインをインストールした Eclipse 環境を使って作成されたものです。(Eclipse での Android アプリケーションの開発についての詳細は「参考文献」を参照してください。) リスト 1 は、このアプリケーションのコードを示しています。
リスト 1. IBMEyes.java
package com.msi.ibm.eyes;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.SensorListener;
public class IBMEyes extends Activity implements SensorListener {
final String tag = "IBMEyes";
SensorManager sm = null;
TextView xViewA = null;
TextView yViewA = null;
TextView zViewA = null;
TextView xViewO = null;
TextView yViewO = null;
TextView zViewO = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get reference to SensorManager
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
setContentView(R.layout.main);
xViewA = (TextView) findViewById(R.id.xbox);
yViewA = (TextView) findViewById(R.id.ybox);
zViewA = (TextView) findViewById(R.id.zbox);
xViewO = (TextView) findViewById(R.id.xboxo);
yViewO = (TextView) findViewById(R.id.yboxo);
zViewO = (TextView) findViewById(R.id.zboxo);
}
public void onSensorChanged(int sensor, float[] values) {
synchronized (this) {
Log.d(tag, "onSensorChanged: " + sensor + ", x: " +
values[0] + ", y: " + values[1] + ", z: " + values[2]);
if (sensor == SensorManager.SENSOR_ORIENTATION) {
xViewO.setText("Orientation X: " + values[0]);
yViewO.setText("Orientation Y: " + values[1]);
zViewO.setText("Orientation Z: " + values[2]);
}
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
xViewA.setText("Accel X: " + values[0]);
yViewA.setText("Accel Y: " + values[1]);
zViewA.setText("Accel Z: " + values[2]);
}
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
}
@Override
protected void onResume() {
super.onResume();
// register this class as a listener for the orientation and accelerometer sensors
sm.registerListener(this,
SensorManager.SENSOR_ORIENTATION |SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onStop() {
// unregister listener
sm.unregisterListener(this);
super.onStop();
}
}
|
このアプリケーションは通常のアクティビティー・ベースのアプリケーションとして作成されていますが、それはこのアプリケーションが単にセンサーから取得したデータで画面を更新しているにすぎないからです。機器がフォアグラウンドで他のアクティビティーを実行している可能性があるアプリケーションでは、サービスとしてアプリケーションを作成した方が適切です。
アクティビティーの onCreate メソッドは、センサー関連のすべての関数を管理する SensorManager への参照を取得します。また onCreate メソッドは、センサー・データの値の更新に必要な 6 つの TextView ウィジェットへの参照も確立しています。
onResume() メソッドは、次のように SensorManager への参照を使用して、registerListener メソッドからセンサーに対して更新情報を登録します。registerListener メソッドに渡される引数は以下のとおりです。
- 1 番目の引数は
SensorListener インターフェースを実装するクラスのインスタンスです。
- 2 番目の引数は対象とするセンサーのビットマスクです。この場合では、アプリケーションは
SENSOR_ORIENTATION と SENSOR_ACCELEROMETER からのデータを要求しています。
- 3 番目の引数は、このアプリケーションがどの程度素早くセンサー値を更新する必要があるかをシステムに知らせるためのヒントです。
アプリケーション (アクティビティー) が中断された場合にはリスナーを登録解除し、それ以降はセンサーの更新を受信しないようにする必要があります。登録解除には SensorManager の unregisterListener メソッドを使います。このメソッドの引数は SensorListener のインスタンスのみです。
registerListener メソッド、unregisterListener メソッドのどちらを呼び出す場合にも、アプリケーションは this キーワードを使います。クラス定義の中の implements キーワードに注目してください。このクラスが SensorListener インターフェースを実装していることを宣言しています。そのため、registerListener と unregisterListener に this を渡しているのです。
SensorListener は onSensorChange と onAccuracyChanged という 2 つのメソッドを実装する必要があります。このサンプル・アプリケーションでは実際にはセンサーの精度を気にせず、センサーの現在の X、Y、Z の値を関心対象としています。onAccuracyChanged メソッドは基本的に何もせず、呼び出されるたびにログ・エントリーを追加するだけです。
onSensorChanged メソッドが常に呼び出されているように見えますが、これは加速度センサーと方向センサーが次々とデータを送信し続けているためです。1 番目のパラメーターを調べ、どのセンサーがデータを送信しているのかを判断します。データを送信しているセンサーを特定できたら、このメソッドへの 2 番目の引数として渡された浮動小数点数の値の配列の中に含まれているデータを使用して、適切な UI 要素を更新します。この例ではそうした値を単純に表示していますが、より高度なアプリケーションでは、値を分析したり、前の値と比較したり、あるいは何らかのパターン認識アルゴリズムを実行し、ユーザー (または外部環境) が何をしているのかを判断することもできます。
ここまででセンサー・サブシステムを詳しく調べたので、次のセクションでは Android を搭載した携帯電話で音声を記録するコード・サンプルを検証します。このサンプルは DEV1 という開発機器上で実行されたものです。
MediaRecorder を使う
android.media パッケージには、メディア・サブシステムとやり取りするためのクラスが含まれています。android.media.MediaRecorder クラスは、音声や映像など、メディアのサンプルを取得するために使われます。MediaRecorder はステート・マシンとして動作します。MediaRecorder を使うには、ソース機器やフォーマットなど、さまざまなパラメーターを設定する必要があります。それらの設定が終わると、記録を開始することができ、その後停止されるまで任意の期間、記録が行われます。
リスト 2 には Android 機器で音声を録音するためのコードが含まれています。下記のコードにはアプリケーションの UI 要素は含まれていません (完全なソース・コードは「ダウンロード」セクションを参照してください)。
リスト 2. 音声を記録する
MediaRecorder mrec ;
File audiofile = null;
private static final String TAG="SoundRecordingDemo";
protected void startRecording() throws IOException
{
mrec.setAudioSource(MediaRecorder.AudioSource.MIC);
mrec.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
if (mSampleFile == null)
{
File sampleDir = Environment.getExternalStorageDirectory();
try
{
audiofile = File.createTempFile("ibm", ".3gp", sampleDir);
}
catch (IOException e)
{
Log.e(TAG,"sdcard access error");
return;
}
}
mrec.setOutputFile(audiofile.getAbsolutePath());
mrec.prepare();
mrec.start();
}
protected void stopRecording()
{
mrec.stop();
mrec.release();
processaudiofile(audiofile.getAbsolutePath());
}
protected void processaudiofile()
{
ContentValues values = new ContentValues(3);
long current = System.currentTimeMillis();
values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gpp");
values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
ContentResolver contentResolver = getContentResolver();
Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri newUri = contentResolver.insert(base, values);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}
|
startRecording メソッドの中では、以下のように MediaRecorder のインスタンスがインスタンス化され、初期化されています。
- 入力ソースがマイク (
MIC) に設定されます。
- 出力フォーマットはモバイル機器用のメディア・フォーマット、3GPP (*.3gp ファイル) に設定されます。
- エンコーダーは
AMR_NB (サンプリング周波数が 8 KHz の音声フォーマット) に設定されます。NB は狭帯域 (Narrow Band) を表します。SDK のドキュメントには、さまざまなデータ・フォーマットと利用可能なエンコーダーについての説明があります。
音声ファイルは内部メモリーに保存されるのではなく、ストレージ・カードに保存されます。External.getExternalStorageDirectory() がストレージ・カードの場所を示す名前を返すと、そのディレクトリーに一時ファイルが作成されます。この一時ファイルは、setOutputFile メソッドが呼び出されると MediaRecorder インスタンスに関連付けられます。音声データはこのファイルに保存されます。
prepare メソッドを呼び出すと MediaRecorder の初期化が終了します。録音プロセスを開始する準備が整ったら、start メソッドを呼び出します。ストレージ・カード上の一時ファイルに音声が録音され、stop メソッドが呼び出されるまで録音され続けます。release メソッドによって、MediaRecorder インスタンスに割り当てられたリソースが解放されます。
音声サンプルを録音できると、以下のようないくつかのアクションを実行することができます。
- その音声を機器上のメディア・ライブラリーに追加する。
- パターン認識ステップを実行し、以下のようにその音声を識別する。
- 音声が赤ちゃんの泣き声かどうか。
- 音声が所有者の声で、携帯電話のロックを解除する必要があるかどうか。
- 音声が秘密の入り口の鍵を開ける「開けゴマ」のような合言葉かどうか。
- この音声ファイルをネットワーク上の場所に自動的にアップロードして処理を行う。
このコード・サンプルでは、processaudiofile メソッドによってメディア・ライブラリーに音声が追加されます。「インテント」を使用することで、機器上のメディア・アプリケーションに新しいコンテンツが利用可能になったことを通知します。
このコード・スニペットに関する最後の注意として、このコードを実行しても最初は音声が録音されません。ファイルは作成されますが、そのファイルには音声が入っていません。そこで以下のように AndroidManifest.xml ファイルにパーミッションを追加する必要があります。
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> |
ここまでの時点で、Android によるセンサーとのやり取り、そして音声の録音について少し学びました。次のセクションでは、データ収集およびレポート・システムに関するアプリケーションのアーキテクチャーの概要について説明します。
センサー・プラットフォームとしての Android
Android プラットフォームには、環境をモニタリングするための豊富なセンサーの選択肢があります。こうした豊富な入力 (外界からの刺激) の選択肢を、優れた計算処理機能およびネットワーキング機能と組み合わせると、Android は実用的なシステムを構築するための魅力的なプラットフォームとなります。図 2 は、入力 (Inputs)、アプリケーション・ロジック (Logic)、通知方法または出力 (Actions) の間の関係を単純化したものです。
図 2. Android を中心とするセンサー・システムのブロック図
このアーキテクチャーには柔軟性があります。アプリケーション・ロジックを分割し、ローカルの Android 機器と、大規模なデータベースや計算処理能力を活用できるサーバー・サイドのリソースに分けて配置することができます。例えば、ローカルの Android 機器に録音された音声トラックを POST を使って Web サーバーに送信し、その録音データを Web サーバー上で声のパターンのデータベースと比較することができます。もちろん、これはさまざまな可能性のうちの、ごく一部にすぎません。皆さんが、携帯電話以外のアプリケーションのプラットフォームとして Android を活用する方法を調べてみる気になったようであれば幸いです。
まとめ
この記事では Android のセンサーについて紹介しました。サンプル・アプリケーションでは、方向と加速度を測定し、MediaRecorder クラスを使って録音機能の操作を行いました。Android は実用的なシステムを構築するための柔軟で魅力的なプラットフォームです。Android の世界は急速に成熟しつつあり、また成功しつつあります。このプラットフォームから目を離さないでください。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| The Eyes source code | os-android-sensorEyes.zip | 28KB | HTTP |
|---|
| IBMAudio source code | os-android-sensorIBMAudio.zip | 33KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
- Android SDK をダウンロードしてください。
- 最新の Eclipse IDE をダウンロードしてください。
- 皆さんの次期オープンソース開発プロジェクトを IBM ソフトウェアの試用版を使って革新してください。ダウンロード、あるいは DVD で入手することができます。
- IBM 製品の試用版をダウンロードするか、あるいはオンラインで IBM SOA Sandbox を試し、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。
議論するために
著者について  | |  | Frank Ableson はニュージャージー州北部の起業家、ソフトウェア開発者であり、モバイルおよび組み込みアプリケーション・ソフトウェアを専門としています。彼は現在、Android によるアプリケーション開発に関する本を執筆中です。この本は Manning Publications から出版される予定です。彼は組み込みシステムとワイヤレス通信、そしてカー・エレクトロニクスにプロとしての関心を持っています。彼の最大のファンは、彼の妻の Nikki と彼らの子供達です。 |
記事の評価
|