#include "embdrv/g722/g722_enc_dec.h"
#include "gap_api.h"
#include "gatt_api.h"
+#include "osi/include/properties.h"
#include <base/bind.h>
#include <base/logging.h>
using bluetooth::Uuid;
using bluetooth::hearing_aid::ConnectionState;
+// The MIN_CE_LEN parameter for Connection Parameters based on the current
+// Connection Interval
+constexpr uint16_t MIN_CE_LEN_10MS_CI = 0x0006;
+constexpr uint16_t MIN_CE_LEN_20MS_CI = 0x000C;
+constexpr uint16_t CONNECTION_INTERVAL_10MS_PARAM = 0x0008;
+constexpr uint16_t CONNECTION_INTERVAL_20MS_PARAM = 0x0010;
+
void btif_storage_add_hearing_aid(const RawAddress& address, uint16_t psm,
uint8_t capabilities, uint16_t codecs,
uint16_t audio_control_point_handle,
Uuid LE_PSM_UUID = Uuid::FromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
// clang-format on
-constexpr uint16_t MIN_CE_LEN_1M = 0x0006;
-
void hearingaid_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
tBTM_STATUS);
: gatt_if(0),
seq_counter(0),
current_volume(VOLUME_UNKNOWN),
- callbacks(callbacks) {
- DVLOG(2) << __func__;
+ callbacks(callbacks),
+ codec_in_use(0) {
+ default_data_interval_ms = (uint16_t)osi_property_get_int32(
+ "persist.bluetooth.hearingaid.interval", (int32_t)HA_INTERVAL_20_MS);
+ if ((default_data_interval_ms != HA_INTERVAL_10_MS) &&
+ (default_data_interval_ms != HA_INTERVAL_20_MS)) {
+ LOG(ERROR) << __func__
+ << ": invalid interval=" << default_data_interval_ms
+ << "ms. Overwriting back to default";
+ default_data_interval_ms = HA_INTERVAL_20_MS;
+ }
+ VLOG(2) << __func__
+ << ", default_data_interval_ms=" << default_data_interval_ms;
+
BTA_GATTC_AppRegister(
hearingaid_gattc_callback,
base::Bind(
initCb));
}
+ void UpdateBleConnParams(const RawAddress& address) {
+ /* List of parameters that depends on the chosen Connection Interval */
+ uint16_t min_ce_len;
+ uint16_t connection_interval;
+
+ switch (default_data_interval_ms) {
+ case HA_INTERVAL_10_MS:
+ min_ce_len = MIN_CE_LEN_10MS_CI;
+ connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+ break;
+ case HA_INTERVAL_20_MS:
+ min_ce_len = MIN_CE_LEN_20MS_CI;
+ connection_interval = CONNECTION_INTERVAL_20MS_PARAM;
+ break;
+ default:
+ LOG(ERROR) << __func__ << ":Error: invalid default_data_interval_ms="
+ << default_data_interval_ms;
+ min_ce_len = MIN_CE_LEN_10MS_CI;
+ connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+ }
+
+ L2CA_UpdateBleConnParams(address, connection_interval, connection_interval,
+ 0x000A, 0x0064 /*1s*/, min_ce_len, min_ce_len);
+ }
+
void Connect(const RawAddress& address) override {
DVLOG(2) << __func__ << " " << address;
hearingDevices.Add(HearingDevice(address, true));
// update now, it'll be started once current device finishes.
hearingDevice->connection_update_pending = true;
if (!any_update_pending) {
- L2CA_UpdateBleConnParams(address, 0x0008, 0x0008, 0x000A, 0x0064 /*1s*/,
- MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+ UpdateBleConnParams(address);
}
// Set data length
// TODO(jpawlowski: for 16khz only 87 is required, optimize
- BTM_SetBleDataLength(address, 147);
+ BTM_SetBleDataLength(address, 168);
tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(address);
if (p_dev_rec) {
for (auto& device : hearingDevices.devices) {
if (device.conn_id && device.connection_update_pending) {
- L2CA_UpdateBleConnParams(device.address, 0x0008, 0x0008, 0x000A,
- 0x0064 /*1s*/, MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+ UpdateBleConnParams(device.address);
return;
}
}
}
}
+ uint16_t CalcCompressedAudioPacketSize(uint16_t codec_type,
+ int connection_interval) {
+ int sample_rate;
+
+ const int sample_bit_rate = 16; /* 16 bits per sample */
+ const int compression_ratio = 4; /* G.722 has a 4:1 compression ratio */
+ if (codec_type == CODEC_G722_24KHZ) {
+ sample_rate = 24000;
+ } else {
+ sample_rate = 16000;
+ }
+
+ // compressed_data_packet_size is the size in bytes of the compressed audio
+ // data buffer that is generated for each connection interval.
+ uint32_t compressed_data_packet_size =
+ (sample_rate * connection_interval * (sample_bit_rate / 8) /
+ compression_ratio) /
+ 1000;
+ return ((uint16_t)compressed_data_packet_size);
+ }
+
+ void ChooseCodec(const HearingDevice& hearingDevice) {
+ if (codec_in_use) return;
+
+ // use the best codec available for this pair of devices.
+ uint16_t codecs = hearingDevice.codecs;
+ if (hearingDevice.hi_sync_id != 0) {
+ for (const auto& device : hearingDevices.devices) {
+ if (device.hi_sync_id != hearingDevice.hi_sync_id) continue;
+
+ codecs &= device.codecs;
+ }
+ }
+
+ if ((codecs & (1 << CODEC_G722_24KHZ)) &&
+ controller_get_interface()->supports_ble_2m_phy() &&
+ default_data_interval_ms == HA_INTERVAL_10_MS) {
+ codec_in_use = CODEC_G722_24KHZ;
+ } else if (codecs & (1 << CODEC_G722_16KHZ)) {
+ codec_in_use = CODEC_G722_16KHZ;
+ }
+ }
+
void OnAudioStatus(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, uint8_t* value, void* data) {
DVLOG(2) << __func__ << " " << base::HexEncode(value, len);
hearingDevice->first_connection = false;
}
+ ChooseCodec(*hearingDevice);
+
SendStart(*hearingDevice);
hearingDevice->accepting_audio = true;
LOG(INFO) << __func__ << ": address=" << address
- << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id);
+ << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id)
+ << ", codec_in_use=" << loghex(codec_in_use);
StartSendingAudio(*hearingDevice);
}
}
- if ((codecs & (1 << CODEC_G722_24KHZ)) &&
- controller_get_interface()->supports_ble_2m_phy()) {
- codec_in_use = CODEC_G722_24KHZ;
+ CodecConfiguration codec;
+ if (codec_in_use == CODEC_G722_24KHZ) {
codec.sample_rate = 24000;
- } else if (codecs & (1 << CODEC_G722_16KHZ)) {
- codec_in_use = CODEC_G722_16KHZ;
+ } else {
codec.sample_rate = 16000;
}
-
codec.bit_rate = 16;
- codec.data_interval_ms = 10;
+ codec.data_interval_ms = default_data_interval_ms;
HearingAidAudioSource::Start(codec, audioReceiver);
}
// TODO: this should basically fit the encoded data, tune the size later
std::vector<uint8_t> encoded_data_left;
if (left) {
- encoded_data_left.resize(2000);
+ // TODO: instead of a magic number, we need to figure out the correct
+ // buffer size
+ encoded_data_left.resize(4000);
int encoded_size =
g722_encode(encoder_state_left, encoded_data_left.data(),
(const int16_t*)chan_left.data(), chan_left.size());
std::vector<uint8_t> encoded_data_right;
if (right) {
- encoded_data_right.resize(2000);
+ // TODO: instead of a magic number, we need to figure out the correct
+ // buffer size
+ encoded_data_right.resize(4000);
int encoded_size =
g722_encode(encoder_state_right, encoded_data_right.data(),
(const int16_t*)chan_right.data(), chan_right.size());
size_t encoded_data_size =
std::max(encoded_data_left.size(), encoded_data_right.size());
- // TODO: make it also dependent on the interval, when we support intervals
- // different than 10ms
- uint16_t packet_size;
-
- if (codec_in_use == CODEC_G722_24KHZ) {
- packet_size = 120;
- } else /* if (codec_in_use == CODEC_G722_16KHZ) */ {
- packet_size = 80;
- }
+ uint16_t packet_size =
+ CalcCompressedAudioPacketSize(codec_in_use, default_data_interval_ms);
for (size_t i = 0; i < encoded_data_size; i += packet_size) {
if (left) {
/* currently used codec */
uint8_t codec_in_use;
- CodecConfiguration codec;
+
+ uint16_t default_data_interval_ms;
HearingDevices hearingDevices;
};