AudioManager is introducing delay in the setMode(MODE_IN_COMMUNICATION)
Asked Answered
S

2

8

I am trying to play sound on speaker even if headphones are on, BUT if there is music playing in background, I want the music to be played on headphones until the sound is played.

So I am taking the next steps:

  • Before I play sound, I gain audioFocus so all the background music is stopped
  • After audioFocus is gained, I set MODE_COMMUNICATION to the AudioManager, so the sound can be played on the speaker.
  • After the sound ends, I abandonAudioFocus and set back MODE_NORMAL to AudioManager, so the background music can continue playing on the headphones.

The strange thing is that it depends from the device, on some device this is OK, but on Nexus 6P (Huawei) devices after setting the MODE_COMMUNICATION there is delay 3-4 seconds before the sound can be played. If I play the sound without the delay, it is not played neither to speaker nor to headphones.

THE QUESTION

How can I know how much delay to set before the sound is played? Is there any listener that I can attach, so can notify me that after setting MODE_COMMUNICATION the sound is ready to be played?

I don't want to set delay even if it is not necessary!

The solution is not setting the mode into the constructor of the class(to skip the delay), because I need the sound to be played on speaker in the specific moment!

P.S: I am playing the sound with AudioTrack, but I tried with MediaPlayer as well (setting setAudioStreamType(MODE_IN_COMMUNICATION)), but no success, the delay is still there!

So any suggestions?

Suchlike answered 6/12, 2017 at 14:41 Comment(1)
i have a similar problem at #56962912 did you find some solutions ?Susurrant
R
1

In case anyone stumbles upon this post, what worked for me was calling AudioManager.setMode() and then recreating my MediaPlayer (via constructor, not create()) and using setAudioAttributes() to change the output source.

Kotlin snippet:

fun switchOutput(inCommunication: Boolean) {

    //STEP 1: Change the AudioManager's audio mode
    if(inCommunication) {
        audioManager.mode = AudioManager.MODE_IN_CALL
        audioManager.isSpeakerphoneOn = false
    } else {
        audioManager.mode = AudioManager.MODE_NORMAL
        audioManager.isSpeakerphoneOn = true
    }

    //STEP 2: Recreate the MediaPlayer
    if (player != null) {
        try {
            player?.stop()
        } catch (e: RuntimeException) {
        } finally {
            player?.reset()
            player?.release()
            player = null
        }
    }

    player = MediaPlayer()
    try {
        val streamType =
                if (inCommunication) AudioManager.STREAM_VOICE_CALL
                else AudioManager.STREAM_MUSIC
        player?.setDataSource(dataSource)
        player?.setAudioAttributes(AudioAttributes.Builder()
                .setLegacyStreamType(streamType)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build())
        player?.prepare()
        player?.start()
    } catch (e: java.lang.Exception) {}
}

Hope this helps :)

P.S. Notice I've used MODE_IN_CALL instead of MODE_IN_COMMUNICATION

Reconciliatory answered 2/5, 2020 at 10:21 Comment(3)
your solution required API level 21 at least.Iddo
@MuhammadSaqib it's been a long long time since I developed an app with a minSDK lower than 21 >.<Reconciliatory
I create issue about this to google tracker: issuetracker.google.com/issues/204709351. The issue includes a simple app that can be used to reproduce it.Stutzman
S
0

If device supports API level 31 or greater, it is possible to use AudioManager.OnModeChangedListener to detect when audio mode changes to MODE_IN_COMMUNICATION, something like this:

val am = getSystemService(AUDIO_SERVICE) as AudioManager
var audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
  if (mode == AudioManager.MODE_IN_COMMUNICATION) {
    Log.d(TAG, "Audio mode changed to MODE_IN_COMMUNICATION")
    if (audioModeChangedListener != null) {
      am.removeOnModeChangedListener(audioModeChangedListener!!)
      audioModeChangedListener = null
    }
    // start playing
  }
}
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
am.mode = AudioManager.MODE_IN_COMMUNICATION
Stutzman answered 12/4, 2023 at 16:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.