前の「Androidで音声録音して、Webで公開してみる」では、指を使ったジェスチャで制御してましたが、

今回は、振って(シェイク)制御してみます。

こんなことして何が面白いのか..というと、片手で操作できます..そんだけのことかも(^^)。



加速度センサーを使ったシェイク検出の基本コードはこの方(明日の鍵)のを参考にさせていただきました。

検出から実際の制御を行うコードを記述してみます。

このページの最後に、シェイク動作のテストというかトレーニング用アプリをつけておきました。


Androidで音声録音して、Webで公開してみる



概略

流れはこんな感じ。





1:スマホで録音して、サーバーにアップロード

2:知り合いにIDを知らせる

3:公開ID(Friend's name)を使ってファイルをダウンロードして、再生



スマホ(Android)の識別番号をID(Friend's name)に使用します。

確認するには、電話を起動して以下のように入力すれば、表示されます。







TOP

コード(抜粋)

加速度センサーを使ったシェイク検出コード(ShakeListener.java)

元ネタ(明日の鍵)

import java.util.Arrays;
import java.util.LinkedList;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

import android.util.Log;

public class ShakeListener implements SensorEventListener {
public static final String LOG_TAG = ShakeListener.class.getSimpleName();
    
    
    //
    
    public double houi_angle = 0;
    public double keisha_angle = 0;
    public double kaiten_angle = 0;
    
    public static final int DIRECTION_X = 0x0001;
    
    public static final int DIRECTION_Y = 0x0010;
    
    public static final int DIRECTION_Z = 0x0100;
    
    private static final int SAMPLING_SIZE = 50;
    
    public interface OnShakeListener {

        //add
        void onShaked(int direction);
    }
    
   
    private OnShakeListener mOnShakeListener = null;

    private boolean mIsCallbackAlways = false;
    
    private int mDifferenceThreshold = 500;
    
    public LinkedList<float[]> mAccelerometerList;
    
    
    private float[] mAccelerometerSamplingSums;

    public ShakeListener() {
        mAccelerometerList = new LinkedList<float[]>();
        mAccelerometerSamplingSums = new float[] {
                0f, 0f, 0f
        };
    }
    
    
    public void registerListener(SensorManager sensorManager, OnShakeListener l) {
        registerListener(sensorManager, l, SensorManager.SENSOR_DELAY_FASTEST, false);
    }
    
    public void registerListener(SensorManager sensorManager, OnShakeListener l,
            boolean isCallbackAlways) {
        registerListener(sensorManager, l, SensorManager.SENSOR_DELAY_FASTEST, isCallbackAlways);
    }
    
    public void registerListener(SensorManager sensorManager, OnShakeListener l, int rate) {
        registerListener(sensorManager, l, rate, false);
    }

    public void registerListener(SensorManager sensorManager, OnShakeListener l, int rate,
            boolean isCallbackAlways) {
        if (l == null)
            throw new IllegalArgumentException("OnShakeListener is required");

        mOnShakeListener = l;
        mIsCallbackAlways = isCallbackAlways;
        mAccelerometerList.clear();
        mAccelerometerSamplingSums = new float[] {
                0f, 0f, 0f
        };
        sensorManager.registerListener(this,
                sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), rate);
    }

    public void unregisterListener(SensorManager sensorManager) {
        sensorManager.unregisterListener(this);
        mOnShakeListener = null;
    }

    public boolean isRegisteredListener() {
        return mOnShakeListener != null;
    }

    public void setDifferenceThreshold(int differenceThreshold) {
        mDifferenceThreshold = differenceThreshold;
    }

    // @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

    // @Override
    public void onSensorChanged(SensorEvent event) {
        //Log.i("SIZE",event.values[SensorManager.DATA_X] + "");
        //add
        
        
        //
        mAccelerometerSamplingSums[0] += event.values[SensorManager.DATA_X];
        mAccelerometerSamplingSums[1] += event.values[SensorManager.DATA_Y];
        mAccelerometerSamplingSums[2] += event.values[SensorManager.DATA_Z];
        mAccelerometerList.add(Arrays.copyOf(event.values, event.values.length));
        if (mAccelerometerList.size() > SAMPLING_SIZE) {
            float[] removedValues = mAccelerometerList.removeFirst();
            mAccelerometerSamplingSums[0] -= removedValues[SensorManager.DATA_X];
            mAccelerometerSamplingSums[1] -= removedValues[SensorManager.DATA_Y];
            mAccelerometerSamplingSums[2] -= removedValues[SensorManager.DATA_Z];
        }

        float xAverage = mAccelerometerSamplingSums[0] / mAccelerometerList.size();
        float yAverage = mAccelerometerSamplingSums[1] / mAccelerometerList.size();
        float zAverage = mAccelerometerSamplingSums[2] / mAccelerometerList.size();

        float xAbsTotal = 0;
        float yAbsTotal = 0;
        float zAbsTotal = 0;
        for (int i = 0; i < mAccelerometerList.size(); i++) {
            float[] values = mAccelerometerList.get(i);
            xAbsTotal += Math.abs(values[SensorManager.DATA_X] - xAverage);
            yAbsTotal += Math.abs(values[SensorManager.DATA_Y] - yAverage);
            zAbsTotal += Math.abs(values[SensorManager.DATA_Z] - zAverage);
        }
        
        
        int direction = 0;

        if (xAbsTotal > mDifferenceThreshold)
            direction |= DIRECTION_X;

        if (yAbsTotal > mDifferenceThreshold)
            direction |= DIRECTION_Y;

        if (zAbsTotal > mDifferenceThreshold)
            direction |= DIRECTION_Z;
        
        //add
        if (mOnShakeListener != null && (mIsCallbackAlways || direction != 0))
            mOnShakeListener.onShaked(direction);
            
        
    }
}


シェイクが検出されたら、なんかを実行するコード

シェイク状態の検知終了をモニターするために、タイマーを使っています。

//Shake
import com.yyyy.xxxx.ShakeListener.OnShakeListener;//アンダーラインの部分は読み替えてください(package名)!
import android.content.Context;
import android.graphics.Color;
import android.hardware.SensorManager;

import java.util.ArrayList; 

//Timer
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
    ・
    ・
    ・
    ・

private ArrayList<String> touchList = new ArrayList<String>();
private SensorManager mSensorManager;
    private ShakeListener mShakeListener;
    
    private int sensor_X = 0;
    private int sensor_Y = 0;
    private int sensor_Z = 0;
    
    private boolean stable_X = true;
    private boolean stable_Y = true;
    private boolean stable_Z = true;
    
    private boolean shaked_X = false;
    private boolean shaked_Y = false;
    private boolean shaked_Z = false;
    
    
    //Timer
    //sensor_X,sensor_Y,sensor_Zの値を見て(0.3秒間隔)、リセット
    Timer timer1;
    private int mInterval = 300;
    private int prev_sensor_X = 0;
    private int prev_sensor_Y = 0;
    private int prev_sensor_Z = 0;
    
    ・
    ・
    ・
    ・

@Override
public void onCreate(Bundle savedInstanceState) {
    ・
    ・
    ・
    ・
    //shake
        mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        mShakeListener = new ShakeListener();
    
    
    //--------------------------------------
        //Timer
        timer1 = new Timer();
        timer1.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (prev_sensor_X == sensor_X) {
                            sensor_X = 0;
                            stable_X = true;
                        }else{
                            stable_X = false;
                            shaked_X = true;
                        }
                        prev_sensor_X = sensor_X;
                        
                        //---------------------------------
                        if (prev_sensor_Y == sensor_Y) {
                            sensor_Y = 0;
                            stable_Y = true;
                        }else{
                            stable_Y = false;
                            shaked_Y = true;
                        }
                        prev_sensor_Y = sensor_Y;
                        
                        //---------------------------------
                        if (prev_sensor_Z == sensor_Z) {
                            sensor_Z = 0;
                            stable_Z = true;
                        }else{
                            stable_Z = false;
                            shaked_Z = true;
                        }
                        prev_sensor_Z = sensor_Z;
                        
                        //---------------------------------
                        //judge
                        if ((stable_X == true)&&(stable_Y == true)&&(stable_Z == true)){
                            String res = "";
                            
                            if (shaked_X == true){
                                res += "X";
                            }
                            
                            if (shaked_Y == true){
                                res += "Y";
                            }
                            
                            if (shaked_Z == true){
                                res += "Z";
                            }
                            
                            //-----------------------
                            if (res.equals("X")){
                                if (rec_out == false) {
                                    //ToDo
                                }
                                
                            }
                            
                            if (res.equals("Z")){
                                //ToDo
                            }
                            
                            if ((res.equals("XZ"))||(res.equals("XY"))||(res.equals("XYZ"))){
                                //ToDo
                            }
                            
                            //-----------------------
                            shaked_X = false;
                            shaked_Y = false;
                            shaked_Z = false;
                        }
                        
                    }
                });
            }
        }, 0, mInterval);
        
}

