xref: /btstack/src/le-audio/le_audio_util.c (revision ce6f85e79d1d141c1b45dfa16b2671762457cbb4)
1 /*
2  * Copyright (C) 2022 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "le_audio_util.c"
39 
40 #include "btstack_util.h"
41 #include "btstack_debug.h"
42 #include "le-audio/le_audio_util.h"
43 
44 
45 static const le_audio_codec_configuration_t codec_specific_config_settings[] = {
46     {"8_1",  LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ,  LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   26},
47     {"8_2",  LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ,  LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  30},
48     {"16_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   30},
49     {"16_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  40},
50     {"24_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   45},
51     {"24_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  60},
52     {"32_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   60},
53     {"32_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US,  80},
54     {"441_1",LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   97},
55     {"441_2",LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 130},
56     {"48_1", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   75},
57     {"48_2", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 100},
58     {"48_3", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,   90},
59     {"48_4", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 120},
60     {"48_5", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US,  117},
61     {"48_6", LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ, LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US, 155},
62 };
63 
64 static const le_audio_qos_configuration_t qos_config_settings[] = {
65     {"8_1_1",    7500, 0,  26, 2, 8},
66     {"8_2_1",   10000, 0,  30, 2, 10},
67     {"16_1_1",   7500, 0,  30, 2, 8},
68     {"16_2_1",  10000, 0,  40, 2, 10},
69     {"24_1_1",   7500, 0,  45, 2, 8},
70     {"24_2_1",  10000, 0,  60, 2, 10},
71     {"32_1_1",   7500, 0,  60, 2, 8},
72     {"32_2_1",  10000, 0,  80, 2, 10},
73     {"441_1_1",  8163, 1,  97, 5, 24},
74     {"441_2_1", 10884, 1, 130, 5, 31},
75     {"48_1_1",   7500, 0,  75, 5, 15},
76     {"48_2_1",  10000, 0, 100, 5, 20},
77     {"48_3_1",   7500, 0,  90, 5, 15},
78     {"48_4_1",  10000, 0, 120, 5, 20},
79     {"48_5_1",   7500, 0, 117, 5, 15},
80     {"48_6_1",  10000, 0, 115, 5, 20},
81 
82     {"8_1_2",    7500, 0,  26, 13, 75},
83     {"8_2_2",   10000, 0,  30, 13, 95},
84     {"16_1_2",   7500, 0,  30, 13, 75},
85     {"16_2_2",  10000, 0,  40, 13, 95},
86     {"24_1_2",   7500, 0,  45, 13, 75},
87     {"24_2_2",  10000, 0,  60, 13, 95},
88     {"32_1_2",   7500, 0,  60, 13, 75},
89     {"32_2_2",  10000, 0,  80, 13, 95},
90     {"441_1_2",  8163, 1,  97, 13, 80},
91     {"441_2_2", 10884, 1, 130, 13, 85},
92     {"48_1_2",   7500, 0,  75, 13, 75},
93     {"48_2_2",  10000, 0, 100, 13, 95},
94     {"48_3_2",   7500, 0,  90, 13, 75},
95     {"48_4_2",  10000, 0, 120, 13, 100},
96     {"48_5_2",   7500, 0, 117, 13, 75},
97     {"48_6_2",  10000, 0, 115, 13, 100}
98 };
99 
100 static uint8_t codec_offset(le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
101     le_audio_codec_frame_duration_index_t frame_duration_index,
102     le_audio_quality_t audio_quality){
103 
104     btstack_assert(audio_quality <= LE_AUDIO_QUALITY_HIGH);
105 
106     if (sampling_frequency_index == LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ){
107         return 10 + (audio_quality - LE_AUDIO_QUALITY_LOW) * 2 + (frame_duration_index - LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US);
108     }
109     return (sampling_frequency_index - LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ) * 2 + (frame_duration_index - LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US);
110 }
111 
112 const le_audio_codec_configuration_t * le_audio_util_get_codec_setting(
113     le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
114     le_audio_codec_frame_duration_index_t frame_duration_index,
115     le_audio_quality_t audio_quality){
116 
117     return &codec_specific_config_settings[codec_offset(sampling_frequency_index, frame_duration_index, audio_quality)];
118 }
119 
120 const le_audio_qos_configuration_t * le_audio_util_get_qos_setting(
121     le_audio_codec_sampling_frequency_index_t sampling_frequency_index,
122     le_audio_codec_frame_duration_index_t frame_duration_index,
123     le_audio_quality_t audio_quality, uint8_t num_channels){
124 
125     btstack_assert((num_channels >= 1) && (num_channels <= 2));
126 
127     return &qos_config_settings[(num_channels - 1) * 16 + codec_offset(sampling_frequency_index, frame_duration_index, audio_quality)];
128 }
129 
130 
131 // help with buffer == NULL
132 uint16_t le_audio_util_virtual_memcpy_helper(
133     const uint8_t * field_data, uint16_t field_len, uint16_t field_offset,
134     uint8_t * buffer, uint16_t buffer_size, uint16_t buffer_offset){
135 
136     // only calc total size
137     if (buffer == NULL) {
138         return field_len;
139     }
140     return btstack_virtual_memcpy(field_data, field_len, field_offset, buffer, buffer_size, buffer_offset);
141 }
142 
143 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){
144     uint16_t metadata_type;
145     uint8_t  field_data[7];
146     uint16_t stored_bytes = 0;
147 
148     uint16_t metadata_length_pos = *records_offset;
149     field_data[0] = 0;
150     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *records_offset, buffer, buffer_size,
151                                                         buffer_offset);
152     *records_offset += 1;
153 
154     if (metadata_length == 0){
155         return stored_bytes;
156     }
157 
158     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
159         if ((metadata->metadata_mask & (1 << metadata_type) ) != 0 ){
160             // reserve field_data[0] for num butes to store
161             field_data[0] = 1;
162             field_data[1] = metadata_type;
163 
164             switch ((le_audio_metadata_type_t)metadata_type){
165                 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
166                     field_data[0] += 2;
167                     little_endian_store_16(field_data, 2, metadata->preferred_audio_contexts_mask);
168                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
169                                                                         buffer, buffer_size, buffer_offset);
170                     *records_offset += field_data[0] + 1;
171                     break;
172                 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
173                     field_data[0] += 2;
174                     little_endian_store_16(field_data, 2, metadata->streaming_audio_contexts_mask);
175                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
176                                                                         buffer, buffer_size, buffer_offset);
177                     *records_offset += field_data[0] + 1;
178                     break;
179                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
180                     field_data[0] += metadata->program_info_length;
181                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
182                                                                         buffer_size, buffer_offset);
183                     *records_offset += 2;
184                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info,
185                                                                         metadata->program_info_length, *records_offset,
186                                                                         buffer, buffer_size, buffer_offset);
187                     *records_offset += metadata->program_info_length;
188                     break;
189                 case LE_AUDIO_METADATA_TYPE_LANGUAGE:
190                     field_data[0] += 3;
191                     little_endian_store_24(field_data, 2, metadata->language_code);
192                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
193                                                                         buffer, buffer_size, buffer_offset);
194                     *records_offset += field_data[0] + 1;
195                     break;
196                 case LE_AUDIO_METADATA_TYPE_CCID_LIST:
197                     field_data[0] += metadata->ccids_num;
198                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
199                                                                         buffer_size, buffer_offset);
200                     *records_offset += 2;
201                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->ccids, metadata->ccids_num,
202                                                                         *records_offset, buffer, buffer_size,
203                                                                         buffer_offset);
204                     *records_offset += metadata->ccids_num;
205                     break;
206                 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
207                     field_data[0] += 1;
208                     field_data[2] = (uint8_t) metadata->parental_rating;
209                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset,
210                                                                         buffer, buffer_size, buffer_offset);
211                     *records_offset += field_data[0] + 1;
212                     break;
213                 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
214                     field_data[0] += metadata->program_info_uri_length;
215                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer,
216                                                                         buffer_size, buffer_offset);
217                     *records_offset += 2;
218                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info_uri,
219                                                                         metadata->program_info_uri_length,
220                                                                         *records_offset, buffer, buffer_size,
221                                                                         buffer_offset);
222                     *records_offset += metadata->program_info_uri_length;
223                     break;
224                 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
225                     field_data[0] += 2 + metadata->extended_metadata_length;
226                     field_data[1] = LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA;
227                     little_endian_store_16(field_data, 2, metadata->extended_metadata_type);
228                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer,
229                                                                         buffer_size, buffer_offset);
230                     *records_offset += 4;
231                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->extended_metadata,
232                                                                         metadata->extended_metadata_length,
233                                                                         *records_offset, buffer, buffer_size,
234                                                                         buffer_offset);
235                     *records_offset += metadata->extended_metadata_length;
236                     break;
237                 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
238                     field_data[0] += 2 + metadata->vendor_specific_metadata_length;
239                     field_data[1] = LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA;
240                     little_endian_store_16(field_data, 2, metadata->vendor_specific_company_id);
241                     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer,
242                                                                         buffer_size, buffer_offset);
243                     *records_offset += 4;
244                     stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->vendor_specific_metadata,
245                                                                         metadata->vendor_specific_metadata_length,
246                                                                         *records_offset, buffer, buffer_size,
247                                                                         buffer_offset);
248                     *records_offset += metadata->vendor_specific_metadata_length;
249                     break;
250                 default:
251                     btstack_assert(false);
252                     break;
253             }
254         }
255     }
256 
257     field_data[0] =  *records_offset - metadata_length_pos - 1;
258     le_audio_util_virtual_memcpy_helper(field_data, 1, metadata_length_pos, buffer, buffer_size, buffer_offset);
259     return stored_bytes;
260 }
261 
262 
263 uint16_t le_audio_util_metadata_parse(uint8_t * buffer, uint8_t buffer_size, le_audio_metadata_t * metadata){
264     // parse config to get sampling frequency and frame duration
265     uint8_t offset = 0;
266     uint8_t metadata_config_lenght = buffer[offset++];
267     if (buffer_size < metadata_config_lenght){
268         return 0;
269     }
270 
271     metadata->metadata_mask = 0;
272 
273     while ((offset + 1) < metadata_config_lenght){
274         uint8_t ltv_len = buffer[offset++];
275 
276         le_audio_metadata_type_t ltv_type = (le_audio_metadata_type_t)buffer[offset];
277         le_audio_parental_rating_t parental_rating;
278 
279         switch (ltv_type){
280             case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
281                 metadata->preferred_audio_contexts_mask = little_endian_read_16(buffer, offset+1);
282                 metadata->metadata_mask |= (1 << ltv_type);
283                 break;
284             case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
285                 metadata->streaming_audio_contexts_mask = little_endian_read_16(buffer, offset+1);
286                 metadata->metadata_mask |= (1 << ltv_type);
287                 break;
288             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
289                 metadata->program_info_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_MAX_LENGTH);
290                 memcpy(metadata->program_info, &buffer[offset+1], metadata->program_info_length);
291                 metadata->metadata_mask |= (1 << ltv_type);
292                 break;
293             case LE_AUDIO_METADATA_TYPE_LANGUAGE:
294                 metadata->language_code = little_endian_read_24(buffer, offset+1);
295                 metadata->metadata_mask |= (1 << ltv_type);
296                 break;
297             case LE_AUDIO_METADATA_TYPE_CCID_LIST:
298                 metadata->ccids_num = btstack_min(ltv_len, LE_CCIDS_MAX_NUM);
299                 memcpy(metadata->ccids, &buffer[offset+1], metadata->ccids_num);
300                 metadata->metadata_mask |= (1 << ltv_type);
301                 break;
302             case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
303                 parental_rating = (le_audio_parental_rating_t)buffer[offset+1];
304                 metadata->parental_rating = parental_rating;
305                 metadata->metadata_mask |= (1 << ltv_type);
306                 break;
307             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
308                 metadata->program_info_uri_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_URI_MAX_LENGTH);
309                 memcpy(metadata->program_info_uri, &buffer[offset+1], metadata->program_info_uri_length);
310                 metadata->metadata_mask |= (1 << ltv_type);
311                 break;
312             case LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA:
313                 if (ltv_len < 2){
314                     break;
315                 }
316                 metadata->extended_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_EXTENDED_METADATA_MAX_LENGHT);
317                 metadata->extended_metadata_type = little_endian_read_16(buffer, offset+1);
318                 memcpy(metadata->extended_metadata, &buffer[offset+3], metadata->extended_metadata_length);
319                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION);
320                 break;
321             case LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA:
322                 if (ltv_len < 2){
323                     break;
324                 }
325                 metadata->vendor_specific_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_VENDOR_SPECIFIC_METADATA_MAX_LENGTH);
326                 metadata->vendor_specific_company_id = little_endian_read_16(buffer, offset+1);
327                 memcpy(metadata->vendor_specific_metadata, &buffer[offset+3], metadata->vendor_specific_metadata_length);
328                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION);
329                 break;
330             default:
331                 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_RFU);
332                 break;
333         }
334         offset += ltv_len;
335     }
336     return offset;
337 }
338 
339 uint16_t le_audio_util_metadata_serialize(le_audio_metadata_t * metadata, uint8_t * event, uint16_t event_size){
340     uint8_t pos = 0;
341 
342     event[pos++] = (uint8_t)metadata->metadata_mask;
343     little_endian_store_16(event, pos, metadata->preferred_audio_contexts_mask);
344     pos += 2;
345     little_endian_store_16(event, pos, metadata->streaming_audio_contexts_mask);
346     pos += 2;
347 
348     event[pos++] = metadata->program_info_length;
349     memcpy(&event[pos], &metadata->program_info[0], metadata->program_info_length);
350     pos += metadata->program_info_length;
351 
352     little_endian_store_24(event, pos, metadata->language_code);
353     pos += 3;
354 
355     event[pos++] = metadata->ccids_num;
356     memcpy(&event[pos], &metadata->ccids[0], metadata->ccids_num);
357     pos += metadata->ccids_num;
358 
359     event[pos++] = (uint8_t)metadata->parental_rating;
360 
361     event[pos++] = metadata->program_info_uri_length;
362     memcpy(&event[pos], &metadata->program_info_uri[0], metadata->program_info_uri_length);
363     pos += metadata->program_info_uri_length;
364 
365     little_endian_store_16(event, pos, metadata->extended_metadata_type);
366     pos += 2;
367 
368     event[pos++] = metadata->extended_metadata_length;
369     memcpy(&event[pos], &metadata->extended_metadata[0], metadata->extended_metadata_length);
370     pos += metadata->extended_metadata_length;
371 
372     little_endian_store_16(event, pos, metadata->vendor_specific_company_id);
373     pos += 2;
374 
375     event[pos++] = metadata->vendor_specific_metadata_length;
376     memcpy(&event[pos], &metadata->vendor_specific_metadata[0], metadata->vendor_specific_metadata_length);
377     pos += metadata->vendor_specific_metadata_length;
378 
379     return pos;
380 }
381 
382 static uint16_t le_audio_util_get_value_size_for_metadata_type(le_audio_metadata_t * metadata, le_audio_metadata_type_t metadata_type){
383     switch (metadata_type){
384         case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
385         case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
386             return 2;
387 
388         case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
389             return metadata->program_info_length;
390 
391         case LE_AUDIO_METADATA_TYPE_LANGUAGE:
392             return 3;
393 
394         case LE_AUDIO_METADATA_TYPE_CCID_LIST:
395             return metadata->ccids_num;
396 
397         case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
398             return 1;
399 
400         case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
401             return metadata->program_info_uri_length;
402 
403         case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
404             return 2 + metadata->extended_metadata_length;
405 
406         case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
407             return 2 + metadata->vendor_specific_metadata_length;
408         default:
409             break;
410     }
411     return 0;
412 }
413 
414 uint16_t le_audio_util_metadata_serialize_using_mask(le_audio_metadata_t * metadata, uint8_t * tlv_buffer, uint16_t tlv_buffer_size){
415     uint16_t metadata_type;
416     uint16_t pos = 0;
417 
418     uint16_t remaining_bytes = tlv_buffer_size;
419 
420     for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){
421         if ((metadata->metadata_mask & (1 << metadata_type)) == 0){
422             continue;
423         }
424 
425         uint8_t payload_length = le_audio_util_get_value_size_for_metadata_type(metadata, (le_audio_metadata_type_t)metadata_type);
426         // ensure that there is enough space in TLV to store length (1), type(1) and payload
427         if (remaining_bytes < (2 + payload_length)){
428             return pos;
429         }
430 
431         tlv_buffer[pos++] = 1 + payload_length; // add one extra byte to count size of type (1 byte)
432         tlv_buffer[pos++] = metadata_type;
433 
434         switch ((le_audio_metadata_type_t)metadata_type){
435             case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS:
436                 little_endian_store_16(tlv_buffer, pos, metadata->preferred_audio_contexts_mask);
437                 break;
438             case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS:
439                 little_endian_store_16(tlv_buffer, pos, metadata->streaming_audio_contexts_mask);
440                 break;
441             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO:
442                 memcpy(&tlv_buffer[pos], metadata->program_info, metadata->program_info_length);
443                 break;
444             case LE_AUDIO_METADATA_TYPE_LANGUAGE:
445                 little_endian_store_24(tlv_buffer, pos, metadata->language_code);
446                 break;
447             case LE_AUDIO_METADATA_TYPE_CCID_LIST:
448                 memcpy(&tlv_buffer[pos], metadata->ccids, metadata->ccids_num);
449                 break;
450             case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING:
451                 break;
452             case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI:
453                 memcpy(&tlv_buffer[pos], metadata->program_info_uri, metadata->program_info_uri_length);
454                 break;
455             case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION:
456                 little_endian_store_16(tlv_buffer, pos, metadata->extended_metadata_type);
457                 memcpy(&tlv_buffer[pos + 2], metadata->extended_metadata, metadata->extended_metadata_length);
458                 break;
459             case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION:
460                 little_endian_store_16(tlv_buffer, pos, metadata->vendor_specific_company_id);
461                 memcpy(&tlv_buffer[pos + 2], metadata->vendor_specific_metadata, metadata->vendor_specific_metadata_length);
462                 break;
463             default:
464                 break;
465         }
466         pos += payload_length;
467         remaining_bytes -= (payload_length + 2);
468     }
469     return pos;
470 }
471 
472 btstack_lc3_frame_duration_t le_audio_util_get_btstack_lc3_frame_duration(le_audio_codec_frame_duration_index_t frame_duration_index){
473     switch (frame_duration_index){
474         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US:
475             return BTSTACK_LC3_FRAME_DURATION_7500US;
476         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US:
477             return BTSTACK_LC3_FRAME_DURATION_10000US;
478         default:
479             btstack_assert(false);
480             break;
481     }
482     return 0;
483 }
484 
485 uint16_t le_audio_get_frame_duration_us(le_audio_codec_frame_duration_index_t frame_duration_index){
486     switch (frame_duration_index){
487         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US:
488             return 7500;
489         case LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US:
490             return 10000;
491         default:
492             return 0;
493     }
494 }
495 
496 le_audio_codec_frame_duration_index_t le_audio_get_frame_duration_index(uint16_t frame_duration_us){
497     switch (frame_duration_us){
498         case 0:
499             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_INVALID;
500         case 7500:
501             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_7500US;
502         case 10000:
503             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_10000US;
504         default:
505             return LE_AUDIO_CODEC_FRAME_DURATION_INDEX_RFU;
506     }
507 }
508 
509 uint32_t le_audio_get_sampling_frequency_hz(le_audio_codec_sampling_frequency_index_t sampling_frequency_index){
510     switch (sampling_frequency_index){
511         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ:
512                 return 8000;
513         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_11025_HZ:
514                 return 11025;
515         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ:
516                 return 16000;
517         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_22050_HZ:
518                 return 22050;
519         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ:
520                 return 24000;
521         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ:
522                 return 32000;
523         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ:
524                 return 44100;
525         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ:
526                 return 48000;
527         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_88200_HZ:
528                 return 88200;
529         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_96000_HZ:
530                 return 96000;
531         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_176400_HZ:
532                 return 176400;
533         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_192000_HZ:
534                 return 192000;
535         case LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_384000_HZ:
536                 return 384000;
537         default:
538             return 0;
539     }
540 }
541 
542 le_audio_codec_sampling_frequency_index_t le_audio_get_sampling_frequency_index(uint32_t sampling_frequency_hz){
543     switch (sampling_frequency_hz){
544         case 0:
545             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_INVALID;
546         case 8000:
547             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_8000_HZ;
548         case 11025:
549             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_11025_HZ;
550         case 16000:
551             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_16000_HZ;
552         case 22050:
553             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_22050_HZ;
554         case 24000:
555             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_24000_HZ;
556         case 32000:
557             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_32000_HZ;
558         case 44100:
559             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_44100_HZ;
560         case 48000:
561             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_48000_HZ;
562         case 88200:
563             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_88200_HZ;
564         case 96000:
565             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_96000_HZ;
566         case 176400:
567             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_176400_HZ;
568         case 192000:
569             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_192000_HZ;
570         case 384000:
571             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_384000_HZ;
572         default:
573             return LE_AUDIO_CODEC_SAMPLING_FREQUENCY_INDEX_RFU;
574     }
575 }
576 
577 
578