Skip to content
This repository has been archived by the owner on Jan 25, 2021. It is now read-only.

Commit

Permalink
SBC codec configuration options support (#90)
Browse files Browse the repository at this point in the history
* Introduce sbc_min_bitpool and sbc_max_bitpool params for module-bluetooth-discover's a2dp_config option

* Minor fixes and refactoring for new SBC bitpool-setup logic

* More fixes and refactoring for new SBC bitpool-setup logic

* Rename sbc_min_bitpool and sbc_max_bitpool options to sbc_min_bp and sbc_max_bp; introduce other options to override SBC codec auto-configuration: sbc_freq, sbc_cmode, sbc_alloc, sbc_sbands, sbc_blen

* Some fixes and refactoring for new SBC user-options setup logic

* Introduce new max SBC bitpool limit when using forced value

* README.md updated

* Minor fixes and refactoring

* README.md updated

* Minor fixes

* Make configuration options for SBC codec more user-friendly

* README.md updated

* Use SBC_* macros when setting-up overrides for SBC codec
  • Loading branch information
DarkCaster authored and EHfive committed Jan 17, 2020
1 parent b9d3bd3 commit 1a396e7
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 10 deletions.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

this repo is a fork of pulseaudio bluetooth modules

and adds LDAC, APTX, APTX-HD, AAC support
and adds LDAC, APTX, APTX-HD, AAC support, extended configuration for SBC

#### Added Codecs
|Codec|Encoding(source role)|Decoding(sink role)|Sample format(s)|Sample frequencies|
Expand All @@ -15,6 +15,25 @@ and adds LDAC, APTX, APTX-HD, AAC support
APTX/APTX_HD sample format fixed to s32 in PA.
(ffmpeg do the sample format transformation)

#### Extended SBC configuration

Added support for manual (expert) configuration for SBC codec parameters:

* Min and Max bitpool limits (2-250)
* Sampling frequency
* Audio channel mode
* Quantization bit allocation mode
* Frequency bands number
* Audio blocks number

You can use this parameters to override and fine-tune default SBC codec config and manually setup configurations like SBC XQ, or Dual Channel HD mode.
More info about SBC configuration options can be found at [LineageOS documentation](https://lineageos.org/engineering/Bluetooth-SBC-XQ).
Also there is [interactive calculator](https://btcodecs.valdikss.org.ru/sbc-bitrate-calculator) and unofficial [device compatibility list](https://btcodecs.valdikss.org.ru/codec-compatibility) that may help you to select proper values.

Parameter-names for module-bluez5-discover and valid values provided at the table below.

NOTE: Use these parameters with caution at your own risk! Invalid or extreme "out-of-spec" configurations may sometimes even cause malfunction for some cheap BT-audio devices. Usually these malfunctions can be fixed by resetting audio-device or sometimes simply by reconnecting with valid configuration.

## Usage
### Packages

Expand Down Expand Up @@ -120,6 +139,22 @@ Encoders configurations

|Key| Value|Desc |Default|
|---|---|---|---|
|sbc_min_bp|2-250|minimum allowed bitpool|auto|
|sbc_max_bp|2-250|maximum allowed bitpool, may not be < sbc_min_bp|auto|
|sbc_freq|16k, 32k, 44k, 48k|16000/32000/44100/48000 Hz sample frequency|auto|
||auto|do not enforce sample frequency (default)|
|sbc_cmode|mono|mono channel-mode|auto|
||dual|dual channel-mode|
||stereo|stereo channel-mode|
||joint_stereo|joint stereo channel-mode|
||auto|do not enforce channel-mode (default)|
|sbc_alloc|snr|use SNR bit-allocation algorithm|auto|
||loudness|use loudness bit-allocation algorithm|
||auto|do not enforce bit-allocation algorithm (default)|
|sbc_sbands|4, 8|4 or 8 subbands|auto|
||auto|do not enforce subbands count (default)|
|sbc_blen|4, 8, 12, 16|4/8/12/16 audio blocks in one audio frame|auto|
||auto|do not enforce audio blocks count (default)|
|ldac_eqmid|hq|LDAC High Quality|auto|
||sq|LDAC Standard Quality|
||mq|LDAC Mobile use Quality|
Expand Down
1 change: 1 addition & 0 deletions src/modules/bluetooth/a2dp/a2dp-codecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#define SBC_ALLOCATION_SNR (1 << 1)
#define SBC_ALLOCATION_LOUDNESS 1

#define SBC_MAX_BITPOOL_FORCED 250
#define SBC_MAX_BITPOOL 64
#define SBC_MIN_BITPOOL 2

Expand Down
147 changes: 138 additions & 9 deletions src/modules/bluetooth/a2dp/a2dp_sbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ typedef struct sbc_info {
uint8_t min_bitpool;
uint8_t max_bitpool;

uint8_t forced_min_bitpool;
uint8_t forced_max_bitpool;
uint8_t forced_frequency;
uint8_t forced_channel_mode;
uint8_t forced_allocation_method;
uint8_t forced_subbands;
uint8_t forced_block_length;

size_t read_block_size;
size_t write_block_size;

Expand Down Expand Up @@ -86,7 +94,127 @@ pa_sbc_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_fr
}

static int pa_sbc_update_user_config(pa_proplist *user_config, void **codec_data) {
return 0;
int ret = 0;
sbc_info_t *i = *codec_data;
const char *sbc_min_bp_str, *sbc_max_bp_str, *sbc_freq_str, *sbc_cmode_str, *sbc_alloc_str, *sbc_sbands_str, *sbc_blen_str;
uint8_t sbc_min_bitpool = 0, sbc_max_bitpool = 0, sbc_freq = 0, sbc_cmode = 0, sbc_alloc = 0, sbc_sbands = 0, sbc_blen = 0;

sbc_min_bp_str = pa_proplist_gets(user_config, "sbc_min_bp");
sbc_max_bp_str = pa_proplist_gets(user_config, "sbc_max_bp");
sbc_freq_str = pa_proplist_gets(user_config, "sbc_freq");
sbc_cmode_str = pa_proplist_gets(user_config, "sbc_cmode");
sbc_alloc_str = pa_proplist_gets(user_config, "sbc_alloc");
sbc_sbands_str = pa_proplist_gets(user_config, "sbc_sbands");
sbc_blen_str = pa_proplist_gets(user_config, "sbc_blen");

if (sbc_min_bp_str && !streq(sbc_min_bp_str, "auto")) {
sbc_min_bitpool = (uint8_t) atoi(sbc_min_bp_str);
if (sbc_min_bitpool < SBC_MIN_BITPOOL || sbc_min_bitpool > SBC_MAX_BITPOOL_FORCED) {
sbc_min_bitpool = 0;
pa_log_warn("Forced SBC min bitpool value is invalid, ignoring");
} else {
pa_log_notice("Using forced SBC min bitpool value: %d", sbc_min_bitpool);
ret++;
}
}

if (sbc_max_bp_str && !streq(sbc_max_bp_str, "auto")) {
sbc_max_bitpool = (uint8_t) atoi(sbc_max_bp_str);
if (sbc_max_bitpool < sbc_min_bitpool || sbc_max_bitpool < SBC_MIN_BITPOOL || sbc_max_bitpool > SBC_MAX_BITPOOL_FORCED) {
sbc_max_bitpool=0;
pa_log_warn("Forced SBC max bitpool value is invalid, ignoring");
} else {
pa_log_notice("Using forced SBC max bitpool value: %d", sbc_max_bitpool);
ret++;
}
}

if (sbc_freq_str) {
if (streq(sbc_freq_str, "16k"))
sbc_freq = SBC_SAMPLING_FREQ_16000;
else if (streq(sbc_freq_str, "32k"))
sbc_freq = SBC_SAMPLING_FREQ_32000;
else if (streq(sbc_freq_str, "44k"))
sbc_freq = SBC_SAMPLING_FREQ_44100;
else if (streq(sbc_freq_str, "48k"))
sbc_freq = SBC_SAMPLING_FREQ_48000;

if (sbc_freq > 0) {
pa_log_notice("Using forced SBC frequency: %s", sbc_freq_str);
ret++;
} else if (!streq(sbc_freq_str, "auto"))
pa_log_warn("Forced SBC frequency value is invalid, ignoring");
}

if (sbc_cmode_str) {
if (streq(sbc_cmode_str, "mono"))
sbc_cmode = SBC_CHANNEL_MODE_MONO;
else if (streq(sbc_cmode_str, "dual"))
sbc_cmode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
else if (streq(sbc_cmode_str, "stereo"))
sbc_cmode = SBC_CHANNEL_MODE_STEREO;
else if (streq(sbc_cmode_str, "joint_stereo"))
sbc_cmode = SBC_CHANNEL_MODE_JOINT_STEREO;

if (sbc_cmode > 0) {
pa_log_notice("Using forced SBC channel-mode: %s", sbc_cmode_str);
ret++;
} else if (!streq(sbc_cmode_str, "auto"))
pa_log_warn("Forced SBC channel-mode value is invalid, ignoring");
}

if (sbc_alloc_str) {
if (streq(sbc_alloc_str, "snr"))
sbc_alloc = SBC_ALLOCATION_SNR;
else if (streq(sbc_alloc_str, "loudness"))
sbc_alloc = SBC_ALLOCATION_LOUDNESS;

if (sbc_alloc > 0) {
pa_log_notice("Using forced SBC allocation method: %s", sbc_alloc_str);
ret++;
} else if (!streq(sbc_alloc_str, "auto"))
pa_log_warn("Forced SBC allocation method value is invalid, ignoring");
}

if (sbc_sbands_str) {
if (streq(sbc_sbands_str, "4"))
sbc_sbands = SBC_SUBBANDS_4;
else if (streq(sbc_sbands_str, "8"))
sbc_sbands = SBC_SUBBANDS_8;

if (sbc_sbands > 0) {
pa_log_notice("Using forced SBC subbands: %s", sbc_sbands_str);
ret++;
} else if (!streq(sbc_sbands_str, "auto"))
pa_log_warn("Forced SBC subbands value is invalid, ignoring");
}

if (sbc_blen_str) {
if (streq(sbc_blen_str, "4"))
sbc_blen = SBC_BLOCK_LENGTH_4;
else if (streq(sbc_blen_str, "8"))
sbc_blen = SBC_BLOCK_LENGTH_8;
else if (streq(sbc_blen_str, "12"))
sbc_blen = SBC_BLOCK_LENGTH_12;
else if (streq(sbc_blen_str, "16"))
sbc_blen = SBC_BLOCK_LENGTH_16;

if (sbc_blen > 0) {
pa_log_notice("Trying forced SBC block length: %s", sbc_blen_str);
ret++;
} else if (!streq(sbc_blen_str, "auto"))
pa_log_warn("Forced SBC block length value is invalid, ignoring");
}

i->forced_min_bitpool = sbc_min_bitpool;
i->forced_max_bitpool = sbc_max_bitpool;
i->forced_frequency = sbc_freq;
i->forced_channel_mode = sbc_cmode;
i->forced_allocation_method = sbc_alloc;
i->forced_subbands = sbc_sbands;
i->forced_block_length = sbc_blen;

return ret;
}

static size_t
Expand Down Expand Up @@ -251,7 +379,7 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur

sample_spec->format = PA_SAMPLE_S16LE;

switch (config->frequency) {
switch (sbc_info->forced_frequency > 0 ? sbc_info->forced_frequency : config->frequency) {
case SBC_SAMPLING_FREQ_16000:
sbc_info->sbc.frequency = SBC_FREQ_16000;
sample_spec->rate = 16000U;
Expand All @@ -272,7 +400,7 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur
pa_assert_not_reached();
}

switch (config->channel_mode) {
switch (sbc_info->forced_channel_mode > 0 ? sbc_info->forced_channel_mode : config->channel_mode) {
case SBC_CHANNEL_MODE_MONO:
sbc_info->sbc.mode = SBC_MODE_MONO;
sample_spec->channels = 1;
Expand All @@ -293,7 +421,7 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur
pa_assert_not_reached();
}

switch (config->allocation_method) {
switch (sbc_info->forced_allocation_method > 0 ? sbc_info->forced_allocation_method : config->allocation_method) {
case SBC_ALLOCATION_SNR:
sbc_info->sbc.allocation = SBC_AM_SNR;
break;
Expand All @@ -304,7 +432,7 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur
pa_assert_not_reached();
}

switch (config->subbands) {
switch (sbc_info->forced_subbands > 0 ? sbc_info->forced_subbands : config->subbands) {
case SBC_SUBBANDS_4:
sbc_info->sbc.subbands = SBC_SB_4;
break;
Expand All @@ -315,7 +443,7 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur
pa_assert_not_reached();
}

switch (config->block_length) {
switch (sbc_info->forced_block_length > 0 ? sbc_info->forced_block_length : config->block_length) {
case SBC_BLOCK_LENGTH_4:
sbc_info->sbc.blocks = SBC_BLK_4;
break;
Expand All @@ -332,9 +460,10 @@ pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configur
pa_assert_not_reached();
}


sbc_info->min_bitpool = config->min_bitpool;
sbc_info->max_bitpool = config->max_bitpool;
sbc_info->min_bitpool = sbc_info->forced_min_bitpool ? sbc_info->forced_min_bitpool : config->min_bitpool;
sbc_info->max_bitpool = sbc_info->forced_max_bitpool ? sbc_info->forced_max_bitpool : config->max_bitpool;
if (sbc_info->max_bitpool < sbc_info->min_bitpool)
sbc_info->max_bitpool = sbc_info->min_bitpool;

/* Set minimum bitpool for source to get the maximum possible block_size */
sbc_info->sbc.bitpool = sbc_info->is_a2dp_sink ? sbc_info->min_bitpool : sbc_info->max_bitpool;
Expand Down

1 comment on commit 1a396e7

@Atemu
Copy link

@Atemu Atemu commented on 1a396e7 Jun 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool, thank you!

Please sign in to comment.