我正在尝试通过创建(我认为会是)一个简单的应用程序来模仿粗糙的马拉卡,来学习如何使用加速度计。
目的是当手机快速向下轻弹时,它会在轻弹结束时发出马拉卡声音,同样在向上轻弹结束时会发出不同的声音。
实现这一点的策略是检测加速度何时超过某个阈值。发生这种情况时,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 是否可以成为实现工作马拉卡模拟器应用程序的不同方法?