@Override
public void onStart() {
    super.onStart();
    //検知の強度、500や750にすると、より強く振らないと検知されません
    mShakeListener.setDifferenceThreshold(300);
}

@Override
public void onResume() {
    super.onResume();
    mShakeListener.registerListener(mSensorManager, mOnShakeListener, true);
}

@Override
public void onPause() {
    super.onPause();
    mShakeListener.unregisterListener(mSensorManager);
}

private OnShakeListener mOnShakeListener = new OnShakeListener() {
    // @Override
    public void onShaked(int direction) {
        
        if ((direction & ShakeListener.DIRECTION_X) > 0) {
            sensor_X++;
            
        } else {
            //
        }
        
        if ((direction & ShakeListener.DIRECTION_Y) > 0) {
            sensor_Y++;
            
        } else {
            //
        }
        
        if ((direction & ShakeListener.DIRECTION_Z) > 0) {
            sensor_Z++;
            
        } else {
            //
        }
        
    }
};











TOP

サンプル

初期画面。





下記のジェスチャーを実行すると、一呼吸置く感じで操作が行われます。


録音

手首を支点にして左右(どっちかの方)に振ります。





状態表示が赤に変わって、録音開始。





再度左右(どっちかの方)に振ると、元に戻って停止状態になります。


公開用にアップロード

