说明:本文适用于 RK 作为蓝牙设备的蓝牙通话设计。硬件上,BT 芯片的 PCM 时钟和数据线直接连接到 RK 芯片端的一组 i2s 。软件上,需要将 BT 芯片注册成一个声卡。
BT HFP 通话数据流程:
Downlink:
远端信号 -> 蓝牙端 -> AP -> BT SoundCard -> PCM -> SOC SoundCard -> 输出设备
Uplink:
远端信号 <- 蓝牙端 <- AP <- BT SoundCard <- PCM <- SOC SoundCard <- 输入设备
(注:以下处理以 RK3326_ANDROID8.1 为例,其他安卓版本均可参考下述代码实现)
BT 声卡 DTS 配置:
1、蓝牙端 i2s 作主,提供 PCM 时钟:
bt-sound {
compatible = "simple-audio-card";
simple-audio-card,format = "dsp_b";
simple-audio-card,bitclock-inversion = <1>;
simple-audio-card,mclk-fs = <256>;
simple-audio-card,name = "rockchip,bt";
simple-audio-card,bitclock-master = <&sound2_master>;
simple-audio-card,frame-master = <&sound2_master>;
simple-audio-card,cpu {
sound-dai = <&i2s2_2ch>;//RK3326 i2s2_2ch
};
sound2_master:simple-audio-card,codec {
sound-dai = <&bt_sco>;
};
};
bt_sco: bt-sco {
compatible = "delta,dfbmcs320";
#sound-dai-cells = <0>;
status = "okay";
};
2、RK i2s 作主,提供 PCM 时钟:
bt-sound {
compatible = "simple-audio-card";
simple-audio-card,format = "dsp_b";
simple-audio-card,bitclock-inversion = <1>;
simple-audio-card,mclk-fs = <256>;
simple-audio-card,name = "rockchip,bt";
simple-audio-card,cpu {
sound-dai = <&i2s2_2ch>;//RK3326 i2s2_2ch
};
simple-audio-card,codec {
sound-dai = <&bt_sco>;
};
};
bt_sco: bt-sco {
compatible = "delta,dfbmcs320";
#sound-dai-cells = <0>;
status = "okay";
};
HAL层处理:
hardware/rockchip/audio/tinyalsa_hal
diff --git a/tinyalsa_hal/audio_hw.c b/tinyalsa_hal/audio_hw.c
index 0a35b80..8fef004 100755
--- a/tinyalsa_hal/audio_hw.c
+++ b/tinyalsa_hal/audio_hw.c
@@ -36,7 +36,7 @@
* @date 2015-08-24
*/
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardwareTiny"
#include "alsa_audio.h"
@@ -47,13 +47,24 @@
#include "audio_bitstream.h"
#include "audio_setting.h"
+
+#include "speex/speex_echo.h"
+#include "speex/speex_preprocess.h"
+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+//#define BT_HFP_DUMP_FAR_IN //touch /data/fd_far_in.pcm 8K
+//#define BT_HFP_DUMP_NEAR_OUT //touch /data/fd_near_out.pcm 48k
+//#define BT_HFP_DUMP_NEAR_IN //touch /data/fd_near_in.pcm 48k
+//#define BT_HFP_DUMP_FAR_OUT //touch /data/fd_far_out.pcm 8k
+
//#define ALSA_DEBUG
#ifdef ALSA_IN_DEBUG
FILE *in_debug;
#endif
+bool hfp_status = false;
+
int in_dump(const struct audio_stream *stream, int fd);
int out_dump(const struct audio_stream *stream, int fd);
@@ -123,6 +134,432 @@ int get_input_source_id(audio_source_t source)
}
}
+
+int SCO_PCM_FAR_IN = 1; // read pcm date for bt pcm sound card
+int SCO_PCM_FAR_OUT = 1; // out to bt pcm sound card
+int SCO_PCM_NEAR_IN = 0; // mic in sound card
+int SCO_PCM_NEAR_OUT = 0; // speaker out sound card
+//#define SPEEX_AEC
+#ifdef SPEEX_AEC
+//#define AEC_DUMP
+#endif
+void volume_process(const void *buffer, size_t length, float volume) {
+
+ short * buffer_end = (short*)buffer + (length/2);
+ short * pcmData = (short *)buffer;
+
+ while (pcmData < buffer_end) {
+ *pcmData = (short)((float)*pcmData * volume);
+ ++pcmData;
+ }
+}
+
+#define INT16_MIN (-32768)
+#define INT16_MAX (32767)
+/*left & right channel right mix data*/
+void stereo_lr_mix(void * indata, int bytes)
+{
+ int i = 0;
+ float channell = 0.0;
+ float channelr = 0.0;
+ float channelmix = 0.0;
+ short *buffer = (short *)indata;
+
+ for (i = 0; i < bytes / 2 ; i = i + 2) {
+ channell = (float)(((short *)buffer)[i]);
+ channelr = (float)(((short *)buffer)[i + 1]);
+ if ((channell > 0.0) && (channelr > 0.0)) {
+ channelmix = (channell + channelr) - ((channell * channelr) / INT16_MAX);
+ } else if ((channell < 0.0) && (channelr < 0.0)) {
+ channelmix = (channell + channelr) - ((channell * channelr) / INT16_MIN);
+ } else {
+ channelmix = channell + channelr;
+ }
+ ((short *)buffer)[i] = ((short *)buffer)[i + 1] = (short)channelmix;
+ }
+}
+/*left channel is noise data so copy right to left*/
+void stereo_rtol(void * indata, int bytes)
+{
+ int i = 0;
+ short