转自: https://blog.csdn.net/hb9312z/article/details/103345252

说明:本文适用于 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_DEBUGFILE *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_3Aadev->voice_api = NULL;#endifdiff --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 {

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

  1. RK系列SDK -- dummy codec虚拟声卡注册

    注:适用于基于4.4内核版本开发的RK系列SDK 虚拟声卡的注册主要应用于硬解码芯片的使用,硬件设计上主控I2S直接接到该类芯片,硬解码芯片能够将数字信号直接转换成模拟信号输出.同时,这类芯片需要主控 ...

  2. Rk系列主板 Android 隐藏底部工具栏虚拟按键

    ztlManager = new ZtlManager(mContext);ztlManager.setCloseSystemBar(); //开启隐藏工具栏ztlManager.setOpenSys ...

  3. 蓝牙通话之HFP协议

    HFP协议浅述 在开始学习该协议之前,先说一下学习该协议的关键点: 弄清该协议的应用场景,协议中定义的两个角色AG和HF 两个设备建立hfp连接的流程 基于AT指令集的控制-反馈机制,不知道AT指令集 ...

  4. RK系列开发板音频驱动适配指南(二)

    背景: 上一篇文章RK系列开发板音频驱动适配指南-DAI模块适配中已经阐述音频驱动适配的DAI模块适配步骤以及核心代码的展示,本次主要介绍音频驱动适配中的DMA模块适配. RK系列开发板 DMA模块适 ...

  5. Android蓝牙开发音频焦点

    在车机开发中,蓝牙模块一般是定制的,而蓝牙的音频输出,包括蓝牙电话,蓝牙音乐,都要制定声音策略,进行音频焦点的管理. 音频焦点的管理,这一点类似于android多媒体开发时的音频焦点管理,也是通过Au ...

  6. RK系列开发板音频驱动适配指南(一)

    背景: 多媒体系统目前在Linux操作系统,windows操作系统已经非常成熟,并且应用场景广泛,但是,基于OpenAtom OpenHarmony(以下简称"OpenHarmony&quo ...

  7. 车机蓝牙通话流程分析的流程分析

    PlantUML Web Server 部分内容参照Android HeadSetClient端通话的传递_天花板之恋的博客-CSDN博客 Android源代码中,如果通话状态有改变,会沿着这样的顺序 ...

  8. 光纤同轴音频解码蓝牙MP3音箱芯片方案

    一.简介 随着蓝牙技术的更新换代,蓝牙芯片的功能越来越多,应用范围也越来越广.从经典的牙音乐播放,到今天的光纤数字解码,BLE数传.蓝牙Mesh 组网.KT7693作为一个单芯片的光纤蓝牙解决方案.支 ...

  9. KT1025A蓝牙音频数据芯片ic方案使用方法介绍

    KT1025A芯片是一款支持蓝牙音频和数据以及U盘.TF卡播放的4合一的单芯片,芯片的亮点在支持无损音乐的播放,以及简单明了的串口控制功能,支持BLE透传,以及SPP透传功能.大大降低了嵌入蓝牙在其它 ...

  10. Android音视频学习系列(九) — Android端实现rtmp推流

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

最新文章

  1. 【Python】数据提取xpath和lxml模块(糗事百科的爬虫)
  2. 安装配置Mysql主从
  3. linux ksh怎么查找僵尸进程,Unix 进程管理
  4. Akka编写一个RPC框架,模拟多个Worker连接Master的情况的案例
  5. 架构 | 微服务架构下如何解耦,对于已经紧耦合下如何重构?
  6. java 入门 博客园_javaweb入门
  7. 五大板块(3)—— 结构体
  8. Ugly Numbers UVA - 136
  9. Flink 新一代流计算和容错——阶段总结和展望
  10. 【雅思单词】【绿皮书】雅思单词-错词-第三遍
  11. 正版授权| iObit Uninstaller 12 Pro 专业卸载器工具
  12. CentOS安装Nacos后,输入默认用户名和密码nacos/nacos,提示“用户名或密码错误”
  13. 2021年教你如何用笔记本把Windows10系统安装到移动固态硬盘或U盘简易教程
  14. python制作gif动图_PIL生成透明GIF动图
  15. Java Seckill Module:product details
  16. Qt:操作系统注册表
  17. tiny-emitter 源码解析
  18. ckc交易什么意思_股票熔断是什么意思啊?熔断机制对股民的影响有那些
  19. Pandownload 开发者被抓了,但是其背后,百度难道不需要反思吗?
  20. 面试题:如果要画一只鸟和一个人,你会如何构图?

热门文章

  1. 本地用户和组 无法访问计算机 远程过程调用失败,如何解决远程过程调用失败?怎样使用向日葵远程控制?...
  2. App、H5、PC应用多端开发框架Flutter 2发布
  3. 多维数组展平的几种方式
  4. 主板螺丝是机箱配还是主板配_MATX主板配什么机箱好?曜越Tt启航者A3装机记
  5. 汤姆猫炫跑鸿蒙,汤姆猫炫跑游戏怎么操作-玩法规则一览
  6. Unity3D坦克大战游戏开发——学习笔记(中)
  7. ArcGISPro通视分析之视线分析
  8. DirectShow安装
  9. 带宽与码元的关系_比特率与带宽什么关系
  10. Lempel-Ziv压缩算法