Reputation: 985
I'm making an app for Android and in it's core it has a metronome. I need to produce sounds precisly in beat. And every 2nd sound is delayed for "swing" percentage of interval. My current code looks like this:
public void play() {
mainCountHandler.postDelayed(mainClick, 0);
subCountHandler.postDelayed(subClick, (MILLIS_IN_MINUTE/ (2 * bpm)) * (100 + swing));
}
private Runnable mainClick = new Runnable()
{
public void run()
{
playSound();
mainCountHandler.postDelayed(this, MILLIS_IN_MINUTE / bpm);
}
};
private Runnable subClick = new Runnable()
{
public void run()
{
playSound();
subCountHandler.postDelayed(this, MILLIS_IN_MINUTE / bpm);
}
};
private void playSound() {
final MediaPlayer mp = MediaPlayer.create(context, SOUND);
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
};
});
++currentBeat;
if (currentBeat >= beatsCount) {
if (loop) {
currentBeat = 0;
} else {
stop();
}
}
}
And this runs terribly inaccuate. I tried SoundPool and also creating MediaPlayer outside of playSound and there's no singnificant changes. So is there any modifications or maybe another Android classes that can help me?
Upvotes: 2
Views: 4155
Reputation: 985
So the most precise metronome that I can achieve looks like this:
public void play() {
mainTimer = new Timer();
subTimer = new Timer();
mainTimerTask = new MyTimerTask();
subTimerTask = new MyTimerTask();
mainTimer.schedule(mainTimerTask, 0, MILLIS_IN_MINUTE / bpm);
subTimer.schedule(subTimerTask, (300 * (100+swing)) / bpm, MILLIS_IN_MINUTE / bpm);
}
private void playSound() {
final MediaPlayer mp = MediaPlayer.create(context, SOUND);
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
};
});
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
playSound();
}
}
Upvotes: 2