diff --git a/doc/connectivity/bluetooth/api/a2dp.rst b/doc/connectivity/bluetooth/api/a2dp.rst new file mode 100644 index 0000000000000..0ba7e347ac60f --- /dev/null +++ b/doc/connectivity/bluetooth/api/a2dp.rst @@ -0,0 +1,10 @@ +.. _bt_a2dp: + +Advanced Audio Distribution Profile (A2DP) +########################################### + + +API Reference +************* + +.. doxygengroup:: bt_a2dp diff --git a/doc/connectivity/bluetooth/api/index.rst b/doc/connectivity/bluetooth/api/index.rst index 479f276581056..dc7a856cfb92f 100644 --- a/doc/connectivity/bluetooth/api/index.rst +++ b/doc/connectivity/bluetooth/api/index.rst @@ -12,6 +12,7 @@ Bluetooth Classic Host and profiles hfp.rst rfcomm.rst sdp.rst + a2dp.rst Bluetooth LE Audio ================== diff --git a/include/zephyr/bluetooth/classic/a2dp.h b/include/zephyr/bluetooth/classic/a2dp.h index f14cbb90b4304..6d1436e80e890 100644 --- a/include/zephyr/bluetooth/classic/a2dp.h +++ b/include/zephyr/bluetooth/classic/a2dp.h @@ -11,6 +11,14 @@ #ifndef ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ #define ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ +/** + * @file + * @brief Advanced Audio Distribution Profile (A2DP) + * @defgroup bt_a2dp Advanced Audio Distribution Profile (A2DP) + * @ingroup bluetooth + * @{ + */ + #include #include @@ -872,4 +880,8 @@ struct net_buf *bt_a2dp_stream_create_pdu(struct net_buf_pool *pool, k_timeout_t } #endif +/** + * @} + */ + #endif /* ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ */ diff --git a/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h b/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h index 43f4c44f3355a..2a8013d02d22f 100644 --- a/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h +++ b/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h @@ -111,6 +111,14 @@ struct bt_a2dp_codec_sbc_params { */ uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec); +/** @brief get channel mode of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the channel mode. + */ +enum sbc_ch_mode bt_a2dp_sbc_get_channel_mode(struct bt_a2dp_codec_sbc_params *sbc_codec); + /** @brief get sample rate of a2dp sbc config. * * @param sbc_codec The a2dp sbc parameter. @@ -119,6 +127,30 @@ uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec); */ uint32_t bt_a2dp_sbc_get_sampling_frequency(struct bt_a2dp_codec_sbc_params *sbc_codec); +/** @brief get subband num of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the subband num. + */ +uint8_t bt_a2dp_sbc_get_subband_num(struct bt_a2dp_codec_sbc_params *sbc_codec); + +/** @brief get block length of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the block length. + */ +uint8_t bt_a2dp_sbc_get_block_length(struct bt_a2dp_codec_sbc_params *sbc_codec); + +/** @brief get allocation method of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the allocation method. + */ +enum sbc_alloc_mthd bt_a2dp_sbc_get_allocation_method(struct bt_a2dp_codec_sbc_params *sbc_codec); + #ifdef __cplusplus } #endif diff --git a/samples/bluetooth/classic/a2dp_sink/CMakeLists.txt b/samples/bluetooth/classic/a2dp_sink/CMakeLists.txt new file mode 100644 index 0000000000000..aeaf13e71961d --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/CMakeLists.txt @@ -0,0 +1,18 @@ +#SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(a2dp_sink) + + +if(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + FILE(GLOB app_sources src/*.c) +else() + FILE(GLOB app_sources src/main.c) +endif() +target_sources(app PRIVATE ${app_sources}) + +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) +if(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) +target_include_directories(app PRIVATE src) +endif() diff --git a/samples/bluetooth/classic/a2dp_sink/Kconfig b/samples/bluetooth/classic/a2dp_sink/Kconfig new file mode 100644 index 0000000000000..46bdd0b11da8b --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/Kconfig @@ -0,0 +1,36 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +mainmenu "Bluetooth: A2DP Sink" + +config BT_A2DP_SINK_BOARD_CODEC_ENABLE + bool "play audio data to board's codec when enabled" + +if BT_A2DP_SINK_BOARD_CODEC_ENABLE + +config USE_CODEC_CLOCK + bool "I2S BCK is generated by a selected codec device" + help + If selected, the I2S selected peripheral will be configured to consume + (receive) the I2S BCK and WS signals and the codec will be configured + to generate those. If not selected, the I2S peripheral will generate + them and the codec will be expected to consume them. + +config A2DP_SBC_PCM_BUFFER_PLAY_COUNT + int "The buffer count that every size is 480*2*2 bytes" + default 50 + +config A2DP_BOARD_CODEC_PLAY_COUNT + int "The play count of the board codec interface (i2s)" + default 4 + +config A2DP_BOARD_CODEC_PLAY_THRESHOLD + int "The threshold of pcm buffer when playing data to codec (n%)" + default 20 + +endif # BT_A2DP_SINK_BOARD_CODEC_ENABLE + +source "Kconfig.zephyr" diff --git a/samples/bluetooth/classic/a2dp_sink/README.rst b/samples/bluetooth/classic/a2dp_sink/README.rst new file mode 100644 index 0000000000000..0c0485a50e797 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/README.rst @@ -0,0 +1,24 @@ +.. zephyr:code-sample:: bluetooth_a2dp_sink + :name: a2dp-sink + :relevant-api: bt_a2dp bluetooth + + Use the A2DP APIs. + +Overview +******** + +Application demonstrating usage of the A2DP APIs. + +Requirements +************ + +* BlueZ running on the host, or +* A board with Bluetooth BR/EDR (Classic) support + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/classic/a2dp_sink` in +the Zephyr tree. + +See :zephyr:code-sample-category:`bluetooth` samples for details. diff --git a/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf b/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf new file mode 100644 index 0000000000000..93816b1a65f2e --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf @@ -0,0 +1,8 @@ +#select NXP NW612 Chipset +CONFIG_BT_NXP_NW612=y + +CONFIG_I2S=y +CONFIG_AUDIO=y +CONFIG_AUDIO_CODEC=y +CONFIG_DMA_TCD_QUEUE_SIZE=4 +CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE=y diff --git a/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay b/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay new file mode 100644 index 0000000000000..074232d47e850 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay @@ -0,0 +1,21 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,sram = &ocram1; + }; +}; + +&sai1 { + mclk-output; + podf = <32>; + pll-clocks = <&anatop 0 0 0>, + <&anatop 0 0 30>, + <&anatop 0 0 1>, + <&anatop 0 0 106>, + <&anatop 0 0 1000>; +}; diff --git a/samples/bluetooth/classic/a2dp_sink/prj.conf b/samples/bluetooth/classic/a2dp_sink/prj.conf new file mode 100644 index 0000000000000..992a5cdca4739 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/prj.conf @@ -0,0 +1,10 @@ +CONFIG_BT=y +CONFIG_BT_CLASSIC=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_AVDTP=y +CONFIG_BT_A2DP=y +CONFIG_BT_A2DP_SINK=y +CONFIG_LIBSBC_DECODER=y +CONFIG_BT_DEVICE_NAME="a2dp_sink" +CONFIG_BT_COD=0x200404 +CONFIG_BT_BUF_ACL_RX_SIZE=1300 diff --git a/samples/bluetooth/classic/a2dp_sink/sample.yaml b/samples/bluetooth/classic/a2dp_sink/sample.yaml new file mode 100644 index 0000000000000..06b8b8f879f8d --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/sample.yaml @@ -0,0 +1,11 @@ +sample: + name: Bluetooth A2DP Sink +tests: + sample.bluetooth.a2dp.sink: + harness: bluetooth + platform_allow: + - qemu_cortex_m3 + - qemu_x86 + tags: bluetooth + integration_platforms: + - qemu_cortex_m3 diff --git a/samples/bluetooth/classic/a2dp_sink/src/audio_buf.c b/samples/bluetooth/classic/a2dp_sink/src/audio_buf.c new file mode 100644 index 0000000000000..e01b1e6539c69 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/src/audio_buf.c @@ -0,0 +1,218 @@ +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_buf.h" + +/* The whole PCM buffer (save the audio data after decoding) size */ +#define A2DP_SBC_DECODER_PCM_BUFFER_SIZE (A2DP_SBC_DATA_PLAY_SIZE_48K *\ + CONFIG_A2DP_SBC_PCM_BUFFER_PLAY_COUNT) +uint32_t pcm_buffer_size; /* set it dynamically based on frequency */ +/* The PCM data size for one sbc frame + * (channel num: 2, sample size: 2, max subband: 8, max block length: 16) + */ +#define A2DP_SBC_ONE_FRAME_MAX_SIZE (2 * 2 * 16 * 8) +/* audio stream control variables */ +static volatile uint32_t pcm_r; +static volatile uint32_t pcm_w; +static volatile uint32_t pcm_rm; +static volatile uint32_t pcm_w_count; +static volatile uint32_t pcm_r_count; +static volatile uint32_t pcm_rm_count; +static struct sbc_decoder decoder; +static volatile bool sbc_first_data; +static uint32_t sbc_expected_ts; +static uint16_t pcm_frame_buffer[A2DP_SBC_ONE_FRAME_MAX_SIZE / 2]; +static __aligned(4) uint8_t decoded_pcm_buf[A2DP_SBC_DECODER_PCM_BUFFER_SIZE]; + +static uint32_t audio_pcm_buffer_free_size(void) +{ + uint32_t w_count = pcm_w_count; + uint32_t rm_count = pcm_rm_count; + + if (w_count >= rm_count) { + return pcm_buffer_size - (w_count - rm_count); + } else { + return pcm_buffer_size - ((uint32_t)(((uint64_t)w_count + + (uint64_t)0x100000000u) - (uint64_t)rm_count)); + } +} + +static uint32_t audio_add_pcm_data(uint8_t *data, uint32_t length) +{ + uint32_t free_space; + + free_space = audio_pcm_buffer_free_size(); + + if (free_space < length) { + length = free_space; + } + + /* copy data to buffer */ + if ((pcm_w + length) <= pcm_buffer_size) { + if (data != NULL) { + memcpy(&decoded_pcm_buf[pcm_w], data, length); + } else { + memset(&decoded_pcm_buf[pcm_w], 0, length); + } + + pcm_w += length; + } else { + if (data != NULL) { + memcpy(&decoded_pcm_buf[pcm_w], data, (pcm_buffer_size - pcm_w)); + memcpy(&decoded_pcm_buf[0], &data[(pcm_buffer_size - pcm_w)], + length - (pcm_buffer_size - pcm_w)); + } else { + memset(&decoded_pcm_buf[pcm_w], 0, (pcm_buffer_size - pcm_w)); + memset(&decoded_pcm_buf[0], 0, length - (pcm_buffer_size - pcm_w)); + } + + pcm_w = length - (pcm_buffer_size - pcm_w); + } + pcm_w_count += length; + + if (pcm_w == pcm_buffer_size) { + pcm_w = 0; + } + + return length; +} + +int audio_media_sync(uint8_t *data, uint16_t datalen) +{ + if (data != NULL) { + pcm_rm += datalen; + pcm_rm_count += datalen; + + if (pcm_rm >= pcm_buffer_size) { + pcm_rm = 0; + } + } + + return 0; +} + +void audio_get_pcm_data(uint8_t **data, uint32_t length) +{ + static bool reach_threshold; + uint32_t data_space; + uint32_t w_count = pcm_w_count; + uint32_t r_count = pcm_r_count; + + if (w_count >= r_count) { + data_space = w_count - r_count; + } else { + data_space = (uint32_t)(((uint64_t)w_count + + (uint64_t)0x100000000u) - (uint64_t)r_count); + } + + if (!reach_threshold) { + if (data_space > CONFIG_A2DP_BOARD_CODEC_PLAY_THRESHOLD * pcm_buffer_size / 100) { + reach_threshold = true; + } else { + *data = NULL; + return; + } + } + + if (data_space < length) { + *data = NULL; + reach_threshold = false; + } else { + pcm_r_count += length; + *data = &decoded_pcm_buf[pcm_r]; + pcm_r += length; + + if (pcm_r >= pcm_buffer_size) { + pcm_r = 0; + } + } +} + +void audio_process_sbc_buf(uint8_t sbc_hdr, uint8_t *data, size_t len, uint16_t seq_num, + uint32_t ts, uint8_t channel_num) +{ + const void *in_data; + size_t samples_count; + size_t out_size; + size_t samples_lost; + uint8_t num_frames; + int err; + + samples_lost = 0; + + if (!sbc_first_data) { + sbc_first_data = true; + sbc_expected_ts = ts; + } else { + if (sbc_expected_ts != ts) { + if (sbc_expected_ts < ts) { + samples_lost = ts - sbc_expected_ts; + } + } + } + + if (samples_lost != 0) { + (void)audio_add_pcm_data(NULL, samples_lost * 2U * channel_num); + sbc_expected_ts = sbc_expected_ts + samples_lost; + } + + num_frames = BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_GET(sbc_hdr); + + samples_count = 0; + in_data = (void *)data; + for (uint8_t i = 0; i < num_frames; ++i) { + if (audio_pcm_buffer_free_size() == 0) { + /* if no enough space, don't need decode. */ + continue; + } + + out_size = sizeof(pcm_frame_buffer); + err = sbc_decode(&decoder, &in_data, &len, pcm_frame_buffer, &out_size); + + if (!err) { + audio_add_pcm_data((uint8_t *)pcm_frame_buffer, out_size); + } else { + printk("decode err\n"); + break; + } + + samples_count += (out_size / 2 / channel_num); + } + + sbc_expected_ts = ts + samples_count; +} + +void audio_buf_reset(uint32_t fs) +{ + pcm_r = 0; + pcm_w = 0; + pcm_rm = 0; + pcm_w_count = 0; + pcm_r_count = 0; + pcm_rm_count = 0; + + sbc_setup_decoder(&decoder); + + if (fs == 48000) { + pcm_buffer_size = A2DP_SBC_DECODER_PCM_BUFFER_SIZE; + } else if (fs == 44100) { + pcm_buffer_size = A2DP_SBC_DECODER_PCM_BUFFER_SIZE - + A2DP_SBC_DECODER_PCM_BUFFER_SIZE % A2DP_SBC_DATA_PLAY_SIZE_44_1K; + } else { + printk("wrong frequency\n"); + } + + sbc_first_data = false; +} diff --git a/samples/bluetooth/classic/a2dp_sink/src/audio_buf.h b/samples/bluetooth/classic/a2dp_sink/src/audio_buf.h new file mode 100644 index 0000000000000..2abd84da1292b --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/src/audio_buf.h @@ -0,0 +1,22 @@ +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_AUDIO_BUF_H_ +#define ZEPHYR_INCLUDE_AUDIO_BUF_H_ + +/* The played audio data is 10ms data size */ +#define A2DP_SBC_DATA_PLAY_SIZE_44_1K (441 * 2 * 2) +#define A2DP_SBC_DATA_PLAY_SIZE_48K (480 * 2 * 2) + +void audio_buf_reset(uint32_t fs); + +void audio_process_sbc_buf(uint8_t sbc_hdr, uint8_t *data, size_t len, uint16_t seq_num, + uint32_t ts, uint8_t channel_num); + +int audio_media_sync(uint8_t *data, uint16_t datalen); + +void audio_get_pcm_data(uint8_t **data, uint32_t length); +#endif diff --git a/samples/bluetooth/classic/a2dp_sink/src/codec_play.c b/samples/bluetooth/classic/a2dp_sink/src/codec_play.c new file mode 100644 index 0000000000000..477faf8ee9644 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/src/codec_play.c @@ -0,0 +1,197 @@ +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_buf.h" +#include "codec_play.h" + +#if CONFIG_NOCACHE_MEMORY +#define __NOCACHE __attribute__((__section__(".nocache"))) +#elif defined(CONFIG_DT_DEFINED_NOCACHE) +#define __NOCACHE __attribute__((__section__(CONFIG_DT_DEFINED_NOCACHE_NAME))) +#else /* CONFIG_NOCACHE_MEMORY */ +#define __NOCACHE +#endif /* CONFIG_NOCACHE_MEMORY */ + +/* audio stream control variables */ +static volatile bool audio_start; +static uint32_t audio_sample_rate; +static uint8_t *audio_data_sync_buf[CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT]; +static uint32_t audio_data_sync_buf_size[CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT]; +static uint8_t audio_data_sync_buf_w; +static uint8_t audio_data_sync_buf_r; +static __NOCACHE __aligned(4) uint8_t a2dp_silence_data[A2DP_SBC_DATA_PLAY_SIZE_48K]; +#define I2S_CODEC_TX DT_ALIAS(i2s_codec_tx) +#define I2S_TIMEOUT (2000U) + +const struct device *const codec_tx = DEVICE_DT_GET(I2S_CODEC_TX); + +static __NOCACHE __aligned(4) uint8_t mem_slab_buffer[CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT * + A2DP_SBC_DATA_PLAY_SIZE_48K]; +static struct k_mem_slab mem_slab; + +int codec_play_init(void) +{ + const struct device *const codec_dev = DEVICE_DT_GET(DT_NODELABEL(audio_codec)); + + if (!device_is_ready(codec_tx)) { + printk("%s is not ready\n", codec_tx->name); + return -EIO; + } + + if (!device_is_ready(codec_dev)) { + printk("%s is not ready\n", codec_dev->name); + return -EIO; + } + + return 0; +} + +void codec_play_configure(uint32_t sample_rate, uint8_t sample_width, uint8_t channels) +{ + const struct device *const codec_dev = DEVICE_DT_GET(DT_NODELABEL(audio_codec)); + struct i2s_config config; + struct audio_codec_cfg audio_cfg; + size_t block_size; + + audio_sample_rate = sample_rate; + if (sample_rate == 44100) { + block_size = A2DP_SBC_DATA_PLAY_SIZE_44_1K; + } else { + block_size = A2DP_SBC_DATA_PLAY_SIZE_48K; + } + + audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK; + audio_cfg.dai_type = AUDIO_DAI_TYPE_I2S; + audio_cfg.dai_cfg.i2s.word_size = sample_width; + audio_cfg.dai_cfg.i2s.channels = channels; + audio_cfg.dai_cfg.i2s.format = I2S_FMT_DATA_FORMAT_I2S; +#ifdef CONFIG_USE_CODEC_CLOCK + audio_cfg.dai_cfg.i2s.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; +#else + audio_cfg.dai_cfg.i2s.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; +#endif + audio_cfg.dai_cfg.i2s.frame_clk_freq = sample_rate; + audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab; + audio_cfg.dai_cfg.i2s.block_size = block_size; + audio_codec_configure(codec_dev, &audio_cfg); + k_msleep(1000); + + config.word_size = sample_width; + config.channels = channels; + config.format = I2S_FMT_DATA_FORMAT_I2S; +#ifdef CONFIG_USE_CODEC_CLOCK + config.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE; +#else + config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER; +#endif + config.frame_clk_freq = sample_rate; + config.mem_slab = &mem_slab; + config.block_size = block_size; + config.timeout = I2S_TIMEOUT; + if (i2s_configure(codec_tx, I2S_DIR_TX, &config)) { + printk("failure to config streams\n"); + } + + k_mem_slab_init(&mem_slab, mem_slab_buffer, block_size, CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT); +} + +static void codec_play_to_dev(uint8_t *data, uint32_t length) +{ + int ret; + + ret = i2s_buf_write(codec_tx, data, length); + if (ret < 0) { + printk("Failed to write data: %d\n", ret); + } +} + +static void codec_play_data(uint8_t *data, uint32_t length) +{ + audio_data_sync_buf[audio_data_sync_buf_w % CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT] = data; + audio_data_sync_buf_size[audio_data_sync_buf_w % CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT] = + length; + audio_data_sync_buf_w++; + + if ((data != NULL) && (length != 0U)) { + if (!audio_start) { + return; + } + + codec_play_to_dev(data, length); + } else { + codec_play_to_dev(a2dp_silence_data, + audio_sample_rate == A2DP_SBC_DATA_PLAY_SIZE_48K ? + A2DP_SBC_DATA_PLAY_SIZE_48K : A2DP_SBC_DATA_PLAY_SIZE_44_1K); + } +} + +void codec_play_start(void) +{ + if (audio_start) { + return; + } + + audio_start = true; + + for (uint8_t i = 0; i < CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT; i++) { + codec_play_data(a2dp_silence_data, + audio_sample_rate == A2DP_SBC_DATA_PLAY_SIZE_48K ? + A2DP_SBC_DATA_PLAY_SIZE_48K : A2DP_SBC_DATA_PLAY_SIZE_44_1K); + + if (i == 0) { + i2s_trigger(codec_tx, I2S_DIR_TX, I2S_TRIGGER_START); + } + } +} + +void codec_play_stop(void) +{ + if (!audio_start) { + return; + } + + audio_start = false; + /* Don't need to stop codec_tx. After all the written buf is sent, the I2S tx is stopeed. */ + /* i2s_trigger(codec_tx, I2S_DIR_TX, I2S_TRIGGER_STOP); */ +} + +void codec_keep_play(void) +{ + uint8_t *get_data; + uint32_t length; + + while (true) { + if (!audio_start) { + continue; + } + + if (audio_sample_rate == 44100) { + length = A2DP_SBC_DATA_PLAY_SIZE_44_1K; + } else { + length = A2DP_SBC_DATA_PLAY_SIZE_48K; + } + /* play data */ + audio_get_pcm_data(&get_data, length); + codec_play_data(get_data, length); + + /* sync the already played media data */ + audio_media_sync(audio_data_sync_buf[audio_data_sync_buf_r % + CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT], + audio_data_sync_buf_size[audio_data_sync_buf_r % + CONFIG_A2DP_BOARD_CODEC_PLAY_COUNT]); + + audio_data_sync_buf_r++; + } +} diff --git a/samples/bluetooth/classic/a2dp_sink/src/codec_play.h b/samples/bluetooth/classic/a2dp_sink/src/codec_play.h new file mode 100644 index 0000000000000..d8a283821144f --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/src/codec_play.h @@ -0,0 +1,20 @@ +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_CODEC_PLAY_H_ +#define ZEPHYR_INCLUDE_CODEC_PLAY_H_ + +int codec_play_init(void); + +void codec_play_configure(uint32_t sample_rate, uint8_t sample_width, uint8_t channels); + +void codec_play_start(void); + +void codec_play_stop(void); + +void codec_keep_play(void); + +#endif diff --git a/samples/bluetooth/classic/a2dp_sink/src/main.c b/samples/bluetooth/classic/a2dp_sink/src/main.c new file mode 100644 index 0000000000000..52cb3b6ed58ad --- /dev/null +++ b/samples/bluetooth/classic/a2dp_sink/src/main.c @@ -0,0 +1,281 @@ +/* main.c - Application main entry point */ + +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) +#include "audio_buf.h" +#include "codec_play.h" +#endif + +#define SAMPLE_BIT_WIDTH (16U) + +struct bt_a2dp *default_a2dp; +BT_A2DP_SBC_SINK_EP_DEFAULT(sbc_sink_ep); +static struct bt_a2dp_stream sbc_stream; +BT_A2DP_SBC_EP_CFG_DEFAULT(sbc_cfg, A2DP_SBC_SAMP_FREQ_44100); + +static struct bt_sdp_attribute a2dp_sink_attrs[] = { + BT_SDP_NEW_SERVICE, + BT_SDP_LIST( + BT_SDP_ATTR_SVCLASS_ID_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), /* 35 03 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), /* 19 */ + BT_SDP_ARRAY_16(BT_SDP_AUDIO_SINK_SVCLASS) /* 11 0B */ + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROTO_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16),/* 35 10 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),/* 35 06 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), /* 19 */ + BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP) /* 01 00 */ + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), /* 09 */ + BT_SDP_ARRAY_16(BT_UUID_AVDTP_VAL) /* 00 19 */ + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),/* 35 06 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), /* 19 */ + BT_SDP_ARRAY_16(BT_UUID_AVDTP_VAL) /* 00 19 */ + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), /* 09 */ + BT_SDP_ARRAY_16(AVDTP_VERSION) /* AVDTP version: 01 03 */ + }, + ) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROFILE_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8), /* 35 08 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), /* 35 06 */ + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), /* 19 */ + BT_SDP_ARRAY_16(BT_SDP_ADVANCED_AUDIO_SVCLASS) /* 11 0d */ + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), /* 09 */ + BT_SDP_ARRAY_16(0x0103U) /* 01 03 */ + }, + ) + }, + ) + ), + BT_SDP_SERVICE_NAME("A2DPSink"), + BT_SDP_SUPPORTED_FEATURES(0x0001U), +}; + +static struct bt_sdp_record a2dp_sink_rec = BT_SDP_RECORD(a2dp_sink_attrs); + +static void sbc_stop_play(void) +{ + printk("stream stopped\n"); +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + codec_play_stop(); +#endif +} + +void sbc_stream_configured(struct bt_a2dp_stream *stream) +{ +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + uint32_t sample_freq; + uint8_t channel_num; + struct bt_a2dp_codec_sbc_params *sbc_config = (struct bt_a2dp_codec_sbc_params *) + &sbc_cfg.codec_config->codec_ie[0]; + + channel_num = bt_a2dp_sbc_get_channel_num(sbc_config); + sample_freq = bt_a2dp_sbc_get_sampling_frequency(sbc_config); + codec_play_configure(sample_freq, SAMPLE_BIT_WIDTH, channel_num); +#endif + printk("stream configured\n"); +} + +void sbc_stream_established(struct bt_a2dp_stream *stream) +{ + printk("stream established\n"); +} + +void sbc_stream_released(struct bt_a2dp_stream *stream) +{ + sbc_stop_play(); +} + +void sbc_stream_started(struct bt_a2dp_stream *stream) +{ + printk("stream started\n"); + +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + uint32_t sample_freq; + struct bt_a2dp_codec_sbc_params *sbc_config = (struct bt_a2dp_codec_sbc_params *) + &sbc_cfg.codec_config->codec_ie[0]; + + sample_freq = bt_a2dp_sbc_get_sampling_frequency(sbc_config); + audio_buf_reset(sample_freq); + codec_play_start(); +#endif +} + +void sbc_stream_suspended(struct bt_a2dp_stream *stream) +{ + sbc_stop_play(); +} + +void sbc_stream_recv(struct bt_a2dp_stream *stream, struct net_buf *buf, uint16_t seq_num, + uint32_t ts) +{ +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + uint8_t channel_num; + struct bt_a2dp_codec_sbc_params *sbc_config = (struct bt_a2dp_codec_sbc_params *) + &sbc_cfg.codec_config->codec_ie[0]; + + channel_num = bt_a2dp_sbc_get_channel_num(sbc_config); + + audio_process_sbc_buf(net_buf_pull_u8(buf), buf->data, buf->len, seq_num, ts, channel_num); +#endif +} + +static struct bt_a2dp_stream_ops stream_ops = { + .configured = sbc_stream_configured, + .established = sbc_stream_established, + .released = sbc_stream_released, + .started = sbc_stream_started, + .suspended = sbc_stream_suspended, + .recv = sbc_stream_recv, +}; + +void app_a2dp_connected(struct bt_a2dp *a2dp, int err) +{ + if (!err) { + default_a2dp = a2dp; + printk("a2dp connected success\n"); + } else { + printk("a2dp connected fail\n"); + } +} + +void app_a2dp_disconnected(struct bt_a2dp *a2dp) +{ + default_a2dp = NULL; +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + codec_play_stop(); +#endif + printk("a2dp disconnected\n"); +} + +int app_a2dp_config_req(struct bt_a2dp *a2dp, struct bt_a2dp_ep *ep, + struct bt_a2dp_codec_cfg *codec_cfg, struct bt_a2dp_stream **stream, + uint8_t *rsp_err_code) +{ + uint32_t sample_rate; + + *stream = &sbc_stream; + *sbc_cfg.codec_config = *codec_cfg->codec_config; + + bt_a2dp_stream_cb_register(&sbc_stream, &stream_ops); + *stream = &sbc_stream; + *rsp_err_code = 0; + + printk("receive requesting config and accept\n"); + sample_rate = bt_a2dp_sbc_get_sampling_frequency( + (struct bt_a2dp_codec_sbc_params *)&codec_cfg->codec_config->codec_ie[0]); + printk("sample rate %dHz\n", sample_rate); + + return 0; +} + +static struct bt_a2dp_cb a2dp_cb = { + .connected = app_a2dp_connected, + .disconnected = app_a2dp_disconnected, + .config_req = app_a2dp_config_req, +}; + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + printk("Bluetooth initialized\n"); + + bt_sdp_register_service(&a2dp_sink_rec); + + bt_a2dp_register_ep(&sbc_sink_ep, BT_AVDTP_AUDIO, BT_AVDTP_SINK); + bt_a2dp_register_cb(&a2dp_cb); + + err = bt_br_set_connectable(true); + if (err) { + printk("BR/EDR set/rest connectable failed (err %d)\n", err); + return; + } + err = bt_br_set_discoverable(true, false); + if (err) { + printk("BR/EDR set discoverable failed (err %d)\n", err); + return; + } + + printk("BR/EDR set connectable and discoverable done\n"); +} + +int main(void) +{ + int err; + +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + if (codec_play_init()) { + printk("codec initialization fail\n"); + return 0; + } +#endif + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + +#if defined(CONFIG_BT_A2DP_SINK_BOARD_CODEC_ENABLE) + codec_keep_play(); +#endif + + return 0; +} diff --git a/samples/bluetooth/classic/a2dp_source/CMakeLists.txt b/samples/bluetooth/classic/a2dp_source/CMakeLists.txt new file mode 100644 index 0000000000000..2fa9249268cea --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/CMakeLists.txt @@ -0,0 +1,10 @@ +#SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(a2dp_source) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) diff --git a/samples/bluetooth/classic/a2dp_source/Kconfig b/samples/bluetooth/classic/a2dp_source/Kconfig new file mode 100644 index 0000000000000..6071bec1e66c1 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/Kconfig @@ -0,0 +1,29 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +mainmenu "Bluetooth: A2DP Source" + +config BT_A2DP_SOURCE_DISCOVER_RESULT_COUNT + int "Maximum result count per device discovery" + default 10 + +config BT_A2DP_SOURCE_EPS_DISCOVER_COUNT + int "Maximum result count per a2dp endpoint discovery" + default 10 + +config BT_A2DP_SOURCE_SBC_BIT_RATE_DEFAULT + int "default sbc bit rate" + default 229 + +config BT_A2DP_SOURCE_DATA_SEND_INTERVAL + int "data send interval (ms)" + default 20 + +config BT_A2DP_SOURCE_DATA_BUF_SIZE + int "data buffer size (bytes)" + default 1000 + +source "Kconfig.zephyr" diff --git a/samples/bluetooth/classic/a2dp_source/README.rst b/samples/bluetooth/classic/a2dp_source/README.rst new file mode 100644 index 0000000000000..7e6d7c9bfc8c1 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/README.rst @@ -0,0 +1,24 @@ +.. zephyr:code-sample:: bluetooth_a2dp_source + :name: a2dp-source + :relevant-api: bt_a2dp bluetooth + + Use the A2DP APIs. + +Overview +******** + +Application demonstrating usage of the A2DP APIs. + +Requirements +************ + +* Running on the host with Bluetooth BR/EDR (Classic) support, or +* A board with Bluetooth BR/EDR (Classic) support + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/classic/a2dp_source` in +the Zephyr tree. + +See :zephyr:code-sample-category:`bluetooth` samples for details. diff --git a/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf b/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf new file mode 100644 index 0000000000000..9e8851c73af1b --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.conf @@ -0,0 +1,2 @@ +#select NXP NW612 Chipset +CONFIG_BT_NXP_NW612=y diff --git a/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay b/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay new file mode 100644 index 0000000000000..2571c82dc0e50 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/boards/mimxrt1170_evk_mimxrt1176_cm7_B.overlay @@ -0,0 +1,11 @@ +/* + * Copyright 2024 - 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,sram = &dtcm; + }; +}; diff --git a/samples/bluetooth/classic/a2dp_source/prj.conf b/samples/bluetooth/classic/a2dp_source/prj.conf new file mode 100644 index 0000000000000..77545a3987205 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/prj.conf @@ -0,0 +1,8 @@ +CONFIG_BT=y +CONFIG_BT_CLASSIC=y +CONFIG_BT_AVDTP=y +CONFIG_BT_A2DP=y +CONFIG_BT_A2DP_SOURCE=y +CONFIG_LIBSBC_ENCODER=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_DEVICE_NAME="a2dp-source" diff --git a/samples/bluetooth/classic/a2dp_source/sample.yaml b/samples/bluetooth/classic/a2dp_source/sample.yaml new file mode 100644 index 0000000000000..5d4aefd5d0084 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/sample.yaml @@ -0,0 +1,11 @@ +sample: + name: Bluetooth A2DP Source +tests: + sample.bluetooth.a2dp.source: + harness: bluetooth + platform_allow: + - qemu_cortex_m3 + - qemu_x86 + tags: bluetooth + integration_platforms: + - qemu_cortex_m3 diff --git a/samples/bluetooth/classic/a2dp_source/src/main.c b/samples/bluetooth/classic/a2dp_source/src/main.c new file mode 100644 index 0000000000000..7021206365876 --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/src/main.c @@ -0,0 +1,645 @@ +/* main.c - Application main entry point */ + +/* + * Copyright 2024-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sine.h" + +static struct bt_br_discovery_param br_discover; +static struct bt_br_discovery_result scan_result[CONFIG_BT_A2DP_SOURCE_DISCOVER_RESULT_COUNT]; +struct k_work discover_work; + +static struct bt_conn *default_conn; +struct bt_a2dp *default_a2dp; +static struct bt_a2dp_stream sbc_stream; +struct sbc_encoder encoder; + +BT_A2DP_SBC_SOURCE_EP_DEFAULT(sbc_source_ep); +static bool peer_sbc_found; +struct bt_a2dp_codec_ie sbc_sink_ep_capabilities; +static struct bt_a2dp_ep sbc_sink_ep = { + .codec_cap = &sbc_sink_ep_capabilities, +}; +BT_A2DP_SBC_EP_CFG_DEFAULT(sbc_cfg_default, A2DP_SBC_SAMP_FREQ_44100); + +/* sbc audio stream control variables */ +static int64_t ref_time; +static uint32_t a2dp_src_missed_count; +static volatile bool a2dp_src_playback; +static volatile int media_index; +static uint32_t a2dp_src_sf; +static uint8_t a2dp_src_nc; +static uint32_t send_samples_count; +static uint16_t send_count; +/* 20ms max packet pcm data size. the max is 480 * 2 * 2 * 2 */ +static uint8_t a2dp_pcm_buffer[480 * 2 * 2 * 2]; +NET_BUF_POOL_DEFINE(a2dp_tx_pool, CONFIG_BT_MAX_CONN, + BT_L2CAP_BUF_SIZE(CONFIG_BT_A2DP_SOURCE_DATA_BUF_SIZE), + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); +static void a2dp_playback_timeout_handler(struct k_timer *timer); +K_TIMER_DEFINE(a2dp_player_timer, a2dp_playback_timeout_handler, NULL); + +NET_BUF_POOL_DEFINE(sdp_discover_pool, 10, BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU), + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); + +static struct bt_sdp_attribute a2dp_source_attrs[] = { + BT_SDP_NEW_SERVICE, + BT_SDP_LIST( + BT_SDP_ATTR_SVCLASS_ID_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_AUDIO_SOURCE_SVCLASS) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROTO_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(BT_UUID_AVDTP_VAL) + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_UUID_AVDTP_VAL) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0103) + }, + ) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROFILE_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_ADVANCED_AUDIO_SVCLASS) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0103U) + }, + ) + }, + ) + ), + BT_SDP_SERVICE_NAME("A2DPSource"), + BT_SDP_SUPPORTED_FEATURES(0x0001U), +}; + +static struct bt_sdp_record a2dp_source_rec = BT_SDP_RECORD(a2dp_source_attrs); + +static uint8_t *a2dp_produce_media(uint32_t samples_num) +{ + uint8_t *media = NULL; + uint16_t medialen; + + /* Music Audio is Stereo */ + medialen = (samples_num * a2dp_src_nc * 2); + + /* For mono or dual configuration, skip alternative samples */ + if (1 == a2dp_src_nc) { + uint16_t index; + + media = (uint8_t *)&a2dp_pcm_buffer[0]; + + for (index = 0; index < samples_num; index++) { + media[(2 * index)] = *((uint8_t *)media_data + media_index); + media[(2 * index) + 1] = *((uint8_t *)media_data + media_index + 1); + /* Update the tone index */ + media_index += 4u; + if (media_index >= sizeof(media_data)) { + media_index = 0U; + } + } + } else { + if ((media_index + (samples_num << 2)) > sizeof(media_data)) { + media = (uint8_t *)&a2dp_pcm_buffer[0]; + memcpy(media, ((uint8_t *)media_data + media_index), + sizeof(media_data) - media_index); + memcpy(&media[sizeof(media_data) - media_index], + ((uint8_t *)media_data), + ((samples_num << 2) - (sizeof(media_data) - media_index))); + /* Update the tone index */ + media_index = ((samples_num << 2) - + (sizeof(media_data) - media_index)); + } else { + media = ((uint8_t *)media_data + media_index); + /* Update the tone index */ + media_index += (samples_num << 2); + if (media_index >= sizeof(media_data)) { + media_index = 0U; + } + } + } + + return media; +} + +static void audio_work_handler(struct k_work *work) +{ + int64_t period_ms; + uint32_t a2dp_src_num_samples; + uint8_t *pcm_data; + uint8_t index; + uint32_t pcm_frame_size; + uint32_t pcm_frame_samples; + uint32_t encoded_frame_size; + struct net_buf *buf; + uint8_t frame_num = 0; + uint8_t *sbc_hdr; + uint32_t pdu_len; + uint32_t out_size; + int err; + + /* If stopped then return */ + if (!a2dp_src_playback) { + return; + } + + buf = bt_a2dp_stream_create_pdu(&a2dp_tx_pool, K_NO_WAIT); + if (buf == NULL) { + /* fail */ + return; + } + + period_ms = k_uptime_delta(&ref_time); + + pcm_frame_size = sbc_frame_bytes(&encoder); + pcm_frame_samples = sbc_frame_samples(&encoder); + encoded_frame_size = sbc_frame_encoded_bytes(&encoder); + + sbc_hdr = net_buf_add(buf, 1u); + /* Get the number of samples */ + a2dp_src_num_samples = (uint16_t)((period_ms * a2dp_src_sf) / 1000); + a2dp_src_missed_count += (uint32_t)((period_ms * a2dp_src_sf) % 1000); + a2dp_src_missed_count += ((a2dp_src_num_samples % pcm_frame_samples) * 1000); + a2dp_src_num_samples = (a2dp_src_num_samples / pcm_frame_samples) * pcm_frame_samples; + frame_num = a2dp_src_num_samples / pcm_frame_samples; + + pdu_len = buf->len + frame_num * encoded_frame_size; + + if (pdu_len > net_buf_tailroom(buf)) { + printk("need increase buf size\n"); + return; + } + + if (pdu_len > bt_a2dp_get_mtu(&sbc_stream)) { + printk("need decrease CONFIG_BT_A2DP_SOURCE_DATA_SEND_INTERVAL\n"); + return; + } + + /* Raw adjust for the drift */ + while (a2dp_src_missed_count >= (1000 * pcm_frame_samples)) { + if (pdu_len + encoded_frame_size > bt_a2dp_get_mtu(&sbc_stream) || + pdu_len + encoded_frame_size > net_buf_tailroom(buf)) { + break; + } + + pdu_len += encoded_frame_size; + a2dp_src_num_samples += pcm_frame_samples; + frame_num++; + a2dp_src_missed_count -= (1000 * pcm_frame_samples); + } + + pcm_data = a2dp_produce_media(a2dp_src_num_samples); + if (pcm_data == NULL) { + printk("no media data\n"); + return; + } + + for (index = 0; index < frame_num; index++) { + out_size = sbc_encode(&encoder, (uint8_t *)&pcm_data[index * pcm_frame_size], + net_buf_tail(buf)); + if (encoded_frame_size != out_size) { + printk("sbc encode fail\n"); + continue; + } + + net_buf_add(buf, encoded_frame_size); + } + + *sbc_hdr = (uint8_t)BT_A2DP_SBC_MEDIA_HDR_ENCODE(frame_num, 0, 0, 0); + + if (default_a2dp != NULL) { + err = bt_a2dp_stream_send(&sbc_stream, buf, send_count, send_samples_count); + if (err < 0) { + printk(" Failed to send SBC audio data on streams(%d)\n", err); + net_buf_unref(buf); + } + } + + send_count++; + send_samples_count += a2dp_src_num_samples; +} + +static K_WORK_DEFINE(audio_work, audio_work_handler); + +static void a2dp_playback_timeout_handler(struct k_timer *timer) +{ + k_work_submit(&audio_work); +} + +static void sbc_stream_configured(struct bt_a2dp_stream *stream) +{ + struct sbc_encoder_init_param param; + struct bt_a2dp_codec_sbc_params *sbc_config = (struct bt_a2dp_codec_sbc_params *) + &sbc_cfg_default.codec_config->codec_ie[0]; + + printk("stream configured\n"); + + a2dp_src_sf = bt_a2dp_sbc_get_sampling_frequency(sbc_config); + a2dp_src_nc = bt_a2dp_sbc_get_channel_num(sbc_config); + + param.bit_rate = CONFIG_BT_A2DP_SOURCE_SBC_BIT_RATE_DEFAULT; + param.samp_freq = a2dp_src_sf; + param.blk_len = bt_a2dp_sbc_get_block_length(sbc_config); + param.subband = bt_a2dp_sbc_get_subband_num(sbc_config); + param.alloc_mthd = bt_a2dp_sbc_get_allocation_method(sbc_config); + param.ch_mode = bt_a2dp_sbc_get_channel_mode(sbc_config); + param.ch_num = bt_a2dp_sbc_get_channel_num(sbc_config); + param.min_bitpool = sbc_config->min_bitpool; + param.max_bitpool = sbc_config->max_bitpool; + + if (sbc_setup_encoder(&encoder, ¶m)) { + printk("sbc encoder initialization fail\n"); + return; + } + + bt_a2dp_stream_establish(stream); +} + +static void sbc_stream_established(struct bt_a2dp_stream *stream) +{ + printk("stream established\n"); + bt_a2dp_stream_start(&sbc_stream); +} + +static void sbc_stream_released(struct bt_a2dp_stream *stream) +{ + printk("stream released\n"); + k_timer_stop(&a2dp_player_timer); +} + +static void sbc_stream_started(struct bt_a2dp_stream *stream) +{ + uint32_t audio_time_interval = CONFIG_BT_A2DP_SOURCE_DATA_SEND_INTERVAL; + + printk("stream started\n"); + /* Start Audio Source */ + a2dp_src_playback = true; + + k_uptime_delta(&ref_time); + k_timer_start(&a2dp_player_timer, K_MSEC(audio_time_interval), K_MSEC(audio_time_interval)); +} + +static struct bt_a2dp_stream_ops sbc_stream_ops = { + .configured = sbc_stream_configured, + .established = sbc_stream_established, + .released = sbc_stream_released, + .started = sbc_stream_started, +}; + +static uint8_t a2dp_discover_ep_cb(struct bt_a2dp *a2dp, struct bt_a2dp_ep_info *info, + struct bt_a2dp_ep **ep) +{ + if (peer_sbc_found) { + int err; + + bt_a2dp_stream_cb_register(&sbc_stream, &sbc_stream_ops); + err = bt_a2dp_stream_config(a2dp, &sbc_stream, + &sbc_source_ep, &sbc_sink_ep, + &sbc_cfg_default); + if (err) { + printk("fail to configure\n"); + } + + return BT_A2DP_DISCOVER_EP_STOP; + } + + if (info != NULL) { + printk("find one endpoint:"); + + if (info->codec_type == BT_A2DP_SBC) { + printk("it is SBC codec and use it\n"); + + if (ep != NULL && !peer_sbc_found) { + peer_sbc_found = true; + *ep = &sbc_sink_ep; + } + } else { + printk("it is not SBC codecs\n"); + } + } + + return BT_A2DP_DISCOVER_EP_CONTINUE; +} + +static struct bt_avdtp_sep_info found_seps[CONFIG_BT_A2DP_SOURCE_EPS_DISCOVER_COUNT]; + +static struct bt_a2dp_discover_param ep_discover_param = { + .cb = a2dp_discover_ep_cb, + .seps_info = &found_seps[0], + .sep_count = CONFIG_BT_A2DP_SOURCE_EPS_DISCOVER_COUNT, +}; + +static void app_a2dp_connected(struct bt_a2dp *a2dp, int err) +{ + if (err == 0) { + peer_sbc_found = false; + + err = bt_a2dp_discover(a2dp, &ep_discover_param); + if (err != 0) { + printk("fail to discover\n"); + } + + printk("a2dp connected success\n"); + } else { + if (default_a2dp != NULL) { + default_a2dp = NULL; + } + + printk("a2dp connected fail\n"); + } +} + +static void app_a2dp_disconnected(struct bt_a2dp *a2dp) +{ + if (default_a2dp != NULL) { + default_a2dp = NULL; + } + + a2dp_src_playback = false; + /* stop timer */ + k_timer_stop(&a2dp_player_timer); + printk("a2dp disconnected\n"); +} + +static uint8_t sdp_discover_cb(struct bt_conn *conn, struct bt_sdp_client_result *result, + const struct bt_sdp_discover_params *params) +{ + int err; + uint16_t value; + + printk("Discover done\n"); + + if (result && result->resp_buf != NULL) { + err = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_L2CAP, &value); + + if (err != 0) { + printk("PSM is not found\n"); + } else if (value == BT_UUID_AVDTP_VAL) { + printk("The A2DP server found, connecting a2dp\n"); + default_a2dp = bt_a2dp_connect(conn); + if (err != 0) { + printk("Fail to create A2DP connection (err %d)\n", err); + } + } + } + + return BT_SDP_DISCOVER_UUID_STOP; +} + +static struct bt_sdp_discover_params sdp_discover = { + .type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR, + .func = sdp_discover_cb, + .pool = &sdp_discover_pool, + .uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SINK_SVCLASS), +}; + +static void connected(struct bt_conn *conn, uint8_t err) +{ + int res; + + if (err) { + if (default_conn != NULL) { + default_conn = NULL; + } + + printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err)); + } else { + if (default_conn == conn) { + struct bt_conn_info info; + + bt_conn_get_info(conn, &info); + if (info.type != BT_CONN_TYPE_BR) { + return; + } + + /* + * Do an SDP Query on Successful ACL connection complete with the + * required device + */ + res = bt_sdp_discover(default_conn, &sdp_discover); + if (res) { + printk("SDP discovery failed (err %d)\n", res); + } else { + printk("SDP discovery started\n"); + } + printk("Connected\n"); + } + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason)); + + if (default_conn != conn) { + return; + } + + if (default_conn) { + default_conn = NULL; + } else { + return; + } +} + +static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + struct bt_conn_info info; + + bt_conn_get_info(conn, &info); + + bt_addr_to_str(info.br.dst, addr, sizeof(addr)); + + printk("Security changed: %s level %u, err %s(%d)\n", addr, level, + bt_security_err_to_str(err), err); +} + +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, + .security_changed = security_changed, +}; + +static void discovery_timeout_cb(const struct bt_br_discovery_result *results, size_t count) +{ + char addr[BT_ADDR_LE_STR_LEN]; + const uint8_t *eir; + bool cod_hf = false; + static uint8_t temp[240]; + size_t len = sizeof(results->eir); + uint8_t major_device; + uint8_t minor_device; + size_t i; + + for (i = 0; i < count; i++) { + bt_addr_to_str(&results[i].addr, addr, sizeof(addr)); + printk("Device[%d]: %s, rssi %d, cod 0x%02x%02x%02x", i, addr, results[i].rssi, + results[i].cod[0], results[i].cod[1], results[i].cod[2]); + + major_device = (uint8_t)BT_COD_MAJOR_DEVICE_CLASS(results[i].cod); + minor_device = (uint8_t)BT_COD_MINOR_DEVICE_CLASS(results[i].cod); + + if ((major_device & BT_COD_MAJOR_AUDIO_VIDEO) && + (minor_device & BT_COD_MAJOR_AUDIO_VIDEO_MINOR_WEARABLE_HEADSET)) { + cod_hf = true; + } + + eir = results[i].eir; + + while ((eir[0] > 2) && (len > eir[0])) { + switch (eir[1]) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + memcpy(temp, &eir[2], eir[0] - 1); + temp[eir[0] - 1] = '\0'; /* Set end flag */ + printk(", name %s", temp); + break; + default: + /* Skip the EIR */ + break; + } + len = len - eir[0] - 1; + eir = eir + eir[0] + 1; + } + printk("\n"); + + if (cod_hf) { + break; + } + } + + if (!cod_hf) { + (void)k_work_submit(&discover_work); + } else { + (void)k_work_cancel(&discover_work); + default_conn = bt_conn_create_br(&results[i].addr, BT_BR_CONN_PARAM_DEFAULT); + + if (default_conn == NULL) { + printk("Fail to create the connecton\n"); + } else { + bt_conn_unref(default_conn); + } + } +} + +static void discover_work_handler(struct k_work *work) +{ + int err; + + br_discover.length = 10; + br_discover.limited = false; + + err = bt_br_discovery_start(&br_discover, scan_result, + CONFIG_BT_A2DP_SOURCE_DISCOVER_RESULT_COUNT); + if (err) { + printk("Fail to start discovery (err %d)\n", err); + return; + } +} + +static struct bt_br_discovery_cb discovery_cb = { + .timeout = discovery_timeout_cb, +}; + +static struct bt_a2dp_cb a2dp_cb = { + .connected = app_a2dp_connected, + .disconnected = app_a2dp_disconnected, +}; + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + printk("Bluetooth initialized\n"); + + bt_conn_cb_register(&conn_callbacks); + + bt_br_discovery_cb_register(&discovery_cb); + + bt_sdp_register_service(&a2dp_source_rec); + + bt_a2dp_register_ep(&sbc_source_ep, BT_AVDTP_AUDIO, BT_AVDTP_SOURCE); + + bt_a2dp_register_cb(&a2dp_cb); + + k_work_init(&discover_work, discover_work_handler); + + (void)k_work_submit(&discover_work); +} + +int main(void) +{ + int err; + + printk("Bluetooth A2DP Source demo start...\n"); + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + } + return 0; +} diff --git a/samples/bluetooth/classic/a2dp_source/src/sine.h b/samples/bluetooth/classic/a2dp_source/src/sine.h new file mode 100644 index 0000000000000..90fecda15046c --- /dev/null +++ b/samples/bluetooth/classic/a2dp_source/src/sine.h @@ -0,0 +1,439 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SINE_H_ +#define ZEPHYR_INCLUDE_SINE_H_ + +static const uint8_t media_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x08, 0x0b, 0xbb, 0x15, 0xbb, 0x15, 0xc9, 0x1f, 0xc9, + 0x1f, 0xe4, 0x28, 0xe4, 0x28, 0xc8, 0x30, 0xc8, 0x30, 0x38, 0x37, 0x38, 0x37, 0x03, 0x3c, + 0x03, 0x3c, 0x04, 0x3f, 0x04, 0x3f, 0x25, 0x40, 0x25, 0x40, 0x5d, 0x3f, 0x5d, 0x3f, 0xb1, + 0x3c, 0xb1, 0x3c, 0x38, 0x38, 0x38, 0x38, 0x11, 0x32, 0x11, 0x32, 0x6d, 0x2a, 0x6d, 0x2a, + 0x85, 0x21, 0x85, 0x21, 0x9e, 0x17, 0x9e, 0x17, 0x02, 0x0d, 0x02, 0x0d, 0x04, 0x02, 0x04, + 0x02, 0xf6, 0xf6, 0xf6, 0xf6, 0x2d, 0xec, 0x2d, 0xec, 0xfb, 0xe1, 0xfb, 0xe1, 0xae, 0xd8, + 0xae, 0xd8, 0x8d, 0xd0, 0x8d, 0xd0, 0xd6, 0xc9, 0xd6, 0xc9, 0xbb, 0xc4, 0xbb, 0xc4, 0x65, + 0xc1, 0x65, 0xc1, 0xeb, 0xbf, 0xeb, 0xbf, 0x5b, 0xc0, 0x5b, 0xc0, 0xaf, 0xc2, 0xaf, 0xc2, + 0xd7, 0xc6, 0xd7, 0xc6, 0xb3, 0xcc, 0xb3, 0xcc, 0x16, 0xd4, 0x16, 0xd4, 0xc7, 0xdc, 0xc7, + 0xdc, 0x86, 0xe6, 0x86, 0xe6, 0x06, 0xf1, 0x06, 0xf1, 0xf9, 0xfb, 0xf9, 0xfb, 0x0a, 0x07, + 0x0a, 0x07, 0xe6, 0x11, 0xe6, 0x11, 0x39, 0x1c, 0x39, 0x1c, 0xb5, 0x25, 0xb5, 0x25, 0x12, + 0x2e, 0x12, 0x2e, 0x0f, 0x35, 0x0f, 0x35, 0x78, 0x3a, 0x78, 0x3a, 0x23, 0x3e, 0x23, 0x3e, + 0xf4, 0x3f, 0xf4, 0x3f, 0xde, 0x3f, 0xde, 0x3f, 0xe1, 0x3d, 0xe1, 0x3d, 0x0c, 0x3a, 0x0c, + 0x3a, 0x7c, 0x34, 0x7c, 0x34, 0x5d, 0x2d, 0x5d, 0x2d, 0xe3, 0x24, 0xe3, 0x24, 0x51, 0x1b, + 0x51, 0x1b, 0xee, 0x10, 0xee, 0x10, 0x0a, 0x06, 0x0a, 0x06, 0xf7, 0xfa, 0xf7, 0xfa, 0x0c, + 0xf0, 0x0c, 0xf0, 0x9a, 0xe5, 0x9a, 0xe5, 0xf1, 0xdb, 0xf1, 0xdb, 0x5b, 0xd3, 0x5b, 0xd3, + 0x1a, 0xcc, 0x1a, 0xcc, 0x64, 0xc6, 0x64, 0xc6, 0x65, 0xc2, 0x65, 0xc2, 0x3c, 0xc0, 0x3c, + 0xc0, 0xfa, 0xbf, 0xfa, 0xbf, 0x9f, 0xc1, 0x9f, 0xc1, 0x20, 0xc5, 0x20, 0xc5, 0x62, 0xca, + 0x62, 0xca, 0x3c, 0xd1, 0x3c, 0xd1, 0x7b, 0xd9, 0x7b, 0xd9, 0xe0, 0xe2, 0xe0, 0xe2, 0x23, + 0xed, 0x23, 0xed, 0xf6, 0xf7, 0xf6, 0xf7, 0x06, 0x03, 0x06, 0x03, 0xff, 0x0d, 0xff, 0x0d, + 0x8d, 0x18, 0x8d, 0x18, 0x60, 0x22, 0x60, 0x22, 0x2d, 0x2b, 0x2d, 0x2b, 0xb1, 0x32, 0xb1, + 0x32, 0xb2, 0x38, 0xb2, 0x38, 0x03, 0x3d, 0x03, 0x3d, 0x83, 0x3f, 0x83, 0x3f, 0x1f, 0x40, + 0x1f, 0x40, 0xd2, 0x3e, 0xd2, 0x3e, 0xa6, 0x3b, 0xa6, 0x3b, 0xb3, 0x36, 0xb3, 0x36, 0x1f, + 0x30, 0x1f, 0x30, 0x1c, 0x28, 0x1c, 0x28, 0xe8, 0x1e, 0xe8, 0x1e, 0xc8, 0x14, 0xc8, 0x14, + 0x09, 0x0a, 0x09, 0x0a, 0xfe, 0xfe, 0xfe, 0xfe, 0xfb, 0xf3, 0xfb, 0xf3, 0x53, 0xe9, 0x53, + 0xe9, 0x58, 0xdf, 0x58, 0xdf, 0x56, 0xd6, 0x56, 0xd6, 0x92, 0xce, 0x92, 0xce, 0x47, 0xc8, + 0x47, 0xc8, 0xa4, 0xc3, 0xa4, 0xc3, 0xce, 0xc0, 0xce, 0xc0, 0xd9, 0xbf, 0xd9, 0xbf, 0xce, + 0xc0, 0xce, 0xc0, 0xa4, 0xc3, 0xa4, 0xc3, 0x47, 0xc8, 0x47, 0xc8, 0x92, 0xce, 0x92, 0xce, + 0x56, 0xd6, 0x56, 0xd6, 0x58, 0xdf, 0x58, 0xdf, 0x53, 0xe9, 0x53, 0xe9, 0xfb, 0xf3, 0xfb, + 0xf3, 0xfe, 0xfe, 0xfe, 0xfe, 0x09, 0x0a, 0x09, 0x0a, 0xc8, 0x14, 0xc8, 0x14, 0xe8, 0x1e, + 0xe8, 0x1e, 0x1c, 0x28, 0x1c, 0x28, 0x1f, 0x30, 0x1f, 0x30, 0xb3, 0x36, 0xb3, 0x36, 0xa6, + 0x3b, 0xa6, 0x3b, 0xd2, 0x3e, 0xd2, 0x3e, 0x1f, 0x40, 0x1f, 0x40, 0x83, 0x3f, 0x83, 0x3f, + 0x03, 0x3d, 0x03, 0x3d, 0xb2, 0x38, 0xb2, 0x38, 0xb1, 0x32, 0xb1, 0x32, 0x2d, 0x2b, 0x2d, + 0x2b, 0x60, 0x22, 0x60, 0x22, 0x8d, 0x18, 0x8d, 0x18, 0xff, 0x0d, 0xff, 0x0d, 0x06, 0x03, + 0x06, 0x03, 0xf6, 0xf7, 0xf6, 0xf7, 0x23, 0xed, 0x23, 0xed, 0xe0, 0xe2, 0xe0, 0xe2, 0x7b, + 0xd9, 0x7b, 0xd9, 0x3c, 0xd1, 0x3c, 0xd1, 0x62, 0xca, 0x62, 0xca, 0x20, 0xc5, 0x20, 0xc5, + 0x9f, 0xc1, 0x9f, 0xc1, 0xfa, 0xbf, 0xfa, 0xbf, 0x3c, 0xc0, 0x3c, 0xc0, 0x65, 0xc2, 0x65, + 0xc2, 0x64, 0xc6, 0x64, 0xc6, 0x1a, 0xcc, 0x1a, 0xcc, 0x5b, 0xd3, 0x5b, 0xd3, 0xf1, 0xdb, + 0xf1, 0xdb, 0x9a, 0xe5, 0x9a, 0xe5, 0x0c, 0xf0, 0x0c, 0xf0, 0xf7, 0xfa, 0xf7, 0xfa, 0x0a, + 0x06, 0x0a, 0x06, 0xee, 0x10, 0xee, 0x10, 0x51, 0x1b, 0x51, 0x1b, 0xe3, 0x24, 0xe3, 0x24, + 0x5d, 0x2d, 0x5d, 0x2d, 0x7c, 0x34, 0x7c, 0x34, 0x0c, 0x3a, 0x0c, 0x3a, 0xe1, 0x3d, 0xe1, + 0x3d, 0xde, 0x3f, 0xde, 0x3f, 0xf4, 0x3f, 0xf4, 0x3f, 0x23, 0x3e, 0x23, 0x3e, 0x78, 0x3a, + 0x78, 0x3a, 0x0f, 0x35, 0x0f, 0x35, 0x12, 0x2e, 0x12, 0x2e, 0xb5, 0x25, 0xb5, 0x25, 0x39, + 0x1c, 0x39, 0x1c, 0xe6, 0x11, 0xe6, 0x11, 0x0a, 0x07, 0x0a, 0x07, 0xf9, 0xfb, 0xf9, 0xfb, + 0x06, 0xf1, 0x06, 0xf1, 0x86, 0xe6, 0x86, 0xe6, 0xc7, 0xdc, 0xc7, 0xdc, 0x16, 0xd4, 0x16, + 0xd4, 0xb3, 0xcc, 0xb3, 0xcc, 0xd7, 0xc6, 0xd7, 0xc6, 0xaf, 0xc2, 0xaf, 0xc2, 0x5b, 0xc0, + 0x5b, 0xc0, 0xeb, 0xbf, 0xeb, 0xbf, 0x65, 0xc1, 0x65, 0xc1, 0xbb, 0xc4, 0xbb, 0xc4, 0xd6, + 0xc9, 0xd6, 0xc9, 0x8d, 0xd0, 0x8d, 0xd0, 0xae, 0xd8, 0xae, 0xd8, 0xfb, 0xe1, 0xfb, 0xe1, + 0x2d, 0xec, 0x2d, 0xec, 0xf6, 0xf6, 0xf6, 0xf6, 0x04, 0x02, 0x04, 0x02, 0x02, 0x0d, 0x02, + 0x0d, 0x9e, 0x17, 0x9e, 0x17, 0x85, 0x21, 0x85, 0x21, 0x6d, 0x2a, 0x6d, 0x2a, 0x11, 0x32, + 0x11, 0x32, 0x38, 0x38, 0x38, 0x38, 0xb1, 0x3c, 0xb1, 0x3c, 0x5d, 0x3f, 0x5d, 0x3f, 0x25, + 0x40, 0x25, 0x40, 0x04, 0x3f, 0x04, 0x3f, 0x03, 0x3c, 0x03, 0x3c, 0x38, 0x37, 0x38, 0x37, + 0xc8, 0x30, 0xc8, 0x30, 0xe4, 0x28, 0xe4, 0x28, 0xc9, 0x1f, 0xc9, 0x1f, 0xbb, 0x15, 0xbb, + 0x15, 0x08, 0x0b, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf4, 0xf8, 0xf4, 0x45, 0xea, + 0x45, 0xea, 0x37, 0xe0, 0x37, 0xe0, 0x1c, 0xd7, 0x1c, 0xd7, 0x38, 0xcf, 0x38, 0xcf, 0xc8, + 0xc8, 0xc8, 0xc8, 0xfd, 0xc3, 0xfd, 0xc3, 0xfc, 0xc0, 0xfc, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, + 0xa3, 0xc0, 0xa3, 0xc0, 0x4f, 0xc3, 0x4f, 0xc3, 0xc8, 0xc7, 0xc8, 0xc7, 0xef, 0xcd, 0xef, + 0xcd, 0x93, 0xd5, 0x93, 0xd5, 0x7b, 0xde, 0x7b, 0xde, 0x62, 0xe8, 0x62, 0xe8, 0xfe, 0xf2, + 0xfe, 0xf2, 0xfc, 0xfd, 0xfc, 0xfd, 0x0a, 0x09, 0x0a, 0x09, 0xd3, 0x13, 0xd3, 0x13, 0x05, + 0x1e, 0x05, 0x1e, 0x52, 0x27, 0x52, 0x27, 0x73, 0x2f, 0x73, 0x2f, 0x2a, 0x36, 0x2a, 0x36, + 0x45, 0x3b, 0x45, 0x3b, 0x9b, 0x3e, 0x9b, 0x3e, 0x15, 0x40, 0x15, 0x40, 0xa5, 0x3f, 0xa5, + 0x3f, 0x51, 0x3d, 0x51, 0x3d, 0x29, 0x39, 0x29, 0x39, 0x4d, 0x33, 0x4d, 0x33, 0xea, 0x2b, + 0xea, 0x2b, 0x39, 0x23, 0x39, 0x23, 0x7a, 0x19, 0x7a, 0x19, 0xfa, 0x0e, 0xfa, 0x0e, 0x07, + 0x04, 0x07, 0x04, 0xf6, 0xf8, 0xf6, 0xf8, 0x1a, 0xee, 0x1a, 0xee, 0xc7, 0xe3, 0xc7, 0xe3, + 0x4b, 0xda, 0x4b, 0xda, 0xee, 0xd1, 0xee, 0xd1, 0xf1, 0xca, 0xf1, 0xca, 0x88, 0xc5, 0x88, + 0xc5, 0xdd, 0xc1, 0xdd, 0xc1, 0x0c, 0xc0, 0x0c, 0xc0, 0x22, 0xc0, 0x22, 0xc0, 0x1f, 0xc2, + 0x1f, 0xc2, 0xf4, 0xc5, 0xf4, 0xc5, 0x84, 0xcb, 0x84, 0xcb, 0xa3, 0xd2, 0xa3, 0xd2, 0x1d, + 0xdb, 0x1d, 0xdb, 0xaf, 0xe4, 0xaf, 0xe4, 0x12, 0xef, 0x12, 0xef, 0xf6, 0xf9, 0xf6, 0xf9, + 0x09, 0x05, 0x09, 0x05, 0xf4, 0x0f, 0xf4, 0x0f, 0x66, 0x1a, 0x66, 0x1a, 0x0f, 0x24, 0x0f, + 0x24, 0xa5, 0x2c, 0xa5, 0x2c, 0xe6, 0x33, 0xe6, 0x33, 0x9c, 0x39, 0x9c, 0x39, 0x9b, 0x3d, + 0x9b, 0x3d, 0xc4, 0x3f, 0xc4, 0x3f, 0x06, 0x40, 0x06, 0x40, 0x61, 0x3e, 0x61, 0x3e, 0xe0, + 0x3a, 0xe0, 0x3a, 0x9e, 0x35, 0x9e, 0x35, 0xc4, 0x2e, 0xc4, 0x2e, 0x85, 0x26, 0x85, 0x26, + 0x20, 0x1d, 0x20, 0x1d, 0xdd, 0x12, 0xdd, 0x12, 0x0a, 0x08, 0x0a, 0x08, 0xfa, 0xfc, 0xfa, + 0xfc, 0x01, 0xf2, 0x01, 0xf2, 0x73, 0xe7, 0x73, 0xe7, 0xa0, 0xdd, 0xa0, 0xdd, 0xd3, 0xd4, + 0xd3, 0xd4, 0x4f, 0xcd, 0x4f, 0xcd, 0x4e, 0xc7, 0x4e, 0xc7, 0xfd, 0xc2, 0xfd, 0xc2, 0x7d, + 0xc0, 0x7d, 0xc0, 0xe1, 0xbf, 0xe1, 0xbf, 0x2e, 0xc1, 0x2e, 0xc1, 0x5a, 0xc4, 0x5a, 0xc4, + 0x4d, 0xc9, 0x4d, 0xc9, 0xe1, 0xcf, 0xe1, 0xcf, 0xe4, 0xd7, 0xe4, 0xd7, 0x18, 0xe1, 0x18, + 0xe1, 0x38, 0xeb, 0x38, 0xeb, 0xf7, 0xf5, 0xf7, 0xf5, 0x02, 0x01, 0x02, 0x01, 0x05, 0x0c, + 0x05, 0x0c, 0xad, 0x16, 0xad, 0x16, 0xa8, 0x20, 0xa8, 0x20, 0xaa, 0x29, 0xaa, 0x29, 0x6e, + 0x31, 0x6e, 0x31, 0xb9, 0x37, 0xb9, 0x37, 0x5c, 0x3c, 0x5c, 0x3c, 0x32, 0x3f, 0x32, 0x3f, + 0x27, 0x40, 0x27, 0x40, 0x32, 0x3f, 0x32, 0x3f, 0x5c, 0x3c, 0x5c, 0x3c, 0xb9, 0x37, 0xb9, + 0x37, 0x6e, 0x31, 0x6e, 0x31, 0xaa, 0x29, 0xaa, 0x29, 0xa8, 0x20, 0xa8, 0x20, 0xad, 0x16, + 0xad, 0x16, 0x05, 0x0c, 0x05, 0x0c, 0x02, 0x01, 0x02, 0x01, 0xf7, 0xf5, 0xf7, 0xf5, 0x38, + 0xeb, 0x38, 0xeb, 0x18, 0xe1, 0x18, 0xe1, 0xe4, 0xd7, 0xe4, 0xd7, 0xe1, 0xcf, 0xe1, 0xcf, + 0x4d, 0xc9, 0x4d, 0xc9, 0x5a, 0xc4, 0x5a, 0xc4, 0x2e, 0xc1, 0x2e, 0xc1, 0xe1, 0xbf, 0xe1, + 0xbf, 0x7d, 0xc0, 0x7d, 0xc0, 0xfd, 0xc2, 0xfd, 0xc2, 0x4e, 0xc7, 0x4e, 0xc7, 0x4f, 0xcd, + 0x4f, 0xcd, 0xd3, 0xd4, 0xd3, 0xd4, 0xa0, 0xdd, 0xa0, 0xdd, 0x73, 0xe7, 0x73, 0xe7, 0x01, + 0xf2, 0x01, 0xf2, 0xfa, 0xfc, 0xfa, 0xfc, 0x0a, 0x08, 0x0a, 0x08, 0xdd, 0x12, 0xdd, 0x12, + 0x20, 0x1d, 0x20, 0x1d, 0x85, 0x26, 0x85, 0x26, 0xc4, 0x2e, 0xc4, 0x2e, 0x9e, 0x35, 0x9e, + 0x35, 0xe0, 0x3a, 0xe0, 0x3a, 0x61, 0x3e, 0x61, 0x3e, 0x06, 0x40, 0x06, 0x40, 0xc4, 0x3f, + 0xc4, 0x3f, 0x9b, 0x3d, 0x9b, 0x3d, 0x9c, 0x39, 0x9c, 0x39, 0xe6, 0x33, 0xe6, 0x33, 0xa5, + 0x2c, 0xa5, 0x2c, 0x0f, 0x24, 0x0f, 0x24, 0x66, 0x1a, 0x66, 0x1a, 0xf4, 0x0f, 0xf4, 0x0f, + 0x09, 0x05, 0x09, 0x05, 0xf6, 0xf9, 0xf6, 0xf9, 0x12, 0xef, 0x12, 0xef, 0xaf, 0xe4, 0xaf, + 0xe4, 0x1d, 0xdb, 0x1d, 0xdb, 0xa3, 0xd2, 0xa3, 0xd2, 0x84, 0xcb, 0x84, 0xcb, 0xf4, 0xc5, + 0xf4, 0xc5, 0x1f, 0xc2, 0x1f, 0xc2, 0x22, 0xc0, 0x22, 0xc0, 0x0c, 0xc0, 0x0c, 0xc0, 0xdd, + 0xc1, 0xdd, 0xc1, 0x88, 0xc5, 0x88, 0xc5, 0xf1, 0xca, 0xf1, 0xca, 0xee, 0xd1, 0xee, 0xd1, + 0x4b, 0xda, 0x4b, 0xda, 0xc7, 0xe3, 0xc7, 0xe3, 0x1a, 0xee, 0x1a, 0xee, 0xf6, 0xf8, 0xf6, + 0xf8, 0x07, 0x04, 0x07, 0x04, 0xfa, 0x0e, 0xfa, 0x0e, 0x7a, 0x19, 0x7a, 0x19, 0x39, 0x23, + 0x39, 0x23, 0xea, 0x2b, 0xea, 0x2b, 0x4d, 0x33, 0x4d, 0x33, 0x29, 0x39, 0x29, 0x39, 0x51, + 0x3d, 0x51, 0x3d, 0xa5, 0x3f, 0xa5, 0x3f, 0x15, 0x40, 0x15, 0x40, 0x9b, 0x3e, 0x9b, 0x3e, + 0x45, 0x3b, 0x45, 0x3b, 0x2a, 0x36, 0x2a, 0x36, 0x73, 0x2f, 0x73, 0x2f, 0x52, 0x27, 0x52, + 0x27, 0x05, 0x1e, 0x05, 0x1e, 0xd3, 0x13, 0xd3, 0x13, 0x0a, 0x09, 0x0a, 0x09, 0xfc, 0xfd, + 0xfc, 0xfd, 0xfe, 0xf2, 0xfe, 0xf2, 0x62, 0xe8, 0x62, 0xe8, 0x7b, 0xde, 0x7b, 0xde, 0x93, + 0xd5, 0x93, 0xd5, 0xef, 0xcd, 0xef, 0xcd, 0xc8, 0xc7, 0xc8, 0xc7, 0x4f, 0xc3, 0x4f, 0xc3, + 0xa3, 0xc0, 0xa3, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, 0xfc, 0xc0, 0xfc, 0xc0, 0xfd, 0xc3, 0xfd, + 0xc3, 0xc8, 0xc8, 0xc8, 0xc8, 0x38, 0xcf, 0x38, 0xcf, 0x1c, 0xd7, 0x1c, 0xd7, 0x37, 0xe0, + 0x37, 0xe0, 0x45, 0xea, 0x45, 0xea, 0xf8, 0xf4, 0xf8, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x0b, 0x08, 0x0b, 0xbb, 0x15, 0xbb, 0x15, 0xc9, 0x1f, 0xc9, 0x1f, 0xe4, 0x28, 0xe4, 0x28, + 0xc8, 0x30, 0xc8, 0x30, 0x38, 0x37, 0x38, 0x37, 0x03, 0x3c, 0x03, 0x3c, 0x04, 0x3f, 0x04, + 0x3f, 0x25, 0x40, 0x25, 0x40, 0x5d, 0x3f, 0x5d, 0x3f, 0xb1, 0x3c, 0xb1, 0x3c, 0x38, 0x38, + 0x38, 0x38, 0x11, 0x32, 0x11, 0x32, 0x6d, 0x2a, 0x6d, 0x2a, 0x85, 0x21, 0x85, 0x21, 0x9e, + 0x17, 0x9e, 0x17, 0x02, 0x0d, 0x02, 0x0d, 0x04, 0x02, 0x04, 0x02, 0xf6, 0xf6, 0xf6, 0xf6, + 0x2d, 0xec, 0x2d, 0xec, 0xfb, 0xe1, 0xfb, 0xe1, 0xae, 0xd8, 0xae, 0xd8, 0x8d, 0xd0, 0x8d, + 0xd0, 0xd6, 0xc9, 0xd6, 0xc9, 0xbb, 0xc4, 0xbb, 0xc4, 0x65, 0xc1, 0x65, 0xc1, 0xeb, 0xbf, + 0xeb, 0xbf, 0x5b, 0xc0, 0x5b, 0xc0, 0xaf, 0xc2, 0xaf, 0xc2, 0xd7, 0xc6, 0xd7, 0xc6, 0xb3, + 0xcc, 0xb3, 0xcc, 0x16, 0xd4, 0x16, 0xd4, 0xc7, 0xdc, 0xc7, 0xdc, 0x86, 0xe6, 0x86, 0xe6, + 0x06, 0xf1, 0x06, 0xf1, 0xf9, 0xfb, 0xf9, 0xfb, 0x0a, 0x07, 0x0a, 0x07, 0xe6, 0x11, 0xe6, + 0x11, 0x39, 0x1c, 0x39, 0x1c, 0xb5, 0x25, 0xb5, 0x25, 0x12, 0x2e, 0x12, 0x2e, 0x0f, 0x35, + 0x0f, 0x35, 0x78, 0x3a, 0x78, 0x3a, 0x23, 0x3e, 0x23, 0x3e, 0xf4, 0x3f, 0xf4, 0x3f, 0xde, + 0x3f, 0xde, 0x3f, 0xe1, 0x3d, 0xe1, 0x3d, 0x0c, 0x3a, 0x0c, 0x3a, 0x7c, 0x34, 0x7c, 0x34, + 0x5d, 0x2d, 0x5d, 0x2d, 0xe3, 0x24, 0xe3, 0x24, 0x51, 0x1b, 0x51, 0x1b, 0xee, 0x10, 0xee, + 0x10, 0x0a, 0x06, 0x0a, 0x06, 0xf7, 0xfa, 0xf7, 0xfa, 0x0c, 0xf0, 0x0c, 0xf0, 0x9a, 0xe5, + 0x9a, 0xe5, 0xf1, 0xdb, 0xf1, 0xdb, 0x5b, 0xd3, 0x5b, 0xd3, 0x1a, 0xcc, 0x1a, 0xcc, 0x64, + 0xc6, 0x64, 0xc6, 0x65, 0xc2, 0x65, 0xc2, 0x3c, 0xc0, 0x3c, 0xc0, 0xfa, 0xbf, 0xfa, 0xbf, + 0x9f, 0xc1, 0x9f, 0xc1, 0x20, 0xc5, 0x20, 0xc5, 0x62, 0xca, 0x62, 0xca, 0x3c, 0xd1, 0x3c, + 0xd1, 0x7b, 0xd9, 0x7b, 0xd9, 0xe0, 0xe2, 0xe0, 0xe2, 0x23, 0xed, 0x23, 0xed, 0xf6, 0xf7, + 0xf6, 0xf7, 0x06, 0x03, 0x06, 0x03, 0xff, 0x0d, 0xff, 0x0d, 0x8d, 0x18, 0x8d, 0x18, 0x60, + 0x22, 0x60, 0x22, 0x2d, 0x2b, 0x2d, 0x2b, 0xb1, 0x32, 0xb1, 0x32, 0xb2, 0x38, 0xb2, 0x38, + 0x03, 0x3d, 0x03, 0x3d, 0x83, 0x3f, 0x83, 0x3f, 0x1f, 0x40, 0x1f, 0x40, 0xd2, 0x3e, 0xd2, + 0x3e, 0xa6, 0x3b, 0xa6, 0x3b, 0xb3, 0x36, 0xb3, 0x36, 0x1f, 0x30, 0x1f, 0x30, 0x1c, 0x28, + 0x1c, 0x28, 0xe8, 0x1e, 0xe8, 0x1e, 0xc8, 0x14, 0xc8, 0x14, 0x09, 0x0a, 0x09, 0x0a, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfb, 0xf3, 0xfb, 0xf3, 0x53, 0xe9, 0x53, 0xe9, 0x58, 0xdf, 0x58, 0xdf, + 0x56, 0xd6, 0x56, 0xd6, 0x92, 0xce, 0x92, 0xce, 0x47, 0xc8, 0x47, 0xc8, 0xa4, 0xc3, 0xa4, + 0xc3, 0xce, 0xc0, 0xce, 0xc0, 0xd9, 0xbf, 0xd9, 0xbf, 0xce, 0xc0, 0xce, 0xc0, 0xa4, 0xc3, + 0xa4, 0xc3, 0x47, 0xc8, 0x47, 0xc8, 0x92, 0xce, 0x92, 0xce, 0x56, 0xd6, 0x56, 0xd6, 0x58, + 0xdf, 0x58, 0xdf, 0x53, 0xe9, 0x53, 0xe9, 0xfb, 0xf3, 0xfb, 0xf3, 0xfe, 0xfe, 0xfe, 0xfe, + 0x09, 0x0a, 0x09, 0x0a, 0xc8, 0x14, 0xc8, 0x14, 0xe8, 0x1e, 0xe8, 0x1e, 0x1c, 0x28, 0x1c, + 0x28, 0x1f, 0x30, 0x1f, 0x30, 0xb3, 0x36, 0xb3, 0x36, 0xa6, 0x3b, 0xa6, 0x3b, 0xd2, 0x3e, + 0xd2, 0x3e, 0x1f, 0x40, 0x1f, 0x40, 0x83, 0x3f, 0x83, 0x3f, 0x03, 0x3d, 0x03, 0x3d, 0xb2, + 0x38, 0xb2, 0x38, 0xb1, 0x32, 0xb1, 0x32, 0x2d, 0x2b, 0x2d, 0x2b, 0x60, 0x22, 0x60, 0x22, + 0x8d, 0x18, 0x8d, 0x18, 0xff, 0x0d, 0xff, 0x0d, 0x06, 0x03, 0x06, 0x03, 0xf6, 0xf7, 0xf6, + 0xf7, 0x23, 0xed, 0x23, 0xed, 0xe0, 0xe2, 0xe0, 0xe2, 0x7b, 0xd9, 0x7b, 0xd9, 0x3c, 0xd1, + 0x3c, 0xd1, 0x62, 0xca, 0x62, 0xca, 0x20, 0xc5, 0x20, 0xc5, 0x9f, 0xc1, 0x9f, 0xc1, 0xfa, + 0xbf, 0xfa, 0xbf, 0x3c, 0xc0, 0x3c, 0xc0, 0x65, 0xc2, 0x65, 0xc2, 0x64, 0xc6, 0x64, 0xc6, + 0x1a, 0xcc, 0x1a, 0xcc, 0x5b, 0xd3, 0x5b, 0xd3, 0xf1, 0xdb, 0xf1, 0xdb, 0x9a, 0xe5, 0x9a, + 0xe5, 0x0c, 0xf0, 0x0c, 0xf0, 0xf7, 0xfa, 0xf7, 0xfa, 0x0a, 0x06, 0x0a, 0x06, 0xee, 0x10, + 0xee, 0x10, 0x51, 0x1b, 0x51, 0x1b, 0xe3, 0x24, 0xe3, 0x24, 0x5d, 0x2d, 0x5d, 0x2d, 0x7c, + 0x34, 0x7c, 0x34, 0x0c, 0x3a, 0x0c, 0x3a, 0xe1, 0x3d, 0xe1, 0x3d, 0xde, 0x3f, 0xde, 0x3f, + 0xf4, 0x3f, 0xf4, 0x3f, 0x23, 0x3e, 0x23, 0x3e, 0x78, 0x3a, 0x78, 0x3a, 0x0f, 0x35, 0x0f, + 0x35, 0x12, 0x2e, 0x12, 0x2e, 0xb5, 0x25, 0xb5, 0x25, 0x39, 0x1c, 0x39, 0x1c, 0xe6, 0x11, + 0xe6, 0x11, 0x0a, 0x07, 0x0a, 0x07, 0xf9, 0xfb, 0xf9, 0xfb, 0x06, 0xf1, 0x06, 0xf1, 0x86, + 0xe6, 0x86, 0xe6, 0xc7, 0xdc, 0xc7, 0xdc, 0x16, 0xd4, 0x16, 0xd4, 0xb3, 0xcc, 0xb3, 0xcc, + 0xd7, 0xc6, 0xd7, 0xc6, 0xaf, 0xc2, 0xaf, 0xc2, 0x5b, 0xc0, 0x5b, 0xc0, 0xeb, 0xbf, 0xeb, + 0xbf, 0x65, 0xc1, 0x65, 0xc1, 0xbb, 0xc4, 0xbb, 0xc4, 0xd6, 0xc9, 0xd6, 0xc9, 0x8d, 0xd0, + 0x8d, 0xd0, 0xae, 0xd8, 0xae, 0xd8, 0xfb, 0xe1, 0xfb, 0xe1, 0x2d, 0xec, 0x2d, 0xec, 0xf6, + 0xf6, 0xf6, 0xf6, 0x04, 0x02, 0x04, 0x02, 0x02, 0x0d, 0x02, 0x0d, 0x9e, 0x17, 0x9e, 0x17, + 0x85, 0x21, 0x85, 0x21, 0x6d, 0x2a, 0x6d, 0x2a, 0x11, 0x32, 0x11, 0x32, 0x38, 0x38, 0x38, + 0x38, 0xb1, 0x3c, 0xb1, 0x3c, 0x5d, 0x3f, 0x5d, 0x3f, 0x25, 0x40, 0x25, 0x40, 0x04, 0x3f, + 0x04, 0x3f, 0x03, 0x3c, 0x03, 0x3c, 0x38, 0x37, 0x38, 0x37, 0xc8, 0x30, 0xc8, 0x30, 0xe4, + 0x28, 0xe4, 0x28, 0xc9, 0x1f, 0xc9, 0x1f, 0xbb, 0x15, 0xbb, 0x15, 0x08, 0x0b, 0x08, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf4, 0xf8, 0xf4, 0x45, 0xea, 0x45, 0xea, 0x37, 0xe0, 0x37, + 0xe0, 0x1c, 0xd7, 0x1c, 0xd7, 0x38, 0xcf, 0x38, 0xcf, 0xc8, 0xc8, 0xc8, 0xc8, 0xfd, 0xc3, + 0xfd, 0xc3, 0xfc, 0xc0, 0xfc, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, 0xa3, 0xc0, 0xa3, 0xc0, 0x4f, + 0xc3, 0x4f, 0xc3, 0xc8, 0xc7, 0xc8, 0xc7, 0xef, 0xcd, 0xef, 0xcd, 0x93, 0xd5, 0x93, 0xd5, + 0x7b, 0xde, 0x7b, 0xde, 0x62, 0xe8, 0x62, 0xe8, 0xfe, 0xf2, 0xfe, 0xf2, 0xfc, 0xfd, 0xfc, + 0xfd, 0x0a, 0x09, 0x0a, 0x09, 0xd3, 0x13, 0xd3, 0x13, 0x05, 0x1e, 0x05, 0x1e, 0x52, 0x27, + 0x52, 0x27, 0x73, 0x2f, 0x73, 0x2f, 0x2a, 0x36, 0x2a, 0x36, 0x45, 0x3b, 0x45, 0x3b, 0x9b, + 0x3e, 0x9b, 0x3e, 0x15, 0x40, 0x15, 0x40, 0xa5, 0x3f, 0xa5, 0x3f, 0x51, 0x3d, 0x51, 0x3d, + 0x29, 0x39, 0x29, 0x39, 0x4d, 0x33, 0x4d, 0x33, 0xea, 0x2b, 0xea, 0x2b, 0x39, 0x23, 0x39, + 0x23, 0x7a, 0x19, 0x7a, 0x19, 0xfa, 0x0e, 0xfa, 0x0e, 0x07, 0x04, 0x07, 0x04, 0xf6, 0xf8, + 0xf6, 0xf8, 0x1a, 0xee, 0x1a, 0xee, 0xc7, 0xe3, 0xc7, 0xe3, 0x4b, 0xda, 0x4b, 0xda, 0xee, + 0xd1, 0xee, 0xd1, 0xf1, 0xca, 0xf1, 0xca, 0x88, 0xc5, 0x88, 0xc5, 0xdd, 0xc1, 0xdd, 0xc1, + 0x0c, 0xc0, 0x0c, 0xc0, 0x22, 0xc0, 0x22, 0xc0, 0x1f, 0xc2, 0x1f, 0xc2, 0xf4, 0xc5, 0xf4, + 0xc5, 0x84, 0xcb, 0x84, 0xcb, 0xa3, 0xd2, 0xa3, 0xd2, 0x1d, 0xdb, 0x1d, 0xdb, 0xaf, 0xe4, + 0xaf, 0xe4, 0x12, 0xef, 0x12, 0xef, 0xf6, 0xf9, 0xf6, 0xf9, 0x09, 0x05, 0x09, 0x05, 0xf4, + 0x0f, 0xf4, 0x0f, 0x66, 0x1a, 0x66, 0x1a, 0x0f, 0x24, 0x0f, 0x24, 0xa5, 0x2c, 0xa5, 0x2c, + 0xe6, 0x33, 0xe6, 0x33, 0x9c, 0x39, 0x9c, 0x39, 0x9b, 0x3d, 0x9b, 0x3d, 0xc4, 0x3f, 0xc4, + 0x3f, 0x06, 0x40, 0x06, 0x40, 0x61, 0x3e, 0x61, 0x3e, 0xe0, 0x3a, 0xe0, 0x3a, 0x9e, 0x35, + 0x9e, 0x35, 0xc4, 0x2e, 0xc4, 0x2e, 0x85, 0x26, 0x85, 0x26, 0x20, 0x1d, 0x20, 0x1d, 0xdd, + 0x12, 0xdd, 0x12, 0x0a, 0x08, 0x0a, 0x08, 0xfa, 0xfc, 0xfa, 0xfc, 0x01, 0xf2, 0x01, 0xf2, + 0x73, 0xe7, 0x73, 0xe7, 0xa0, 0xdd, 0xa0, 0xdd, 0xd3, 0xd4, 0xd3, 0xd4, 0x4f, 0xcd, 0x4f, + 0xcd, 0x4e, 0xc7, 0x4e, 0xc7, 0xfd, 0xc2, 0xfd, 0xc2, 0x7d, 0xc0, 0x7d, 0xc0, 0xe1, 0xbf, + 0xe1, 0xbf, 0x2e, 0xc1, 0x2e, 0xc1, 0x5a, 0xc4, 0x5a, 0xc4, 0x4d, 0xc9, 0x4d, 0xc9, 0xe1, + 0xcf, 0xe1, 0xcf, 0xe4, 0xd7, 0xe4, 0xd7, 0x18, 0xe1, 0x18, 0xe1, 0x38, 0xeb, 0x38, 0xeb, + 0xf7, 0xf5, 0xf7, 0xf5, 0x02, 0x01, 0x02, 0x01, 0x05, 0x0c, 0x05, 0x0c, 0xad, 0x16, 0xad, + 0x16, 0xa8, 0x20, 0xa8, 0x20, 0xaa, 0x29, 0xaa, 0x29, 0x6e, 0x31, 0x6e, 0x31, 0xb9, 0x37, + 0xb9, 0x37, 0x5c, 0x3c, 0x5c, 0x3c, 0x32, 0x3f, 0x32, 0x3f, 0x27, 0x40, 0x27, 0x40, 0x32, + 0x3f, 0x32, 0x3f, 0x5c, 0x3c, 0x5c, 0x3c, 0xb9, 0x37, 0xb9, 0x37, 0x6e, 0x31, 0x6e, 0x31, + 0xaa, 0x29, 0xaa, 0x29, 0xa8, 0x20, 0xa8, 0x20, 0xad, 0x16, 0xad, 0x16, 0x05, 0x0c, 0x05, + 0x0c, 0x02, 0x01, 0x02, 0x01, 0xf7, 0xf5, 0xf7, 0xf5, 0x38, 0xeb, 0x38, 0xeb, 0x18, 0xe1, + 0x18, 0xe1, 0xe4, 0xd7, 0xe4, 0xd7, 0xe1, 0xcf, 0xe1, 0xcf, 0x4d, 0xc9, 0x4d, 0xc9, 0x5a, + 0xc4, 0x5a, 0xc4, 0x2e, 0xc1, 0x2e, 0xc1, 0xe1, 0xbf, 0xe1, 0xbf, 0x7d, 0xc0, 0x7d, 0xc0, + 0xfd, 0xc2, 0xfd, 0xc2, 0x4e, 0xc7, 0x4e, 0xc7, 0x4f, 0xcd, 0x4f, 0xcd, 0xd3, 0xd4, 0xd3, + 0xd4, 0xa0, 0xdd, 0xa0, 0xdd, 0x73, 0xe7, 0x73, 0xe7, 0x01, 0xf2, 0x01, 0xf2, 0xfa, 0xfc, + 0xfa, 0xfc, 0x0a, 0x08, 0x0a, 0x08, 0xdd, 0x12, 0xdd, 0x12, 0x20, 0x1d, 0x20, 0x1d, 0x85, + 0x26, 0x85, 0x26, 0xc4, 0x2e, 0xc4, 0x2e, 0x9e, 0x35, 0x9e, 0x35, 0xe0, 0x3a, 0xe0, 0x3a, + 0x61, 0x3e, 0x61, 0x3e, 0x06, 0x40, 0x06, 0x40, 0xc4, 0x3f, 0xc4, 0x3f, 0x9b, 0x3d, 0x9b, + 0x3d, 0x9c, 0x39, 0x9c, 0x39, 0xe6, 0x33, 0xe6, 0x33, 0xa5, 0x2c, 0xa5, 0x2c, 0x0f, 0x24, + 0x0f, 0x24, 0x66, 0x1a, 0x66, 0x1a, 0xf4, 0x0f, 0xf4, 0x0f, 0x09, 0x05, 0x09, 0x05, 0xf6, + 0xf9, 0xf6, 0xf9, 0x12, 0xef, 0x12, 0xef, 0xaf, 0xe4, 0xaf, 0xe4, 0x1d, 0xdb, 0x1d, 0xdb, + 0xa3, 0xd2, 0xa3, 0xd2, 0x84, 0xcb, 0x84, 0xcb, 0xf4, 0xc5, 0xf4, 0xc5, 0x1f, 0xc2, 0x1f, + 0xc2, 0x22, 0xc0, 0x22, 0xc0, 0x0c, 0xc0, 0x0c, 0xc0, 0xdd, 0xc1, 0xdd, 0xc1, 0x88, 0xc5, + 0x88, 0xc5, 0xf1, 0xca, 0xf1, 0xca, 0xee, 0xd1, 0xee, 0xd1, 0x4b, 0xda, 0x4b, 0xda, 0xc7, + 0xe3, 0xc7, 0xe3, 0x1a, 0xee, 0x1a, 0xee, 0xf6, 0xf8, 0xf6, 0xf8, 0x07, 0x04, 0x07, 0x04, + 0xfa, 0x0e, 0xfa, 0x0e, 0x7a, 0x19, 0x7a, 0x19, 0x39, 0x23, 0x39, 0x23, 0xea, 0x2b, 0xea, + 0x2b, 0x4d, 0x33, 0x4d, 0x33, 0x29, 0x39, 0x29, 0x39, 0x51, 0x3d, 0x51, 0x3d, 0xa5, 0x3f, + 0xa5, 0x3f, 0x15, 0x40, 0x15, 0x40, 0x9b, 0x3e, 0x9b, 0x3e, 0x45, 0x3b, 0x45, 0x3b, 0x2a, + 0x36, 0x2a, 0x36, 0x73, 0x2f, 0x73, 0x2f, 0x52, 0x27, 0x52, 0x27, 0x05, 0x1e, 0x05, 0x1e, + 0xd3, 0x13, 0xd3, 0x13, 0x0a, 0x09, 0x0a, 0x09, 0xfc, 0xfd, 0xfc, 0xfd, 0xfe, 0xf2, 0xfe, + 0xf2, 0x62, 0xe8, 0x62, 0xe8, 0x7b, 0xde, 0x7b, 0xde, 0x93, 0xd5, 0x93, 0xd5, 0xef, 0xcd, + 0xef, 0xcd, 0xc8, 0xc7, 0xc8, 0xc7, 0x4f, 0xc3, 0x4f, 0xc3, 0xa3, 0xc0, 0xa3, 0xc0, 0xdb, + 0xbf, 0xdb, 0xbf, 0xfc, 0xc0, 0xfc, 0xc0, 0xfd, 0xc3, 0xfd, 0xc3, 0xc8, 0xc8, 0xc8, 0xc8, + 0x38, 0xcf, 0x38, 0xcf, 0x1c, 0xd7, 0x1c, 0xd7, 0x37, 0xe0, 0x37, 0xe0, 0x45, 0xea, 0x45, + 0xea, 0xf8, 0xf4, 0xf8, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x08, 0x0b, 0xbb, 0x15, + 0xbb, 0x15, 0xc9, 0x1f, 0xc9, 0x1f, 0xe4, 0x28, 0xe4, 0x28, 0xc8, 0x30, 0xc8, 0x30, 0x38, + 0x37, 0x38, 0x37, 0x03, 0x3c, 0x03, 0x3c, 0x04, 0x3f, 0x04, 0x3f, 0x25, 0x40, 0x25, 0x40, + 0x5d, 0x3f, 0x5d, 0x3f, 0xb1, 0x3c, 0xb1, 0x3c, 0x38, 0x38, 0x38, 0x38, 0x11, 0x32, 0x11, + 0x32, 0x6d, 0x2a, 0x6d, 0x2a, 0x85, 0x21, 0x85, 0x21, 0x9e, 0x17, 0x9e, 0x17, 0x02, 0x0d, + 0x02, 0x0d, 0x04, 0x02, 0x04, 0x02, 0xf6, 0xf6, 0xf6, 0xf6, 0x2d, 0xec, 0x2d, 0xec, 0xfb, + 0xe1, 0xfb, 0xe1, 0xae, 0xd8, 0xae, 0xd8, 0x8d, 0xd0, 0x8d, 0xd0, 0xd6, 0xc9, 0xd6, 0xc9, + 0xbb, 0xc4, 0xbb, 0xc4, 0x65, 0xc1, 0x65, 0xc1, 0xeb, 0xbf, 0xeb, 0xbf, 0x5b, 0xc0, 0x5b, + 0xc0, 0xaf, 0xc2, 0xaf, 0xc2, 0xd7, 0xc6, 0xd7, 0xc6, 0xb3, 0xcc, 0xb3, 0xcc, 0x16, 0xd4, + 0x16, 0xd4, 0xc7, 0xdc, 0xc7, 0xdc, 0x86, 0xe6, 0x86, 0xe6, 0x06, 0xf1, 0x06, 0xf1, 0xf9, + 0xfb, 0xf9, 0xfb, 0x0a, 0x07, 0x0a, 0x07, 0xe6, 0x11, 0xe6, 0x11, 0x39, 0x1c, 0x39, 0x1c, + 0xb5, 0x25, 0xb5, 0x25, 0x12, 0x2e, 0x12, 0x2e, 0x0f, 0x35, 0x0f, 0x35, 0x78, 0x3a, 0x78, + 0x3a, 0x23, 0x3e, 0x23, 0x3e, 0xf4, 0x3f, 0xf4, 0x3f, 0xde, 0x3f, 0xde, 0x3f, 0xe1, 0x3d, + 0xe1, 0x3d, 0x0c, 0x3a, 0x0c, 0x3a, 0x7c, 0x34, 0x7c, 0x34, 0x5d, 0x2d, 0x5d, 0x2d, 0xe3, + 0x24, 0xe3, 0x24, 0x51, 0x1b, 0x51, 0x1b, 0xee, 0x10, 0xee, 0x10, 0x0a, 0x06, 0x0a, 0x06, + 0xf7, 0xfa, 0xf7, 0xfa, 0x0c, 0xf0, 0x0c, 0xf0, 0x9a, 0xe5, 0x9a, 0xe5, 0xf1, 0xdb, 0xf1, + 0xdb, 0x5b, 0xd3, 0x5b, 0xd3, 0x1a, 0xcc, 0x1a, 0xcc, 0x64, 0xc6, 0x64, 0xc6, 0x65, 0xc2, + 0x65, 0xc2, 0x3c, 0xc0, 0x3c, 0xc0, 0xfa, 0xbf, 0xfa, 0xbf, 0x9f, 0xc1, 0x9f, 0xc1, 0x20, + 0xc5, 0x20, 0xc5, 0x62, 0xca, 0x62, 0xca, 0x3c, 0xd1, 0x3c, 0xd1, 0x7b, 0xd9, 0x7b, 0xd9, + 0xe0, 0xe2, 0xe0, 0xe2, 0x23, 0xed, 0x23, 0xed, 0xf6, 0xf7, 0xf6, 0xf7, 0x06, 0x03, 0x06, + 0x03, 0xff, 0x0d, 0xff, 0x0d, 0x8d, 0x18, 0x8d, 0x18, 0x60, 0x22, 0x60, 0x22, 0x2d, 0x2b, + 0x2d, 0x2b, 0xb1, 0x32, 0xb1, 0x32, 0xb2, 0x38, 0xb2, 0x38, 0x03, 0x3d, 0x03, 0x3d, 0x83, + 0x3f, 0x83, 0x3f, 0x1f, 0x40, 0x1f, 0x40, 0xd2, 0x3e, 0xd2, 0x3e, 0xa6, 0x3b, 0xa6, 0x3b, + 0xb3, 0x36, 0xb3, 0x36, 0x1f, 0x30, 0x1f, 0x30, 0x1c, 0x28, 0x1c, 0x28, 0xe8, 0x1e, 0xe8, + 0x1e, 0xc8, 0x14, 0xc8, 0x14, 0x09, 0x0a, 0x09, 0x0a, 0xfe, 0xfe, 0xfe, 0xfe, 0xfb, 0xf3, + 0xfb, 0xf3, 0x53, 0xe9, 0x53, 0xe9, 0x58, 0xdf, 0x58, 0xdf, 0x56, 0xd6, 0x56, 0xd6, 0x92, + 0xce, 0x92, 0xce, 0x47, 0xc8, 0x47, 0xc8, 0xa4, 0xc3, 0xa4, 0xc3, 0xce, 0xc0, 0xce, 0xc0, + 0xd9, 0xbf, 0xd9, 0xbf, 0xce, 0xc0, 0xce, 0xc0, 0xa4, 0xc3, 0xa4, 0xc3, 0x47, 0xc8, 0x47, + 0xc8, 0x92, 0xce, 0x92, 0xce, 0x56, 0xd6, 0x56, 0xd6, 0x58, 0xdf, 0x58, 0xdf, 0x53, 0xe9, + 0x53, 0xe9, 0xfb, 0xf3, 0xfb, 0xf3, 0xfe, 0xfe, 0xfe, 0xfe, 0x09, 0x0a, 0x09, 0x0a, 0xc8, + 0x14, 0xc8, 0x14, 0xe8, 0x1e, 0xe8, 0x1e, 0x1c, 0x28, 0x1c, 0x28, 0x1f, 0x30, 0x1f, 0x30, + 0xb3, 0x36, 0xb3, 0x36, 0xa6, 0x3b, 0xa6, 0x3b, 0xd2, 0x3e, 0xd2, 0x3e, 0x1f, 0x40, 0x1f, + 0x40, 0x83, 0x3f, 0x83, 0x3f, 0x03, 0x3d, 0x03, 0x3d, 0xb2, 0x38, 0xb2, 0x38, 0xb1, 0x32, + 0xb1, 0x32, 0x2d, 0x2b, 0x2d, 0x2b, 0x60, 0x22, 0x60, 0x22, 0x8d, 0x18, 0x8d, 0x18, 0xff, + 0x0d, 0xff, 0x0d, 0x06, 0x03, 0x06, 0x03, 0xf6, 0xf7, 0xf6, 0xf7, 0x23, 0xed, 0x23, 0xed, + 0xe0, 0xe2, 0xe0, 0xe2, 0x7b, 0xd9, 0x7b, 0xd9, 0x3c, 0xd1, 0x3c, 0xd1, 0x62, 0xca, 0x62, + 0xca, 0x20, 0xc5, 0x20, 0xc5, 0x9f, 0xc1, 0x9f, 0xc1, 0xfa, 0xbf, 0xfa, 0xbf, 0x3c, 0xc0, + 0x3c, 0xc0, 0x65, 0xc2, 0x65, 0xc2, 0x64, 0xc6, 0x64, 0xc6, 0x1a, 0xcc, 0x1a, 0xcc, 0x5b, + 0xd3, 0x5b, 0xd3, 0xf1, 0xdb, 0xf1, 0xdb, 0x9a, 0xe5, 0x9a, 0xe5, 0x0c, 0xf0, 0x0c, 0xf0, + 0xf7, 0xfa, 0xf7, 0xfa, 0x0a, 0x06, 0x0a, 0x06, 0xee, 0x10, 0xee, 0x10, 0x51, 0x1b, 0x51, + 0x1b, 0xe3, 0x24, 0xe3, 0x24, 0x5d, 0x2d, 0x5d, 0x2d, 0x7c, 0x34, 0x7c, 0x34, 0x0c, 0x3a, + 0x0c, 0x3a, 0xe1, 0x3d, 0xe1, 0x3d, 0xde, 0x3f, 0xde, 0x3f, 0xf4, 0x3f, 0xf4, 0x3f, 0x23, + 0x3e, 0x23, 0x3e, 0x78, 0x3a, 0x78, 0x3a, 0x0f, 0x35, 0x0f, 0x35, 0x12, 0x2e, 0x12, 0x2e, + 0xb5, 0x25, 0xb5, 0x25, 0x39, 0x1c, 0x39, 0x1c, 0xe6, 0x11, 0xe6, 0x11, 0x0a, 0x07, 0x0a, + 0x07, 0xf9, 0xfb, 0xf9, 0xfb, 0x06, 0xf1, 0x06, 0xf1, 0x86, 0xe6, 0x86, 0xe6, 0xc7, 0xdc, + 0xc7, 0xdc, 0x16, 0xd4, 0x16, 0xd4, 0xb3, 0xcc, 0xb3, 0xcc, 0xd7, 0xc6, 0xd7, 0xc6, 0xaf, + 0xc2, 0xaf, 0xc2, 0x5b, 0xc0, 0x5b, 0xc0, 0xeb, 0xbf, 0xeb, 0xbf, 0x65, 0xc1, 0x65, 0xc1, + 0xbb, 0xc4, 0xbb, 0xc4, 0xd6, 0xc9, 0xd6, 0xc9, 0x8d, 0xd0, 0x8d, 0xd0, 0xae, 0xd8, 0xae, + 0xd8, 0xfb, 0xe1, 0xfb, 0xe1, 0x2d, 0xec, 0x2d, 0xec, 0xf6, 0xf6, 0xf6, 0xf6, 0x04, 0x02, + 0x04, 0x02, 0x02, 0x0d, 0x02, 0x0d, 0x9e, 0x17, 0x9e, 0x17, 0x85, 0x21, 0x85, 0x21, 0x6d, + 0x2a, 0x6d, 0x2a, 0x11, 0x32, 0x11, 0x32, 0x38, 0x38, 0x38, 0x38, 0xb1, 0x3c, 0xb1, 0x3c, + 0x5d, 0x3f, 0x5d, 0x3f, 0x25, 0x40, 0x25, 0x40, 0x04, 0x3f, 0x04, 0x3f, 0x03, 0x3c, 0x03, + 0x3c, 0x38, 0x37, 0x38, 0x37, 0xc8, 0x30, 0xc8, 0x30, 0xe4, 0x28, 0xe4, 0x28, 0xc9, 0x1f, + 0xc9, 0x1f, 0xbb, 0x15, 0xbb, 0x15, 0x08, 0x0b, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xf4, 0xf8, 0xf4, 0x45, 0xea, 0x45, 0xea, 0x37, 0xe0, 0x37, 0xe0, 0x1c, 0xd7, 0x1c, 0xd7, + 0x38, 0xcf, 0x38, 0xcf, 0xc8, 0xc8, 0xc8, 0xc8, 0xfd, 0xc3, 0xfd, 0xc3, 0xfc, 0xc0, 0xfc, + 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, 0xa3, 0xc0, 0xa3, 0xc0, 0x4f, 0xc3, 0x4f, 0xc3, 0xc8, 0xc7, + 0xc8, 0xc7, 0xef, 0xcd, 0xef, 0xcd, 0x93, 0xd5, 0x93, 0xd5, 0x7b, 0xde, 0x7b, 0xde, 0x62, + 0xe8, 0x62, 0xe8, 0xfe, 0xf2, 0xfe, 0xf2, 0xfc, 0xfd, 0xfc, 0xfd, 0x0a, 0x09, 0x0a, 0x09, + 0xd3, 0x13, 0xd3, 0x13, 0x05, 0x1e, 0x05, 0x1e, 0x52, 0x27, 0x52, 0x27, 0x73, 0x2f, 0x73, + 0x2f, 0x2a, 0x36, 0x2a, 0x36, 0x45, 0x3b, 0x45, 0x3b, 0x9b, 0x3e, 0x9b, 0x3e, 0x15, 0x40, + 0x15, 0x40, 0xa5, 0x3f, 0xa5, 0x3f, 0x51, 0x3d, 0x51, 0x3d, 0x29, 0x39, 0x29, 0x39, 0x4d, + 0x33, 0x4d, 0x33, 0xea, 0x2b, 0xea, 0x2b, 0x39, 0x23, 0x39, 0x23, 0x7a, 0x19, 0x7a, 0x19, + 0xfa, 0x0e, 0xfa, 0x0e, 0x07, 0x04, 0x07, 0x04, 0xf6, 0xf8, 0xf6, 0xf8, 0x1a, 0xee, 0x1a, + 0xee, 0xc7, 0xe3, 0xc7, 0xe3, 0x4b, 0xda, 0x4b, 0xda, 0xee, 0xd1, 0xee, 0xd1, 0xf1, 0xca, + 0xf1, 0xca, 0x88, 0xc5, 0x88, 0xc5, 0xdd, 0xc1, 0xdd, 0xc1, 0x0c, 0xc0, 0x0c, 0xc0, 0x22, + 0xc0, 0x22, 0xc0, 0x1f, 0xc2, 0x1f, 0xc2, 0xf4, 0xc5, 0xf4, 0xc5, 0x84, 0xcb, 0x84, 0xcb, + 0xa3, 0xd2, 0xa3, 0xd2, 0x1d, 0xdb, 0x1d, 0xdb, 0xaf, 0xe4, 0xaf, 0xe4, 0x12, 0xef, 0x12, + 0xef, 0xf6, 0xf9, 0xf6, 0xf9, 0x09, 0x05, 0x09, 0x05, 0xf4, 0x0f, 0xf4, 0x0f, 0x66, 0x1a, + 0x66, 0x1a, 0x0f, 0x24, 0x0f, 0x24, 0xa5, 0x2c, 0xa5, 0x2c, 0xe6, 0x33, 0xe6, 0x33, 0x9c, + 0x39, 0x9c, 0x39, 0x9b, 0x3d, 0x9b, 0x3d, 0xc4, 0x3f, 0xc4, 0x3f, 0x06, 0x40, 0x06, 0x40, + 0x61, 0x3e, 0x61, 0x3e, 0xe0, 0x3a, 0xe0, 0x3a, 0x9e, 0x35, 0x9e, 0x35, 0xc4, 0x2e, 0xc4, + 0x2e, 0x85, 0x26, 0x85, 0x26, 0x20, 0x1d, 0x20, 0x1d, 0xdd, 0x12, 0xdd, 0x12, 0x0a, 0x08, + 0x0a, 0x08, 0xfa, 0xfc, 0xfa, 0xfc, 0x01, 0xf2, 0x01, 0xf2, 0x73, 0xe7, 0x73, 0xe7, 0xa0, + 0xdd, 0xa0, 0xdd, 0xd3, 0xd4, 0xd3, 0xd4, 0x4f, 0xcd, 0x4f, 0xcd, 0x4e, 0xc7, 0x4e, 0xc7, + 0xfd, 0xc2, 0xfd, 0xc2, 0x7d, 0xc0, 0x7d, 0xc0, 0xe1, 0xbf, 0xe1, 0xbf, 0x2e, 0xc1, 0x2e, + 0xc1, 0x5a, 0xc4, 0x5a, 0xc4, 0x4d, 0xc9, 0x4d, 0xc9, 0xe1, 0xcf, 0xe1, 0xcf, 0xe4, 0xd7, + 0xe4, 0xd7, 0x18, 0xe1, 0x18, 0xe1, 0x38, 0xeb, 0x38, 0xeb, 0xf7, 0xf5, 0xf7, 0xf5, 0x02, + 0x01, 0x02, 0x01, 0x05, 0x0c, 0x05, 0x0c, 0xad, 0x16, 0xad, 0x16, 0xa8, 0x20, 0xa8, 0x20, + 0xaa, 0x29, 0xaa, 0x29, 0x6e, 0x31, 0x6e, 0x31, 0xb9, 0x37, 0xb9, 0x37, 0x5c, 0x3c, 0x5c, + 0x3c, 0x32, 0x3f, 0x32, 0x3f, 0x27, 0x40, 0x27, 0x40, 0x32, 0x3f, 0x32, 0x3f, 0x5c, 0x3c, + 0x5c, 0x3c, 0xb9, 0x37, 0xb9, 0x37, 0x6e, 0x31, 0x6e, 0x31, 0xaa, 0x29, 0xaa, 0x29, 0xa8, + 0x20, 0xa8, 0x20, 0xad, 0x16, 0xad, 0x16, 0x05, 0x0c, 0x05, 0x0c, 0x02, 0x01, 0x02, 0x01, + 0xf7, 0xf5, 0xf7, 0xf5, 0x38, 0xeb, 0x38, 0xeb, 0x18, 0xe1, 0x18, 0xe1, 0xe4, 0xd7, 0xe4, + 0xd7, 0xe1, 0xcf, 0xe1, 0xcf, 0x4d, 0xc9, 0x4d, 0xc9, 0x5a, 0xc4, 0x5a, 0xc4, 0x2e, 0xc1, + 0x2e, 0xc1, 0xe1, 0xbf, 0xe1, 0xbf, 0x7d, 0xc0, 0x7d, 0xc0, 0xfd, 0xc2, 0xfd, 0xc2, 0x4e, + 0xc7, 0x4e, 0xc7, 0x4f, 0xcd, 0x4f, 0xcd, 0xd3, 0xd4, 0xd3, 0xd4, 0xa0, 0xdd, 0xa0, 0xdd, + 0x73, 0xe7, 0x73, 0xe7, 0x01, 0xf2, 0x01, 0xf2, 0xfa, 0xfc, 0xfa, 0xfc, 0x0a, 0x08, 0x0a, + 0x08, 0xdd, 0x12, 0xdd, 0x12, 0x20, 0x1d, 0x20, 0x1d, 0x85, 0x26, 0x85, 0x26, 0xc4, 0x2e, + 0xc4, 0x2e, 0x9e, 0x35, 0x9e, 0x35, 0xe0, 0x3a, 0xe0, 0x3a, 0x61, 0x3e, 0x61, 0x3e, 0x06, + 0x40, 0x06, 0x40, 0xc4, 0x3f, 0xc4, 0x3f, 0x9b, 0x3d, 0x9b, 0x3d, 0x9c, 0x39, 0x9c, 0x39, + 0xe6, 0x33, 0xe6, 0x33, 0xa5, 0x2c, 0xa5, 0x2c, 0x0f, 0x24, 0x0f, 0x24, 0x66, 0x1a, 0x66, + 0x1a, 0xf4, 0x0f, 0xf4, 0x0f, 0x09, 0x05, 0x09, 0x05, 0xf6, 0xf9, 0xf6, 0xf9, 0x12, 0xef, + 0x12, 0xef, 0xaf, 0xe4, 0xaf, 0xe4, 0x1d, 0xdb, 0x1d, 0xdb, 0xa3, 0xd2, 0xa3, 0xd2, 0x84, + 0xcb, 0x84, 0xcb, 0xf4, 0xc5, 0xf4, 0xc5, 0x1f, 0xc2, 0x1f, 0xc2, 0x22, 0xc0, 0x22, 0xc0, + 0x0c, 0xc0, 0x0c, 0xc0, 0xdd, 0xc1, 0xdd, 0xc1, 0x88, 0xc5, 0x88, 0xc5, 0xf1, 0xca, 0xf1, + 0xca, 0xee, 0xd1, 0xee, 0xd1, 0x4b, 0xda, 0x4b, 0xda, 0xc7, 0xe3, 0xc7, 0xe3, 0x1a, 0xee, + 0x1a, 0xee, 0xf6, 0xf8, 0xf6, 0xf8, 0x07, 0x04, 0x07, 0x04, 0xfa, 0x0e, 0xfa, 0x0e, 0x7a, + 0x19, 0x7a, 0x19, 0x39, 0x23, 0x39, 0x23, 0xea, 0x2b, 0xea, 0x2b, 0x4d, 0x33, 0x4d, 0x33, + 0x29, 0x39, 0x29, 0x39, 0x51, 0x3d, 0x51, 0x3d, 0xa5, 0x3f, 0xa5, 0x3f, 0x15, 0x40, 0x15, + 0x40, 0x9b, 0x3e, 0x9b, 0x3e, 0x45, 0x3b, 0x45, 0x3b, 0x2a, 0x36, 0x2a, 0x36, 0x73, 0x2f, + 0x73, 0x2f, 0x52, 0x27, 0x52, 0x27, 0x05, 0x1e, 0x05, 0x1e, 0xd3, 0x13, 0xd3, 0x13, 0x0a, + 0x09, 0x0a, 0x09, 0xfc, 0xfd, 0xfc, 0xfd, 0xfe, 0xf2, 0xfe, 0xf2, 0x62, 0xe8, 0x62, 0xe8, + 0x7b, 0xde, 0x7b, 0xde, 0x93, 0xd5, 0x93, 0xd5, 0xef, 0xcd, 0xef, 0xcd, 0xc8, 0xc7, 0xc8, + 0xc7, 0x4f, 0xc3, 0x4f, 0xc3, 0xa3, 0xc0, 0xa3, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, 0xfc, 0xc0, + 0xfc, 0xc0, 0xfd, 0xc3, 0xfd, 0xc3, 0xc8, 0xc8, 0xc8, 0xc8, 0x38, 0xcf, 0x38, 0xcf, 0x1c, + 0xd7, 0x1c, 0xd7, 0x37, 0xe0, 0x37, 0xe0, 0x45, 0xea, 0x45, 0xea, 0xf8, 0xf4, 0xf8, 0xf4, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x08, 0x0b, 0xbb, 0x15, 0xbb, 0x15, 0xc9, 0x1f, 0xc9, + 0x1f, 0xe4, 0x28, 0xe4, 0x28, 0xc8, 0x30, 0xc8, 0x30, 0x38, 0x37, 0x38, 0x37, 0x03, 0x3c, + 0x03, 0x3c, 0x04, 0x3f, 0x04, 0x3f, 0x25, 0x40, 0x25, 0x40, 0x5d, 0x3f, 0x5d, 0x3f, 0xb1, + 0x3c, 0xb1, 0x3c, 0x38, 0x38, 0x38, 0x38, 0x11, 0x32, 0x11, 0x32, 0x6d, 0x2a, 0x6d, 0x2a, + 0x85, 0x21, 0x85, 0x21, 0x9e, 0x17, 0x9e, 0x17, 0x02, 0x0d, 0x02, 0x0d, 0x04, 0x02, 0x04, + 0x02, 0xf6, 0xf6, 0xf6, 0xf6, 0x2d, 0xec, 0x2d, 0xec, 0xfb, 0xe1, 0xfb, 0xe1, 0xae, 0xd8, + 0xae, 0xd8, 0x8d, 0xd0, 0x8d, 0xd0, 0xd6, 0xc9, 0xd6, 0xc9, 0xbb, 0xc4, 0xbb, 0xc4, 0x65, + 0xc1, 0x65, 0xc1, 0xeb, 0xbf, 0xeb, 0xbf, 0x5b, 0xc0, 0x5b, 0xc0, 0xaf, 0xc2, 0xaf, 0xc2, + 0xd7, 0xc6, 0xd7, 0xc6, 0xb3, 0xcc, 0xb3, 0xcc, 0x16, 0xd4, 0x16, 0xd4, 0xc7, 0xdc, 0xc7, + 0xdc, 0x86, 0xe6, 0x86, 0xe6, 0x06, 0xf1, 0x06, 0xf1, 0xf9, 0xfb, 0xf9, 0xfb, 0x0a, 0x07, + 0x0a, 0x07, 0xe6, 0x11, 0xe6, 0x11, 0x39, 0x1c, 0x39, 0x1c, 0xb5, 0x25, 0xb5, 0x25, 0x12, + 0x2e, 0x12, 0x2e, 0x0f, 0x35, 0x0f, 0x35, 0x78, 0x3a, 0x78, 0x3a, 0x23, 0x3e, 0x23, 0x3e, + 0xf4, 0x3f, 0xf4, 0x3f, 0xde, 0x3f, 0xde, 0x3f, 0xe1, 0x3d, 0xe1, 0x3d, 0x0c, 0x3a, 0x0c, + 0x3a, 0x7c, 0x34, 0x7c, 0x34, 0x5d, 0x2d, 0x5d, 0x2d, 0xe3, 0x24, 0xe3, 0x24, 0x51, 0x1b, + 0x51, 0x1b, 0xee, 0x10, 0xee, 0x10, 0x0a, 0x06, 0x0a, 0x06, 0xf7, 0xfa, 0xf7, 0xfa, 0x0c, + 0xf0, 0x0c, 0xf0, 0x9a, 0xe5, 0x9a, 0xe5, 0xf1, 0xdb, 0xf1, 0xdb, 0x5b, 0xd3, 0x5b, 0xd3, + 0x1a, 0xcc, 0x1a, 0xcc, 0x64, 0xc6, 0x64, 0xc6, 0x65, 0xc2, 0x65, 0xc2, 0x3c, 0xc0, 0x3c, + 0xc0, 0xfa, 0xbf, 0xfa, 0xbf, 0x9f, 0xc1, 0x9f, 0xc1, 0x20, 0xc5, 0x20, 0xc5, 0x62, 0xca, + 0x62, 0xca, 0x3c, 0xd1, 0x3c, 0xd1, 0x7b, 0xd9, 0x7b, 0xd9, 0xe0, 0xe2, 0xe0, 0xe2, 0x23, + 0xed, 0x23, 0xed, 0xf6, 0xf7, 0xf6, 0xf7, 0x06, 0x03, 0x06, 0x03, 0xff, 0x0d, 0xff, 0x0d, + 0x8d, 0x18, 0x8d, 0x18, 0x60, 0x22, 0x60, 0x22, 0x2d, 0x2b, 0x2d, 0x2b, 0xb1, 0x32, 0xb1, + 0x32, 0xb2, 0x38, 0xb2, 0x38, 0x03, 0x3d, 0x03, 0x3d, 0x83, 0x3f, 0x83, 0x3f, 0x1f, 0x40, + 0x1f, 0x40, 0xd2, 0x3e, 0xd2, 0x3e, 0xa6, 0x3b, 0xa6, 0x3b, 0xb3, 0x36, 0xb3, 0x36, 0x1f, + 0x30, 0x1f, 0x30, 0x1c, 0x28, 0x1c, 0x28, 0xe8, 0x1e, 0xe8, 0x1e, 0xc8, 0x14, 0xc8, 0x14, + 0x09, 0x0a, 0x09, 0x0a, 0xfe, 0xfe, 0xfe, 0xfe, 0xfb, 0xf3, 0xfb, 0xf3, 0x53, 0xe9, 0x53, + 0xe9, 0x58, 0xdf, 0x58, 0xdf, 0x56, 0xd6, 0x56, 0xd6, 0x92, 0xce, 0x92, 0xce, 0x47, 0xc8, + 0x47, 0xc8, 0xa4, 0xc3, 0xa4, 0xc3, 0xce, 0xc0, 0xce, 0xc0, 0xd9, 0xbf, 0xd9, 0xbf, 0xce, + 0xc0, 0xce, 0xc0, 0xa4, 0xc3, 0xa4, 0xc3, 0x47, 0xc8, 0x47, 0xc8, 0x92, 0xce, 0x92, 0xce, + 0x56, 0xd6, 0x56, 0xd6, 0x58, 0xdf, 0x58, 0xdf, 0x53, 0xe9, 0x53, 0xe9, 0xfb, 0xf3, 0xfb, + 0xf3, 0xfe, 0xfe, 0xfe, 0xfe, 0x09, 0x0a, 0x09, 0x0a, 0xc8, 0x14, 0xc8, 0x14, 0xe8, 0x1e, + 0xe8, 0x1e, 0x1c, 0x28, 0x1c, 0x28, 0x1f, 0x30, 0x1f, 0x30, 0xb3, 0x36, 0xb3, 0x36, 0xa6, + 0x3b, 0xa6, 0x3b, 0xd2, 0x3e, 0xd2, 0x3e, 0x1f, 0x40, 0x1f, 0x40, 0x83, 0x3f, 0x83, 0x3f, + 0x03, 0x3d, 0x03, 0x3d, 0xb2, 0x38, 0xb2, 0x38, 0xb1, 0x32, 0xb1, 0x32, 0x2d, 0x2b, 0x2d, + 0x2b, 0x60, 0x22, 0x60, 0x22, 0x8d, 0x18, 0x8d, 0x18, 0xff, 0x0d, 0xff, 0x0d, 0x06, 0x03, + 0x06, 0x03, 0xf6, 0xf7, 0xf6, 0xf7, 0x23, 0xed, 0x23, 0xed, 0xe0, 0xe2, 0xe0, 0xe2, 0x7b, + 0xd9, 0x7b, 0xd9, 0x3c, 0xd1, 0x3c, 0xd1, 0x62, 0xca, 0x62, 0xca, 0x20, 0xc5, 0x20, 0xc5, + 0x9f, 0xc1, 0x9f, 0xc1, 0xfa, 0xbf, 0xfa, 0xbf, 0x3c, 0xc0, 0x3c, 0xc0, 0x65, 0xc2, 0x65, + 0xc2, 0x64, 0xc6, 0x64, 0xc6, 0x1a, 0xcc, 0x1a, 0xcc, 0x5b, 0xd3, 0x5b, 0xd3, 0xf1, 0xdb, + 0xf1, 0xdb, 0x9a, 0xe5, 0x9a, 0xe5, 0x0c, 0xf0, 0x0c, 0xf0, 0xf7, 0xfa, 0xf7, 0xfa, 0x0a, + 0x06, 0x0a, 0x06, 0xee, 0x10, 0xee, 0x10, 0x51, 0x1b, 0x51, 0x1b, 0xe3, 0x24, 0xe3, 0x24, + 0x5d, 0x2d, 0x5d, 0x2d, 0x7c, 0x34, 0x7c, 0x34, 0x0c, 0x3a, 0x0c, 0x3a, 0xe1, 0x3d, 0xe1, + 0x3d, 0xde, 0x3f, 0xde, 0x3f, 0xf4, 0x3f, 0xf4, 0x3f, 0x23, 0x3e, 0x23, 0x3e, 0x78, 0x3a, + 0x78, 0x3a, 0x0f, 0x35, 0x0f, 0x35, 0x12, 0x2e, 0x12, 0x2e, 0xb5, 0x25, 0xb5, 0x25, 0x39, + 0x1c, 0x39, 0x1c, 0xe6, 0x11, 0xe6, 0x11, 0x0a, 0x07, 0x0a, 0x07, 0xf9, 0xfb, 0xf9, 0xfb, + 0x06, 0xf1, 0x06, 0xf1, 0x86, 0xe6, 0x86, 0xe6, 0xc7, 0xdc, 0xc7, 0xdc, 0x16, 0xd4, 0x16, + 0xd4, 0xb3, 0xcc, 0xb3, 0xcc, 0xd7, 0xc6, 0xd7, 0xc6, 0xaf, 0xc2, 0xaf, 0xc2, 0x5b, 0xc0, + 0x5b, 0xc0, 0xeb, 0xbf, 0xeb, 0xbf, 0x65, 0xc1, 0x65, 0xc1, 0xbb, 0xc4, 0xbb, 0xc4, 0xd6, + 0xc9, 0xd6, 0xc9, 0x8d, 0xd0, 0x8d, 0xd0, 0xae, 0xd8, 0xae, 0xd8, 0xfb, 0xe1, 0xfb, 0xe1, + 0x2d, 0xec, 0x2d, 0xec, 0xf6, 0xf6, 0xf6, 0xf6, 0x04, 0x02, 0x04, 0x02, 0x02, 0x0d, 0x02, + 0x0d, 0x9e, 0x17, 0x9e, 0x17, 0x85, 0x21, 0x85, 0x21, 0x6d, 0x2a, 0x6d, 0x2a, 0x11, 0x32, + 0x11, 0x32, 0x38, 0x38, 0x38, 0x38, 0xb1, 0x3c, 0xb1, 0x3c, 0x5d, 0x3f, 0x5d, 0x3f, 0x25, + 0x40, 0x25, 0x40, 0x04, 0x3f, 0x04, 0x3f, 0x03, 0x3c, 0x03, 0x3c, 0x38, 0x37, 0x38, 0x37, + 0xc8, 0x30, 0xc8, 0x30, 0xe4, 0x28, 0xe4, 0x28, 0xc9, 0x1f, 0xc9, 0x1f, 0xbb, 0x15, 0xbb, + 0x15, 0x08, 0x0b, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf4, 0xf8, 0xf4, 0x45, 0xea, + 0x45, 0xea, 0x37, 0xe0, 0x37, 0xe0, 0x1c, 0xd7, 0x1c, 0xd7, 0x38, 0xcf, 0x38, 0xcf, 0xc8, + 0xc8, 0xc8, 0xc8, 0xfd, 0xc3, 0xfd, 0xc3, 0xfc, 0xc0, 0xfc, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, + 0xa3, 0xc0, 0xa3, 0xc0, 0x4f, 0xc3, 0x4f, 0xc3, 0xc8, 0xc7, 0xc8, 0xc7, 0xef, 0xcd, 0xef, + 0xcd, 0x93, 0xd5, 0x93, 0xd5, 0x7b, 0xde, 0x7b, 0xde, 0x62, 0xe8, 0x62, 0xe8, 0xfe, 0xf2, + 0xfe, 0xf2, 0xfc, 0xfd, 0xfc, 0xfd, 0x0a, 0x09, 0x0a, 0x09, 0xd3, 0x13, 0xd3, 0x13, 0x05, + 0x1e, 0x05, 0x1e, 0x52, 0x27, 0x52, 0x27, 0x73, 0x2f, 0x73, 0x2f, 0x2a, 0x36, 0x2a, 0x36, + 0x45, 0x3b, 0x45, 0x3b, 0x9b, 0x3e, 0x9b, 0x3e, 0x15, 0x40, 0x15, 0x40, 0xa5, 0x3f, 0xa5, + 0x3f, 0x51, 0x3d, 0x51, 0x3d, 0x29, 0x39, 0x29, 0x39, 0x4d, 0x33, 0x4d, 0x33, 0xea, 0x2b, + 0xea, 0x2b, 0x39, 0x23, 0x39, 0x23, 0x7a, 0x19, 0x7a, 0x19, 0xfa, 0x0e, 0xfa, 0x0e, 0x07, + 0x04, 0x07, 0x04, 0xf6, 0xf8, 0xf6, 0xf8, 0x1a, 0xee, 0x1a, 0xee, 0xc7, 0xe3, 0xc7, 0xe3, + 0x4b, 0xda, 0x4b, 0xda, 0xee, 0xd1, 0xee, 0xd1, 0xf1, 0xca, 0xf1, 0xca, 0x88, 0xc5, 0x88, + 0xc5, 0xdd, 0xc1, 0xdd, 0xc1, 0x0c, 0xc0, 0x0c, 0xc0, 0x22, 0xc0, 0x22, 0xc0, 0x1f, 0xc2, + 0x1f, 0xc2, 0xf4, 0xc5, 0xf4, 0xc5, 0x84, 0xcb, 0x84, 0xcb, 0xa3, 0xd2, 0xa3, 0xd2, 0x1d, + 0xdb, 0x1d, 0xdb, 0xaf, 0xe4, 0xaf, 0xe4, 0x12, 0xef, 0x12, 0xef, 0xf6, 0xf9, 0xf6, 0xf9, + 0x09, 0x05, 0x09, 0x05, 0xf4, 0x0f, 0xf4, 0x0f, 0x66, 0x1a, 0x66, 0x1a, 0x0f, 0x24, 0x0f, + 0x24, 0xa5, 0x2c, 0xa5, 0x2c, 0xe6, 0x33, 0xe6, 0x33, 0x9c, 0x39, 0x9c, 0x39, 0x9b, 0x3d, + 0x9b, 0x3d, 0xc4, 0x3f, 0xc4, 0x3f, 0x06, 0x40, 0x06, 0x40, 0x61, 0x3e, 0x61, 0x3e, 0xe0, + 0x3a, 0xe0, 0x3a, 0x9e, 0x35, 0x9e, 0x35, 0xc4, 0x2e, 0xc4, 0x2e, 0x85, 0x26, 0x85, 0x26, + 0x20, 0x1d, 0x20, 0x1d, 0xdd, 0x12, 0xdd, 0x12, 0x0a, 0x08, 0x0a, 0x08, 0xfa, 0xfc, 0xfa, + 0xfc, 0x01, 0xf2, 0x01, 0xf2, 0x73, 0xe7, 0x73, 0xe7, 0xa0, 0xdd, 0xa0, 0xdd, 0xd3, 0xd4, + 0xd3, 0xd4, 0x4f, 0xcd, 0x4f, 0xcd, 0x4e, 0xc7, 0x4e, 0xc7, 0xfd, 0xc2, 0xfd, 0xc2, 0x7d, + 0xc0, 0x7d, 0xc0, 0xe1, 0xbf, 0xe1, 0xbf, 0x2e, 0xc1, 0x2e, 0xc1, 0x5a, 0xc4, 0x5a, 0xc4, + 0x4d, 0xc9, 0x4d, 0xc9, 0xe1, 0xcf, 0xe1, 0xcf, 0xe4, 0xd7, 0xe4, 0xd7, 0x18, 0xe1, 0x18, + 0xe1, 0x38, 0xeb, 0x38, 0xeb, 0xf7, 0xf5, 0xf7, 0xf5, 0x02, 0x01, 0x02, 0x01, 0x05, 0x0c, + 0x05, 0x0c, 0xad, 0x16, 0xad, 0x16, 0xa8, 0x20, 0xa8, 0x20, 0xaa, 0x29, 0xaa, 0x29, 0x6e, + 0x31, 0x6e, 0x31, 0xb9, 0x37, 0xb9, 0x37, 0x5c, 0x3c, 0x5c, 0x3c, 0x32, 0x3f, 0x32, 0x3f, + 0x27, 0x40, 0x27, 0x40, 0x32, 0x3f, 0x32, 0x3f, 0x5c, 0x3c, 0x5c, 0x3c, 0xb9, 0x37, 0xb9, + 0x37, 0x6e, 0x31, 0x6e, 0x31, 0xaa, 0x29, 0xaa, 0x29, 0xa8, 0x20, 0xa8, 0x20, 0xad, 0x16, + 0xad, 0x16, 0x05, 0x0c, 0x05, 0x0c, 0x02, 0x01, 0x02, 0x01, 0xf7, 0xf5, 0xf7, 0xf5, 0x38, + 0xeb, 0x38, 0xeb, 0x18, 0xe1, 0x18, 0xe1, 0xe4, 0xd7, 0xe4, 0xd7, 0xe1, 0xcf, 0xe1, 0xcf, + 0x4d, 0xc9, 0x4d, 0xc9, 0x5a, 0xc4, 0x5a, 0xc4, 0x2e, 0xc1, 0x2e, 0xc1, 0xe1, 0xbf, 0xe1, + 0xbf, 0x7d, 0xc0, 0x7d, 0xc0, 0xfd, 0xc2, 0xfd, 0xc2, 0x4e, 0xc7, 0x4e, 0xc7, 0x4f, 0xcd, + 0x4f, 0xcd, 0xd3, 0xd4, 0xd3, 0xd4, 0xa0, 0xdd, 0xa0, 0xdd, 0x73, 0xe7, 0x73, 0xe7, 0x01, + 0xf2, 0x01, 0xf2, 0xfa, 0xfc, 0xfa, 0xfc, 0x0a, 0x08, 0x0a, 0x08, 0xdd, 0x12, 0xdd, 0x12, + 0x20, 0x1d, 0x20, 0x1d, 0x85, 0x26, 0x85, 0x26, 0xc4, 0x2e, 0xc4, 0x2e, 0x9e, 0x35, 0x9e, + 0x35, 0xe0, 0x3a, 0xe0, 0x3a, 0x61, 0x3e, 0x61, 0x3e, 0x06, 0x40, 0x06, 0x40, 0xc4, 0x3f, + 0xc4, 0x3f, 0x9b, 0x3d, 0x9b, 0x3d, 0x9c, 0x39, 0x9c, 0x39, 0xe6, 0x33, 0xe6, 0x33, 0xa5, + 0x2c, 0xa5, 0x2c, 0x0f, 0x24, 0x0f, 0x24, 0x66, 0x1a, 0x66, 0x1a, 0xf4, 0x0f, 0xf4, 0x0f, + 0x09, 0x05, 0x09, 0x05, 0xf6, 0xf9, 0xf6, 0xf9, 0x12, 0xef, 0x12, 0xef, 0xaf, 0xe4, 0xaf, + 0xe4, 0x1d, 0xdb, 0x1d, 0xdb, 0xa3, 0xd2, 0xa3, 0xd2, 0x84, 0xcb, 0x84, 0xcb, 0xf4, 0xc5, + 0xf4, 0xc5, 0x1f, 0xc2, 0x1f, 0xc2, 0x22, 0xc0, 0x22, 0xc0, 0x0c, 0xc0, 0x0c, 0xc0, 0xdd, + 0xc1, 0xdd, 0xc1, 0x88, 0xc5, 0x88, 0xc5, 0xf1, 0xca, 0xf1, 0xca, 0xee, 0xd1, 0xee, 0xd1, + 0x4b, 0xda, 0x4b, 0xda, 0xc7, 0xe3, 0xc7, 0xe3, 0x1a, 0xee, 0x1a, 0xee, 0xf6, 0xf8, 0xf6, + 0xf8, 0x07, 0x04, 0x07, 0x04, 0xfa, 0x0e, 0xfa, 0x0e, 0x7a, 0x19, 0x7a, 0x19, 0x39, 0x23, + 0x39, 0x23, 0xea, 0x2b, 0xea, 0x2b, 0x4d, 0x33, 0x4d, 0x33, 0x29, 0x39, 0x29, 0x39, 0x51, + 0x3d, 0x51, 0x3d, 0xa5, 0x3f, 0xa5, 0x3f, 0x15, 0x40, 0x15, 0x40, 0x9b, 0x3e, 0x9b, 0x3e, + 0x45, 0x3b, 0x45, 0x3b, 0x2a, 0x36, 0x2a, 0x36, 0x73, 0x2f, 0x73, 0x2f, 0x52, 0x27, 0x52, + 0x27, 0x05, 0x1e, 0x05, 0x1e, 0xd3, 0x13, 0xd3, 0x13, 0x0a, 0x09, 0x0a, 0x09, 0xfc, 0xfd, + 0xfc, 0xfd, 0xfe, 0xf2, 0xfe, 0xf2, 0x62, 0xe8, 0x62, 0xe8, 0x7b, 0xde, 0x7b, 0xde, 0x93, + 0xd5, 0x93, 0xd5, 0xef, 0xcd, 0xef, 0xcd, 0xc8, 0xc7, 0xc8, 0xc7, 0x4f, 0xc3, 0x4f, 0xc3, + 0xa3, 0xc0, 0xa3, 0xc0, 0xdb, 0xbf, 0xdb, 0xbf, 0xfc, 0xc0, 0xfc, 0xc0, 0xfd, 0xc3, 0xfd, + 0xc3, 0xc8, 0xc8, 0xc8, 0xc8, 0x38, 0xcf, 0x38, 0xcf, 0x1c, 0xd7, 0x1c, 0xd7, 0x37, 0xe0, + 0x37, 0xe0, 0x45, 0xea, 0x45, 0xea, 0xf8, 0xf4, 0xf8, 0xf4}; + +#endif /* ZEPHYR_INCLUDE_SINE_H_ */ diff --git a/subsys/bluetooth/host/classic/a2dp_codec_sbc.c b/subsys/bluetooth/host/classic/a2dp_codec_sbc.c index 33c1a39a098f6..03f1ac9a8d850 100644 --- a/subsys/bluetooth/host/classic/a2dp_codec_sbc.c +++ b/subsys/bluetooth/host/classic/a2dp_codec_sbc.c @@ -14,7 +14,7 @@ #include #include - +#include uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec) { @@ -33,6 +33,23 @@ uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec) } } +enum sbc_ch_mode bt_a2dp_sbc_get_channel_mode(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_MONO) { + return SBC_CH_MODE_MONO; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_DUAL) { + return SBC_CH_MODE_DUAL_CHANNEL; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_STEREO) { + return SBC_CH_MODE_STEREO; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_JOINT) { + return SBC_CH_MODE_JOINT_STEREO; + } else { + return SBC_CH_MODE_MONO; + } +} + uint32_t bt_a2dp_sbc_get_sampling_frequency(struct bt_a2dp_codec_sbc_params *sbc_codec) { __ASSERT_NO_MSG(sbc_codec != NULL); @@ -49,3 +66,44 @@ uint32_t bt_a2dp_sbc_get_sampling_frequency(struct bt_a2dp_codec_sbc_params *sbc return 0U; } } + +uint8_t bt_a2dp_sbc_get_subband_num(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[1] & A2DP_SBC_SUBBAND_4) { + return 4U; + } else if (sbc_codec->config[1] & A2DP_SBC_SUBBAND_8) { + return 8U; + } else { + return 0U; + } +} + +uint8_t bt_a2dp_sbc_get_block_length(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[1] & A2DP_SBC_BLK_LEN_4) { + return 4U; + } else if (sbc_codec->config[1] & A2DP_SBC_BLK_LEN_8) { + return 8U; + } else if (sbc_codec->config[1] & A2DP_SBC_BLK_LEN_12) { + return 12U; + } else if (sbc_codec->config[1] & A2DP_SBC_BLK_LEN_16) { + return 16U; + } else { + return 0U; + } +} + +enum sbc_alloc_mthd bt_a2dp_sbc_get_allocation_method(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[1] & A2DP_SBC_ALLOC_MTHD_SNR) { + return SBC_ALLOC_MTHD_SNR; + } else { + return SBC_ALLOC_MTHD_LOUDNESS; + } +}