//*****************************************************************************
//
//! @file am_vos_task.c
//!
//! @brief VoS core tasks and handlers
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

//*****************************************************************************
// Global includes for this project.
//
//*****************************************************************************
#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include "am_vos_utils.h"

#include "am_vos_task.h"
#include "am_vos_init.h"
#include "am_vos_audio.h"
#include "am_vos_codec.h"
#include "am_vos_logic.h"

#if configUSE_BLE
#include "am_vos_ble.h"
#endif // configUSE_BLE

#if configUSE_AMVOS_AMA
#include "am_vos_ama.h"
#endif // configUSE_AMVOS_AMA

#if configUSE_Sensory_THF
#include "SensoryLib.h"
#include "am_vos_thf.h"
#endif // configUSE_Sensory_THF

#if configUSE_Fluent
#include "fluentai-sdk.h"
#endif // configUSE_Fluent

#if configUSE_SPP_AGC
#include "am_vos_agc.h"
#endif

#if configUSE_SPP_P2A
#include "am_vos_p2a.h"
#endif

#if configUSE_SPP_DRC
#include "am_vos_comp.h"
#include "am_vos_agc.h"
#include "am_vos_exp.h"
#endif // configUSE_SPP_DRC

#if configUSE_AMBIQ_VADv3
#include "webrtc_vad.h"
#endif // configUSE_AMBIQ_VADv3

#if configUSE_AMVOS_ATVV
#include "am_vos_atvv.h"
#endif // configUSE_AMVOS_ATVV

#if configUSE_BLE_Measure_Throughput
#include "wsf_types.h"
#include "amvos_api.h"

extern VosBleInfo g_sVosBle;
#endif // configUSE_BLE_Measure_Throughput

#if configUSE_LOG_UART0 || configUSE_PRINTF_UART0
//*****************************************************************************
//
// Serial communication task to transmit data.
//
//*****************************************************************************

void am_vos_uart0_gatekeeper_task(void *pvParameters)
{
    while(1)
    {
        am_vos_uart_process();
    }
}
#endif // configUSE_LOG_UART0 || configUSE_PRINTF_UART0

#if configUSE_PRINTF_RTT || configUSE_PRINTF_SWO
//*****************************************************************************
//
// Standard IO task to print data on configured interface
//
//*****************************************************************************
void am_vos_stdio_gatekeeper_task(void* pvParameters)
{
    am_vos_task_queue_element_t QueueElement;
    uint8_t transmit_buff[AM_VOS_PRINTF_BUFFSIZE*AM_VOS_STDIO_BUFFNUM + 8 + 7 + 4]; // extra 19 bytes for color coding, RTT (BG = 8bytes, text = 7bytes, reset = 4bytes)
    int32_t transmit_length = 0;
    uint32_t index = 0;


    // during init
    // rtt print examples:
//    am_util_stdio_printf("Example: Use AM_VOS_LOG_DEBUG(); for Debug log print. \n");
//    am_util_stdio_printf("Example: Use AM_VOS_LOG_INFO(); for Info log print. \n");
//    am_util_stdio_printf("Example: Use AM_VOS_LOG_WARNING(); for Warning log print. \n");

//    AM_VOS_LOG_DEBUG("BG coding length = %d, Text coding length = %d, Ctrl reset coding length = %d \n", strlen(RTT_CTRL_BG_BLACK),  strlen(RTT_CTRL_TEXT_BRIGHT_GREEN), strlen(RTT_CTRL_RESET));

    while(1)
    {
        am_vos_task_read(AM_VOS_TASK_STDIO, &QueueElement);
        uint8_t color_offset = 0;
        switch(QueueElement.ui32MessageType)
        {
            case AM_VOS_MESSAGE_STR:
#if configUSE_PRINTF_RTT
                switch(QueueElement.Source)
                {
                    case 0:
                      // plain text, write
                      memcpy(transmit_buff, RTT_CTRL_RESET, strlen(RTT_CTRL_RESET));
                      color_offset = strlen(RTT_CTRL_RESET);
                      break;
                    case 1:
                      // debug, green, black background
                      memcpy(transmit_buff, RTT_CTRL_BG_BLACK, strlen(RTT_CTRL_BG_BLACK));
                      memcpy(transmit_buff + strlen(RTT_CTRL_BG_BLACK), RTT_CTRL_TEXT_BRIGHT_GREEN, strlen(RTT_CTRL_TEXT_BRIGHT_GREEN));
                      color_offset = strlen(RTT_CTRL_BG_BLACK) + strlen(RTT_CTRL_TEXT_BRIGHT_GREEN);
                      break;
                    case 2:
                      // warning, white, red background
                      memcpy(transmit_buff, RTT_CTRL_BG_RED, strlen(RTT_CTRL_BG_RED));
                      memcpy(transmit_buff + strlen(RTT_CTRL_BG_RED), RTT_CTRL_TEXT_BRIGHT_WHITE, strlen(RTT_CTRL_TEXT_BRIGHT_WHITE));
                      color_offset = strlen(RTT_CTRL_BG_RED) + strlen(RTT_CTRL_TEXT_BRIGHT_WHITE);
                      break;
                    case 3:
                      // info, cyan, black background
                      memcpy(transmit_buff, RTT_CTRL_BG_BLACK, strlen(RTT_CTRL_BG_BLACK));
                      memcpy(transmit_buff + strlen(RTT_CTRL_BG_BLACK), RTT_CTRL_TEXT_BRIGHT_CYAN, strlen(RTT_CTRL_TEXT_BRIGHT_CYAN));
                      color_offset = strlen(RTT_CTRL_BG_BLACK) + strlen(RTT_CTRL_TEXT_BRIGHT_CYAN);
                      break;
                    default:
                      // plain text, write
                      memcpy(transmit_buff, RTT_CTRL_RESET, strlen(RTT_CTRL_RESET));
                      color_offset = strlen(RTT_CTRL_RESET);
                      break;
//                    SEGGER_RTT_Write(0, transmit_buff, transmit_length);
                }
#endif // configUSE_PRINTF_RTT

#if configUSE_PRINTF_SWO
                color_offset = 0;
#endif // configUSE_PRINTF_SWO

                transmit_length = (int32_t)strlen(&(g_sAmUtil.pcStdioBuff[QueueElement.info.ui32Indx]));

                configASSERT(transmit_length < AM_VOS_PRINTF_BUFFSIZE);

                for(index=0; index < transmit_length; index++)
                {
                    transmit_buff[index + color_offset] = g_sAmUtil.pcStdioBuff[QueueElement.info.ui32Indx + index];
                }
                transmit_buff[transmit_length + color_offset] = NULL;
#if configUSE_PRINTF_RTT
                SEGGER_RTT_printf(0, (const char *)transmit_buff);
#endif // configUSE_PRINTF_RTT

#if configUSE_PRINTF_SWO
                am_util_stdio_printf((const char *)transmit_buff);
#endif // configUSE_PRINTF_SWO
                break;
            default:
                break;
        }

    }


}
#endif // configUSE_PRINTF_RTT || configUSE_PRINTF_SWO

