我想播放一些音量lvl调整到耳朵aka的音频。“电话呼叫模式”。为此,我使用了著名的和普遍建议的
audioManager.setMode(audioNormalState ?
AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
问题是,我并不总是在模式切换后播放音频,我必须等待,不能确定多长时间,甚至可能是几分钟。我做了一些循环日志记录和MODE_IN_COMMUNICATION
模式只要用户在我的应用程序中处于“电话通话模式”,就会在Android 9的一些摩托罗拉上保持,但在Pixel 3上,Android 12在6秒后模式会在没有播放任何内容时自动切换回MODE_NORMAL
。没有执行额外的代码(如一些侦听器),没有额外的(系统)日志。当我在切换到MODE_IN_COMMUNICATION
模式后1秒开始播放音频时,只要播放音频(甚至超过6秒),它就不会自动切换,但在完成模式之后,也会自动切换到MODE_NORMAL
。
我的应用程序可以进行各种实时语音呼叫(命令),但也可以“哔哔”一些信号模式,并且还有一个历史记录功能,提供所有按时间顺序排列的声音制作操作,以便按顺序再次播放。如果这只是语音,那么仅在通话期间切换到MODE_IN_COMMUNICATION
和返回可能就足够了,但是如何处理非常重要的SoundPool
叮当声,我是否也必须为他们切换模式?(或者用于历史游戏,这是一个混合)AFAIK模式切换速度不快(在某些设备上甚至只有几秒钟),所以我可能会对短的数百毫秒信号应用一些显着的延迟(没办法,每个毫秒都至关重要!)或者即使在“电话呼叫模式”中,我也会冒着大声播放信号/语音的风险,当模式变化得不够“足够快”时(用户不会高兴)。我依靠设置固定(但根据应用程序状态和设置进行配置)MODE_IN_COMMUNICATION
,它一直工作到Android 12...(可以确认像素和三星上的新/错误行为)
下面目前使用的切换音频模式配置的方法,值得注意的是,setSpeakerphoneOn
方法在Android 12上也并不总是有效。至少在MODE_NORMAL
时不是这样,这是现在默认的自动切换回模式,也是SpeakerphoneOn
在第一次启动时是错误的
,但我的所有音频源实际上都是大声播放的......
// 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"
}
请注意,在上面的代码段中,没有关于当前播放状态的标志/信息,只有apps state/config
我想自己管理这些模式,并决定将使用哪个音频输出,或者可能有任何其他方法来强制播放所有音频(< code>AudioTrack 、< code>SoundPool 、< code>MediaPlayer 、< code>ExoPlayer等。)带耳友好的调节音量?
编辑:刚刚注意到,当模式自动切换到<code>mode_NORMAL</code>时,我将开始播放<code>STREAM_VOICE_CALL</code>它将自动切换到通信模式</code>(有一些小但显著的延迟),并在完成音频后再次重置…这是系统总体上的一些新的未记录行为,变得非常不友好、有漏洞和不清晰的API。。。
编辑2:这看起来像相关的问题
我注意到Android 12设备上的MediaSession
应用程序(例如音乐播放器)在通知
上有一个新选项,用于在连接有线/ bt耳机/耳机时选择扬声器/耳机,但我根本没有使用会话API。奖励问题:是否有API?
找到了我自己问题的一些答案,与社区分享
6 秒自动切换模式是 Android 12 中的一项新功能,仅当 (mode == AudioSystem.MODE_IN_COMMUNICATION)
时才有效(请查看与 MSG_CHECK_MODE_FOR_UID
标志相关的流程)。这应该有助于MODE_IN_COMMUNICATION
设置为AudioManager
并在应用程序退出后离开,这弄乱了全局/系统级音频路由。还有一个全新的AudioManager.OnModeChangedListener
,当模式(自动)更改时调用
而<code>SetSpeakerPhone</code>被证明是不推荐使用的,即使这在文档中没有标记……我们有了一个新方法<code>setCommunicationDevice(AudioDeviceInfo),在它的描述中,我们有关于<code〉startBluetoothSco()</code>、<code>stopBluetoothSco()</code>和<code>SetSpeakorPhone(boolean)</code>的信息。我正在使用这三种方法,现在在Android 12上,我正在迭代getAvailableCommunicationDevices()
,比较每个项目的类型,如果需要,我会调用setCommunicationDevice(targetAudioDeviceInfo)
。我现在根本不切换音频模式,而是保持模式_NORMAL
。我的所有流都是AudioManager。STREAM_VOICE_CALL
类型(如适用)
用于内置耳机音频播放,又名。我们正在使用的“耳友好模式”
if (earpieceMode) {
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(false); // call AFTER setMode
}
这在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!!");
}
值得一提的是,蓝牙SCO耳机盒-当刚与设备连接/配对时,我的所有配件都被识别为AudioDeviceInfo。TYPE_BLUETOOTH_A2DP
TYPE(getCommunicationDevice()
)。我确实想要SCO,在A2DP连接后的几秒钟内,它没有列在getAvailableCommunicationDevices()
中,因此我留下了一些倒计时计时器,它检查(间隔2秒)并等待AudioDeviceInfo。键入_ BLUETOOTH_
定义自己的索引模式 加载到 Elasticsearch 的每组数据都有一个索引模式(Index Pattern)。 在上一节中,为莎士比亚数据集创建了名为 shakespeare 的索引,为 accounts 数据集创建了名为 bank 的索引。一个 _索引模式_ 是可以匹配多个索引的带可选通配符的字符串。例如一般在通用日志记录中,一个典型的索引名称一般包含类似 YYYY.MM.DD 格式的日期信
使用JavaScript 可以从多个方面增强表单字段的易用性。其中,最常见的一种方式就是在用户填写完当前字段时,自动将焦点切换到下一个字段。通常,在自动切换焦点之前,必须知道用户已经输入了既定长度的数据(例如电话号码)。例如,美国的电话号码通常会分为三部分:区号、局号和另外4 位数字。为取得完整的电话号码,很多网页中都会提供下列3 个文本框: <input type="text" name="te
服务模式切换比较麻烦,需要您的Kubernetes支持,目前我们使用的是istio的方案,也就是说您需要在你的kubernetes上安装istio的相关服务,并且在我们的模版管理将istio所需要的几个模版配置上。才能开启此功能。 如果您没有安装Istio,可跳过此章。 依赖 在"模版管理"菜单找到Gateway、VritualService、InitContainer、IstioProxy这几个
好了,现在我们总结一下模式间切换的方法 其它模式 普通模式 Esc 普通模式 插入模式 i 在光标前插入 I 在行首插入 a 在光标后插入 A 在行末插入 o 在当前行之下新建行 O 在当前行之上新建行 r 替换当前字符 R 从当前字符开始替换 普通模式 命令模式 : 普通模式 可视模式 v 可视模式 V 可视行模式 Ctrl+v 可视块模式