Line-in、SPDIF与声卡功能
本文档介绍杰理(Jieli)Android 蓝牙 SDK 中 Line-in(AUX 外接音源)、SPDIF(数字音频输入) 与 声卡(Sound Card) 三大音频外设功能的实现机制、控制流程与 API 用法。这些功能均通过 RCSPController 与设备建立 RCSP(Remote Control Serial Protocol)通信,并由 BTRcspEventCallback 事件回调驱动 UI 更新,是设备端音频输入通道在 App 侧的完整控制入口。
Purpose and Scope
本页面覆盖以下能力的完整实现链路:
- Line-in / AUX 模式:检测设备是否支持 AUX、当前是否处于 AUX 模式、获取外接设备播放状态、播放/暂停控制;
- SPDIF 数字音频输入:查询 SPDIF 信息、播放/暂停、切换音源(HDMI / 光纤 / 同轴),由
SPDIFControlViewModel封装为 LiveData 数据流; - 声卡功能:声卡状态查询、声卡 EQ 获取与设置、声卡功能参数下发,以及
JLChipFlag.JL_CHIP_FLAG_695X_SOUND_CARD芯片类型判定。
不涉及的内容(请参考兄弟页面):蓝牙设备连接与 RCSP 协议底层封装(见"设备连接"相关页面)、EQ 通用效果调节(见"EQ 均衡器"页面)、以及 USB 声卡等未在本仓库实现的能力。
Overview
在杰理蓝牙音频方案中,设备端芯片可以同时具备多种音频输入通道:蓝牙 A2DP、Line-in(3.5mm 外接模拟音源,也称 AUX)、SPDIF(Sony/Philips Digital Interface,承载 HDMI/光纤/同轴数字音频信号)以及 USB 声卡模式。App 端通过 RCSPController(单例)向设备下发 RCSP 指令,设备解析指令后切换音频路由或返回状态,再通过 BTRcspEventCallback 回调把结果推送给 App。
三个功能在实现上遵循同一套**「指令-回调」模式**:
- App 调用
RCSPController的getXxx/setXxx方法发送指令; - 指令执行结果通过
OnRcspActionCallback<Boolean>返回(仅表示指令是否送达/执行成功); - 真正的数据内容通过
BTRcspEventCallback的onXxxChange系列回调异步到达; - UI 层(ViewModel / Activity)把回调数据封装为
LiveData或直接刷新界面。
SPDIFControlViewModel 是仓库内唯一完整的 ViewModel 层实现,展示了这套模式在生产代码中的标准写法;LineinDemo 与 SoundCardDemo 是官方提供的测试用例(demo),可作为接入参考。
Architecture
flowchart TD
subgraph sg_App["App 层 (btsmart)"]
SPDIFVM["SPDIFControlViewModel"]
UI["Activity / Fragment"]
LDM["LineinDemo (test)"]
SCD["SoundCardDemo (test)"]
UH["UIHelper.isSoundCardType()"]
end
subgraph sg_SDK["SDK 层 (com.jieli.bluetooth)"]
RCSP["RCSPController (单例)"]
CB["BTRcspEventCallback"]
ACB["OnRcspActionCallback"]
DI["DeviceInfo"]
SPI["SPDIFPlayStatusInfo / SPDIFAudioSourceInfo"]
SC["SoundCard 状态/EQ 数据"]
end
subgraph sg_Dev["设备端"]
DEV["蓝牙音频设备 (695X 等)"]
end
SPDIFVM -->|"注册/注销事件"| CB
SPDIFVM -->|"getSPDIFInfo / setSPDIFPlayStatusInfo / setSPDIFSPDIFAudioSourceInfo"| RCSP
LDM -->|"isAuxEnable / getAuxStatusInfo / auxPlayOrPause"| RCSP
SCD -->|"getSoundCardStatusInfo / getSoundCardEqInfo / setSoundCardEqInfo / setSoundCardFunction"| RCSP
UH -->|"JLChipFlag.JL_CHIP_FLAG_695X_SOUND_CARD 判定"| RCSP
RCSP -->|"RCSP 指令下发 (BLE/经典蓝牙)"| DEV
DEV -->|"状态上报"| RCSP
RCSP -->|"解析到 DeviceInfo 缓存"| DI
DI --> SPI
DI --> SC
RCSP -->|"事件分发"| CB
CB -->|"onSPDIFPlayStatusChange / onSPDIFAudioSourceInfoChange"| SPDIFVM
CB -->|"onAuxStatusChange / onSoundCardStatusChange / onSoundCardEqChange"| LDM
CB -->|"onSoundCardStatusChange / onSoundCardEqChange"| SCD
SPDIFVM -->|"MutableLiveData 推送"| UI
ACB -->|"onSuccess / onError"| LDM
ACB -->|"onSuccess / onError"| SCD
架构解读: 上层 App 组件(ViewModel、Demo 用例、工具类)只依赖 RCSPController 单例与 BTRcspEventCallback 抽象,不直接接触蓝牙协议细节。RCSPController 负责指令序列化、发送、响应解析,并把设备状态缓存进 DeviceInfo;DeviceInfo 持有 SPDIFPlayStatusInfo、SPDIFAudioSourceInfo 等状态对象,供 ViewModel 初始化时同步读取。这种「单例控制器 + 事件回调 + 状态缓存」的设计把协议复杂度隔离在 SDK 内部,使 App 层代码保持简洁且可测试。
Main Content
1. Line-in(AUX)外接音源功能
Line-in 功能在仓库中体现为 AUX(Auxiliary)模式,设备通过 3.5mm 接口接收外部模拟音频,App 侧负责查询支持能力、查询播放状态并控制播放/暂停。参考实现位于 LineinDemo.java。
1.1 能力与模式判定
public boolean isSupportAuxMode() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//获取当前操作设备
BluetoothDevice usingDevice = controller.getUsingDevice();
if (null == usingDevice) return false;
DeviceInfo deviceInfo = controller.getDeviceInfo(usingDevice);
if (null == deviceInfo) return false; //设备未初始化
return deviceInfo.isAuxEnable();
}
public boolean isDeviceInAuxMode() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//获取当前操作设备
BluetoothDevice usingDevice = controller.getUsingDevice();
if (null == usingDevice) return false;
DeviceInfo deviceInfo = controller.getDeviceInfo(usingDevice);
if (null == deviceInfo) return false; //设备未初始化
return deviceInfo.getCurFunction() == AttrAndFunCode.SYS_INFO_FUNCTION_AUX;
}
Source: LineinDemo.java
设计意图:isAuxEnable() 读取的是设备能力位(设备固件是否编译了 AUX 功能),而 getCurFunction() 与 AttrAndFunCode.SYS_INFO_FUNCTION_AUX 比较得到的是设备当前运行的功能模式。两者含义不同——前者是"硬件/固件支持",后者是"当前是否处于该模式",UI 层通常先用前者决定是否展示入口,再用后者决定入口的选中状态。
1.2 播放状态查询与控制
void getAuxStatusInfo() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//添加蓝牙RCSP事件监听器
controller.addBTRcspEventCallback(new BTRcspEventCallback() {
@Override
public void onAuxStatusChange(BluetoothDevice device, boolean isPlay) {
//此处回调外接设备播放状态
}
});
//执行获取外接设备播放状态功能并等待结果回调
controller.getAuxStatusInfo(controller.getUsingDevice(), new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
//结果将会在BTRcspEventCallback#onAuxStatusChange回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
void auxPlayOrPause() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//执行外接设备播放或暂停功能并等待结果回调
controller.auxPlayOrPause(controller.getUsingDevice(), new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
Source: LineinDemo.java
设计意图:getAuxStatusInfo 的 OnRcspActionCallback<Boolean> 只确认"指令执行成功与否",真正的播放状态 isPlay 通过 onAuxStatusChange 事件回调返回。这种查询指令与状态事件分离的设计允许设备在播放状态自发变化(如拔出 AUX 线)时也能主动通知 App,而不仅是应答查询。
2. SPDIF 数字音频输入功能
SPDIF 功能是仓库中实现最完整的一块,由 SPDIFControlViewModel.java 承担。它继承自 BtBasicVM,通过 AndroidX MutableLiveData 向界面暴露两类状态:SPDIFPlayStatusInfo(播放状态)与 SPDIFAudioSourceInfo(音频源类型)。
2.1 ViewModel 初始化与状态同步
public SPDIFControlViewModel() {
mRCSPController.addBTRcspEventCallback(callback);
DeviceInfo deviceInfo = mRCSPController.getDeviceInfo();
SPDIFPlayStatusInfo spdifPlayStatusInfo = deviceInfo.getSPDIFPlayStatusInfo();
if (spdifPlayStatusInfo != null) {
mSPDIFPlayStatusInfo = spdifPlayStatusInfo;
spdifPlayStatusInfoLiveData.postValue(spdifPlayStatusInfo);
}
SPDIFAudioSourceInfo spdifAudioSourceInfo = deviceInfo.getSPDIFAudioSourceInfo();
if (spdifAudioSourceInfo != null) {
mSPDIFAudioSourceInfo = spdifAudioSourceInfo;
spdifAudioSourceInfoLiveData.postValue(spdifAudioSourceInfo);
}
}
@Override
protected void release() {
mRCSPController.removeBTRcspEventCallback(callback);
super.release();
}
Source: SPDIFControlViewModel.java
设计意图:构造时先注册事件回调、再从 DeviceInfo 缓存中同步读取最新状态并立即 postValue,保证 ViewModel 重建(如旋转屏幕)后界面能立刻显示设备当前真实状态,无需等待网络往返;release() 中对称地移除回调,避免内存泄漏。
2.2 播放控制与音源切换
/**
* 播放暂停
*/
public void playOrPause() {
SPDIFPlayStatusInfo spdifPlayStatusInfo = new SPDIFPlayStatusInfo();
spdifPlayStatusInfo.setPlayStatus(mSPDIFPlayStatusInfo.getPlayStatus() == SPDIFPlayStatusInfo.PLAY_STATUS_PAUSE ? SPDIFPlayStatusInfo.PLAY_STATUS_PLAY : SPDIFPlayStatusInfo.PLAY_STATUS_PAUSE);
mRCSPController.setSPDIFPlayStatusInfo(getConnectedDevice(), spdifPlayStatusInfo, null);
}
public void getSPDIFInfo() {
mRCSPController.getSPDIFInfo(mRCSPController.getUsingDevice(), null);
}
public void setAudioSourceHDMI() {
SPDIFAudioSourceInfo spdifAudioSourceInfo = new SPDIFAudioSourceInfo();
spdifAudioSourceInfo.setAudioSource(SPDIFAudioSourceInfo.AUDIO_SOURCE_HDMI);
mRCSPController.setSPDIFSPDIFAudioSourceInfo(getConnectedDevice(), spdifAudioSourceInfo, null);
}
public void setAudioSourceOptical() {
SPDIFAudioSourceInfo spdifAudioSourceInfo = new SPDIFAudioSourceInfo();
spdifAudioSourceInfo.setAudioSource(SPDIFAudioSourceInfo.AUDIO_SOURCE_OPTICAL);
mRCSPController.setSPDIFSPDIFAudioSourceInfo(getConnectedDevice(), spdifAudioSourceInfo, null);
}
public void setAudioSourceCoaxial() {
SPDIFAudioSourceInfo spdifAudioSourceInfo = new SPDIFAudioSourceInfo();
spdifAudioSourceInfo.setAudioSource(SPDIFAudioSourceInfo.AUDIO_SOURCE_COAXIAL);
mRCSPController.setSPDIFSPDIFAudioSourceInfo(getConnectedDevice(), spdifAudioSourceInfo, null);
}
Source: SPDIFControlViewModel.java
设计意图:playOrPause() 基于本地缓存的 mSPDIFPlayStatusInfo 计算目标状态(取反),再通过 setSPDIFPlayStatusInfo 下发——这种"读缓存-取反-写设备"的模式避免了先查询再下发的额外往返。音源切换(HDMI/光纤/同轴)通过复用同一个 setSPDIFSPDIFAudioSourceInfo 方法、仅改变 SPDIFAudioSourceInfo.AUDIO_SOURCE_* 常量实现,体现了参数化指令的设计:同一指令通道承载不同取值。
2.3 事件回调与设备切换
private final BTRcspEventCallback callback = new BTRcspEventCallback() {
@Override
public void onSPDIFPlayStatusChange(BluetoothDevice device, SPDIFPlayStatusInfo spdifPlayStatusInfo) {
mSPDIFPlayStatusInfo = spdifPlayStatusInfo;
spdifPlayStatusInfoLiveData.postValue(spdifPlayStatusInfo);
}
@Override
public void onSPDIFAudioSourceInfoChange(BluetoothDevice device, SPDIFAudioSourceInfo spdifAudioSourceInfo) {
mSPDIFAudioSourceInfo = spdifAudioSourceInfo;
spdifAudioSourceInfoLiveData.postValue(spdifAudioSourceInfo);
}
@Override
public void onSwitchConnectedDevice(BluetoothDevice device) {
super.onSwitchConnectedDevice(device);
getSPDIFInfo();
}
};
Source: SPDIFControlViewModel.java
设计意图:两个状态回调都先更新本地缓存、再 postValue 到 LiveData,保证 UI 与缓存始终一致。关键点在于重写 onSwitchConnectedDevice:当用户切换连接的设备时,主动调用 getSPDIFInfo() 重新拉取新设备的 SPDIF 状态——这是因为不同设备的状态不共享,必须在新设备上重新初始化,否则界面会显示上一台设备的过期数据。
3. 声卡(Sound Card)功能
声卡功能将蓝牙设备模拟为外部 USB/数字声卡,App 侧负责查询声卡状态、读取/设置 EQ、下发声卡功能参数。参考实现位于 SoundCardDemo.java。
3.1 声卡状态查询
@Test
void getSoundCardStatusInfo() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//注册蓝牙RCSP事件监听器
controller.addBTRcspEventCallback(new BTRcspEventCallback() {
@Override
public void onSoundCardStatusChange(BluetoothDevice device, long mask, byte[] values) {
//此处将会回调声卡状态
}
});
//执行获取声卡状态信息功能并等待结果回调
controller.getSoundCardStatusInfo(controller.getUsingDevice(), new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
//结果将会在BTRcspEventCallback#onSoundCardStatusChange回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
Source: SoundCardDemo.java
设计意图:声卡状态回调携带 long mask(功能位掩码,标识哪些功能参数发生变化)与 byte[] values(原始状态数据)。掩码机制让设备可以一次上报多项状态变更,App 通过 mask 判断需要刷新哪些 UI 项,避免对未变化项做无谓重绘。
3.2 声卡 EQ 与功能设置
@Test
void getSoundCardEqInfo() {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//注册蓝牙RCSP事件监听器
controller.addBTRcspEventCallback(new BTRcspEventCallback() {
@Override
public void onSoundCardEqChange(BluetoothDevice device, EqInfo eqInfo) {
//此处将会回调声卡效果信息
}
});
//执行获取声卡效果信息功能并等待结果回调
controller.getSoundCardEqInfo(controller.getUsingDevice(), new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
//结果将会在BTRcspEventCallback#onSoundCardEqChange回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
@Test
void setSoundCardEqInf(byte[] value) {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//执行设置声卡效果功能并等待结果回调
controller.setSoundCardEqInfo(controller.getUsingDevice(), value, new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
@Test
void setSoundCardFunction(byte index, int value) {
//获取RCSPController对象
RCSPController controller = RCSPController.getInstance();
//设置声卡功能并等待结果回调
controller.setSoundCardFunction(controller.getUsingDevice(), index, value, new OnRcspActionCallback<Boolean>() {
@Override
public void onSuccess(BluetoothDevice device, Boolean message) {
//成功回调
}
@Override
public void onError(BluetoothDevice device, BaseError error) {
//失败回调
//error - 错误信息
}
});
}
Source: SoundCardDemo.java
设计意图:声卡 EQ 的获取返回结构化对象 EqInfo(与通用 EQ 功能复用同一数据类型),而设置则接受原始 byte[] 参数——这是因为 EQ 曲线数据格式由固件定义,SDK 层透传避免重复解析。setSoundCardFunction(byte index, int value) 则是"功能索引 + 值"的通用设置通道,可用于各类开关与数值型声卡参数,保持协议的可扩展性。
3.3 声卡芯片类型判定
public static boolean isSoundCardType(int sdkType) {
return JLChipFlag.JL_CHIP_FLAG_695X_SOUND_CARD == sdkType;
}
Source: UIHelper.java
设计意图:UIHelper.isSoundCardType 通过 SDK 上报的芯片标志 JLChipFlag.JL_CHIP_FLAG_695X_SOUND_CARD 判断当前设备是否为 695X 系列声卡芯片。这类"类型判断"通常用于决定界面是否显示声卡相关入口——非声卡芯片的设备不应展示声卡设置项,从而避免用户对不支持功能的误操作。
Core Flow
SPDIF 控制完整时序
sequenceDiagram
participant UI as Activity (UI)
participant VM as SPDIFControlViewModel
participant RCSP as RCSPController
participant CB as BTRcspEventCallback
participant DEV as 蓝牙设备
Note over VM: 构造:注册回调 + 读取 DeviceInfo 缓存
VM->>RCSP: addBTRcspEventCallback(callback)
RCSP-->>VM: 缓存状态 postValue → LiveData
UI->>VM: observe(spdifPlayStatusInfoLiveData)
UI->>VM: 用户点击播放/暂停 → playOrPause()
VM->>VM: 读取本地缓存,计算目标 PlayStatus(取反)
VM->>RCSP: setSPDIFPlayStatusInfo(device, info, null)
RCSP->>DEV: RCSP 指令下发 (播放/暂停)
DEV-->>RCSP: 状态上报
RCSP-->>CB: onSPDIFPlayStatusChange(device, info)
CB-->>VM: 更新 mSPDIFPlayStatusInfo + postValue
VM-->>UI: LiveData 通知,界面刷新
Note over VM: 设备切换时
CB->>VM: onSwitchConnectedDevice(device)
VM->>RCSP: getSPDIFInfo() 重新拉取新设备状态
Line-in / 声卡查询时序
sequenceDiagram
participant APP as App (Demo/UI)
participant RCSP as RCSPController
participant CB as BTRcspEventCallback
participant DEV as 蓝牙设备
APP->>RCSP: getAuxStatusInfo(device, callback)
RCSP->>DEV: 查询指令
DEV-->>RCSP: 状态数据
RCSP-->>CB: onAuxStatusChange(device, isPlay)
CB-->>APP: 刷新播放状态
APP->>RCSP: auxPlayOrPause(device, callback)
RCSP->>DEV: 播放/暂停指令
DEV-->>RCSP: 执行结果
RCSP-->>APP: OnRcspActionCallback.onSuccess/onError
APP->>RCSP: getSoundCardStatusInfo(device, callback)
RCSP->>DEV: 声卡状态查询
DEV-->>RCSP: mask + values
RCSP-->>CB: onSoundCardStatusChange(device, mask, values)
CB-->>APP: 按 mask 刷新声卡 UI
流程要点: 所有功能遵循「发送指令 → OnRcspActionCallback 确认执行 → BTRcspEventCallback 推送数据」三段式流程。OnRcspActionCallback 的 Boolean 结果只代表指令是否执行成功,业务数据一律走事件回调;SDK 内部会把设备状态缓存进 DeviceInfo,因此 ViewModel 构造时可以直接同步初始化,无需每次进入页面都发起查询。
API Reference
以下 API 均由 RCSPController 单例提供(com.jieli.bluetooth.impl.rcsp.RCSPController),OnRcspActionCallback<Boolean> 的 onSuccess/onError 表示指令执行结果,BaseError 携带错误信息。
getAuxStatusInfo(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)
查询外接设备(AUX)播放状态。
- 参数:
device目标设备;callback指令结果回调(可为 null) - 返回:指令执行结果
Boolean;真实播放状态通过BTRcspEventCallback#onAuxStatusChange(BluetoothDevice, boolean isPlay)回调 - 典型场景:进入 AUX 页面时刷新播放/暂停按钮状态
auxPlayOrPause(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)
控制外接设备播放/暂停。
- 参数:
device目标设备;callback指令结果回调 - 返回:指令执行结果;播放状态变化由
onAuxStatusChange事件回调确认
getSPDIFInfo(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)
拉取 SPDIF 完整信息(播放状态 + 音频源类型)。
- 参数:
device目标设备;callback指令结果回调(可为 null) - 返回:数据经
onSPDIFPlayStatusChange/onSPDIFAudioSourceInfoChange事件回调送达 - 典型场景:
onSwitchConnectedDevice后重新同步新设备状态
setSPDIFPlayStatusInfo(BluetoothDevice device, SPDIFPlayStatusInfo info, OnRcspActionCallback<Boolean> callback)
设置 SPDIF 播放状态(播放/暂停)。
- 参数:
device目标设备;info携带PLAY_STATUS_PLAY或PLAY_STATUS_PAUSE的SPDIFPlayStatusInfo - 返回:指令执行结果;设备确认后经
onSPDIFPlayStatusChange回调
setSPDIFSPDIFAudioSourceInfo(BluetoothDevice device, SPDIFAudioSourceInfo info, OnRcspActionCallback<Boolean> callback)
设置 SPDIF 音频源类型。
- 参数:
device;info的audioSource取值为AUDIO_SOURCE_HDMI/AUDIO_SOURCE_OPTICAL/AUDIO_SOURCE_COAXIAL之一 - 返回:指令执行结果;设备确认后经
onSPDIFAudioSourceInfoChange回调
getSoundCardStatusInfo(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)
查询声卡状态。
- 参数:
device;callback指令结果回调 - 返回:状态数据经
onSoundCardStatusChange(BluetoothDevice, long mask, byte[] values)回调;mask为变更位掩码,values为原始状态字节
getSoundCardEqInfo(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)
获取声卡 EQ 效果信息。
- 参数:
device;callback指令结果回调 - 返回:
EqInfo经onSoundCardEqChange(BluetoothDevice, EqInfo)回调
setSoundCardEqInfo(BluetoothDevice device, byte[] value, OnRcspActionCallback<Boolean> callback)
设置声卡 EQ 效果。
- 参数:
device;value固件定义的 EQ 原始字节数据;callback指令结果回调 - 返回:指令执行结果
setSoundCardFunction(BluetoothDevice device, byte index, int value, OnRcspActionCallback<Boolean> callback)
设置声卡功能参数(通用功能索引通道)。
- 参数:
device;index功能索引(由固件协议定义);value功能值;callback指令结果回调 - 返回:指令执行结果
事件回调(BTRcspEventCallback 需重写的方法)
| 回调方法 | 触发时机 | 携带数据 |
|---|---|---|
onAuxStatusChange(device, isPlay) | AUX 播放状态变化 | boolean isPlay |
onSPDIFPlayStatusChange(device, info) | SPDIF 播放状态变化 | SPDIFPlayStatusInfo |
onSPDIFAudioSourceInfoChange(device, info) | SPDIF 音频源变化 | SPDIFAudioSourceInfo |
onSoundCardStatusChange(device, mask, values) | 声卡状态上报 | long mask + byte[] values |
onSoundCardEqChange(device, eqInfo) | 声卡 EQ 变化 | EqInfo |
onSwitchConnectedDevice(device) | 切换连接设备 | BluetoothDevice |
状态常量
| 常量 | 所属类 | 含义 |
|---|---|---|
PLAY_STATUS_PLAY / PLAY_STATUS_PAUSE | SPDIFPlayStatusInfo | SPDIF 播放/暂停状态 |
AUDIO_SOURCE_HDMI | SPDIFAudioSourceInfo | HDMI 数字音源 |
AUDIO_SOURCE_OPTICAL | SPDIFAudioSourceInfo | 光纤数字音源 |
AUDIO_SOURCE_COAXIAL | SPDIFAudioSourceInfo | 同轴数字音源 |
SYS_INFO_FUNCTION_AUX | AttrAndFunCode | 设备当前处于 AUX 功能模式 |
JL_CHIP_FLAG_695X_SOUND_CARD | JLChipFlag | 695X 声卡芯片类型标志 |
Configuration Options
本功能组不涉及独立的配置文件,其"配置"主要体现在设备能力位与芯片标志上:
| 配置/标志 | 类型 | 来源 | 说明 |
|---|---|---|---|
DeviceInfo.isAuxEnable() | boolean | 设备能力位 | 设备固件是否支持 AUX 模式,false 时不应展示 AUX 入口 |
DeviceInfo.getCurFunction() | int | 设备当前模式 | 与 SYS_INFO_FUNCTION_AUX 比较判断是否处于 AUX 模式 |
DeviceInfo.getSPDIFPlayStatusInfo() | SPDIFPlayStatusInfo | DeviceInfo 缓存 | SPDIF 播放状态缓存,ViewModel 构造时同步读取 |
DeviceInfo.getSPDIFAudioSourceInfo() | SPDIFAudioSourceInfo | DeviceInfo 缓存 | SPDIF 音频源缓存 |
JLChipFlag.JL_CHIP_FLAG_695X_SOUND_CARD | int | SDK 芯片标志 | 用于 UIHelper.isSoundCardType() 判断声卡芯片 |
Professional Notes
失败模式与边界情况
- 设备未连接/未初始化:
LineinDemo中对getUsingDevice()与getDeviceInfo()均做了 null 检查并返回false。实际 UI 接入时应同样处理,避免 NPE;若设备未初始化就调用getSPDIFInfo,指令将无法送达,OnRcspActionCallback.onError会携带BaseError返回。 - 回调未注册导致数据丢失:事件回调必须在指令发送之前注册(如
LineinDemo.getAuxStatusInfo先addBTRcspEventCallback再getAuxStatusInfo)。若先发指令后注册,设备响应到达时无人监听,查询结果会静默丢失。 - 设备切换状态串台:SPDIF 状态缓存属于单台设备,
SPDIFControlViewModel通过重写onSwitchConnectedDevice主动调用getSPDIFInfo()重新同步,防止显示上一台设备的过期状态。 playOrPause的取反竞态:playOrPause()依赖本地缓存mSPDIFPlayStatusInfo取反。若连续快速点击,缓存尚未被onSPDIFPlayStatusChange更新,可能产生两次相同的目标状态。UI 层应在收到回调前禁用按钮或做防抖处理。
并发与线程
RCSPController为全局单例,所有指令串行经蓝牙通道发送,SDK 内部处理指令排队;App 层无需额外加锁。- ViewModel 使用
postValue()(而非setValue())更新 LiveData,因为回调可能来自蓝牙 SDK 的工作线程,postValue是线程安全的。 release()中必须removeBTRcspEventCallback,否则 ViewModel 销毁后回调仍会触发,导致内存泄漏或对已销毁 UI 的更新。
扩展点
- 新增 SPDIF 音频源:在
SPDIFAudioSourceInfo中定义新常量并仿照setAudioSourceHDMI/Optical/Coaxial添加方法即可,无需改动协议通道。 - 新增声卡功能参数:通过通用通道
setSoundCardFunction(byte index, int value)扩展,只需在固件协议中定义新的index含义。 - 界面层可通过继承
BtBasicVM并复用mRCSPController的既有模式,为其他外设功能(如 FM、USB)建立同样的 ViewModel 封装。
测试覆盖
LineinDemo(isSupportAuxMode、isDeviceInAuxMode、getAuxStatusInfo、auxPlayOrPause)覆盖 AUX 能力判定、状态查询与控制路径。SoundCardDemo(getSoundCardStatusInfo、getSoundCardEqInfo、setSoundCardEqInf、setSoundCardFunction)覆盖声卡状态、EQ 读写与功能设置路径。- Demo 以
@Test标注,可在 JUnit 环境中作为集成测试用例运行,是接入第三方 App 的最佳参考范例。
Related Links
- SPDIFControlViewModel.java(SPDIF 控制 ViewModel 实现)
- LineinDemo.java(Line-in/AUX 测试用例)
- SoundCardDemo.java(声卡功能测试用例)
- UIHelper.java(声卡芯片类型判定工具)
- 相关兄弟页面:设备连接与 RCSP 协议(另见)、EQ 均衡器功能(另见)