Patch V2 with added DUAL_CHANNEL as preferred mode. Works without any issue on more than 10 stereo and mono devices that I have here. http://download.zenwalk.org/x86_64/testing/pulseaudio-13.0-SBC-XQ_V2.patch /*** This file is part of PulseAudio. Copyright 2018-2019 Pali Rohár <pali.rohar@xxxxxxxxx> PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. ***/ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <pulsecore/core-util.h> #include <pulsecore/log.h> #include <pulsecore/macro.h> #include <pulsecore/once.h> #include <pulse/sample.h> #include <pulse/xmalloc.h> #include <arpa/inet.h> #include <sbc/sbc.h> #include "a2dp-codecs.h" #include "a2dp-codec-api.h" #include "rtp.h" #define SBC_BITPOOL_DEC_LIMIT 32 #define SBC_BITPOOL_DEC_STEP 5 struct sbc_info { sbc_t sbc; /* Codec data */ size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ uint16_t seq_num; /* Cumulative packet sequence */ uint8_t frequency; uint8_t blocks; uint8_t subbands; uint8_t mode; uint8_t allocation; uint8_t initial_bitpool; uint8_t min_bitpool; uint8_t max_bitpool; }; static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) { const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; if (capabilities_size != sizeof(*capabilities)) return false; if (!(capabilities->frequency & (SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000))) return false; if (!(capabilities->channel_mode & (SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO))) return false; if (!(capabilities->allocation_method & (SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS))) return false; if (!(capabilities->subbands & (SBC_SUBBANDS_4 | SBC_SUBBANDS_8))) return false; if (!(capabilities->block_length & (SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16))) return false; return true; } static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) { const pa_a2dp_codec_capabilities *a2dp_capabilities; const char *key; void *state; /* There is no preference, just choose random valid entry */ PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, capabilities_hashmap, state) { if (can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, for_encoding)) return key; } return NULL; } static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) { a2dp_sbc_t *capabilities = (a2dp_sbc_t *) capabilities_buffer; pa_zero(*capabilities); capabilities->channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO; capabilities->frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000; capabilities->allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS; capabilities->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; capabilities->block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; capabilities->min_bitpool = SBC_MIN_BITPOOL; capabilities->max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100; return sizeof(*capabilities); } static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_size) { const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer; if (config_size != sizeof(*config)) { pa_log_error("Invalid size of config buffer"); return false; } if (config->frequency != SBC_SAMPLING_FREQ_16000 && config->frequency != SBC_SAMPLING_FREQ_32000 && config->frequency != SBC_SAMPLING_FREQ_44100 && config->frequency != SBC_SAMPLING_FREQ_48000) { pa_log_error("Invalid sampling frequency in configuration"); return false; } if (config->channel_mode != SBC_CHANNEL_MODE_MONO && config->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL && config->channel_mode != SBC_CHANNEL_MODE_STEREO && config->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) { pa_log_error("Invalid channel mode in configuration"); return false; } if (config->allocation_method != SBC_ALLOCATION_SNR && config->allocation_method != SBC_ALLOCATION_LOUDNESS) { pa_log_error("Invalid allocation method in configuration"); return false; } if (config->subbands != SBC_SUBBANDS_4 && config->subbands != SBC_SUBBANDS_8) { pa_log_error("Invalid SBC subbands in configuration"); return false; } if (config->block_length != SBC_BLOCK_LENGTH_4 && config->block_length != SBC_BLOCK_LENGTH_8 && config->block_length != SBC_BLOCK_LENGTH_12 && config->block_length != SBC_BLOCK_LENGTH_16) { pa_log_error("Invalid block length in configuration"); return false; } if (config->min_bitpool > config->max_bitpool) { pa_log_error("Invalid bitpool in configuration"); return false; } return true; } static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { /* These bitpool values were chosen based on the A2DP spec recommendation */ switch (freq) { case SBC_SAMPLING_FREQ_48000: switch (mode) { case SBC_CHANNEL_MODE_MONO: case SBC_CHANNEL_MODE_DUAL_CHANNEL: return SBC_BITPOOL_HQ_MONO_48000; case SBC_CHANNEL_MODE_STEREO: case SBC_CHANNEL_MODE_JOINT_STEREO: return SBC_BITPOOL_HQ_JOINT_STEREO_48000; } break; case SBC_SAMPLING_FREQ_44100: switch (mode) { case SBC_CHANNEL_MODE_MONO: case SBC_CHANNEL_MODE_DUAL_CHANNEL: return SBC_BITPOOL_HQ_MONO_44100; case SBC_CHANNEL_MODE_STEREO: case SBC_CHANNEL_MODE_JOINT_STEREO: return SBC_BITPOOL_HQ_JOINT_STEREO_44100; } break; case SBC_SAMPLING_FREQ_16000: case SBC_SAMPLING_FREQ_32000: switch (mode) { case SBC_CHANNEL_MODE_MONO: case SBC_CHANNEL_MODE_DUAL_CHANNEL: case SBC_CHANNEL_MODE_STEREO: case SBC_CHANNEL_MODE_JOINT_STEREO: return SBC_BITPOOL_HQ_JOINT_STEREO_44100; } break; } pa_assert_not_reached(); } static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) { a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer; const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer; int i; static const struct { uint32_t rate; uint8_t cap; } freq_table[] = { { 16000U, SBC_SAMPLING_FREQ_16000 }, { 32000U, SBC_SAMPLING_FREQ_32000 }, { 44100U, SBC_SAMPLING_FREQ_44100 }, { 48000U, SBC_SAMPLING_FREQ_48000 } }; if (capabilities_size != sizeof(*capabilities)) { pa_log_error("Invalid size of capabilities buffer"); return 0; } pa_zero(*config); /* Find the lowest freq that is at least as high as the requested sampling rate */ for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) { config->frequency = freq_table[i].cap; break; } if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { for (--i; i >= 0; i--) { if (capabilities->frequency & freq_table[i].cap) { config->frequency = freq_table[i].cap; break; } } if (i < 0) { pa_log_error("Not suitable sample rate"); return 0; } } pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); if (default_sample_spec->channels <= 1) { if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) config->channel_mode = SBC_CHANNEL_MODE_MONO; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) config->channel_mode = SBC_CHANNEL_MODE_STEREO; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; else { pa_log_error("No supported channel modes"); return 0; } } else { if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO) config->channel_mode = SBC_CHANNEL_MODE_STEREO; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; else if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO) config->channel_mode = SBC_CHANNEL_MODE_MONO; else { pa_log_error("No supported channel modes"); return 0; } } if (capabilities->block_length & SBC_BLOCK_LENGTH_16) config->block_length = SBC_BLOCK_LENGTH_16; else if (capabilities->block_length & SBC_BLOCK_LENGTH_12) config->block_length = SBC_BLOCK_LENGTH_12; else if (capabilities->block_length & SBC_BLOCK_LENGTH_8) config->block_length = SBC_BLOCK_LENGTH_8; else if (capabilities->block_length & SBC_BLOCK_LENGTH_4) config->block_length = SBC_BLOCK_LENGTH_4; else { pa_log_error("No supported block lengths"); return 0; } if (capabilities->subbands & SBC_SUBBANDS_8) config->subbands = SBC_SUBBANDS_8; else if (capabilities->subbands & SBC_SUBBANDS_4) config->subbands = SBC_SUBBANDS_4; else { pa_log_error("No supported subbands"); return 0; } if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) config->allocation_method = SBC_ALLOCATION_LOUDNESS; else if (capabilities->allocation_method & SBC_ALLOCATION_SNR) config->allocation_method = SBC_ALLOCATION_SNR; else { pa_log_error("No supported allocation method"); return 0; } config->min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, capabilities->min_bitpool); config->max_bitpool = (uint8_t) PA_MIN(default_bitpool(config->frequency, config->channel_mode), capabilities->max_bitpool); if (config->min_bitpool > config->max_bitpool) { pa_log_error("No supported bitpool"); return 0; } return sizeof(*config); } static void set_params(struct sbc_info *sbc_info) { sbc_info->sbc.frequency = sbc_info->frequency; sbc_info->sbc.blocks = sbc_info->blocks; sbc_info->sbc.subbands = sbc_info->subbands; sbc_info->sbc.mode = sbc_info->mode; sbc_info->sbc.allocation = sbc_info->allocation; sbc_info->sbc.bitpool = sbc_info->initial_bitpool; sbc_info->sbc.endian = SBC_LE; sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); } static void *init(bool for_encoding, bool for_backchannel, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *sample_spec) { struct sbc_info *sbc_info; const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer; int ret; pa_assert(config_size == sizeof(*config)); pa_assert(!for_backchannel); sbc_info = pa_xnew0(struct sbc_info, 1); ret = sbc_init(&sbc_info->sbc, 0); if (ret != 0) { pa_xfree(sbc_info); pa_log_error("SBC initialization failed: %d", ret); return NULL; } sample_spec->format = PA_SAMPLE_S16LE; switch (config->frequency) { case SBC_SAMPLING_FREQ_48000: sbc_info->frequency = SBC_FREQ_48000; sample_spec->rate = 48000U; break; case SBC_SAMPLING_FREQ_44100: sbc_info->frequency = SBC_FREQ_44100; sample_spec->rate = 44100U; break; case SBC_SAMPLING_FREQ_32000: sbc_info->frequency = SBC_FREQ_32000; sample_spec->rate = 32000U; break; case SBC_SAMPLING_FREQ_16000: sbc_info->frequency = SBC_FREQ_16000; sample_spec->rate = 16000U; break; default: pa_assert_not_reached(); } switch (config->channel_mode) { case SBC_CHANNEL_MODE_DUAL_CHANNEL: sbc_info->mode = SBC_MODE_DUAL_CHANNEL; sample_spec->channels = 2; break; case SBC_CHANNEL_MODE_STEREO: sbc_info->mode = SBC_MODE_STEREO; sample_spec->channels = 2; break; case SBC_CHANNEL_MODE_JOINT_STEREO: sbc_info->mode = SBC_MODE_JOINT_STEREO; sample_spec->channels = 2; break; case SBC_CHANNEL_MODE_MONO: sbc_info->mode = SBC_MODE_MONO; sample_spec->channels = 1; break; default: pa_assert_not_reached(); } switch (config->allocation_method) { case SBC_ALLOCATION_LOUDNESS: sbc_info->allocation = SBC_AM_LOUDNESS; break; case SBC_ALLOCATION_SNR: sbc_info->allocation = SBC_AM_SNR; break; default: pa_assert_not_reached(); } switch (config->subbands) { case SBC_SUBBANDS_4: sbc_info->subbands = SBC_SB_4; break; case SBC_SUBBANDS_8: sbc_info->subbands = SBC_SB_8; break; default: pa_assert_not_reached(); } switch (config->block_length) { case SBC_BLOCK_LENGTH_4: sbc_info->blocks = SBC_BLK_4; break; case SBC_BLOCK_LENGTH_8: sbc_info->blocks = SBC_BLK_8; break; case SBC_BLOCK_LENGTH_12: sbc_info->blocks = SBC_BLK_12; break; case SBC_BLOCK_LENGTH_16: sbc_info->blocks = SBC_BLK_16; break; default: pa_assert_not_reached(); } sbc_info->min_bitpool = config->min_bitpool; sbc_info->max_bitpool = config->max_bitpool; /* Set minimum bitpool for source to get the maximum possible block_size * in get_block_size() function. This block_size is length of buffer used * for decoded audio data and so is inversely proportional to frame length * which depends on bitpool value. Bitpool is controlled by other side from * range [min_bitpool, max_bitpool]. */ sbc_info->initial_bitpool = for_encoding ? sbc_info->max_bitpool : sbc_info->min_bitpool; set_params(sbc_info); pa_log_info("SBC parameters: allocation=%s, subbands=%u, blocks=%u, mode=%s bitpool=%u codesize=%u frame_length=%u", sbc_info->sbc.allocation ? "SNR" : "Loudness", sbc_info->sbc.subbands ? 8 : 4, (sbc_info->sbc.blocks+1)*4, sbc_info->sbc.mode == SBC_MODE_MONO ? "Mono" : sbc_info->sbc.mode == SBC_MODE_DUAL_CHANNEL ? "DualChannel" : sbc_info->sbc.mode == SBC_MODE_STEREO ? "Stereo" : "JointStereo", sbc_info->sbc.bitpool, (unsigned)sbc_info->codesize, (unsigned)sbc_info->frame_length); return sbc_info; } static void deinit(void *codec_info) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; sbc_finish(&sbc_info->sbc); pa_xfree(sbc_info); } static void set_bitpool(struct sbc_info *sbc_info, uint8_t bitpool) { if (bitpool > sbc_info->max_bitpool) bitpool = sbc_info->max_bitpool; else if (bitpool < sbc_info->min_bitpool) bitpool = sbc_info->min_bitpool; sbc_info->sbc.bitpool = bitpool; sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool); } static int reset(void *codec_info) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; int ret; ret = sbc_reinit(&sbc_info->sbc, 0); if (ret != 0) { pa_log_error("SBC reinitialization failed: %d", ret); return -1; } /* sbc_reinit() sets also default parameters, so reset them back */ set_params(sbc_info); sbc_info->seq_num = 0; return 0; } static size_t get_block_size(void *codec_info, size_t link_mtu) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; size_t rtp_size = sizeof(struct rtp_header) + sizeof(struct rtp_sbc_payload); size_t frame_count = (link_mtu - rtp_size) / sbc_info->frame_length; /* frame_count is only 4 bit number */ if (frame_count > 15) frame_count = 15; return frame_count * sbc_info->codesize; } static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; uint8_t bitpool; /* Check if bitpool is already at its limit */ if (sbc_info->sbc.bitpool <= SBC_BITPOOL_DEC_LIMIT) return 0; bitpool = sbc_info->sbc.bitpool - SBC_BITPOOL_DEC_STEP; if (bitpool < SBC_BITPOOL_DEC_LIMIT) bitpool = SBC_BITPOOL_DEC_LIMIT; if (sbc_info->sbc.bitpool == bitpool) return 0; set_bitpool(sbc_info, bitpool); return get_block_size(codec_info, write_link_mtu); } static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; struct rtp_header *header; struct rtp_sbc_payload *payload; uint8_t *d; const uint8_t *p; size_t to_write, to_encode; uint8_t frame_count; header = (struct rtp_header*) output_buffer; payload = (struct rtp_sbc_payload*) (output_buffer + sizeof(*header)); frame_count = 0; p = input_buffer; to_encode = input_size; d = output_buffer + sizeof(*header) + sizeof(*payload); to_write = output_size - sizeof(*header) - sizeof(*payload); /* frame_count is only 4 bit number */ while (PA_LIKELY(to_encode > 0 && to_write > 0 && frame_count < 15)) { ssize_t written; ssize_t encoded; encoded = sbc_encode(&sbc_info->sbc, p, to_encode, d, to_write, &written); if (PA_UNLIKELY(encoded <= 0)) { pa_log_error("SBC encoding error (%li)", (long) encoded); break; } if (PA_UNLIKELY(written < 0)) { pa_log_error("SBC encoding error (%li)", (long) written); break; } pa_assert_fp((size_t) encoded <= to_encode); pa_assert_fp((size_t) encoded == sbc_info->codesize); pa_assert_fp((size_t) written <= to_write); pa_assert_fp((size_t) written == sbc_info->frame_length); p += encoded; to_encode -= encoded; d += written; to_write -= written; frame_count++; } PA_ONCE_BEGIN { pa_log_debug("Using SBC codec implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc))); } PA_ONCE_END; if (PA_UNLIKELY(frame_count == 0)) { *processed = 0; return 0; } /* write it to the fifo */ pa_memzero(output_buffer, sizeof(*header) + sizeof(*payload)); header->v = 2; /* A2DP spec: "A payload type in the RTP dynamic range shall be chosen". * RFC3551 defines the dynamic range to span from 96 to 127, and 96 appears * to be the most common choice in A2DP implementations. */ header->pt = 96; header->sequence_number = htons(sbc_info->seq_num++); header->timestamp = htonl(timestamp); header->ssrc = htonl(1); payload->frame_count = frame_count; *processed = p - input_buffer; return d - output_buffer; } static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) { struct sbc_info *sbc_info = (struct sbc_info *) codec_info; struct rtp_header *header; struct rtp_sbc_payload *payload; const uint8_t *p; uint8_t *d; size_t to_write, to_decode; uint8_t frame_count; header = (struct rtp_header *) input_buffer; payload = (struct rtp_sbc_payload*) (input_buffer + sizeof(*header)); frame_count = payload->frame_count; /* TODO: Add support for decoding fragmented SBC frames */ if (payload->is_fragmented) { pa_log_error("Unsupported fragmented SBC frame"); *processed = 0; return 0; } p = input_buffer + sizeof(*header) + sizeof(*payload); to_decode = input_size - sizeof(*header) - sizeof(*payload); d = output_buffer; to_write = output_size; while (PA_LIKELY(to_decode > 0 && to_write > 0 && frame_count > 0)) { size_t written; ssize_t decoded; decoded = sbc_decode(&sbc_info->sbc, p, to_decode, d, to_write, &written); if (PA_UNLIKELY(decoded <= 0)) { pa_log_error("SBC decoding error (%li)", (long) decoded); break; } /* Reset frame length, it can be changed due to bitpool change */ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); pa_assert_fp((size_t) decoded <= to_decode); pa_assert_fp((size_t) decoded == sbc_info->frame_length); pa_assert_fp((size_t) written <= to_write); pa_assert_fp((size_t) written == sbc_info->codesize); p += decoded; to_decode -= decoded; d += written; to_write -= written; frame_count--; } *processed = p - input_buffer; return d - output_buffer; } const pa_a2dp_codec pa_a2dp_codec_sbc = { .name = "sbc", .description = "SBC", .id = { A2DP_CODEC_SBC, 0, 0 }, .support_backchannel = false, .can_accept_capabilities = can_accept_capabilities, .choose_remote_endpoint = choose_remote_endpoint, .fill_capabilities = fill_capabilities, .is_configuration_valid = is_configuration_valid, .fill_preferred_configuration = fill_preferred_configuration, .init = init, .deinit = deinit, .reset = reset, .get_read_block_size = get_block_size, .get_write_block_size = get_block_size, .reduce_encoder_bitrate = reduce_encoder_bitrate, .encode_buffer = encode_buffer, .decode_buffer = decode_buffer, }; 17.09.2019, 09:34, "Hyperion" <h1p8r10n@xxxxxxxxxx>: > btw here's the updated V12-12 patch in Pali's latest rel95 patchset that applies to PA 13.0. > > http://download.zenwalk.org/x86_64/testing/v12-12-13-zen-bluetooth-Implement-A2DP-codec-switching-and-backchannel-support-.patch > > JP > > 16.09.2019, 09:32, "Hyperion" <h1p8r10n@xxxxxxxxxx>: >> Hi, >> >> Following what has already be done in some Android derivatives, here's a simple patch that extend SBP bitpool negociation to XQ quality. >> >> According to : >> --> http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec >> --> https://lineageos.org/engineering/Bluetooth-SBC-XQ/ , >> --> and confirmed by my own experimentation on more than 10 different devices, >> >> here are the features of the (very simple and non intrusive) patch : >> - allow to use bitpool 76 on devices that support it, aka SBC XQ >> - harmless for devices limited to bitpool 53 >> - deprecates the need for APTX & APTX HD support, which are not better than SBC XQ, are not Open Source, and less supported by devices >> >> This patch will be superseded by multi-profiles Pali Rohar A2DP stack implementation, when it's ready for production. Thanks to Pali for the help testing many codec parameters. >> >> Let me know if I have to clone the git to push my patch, or if a regular PA dev could do it. >> >> All the best >> JP >> >> diff -rNaud pulseaudio-13.0/src/modules/bluetooth/a2dp-codec-sbc.c pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codec-sbc.c >> --- pulseaudio-13.0/src/modules/bluetooth/a2dp-codec-sbc.c 2019-09-13 15:20:03.000000000 +0200 >> +++ pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codec-sbc.c 2019-09-16 08:57:50.363122019 +0200 >> @@ -290,10 +290,10 @@ >> return 0; >> } >> >> - if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) >> - config->allocation_method = SBC_ALLOCATION_LOUDNESS; >> - else if (capabilities->allocation_method & SBC_ALLOCATION_SNR) >> + if (capabilities->allocation_method & SBC_ALLOCATION_SNR) >> config->allocation_method = SBC_ALLOCATION_SNR; >> + else if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS) >> + config->allocation_method = SBC_ALLOCATION_LOUDNESS; >> else { >> pa_log_error("No supported allocation method"); >> return 0; >> diff -rNaud pulseaudio-13.0/src/modules/bluetooth/a2dp-codecs.h pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codecs.h >> --- pulseaudio-13.0/src/modules/bluetooth/a2dp-codecs.h 2019-09-13 15:20:03.000000000 +0200 >> +++ pulseaudio-13.0-new/src/modules/bluetooth/a2dp-codecs.h 2019-09-16 08:44:20.382086305 +0200 >> @@ -61,14 +61,11 @@ >> * Allocation method = Loudness >> * Subbands = 8 >> */ >> -#define SBC_BITPOOL_MQ_MONO_44100 19 >> -#define SBC_BITPOOL_MQ_MONO_48000 18 >> -#define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35 >> -#define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33 >> -#define SBC_BITPOOL_HQ_MONO_44100 31 >> -#define SBC_BITPOOL_HQ_MONO_48000 29 >> -#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53 >> -#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51 >> + >> +#define SBC_BITPOOL_HQ_MONO_44100 38 >> +#define SBC_BITPOOL_HQ_MONO_48000 38 >> +#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 76 >> +#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 76 >> >> #define MPEG_CHANNEL_MODE_MONO (1 << 3) >> #define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) >> >> _______________________________________________ >> pulseaudio-discuss mailing list >> pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx >> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss _______________________________________________ pulseaudio-discuss mailing list pulseaudio-discuss@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss