RK系列SDK -- Android HFP蓝牙通话音频处理(蓝牙音箱方案)

说明:本文适用于 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 *buffer = (short *)indata;
+	for (i = 0; i < bytes / 2 ; i = i + 2) {
+		((short *)buffer)[i] = ((short *)buffer)[i + 1];
+	}
+}
+
+void stereo_ltor(void * indata, int bytes)
+{
+	int i = 0;
+	short *buffer = (short *)indata;
+	for (i = 0; i < bytes / 2 ; i = i + 2) {
+		((short *)buffer)[i + 1] = ((short *)buffer)[i];
+	}
+}
+
+
+void* run_hfp_sco(void * args) {
+	
+	 int rc;
+	 struct audio_device * adev = (struct audio_device *)args;
+    struct resampler_itfe *resampler_8to48;
+    struct resampler_itfe *resampler_48to8;
+	
+    int16_t *framebuf_far_stereo_out;
+	 int16_t *framebuf_far_stereo_in;
+    int16_t *framebuf_near_stereo_out;
+	 int16_t *framebuf_near_stereo_in;
+	
+    size_t bytes_of_far_stereo = 0;
+    size_t bytes_of_near_stereo = 0;
+    size_t frames_of_near = 0;
+    size_t frames_of_far = 0;
+    size_t offset_in = 0;
+	
+#ifdef SPEEX_AEC
+	 int16_t *rec_buf;
+	 int16_t *play_buf;
+	 int16_t *out_buf;
+	 SpeexEchoState *echo_state = NULL;
+	 SpeexPreprocessState *preprocess_state = NULL; 
+#endif
+	
+#ifdef AEC_DUMP	
+
+	 static FILE* out_fd;
+	
+    if(out_fd == NULL) {
+        out_fd=fopen("/data/out_buf.pcm","wb+");
+            if(out_fd == NULL) {
+            ALOGD("DEBUG open /data/out_buf.pcm error =%d ,errno = %d",out_fd,errno);
+        }
+    }
+
+	 static FILE* rec_fd;
+    if(rec_fd == NULL) {
+        rec_fd=fopen("/data/rec_buf.pcm","wb+");
+            if(rec_fd == NULL) {
+            ALOGD("DEBUG open /data/rec_buf.pcm error =%d ,errno = %d",rec_fd,errno);
+        }
+    }
+	
+	static FILE* play_fd;
+    if(play_fd == NULL) {
+        play_fd=fopen("/data/play_buf.pcm","wb+");
+            if(play_fd == NULL) {
+            ALOGD("DEBUG open /data/play_buf.pcm error =%d ,errno = %d",play_fd,errno);
+        }
+    }
+	
+#endif
+
+#ifdef BT_HFP_DUMP_FAR_IN
+    static FILE* fd_far_in;
+#endif
+#ifdef BT_HFP_DUMP_NEAR_OUT
+	 static FILE* fd_near_out;
+#endif
+#ifdef BT_HFP_DUMP_NEAR_IN
+    static FILE* fd_near_in;
+#endif
+#ifdef BT_HFP_DUMP_FAR_OUT
+    static FILE* fd_far_out;
+#endif
+
+	 adev->pcm_sco_far_in = pcm_open(SCO_PCM_FAR_IN, 0, PCM_IN, &pcm_config_sco);
+    if (adev->pcm_sco_far_in == 0) {
+        ALOGD("%s: failed to allocate memory for PCM far/in", __func__);
+        return NULL;
+    } else if (!pcm_is_ready(adev->pcm_sco_far_in)){
+        pcm_close(adev->pcm_sco_far_in);
+        ALOGD("%s: failed to open PCM far/in", __func__);
+        return NULL;
+    }
+	 ALOGD("%s: open sucess pcm_sco_far_in", __func__);
+    adev->pcm_sco_far_out = pcm_open(SCO_PCM_FAR_OUT, 0, PCM_OUT, &pcm_config_sco);
+    if (adev->pcm_sco_far_out == 0) {
+        ALOGD("%s: failed to allocate memory for PCM far/out", __func__);
+        return NULL;
+    } else if (!pcm_is_ready(adev->pcm_sco_far_out)){
+        pcm_close(adev->pcm_sco_far_out);
+        ALOGD("%s: failed to open PCM far/out", __func__);
+        return NULL;
+    }
+	ALOGD("%s: open sucess pcm_sco_far_out", __func__);
+		
+	adev->pcm_sco_near_out = pcm_open(SCO_PCM_NEAR_OUT, 0, PCM_OUT, &pcm_config);
+    if (adev->pcm_sco_near_out == 0) {
+        ALOGD("%s: failed to allocate memory for PCM near/out", __func__);
+        return NULL;
+    } else if (!pcm_is_ready(adev->pcm_sco_near_out)){
+        pcm_close(adev->pcm_sco_near_out);
+        ALOGD("%s: failed to open PCM near/out", __func__);
+        return NULL;
+    }
+	 ALOGD("%s: open sucess pcm_sco_near_out", __func__);
+	
+	 adev->pcm_sco_near_in = pcm_open(SCO_PCM_NEAR_IN, 0, PCM_IN, &pcm_config);
+    if (adev->pcm_sco_near_in == 0) {
+        ALOGD("%s: failed to allocate memory for PCM near/in", __func__);
+        return NULL;
+    } else if (!pcm_is_ready(adev->pcm_sco_near_in)){
+        pcm_close(adev->pcm_sco_near_in);
+        ALOGD("%s: failed to open PCM near/in", __func__);
+        return NULL;
+    }
+	 ALOGD("%s: open sucess pcm_sco_near_in", __func__);
+
+    // bytes / frame: channels * bytes/sample. 
+	// 2 channels * 16 bits/sample = 2 channels * 2 bytes/sample = 4 (stereo), 2 (mono)
+    // We read/write in blocks of 10 ms = samplerate / 100 = 80, 160, or 480 frames.
+
+    frames_of_near = 480;
+    frames_of_far = 80;
+
+    bytes_of_far_stereo = 4 * frames_of_far;
+    bytes_of_near_stereo = 4 * frames_of_near;
+
+    framebuf_far_stereo_out = (int16_t *)malloc(bytes_of_far_stereo);
+	 framebuf_far_stereo_in = (int16_t *)malloc(bytes_of_far_stereo);
+    framebuf_near_stereo_out = (int16_t *)malloc(bytes_of_near_stereo);
+	 framebuf_near_stereo_in = (int16_t *)malloc(bytes_of_near_stereo);
+	
+#ifdef SPEEX_AEC	
+	 rec_buf = (int16_t *)malloc(bytes_of_far_stereo);
+	 play_buf = (int16_t *)malloc(bytes_of_far_stereo);
+    out_buf = (int16_t *)malloc(bytes_of_far_stereo);
+
+if (rec_buf == NULL|| play_buf == NULL || out_buf == NULL) {
+        ALOGD("%s: SPEEX_AEC failed to allocate frames", __func__);
+        return NULL;
+    }	
+#endif
+
+	if (framebuf_far_stereo_in == NULL || framebuf_far_stereo_out == NULL ||
+		framebuf_near_stereo_in == NULL || framebuf_near_stereo_out == NULL) {
+        ALOGD("%s: failed to allocate frames", __func__);
+        pcm_close(adev->pcm_sco_near_in);
+        pcm_close(adev->pcm_sco_near_out);
+        pcm_close(adev->pcm_sco_far_in);
+        pcm_close(adev->pcm_sco_far_out);
+        adev->pcm_sco_near_in = 0;
+        adev->pcm_sco_near_out = 0;
+        adev->pcm_sco_far_in = 0;
+        adev->pcm_sco_far_out = 0;
+        return NULL;
+    }
+
+    rc = create_resampler(8000, 48000, 2, RESAMPLER_QUALITY_DEFAULT, NULL, &resampler_8to48);
+    if (rc != 0) {
+        resampler_8to48 = NULL;
+        ALOGD("%s: () failure to create resampler %d", __func__, rc);
+        return NULL;
+    }
+
+    rc = create_resampler(48000, 8000, 2, RESAMPLER_QUALITY_DEFAULT, NULL, &resampler_48to8);
+    if (rc != 0) {
+        resampler_48to8 = NULL;
+        ALOGD("%s: () failure to create resampler %d", __func__, rc);
+        return NULL;
+    }
+#ifdef SPEEX_AEC
+	int frame_size = frames_of_far; //10ms
+	int filter_length = frames_of_far * 6; //60ms
+	int rate = 8000;
+	echo_state	= speex_echo_state_init_mc(frame_size, filter_length, 2, 2);
+	frame_size *= 2; // length read each time
+	preprocess_state = speex_preprocess_state_init(frame_size, rate);
+	speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
+	speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);
+#endif	
+    ALOGD("%s: PCM SCO loop starting", __func__);
+
+    while (adev->hfp_enable && pcm_read(adev->pcm_sco_far_in, framebuf_far_stereo_in, bytes_of_far_stereo) == 0){
+		
+#ifdef BT_HFP_DUMP_FAR_IN
+		if(fd_far_in == NULL) {
+			fd_far_in=fopen("/data/fd_far_in.pcm","wb+");
+            if(fd_far_in == NULL) {
+                ALOGD("DEBUG open /data/fd_far_in.pcm error =%d ,errno = %d",fd_far_in,errno);
+                offset_in = 0;
+            }
+        }
+        fwrite(framebuf_far_stereo_in,bytes_of_far_stereo,1,fd_far_in);
+        offset_in += bytes_of_far_stereo;
+        fflush(fd_far_in);
+        if(offset_in >= 10*1024*1024) {
+            fclose(fd_far_in);
+            offset_in = 0;
+            ALOGD("TEST fd_far_in.pcm end");
+        }
+#endif
+
+		stereo_lr_mix(framebuf_far_stereo_in, bytes_of_far_stereo);
+		volume_process(framebuf_far_stereo_in, bytes_of_far_stereo, adev->hfp_volume);
+		
+		
+		resampler_8to48->resample_from_input(resampler_8to48, (int16_t *)framebuf_far_stereo_in,
+											 (size_t *)&frames_of_far,
+											 (int16_t *) framebuf_near_stereo_out,
+											 (size_t *)&frames_of_near);
+		pcm_write(adev->pcm_sco_near_out, framebuf_near_stereo_out, bytes_of_near_stereo);
+		
+#ifdef BT_HFP_DUMP_NEAR_OUT
+		if(fd_near_out == NULL) {
+			fd_near_out=fopen("/data/fd_near_out.pcm","wb+");
+            if(fd_near_out == NULL) {
+                ALOGD("DEBUG open /data/fd_near_out.pcm error =%d ,errno = %d",fd_near_out,errno);
+                offset_in = 0;
+            }
+        }
+        fwrite(framebuf_near_stereo_out,bytes_of_near_stereo,1,fd_near_out);
+        offset_in += bytes_of_near_stereo;
+        fflush(fd_near_out);
+        if(offset_in >= 10*1024*1024) {
+            fclose(fd_near_out);
+            offset_in = 0;
+            ALOGD("TEST fd_near_out.pcm end");
+        }
+#endif
+
+		pcm_read(adev->pcm_sco_near_in, framebuf_near_stereo_in, bytes_of_near_stereo);
+		
+#ifdef BT_HFP_DUMP_NEAR_IN
+		if(fd_near_in == NULL) {
+			fd_near_in=fopen("/data/fd_near_in.pcm","wb+");
+            if(fd_near_in == NULL) {
+                ALOGD("DEBUG open /data/fd_near_in.pcm error =%d ,errno = %d",fd_near_in,errno);
+                offset_in = 0;
+            }
+        }
+        fwrite(framebuf_near_stereo_in,bytes_of_near_stereo,1,fd_near_in);
+        offset_in += bytes_of_near_stereo;
+        fflush(fd_near_in);
+        if(offset_in >= 10*1024*1024) {
+            fclose(fd_near_in);
+            offset_in = 0;
+            ALOGD("TEST fd_near_in.pcm end");
+        }
+#endif
+		
+		resampler_48to8->resample_from_input(resampler_48to8, (int16_t *)framebuf_near_stereo_in,
+											 (size_t *)&frames_of_near,
+											 (int16_t *)framebuf_far_stereo_out,
+											 (size_t *)&frames_of_far);
+        //stereo_ltor(framebuf_far_stereo_out, bytes_of_far_stereo);
+        //stereo_rtol(framebuf_far_stereo_out, bytes_of_far_stereo);
+        stereo_lr_mix(framebuf_far_stereo_out, bytes_of_far_stereo);
+
+#ifdef BT_HFP_DUMP_FAR_OUT
+		if(fd_far_out == NULL) {
+			fd_far_out=fopen("/data/fd_far_out.pcm","wb+");
+            if(fd_far_out == NULL) {
+                ALOGD("DEBUG open /data/fd_far_out.pcm error =%d ,errno = %d",fd_far_out,errno);
+                offset_in = 0;
+            }
+        }
+        fwrite(framebuf_far_stereo_out,bytes_of_far_stereo,1,fd_far_out);
+        offset_in += bytes_of_far_stereo;
+        fflush(fd_far_out);
+        if(offset_in >= 10*1024*1024) {
+            fclose(fd_far_out);
+            offset_in = 0;
+            ALOGD("TEST fd_far_out.pcm end");
+        }
+#endif									 
+		
+		volume_process(framebuf_far_stereo_out, bytes_of_far_stereo, adev->hfp_volume);
+		
+#ifdef  SPEEX_AEC			
+		play_buf = framebuf_far_stereo_in;
+		rec_buf = framebuf_far_stereo_out;
+		speex_echo_cancellation(echo_state, rec_buf, play_buf, out_buf);
+		speex_preprocess_run(preprocess_state, out_buf);
+		pcm_write(adev->pcm_sco_far_out, out_buf, bytes_of_far_stereo);
+#else
+		pcm_write(adev->pcm_sco_far_out, framebuf_far_stereo_out, bytes_of_far_stereo);
+#endif
+	
+#ifdef AEC_DUMP	 		
+		if(rec_fd)
+			fwrite(rec_buf, bytes_of_far_stereo,1,rec_fd);
+		if(play_fd)
+			fwrite(play_buf, bytes_of_far_stereo,1,play_fd);
+		if(out_fd)
+			fwrite(out_buf, bytes_of_far_stereo,1,out_fd);
+#endif
+
+    }
+
+	hfp_status = false;
+	
+#ifdef SPEEX_AEC	
+	// Destroys an echo canceller state
+	speex_echo_state_destroy(echo_state);
+	speex_preprocess_state_destroy(preprocess_state);
+#endif	
+
+    pcm_close(adev->pcm_sco_near_in);
+    pcm_close(adev->pcm_sco_near_out);
+    pcm_close(adev->pcm_sco_far_in);
+    pcm_close(adev->pcm_sco_far_out);
+
+    adev->pcm_sco_near_in = 0;
+    adev->pcm_sco_near_out = 0;
+    adev->pcm_sco_far_in = 0;
+    adev->pcm_sco_far_out = 0;
+	if(framebuf_far_stereo_in){
+		free(framebuf_far_stereo_in);
+		framebuf_far_stereo_in = NULL;
+	}
+	if(framebuf_far_stereo_out){
+		free(framebuf_far_stereo_out);
+		framebuf_far_stereo_out = NULL;
+	}
+	if(framebuf_near_stereo_in){
+		free(framebuf_near_stereo_in);
+		framebuf_near_stereo_in = NULL;
+	}
+	if(framebuf_near_stereo_out){
+		free(framebuf_near_stereo_out);
+		framebuf_near_stereo_out = NULL;
+	}
+#ifdef AEC_DUMP		
+	if(rec_fd){
+		fflush(rec_fd);
+		fclose(rec_fd);
+		rec_fd = NULL;
+	}
+	if(play_fd){
+		fflush(play_fd);
+		fclose(play_fd);
+		play_fd = NULL;
+	}
+	if(out_fd){
+		fflush(out_fd);
+		fclose(out_fd);
+		out_fd = NULL;
+	}
+#endif
+
+#ifdef SPEEX_AEC	
+	if(rec_buf){
+		free(rec_buf);
+		rec_buf = NULL;
+	}
+	if(play_buf){
+		free(play_buf);
+		play_buf = NULL;
+	}
+	if(out_buf){
+		free(out_buf);
+		out_buf = NULL;
+	}
+#endif
+    adev->hfp_sco_thread = 0;
+    ALOGD("%s: HFP PCM SCO loop terminated", __func__);
+    return NULL;
+}
+
 /**
  * @brief force_non_hdmi_out_standby
  * must be called with hw device outputs list, all out streams, and hw device mutexes locked*/
