Android:使用加速度计创建一个简单的马拉卡应用
Posted
技术标签:
【中文标题】Android:使用加速度计创建一个简单的马拉卡应用【英文标题】:Android: Using the accelerometer to create a simple maraca app 【发布时间】:2013-09-11 21:50:12 【问题描述】:我正在尝试通过创建(我认为应该是的)一个简单的应用程序来模仿粗糙的马拉卡,来学习如何使用加速度计。
目的是当手机快速向下轻弹时,它会在轻弹结束时发出马拉卡声音,同样在向上轻弹结束时发出不同的声音。
实现这一点的策略是检测加速度何时超过某个阈值。发生这种情况时,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();
我遇到的一些问题是:
我认为 ShakeHasHappened 在减速开始时开始,当加速度低于零时。也许这应该是当减速停止时,当加速度变为负值并且现在正回到零时。这听起来合理吗?
检测运动是向上还是向下的方法不起作用 - 这是因为我在查看 z 轴时无法准确读取手机的位置,因为加速度也包括在内在 z 轴数据中,因此没有给我手机的准确位置?
我得到了很多双击,但我不太明白这是为什么。有时它根本不点击。
如果有人想尝试一下这段代码,看看他们是否能找到一种方法让它更准确、更高效,请继续分享你的发现。如果有人能发现为什么它没有按照我想要的方式工作,请再次分享您的想法。
要将声音链接到此代码,请将 wav 文件放入 res\raw 文件夹并在 R.raw.shake1 位(无扩展名)中引用它们
谢谢
编辑:我做了一些研究,偶然发现了一种叫做动态时间扭曲的东西。我对此一无所知,但会开始研究它。有谁知道 DTW 是否可以成为实现工作马拉卡模拟器应用程序的不同方法?
【问题讨论】:
【参考方案1】:我可以给你一些建议:
首先,我注意到您为两种结果使用了相同的资源:
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 毫秒,不要处理传感器值。让运动稳定下来。您需要使用它来避免多次播放。
注意:请在您的代码中包含 cmets。至少,在每个代码块上放置 cmets 以传达您试图用它完成的任务。
【讨论】:
谢谢@user2558882,你给了我很多思考和尝试。很高兴你用细齿梳浏览了我的代码。您关于添加 cmets 的建议已得到适当注意,我会采纳。也许如果我将来这样做,我就不必在赏金上花费积分。非常感谢 @DickyMoore 不客气,伙计。我只想指出一些可以帮助您的事情。不过,我还没有给你一个“代码”解决方案。所以,如果你想暂时不接受我的回答,并等待一个更好的答案,那就继续吧。 @DickyMoore 完全没问题。我注意到人们不太关注已经有公认答案的问题。您应该能够为您花费的代表点数达到最大里程数。 但是,如果我尝试您的更改并且它使应用程序按照我想要的方式运行,那么这是一个确定的答案! 什么是 epsilon?它是一个包含范围的变量类型吗?如何申报?【参考方案2】:前段时间我尝试自己实现,最终使用了这个解决方案。-
http://jarofgreen.co.uk/2013/02/android-shake-detection-library/
基于您问题中的相同概念。
【讨论】:
以上是关于Android:使用加速度计创建一个简单的马拉卡应用的主要内容,如果未能解决你的问题,请参考以下文章