xref: /btstack/src/le-audio/le_audio_util.c (revision d21278d067fcdee1c95bdf85b9ac56975680f804)
15deb0bb6SMatthias Ringwald /*
25deb0bb6SMatthias Ringwald  * Copyright (C) 2022 BlueKitchen GmbH
35deb0bb6SMatthias Ringwald  *
45deb0bb6SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
55deb0bb6SMatthias Ringwald  * modification, are permitted provided that the following conditions
65deb0bb6SMatthias Ringwald  * are met:
75deb0bb6SMatthias Ringwald  *
85deb0bb6SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
95deb0bb6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
105deb0bb6SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
115deb0bb6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
125deb0bb6SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
135deb0bb6SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
145deb0bb6SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
155deb0bb6SMatthias Ringwald  *    from this software without specific prior written permission.
165deb0bb6SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
175deb0bb6SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
185deb0bb6SMatthias Ringwald  *    monetary gain.
195deb0bb6SMatthias Ringwald  *
205deb0bb6SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
215deb0bb6SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
225deb0bb6SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
235deb0bb6SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
245deb0bb6SMatthias Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
255deb0bb6SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
265deb0bb6SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
275deb0bb6SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
285deb0bb6SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
295deb0bb6SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
305deb0bb6SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
315deb0bb6SMatthias Ringwald  * SUCH DAMAGE.
325deb0bb6SMatthias Ringwald  *
335deb0bb6SMatthias Ringwald  * Please inquire about commercial licensing options at
345deb0bb6SMatthias Ringwald  * [email protected]
355deb0bb6SMatthias Ringwald  *
365deb0bb6SMatthias Ringwald  */
375deb0bb6SMatthias Ringwald 
385deb0bb6SMatthias Ringwald #define BTSTACK_FILE__ "le_audio_util.c"
395deb0bb6SMatthias Ringwald 
405deb0bb6SMatthias Ringwald #include "btstack_util.h"
415deb0bb6SMatthias Ringwald #include "btstack_debug.h"
425deb0bb6SMatthias Ringwald #include "le-audio/le_audio_util.h"
43*d21278d0SMatthias Ringwald #include "btstack_event.h"
445deb0bb6SMatthias Ringwald 
459ef2c435SMilanka Ringwald 
469ef2c435SMilanka Ringwald static const le_audio_codec_configuration_t codec_specific_config_settings[] = {
479ef2c435SMilanka Ringwald     {"8_1",  LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ,  LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   26},
489ef2c435SMilanka Ringwald     {"8_2",  LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ,  LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  30},
499ef2c435SMilanka Ringwald     {"16_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   30},
509ef2c435SMilanka Ringwald     {"16_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  40},
519ef2c435SMilanka Ringwald     {"24_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   45},
529ef2c435SMilanka Ringwald     {"24_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  60},
539ef2c435SMilanka Ringwald     {"32_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   60},
549ef2c435SMilanka Ringwald     {"32_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  80},
559ef2c435SMilanka Ringwald     {"441_1",LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   97},
569ef2c435SMilanka Ringwald     {"441_2",LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 130},
579ef2c435SMilanka Ringwald     {"48_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   75},
589ef2c435SMilanka Ringwald     {"48_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 100},
599ef2c435SMilanka Ringwald     {"48_3", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   90},
609ef2c435SMilanka Ringwald     {"48_4", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 120},
619ef2c435SMilanka Ringwald     {"48_5", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,  117},
629ef2c435SMilanka Ringwald     {"48_6", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 155},
639ef2c435SMilanka Ringwald };
649ef2c435SMilanka Ringwald 
659ef2c435SMilanka Ringwald static const le_audio_qos_configuration_t qos_config_settings[] = {
664ea63b4dSMilanka Ringwald     {"8_1_1",    7500, 0,  26, 2, 8},
674ea63b4dSMilanka Ringwald     {"8_2_1",   10000, 0,  30, 2, 10},
684ea63b4dSMilanka Ringwald     {"16_1_1",   7500, 0,  30, 2, 8},
694ea63b4dSMilanka Ringwald     {"16_2_1",  10000, 0,  40, 2, 10},
704ea63b4dSMilanka Ringwald     {"24_1_1",   7500, 0,  45, 2, 8},
714ea63b4dSMilanka Ringwald     {"24_2_1",  10000, 0,  60, 2, 10},
724ea63b4dSMilanka Ringwald     {"32_1_1",   7500, 0,  60, 2, 8},
734ea63b4dSMilanka Ringwald     {"32_2_1",  10000, 0,  80, 2, 10},
744ea63b4dSMilanka Ringwald     {"441_1_1",  8163, 1,  97, 5, 24},
754ea63b4dSMilanka Ringwald     {"441_2_1", 10884, 1, 130, 5, 31},
764ea63b4dSMilanka Ringwald     {"48_1_1",   7500, 0,  75, 5, 15},
774ea63b4dSMilanka Ringwald     {"48_2_1",  10000, 0, 100, 5, 20},
784ea63b4dSMilanka Ringwald     {"48_3_1",   7500, 0,  90, 5, 15},
794ea63b4dSMilanka Ringwald     {"48_4_1",  10000, 0, 120, 5, 20},
804ea63b4dSMilanka Ringwald     {"48_5_1",   7500, 0, 117, 5, 15},
814ea63b4dSMilanka Ringwald     {"48_6_1",  10000, 0, 115, 5, 20},
829ef2c435SMilanka Ringwald 
834ea63b4dSMilanka Ringwald     {"8_1_2",    7500, 0,  26, 13, 75},
844ea63b4dSMilanka Ringwald     {"8_2_2",   10000, 0,  30, 13, 95},
854ea63b4dSMilanka Ringwald     {"16_1_2",   7500, 0,  30, 13, 75},
864ea63b4dSMilanka Ringwald     {"16_2_2",  10000, 0,  40, 13, 95},
874ea63b4dSMilanka Ringwald     {"24_1_2",   7500, 0,  45, 13, 75},
884ea63b4dSMilanka Ringwald     {"24_2_2",  10000, 0,  60, 13, 95},
894ea63b4dSMilanka Ringwald     {"32_1_2",   7500, 0,  60, 13, 75},
904ea63b4dSMilanka Ringwald     {"32_2_2",  10000, 0,  80, 13, 95},
914ea63b4dSMilanka Ringwald     {"441_1_2",  8163, 1,  97, 13, 80},
924ea63b4dSMilanka Ringwald     {"441_2_2", 10884, 1, 130, 13, 85},
934ea63b4dSMilanka Ringwald     {"48_1_2",   7500, 0,  75, 13, 75},
944ea63b4dSMilanka Ringwald     {"48_2_2",  10000, 0, 100, 13, 95},
954ea63b4dSMilanka Ringwald     {"48_3_2",   7500, 0,  90, 13, 75},
964ea63b4dSMilanka Ringwald     {"48_4_2",  10000, 0, 120, 13, 100},
974ea63b4dSMilanka Ringwald     {"48_5_2",   7500, 0, 117, 13, 75},
984ea63b4dSMilanka Ringwald     {"48_6_2",  10000, 0, 115, 13, 100}
999ef2c435SMilanka Ringwald };
1009ef2c435SMilanka Ringwald 
codec_offset(le_audio_codec_sampling_frequency_index_t sampling_frequency_index,le_audio_codec_frame_duration_index_t frame_duration_index,le_audio_quality_t audio_quality)1019ef2c435SMilanka Ringwald static uint8_t codec_offset(le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
1029ef2c435SMilanka Ringwald     le_audio_codec_frame_duration_index_t frame_duration_index,
1039ef2c435SMilanka Ringwald     le_audio_quality_t audio_quality){
1049ef2c435SMilanka Ringwald 
1059ef2c435SMilanka Ringwald     btstack_assert(audio_quality <= LE_AUDIO_QUALITY_HIGH);
1069ef2c435SMilanka Ringwald 
1079ef2c435SMilanka Ringwald     if (sampling_frequency_index == LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ){
1089ef2c435SMilanka Ringwald         return 10 + (audio_quality - LE_AUDIO_QUALITY_LOW) * 2 + (frame_duration_index - LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US);
1099ef2c435SMilanka Ringwald     }
1109ef2c435SMilanka Ringwald     return (sampling_frequency_index - LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ) * 2 + (frame_duration_index - LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US);
1119ef2c435SMilanka Ringwald }
1129ef2c435SMilanka Ringwald 
le_audio_util_get_codec_setting(le_audio_codec_sampling_frequency_index_t sampling_frequency_index,le_audio_codec_frame_duration_index_t frame_duration_index,le_audio_quality_t audio_quality)1139ef2c435SMilanka Ringwald const le_audio_codec_configuration_t * le_audio_util_get_codec_setting(
1149ef2c435SMilanka Ringwald     le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
1159ef2c435SMilanka Ringwald     le_audio_codec_frame_duration_index_t frame_duration_index,
1169ef2c435SMilanka Ringwald     le_audio_quality_t audio_quality){
1179ef2c435SMilanka Ringwald 
1189ef2c435SMilanka Ringwald     return &codec_specific_config_settings[codec_offset(sampling_frequency_index, frame_duration_index, audio_quality)];
1199ef2c435SMilanka Ringwald }
1209ef2c435SMilanka Ringwald 
le_audio_util_get_qos_setting(le_audio_codec_sampling_frequency_index_t sampling_frequency_index,le_audio_codec_frame_duration_index_t frame_duration_index,le_audio_quality_t audio_quality,uint8_t num_channels)1219ef2c435SMilanka Ringwald const le_audio_qos_configuration_t * le_audio_util_get_qos_setting(
1229ef2c435SMilanka Ringwald     le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
1239ef2c435SMilanka Ringwald     le_audio_codec_frame_duration_index_t frame_duration_index,
1249ef2c435SMilanka Ringwald     le_audio_quality_t audio_quality, uint8_t num_channels){
1259ef2c435SMilanka Ringwald 
1269ef2c435SMilanka Ringwald     btstack_assert((num_channels >= 1) && (num_channels <= 2));
1279ef2c435SMilanka Ringwald 
1284ea63b4dSMilanka Ringwald     return &qos_config_settings[(num_channels - 1) * 16 + codec_offset(sampling_frequency_index, frame_duration_index, audio_quality)];
1299ef2c435SMilanka Ringwald }
1309ef2c435SMilanka Ringwald 
1319ef2c435SMilanka Ringwald 
1325deb0bb6SMatthias Ringwald // help with buffer == NULL
le_audio_util_virtual_memcpy_helper(const uint8_t * field_data,uint16_t field_len,uint16_t field_offset,uint8_t * buffer,uint16_t buffer_size,uint16_t buffer_offset)13354461c80SMatthias Ringwald uint16_t le_audio_util_virtual_memcpy_helper(
1345deb0bb6SMatthias Ringwald     const uint8_t * field_data, uint16_t field_len, uint16_t field_offset,
1355deb0bb6SMatthias Ringwald     uint8_t * buffer, uint16_t buffer_size, uint16_t buffer_offset){
1365deb0bb6SMatthias Ringwald 
1375deb0bb6SMatthias Ringwald     // only calc total size
1385deb0bb6SMatthias Ringwald     if (buffer == NULL) {
1395deb0bb6SMatthias Ringwald         return field_len;
1405deb0bb6SMatthias Ringwald     }
1415deb0bb6SMatthias Ringwald     return btstack_virtual_memcpy(field_data, field_len, field_offset, buffer, buffer_size, buffer_offset);
1425deb0bb6SMatthias Ringwald }
1435deb0bb6SMatthias Ringwald 
le_audio_util_metadata_virtual_memcpy(const le_audio_metadata_t * metadata,uint8_t metadata_length,uint16_t * records_offset,uint8_t * buffer,uint16_t buffer_size,uint16_t buffer_offset)14454461c80SMatthias Ringwald uint16_t le_audio_util_metadata_virtual_memcpy(const le_audio_metadata_t * metadata, uint8_t metadata_length, uint16_t * records_offset, uint8_t * buffer, uint16_t buffer_size, uint16_t buffer_offset){
1455deb0bb6SMatthias Ringwald     uint16_t metadata_type;
1465deb0bb6SMatthias Ringwald     uint8_t  field_data[7];
1475deb0bb6SMatthias Ringwald     uint16_t stored_bytes = 0;
1485deb0bb6SMatthias Ringwald 
1495deb0bb6SMatthias Ringwald     uint16_t metadata_length_pos = *records_offset;
1505deb0bb6SMatthias Ringwald     field_data[0] = 0;
15154461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *records_offset, buffer, buffer_size,
15254461c80SMatthias Ringwald                                                         buffer_offset);
1535deb0bb6SMatthias Ringwald     *records_offset += 1;
1545deb0bb6SMatthias Ringwald 
1555deb0bb6SMatthias Ringwald     if (metadata_length == 0){
1565deb0bb6SMatthias Ringwald         return stored_bytes;
1575deb0bb6SMatthias Ringwald     }
1585deb0bb6SMatthias Ringwald 
1595deb0bb6SMatthias Ringwald     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
1605deb0bb6SMatthias Ringwald         if ((metadata->metadata_mask & (1 << metadata_type) ) != 0 ){
1615deb0bb6SMatthias Ringwald             // reserve field_data[0] for num butes to store
1625deb0bb6SMatthias Ringwald             field_data[0] = 1;
1635deb0bb6SMatthias Ringwald             field_data[1] = metadata_type;
1645deb0bb6SMatthias Ringwald 
1655deb0bb6SMatthias Ringwald             switch ((le_audio_metadata_type_t)metadata_type){
1665deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
1675deb0bb6SMatthias Ringwald                     field_data[0] += 2;
1685deb0bb6SMatthias Ringwald                     little_endian_store_16(field_data, 2, metadata->preferred_audio_contexts_mask);
16954461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
17054461c80SMatthias Ringwald                                                                         buffer, buffer_size, buffer_offset);
1715deb0bb6SMatthias Ringwald                     *records_offset += field_data[0] + 1;
1725deb0bb6SMatthias Ringwald                     break;
1735deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
1745deb0bb6SMatthias Ringwald                     field_data[0] += 2;
1755deb0bb6SMatthias Ringwald                     little_endian_store_16(field_data, 2, metadata->streaming_audio_contexts_mask);
17654461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
17754461c80SMatthias Ringwald                                                                         buffer, buffer_size, buffer_offset);
1785deb0bb6SMatthias Ringwald                     *records_offset += field_data[0] + 1;
1795deb0bb6SMatthias Ringwald                     break;
1805deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
1815deb0bb6SMatthias Ringwald                     field_data[0] += metadata->program_info_length;
18254461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
18354461c80SMatthias Ringwald                                                                         buffer_size, buffer_offset);
1845deb0bb6SMatthias Ringwald                     *records_offset += 2;
18554461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info,
18654461c80SMatthias Ringwald                                                                         metadata->program_info_length, *records_offset,
18754461c80SMatthias Ringwald                                                                         buffer, buffer_size, buffer_offset);
1885deb0bb6SMatthias Ringwald                     *records_offset += metadata->program_info_length;
1895deb0bb6SMatthias Ringwald                     break;
1905deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_LANGUAGE:
1915deb0bb6SMatthias Ringwald                     field_data[0] += 3;
1925deb0bb6SMatthias Ringwald                     little_endian_store_24(field_data, 2, metadata->language_code);
19354461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
19454461c80SMatthias Ringwald                                                                         buffer, buffer_size, buffer_offset);
1955deb0bb6SMatthias Ringwald                     *records_offset += field_data[0] + 1;
1965deb0bb6SMatthias Ringwald                     break;
1975deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_CCID_LIST:
1985deb0bb6SMatthias Ringwald                     field_data[0] += metadata->ccids_num;
19954461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
20054461c80SMatthias Ringwald                                                                         buffer_size, buffer_offset);
2015deb0bb6SMatthias Ringwald                     *records_offset += 2;
20254461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->ccids, metadata->ccids_num,
20354461c80SMatthias Ringwald                                                                         *records_offset, buffer, buffer_size,
20454461c80SMatthias Ringwald                                                                         buffer_offset);
2055deb0bb6SMatthias Ringwald                     *records_offset += metadata->ccids_num;
2065deb0bb6SMatthias Ringwald                     break;
2075deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
2085deb0bb6SMatthias Ringwald                     field_data[0] += 1;
2095deb0bb6SMatthias Ringwald                     field_data[2] = (uint8_t) metadata->parental_rating;
21054461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
21154461c80SMatthias Ringwald                                                                         buffer, buffer_size, buffer_offset);
2125deb0bb6SMatthias Ringwald                     *records_offset += field_data[0] + 1;
2135deb0bb6SMatthias Ringwald                     break;
2145deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
2155deb0bb6SMatthias Ringwald                     field_data[0] += metadata->program_info_uri_length;
21654461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
21754461c80SMatthias Ringwald                                                                         buffer_size, buffer_offset);
2185deb0bb6SMatthias Ringwald                     *records_offset += 2;
21954461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info_uri,
22054461c80SMatthias Ringwald                                                                         metadata->program_info_uri_length,
22154461c80SMatthias Ringwald                                                                         *records_offset, buffer, buffer_size,
22254461c80SMatthias Ringwald                                                                         buffer_offset);
2235deb0bb6SMatthias Ringwald                     *records_offset += metadata->program_info_uri_length;
2245deb0bb6SMatthias Ringwald                     break;
2255deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
2265deb0bb6SMatthias Ringwald                     field_data[0] += 2 + metadata->extended_metadata_length;
2275deb0bb6SMatthias Ringwald                     field_data[1] = LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA;
2285deb0bb6SMatthias Ringwald                     little_endian_store_16(field_data, 2, metadata->extended_metadata_type);
22954461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer,
23054461c80SMatthias Ringwald                                                                         buffer_size, buffer_offset);
2315deb0bb6SMatthias Ringwald                     *records_offset += 4;
23254461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->extended_metadata,
23354461c80SMatthias Ringwald                                                                         metadata->extended_metadata_length,
23454461c80SMatthias Ringwald                                                                         *records_offset, buffer, buffer_size,
23554461c80SMatthias Ringwald                                                                         buffer_offset);
2365deb0bb6SMatthias Ringwald                     *records_offset += metadata->extended_metadata_length;
2375deb0bb6SMatthias Ringwald                     break;
2385deb0bb6SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
2395deb0bb6SMatthias Ringwald                     field_data[0] += 2 + metadata->vendor_specific_metadata_length;
2405deb0bb6SMatthias Ringwald                     field_data[1] = LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA;
2415deb0bb6SMatthias Ringwald                     little_endian_store_16(field_data, 2, metadata->vendor_specific_company_id);
24254461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer,
24354461c80SMatthias Ringwald                                                                         buffer_size, buffer_offset);
2445deb0bb6SMatthias Ringwald                     *records_offset += 4;
24554461c80SMatthias Ringwald                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->vendor_specific_metadata,
24654461c80SMatthias Ringwald                                                                         metadata->vendor_specific_metadata_length,
24754461c80SMatthias Ringwald                                                                         *records_offset, buffer, buffer_size,
24854461c80SMatthias Ringwald                                                                         buffer_offset);
2495deb0bb6SMatthias Ringwald                     *records_offset += metadata->vendor_specific_metadata_length;
2505deb0bb6SMatthias Ringwald                     break;
2515deb0bb6SMatthias Ringwald                 default:
2525deb0bb6SMatthias Ringwald                     btstack_assert(false);
2535deb0bb6SMatthias Ringwald                     break;
2545deb0bb6SMatthias Ringwald             }
2555deb0bb6SMatthias Ringwald         }
2565deb0bb6SMatthias Ringwald     }
2575deb0bb6SMatthias Ringwald 
2585deb0bb6SMatthias Ringwald     field_data[0] =  *records_offset - metadata_length_pos - 1;
25954461c80SMatthias Ringwald     le_audio_util_virtual_memcpy_helper(field_data, 1, metadata_length_pos, buffer, buffer_size, buffer_offset);
2605deb0bb6SMatthias Ringwald     return stored_bytes;
2615deb0bb6SMatthias Ringwald }
2625deb0bb6SMatthias Ringwald 
263*d21278d0SMatthias Ringwald // parse metadata, first unsupported type is stored in metadata.unsupported_type
le_audio_util_metadata_parse(const uint8_t * buffer,uint8_t buffer_size,le_audio_metadata_t * metadata)264f67c2203SMatthias Ringwald uint16_t le_audio_util_metadata_parse(const uint8_t *buffer, uint8_t buffer_size, le_audio_metadata_t * metadata){
265856b442cSMatthias Ringwald 
266856b442cSMatthias Ringwald     // reset capabilities
267856b442cSMatthias Ringwald     memset(metadata, 0, sizeof(le_audio_metadata_t));
268856b442cSMatthias Ringwald 
2695deb0bb6SMatthias Ringwald     // parse config to get sampling frequency and frame duration
2705deb0bb6SMatthias Ringwald     uint8_t offset = 0;
271856b442cSMatthias Ringwald     uint8_t metadata_config_length = buffer[offset++];
272856b442cSMatthias Ringwald     if (buffer_size < metadata_config_length){
2735deb0bb6SMatthias Ringwald         return 0;
2745deb0bb6SMatthias Ringwald     }
2755deb0bb6SMatthias Ringwald 
2765deb0bb6SMatthias Ringwald     metadata->metadata_mask = 0;
2775deb0bb6SMatthias Ringwald 
278856b442cSMatthias Ringwald     while ((offset + 1) < metadata_config_length){
2795deb0bb6SMatthias Ringwald         uint8_t ltv_len = buffer[offset++];
2805deb0bb6SMatthias Ringwald 
2815deb0bb6SMatthias Ringwald         le_audio_metadata_type_t ltv_type = (le_audio_metadata_type_t)buffer[offset];
2825deb0bb6SMatthias Ringwald         le_audio_parental_rating_t parental_rating;
2835deb0bb6SMatthias Ringwald 
2845deb0bb6SMatthias Ringwald         switch (ltv_type){
2855deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
2865deb0bb6SMatthias Ringwald                 metadata->preferred_audio_contexts_mask = little_endian_read_16(buffer, offset+1);
2875deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
2885deb0bb6SMatthias Ringwald                 break;
2895deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
2905deb0bb6SMatthias Ringwald                 metadata->streaming_audio_contexts_mask = little_endian_read_16(buffer, offset+1);
2915deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
2925deb0bb6SMatthias Ringwald                 break;
2935deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
2945deb0bb6SMatthias Ringwald                 metadata->program_info_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_MAX_LENGTH);
2955deb0bb6SMatthias Ringwald                 memcpy(metadata->program_info, &buffer[offset+1], metadata->program_info_length);
2965deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
2975deb0bb6SMatthias Ringwald                 break;
2985deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_LANGUAGE:
2995deb0bb6SMatthias Ringwald                 metadata->language_code = little_endian_read_24(buffer, offset+1);
3005deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
3015deb0bb6SMatthias Ringwald                 break;
3025deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_CCID_LIST:
303*d21278d0SMatthias Ringwald                 if (ltv_len > 1){
304*d21278d0SMatthias Ringwald                     metadata->ccids_num = btstack_min(ltv_len - 1, LE_CCIDS_MAX_NUM);
3055deb0bb6SMatthias Ringwald                     memcpy(metadata->ccids, &buffer[offset+1], metadata->ccids_num);
3065deb0bb6SMatthias Ringwald                     metadata->metadata_mask |= (1 << ltv_type);
307*d21278d0SMatthias Ringwald                 }
3085deb0bb6SMatthias Ringwald                 break;
3095deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
3105deb0bb6SMatthias Ringwald                 parental_rating = (le_audio_parental_rating_t)buffer[offset+1];
3115deb0bb6SMatthias Ringwald                 metadata->parental_rating = parental_rating;
3125deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
3135deb0bb6SMatthias Ringwald                 break;
3145deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
3155deb0bb6SMatthias Ringwald                 metadata->program_info_uri_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_URI_MAX_LENGTH);
3165deb0bb6SMatthias Ringwald                 memcpy(metadata->program_info_uri, &buffer[offset+1], metadata->program_info_uri_length);
3175deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << ltv_type);
3185deb0bb6SMatthias Ringwald                 break;
3195deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA:
3205deb0bb6SMatthias Ringwald                 if (ltv_len < 2){
3215deb0bb6SMatthias Ringwald                     break;
3225deb0bb6SMatthias Ringwald                 }
3235deb0bb6SMatthias Ringwald                 metadata->extended_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_EXTENDED_METADATA_MAX_LENGHT);
3245deb0bb6SMatthias Ringwald                 metadata->extended_metadata_type = little_endian_read_16(buffer, offset+1);
3255deb0bb6SMatthias Ringwald                 memcpy(metadata->extended_metadata, &buffer[offset+3], metadata->extended_metadata_length);
3265deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION);
3275deb0bb6SMatthias Ringwald                 break;
3285deb0bb6SMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA:
3295deb0bb6SMatthias Ringwald                 if (ltv_len < 2){
3305deb0bb6SMatthias Ringwald                     break;
3315deb0bb6SMatthias Ringwald                 }
3325deb0bb6SMatthias Ringwald                 metadata->vendor_specific_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_VENDOR_SPECIFIC_METADATA_MAX_LENGTH);
3335deb0bb6SMatthias Ringwald                 metadata->vendor_specific_company_id = little_endian_read_16(buffer, offset+1);
3345deb0bb6SMatthias Ringwald                 memcpy(metadata->vendor_specific_metadata, &buffer[offset+3], metadata->vendor_specific_metadata_length);
3355deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION);
3365deb0bb6SMatthias Ringwald                 break;
3375deb0bb6SMatthias Ringwald             default:
3385deb0bb6SMatthias Ringwald                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_RFU);
339*d21278d0SMatthias Ringwald                 metadata->unsupported_type = ltv_type;
3405deb0bb6SMatthias Ringwald                 break;
3415deb0bb6SMatthias Ringwald         }
3425deb0bb6SMatthias Ringwald         offset += ltv_len;
3435deb0bb6SMatthias Ringwald     }
3445deb0bb6SMatthias Ringwald     return offset;
3455deb0bb6SMatthias Ringwald }
3465deb0bb6SMatthias Ringwald 
le_audio_util_metadata_serialize(const le_audio_metadata_t * metadata,uint8_t * event,uint16_t event_size)347f67c2203SMatthias Ringwald uint16_t le_audio_util_metadata_serialize(const le_audio_metadata_t *metadata, uint8_t * event, uint16_t event_size){
348a2763e0cSDirk Helbig     UNUSED(event_size);
349a2763e0cSDirk Helbig 
3505deb0bb6SMatthias Ringwald     uint8_t pos = 0;
3515deb0bb6SMatthias Ringwald 
3525deb0bb6SMatthias Ringwald     event[pos++] = (uint8_t)metadata->metadata_mask;
3535deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, metadata->preferred_audio_contexts_mask);
3545deb0bb6SMatthias Ringwald     pos += 2;
3555deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, metadata->streaming_audio_contexts_mask);
3565deb0bb6SMatthias Ringwald     pos += 2;
3575deb0bb6SMatthias Ringwald 
3585deb0bb6SMatthias Ringwald     event[pos++] = metadata->program_info_length;
3595deb0bb6SMatthias Ringwald     memcpy(&event[pos], &metadata->program_info[0], metadata->program_info_length);
3605deb0bb6SMatthias Ringwald     pos += metadata->program_info_length;
3615deb0bb6SMatthias Ringwald 
3625deb0bb6SMatthias Ringwald     little_endian_store_24(event, pos, metadata->language_code);
3635deb0bb6SMatthias Ringwald     pos += 3;
3645deb0bb6SMatthias Ringwald 
3655deb0bb6SMatthias Ringwald     event[pos++] = metadata->ccids_num;
3665deb0bb6SMatthias Ringwald     memcpy(&event[pos], &metadata->ccids[0], metadata->ccids_num);
3675deb0bb6SMatthias Ringwald     pos += metadata->ccids_num;
3685deb0bb6SMatthias Ringwald 
3695deb0bb6SMatthias Ringwald     event[pos++] = (uint8_t)metadata->parental_rating;
3705deb0bb6SMatthias Ringwald 
3715deb0bb6SMatthias Ringwald     event[pos++] = metadata->program_info_uri_length;
3725deb0bb6SMatthias Ringwald     memcpy(&event[pos], &metadata->program_info_uri[0], metadata->program_info_uri_length);
3735deb0bb6SMatthias Ringwald     pos += metadata->program_info_uri_length;
3745deb0bb6SMatthias Ringwald 
3755deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, metadata->extended_metadata_type);
3765deb0bb6SMatthias Ringwald     pos += 2;
3775deb0bb6SMatthias Ringwald 
3785deb0bb6SMatthias Ringwald     event[pos++] = metadata->extended_metadata_length;
3795deb0bb6SMatthias Ringwald     memcpy(&event[pos], &metadata->extended_metadata[0], metadata->extended_metadata_length);
3805deb0bb6SMatthias Ringwald     pos += metadata->extended_metadata_length;
3815deb0bb6SMatthias Ringwald 
3825deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, metadata->vendor_specific_company_id);
3835deb0bb6SMatthias Ringwald     pos += 2;
3845deb0bb6SMatthias Ringwald 
3855deb0bb6SMatthias Ringwald     event[pos++] = metadata->vendor_specific_metadata_length;
3865deb0bb6SMatthias Ringwald     memcpy(&event[pos], &metadata->vendor_specific_metadata[0], metadata->vendor_specific_metadata_length);
3875deb0bb6SMatthias Ringwald     pos += metadata->vendor_specific_metadata_length;
3885deb0bb6SMatthias Ringwald 
3895deb0bb6SMatthias Ringwald     return pos;
3905deb0bb6SMatthias Ringwald }
391fd6b825bSMatthias Ringwald 
le_audio_util_get_value_size_for_metadata_type(const le_audio_metadata_t * metadata,le_audio_metadata_type_t metadata_type)392f67c2203SMatthias Ringwald static uint16_t le_audio_util_get_value_size_for_metadata_type(const le_audio_metadata_t *metadata, le_audio_metadata_type_t metadata_type){
393fd6b825bSMatthias Ringwald     switch (metadata_type){
394fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
395fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
396fd6b825bSMatthias Ringwald             return 2;
397fd6b825bSMatthias Ringwald 
398fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
399fd6b825bSMatthias Ringwald             return metadata->program_info_length;
400fd6b825bSMatthias Ringwald 
401fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_LANGUAGE:
402fd6b825bSMatthias Ringwald             return 3;
403fd6b825bSMatthias Ringwald 
404fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_CCID_LIST:
405fd6b825bSMatthias Ringwald             return metadata->ccids_num;
406fd6b825bSMatthias Ringwald 
407fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
408fd6b825bSMatthias Ringwald             return 1;
409fd6b825bSMatthias Ringwald 
410fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
411fd6b825bSMatthias Ringwald             return metadata->program_info_uri_length;
412fd6b825bSMatthias Ringwald 
413fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
414fd6b825bSMatthias Ringwald             return 2 + metadata->extended_metadata_length;
415fd6b825bSMatthias Ringwald 
416fd6b825bSMatthias Ringwald         case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
417fd6b825bSMatthias Ringwald             return 2 + metadata->vendor_specific_metadata_length;
418fd6b825bSMatthias Ringwald         default:
419fd6b825bSMatthias Ringwald             break;
420fd6b825bSMatthias Ringwald     }
421fd6b825bSMatthias Ringwald     return 0;
422fd6b825bSMatthias Ringwald }
423fd6b825bSMatthias Ringwald 
le_audio_util_metadata_serialize_using_mask(const le_audio_metadata_t * metadata,uint8_t * tlv_buffer,uint16_t tlv_buffer_size)424f67c2203SMatthias Ringwald uint16_t le_audio_util_metadata_serialize_using_mask(const le_audio_metadata_t *metadata, uint8_t * tlv_buffer, uint16_t tlv_buffer_size){
425fd6b825bSMatthias Ringwald     uint16_t metadata_type;
426fd6b825bSMatthias Ringwald     uint16_t pos = 0;
427fd6b825bSMatthias Ringwald 
428fd6b825bSMatthias Ringwald     uint16_t remaining_bytes = tlv_buffer_size;
429fd6b825bSMatthias Ringwald 
430fd6b825bSMatthias Ringwald     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
431fd6b825bSMatthias Ringwald         if ((metadata->metadata_mask & (1 << metadata_type)) == 0){
432fd6b825bSMatthias Ringwald             continue;
433fd6b825bSMatthias Ringwald         }
434fd6b825bSMatthias Ringwald 
435fd6b825bSMatthias Ringwald         uint8_t payload_length = le_audio_util_get_value_size_for_metadata_type(metadata, (le_audio_metadata_type_t)metadata_type);
436fd6b825bSMatthias Ringwald         // ensure that there is enough space in TLV to store length (1), type(1) and payload
437fd6b825bSMatthias Ringwald         if (remaining_bytes < (2 + payload_length)){
438fd6b825bSMatthias Ringwald             return pos;
439fd6b825bSMatthias Ringwald         }
440fd6b825bSMatthias Ringwald 
441fd6b825bSMatthias Ringwald         tlv_buffer[pos++] = 1 + payload_length; // add one extra byte to count size of type (1 byte)
442fd6b825bSMatthias Ringwald         tlv_buffer[pos++] = metadata_type;
443fd6b825bSMatthias Ringwald 
444fd6b825bSMatthias Ringwald         switch ((le_audio_metadata_type_t)metadata_type){
445fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
446fd6b825bSMatthias Ringwald                 little_endian_store_16(tlv_buffer, pos, metadata->preferred_audio_contexts_mask);
447fd6b825bSMatthias Ringwald                 break;
448fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
449fd6b825bSMatthias Ringwald                 little_endian_store_16(tlv_buffer, pos, metadata->streaming_audio_contexts_mask);
450fd6b825bSMatthias Ringwald                 break;
451fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
452fd6b825bSMatthias Ringwald                 memcpy(&tlv_buffer[pos], metadata->program_info, metadata->program_info_length);
453fd6b825bSMatthias Ringwald                 break;
454fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_LANGUAGE:
455fd6b825bSMatthias Ringwald                 little_endian_store_24(tlv_buffer, pos, metadata->language_code);
456fd6b825bSMatthias Ringwald                 break;
457fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_CCID_LIST:
458fd6b825bSMatthias Ringwald                 memcpy(&tlv_buffer[pos], metadata->ccids, metadata->ccids_num);
459fd6b825bSMatthias Ringwald                 break;
460fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
461fd6b825bSMatthias Ringwald                 break;
462fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
463fd6b825bSMatthias Ringwald                 memcpy(&tlv_buffer[pos], metadata->program_info_uri, metadata->program_info_uri_length);
464fd6b825bSMatthias Ringwald                 break;
465fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
466fd6b825bSMatthias Ringwald                 little_endian_store_16(tlv_buffer, pos, metadata->extended_metadata_type);
467fd6b825bSMatthias Ringwald                 memcpy(&tlv_buffer[pos + 2], metadata->extended_metadata, metadata->extended_metadata_length);
468fd6b825bSMatthias Ringwald                 break;
469fd6b825bSMatthias Ringwald             case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
470fd6b825bSMatthias Ringwald                 little_endian_store_16(tlv_buffer, pos, metadata->vendor_specific_company_id);
471fd6b825bSMatthias Ringwald                 memcpy(&tlv_buffer[pos + 2], metadata->vendor_specific_metadata, metadata->vendor_specific_metadata_length);
472fd6b825bSMatthias Ringwald                 break;
473fd6b825bSMatthias Ringwald             default:
474fd6b825bSMatthias Ringwald                 break;
475fd6b825bSMatthias Ringwald         }
476fd6b825bSMatthias Ringwald         pos += payload_length;
477fd6b825bSMatthias Ringwald         remaining_bytes -= (payload_length + 2);
478fd6b825bSMatthias Ringwald     }
479fd6b825bSMatthias Ringwald     return pos;
480fd6b825bSMatthias Ringwald }
481597c9a4eSMilanka Ringwald 
le_audio_util_get_btstack_lc3_frame_duration(le_audio_codec_frame_duration_index_t frame_duration_index)48209d23cbeSMilanka Ringwald btstack_lc3_frame_duration_t le_audio_util_get_btstack_lc3_frame_duration(le_audio_codec_frame_duration_index_t frame_duration_index){
48309d23cbeSMilanka Ringwald     switch (frame_duration_index){
484597c9a4eSMilanka Ringwald         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US:
485597c9a4eSMilanka Ringwald             return BTSTACK_LC3_FRAME_DURATION_7500US;
486597c9a4eSMilanka Ringwald         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US:
487597c9a4eSMilanka Ringwald             return BTSTACK_LC3_FRAME_DURATION_10000US;
488597c9a4eSMilanka Ringwald         default:
489597c9a4eSMilanka Ringwald             btstack_assert(false);
490597c9a4eSMilanka Ringwald             break;
491597c9a4eSMilanka Ringwald     }
492597c9a4eSMilanka Ringwald     return 0;
493597c9a4eSMilanka Ringwald }
494597c9a4eSMilanka Ringwald 
le_audio_get_frame_duration_us(le_audio_codec_frame_duration_index_t frame_duration_index)49509d23cbeSMilanka Ringwald uint16_t le_audio_get_frame_duration_us(le_audio_codec_frame_duration_index_t frame_duration_index){
49609d23cbeSMilanka Ringwald     switch (frame_duration_index){
497597c9a4eSMilanka Ringwald         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US:
498597c9a4eSMilanka Ringwald             return 7500;
499597c9a4eSMilanka Ringwald         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US:
500597c9a4eSMilanka Ringwald             return 10000;
501597c9a4eSMilanka Ringwald         default:
502597c9a4eSMilanka Ringwald             return 0;
503597c9a4eSMilanka Ringwald     }
504597c9a4eSMilanka Ringwald }
50509d23cbeSMilanka Ringwald 
le_audio_get_frame_duration_index(uint16_t frame_duration_us)50609d23cbeSMilanka Ringwald le_audio_codec_frame_duration_index_t le_audio_get_frame_duration_index(uint16_t frame_duration_us){
50709d23cbeSMilanka Ringwald     switch (frame_duration_us){
50809d23cbeSMilanka Ringwald         case 7500:
50909d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US;
51009d23cbeSMilanka Ringwald         case 10000:
51109d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US;
51209d23cbeSMilanka Ringwald         default:
51309d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_RFU;
51409d23cbeSMilanka Ringwald     }
51509d23cbeSMilanka Ringwald }
51609d23cbeSMilanka Ringwald 
le_audio_get_sampling_frequency_hz(le_audio_codec_sampling_frequency_index_t sampling_frequency_index)51709d23cbeSMilanka Ringwald uint32_t le_audio_get_sampling_frequency_hz(le_audio_codec_sampling_frequency_index_t sampling_frequency_index){
51809d23cbeSMilanka Ringwald     switch (sampling_frequency_index){
51909d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ:
52009d23cbeSMilanka Ringwald                 return 8000;
52109d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_11025_HZ:
52209d23cbeSMilanka Ringwald                 return 11025;
52309d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ:
52409d23cbeSMilanka Ringwald                 return 16000;
52509d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_22050_HZ:
52609d23cbeSMilanka Ringwald                 return 22050;
52709d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ:
52809d23cbeSMilanka Ringwald                 return 24000;
52909d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ:
53009d23cbeSMilanka Ringwald                 return 32000;
53109d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ:
53209d23cbeSMilanka Ringwald                 return 44100;
53309d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ:
53409d23cbeSMilanka Ringwald                 return 48000;
53509d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_88200_HZ:
53609d23cbeSMilanka Ringwald                 return 88200;
53709d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_96000_HZ:
53809d23cbeSMilanka Ringwald                 return 96000;
53909d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_176400_HZ:
54009d23cbeSMilanka Ringwald                 return 176400;
54109d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_192000_HZ:
54209d23cbeSMilanka Ringwald                 return 192000;
54309d23cbeSMilanka Ringwald         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_384000_HZ:
54409d23cbeSMilanka Ringwald                 return 384000;
54509d23cbeSMilanka Ringwald         default:
54677a8d8daSMatthias Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_INVALID;
54709d23cbeSMilanka Ringwald     }
54809d23cbeSMilanka Ringwald }
54909d23cbeSMilanka Ringwald 
le_audio_get_sampling_frequency_index(uint32_t sampling_frequency_hz)55009d23cbeSMilanka Ringwald le_audio_codec_sampling_frequency_index_t le_audio_get_sampling_frequency_index(uint32_t sampling_frequency_hz){
55109d23cbeSMilanka Ringwald     switch (sampling_frequency_hz){
55209d23cbeSMilanka Ringwald         case 0:
55309d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_INVALID;
55409d23cbeSMilanka Ringwald         case 8000:
55509d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ;
55609d23cbeSMilanka Ringwald         case 11025:
55709d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_11025_HZ;
55809d23cbeSMilanka Ringwald         case 16000:
55909d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ;
56009d23cbeSMilanka Ringwald         case 22050:
56109d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_22050_HZ;
56209d23cbeSMilanka Ringwald         case 24000:
56309d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ;
56409d23cbeSMilanka Ringwald         case 32000:
56509d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ;
56609d23cbeSMilanka Ringwald         case 44100:
56709d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ;
56809d23cbeSMilanka Ringwald         case 48000:
56909d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ;
57009d23cbeSMilanka Ringwald         case 88200:
57109d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_88200_HZ;
57209d23cbeSMilanka Ringwald         case 96000:
57309d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_96000_HZ;
57409d23cbeSMilanka Ringwald         case 176400:
57509d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_176400_HZ;
57609d23cbeSMilanka Ringwald         case 192000:
57709d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_192000_HZ;
57809d23cbeSMilanka Ringwald         case 384000:
57909d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_384000_HZ;
58009d23cbeSMilanka Ringwald         default:
58109d23cbeSMilanka Ringwald             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_RFU;
58209d23cbeSMilanka Ringwald     }
58309d23cbeSMilanka Ringwald }
58409d23cbeSMilanka Ringwald 
le_audio_util_metadata_using_mask_from_metadata_event(const uint8_t * packet,uint16_t packet_size,le_audio_metadata_t * metadata_out)585*d21278d0SMatthias Ringwald void le_audio_util_metadata_using_mask_from_metadata_event(const uint8_t * packet, uint16_t packet_size, le_audio_metadata_t * metadata_out){
586*d21278d0SMatthias Ringwald     UNUSED(packet_size);
587*d21278d0SMatthias Ringwald     btstack_assert(metadata_out != NULL);
588*d21278d0SMatthias Ringwald     if (packet_size == 0){
589*d21278d0SMatthias Ringwald         return;
590*d21278d0SMatthias Ringwald     }
591*d21278d0SMatthias Ringwald 
592*d21278d0SMatthias Ringwald     memset(metadata_out, 0, sizeof(le_audio_metadata_t));
593*d21278d0SMatthias Ringwald     metadata_out->metadata_mask = leaudio_subevent_ascs_server_metadata_get_metadata_mask(packet);
594*d21278d0SMatthias Ringwald 
595*d21278d0SMatthias Ringwald     uint16_t metadata_type;
596*d21278d0SMatthias Ringwald     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
597*d21278d0SMatthias Ringwald         if ((metadata_out->metadata_mask & (1 << metadata_type) ) != 0 ){
598*d21278d0SMatthias Ringwald 
599*d21278d0SMatthias Ringwald             switch ((le_audio_metadata_type_t)metadata_type){
600*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
601*d21278d0SMatthias Ringwald                     metadata_out->preferred_audio_contexts_mask = leaudio_subevent_ascs_server_metadata_get_preferred_audio_contexts_mask(packet);
602*d21278d0SMatthias Ringwald                     break;
603*d21278d0SMatthias Ringwald 
604*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
605*d21278d0SMatthias Ringwald                     metadata_out->streaming_audio_contexts_mask = leaudio_subevent_ascs_server_metadata_get_streaming_audio_contexts_mask(packet);
606*d21278d0SMatthias Ringwald                     break;
607*d21278d0SMatthias Ringwald 
608*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
609*d21278d0SMatthias Ringwald                     metadata_out->program_info_length = leaudio_subevent_ascs_server_metadata_get_program_info_length(packet);
610*d21278d0SMatthias Ringwald                     memcpy(metadata_out->program_info, leaudio_subevent_ascs_server_metadata_get_program_info(packet), metadata_out->program_info_length);
611*d21278d0SMatthias Ringwald                     break;
612*d21278d0SMatthias Ringwald 
613*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_LANGUAGE:
614*d21278d0SMatthias Ringwald                     metadata_out->language_code = leaudio_subevent_ascs_server_metadata_get_language_code(packet);
615*d21278d0SMatthias Ringwald                     break;
616*d21278d0SMatthias Ringwald 
617*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_CCID_LIST:
618*d21278d0SMatthias Ringwald                     metadata_out->ccids_num = leaudio_subevent_ascs_server_metadata_get_ccids_num(packet);
619*d21278d0SMatthias Ringwald                     memcpy(metadata_out->ccids, leaudio_subevent_ascs_server_metadata_get_ccids(packet), metadata_out->ccids_num);
620*d21278d0SMatthias Ringwald                     break;
621*d21278d0SMatthias Ringwald 
622*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
623*d21278d0SMatthias Ringwald                     metadata_out->parental_rating = (le_audio_parental_rating_t)leaudio_subevent_ascs_server_metadata_get_parental_rating(packet);
624*d21278d0SMatthias Ringwald                     break;
625*d21278d0SMatthias Ringwald 
626*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
627*d21278d0SMatthias Ringwald                     metadata_out->program_info_uri_length = leaudio_subevent_ascs_server_metadata_get_program_info_uri_length(packet);
628*d21278d0SMatthias Ringwald                     memcpy(metadata_out->program_info_uri, leaudio_subevent_ascs_server_metadata_get_program_info_uri(packet), metadata_out->program_info_uri_length);
629*d21278d0SMatthias Ringwald                     break;
630*d21278d0SMatthias Ringwald 
631*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
632*d21278d0SMatthias Ringwald                     metadata_out->extended_metadata_type = leaudio_subevent_ascs_server_metadata_get_extended_metadata_type(packet);
633*d21278d0SMatthias Ringwald                     metadata_out->extended_metadata_length = leaudio_subevent_ascs_server_metadata_get_extended_metadata_value_length(packet);
634*d21278d0SMatthias Ringwald                     memcpy(metadata_out->extended_metadata, leaudio_subevent_ascs_server_metadata_get_extended_metadata_value(packet), metadata_out->extended_metadata_length);
635*d21278d0SMatthias Ringwald                     break;
636*d21278d0SMatthias Ringwald 
637*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
638*d21278d0SMatthias Ringwald                     metadata_out->vendor_specific_company_id = leaudio_subevent_ascs_server_metadata_get_vendor_specific_metadata_type(packet);
639*d21278d0SMatthias Ringwald                     metadata_out->vendor_specific_metadata_length = leaudio_subevent_ascs_server_metadata_get_vendor_specific_metadata_value_length(packet);
640*d21278d0SMatthias Ringwald                     memcpy(metadata_out->vendor_specific_metadata, leaudio_subevent_ascs_server_metadata_get_vendor_specific_metadata_value(packet), metadata_out->vendor_specific_metadata_length);
641*d21278d0SMatthias Ringwald                     break;
642*d21278d0SMatthias Ringwald 
643*d21278d0SMatthias Ringwald                 default:
644*d21278d0SMatthias Ringwald                     btstack_assert(false);
645*d21278d0SMatthias Ringwald                     break;
646*d21278d0SMatthias Ringwald             }
647*d21278d0SMatthias Ringwald         }
648*d21278d0SMatthias Ringwald     }
649*d21278d0SMatthias Ringwald }
650*d21278d0SMatthias Ringwald 
le_audio_util_metadata_using_mask_from_enable_event(const uint8_t * packet,uint16_t packet_size,le_audio_metadata_t * metadata_out)651*d21278d0SMatthias Ringwald void le_audio_util_metadata_using_mask_from_enable_event(const uint8_t * packet, uint16_t packet_size, le_audio_metadata_t * metadata_out){
652*d21278d0SMatthias Ringwald     UNUSED(packet_size);
653*d21278d0SMatthias Ringwald     btstack_assert(metadata_out != NULL);
654*d21278d0SMatthias Ringwald     if (packet_size == 0){
655*d21278d0SMatthias Ringwald         return;
656*d21278d0SMatthias Ringwald     }
657*d21278d0SMatthias Ringwald 
658*d21278d0SMatthias Ringwald     memset(metadata_out, 0, sizeof(le_audio_metadata_t));
659*d21278d0SMatthias Ringwald     metadata_out->metadata_mask = leaudio_subevent_ascs_server_metadata_get_metadata_mask(packet);
660*d21278d0SMatthias Ringwald 
661*d21278d0SMatthias Ringwald     uint16_t metadata_type;
662*d21278d0SMatthias Ringwald     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
663*d21278d0SMatthias Ringwald         if ((metadata_out->metadata_mask & (1 << metadata_type) ) != 0 ){
664*d21278d0SMatthias Ringwald 
665*d21278d0SMatthias Ringwald             switch ((le_audio_metadata_type_t)metadata_type){
666*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
667*d21278d0SMatthias Ringwald                     metadata_out->preferred_audio_contexts_mask = leaudio_subevent_ascs_server_enable_get_preferred_audio_contexts_mask(packet);
668*d21278d0SMatthias Ringwald                     break;
669*d21278d0SMatthias Ringwald 
670*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
671*d21278d0SMatthias Ringwald                     metadata_out->streaming_audio_contexts_mask = leaudio_subevent_ascs_server_enable_get_streaming_audio_contexts_mask(packet);
672*d21278d0SMatthias Ringwald                     break;
673*d21278d0SMatthias Ringwald 
674*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
675*d21278d0SMatthias Ringwald                     metadata_out->program_info_length = leaudio_subevent_ascs_server_enable_get_program_info_length(packet);
676*d21278d0SMatthias Ringwald                     memcpy(metadata_out->program_info, leaudio_subevent_ascs_server_enable_get_program_info(packet), metadata_out->program_info_length);
677*d21278d0SMatthias Ringwald                     break;
678*d21278d0SMatthias Ringwald 
679*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_LANGUAGE:
680*d21278d0SMatthias Ringwald                     metadata_out->language_code = leaudio_subevent_ascs_server_enable_get_language_code(packet);
681*d21278d0SMatthias Ringwald                     break;
682*d21278d0SMatthias Ringwald 
683*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_CCID_LIST:
684*d21278d0SMatthias Ringwald                     metadata_out->ccids_num = leaudio_subevent_ascs_server_enable_get_ccids_num(packet);
685*d21278d0SMatthias Ringwald                     memcpy(metadata_out->ccids, leaudio_subevent_ascs_server_enable_get_ccids(packet), metadata_out->ccids_num);
686*d21278d0SMatthias Ringwald                     break;
687*d21278d0SMatthias Ringwald 
688*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
689*d21278d0SMatthias Ringwald                     metadata_out->parental_rating = (le_audio_parental_rating_t)leaudio_subevent_ascs_server_enable_get_parental_rating(packet);
690*d21278d0SMatthias Ringwald                     break;
691*d21278d0SMatthias Ringwald 
692*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
693*d21278d0SMatthias Ringwald                     metadata_out->program_info_uri_length = leaudio_subevent_ascs_server_enable_get_program_info_uri_length(packet);
694*d21278d0SMatthias Ringwald                     memcpy(metadata_out->program_info_uri, leaudio_subevent_ascs_server_enable_get_program_info_uri(packet), metadata_out->program_info_uri_length);
695*d21278d0SMatthias Ringwald                     break;
696*d21278d0SMatthias Ringwald 
697*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
698*d21278d0SMatthias Ringwald                     metadata_out->extended_metadata_type = leaudio_subevent_ascs_server_enable_get_extended_metadata_type(packet);
699*d21278d0SMatthias Ringwald                     metadata_out->extended_metadata_length = leaudio_subevent_ascs_server_enable_get_extended_metadata_value_length(packet);
700*d21278d0SMatthias Ringwald                     memcpy(metadata_out->extended_metadata, leaudio_subevent_ascs_server_enable_get_extended_metadata_value(packet), metadata_out->extended_metadata_length);
701*d21278d0SMatthias Ringwald                     break;
702*d21278d0SMatthias Ringwald 
703*d21278d0SMatthias Ringwald                 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
704*d21278d0SMatthias Ringwald                     metadata_out->vendor_specific_company_id = leaudio_subevent_ascs_server_enable_get_vendor_specific_metadata_type(packet);
705*d21278d0SMatthias Ringwald                     metadata_out->vendor_specific_metadata_length = leaudio_subevent_ascs_server_enable_get_vendor_specific_metadata_value_length(packet);
706*d21278d0SMatthias Ringwald                     memcpy(metadata_out->vendor_specific_metadata, leaudio_subevent_ascs_server_enable_get_vendor_specific_metadata_value(packet), metadata_out->vendor_specific_metadata_length);
707*d21278d0SMatthias Ringwald                     break;
708*d21278d0SMatthias Ringwald 
709*d21278d0SMatthias Ringwald                 default:
710*d21278d0SMatthias Ringwald                     btstack_assert(false);
711*d21278d0SMatthias Ringwald                     break;
712*d21278d0SMatthias Ringwald             }
713*d21278d0SMatthias Ringwald         }
714*d21278d0SMatthias Ringwald     }
715*d21278d0SMatthias Ringwald }
7169ef2c435SMilanka Ringwald 
717