aboutsummaryrefslogtreecommitdiff
path: root/application/audio/gl_audio.c
diff options
context:
space:
mode:
authorChunyi Chen <cchen@atmosic.com>2023-05-12 17:49:28 +0800
committerChunyi Chen <cchen@atmosic.com>2023-05-16 11:49:02 +0800
commit208e1139d7a401b29d397e274d835e68b9b48201 (patch)
tree59fda1c7d4bd051d0d2fbf24abc160b2859f40ce /application/audio/gl_audio.c
parentc603c2df935a82347e2cd4fbd27ba80d05c3793d (diff)
downloadatmosic-208e1139d7a401b29d397e274d835e68b9b48201.tar.gz
Android TV Ref RCU SDKHEADmastermain
Check in the RCU SDK based on Atmosic ATM2231 chipset, solution provided by Atmosic Technologies RCU SDK Version: 1.0 -) Support IR over BLE service -) Support Voice over BLE service -) Support NEC IR code override -) Support RCU Wake Up service Test: make run_all USE_LIB=1 CFG_ATVRC=1 USE_BD_ADDR:="AABBCCDDEEFF" BYPASS_INCLUSIVE_LANGUAGE_REASON=existing methods Change-Id: I9d7db82ff40f5552e8896b5356886982feb95ade
Diffstat (limited to 'application/audio/gl_audio.c')
-rw-r--r--application/audio/gl_audio.c1277
1 files changed, 1277 insertions, 0 deletions
diff --git a/application/audio/gl_audio.c b/application/audio/gl_audio.c
new file mode 100644
index 0000000..45534c4
--- /dev/null
+++ b/application/audio/gl_audio.c
@@ -0,0 +1,1277 @@
+/******************************************************************************
+ * @file gl_audio.c
+ *
+ * @brief for TLSR chips
+ *
+ * @author public@telink-semi.com;
+ * @date Sep. 30, 2010
+ *
+ * @attention
+ *
+ * Copyright (C) 2019-2020 Telink Semiconductor (Shanghai) Co., Ltd.
+ * Copyright (C) Atmosic 2022
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************/
+
+#ifdef CFG_ATM_SDK
+#include "refdesignrcu.h"
+#include "bridge_att.h"
+#include "bridge_audio.h"
+#include "gl_audio.h"
+#include "vendor/827x_ble_remote/app_att.h"
+#else
+#include "tl_common.h"
+#include "drivers.h"
+#include "audio_config.h"
+#include "adpcm.h"
+#include "gl_audio.h"
+#include "tl_audio.h"
+#endif
+
+#if defined(CFG_ATM_SDK) || (TL_AUDIO_MODE == TL_AUDIO_RCU_ADPCM_GATT_GOOGLE)
+
+#ifdef CFG_ATM_SDK
+#include "stack/ble/ble_format.h"
+#else
+#include "stack/ble/ble.h"
+#endif
+
+extern void ui_enable_mic(int en);
+#ifndef CFG_ATM_SDK
+extern u8 buffer_mic_pkt_wptr;
+extern u8 buffer_mic_pkt_rptr;
+#endif
+
+u8 audio_start_reason = 0; //on-request(0x00),PTT(0x01),HTT(0x03) etc.
+u8 audio_stop_reason = 0;
+
+
+u8 app_audio_send_index = 0;
+u8 app_audio_sync_serial = 0; //Used for google voice v0.4 sync cmd
+
+u16 mic_open_error_code = 0;
+
+_attribute_data_retention_ u8 mic_open_mode = CAPTURE_MODE; //0x00:playback mode 0x01:capture mode
+
+_attribute_data_retention_ u8 google_voice_ver = 4; //4: ver 0.4e 10:ver 1.0
+_attribute_data_retention_ u8 used_assistant_model = REASON_MICOPEN;
+
+//Google voice v1.0 control parameter
+_attribute_data_retention_ u8 auido_frame_size = 20; //Byte. this variable is updated after sending CAPS_RESP
+_attribute_data_retention_ u8 stream_id = 0; //The identifier of an audio stream to extend.
+_attribute_data_retention_ u8 flag_active_mic_open = 0; //Enable/disable mic open command
+
+_attribute_data_retention_ u16 google_voice_ctl = 0;
+
+_attribute_data_retention_ u16 atv_char_ctl_ccc = 0;
+_attribute_data_retention_ u16 atv_char_rx_ccc = 0;
+
+_attribute_data_retention_ u16 google_voice_codec_used = CODEC_USED_16K;
+_attribute_data_retention_ u16 google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN;
+_attribute_data_retention_ u16 google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE;
+
+_attribute_data_retention_ u32 app_audio_start_delay = 0;
+_attribute_data_retention_ u32 app_active_remote_timer = 0; //Wait mic open cmd after send START_SEARCH
+_attribute_data_retention_ u32 app_audio_transfer_timer = 0; //Used for google voice timeout
+_attribute_data_retention_ u32 g_delay_send_audio_stop = 0;
+extern u8 device_in_connection_state;
+
+/**
+ * @brief initialize packet parameters according to the version of google voice
+ * @param none
+ * @return none
+ */
+void google_voice_para_init(){
+
+ if(google_voice_ver == 4){ //ver0.4e
+ google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN; //128+6+2
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE; //
+
+ }else if(google_voice_ver == 10){ //ver1.0
+ google_voice_packet_length = VOICE_V1P0_ADPCM_PACKET_LEN; //
+ google_voice_pcm_sample_packet = VOICE_V1P0_ADPCM_UNIT_SIZE; //
+
+ google_voice_codec_used = CODEC_USED_16K;
+ }else{//set to ver0.4e
+ google_voice_ver = 4;
+
+ google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN; //128+6+2
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE; //
+ }
+}
+
+/**
+ * @brief initialize voice control parameters
+ * @param none
+ * @return none
+ */
+void app_audio_parameter_init(){
+
+ app_audio_send_index = 0;
+
+ //v0.4
+ app_audio_sync_serial = 0;
+ //app_audio_send_index = 0;
+
+#ifndef CFG_ATM_SDK
+ //adpcm parameter
+ adpcm_predict = 0;
+ adpcm_predict_idx = 1;
+ //adpcm_predict_idx = 0;
+
+ adpcm_sequence_num = 0;
+
+ //adpcm buffer
+ buffer_mic_pkt_wptr = 0;
+ buffer_mic_pkt_rptr = 0;
+#endif
+
+// previous_sampling_rate = U16_LO(CODEC_USED_16K); //default sampling rate:16k
+// previous_seq_no = 0xffff;
+}
+
+/**
+ * @brief this function is used to save the assistant model.
+ * @param[in] model:the model type
+ * @return none
+ */
+void set_assistant_model(u8 model){
+ used_assistant_model = model;
+}
+
+/**
+ * @brief this function is used to read the assistant model.
+ * @param none
+ * @return the model type
+ */
+u8 read_assistant_model(){
+ return used_assistant_model;
+}
+
+/**
+ * @brief this function is used to enable the mic open command.
+ * @param none
+ * @return none
+ */
+void active_mic_open(){
+ flag_active_mic_open = 1;
+ app_active_remote_timer = clock_time()|1;
+}
+
+/**
+ * @brief this function is used to read the audio frame size.
+ * @param none
+ * @return the audio frame size
+ */
+u8 read_audio_frame_size(){
+
+ return auido_frame_size;
+}
+
+/**
+ * @brief this function is used to set the audio frame size.
+ * @param[in] frame_size:The frame size(data length exchange)
+ * @return none
+ */
+void set_audio_frame_size(u8 frame_size){
+ auido_frame_size = 20;
+
+ if(frame_size >= 123){
+ auido_frame_size = 120;
+ }else if(frame_size >= 63){
+ auido_frame_size = 60;
+ }else if(frame_size >= 43){
+ auido_frame_size = 40;
+ }else if(frame_size >= 33){
+ auido_frame_size = 30;
+ }else{
+ auido_frame_size = 20;
+ }
+}
+
+/**
+ * @brief initial settings before sending voice.
+ * @param none
+ * @return none
+ */
+void google_mic_enable(){
+
+ app_audio_parameter_init();
+ ui_enable_mic(1);
+
+ //app_active_remote_timer = 0;
+ app_audio_transfer_timer = clock_time() | 1;
+
+ google_voice_ctl |= FLAG_NOTIFY_AUDIO_DATA;
+ google_voice_ctl |= FLAG_GOOGLE_AUDIO_FIRST_SYNC;
+
+ google_voice_ctl |= FLAG_DELAY_NOTIFY;
+ app_audio_start_delay = clock_time() | 1;
+}
+
+/**
+ * @brief set FLAG_GOOGLE_SEARCH bit for send search key(on-request)
+ * @param none
+ * @return none
+ */
+#define PREVENT_TIME 600 //ms
+_attribute_data_retention_ u32 prevent_repeat_trigger_on_request_tick = 0;
+void google_voice_on_request(){
+
+ //Prevent repeated triggers(2020.12.15)
+ if(prevent_repeat_trigger_on_request_tick && (!clock_time_exceed(prevent_repeat_trigger_on_request_tick,PREVENT_TIME*1000))) return;
+ prevent_repeat_trigger_on_request_tick = clock_time()|1;
+ google_voice_ctl |= FLAG_GOOGLE_SEARCH;
+}
+
+/**
+ * @brief set FLAG_GOOGLE_DPAD_SELECT bit for send DPAD cmd.(ver 0.4e 3.2.3)
+ * @param none
+ * @return none
+ */
+void google_voice_dpad_select(){
+ if(google_voice_ver == 4){
+ google_voice_ctl |= FLAG_GOOGLE_DPAD_SELECT;
+ }
+}
+
+void google_init_audio_parameter(void)
+{
+ u8 sendBuff[20] = {0};
+
+ stream_id++; //increase stream_id
+ if(stream_id > 0x80) stream_id = 1; //stream_id range 0x01~0x80
+ google_voice_codec_used = CODEC_USED_16K;
+ //init audio parameter
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ sendBuff[1] = audio_start_reason;
+ sendBuff[2] = U16_LO(google_voice_codec_used);
+ sendBuff[3] = stream_id;
+
+ bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 4);
+
+ google_mic_enable();
+
+}
+
+/**
+ * @brief this function is used to start the voice.
+ * @param[in] start_reason:audio start reason
+ * @return none
+ */
+u8 app_audio_key_start(u8 start_reason){
+
+ printf("app_audio_key_start\n");
+ if(!atv_char_rx_ccc) return 1; //ATVV_CHAR_AUDIO ccc is disabled
+ if(google_voice_ver != 10) return 2; //google voice version error
+
+ switch(start_reason){
+ case REASON_PTT:
+ {
+ printf("audio_start_reason = REASON_PTT\n");
+ audio_start_reason = REASON_PTT;
+ google_init_audio_parameter();
+ break;
+ }
+ case REASON_HTT:
+ {
+ if(start_reason == REASON_HTT)
+ {
+ audio_start_reason = REASON_HTT;
+ printf("audio_start_reason = REASON_HTT\n");
+ }
+ google_init_audio_parameter();
+ break;
+ }
+ case REASON_MICOPEN:
+ {
+ printf("audio_start_reason = REASON_MICOPEN\n");
+
+ //send google voice CHAR_CTL search
+ google_voice_on_request();
+ active_mic_open();
+
+ break;
+ }
+ default:
+ {
+ return 4; //not supported
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @brief this function is used to stop the voice.
+ * @param[in] reason:Audio stop reason
+ * @return none
+ */
+u8 app_audio_key_stop(u8 reason){
+ ui_enable_mic(0);
+ app_active_remote_timer = 0;
+ app_audio_transfer_timer = 0;
+
+ audio_stop_reason = reason;
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+
+ return 0;
+}
+
+/**
+ * @brief this function is used to stop the voice by HTT.
+ * @param none
+ * @return 0: ready to stop voice 1:voice start reason is not HTT
+ */
+u8 app_audio_key_stop_by_htt(){
+ if(audio_start_reason != REASON_HTT) return 1;
+
+ app_audio_key_stop(REASON_RELEASE_HTT);
+
+ return 0;
+}
+
+/**
+ * @brief according to the version of google voice, start the corresponding process
+ * @param none
+ * @return none
+ */
+void google_voice_start(){
+
+ if(google_voice_ver == 10){
+ app_audio_key_start(read_assistant_model());
+
+ }else{
+ google_voice_on_request();
+ }
+}
+
+/**
+ * @brief this function is used to stop the voice.
+ * @param none
+ * @return none
+ */
+void google_voice_stop(){
+ if(audio_start_reason == REASON_HTT)
+ app_audio_key_stop_by_htt();
+}
+
+/**
+ * @brief google voice 0.4 processing function.
+ * @param[in] pw:received RF data
+ * @return none
+ */
+void google_voice_v0p4(rf_packet_att_data_t *pw){
+ u8 sendBuff[20] = {0};
+
+ u8 cmd = pw->dat[0];
+ if(cmd == AUDIO_GOOGLE_CMD_OPEN)
+ {
+ //v0.4e proc
+
+ u8 len = pw->l2cap-3;
+ u16 ATV_codec_used = 2;
+ printf("received pkt len:%d\n",len);
+
+ array_printf(pw->dat,len);
+
+ if(len == 3){
+ ATV_codec_used = (pw->dat[1]<<8) + pw->dat[2];
+ }else{
+ ATV_codec_used = 2;
+ }
+
+
+ printf("received v0.4 mic open:%d\n",ATV_codec_used);
+ switch(ATV_codec_used){
+ case CODEC_USED_8K:
+ {
+ printf("ADPCM, 8khz/16bit\n");
+
+ google_voice_codec_used = CODEC_USED_8K; //8k
+ google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN ; // 2 is just for 4*n
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE*2; //512.
+
+
+ app_audio_send_index = 0;
+
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 1);
+
+ google_mic_enable();
+
+ break;
+ }
+ case CODEC_USED_16K:
+ {
+ printf("ADPCM, 16khz/16bit\n");
+
+ google_voice_codec_used = CODEC_USED_16K;
+ google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN ; // 2 is just for 4*n
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE; //256
+
+ app_audio_send_index = 0;
+
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 1);
+
+ google_mic_enable();
+
+ break;
+ }
+ case CODEC_USED_OPUS:
+ //printf("Opus(future),current sdk do Not support\n");
+ default:
+ {
+ printf("only support 8k voice\n");
+ printf("default ADPCM, 8khz/16bit\n");
+
+ google_voice_codec_used = CODEC_USED_8K; //8k
+ google_voice_packet_length = VOICE_V0P4_ADPCM_PACKET_LEN ; // 2 is just for 4*n
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE*2; //512.
+
+
+ app_audio_send_index = 0;
+
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 1);
+
+ google_mic_enable();
+ //err parameter
+ //printf("default\n");
+// sendBuff[0] = ATV_MIC_CHAR_CTL_MIC_OPEN_ERROR;
+// sendBuff[1] = U16_HI(ERROR_INVALIED_CODEC);
+// sendBuff[2] = U16_LO(ERROR_INVALIED_CODEC);
+// bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 3);
+ break;
+ }
+ }
+ printf("google_voice_pcm_sample_packet:%d\n",google_voice_pcm_sample_packet);
+ printf("google_voice_packet_length:%d\n",google_voice_packet_length);
+ }else if(cmd == AUDIO_GOOGLE_CMD_CLOSE){
+ printf("AUDIO_GOOGLE_CMD_CLOSE\n");
+ //u8 close_stream_id = pw->dat[1];
+
+ ui_enable_mic(0);
+
+ //send audio close cmd
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+ }
+}
+
+/**
+ * @brief verify stream id.
+ * @param[in] received stream id from host
+ * @return 0: OK 1: audio_start_reason is not REASON_MICOPEN
+ * 2: stream id out of range 3: the received id is inconsistent with the current id
+ */
+u8 verify_stream_id(u8 id){
+
+ switch(id){
+ case 0x00: // Check if start reason is on-request.
+ {
+ if(audio_start_reason != REASON_MICOPEN) return 1;
+ break;
+ }
+ case 0xff: //any ongoing audio stream.
+ {
+ break;
+ }
+ default: //when start reason is htt or ptt,check the stream_id.
+ {
+ if(id > 0x80) return 2; //audio stream id range: 0x01 ~ 0x80
+ if(id != stream_id) return 3;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief google voice 1.0 processing function.
+ * @param[in] received RF data
+ * @return none
+ */
+void google_voice_v1p0(rf_packet_att_data_t *pw){
+ if(pw->dat[0] == AUDIO_GOOGLE_CMD_OPEN){
+ printf("AUDIO_GOOGLE_CMD_OPEN\n");
+ //check ccc
+ if(!atv_char_rx_ccc){
+ printf("atv_char_rx_ccc is disabled\n");
+
+ //notifications are disabled while audio data transfer is in progress
+ google_voice_ctl |= FLAG_GOOGLE_OPEN_ERROR;
+ mic_open_error_code = ERROR_CCC_NOT_ENABLED;
+ return;
+ }
+
+ if(!flag_active_mic_open){
+ printf("mic is not activated\n");
+ }
+
+ u8 mic_mode = pw->dat[1];
+ if(google_voice_ctl & FLAG_NOTIFY_AUDIO_DATA){ //ongoing audio stream
+ printf("ongoing audio stream_audio_start_reason:%d\n",audio_start_reason);
+ switch(audio_start_reason){
+ case REASON_PTT:
+ case REASON_HTT:
+ {
+ //send open error.microphone is open because of ongoing PTT/HTT interaction
+ mic_open_error_code = ERROR_ONGOING_PTT_HTT;
+ google_voice_ctl |= FLAG_GOOGLE_OPEN_ERROR;
+ break;
+ }
+ case REASON_MICOPEN:
+ default:
+ {
+ //send audio stop and restart audio or ignore cmd
+ //Prevent repeated triggers(2020.12.15)
+ ui_enable_mic(0);
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+ audio_stop_reason = REASON_UPCOMING_AUDIO_START;
+ audio_start_reason = REASON_MICOPEN;
+ break;
+ }
+ }
+ }else{
+ printf("new audio. mic mode:0x%x \n",mic_mode);
+ //start audio
+ switch(mic_mode){
+ case PLAYBACK_MODE:
+ {
+ mic_open_mode = PLAYBACK_MODE;
+ break;
+ }
+ case CAPTURE_MODE:
+// mic_open_mode = CAPTURE_MODE;
+// break;
+ default:
+ {
+ mic_open_mode = CAPTURE_MODE;
+ break;
+ }
+ }
+ //SEND AUDIO START
+
+ audio_start_reason = REASON_MICOPEN;
+ google_voice_codec_used = CODEC_USED_16K;
+
+ u8 sendBuff[4] = {0};
+ //init audio parameter
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ sendBuff[1] = audio_start_reason;
+ sendBuff[2] = U16_LO(google_voice_codec_used);
+ sendBuff[3] = 0x00; //an audio stream which was initiated by the MIC_OPEN command
+
+ printf("sendBuff:");
+ array_printf(sendBuff,4);
+ bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 4);
+
+ google_mic_enable();
+ }
+ }else if(pw->dat[0] == AUDIO_GOOGLE_CMD_CLOSE){
+ printf("AUDIO_GOOGLE_CMD_CLOSE\n");
+ u8 close_stream_id = pw->dat[1];
+
+ if(verify_stream_id(close_stream_id)) return;
+
+ //close ongoing audio stream
+ ui_enable_mic(0);
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+ audio_stop_reason = REASON_MICCLOSE;
+
+ }else if(pw->dat[0] == AUDIO_GOOGLE_CMD_EXTEND){
+ printf("AUDIO_GOOGLE_CMD_EXTEND\n");
+ u8 close_stream_id = pw->dat[1];
+
+ if(verify_stream_id(close_stream_id)) return;
+
+ //fresh the timeout
+ app_audio_transfer_timer = clock_time() | 1;
+ }
+}
+
+/**
+ * @brief google voice processing function.
+ * @param[in] p: received RF data
+ * @return none
+ */
+int app_auido_google_callback(void* p){
+ //u8 sendBuff[20] = {0};
+ rf_packet_att_data_t *pw = (rf_packet_att_data_t *)p;
+
+ u8 google_command = pw->dat[0];
+
+ printf("google_command:%x\n",google_command);
+
+ if(google_command == AUDIO_GOOGLE_CMD_CAP){
+ u16 voice_ver = (pw->dat[1]<<8) + pw->dat[2];
+ printf("STB google voice ver: 0x%x\n",voice_ver);
+ if(ENABLE_GOOGLE_VOICE_1P0 && voice_ver == GOOGLE_VOICE_VERSION_1P0){
+ printf("GOOGLE_VOICE_VERSION_1P0\n");
+ array_printf(pw->dat,pw->l2cap-3);
+
+ google_voice_ver = 10;
+ //google_voice_para_init();
+ //u8 supported_assistant_models = pw->dat[5];
+ printf("supported_assistant_models:0x%x\n",pw->dat[5]);
+ set_assistant_model(pw->dat[5]);
+ }else{
+ printf("GOOGLE_VOICE_VERSION_0P4\n");
+
+ google_voice_ver = 0x04;
+ }
+ google_voice_ctl |= FLAG_GOOGLE_CAPS_RESP;
+ google_voice_para_init();
+#ifdef CFG_ATM_SDK
+ bridge_audio_ready_ind(atv_char_ctl_ccc && atv_char_rx_ccc);
+#endif
+ }else{
+ if(google_voice_ver == 10){
+ google_voice_v1p0(pw);
+ }else{
+ google_voice_v0p4(pw);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief google command process for google voice v0.4.
+ * @param none
+ * @return none
+ */
+void google_cmd_v0p4_proc(){
+
+ if(google_voice_ctl & FLAG_GOOGLE_DPAD_SELECT){
+ //audio stop. google voice auido end
+ ble_sts_t ret;
+ u8 sendData = ATV_MIC_CHAR_CTL_DPAD_SELECT;
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, &sendData, 1);
+// if(ret != BLE_SUCCESS) return;
+// google_voice_ctl &= ~FLAG_GOOGLE_DPAD_SELECT;
+ if(ret == BLE_SUCCESS)
+ google_voice_ctl &= ~FLAG_GOOGLE_DPAD_SELECT;
+ }
+
+ if(google_voice_ctl & FLAG_AUDIO_CLOSE){
+ //audio stop. google voice auido end
+ printf("google_cmd_v0p4_proc_close\r\n");
+ ble_sts_t ret;
+ u8 sendData = ATV_MIC_CHAR_CTL_AUDIO_STOP;
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, &sendData, 1);
+
+ if(ret == BLE_SUCCESS){
+ google_voice_ctl = 0;
+ ui_enable_mic(0);
+ }
+ }
+
+ if(google_voice_ctl & FLAG_GOOGLE_AUDIO_SYNC){
+ //audio sync
+#ifdef CFG_ATM_SDK
+ u8 *p = bridge_audio_read_frame();
+#else
+ int *p = mic_encoder_data_buffer ();
+#endif
+ u16 frame_no;
+ if(p){
+ frame_no = (p[0] <<8) + p[1];
+ }else{
+#ifdef CFG_ATM_SDK
+ ASSERT_ERR(0);
+ return;
+#else
+ frame_no = adpcm_sequence_num;
+#endif
+ }
+
+ ble_sts_t ret;
+ u8 sendBuff[3] = {0};
+ sendBuff[0] = 0x0A;
+ sendBuff[1] = U16_HI(frame_no);
+ sendBuff[2] = U16_LO(frame_no);
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 3);
+
+ if(ret == BLE_SUCCESS)
+ google_voice_ctl &= ~FLAG_GOOGLE_AUDIO_SYNC;
+
+ }
+
+ if(google_voice_ctl & FLAG_GOOGLE_OPEN_ERROR){
+ //mic open error.
+ //Timeout1.This is a timeout on the Android TV device.
+ ble_sts_t ret;
+ u8 sendBuff[3] = {0};
+ sendBuff[0] = ATV_MIC_CHAR_CTL_MIC_OPEN_ERROR;
+ sendBuff[1] = U16_HI(ERROR_INVALIED_CODEC);
+ sendBuff[2] = U16_LO(ERROR_INVALIED_CODEC);
+
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 3);
+ if(ret== BLE_SUCCESS){
+ google_voice_ctl = 0;
+ ui_enable_mic(0);
+ }
+ }
+}
+
+/**
+ * @brief google command process for google voice v1.0.
+ * @param none
+ * @return none
+ */
+
+
+void google_cmd_v1p0_proc(){
+
+ if(!atv_char_rx_ccc && (google_voice_ctl & FLAG_NOTIFY_AUDIO_DATA)){
+ //notifications are disabled while audio data transfer is in progress
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+ audio_stop_reason = REASON_DISABLE_CCC;
+ }
+
+ //audio stop
+ if(google_voice_ctl & FLAG_AUDIO_CLOSE){
+ //audio stop. google voice auido end
+ //printf("google_cmd_v1p0_proc_close\r\n");
+
+ u8 send_audio_stop = 1;
+ if(REASON_RELEASE_HTT == audio_stop_reason){
+ // If the voice is stopped by HTT, wait for adpcm buffer to be empty before sending the stop command
+ send_audio_stop = 0;
+
+ // If the adpcm data is not sent within 1 second, send the stop command
+ if(clock_time_exceed(g_delay_send_audio_stop,1000000)){
+ send_audio_stop = 1;
+ }
+
+ // adpcm buffer is empty
+#ifdef CFG_ATM_SDK
+ u8 *p = bridge_audio_read_frame();
+#else
+ int *p = mic_encoder_data_buffer ();
+#endif
+ if(!p){
+ send_audio_stop = 1;
+ }
+ }
+
+ if(send_audio_stop){
+ ble_sts_t ret;
+ u8 sendBuff[2] = {0};
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_STOP;
+ sendBuff[1] = audio_stop_reason;
+
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 2);
+ printf("google_cmd_v1p0_proc_close ret=%x\r\n",ret);
+ if(ret == BLE_SUCCESS){
+ g_delay_send_audio_stop = 0;
+ printf("ATV_MIC_CHAR_CTL_AUDIO_STOP:0x%x\n",audio_stop_reason);
+ google_voice_ctl = 0;
+ ui_enable_mic(0);
+
+ if(audio_stop_reason == REASON_UPCOMING_AUDIO_START){
+ google_voice_ctl |= FLAG_GOOGLE_AUDIO_START;
+ }
+ }
+ }
+ }
+
+ //audio start
+ if(google_voice_ctl & FLAG_GOOGLE_AUDIO_START){
+ //audio start. triggered by an upcoming AUDIO_START command;
+
+ google_voice_codec_used = CODEC_USED_16K;
+
+ ble_sts_t ret;
+ u8 sendBuff[4] = {0};
+ sendBuff[0] = ATV_MIC_CHAR_CTL_AUDIO_START;
+ sendBuff[1] = audio_start_reason;
+ sendBuff[2] = U16_LO(google_voice_codec_used);
+ sendBuff[3] = 0x00; //an audio stream which was initiated by the MIC_OPEN command
+
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 4);
+
+ if(ret == BLE_SUCCESS){
+ google_voice_ctl &= ~FLAG_GOOGLE_AUDIO_START;
+ //start send audio data
+ google_mic_enable();
+ }
+ }
+
+ //mic open error
+ if(google_voice_ctl & FLAG_GOOGLE_OPEN_ERROR){
+ //mic open error.
+ //Timeout1.This is a timeout on the Android TV device.
+ printf("google_voice_ctl & FLAG_GOOGLE_OPEN_ERROR:%x",mic_open_error_code);
+
+ ble_sts_t ret;
+ u8 sendBuff[3] = {0};
+ sendBuff[0] = ATV_MIC_CHAR_CTL_MIC_OPEN_ERROR;
+ sendBuff[1] = U16_HI(mic_open_error_code);
+ sendBuff[2] = U16_LO(mic_open_error_code);
+
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 3);
+ if(ret == BLE_SUCCESS){
+ google_voice_ctl = 0;
+ ui_enable_mic(0);
+ }
+ }
+}
+
+/**
+ * @brief notify CAPS_RESP packet.
+ * @param none
+ * @return none
+ */
+void google_get_rsp(void){
+
+ u8 sendBuff[9] = {0};
+ if(google_voice_ver == 10){
+
+ sendBuff[0] = ATV_MIC_CHAR_CTL_CAPS_RESP;
+ sendBuff[1] = U16_HI(GOOGLE_VOICE_VERSION_1P0); //major version
+ sendBuff[2] = U16_LO(GOOGLE_VOICE_VERSION_1P0); //minor version
+ sendBuff[3] = CODEC_SUPPORTED_8K16K; //codecs supported (1)
+ sendBuff[4] = read_assistant_model(); //set assistant interaction model. on-request,ptt,htt
+ sendBuff[5] = 0x00; //audio frame size (2)
+ sendBuff[6] = read_audio_frame_size(); //audio frame size (2)
+ //sendBuff[7] = (read_audio_frame_size() == 20)?0:1; //extra configuration (1)
+ extern u8 app_mtu_size;
+ sendBuff[7] = ((read_audio_frame_size()+3) > app_mtu_size)?1:0; //extra configuration (1)
+ sendBuff[8] = 0x00; //reserved(1)
+
+ }else{
+ sendBuff[0] = ATV_MIC_CHAR_CTL_CAPS_RESP;
+ sendBuff[1] = U16_HI(GOOGLE_VOICE_VERSION_0P4); //major version
+ sendBuff[2] = U16_LO(GOOGLE_VOICE_VERSION_0P4); //minor version
+ sendBuff[3] = 0x00; //codecs_supported high
+ sendBuff[4] = CODEC_SUPPORTED_8K; //CODEC_SUPPORTED_8K16K;
+ sendBuff[5] = 0x00; //frame lengths high
+ sendBuff[6] = 0x86; //frame lengths low -- 134Byts
+ sendBuff[7] = 0x00; //packet lengths high
+ sendBuff[8] = 0x14; //packet lengths low -- 20 Bytes
+ }
+ ble_sts_t ret;
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 9);
+ printf("google_get_rsp_ret=%x\r\n",ret);
+ if(ret == BLE_SUCCESS){
+ if(google_voice_ver == 10)
+ {
+ printf("read_audio_frame_size():%d\n",sendBuff[6]);
+ }
+ printf("send FLAG_GOOGLE_CAPS_RESP_ver:%d\n",google_voice_ver);
+ google_voice_ctl &= ~FLAG_GOOGLE_CAPS_RESP;
+ }
+}
+
+_attribute_data_retention_ u8 notify_get_rsp_en = 0;
+_attribute_data_retention_ u32 notify_get_rsp_tick = 0;
+_attribute_data_retention_ u32 notify_get_rsp_delay_time = 6000000;
+/**
+ * @brief google command process at app_audio_task.
+ * @param none
+ * @return none
+ */
+void google_cmd_proc(){
+ ble_sts_t ret;
+
+// if(app_active_remote_timer && clock_time_exceed(app_active_remote_timer, APP_AUDIO_GOOGLE_TIMEOUT1)){
+// //wait mic open cmd
+// printf("app_active_remote_timer timeout\n");
+// app_active_remote_timer = 0;
+//
+// mic_open_error_code = ERROR_RCU_NOT_ACTIVE; //google voice v1.0
+// google_voice_ctl = 0;
+// google_voice_ctl |= FLAG_GOOGLE_OPEN_ERROR;
+// }
+
+ if(app_audio_transfer_timer && clock_time_exceed(app_audio_transfer_timer, APP_AUDIO_GOOGLE_TIMEOUT2)){
+ printf("app_audio_transfer_timer timeout\n");
+ ui_enable_mic(0); //app_audio_disable();
+
+ app_audio_transfer_timer = 0;
+
+ if(google_voice_ctl & FLAG_NOTIFY_AUDIO_DATA){
+ audio_stop_reason = REASON_TIMEOUT;
+ google_voice_ctl |= FLAG_AUDIO_CLOSE;
+ }
+ }
+
+ if(google_voice_ctl & FLAG_GOOGLE_SEARCH){
+ //google voice CHAR_CTL search
+#ifdef CFG_ATM_SDK
+ if (1){
+#else
+ if (blc_ll_getTxFifoNumber() < (14 - 3)){
+#endif
+ printf("sendData = ATV_MIC_CHAR_CTL_SEARCH\n");
+ u8 sendData[2] = {0};
+
+ sendData[0] = ATV_MIC_CHAR_CTL_SEARCH;
+ ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendData, 1);
+ printf("search 1 ret=%x\r\n",ret);
+
+#ifdef CFG_ATM_SDK
+ google_voice_ctl &= ~FLAG_GOOGLE_SEARCH;
+#else
+ sendData[0] = 0x21;
+ sendData[1] = 0x02;
+ ret = bls_att_pushNotifyData(HID_CONSUME_REPORT_INPUT_DP_H, sendData, 2); // 8 HID_NORMAL_KB_REPORT_INPUT_DP_H
+
+ printf("search 2 ret=%x\r\n",ret);
+
+ sendData[0] = 0x00;
+ sendData[1] = 0x00;
+ ret = bls_att_pushNotifyData (HID_CONSUME_REPORT_INPUT_DP_H, sendData, 2);
+
+ printf("search 3 ret=%x\r\n",ret);
+
+ google_voice_ctl &= ~FLAG_GOOGLE_SEARCH;
+ extern void app_ota_status(u8 status);
+ app_ota_status(0);
+#endif
+ }
+ }
+
+ if(google_voice_ctl & FLAG_GOOGLE_CAPS_RESP){
+
+ if(notify_get_rsp_en)
+ {
+ if(notify_get_rsp_tick && clock_time_exceed(notify_get_rsp_tick,notify_get_rsp_delay_time))
+ {
+ notify_get_rsp_en = 0;
+ notify_get_rsp_tick = 0;
+ notify_get_rsp_delay_time = 6000000;
+ google_get_rsp();
+ }
+ }
+ else
+ {
+ google_get_rsp();
+ }
+ }
+
+ if(google_voice_ver == 10){
+ google_cmd_v1p0_proc();
+ }else{
+ google_cmd_v0p4_proc();
+ }
+}
+
+/**
+ * @brief delay caps rsp time.
+ * @param none
+ * @return none
+ */
+void google_get_rsp_delay(void)
+{
+ if(notify_get_rsp_en)
+ {
+ notify_get_rsp_tick = clock_time()|1;
+ notify_get_rsp_delay_time = 300000;
+ }
+}
+
+void google_reset_rsp_delay(void)
+{
+ notify_get_rsp_delay_time = 12000000;
+}
+
+
+/**
+ * @brief notify audio data process for google voice v0.4.
+ * @param none
+ * @return none
+ */
+void google_voice_data_notify_v0p4_proc(){
+
+ //////////////////////////////////////////////////////////////////
+#ifdef CFG_ATM_SDK
+ u8 *p = bridge_audio_read_frame();
+#else
+ if(blc_ll_getTxFifoNumber() >= (7 + app_audio_send_index)) return;
+ int *p = mic_encoder_data_buffer ();
+#endif
+
+ if(p == 0) return;
+ u8 audio_send_length;
+
+ for(u8 i=0; i<7; i++){
+ if(app_audio_send_index < 6){
+ audio_send_length = 20;
+ }else if(app_audio_send_index == 6){
+ audio_send_length = 14;
+ }else{
+ audio_send_length = 0;
+ }
+
+ if(BLE_SUCCESS == bls_att_pushNotifyData(AUDIO_GOOGLE_RX_DP_H, (u8*)p + app_audio_send_index*20, audio_send_length)){
+ app_audio_send_index++;
+ }else{
+ return ;
+ }
+
+ if(app_audio_send_index == 7){
+ app_audio_send_index = 0;
+#ifdef CFG_ATM_SDK
+ bridge_audio_sent_frame_ind();
+#else
+ mic_encoder_data_read_ok();
+#endif
+
+ app_audio_sync_serial ++;
+ if(app_audio_sync_serial > 10){
+ app_audio_sync_serial =0;
+ google_voice_ctl |= FLAG_GOOGLE_AUDIO_SYNC;
+ }
+ return ;
+ }
+ }
+}
+
+u8 previous_sampling_rate = U16_LO(CODEC_USED_16K); //default sampling rate:16k
+u16 previous_seq_no = 0xffff;
+/**
+ * @brief notify audio data process for google voice v1.0
+ * @param none
+ * @return none
+ */
+#ifdef CFG_ATM_SDK
+__FAST
+#endif
+void google_voice_data_notify_v1p0_proc(){
+#ifdef CFG_ATM_SDK
+ u8 *p = bridge_audio_read_frame();
+#else
+ int *p = mic_encoder_data_buffer ();
+#endif
+
+ if(p){
+ //printf("google voice 1p0\n");
+
+ u8 *pd = (u8*)p;
+#ifdef CFG_ATM_SDK
+ audio_sync_t *sync = bridge_audio_get_sync_info();
+ u16 current_seq_no = sync->frame_no;
+ u8 current_sampling_rate = sync->codec;
+#else
+ u16 current_seq_no = (pd[0]<<8) + pd[1];
+ u8 current_sampling_rate = pd[2];
+#endif
+
+ u8 need_buffer_count = 0;
+ u8 send_sync_cmd_flag = 0;
+
+ //check seq no and sampling rate before notify audio data
+ if(current_sampling_rate != previous_sampling_rate){
+ send_sync_cmd_flag |= SAMPLING_CHANGE;
+ need_buffer_count = 1;
+ }
+
+ if((u16)(current_seq_no - previous_seq_no) != 1){
+ send_sync_cmd_flag |= PACKET_LOSS;
+ need_buffer_count = 1;
+ }
+
+ app_audio_sync_serial ++;
+ if(app_audio_sync_serial >= 20){
+ //app_audio_sync_serial = 0;
+ send_sync_cmd_flag |= SYNC_PACKET;
+ need_buffer_count = 1;
+ }
+
+ if(google_voice_ctl & FLAG_GOOGLE_AUDIO_FIRST_SYNC){
+ //printf("FLAG_GOOGLE_AUDIO_FIRST_SYNC\n");
+ google_voice_ctl &= ~FLAG_GOOGLE_AUDIO_FIRST_SYNC;
+ //send_sync_cmd_flag |= FIRST_SYNC;
+ //need_buffer_count = 1;
+ previous_sampling_rate = U16_LO(CODEC_USED_16K); //default sampling rate:16k
+ previous_seq_no = 0xffff;
+ app_audio_sync_serial = 0;
+ //printf("start rate:0x%x",current_sampling_rate);
+ }
+
+ //adpcm packet: header:6byte, adpcm data:120byte,dummy:2byte
+ need_buffer_count += (VOICE_V1P0_ADPCM_PACKET_LEN - 8)/auido_frame_size;
+
+#ifndef CFG_ATM_SDK
+ if(blc_ll_getTxFifoNumber() >= (14 - need_buffer_count + app_audio_send_index)) return;
+#endif
+
+ //check sync data
+ if(send_sync_cmd_flag && !app_audio_send_index){
+
+ u8 sendBuff[7] = {0};
+ //send sync cmd
+ u32 notify_seq_no = current_seq_no * ((VOICE_V1P0_ADPCM_PACKET_LEN - 8)/auido_frame_size);
+ //printf("notify_seq_no:%d",notify_seq_no);
+
+ sendBuff[0] = ATV_MIC_CHAR_CTL_SYNC;
+ sendBuff[1] = current_sampling_rate; //The audio codec that will be used for the audio stream after this sync point.
+ sendBuff[2] = (notify_seq_no >> 8) & 0xFF; //Sequence number of the audio frame
+ sendBuff[3] = notify_seq_no & 0xFF; //Sequence number of the audio frame
+
+#ifdef CFG_ATM_SDK
+ sendBuff[4] = (sync->pred_val >> 8) & 0xFF;
+ sendBuff[5] = sync->pred_val & 0xFF;
+ sendBuff[6] = sync->step_idx;
+#else
+ sendBuff[4] = pd[3]; //Predicted ADPCM value.
+ sendBuff[5] = pd[4]; //Predicted ADPCM value.
+ sendBuff[6] = pd[5]; //step index (1) Index in ADPCM step size table.
+#endif
+
+ if(send_sync_cmd_flag&FIRST_SYNC){
+#ifndef CFG_ATM_SDK
+ printf("Predicted ADPCM value1:0x%x\n",sendBuff[4]);
+ printf("Predicted ADPCM value2:0x%x\n",sendBuff[5]);
+ printf("step index:0x%x\n",sendBuff[6]);
+ printf("need_buffer_count:0x%x\n",need_buffer_count);
+ printf("(VOICE_V1P0_ADPCM_PACKET_LEN - 8)/auido_frame_size:%d\n",(VOICE_V1P0_ADPCM_PACKET_LEN - 8)/auido_frame_size);
+#endif
+ }
+
+ ble_sts_t ret = bls_att_pushNotifyData(AUDIO_GOOGLE_CTL_DP_H, sendBuff, 7);
+ if(ret == BLE_SUCCESS){
+
+#ifndef CFG_ATM_SDK
+ printf("send_sync_cmd_flag:%d\n",send_sync_cmd_flag);
+ if(send_sync_cmd_flag&SAMPLING_CHANGE) printf("SAMPLING_CHANGE previous_sampling_rate:%d_current:%d\n",previous_sampling_rate,current_sampling_rate);
+ if(send_sync_cmd_flag&PACKET_LOSS) printf("PACKET_LOSS previous_seq_no:%d_current:%d\n",previous_seq_no,current_seq_no);
+#endif
+// array_printf(sendBuff,7);
+
+ app_audio_send_index++;
+ }else{
+ return ;
+ }
+ }
+
+ //send audio data
+ for(u8 i=0; i< ((VOICE_V1P0_ADPCM_PACKET_LEN - 8)/auido_frame_size); i++){
+
+ u8 audio_addr_shift = app_audio_send_index - (send_sync_cmd_flag==0)?0:1;
+
+// printf("send_sync_cmd_flag:%d\n",send_sync_cmd_flag);
+// printf("audio_addr_shift:%d\n",audio_addr_shift);
+#ifdef CFG_ATM_SDK
+ if(BLE_SUCCESS == bls_att_pushNotifyData(AUDIO_GOOGLE_RX_DP_H, pd, auido_frame_size)){
+#else
+ if(BLE_SUCCESS == bls_att_pushNotifyData(AUDIO_GOOGLE_RX_DP_H, pd + 6 + audio_addr_shift *auido_frame_size, auido_frame_size)){
+#endif
+ app_audio_send_index++;
+// array_printf(pd + 6 + audio_addr_shift *auido_frame_size,20);
+ }else{
+ return;
+ }
+
+
+ if(app_audio_send_index >= need_buffer_count){
+// printf("need_buffer_count:0x%x\n",need_buffer_count);
+// printf("app_audio_send_index:0x%x\n",app_audio_send_index);
+ app_audio_send_index = 0;
+ //printf("notify data:");
+ //array_printf(pd,10);
+
+ app_audio_sync_serial = 0;
+
+#ifdef CFG_ATM_SDK
+ bridge_audio_sent_frame_ind();
+#else
+ mic_encoder_data_read_ok();
+#endif
+
+ previous_sampling_rate = current_sampling_rate;
+ previous_seq_no = current_seq_no;
+
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * @brief notify audio data process.
+ * @param none
+ * @return none
+ */
+void google_voice_data_notify_proc(){
+ //send voice data
+ if( google_voice_ver == 10){
+ google_voice_data_notify_v1p0_proc();
+ }else{
+ //google voice version error
+ google_voice_data_notify_v0p4_proc();
+ }
+}
+
+/**
+ * @brief google voice task at main loop.
+ * @param none
+ * @return none
+ */
+void app_audio_task(void){
+
+ if(app_active_remote_timer && clock_time_exceed(app_active_remote_timer, APP_AUDIO_GOOGLE_TIMEOUT1)){
+ //wait mic open cmd
+ printf("app_active_remote_timer timeout\n");
+ app_active_remote_timer = 0;
+ flag_active_mic_open = 0;
+ }
+
+ if(!google_voice_ctl) return;
+
+ if(device_in_connection_state)
+ google_cmd_proc();
+
+ if(google_voice_ctl & FLAG_DELAY_NOTIFY){
+
+ if(clock_time_exceed(app_audio_start_delay,30*1000)){
+
+ google_voice_ctl &= ~FLAG_DELAY_NOTIFY;
+ audio_set_mute_pga(1);
+ printf("start notify audio data\n");
+ if(google_voice_ver == 10){
+ if(google_voice_codec_used == CODEC_USED_16K){
+ printf("init adpcm unit size: to 240\n");
+ google_voice_pcm_sample_packet = VOICE_V1P0_ADPCM_UNIT_SIZE;
+ }else if(google_voice_codec_used == CODEC_USED_8K){
+ google_voice_pcm_sample_packet = VOICE_V1P0_ADPCM_UNIT_SIZE*2;
+ }
+ }else{
+ if(google_voice_codec_used == CODEC_USED_16K){
+ printf("init adpcm unit size: to 256\n");
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE;
+ }else if(google_voice_codec_used == CODEC_USED_8K){
+ google_voice_pcm_sample_packet = VOICE_V0P4_ADPCM_UNIT_SIZE*2;
+ }
+ }
+ }
+ return;
+ }
+
+ if((google_voice_ctl & FLAG_NOTIFY_AUDIO_DATA) == 0) return;
+ prevent_repeat_trigger_on_request_tick = 0;
+ proc_mic_encoder();
+
+ google_voice_data_notify_proc();
+}
+
+#else
+
+#endif