From 45796ff1c6be75ebedee55d6ee108137ec440a44 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Wed, 28 Apr 2021 14:44:38 +0200 Subject: [PATCH] hfp_ag: enhanced voice recognition --- src/classic/hfp.c | 15 +++-- src/classic/hfp.h | 19 ++++-- src/classic/hfp_ag.c | 133 ++++++++++++++++++++++++++++++++++++++++- src/classic/hfp_ag.h | 10 ++++ test/pts/hfp_ag.md | 28 ++++----- test/pts/hfp_ag_test.c | 90 ++++++++++++++++++++++++++-- 6 files changed, 263 insertions(+), 32 deletions(-) diff --git a/src/classic/hfp.c b/src/classic/hfp.c index d42a1c6e3..29155cfe5 100644 --- a/src/classic/hfp.c +++ b/src/classic/hfp.c @@ -436,10 +436,10 @@ static void hfp_emit_enhanced_voice_recognition_text(hfp_connection_t * hfp_conn event[pos++] = HFP_SUBEVENT_ENHANCED_VOICE_RECOGNITION_TEXT; little_endian_store_16(event, pos, hfp_connection->acl_handle); pos += 2; - little_endian_store_16(event, pos, hfp_connection->ag_text_id); + little_endian_store_16(event, pos, hfp_connection->ag_msg.text_id); pos += 2; - event[pos++] = hfp_connection->ag_text_operation; - event[pos++] = hfp_connection->ag_text_type; + event[pos++] = hfp_connection->ag_msg.text_operation; + event[pos++] = hfp_connection->ag_msg.text_type; // length, zero ending uint16_t size = btstack_min(value_length, sizeof(event) - pos - 2 - 1); @@ -1621,17 +1621,16 @@ static void parse_sequence(hfp_connection_t * hfp_connection){ hfp_emit_enhanced_voice_recognition_state(hfp_connection); break; case 2: - hfp_connection->ag_text_id = 0; + hfp_connection->ag_msg.text_id = 0; for (i = 0 ; i < 4; i++){ - hfp_connection->ag_text_id = (hfp_connection->ag_text_id << 4) | nibble_for_char(hfp_connection->line_buffer[i]); + hfp_connection->ag_msg.text_id = (hfp_connection->ag_msg.text_id << 4) | nibble_for_char(hfp_connection->line_buffer[i]); } - printf("text ID 0x%04X\n", hfp_connection->ag_text_id); break; case 3: - hfp_connection->ag_text_operation = btstack_atoi((char *)&hfp_connection->line_buffer[0]); + hfp_connection->ag_msg.text_operation = btstack_atoi((char *)&hfp_connection->line_buffer[0]); break; case 4: - hfp_connection->ag_text_type = btstack_atoi((char *)&hfp_connection->line_buffer[0]); + hfp_connection->ag_msg.text_type = btstack_atoi((char *)&hfp_connection->line_buffer[0]); break; case 5: printf("text%s\n", hfp_connection->line_buffer); diff --git a/src/classic/hfp.h b/src/classic/hfp.h index 387947fb7..4400014ce 100644 --- a/src/classic/hfp.h +++ b/src/classic/hfp.h @@ -206,8 +206,8 @@ typedef enum { HFP_CMD_REDIAL_LAST_NUMBER, HFP_CMD_TURN_OFF_EC_AND_NR, HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION, - HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION, HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION, + HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION, HFP_CMD_HF_REQUEST_PHONE_NUMBER, HFP_CMD_AG_SENT_PHONE_NUMBER, HFP_CMD_TRANSMIT_DTMF_CODES, @@ -316,7 +316,8 @@ typedef enum { } hfp_parser_state_t; typedef enum { - HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT = 0, + HFP_VOICE_RECOGNITION_STATE_AG_READY = 0, + HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT = 1, HFP_VOICE_RECOGNITION_STATE_AG_IS_SENDING_AUDIO_TO_HF = 2, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT = 4 } hfp_voice_recognition_state_t; @@ -397,10 +398,19 @@ typedef enum { HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_OFF, HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_ACTIVATED, + HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_STATUS, + HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_MSG, HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_NEW_SESSION, HFP_VRA_ENHANCED_VOICE_RECOGNITION_ACTIVATED } hfp_voice_recognition_activation_status_t; +typedef struct { + uint16_t text_id; + hfp_text_type_t text_type; + hfp_text_operation_t text_operation; + uint8_t * text; +} hfp_voice_recognition_message_t; + typedef enum { HFP_CODECS_IDLE, HFP_CODECS_RECEIVED_LIST, @@ -672,9 +682,8 @@ typedef struct hfp_connection { hfp_voice_recognition_activation_status_t ag_vra_status; hfp_voice_recognition_state_t ag_vra_state; - uint16_t ag_text_id; - hfp_text_operation_t ag_text_operation; - hfp_text_type_t ag_text_type; + + hfp_voice_recognition_message_t ag_msg; uint8_t clcc_idx; uint8_t clcc_dir; diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c index 7c76aaac6..60d9156f2 100644 --- a/src/classic/hfp_ag.c +++ b/src/classic/hfp_ag.c @@ -509,6 +509,23 @@ static int hfp_ag_send_set_response_and_hold(uint16_t cid, int state){ return hfp_ag_send_cmd_with_space_and_int(cid, HFP_RESPONSE_AND_HOLD, state); } +static int hfp_ag_send_enhanced_voice_recognition_cmd(uint16_t cid, uint8_t status, uint8_t state){ + char buffer[30]; + snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, status, state); + return send_str_over_rfcomm(cid, buffer); +} + +static int hfp_ag_send_enhanced_voice_recognition_msg_cmd(hfp_connection_t *hfp_connection){ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "\r\n%s: 1,%d,%X,%d,%d,\"%s\"\r\n", HFP_ACTIVATE_VOICE_RECOGNITION, + hfp_connection->ag_vra_state, + hfp_connection->ag_msg.text_id, + hfp_connection->ag_msg.text_type, + hfp_connection->ag_msg.text_operation, + hfp_connection->ag_msg.text); + return send_str_over_rfcomm(hfp_connection->rfcomm_cid, buffer); +} + static uint8_t hfp_ag_suggest_codec(hfp_connection_t *hfp_connection){ if (hfp_connection->sco_for_msbc_failed) return HFP_CODEC_CVSD; @@ -741,11 +758,45 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio hfp_release_audio_connection(hfp_connection); } return 1; + + case HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION: + switch (hfp_connection->ag_vra_status){ + case HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_ACTIVATED: + hfp_connection->ag_vra_status = HFP_VRA_ENHANCED_VOICE_RECOGNITION_ACTIVATED; + hfp_ag_send_enhanced_voice_recognition_cmd(hfp_connection->rfcomm_cid, 1, hfp_connection->ag_vra_state); + hfp_ag_setup_audio_connection(hfp_connection); + break; + case HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_STATUS: + hfp_connection->ag_vra_status = HFP_VRA_ENHANCED_VOICE_RECOGNITION_ACTIVATED; + hfp_ag_send_enhanced_voice_recognition_cmd(hfp_connection->rfcomm_cid, 1, hfp_connection->ag_vra_state); + break; + case HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_OFF: + hfp_connection->ag_vra_status = HFP_VRA_VOICE_RECOGNITION_OFF; + hfp_ag_send_enhanced_voice_recognition_cmd(hfp_connection->rfcomm_cid, 0, hfp_connection->ag_vra_state); + hfp_release_audio_connection(hfp_connection); + break; + case HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_MSG: + hfp_connection->ag_vra_status = HFP_VRA_ENHANCED_VOICE_RECOGNITION_ACTIVATED; + hfp_ag_send_enhanced_voice_recognition_msg_cmd(hfp_connection); + break; + default: + return 0; + } + return 1; + case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION: + if (get_bit(hfp_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION)){ hfp_ag_send_ok(hfp_connection->rfcomm_cid); } else { - hfp_ag_send_error(hfp_connection->rfcomm_cid); + switch (hfp_connection->ag_vra_status){ + case HFP_VRA_ENHANCED_VOICE_RECOGNITION_ACTIVATED: + hfp_ag_send_ok(hfp_connection->rfcomm_cid); + break; + default: + hfp_ag_send_error(hfp_connection->rfcomm_cid); + break; + } } return 1; case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING: @@ -1951,6 +2002,11 @@ static void hfp_ag_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uin hfp_generic_status_indicator_t * indicator; switch(hfp_connection->command){ + case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION: + if (hfp_connection->ag_activate_voice_recognition == 0){ + hfp_release_audio_connection(hfp_connection); + } + break; case HFP_CMD_RESPONSE_AND_HOLD_QUERY: if (hfp_ag_response_and_hold_active){ hfp_connection->send_response_and_hold_status = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD + 1; @@ -2466,6 +2522,81 @@ void hfp_ag_activate_voice_recognition(hci_con_handle_t acl_handle, int activate hfp_ag_run_for_context(hfp_connection); } +void hfp_ag_enhanced_voice_recognition_activate(hci_con_handle_t acl_handle){ + if (!get_bit(hfp_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); + if (!hfp_connection){ + log_error("HFP AG: ACL connection 0x%2x is not found.", acl_handle); + return; + } + if (!get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection->command = HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION; + hfp_connection->ag_vra_state = HFP_VOICE_RECOGNITION_STATE_AG_READY; + hfp_connection->ag_vra_status = HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_ACTIVATED; + hfp_ag_run_for_context(hfp_connection); +} + +void hfp_ag_enhanced_voice_recognition_deactivate(hci_con_handle_t acl_handle){ + if (!get_bit(hfp_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); + if (!hfp_connection){ + log_error("HFP AG: ACL connection 0x%2x is not found.", acl_handle); + return; + } + if (!get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection->command = HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION; + hfp_connection->ag_vra_state = HFP_VOICE_RECOGNITION_STATE_AG_READY; + hfp_connection->ag_vra_status = HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_OFF; + hfp_ag_run_for_context(hfp_connection); +} + +void hfp_ag_enhanced_voice_recognition_status(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state){ + if (!get_bit(hfp_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); + if (!hfp_connection){ + log_error("HFP AG: ACL connection 0x%2x is not found.", acl_handle); + return; + } + if (!get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection->command = HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION; + hfp_connection->ag_vra_state = state; + hfp_connection->ag_vra_status = HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_STATUS; + hfp_ag_run_for_context(hfp_connection); +} + +void hfp_ag_enhanced_voice_recognition_starting_sound(hci_con_handle_t acl_handle){ + hfp_ag_enhanced_voice_recognition_status(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_SENDING_AUDIO_TO_HF); +} +void hfp_ag_enhanced_voice_recognition_ready_for_input(hci_con_handle_t acl_handle){ + hfp_ag_enhanced_voice_recognition_status(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT); +} +void hfp_ag_enhanced_voice_recognition_processing_input(hci_con_handle_t acl_handle){ + hfp_ag_enhanced_voice_recognition_status(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT); +} + +void hfp_ag_enhanced_voice_recognition_message(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state, hfp_voice_recognition_message_t msg){ + if (!get_bit(hfp_supported_features, HFP_AGSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); + if (!hfp_connection){ + log_error("HFP AG: ACL connection 0x%2x is not found.", acl_handle); + return; + } + if (!get_bit(hfp_connection->remote_supported_features, HFP_HFSF_VOICE_RECOGNITION_FUNCTION)) return; + + hfp_connection->command = HFP_CMD_AG_ACTIVATE_ENHANCED_VOICE_RECOGNITION; + hfp_connection->ag_vra_state = state; + hfp_connection->ag_vra_status = HFP_VRA_W4_ENHANCED_VOICE_RECOGNITION_MSG; + hfp_connection->ag_msg = msg; + hfp_ag_run_for_context(hfp_connection); +} + void hfp_ag_set_microphone_gain(hci_con_handle_t acl_handle, int gain){ hfp_connection_t * hfp_connection = get_hfp_ag_connection_context_for_acl_handle(acl_handle); if (!hfp_connection){ diff --git a/src/classic/hfp_ag.h b/src/classic/hfp_ag.h index 1cbaba345..6f1674314 100644 --- a/src/classic/hfp_ag.h +++ b/src/classic/hfp_ag.h @@ -245,6 +245,16 @@ void hfp_ag_notify_incoming_call_waiting(hci_con_handle_t acl_handle); */ void hfp_ag_activate_voice_recognition(hci_con_handle_t acl_handle, int activate); +void hfp_ag_enhanced_voice_recognition_activate(hci_con_handle_t acl_handle); + +void hfp_ag_enhanced_voice_recognition_status(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state); +void hfp_ag_enhanced_voice_recognition_starting_sound(hci_con_handle_t acl_handle); +void hfp_ag_enhanced_voice_recognition_ready_for_input(hci_con_handle_t acl_handle); +void hfp_ag_enhanced_voice_recognition_processing_input(hci_con_handle_t acl_handle); + +void hfp_ag_enhanced_voice_recognition_message(hci_con_handle_t acl_handle, hfp_voice_recognition_state_t state, hfp_voice_recognition_message_t msg); +void hfp_ag_enhanced_voice_recognition_deactivate(hci_con_handle_t acl_handle); + /* * @brief Send a phone number back to the HF. * @param bd_addr Bluetooth address of the HF diff --git a/test/pts/hfp_ag.md b/test/pts/hfp_ag.md index 3e8859dda..1bdc72da9 100644 --- a/test/pts/hfp_ag.md +++ b/test/pts/hfp_ag.md @@ -153,23 +153,23 @@ HFP/AG/IIC/BV-03-I: a, f, g, t HFP/AG/HFI/BV-02-I: a, t HFP/AG/HFI/BI-03-I: a, t -HFP/AG/EVR/BV-01-I: -HFP/AG/EVR/BV-02-I: -HFP/AG/EVR/BV-03-I: +HFP/AG/EVR/BV-01-I: a, 1, 2, 5, A +HFP/AG/EVR/BV-02-I: a, 1, 3, 5, A +HFP/AG/EVR/BV-03-I: a, 1, 4, 5, A -HFP/AG/VRT/BV-01-I: -HFP/AG/VRT/BV-02-I: -HFP/AG/VRT/BV-03-I: -HFP/AG/VRT/BV-04-I: -HFP/AG/VRT/BV-05-I: -HFP/AG/VRT/BV-06-I: -HFP/AG/VRT/BV-07-I: -HFP/AG/VRT/BV-08-I: -HFP/AG/VRT/BV-09-I: +HFP/AG/VRT/BV-01-I: a, 1, 6, 5, A +HFP/AG/VRT/BV-02-I: a, 1, 6, 7, 5, A +HFP/AG/VRT/BV-03-I: a, 1, 6, 7, 5, A +HFP/AG/VRT/BV-04-I: a, 1, 6, 8, 5, A +HFP/AG/VRT/BV-05-I: a, 1, 6, 9, 5, A +HFP/AG/VRT/BV-06-I: a, 1, 6, 5, A +HFP/AG/VRT/BV-07-I: a, 1, 7, 5, A +HFP/AG/VRT/BV-08-I: a, 1, *, 5, A +HFP/AG/VRT/BV-09-I: a, 1, @, 5, A -HFP/AG/VRR/BV-02-I: +HFP/AG/VRR/BV-02-I: a, 1, 5, A -HFP/AG/VTA/BV-02-I: +HFP/AG/VTA/BV-02-I: a, 1, 3, 2, 5, A HFP/AG/ATAH/BV-01-I: a, c, B, B, b, C, A diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index 5e5d92058..90b285186 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -70,7 +70,7 @@ const uint8_t rfcomm_channel_nr = 1; const char hfp_ag_service_name[] = "HFP AG Demo"; static bd_addr_t device_addr; -static const char * device_addr_string = "00:1B:DC:08:E2:72"; +static const char * device_addr_string = "00:1B:DC:08:E2:5C"; // configuration static const int wide_band_speech = 1; @@ -164,7 +164,9 @@ static void show_usage(void){ printf("l - Clear last number | L - Set last number to 7654321\n"); printf("m - simulate incoming call from 7654321\n"); printf("M - simulate outgoing call to 1234567\n"); - printf("n - Disable Voice Regocnition | N - Enable Voice Recognition\n"); + printf("n - Disable Voice Recognition | N - Enable Voice Recognition\n"); + printf("z - Disable Enhanced Voice Recognition | Z - Enable Enhanced Voice Recognition\n"); + printf("o - Set speaker volume to 0 (minimum) | O - Set speaker volume to 9 (default)\n"); printf("p - Set speaker volume to 12 (higher) | P - Set speaker volume to 15 (maximum)\n"); printf("q - Set microphone gain to 0 (minimum) | Q - Set microphone gain to 9 (default)\n"); @@ -327,6 +329,86 @@ static void stdin_process(char cmd){ printf("Enable Voice Recognition\n"); hfp_ag_activate_voice_recognition(acl_handle, 1); break; + + case '1': + printf("Enable Enhanced Voice Recognition\n"); + hfp_ag_enhanced_voice_recognition_activate(acl_handle); + break; + case '2': + printf("EVR Status ready_for_input\n"); + hfp_ag_enhanced_voice_recognition_ready_for_input(acl_handle); + break; + case '3': + printf("EVR Send audio outputt\n"); + hfp_ag_enhanced_voice_recognition_starting_sound(acl_handle); + break; + case '4': + printf("EVR Processing Input\n"); + hfp_ag_enhanced_voice_recognition_processing_input(acl_handle); + break; + + case '5': + printf("Disable Enhanced Voice Recognition\n"); + hfp_ag_enhanced_voice_recognition_deactivate(acl_handle); + break; + + case '6':{ + hfp_voice_recognition_message_t msg = { + 0xAB13, 0, 1, (uint8_t *) "test" + }; + printf("EVR Msg, Status ready_for_input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT, msg); + break; + } + + case '7':{ + // changed type, change iD + hfp_voice_recognition_message_t msg = { + 0xAB14, 1, 1, (uint8_t *) "test" + }; + printf("EVR Msg Processing Input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT, msg); + break; + } + + case '8':{ + // replace op, leave id and type + hfp_voice_recognition_message_t msg = { + 0xAB13, 0, 2, (uint8_t *) "test" + }; + printf("EVR Msg Processing Input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT, msg); + break; + } + + case '9':{ + // replace op, leave id and type + hfp_voice_recognition_message_t msg = { + 0xAB13, 0, 3, (uint8_t *) "test" + }; + printf("EVR Msg Processing Input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_IS_PROCESSING_AUDIO_INPUT, msg); + break; + } + + case '*':{ + hfp_voice_recognition_message_t msg = { + 0xAB13, 2, 1, (uint8_t *) "test" + }; + printf("EVR Msg, Status ready_for_input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT, msg); + break; + } + + case '@':{ + hfp_voice_recognition_message_t msg = { + 0xAB13, 3, 1, (uint8_t *) "test" + }; + printf("EVR Msg, Status ready_for_input\n"); + hfp_ag_enhanced_voice_recognition_message(acl_handle, HFP_VOICE_RECOGNITION_STATE_AG_READY_TO_ACCEPT_AUDIO_INPUT, msg); + break; + } + case 'o': log_info("USER:\'%c\'", cmd); printf("Set speaker gain to 0 (minimum)\n"); @@ -595,8 +677,8 @@ int btstack_main(int argc, const char * argv[]){ (1<