How to double beep with ToneGenerator

2.8k Views Asked by At

The problem

I've been trying to achieve a double beep sound on an app I'm developing once a button is pressed.

Problem is, I didn't manage to get it quite right. Sometimes the sounds overlapped, other times they played faster than they should.

I wanted to play an arbitrary tone, have an arbitrary pause/delay and then play the tone again.

OBS: I've seen some solutions for this problem using the MediaPlayer or the Ringtone class, but this question is on how to achieve this using ToneGenerator only, without using the TONE_PROP_BEEP2 or TONE_PROP_ACK tones.

What I've tried so far

First of all, I tried to call the startTone() method twice, but soon I found out that the sounds played simultaneously.

button.setOnClickListener {
    val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100)
    toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
    toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
}

Then I tried to use Handler in two ways.

First I tried to use two of them:

button.setOnClickListener {
    val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100)
    val handler = Handler()

    handler.postDelayed({
        toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
    }, 50)
    handler.postDelayed({
        toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
    }, 350)
}

Second, I also tried with Thread.sleep():

button.setOnClickListener {
    val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100)
    val handler = Handler()

    handler.postDelayed({
        toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
        Thread.sleep(100)
        toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
    }, 50)
}

Both ideas "kinda" worked. But on the first button press, the beeps sounded funny, as they played way too fast, and sometimes even overlapping.

The second time I pressed the Button, the tones played correctly.

3

There are 3 best solutions below

0
On

this is working for me:

val tg = ToneGenerator(AudioManager.STREAM_ALARM, 1000)
tg.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 2000)
0
On
val toneGen1 = ToneGenerator(AudioManager.STREAM_MUSIC, 100)
                toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150)
                Handler().postDelayed({
                    val toneGen1 = ToneGenerator(AudioManager.STREAM_MUSIC, 100)
                    toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150)
                }, 150)
1
On

You could try a CountDownTimer and see if it works better. I actually tried your first option and did not notice the affect you mentioned. I am not sure postDelayed makes any promises other than "Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses" , you could also try postAtTime() instead of postDelayed()

    button.setOnClickListener {
        val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100)
        val timer = object: CountDownTimer(350,50) {

            var skipThisInterval = false

            override fun onFinish() {
                toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
            }

            override fun onTick(p0: Long) {
                if (skipThisInterval) return
                skipThisInterval = true
                toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
            }
        }
        timer.start()

    }    

using postAtTime() rather than postDelayed() might work better also.

    button.setOnClickListener {
        val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100)
        val handler = Handler()
        val timeNow = SystemClock.uptimeMillis()

        handler.postAtTime({
            toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
        }, 50 + timeNow)
        handler.postAtTime({
            toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200)
        }, 350 + timeNow)
    }