Rockchip安卓11.0 16k wbs msbc HFP PCM语音通话支持

article/2025/6/19 12:38:08

Rockchip安卓11.0 16k wbs/msbc HFP PCM语音通话支持

调试平台: 安卓11.0, rk3328, 博通ap6212芯片, HFP 8K已经调通的情况下.

SDK修改支持16k wbs/msbc HFP PCM语音注意点如下:

1. bluedroid(system/bt)

博通方案中, ESCO_DATA_PATH_PCM 为1代表蓝牙芯片作为pcm master, 6作为pcm slave.

因为Host系统作为pcm master, ap6212作为slave. 默认使用HCI_Enhanced_Setup_Synchronous_Connection, 所以修改如下:

diff --git a/device/include/esco_parameters.h b/device/include/esco_parameters.h
-#define ESCO_DATA_PATH_PCM 1                /* 0x01-0xFE (PCM Chan) */
+#define ESCO_DATA_PATH_PCM 6                /* 0x01-0xFE (PCM Chan) */   

另外,检查以下配置, DISABLE_WBS已经被设置为FALSE, BTA_HF_CLIENT_FEAT_CODEC有被设置.

diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
+#undef DISABLE_WBS#ifndef DISABLE_WBS#define DISABLE_WBS FALSE#endif
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 583421a7a..de0e3dbd5 100755
--- a/btif/src/btif_hf.cc
+++ b/btif/src/btif_hf.cc
@@ -81,7 +81,7 @@ namespace headset {#define BTIF_HF_FEATURES                                       \(BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | BTA_AG_FEAT_REJECT |  \BTA_AG_FEAT_ECS | BTA_AG_FEAT_EXTERR | BTA_AG_FEAT_VREC |   \
-   /*BTA_AG_FEAT_CODEC |*/ BTA_AG_FEAT_HF_IND | BTA_AG_FEAT_ESCO | \
+   BTA_AG_FEAT_CODEC | BTA_AG_FEAT_HF_IND | BTA_AG_FEAT_ESCO | \BTA_AG_FEAT_UNAT)#endif

2. kernel

2.1 dts

因16k时, 博通要求BCLK要是512M, 所以做如下修改:

diff --git a/arch/arm64/boot/dts/rockchip/rk3328-evb-android-natrium.dts b/arch/arm64/boot/dts/rockchip/rk3328-evb-android-natrium.dts
&i2s2{#sound-dai-cells = <0>;
-       /* rockchip,bclk-fs = <64>; */
+       rockchip,bclk-fs = <32>;rockchip,clk-trcm = <1>;pinctrl-0 = <&i2s2m0_sclk&i2s2m0_lrcktx
@@ -81,3 +81,40 @@};};

另外, 上述rockchip,clk-trcm = <1>; 代表TX/RX 逻辑同步,共享 TX 的时钟,IO上只有 TX 的时钟. 默认0表示各自使用各自时钟. 2表示共享RX时钟. 这里是其他芯片没有为自身提供clk的能力,所以使用TX时钟.

2.2 sound

主要是kernel要支持16K采样率.

diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index d07d7be7710d..cfeae64b1d66 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -32,14 +32,14 @@ static struct snd_soc_dai_driver bt_sco_dai[] = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_8000,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,.formats = SNDRV_PCM_FMTBIT_S16_LE,},.capture = {.stream_name = "Capture",.channels_min = 1,.channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_8000,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,.formats = SNDRV_PCM_FMTBIT_S16_LE,},},

3. hardware/rockchip/audio

支持16-8K动态切换. 打包的代码详见redmine #.