@@ -143,6 +580,19 @@ static void force_non_hdmi_out_standby(struct audio_device *adev)
     }
 }
 
+static void force_out_standby(struct audio_device *adev)
+{
+    enum output_type type;
+    struct stream_out *out;
+
+    for (type = 0; type < OUTPUT_TOTAL; ++type) {
+        out = adev->outputs[type];
+        if (!out)
+            continue;
+        /* This will never recurse more than 2 levels deep. */
+        do_out_standby(out);
+    }
+}
 
 /**
  * @brief start_bt_sco */
@@ -659,11 +1109,11 @@ static int start_output_stream(struct stream_out *out)
         }
     }
 
-    if (out->device & (AUDIO_DEVICE_OUT_SPEAKER |
+    ALOGD("%s out->device=0x%x,out->pcm_device=0x%d,hfp_status=%d\n",__FUNCTION__,out->device,hfp_status?1:0);
+    if ((hfp_status == false) && (out->device & (AUDIO_DEVICE_OUT_SPEAKER |
                        AUDIO_DEVICE_OUT_WIRED_HEADSET |
                        AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-                       AUDIO_DEVICE_OUT_ALL_SCO)) {
-
+                       AUDIO_DEVICE_OUT_ALL_SCO))) {
         out->pcm[PCM_CARD] = pcm_open(PCM_CARD, out->pcm_device,
                                       PCM_OUT | PCM_MONOTONIC, &out->config);
         if (out->pcm[PCM_CARD] && !pcm_is_ready(out->pcm[PCM_CARD])) {
@@ -1120,8 +1570,11 @@ static void do_out_standby(struct stream_out *out)
             adev->voice_api->flush();
         }
 #endif
-        route_pcm_close(PLAYBACK_OFF_ROUTE);
-        ALOGD("close device");
+
+	if (hfp_status == false) {
+		route_pcm_close(PLAYBACK_OFF_ROUTE);
+		ALOGD("close device");
+	}
 
         /* Skip resetting the mixer if no output device is active */
         if (adev->out_device) {
@@ -2505,13 +2958,24 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
       val = str_parms_get_str(parms, "hfp_enable", value, sizeof(value));
       if (0 <= val) {
          if (strcmp(value, "true") == 0) {
-           ALOGD("Enable HFP client feature!");
-	   route_pcm_open(SPEAKER_INCALL_ROUTE);
-	   start_bt_hfp(adev);
+            ALOGD("Enable HFP client feature!");
+            force_out_standby(adev);//Force shutdown of working sound card
+            hfp_status = true;
+	   		if (adev->hfp_sco_thread == 0) {
+					adev->hfp_enable = true;
+					pthread_create(&adev->hfp_sco_thread, NULL, &run_hfp_sco, adev);
+				}
+	   		route_pcm_open(SPEAKER_NORMAL_ROUTE);
+	   		route_pcm_open(MAIN_MIC_CAPTURE_ROUTE);
          } else if (strcmp(value, "false") == 0) {
            ALOGD("Disable HFP client feature!");
-	   stop_bt_hfp(adev);
-	   route_pcm_open(INCALL_OFF_ROUTE);
+				if (adev->hfp_sco_thread != 0) {
+					adev->hfp_enable = false; // this will cause the thread to exit the main loop and terminate.
+					adev->hfp_sco_thread = 0;
+				}
+			    route_pcm_close(PLAYBACK_OFF_ROUTE);
+  				route_pcm_close(CAPTURE_OFF_ROUTE);
+
          } else {
            ALOGE("Unknown HFP client state %s!!!", value);
            ret = -EINVAL;
@@ -2549,6 +3013,12 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
            ret = -EINVAL;
          }
       }
+		ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME, value, sizeof(value));
+		if (ret >= 0) {
+			val = atoi(value);
+			adev->hfp_volume = val * 1.0/15;
+			ALOGD("%s hfp_volume =%lf\n", __func__, adev->hfp_volume);
+		}
     }
 
     pthread_mutex_unlock(&adev->lock);
@@ -2994,6 +3464,8 @@ static int adev_open(const hw_module_t* module, const char* name,
      * selection is always applied by select_devices() */
 
     adev->hdmi_drv_fd = -1;
+	adev->hfp_enable = false;
+	adev->hfp_volume = 1.0f;//add volume control
 #ifdef AUDIO_3A
     adev->voice_api = NULL;
 #endif


diff --git a/tinyalsa_hal/audio_hw.h b/tinyalsa_hal/audio_hw.h
index a81c85f..a8b3a84 100755
--- a/tinyalsa_hal/audio_hw.h
+++ b/tinyalsa_hal/audio_hw.h
@@ -317,6 +317,15 @@ struct audio_device {
     rk_process_api* voice_api;
 #endif
 
+	/*for bt hfp*/
+	 pthread_t hfp_sco_thread;
+    pthread_mutex_t hfp_sco_thread_lock;
+    struct pcm *pcm_sco_far_in;
+    struct pcm *pcm_sco_far_out;
+    struct pcm *pcm_sco_near_in;
+    struct pcm *pcm_sco_near_out;
+	 bool hfp_enable;
+	 float hfp_volume;
 };
 
 struct stream_out {

你可能感兴趣的:(RockChip,Android,音频)