//*****************************************************************************
//
// LED task to indicate external events, such as heart beat and key word detected.
//
//*****************************************************************************
void am_vos_led_task(void *pvParameters)
{
    am_vos_task_queue_element_t QueueElement;

    // power up swirl
    //am_vos_logic_led_swirl(1);

    while(1)
    {
        am_vos_task_read(AM_VOS_TASK_LED, &QueueElement);
        if(QueueElement.info.ui32Note >= KEY_WORD_GOT_MESSAGE)
        {
#if configUSE_LEDs
            am_vos_logic_led_swirl(QueueElement.info.ui32Note == KEY_WORD_GOT_MESSAGE ? 0 : 3);
#endif // configUSE_LEDs
        }
        else if(QueueElement.info.ui32Note == HEARTBEAT_TIMER_MESSAGE)
        {
#if configUSE_LEDs
            am_vos_heartbeat_led_process();
#endif // configUSE_LEDs

#if configUSE_AMVOS_ATVV
            if(g_sVosSys.ui32MicOpenTimeOutCount)
            {
                if(g_sVosSys.ui32MicOpenTimeOutCount > AM_VOS_ATVV_MIC_OPEN_TIMEOUT_SEC)
                {
                    am_vos_audio_reset_flag_and_buffer();
                    g_sVosSys.ui32MicOpenTimeOutCount = 0;
                    am_vos_atvv_start_search_cancel();
                }
                else
                {
                    g_sVosSys.ui32MicOpenTimeOutCount++;
                }
            }
#endif // configUSE_AMVOS_ATVV

#if configUSE_BLE_Measure_Throughput
            if(g_sVosBle.ui32AmaDataSentLength > 0)
            {
                AM_VOS_LOG_INFO("[AMA] Data sent %d B/s\n", g_sVosBle.ui32AmaDataSentLength);
                //AM_VOS_LOG_INFO("[AMA] Data sent %d B/s\n", g_sVosBle.ui32AmaDataSentLength * g_sVosBle.ui32AmaDataCnfCnt / g_sVosBle.ui32AmaDataSentCnt);
                g_sVosBle.ui32AmaDataSentLength = 0;
                g_sVosBle.ui32AmaDataSentCnt -= g_sVosBle.ui32AmaDataCnfCnt;
                g_sVosBle.ui32AmaDataCnfCnt = 0;
            }
#endif // configUSE_BLE_Measure_Throughput

#if VOS_MEASUREMENT_ENABLE
            ++g_sVosSys.ui32VosBenchTimeSec;
#endif

#if VOS_MEASURE_AMSPP_MIPS
            if((g_sVosSys.ui32PcmTimeTick > 0) && ((g_sVosSys.ui32VosBenchTimeSec) % AM_VOS_BENCHMARK_TIME_SEC) == 0)
            {
                AM_VOS_LOG_INFO("[VoS] AM_SPP %d ticks/%d sec Call %d AVG %0.2f ms CPU %0.2f\n", 
                                g_sVosSys.ui32PcmTimeTick, g_sVosSys.ui32PcmCallNum, AM_VOS_BENCHMARK_TIME_SEC,
                                ((float)(g_sVosSys.ui32PcmTimeTick * 1000 / g_sVosSys.ui32PcmCallNum))/(AM_VOS_BENCHMARK_TIMER_FREQ), 
                                (float)(g_sVosSys.ui32PcmTimeTick * 100 / AM_VOS_BENCHMARK_TIME_SEC)/(AM_VOS_BENCHMARK_TIMER_FREQ));
                g_sVosSys.ui32PcmTimeTick = 0;
                g_sVosSys.ui32PcmCallNum = 0;
            }
//            AM_VOS_LOG_INFO("[VoS] Timer : %d\n", am_vos_timer_benchmark_read());
#endif // VOS_MEASURE_AMSPP_MIPS

#if VOS_MEASURE_SPP_MIPS
            if((g_sVosSys.ui32SppTimeTick > 0) && ((g_sVosSys.ui32VosBenchTimeSec) % AM_VOS_BENCHMARK_TIME_SEC) == 0)
            {
                AM_VOS_LOG_INFO("[VoS] SPP %d ticks / %d sec Call %d AVG %0.2f ms CPU %0.2f\n", 
                                g_sVosSys.ui32SppTimeTick, AM_VOS_BENCHMARK_TIME_SEC, g_sVosSys.ui32SppCallNum,
                                ((float)(g_sVosSys.ui32SppTimeTick * 1000 / g_sVosSys.ui32SppCallNum))/(AM_VOS_BENCHMARK_TIMER_FREQ), 
                                (float)(g_sVosSys.ui32SppTimeTick * 100 / AM_VOS_BENCHMARK_TIME_SEC) / (AM_VOS_BENCHMARK_TIMER_FREQ));
                g_sVosSys.ui32SppTimeTick = 0;
                g_sVosSys.ui32SppCallNum = 0;
            }
#endif // VOS_MEASURE_SPP_MIPS

#if VOS_MEASURE_WWE_MIPS
            if((g_sVosSys.ui32WweTimeTick > 0) && ((g_sVosSys.ui32VosBenchTimeSec) % AM_VOS_BENCHMARK_TIME_SEC) == 0)
            {
                AM_VOS_LOG_INFO("[VoS] WWE %d ticks/%d sec Call %d AVG %0.2f ms CPU %0.1f\n",
                                g_sVosSys.ui32WweTimeTick, g_sVosSys.ui32WweCallNum, AM_VOS_BENCHMARK_TIME_SEC,
                                ((float)(g_sVosSys.ui32WweTimeTick * 1000 / g_sVosSys.ui32WweCallNum)) / (AM_VOS_BENCHMARK_TIMER_FREQ), 
                                (float)(g_sVosSys.ui32WweTimeTick * 100 / AM_VOS_BENCHMARK_TIME_SEC) / (AM_VOS_BENCHMARK_TIMER_FREQ));
                g_sVosSys.ui32WweTimeTick = 0;
                g_sVosSys.ui32WweCallNum = 0;
            }
#endif // VOS_MEASURE_WWE_MIPS

#if VOS_MEASURE_CODEC_MIPS
            if((g_sVosSys.ui32CodecTimeTick > 0) && ((g_sVosSys.ui32VosBenchTimeSec) % AM_VOS_BENCHMARK_TIME_SEC) == 0)
            {
                AM_VOS_LOG_INFO("[VoS] WWE %d ticks/%d sec Call %d AVG %0.2f ms CPU %0.1f\n",
                                g_sVosSys.ui32CodecTimeTick, g_sVosSys.ui32CodecCallNum, AM_VOS_BENCHMARK_TIME_SEC,
                                ((float)(g_sVosSys.ui32CodecTimeTick * 1000 / g_sVosSys.ui32CodecCallNum)) / (AM_VOS_BENCHMARK_TIMER_FREQ),
                                (float)(g_sVosSys.ui32CodecTimeTick * 100 / AM_VOS_BENCHMARK_TIME_SEC) / (AM_VOS_BENCHMARK_TIMER_FREQ));
                g_sVosSys.ui32CodecTimeTick = 0;
                g_sVosSys.ui32CodecCallNum = 0;
            }
#endif // VOS_MEASURE_CODEC_MIPS

#if VOS_MEASURE_SYS_MIPS
            if( (g_sVosSys.ui32VosSleepTimeTick > 0) && ((g_sVosSys.ui32VosBenchTimeSec) % AM_VOS_BENCHMARK_TIME_SEC) == 0)
            {
                AM_VOS_LOG_INFO("[VoS] Sleep %d ticks / %d sec Call %d CPU %0.2f\n", g_sVosSys.ui32VosSleepTimeTick,
                                AM_VOS_BENCHMARK_TIME_SEC, g_sVosSys.ui32VosSleepCallNum, 
                                (float)((AM_VOS_BENCHMARK_TIMER_FREQ * AM_VOS_BENCHMARK_TIME_SEC - g_sVosSys.ui32VosSleepTimeTick)
                                 * 100 / AM_VOS_BENCHMARK_TIME_SEC) / (AM_VOS_BENCHMARK_TIMER_FREQ));
                g_sVosSys.ui32VosSleepTimeTick = 0;
                g_sVosSys.ui32VosSleepCallNum = 0;
            }
#endif // VOS_MEASURE_SYS_MIPS

#if VOS_MEASURE_VAD_WAKETIME
            if((g_sVosSys.ui32VosVadWakeTimeTickTotal > 0) && ((g_sVosSys.ui32VosBenchTimeSec % AM_VOS_BENCHMARK_TIME_SEC) == 0))
            {
              AM_VOS_LOG_INFO("[VoS] WakeTime %d ticks %0.2f secs / %d secs\n", g_sVosSys.ui32VosVadWakeTimeTickTotal,
                                (float)(g_sVosSys.ui32VosVadWakeTimeTickTotal / AM_VOS_BENCHMARK_TIMER_FREQ), g_sVosSys.ui32VosBenchTimeSec);
            }
#endif // VOS_MEASURE_VAD_WAKETIME

#if VOS_MEASURE_ISR_PCM_DATA
            if(( g_sVosSys.ui32VosIsrCount > 0) && ((g_sVosSys.ui32VosBenchTimeSec % AM_VOS_BENCHMARK_TIME_SEC) == 0))
            {
                AM_VOS_LOG_INFO("ISR %d Data %d bytes\n", g_sVosSys.ui32VosIsrCount, g_sVosSys.ui32VosPcmSize);
                g_sVosSys.ui32VosIsrCount = 0;
                g_sVosSys.ui32VosPcmSize = 0;
            }
#endif // VOS_MEASURE_ISR_PCM_DATA
        }
        else if(QueueElement.info.ui32Note == DOUBLE_TAP_MESSAGE)
        {
#if configUSE_LEDs
            am_vos_logic_led_swirl(2);
#endif // configUSE_LEDs
        }
#if configUSE_MUTE_MIC
        else if(QueueElement.info.ui32Note == MUTE_MIC_MESSAGE)
        {
            if(am_vos_button_gpio_check(MUTE_MIC_BUTTON))
            {
                am_vos_mute_mic_toggle();
                vTaskDelay(pdMS_TO_TICKS(200));
            }
            am_vos_gpio_enable_irq(GRP_IRQ_MUTE_MIC_BUTTON, MUTE_MIC_BUTTON);
        }
#endif // configUSE_MUTE_MIC
#if configUSE_RTT_RECORDER && !defined(AM_PART_APOLLO5_API)
        else if(QueueElement.info.ui32Note == RTT_AMU2S_MESSAGE)
        {
            if(am_vos_button_gpio_check(RTT_DUMP_BUTTON))
            {
                if(g_sVosSys.ui8RecordStartFlag == 0)
                {
                    g_sVosSys.ui8RecordStartFlag = 1;
                    xTimerChangePeriod(am_KWD_timers[AM_VOS_TIMER_HEART_BEAT], RTT_RECORDER_RUNNING, 0);
                }
                else
                {
                    g_sVosSys.ui8RecordStartFlag = 0;
                    xTimerChangePeriod(am_KWD_timers[AM_VOS_TIMER_HEART_BEAT], HEART_BEAT_PERIOD, 0);
                }
            }
            am_vos_gpio_enable_irq(GRP_IRQ_RTT_DUMP_BUTTON, RTT_DUMP_BUTTON);
        }
#endif // configUSE_RTT_RECORDER
    }
}

