2010年11月29日月曜日

傾き、加速度の手振れノイズをフィルターする

人間の手の震えもありますが、端末で取れる加速度センサーの値は、結構細かく震えています。
この加速度センサーの値をもとにLinearで中心にあるボールを動かしたかったのですが、この震えのせいで、
きれいに移動しないで、ぴょこぴょこと跳ねているような動きに見えてしまうので困りました。

センサーの精度かと思って、そちらを調査してみたり、
アニメーションの処理飛びなのかと思ってログを見てみたりしていたのですが、
特に解決策は見つからず。

結局のところこのブレを減らすための解決策が必要だろうと推測して、ノイズフィルターを探していたところ身近なところに解決策がありました。

DJミキサーでよくある、ハイパスフィルターとローパスフィルターです。

ハイパスフィルターは、つまみを回すとだんだんシャキシャキの音になってくあれですw。
ローパスフィルターは、つまみを回すとだんだんモコモコの音になってくあれですw。


WikiPedia ローパスフィルター(ハイカット)
ローパスフィルタ(Low-pass filter: LPF)はフィルタ回路の一種で、低周波を良く通し、ある遮断周波数より高い周波数の帯域を通さない(減衰させる)フィルタである。

WikiPedia ハイパスフィルター(ローカット)
ハイパスフィルタ(High-pass filter: HPF)はフィルタ回路の一種で、高周波を良く通し、遮断周波数より低い周波数の帯域を通さない(減衰させる)フィルタである。 日本語では「高域通過濾波器」とも言われる。また英語では「low-cut filter」とも言われ、これは「bass-cut filter」または「rumble filter」としてオーディオ機器などで使用される。


加速度は、重力加速度+手振れなどの実際の端末の動きが加速度が影響してきます。
あるXYZで考えてどれか一方向についての変化
重力加速度の変化:端末の大体の傾きになりますので周波数で考えるとだんだん傾いていれば低い周波数です。
端末の動きの変化:手振れなので重力加速度の変化より高い周波数のノイズになります。

ぴょこぴょこと動いてしまうのは、「端末の動きの変化」が影響しているので、高い周波数をカットすればいい。すなわち低い周波数だけ通せばいいということになります。

ってことで、ロジック。
一つ前の値と比較しながら、値を取ります。
XYだけの例です。

部分的に抜き出すとわけわからなくなったので、全部載せます。

イメージを最初に真ん中において、ローパスフィルタをかけた加速度で、イメージを移動しています。

package jp.mediba.android.xxxxxx;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.util.Log;
import android.widget.ImageView;

public class PointViewOverlay extends ImageView {

 private Matrix mMatrix = new Matrix();
 private Matrix mSavedMatrix = new Matrix();
 private float[] currentOrientationValues = {0.0f, 0.0f};
 
 private PointF startPoint = new PointF();
 
 private ImageView imageView;
 private int frameWidth = 0;
 private int frameHeight = 0;
 
 public PointViewOverlay(Context context) {
  super(context);
  // TODO 自動生成されたコンストラクター・スタブ
 }

 public void setImageView(ImageView imageView) {this.imageView = imageView;}
 public void setFrameSize(int frameWidth, int frameHeight) {
  this.frameWidth = frameWidth;
  this.frameHeight = frameHeight;
 }
 
 public void resetStartPoint() {
  mMatrix.set(mSavedMatrix);
  this.startPoint = new PointF((float)this.frameWidth/2,(float)this.frameHeight/2);
  mMatrix.postTranslate(this.startPoint.x, this.startPoint.y);
  imageView.setImageMatrix(mMatrix);
  Log.v("resetStartPoint.",String.valueOf("X:" + this.startPoint.x) + "Y:" + String.valueOf(this.startPoint.y));
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // TODO 自動生成されたメソッド・スタブ
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 }

