4

I'm calling ToneGenerator.startTone() repeatedly to issue short bursts of sound. But on the first call, it blocks for a long period of time. So the first burst is way too long. Here's an example:

Member variables:

private ToneGenerator mDTMFPlayer

In the constructor:

mDTMFPlayer = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, TONE_RELATIVE_VOLUME);

In a Thread started by OnClickListener.onClick():

long startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 1st: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 2nd: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 3rd: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

Here's the output, with execution time of startTone() in milliseconds:

11-16 18:07:35.885 16927-17977/com.my.project D/Ring: After 1st: 454
11-16 18:07:36.502 16927-17977/com.my.project D/Ring: After 2nd: 0
11-16 18:07:36.672 16927-17977/com.my.project D/Ring: After 3rd: 1

The first call is blocking for almost half a second which is way too long for what I need. Any calls after that makes the blocking go away for a while. What's weird is that if I wait a bit and try again, it's slow again. There seems to be a period after which the blocking comes back.

Please advise.

Emmanuel
  • 16,791
  • 6
  • 48
  • 74
  • How long is "a bit"? IOW, is this a per-process issue, where "a bit" is after your process was terminated and, by user action, was re-created? – CommonsWare Nov 16 '17 at 23:22
  • No, the process continues, I wait less than a second and then and I click on the button again which calls onClick() and starts a separate thread (I don't want to block on the GUI thread) and calls startTone() again. – Emmanuel Nov 16 '17 at 23:27
  • 1
    Well `startTone()` just calls a `native` method (see [the source](https://android.googlesource.com/platform/frameworks/base/+/android-cts-8.0_r4/media/java/android/media/ToneGenerator.java)), so I'm not sure that there's a lot that you can do about it. – CommonsWare Nov 16 '17 at 23:50
  • 1
    If it wasn't native I probably would have found the cause already. :) – Emmanuel Nov 16 '17 at 23:55
  • 2
    Point. Sorry if my comment implied otherwise -- as much as anything, my comment was for the next person to run into this problem, if you don't get an answer. – CommonsWare Nov 16 '17 at 23:57
  • don't worry about it. – Emmanuel Nov 17 '17 at 03:26

1 Answers1

1

I think that AudioManager.STREAM_VOICE_CALL is causing this. The behavior on my devise is similar to yours. After i run app it has long init for the first startTone() call. If i exit and enter app it can be fast for all 3 calls. But it will show same "slow, fast, fast" result if some system sound plays before the app starts.

So i suppose it has something to do with stream switching/blocking, because with AudioManager.STREAM_NOTIFICATION it takes only 4-10 msecs on my device. Also can read more info here: What is the difference between AudioManager's stream types at low level?

Consider this code:

for (int i = -1; i < 10; i++) {
    System.out.println("AudioSystem stream " + i);
    mDTMFPlayer = new ToneGenerator(i, TONE_RELATIVE_VOLUME);
    long startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 1st: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();

    startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 2nd: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();

    startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 3rd: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();
    mDTMFPlayer.release();
}

Output:

I/System.out: AudioSystem stream -1 STREAM_DEFAULT
D/com.example.MainActivity: After 1st: 8
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 0 STREAM_VOICE_CALL
D/com.example.MainActivity: After 1st: 325
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 1 STREAM_SYSTEM
D/com.example.MainActivity: After 1st: 17
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 3
I/System.out: AudioSystem stream 2 STREAM_RING
D/com.example.MainActivity: After 1st: 28
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 3 STREAM_MUSIC
D/com.example.MainActivity: After 1st: 19
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 4 STREAM_ALARM
D/com.example.MainActivity: After 1st: 28
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 5 STREAM_NOTIFICATION
D/com.example.MainActivity: After 1st: 16
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 6 STREAM_BLUETOOTH_SCO
D/com.example.MainActivity: After 1st: 332
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 7 STREAM_SYSTEM_ENFORCED
D/com.example.MainActivity: After 1st: 324
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 8 STREAM_DTMF
D/com.example.MainActivity: After 1st: 26
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 4
I/System.out: AudioSystem stream 9 STREAM_TTS
D/com.example.MainActivity: After 1st: 12
D/com.example.MainActivity: After 2nd: 4
D/com.example.MainActivity: After 3rd: 2  

Btw, if you want to research related C sourse code you can look at android_media_ToneGenerator.cpp, ToneGenerator.h, ToneGenerator.cpp AudioService.java

varren
  • 14,551
  • 2
  • 41
  • 72
  • In the end I used the STREAM_DTMF which is actually made for that, and there's NO delay at all. Perfect solution. Thanks for your answer. Will give you the bounty for sure. – Emmanuel Nov 24 '17 at 01:46
  • How long should the duration of startTone actually be? 50 ms? 150 ms? What's the default/standard? – android developer Nov 08 '21 at 14:41