From a8500c8fabd3815e1482f2e0e820be5feb00f96e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 11:49:15 +0100 Subject: [PATCH 01/38] hfp_ag: less debug output --- src/classic/hfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classic/hfp.c b/src/classic/hfp.c index 9bb8c2308..b3786d923 100644 --- a/src/classic/hfp.c +++ b/src/classic/hfp.c @@ -539,7 +539,7 @@ void hfp_handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet hfp_connection_t * hfp_connection = NULL; uint8_t status; - log_info("AG packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size); + log_debug("HFP packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size); switch (hci_event_packet_get_type(packet)) { case HCI_EVENT_CONNECTION_REQUEST: From a11bf4163dc88898b2f802a514394bc106de4741 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 11:55:07 +0100 Subject: [PATCH 02/38] sco_util: fix compile for SCO_DEMO_MODE_55 --- example/sco_demo_util.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 31f45ecf2..2e902927b 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -387,7 +387,6 @@ void sco_demo_close(void){ printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr); } #endif -#endif #ifdef HAVE_PORTAUDIO if (pa_stream_started){ @@ -410,8 +409,6 @@ void sco_demo_close(void){ } } #endif - -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef SCO_WAV_FILENAME #if 0 @@ -617,7 +614,7 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){ } printf("\n"); #endif -#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE_00 +#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE == SCO_DEMO_MODE_00 int i; int contains_error = 0; for (i=3;i Date: Thu, 19 Jan 2017 12:12:25 +0100 Subject: [PATCH 03/38] libusb: use alternate setting 1 for now, add table for iso packet size for each alt setting --- platform/libusb/hci_transport_h2_libusb.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 2f7da3d28..b988f1ace 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -85,13 +85,23 @@ // 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding // 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding // --> support only a single SCO connection -#define ALT_SETTING (2) +#define ALT_SETTING (1) // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets // One complete SCO packet with 24 frames every 3 frames (== 3 ms) #define NUM_ISO_PACKETS (3) -// results in 9 bytes per frame -#define ISO_PACKET_SIZE (9) + +const uint16_t iso_packet_size_for_alt_setting[] = { + 0, + 9, + 17, + 25, + 33, + 49, + 63, +}; + +#define ISO_PACKET_SIZE (iso_packet_size_for_alt_setting[ALT_SETTING]) // 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel) // note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload From 1f8694cc3a397a7497b8bd4505d97fb09054221d Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 12:27:44 +0100 Subject: [PATCH 04/38] sco_util: use larger buffer for processing CVSD data --- example/sco_demo_util.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 2e902927b..f1d2a38cd 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -344,15 +344,17 @@ static void sco_demo_init_CVSD(void){ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ if (!num_samples_to_write) return; + int8_t audio_frame_out[255]; // + + if (size > sizeof(audio_frame_out)){ + printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n"); + return; + } const int num_samples = size - 3; const int samples_to_write = btstack_min(num_samples, num_samples_to_write); - int8_t audio_frame_out[24]; - - // memcpy(audio_frame_out, (int8_t*)(packet+3), 24); btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out); - // int8_t * audio_frame_out = (int8_t*)&packet[3]; wav_writer_write_int8(samples_to_write, audio_frame_out); num_samples_to_write -= samples_to_write; From 47184392b9bed54fe9832d42560ce8225cd5fbe4 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 14:09:28 +0100 Subject: [PATCH 05/38] libusb: remove debug msg for sco cancel on shutdown --- platform/libusb/hci_transport_h2_libusb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index b988f1ace..850fffc02 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -1045,10 +1045,8 @@ static int usb_close(void){ } for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){ if (sco_out_transfers_in_flight[c]) { - log_info("libusb_cancel_transfer sco_out_transfers[%d]", c); libusb_cancel_transfer(sco_out_transfers[c]); } else { - log_info("libusb_free_transfer sco_out_transfers[%d]", c); libusb_free_transfer(sco_out_transfers[c]); sco_out_transfers[c] = 0; } From a42798c331d083ec39ba2fd4c1f1d00e647d42ab Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 14:19:30 +0100 Subject: [PATCH 06/38] hci: configure Broadcom chipsets to send SCO data via HCI transport --- src/hci.c | 13 +++++++++++++ src/hci.h | 4 ++++ src/hci_cmd.c | 15 +++++++++++++++ src/hci_cmd.h | 4 ++++ 4 files changed, 36 insertions(+) diff --git a/src/hci.c b/src/hci.c index 8dd43ec5f..adc217c0e 100644 --- a/src/hci.c +++ b/src/hci.c @@ -1157,6 +1157,13 @@ static void hci_initializing_run(void){ hci_stack->substate = HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING; hci_send_cmd(&hci_write_default_erroneous_data_reporting, 1); break; + // only sent if ENABLE_SCO_OVER_HCI and manufacturer is Broadcom + case HCI_INIT_BCM_WRITE_SCO_PCM_INT: + hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT; + log_info("BCM: Route SCO data via HCI transport"); + hci_send_cmd(&hci_bcm_write_sco_pcm_int, 1, 0, 0, 0, 0); + break; + #endif #ifdef ENABLE_BLE // LE INIT @@ -1447,6 +1454,12 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){ // explicit fall through to reduce repetitions case HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING: + // skip bcm set sco pcm config on non-Broadcom chipsets + if (hci_stack->manufacturer == COMPANY_ID_BROADCOM_CORPORATION) break; + hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT; + // explicit fall through to reduce repetitions + + case HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT: if (!hci_le_supported()){ // SKIP LE init for Classic only configuration hci_init_done(); diff --git a/src/hci.h b/src/hci.h index 47f5d8eb8..9a603c076 100644 --- a/src/hci.h +++ b/src/hci.h @@ -550,6 +550,10 @@ typedef enum hci_init_state{ HCI_INIT_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING, HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING, + // SCO over HCI Broadcom + HCI_INIT_BCM_WRITE_SCO_PCM_INT, + HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT, + #ifdef ENABLE_BLE HCI_INIT_LE_READ_BUFFER_SIZE, HCI_INIT_W4_LE_READ_BUFFER_SIZE, diff --git a/src/hci_cmd.c b/src/hci_cmd.c index f85794e49..fb7db4914 100644 --- a/src/hci_cmd.c +++ b/src/hci_cmd.c @@ -1061,3 +1061,18 @@ OPCODE(OGF_LE_CONTROLLER, 0x26), "QQ" }; #endif + +// Broadcom / Cypress specific HCI commands + +/** + * @brief Configure SCO Routing (BCM) + * @param sco_routing is 0 for PCM, 1 for Transport, 2 for Codec and 3 for I2S + * @param pcm_interface_rate is 0 for 128KBps, 1 for 256 KBps, 2 for 512KBps, 3 for 1024KBps, and 4 for 2048Kbps + * @param frame_type is 0 for short and 1 for long + * @param sync_mode is 0 for slave and 1 for master + * @param clock_mode is 0 for slabe and 1 for master + */ +const hci_cmd_t hci_bcm_write_sco_pcm_int = { +OPCODE(0x3f, 0x1c), "11111" +// return: status +}; diff --git a/src/hci_cmd.h b/src/hci_cmd.h index a2ca927a2..8a0ef5381 100644 --- a/src/hci_cmd.h +++ b/src/hci_cmd.h @@ -187,6 +187,10 @@ extern const hci_cmd_t hci_le_set_scan_response_data; extern const hci_cmd_t hci_le_start_encryption; extern const hci_cmd_t hci_le_test_end; extern const hci_cmd_t hci_le_transmitter_test; + +// Broadcom / Cypress specific HCI commands +extern const hci_cmd_t hci_bcm_write_sco_pcm_int; + /** * construct HCI Command based on template * From d77060bd01875dc6b98deffb124ce460b1361d6b Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 14:35:11 +0100 Subject: [PATCH 07/38] bcm: list SCO over HCI as supported via USB --- chipset/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chipset/README.md b/chipset/README.md index a23c4a323..010f5ad96 100644 --- a/chipset/README.md +++ b/chipset/README.md @@ -33,8 +33,8 @@ CSR, which has been acquired by Qualcomm, provides all relevant information on t Chipset | Type | HCI Transport | BD_ADDR (1) | SCO over HCI (2) | LE DLE | Multiple LE Roles | BTstack folder | Comment -------------------- |-----------| ---------------|--------------|------------------|--------|----------------------|----------------|--------- -Broadcom UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | Maybe (3) | bcm | Max UART baudrate 3 mbps -Broadcom USB Dongles | Dual mode | USB | Yes | No (didn't work) | No | No | bcm | +Broadcom UART | Dual mode | H4, H5 | rarely | Maybe | No | Maybe (3) | bcm | Max UART baudrate 3 mbps +Broadcom USB Dongles | Dual mode | USB | Yes | Yes | No | No | bcm | CSR UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | No | csr | CSR USB Dongles | Dual mode | USB | Mostly | Yes | No | No | csr | Dialog DA14581 | LE | H4, SPI | ? | n.a. | No | No | | Waiting for dev kit From ee752bb8dbefd3155bffbb0d0f510e9d68b6e1c2 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 17:05:43 +0100 Subject: [PATCH 08/38] hci_transport: add set_sco_config and call when nr. of SCO connections changes --- platform/libusb/hci_transport_h2_libusb.c | 5 +++ platform/windows/hci_transport_h2_winusb.c | 1 + src/hci.c | 43 ++++++++++++++++++++++ src/hci.h | 1 + src/hci_transport.h | 5 +++ src/hci_transport_h4.c | 1 + src/hci_transport_h5.c | 1 + 7 files changed, 57 insertions(+) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 850fffc02..804e629af 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -1205,6 +1205,10 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ } } +static void usb_set_sco_config(uint16_t voice_setting, int num_connections){ + log_info("usb_set_sco_config: voice settings 0x04%x, num connections %u", voice_setting, num_connections); +} + static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ log_info("registering packet handler"); packet_handler = handler; @@ -1227,6 +1231,7 @@ const hci_transport_t * hci_transport_usb_instance(void) { hci_transport_usb->register_packet_handler = usb_register_packet_handler; hci_transport_usb->can_send_packet_now = usb_can_send_packet_now; hci_transport_usb->send_packet = usb_send_packet; + hci_transport_usb->set_sco_config = usb_set_sco_config; } return hci_transport_usb; } diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 5bd4897dd..f71d8a2dd 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -1292,6 +1292,7 @@ static const hci_transport_t hci_transport_usb = { /* int (*send_packet)(...); */ &usb_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ NULL, /* void (*reset_link)(void); */ NULL, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, }; const hci_transport_t * hci_transport_usb_instance(void) { diff --git a/src/hci.c b/src/hci.c index adc217c0e..efa277690 100644 --- a/src/hci.c +++ b/src/hci.c @@ -219,6 +219,20 @@ hci_connection_t * hci_connection_for_bd_addr_and_type(bd_addr_t addr, bd_addr_ #ifdef ENABLE_CLASSIC +#ifdef ENABLE_SCO_OVER_HCI +static int hci_number_sco_connections(void){ + int connections = 0; + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &hci_stack->connections); + while (btstack_linked_list_iterator_has_next(&it)){ + hci_connection_t * connection = (hci_connection_t *) btstack_linked_list_iterator_next(&it); + if (connection->address_type != BD_ADDR_TYPE_SCO) continue; + connections++; + } + return connections; +} +#endif + static void hci_connection_timeout_handler(btstack_timer_source_t *timer){ hci_connection_t * connection = (hci_connection_t *) btstack_run_loop_get_timer_context(timer); #ifdef HAVE_EMBEDDED_TICK @@ -749,6 +763,10 @@ static void acl_handler(uint8_t *packet, int size){ static void hci_shutdown_connection(hci_connection_t *conn){ log_info("Connection closed: handle 0x%x, %s", conn->con_handle, bd_addr_to_str(conn->address)); +#ifdef ENABLE_SCO_OVER_HCI + int addr_type = conn->address_type; +#endif + btstack_run_loop_remove_timer(&conn->timeout); btstack_linked_list_remove(&hci_stack->connections, (btstack_linked_item_t *) conn); @@ -756,6 +774,11 @@ static void hci_shutdown_connection(hci_connection_t *conn){ // now it's gone hci_emit_nr_connections_changed(); + + // update SCO + if (addr_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){ + hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections()); + } } #ifdef ENABLE_CLASSIC @@ -1737,6 +1760,13 @@ static void event_handler(uint8_t *packet, int size){ } conn->state = OPEN; conn->con_handle = little_endian_read_16(packet, 3); + +#ifdef ENABLE_SCO_OVER_HCI + // update SCO + if (conn->address_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){ + hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections()); + } +#endif break; case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE: @@ -3087,6 +3117,19 @@ int hci_send_cmd_packet(uint8_t *packet, int size){ connectionClearAuthenticationFlags(conn, SSP_PAIRING_ACTIVE); } } + +#ifdef ENABLE_SCO_OVER_HCI + // setup_synchronous_connection? Voice setting at offset 22 + if (IS_COMMAND(packet, hci_setup_synchronous_connection)){ + // TODO: compare to current setting if sco connection already active + hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 15); + } + // accept_synchronus_connection? Voice setting at offset 18 + if (IS_COMMAND(packet, hci_accept_synchronous_connection)){ + // TODO: compare to current setting if sco connection already active + hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 19); + } +#endif #endif #ifdef ENABLE_BLE diff --git a/src/hci.h b/src/hci.h index 9a603c076..107e36b71 100644 --- a/src/hci.h +++ b/src/hci.h @@ -698,6 +698,7 @@ typedef struct { uint8_t new_scan_enable_value; uint16_t sco_voice_setting; + uint16_t sco_voice_setting_active; uint8_t loopback_mode; diff --git a/src/hci_transport.h b/src/hci_transport.h index 60f6f93e0..2a8cb67aa 100644 --- a/src/hci_transport.h +++ b/src/hci_transport.h @@ -104,6 +104,11 @@ typedef struct { */ void (*reset_link)(void); + /** + * extension for USB transport implementations: config SCO connections + */ + void (*set_sco_config)(uint16_t voice_setting, int num_connections); + } hci_transport_t; typedef enum { diff --git a/src/hci_transport_h4.c b/src/hci_transport_h4.c index fbdb5949b..e1f2deb26 100644 --- a/src/hci_transport_h4.c +++ b/src/hci_transport_h4.c @@ -549,6 +549,7 @@ static const hci_transport_t hci_transport_h4 = { /* int (*send_packet)(...); */ &hci_transport_h4_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate, /* void (*reset_link)(void); */ NULL, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, }; // configure and return h4 singleton diff --git a/src/hci_transport_h5.c b/src/hci_transport_h5.c index 58f4b52ca..eff1cbf20 100644 --- a/src/hci_transport_h5.c +++ b/src/hci_transport_h5.c @@ -773,6 +773,7 @@ static const hci_transport_t hci_transport_h5 = { /* int (*send_packet)(...); */ &hci_transport_h5_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h5_set_baudrate, /* void (*reset_link)(void); */ &hci_transport_h5_reset_link, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, }; // configure and return h5 singleton From 56e558dbc235d11ada89910532cac79ace2247ef Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 17:44:52 +0100 Subject: [PATCH 09/38] libusb: start/stop isochronous transfers when SCO connections change --- platform/libusb/hci_transport_h2_libusb.c | 185 ++++++++++++++++------ 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 804e629af..90e476e55 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -172,6 +172,12 @@ static int sco_ring_write; // packet idx static int sco_out_transfers_active; static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT]; static int sco_out_transfers_in_flight[SCO_OUT_BUFFER_COUNT]; + +// pause/resume +static uint16_t sco_voice_setting; +static int sco_num_connections; +static int sco_shutdown; + #endif // outgoing buffer for HCI Command packets @@ -250,22 +256,8 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){ int c; // identify and free transfers as part of shutdown - if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) { - for (c=0;cregister_packet_handler = usb_register_packet_handler; hci_transport_usb->can_send_packet_now = usb_can_send_packet_now; hci_transport_usb->send_packet = usb_send_packet; +#ifdef ENABLE_SCO_OVER_HCI hci_transport_usb->set_sco_config = usb_set_sco_config; +#endif } return hci_transport_usb; } From 2117d3a5b1d4330d5aaf5e137387e3c371fd70c8 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 17:55:42 +0100 Subject: [PATCH 10/38] libusb: dynamically set alternate setting for interface #1 --- platform/libusb/hci_transport_h2_libusb.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 90e476e55..8f49b381b 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -788,6 +788,13 @@ static int usb_sco_start(void){ sco_state_machine_init(); sco_ring_init(); + log_info("Switching to setting %u on interface 1..", ALT_SETTING); + int r = libusb_set_interface_alt_setting(handle, 1, ALT_SETTING); + if (r < 0) { + log_error("Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r)); + return r; + } + // incoming int c; for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) { @@ -800,7 +807,7 @@ static int usb_sco_start(void){ libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr, hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0); libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_PACKET_SIZE); - int r = libusb_submit_transfer(sco_in_transfer[c]); + r = libusb_submit_transfer(sco_in_transfer[c]); if (r) { log_error("Error submitting isochronous in transfer %d", r); usb_close(); @@ -867,6 +874,14 @@ static void usb_sco_stop(void){ } sco_shutdown = 0; libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); + + log_info("Switching to setting %u on interface 1..", 0); + int r = libusb_set_interface_alt_setting(handle, 1, 0); + if (r < 0) { + log_error("Error setting alternative setting %u for interface 1: %s", 0, libusb_error_name(r)); + return; + } + printf("usb_sco_stop done\n"); } From c6f9dc916d1ed1275579187a9d0b2d77bd84e0e5 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 19 Jan 2017 18:10:47 +0100 Subject: [PATCH 11/38] libusb: pick alt setting based on voice settings/num connections --- platform/libusb/hci_transport_h2_libusb.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 8f49b381b..33ffb7ed0 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -87,6 +87,10 @@ // --> support only a single SCO connection #define ALT_SETTING (1) +// alt setting for 1-3 connections and 8/16 bit +static const int alt_setting_8_bit[] = {1,2,3}; +static const int alt_setting_16_bit[] = {2,4,5}; + // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets // One complete SCO packet with 24 frames every 3 frames (== 3 ms) #define NUM_ISO_PACKETS (3) @@ -788,10 +792,19 @@ static int usb_sco_start(void){ sco_state_machine_init(); sco_ring_init(); - log_info("Switching to setting %u on interface 1..", ALT_SETTING); - int r = libusb_set_interface_alt_setting(handle, 1, ALT_SETTING); + int alt_setting; + if (sco_voice_setting & 0x0020){ + // 16-bit PCM + alt_setting = alt_setting_16_bit[sco_num_connections-1]; + } else { + // 8-bit PCM or mSBC + alt_setting = alt_setting_8_bit[sco_num_connections-1]; + } + + log_info("Switching to setting %u on interface 1..", alt_setting); + int r = libusb_set_interface_alt_setting(handle, 1, alt_setting); if (r < 0) { - log_error("Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r)); + log_error("Error setting alternative setting %u for interface 1: %s\n", alt_setting, libusb_error_name(r)); return r; } @@ -1290,17 +1303,17 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ #ifdef ENABLE_SCO_OVER_HCI static void usb_set_sco_config(uint16_t voice_setting, int num_connections){ - log_info("usb_set_sco_config: voice settings 0x04%x, num connections %u", voice_setting, num_connections); + log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections); if (num_connections != sco_num_connections){ sco_voice_setting = voice_setting; if (sco_num_connections){ usb_sco_stop(); } + sco_num_connections = num_connections; if (num_connections){ usb_sco_start(); } - sco_num_connections = num_connections; } } #endif From b038503d09b39c9288ac10e6ee2f1a8fadb15920 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 20 Jan 2017 15:38:27 +0100 Subject: [PATCH 12/38] hfp_ag_demo: cleanup log output, call sco_demo_close on audio release --- example/hfp_ag_demo.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/example/hfp_ag_demo.c b/example/hfp_ag_demo.c index e0d690a1b..328448dda 100644 --- a/example/hfp_ag_demo.c +++ b/example/hfp_ag_demo.c @@ -622,17 +622,18 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even } break; case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED: - printf("\n** Audio connection released **\n"); + printf("Audio connection released\n"); sco_handle = 0; + sco_demo_close(); break; case HFP_SUBEVENT_START_RINGINIG: - printf("\n** Start Ringing **\n"); + printf("Start Ringing\n"); break; case HFP_SUBEVENT_STOP_RINGINIG: - printf("\n** Stop Ringing **\n"); + printf("Stop Ringing\n"); break; case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER: - printf("\n** Outgoing call '%s' **\n", hfp_subevent_place_call_with_number_get_number(event)); + printf("Outgoing call '%s'\n", hfp_subevent_place_call_with_number_get_number(event)); // validate number if ( strcmp("1234567", hfp_subevent_place_call_with_number_get_number(event)) == 0 || strcmp("7654321", hfp_subevent_place_call_with_number_get_number(event)) == 0 @@ -646,11 +647,11 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even break; case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG: - printf("\n** Attach number to voice tag. Sending '1234567\n"); + printf("Attach number to voice tag. Sending '1234567\n"); hfp_ag_send_phone_number_for_voice_tag(acl_handle, "1234567"); break; case HFP_SUBEVENT_TRANSMIT_DTMF_CODES: - printf("\n** Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event)); + printf("Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event)); hfp_ag_send_dtmf_code_done(acl_handle); break; case HFP_SUBEVENT_CALL_ANSWERED: From e4f319364d6fdd81024360fceac763a1ce6ca17f Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 20 Jan 2017 15:39:39 +0100 Subject: [PATCH 13/38] sco_util_demo: enable to start/stop audio multiple times --- example/sco_demo_util.c | 76 +++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index f1d2a38cd..31cc4d31a 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -102,7 +102,8 @@ // portaudio globals static PaStream * stream; -static uint8_t pa_stream_started = 0; +static int pa_stream_started = 0; +static int pa_stream_paused = 0; static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES]; static btstack_ring_buffer_t ring_buffer; @@ -111,7 +112,7 @@ static btstack_ring_buffer_t ring_buffer; static int dump_data = 1; static int count_sent = 0; static int count_received = 0; -static uint8_t negotiated_codec = 0; +static int negotiated_codec = -1; #if SCO_DEMO_MODE != SCO_DEMO_MODE_55 static int phase = 0; #endif @@ -189,21 +190,38 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, (void) inputBuffer; (void) userData; - uint32_t bytes_read = 0; - int bytes_per_buffer = framesPerBuffer; + // config based on codec + int bytes_to_copy; + int prebuffer_bytes; if (negotiated_codec == HFP_CODEC_MSBC){ - bytes_per_buffer *= MSBC_BYTES_PER_FRAME; + bytes_to_copy = framesPerBuffer * MSBC_BYTES_PER_FRAME; + prebuffer_bytes = MSBC_PREBUFFER_BYTES; } else { - bytes_per_buffer *= CVSD_BYTES_PER_FRAME; + bytes_to_copy = framesPerBuffer * CVSD_BYTES_PER_FRAME; + prebuffer_bytes = CVSD_PREBUFFER_BYTES; } - if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){ - btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read); - } else { - printf("NOT ENOUGH DATA!\n"); - memset(outputBuffer, 0, bytes_per_buffer); + // fill with silence while paused + if (pa_stream_paused){ + if (btstack_ring_buffer_bytes_available(&ring_buffer) < prebuffer_bytes){ + memset(outputBuffer, 0, bytes_to_copy); + return 0; + } else { + // resume playback + pa_stream_paused = 0; + } + } + + // get data from ringbuffer + uint32_t bytes_read = 0; + btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); + bytes_to_copy -= bytes_read; + + // fill with 0 if not enough + if (bytes_to_copy){ + memset(outputBuffer + bytes_read, 0, bytes_to_copy); + pa_stream_paused = 1; } - // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); return 0; } #endif @@ -213,8 +231,8 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i UNUSED(sample_rate); // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); -#ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){ +#ifdef HAVE_PORTAUDIO + if (!pa_stream_started){ /* -- start stream -- */ PaError err = Pa_StartStream(stream); if (err != paNoError){ @@ -222,12 +240,12 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i return; } pa_stream_started = 1; + pa_stream_paused = 1; } btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); - // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); #else UNUSED(num_channels); -#endif +#endif if (!num_samples_to_write) return; @@ -242,6 +260,8 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i } static void sco_demo_init_mSBC(void){ + printf("SCO Demo: Init CVSD\n"); + int sample_rate = 16000; wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL); @@ -265,6 +285,7 @@ static void sco_demo_init_mSBC(void){ PaStreamParameters outputParameters; /* -- initialize PortAudio -- */ + printf("PortAudio: Initialize\n"); err = Pa_Initialize(); if( err != paNoError ) return; /* -- setup input and output -- */ @@ -274,6 +295,7 @@ static void sco_demo_init_mSBC(void){ outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; /* -- setup stream -- */ + printf("PortAudio: Open stream\n"); err = Pa_OpenStream( &stream, NULL, // &inputParameters, @@ -284,7 +306,7 @@ static void sco_demo_init_mSBC(void){ patestCallback, /* no callback, use blocking API */ NULL ); /* no callback, so no callback userData */ if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); + printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err)); return; } memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); @@ -304,6 +326,8 @@ static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){ } static void sco_demo_init_CVSD(void){ + printf("SCO Demo: Init CVSD\n"); + int sample_rate = 8000; wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); btstack_cvsd_plc_init(&cvsd_plc_state); @@ -314,6 +338,7 @@ static void sco_demo_init_CVSD(void){ PaStreamParameters outputParameters; /* -- initialize PortAudio -- */ + printf("PortAudio: Initialize\n"); err = Pa_Initialize(); if( err != paNoError ) return; /* -- setup input and output -- */ @@ -323,6 +348,7 @@ static void sco_demo_init_CVSD(void){ outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; /* -- setup stream -- */ + printf("PortAudio: Open stream\n"); err = Pa_OpenStream( &stream, NULL, // &inputParameters, @@ -333,7 +359,7 @@ static void sco_demo_init_CVSD(void){ patestCallback, /* no callback, use blocking API */ NULL ); /* no callback, so no callback userData */ if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); + printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err)); return; } memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); @@ -362,14 +388,15 @@ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ sco_demo_close(); } #ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){ + if (!pa_stream_started){ /* -- start stream -- */ PaError err = Pa_StartStream(stream); if (err != paNoError){ - printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); + printf("sco_demo_receive_CVSD: Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } pa_stream_started = 1; + pa_stream_paused = 1; } btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write); #endif @@ -379,6 +406,7 @@ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ #endif void sco_demo_close(void){ + printf("SCO demo close\n"); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) wav_writer_close(); @@ -392,18 +420,21 @@ void sco_demo_close(void){ #ifdef HAVE_PORTAUDIO if (pa_stream_started){ + printf("PortAudio: Stop Stream\n"); PaError err = Pa_StopStream(stream); if (err != paNoError){ printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } pa_stream_started = 0; + printf("PortAudio: Close Stream\n"); err = Pa_CloseStream(stream); if (err != paNoError){ printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } + printf("PortAudio: Terminate\n"); err = Pa_Terminate(); if (err != paNoError){ printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); @@ -411,8 +442,8 @@ void sco_demo_close(void){ } } #endif + #ifdef SCO_WAV_FILENAME - #if 0 printf("SCO Demo: closing wav file\n"); if (negotiated_codec == HFP_CODEC_MSBC){ @@ -425,6 +456,9 @@ void sco_demo_close(void){ } #endif #endif + + negotiated_codec = -1; + #endif } From e5167d910d1c2aeeb5c1d14ced9222c44b008064 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 20 Jan 2017 16:11:21 +0100 Subject: [PATCH 14/38] sco_demo_util: ignore sco data after audio release --- example/sco_demo_util.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 31cc4d31a..fe3025f0d 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -610,10 +610,15 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){ #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef SCO_WAV_FILENAME - if (negotiated_codec == HFP_CODEC_MSBC){ - sco_demo_receive_mSBC(packet, size); - } else { - sco_demo_receive_CVSD(packet, size); + switch (negotiated_codec){ + case HFP_CODEC_MSBC: + sco_demo_receive_mSBC(packet, size); + break; + case HFP_CODEC_CVSD: + sco_demo_receive_CVSD(packet, size); + break; + default: + break; } dump_data = 0; #endif From e68aec34d69e79e63805a1a9cf3ada15af3ae2c8 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 20 Jan 2017 17:01:36 +0100 Subject: [PATCH 15/38] sco_demo_util: prepare for 16-bit CVSD --- example/sco_demo_util.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index fe3025f0d..89616e053 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -78,25 +78,25 @@ #define USE_PORTAUDIO #endif +#define NUM_CHANNELS 1 +#define CVSD_BYTES_PER_FRAME (1*NUM_CHANNELS) +#define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) #ifdef USE_PORTAUDIO #include #include "btstack_ring_buffer.h" // portaudio config -#define NUM_CHANNELS 1 #define CVSD_SAMPLE_RATE 8000 #define CVSD_FRAMES_PER_BUFFER 24 #define CVSD_PA_SAMPLE_TYPE paInt8 -#define CVSD_BYTES_PER_FRAME (1*NUM_CHANNELS) #define CVSD_PREBUFFER_MS 5 #define CVSD_PREBUFFER_BYTES (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) #define MSBC_SAMPLE_RATE 16000 #define MSBC_FRAMES_PER_BUFFER 120 #define MSBC_PA_SAMPLE_TYPE paInt16 -#define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) #define MSBC_PREBUFFER_MS 50 #define MSBC_PREBUFFER_BYTES (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) @@ -260,7 +260,7 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i } static void sco_demo_init_mSBC(void){ - printf("SCO Demo: Init CVSD\n"); + printf("SCO Demo: Init mSBC\n"); int sample_rate = 16000; wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); @@ -540,25 +540,25 @@ void sco_demo_send(hci_con_handle_t sco_handle){ } #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII - memset(&sco_packet[3], phase++, audio_samples_per_packet); + memset(&sco_packet[3], phase++, sco_payload_length); if (phase > 'z') phase = 'a'; #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER int j; - for (j=0;j Date: Fri, 20 Jan 2017 22:26:57 +0100 Subject: [PATCH 16/38] libusb: use appropriate iso packet size for alternate setting --- platform/libusb/hci_transport_h2_libusb.c | 27 ++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index 33ffb7ed0..df8b2917c 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -85,7 +85,7 @@ // 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding // 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding // --> support only a single SCO connection -#define ALT_SETTING (1) +// #define ALT_SETTING (1) // alt setting for 1-3 connections and 8/16 bit static const int alt_setting_8_bit[] = {1,2,3}; @@ -105,11 +105,9 @@ const uint16_t iso_packet_size_for_alt_setting[] = { 63, }; -#define ISO_PACKET_SIZE (iso_packet_size_for_alt_setting[ALT_SETTING]) - // 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel) // note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload -#define SCO_PACKET_SIZE (49) +#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS) // Outgoing SCO packet queue // simplified ring buffer implementation @@ -182,6 +180,9 @@ static uint16_t sco_voice_setting; static int sco_num_connections; static int sco_shutdown; +// dynamic SCO configuration +static uint16_t iso_packet_size; + #endif // outgoing buffer for HCI Command packets @@ -349,9 +350,10 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ memcpy(data, packet, size); // setup transfer + // log_info("usb_send_sco_packet: size %u, max size %u, iso packet size %u", size, NUM_ISO_PACKETS * iso_packet_size, iso_packet_size); struct libusb_transfer * sco_transfer = sco_out_transfers[tranfer_index]; - libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, size, NUM_ISO_PACKETS, async_callback, NULL, 0); - libusb_set_iso_packet_lengths(sco_transfer, ISO_PACKET_SIZE); + libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0); + libusb_set_iso_packet_lengths(sco_transfer, iso_packet_size); r = libusb_submit_transfer(sco_transfer); if (r < 0) { log_error("Error submitting sco transfer, %d", r); @@ -746,13 +748,6 @@ static int prepare_device(libusb_device_handle * aHandle){ libusb_close(aHandle); return r; } - log_info("Switching to setting %u on interface 1..", ALT_SETTING); - r = libusb_set_interface_alt_setting(aHandle, 1, ALT_SETTING); - if (r < 0) { - fprintf(stderr, "Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r)); - libusb_close(aHandle); - return r; - } #endif return 0; @@ -800,6 +795,8 @@ static int usb_sco_start(void){ // 8-bit PCM or mSBC alt_setting = alt_setting_8_bit[sco_num_connections-1]; } + // derive iso packet size from alt setting + iso_packet_size = iso_packet_size_for_alt_setting[alt_setting]; log_info("Switching to setting %u on interface 1..", alt_setting); int r = libusb_set_interface_alt_setting(handle, 1, alt_setting); @@ -818,8 +815,8 @@ static int usb_sco_start(void){ } // configure sco_in handlers libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr, - hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0); - libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_PACKET_SIZE); + hci_sco_in_buffer[c], NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0); + libusb_set_iso_packet_lengths(sco_in_transfer[c], iso_packet_size); r = libusb_submit_transfer(sco_in_transfer[c]); if (r) { log_error("Error submitting isochronous in transfer %d", r); From c4e666bc60ca1e15d942dd54f2d0968baa7ebb13 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 20 Jan 2017 23:08:05 +0100 Subject: [PATCH 17/38] sco_util: refactor portaudio code --- example/sco_demo_util.c | 376 ++++++++++++++++++++-------------------- 1 file changed, 190 insertions(+), 186 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index f1d2a38cd..656a7c07f 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -39,7 +39,6 @@ * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo */ - #include #include "sco_demo_util.h" @@ -53,86 +52,76 @@ #include "wav_util.h" #endif -// configure test mode +#ifdef HAVE_PORTAUDIO +#include +#include "btstack_ring_buffer.h" +#endif + + +// test modes #define SCO_DEMO_MODE_SINE 0 #define SCO_DEMO_MODE_ASCII 1 #define SCO_DEMO_MODE_COUNTER 2 #define SCO_DEMO_MODE_55 3 #define SCO_DEMO_MODE_00 4 - // SCO demo configuration -#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE -#define SCO_REPORT_PERIOD 100 +#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE -#ifdef HAVE_POSIX_FILE_IO -#define SCO_WAV_FILENAME "sco_input.wav" -#define SCO_MSBC_OUT_FILENAME "sco_output.msbc" -#define SCO_MSBC_IN_FILENAME "sco_input.msbc" +// number of sco packets until 'report' on console +#define SCO_REPORT_PERIOD 100 +// length and name of wav file on disc #define SCO_WAV_DURATION_IN_SECONDS 15 -#endif +#define SCO_WAV_FILENAME "sco_input.wav" +// name of sbc test files +#define SCO_MSBC_OUT_FILENAME "sco_output.msbc" +#define SCO_MSBC_IN_FILENAME "sco_input.msbc" + +// pre-buffer for CVSD and mSBC - also defines latency +#define SCO_CVSD_PA_PREBUFFER_MS 50 +#define SCO_MSBC_PA_PREBUFFER_MS 50 + +// constants +#define NUM_CHANNELS 1 +#define CVSD_BYTES_PER_FRAME (2*NUM_CHANNELS) +#define CVSD_SAMPLE_RATE 8000 +#define MSBC_SAMPLE_RATE 16000 +#define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) #define USE_PORTAUDIO +#define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) +#define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) #endif - #ifdef USE_PORTAUDIO -#include -#include "btstack_ring_buffer.h" - -// portaudio config -#define NUM_CHANNELS 1 - -#define CVSD_SAMPLE_RATE 8000 -#define CVSD_FRAMES_PER_BUFFER 24 -#define CVSD_PA_SAMPLE_TYPE paInt8 -#define CVSD_BYTES_PER_FRAME (1*NUM_CHANNELS) -#define CVSD_PREBUFFER_MS 5 -#define CVSD_PREBUFFER_BYTES (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) - -#define MSBC_SAMPLE_RATE 16000 -#define MSBC_FRAMES_PER_BUFFER 120 -#define MSBC_PA_SAMPLE_TYPE paInt16 -#define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) -#define MSBC_PREBUFFER_MS 50 -#define MSBC_PREBUFFER_BYTES (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) - -// portaudio globals -static PaStream * stream; -static uint8_t pa_stream_started = 0; - -static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES]; +static PaStream * stream; +static int pa_stream_started = 0; +static int pa_stream_paused = 0; +static uint8_t ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; static btstack_ring_buffer_t ring_buffer; #endif static int dump_data = 1; static int count_sent = 0; static int count_received = 0; -static uint8_t negotiated_codec = 0; -#if SCO_DEMO_MODE != SCO_DEMO_MODE_55 +static int negotiated_codec = -1; static int phase = 0; -#endif +static int num_audio_frames = 0; + +static btstack_sbc_decoder_state_t decoder_state; +static btstack_cvsd_plc_state_t cvsd_plc_state; +static int num_samples_to_write; FILE * msbc_file_in; FILE * msbc_file_out; #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE -// input signal: pre-computed sine wave, at 8000 kz -static const uint8_t sine_uint8[] = { - 0, 15, 31, 46, 61, 74, 86, 97, 107, 114, - 120, 124, 126, 126, 124, 120, 114, 107, 97, 86, - 74, 61, 46, 31, 15, 0, 241, 225, 210, 195, - 182, 170, 159, 149, 142, 136, 132, 130, 130, 132, - 136, 142, 149, 159, 170, 182, 195, 210, 225, 241, -}; - - // input signal: pre-computed sine wave, 160 Hz at 16000 kHz -static const int16_t sine_int16[] = { +static const int16_t sine_int16_at_16000hz[] = { 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, 19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466, 31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738, @@ -145,41 +134,30 @@ static const int16_t sine_int16[] = { -19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, }; -static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){ - int i; - for (i=0; i= sizeof(sine_uint8)) phase = 0; - } -} - -static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){ +// ony use every second value from 16khz table +static void sco_demo_sine_wave_int16_at_8000_hz(int num_samples, int16_t * data){ int i; for (i=0; i < num_samples; i++){ - data[i] = sine_int16[phase++]; - if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ + data[i] = sine_int16_at_16000hz[phase++]; + phase++; + if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ phase = 0; } } } -static int num_audio_frames = 0; static void sco_demo_fill_audio_frame(void){ if (!hfp_msbc_can_encode_audio_frame_now()) return; int num_samples = hfp_msbc_num_audio_samples_per_frame(); int16_t sample_buffer[num_samples]; - sco_demo_sine_wave_int16(num_samples, sample_buffer); + sco_demo_sine_wave_int16_at_8000_hz(num_samples, sample_buffer); hfp_msbc_encode_audio_frame(sample_buffer); num_audio_frames++; } -#ifdef SCO_WAV_FILENAME -static btstack_sbc_decoder_state_t decoder_state; -static btstack_cvsd_plc_state_t cvsd_plc_state; -static int num_samples_to_write; +#ifdef SCO_WAV_FILENAME #ifdef USE_PORTAUDIO -static int patestCallback( const void *inputBuffer, void *outputBuffer, +static int portaudio_callback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, @@ -189,32 +167,50 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, (void) inputBuffer; (void) userData; - uint32_t bytes_read = 0; - int bytes_per_buffer = framesPerBuffer; - if (negotiated_codec == HFP_CODEC_MSBC){ - bytes_per_buffer *= MSBC_BYTES_PER_FRAME; - } else { - bytes_per_buffer *= CVSD_BYTES_PER_FRAME; + // config based on codec + int bytes_to_copy; + int prebuffer_bytes; + switch (negotiated_codec){ + case HFP_CODEC_MSBC: + bytes_to_copy = framesPerBuffer * MSBC_BYTES_PER_FRAME; + prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES; + break; + case HFP_CODEC_CVSD: + bytes_to_copy = framesPerBuffer * CVSD_BYTES_PER_FRAME; + prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES; + break; + default: + bytes_to_copy = framesPerBuffer * 2; // assume 1 channel / 16 bit audio samples + prebuffer_bytes = 0xfffffff; + break; } - if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){ - btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read); - } else { - printf("NOT ENOUGH DATA!\n"); - memset(outputBuffer, 0, bytes_per_buffer); + // fill with silence while paused + if (pa_stream_paused){ + if (btstack_ring_buffer_bytes_available(&ring_buffer) < prebuffer_bytes){ + memset(outputBuffer, 0, bytes_to_copy); + return 0; + } else { + // resume playback + pa_stream_paused = 0; + } + } + + // get data from ringbuffer + uint32_t bytes_read = 0; + btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); + bytes_to_copy -= bytes_read; + + // fill with 0 if not enough + if (bytes_to_copy){ + memset(outputBuffer + bytes_read, 0, bytes_to_copy); + pa_stream_paused = 1; } - // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); return 0; } -#endif -static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ - UNUSED(context); - UNUSED(sample_rate); - - // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); -#ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){ +static void portaudio_start(void){ + if (!pa_stream_started){ /* -- start stream -- */ PaError err = Pa_StartStream(stream); if (err != paNoError){ @@ -222,12 +218,59 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i return; } pa_stream_started = 1; + pa_stream_paused = 1; } +} + +// return 1 if ok +static int portaudio_initialize(int sample_rate){ + PaError err; + PaStreamParameters outputParameters; + + /* -- initialize PortAudio -- */ + printf("PortAudio: Initialize\n"); + err = Pa_Initialize(); + if( err != paNoError ) return 0; + /* -- setup input and output -- */ + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = NUM_CHANNELS; + outputParameters.sampleFormat = paInt16; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + /* -- setup stream -- */ + printf("PortAudio: Open stream\n"); + err = Pa_OpenStream( + &stream, + NULL, // &inputParameters, + &outputParameters, + sample_rate, + 0, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + portaudio_callback, + NULL ); + if (err != paNoError){ + printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err)); + return 0; + } + memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); + btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); + pa_stream_started = 0; + return 1; +} +#endif + + +static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ + UNUSED(context); + UNUSED(sample_rate); + + // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); +#ifdef HAVE_PORTAUDIO + portaudio_start(); btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); - // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); #else UNUSED(num_channels); -#endif +#endif if (!num_samples_to_write) return; @@ -242,11 +285,12 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i } static void sco_demo_init_mSBC(void){ - int sample_rate = 16000; - wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); + printf("SCO Demo: Init mSBC\n"); + + wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE); btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL); - num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS; + num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; hfp_msbc_init(); sco_demo_fill_audio_frame(); @@ -261,35 +305,7 @@ static void sco_demo_init_mSBC(void){ #endif #ifdef USE_PORTAUDIO - PaError err; - PaStreamParameters outputParameters; - - /* -- initialize PortAudio -- */ - err = Pa_Initialize(); - if( err != paNoError ) return; - /* -- setup input and output -- */ - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = NUM_CHANNELS; - outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - /* -- setup stream -- */ - err = Pa_OpenStream( - &stream, - NULL, // &inputParameters, - &outputParameters, - MSBC_SAMPLE_RATE, - MSBC_FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); - btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); - pa_stream_started = 0; + portaudio_initialize(MSBC_SAMPLE_RATE); #endif } @@ -304,74 +320,44 @@ static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){ } static void sco_demo_init_CVSD(void){ - int sample_rate = 8000; - wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); + printf("SCO Demo: Init CVSD\n"); + + wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE); btstack_cvsd_plc_init(&cvsd_plc_state); - num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS; + + num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; #ifdef USE_PORTAUDIO - PaError err; - PaStreamParameters outputParameters; - - /* -- initialize PortAudio -- */ - err = Pa_Initialize(); - if( err != paNoError ) return; - /* -- setup input and output -- */ - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = NUM_CHANNELS; - outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - /* -- setup stream -- */ - err = Pa_OpenStream( - &stream, - NULL, // &inputParameters, - &outputParameters, - CVSD_SAMPLE_RATE, - CVSD_FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); - btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); - pa_stream_started = 0; + portaudio_initialize(CVSD_SAMPLE_RATE); #endif } static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ if (!num_samples_to_write) return; - int8_t audio_frame_out[255]; // + int16_t audio_frame_out[255]; // if (size > sizeof(audio_frame_out)){ printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n"); return; } - - const int num_samples = size - 3; + const int audio_bytes_read = size - 3; + const int num_samples = audio_bytes_read / CVSD_BYTES_PER_FRAME; const int samples_to_write = btstack_min(num_samples, num_samples_to_write); +#if 0 btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out); +#else + memcpy(audio_frame_out, packet+3, audio_bytes_read); +#endif - wav_writer_write_int8(samples_to_write, audio_frame_out); + wav_writer_write_int16(samples_to_write, audio_frame_out); num_samples_to_write -= samples_to_write; if (num_samples_to_write == 0){ sco_demo_close(); } #ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){ - /* -- start stream -- */ - PaError err = Pa_StartStream(stream); - if (err != paNoError){ - printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_stream_started = 1; - } - btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write); + portaudio_start(); + btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); #endif } @@ -379,6 +365,7 @@ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ #endif void sco_demo_close(void){ + printf("SCO demo close\n"); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) wav_writer_close(); @@ -392,18 +379,21 @@ void sco_demo_close(void){ #ifdef HAVE_PORTAUDIO if (pa_stream_started){ + printf("PortAudio: Stop Stream\n"); PaError err = Pa_StopStream(stream); if (err != paNoError){ printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } pa_stream_started = 0; + printf("PortAudio: Close Stream\n"); err = Pa_CloseStream(stream); if (err != paNoError){ printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } + printf("PortAudio: Terminate\n"); err = Pa_Terminate(); if (err != paNoError){ printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); @@ -411,8 +401,8 @@ void sco_demo_close(void){ } } #endif + #ifdef SCO_WAV_FILENAME - #if 0 printf("SCO Demo: closing wav file\n"); if (negotiated_codec == HFP_CODEC_MSBC){ @@ -425,6 +415,9 @@ void sco_demo_close(void){ } #endif #endif + + negotiated_codec = -1; + #endif } @@ -459,7 +452,9 @@ void sco_demo_init(void){ printf("SCO Demo: Sending counter value, hexdump received data.\n"); #endif -#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE +#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE + hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD +#else hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent #endif @@ -477,20 +472,18 @@ void sco_demo_send(hci_con_handle_t sco_handle){ if (!sco_handle) return; - const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length(); - const int sco_payload_length = sco_packet_length - 3; + int sco_packet_length = hci_get_sco_packet_length(); + int sco_payload_length = sco_packet_length - 3; hci_reserve_packet_buffer(); uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); - // set handle + flags - little_endian_store_16(sco_packet, 0, sco_handle); - // set len - sco_packet[2] = sco_payload_length; - const int audio_samples_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2 - #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE if (negotiated_codec == HFP_CODEC_MSBC){ + // overwrite + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; + if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ log_error("mSBC stream is empty."); } @@ -502,29 +495,30 @@ void sco_demo_send(hci_con_handle_t sco_handle){ sco_demo_fill_audio_frame(); } else { - sco_demo_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3)); + const int audio_samples_per_packet = sco_payload_length / CVSD_BYTES_PER_FRAME; + sco_demo_sine_wave_int16_at_8000_hz(audio_samples_per_packet, (int16_t *) (sco_packet+3)); } #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII - memset(&sco_packet[3], phase++, audio_samples_per_packet); + memset(&sco_packet[3], phase++, sco_payload_length); if (phase > 'z') phase = 'a'; #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER int j; - for (j=0;j Date: Sat, 21 Jan 2017 17:38:59 +0100 Subject: [PATCH 18/38] hci: fix compile without ENABLE_BLE --- src/hci.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hci.c b/src/hci.c index adc217c0e..2a15603e1 100644 --- a/src/hci.c +++ b/src/hci.c @@ -1409,14 +1409,15 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){ case HCI_INIT_W4_SET_EVENT_MASK: // skip Classic init commands for LE only chipsets if (!hci_classic_supported()){ +#ifdef ENABLE_BLE if (hci_le_supported()){ hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command return; - } else { - log_error("Neither BR/EDR nor LE supported"); - hci_init_done(); - return; } +#endif + log_error("Neither BR/EDR nor LE supported"); + hci_init_done(); + return; } if (!gap_ssp_supported()){ hci_stack->substate = HCI_INIT_WRITE_PAGE_TIMEOUT; From 86f9f81a4358aca602d1d7a8a606e3f51b0278f5 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 21 Jan 2017 17:58:17 +0100 Subject: [PATCH 19/38] tools: update Bluetooth SIG scrapers --- tool/bluetooth_data_types.py | 2 +- tool/bluetooth_gatt.py | 2 +- tool/convert_gatt_service.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tool/bluetooth_data_types.py b/tool/bluetooth_data_types.py index 55e8688a3..1161c7e2d 100755 --- a/tool/bluetooth_data_types.py +++ b/tool/bluetooth_data_types.py @@ -48,7 +48,7 @@ def scrape_page(fout, url): print('-' * 70) # get all elements in - rows = tree.xpath('//div[@class="table-time"]/table/tbody/tr') + rows = tree.xpath('//div[@class="copy-block ta-left"]/table/tbody/tr') for row in rows: children = row.getchildren() data_type_value = children[0].text_content() diff --git a/tool/bluetooth_gatt.py b/tool/bluetooth_gatt.py index f2648eda0..256767db5 100755 --- a/tool/bluetooth_gatt.py +++ b/tool/bluetooth_gatt.py @@ -40,7 +40,7 @@ def scrape_page(fout, url): page = requests.get(url) tree = html.fromstring(page.content) # get all elements in
- rows = tree.xpath('//table[@id="gattTable"]/tr') + rows = tree.xpath('//table[@id="gattTable"]/tbody/tr') for row in rows: children = row.getchildren() summary = children[0].text_content() diff --git a/tool/convert_gatt_service.py b/tool/convert_gatt_service.py index f851068a3..55bf56764 100755 --- a/tool/convert_gatt_service.py +++ b/tool/convert_gatt_service.py @@ -38,7 +38,7 @@ def list_services(): page = requests.get(url) tree = html.fromstring(page.content) # get all elements in
- rows = tree.xpath('//table[@id="gattTable"]/tr') + rows = tree.xpath('//table[@id="gattTable"]/tbody/tr') print("%-55s| %-30s| %s" % ('Specification Type', 'Specification Name', 'UUID')) print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10) maxlen_type = 0 From c0857b5e793979ffff0a4dc5e5cc1127b59d2c93 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 21 Jan 2017 18:12:52 +0100 Subject: [PATCH 20/38] tool/convert_gatt: fix WriteWithoutResponse, ignore InfromationText, support Report Reference --- tool/convert_gatt_service.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tool/convert_gatt_service.py b/tool/convert_gatt_service.py index 55bf56764..fc44e6412 100755 --- a/tool/convert_gatt_service.py +++ b/tool/convert_gatt_service.py @@ -65,9 +65,14 @@ def parse_properties(element): property_list_human = [] for property in properties: property_name = property.tag + if property_name == "WriteWithoutResponse": + property_name = "Write_Without_Response" + if property_name == "InformationText": + continue property_requirement = property.text - if (property_requirement != "Excluded"): - property_list_human.append(property_name) + if (property_requirement == "Excluded"): + continue + property_list_human.append(property_name) return (property_list_human) print("Characteristic %s - properties %s" % (name, property_list_human)) @@ -163,7 +168,8 @@ def convert_service(fout, specification_type): continue if (type == 'org.bluetooth.descriptor.report_reference'): - print('-- Descriptor %-40s - WARNING: REPORT_REFERENCE not supported yet' % name) + print('-- Descriptor %-40s' % name) + fout.write('REPORT_REFERENCE, %s,\n' % properties) continue if (type == 'org.bluetooth.descriptor.number_of_digitals'): From 4e4f6f264bd535007b0e77e37188d05d9626610e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 11:57:07 +0100 Subject: [PATCH 21/38] hfp: streamline code in sbc and cvsd plc --- src/classic/btstack_cvsd_plc.c | 40 ++++++++++++------------ src/classic/btstack_sbc_plc.c | 56 ++++++++++++++++------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/classic/btstack_cvsd_plc.c b/src/classic/btstack_cvsd_plc.c index 8c52ad36b..c48ba30d0 100644 --- a/src/classic/btstack_cvsd_plc.c +++ b/src/classic/btstack_cvsd_plc.c @@ -49,11 +49,13 @@ #include "btstack_cvsd_plc.h" #include "btstack_debug.h" +#define SAMPLE_FORMAT int8_t + static float rcos[CVSD_OLAL] = { - 0.99148655,0.96623611,0.92510857,0.86950446, - 0.80131732,0.72286918,0.63683150,0.54613418, - 0.45386582,0.36316850,0.27713082,0.19868268, - 0.13049554,0.07489143,0.03376389,0.00851345}; + 0.99148655f,0.96623611f,0.92510857f,0.86950446f, + 0.80131732f,0.72286918f,0.63683150f,0.54613418f, + 0.45386582f,0.36316850f,0.27713082f,0.19868268f, + 0.13049554f,0.07489143f,0.03376389f,0.00851345f}; // taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi // Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation @@ -79,7 +81,7 @@ static float absolute(float x){ return x; } -static float CrossCorrelation(int8_t *x, int8_t *y){ +static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){ float num = 0; float den = 0; float x2 = 0; @@ -94,7 +96,7 @@ static float CrossCorrelation(int8_t *x, int8_t *y){ return num/den; } -static int PatternMatch(int8_t *y){ +static int PatternMatch(SAMPLE_FORMAT *y){ float maxCn = -999999.0; // large negative number int bestmatch = 0; float Cn; @@ -109,7 +111,7 @@ static int PatternMatch(int8_t *y){ return bestmatch; } -static float AmplitudeMatch(int8_t *y, int8_t bestmatch) { +static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) { int i; float sumx = 0; float sumy = 0.000001f; @@ -126,11 +128,11 @@ static float AmplitudeMatch(int8_t *y, int8_t bestmatch) { return sf; } -static int8_t crop_to_int8(float val){ +static SAMPLE_FORMAT crop_sample(float val){ float croped_val = val; if (croped_val > 127.0) croped_val= 127.0; if (croped_val < -128.0) croped_val=-128.0; - return (int8_t) croped_val; + return (SAMPLE_FORMAT) croped_val; } @@ -138,7 +140,7 @@ void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){ memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t)); } -void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out){ +void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FORMAT *out){ float val; int i = 0; float sf = 1; @@ -154,19 +156,19 @@ void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag); for (i=0;ihist[plc_state->bestlag+i]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; float right = plc_state->hist[plc_state->bestlag+i]; val = left*rcos[i-CVSD_FS] + right*rcos[CVSD_OLAL-1-i+CVSD_FS]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;inbf>0){ @@ -199,7 +201,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in float left = plc_state->hist[CVSD_LHIST+i]; float right = in[i]; val = left * rcos[i-CVSD_RT] + right *rcos[CVSD_OLAL+CVSD_RT-1-i]; - out[i] = (int8_t)val; + out[i] = (SAMPLE_FORMAT)val; } } @@ -217,7 +219,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in plc_state->nbf=0; } -static int count_equal_bytes(int8_t * packet, uint16_t size){ +static int count_equal_bytes(SAMPLE_FORMAT * packet, uint16_t size){ int count = 0; int temp_count = 1; int i; @@ -237,11 +239,11 @@ static int count_equal_bytes(int8_t * packet, uint16_t size){ return count; } -static int bad_frame(int8_t * frame, uint16_t size){ +static int bad_frame(SAMPLE_FORMAT * frame, uint16_t size){ return count_equal_bytes(frame, size) > 20; } -void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){ +void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){ if (size != 24){ log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); } @@ -263,7 +265,7 @@ void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in } } -void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){ +void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){ if (size != 24){ log_error("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); } diff --git a/src/classic/btstack_sbc_plc.c b/src/classic/btstack_sbc_plc.c index 07b77fcb0..b6f104c7f 100644 --- a/src/classic/btstack_sbc_plc.c +++ b/src/classic/btstack_sbc_plc.c @@ -47,6 +47,8 @@ #include "btstack_sbc_plc.h" +#define SAMPLE_FORMAT int16_t + static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, @@ -84,7 +86,7 @@ static float absolute(float x){ return x; } -static float CrossCorrelation(int16_t *x, int16_t *y){ +static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){ float num = 0; float den = 0; float x2 = 0; @@ -99,13 +101,13 @@ static float CrossCorrelation(int16_t *x, int16_t *y){ return num/den; } -static int PatternMatch(int16_t *y){ - float maxCn = -999999.0; /* large negative number */ +static int PatternMatch(SAMPLE_FORMAT *y){ + float maxCn = -999999.0; // large negative number int bestmatch = 0; float Cn; int n; for (n=0;nmaxCn){ bestmatch=n; maxCn = Cn; @@ -114,8 +116,7 @@ static int PatternMatch(int16_t *y){ return bestmatch; } - -static float AmplitudeMatch(int16_t *y, int16_t bestmatch) { +static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) { int i; float sumx = 0; float sumy = 0.000001f; @@ -126,17 +127,17 @@ static float AmplitudeMatch(int16_t *y, int16_t bestmatch) { sumy += absolute(y[bestmatch+i]); } sf = sumx/sumy; - /* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */ + // This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts if (sf<0.75f) sf=0.75f; if (sf>1.2f) sf=1.2f; return sf; } -static int16_t crop_to_int16(float val){ +static SAMPLE_FORMAT crop_sample(float val){ float croped_val = val; if (croped_val > 32767.0) croped_val= 32767.0; if (croped_val < -32768.0) croped_val=-32768.0; - return (int16_t) croped_val; + return (SAMPLE_FORMAT) croped_val; } uint8_t * btstack_sbc_plc_zero_signal_frame(void){ @@ -149,46 +150,44 @@ void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){ memset(plc_state->hist,0,sizeof(plc_state->hist)); } -void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){ +void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *ZIRbuf, SAMPLE_FORMAT *out){ float val; int i = 0; float sf = 1; - plc_state->nbf++; if (plc_state->nbf==1){ - /* Perform pattern matching to find where to replicate */ + // Perform pattern matching to find where to replicate plc_state->bestlag = PatternMatch(plc_state->hist); - /* the replication begins after the template match */ + // the replication begins after the template match plc_state->bestlag += SBC_M; - /* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */ + // Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag); for (i=0;ihist[plc_state->bestlag+i]; val = left*rcos[i] + right*rcos[SBC_OLAL-1-i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; float right = plc_state->hist[plc_state->bestlag+i]; - val = left*rcos[i-SBC_FS]+right*rcos[SBC_FS+SBC_OLAL-1-i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS]; + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i]; } - } else { - for (i=0;ihist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i]; } } @@ -196,14 +195,13 @@ void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRb out[i] = plc_state->hist[SBC_LHIST+i]; } - /* shift the history buffer */ + // shift the history buffer for (i=0;ihist[i] = plc_state->hist[i+SBC_FS]; } - } -void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){ +void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){ float val; int i = 0; if (plc_state->nbf>0){ @@ -211,22 +209,22 @@ void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, out[i] = plc_state->hist[SBC_LHIST+i]; } - for (;ihist[SBC_LHIST+i]; float right = in[i]; - val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT]; - out[i] = (int16_t)val; + val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL+SBC_RT-1-i]; + out[i] = (SAMPLE_FORMAT)val; } } + for (;ihist[SBC_LHIST+i] = out[i]; } - /* shift the history buffer */ + // shift the history buffer for (i=0;ihist[i] = plc_state->hist[i+SBC_FS]; } From 815e8f5127bf6f625c90944f348a51b7068f00d7 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 12:42:34 +0100 Subject: [PATCH 22/38] test/hfp: create 16-bit live sample, but use 8-bit for tst --- test/hfp/cvsd_plc_test.c | 4 ++-- test/hfp/data/sco_input-16bit.wav | Bin 0 -> 240044 bytes .../data/{sco_input.wav => sco_input-8bit.wav} | Bin 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 test/hfp/data/sco_input-16bit.wav rename test/hfp/data/{sco_input.wav => sco_input-8bit.wav} (100%) diff --git a/test/hfp/cvsd_plc_test.c b/test/hfp/cvsd_plc_test.c index 0dbaaeb38..119f8f0af 100644 --- a/test/hfp/cvsd_plc_test.c +++ b/test/hfp/cvsd_plc_test.c @@ -161,8 +161,8 @@ TEST(CVSD_PLC, CountEqBytes){ TEST(CVSD_PLC, TestLiveWavFile){ int corruption_step = 10; - introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input.wav", 0); - introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input_with_bad_frames.wav", corruption_step); + introduce_bad_frames_to_wav_file("data/sco_input-8bit.wav", "results/sco_input.wav", 0); + introduce_bad_frames_to_wav_file("data/sco_input-8bit.wav", "results/sco_input_with_bad_frames.wav", corruption_step); mark_bad_frames_wav_file("results/sco_input.wav", "results/sco_input_detected_frames.wav"); process_wav_file_with_plc("results/sco_input.wav", "results/sco_input_after_plc.wav"); diff --git a/test/hfp/data/sco_input-16bit.wav b/test/hfp/data/sco_input-16bit.wav new file mode 100644 index 0000000000000000000000000000000000000000..b002632bb151fb91e62ded5c0e87d3f15ee2d6c8 GIT binary patch literal 240044 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 Date: Mon, 23 Jan 2017 14:59:39 +0100 Subject: [PATCH 23/38] hfp: use 16-bit samples for narrow-band / CVSD --- src/classic/btstack_cvsd_plc.c | 34 ++------- src/classic/btstack_cvsd_plc.h | 10 +-- src/hci.c | 4 +- test/hfp/cvsd_plc_test.c | 131 ++++++++++++++++++++------------- 4 files changed, 93 insertions(+), 86 deletions(-) diff --git a/src/classic/btstack_cvsd_plc.c b/src/classic/btstack_cvsd_plc.c index c48ba30d0..7bbae3eb4 100644 --- a/src/classic/btstack_cvsd_plc.c +++ b/src/classic/btstack_cvsd_plc.c @@ -49,7 +49,7 @@ #include "btstack_cvsd_plc.h" #include "btstack_debug.h" -#define SAMPLE_FORMAT int8_t +#define SAMPLE_FORMAT int16_t static float rcos[CVSD_OLAL] = { 0.99148655f,0.96623611f,0.92510857f,0.86950446f, @@ -130,12 +130,11 @@ static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) { static SAMPLE_FORMAT crop_sample(float val){ float croped_val = val; - if (croped_val > 127.0) croped_val= 127.0; - if (croped_val < -128.0) croped_val=-128.0; + if (croped_val > 32767.0) croped_val= 32767.0; + if (croped_val < -32768.0) croped_val=-32768.0; return (SAMPLE_FORMAT) croped_val; } - void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){ memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t)); } @@ -219,7 +218,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FOR plc_state->nbf=0; } -static int count_equal_bytes(SAMPLE_FORMAT * packet, uint16_t size){ +static int count_equal_samples(SAMPLE_FORMAT * packet, uint16_t size){ int count = 0; int temp_count = 1; int i; @@ -239,13 +238,15 @@ static int count_equal_bytes(SAMPLE_FORMAT * packet, uint16_t size){ return count; } +// @assumption frame len 24 samples static int bad_frame(SAMPLE_FORMAT * frame, uint16_t size){ - return count_equal_bytes(frame, size) > 20; + return count_equal_samples(frame, size) > 20; } void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){ if (size != 24){ log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); + return; } state->frame_count++; if (bad_frame(in,size)){ @@ -265,27 +266,6 @@ void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, SAMPLE_FORM } } -void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){ - if (size != 24){ - log_error("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); - } - state->frame_count++; - - if (bad_frame(in,size)){ - memcpy(out, in, size); - if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){ - memset(out, 50, size); - state->bad_frames_nr++; - } - } else { - memcpy(out, in, size); - state->good_frames_nr++; - if (state->good_frames_nr == 1){ - log_info("First good frame at index %d\n", state->frame_count-1); - } - } -} - void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state){ log_info("Good frames: %d\n", state->good_frames_nr); log_info("Bad frames: %d\n", state->bad_frames_nr); diff --git a/src/classic/btstack_cvsd_plc.h b/src/classic/btstack_cvsd_plc.h index a3f777f0d..40d53a86a 100644 --- a/src/classic/btstack_cvsd_plc.h +++ b/src/classic/btstack_cvsd_plc.h @@ -58,7 +58,7 @@ extern "C" { /* PLC State Information */ typedef struct cvsd_plc_state { - int8_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL]; + int16_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL]; int16_t bestlag; int nbf; @@ -69,11 +69,9 @@ typedef struct cvsd_plc_state { } btstack_cvsd_plc_state_t; void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state); -void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out); -void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in, int8_t *out); -uint8_t * btstack_cvsd_plc_zero_signal_frame(void); -void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out); -void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out); +void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *out); +void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *in, int16_t *out); +void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out); void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state); #if defined __cplusplus diff --git a/src/hci.c b/src/hci.c index 2a15603e1..78eb2c0dd 100644 --- a/src/hci.c +++ b/src/hci.c @@ -2189,8 +2189,8 @@ void hci_init(const hci_transport_t *transport, const void *config){ hci_stack->ssp_authentication_requirement = SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING; hci_stack->ssp_auto_accept = 1; - // voice setting - signed 8 bit pcm data with CVSD over the air - hci_stack->sco_voice_setting = 0x40; + // voice setting - signed 16 bit pcm data with CVSD over the air + hci_stack->sco_voice_setting = 0x60; hci_state_reset(); } diff --git a/test/hfp/cvsd_plc_test.c b/test/hfp/cvsd_plc_test.c index 119f8f0af..65849c726 100644 --- a/test/hfp/cvsd_plc_test.c +++ b/test/hfp/cvsd_plc_test.c @@ -11,10 +11,10 @@ #include "btstack_cvsd_plc.h" #include "wav_util.h" -const int audio_samples_per_frame = 24; -static int8_t audio_frame_in[audio_samples_per_frame]; +const int audio_samples_per_frame = 24; +static int16_t audio_frame_in[audio_samples_per_frame]; -static uint8_t test_data[][audio_samples_per_frame] = { +static int16_t test_data[][audio_samples_per_frame] = { { 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05 }, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 }, @@ -23,16 +23,6 @@ static uint8_t test_data[][audio_samples_per_frame] = { static btstack_cvsd_plc_state_t plc_state; -// input signal: pre-computed sine wave, at 8000 kz -static const uint8_t sine_uint8[] = { - 0, 15, 31, 46, 61, 74, 86, 97, 107, 114, - 120, 124, 126, 126, 124, 120, 114, 107, 97, 86, - 74, 61, 46, 31, 15, 0, 241, 225, 210, 195, - 182, 170, 159, 149, 142, 136, 132, 130, 130, 132, - 136, 142, 149, 159, 170, 182, 195, 210, 225, 241, -}; - - // input signal: pre-computed sine wave, 160 Hz at 16000 kHz static const int16_t sine_int16[] = { 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, @@ -47,27 +37,7 @@ static const int16_t sine_int16[] = { -19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, }; -static int phase = 0; -void sco_demo_sine_wave_int8(int num_samples, int8_t * data){ - int i; - for (i=0; i= sizeof(sine_uint8)) phase = 0; - } -} - -void sco_demo_sine_wave_int16(int num_samples, int16_t * data){ - int i; - for (i=0; i < num_samples; i++){ - data[i] = sine_int16[phase++]; - if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ - phase = 0; - } - } -} - -static int count_equal_bytes(uint8_t * packet, uint16_t size){ +static int count_equal_samples(int16_t * packet, uint16_t size){ int count = 0; int temp_count = 1; int i; @@ -87,61 +57,120 @@ static int count_equal_bytes(uint8_t * packet, uint16_t size){ return count; } -static void create_sine_wav(const char * out_filename){ +// @assumption frame len 24 samples +static int bad_frame(int16_t * frame, uint16_t size){ + return count_equal_samples(frame, size) > 20; +} + +static void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out){ + if (size != 24){ + printf("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d\n", CVSD_FS, size); + return; + } + state->frame_count++; + + if (bad_frame(in,size)){ + memcpy(out, in, size * 2); + if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){ + memset(out, 0x33, size * 2); + state->bad_frames_nr++; + } + } else { + memcpy(out, in, size); + state->good_frames_nr++; + if (state->good_frames_nr == 1){ + printf("First good frame at index %d\n", state->frame_count-1); + } + } +} + +static int phase = 0; +static void create_sine_wave_int16_data(int num_samples, int16_t * data){ + int i; + for (i=0; i < num_samples; i++){ + data[i] = sine_int16[phase++]; + phase++; + if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ + phase = 0; + } + } +} + +static int count_equal_bytes(int16_t * packet, uint16_t size){ + int count = 0; + int temp_count = 1; + int i; + for (i = 0; i < size-1; i++){ + if (packet[i] == packet[i+1]){ + temp_count++; + continue; + } + if (count < temp_count){ + count = temp_count; + } + temp_count = 1; + } + if (temp_count > count + 1){ + count = temp_count; + } + return count; +} + +void create_sine_wav(const char * out_filename){ btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); int i; for (i=0; i<2000; i++){ - sco_demo_sine_wave_int8(audio_samples_per_frame, audio_frame_in); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_in); + create_sine_wave_int16_data(audio_samples_per_frame, audio_frame_in); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_in); } wav_writer_close(); } -static void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){ +void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){ btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); wav_reader_open(in_filename); int fc = 0; - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ if (corruption_step > 0 && fc >= corruption_step && fc%corruption_step == 0){ - memset(audio_frame_in, 50, audio_samples_per_frame); + memset(audio_frame_in, 50, audio_samples_per_frame * 2); } - wav_writer_write_int8(audio_samples_per_frame, audio_frame_in); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_in); fc++; } wav_reader_close(); wav_writer_close(); } -static void process_wav_file_with_plc(const char * in_filename, const char * out_filename){ +void process_wav_file_with_plc(const char * in_filename, const char * out_filename){ // printf("\nProcess %s -> %s\n", in_filename, out_filename); btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); wav_reader_open(in_filename); - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ - int8_t audio_frame_out[audio_samples_per_frame]; + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ + int16_t audio_frame_out[audio_samples_per_frame]; btstack_cvsd_plc_process_data(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_out); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_out); } wav_reader_close(); wav_writer_close(); btstack_cvsd_dump_statistics(&plc_state); } -static void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){ +void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){ // printf("\nMark bad frame %s -> %s\n", in_filename, out_filename); btstack_cvsd_plc_init(&plc_state); CHECK_EQUAL(wav_writer_open(out_filename, 1, 8000), 0); CHECK_EQUAL(wav_reader_open(in_filename), 0); - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ - int8_t audio_frame_out[audio_samples_per_frame]; + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ + int16_t audio_frame_out[audio_samples_per_frame]; btstack_cvsd_plc_mark_bad_frame(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_out); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_out); } wav_reader_close(); wav_writer_close(); @@ -161,8 +190,8 @@ TEST(CVSD_PLC, CountEqBytes){ TEST(CVSD_PLC, TestLiveWavFile){ int corruption_step = 10; - introduce_bad_frames_to_wav_file("data/sco_input-8bit.wav", "results/sco_input.wav", 0); - introduce_bad_frames_to_wav_file("data/sco_input-8bit.wav", "results/sco_input_with_bad_frames.wav", corruption_step); + introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input.wav", 0); + introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input_with_bad_frames.wav", corruption_step); mark_bad_frames_wav_file("results/sco_input.wav", "results/sco_input_detected_frames.wav"); process_wav_file_with_plc("results/sco_input.wav", "results/sco_input_after_plc.wav"); From 034e9b53bb50b8a1370891a8c12cbfd550eb1b83 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 15:26:05 +0100 Subject: [PATCH 24/38] hci: fix compile --- src/hci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hci.c b/src/hci.c index 5df6abebb..c31224997 100644 --- a/src/hci.c +++ b/src/hci.c @@ -775,10 +775,12 @@ static void hci_shutdown_connection(hci_connection_t *conn){ // now it's gone hci_emit_nr_connections_changed(); +#ifdef ENABLE_SCO_OVER_HCI // update SCO if (addr_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){ hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections()); } +#endif } #ifdef ENABLE_CLASSIC From 1b3ee5c3b14bd6b24b45198b1b627ded0f248c0f Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 16:49:48 +0100 Subject: [PATCH 25/38] winusb: track sco changes --- platform/windows/hci_transport_h2_winusb.c | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index f71d8a2dd..6e548d5f5 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -278,6 +278,10 @@ static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT]; static uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE]; static int sco_ring_write; // packet idx +// SCO Reconfiguration - pause/resume +static uint16_t sco_voice_setting; +static int sco_num_connections; + #endif #if 0 @@ -1281,6 +1285,24 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ } } +#ifdef ENABLE_SCO_OVER_HCI +static void usb_set_sco_config(uint16_t voice_setting, int num_connections){ + log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections); + + if (num_connections != sco_num_connections){ + sco_voice_setting = voice_setting; + if (sco_num_connections){ +// usb_sco_stop(); + } + sco_num_connections = num_connections; + if (num_connections){ +// usb_sco_start(); + } + } + +} +#endif + // get usb singleton static const hci_transport_t hci_transport_usb = { /* const char * name; */ "H2_WINUSB", @@ -1292,7 +1314,7 @@ static const hci_transport_t hci_transport_usb = { /* int (*send_packet)(...); */ &usb_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ NULL, /* void (*reset_link)(void); */ NULL, - /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config, }; const hci_transport_t * hci_transport_usb_instance(void) { From b9227387ac7df0c04067ec0d1c7b4e92284715ab Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 16:56:32 +0100 Subject: [PATCH 26/38] winusb: extract usb_sco_start/usb_sco_stop --- platform/windows/hci_transport_h2_winusb.c | 76 +++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 6e548d5f5..c7b387f9a 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -770,6 +770,75 @@ exit_on_error: return FALSE; } +#ifdef ENABLE_SCO_OVER_HCI +static int usb_sco_start(void){ + printf("usb_sco_start\n"); + log_info("usb_sco_start"); + + sco_state_machine_init(); + sco_ring_init(); + +#if 0 + // calc alt setting + int alt_setting; + if (sco_voice_setting & 0x0020){ + // 16-bit PCM + alt_setting = alt_setting_16_bit[sco_num_connections-1]; + } else { + // 8-bit PCM or mSBC + alt_setting = alt_setting_8_bit[sco_num_connections-1]; + } + + log_info("Switching to setting %u on interface 1..", alt_setting); + // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. + BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting); + if (!result) goto exit_on_error; +#endif + +#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY + // get current frame number + ULONG current_frame_number; + LARGE_INTEGER timestamp; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + // plan for next tranfer + sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS; +#endif + + int i; + for (i=0;i Date: Mon, 23 Jan 2017 17:12:39 +0100 Subject: [PATCH 27/38] winusb: start/stop sco transfers --- platform/windows/hci_transport_h2_winusb.c | 69 +--------------------- 1 file changed, 2 insertions(+), 67 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index c7b387f9a..0925facc5 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -962,71 +962,6 @@ static int usb_try_open_device(const char * device_path){ // submit all incoming transfers usb_submit_event_in_transfer(); usb_submit_acl_in_transfer(); - -#ifdef ENABLE_SCO_OVER_HCI - -#if 0 -#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY - // get current frame number - ULONG current_frame_number; - LARGE_INTEGER timestamp; - WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); - - // plan for next tranfer - sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS; -#endif - - for (i=0;i Date: Mon, 23 Jan 2017 17:24:04 +0100 Subject: [PATCH 28/38] winusb: add iso packet sizes --- platform/windows/hci_transport_h2_winusb.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 0925facc5..913bd9021 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -167,10 +167,24 @@ static WinUsb_GetCurrentFrameNumber_t WinUsb_GetCurrentFrameNumber; // --> support only a single SCO connection #define ALT_SETTING (1) +// alt setting for 1-3 connections and 8/16 bit +const int alt_setting_8_bit[] = {1,2,3}; +const int alt_setting_16_bit[] = {2,4,5}; + // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets // One complete SCO packet with 24 frames every 3 frames (== 3 ms) #define NUM_ISO_PACKETS (3) +const uint16_t iso_packet_size_for_alt_setting[] = { + 0, + 9, + 17, + 25, + 33, + 49, + 63, +}; + // results in 9 bytes per frame #define ISO_PACKET_SIZE (9) @@ -903,7 +917,7 @@ static int usb_try_open_device(const char * device_path){ memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors)); log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors)); - result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle); + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle); if (!result) goto exit_on_error; log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); From 99fc8027cdff8f29524b3480789e28ffb7f377db Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 17:26:53 +0100 Subject: [PATCH 29/38] winusb: seach for ios endpoints on interface #1, alt setting #1 --- platform/windows/hci_transport_h2_winusb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 913bd9021..108ff9c61 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -723,19 +723,20 @@ static BOOL usb_scan_for_bluetooth_endpoints(void) { sco_out_addr = 0; sco_in_addr = 0; - // look for SCO pipes on Interface #1, Alt Setting ALT_SETTING - result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor); + // look for SCO pipes on Interface #1, Alt Setting 1 + int alt_setting = 1; + result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, alt_setting, &usb_interface_descriptor); if (!result) goto exit_on_error; for (i=0;i Date: Mon, 23 Jan 2017 22:01:35 +0100 Subject: [PATCH 30/38] winusb: extract usb_sco_[un]register_buffers, make sure to call after setting alternative setting --- platform/windows/hci_transport_h2_winusb.c | 66 ++++++++++++++-------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 108ff9c61..30cef273c 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -328,6 +328,30 @@ static void sco_ring_init(void){ static int sco_ring_have_space(void){ return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT; } +static void usb_sco_register_buffers(void){ + BOOL result; + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle); + if (!result) { + log_error("usb_sco_register_buffers: register in buffer failed, error %lu", GetLastError()); + } + log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); + + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle); + if (!result) { + log_error("usb_sco_unregister_buffers: register out buffer failed, error %lu", GetLastError()); + } + log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle); +} +static void usb_sco_unregister_buffers(void){ + if (hci_sco_in_buffer_handle){ + WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle); + hci_sco_in_buffer_handle = NULL; + } + if (hci_sco_out_buffer_handle){ + WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle); + hci_sco_out_buffer_handle = NULL; + } +} #endif static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ @@ -358,14 +382,7 @@ static void usb_free_resources(void){ } #ifdef ENABLE_SCO_OVER_HCI - if (hci_sco_in_buffer_handle){ - WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle); - hci_sco_in_buffer_handle = NULL; - } - if (hci_sco_out_buffer_handle){ - WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle); - hci_sco_out_buffer_handle = NULL; - } + usb_sco_unregister_buffers(); #endif } @@ -786,6 +803,7 @@ exit_on_error: } #ifdef ENABLE_SCO_OVER_HCI + static int usb_sco_start(void){ printf("usb_sco_start\n"); log_info("usb_sco_start"); @@ -899,10 +917,10 @@ static int usb_try_open_device(const char * device_path){ if (!result) goto exit_on_error; log_info("Claiming interface 1: success"); - log_info("Switching to setting %u on interface 1..", ALT_SETTING); - // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. - result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); - if (!result) goto exit_on_error; + // log_info("Switching to setting %u on interface 1..", ALT_SETTING); + // // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. + // result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); + // if (!result) goto exit_on_error; #endif result = usb_scan_for_bluetooth_endpoints(); @@ -918,14 +936,6 @@ static int usb_try_open_device(const char * device_path){ memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors)); log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors)); - result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle); - if (!result) goto exit_on_error; - log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); - - result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle); - if (!result) goto exit_on_error; - log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle); - // setup async io && btstack handler memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in)); for (i=0;i Date: Mon, 23 Jan 2017 22:27:04 +0100 Subject: [PATCH 31/38] winusb: switch to alt setting #1 in usc_sco_start and back to #0 in usb_sco_stop --- platform/windows/hci_transport_h2_winusb.c | 75 +++++++++++++--------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 30cef273c..c412d7968 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -295,7 +295,7 @@ static int sco_ring_write; // packet idx // SCO Reconfiguration - pause/resume static uint16_t sco_voice_setting; static int sco_num_connections; - +static int sco_shutdown; #endif #if 0 @@ -422,6 +422,11 @@ exit_on_error: // frame number gets updated static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called"); + return; + } + LARGE_INTEGER timestamp; ULONG current_frame_number; WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); @@ -431,7 +436,7 @@ static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); - log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); + // log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); if (!result) { if (GetLastError() == ERROR_IO_PENDING) { @@ -450,6 +455,11 @@ exit_on_error: static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){ + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called"); + return; + } + LARGE_INTEGER timestamp; ULONG current_frame_number; WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); @@ -565,6 +575,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_process_sco_out called"); + return; + } + // get current frame number ULONG current_frame_number; LARGE_INTEGER timestamp; @@ -576,11 +591,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ if (ds == &usb_data_source_sco_out[transfer_index]) break; } - log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number); + // log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number); DWORD bytes_transferred; BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_out[transfer_index], &bytes_transferred, FALSE); - log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state); + // log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state); if(!ok){ DWORD err = GetLastError(); if (err == ERROR_IO_INCOMPLETE){ @@ -598,11 +613,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ if (sco_ring_transfers_active){ // update expected and wait for completion usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT; - log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); + // log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE); } - log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); + // log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); // mark free if (sco_ring_have_space()) { @@ -615,6 +630,10 @@ static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_c btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + if (sco_shutdown){ + log_info("USB SCO Shutdown: usb_process_sco_out called"); + return; + } // find index int i; @@ -808,6 +827,8 @@ static int usb_sco_start(void){ printf("usb_sco_start\n"); log_info("usb_sco_start"); + sco_shutdown = 0; + sco_state_machine_init(); sco_ring_init(); @@ -821,12 +842,15 @@ static int usb_sco_start(void){ // 8-bit PCM or mSBC alt_setting = alt_setting_8_bit[sco_num_connections-1]; } - +#endif + int alt_setting = 1; log_info("Switching to setting %u on interface 1..", alt_setting); // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting); if (!result) goto exit_on_error; -#endif + + // register isochronous buffer after setting alternate setting + usb_sco_register_buffers(); #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY // get current frame number @@ -852,23 +876,29 @@ static int usb_sco_start(void){ btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ); return 1; -#if 0 exit_on_error: log_error("usb_sco_start: last error %lu", GetLastError()); usb_free_resources(); return 0; -#endif } static void usb_sco_stop(void){ + printf("usb_sco_stop\n"); + log_info("usb_sco_stop"); + + sco_shutdown = 1; + + // abort SCO transfers WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr); WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr); -#if 0 + + // unlock/free SCO buffers + usb_sco_unregister_buffers(); + int alt_setting = 0; log_info("Switching to setting %u on interface 1..", alt_setting); // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting); -#endif } #endif @@ -916,11 +946,6 @@ static int usb_try_open_device(const char * device_path){ result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle); if (!result) goto exit_on_error; log_info("Claiming interface 1: success"); - - // log_info("Switching to setting %u on interface 1..", ALT_SETTING); - // // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. - // result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); - // if (!result) goto exit_on_error; #endif result = usb_scan_for_bluetooth_endpoints(); @@ -986,17 +1011,7 @@ static int usb_try_open_device(const char * device_path){ // submit all incoming transfers usb_submit_event_in_transfer(); - usb_submit_acl_in_transfer(); - -#ifdef ENABLE_SCO_OVER_HCI - log_info("Switching to setting %u on interface 1..", ALT_SETTING); - // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. - result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); - if (!result) goto exit_on_error; - - usb_sco_register_buffers(); -#endif - + usb_submit_acl_in_transfer(); return 1; exit_on_error: @@ -1278,7 +1293,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ // setup transfer int continue_stream = sco_ring_transfers_active > 0; BOOL ok = WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]); - log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); + // log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); if (!ok) { if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; } @@ -1297,7 +1312,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0}; packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); - log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); + // log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); // and if we have more space for SCO packets if (sco_ring_have_space()) { From 5a97f1ba9b249d1a09a75973114c3e68f2a433ae Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 23 Jan 2017 23:08:33 +0100 Subject: [PATCH 32/38] winusb: dynamically pick alt setting based on voice settings and num connections --- platform/windows/hci_transport_h2_winusb.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index c412d7968..6653eae7c 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -185,12 +185,9 @@ const uint16_t iso_packet_size_for_alt_setting[] = { 63, }; -// results in 9 bytes per frame -#define ISO_PACKET_SIZE (9) - // 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel) // note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload -#define SCO_PACKET_SIZE (NUM_ISO_PACKETS * ISO_PACKET_SIZE) +#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS) #define ISOC_BUFFERS 8 @@ -296,6 +293,8 @@ static int sco_ring_write; // packet idx static uint16_t sco_voice_setting; static int sco_num_connections; static int sco_shutdown; + +static uint16_t iso_packet_size; #endif #if 0 @@ -433,7 +432,7 @@ static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ ULONG frame_before = *frame_number; - BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS, frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); // log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); @@ -466,7 +465,7 @@ static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){ // log_info("usb_submit_sco_in_transfer[%02u]: current frame %lu", i, current_frame_number); - BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS, continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); if (!result) { @@ -832,7 +831,6 @@ static int usb_sco_start(void){ sco_state_machine_init(); sco_ring_init(); -#if 0 // calc alt setting int alt_setting; if (sco_voice_setting & 0x0020){ @@ -842,13 +840,15 @@ static int usb_sco_start(void){ // 8-bit PCM or mSBC alt_setting = alt_setting_8_bit[sco_num_connections-1]; } -#endif - int alt_setting = 1; + log_info("Switching to setting %u on interface 1..", alt_setting); // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting); if (!result) goto exit_on_error; + // derive iso packet size from alt setting + iso_packet_size = iso_packet_size_for_alt_setting[alt_setting]; + // register isochronous buffer after setting alternate setting usb_sco_register_buffers(); From 463c9c89cdd7599d89d8bc1ecdc22cc8db4e19c1 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 24 Jan 2017 17:26:55 +0100 Subject: [PATCH 33/38] sco_util: prefix port audio vars with pa_output --- example/sco_demo_util.c | 59 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 656a7c07f..095e528fe 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -59,11 +59,12 @@ // test modes -#define SCO_DEMO_MODE_SINE 0 -#define SCO_DEMO_MODE_ASCII 1 -#define SCO_DEMO_MODE_COUNTER 2 -#define SCO_DEMO_MODE_55 3 -#define SCO_DEMO_MODE_00 4 +#define SCO_DEMO_MODE_SINE 0 +#define SCO_DEMO_MODE_ASCII 1 +#define SCO_DEMO_MODE_COUNTER 2 +#define SCO_DEMO_MODE_55 3 +#define SCO_DEMO_MODE_00 4 +#define SCO_DEMO_MODE_MICROPHONE 5 // SCO demo configuration #define SCO_DEMO_MODE SCO_DEMO_MODE_SINE @@ -90,18 +91,18 @@ #define MSBC_SAMPLE_RATE 16000 #define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) -#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) +#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE_MICROPHONE) #define USE_PORTAUDIO #define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) #define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) #endif #ifdef USE_PORTAUDIO -static PaStream * stream; -static int pa_stream_started = 0; -static int pa_stream_paused = 0; -static uint8_t ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; -static btstack_ring_buffer_t ring_buffer; +static PaStream * pa_output_stream; +static int pa_output_started = 0; +static int pa_output_paused = 0; +static uint8_t pa_output_ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; +static btstack_ring_buffer_t pa_output_ring_buffer; #endif static int dump_data = 1; @@ -186,39 +187,39 @@ static int portaudio_callback( const void *inputBuffer, void *outputBuffer, } // fill with silence while paused - if (pa_stream_paused){ - if (btstack_ring_buffer_bytes_available(&ring_buffer) < prebuffer_bytes){ + if (pa_output_paused){ + if (btstack_ring_buffer_bytes_available(&pa_output_ring_buffer) < prebuffer_bytes){ memset(outputBuffer, 0, bytes_to_copy); return 0; } else { // resume playback - pa_stream_paused = 0; + pa_output_paused = 0; } } // get data from ringbuffer uint32_t bytes_read = 0; - btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); + btstack_ring_buffer_read(&pa_output_ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); bytes_to_copy -= bytes_read; // fill with 0 if not enough if (bytes_to_copy){ memset(outputBuffer + bytes_read, 0, bytes_to_copy); - pa_stream_paused = 1; + pa_output_paused = 1; } return 0; } static void portaudio_start(void){ - if (!pa_stream_started){ + if (!pa_output_started){ /* -- start stream -- */ - PaError err = Pa_StartStream(stream); + PaError err = Pa_StartStream(pa_output_stream); if (err != paNoError){ printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } - pa_stream_started = 1; - pa_stream_paused = 1; + pa_output_started = 1; + pa_output_paused = 1; } } @@ -252,9 +253,9 @@ static int portaudio_initialize(int sample_rate){ printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err)); return 0; } - memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); - btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); - pa_stream_started = 0; + memset(pa_output_ring_buffer_storage, 0, sizeof(pa_output_ring_buffer_storage)); + btstack_ring_buffer_init(&pa_output_ring_buffer, pa_output_ring_buffer_storage, sizeof(pa_output_ring_buffer_storage)); + pa_output_started = 0; return 1; } #endif @@ -267,7 +268,7 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); #ifdef HAVE_PORTAUDIO portaudio_start(); - btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); + btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2); #else UNUSED(num_channels); #endif @@ -357,7 +358,7 @@ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ } #ifdef USE_PORTAUDIO portaudio_start(); - btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); + btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); #endif } @@ -378,16 +379,16 @@ void sco_demo_close(void){ #endif #ifdef HAVE_PORTAUDIO - if (pa_stream_started){ + if (pa_output_started){ printf("PortAudio: Stop Stream\n"); - PaError err = Pa_StopStream(stream); + PaError err = Pa_StopStream(pa_output_stream); if (err != paNoError){ printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); return; } - pa_stream_started = 0; + pa_output_started = 0; printf("PortAudio: Close Stream\n"); - err = Pa_CloseStream(stream); + err = Pa_CloseStream(pa_output_stream); if (err != paNoError){ printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); return; From c79b4cab43c41297de70183f469da23c49cd2aff Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 24 Jan 2017 19:14:53 +0100 Subject: [PATCH 34/38] hfp_ag: remove debug output --- src/classic/hfp_ag.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c index 901823dca..7902c0799 100644 --- a/src/classic/hfp_ag.c +++ b/src/classic/hfp_ag.c @@ -650,7 +650,7 @@ static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){ } static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * hfp_connection){ - log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command); + // log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command); if (hfp_connection->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0; int done = 0; switch(hfp_connection->command){ @@ -1641,7 +1641,7 @@ static void hfp_ag_send_call_status(hfp_connection_t * hfp_connection, int call_ static void hfp_run_for_context(hfp_connection_t *hfp_connection){ - log_info("hfp_run_for_context %p", hfp_connection); + // log_info("hfp_run_for_context %p", hfp_connection); if (!hfp_connection) return; From 2b89dbfc6bb27df2ec8d6a87caf9eeb8d3765ccd Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Jan 2017 16:58:03 +0100 Subject: [PATCH 35/38] sco_util: send PortAudio input as CVSD --- example/sco_demo_util.c | 245 +++++++++++++++++++++++++++++----------- 1 file changed, 181 insertions(+), 64 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 095e528fe..e0fa32c06 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -91,34 +91,50 @@ #define MSBC_SAMPLE_RATE 16000 #define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) -#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE_MICROPHONE) +#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) #define USE_PORTAUDIO #define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) #define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) #endif #ifdef USE_PORTAUDIO -static PaStream * pa_output_stream; + +// bidirectional audio stream +static PaStream * pa_stream; + +// output static int pa_output_started = 0; static int pa_output_paused = 0; static uint8_t pa_output_ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; static btstack_ring_buffer_t pa_output_ring_buffer; + +// input +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE +#define USE_PORTAUDIO_INPUT +static int pa_input_started = 0; +static int pa_input_paused = 0; +static uint8_t pa_input_ring_buffer_storage[2*8000]; // full second input buffer +static btstack_ring_buffer_t pa_input_ring_buffer; +static int pa_input_counter; +#endif + #endif static int dump_data = 1; static int count_sent = 0; static int count_received = 0; static int negotiated_codec = -1; -static int phase = 0; -static int num_audio_frames = 0; -static btstack_sbc_decoder_state_t decoder_state; -static btstack_cvsd_plc_state_t cvsd_plc_state; -static int num_samples_to_write; +btstack_sbc_decoder_state_t decoder_state; +btstack_cvsd_plc_state_t cvsd_plc_state; FILE * msbc_file_in; FILE * msbc_file_out; +int num_samples_to_write; +int num_audio_frames; +int phase; + #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE // input signal: pre-computed sine wave, 160 Hz at 16000 kHz @@ -155,8 +171,8 @@ static void sco_demo_fill_audio_frame(void){ hfp_msbc_encode_audio_frame(sample_buffer); num_audio_frames++; } +#endif -#ifdef SCO_WAV_FILENAME #ifdef USE_PORTAUDIO static int portaudio_callback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, @@ -168,6 +184,8 @@ static int portaudio_callback( const void *inputBuffer, void *outputBuffer, (void) inputBuffer; (void) userData; +// output part + // config based on codec int bytes_to_copy; int prebuffer_bytes; @@ -207,42 +225,54 @@ static int portaudio_callback( const void *inputBuffer, void *outputBuffer, memset(outputBuffer + bytes_read, 0, bytes_to_copy); pa_output_paused = 1; } - return 0; -} +// end of output part -static void portaudio_start(void){ - if (!pa_output_started){ - /* -- start stream -- */ - PaError err = Pa_StartStream(pa_output_stream); - if (err != paNoError){ - printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_output_started = 1; - pa_output_paused = 1; - } +// input part -- just store in ring buffer +#ifdef USE_PORTAUDIO_INPUT + btstack_ring_buffer_write(&pa_input_ring_buffer, (uint8_t *)inputBuffer, framesPerBuffer * 2); + pa_input_counter += framesPerBuffer * 2; +#endif + + return 0; } // return 1 if ok static int portaudio_initialize(int sample_rate){ PaError err; - PaStreamParameters outputParameters; /* -- initialize PortAudio -- */ printf("PortAudio: Initialize\n"); err = Pa_Initialize(); if( err != paNoError ) return 0; + /* -- setup input and output -- */ + const PaDeviceInfo *deviceInfo; + PaStreamParameters * inputParameters = NULL; + PaStreamParameters outputParameters; outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ outputParameters.channelCount = NUM_CHANNELS; outputParameters.sampleFormat = paInt16; outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; - /* -- setup stream -- */ + deviceInfo = Pa_GetDeviceInfo( outputParameters.device ); + log_info("PortAudio: Output device: %s", deviceInfo->name); +#ifdef USE_PORTAUDIO_INPUT + PaStreamParameters theInputParameters; + theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ + theInputParameters.channelCount = NUM_CHANNELS; + theInputParameters.sampleFormat = paInt16; + theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighOutputLatency; + theInputParameters.hostApiSpecificStreamInfo = NULL; + inputParameters = &theInputParameters; + deviceInfo = Pa_GetDeviceInfo( inputParameters->device ); + log_info("PortAudio: Input device: %s", deviceInfo->name); +#endif + + /* -- setup output stream -- */ printf("PortAudio: Open stream\n"); err = Pa_OpenStream( - &stream, - NULL, // &inputParameters, + &pa_stream, + inputParameters, &outputParameters, sample_rate, 0, @@ -255,9 +285,52 @@ static int portaudio_initialize(int sample_rate){ } memset(pa_output_ring_buffer_storage, 0, sizeof(pa_output_ring_buffer_storage)); btstack_ring_buffer_init(&pa_output_ring_buffer, pa_output_ring_buffer_storage, sizeof(pa_output_ring_buffer_storage)); - pa_output_started = 0; +#ifdef USE_PORTAUDIO_INPUT + memset(pa_input_ring_buffer_storage, 0, sizeof(pa_input_ring_buffer_storage)); + btstack_ring_buffer_init(&pa_input_ring_buffer, pa_input_ring_buffer_storage, sizeof(pa_input_ring_buffer_storage)); + printf("PortAudio: Input buffer size %u\n", btstack_ring_buffer_bytes_free(&pa_input_ring_buffer)); +#endif + + /* -- start stream -- */ + err = Pa_StartStream(pa_stream); + if (err != paNoError){ + printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); + return 0; + } + pa_output_started = 1; + pa_output_paused = 1; +#ifdef USE_PORTAUDIO_INPUT + pa_input_started = 1; + pa_input_paused = 1; +#endif + return 1; } + +static void portaudio_terminate(void){ + if (!pa_stream) return; + + PaError err; + printf("PortAudio: Stop Stream\n"); + err = Pa_StopStream(pa_stream); + if (err != paNoError){ + printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + printf("PortAudio: Close Stream\n"); + err = Pa_CloseStream(pa_stream); + if (err != paNoError){ + printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + pa_stream = NULL; + printf("PortAudio: Terminate\n"); + err = Pa_Terminate(); + if (err != paNoError){ + printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); + return; + } +} #endif @@ -267,7 +340,6 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); #ifdef HAVE_PORTAUDIO - portaudio_start(); btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2); #else UNUSED(num_channels); @@ -294,12 +366,15 @@ static void sco_demo_init_mSBC(void){ num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; hfp_msbc_init(); +#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE sco_demo_fill_audio_frame(); +#endif #ifdef SCO_MSBC_IN_FILENAME msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb"); printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in); #endif + #ifdef SCO_MSBC_OUT_FILENAME msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb"); printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out); @@ -357,50 +432,26 @@ static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ sco_demo_close(); } #ifdef USE_PORTAUDIO - portaudio_start(); btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); #endif } -#endif -#endif - void sco_demo_close(void){ printf("SCO demo close\n"); -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) + #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) wav_writer_close(); +#endif printf("SCO demo statistics: "); if (negotiated_codec == HFP_CODEC_MSBC){ printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr); } else { printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr); } -#endif #ifdef HAVE_PORTAUDIO - if (pa_output_started){ - printf("PortAudio: Stop Stream\n"); - PaError err = Pa_StopStream(pa_output_stream); - if (err != paNoError){ - printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_output_started = 0; - printf("PortAudio: Close Stream\n"); - err = Pa_CloseStream(pa_output_stream); - if (err != paNoError){ - printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - - printf("PortAudio: Terminate\n"); - err = Pa_Terminate(); - if (err != paNoError){ - printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - } + portaudio_terminate(); #endif #ifdef SCO_WAV_FILENAME @@ -425,7 +476,7 @@ void sco_demo_close(void){ void sco_demo_set_codec(uint8_t codec){ if (negotiated_codec == codec) return; negotiated_codec = codec; -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) if (negotiated_codec == HFP_CODEC_MSBC){ sco_demo_init_mSBC(); @@ -437,8 +488,10 @@ void sco_demo_set_codec(uint8_t codec){ } void sco_demo_init(void){ - // status +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE + printf("SCO Demo: Sending and receiving audio via portaudio.\n"); +#endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef HAVE_PORTAUDIO printf("SCO Demo: Sending sine wave, audio output via portaudio.\n"); @@ -453,7 +506,7 @@ void sco_demo_init(void){ printf("SCO Demo: Sending counter value, hexdump received data.\n"); #endif -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD #else hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent @@ -480,7 +533,6 @@ void sco_demo_send(hci_con_handle_t sco_handle){ uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE if (negotiated_codec == HFP_CODEC_MSBC){ - // overwrite sco_payload_length = 24; sco_packet_length = sco_payload_length + 3; @@ -500,6 +552,70 @@ void sco_demo_send(hci_con_handle_t sco_handle){ sco_demo_sine_wave_int16_at_8000_hz(audio_samples_per_packet, (int16_t *) (sco_packet+3)); } #endif + +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE + +#ifdef HAVE_PORTAUDIO + if (negotiated_codec == HFP_CODEC_MSBC){ +#if 1 + log_error("Microphone not supported with mSBC yet"); +#else + // MSBC + + // overwrite + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; + + if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ + log_error("mSBC stream is empty."); + } + hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length); + if (msbc_file_out){ + // log outgoing mSBC data for testing + fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); + } + + // TODO: + sco_demo_fill_audio_frame(); +#endif + } else { + // CVSD + + log_info("send: bytes avail %u, free %u, counter %u", btstack_ring_buffer_bytes_available(&pa_input_ring_buffer), btstack_ring_buffer_bytes_free(&pa_input_ring_buffer), pa_input_counter); + // fill with silence while paused + int bytes_to_copy = sco_payload_length; + if (pa_input_paused){ + if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= CVSD_PA_PREBUFFER_BYTES){ + // resume sending + pa_input_paused = 0; + } + } + + // get data from ringbuffer + uint16_t pos = 0; + if (!pa_input_paused){ + uint32_t bytes_read = 0; + btstack_ring_buffer_read(&pa_input_ring_buffer, sco_packet + 3, bytes_to_copy, &bytes_read); + bytes_to_copy -= bytes_read; + pos += bytes_read; + } + + // fill with 0 if not enough + if (bytes_to_copy){ + memset(sco_packet + 3 + pos, 0, bytes_to_copy); + pa_input_paused = 1; + } + } +#else + // just send '0's + if (negotiated_codec == HFP_CODEC_MSBC){ + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; + } + memset(sco_packet + 3, 0, sco_payload_length); +#endif +#endif + #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII memset(&sco_packet[3], phase++, sco_payload_length); if (phase > 'z') phase = 'a'; @@ -527,6 +643,9 @@ void sco_demo_send(hci_con_handle_t sco_handle){ (void) phase; #endif + // test silence + // memset(sco_packet+3, 0, sco_payload_length); + // set handle + flags little_endian_store_16(sco_packet, 0, sco_handle); // set len @@ -574,20 +693,18 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){ packets = 0; } -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE -#ifdef SCO_WAV_FILENAME +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) switch (negotiated_codec){ case HFP_CODEC_MSBC: - sco_demo_receive_mSBC(packet, size); + sco_demo_receive_mSBC(packet, size); break; case HFP_CODEC_CVSD: - sco_demo_receive_CVSD(packet, size); + sco_demo_receive_CVSD(packet, size); break; default: break; } dump_data = 0; -#endif #endif if (packet[1] & 0x30){ From b025eb5fa2115abdeda723210cb8c448c7e00010 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Jan 2017 17:23:55 +0100 Subject: [PATCH 36/38] sco_util: support mSBC with PortAudio input --- example/sco_demo_util.c | 47 +++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index e0fa32c06..59f5db21e 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -163,7 +163,7 @@ static void sco_demo_sine_wave_int16_at_8000_hz(int num_samples, int16_t * data) } } -static void sco_demo_fill_audio_frame(void){ +static void sco_demo_msbc_fill_sine_audio_frame(void){ if (!hfp_msbc_can_encode_audio_frame_now()) return; int num_samples = hfp_msbc_num_audio_samples_per_frame(); int16_t sample_buffer[num_samples]; @@ -367,7 +367,7 @@ static void sco_demo_init_mSBC(void){ hfp_msbc_init(); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE - sco_demo_fill_audio_frame(); + sco_demo_msbc_fill_sine_audio_frame(); #endif #ifdef SCO_MSBC_IN_FILENAME @@ -546,7 +546,7 @@ void sco_demo_send(hci_con_handle_t sco_handle){ fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); } - sco_demo_fill_audio_frame(); + sco_demo_msbc_fill_sine_audio_frame(); } else { const int audio_samples_per_packet = sco_payload_length / CVSD_BYTES_PER_FRAME; sco_demo_sine_wave_int16_at_8000_hz(audio_samples_per_packet, (int16_t *) (sco_packet+3)); @@ -557,27 +557,42 @@ void sco_demo_send(hci_con_handle_t sco_handle){ #ifdef HAVE_PORTAUDIO if (negotiated_codec == HFP_CODEC_MSBC){ -#if 1 - log_error("Microphone not supported with mSBC yet"); -#else // MSBC // overwrite sco_payload_length = 24; sco_packet_length = sco_payload_length + 3; - if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ - log_error("mSBC stream is empty."); - } - hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length); - if (msbc_file_out){ - // log outgoing mSBC data for testing - fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); + if (pa_input_paused){ + if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= MSBC_PA_PREBUFFER_BYTES){ + // resume sending + pa_input_paused = 0; + } + } + + if (!pa_input_paused){ + int num_samples = hfp_msbc_num_audio_samples_per_frame(); + if (hfp_msbc_can_encode_audio_frame_now() && btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= (num_samples * MSBC_BYTES_PER_FRAME)){ + int16_t sample_buffer[num_samples]; + uint32_t bytes_read; + btstack_ring_buffer_read(&pa_input_ring_buffer, (uint8_t*) sample_buffer, num_samples * MSBC_BYTES_PER_FRAME, &bytes_read); + hfp_msbc_encode_audio_frame(sample_buffer); + num_audio_frames++; + } + } + + if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ + log_error("mSBC stream should not be empty."); + memset(sco_packet + 3, 0, sco_payload_length); + pa_input_paused = 1; + } else { + hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length); + if (msbc_file_out){ + // log outgoing mSBC data for testing + fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); + } } - // TODO: - sco_demo_fill_audio_frame(); -#endif } else { // CVSD From 2c36c7916996e318bb90a89f2d4cf0bae1286925 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 25 Jan 2017 21:13:50 +0100 Subject: [PATCH 37/38] Ring Buffer: speed up by using memcpy --- src/btstack_ring_buffer.c | 68 ++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/src/btstack_ring_buffer.c b/src/btstack_ring_buffer.c index 8663c784a..1c78f6cde 100644 --- a/src/btstack_ring_buffer.c +++ b/src/btstack_ring_buffer.c @@ -42,8 +42,10 @@ #include #include +#include #include "btstack_ring_buffer.h" +#include "btstack_util.h" #define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07 @@ -79,15 +81,27 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat if (btstack_ring_buffer_bytes_free(ring_buffer) < data_length){ return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; } - int count = 0; - while (count < data_length){ - if (ring_buffer->last_written_index < ring_buffer->size - 1){ - ring_buffer->last_written_index++; - } else { - ring_buffer->last_written_index = 0; - } - ring_buffer->storage[ring_buffer->last_written_index] = data[count++]; + + // copy first chunk + unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_written_index; + unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length); + memcpy(&ring_buffer->storage[ring_buffer->last_written_index], data, bytes_to_copy); + data_length -= bytes_to_copy; + data += bytes_to_copy; + + // update last written index + ring_buffer->last_written_index += bytes_to_copy; + if (ring_buffer->last_written_index == ring_buffer->size){ + ring_buffer->last_written_index = 0; } + + // copy second chunk + if (data_length) { + memcpy(&ring_buffer->storage[0], data, data_length); + ring_buffer->last_written_index += data_length; + } + + // mark buffer as full if (ring_buffer->last_written_index == ring_buffer->last_read_index){ ring_buffer->full = 1; } @@ -96,20 +110,30 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat // fetch data_length bytes from ring buffer void btstack_ring_buffer_read(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint32_t data_length, uint32_t * number_of_bytes_read){ - uint32_t count = 0; - while (count < data_length && btstack_ring_buffer_bytes_available(ring_buffer)){ - if (ring_buffer->last_read_index < ring_buffer->last_written_index ) { - ring_buffer->last_read_index++; - } else { - if (ring_buffer->last_read_index < ring_buffer->size - 1){ - ring_buffer->last_read_index++; - } else { - ring_buffer->last_read_index = 0; - } - } - ring_buffer->full = 0; - data[count++] = ring_buffer->storage[ring_buffer->last_read_index]; + // limit data to get and report + data_length = btstack_min(data_length, btstack_ring_buffer_bytes_available(ring_buffer)); + *number_of_bytes_read = data_length; + + // copy first chunk + unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_read_index; + unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length); + memcpy(data, &ring_buffer->storage[ring_buffer->last_read_index], bytes_to_copy); + data_length -= bytes_to_copy; + data += bytes_to_copy; + + // update last read index + ring_buffer->last_read_index += bytes_to_copy; + if (ring_buffer->last_read_index == ring_buffer->size){ + ring_buffer->last_read_index = 0; } - *number_of_bytes_read = count; + + // copy second chunk + if (data_length) { + memcpy(data, &ring_buffer->storage[0], data_length); + ring_buffer->last_read_index += data_length; + } + + // clear full flag + ring_buffer->full = 0; } From d43fe610a00629e57536be68513fb5cf1c8d3a85 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 26 Jan 2017 17:44:52 +0100 Subject: [PATCH 38/38] test/ringbuffer: update test --- test/ring_buffer/btstack_ring_buffer_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/ring_buffer/btstack_ring_buffer_test.c b/test/ring_buffer/btstack_ring_buffer_test.c index eee95e6ca..1074eaeb7 100644 --- a/test/ring_buffer/btstack_ring_buffer_test.c +++ b/test/ring_buffer/btstack_ring_buffer_test.c @@ -1,9 +1,14 @@ #include "CppUTest/TestHarness.h" #include "CppUTest/CommandLineTestRunner.h" #include "btstack_ring_buffer.h" +#include "btstack_util.h" static uint8_t storage[10]; +uint32_t btstack_min(uint32_t a, uint32_t b){ + return a < b ? a : b; +} + TEST_GROUP(RingBuffer){ btstack_ring_buffer_t ring_buffer; int storage_size;