sna*_*msm 12 android android-audiomanager android-12
我想播放一些音频,音量级别调整为耳朵的大小。“通话模式”。为此,我使用众所周知且普遍建议的
audioManager.setMode(audioNormalState ?
AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
Run Code Online (Sandbox Code Playgroud)
问题是我并不总是在模式切换后播放音频,我必须等待并且不确定多久,甚至可能是几分钟。我做了一些循环记录,MODE_IN_COMMUNICATION
只要用户在某些搭载 Android 9 的摩托罗拉上的应用程序中处于“电话通话模式”,模式就会保留,但在配备Android 12 的 Pixel 3 上,6 秒后模式会自动切换回MODE_NORMAL
当没有播放任何内容时。没有执行额外的代码(如某些侦听器),没有额外的(系统)日志。当我在切换到MODE_IN_COMMUNICATION
模式后 1 秒开始播放音频时,只要播放音频(甚至超过 6 秒),它就不会自动切换,但在完成模式后也会自动切换到MODE_NORMAL
.
我的应用程序可以发出某种实时语音呼叫(命令),但也可以“发出蜂鸣声”一些信号模式,并且还有一个历史记录功能,可以按时间顺序再次播放所有发声动作。如果这只是语音,那么MODE_IN_COMMUNICATION
仅在通话期间切换到和返回可能就足够了,但是如何处理非常重要的SoundPool
广告歌曲,我是否也必须为它们切换模式?(或者对于历史播放,这是一个混合)据我所知模式切换并不快(在某些设备上甚至是几秒),所以我可能会对数百毫秒的短信号应用一些显着的延迟(没办法,每个毫秒都是至关重要!)或者即使在“电话呼叫模式”下,当模式没有“足够快”改变时(用户不会高兴),我也冒着大声播放信号/语音的风险。我依赖于固定设置(但根据应用程序状态和设置进行配置)MODE_IN_COMMUNICATION
,该设置一直有效到 Android 12...(可以确认 Pixel 和 Samsung 上的新/错误行为)
目前使用的切换音频模式+配置的方法如下,值得注意的是,该setSpeakerphoneOn
方法在 Android 12 上也并不总是有效。至少在打开时不起作用MODE_NORMAL
,这是现在默认的自动切换回模式,也是isSpeakerphoneOn
第false
一个开始,但我所有的音频源实际上都大声播放......
// forceAudioNormalState = true only when app exit!
public static void resolveLoudState(AudioManager audioManager, boolean forceAudioNormalState) {
boolean silentPhoneCallMode = isPhoneCallModeEnabled(); // phone call GUI, only ear-friendly volume!!
boolean silentHeadset = HeadsetPlugReceiver.isHeadsetPlugged &&
!HeadsetPlugReceiver.forceSpeakerWhenHeadsetOn; // headset plugged, but "muted", force speaker
boolean silentBluetooth = BluetoothController.isAudioDeviceConnected() &&
!audioManager.isBluetoothScoOn(); // bt headset plugged, but "muted", force speaker
boolean loud = true; // by default
if (silentPhoneCallMode || silentHeadset || silentBluetooth) loud = false;
String log = String.format("resolveLoudState play loud: %s," +
" silentPhoneCallMode: %s, silentHeadset: %s, silentBluetooth: %s",
loud, silentPhoneCallMode, silentHeadset, silentBluetooth);
Timber.i(log);
audioManager.setMode(forceAudioNormalState ?
AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(loud);
// even if deprecated this still works! fake wired headset on even for bt
audioManager.setWiredHeadsetOn(!loud && (silentHeadset || silentBluetooth));
// not loud and any headset connected and "muted"
}
Run Code Online (Sandbox Code Playgroud)
请注意,在上面的代码片段中没有有关当前播放状态的标志/信息,只有应用程序状态/配置
我想自己管理这些模式并决定使用哪个音频输出,或者也许有其他方法可以强制播放所有音频(AudioTrack
、SoundPool
、MediaPlayer
等ExoPlayer
),并调整耳朵友好的音量?
编辑:刚刚注意到当模式自动切换到MODE_NORMAL
并且我将开始播放时STREAM_VOICE_CALL
,它将自动切换到MODE_IN_COMMUNICATION
(有一些小但显着的延迟)并在完成音频后再次重置...这是整个系统的一些新的未记录的行为,变得非常不友好、有 bug 且不清楚的 API...
edit2:这看起来像相关问题
附言。我注意到MediaSession
Android 12 设备上的应用程序(例如音乐播放器)有一个新选项,Notification
可以在连接有线/BT 耳机/耳机时直接选择扬声器/耳机,但我根本不使用会话 API。额外问题:有相应的 API 吗?
找到了我自己的问题的一些答案,与社区分享
6秒自动切换模式是Android 12中的新功能,仅起作用if (mode == AudioSystem.MODE_IN_COMMUNICATION)
(查看与MSG_CHECK_MODE_FOR_UID
flag相关的流程)。这应该有助于在应用程序退出后MODE_IN_COMMUNICATION
设置AudioManager
和离开,这会扰乱全局/系统级音频路由。还有一个全新的AudioManager.OnModeChangedListener
称为模式(自动)更改时
事实setSpeakerphoneOn
证明已被弃用,即使这没有在文档中标记...我们有新方法setCommunicationDevice(AudioDeviceInfo)
,在其描述中我们有关于startBluetoothSco()
和弃用stopBluetoothSco()
的信息setSpeakerphoneOn(boolean)
。我正在使用所有三种方法,现在在 Android 12 上我正在迭代getAvailableCommunicationDevices()
,比较每个项目的类型,如果需要找到我正在调用的类型setCommunicationDevice(targetAudioDeviceInfo)
。我现在根本不切换音频模式,保持在MODE_NORMAL
. 我的所有流都是AudioManager.STREAM_VOICE_CALL
类型(如果适用)
用于内置耳机音频播放。我们使用的“耳朵友好模式”
if (earpieceMode) {
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(false); // call AFTER setMode
}
Run Code Online (Sandbox Code Playgroud)
这在 Android 12 上无法可靠运行(多次扬声器状态切换后)。现在我正在使用下面的代码(综合片段)
ArrayList<Integer> targetTypes = new ArrayList<>();
//add types according to needs, may be few in order of importance
if (bluetoothScoConnected) {
targetTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
} else if (wiredHeadsetConnected) {
if (isUsbHeadset) {
targetTypes.add(AudioDeviceInfo.TYPE_USB_HEADSET);
targetTypes.add(AudioDeviceInfo.TYPE_USB_DEVICE);
targetTypes.add(AudioDeviceInfo.TYPE_USB_ACCESSORY);
} else {
targetTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADSET);
targetTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADPHONES);
}
} else if (earpieceMode) {
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
} else { // play out loud
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
Boolean result = null;
List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
outer:
for (Integer targetType : targetTypes) {
for (AudioDeviceInfo device : devices) {
if (device.getType() == targetType) {
result = audioManager.setCommunicationDevice(device);
Log.i("AUDIO_MANAGER", "setCommunicationDevice type:" + targetType + " result:" + result);
break outer;
}
}
}
if (result == null) {
Log.i("AUDIO_MANAGER", "setCommunicationDevice targetType NOT FOUND!!");
}
Run Code Online (Sandbox Code Playgroud)
值得一提的是蓝牙 SCO 耳机盒 - 刚与设备连接/配对时,我的所有配件都被识别为AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
类型 ( getCommunicationDevice()
)。我确实想要 SCO,它在 A2DP 连接后几秒钟内没有列出getAvailableCommunicationDevices()
,所以我留下了一些倒计时器,它检查(间隔 2 秒)并等待AudioDeviceInfo.TYPE_BLUETOOTH_SCO
几秒(我设置了 16),然后我'当出现在列表中或只是关闭计时器时切换到此类型
归档时间: |
|
查看次数: |
3870 次 |
最近记录: |