3

我正在尝试通过创建(我认为会是)一个简单的应用程序来模仿粗糙的马拉卡,来学习如何使用加速度计。

目的是当手机快速向下轻弹时,它会在轻弹结束时发出马拉卡声音,同样在向上轻弹结束时会发出不同的声音。

实现这一点的策略是检测加速度何时超过某个阈值。发生这种情况时,ShakeIsHappening 被设置为 true,并且来自 z 轴的数据被输入到一个数组中。进行比较以查看z数组中的第一个位置是否大于或小于最近的位置,以查看电话是向上移动还是向下移动。这存储在一个名为 zup 的布尔值中。

一旦加速度低于零,我们假设轻弹运动已经结束并发出声音,根据运动是向上还是向下 (zup) 来选择。

这是代码:

public class MainActivity extends Activity implements SensorEventListener {

    private float mAccelNoGrav;
    private float mAccelWithGrav;
    private float mLastAccelWithGrav;

    ArrayList<Float> z = new ArrayList<Float>();

    public static float finalZ;

    public static boolean shakeIsHappening;
    public static int beatnumber = 0;
    public static float highZ;
    public static float lowZ;
    public static boolean flick;
    public static boolean pull;
    public static SensorManager sensorManager;
    public static Sensor accelerometer;


    private SoundPool soundpool; 
    private HashMap<Integer, Integer> soundsMap;

    private boolean zup;

    private boolean shakeHasHappened;
    public static int shakesound1 = 1;
    public static int shakesound2 = 2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        results = (TextView) findViewById(R.id.results);
        clickresults = (TextView) findViewById(R.id.clickresults);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mAccelNoGrav = 0.00f;
        mAccelWithGrav = SensorManager.GRAVITY_EARTH;
        mLastAccelWithGrav = SensorManager.GRAVITY_EARTH;

        soundpool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
        soundsMap = new HashMap<Integer, Integer>();
        soundsMap.put(shakesound1, soundpool.load(this, R.raw.shake1, 1));
        soundsMap.put(shakesound2, soundpool.load(this, R.raw.shake1, 1));

    }

    public void playSound(int sound, float fSpeed) {
        AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
        float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        float volume = streamVolumeCurrent / streamVolumeMax;  
        soundpool.play(soundsMap.get(sound), volume, volume, 1, 0, fSpeed);
       }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

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

    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        float x = event.values[0];
        float y = event.values[1];
        z.add((event.values[2])-SensorManager.GRAVITY_EARTH);
        mLastAccelWithGrav = mAccelWithGrav;
        mAccelWithGrav = android.util.FloatMath.sqrt(x * x + y * y + z.indexOf(z.size()-1) * z.indexOf(z.size()-1));
        float delta = mAccelWithGrav - mLastAccelWithGrav;
        mAccelNoGrav = mAccelNoGrav * 0.9f + delta; // Low-cut filter

        if (mAccelNoGrav > 3) {
            shakeIsHappening = true;
            z.clear();
        } 
        if (mAccelNoGrav < 0) {
            if (shakeIsHappening) {
            shakeIsHappening = false;
            shakeHasHappened = true;
        }
        }

        if (shakeIsHappening && z.size() != 0) {
            if (z.get(z.size()-1) > z.get(0)) {
                zup = true;
            } else if (z.get(0) > z.get(z.size()-1)) {
                zup  = false;
        }
        }
        if (shakeHasHappened) {
            Log.d("click", "up is" + zup + "Low Z:" + z.get(0) + " high Z:" + z.get(z.size()-1));
            if (!zup) {
                shakeHasHappened = false;
                playSound(shakesound2, 1.0f);
                z.clear();

            } else if (zup) {
                shakeHasHappened = false;
                playSound(shakesound1, 1.0f);
                z.clear();              
            }
        }
        }

我遇到的一些问题是:

  1. 我认为 ShakeHasHappened 在减速开始时开始,当加速度低于零时。也许这应该是当减速停止时,当加速度变为负值并且现在正回到零时。这听起来合理吗?

  2. 检测运动是向上还是向下的方法不起作用 - 这是因为我在查看 z 轴时无法准确读取手机的位置,因为加速度也包含在 z 轴数据中因此没有给我电话的准确位置吗?

  3. 我得到了很多双击,我不太明白为什么会这样。有时它根本不点击。

如果有人想玩弄这段代码,看看他们是否能找到一种让它更准确、更高效的方法,请继续分享你的发现。如果有人能发现为什么它没有按照我想要的方式工作,请再次分享你的想法。

要将声音链接到此代码,请将 wav 文件放入 res\raw 文件夹并在 R.raw.shake1 位(无扩展名)中引用它们

谢谢

编辑:我做了一些研究,偶然发现了一种叫做动态时间扭曲的东西。我对此一无所知,但会开始研究它。有谁知道 DTW 是否可以成为实现工作马拉卡模拟器应用程序的不同方法?

4

2 回答 2

3

我可以给你一些建议:

首先,我注意到您对两种结果都使用了相同的资源:

soundsMap.put(shakesound1, soundpool.load(this, R.raw.shake1, 1));
soundsMap.put(shakesound2, soundpool.load(this, R.raw.shake1, 1));

情况下的资源shakesound2应该是R.raw.shake2.

其次,以下仅涉及其中一项动议:

if (mAccelNoGrav > 3)

这应该改为:

if (mAccelNoGrav > 3 || mAccelNoGrav < -3)

目前,您没有拦截向下运动。

第三,加速度值3相当低。如果你想避免/过滤掉正常的手臂运动,这个值应该在 6 或 7 和 -6 或 -7 左右。

第四,您不需要存储 z 值来检查运动是向上还是向下。您可以检查是否:

mAccelnoGrav > 6 ===> implies motion was upwards

mAccelnoGrav < -6 ===> implies motion was downwards

您可以使用此信息进行zup相应设置。

第五:我只能猜测你是if (mAccelNoGrav < 0)用来播放动作结束时的声音。在这种情况下,此检查应更改为:

if (mAccelNoGrav < epsilon || mAccelNoGrav > -epsilon)

其中epsilon是某个范围,例如 (-1, 1)。

第六,您应该lockout在申请中包含一个句点。这将是在满足所有条件并且即将播放声音之后的时期。接下来,比如 1000 毫秒,不要处理传感器值。让运动稳定下来。你需要这个来避免多次播放。

注意:请在您的代码中包含注释。至少,在每个代码块上添加注释,以传达您试图用它完成的任务。

于 2013-09-26T11:24:45.623 回答
2

前段时间我尝试自己实现它,最终使用了这个解决方案。-

http://jarofgreen.co.uk/2013/02/android-shake-detection-library/

基于您问题中的相同概念。

于 2013-10-01T22:43:10.497 回答