Androidで音声録音して、Webで公開してみる II
流れはこんな感じ。
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 { // } } };
初期画面。
下記のジェスチャーを実行すると、一呼吸置く感じで操作が行われます。
録音
手首を支点にして左右(どっちかの方)に振ります。
状態表示が赤に変わって、録音開始。
再度左右(どっちかの方)に振ると、元に戻って停止状態になります。
公開用にアップロード
スマホを握って、斜め上へ振り上げます(強く振り上げなくてもいいです)。
まだ録音停止していない場合は、録音状態は下のように、一度グリーンに変わり停止させてからアップロードします。
2点のご注意
1:
この動作は、ダウンロード・ジェスチャーと同じです。
アップロードの場合は、「Friend's name」が空欄であることを確認しておいてください。
「 Friend's name」に何かある場合は、ダウンロードを実行しようとします。
まだ操作になれていない状態でやると、録音ジェスチャと間違える可能性があります。
その場合は、空白を入力してください。
2:
この動作は録音(停止)・ジェスチャーと似ています。
停止しているはずなのに勝手に録音状態になるのを避ける場合は、スマホの画面を
ダブル・タップしてください。
状態表示がこういう風に変われば、録音されません。
再度ダブル・タップすれば解除されます。
公開されているデータをダウンロード
メニュキーを押して入力ダイアログを出し、半角数字でIDを入力してください。
例えば、「123456789012345」の15桁の数字を入力してデータをダウンロードすると、サッカー選手がフリーキックのコツを伝授してくれます(^^)。
スマホを握って、斜め下へ振り下げます(強く振り下げなくてもいいです)。。
ダウンロードが実行されます。
再生
スマホを握って、上下(どっちかの方へ)に振ります。
ぐっと下(上)に突きおろす感じ(軽くていいです、あまり強く振ると誤認識されますんで)。
まだバグがあるかも...。うまく再生されな場合は再起動してみてください(TT;
Friend's nameがあれば、その人の声の再生が優先されます。なければ、自分の声を再生。
ダイアログで空白にしておけば、一時的にFriend's nameが消され自分の声を再生できます。
再起動でFriend's nameが復活します。
友人の声の再生
自分の声の再生
明日の鍵さんのコードをアプリにしてみました。
インストール
アプリをQRコードからインストールする方法はコチラを参照
X、Y、Z軸方向のセンサーの反応を確認できます。
検知強度の選択は、メニュから。
今回のアプリでは、「weak」に設定しています。
Xが反転した場合:録音動作に対応
Zが反転した場合:再生動作に対応
それ以外:アップ/ダウンロード動作に対応
......etc
TOP