//*****************************************************************************
//
// Audio task to execute the SPP and Wakeword engine
//
//*****************************************************************************
void am_vos_audio_processing_task(void *pvParameters)
{
    am_vos_task_queue_element_t QueueElement;
    int32_t in32LRSample[PCM_FRAME_SIZE_SAMPLES];

#if configUSE_Sensory_THF || configUSE_Fluent
    if(am_vos_engine_init())
    {
        AM_VOS_LOG_WARNING("Wakeword Detection Engine Init Fail!\n");
    }

    am_vos_audio_wwd_enable();
#endif // configUSE_Sensory_THF || configUSE_Fluent

    while(1)
    {
        am_vos_task_read(AM_VOS_TASK_AUD_PROCESSING, &QueueElement);

        switch(QueueElement.ui32MessageType)
        {
            case AM_VOS_MESSAGE_SHORT:
//#if configUSE_PUSH_TO_TALK && configUSE_BLE
#if configUSE_PUSH_TO_TALK
                if(QueueElement.info.ui32Note == PUSH_TO_TALK_MESSAGE)
                {
                    if(am_vos_button_gpio_check(PUSH_TO_TALK_BUTTON))
                    {
                        if((g_sVosSys.ui8PushTalkFlag == 0) && (g_sVosSys.ui8KwdDetectedFlag == 0))
                        {
#if configUSE_BLE
                            am_vos_streaming_push_to_talk();
#else
                            am_vos_audio_reset_flag_and_buffer();
#if (!configUSE_VS_CMD)
                            am_vos_audio_wwd_disable();
#endif
                            g_sVosSys.ui8PushTalkFlag = 1;
#endif
                        }
                        else
                        {
                            am_vos_audio_reset_flag_and_buffer();
                            //am_vos_streaming_stop();
#if configUSE_AMVOS_AMA
                            am_vos_ama_stop_speech_send(ErrorCode_USER_CANCELLED);
#elif configUSE_AMVOS_ATVV
                            am_vos_atvv_audio_end_send();
#endif
                        }
                    }
                    // barge-in not allowed
                    am_vos_gpio_enable_irq(GRP_IRQ_PUSH_TO_TALK_BUTTON, PUSH_TO_TALK_BUTTON);
                }
#endif // configUSE_PUSH_TO_TALK

#if configUSE_PAIRING_MODE_BTN && configUSE_BLE
                else if(QueueElement.info.ui32Note == PAIRING_MODE_MESSAGE)
                {
                    if(am_vos_button_gpio_check(PAIRING_MODE_BUTTON))
                    {
                        am_vos_ble_pairing_mode();
                    }
                    am_vos_gpio_enable_irq(GRP_IRQ_PAIRING_MODE_BUTTON, PAIRING_MODE_BUTTON);
                }
#endif // configUSE_PAIRING_MODE_BTN && configUSE_BLE

                break;

            case AM_VOS_MESSAGE_LONG:
                am_vos_ring_buffer_pop(QueueElement.pDataBuffer, in32LRSample, QueueElement.info.ui32Length);

#if configUSE_AAD
                if(g_sVosSys.bWwdEnabled == true)
                {
                    am_vos_aad_check_status();
                }
#endif // configUSE_AAD
                break;

            default:
                break;
        }

#if configUSE_AMBIQ_VADv3
        uint32_t ui32BytesRewind = 0;
        //
        // take 1-ch data for VAD
        //
        if (g_sVosSys.ui8RequireFullProcess)
        {
            // Place the samples in the AWE processing buffers
            if(g_sVosSys.ui8FullProcessedFlag == 0) {
                g_sVosSys.ui8FullProcessedFlag = 1;
#if configUSE_AAD
                g_sVosSys.ui8AadDebounceFlag = 0;
                g_sVosSys.ui8AadEnabled = 0;
#endif // configUSE_AAD
                ui32BytesRewind = am_audio_buffer_rewind(AM_AUDIO_BUFFER_STEREO, (AM_VAD_PREROLL_MS * (USE_PCM_SAMPLE_RATE / 1000) * PCM_SAMPLE_BYTES * USE_MIC_NUM));
                if(ui32BytesRewind != (AM_VAD_PREROLL_MS * (USE_PCM_SAMPLE_RATE / 1000) * PCM_SAMPLE_BYTES * USE_MIC_NUM))
                {
                    AM_VOS_LOG_INFO("No enough audio to rewind, %d bytes %d ms instead...\n", ui32BytesRewind, ui32BytesRewind/((USE_PCM_SAMPLE_RATE / 1000) * PCM_SAMPLE_BYTES * USE_MIC_NUM));
                }
#if USE_VAD_DEBUG
                AM_VOS_LOG_INFO("[AM-VoS] VAD - Enabled KWD\n");
#endif // USE_VAD_DEBUG
#if VOS_MEASURE_VAD_WAKETIME
                //AM_VOS_LOG_INFO("[AM-VoS] Timer Count %d\n", am_vos_timer_benchmark_read());
                g_sVosSys.ui32VosVadWakeTimeTick = am_vos_timer_benchmark_read();
#endif // VOS_MEASURE_VAD_WAKETIME
            }
#if USE_VAD_DEBOUNCE
            g_sVosSys.i16VadDebounceCnt = AM_VAD_DEBOUNCE_SAMPLES / PCM_FRAME_SIZE_SAMPLES;
#endif // USE_VAD_DEBOUNCE
        }
        else
        {
            if(g_sVosSys.ui8FullProcessedFlag == 1)
            {
#if USE_VAD_DEBOUNCE
                if(g_sVosSys.i16VadDebounceCnt-- <= 0)
#endif // USE_VAD_DEBOUNCE
                {
                    g_sVosSys.ui8FullProcessedFlag = 0;
#if configUSE_AAD
                    g_sVosSys.i32TimeoutToAadEnable = AAD_TIMEOUT_COUNT;
                    g_sVosSys.ui8AadDebounceFlag = 1;
#endif // configUSE_AAD
#if USE_VAD_DEBUG
                    AM_VOS_LOG_INFO("[AM-VoS] VAD - Disabled KWD\n");
#endif // USE_VAD_DEBUG
#if VOS_MEASURE_VAD_WAKETIME
                    AM_VOS_LOG_INFO("[AM-VoS] Timer Count %d\n", am_vos_timer_benchmark_read());
                    if((am_vos_timer_benchmark_read() - g_sVosSys.ui32VosVadWakeTimeTick) > 0)
                    {
                        g_sVosSys.ui32VosVadWakeTimeTickTotal += (am_vos_timer_benchmark_read() - g_sVosSys.ui32VosVadWakeTimeTick);
                    }
                    g_sVosSys.ui32VosVadWakeTimeTick = 0;
                    am_vos_timer_benchmark_clear_and_start();
#endif // VOS_MEASURE_VAD_WAKETIME
                }
            }
#if configUSE_AAD
            am_vos_aad_check_status();
#endif // configUSE_AAD
        }

        if (g_sVosSys.ui8FullProcessedFlag || g_sVosSys.ui8KwdDetectedFlag || g_sVosSys.ui8PushTalkFlag || g_sVosSys.ui8ProvideSpeechFlag)
        {
            while(am_vos_ring_process(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO]), in32LRSample,
                                      PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM))
            {
                am_vos_audio_handler(in32LRSample);
            }
        }