commit  (HEAD)
Author: 
Date:   1.add 16k wbs pcm support for btsco. it can dynamic change from 8k<->16k.2.change 44100 to 48000, for audio mode to 8k/16k.diff --git a/tinyalsa_hal/audio_hw.c b/tinyalsa_hal/audio_hw.c
index 605c4b2..441f5f0 100755
--- a/tinyalsa_hal/audio_hw.c
+++ b/tinyalsa_hal/audio_hw.c
@@ -879,7 +879,7 @@ static int start_output_stream(struct stream_out *out)out_dump(out, 0);-    if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL/* && !out->device & AUDIO_DEVICE_OUT_ALL_SCO*/) {
+    if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {audio_devices_t route_device = out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL;route_pcm_card_open(adev->dev_out[SND_OUT_SOUND_CARD_HDMI].card, getRouteFromDevice(route_device));@@ -965,12 +965,15 @@ static int start_output_stream(struct stream_out *out)if (out->device & AUDIO_DEVICE_OUT_ALL_SCO) {card = adev->dev_out[SND_OUT_SOUND_CARD_BT].card;device = adev->dev_out[SND_OUT_SOUND_CARD_BT].device;
-        ALOGD("pcm_open bt card number = %d, device=%d",card, device);
+        struct pcm_config *pcm_config = &pcm_config_ap_sco;
+        pcm_config->rate = adev->bt_wb_speech_enabled?16000:8000;
+
+        ALOGD("%s pcm_open bt card number = %d, device=%d, src rate: %d dest rate:%d, wbs:%d", __func__, card, device, out->config.rate, pcm_config->rate, adev->bt_wb_speech_enabled);;if(card != (int)SND_OUT_SOUND_CARD_UNKNOWN) {out->pcm[SND_OUT_SOUND_CARD_BT] = pcm_open(card, device,
-                                    PCM_OUT | PCM_MONOTONIC, &pcm_config_ap_sco);
+                                    PCM_OUT | PCM_MONOTONIC, pcm_config);ret = create_resampler(out->config.rate,
-                               pcm_config_ap_sco.rate,
+                               pcm_config->rate,2,RESAMPLER_QUALITY_DEFAULT,NULL,
@@ -1150,6 +1153,9 @@ static int start_input_stream(struct stream_in *in)if ((in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) &&(adev->mode == AUDIO_MODE_IN_COMMUNICATION)) {in->config = &pcm_config_in_bt;
+
+        in->config->rate = adev->bt_wb_speech_enabled?16000:8000;
+card = adev->dev_in[SND_IN_SOUND_CARD_BT].card;device =  adev->dev_in[SND_IN_SOUND_CARD_BT].device;if(card != SND_IN_SOUND_CARD_UNKNOWN){
@@ -1160,7 +1166,8 @@ static int start_input_stream(struct stream_in *in)in->buf_provider.get_next_buffer = get_next_buffer;in->buf_provider.release_buffer = release_buffer;-                ret = create_resampler(8000,
+                ALOGE("%s: %d,src rate:%d, dest(in->requested_rate) = %d",__FUNCTION__,__LINE__,in->config->rate, in->requested_rate);
+                ret = create_resampler(in->config->rate,//8000,in->requested_rate,audio_channel_count_from_in_mask(in->channel_mask),RESAMPLER_QUALITY_DEFAULT,
@@ -1648,6 +1655,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)val = atoi(value);/* Don't switch HDMI audio in box products */if ((val != 0) && ((out->device & val) != val) ||
+            (val != 0) && (val & AUDIO_DEVICE_OUT_ALL_SCO) ||(val != 0) && !(out->device & AUDIO_DEVICE_OUT_HDMI)) {/* Force standby if moving to/from SPDIF or if the output* device changes when in SPDIF mode */
@@ -1659,7 +1667,8 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)/* force output standby to start or stop SCO pcm stream if needed */if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^
-                    (out->device & AUDIO_DEVICE_OUT_ALL_SCO)) {
+                    (out->device & AUDIO_DEVICE_OUT_ALL_SCO)||adev->bt_sco_reroute) {
+                adev->bt_sco_reroute = 0;do_out_standby(out);}@@ -2165,10 +2174,12 @@ false_alarm:if (i == SND_OUT_SOUND_CARD_BT) {// HARD CODE FIXME 48000 stereo -> 8000 stereosize_t inFrameCount = bytes/2/2;
-                    size_t outFrameCount = inFrameCount/6;
+                    int destRate = out->config.rate;//48000;
+                    int srcRate = adev->bt_wb_speech_enabled? 16000:8000;//16000:8000;
+                    int coefficient = (destRate/srcRate);
+                    size_t outFrameCount = inFrameCount/coefficient;int16_t out_buffer[outFrameCount*2];memset(out_buffer, 0x00, outFrameCount*2);
-out->resampler->resample_from_input(out->resampler,(const int16_t *)buffer,&inFrameCount,
@@ -3276,6 +3287,21 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)}}+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
+    if (ret >= 0) {
+        adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
+        ALOGD("%s: adev:0x%p, bt_wb_speech_enabled = %d", __func__, adev, adev->bt_wb_speech_enabled);
+    }
+
+    ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
+    if (ret >= 0) {
+        adev->bt_sco_reroute ^= !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) != 0){
+            adev->bt_wb_speech_enabled = false;
+        }
+    }
+    ALOGD("%s:bt_wb_speech_enabled = %d, sco reroute=%u", __func__, adev->bt_wb_speech_enabled, adev->bt_sco_reroute);
+#ifdef AUDIO_BITSTREAM_REOPEN_HDMI// hdmi reconnectret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, // hdmi reconnect
@@ -3588,6 +3614,7 @@ static int adev_open_input_stream(struct audio_hw_device *dev,#ifdef BT_AP_SCOif (adev->mode == AUDIO_MODE_IN_COMMUNICATION && in->device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {pcm_config = &pcm_config_in_bt;
+        pcm_config->rate = adev->bt_wb_speech_enabled? 16000:8000;}#endif@@ -3606,11 +3633,13 @@ static int adev_open_input_stream(struct audio_hw_device *dev,goto err_malloc;}-    if ((in->requested_rate != 0) && (in->requested_rate != pcm_config->rate)) {
+    ALOGD("pcm_config->rate:%d! in->requested_rate:%d", pcm_config->rate, in->requested_rate);
+
+    if ((in->requested_rate != 0) && ((in->requested_rate != pcm_config->rate))) {in->buf_provider.get_next_buffer = get_next_buffer;in->buf_provider.release_buffer = release_buffer;-        ALOGD("pcm_config->rate:%d,in->requested_rate:%d,in->channel_mask:%d",
+        ALOGD("src(pcm_config->rate):%d, dest(in->requested_rate):%d,in->channel_mask:%d",pcm_config->rate,in->requested_rate,audio_channel_count_from_in_mask(in->channel_mask));ret = create_resampler(pcm_config->rate,in->requested_rate,
@@ -3838,6 +3867,10 @@ static int adev_open(const hw_module_t* module, const char* name,//route_init();/* adev->cur_route_id initial value is 0 and such that first device* selection is always applied by select_devices() */
+
+    adev->bt_wb_speech_enabled = false;
+    adev->bt_sco_reroute = false;
+*device = &adev->hw_device.common;adev_open_init(adev);
diff --git a/tinyalsa_hal/audio_hw.h b/tinyalsa_hal/audio_hw.h
old mode 100644
new mode 100755
index 1f8c785..f23f529
--- a/tinyalsa_hal/audio_hw.h
+++ b/tinyalsa_hal/audio_hw.h
@@ -112,7 +112,7 @@#ifdef BOX_HALstruct pcm_config pcm_config = {.channels = 2,
-    .rate = 44100,
+    .rate = 48000,.period_size = 512,.period_count = 3,.format = PCM_FORMAT_S16_LE,
@@ -120,7 +120,7 @@ struct pcm_config pcm_config = {struct pcm_config pcm_config_in = {.channels = 2,
-    .rate = 44100,
+    .rate = 48000,.period_size = 1024,.period_count = 4,.format = PCM_FORMAT_S16_LE,
@@ -144,7 +144,7 @@ struct pcm_config pcm_config_in = {#elsestruct pcm_config pcm_config = {.channels = 2,
-    .rate = 44100,
+    .rate = 48000,.period_size = 512,.period_count = 6,.format = PCM_FORMAT_S16_LE,
@@ -190,7 +190,7 @@ struct pcm_config pcm_config_hfp = {struct pcm_config pcm_config_ap_sco = {.channels = 2,.rate = 8000,
-    .period_size = 80,
+    .period_size = 160,.period_count = 4,};@@ -201,6 +201,7 @@ struct pcm_config pcm_config_in_bt = {.period_count = 4,.format = PCM_FORMAT_S16_LE,};
+#endifstruct pcm_config pcm_config_deep = {.channels = 2,
@@ -304,6 +305,9 @@ struct audio_device {struct dev_info dev_out[SND_OUT_SOUND_CARD_MAX];struct dev_info dev_in[SND_IN_SOUND_CARD_MAX];
+
+    bool bt_wb_speech_enabled;
+    unsigned int bt_sco_reroute;};struct stream_out {
(END)

4. 蓝牙核心协议相关内容

4.1 HCI层定义的pcm音频编码格式

Transmit_Coding_Format和Receive_Coding_Format是定义空中传输的格式. Assigned Numbers.

对应的, Host传输给控制的格式, 理论上也可以是以上任意之一. Android Bluedroid默认支持0x04(Linear PCM).

Host Controller Interface 见Assigned Number https://www.bluetooth.com/specifications/assigned-numbers/

Coding Format (1 Octet):

Assigned NumberMeaningNote
0x00u-Law log
0x01A-law log
0x02CVSD
0x03TransparentIndicates that the controller does not do any transcoding or resampling. See the command description for restrictions on the use of this value. This is also used for test mode.
0x04Linear PCM
0x05mSBC
0x06LC3
0x07G.729AThis is a draft allocation associated with draft specifications and is subject to change.
0x08-0xFEReserved
0xFFVendor SpecificThe codec is vendor-specific, as defined by the following 4 octets in the full coding format.

空中数据示例:

image-20220110210304780

4.2 LMP层定义的air mode格式.

0: µ-law log

1: A-law log

2: CVSD

3: transparent data

4-255: reserved for future use

image-20220110211120322


http://chatgpt.dhexx.cn/article/ARamoJrQ.shtml

相关文章

蓝牙电话之HFP-电话音频

蓝牙电话之HFP协议中的电话音频 蓝牙技术通信的内容多种多样&#xff0c;其中音频部分包含媒体音频和电话音频。 媒体音频&#xff1a;播放蓝牙音乐的数据&#xff0c;这种音频对质量要求高&#xff0c;数据发送有重传机制&#xff0c;从而以l2cap的数据形式走ACL链路。编码方…

VSCode 按下tab键自动补全功能失效

问题描述&#xff1a;标签自动补全插件已经安装&#xff0c;某天按下tab键自动补全标签的功能突然失效了。解决方法&#xff1a; 1、找到路径 file > Preferences >settings 2、搜索tabs&#xff0c;选择Extensions里面的Emmet&#xff0c;将复选框的√打上就ok啦!

javascript 按下回车键触发input表单的切换(enter键代替tab键功能)

本来是想用js代码去模拟键盘按下来实现该功能&#xff0c;但是没有实现到。后来想到一种比较笨的方法。局限性是只能这些表单元素只能在同个层面下才能切换。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><m…

sublime text3 tab键功能失效

选择首选项 -> 快捷键设置 出现下面编辑页 在右边添加下面一些设置&#xff08;记得要用逗号与前面内容分开&#xff09;&#xff1a; { "keys": ["tab"], "command": "reindent", "context":[{ "key": "…

计算机键盘上的2个定位键,电脑键盘上Tab 键的神奇之处!(用途详解)

下面通过实例来详细解释下&#xff1a; 假设“D:\cfan”路径下&#xff0c;有文件夹&#xff1a; a、 ab &#xff0c;有文件&#xff1a; abc.txt &#xff0c; ac.txt 。依次点击“开始→运行”输入“cmd”&#xff0c;按下回车打开命令提示符。先输入“D:”&#xff0c;回车…

微型计算机上的tab作用,TAB键有什么用处

Tab键对大家来说都是比较熟悉吧。比如写文章时开头要空两格就可用这个键。但是它还有什么特殊功能呢&#xff0c;不一定是大家都知道的。下面小编来详细的介绍下几种tab键的常见功能 tab键作用一&#xff1a;改变焦点 聚焦到下一个按钮&#xff0c;输入框或者链接等。例如&…

计算机键盘上的tab键是什么键,电脑Tab键有什么用处

电脑Tab键有什么用处 Tab键对大家来说都是比较熟悉吧。比如写文章时开头要空两格就可用这个键。但是它还有什么特殊功能呢&#xff0c;不一定是大家都知道的。下面小编来详细的介绍下几种tab键的常见用处&#xff1a; tab键作用一&#xff1a;改变焦点 聚焦到下一个按钮&#x…

电脑Tab键有什么功能?6个实用功能总结!

我最近对电脑的键盘很有兴趣&#xff0c;想了解一下各个键有什么特殊的用法。今天正好看到Tab键&#xff0c;想问下大家知道电脑的Tab键有什么比较好用的功能吗&#xff1f; 在电脑键盘上&#xff0c;Tab键是一个常见的键&#xff0c;它具有多种功能和用途。无论是在文本处理、…

tab键功能

tab 键可以实现命令及路径等补全&#xff0c;提高输入效率&#xff0c;避免出错 tab键可以命令补全&#xff0c;还有路径、文件名补全。 命令补全&#xff1a; 在Linux命令行下&#xff0c;输入字符后&#xff0c;按两次Tab键&#xff0c;shell就会列出以这些字符打头的所有…

计算机键盘上的tab键是什么键,键盘Tab键有什么作用?

Tab键——跳格键&#xff0c;大家都比较熟悉了吧&#xff0c;但是到底它有什么特殊的使用功能&#xff0c;却未必是大家都知道的哦。今天就将Tab键的几种使用功能进行了整理&#xff0c;大家可以参考学习学习。 1、跳格。当我们在网页输入表单的时候&#xff0c;在一个输入控件…

电脑Tab键有什么用

Tab键对大家来说都是比较熟悉吧。比如写文章时开头要空两格就可用这个键。但是它还有什么特殊功能呢&#xff0c;不一定是大家都知道的。下面我来详细的介绍下几种tab键的常见用处&#xff1a; tab键作用一&#xff1a;改变焦点 聚焦到下一个按钮&#xff0c;输入框或者链接等…

电脑Tab键有什么功能?分享Tab键的6个妙用

Tab键tabulator key 的缩写&#xff0c;意思是跳格键。基本用法是可以用来绘制无边框的表格&#xff0c;还可以在单词间留下间隔&#xff0c;一般等于八个空格的长度。但是您知道电脑Tab键有什么功能吗&#xff1f;以下一些关于Tab键的使用和功能的介绍&#xff0c;希望这些可以…

matlab EOF程序

% eof第一模态图用变量eof的第一列&#xff0c;以此类推&#xff1b;相应的时间系数用pc第一行 clear clcx [2 6 1 5 2;9 4 0 5 4;12 2 55 9 10;4 55 78 2 13]; %原始数据&#xff0c;列为站点或格点&#xff0c;行为时间序列上的值 x(1,:) x(1,:) - mean(x(1,:)); x(…

C语言——EOF的用法——while(scanf(“%d“,num)!=EOF)

简介&#xff1a; EOF是一个计算机术语&#xff0c;为End Of File的缩写&#xff0c;在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。、 概念&#xff1a; 在C语言中&#xff0c;或更精确地说成C标准函数库中…

cat << EOF 什么意思?

cat <<EOF 什么意思&#xff1f; cat命令表示查看&#xff0c;而cat <<EOF命令表示将进行输入&#xff0c;直到以EOF终止符来结束输入&#xff08;最后的新行&#xff09;。EOF必须写在一行的头部&#xff0c;前面不能有制表符或者空格。如果结束符EOF前面有制表符…

【C语言中的EOF】零基础看懂EOF如何使用while(scanf(“%d %d”,a,b)感叹号=EOF)

&#x1f31f;前言 ✨如今我们努力奔跑&#xff0c;不过是为了追上那个曾经被寄予厚望的自己&#xff01; 【&#x1f47b;&#x1f47b;&#x1f47b;大家好&#xff0c;我是姜姜爱柚子&#xff0c;将在这里分享所学所得&#xff0c;如果有不正确的地方&#xff0c;希望大家多…

c语言中eof的作用,eof在c语言中表示什么

eof在c语言中表示文件结束符。在while循环中以EOF作为文件结束标志&#xff0c;这种以EOF作为文件结束标志的文件&#xff0c;必须是文本文件。在文本文件中&#xff0c;数据都是以字符的ASCII码值的形式存放的。 在C语言中&#xff0c;或更精确地说成C标准函数库中表示文件结束…