You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android平台USB外接MIDI控制器音频端口检测及多输出疑问

问题背景

我正在开发一款流媒体音乐应用,通过USB连接了一台带有2个3.5mm音频接口(一个音箱接口、一个耳机接口)的MIDI控制器,已完成连接与通信。Android自动将音箱接口设为默认音频端口,当音箱或耳机插入该默认接口时,应用运行正常可播放音乐,但插入第二个耳机接口时无声音输出。

在三星Galaxy Tab S8设备上,使用AudioManager.getDevices()检测到连接控制器后存在3个设备:

  • Type 2(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
  • Type 11(AudioDeviceInfo.TYPE_USB_DEVICE)
  • Type 18(AudioDeviceInfo.TYPE_TELEPHONY)
    连接后选中的设备为Type 11,音频输出至Android自动选择的主端口。

已在清单文件中添加权限:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

尝试用以下代码检测第二个耳机接口插入事件,但未捕获到相关信息:

AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for(AudioDeviceInfo deviceInfo : audioDevices){
    if(deviceInfo.getType()==AudioDeviceInfo.TYPE_WIRED_HEADPHONES){
        Log.d(TAG, "AudioDeviceInfo.TYPE_WIRED_HEADPHONES");
    } else if(deviceInfo.getType()==AudioDeviceInfo.TYPE_WIRED_HEADSET) {
        Log.d(TAG, "AudioDeviceInfo.TYPE_WIRED_HEADSET");
    } else {
        Log.d(TAG, "something else - " + deviceInfo.getType());
    }
}

Logcat日志显示:I/AudioPathManager: loadAudioPath() : loaded audio path is usb (6),推测Android自动将第一个音频端口设为默认。

疑问

  1. 如何检测耳机插入第二个接口的事件?
  2. 是否有可用的辅助库?
  3. 是否可获取USB外接设备的所有音频端口并实现同时输出音频?

问题解答

1. 检测第二个耳机接口插入事件

你当前的代码只检测了独立的有线耳机设备类型,但第二个接口属于USB设备的子端口,不会被识别为TYPE_WIRED_HEADPHONES或TYPE_WIRED_HEADSET。要检测这类子端口的状态变化,推荐以下方案:

  • 使用AudioDeviceCallback监听设备变化(Android 7.0+):这是最可靠的方式,通过注册回调监听音频设备的连接/断开事件。当USB设备的子端口状态变化时,会触发onAudioDevicesAdded()或onAudioDevicesRemoved(),你可以在回调中解析AudioDeviceInfo的详细参数,比如getPortNames()(Android 10+)或getPortInfo()来区分不同的子端口。
    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    AudioDeviceCallback callback = new AudioDeviceCallback() {
        @Override
        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
            for (AudioDeviceInfo device : addedDevices) {
                if (device.getType() == AudioDeviceInfo.TYPE_USB_DEVICE) {
                    // 解析USB设备的端口信息,判断第二个接口是否插入
                    Log.d(TAG, "USB设备端口更新: " + Arrays.toString(device.getPortNames()));
                }
            }
        }
    
        @Override
        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
            // 处理端口移除逻辑
        }
    };
    audioManager.registerAudioDeviceCallback(callback, null);
    
  • 轮询USB设备端口属性:定时调用getDevices(),对比前后AudioDeviceInfo的getChannels()、getProductName等参数变化,判断第二个接口是否插入。

2. 可用的辅助库

Android官方没有专门针对多USB音频端口的辅助库,但可以借助以下工具实现更精准的控制:

  • Android USB Host API:直接与USB设备通信,读取设备的UAC(USB Audio Class)协议描述符,手动识别和控制各个音频端口,需要你熟悉USB音频协议细节。
  • 底层音频框架:使用AAudio(Android 8.0+)或OpenSL ES,这些API提供了更细粒度的音频设备控制能力,能直接指定输出到USB设备的特定端口。

3. 获取USB外接设备的所有音频端口并实现同时输出

可以实现,但依赖Android版本和硬件支持:

获取所有端口

在Android 10及以上,通过AudioDeviceInfo.getPortInfo()可以获取USB设备的所有音频端口。每个AudioPortInfo对象包含端口方向、通道数、类型等信息,可用于区分两个3.5mm接口。

同时输出音频

需要为每个端口创建独立的音频播放轨道,并指定对应输出端口:

  1. 枚举USB设备的所有输出端口,通过AudioPortInfo记录每个端口的标识。
  2. 为每个端口创建AudioTrack实例,在构建时通过setAudioDevice()指定输出端口。
  3. 同时启动多个AudioTrack播放音频数据。

注意:部分设备或USB音频硬件可能不支持多端口同时输出,可通过AudioManager.isDeviceSupported()提前检查兼容性。

示例代码片段:

// 找到连接的USB音频设备
AudioDeviceInfo usbDevice = null;
AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo device : devices) {
    if (device.getType() == AudioDeviceInfo.TYPE_USB_DEVICE) {
        usbDevice = device;
        break;
    }
}

if (usbDevice != null) {
    AudioPortInfo[] portInfos = usbDevice.getPortInfo();
    for (AudioPortInfo portInfo : portInfos) {
        if (portInfo.getDirection() == AudioPortInfo.DIRECTION_OUTPUT) {
            // 为当前端口创建AudioTrack
            AudioAttributes attributes = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build();

            AudioFormat format = new AudioFormat.Builder()
                    .setSampleRate(44100)
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
                    .build();

            AudioTrack track = new AudioTrack.Builder()
                    .setAudioAttributes(attributes)
                    .setAudioFormat(format)
                    .setBufferSizeInBytes(AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT))
                    .setAudioDevice(portInfo) // 指定输出到当前端口
                    .build();

            // 加载音频数据并启动播放
            // track.write(audioData, 0, audioData.length);
            // track.play();
        }
    }
}

内容的提问来源于stack exchange,提问作者Vlad Petreaca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:45:10