#else
        am_vos_audio_handler(in32LRSample);
#endif // configUSE_AMBIQ_VADv3

     }
}

#if configUSE_AUDIO_CODEC
//*****************************************************************************
//
// Audio codec task
//
//*****************************************************************************
void am_vos_codec_task(void *pvParameters)
{
    am_vos_task_queue_element_t QueueElement;

    int8_t codecInputBuffer[CODEC_IN_RING_BUFF_SIZE];
    uint8_t codecOutputBuffer[CODEC_OUT_RING_BUFF_SIZE];

    uint32_t codecInBufIndex = 0;
    uint32_t codecInBufRemaining = CODEC_IN_RING_BUFF_SIZE;
    uint32_t codecInBufBytesToPop = 0;

    int8_t *p_CodecInBuf = codecInputBuffer;
    uint8_t *p_CodecOutBuf = codecOutputBuffer;

    int32_t i32CompressedLen;
    uint32_t ui32StreamLen;

    uint8_t ui8EncoderLoopCount = 0;

    while(1)
    {
        am_vos_task_read(AM_VOS_TASK_CODEC, &QueueElement);

        switch(QueueElement.Source)
        {
            case AM_VOS_TASK_AUD_PROCESSING:

                AM_CRITICAL_BEGIN_VOS;
                ui32StreamLen = am_vos_get_ring_buffer_status(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_MONO]));

                AM_CRITICAL_END_VOS;

                while(ui32StreamLen)
                {
                    //
                    // Attempt to clear mono buffer
                    //
                    if(ui32StreamLen > codecInBufRemaining)
                    {
                        codecInBufBytesToPop = codecInBufRemaining;
                    }
                    else
                    {
                        codecInBufBytesToPop = ui32StreamLen;
                    }

                    am_audio_buffer_nested_pop(AM_AUDIO_BUFFER_MONO, &codecInputBuffer[codecInBufIndex], codecInBufBytesToPop);
                    if(codecInBufRemaining > codecInBufBytesToPop)
                    {
                        codecInBufRemaining -= codecInBufBytesToPop;
                        codecInBufIndex += codecInBufBytesToPop;
                    }
                    else
                    {
                        // equal, or less...though there should not be case for less...
                        // reset index and remaining
                        codecInBufRemaining = CODEC_IN_RING_BUFF_SIZE;
                        codecInBufIndex = 0;
                    }

                    if(codecInBufIndex == 0)
                    {

                        vTaskSuspendAll();
                        ui8EncoderLoopCount++;
#if VOS_MEASURE_CODEC_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
                        am_vos_bench_dwt_reset();
                        am_vos_bench_dwt_start();
#else
                        uint32_t TickBeforeCodec = am_vos_timer_benchmark_read();
#endif
#endif // VOS_MEASURE_CODEC_MIPS

                        am_vos_codec_encode(p_CodecInBuf, CODEC_IN_RING_BUFF_SIZE,
                                            p_CodecOutBuf, CODEC_OUT_RING_BUFF_SIZE,
                                            &i32CompressedLen);
#if VOS_MEASURE_CODEC_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
                        g_sVosSys.ui32CodecTimeTick += am_vos_bench_dwt_getcycle();
                        am_vos_bench_dwt_stop();
                        g_sVosSys.ui32CodecCallNum++;
#else
                        if((am_vos_timer_benchmark_read() - TickBeforeCodec) > 0)
                        {
                            g_sVosSys.ui32CodecTimeTick += am_vos_timer_benchmark_read() - TickBeforeCodec;
                            g_sVosSys.ui32CodecCallNum++;
                            am_vos_timer_benchmark_clear_and_start();
                        }
#endif
#endif // VOS_MEASURE_CODEC_MIPS
                        if(i32CompressedLen != CODEC_OUT_RING_BUFF_SIZE)
                        {
                            AM_VOS_LOG_INFO("[AM-VoS] i32CompressedLen = %d\n", i32CompressedLen);
                            am_audio_buffer_nested_push(AM_AUDIO_BUFFER_ENCODED, p_CodecOutBuf, CODEC_OUT_RING_BUFF_SIZE);
                        }
                        else
                        {
                            am_audio_buffer_nested_push(AM_AUDIO_BUFFER_ENCODED, p_CodecOutBuf, i32CompressedLen);
                        }

                        if(am_audio_universal_buffer_status_check(g_sAmUtil.sRingBuf)==false)
                        {
//                            AM_VOS_LOG_DEBUG("There is over-write in universal buffer when pushing to ENCODED!\n\r");
                            AM_VOS_LOG_WARNING("SB RH: %d, SB WT: %d, OV: %d\n",
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO].ui32BufferHead_read,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO].ui32BufferTail_write,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO].ui32OverWriting);
                            AM_VOS_LOG_WARNING("MONO RH: %d, WT: %d, OV: %d\n",
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_MONO].ui32BufferHead_read,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_MONO].ui32BufferTail_write,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_MONO].ui32OverWriting);
                            AM_VOS_LOG_WARNING("EB RH: %d, EB WT: %d, OV: %d \n",
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED].ui32BufferHead_read,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED].ui32BufferTail_write,
                                             g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED].ui32OverWriting);
                        }

                        ui8EncoderLoopCount = 0;
                        xTaskResumeAll();

