RK系列SDK -- Android HFP蓝牙通话音频处理(蓝牙音箱方案)
转自: 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蓝牙通话音频处理(蓝牙音箱方案)相关推荐
- RK系列SDK -- dummy codec虚拟声卡注册
注:适用于基于4.4内核版本开发的RK系列SDK 虚拟声卡的注册主要应用于硬解码芯片的使用,硬件设计上主控I2S直接接到该类芯片,硬解码芯片能够将数字信号直接转换成模拟信号输出.同时,这类芯片需要主控 ...
- Rk系列主板 Android 隐藏底部工具栏虚拟按键
ztlManager = new ZtlManager(mContext);ztlManager.setCloseSystemBar(); //开启隐藏工具栏ztlManager.setOpenSys ...
- 蓝牙通话之HFP协议
HFP协议浅述 在开始学习该协议之前,先说一下学习该协议的关键点: 弄清该协议的应用场景,协议中定义的两个角色AG和HF 两个设备建立hfp连接的流程 基于AT指令集的控制-反馈机制,不知道AT指令集 ...
- RK系列开发板音频驱动适配指南(二)
背景: 上一篇文章RK系列开发板音频驱动适配指南-DAI模块适配中已经阐述音频驱动适配的DAI模块适配步骤以及核心代码的展示,本次主要介绍音频驱动适配中的DMA模块适配. RK系列开发板 DMA模块适 ...
- Android蓝牙开发音频焦点
在车机开发中,蓝牙模块一般是定制的,而蓝牙的音频输出,包括蓝牙电话,蓝牙音乐,都要制定声音策略,进行音频焦点的管理. 音频焦点的管理,这一点类似于android多媒体开发时的音频焦点管理,也是通过Au ...
- RK系列开发板音频驱动适配指南(一)
背景: 多媒体系统目前在Linux操作系统,windows操作系统已经非常成熟,并且应用场景广泛,但是,基于OpenAtom OpenHarmony(以下简称"OpenHarmony&quo ...
- 车机蓝牙通话流程分析的流程分析
PlantUML Web Server 部分内容参照Android HeadSetClient端通话的传递_天花板之恋的博客-CSDN博客 Android源代码中,如果通话状态有改变,会沿着这样的顺序 ...
- 光纤同轴音频解码蓝牙MP3音箱芯片方案
一.简介 随着蓝牙技术的更新换代,蓝牙芯片的功能越来越多,应用范围也越来越广.从经典的牙音乐播放,到今天的光纤数字解码,BLE数传.蓝牙Mesh 组网.KT7693作为一个单芯片的光纤蓝牙解决方案.支 ...
- KT1025A蓝牙音频数据芯片ic方案使用方法介绍
KT1025A芯片是一款支持蓝牙音频和数据以及U盘.TF卡播放的4合一的单芯片,芯片的亮点在支持无损音乐的播放,以及简单明了的串口控制功能,支持BLE透传,以及SPP透传功能.大大降低了嵌入蓝牙在其它 ...
- Android音视频学习系列(九) — Android端实现rtmp推流
系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...
最新文章
- 【Python】数据提取xpath和lxml模块(糗事百科的爬虫)
- 安装配置Mysql主从
- linux ksh怎么查找僵尸进程,Unix 进程管理
- Akka编写一个RPC框架,模拟多个Worker连接Master的情况的案例
- 架构 | 微服务架构下如何解耦,对于已经紧耦合下如何重构?
- java 入门 博客园_javaweb入门
- 五大板块(3)—— 结构体
- Ugly Numbers UVA - 136
- Flink 新一代流计算和容错——阶段总结和展望
- 【雅思单词】【绿皮书】雅思单词-错词-第三遍
- 正版授权| iObit Uninstaller 12 Pro 专业卸载器工具
- CentOS安装Nacos后,输入默认用户名和密码nacos/nacos,提示“用户名或密码错误”
- 2021年教你如何用笔记本把Windows10系统安装到移动固态硬盘或U盘简易教程
- python制作gif动图_PIL生成透明GIF动图
- Java Seckill Module:product details
- Qt:操作系统注册表
- tiny-emitter 源码解析
- ckc交易什么意思_股票熔断是什么意思啊?熔断机制对股民的影响有那些
- Pandownload 开发者被抓了,但是其背后,百度难道不需要反思吗?
- 面试题:如果要画一只鸟和一个人,你会如何构图?