Android:使用加速计创建一个简单的maraca应用程序(Android: Using the accelerometer to create a simple maraca app)

编程入门 行业动态 更新时间:2024-10-22 19:23:46
Android:使用加速计创建一个简单的maraca应用程序(Android: Using the accelerometer to create a simple maraca app)

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

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

实现这一点的策略是检测加速度何时超过某个阈值。 发生这种情况时,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(); } } }

我遇到的一些问题是:

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

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

我得到了很多双击,我无法弄清楚为什么会这样。 有时根本不点击。

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

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

谢谢

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

I'm trying to learn how to use the accelerometer by creating (what I thought would be) a simple app to mimic a crude maraca.

The objective is that when the phone is flicked downwards quickly, it emits a maraca sound at the end of that flick, and likewise a different sound is emitted at the end of an upward flick.

The strategy for implementing this is to detect when the acceleration passes over a certain threshold. When this happens, ShakeIsHappening is set to true, and the data from the z axis is fed into an array. A comparison is made to see whether the first position in the z array is greater or lesser than the most recent position, to see whether the phone has been moved upwards or downwards. This is stored in a boolean called zup.

Once the acceleration goes below zero, we assume the flick movement has ended and emit a sound, chosen depending on whether the movement was up or down (zup).

Here is the code:

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(); } } }

Some of the problems I'm having are:

I think ShakeHasHappened kicks in when deceleration starts, when acceleration goes below zero. Perhaps this should be when deceleration stops, when acceleration has gone negative and is now moving back towards zero. Does that sound sensible?

The way of detecting whether the motion is up or down isn't working - is this because I'm not getting an accurate reading of where the phone is when looking at the z axis because the acceleration is also included in the z-axis data and therefore isn't giving me an accurate position of the phone?

I'm getting lots of double clicks, and I can't quite work out why this is. Sometimes it doesn't click at all.

If anyone wants to have a play around with this code and see if they can find a way of making it more accurate and more efficient, please go ahead and share your findings. And if anyone can spot why it's not working the way I want it to, again please share your thoughts.

To link sounds to this code, drop your wav files into your res\raw folder and reference them in the R.raw.shake1 bit (no extension)

Thanks

EDIT: I've done a bit of research and have stumbled across something called Dynamic Time Warping. I don't know anything about this yet, but will start to look in to it. Does anyone know if DTW could be a different method of achieving a working maraca simulator app?

最满意答案

我可以给你一些指示:

首先,我注意到你在两种结果中使用相同的资源:

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毫秒,不要处理传感器值。 让运动稳定下来。 您需要这样才能避免多次播放。

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

I can give you some pointers on this:

First of all, I noticed that you're using the same resource for both outcomes:

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

The resource in case of shakesound2 should be R.raw.shake2.

Second, the following only deals with one of the motions:

if (mAccelNoGrav > 3)

This should be changed to:

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

Currently, you are not intercepting downward motion.

Third, acceleration value of 3 is rather low. If you want to avoid/filter-out normal arm movement, this value should be around 6 or 7 and -6 or -7.

Fourth, you do not need to store z values to check whether the motion was up or down. You can check whether:

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

You can use this information to set zup accordingly.

Fifth: I can only guess that you are using if (mAccelNoGrav < 0) to play the sound when the motion ends. In that case, this check should be changed to:

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

where epsilon is some range such as (-1, 1).

Sixth, you should include a lockout period in your application. This would be the period after all conditions have been met and a sound is about to be played. For the next, say 1000 ms, don't process the sensor values. Let the motion stabilize. You'll need this to avoid getting multiple playbacks.

Note: Please include comments in your code. At the very least, place comments on every block of code to convey what you are trying to accomplish with it.

更多推荐

本文发布于:2023-08-06 23:44:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1456606.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:创建一个   应用程序   简单   maraca   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!