UpmostScarab
UpmostScarab

Reputation: 985

Making precise android metronome

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

Answers (1)

UpmostScarab
UpmostScarab

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

Related Questions