 public void animateView( SensorEvent event ) {
  
  if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

   // Lowpass Filter ハイカット これ!
   currentOrientationValues[0] = event.values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
   currentOrientationValues[1] = event.values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);

   // 重力加速度
   float accX = currentOrientationValues[0];
   float accY = currentOrientationValues[1];

   // 横画面なのでちょっと大きめにしました。
   float moveX = Math.round(accY*48);
   float moveY = Math.round(accX*32);

   float pointX = this.startPoint.x+moveX;
   float pointY = this.startPoint.y+moveY;
   
   mMatrix.set(mSavedMatrix);
   mMatrix.postTranslate(pointX,pointY);
   
   imageView.setImageMatrix(mMatrix);
   Log.v("animateView","pointX,pointY"+String.valueOf(pointX)+","+String.valueOf(pointY));
   
  }
 }
 
}

2010年11月16日火曜日

DroidDraw Android LayoutXMLのWYSIWYGエディタ

http://www.droiddraw.org/
http://code.google.com/p/droiddraw/

機能を完全には使っていないのですが、レイアウトがWYSIWYGでDrag&Dropできて便利です。
Mac OS X, Windows, Linuxのマルチプラットフォームサポートしています。
使い方さえ覚えればもしかするとデザイナさんでも一人でUI作れるかも?

ADTについているレイアウトエディタだとRelativeLayoutの中に入れた、2つのLinearLayoutを左右にふって上合わせにするプロパティーがどこにあるか見つけにくい(ってかあるのか?)のですが、それをマウス操作で上にあわせたり下にあわせたりできます。
(* (ADT) Android Development Tools for the Eclipse IDE )

これだけで、非常にストレスが減る!
Eclipseと連携して操作する所まで入っていないのですが、ファイルの場所を合わせればリソースファイルも読めるようです。

出力は、.apkパッケージで出力したり、XML単体で出力したりできます。
GUIで要素のIDを付ける操作は、いまいち使い勝手が悪いので、ひと通りの配置まで決めたあとは、XMLをEclipseのADTに貼りつけて、名前を付け直したりしています。

どう書いたらうまく並べられるのかの勉強替わりにもなりますね。
ひと通り慣れたら、プロパティーが覚えられそうなのでHTMLのようにタグがきができるようになれるような気がしています。

一点分かりにくいのが、Layout要素を入れ子にすると、外を囲むレイアウトがなんだったかが、GUI上から見えにくいところかな。

でも何にせよ使う利点は十分にあります。

MoonGiftの紹介記事

2010年11月15日月曜日

XCodeとInterfaceBuilderで10分でモックアプリを作る方法(3)

前回の続き。
全部で5回の予定です。
おそらく5回書き終わってからでないとあまり意味のない記事になっちゃいました…。
でも頑張ります。

では、ボタンを押したら画面遷移する動作を紐付けていきましょう。

まずは、XCodeの左側のツリーから、一番最初の画面の .xib ファイルをダブルクリックして、IB(InterfaceBuilderを立ち上げます。)

立ち上げると、こんな状態です。

1.動作の名前を決める

まず、メインの画面にボタンを押したときの操作を定義します。
先に、それぞれのボタンを押したときの操作を決めておきましょう。

3つボタンがあるので、こんな名前にしてみました。


ヘルプへ = changeViewHelp
設定へ = changeViewSetting
実行へ = changeViewMain

2.「File's Owner」に動作の名前を定義する。
これらを、IBをつかって定義していきます。
これからウィンドウを行ったり来たりするので混乱しないように。www
Document 画面の「File's Owner」をクリックします。



そうすると、右側の「インスペクタ」でこの画面に対するいろいろな設定ができるようになります。が、ここではまだちゃんとは使いません。w
上部にタブが4つ見えると思いますが、( i ) の所を選択してください。
その内容の一番上の「Class」のところに 「NavigationTestViewController」という名前が出ていると思います。これが、この画面の操作の統括をするプログラムの名前です。
(対応して、XCodeの中にも同じ名前のxxx.h xxx.mというファイルがあります。)