#if configUSE_RTT_RECORDER && configUSE_RECORD_CODEC_ENCODED
                        am_vos_rtt_record((void *)p_CodecOutBuf, i32CompressedLen);
#endif // configUSE_RTT_RECORDER && configUSE_RECORD_CODEC_ENCODED

#if configUSE_BLE
                        if(am_vos_get_ring_buffer_status(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED])) >= BLE_DATA_BUFFER_SIZE)
                        {
//                            if(g_sVosSys.ui8AlexaAppPlatform == 1)
//                            {
//                                // UART TX start for iAP.
//                            }
//                            else if(g_sVosSys.ui8AlexaAppPlatform == 2)
#if configUSE_AMVOS_ATVV
                            if(g_sVosSys.ui8ProvideSpeechFlag)
                            {
                                am_vos_ble_stream_send();
                            }
#else
                            am_vos_ble_stream_send();
#endif
                        }
#else // configUSE_BLE
                        am_vos_audio_flush_ring_buffer();
#endif // configUSE_BLE
                    }

                    AM_CRITICAL_BEGIN_VOS;
                    ui32StreamLen = am_vos_get_ring_buffer_status(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_MONO]));
                    AM_CRITICAL_END_VOS;

                    if((ui32StreamLen < CODEC_IN_RING_BUFF_SIZE)||(ui8EncoderLoopCount > 1))
                    {
                        ui8EncoderLoopCount = 0;
                    }
                }
                break;

            default:
                break;
        }
    }
}
#endif // configUSE_AUDIO_CODEC

