Android接收蓝牙听诊器OnCharacteristicChanged连续字节流的音频处理求助
问题:Android应用集成Litmann蓝牙听诊器:解析音频流并播放
我正在开发Android应用,与带蓝牙功能的Litmann听诊器做集成,已经完成设备配对并开启了Characteristic通知。开启通知后,每秒会收到数百次Characteristic changed通知,推测这是听诊器传感器传输的音频流。现在需要了解接收的音频格式,以及如何在应用中播放该音频。
每次Characteristic changed通知接收的字节样本如下(示例):
00 42 11 89 99 BB CA AA AA 80 0A 99 9E BB CD CA A9 01 99 90 08 A9 8A CB A9 10 11 02 20 11 03 53 35 42 02 32 42 21 43 53 23 34 33 34 33 53 35 43 34 34 33 03 34 32 34 42 23 34 32 22 32 21 08 91 90 90 90 99 04 9B BB BD DA BB EB BB BC BB AB CB BC BD BA BB BB 05 CB BC BA CB AB CB AA 9B B9 BB 9A BB BB AB 99 11 06 BA 01 BB 91 B0 91 90 BB BA 99 99 A9 99 BA BB A9
我的代码示例
public static final UUID stethoscopeServiceUuid = UUID.fromString("##respectiveUUID##"); public static final UUID stethoscopeCharacteristicUuid = UUID.fromString("##respectiveUUID##"); public static final UUID generalDescriptorUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); ByteArrayOutputStream stethescopeStream = new ByteArrayOutputStream(); private void handleConnectionStateChange(BluetoothGatt gatt, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices(); } } private void handleServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic stethoscopeCharacteristic = gatt.getService(stethoscopeServiceUuid).getCharacteristic(stethoscopeCharacteristicUuid); gatt.setCharacteristicNotification(stethoscopeCharacteristic, true); BluetoothGattDescriptor descriptor = stethoscopeCharacteristic.getDescriptor(generalDescriptorUuid); descriptor.setValue(BluetoothGattDescriptor .ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); } } private void handleCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value){ handleStethoscopeValueReceived(gatt.getDevice().getName(),characteristic, value); } private void handleStethoscopeValueReceived(String deviceName, BluetoothGattCharacteristic characteristic, byte[] value) { //value //00 42 11 89 99 BB CA AA AA 80 0A 99 9E BB CD CA A9 //01 99 90 08 A9 8A CB A9 10 11 02 20 11 03 53 35 42 //02 32 42 21 43 53 23 34 33 34 33 53 35 43 34 34 33 //03 34 32 34 42 23 34 32 22 32 21 08 91 90 90 90 99 //04 9B BB BD DA BB EB BB BC BB AB CB BC BD BA BB BB //05 CB BC BA CB AB CB AA 9B B9 BB 9A BB BB AB 99 11 //06 BA 01 BB 91 B0 91 90 BB BA 99 99 A9 99 BA BB A9 //.... stethescopeStream.write(Arrays.copyOfRange(value, 0, value.length)); }
解答
1. 解析音频格式
从你提供的字节样本来看,每个数据包的第一个字节是帧序号(00、01、02...),剩余部分为音频采样数据。Litmann蓝牙听诊器的音频参数通常符合医疗设备标准:
- 采样率:8kHz或16kHz(听诊器常用8kHz)
- 位深:16位有符号整数(需验证字节序,大概率为小端模式)
- 通道数:单声道(传感器为单通道采集)
验证方法:
- 截取数据包序号后的字节,每2字节拼接为一个16位整数,将这些整数生成波形图,若呈现连续声波形态,则说明是16位采样格式。
- 统计每秒接收的总采样数:比如每秒收到100个数据包,每个包含8个采样(16字节数据),总采样数为8000,对应8kHz采样率。
2. 音频播放实现步骤
步骤1:修正数据收集逻辑
当前代码直接写入整个数据包,需跳过首字节的帧序号,只保留音频数据:
private void handleStethoscopeValueReceived(String deviceName, BluetoothGattCharacteristic characteristic, byte[] value) { // 跳过首字节的帧序号,取后续音频数据 byte[] audioData = Arrays.copyOfRange(value, 1, value.length); stethescopeStream.write(audioData, 0, audioData.length); }
步骤2:初始化AudioTrack播放组件
使用Android原生AudioTrack播放原始PCM音频,配置参数需匹配听诊器的音频格式:
private AudioTrack audioTrack; private void initAudioTrack() { // 假设采用8kHz采样率、16位单声道、小端编码 int sampleRate = 8000; int channelConfig = AudioFormat.CHANNEL_OUT_MONO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); // 初始化AudioTrack,采用流模式播放 audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, bufferSize * 2, // 放大缓冲避免卡顿 AudioTrack.MODE_STREAM); audioTrack.play(); }
步骤3:实时写入音频数据
修改数据处理逻辑,收到音频数据后直接写入AudioTrack实现实时播放:
// 在应用启动或连接听诊器后调用initAudioTrack() private void handleStethoscopeValueReceived(String deviceName, BluetoothGattCharacteristic characteristic, byte[] value) { // 检查AudioTrack状态,未初始化则创建 if (audioTrack == null || audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) { initAudioTrack(); } byte[] audioData = Arrays.copyOfRange(value, 1, value.length); // 写入AudioTrack实时播放 audioTrack.write(audioData, 0, audioData.length); // 同时保存到流(如果需要录制) stethescopeStream.write(audioData, 0, audioData.length); }
步骤4:处理数据包乱序问题
BLE通知可能出现数据包乱序,可通过帧序号维护一个缓存队列,将数据包按序号排序后再取出音频数据播放,避免音频卡顿或失真。
3. 额外优化建议
- 协商BLE MTU:默认MTU为20字节,可调用
requestMtu()协商更大的MTU(如512字节),减少数据包数量,降低传输延迟。 - 电源管理:高频BLE通知会消耗大量电量,建议在不需要播放时关闭Characteristic通知。
- 官方文档:优先获取Litmann官方开发者文档或SDK,里面会明确标注音频格式和传输协议(部分医疗设备需授权获取)。
内容的提问来源于stack exchange,提问作者Srikanth Sridharan
相关产品推荐
相关产品推荐