スマホを握って、斜め上へ振り上げます(強く振り上げなくてもいいです)。


まだ録音停止していない場合は、録音状態は下のように、一度グリーンに変わり停止させてからアップロードします。



2点のご注意
    1:
    この動作は、ダウンロード・ジェスチャーと同じです。
    アップロードの場合は、「Friend's name」が空欄であることを確認しておいてください。



    「 Friend's name」に何かある場合は、ダウンロードを実行しようとします。
    まだ操作になれていない状態でやると、録音ジェスチャと間違える可能性があります。
    その場合は、空白を入力してください。

   


2:
    この動作は録音(停止)・ジェスチャーと似ています。
    停止しているはずなのに勝手に録音状態になるのを避ける場合は、スマホの画面を
    ダブル・タップしてください。



    状態表示がこういう風に変われば、録音されません。



    再度ダブル・タップすれば解除されます。



公開されているデータをダウンロード

メニュキーを押して入力ダイアログを出し、半角数字でIDを入力してください。

例えば、「123456789012345」の15桁の数字を入力してデータをダウンロードすると、サッカー選手がフリーキックのコツを伝授してくれます(^^)。





スマホを握って、斜め下へ振り下げます(強く振り下げなくてもいいです)。。



ダウンロードが実行されます。








再生

スマホを握って、上下(どっちかの方へ)に振ります。

ぐっと下(上)に突きおろす感じ(軽くていいです、あまり強く振ると誤認識されますんで)。

まだバグがあるかも...。うまく再生されな場合は再起動してみてください(TT;







Friend's nameがあれば、その人の声の再生が優先されます。なければ、自分の声を再生。

ダイアログで空白にしておけば、一時的にFriend's nameが消され自分の声を再生できます。





再起動でFriend's nameが復活します。

友人の声の再生



自分の声の再生








インストール



アプリをQRコードからインストールする方法はコチラを参照



TOP

シェイクの練習

明日の鍵さんのコードをアプリにしてみました。

インストール



アプリをQRコードからインストールする方法はコチラを参照



X、Y、Z軸方向のセンサーの反応を確認できます。





検知強度の選択は、メニュから。





今回のアプリでは、「weak」に設定しています。

Xが反転した場合:録音動作に対応





Zが反転した場合:再生動作に対応





それ以外:アップ/ダウンロード動作に対応





......etc

TOP