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 Number | Meaning | Note |
---|---|---|
0x00 | u-Law log | |
0x01 | A-law log | |
0x02 | CVSD | |
0x03 | Transparent | Indicates 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. |
0x04 | Linear PCM | |
0x05 | mSBC | |
0x06 | LC3 | |
0x07 | G.729A | This is a draft allocation associated with draft specifications and is subject to change. |
0x08-0xFE | Reserved | |
0xFF | Vendor Specific | The codec is vendor-specific, as defined by the following 4 octets in the full coding format. |
空中数据示例:
4.2 LMP层定义的air mode格式.
0: µ-law log
1: A-law log
2: CVSD
3: transparent data
4-255: reserved for future use