それを確認したら、「Class」のボックスにある、矢印をクリックしてください。
そうすると今度は、左側の「Library」のWindowの下側に、 「NavigationTestViewController」というボックスが現れます。


このプルダウンを「Actions」に合わせてください。

その状態で、「+」のボタンが下に現れます。

これをクリックして、3つのボタン操作を定義します。

こんな感じに。



3.「File's Owner」に動作の名前を定義したものを保存する。

これを、実際のファイルに保存します。
下のプルダウンから、「Write Updated Class File」を選択してください。

保護んウィンドウがでるのでこれで、Saveを押します。


もうあるけど、どうする?って聞かれますので「Replace」を押してください。
(置き換えられちゃいます。w まだファイルにはなにもかいていないので、大丈夫。)


4.ボタンの動作を「File's Owner」につなげる。

次に、それぞれのボタンを押した操作を紐付けます。
まずに、「実行へ」のボタンを、Mainの画面機能につなげます。

IBで「実行へ」のボタンをクリックして触ってください。
そうすると、右側の「インスペクタ」でボタンに対するいろいろな設定ができるようになります。




ここで、右側の「Connections」のタブを表示します。
ここには、ボタンの動作がすでに定義されており、押したら、どうとか、おして、指が離れたらどうとか、いろいろつなぐことができます。



ここでは、押して、指が離れたら、画面遷移するということにして、「Touch Up Inside」を使います。
これの右側にある◯をクリックし、ドラッグして、「Document」ウィンドウの「File's Owner」につなげます。「File's Owner」にまで、持って行ってマウスを外します。


と、グレーのサブウィンドウが現れるので、「実行へ」に対応する
changeViewMainをクリックします。



ちゃんとつながると、「Connections」の画面がこんなふうになります。



この状態で、.xibファイルを保存してください。

続いて、XCodeに戻り、画面遷移のプログラムを書きます。
そろそろ時間切れなので、また次…。中途半端でごめんなさい。


10分は無理だなこれ。www 
スゲー速さで作業してYoutubeではや回しビデオでもアップして帳尻合わせやろうかと思ってきました。wwww

2010年11月12日金曜日

師匠からの課題

Android Widgetからダイアログを出す。w

2010年11月11日木曜日

アプリの起動時に、着信音量とMedia音量をあわせる

メタルメガネ(Metal Megane)の「森羅万象おみくじうらない」ですが、Android版で音が出るようになりました。
着信音の音量に合わせて音量コントロールしていたら音楽がイヤフォンと本体の両方から出るようになってしまいました。

さすがにこれは、iPhoneユーザー的にはありえないなーと思ったのですが、Androidでは結構浸透している仕様のようですね。

ということで、これを避けるために、AudioManagerのストリームをAudioManager.STREAM_MUSICに、再度変更したのですがこうすると今度は、着信音量をオフにしていても、アプリが立ち上がると音量が、音楽再生用の音量として出ます。

着信音量をOFFにして、音楽を聞いたときに、音量をUPすると、
AudioManager.STREAM_MUSICとして効果音再生しているアプリは効果音が再生されてしまいます。

さすがにこれだと、iPhoneから移行してきたユーザーや、ガラケーから移行してきたユーザーはビックリするんじゃないかということで、アプリの起動時には、着信音量が0だったら、音楽再生音量を0にするという処理を入れました。

ということで、ソースコード。

この実装は、android.app.Applicationクラスのサブクラスとして、onCreate時に呼び出しています。

// Audio Managerを取得
AudioManager mAudio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// 着信の音量を取得
int vol = mAudio.getStreamVolume(AudioManager.STREAM_RING);

// 着信の音量が0だったら
if ( vol == 0 ) {
 // 音楽の再生音量を同じく0に。
 mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, vol, AudioManager.FLAG_SHOW_UI);
}