有没有办法通过耳机路由麦克风输入并通过智能手机扬声器同时使用音频输出?
我已经观察了几个小时了,我发现这在 iOS 上显然是不可能的,但 Android 上呢..
我使用的是三星 Galaxy S4。
这是我的代码的一部分,即使插入耳机,也可以通过扬声器路由音频输出:
AudioManager speakerOutput = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
speakerOutput.setMode(AudioManager.MODE_IN_CALL);
speakerOutput.setSpeakerphoneOn(true);
但是当我尝试通过耳机的麦克风控制我的应用程序时,什么也没有。所以我尝试使用智能手机,它显然可以正常工作。
其他 AudioManager.MODE 似乎并不只路由音频输出,而将麦克风输入保留到耳机。那么我现在可以做什么而不需要构建新内核、修改驱动程序、HAL 等?
谢谢,C。
如果不更改 Android 上的 HAL 或驱动程序,则无法从耳机麦克风环回至扬声器。如果你可以改变硬件,它可能会成功。
如果您使用
AudioManager.STREAM_RING
,音频应同时路由到扬声器和有线耳机,就像来电时播放铃声一样(删除 setMode
和 setSpeakerphoneOn
呼叫)。
是的,这是可能的。我前几天成功做到了。有 2 个解决方案:
解决方案1:
假设您心情正常且已连接耳机。我们只需要调用 setPreferredDevice 函数来设置您首选的音频输入/输出设备。我们的输入源已经来自耳机。所以我们只需要把音频输出源路由到PhoneSpeaker即可。这是我的媒体播放器示例代码:
@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
}
这个解决方案的优点是我们不需要考虑输入源。我们只改变音频输出源,对输入源没有任何影响。
问题是MediaPlayer的setPreferredDevice来自Android 9。所以此解决方案仅从Android 9支持。
解决方案2:
在第二个解决方案中,我们通过音频管理器打开设备扬声器。因此,打开设备扬声器后,音频输入和输出源将来自设备内部扬声器和设备麦克风。现在我们必须将麦克风路由到耳机。示例代码:
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
}
这个解决方案的积极之处是Android 6支持。消极的是它有点复杂,你必须维护语音输入源。
AudioRecorder、MediaRecorder、AudioTrack 也支持这种解决方案。
谢谢
对 Shohan Ahmed Sijan 解决方案的补充(抱歉,由于缺乏声誉,无法将此作为评论发布):
有时,在激活扬声器输出时,将用于音频录制的首选输入设备路由到耳机麦克风不会产生任何效果。在这种情况下,请尝试使用 MediaRecorder.AudioSource.VOICE_RECOGNITION 源而不是 MediaRecorder.AudioSource.MIC 创建音频记录