2

is there a way to route microphone input through headset and use simultaneously the AUDIO output through smartphone's speaker ?

I've been watching around for hours now and I saw that it's clearly impossible on iOS but what about Android..

I'm using a Samsung Galaxy S4.

Here is a part of my code to route the audio output through the speaker even if the headset is plugged in :

AudioManager speakerOutput = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
speakerOutput.setMode(AudioManager.MODE_IN_CALL);
speakerOutput.setSpeakerphoneOn(true);

But then when I tried to control my app by the headset's microphone, nothing. So I tried with the smartphone's one and it's obviously working.

It don't seems like others AudioManager.MODEs route audio output only and leave the mic input to the headset. So what can I do now without having to build a new kernel, modify drivers, HAL, …etc ?

Thanks, C.

Cedric
  • 112
  • 1
  • 10

3 Answers3

0

If you use AudioManager.STREAM_RING, the audio should be routed to both the loudspeaker and the wired headset, just as when the ringtone is played for an incoming call (remove the setMode and setSpeakerphoneOn calls).

Michael
  • 57,169
  • 9
  • 80
  • 125
  • Not working :( Need to add setSpeakerphoneOn to hear the sound and it route the MIC to the phone again. Thx – Cedric Nov 23 '13 at 15:20
0

There is no way to loopback from headset Mic to Speaker if you do not change HAL or driver on Android. If u can change hardware, it possible works out.

Z.Crina
  • 16
  • 1
0

Yes, It's possible. I successfully do it a few days ago. There are 2 solutions for it:

Solution 1:

Suppose you are in a normal mood & Headset connected. We just need to call setPreferredDevice function to set your preferred audio input/output device. Our input source is already coming from the headset. So we only need to route the audio output source to the Phone Speaker. Here is my sample code by Media Player:

@RequiresApi(Build.VERSION_CODES.P)
private fun playAudioFromPhoneSpeaker(context: Context, audioResId: Int) {
    val mMediaPlayer: MediaPlayer = MediaPlayer.create(context, audioResId)
    val mCurrentDevice: AudioDeviceInfo? = mMediaPlayer.preferredDevice
    val mDeviceSpeaker: AudioDeviceInfo? = getDeviceOutputSpeaker(context)
    mDeviceSpeaker?.let { speaker ->
        if (mCurrentDevice != speaker) {
            val result: Boolean? = mMediaPlayer.setPreferredDevice(mDeviceSpeaker)
            Log.i("PreferredDevice", "is set as speaker: $result")
        }
    }
    mMediaPlayer.start()
    mMediaPlayer.setOnCompletionListener { mediaPlayer ->
        mediaPlayer.release()
    }
}

@RequiresApi(Build.VERSION_CODES.M)
private fun getDeviceOutputSpeaker(context: Context): AudioDeviceInfo? {
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
    mAudioDeviceOutputList?.filterNotNull()?.forEach { speaker ->
        if (speaker.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
            return speaker
        }
    }
    return null
}

The positive thing about this solution is that we don't need to think about the input source. We only change the audio output source and it doesn't have any impact on the input source.

The problem is setPreferredDevice of MediaPlayer come from Android 9. So this solution is only supported from Android 9.

Solution 2:

In 2nd solution we turn on device speaker by Audio Manager. So after turn on the device speaker the audio input & output source will come to the device internal speaker & device mic. Now we have to route the mic to the headset. Sample code:

private val mAudioRecord: AudioRecord? = null
private fun RecordAudioFromHeadphone(context: Context) {
    //Step 1: Turn on Speaker
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    audioManager.isSpeakerphoneOn = true

    //Step 2: Init AudioRecorder TODO for you

    //Step 3: Route mic to headset
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        setPreferredInputMic(getWiredHeadPhoneMic(this))
    }
}

@RequiresApi(Build.VERSION_CODES.M)
private fun setPreferredInputMic(mAudioDeviceInfo: AudioDeviceInfo?) {
    val result: Boolean? = mAudioRecord?.setPreferredDevice(mAudioDeviceInfo)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun getWiredHeadPhoneMic(context: Context): AudioDeviceInfo? {
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
    mAudioDeviceOutputList?.filterNotNull()?.forEach { device ->
        if (device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
            return device
        }
    }
    return null
}

The positive think of this solution is supported from Android 6. And negative thing is it's little bit complex and you have to maintain the voice input source.

AudioRecorder, MediaRecorder, AudioTrack also support this kind of solution.

Thanks

Shohan Ahmed Sijan
  • 4,391
  • 1
  • 33
  • 39