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_unicast_sink.c" 39 40 /* 41 * LE Audio Unicast Sink 42 * Until GATT Services are available, we encode LC3 config in advertising 43 */ 44 45 46 #include "btstack_config.h" 47 48 #include <stdint.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #include "ad_parser.h" 54 #include "bluetooth_data_types.h" 55 #include "bluetooth_company_id.h" 56 #include "btstack_debug.h" 57 #include "btstack_event.h" 58 #include "btstack_stdin.h" 59 #include "gap.h" 60 #include "hci.h" 61 #include "le_audio_demo_util_sink.h" 62 #include "le_audio_demo_util_source.h" 63 64 // max config 65 #define MAX_CHANNELS 2 66 #define MAX_SAMPLES_PER_FRAME 480 67 68 static void show_usage(void); 69 70 static enum { 71 APP_W4_WORKING, 72 APP_W4_SOURCE_ADV, 73 APP_W4_CIG_COMPLETE, 74 APP_W4_CIS_CREATED, 75 APP_STREAMING, 76 APP_IDLE, 77 } app_state = APP_W4_WORKING; 78 79 // 80 static btstack_packet_callback_registration_t hci_event_callback_registration; 81 82 // remote info 83 static char remote_name[20]; 84 static bd_addr_t remote_addr; 85 static bd_addr_type_t remote_type; 86 static hci_con_handle_t remote_handle; 87 88 static le_audio_cig_t cig; 89 static le_audio_cig_params_t cig_params; 90 91 // iso info 92 static bool framed_pdus; 93 static uint16_t frame_duration_us; 94 95 static uint8_t num_cis; 96 static hci_con_handle_t cis_con_handles[MAX_CHANNELS]; 97 static bool cis_established[MAX_CHANNELS]; 98 99 // lc3 codec config 100 static uint16_t sampling_frequency_hz; 101 static btstack_lc3_frame_duration_t frame_duration; 102 static uint16_t number_samples_per_frame; 103 static uint16_t octets_per_frame; 104 static uint8_t num_channels; 105 106 // microphone 107 static bool microphone_enable; 108 109 static void start_scanning() { 110 app_state = APP_W4_SOURCE_ADV; 111 gap_set_scan_params(1, 0x30, 0x30, 0); 112 gap_start_scan(); 113 printf("Start scan..\n"); 114 } 115 116 static void create_cig() { 117 if (sampling_frequency_hz == 44100){ 118 framed_pdus = 1; 119 // same config as for 48k -> frame is longer by 48/44.1 120 frame_duration_us = frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US ? 8163 : 10884; 121 } else { 122 framed_pdus = 0; 123 frame_duration_us = frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US ? 7500 : 10000; 124 } 125 126 printf("Send: LE Set CIG Parameters\n"); 127 128 num_cis = 1; 129 cig_params.cig_id = 0; 130 cig_params.num_cis = 1; 131 cig_params.sdu_interval_c_to_p = frame_duration_us; 132 cig_params.sdu_interval_p_to_c = frame_duration_us; 133 cig_params.worst_case_sca = 0; // 251 ppm to 500 ppm 134 cig_params.packing = 0; // sequential 135 cig_params.framing = framed_pdus; 136 cig_params.max_transport_latency_c_to_p = 40; 137 cig_params.max_transport_latency_p_to_c = 40; 138 uint8_t i; 139 uint16_t max_sdu_c_to_p = microphone_enable ? octets_per_frame : 0; 140 for (i=0; i < num_cis; i++){ 141 cig_params.cis_params[i].cis_id = i; 142 cig_params.cis_params[i].max_sdu_c_to_p = max_sdu_c_to_p; 143 cig_params.cis_params[i].max_sdu_p_to_c = num_channels * octets_per_frame; 144 cig_params.cis_params[i].phy_c_to_p = 2; // 2M 145 cig_params.cis_params[i].phy_p_to_c = 2; // 2M 146 cig_params.cis_params[i].rtn_c_to_p = 2; 147 cig_params.cis_params[i].rtn_p_to_c = 2; 148 } 149 150 app_state = APP_W4_CIG_COMPLETE; 151 gap_cig_create(&cig, &cig_params); 152 } 153 154 static void enter_streaming(void){ 155 // init source 156 if (microphone_enable){ 157 le_audio_demo_util_source_configure(1, 1, sampling_frequency_hz, frame_duration, octets_per_frame); 158 le_audio_demo_util_source_generate_iso_frame(AUDIO_SOURCE_SINE); 159 } 160 // init sink 161 uint16_t iso_interval_1250us = frame_duration_us / 1250; 162 uint8_t flush_timeout = 1; 163 le_audio_demo_util_sink_configure_unicast(1, num_channels, sampling_frequency_hz, frame_duration, octets_per_frame, 164 iso_interval_1250us, flush_timeout); 165 printf("Configure: %u channels, sampling rate %u, samples per frame %u\n", num_channels, sampling_frequency_hz, number_samples_per_frame); 166 } 167 168 static void handle_advertisement(bd_addr_type_t address_type, bd_addr_t address, uint8_t adv_size, const uint8_t * adv_data){ 169 ad_context_t context; 170 bool found = false; 171 remote_name[0] = '\0'; 172 uint16_t uuid; 173 uint16_t company_id; 174 for (ad_iterator_init(&context, adv_size, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) { 175 uint8_t data_type = ad_iterator_get_data_type(&context); 176 uint8_t size = ad_iterator_get_data_len(&context); 177 const uint8_t *data = ad_iterator_get_data(&context); 178 switch (data_type){ 179 case BLUETOOTH_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: 180 company_id = little_endian_read_16(data, 0); 181 if (company_id == BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH){ 182 // subtype = 0 -> le audio unicast source 183 uint8_t subtype = data[2]; 184 if (subtype != 0) break; 185 // flags 186 uint8_t flags = data[3]; 187 // num channels 188 num_channels = data[4]; 189 if (num_channels > 2) break; 190 // sampling frequency 191 sampling_frequency_hz = 1000 * data[5]; 192 // frame duration 193 frame_duration = data[6] == 0 ? BTSTACK_LC3_FRAME_DURATION_7500US : BTSTACK_LC3_FRAME_DURATION_10000US; 194 // octets per frame 195 octets_per_frame = data[7]; 196 // done 197 found = true; 198 } 199 break; 200 case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME: 201 case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME: 202 size = btstack_min(sizeof(remote_name) - 1, size); 203 memcpy(remote_name, data, size); 204 remote_name[size] = 0; 205 break; 206 default: 207 break; 208 } 209 } 210 if (!found) return; 211 212 memcpy(remote_addr, address, 6); 213 remote_type = address_type; 214 printf("Remote Unicast source found, addr %s, name: '%s'\n", bd_addr_to_str(remote_addr), remote_name); 215 216 // stop scanning 217 app_state = APP_W4_CIS_CREATED; 218 gap_stop_scan(); 219 gap_connect(remote_addr, remote_type); 220 } 221 222 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 223 UNUSED(channel); 224 bd_addr_t event_addr; 225 bd_addr_type_t event_addr_type; 226 hci_con_handle_t cis_handle; 227 unsigned int i; 228 uint8_t status; 229 if (packet_type != HCI_EVENT_PACKET) return; 230 switch (packet[0]) { 231 case BTSTACK_EVENT_STATE: 232 switch(btstack_event_state_get_state(packet)) { 233 case HCI_STATE_WORKING: 234 #ifdef ENABLE_DEMO_MODE 235 if (app_state != APP_W4_WORKING) break; 236 start_scanning(); 237 #else 238 show_usage(); 239 #endif 240 break; 241 case HCI_STATE_OFF: 242 printf("Goodbye\n"); 243 exit(0); 244 break; 245 default: 246 break; 247 } 248 break; 249 case HCI_EVENT_DISCONNECTION_COMPLETE: 250 if (hci_event_disconnection_complete_get_connection_handle(packet) == remote_handle){ 251 printf("Disconnected, back to scanning\n"); 252 le_audio_demo_util_sink_close(); 253 start_scanning(); 254 } 255 break; 256 case GAP_EVENT_ADVERTISING_REPORT: 257 if (app_state == APP_W4_SOURCE_ADV) { 258 gap_event_advertising_report_get_address(packet, event_addr); 259 event_addr_type = gap_event_advertising_report_get_address_type(packet); 260 uint8_t adv_size = gap_event_advertising_report_get_data_length(packet); 261 const uint8_t * adv_data = gap_event_advertising_report_get_data(packet); 262 handle_advertisement(event_addr_type, event_addr, adv_size, adv_data); 263 } 264 break; 265 case GAP_EVENT_EXTENDED_ADVERTISING_REPORT: 266 if (app_state == APP_W4_SOURCE_ADV) { 267 gap_event_extended_advertising_report_get_address(packet, event_addr); 268 event_addr_type = gap_event_extended_advertising_report_get_address_type(packet) & 1; 269 uint8_t adv_size = gap_event_extended_advertising_report_get_data_length(packet); 270 const uint8_t * adv_data = gap_event_extended_advertising_report_get_data(packet); 271 handle_advertisement(event_addr_type, event_addr, adv_size, adv_data); 272 } 273 break; 274 case HCI_EVENT_META_GAP: 275 switch (hci_event_gap_meta_get_subevent_code(packet)){ 276 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: 277 gap_subevent_le_connection_complete_get_peer_address(packet, event_addr); 278 remote_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); 279 printf("Connected, remote %s, handle %04x\n", bd_addr_to_str(event_addr), remote_handle); 280 create_cig(); 281 break; 282 case GAP_SUBEVENT_CIG_CREATED: 283 if (app_state == APP_W4_CIG_COMPLETE){ 284 printf("CIS Connection Handles: "); 285 for (i=0; i < num_cis; i++){ 286 cis_con_handles[i] = gap_subevent_cig_created_get_cis_con_handles(packet, i); 287 printf("0x%04x ", cis_con_handles[i]); 288 } 289 printf("\n"); 290 291 printf("Create CIS\n"); 292 hci_con_handle_t acl_connection_handles[MAX_CHANNELS]; 293 for (i=0; i < num_cis; i++){ 294 acl_connection_handles[i] = remote_handle; 295 } 296 gap_cis_create(cig_params.cig_id, cis_con_handles, acl_connection_handles); 297 app_state = APP_W4_CIS_CREATED; 298 } 299 break; 300 case GAP_SUBEVENT_CIS_CREATED: 301 status = gap_subevent_big_created_get_status(packet); 302 cis_handle = gap_subevent_cis_created_get_cis_con_handle(packet); 303 if (status == ERROR_CODE_SUCCESS){ 304 // only look for cis handle 305 for (i=0; i < num_cis; i++){ 306 if (cis_handle == cis_con_handles[i]){ 307 cis_established[i] = true; 308 } 309 } 310 // check for complete 311 bool complete = true; 312 for (i=0; i < num_cis; i++) { 313 complete &= cis_established[i]; 314 } 315 if (complete) { 316 printf("All CIS Established\n"); 317 app_state = APP_STREAMING; 318 enter_streaming(); 319 // start sending 320 if (microphone_enable){ 321 hci_request_cis_can_send_now_events(cis_con_handles[0]); 322 } 323 } 324 } else { 325 printf("CIS Create failed with status 0x%02x for con handle 0x%04x\n", status, cis_handle); 326 } 327 break; 328 default: 329 break; 330 } 331 break; 332 case HCI_EVENT_CIS_CAN_SEND_NOW: 333 le_audio_demo_util_source_send(0, cis_con_handles[0]); 334 le_audio_demo_util_source_generate_iso_frame(AUDIO_SOURCE_SINE); 335 hci_request_cis_can_send_now_events(cis_con_handles[0]); 336 break; 337 default: 338 break; 339 } 340 } 341 342 static void iso_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 343 le_audio_demo_util_sink_receive(0, packet, size); 344 } 345 346 static void show_usage(void){ 347 printf("\n--- LE Audio Unicast Sink Test Console ---\n"); 348 printf("s - start scanning\n"); 349 #ifdef HAVE_LC3PLUS 350 printf("q - use LC3plus decoder if 10 ms ISO interval is used\n"); 351 #endif 352 printf("m - enable virtual microphone\n"); 353 printf("---\n"); 354 } 355 356 static void stdin_process(char c){ 357 switch (c){ 358 case 's': 359 if (app_state != APP_W4_WORKING) break; 360 start_scanning(); 361 break; 362 #ifdef HAVE_LC3PLUS 363 case 'q': 364 printf("Use LC3 Plust for 10 ms frames\n"); 365 le_audio_demo_util_sink_enable_lc3plus(true); 366 break; 367 #endif 368 case 'm': 369 printf("Enable virtual microphone\n"); 370 microphone_enable = true; 371 break; 372 case '\n': 373 case '\r': 374 break; 375 default: 376 show_usage(); 377 break; 378 379 } 380 } 381 382 int btstack_main(int argc, const char * argv[]); 383 int btstack_main(int argc, const char * argv[]){ 384 (void) argv; 385 (void) argc; 386 387 // register for HCI events 388 hci_event_callback_registration.callback = &packet_handler; 389 hci_add_event_handler(&hci_event_callback_registration); 390 391 // register for ISO Packet 392 hci_register_iso_packet_handler(&iso_packet_handler); 393 394 // setup audio processing 395 le_audio_demo_util_sink_init("le_audio_unicast_sink.wav"); 396 le_audio_demo_util_source_init(); 397 398 // turn on! 399 hci_power_control(HCI_POWER_ON); 400 401 btstack_stdin_setup(stdin_process); 402 return 0; 403 } 404