//*****************************************************************************
//
// Software timer callback functions
//
//*****************************************************************************
void am_vos_timer_heart_beat_callback(TimerHandle_t xTimer)
{
    am_vos_task_send(AM_VOS_TASK_NONE, AM_VOS_TASK_LED,
                            AM_VOS_MESSAGE_SHORT, HEARTBEAT_TIMER_MESSAGE, NULL);
}

int16_t g_in16MonoSampleIn[PCM_FRAME_SIZE_SAMPLES];

#if configUSE_AMBIQ_VADv3
uint8_t am_vos_vad_process(uint32_t *pui32PcmVadBuf, uint32_t ui32InputSamples)
{
    int16_t *pi16PcmBuf = (int16_t *)pui32PcmVadBuf;

    //
    // take 1-ch data for VAD
    //
    for(int indx = 0; indx < ui32InputSamples; indx++)
    {
#if USE_MIC_SINGLE
        g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];                       // Left channel
#else // USE_MIC_SINGLE
        g_in16MonoSampleIn[indx] = pui32PcmVadBuf[indx] & 0xFFFF;          // Left channel
#endif // USE_MIC_DUAL
    }

    return WebRtcVad_Process(g_sVosAud.psVadHandle, USE_PCM_SAMPLE_RATE, g_in16MonoSampleIn, ui32InputSamples);
}
#endif // configUSE_AMBIQ_VADv3

//*****************************************************************************
//
// VAD task to execute VAD before SPP and WWE
//
//*****************************************************************************
void am_vos_vad_task(void *pvParameters)
{
    am_vos_task_queue_element_t QueueElement;

#if configUSE_AMBIQ_SPP
    int16_t *pi16PcmBuf = (int16_t *)g_sVosBrd.pui32PcmBuf;
#endif //  configUSE_AMBIQ_SPP

    while(1)
    {
        am_vos_task_read(AM_VOS_TASK_VAD, &QueueElement);
        //
        // queue from PDM-ISR
        //
#if USE_DMIC_PDM
        if(QueueElement.Source == AM_VOS_ISR_PDM)
#elif USE_AMIC_AUDADC
        if(QueueElement.Source == AM_VOS_ISR_ADC)
#endif
        {
#if VOS_MEASURE_AMSPP_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
            am_vos_bench_dwt_reset();
            am_vos_bench_dwt_start();
#else
            uint32_t TickBeforePcm = am_vos_timer_benchmark_read();
#endif
#endif // VOS_MEASURE_AMSPP_MIPS

#if configUSE_RTT_RECORDER && configUSE_RECORD_AM_SPP_IN_OUT
            uint32_t ui32RttBuf[PCM_FRAME_SIZE_SAMPLES];
            for(int i = 0; i < PCM_FRAME_SIZE_SAMPLES; i++)
            {
                ui32RttBuf[i] =  pi16PcmBuf[i] & 0xFFFF;
            }
#endif

#if configUSE_SPP_HPF && USE_MIC_SINGLE
            for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
            {
                g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
            }
            am_vos_filter_hpf(g_in16MonoSampleIn, pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);
            for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
            {
                g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
            }
            am_vos_filter_lpf(g_in16MonoSampleIn, pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);
#endif // configUSE_SPP_HPF && USE_MIC_SINGLE

#if configUSE_AMBIQ_VADv3
            //
            // VAD processing
            //
            g_sVosSys.ui8RequireFullProcess = am_vos_vad_process(g_sVosBrd.pui32PcmBuf, PCM_FRAME_SIZE_SAMPLES);
#endif // configUSE_AMBIQ_VADv3

#if configUSE_SPP_P2A && USE_MIC_SINGLE
#if configUSE_AMBIQ_VADv3
            if(g_sVosSys.ui8RequireFullProcess)
#endif // configUSE_AMBIQ_VADv3
            {
                float fCur = 0;
                float fAvg = 0;
                
//                short sPgaFlag = am_vos_p2a_process(pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);
                am_vos_p2a_process(pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES, &fCur, &fAvg);
//                if(sPgaFlag == 1) 
                if(((fCur - fAvg) < 12) && (fAvg > -22))
                {
                    if(g_sVosSys.u8PgaFlag == 0)
                    {
                        AM_VOS_LOG_DEBUG("P2A : 1\n");
                        g_sVosSys.u8PgaFlag = 1;
                    }
                    // Gain setting
                    g_sVosBrd.fPGAdB -= AM_PGA_ADJ_STEP_DB;
                    if(g_sVosBrd.fPGAdB < AM_PGA_LOWEST_GAIN_DB)
                    {
                        g_sVosBrd.fPGAdB = AM_PGA_LOWEST_GAIN_DB;
                        AM_VOS_LOG_DEBUG("PGA : Saturated %2.2f %2.2f %2.4f PGA %f PGAL 0x%x\n",
                                        fCur, fAvg, fCur - fAvg, g_sVosBrd.fPGAdB,
                                        (g_sVosBrd.fPGAdB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB);
                    }
                    else
                    {
#if USE_DMIC_PDM
                        PDMn(USE_PDM_MODULE)->CORECFG0_b.PGAL = (g_sVosBrd.fPGAdB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB;
#elif USE_AMIC_AUDADC
                        g_sVosBrd.sAudadcGainConfig.ui32LGA = (uint32_t)(g_sVosBrd.fPGAdB * 2 + 12);
                        g_sVosBrd.sAudadcGainConfig.ui32HGADELTA = 0;
                        am_hal_audadc_internal_pga_config(g_sVosBrd.pvAUDADCHandle, &g_sVosBrd.sAudadcGainConfig);
#endif
                    }
                }
                else
                {
                    if(g_sVosSys.u8PgaFlag == 1)
                    {
                        AM_VOS_LOG_INFO("P2A : 0\n");
                        g_sVosSys.u8PgaFlag = 0;
                        g_sVosBrd.fPGAdB = AM_PGA_DEFAULT_GAIN_DB;

                        // Gain setting
#if USE_DMIC_PDM
                        PDMn(USE_PDM_MODULE)->CORECFG0_b.PGAL = (g_sVosBrd.fPGAdB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB;
#elif USE_AMIC_AUDADC
                        g_sVosBrd.sAudadcGainConfig.ui32LGA = (uint32_t)(g_sVosBrd.fPGAdB * 2 + 12);
                        g_sVosBrd.sAudadcGainConfig.ui32HGADELTA = 0;
                        am_hal_audadc_internal_pga_config(g_sVosBrd.pvAUDADCHandle, &g_sVosBrd.sAudadcGainConfig);
#endif
                    }
                }
            }
#endif // configUSE_SPP_P2A && USE_MIC_SINGLE

#if configUSE_SPP_AGC && USE_MIC_SINGLE
#if configUSE_AMBIQ_VADv3
            if(g_sVosSys.ui8RequireFullProcess)
#endif // configUSE_AMBIQ_VADv3
            {
                for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
                {
                    g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
                }
                am_vos_agc_process(g_sVosAud.pvSppAgcHandle, g_in16MonoSampleIn, pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);
            }
#endif // configUSE_SPP_AGC && USE_MIC_SINGLE

#if configUSE_SPP_EXP && USE_MIC_SINGLE
#if configUSE_AMBIQ_VADv3
            if(g_sVosSys.ui8RequireFullProcess)
#endif // configUSE_AMBIQ_VADv3
            {
                for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
                {
                    g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
                }
                am_vos_exp_process(g_in16MonoSampleIn, pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);
            }
#endif // configUSE_SPP_EXP && USE_MIC_SINGLE

#if configUSE_SPP_DRC && USE_MIC_SINGLE
#if configUSE_AMBIQ_VADv3
            if(g_sVosSys.ui8RequireFullProcess)
#endif // configUSE_AMBIQ_VADv3
            {
                for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
                {
                    g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
                }
                am_vos_comp_process(g_sVosAud.pvSppCompHandle, g_in16MonoSampleIn,
                                    pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);

                for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
                {
                    g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
                }
                am_vos_agc_process(g_sVosAud.pvSppAgcHandle, g_in16MonoSampleIn,
                                   pi16PcmBuf, PCM_FRAME_SIZE_SAMPLES);

                for(int indx = 0; indx < PCM_FRAME_SIZE_SAMPLES; indx++)
                {
                    g_in16MonoSampleIn[indx] = pi16PcmBuf[indx];
                }
                am_vos_exp_process(g_in16MonoSampleIn, pi16PcmBuf,
                                   PCM_FRAME_SIZE_SAMPLES);
            }
#endif // configUSE_SPP_DRC && USE_MIC_SINGLE

#if configUSE_RTT_RECORDER && configUSE_RECORD_AM_SPP
#ifndef AM_PART_APOLLO5_API
            if(g_sVosSys.ui8RecordStartFlag == 1)
#endif // AM_PART_APOLLO5A
            {
                am_vos_rtt_record((void *)g_sVosBrd.pui32PcmBuf, 
                                  PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
            }
#endif // configUSE_RTT_RECORDER && configUSE_RECORD_AM_SPP

#if configUSE_RTT_RECORDER && configUSE_RECORD_AM_SPP_IN_OUT
#ifndef AM_PART_APOLLO5_API
            if(g_sVosSys.ui8RecordStartFlag == 1)
#endif // AM_PART_APOLLO5A
            {
                for(int i = 0; i < PCM_FRAME_SIZE_SAMPLES; i++)
                {
                    ui32RttBuf[i] |= ((pi16PcmBuf[i] << 16) & 0xFFFF0000);
                }
                am_vos_rtt_record((void *)ui32RttBuf, 
                                  PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM * 2);
            }
#endif // configUSE_RTT_RECORDER && configUSE_RECORD_AM_SPP_IN_OUT

#if VOS_MEASURE_AMSPP_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
            g_sVosSys.ui32PcmTimeTick += am_vos_bench_dwt_getcycle();
            am_vos_bench_dwt_stop();
            g_sVosSys.ui32PcmCallNum++;
#else
            if((am_vos_timer_benchmark_read() - TickBeforePcm) > 0)
            {
                g_sVosSys.ui32PcmTimeTick += (am_vos_timer_benchmark_read() - TickBeforePcm);
                g_sVosSys.ui32PcmCallNum++;
                am_vos_timer_benchmark_clear_and_start();
            }
#endif
#endif // VOS_MEASURE_AMSPP_MIPS

#if configUSE_AMBIQ_VADv3 || configUSE_NN_VAD
            am_audio_buffer_nested_push(AM_AUDIO_BUFFER_STEREO, g_sVosBrd.pui32PcmBuf,
                                        PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
            am_vos_task_send(AM_VOS_TASK_VAD, AM_VOS_TASK_AUD_PROCESSING,
                                AM_VOS_MESSAGE_SHORT, EMPTY_MESSAGE, NULL);
#else
            am_audio_buffer_nested_push(AM_AUDIO_BUFFER_STEREO, g_sVosBrd.pui32PcmBuf,
                                        PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
            am_vos_task_send(AM_VOS_TASK_VAD, AM_VOS_TASK_AUD_PROCESSING, AM_VOS_MESSAGE_LONG,
                             PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM,
                             &(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO]));
#endif
        }
    }
}

#if configUSE_PAIRING_MODE_BTN && configUSE_BLE
void am_vos_pairing_mode_process(void)
{
    // disable interrupt on this pin
    am_vos_gpio_disable_irq(PAIRING_MODE_BUTTON);

    // button pressed, send a msg to trigger push to talk
    am_vos_task_send_fromISR(AM_VOS_ISR_GPIO, AM_VOS_TASK_AUD_PROCESSING,
                             AM_VOS_MESSAGE_SHORT, PAIRING_MODE_MESSAGE, NULL);
}
#endif // configUSE_PAIRING_MODE_BTN && configUSE_BLE

#if configUSE_PUSH_TO_TALK
void am_vos_push_to_talk_process(void *pArg)
{
    // disable interrupt on this pin
    am_vos_gpio_disable_irq(PUSH_TO_TALK_BUTTON);
    am_vos_task_send_fromISR(AM_VOS_ISR_GPIO, AM_VOS_TASK_AUD_PROCESSING,
                             AM_VOS_MESSAGE_SHORT, PUSH_TO_TALK_MESSAGE, NULL);
}
#endif // configUSE_PUSH_TO_TALK

#if configUSE_MUTE_MIC
void am_vos_mute_mic_process(void *pArg)
{
    am_vos_gpio_disable_irq(MUTE_MIC_BUTTON);

    AM_VOS_LOG_DEBUG("am_vos_mute_mic_process()\n");

    // button pressed, send a msg to trigger mute MIC.
    am_vos_task_send_fromISR(AM_VOS_ISR_GPIO, AM_VOS_TASK_LED, 
                             AM_VOS_MESSAGE_SHORT, MUTE_MIC_MESSAGE, NULL);
}
#endif // configUSE_MUTE_MIC

#if configUSE_RTT_RECORDER && !defined(AM_PART_APOLLO5_API)
void
am_vos_rtt_amu2s_process(void)
{
    am_vos_gpio_disable_irq(RTT_DUMP_BUTTON);

    // button pressed, send a msg to trigger mute MIC.
    am_vos_task_send_fromISR(AM_VOS_ISR_GPIO, AM_VOS_TASK_LED,
                             AM_VOS_MESSAGE_SHORT, RTT_AMU2S_MESSAGE, NULL);
}
#endif // configUSE_RTT_RECORDER
//*****************************************************************************
//
// AAD ISR
//
//*****************************************************************************
#if configUSE_AAD
void
am_vos_aad_handler(void *pArg)
{
    AM_VOS_LOG_INFO("WN\n");

    am_vos_gpio_disable_irq(AM_T5838_WAKE_PIN);
#if (!T5838_AAD_D1)
    am_vos_aad_mic_enable();
#endif
    g_sVosSys.ui8AadSkipFrameFlag = 1;

    if(g_sVosSys.ui8AadEnabled == 1)
    {
#if configUSE_AMBIQ_VADv3
        g_sVosSys.i32TimeoutToAadEnable = AAD_TIMEOUT_HYS_COUNT;
#else
        g_sVosSys.i32TimeoutToAadEnable = AAD_TIMEOUT_COUNT;
#endif
        g_sVosSys.ui8AadDebounceFlag = 1;
    }
    else if(g_sVosSys.ui8AadEnabled == 0)
    {
        g_sVosSys.i32TimeoutToAadEnable = 0;
        g_sVosSys.ui8AadDebounceFlag = 0;
    }
}

void
am_vos_aad_check_status(void)
{
    if((g_sVosSys.i32TimeoutToAadEnable > 0) && (g_sVosSys.ui8AadDebounceFlag == 1))
    {
#if T5838_AAD_D1
        uint32_t ui32PinVal;
        am_hal_gpio_state_read(AM_T5838_WAKE_PIN, AM_HAL_GPIO_INPUT_READ, &ui32PinVal);
        if(!ui32PinVal)
        {
            g_sVosSys.i32TimeoutToAadEnable--;
        }
        else
        {
            g_sVosSys.i32TimeoutToAadEnable = AAD_TIMEOUT_COUNT;
        }
#else
        g_sVosSys.i32TimeoutToAadEnable--;
#endif
    }
    else if((g_sVosSys.i32TimeoutToAadEnable <= 0) && (g_sVosSys.ui8AadDebounceFlag == 1))
    {
        AM_VOS_LOG_INFO("WQ\n");
        //
        // enter into quiet status
        //
        g_sVosSys.ui8AadEnabled = 1;
#if (!T5838_AAD_D1)
        am_vos_aad_mic_disable();
#endif
        //vTaskDelay(pdMS_TO_TICKS(3000));		// Need to review : Lewis!
        am_vos_gpio_enable_irq(GRP_IRQ_AAD_WAKE_PINGRP, AM_T5838_WAKE_PIN);
        g_sVosSys.i32TimeoutToAadEnable = 0;
        g_sVosSys.ui8AadDebounceFlag = 0;
    }
}
#endif // configUSE_AAD

#if configUSE_LEDs
void
am_vos_heartbeat_led_process(void)
{
#if configUSE_BLE
    if(am_vos_is_connected())
    {
        am_devices_led_toggle(am_bsp_psLEDs, LED_SYSTEM);
    }
    else
#endif // configUSE_BLE
    {
        am_devices_led_on(am_bsp_psLEDs, LED_SYSTEM);
        vTaskDelay(pdMS_TO_TICKS(10));
        am_devices_led_off(am_bsp_psLEDs, LED_SYSTEM);
        vTaskDelay(pdMS_TO_TICKS(150));
        am_devices_led_on(am_bsp_psLEDs, LED_SYSTEM);
        vTaskDelay(pdMS_TO_TICKS(10));
        am_devices_led_off(am_bsp_psLEDs, LED_SYSTEM);
    }
}
#endif

